NetBSD-5.0.2/sys/arch/alpha/a12/if_xb.c

Compare this file to the similar file:
Show the results in this format:

/* $NetBSD: if_xb.c,v 1.18 2007/10/17 19:52:54 garbled Exp $ */

/* [Notice revision 2.2]
 * Copyright (c) 1997, 1998 Avalon Computer Systems, Inc.
 * All rights reserved.
 *
 * Author: Ross Harvey
 *
 * 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 and
 *    author 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. Neither the name of Avalon Computer Systems, Inc. nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 4. This copyright will be assigned to The NetBSD Foundation on
 *    1/1/2000 unless these terms (including possibly the assignment
 *    date) are updated in writing by Avalon prior to the latest specified
 *    assignment date.
 *
 * THIS SOFTWARE IS PROVIDED BY AVALON COMPUTER SYSTEMS, 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 AVALON OR THE 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.
 */

/* 
 * Notes.
 * 
 * Since the NetBSD build rules force the use of function prototypes, even on
 * functions that are defined before they are called, I've taken advantage of
 * the opportunity and organized this module in top down fashion, with
 * functions generally calling down the page rather than up. It's different.
 * I think I'm getting to like it this way.
 * 
 * The crossbar interface is not exactly a peripheral device, and it cannot
 * appear on anything other than an alpha-based Avalon A12.  The crossbar
 * controller is built into the core logic.
 * 
 * If this version of the driver supports MPS transport, it may have some
 * large static data declarations. Don't worry about it, as Avalon a12
 * support should not appear in a GENERIC or INSTALL kernel.
 * 
 * (Every A12 ever shipped had 512 MB per CPU except one site, which had 256
 * MB.  Partly has a result of this, it is unlikely that a kernel configured
 * for an A12 would be exactly the thing to use on most workstations, so we
 * don't really need to worry that we might be configured in a generic or
 * site-wide kernel image.)
 * 
 * This preliminary crossbar driver supports IP transport using PIO.  Although
 * it would be nice to have a DMA driver, do note that the crossbar register
 * port is 128 bits wide, so we have 128-bit PIO.  (The 21164 write buffer
 * will combine two 64-bit stores before they get off-chip.) Also, the rtmon
 * driver wasn't DMA either, so at least the NetBSD driver is as good as any
 * other that exists now.
 * 
 * We'll do DMA and specialized transport ops later. Given the high speed of
 * the PIO mode, no current applications require DMA bandwidth, but everyone
 * benefits from low latency. The PIO mode is actually lower in latency
 * anyway.
 */

#include "opt_avalon_a12.h"		/* Config options headers */
#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */

__KERNEL_RCSID(0, "$NetBSD: if_xb.c,v 1.18 2007/10/17 19:52:54 garbled Exp $");

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/socket.h>
#include <sys/mbuf.h>
#include <sys/sockio.h>

#include <uvm/uvm_extern.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/in_var.h>

#include <machine/autoconf.h>
#include <machine/rpb.h>

#include <dev/isa/isareg.h>
#include <dev/isa/isavar.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>

#include <alpha/pci/a12creg.h>
#include <alpha/pci/a12cvar.h>
#include <alpha/pci/pci_a12.h>

#if 1
#define	XB_DEBUG xb_debug
#else
#define	XB_DEBUG 0
#endif

#undef	Static
#if 1
#define	Static
#else
#define	Static static
#endif

#define	IF_XB()	/* Generate ctags(1) key */

#define	XBAR_MTU	   (9*1024)	/* Quite an arbitrary number */
#define	XBAR_MAXFRAMEHDR   48		/* Used to compute if_mtu */

#define	XB_DEFAULT_MTU() (XBAR_MTU - XBAR_MAXFRAMEHDR)

#define	FIFO_WORDCOUNT 60

static int  xb_put_blk __P((struct mbuf *));
static int  xb_put __P((struct mbuf *));
static long xb_fifo_empty(void);

int	xbmatch __P((struct device *, struct cfdata *, void *));
void	xbattach __P((struct device *, struct device *, void *));

struct xb_softc {
	struct	device d;
} xb_softc;

CFATTACH_DECL(xb, sizeof(struct xb_softc),
    xbmatch, xbattach, NULL, NULL);

extern struct cfdriver xb_cd;

long	*xb_incoming;
int	xb_incoming_max = XBAR_MTU;

typedef struct ccode_struct {
	int64_t	lo64,		/* magic channel address s-word, high part*/
		hi64;		/* magic channel address s-word, low part */
} ccode_type;
/*
 * Switch channel codes.  Prepending one of these words will get you through
 * the switch, which will eat the word, open the addressed channel, and
 * forward the rest of the switch frame.  Obviously, this helps if the second
 * switch word in the frame is the address word for a cascaded switch. (This
 * can be repeated for an arbitrary depth of MSN.) The words aren't quite as
 * weird as they look: the switch is really lots of narrow switches in an
 * array, and they don't switch an even number of hex digits.  Also, there is
 * a parity bit on most of the subunits.
 */
ccode_type channel[]={
	    { 0x0000000000000000, 0x0000000000000000  },
	    { 0x8882108421084210, 0x1104210842108421  },
	    { 0x4441084210842108, 0x2208421084210842  },
	    { 0xccc318c6318c6318, 0x330c6318c6318c63  },
	    { 0x2220842108421084, 0x4410842108421084  },
	    { 0xaaa294a5294a5294, 0x5514a5294a5294a5  },
	    { 0x66618c6318c6318c, 0x6618c6318c6318c6  },
	    { 0xeee39ce739ce739c, 0x771ce739ce739ce7  },
	    { 0x1110421084210842, 0x8821084210842108  },
	    { 0x99925294a5294a52, 0x9925294a5294a529  },
	    { 0x55514a5294a5294a, 0xaa294a5294a5294a  },
	    { 0xddd35ad6b5ad6b5a, 0xbb2d6b5ad6b5ad6b  },
	    { 0x3330c6318c6318c6, 0xcc318c6318c6318c  },
	    { 0xbbb2d6b5ad6b5ad6, 0xdd35ad6b5ad6b5ad  }
};

Static enum xb_intr_rcv_state_t {
	XBIR_PKTHDR = 0, XBIR_TRANS
} xb_intr_rcv_state;

struct xb_config { int am_i_used; } xb_configuration;

Static struct ifnet xbi;

Static int frame_len;
static int xb_debug;

Static void xb_start __P((struct ifnet *));
Static void xb_mcrp_write __P((long *, int, int));
static inline void xb_onefree __P((void));
static long set_interrupt_on_fifo_empty(void);
static void xb_init(struct ifnet *);
static int  xb_intr __P((void *));
static void xb_intr_rcv __P((void));
Static void quickload __P((volatile long *, long *));
static void xb_init_config __P((struct xb_config *, int));
static int  xb_output __P((struct ifnet *, struct mbuf *,
			const struct sockaddr *, struct rtentry *));
static int  xb_ioctl __P((struct ifnet *, u_long, void *));
static void xb_stop __P((void));
static void a12_xbar_setup __P((void));

/* There Can Be Only One */

int xbfound;

int
xbmatch(parent, match, aux)
	struct device *parent;
	struct cfdata *match;
	void *aux;
{

	return	cputype == ST_AVALON_A12
		&& !xbfound;
}

void
xbattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct xb_config *ccp;

	strcpy(xbi.if_xname, self->dv_xname);
	xbfound = 1;
	ccp = &xb_configuration;
	xb_init_config(ccp, 1);
	printf(": driver %s mtu %lu\n", "$Revision: 1.18 $", xbi.if_mtu);
}

static void
xb_init_config(ccp, mallocsafe)
	struct xb_config *ccp;
	int mallocsafe;
{
	/* 
	 * The driver actually only needs about 64 bytes of buffer but with a
	 * nice contiguous frame we can call m_devget()
	 */
	if (mallocsafe && xb_incoming == NULL) {
		xb_incoming = malloc(xb_incoming_max, M_DEVBUF, M_NOWAIT);
		if (xb_incoming == NULL)
			DIE();
	}
	a12_xbar_setup();
	a12_intr_register_xb(xb_intr);
}

/*
 * From The A12 Theory of Operation. Used with permission.
 *      --- --- ------ -- ---------
 *
 *	 Message Channel Status Register
 *
 *     31                                 0
 *      |                                 |
 *      10987654 32109876 54321098 76543210 
 *
 *      ........ ........ 0oiefaAr TR...... MCSR
 *
 *     Field   Type    Name    Function
 *
 *      R      R,W1C   RBC     Receive Block Complete
 *      T      R,W1C   TBC     Transmit Block Complete
 *      r      R       IMP     Incoming message pending
 *      A      R       IMFAE   Incoming message fifo almost empty
 *      a      R       OMFAF   Outgoing message fifo almost full
 *      f      R       OMFF    Outgoing message fifo full
 *      e      R       OMFE    Outgoing message fifo empty
 *      i      R       DMAin   Incoming DMA channel armed
 *      o      R       DMAout  Outgoing DMA channel armed
 *     
 *	     Interrupts Generated from MCSR
 *
 *     IMChInt <= (RBC or IMP) and not DMAin
 *     OMChInt <= ((TBC and not OMFAF) or (OMFE and OMR.E(6))
 *		) and not DMAout
 *
 */
static int
xb_intr(p)
	void *p;
{
	int	n;
	long	mcsrval;
	/* 
	 * The actual conditions under which this interrupt is generated are
	 * a bit complicated, and no status flag is available that reads out
	 * the final values of the interrupt inputs. But, it doesn't really
	 * matter. Simply check for receive data and transmitter IFF_OACTIVE.
	 */
	while ((mcsrval = REGVAL(A12_MCSR)) & A12_MCSR_IMP)
		for(n = mcsrval & A12_MCSR_IMFAE ? 1 : 5; n; --n)
			xb_intr_rcv();

	if (xbi.if_flags & IFF_OACTIVE
	&&  mcsrval & A12_MCSR_OMFE) {
		xbi.if_flags &= ~IFF_OACTIVE;
		REGVAL(A12_OMR) &= ~A12_OMR_OMF_ENABLE;
		alpha_wmb();
		xb_start(&xbi);
	}
	return 0;
}
/* 
 * The interface logic will shoot us down with MCE (Missing Close Error) or
 * ECE (Embedded Close Error) if we aren't in sync with the hardware w.r.t.
 * frame boundaries. As those are panic-level errors: Don't Get Them.
 */
static void
xb_intr_rcv()
{
struct	mbuf *m;
long	frameword[2];
static	long *xb_ibp;
int	s = 0;	/* XXX gcc */

	switch (xb_intr_rcv_state) {
	    case XBIR_PKTHDR:
		xb_ibp = xb_incoming;
		quickload(REGADDR(A12_FIFO), frameword); /* frame_len >= 16 */
		frame_len = frameword[0];
		if (!(20 <= frame_len && frame_len+16 <= xb_incoming_max))
			DIE();
		/*
		 * The extra word when frames are of an aligned size is due
		 * to the way the output routines work. After the mbuf is
		 * sent xb_put_blk(NULL) is called. If there is a leftover
		 * 127-bit-or-less fragment then the close word rides on it,
		 * otherwise it gets an entire 128 bits of zeroes.
		 */
		if (frame_len & 0xf)
			frame_len = (frame_len + 0xf) >> 4;
		else	frame_len = (frame_len >> 4) + 1;
	      --frame_len; /* we read the frame len + the first packet int64 */
		*xb_ibp++ = frameword[1];
		xb_intr_rcv_state = XBIR_TRANS;
		break;
	    case XBIR_TRANS:
		if (frame_len > 1)
			quickload(REGADDR(A12_FIFO), frameword);
		else if (frame_len == 1) {
			quickload(REGADDR(A12_FIFO_LWE), frameword);
			xb_intr_rcv_state = XBIR_PKTHDR;
		} else if (XB_DEBUG)
			DIE();
	      --frame_len;
		xb_ibp[0] = frameword[0];
		xb_ibp[1] = frameword[1];
		xb_ibp   += 2;
		if (xb_intr_rcv_state == XBIR_PKTHDR) {
			if (XB_DEBUG) {
				s = splnet();
				if (s != splnet())
					DIE();
			}
		      ++xbi.if_ipackets;
			if (IF_QFULL(&ipintrq)) {
				IF_DROP(&ipintrq);
			      ++xbi.if_iqdrops;
			} else {
				m = m_devget((void *)xb_incoming,
					(char *)xb_ibp - (char *)xb_incoming,
					0, &xbi, 0L);
				if (m) {
					xbi.if_ibytes += m->m_pkthdr.len;
					IF_ENQUEUE(&ipintrq, m);
				} else
				      ++xbi.if_ierrors;
			}
			if (XB_DEBUG)
				splx(s);
		}
		break;
	    default:
		DIE();
	}
}
/*
 * Make it easy for gcc to load a[0..1] without interlocking between
 * a[0] and a[1]. (If it did, that would be two external bus cycles.)
 */
Static void
quickload(volatile long *a, long *b)
{
long	t1,t2;

	t1 = a[0];
	t2 = a[1];
	b[0] = t1;
	b[1] = t2;
}
/*
 * Verify during debugging that we have not overflowed the FIFO 
 */
static inline void
xb_onefree()
{
	if (XB_DEBUG && REGVAL(A12_MCSR) & A12_MCSR_OMFF)
		DIE();
}

static void
xb_init(ifp)
	struct ifnet *ifp;
{
	ifp->if_flags |= IFF_RUNNING;
}

static void
xb_stop()
{
}

static int
xb_ioctl(ifp, cmd, data)
	struct ifnet *ifp;
	u_long cmd;
	void *data;
{
	struct ifaddr *ifa = (struct ifaddr *)data;
	int s, error = 0;

	s = splnet();
	switch (cmd) {
	case SIOCSIFADDR:
		xbi.if_flags |= IFF_UP;
		switch (ifa->ifa_addr->sa_family) {
#ifdef INET
		case AF_INET:
			xb_init(ifp);
			break;
#endif
		default:
			xb_init(ifp);
		}
		break;
	case SIOCSIFFLAGS:
		if ((ifp->if_flags & IFF_UP) == 0 &&
		    (ifp->if_flags & IFF_RUNNING) != 0) {
			xb_stop();
			ifp->if_flags &= ~IFF_RUNNING;
		} else if ((ifp->if_flags & IFF_UP) != 0 &&
		           (ifp->if_flags & IFF_RUNNING) == 0) {
			xb_start(ifp);
		} else
			xb_init(ifp);
		if (ifp->if_flags & IFF_DEBUG)
			xb_debug = 1;
		break;
	default:
		error = EINVAL;
		break;
	}
	splx(s);
	return error;
}
/*
 * XXX - someday, keep a software copy of A12_OMR. We can execute up to
 * 200 or 300 instructions in the time it takes to do the read part of an
 * external bus cycle RMW op. (Or 10 - 20 cache cycles.)
 */
static inline long
xb_fifo_empty(void)
{
	return REGVAL(A12_MCSR) & A12_MCSR_OMFE;
}
/*
 * rtmon frames
 *
 * [ (... data) : commid : sourcepid : dstpid : ktype : length : frametype ]
 *
 * At the moment, NetBSD ip frames are not compatible with rtmon frames:
 *
 * [ ... data : length ]
 */
static int
xb_output(ifp, m0, dst, rt0)
	struct ifnet *ifp;
	struct mbuf *m0;
	const struct sockaddr *dst;
	struct rtentry *rt0;
{
	int	i,s;
	struct	mbuf *m = m0;
	const char	*lladdr;
	char	*xbh;
	long	xbo_framesize;
	const struct	sockaddr_dl *llsa;
	int	xbaddr;

#ifdef DIAGNOSTIC
	if (ifp != &xbi)
		DIE();
#endif
	if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
		m_freem(m);
		return ENETDOWN;
	}
	/*
	 * We want an IP packet with a link level route, on a silver platter.
	 */
	if (rt0 == NULL
	|| (rt0->rt_flags & (RTF_GATEWAY | RTF_LLINFO))
	|| (llsa = satocsdl(rt0->rt_gateway)) == NULL
	||  llsa->sdl_family != AF_LINK
	||  llsa->sdl_slen   != 0) {
	      ++ifp->if_oerrors;
		m_freem(m);
		return EHOSTUNREACH;
	}
	if (dst == NULL
	||  dst->sa_family != AF_INET) {
		/*
		 * This is because we give all received packets to ipintrq
		 * right now.
		 */
		What();
		m_freem(m);
	      ++ifp->if_noproto;
		return EAFNOSUPPORT;
	}
	/* 
	 * The a12MppSwitch is a wormhole routed MSN consisting of a number
	 * (usually n==1) of 14 channel crossbar switches.  Each route through
	 * a switch requires a 128 bit address word that specifies the channel
	 * to emerge on. The address word is eaten by the switch and the
	 * rest of the packet is routed through.
	 */
	lladdr = CLLADDR(llsa);
	if (llsa->sdl_alen != 1)			/* XXX */
		DIE();	/* OK someday, but totally unexpected right now */
	/*
	 * Alternatively, we could lookup the address word and output
	 * it with PIO when the mbuf is dequeued
	 */
	xbo_framesize = m->m_pkthdr.len + 8;
	M_PREPEND(m, 16 * llsa->sdl_alen + 8, M_DONTWAIT);
	if (m == NULL)
		return ENOBUFS;
	xbh = mtod(m, char *);
	for (i=0; i<llsa->sdl_alen; ++i) {
		xbaddr = (lladdr[i] & 0xff) - 1;
		if (!(0 <= xbaddr && xbaddr <= 11))	/* XXX */
			DIE();		/* 12 or 13 will be OK later */
		memcpy(xbh, &channel[xbaddr].lo64, 16);
		xbh += 16;
	}
	memcpy(xbh, &xbo_framesize, 8);
	s = splnet();
	if (IF_QFULL(&ifp->if_snd)) {
		IF_DROP(&ifp->if_snd);
	      ++ifp->if_oerrors;
		splx(s);
		m_freem(m);
		return ENOBUFS;
	}
	ifp->if_obytes += m->m_pkthdr.len;
      ++ifp->if_opackets;
	IF_ENQUEUE(&ifp->if_snd, m);
	if ((ifp->if_flags & IFF_OACTIVE) == 0)
		xb_start(ifp);
	splx(s);
	if (m->m_flags & M_MCAST)
		ifp->if_omcasts++;
	return 0;
}

void
xb_start(ifp)
	struct ifnet *ifp;
{
	struct	mbuf *m;

	if ((xbi.if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING)
		return;
	for (;;) {
		IF_DEQUEUE(&xbi.if_snd, m);
		if (m == 0)
			return;
		/*
		 * XXX The variable-length switch address words cause problems
		 * for bpf, for now, leave it out. XXX It's not too hard to
		 * fix, though, as there are lots of techniques that will
		 * identify the number of switch address words.
		 */
		if (!xb_put(m)) {
			xbi.if_flags |= IFF_OACTIVE;
			return;
		}
	}
}

static int
xb_put(m)
	struct mbuf *m;
{
	struct mbuf *n;
	int len;

	if (XB_DEBUG && (alpha_pal_rdps() & 7) < 3)
		DIE();	/* this "cannot happen", of course */
	for (; m; m = n) {
		len = m->m_len;
		if (len == 0 || xb_put_blk(m))
			MFREE(m, n);
		else	return 0;
	}
	xb_put_blk(NULL);
	return 1;
}
/*
 * Write a single mbuf to the transmit channel fifo. We can only write 128-bit
 * words. Right now, we pad at the end.  It is possible to pad at the
 * beginning, especially since lots of games can be played at the receiver
 * with the mbuf data pointer. Padding at the beginning requires a pad-count
 * field in a header, but it means you can always DMA the data, regardless of
 * alignment. Of course, we don't DMA at all, right now.
 */
static int
xb_put_blk(m)
	struct	mbuf *m;
{
	static	long leftover[2];	/* 0-15 bytes from last xb_put_blk() */
	static	int  leftover_len;	/* non-aligned amount from last call */
	long	xfertmp[8];	/* aligned switch word buffer */
	int 	frag_len,	/* fifo stream unit */
		fifo_len,	/* space left in fifo */
		fillin,		/* amount needed to complete a switch word */
		full,		/* remember to restart on fifo full */
		len;		/* amount of mbuf left to do */
	char	*blk;		/* location we are at in mbuf */
	static	int fifo_free;	/* current # of switch words free in fifo */

#define	XFERADJ() ((char *)xfertmp + leftover_len)

	/* There is always room for the close word */

	if (m == NULL) {
		if (leftover_len)
			leftover_len = 0;
		else	leftover[0]  = leftover[1] = 0;
		xb_mcrp_write(leftover, 1, 1);
	      --fifo_free;
		return 1;
	}

restart:
	if (fifo_free < 2) {
		if (!xb_fifo_empty()) {
			if(!set_interrupt_on_fifo_empty())  {
				/* still empty */
				xbi.if_flags |= IFF_OACTIVE;
				IF_PREPEND(&xbi.if_snd, m);
				return 0;
			}
		}
		fifo_free = FIFO_WORDCOUNT;
	}
	len = m->m_len;
	if (len == 0)
		return 1;	/* clean finish, nothing left over */
	blk = mtod(m, char *);
	if (leftover_len) {
		/* See function intro comment regarding padding */
		if (leftover_len + len < sizeof leftover) {
			/* Heh, not even enough to write out */
			memcpy(XFERADJ(), blk, len);
			leftover_len += len;
			return 1;
		}
		xfertmp[0] = leftover[0];
		xfertmp[1] = leftover[1];
		fillin = sizeof leftover - leftover_len;
		memcpy(XFERADJ(), blk, fillin);
		blk += fillin;
		len -= fillin;
		xb_mcrp_write(xfertmp, 1, 0);
		leftover_len = 0;
	      --fifo_free;
	}
	/* fifo_free is known to be >= 1 at this point */
	while (len >= 16) {
		full = 0;
		frag_len = sizeof xfertmp;
		if (frag_len > len)
			frag_len = len;
		fifo_len = fifo_free * 16;
		if (frag_len > fifo_len) {
			frag_len = fifo_len;
			full = 1;
		}
		frag_len &= ~0xf;
		memcpy(xfertmp, blk, frag_len);
		frag_len >>= 4;		/* Round down to switch word size */
		xb_mcrp_write(xfertmp, frag_len, 0);
		fifo_free -= frag_len;
		frag_len <<= 4;
		len -= frag_len;
		blk += frag_len;
		if (full) {
			m_adj(m, blk - mtod(m, char *));
			goto restart;
		}
	}
	memcpy(leftover, blk, len);
	leftover_len = len;
	return 1;
}

static long
set_interrupt_on_fifo_empty(void)
{
	REGVAL(A12_OMR) |= A12_OMR_OMF_ENABLE;
	alpha_mb();
	if(xb_fifo_empty()) {
		REGVAL(A12_OMR) &= ~A12_OMR_OMF_ENABLE;
		alpha_mb();
		return 1;
	}
	return 0;
}
/*
 * Write an aligned block of switch words to the FIFO
 */
Static void
xb_mcrp_write(d, n, islast)
	long *d;
{
	volatile long *xb_fifo = islast ? REGADDR(A12_FIFO_LWE) 
					: REGADDR(A12_FIFO);
	int i;

	if (XB_DEBUG && islast && n != 1)
		DIE();
	n <<= 1;
	for (i = 0; i < n; i += 2) {
		xb_onefree();
		xb_fifo[0] = d[i];
		xb_fifo[1] = d[i+1];
		alpha_wmb();
	}
}

/*
const
int32_t	xbar_bc_addr = XBAR_BROADCAST;
 */

static void
a12_xbar_setup()
{
	xbi.if_softc    = &xb_softc;
	xbi.if_start    = xb_start;
	xbi.if_ioctl    = xb_ioctl;
	xbi.if_flags    = IFF_BROADCAST	/* ha ha */
		        | IFF_SIMPLEX;

	xbi.if_type     = IFT_A12MPPSWITCH;
	xbi.if_addrlen  = 32;
	xbi.if_hdrlen   = 32;
	xbi.if_mtu      = XB_DEFAULT_MTU();
	xbi.if_output   = xb_output;
	/* xbi.if_broadcastaddr = (u_int8_t)&xbar_bc_addr; */

	if_attach(&xbi);
	if_alloc_sadl(&xbi);

#if NBPFILTER > 0
	bpfattach(&xbi, DLT_NULL, 0);
#endif
}