[BACK]Return to if_mc.c CVS log [TXT][DIR] Up to [local] / sys / arch / macppc / dev

File: [local] / sys / arch / macppc / dev / if_mc.c (download)

Revision 1.1, Tue Mar 4 16:07:29 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*	$OpenBSD: if_mc.c,v 1.10 2007/04/22 22:31:14 deraadt Exp $	*/
/*	$NetBSD: if_mc.c,v 1.9.16.1 2006/06/21 14:53:13 yamt Exp $	*/

/*-
 * Copyright (c) 1997 David Huang <khym@bga.com>
 * All rights reserved.
 *
 * Portions of this code are based on code by Denton Gentry <denny1@home.com>
 * and Yanagisawa Takeshi <yanagisw@aa.ap.titech.ac.jp>.
 *
 * 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. 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 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.
 *
 */

/*
 * AMD AM79C940 (MACE) driver with DBDMA bus attachment and DMA routines 
 * for onboard ethernet found on most old world macs. 
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/mbuf.h>
#include <sys/buf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/device.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>

#include <uvm/uvm_extern.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#endif

#include "bpfilter.h"
#if NBPFILTER > 0
#include <net/bpf.h>
#include <net/bpfdesc.h>
#endif

#include <dev/ofw/openfirm.h>
#include <machine/pio.h>
#include <machine/bus.h>
#include <machine/autoconf.h>

#include <macppc/dev/dbdma.h>

#define MC_REGSPACING   	16
#define MC_REGSIZE      	MACE_NREGS * MC_REGSPACING
#define MACE_REG(x)     	((x)*MC_REGSPACING)
#define MACE_BUFLEN		2048
#define MACE_TXBUFS		2
#define MACE_RXBUFS		8

#define MC_RXDMABUFS		4

#define MACE_BUFSZ       	((MACE_RXBUFS + MACE_TXBUFS + 2) * MACE_BUFLEN)

#define NIC_GET(sc, reg)	(in8rb(sc->sc_reg + MACE_REG(reg)))

#define NIC_PUT(sc, reg, val)   (out8rb(sc->sc_reg + MACE_REG(reg), (val)))

/*
 * AMD MACE (Am79C940) register definitions
 */
#define	MACE_RCVFIFO		0   /* Receive FIFO [15-00] (read only) */
#define	MACE_XMTFIFO		1   /* Transmit FIFO [15-00] (write only) */
#define	MACE_XMTFC		2   /* Transmit Frame Control (read/write) */
#define	MACE_XMTFS		3   /* Transmit Frame Status (read only) */
#define	MACE_XMTRC		4   /* Transmit Retry Count (read only) */
#define	MACE_RCVFC		5   /* Receive Frame Control (read/write) */
#define	MACE_RCVFS		6   /* Receive Frame Status (4 bytes) (read only) */
#define	MACE_FIFOFC		7   /* FIFO Frame Count (read only) */
#define	MACE_IR			8   /* Interrupt Register (read only) */
#define	MACE_IMR		9   /* Interrupt Mask Register (read/write) */
#define	MACE_PR			10  /* Poll Register (read only) */
#define	MACE_BIUCC		11  /* BIU Configuration Control (read/write) */
#define	MACE_FIFOCC		12  /* FIFO Configuration Control (read/write) */
#define	MACE_MACCC		13  /* MAC Configuration Control (read/write) */
#define	MACE_PLSCC		14  /* PLS Configuration Control (read/write) */
#define	MACE_PHYCC		15  /* PHY Confiuration Control (read/write) */
#define	MACE_CHIPIDL		16  /* Chip ID Register [07-00] (read only) */
#define	MACE_CHIPIDH		17  /* Chip ID Register [15-08] (read only) */
#define	MACE_IAC		18  /* Internal Address Configuration (read/write) */
/*	RESERVED		19     Reserved (read/write as 0) */
#define	MACE_LADRF		20  /* Logical Address Filter (8 bytes) (read/write) */
#define	MACE_PADR		21  /* Physical Address (6 bytes) (read/write) */
/*	RESERVED		22     Reserved (read/write as 0) */
/*	RESERVED		23     Reserved (read/write as 0) */
#define	MACE_MPC		24  /* Missed Packet Count (read only) */
/*	RESERVED		25     Reserved (read/write as 0) */
#define	MACE_RNTPC		26  /* Runt Packet Count (read only) */
#define	MACE_RCVCC		27  /* Receive Collision Count (read only) */
/*	RESERVED		28     Reserved (read/write as 0) */
#define	MACE_UTR		29  /* User Test Register (read/write) */
#define	MACE_RTR1		30  /* Reserved Test Register 1 (read/write as 0) */
#define	MACE_RTR2		31  /* Reserved Test Register 2 (read/write as 0) */

#define	MACE_NREGS		32

/* 2: Transmit Frame Control (XMTFC) */
#define	DRTRY			0x80	/* Disable Retry */
#define	DXMTFCS			0x08	/* Disable Transmit FCS */
#define	APADXMT			0x01	/* Auto Pad Transmit */

/* 3: Transmit Frame Status (XMTFS) */
#define	XMTSV			0x80	/* Transmit Status Valid */
#define	UFLO			0x40	/* Underflow */
#define	LCOL			0x20	/* Late Collision */
#define	MORE			0x10	/* More than one retry needed */
#define	ONE			0x08	/* Exactly one retry needed */
#define	DEFER			0x04	/* Transmission deferred */
#define	LCAR			0x02	/* Loss of Carrier */
#define	RTRY			0x01	/* Retry Error */

/* 4: Transmit Retry Count (XMTRC) */
#define	EXDEF			0x80	/* Excessive Defer */
#define	XMTRC			0x0f	/* Transmit Retry Count */

/* 5: Receive Frame Control (RCVFC) */
#define	LLRCV			0x08	/* Low Latency Receive */
#define	MR			0x04	/* Match/Reject */
#define	ASTRPRCV		0x01	/* Auto Strip Receive */

/* 6: Receive Frame Status (RCVFS) */
/* 4 byte register; read 4 times to get all of the bytes */
/* Read 1: RFS0 - Receive Message Byte Count [7-0] (RCVCNT) */

/* Read 2: RFS1 - Receive Status (RCVSTS) */
#define	OFLO			0x80	/* Overflow flag */
#define	CLSN			0x40	/* Collision flag */
#define	FRAM			0x20	/* Framing Error flag */
#define	FCS			0x10	/* FCS Error flag */
#define	RCVCNT			0x0f	/* Receive Message Byte Count [11-8] */

/* Read 3: RFS2 - Runt Packet Count (RNTPC) [7-0] */

/* Read 4: RFS3 - Receive Collision Count (RCVCC) [7-0] */

/* 7: FIFO Frame Count (FIFOFC) */
#define	RCVFC			0xf0	/* Receive Frame Count */
#define	XMTFC			0x0f	/* Transmit Frame Count */

/* 8: Interrupt Register (IR) */
#define	JAB			0x80	/* Jabber Error */
#define	BABL			0x40	/* Babble Error */
#define	CERR			0x20	/* Collision Error */
#define	RCVCCO			0x10	/* Receive Collision Count Overflow */
#define	RNTPCO			0x08	/* Runt Packet Count Overflow */
#define	MPCO			0x04	/* Missed Packet Count Overflow */
#define	RCVINT			0x02	/* Receive Interrupt */
#define	XMTINT			0x01	/* Transmit Interrupt */

/* 9: Interrut Mask Register (IMR) */
#define	JABM			0x80	/* Jabber Error Mask */
#define	BABLM			0x40	/* Babble Error Mask */
#define	CERRM			0x20	/* Collision Error Mask */
#define	RCVCCOM			0x10	/* Receive Collision Count Overflow Mask */
#define	RNTPCOM			0x08	/* Runt Packet Count Overflow Mask */
#define	MPCOM			0x04	/* Missed Packet Count Overflow Mask */
#define	RCVINTM			0x02	/* Receive Interrupt Mask */
#define	XMTINTM			0x01	/* Transmit Interrupt Mask */

/* 10: Poll Register (PR) */
#define	XMTSV			0x80	/* Transmit Status Valid */
#define	TDTREQ			0x40	/* Transmit Data Transfer Request */
#define	RDTREQ			0x20	/* Receive Data Transfer Request */

/* 11: BIU Configuration Control (BIUCC) */
#define	BSWP			0x40	/* Byte Swap */
#define	XMTSP			0x30	/* Transmit Start Point */
#define	XMTSP_4			0x00	/* 4 bytes */
#define	XMTSP_16		0x10	/* 16 bytes */
#define	XMTSP_64		0x20	/* 64 bytes */
#define	XMTSP_112		0x30	/* 112 bytes */
#define	SWRST			0x01	/* Software Reset */

/* 12: FIFO Configuration Control (FIFOCC) */
#define	XMTFW			0xc0	/* Transmit FIFO Watermark */
#define	XMTFW_8			0x00	/* 8 write cycles */
#define	XMTFW_16		0x40	/* 16 write cycles */
#define	XMTFW_32		0x80	/* 32 write cycles */
#define	RCVFW			0x30	/* Receive FIFO Watermark */
#define	RCVFW_16		0x00	/* 16 bytes */
#define	RCVFW_32		0x10	/* 32 bytes */
#define	RCVFW_64		0x20	/* 64 bytes */
#define	XMTFWU			0x08	/* Transmit FIFO Watermark Update */
#define	RCVFWU			0x04	/* Receive FIFO Watermark Update */
#define	XMTBRST			0x02	/* Transmit Burst */
#define	RCVBRST			0x01	/* Receive Burst */

/* 13: MAC Configuration (MACCC) */
#define	PROM			0x80	/* Promiscuous */
#define	DXMT2PD			0x40	/* Disable Transmit Two Part Deferral */
#define	EMBA			0x20	/* Enable Modified Back-off Algorithm */
#define	DRCVPA			0x08	/* Disable Receive Physical Address */
#define	DRCVBC			0x04	/* Disable Receive Broadcast */
#define	ENXMT			0x02	/* Enable Transmit */
#define	ENRCV			0x01	/* Enable Receive */

/* 14: PLS Configuration Control (PLSCC) */
#define	XMTSEL			0x08	/* Transmit Mode Select */
#define	PORTSEL			0x06	/* Port Select */
#define	PORTSEL_AUI		0x00	/* Select AUI */
#define	PORTSEL_10BT		0x02	/* Select 10BASE-T */
#define	PORTSEL_DAI		0x04	/* Select DAI port */
#define	PORTSEL_GPSI		0x06	/* Select GPSI */
#define	ENPLSIO			0x01	/* Enable PLS I/O */

/* 15: PHY Configuration (PHYCC) */
#define	LNKFL			0x80	/* Link Fail */
#define	DLNKTST			0x40	/* Disable Link Test */
#define	REVPOL			0x20	/* Reversed Polarity */
#define	DAPC			0x10	/* Disable Auto Polarity Correction */
#define	LRT			0x08	/* Low Receive Threshold */
#define	ASEL			0x04	/* Auto Select */
#define	RWAKE			0x02	/* Remote Wake */
#define	AWAKE			0x01	/* Auto Wake */

/* 18: Internal Address Configuration (IAC) */
#define	ADDRCHG			0x80	/* Address Change */
#define	PHYADDR			0x04	/* Physical Address Reset */
#define	LOGADDR			0x02	/* Logical Address Reset */

/* 28: User Test Register (UTR) */
#define	RTRE			0x80	/* Reserved Test Register Enable */
#define	RTRD			0x40	/* Reserved Test Register Disable */
#define	RPA			0x20	/* Run Packet Accept */
#define	FCOLL			0x10	/* Force Collision */
#define	RCVFCSE			0x08	/* Receive FCS Enable */
#define	LOOP			0x06	/* Loopback Control */
#define	LOOP_NONE		0x00	/* No Loopback */
#define	LOOP_EXT		0x02	/* External Loopback */
#define	LOOP_INT		0x04	/* Internal Loopback, excludes MENDEC */
#define	LOOP_INT_MENDEC		0x06	/* Internal Loopback, includes MENDEC */

struct mc_rxframe {
	u_int8_t		rx_rcvcnt;
	u_int8_t		rx_rcvsts;
	u_int8_t		rx_rntpc;
	u_int8_t		rx_rcvcc;
	u_char			*rx_frame;
};

struct mc_softc {
	struct device	   	sc_dev;		/* base device glue */
	struct arpcom	   	sc_arpcom;	/* Ethernet common part */
	struct timeout	  	sc_tick_ch;

	struct mc_rxframe       sc_rxframe;
	u_int8_t		sc_biucc;
	u_int8_t		sc_fifocc;
	u_int8_t		sc_plscc;
	u_int8_t		sc_enaddr[6];
	u_int8_t		sc_pad[2];
	int			sc_havecarrier; /* carrier status */

	char			*sc_reg;
	bus_dma_tag_t		sc_dmat;
	bus_dmamap_t		sc_bufmap;
	bus_dma_segment_t       sc_bufseg[1];

	dbdma_regmap_t		*sc_txdma;
	dbdma_regmap_t		*sc_rxdma;
	dbdma_command_t		*sc_txdmacmd;
	dbdma_command_t		*sc_rxdmacmd;
	dbdma_t			sc_txdbdma;
	dbdma_t			sc_rxdbdma;

	caddr_t			sc_txbuf;
	caddr_t			sc_rxbuf;
	paddr_t			sc_txbuf_pa;
	paddr_t			sc_rxbuf_pa;
	int			sc_tail;
	int			sc_rxset;
	int			sc_txset;
	int			sc_txseti;
};

int	mc_match(struct device *, void *, void *);
void	mc_attach(struct device *, struct device *, void *);

struct  cfattach mc_ca = {
	sizeof(struct mc_softc), mc_match, mc_attach
};

struct cfdriver mc_cd = {
	NULL, "mc", DV_IFNET
};

void	mc_init(struct mc_softc *sc);
void	mc_put(struct mc_softc *sc, u_int len);
int	mc_dmaintr(void *arg);
void	mc_reset_rxdma(struct mc_softc *sc);
void	mc_reset_txdma(struct mc_softc *sc);
int     mc_stop(struct mc_softc *sc);
int     mc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
void    mc_start(struct ifnet *ifp);
void    mc_reset(struct mc_softc *sc);
void    mc_tint(struct mc_softc *sc);
void	mc_rint(struct mc_softc *sc);
int	mc_intr(void *);
void	mc_watchdog(struct ifnet *ifp);

u_int   maceput(struct mc_softc *sc, struct mbuf *);
void    mace_read(struct mc_softc *, caddr_t, int);
struct mbuf *mace_get(struct mc_softc *, caddr_t, int);
static void mace_calcladrf(struct mc_softc *, u_int8_t *);
void	mc_putpacket(struct mc_softc *, u_int);

int
mc_match(struct device *parent, void *arg, void *aux)
{
	struct confargs *ca = aux;

	if (strcmp(ca->ca_name, "mace") != 0)
		return 0;

	/* requires 6 regs */
	if (ca->ca_nreg / sizeof(int) != 6)
		return 0;

	/* requires 3 intrs */
	if (ca->ca_nintr / sizeof(int) != 3)
		return 0;

	return 1;
}

void
mc_attach(struct device *parent, struct device *self, void *aux)
{
	struct confargs *ca = aux;
	struct mc_softc *sc = (struct mc_softc *)self;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	u_int8_t lladdr[ETHER_ADDR_LEN];
	int nseg, error;

	if (OF_getprop(ca->ca_node, "local-mac-address", lladdr,
	    ETHER_ADDR_LEN) != ETHER_ADDR_LEN) {
		printf(": failed to get MAC address.\n");
		return;
	}

	ca->ca_reg[0] += ca->ca_baseaddr;
	ca->ca_reg[2] += ca->ca_baseaddr;
	ca->ca_reg[4] += ca->ca_baseaddr;

	if ((sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1])) == NULL) {
		printf(": cannot map registers\n");
		return;
	}

	sc->sc_dmat = ca->ca_dmat;
	sc->sc_tail = 0;

	if ((sc->sc_txdma = mapiodev(ca->ca_reg[2], ca->ca_reg[3])) == NULL) {
		printf(": cannot map TX DMA registers\n");
		goto notxdma;
	}
	if ((sc->sc_rxdma = mapiodev(ca->ca_reg[4], ca->ca_reg[5])) == NULL) {
		printf(": cannot map RX DMA registers\n");
		goto norxdma;
	}
	if ((sc->sc_txdbdma = dbdma_alloc(sc->sc_dmat, 2)) == NULL) {
		printf(": cannot alloc TX DMA descriptors\n");
		goto notxdbdma;
	}
	sc->sc_txdmacmd = sc->sc_txdbdma->d_addr;

	if ((sc->sc_rxdbdma = dbdma_alloc(sc->sc_dmat, 8 + 1)) == NULL) {
		printf(": cannot alloc RX DMA descriptors\n");
		goto norxdbdma;
	}
	sc->sc_rxdmacmd = sc->sc_rxdbdma->d_addr;

	if ((error = bus_dmamem_alloc(sc->sc_dmat, MACE_BUFSZ, PAGE_SIZE, 0,
	    sc->sc_bufseg, 1, &nseg, BUS_DMA_NOWAIT))) {
		printf(": cannot allocate DMA mem (%d)\n", error);
		goto nodmamem;
	}

	if ((error = bus_dmamem_map(sc->sc_dmat, sc->sc_bufseg, nseg,
	    MACE_BUFSZ, &sc->sc_txbuf, BUS_DMA_NOWAIT))) {
		printf(": cannot map DMA mem (%d)\n", error);
		goto nodmamap;
	}

	if ((error = bus_dmamap_create(sc->sc_dmat, MACE_BUFSZ, 1, MACE_BUFSZ,
	    0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_bufmap))) {
		printf(": cannot create DMA map (%d)\n", error);
		goto nodmacreate;
	}

	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_bufmap, sc->sc_txbuf,
	    MACE_BUFSZ, NULL, BUS_DMA_NOWAIT))) {
		printf(": cannot load DMA map (%d)\n", error);
		goto nodmaload;
	}

	sc->sc_txbuf_pa = sc->sc_bufmap->dm_segs->ds_addr;
	sc->sc_rxbuf = sc->sc_txbuf + MACE_BUFLEN * MACE_TXBUFS;
	sc->sc_rxbuf_pa = sc->sc_txbuf_pa + MACE_BUFLEN * MACE_TXBUFS;

	printf(": irq %d,%d,%d", ca->ca_intr[0], ca->ca_intr[1],
	    ca->ca_intr[2]);

	/* disable receive DMA */
	dbdma_reset(sc->sc_rxdma);

	/* disable transmit DMA */
	dbdma_reset(sc->sc_txdma);

	/* install interrupt handlers */
	mac_intr_establish(parent, ca->ca_intr[2], IST_LEVEL, IPL_NET,
	    mc_dmaintr, sc, sc->sc_dev.dv_xname);
	mac_intr_establish(parent, ca->ca_intr[0],  IST_LEVEL, IPL_NET,
	    mc_intr, sc, sc->sc_dev.dv_xname);

	sc->sc_biucc = XMTSP_64;
	sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU |
	    XMTBRST | RCVBRST;
	sc->sc_plscc = PORTSEL_GPSI | ENPLSIO;

	/* reset the chip and disable all interrupts */
	NIC_PUT(sc, MACE_BIUCC, SWRST);
	DELAY(100);

	NIC_PUT(sc, MACE_IMR, ~0);

	bcopy(lladdr, sc->sc_enaddr, ETHER_ADDR_LEN);
	bcopy(sc->sc_enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
	printf(": address %s\n", ether_sprintf(lladdr));

	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
	ifp->if_softc = sc;
	ifp->if_ioctl = mc_ioctl;
	ifp->if_start = mc_start;
	ifp->if_flags =
		IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST;
	ifp->if_watchdog = mc_watchdog;
	ifp->if_timer = 0;
	IFQ_SET_READY(&ifp->if_snd);

	if_attach(ifp);
	ether_ifattach(ifp);

	return;
nodmaload:
	bus_dmamap_destroy(sc->sc_dmat, sc->sc_bufmap);
nodmacreate:
	bus_dmamem_unmap(sc->sc_dmat, sc->sc_txbuf, MACE_BUFSZ);
nodmamap:
	bus_dmamem_free(sc->sc_dmat, sc->sc_bufseg, 1);
nodmamem:
	dbdma_free(sc->sc_rxdbdma);
norxdbdma:
	dbdma_free(sc->sc_txdbdma);
notxdbdma:
	unmapiodev((void *)sc->sc_rxdma, ca->ca_reg[5]);
norxdma:
	unmapiodev((void *)sc->sc_txdma, ca->ca_reg[3]);
notxdma:
	unmapiodev(sc->sc_reg, ca->ca_reg[1]);
}

int
mc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
{
	struct mc_softc *sc = ifp->if_softc;
	struct ifaddr *ifa = (struct ifaddr *)data;
	struct ifreq *ifr;

	int s = splnet(), err = 0;

	switch (cmd) {

	case SIOCSIFADDR:
		ifp->if_flags |= IFF_UP;
		if (!(ifp->if_flags & IFF_RUNNING))
			mc_init(sc);
#ifdef INET
		if (ifa->ifa_addr->sa_family == AF_INET)
			arp_ifinit(&sc->sc_arpcom, ifa);
#endif
		break;

	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    (ifp->if_flags & IFF_RUNNING) != 0) {
			/*
			 * If interface is marked down and it is running,
			 * then stop it.
			 */
			mc_stop(sc);
		} else if ((ifp->if_flags & IFF_UP) != 0 &&
		    (ifp->if_flags & IFF_RUNNING) == 0) {
			/*
			 * If interface is marked up and it is stopped,
			 * then start it.
			 */
			mc_init(sc);
		} else {
			/*
			 * reset the interface to pick up any other changes
			 * in flags
			 */
			mc_reset(sc);
			mc_start(ifp);
		}
		break;

	case SIOCADDMULTI:
	case SIOCDELMULTI:
		ifr = (struct ifreq *) data;
		err = (cmd == SIOCADDMULTI) ?
		    ether_addmulti(ifr, &sc->sc_arpcom) :
		    ether_delmulti(ifr, &sc->sc_arpcom);

		if (err == ENETRESET) {
			/*
			 * Multicast list has changed; set the hardware
			 * filter accordingly. But remember UP flag!
			 */
			if (ifp->if_flags & IFF_RUNNING)
				mc_reset(sc);
			err = 0;
		}
		break;
	default:
		err = EINVAL;
	}
	splx(s);
	return (err);
}

/*
 * Encapsulate a packet of type family for the local net.
 */
void
mc_start(struct ifnet *ifp)
{
	struct mc_softc	*sc = ifp->if_softc;
	struct mbuf	*m;

	if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
		return;

	while (1) {
		if (ifp->if_flags & IFF_OACTIVE)
			return;

		IFQ_DEQUEUE(&ifp->if_snd, m);
		if (m == NULL)
			return;

#if NBPFILTER > 0
		/*
		 * If bpf is listening on this interface, let it
		 * see the packet before we commit it to the wire.
		 */
		if (ifp->if_bpf)
			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
#endif

		/*
		 * Copy the mbuf chain into the transmit buffer.
		 */
		ifp->if_flags |= IFF_OACTIVE;
		maceput(sc, m);

		ifp->if_opackets++;		/* # of pkts */
	}
}

/*
 * reset and restart the MACE.  Called in case of fatal
 * hardware/software errors.
 */
void
mc_reset(struct mc_softc *sc)
{
	mc_stop(sc);
	mc_init(sc);
}

void
mc_init(struct mc_softc *sc)
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	u_int8_t maccc, ladrf[8];
	int s, i;

	s = splnet();

	NIC_PUT(sc, MACE_BIUCC, sc->sc_biucc);
	NIC_PUT(sc, MACE_FIFOCC, sc->sc_fifocc);
	NIC_PUT(sc, MACE_IMR, ~0); /* disable all interrupts */
	NIC_PUT(sc, MACE_PLSCC, sc->sc_plscc);

	NIC_PUT(sc, MACE_UTR, RTRD); /* disable reserved test registers */

	/* set MAC address */
	NIC_PUT(sc, MACE_IAC, ADDRCHG);
	while (NIC_GET(sc, MACE_IAC) & ADDRCHG)
		;
	NIC_PUT(sc, MACE_IAC, PHYADDR);
	for (i = 0; i < ETHER_ADDR_LEN; i++)
		out8rb(sc->sc_reg + MACE_REG(MACE_PADR) + i,
		    sc->sc_enaddr[i]);

	/* set logical address filter */
	mace_calcladrf(sc, ladrf);

	NIC_PUT(sc, MACE_IAC, ADDRCHG);
	while (NIC_GET(sc, MACE_IAC) & ADDRCHG)
		;
	NIC_PUT(sc, MACE_IAC, LOGADDR);
	for (i = 0; i < 8; i++)
		out8rb(sc->sc_reg + MACE_REG(MACE_LADRF) + i,
		    ladrf[i]);

	NIC_PUT(sc, MACE_XMTFC, APADXMT);
	/*
	* No need to autostrip padding on receive... Ethernet frames
	* don't have a length field, unlike 802.3 frames, so the MACE
	* can't figure out the length of the packet anyways.
	*/
	NIC_PUT(sc, MACE_RCVFC, 0);

	maccc = ENXMT | ENRCV;
	if (ifp->if_flags & IFF_PROMISC)
		maccc |= PROM;

	NIC_PUT(sc, MACE_MACCC, maccc);

	mc_reset_rxdma(sc);
	mc_reset_txdma(sc);
	/*
	* Enable all interrupts except receive, since we use the DMA
	* completion interrupt for that.
	*/
	NIC_PUT(sc, MACE_IMR, RCVINTM);

	/* flag interface as "running" */
	ifp->if_flags |= IFF_RUNNING;
	ifp->if_flags &= ~IFF_OACTIVE;

	splx(s);
}

/*
 * Close down an interface and free its buffers.
 * Called on final close of device, or if mcinit() fails
 * part way through.
 */
int
mc_stop(struct mc_softc *sc)
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	int s;

	s = splnet();

	NIC_PUT(sc, MACE_BIUCC, SWRST);
	DELAY(100);

	ifp->if_timer = 0;
	ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);

	splx(s);
	return (0);
}

/*
 * Called if any Tx packets remain unsent after 5 seconds,
 * In all cases we just reset the chip, and any retransmission
 * will be handled by higher level protocol timeouts.
 */
void
mc_watchdog(struct ifnet *ifp)
{
	struct mc_softc *sc = ifp->if_softc;

	printf("mcwatchdog: resetting chip\n");
	mc_reset(sc);
}

int
mc_intr(void *arg)
{
	struct mc_softc *sc = arg;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	u_int8_t ir;

	ir = NIC_GET(sc, MACE_IR) & ~NIC_GET(sc, MACE_IMR);

	if (ir & JAB) {
#ifdef MCDEBUG
		printf("%s: jabber error\n", sc->sc_dev.dv_xname);
#endif
		ifp->if_oerrors++;
	}

	if (ir & BABL) {
#ifdef MCDEBUG
		printf("%s: babble\n", sc->sc_dev.dv_xname);
#endif
		ifp->if_oerrors++;
	 }

	if (ir & CERR) {
#ifdef MCDEBUG
		printf("%s: collision error\n", sc->sc_dev.dv_xname);
#endif
		ifp->if_collisions++;
	 }

	/*
	 * Pretend we have carrier; if we don't this will be cleared
	 * shortly.
	 */
	sc->sc_havecarrier = 1;

	if (ir & XMTINT)
		mc_tint(sc);

	if (ir & RCVINT)
		mc_rint(sc);

	return(1);
}

void
mc_tint(struct mc_softc *sc)
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	u_int8_t xmtrc, xmtfs;

	xmtrc = NIC_GET(sc, MACE_XMTRC);
	xmtfs = NIC_GET(sc, MACE_XMTFS);

	if ((xmtfs & XMTSV) == 0)
		return;

	if (xmtfs & UFLO) {
		printf("%s: underflow\n", sc->sc_dev.dv_xname);
		mc_reset(sc);
		return;
	}

	if (xmtfs & LCOL) {
		printf("%s: late collision\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
		ifp->if_collisions++;
	}

	if (xmtfs & MORE)
		/* Real number is unknown. */
		ifp->if_collisions += 2;
	else if (xmtfs & ONE)
		ifp->if_collisions++;
	else if (xmtfs & RTRY) {
		printf("%s: excessive collisions\n", sc->sc_dev.dv_xname);
		ifp->if_collisions += 16;
		ifp->if_oerrors++;
	}

	if (xmtfs & LCAR) {
		sc->sc_havecarrier = 0;
		printf("%s: lost carrier\n", sc->sc_dev.dv_xname);
		ifp->if_oerrors++;
	}

	ifp->if_flags &= ~IFF_OACTIVE;
	ifp->if_timer = 0;
	mc_start(ifp);
}

void
mc_rint(struct mc_softc *sc)
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
#define rxf	sc->sc_rxframe
	u_int len;

	len = (rxf.rx_rcvcnt | ((rxf.rx_rcvsts & 0xf) << 8)) - 4;

#ifdef MCDEBUG
	if (rxf.rx_rcvsts & 0xf0)
		printf("%s: rcvcnt %02x rcvsts %02x rntpc 0x%02x rcvcc 0x%02x\n",
		    sc->sc_dev.dv_xname, rxf.rx_rcvcnt, rxf.rx_rcvsts,
		    rxf.rx_rntpc, rxf.rx_rcvcc);
#endif

	if (rxf.rx_rcvsts & OFLO) {
#ifdef MCDEBUG
		printf("%s: receive FIFO overflow\n", sc->sc_dev.dv_xname);
#endif
		ifp->if_ierrors++;
		return;
	}

	if (rxf.rx_rcvsts & CLSN)
		ifp->if_collisions++;

	if (rxf.rx_rcvsts & FRAM) {
#ifdef MCDEBUG
		printf("%s: framing error\n", sc->sc_dev.dv_xname);
#endif
		ifp->if_ierrors++;
		return;
	}

	if (rxf.rx_rcvsts & FCS) {
#ifdef MCDEBUG
		printf("%s: frame control checksum error\n", sc->sc_dev.dv_xname);
#endif
		ifp->if_ierrors++;
		return;
	}

	mace_read(sc, rxf.rx_frame, len);
#undef  rxf
}
/*
 * stuff packet into MACE (at splnet)
 */
u_int
maceput(struct mc_softc *sc, struct mbuf *m)
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct mbuf *n;
	u_int len, totlen = 0;
	u_char *buff;

	buff = sc->sc_txbuf;

	for (; m; m = n) {
		u_char *data = mtod(m, u_char *);
		len = m->m_len;
		totlen += len;
		bcopy(data, buff, len);
		buff += len;
		MFREE(m, n);
	}

	if (totlen > PAGE_SIZE)
		panic("%s: maceput: packet overflow", sc->sc_dev.dv_xname);

#if 0
	if (totlen < ETHERMIN + sizeof(struct ether_header)) {
		int pad = ETHERMIN + sizeof(struct ether_header) - totlen;
		bzero(sc->sc_txbuf + totlen, pad);
		totlen = ETHERMIN + sizeof(struct ether_header);
	}
#endif


	/* 5 seconds to watch for failing to transmit */
	ifp->if_timer = 5;
	mc_putpacket(sc, totlen);
	return (totlen);
}

void
mace_read(struct mc_softc *sc, caddr_t pkt, int len)
{
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct mbuf *m;

	if (len <= sizeof(struct ether_header) ||
	    len > ETHERMTU + sizeof(struct ether_header)) {
#ifdef MCDEBUG
		printf("%s: invalid packet size %d; dropping\n",
		    sc->sc_dev.dv_xname, len);
#endif
		ifp->if_ierrors++;
		return;
	}

	m = mace_get(sc, pkt, len);
	if (m == NULL) {
		ifp->if_ierrors++;
		return;
	}

	ifp->if_ipackets++;

#if NBPFILTER > 0
	/* Pass the packet to any BPF listeners. */
	if (ifp->if_bpf)
		bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN);
#endif

	/* Pass the packet up. */
	ether_input_mbuf(ifp, m);
}

/*
 * Pull data off an interface.
 * Len is length of data, with local net header stripped.
 * We copy the data into mbufs.  When full cluster sized units are present
 * we copy into clusters.
 */
struct mbuf *
mace_get(struct mc_softc *sc, caddr_t pkt, int totlen)
{
	 struct mbuf *m;
	 struct mbuf *top, **mp;
	 int len;

	 MGETHDR(m, M_DONTWAIT, MT_DATA);
	 if (m == NULL)
		  return (NULL);

	 m->m_pkthdr.rcvif = &sc->sc_arpcom.ac_if;
	 m->m_pkthdr.len = totlen;
	 len = MHLEN;
	 top = 0;
	 mp = &top;

	 while (totlen > 0) {
		  if (top) {
			   MGET(m, M_DONTWAIT, MT_DATA);
			   if (m == NULL) {
				    m_freem(top);
				    return (NULL);
			   }
			   len = MLEN;
		  }
		  if (totlen >= MINCLSIZE) {
			   MCLGET(m, M_DONTWAIT);
			   if ((m->m_flags & M_EXT) == 0) {
				    m_free(m);
				    m_freem(top);
				    return (NULL);
			   }
			   len = MCLBYTES;
		  }
		  m->m_len = len = min(totlen, len);
		  bcopy(pkt, mtod(m, caddr_t), len);
		  pkt += len;
		  totlen -= len;
		  *mp = m;
		  mp = &m->m_next;
	 }

	 return (top);
}

void
mc_putpacket(struct mc_softc *sc, u_int len)
{
	dbdma_command_t *cmd = sc->sc_txdmacmd;

	DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, len, sc->sc_txbuf_pa,
	   DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
	cmd++;
	DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_ALWAYS,
	   DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);

	dbdma_start(sc->sc_txdma, sc->sc_txdbdma);
}

/*
 * Interrupt handler for the MACE DMA completion interrupts
 */
int
mc_dmaintr(void *arg)
{
	struct mc_softc *sc = arg;
	int status, offset, statoff;
	int datalen, resid;
	int i, n, count;
	dbdma_command_t *cmd;

	/* We've received some packets from the MACE */
	/* Loop through, processing each of the packets */
	i = sc->sc_tail;
	for (n = 0; n < MC_RXDMABUFS; n++, i++) {
		if (i == MC_RXDMABUFS)
			i = 0;

		cmd = &sc->sc_rxdmacmd[i];
		status = dbdma_ld16(&cmd->d_status);
		resid = dbdma_ld16(&cmd->d_resid);

		if ((status & DBDMA_CNTRL_ACTIVE) == 0) {
			continue;
		}

		count = dbdma_ld16(&cmd->d_count);
		datalen = count - resid;
		datalen -= 4; /* 4 == status bytes */

		if (datalen < 4 + sizeof(struct ether_header)) {
			printf("short packet len=%d\n", datalen);
			/* continue; */
			goto next;
		}
		DBDMA_BUILD_CMD(cmd, DBDMA_CMD_STOP, 0, 0, 0, 0);

		offset = i * MACE_BUFLEN;
		statoff = offset + datalen;
		sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[statoff + 0];
		sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[statoff + 1];
		sc->sc_rxframe.rx_rntpc  = sc->sc_rxbuf[statoff + 2];
		sc->sc_rxframe.rx_rcvcc  = sc->sc_rxbuf[statoff + 3];
		sc->sc_rxframe.rx_frame  = sc->sc_rxbuf + offset;

		mc_rint(sc);

next:
		DBDMA_BUILD_CMD(cmd, DBDMA_CMD_IN_LAST, 0, DBDMA_INT_ALWAYS,
		    DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);

		cmd->d_status = 0;
		cmd->d_resid = 0;
		sc->sc_tail = i + 1;
	}

	dbdma_continue(sc->sc_rxdma);

	return 1;
}

void
mc_reset_rxdma(struct mc_softc *sc)
{
	dbdma_command_t *cmd = sc->sc_rxdmacmd;
	int i;
	u_int8_t maccc;

	/* Disable receiver, reset the DMA channels */
	maccc = NIC_GET(sc, MACE_MACCC);
	NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV);

	dbdma_reset(sc->sc_rxdma);

	bzero(sc->sc_rxdmacmd, 8 * sizeof(dbdma_command_t));
	for (i = 0; i < MC_RXDMABUFS; i++) {
		DBDMA_BUILD(cmd, DBDMA_CMD_IN_LAST, 0, MACE_BUFLEN,
		    sc->sc_rxbuf_pa + MACE_BUFLEN * i, DBDMA_INT_ALWAYS,
		    DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
		cmd++;
	}

	DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0,
	    DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS);
	dbdma_st32(&cmd->d_cmddep, sc->sc_rxdbdma->d_paddr);
	cmd++;

	sc->sc_tail = 0;

	dbdma_start(sc->sc_rxdma, sc->sc_rxdbdma);
	/* Reenable receiver, reenable DMA */
	NIC_PUT(sc, MACE_MACCC, maccc);
}

void
mc_reset_txdma(struct mc_softc *sc)
{
	dbdma_command_t *cmd = sc->sc_txdmacmd;
	dbdma_regmap_t *dmareg = sc->sc_txdma;
	u_int8_t maccc;

	/* disable transmitter */
	maccc = NIC_GET(sc, MACE_MACCC);
	NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT);

	dbdma_reset(sc->sc_txdma);

	bzero(sc->sc_txdmacmd, 2 * sizeof(dbdma_command_t));
	DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, 0, sc->sc_txbuf_pa,
	    DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);
	cmd++;
	DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0,
	    DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER);

	out32rb(&dmareg->d_cmdptrhi, 0);
	out32rb(&dmareg->d_cmdptrlo, sc->sc_txdbdma->d_paddr);

	/* restore old value */
	NIC_PUT(sc, MACE_MACCC, maccc);
}

/*
 * Go through the list of multicast addresses and calculate the logical
 * address filter.
 */
void
mace_calcladrf(struct mc_softc *sc, u_int8_t *af)
{
	struct ether_multi *enm;
	u_int32_t crc;
	struct ifnet *ifp = &sc->sc_arpcom.ac_if;
	struct arpcom *ac = &sc->sc_arpcom;
	struct ether_multistep step;
	/*
	 * Set up multicast address filter by passing all multicast addresses
	 * through a crc generator, and then using the high order 6 bits as an
	 * index into the 64 bit logical address filter.  The high order bit
	 * selects the word, while the rest of the bits select the bit within
	 * the word.
	 */

	*((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0;
	ETHER_FIRST_MULTI(step, ac, enm);
	while (enm != NULL) {
		if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
			/*
			 * We must listen to a range of multicast addresses.
			 * For now, just accept all multicasts, rather than
			 * trying to set only those filter bits needed to match
			 * the range.  (At this time, the only use of address
			 * ranges is for IP multicast routing, for which the
			 * range is big enough to require all bits set.)
			 */
			goto allmulti;
		}

		crc = ether_crc32_le(enm->enm_addrlo, sizeof(enm->enm_addrlo));

		/* Just want the 6 most significant bits. */
		crc >>= 26;

		/* Set the corresponding bit in the filter. */
		af[crc >> 3] |= 1 << (crc & 7);

		ETHER_NEXT_MULTI(step, enm);
	}
	ifp->if_flags &= ~IFF_ALLMULTI;
	return;

allmulti:
	ifp->if_flags |= IFF_ALLMULTI;
	*((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0xffffffff;
}