code:
/*	$OpenBSD: si.c,v 1.36 2015/01/12 21:34:15 miod Exp $	*/
/*	$NetBSD: si.c,v 1.38 1997/08/27 11:24:20 bouyer Exp $	*/

/*-
 * Copyright (c) 1996 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Adam Glass, David Jones, Gordon W. Ross, and Jason R. Thorpe.
 *
 * 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 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.
 */

/*
 * This file contains only the machine-dependent parts of the
 * Sun4 SCSI driver.  (Autoconfig stuff and DMA functions.)
 * The machine-independent parts are in ncr5380sbc.c
 *
 * Supported hardware includes:
 * Sun "SCSI Weird" on OBIO (sw: Sun 4/100-series)
 * Sun SCSI-3 on VME (si: Sun 4/200-series, others)
 *
 * The VME variant has a bit to enable or disable the DMA engine,
 * but that bit also gates the interrupt line from the NCR5380!
 * Therefore, in order to get any interrupt from the 5380, (i.e.
 * for reselect) one must clear the DMA engine transfer count and
 * then enable DMA.  This has the further complication that you
 * CAN NOT touch the NCR5380 while the DMA enable bit is set, so
 * we have to turn DMA back off before we even look at the 5380.
 *
 * What wonderfully whacky hardware this is!
 *
 * David Jones wrote the initial version of this module for NetBSD/sun3,
 * which included support for the VME adapter only. (no reselection).
 *
 * Gordon Ross added support for the Sun 3 OBIO adapter, and re-worked
 * both the VME and OBIO code to support disconnect/reselect.
 * (Required figuring out the hardware "features" noted above.)
 *
 * The autoconfiguration boilerplate came from Adam Glass.
 *
 * Jason R. Thorpe ported the autoconfiguration and VME portions to
 * NetBSD/sparc, and added initial support for the 4/100 "SCSI Weird",
 * a wacky OBIO variant of the VME SCSI-3.  Many thanks to Chuck Cranor
 * for lots of helpful tips and suggestions.  Thanks also to Paul Kranenburg
 * and Chris Torek for bits of insight needed along the way.  Thanks to
 * David Gilbert and Andrew Gillham who risked filesystem life-and-limb
 * for the sake of testing.  Andrew Gillham helped work out the bugs
 * in the 4/100 DMA code.
 */

/*
 * NOTE: support for the 4/100 "SCSI Weird" is not complete!  DMA
 * works, but interrupts (and, thus, reselection) don't.  I don't know
 * why, and I don't have a machine to test this on further.
 *
 * DMA, DMA completion interrupts, and reselection work fine on my
 * 4/260 with modern SCSI-II disks attached.  I've had reports of
 * reselection failing on Sun Shoebox-type configurations where
 * there are multiple non-SCSI devices behind Emulex or Adaptec
 * bridges.  These devices pre-date the SCSI-I spec, and might not
 * behave the way the 5380 code expects.  For this reason, only
 * DMA is enabled by default in this driver.
 *
 *	Jason R. Thorpe <thorpej@NetBSD.ORG>
 *	December 8, 1995
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <sys/device.h>
#include <sys/buf.h>
#include <sys/proc.h>

#include <scsi/scsi_all.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/cpu.h>
#include <machine/pmap.h>

#include <sparc/sparc/vaddrs.h>
#include <sparc/sparc/cpuvar.h>

#define COUNT_SW_LEFTOVERS	XXX	/* See sw DMA completion code */

#include <dev/ic/ncr5380reg.h>
#include <dev/ic/ncr5380var.h>

#include <sparc/dev/sireg.h>

/*
 * Transfers smaller than this are done using PIO
 * (on assumption they're not worth DMA overhead)
 */
#define	MIN_DMA_LEN 128

/*
 * Transfers larger than 65535 bytes need to be split-up.
 * (Some of the FIFO logic has only 16 bits counters.)
 * Make the size an integer multiple of the page size
 * to avoid buf/cluster remap problems.  (paranoid?)
 */
#define	MAX_DMA_LEN 0xE000

#ifdef	DEBUG
int si_debug = 0;
static int si_link_flags = 0 /* | SDEV_DB2 */ ;
#endif

/*
 * This structure is used to keep track of mapped DMA requests.
 */
struct si_dma_handle {
	int 		dh_flags;
#define	SIDH_BUSY	0x01		/* This DH is in use */
#define	SIDH_OUT	0x02		/* DMA does data out (write) */
	u_char *	dh_addr;	/* KVA of start of buffer */
	int 		dh_maplen;	/* Original data length */
	long		dh_dvma;	/* VA of buffer in DVMA space */
	long		dh_startingpa;	/* PA of buffer; for "sw" */
};

/*
 * The first structure member has to be the ncr5380_softc
 * so we can just cast to go back and fourth between them.
 */
struct si_softc {
	struct ncr5380_softc	ncr_sc;
	volatile struct si_regs	*sc_regs;
	struct intrhand	sc_ih;
	int		sc_adapter_type;
	int		sc_adapter_iv_am; /* int. vec + address modifier */
	struct si_dma_handle *sc_dma;
	int		sc_xlen;	/* length of current DMA segment. */
	int		sc_options;	/* options for this instance. */
};

/*
 * Options.  By default, configuration files enable DMA and disable
 * DMA completion interrupts and reselect.  You may enable additional features
 * the `flags' directive in your kernel's configuration file.
 *
 * On the "sw", interrupts (and thus) reselection don't work, so they're
 * disabled.  DMA is still a little dangerous, too.
 */
#define	SI_ENABLE_DMA	0x01	/* Use DMA (maybe polled) */
#define	SI_DMA_INTR	0x02	/* DMA completion interrupts */
#define	SI_DO_RESELECT	0x04	/* Allow disconnect/reselect */
#define	SI_OPTIONS_MASK	(SI_ENABLE_DMA|SI_DMA_INTR|SI_DO_RESELECT)
#define	SW_OPTIONS_MASK	(SI_ENABLE_DMA)
#define SI_OPTIONS_BITS	"\10\3RESELECT\2DMA_INTR\1DMA"

/* How long to wait for DMA before declaring an error. */
int si_dma_intr_timo = 500;	/* ticks (sec. X 100) */

static int	si_match(struct device *, void *, void *);
static void	si_attach(struct device *, struct device *, void *);
static int	si_intr(void *);
static void	si_reset_adapter(struct ncr5380_softc *);
static void	si_minphys(struct buf *, struct scsi_link *);

void si_dma_alloc(struct ncr5380_softc *);
void si_dma_free(struct ncr5380_softc *);
void si_dma_poll(struct ncr5380_softc *);

void si_vme_dma_setup(struct ncr5380_softc *);
void si_vme_dma_start(struct ncr5380_softc *);
void si_vme_dma_stop(struct ncr5380_softc *);

void si_vme_intr_on(struct ncr5380_softc *);
void si_vme_intr_off(struct ncr5380_softc *);

void si_obio_dma_setup(struct ncr5380_softc *);
void si_obio_dma_start(struct ncr5380_softc *);
void si_obio_dma_stop(struct ncr5380_softc *);

void si_obio_intr_on(struct ncr5380_softc *);
void si_obio_intr_off(struct ncr5380_softc *);

static struct scsi_adapter	si_ops = {
	ncr5380_scsi_cmd,		/* scsi_cmd() */
	si_minphys,			/* scsi_minphys() */
	NULL,				/* probe_dev() */
	NULL,				/* free_dev() */
};

/* The Sun SCSI-3 VME controller. */
struct cfattach si_ca = {
	sizeof(struct si_softc), si_match, si_attach
};

struct cfdriver si_cd = {
	NULL, "si", DV_DULL
};

/* The Sun "SCSI Weird" 4/100 obio controller. */
struct cfattach sw_ca = {
	sizeof(struct si_softc), si_match, si_attach
};

struct cfdriver sw_cd = {
	NULL, "sw", DV_DULL
};

static int
si_match(parent, vcf, aux)
	struct device	*parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;
	struct romaux *ra = &ca->ca_ra;

	/* Are we looking for the right thing? */
	if (strcmp(cf->cf_driver->cd_name, ra->ra_name))
		return (0);

	/* Nothing but a Sun 4 is going to have these devices. */
	if (!CPU_ISSUN4)
		return (0);

	/*
	 * Default interrupt priority always is 3.  At least, that's
	 * what my board seems to be at.  --thorpej
	 */
	if (ra->ra_intr[0].int_pri == -1)
		ra->ra_intr[0].int_pri = 3;

	/* Figure out the bus type and look for the appropriate adapter. */
	switch (ca->ca_bustype) {
	case BUS_VME16:
		/* AFAIK, the `si' can only exist on the vmes. */
		if (strcmp(ra->ra_name, "si") ||
		    cpuinfo.cpu_type == CPUTYP_4_100)
			return (0);
		break;

	case BUS_OBIO:
		/* AFAIK, an `sw' can only exist on the obio. */
		if (strcmp(ra->ra_name, "sw") ||
		    cpuinfo.cpu_type != CPUTYP_4_100)
			return (0);
		break;

	default:
		/* Don't know what we ended up with ... */
		return (0);
	}

	/* Make sure there is something there... */
	if (probeget(ra->ra_vaddr + 1, 1) == -1)
		return (0);

	/*
	 * If this is a VME SCSI board, we have to determine whether
	 * it is an "sc" (Sun2) or "si" (Sun3) SCSI board.  This can
	 * be determined using the fact that the "sc" board occupies
	 * 4K bytes in VME space but the "si" board occupies 2K bytes.
	 */
	if (strcmp(cf->cf_driver->cd_name, "si") == 0)
		if (probeget(ra->ra_vaddr + 0x801, 1) != -1)
			return(0);

	return (1);
}

static void
si_attach(parent, self, args)
	struct device	*parent, *self;
	void		*args;
{
	struct si_softc *sc = (struct si_softc *) self;
	struct ncr5380_softc *ncr_sc = (struct ncr5380_softc *)sc;
	struct scsibus_attach_args saa;
	volatile struct si_regs *regs;
	struct confargs *ca = args;
	struct romaux *ra = &ca->ca_ra;
	struct bootpath *bp;
	int i;

	/*
	 * Pull in the options flags.  Allow the user to completely
	 * override the default values.
	 */
	sc->sc_options = ncr_sc->sc_dev.dv_cfdata->cf_flags &
	    (ca->ca_bustype == BUS_OBIO ? SW_OPTIONS_MASK : SI_OPTIONS_MASK);

	/* Map the controller registers. */
	regs = (struct si_regs *)
		mapiodev(ra->ra_reg, 0, sizeof(struct si_regs));

	/*
	 * Fill in the prototype scsi_link.
	 */
	ncr_sc->sc_link.adapter_softc = sc;
	ncr_sc->sc_link.adapter_target = 7;
	ncr_sc->sc_link.adapter = &si_ops;
	ncr_sc->sc_link.openings = 4;

	/*
	 * Initialize fields used by the MI code
	 */
	ncr_sc->sci_r0 = &regs->sci.sci_r0;
	ncr_sc->sci_r1 = &regs->sci.sci_r1;
	ncr_sc->sci_r2 = &regs->sci.sci_r2;
	ncr_sc->sci_r3 = &regs->sci.sci_r3;
	ncr_sc->sci_r4 = &regs->sci.sci_r4;
	ncr_sc->sci_r5 = &regs->sci.sci_r5;
	ncr_sc->sci_r6 = &regs->sci.sci_r6;
	ncr_sc->sci_r7 = &regs->sci.sci_r7;

	/*
	 * MD function pointers used by the MI code.
	 */
	ncr_sc->sc_pio_out = ncr5380_pio_out;
	ncr_sc->sc_pio_in =  ncr5380_pio_in;
	if (sc->sc_options & SI_ENABLE_DMA) {
		ncr_sc->sc_dma_alloc = si_dma_alloc;
		ncr_sc->sc_dma_free  = si_dma_free;
		ncr_sc->sc_dma_poll  = si_dma_poll;
	}

	switch (ca->ca_bustype) {
	case BUS_VME16:
		if (sc->sc_options & SI_ENABLE_DMA) {
			ncr_sc->sc_dma_setup = si_vme_dma_setup;
			ncr_sc->sc_dma_start = si_vme_dma_start;
			ncr_sc->sc_dma_stop  = si_vme_dma_stop;
			if (sc->sc_options & SI_DO_RESELECT) {
				/*
				 * Need to enable interrupts (and DMA!)
				 * on this H/W for reselect to work.
				 */
				ncr_sc->sc_intr_on   = si_vme_intr_on;
				ncr_sc->sc_intr_off  = si_vme_intr_off;
			}
		}
		break;

	case BUS_OBIO:
		if (sc->sc_options & SI_ENABLE_DMA) {
			ncr_sc->sc_dma_setup = si_obio_dma_setup;
			ncr_sc->sc_dma_start = si_obio_dma_start;
			ncr_sc->sc_dma_stop  = si_obio_dma_stop;
		}
		ncr_sc->sc_intr_on   = si_obio_intr_on;
		ncr_sc->sc_intr_off  = si_obio_intr_off;
		break;

	default:
		panic("si_attach: impossible bus type 0x%x", ca->ca_bustype);
		/* NOTREACHED */
	}

	ncr_sc->sc_flags = 0;
	if ((sc->sc_options & SI_DO_RESELECT) == 0)
		ncr_sc->sc_flags |= NCR5380_PERMIT_RESELECT;
	if ((sc->sc_options & (SI_ENABLE_DMA | SI_DMA_INTR)) !=
	    (SI_ENABLE_DMA | SI_DMA_INTR))
		ncr_sc->sc_flags |= NCR5380_FORCE_POLLING;
	ncr_sc->sc_min_dma_len = MIN_DMA_LEN;

	/*
	 * Initialize fields used only here in the MD code.
	 */
	sc->sc_regs = regs;
	sc->sc_adapter_type = ca->ca_bustype;
	/*  sc_adapter_iv_am = (was set above) */

	/*
	 * Allocate DMA handles.
	 */
	i = SCI_OPENINGS * sizeof(struct si_dma_handle);
	sc->sc_dma = (struct si_dma_handle *)malloc(i, M_DEVBUF, M_NOWAIT);
	if (sc->sc_dma == NULL)
		panic("si: dma handle malloc failed");
	for (i = 0; i < SCI_OPENINGS; i++)
		sc->sc_dma[i].dh_flags = 0;

	sc->sc_regs = regs;
	sc->sc_adapter_type = ca->ca_bustype;

	/* Establish the interrupt. */
	sc->sc_ih.ih_fun = si_intr;
	sc->sc_ih.ih_arg = sc;

	switch (ca->ca_bustype) {
	case BUS_OBIO:
		/*
		 * This will be an "sw" controller.
		 */
		intr_establish(ra->ra_intr[0].int_pri, &sc->sc_ih, IPL_BIO,
		    self->dv_xname);
		break;

	case BUS_VME16:
		/*
		 * This will be an "si" controller.
		 */
		vmeintr_establish(ra->ra_intr[0].int_vec,
		    ra->ra_intr[0].int_pri, &sc->sc_ih, IPL_BIO,
		    self->dv_xname);
		sc->sc_adapter_iv_am =
		    VME_SUPV_DATA_24 | (ra->ra_intr[0].int_vec & 0xFF);
		break;

	default:
		/* Impossible case handled above. */
		break;
	}
	printf(" pri %d\n", ra->ra_intr[0].int_pri);
#ifdef	DEBUG
	if (sc->sc_options) {
		printf("%s: options=%b\n", ncr_sc->sc_dev.dv_xname,
			sc->sc_options, SI_OPTIONS_BITS);
	}
	if (si_debug)
		printf("si: Set TheSoftC=%p TheRegs=%p\n", sc, regs);
	ncr_sc->sc_link.flags |= si_link_flags;
#endif

	/*
	 *  Initialize si board itself.
	 */
	si_reset_adapter(ncr_sc);
	ncr5380_init(ncr_sc);
	ncr5380_reset_scsibus(ncr_sc);
	DELAY(2000000);

	/*
	 * If the boot path is "sw" or "si" at the moment and it's me, then
	 * walk out pointer to the sub-device, ready for the config
	 * below.
	 */
	bp = ra->ra_bp;
	if (bp != NULL && strcmp(bp->name, ra->ra_name) == 0 &&
	    bp->val[0] == -1 && bp->val[1] == ncr_sc->sc_dev.dv_unit)
		bootpath_store(1, bp + 1);

	bzero(&saa, sizeof(saa));
	saa.saa_sc_link = &(ncr_sc->sc_link);

	/* Configure sub-devices */
	config_found(self, &saa, scsiprint);

	bootpath_store(1, NULL);
}

static void
si_minphys(struct buf *bp, struct scsi_link *sl)
{
	if (bp->b_bcount > MAX_DMA_LEN) {
#ifdef DEBUG
		if (si_debug) {
			printf("si_minphys len = 0x%x.\n", MAX_DMA_LEN);
#ifdef DDB
			Debugger();
#endif
		}
#endif
		bp->b_bcount = MAX_DMA_LEN;
	}
	minphys(bp);
}

#define CSR_WANT (SI_CSR_SBC_IP | SI_CSR_DMA_IP | \
	SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR )

static int
si_intr(void *arg)
{
	struct si_softc *sc = arg;
	volatile struct si_regs *si = sc->sc_regs;
	int dma_error, claimed;
	u_short csr;

	claimed = 0;
	dma_error = 0;

	/* SBC interrupt? DMA interrupt? */
	if (sc->sc_adapter_type == BUS_OBIO)
		csr = si->sw_csr;
	else
		csr = si->si_csr;
	NCR_TRACE("si_intr: csr=0x%x\n", csr);

	if (csr & SI_CSR_DMA_CONFLICT) {
		dma_error |= SI_CSR_DMA_CONFLICT;
		printf("si_intr: DMA conflict\n");
	}
	if (csr & SI_CSR_DMA_BUS_ERR) {
		dma_error |= SI_CSR_DMA_BUS_ERR;
		printf("si_intr: DMA bus error\n");
	}
	if (dma_error) {
		if (sc->ncr_sc.sc_state & NCR_DOINGDMA)
			sc->ncr_sc.sc_state |= NCR_ABORTING;
		/* Make sure we will call the main isr. */
		csr |= SI_CSR_DMA_IP;
	}

	if (csr & (SI_CSR_SBC_IP | SI_CSR_DMA_IP)) {
		claimed = ncr5380_intr(&sc->ncr_sc);
#ifdef DEBUG
		if (!claimed) {
			printf("si_intr: spurious from SBC\n");
#ifdef DDB
			if (si_debug & 4) {
				Debugger();	/* XXX */
			}
#endif
		}
#endif
	}

	return (claimed);
}

static void
si_reset_adapter(struct ncr5380_softc *ncr_sc)
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

#ifdef	DEBUG
	if (si_debug) {
		printf("si_reset_adapter\n");
	}
#endif

	/*
	 * The SCSI3 controller has an 8K FIFO to buffer data between the
	 * 5380 and the DMA.  Make sure it starts out empty.
	 *
	 * The reset bits in the CSR are active low.
	 */
	switch(sc->sc_adapter_type) {
	case BUS_VME16:
		si->si_csr = 0;
		delay(10);
		si->si_csr = SI_CSR_FIFO_RES | SI_CSR_SCSI_RES | SI_CSR_INTR_EN;
		delay(10);
		si->fifo_count = 0;
		si->dma_addrh = 0;
		si->dma_addrl = 0;
		si->dma_counth = 0;
		si->dma_countl = 0;
		si->si_iv_am = sc->sc_adapter_iv_am;
		si->fifo_cnt_hi = 0;
		break;

	case BUS_OBIO:
		si->sw_csr = 0;
		delay(10);
		si->sw_csr = SI_CSR_SCSI_RES;
		si->dma_addr = 0;
		si->dma_count = 0;
		delay(10);
		si->sw_csr |= SI_CSR_INTR_EN;
		break;
	}

	SCI_CLR_INTR(ncr_sc);
}

/*****************************************************************
 * Common functions for DMA
 ****************************************************************/

/*
 * Allocate a DMA handle and put it in sc->sc_dma.  Prepare
 * for DMA transfer.  On the Sun4, this means mapping the buffer
 * into DVMA space.
 */
void
si_dma_alloc(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct scsi_xfer *xs = sr->sr_xs;
	struct si_dma_handle *dh;
	int i, xlen;
	u_long addr;

#ifdef DIAGNOSTIC
	if (sr->sr_dma_hand != NULL)
		panic("si_dma_alloc: already have DMA handle");
#endif

#if 1	/* XXX - Temporary */
	/* XXX - In case we think DMA is completely broken... */
	if ((sc->sc_options & SI_ENABLE_DMA) == 0)
		return;
#endif

	addr = (u_long) ncr_sc->sc_dataptr;
	xlen = ncr_sc->sc_datalen;

	/* If the DMA start addr is misaligned then do PIO */
	if ((addr & 1) || (xlen & 1)) {
#ifdef DEBUG
		printf("si_dma_alloc: misaligned.\n");
#endif
		return;
	}

	/* Make sure our caller checked sc_min_dma_len. */
	if (xlen < MIN_DMA_LEN)
		panic("si_dma_alloc: xlen=0x%x", xlen);

	/* Find free DMA handle.  Guaranteed to find one since we have
	   as many DMA handles as the driver has processes. */
	for (i = 0; i < SCI_OPENINGS; i++) {
		if ((sc->sc_dma[i].dh_flags & SIDH_BUSY) == 0)
			goto found;
	}
	panic("si: no free DMA handles.");
found:

	dh = &sc->sc_dma[i];
	dh->dh_flags = SIDH_BUSY;
	dh->dh_addr = (u_char *) addr;
	dh->dh_maplen  = xlen;
	dh->dh_dvma = 0;

	/* Copy the "write" flag for convenience. */
	if (xs->flags & SCSI_DATA_OUT)
		dh->dh_flags |= SIDH_OUT;

	/*
	 * Double-map the buffer into DVMA space.  If we can't re-map
	 * the buffer, we print a warning and fall back to PIO mode.
	 *
	 * NOTE: it is not safe to sleep here!
	 */
	dh->dh_dvma = (long)kdvma_mapin((caddr_t)addr, xlen, 0);
	if (dh->dh_dvma == 0) {
		/* Can't remap segment */
		printf("si_dma_alloc: can't remap %p/0x%x, doing PIO\n",
			dh->dh_addr, dh->dh_maplen);
		dh->dh_flags = 0;
		return;
	}

	/* success */
	sr->sr_dma_hand = dh;

	return;
}

void
si_dma_free(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;

#ifdef DIAGNOSTIC
	if (dh == NULL)
		panic("si_dma_free: no DMA handle");
#endif

	if (ncr_sc->sc_state & NCR_DOINGDMA)
		panic("si_dma_free: free while in progress");

	if (dh->dh_flags & SIDH_BUSY) {
		/* XXX - Should separate allocation and mapping. */

		/* Give back the DVMA space. */
		dvma_mapout((vaddr_t)dh->dh_dvma, (vaddr_t)dh->dh_addr,
			    dh->dh_maplen);

		dh->dh_dvma = 0;
		dh->dh_flags = 0;
	}
	sr->sr_dma_hand = NULL;
}

/*
 * Poll (spin-wait) for DMA completion.
 * Called right after xx_dma_start(), and
 * xx_dma_stop() will be called next.
 * Same for either VME or OBIO.
 */
void
si_dma_poll(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	volatile struct si_regs *si = sc->sc_regs;
	int tmo, csr_mask, csr;

	/* Make sure DMA started successfully. */
	if (ncr_sc->sc_state & NCR_ABORTING)
		return;

	csr_mask = SI_CSR_SBC_IP | SI_CSR_DMA_IP |
		SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR;

	tmo = 50000;	/* X100 = 5 sec. */
	for (;;) {
		if (sc->sc_adapter_type == BUS_OBIO)
			csr = si->sw_csr;
		else
			csr = si->si_csr;
		if (csr & csr_mask)
			break;
		if (--tmo <= 0) {
			printf("%s: DMA timeout (while polling)\n",
			    ncr_sc->sc_dev.dv_xname);
			/* Indicate timeout as MI code would. */
			sr->sr_flags |= SR_OVERDUE;
			break;
		}
		delay(100);
	}

#ifdef	DEBUG
	if (si_debug) {
		printf("si_dma_poll: done, csr=0x%x\n", csr);
	}
#endif
}

/*****************************************************************
 * VME functions for DMA
 ****************************************************************/

/*
 * This is called when the bus is going idle,
 * so we want to enable the SBC interrupts.
 * That is controlled by the DMA enable!
 * Who would have guessed!
 * What a NASTY trick!
 */
void
si_vme_intr_on(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

	si_vme_dma_setup(ncr_sc);
	si->si_csr |= SI_CSR_DMA_EN;
}

/*
 * This is called when the bus is idle and we are
 * about to start playing with the SBC chip.
 */
void
si_vme_intr_off(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

	si->si_csr &= ~SI_CSR_DMA_EN;
}

/*
 * This function is called during the COMMAND or MSG_IN phase
 * that precedes a DATA_IN or DATA_OUT phase, in case we need
 * to setup the DMA engine before the bus enters a DATA phase.
 *
 * XXX: The VME adapter appears to suppress SBC interrupts
 * when the FIFO is not empty or the FIFO count is non-zero!
 *
 * On the VME version we just clear the DMA count and address
 * here (to make sure it stays idle) and do the real setup
 * later, in dma_start.
 */
void
si_vme_dma_setup(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

	/* Reset the FIFO */
	si->si_csr &= ~SI_CSR_FIFO_RES;		/* active low */
	si->si_csr |= SI_CSR_FIFO_RES;

	/* Set direction (assume recv here) */
	si->si_csr &= ~SI_CSR_SEND;
	/* Assume worst alignment */
	si->si_csr |= SI_CSR_BPCON;

	si->dma_addrh = 0;
	si->dma_addrl = 0;

	si->dma_counth = 0;
	si->dma_countl = 0;

	/* Clear FIFO counter. (also hits dma_count) */
	si->fifo_cnt_hi = 0;
	si->fifo_count = 0;
}

void
si_vme_dma_start(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;
	volatile struct si_regs *si = sc->sc_regs;
	u_long data_pa;
	int xlen;

	/*
	 * Get the DVMA mapping for this segment.
	 * XXX - Should separate allocation and mapin.
	 */
	data_pa = (u_long)(dh->dh_dvma - DVMA_BASE);
	if (data_pa & 1)
		panic("si_dma_start: bad pa=0x%lx", data_pa);
	xlen = ncr_sc->sc_datalen;
	xlen &= ~1;
	sc->sc_xlen = xlen;	/* XXX: or less... */

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_start: dh=%p, pa=0x%lx, xlen=%d\n",
			   dh, data_pa, xlen);
	}
#endif

	/*
	 * Set up the DMA controller.
	 * Note that (dh->dh_len < sc_datalen)
	 */
	si->si_csr &= ~SI_CSR_FIFO_RES;		/* active low */
	si->si_csr |= SI_CSR_FIFO_RES;

	/* Set direction (send/recv) */
	if (dh->dh_flags & SIDH_OUT) {
		si->si_csr |= SI_CSR_SEND;
	} else {
		si->si_csr &= ~SI_CSR_SEND;
	}

	if (data_pa & 2) {
		si->si_csr |= SI_CSR_BPCON;
	} else {
		si->si_csr &= ~SI_CSR_BPCON;
	}

	si->dma_addrh = (u_short)(data_pa >> 16);
	si->dma_addrl = (u_short)(data_pa & 0xFFFF);

	si->dma_counth = (u_short)(xlen >> 16);
	si->dma_countl = (u_short)(xlen & 0xFFFF);

#if 1
	/* Set it anyway, even though dma_count hits it? */
	si->fifo_cnt_hi = (u_short)(xlen >> 16);
	si->fifo_count  = (u_short)(xlen & 0xFFFF);
#endif

#ifdef DEBUG
	if (si->fifo_count != xlen) {
		printf("si_dma_start: Fifo_count=0x%x, xlen=0x%x\n",
		    si->fifo_count, xlen);
#ifdef DDB
		Debugger();
#endif
	}
#endif

	/*
	 * Acknowledge the phase change.  (After DMA setup!)
	 * Put the SBIC into DMA mode, and start the transfer.
	 */
	if (dh->dh_flags & SIDH_OUT) {
		*ncr_sc->sci_tcmd = PHASE_DATA_OUT;
		SCI_CLR_INTR(ncr_sc);
		*ncr_sc->sci_icmd = SCI_ICMD_DATA;
		*ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
		*ncr_sc->sci_dma_send = 0;	/* start it */
	} else {
		*ncr_sc->sci_tcmd = PHASE_DATA_IN;
		SCI_CLR_INTR(ncr_sc);
		*ncr_sc->sci_icmd = 0;
		*ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
		*ncr_sc->sci_irecv = 0;		/* start it */
	}

	/* Let'er rip! */
	si->si_csr |= SI_CSR_DMA_EN;

	ncr_sc->sc_state |= NCR_DOINGDMA;

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_start: started, flags=0x%x\n",
			   ncr_sc->sc_state);
	}
#endif
}

void
si_vme_dma_stop(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;
	volatile struct si_regs *si = sc->sc_regs;
	int resid, ntrans;

	if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) {
#ifdef	DEBUG
		printf("si_dma_stop: dma not running\n");
#endif
		return;
	}
	ncr_sc->sc_state &= ~NCR_DOINGDMA;

	/* First, halt the DMA engine. */
	si->si_csr &= ~SI_CSR_DMA_EN;	/* VME only */

	if (si->si_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) {
		printf("si: DMA error, csr=0x%x, reset\n", si->si_csr);
		sr->sr_xs->error = XS_DRIVER_STUFFUP;
		ncr_sc->sc_state |= NCR_ABORTING;
		si_reset_adapter(ncr_sc);
	}

	/* Note that timeout may have set the error flag. */
	if (ncr_sc->sc_state & NCR_ABORTING)
		goto out;

	/*
	 * Now try to figure out how much actually transferred
	 *
	 * The fifo_count does not reflect how many bytes were
	 * actually transferred for VME.
	 *
	 * SCSI-3 VME interface is a little funny on writes:
	 * if we have a disconnect, the dma has overshot by
	 * one byte and the resid needs to be incremented.
	 * Only happens for partial transfers.
	 * (Thanks to Matt Jacob)
	 */

	resid = si->fifo_count & 0xFFFF;
	if (dh->dh_flags & SIDH_OUT)
		if ((resid > 0) && (resid < sc->sc_xlen))
			resid++;
	ntrans = sc->sc_xlen - resid;

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_stop: resid=0x%x ntrans=0x%x\n",
		    resid, ntrans);
	}
#endif

	if (ntrans < MIN_DMA_LEN) {
#ifdef DEBUG
		printf("si: fifo count: 0x%x\n", resid);
#endif
		ncr_sc->sc_state |= NCR_ABORTING;
		goto out;
	}

	if (ntrans > ncr_sc->sc_datalen)
		panic("si_dma_stop: excess transfer");

	/* Adjust data pointer */
	ncr_sc->sc_dataptr += ntrans;
	ncr_sc->sc_datalen -= ntrans;

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_stop: ntrans=0x%x\n", ntrans);
	}
#endif

	/*
	 * After a read, we may need to clean-up
	 * "Left-over bytes" (yuck!)
	 */
	if (((dh->dh_flags & SIDH_OUT) == 0) &&
		((si->si_csr & SI_CSR_LOB) != 0))
	{
		char *cp = ncr_sc->sc_dataptr;
#ifdef DEBUG
		printf("si: Got Left-over bytes!\n");
#endif
		if (si->si_csr & SI_CSR_BPCON) {
			/* have SI_CSR_BPCON */
			cp[-1] = (si->si_bprl & 0xff00) >> 8;
		} else {
			switch (si->si_csr & SI_CSR_LOB) {
			case SI_CSR_LOB_THREE:
				cp[-3] = (si->si_bprh & 0xff00) >> 8;
				cp[-2] = (si->si_bprh & 0x00ff);
				cp[-1] = (si->si_bprl & 0xff00) >> 8;
				break;
			case SI_CSR_LOB_TWO:
				cp[-2] = (si->si_bprh & 0xff00) >> 8;
				cp[-1] = (si->si_bprh & 0x00ff);
				break;
			case SI_CSR_LOB_ONE:
				cp[-1] = (si->si_bprh & 0xff00) >> 8;
				break;
			}
		}
	}

out:
	si->dma_addrh = 0;
	si->dma_addrl = 0;

	si->dma_counth = 0;
	si->dma_countl = 0;

	si->fifo_cnt_hi = 0;
	si->fifo_count  = 0;

	/* Put SBIC back in PIO mode. */
	*ncr_sc->sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE);
	*ncr_sc->sci_icmd = 0;
}

/*****************************************************************
 * OBIO functions for DMA
 ****************************************************************/

/*
 * This is called when the bus is going idle,
 * so we want to enable the SBC interrupts.
 * That is controlled by the DMA enable!
 * Who would have guessed!
 * What a NASTY trick!
 *
 * XXX THIS MIGHT NOT WORK RIGHT!
 */
void
si_obio_intr_on(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

	si_obio_dma_setup(ncr_sc);
	si->sw_csr |= SI_CSR_DMA_EN;
}

/*
 * This is called when the bus is idle and we are
 * about to start playing with the SBC chip.
 *
 * XXX THIS MIGHT NOT WORK RIGHT!
 */
void
si_obio_intr_off(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

	si->sw_csr &= ~SI_CSR_DMA_EN;
}

/*
 * This function is called during the COMMAND or MSG_IN phase
 * that precedes a DATA_IN or DATA_OUT phase, in case we need
 * to setup the DMA engine before the bus enters a DATA phase.
 *
 * On the OBIO version we just clear the DMA count and address
 * here (to make sure it stays idle) and do the real setup
 * later, in dma_start.
 */
void
si_obio_dma_setup(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	volatile struct si_regs *si = sc->sc_regs;

	/* No FIFO to reset on "sw". */

	/* Set direction (assume recv here) */
	si->sw_csr &= ~SI_CSR_SEND;

	si->dma_addr = 0;
	si->dma_count = 0;
}

void
si_obio_dma_start(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;
	volatile struct si_regs *si = sc->sc_regs;
	u_long data_pa;
	int xlen, adj, adjlen;

	/*
	 * Get the DVMA mapping for this segment.
	 * XXX - Should separate allocation and mapin.
	 */
	data_pa = (u_long)(dh->dh_dvma - DVMA_BASE);
	if (data_pa & 1)
		panic("si_dma_start: bad pa=0x%lx", data_pa);
	xlen = ncr_sc->sc_datalen;
	xlen &= ~1;
	sc->sc_xlen = xlen;	/* XXX: or less... */

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_start: dh=%p, pa=0x%lx, xlen=%d\n",
		    dh, data_pa, xlen);
	}
#endif

	/*
	 * Set up the DMA controller.
	 * Note that (dh->dh_len < sc_datalen)
	 */

	/* Set direction (send/recv) */
	if (dh->dh_flags & SIDH_OUT) {
		si->sw_csr |= SI_CSR_SEND;
	} else {
		si->sw_csr &= ~SI_CSR_SEND;
	}

	/*
	 * The "sw" needs longword aligned transfers.  We
	 * detect a shortword aligned transfer here, and adjust the
	 * DMA transfer by 2 bytes.  These two bytes are read/written
	 * in PIO mode just before the DMA is started.
	 */
	adj = 0;
	if (data_pa & 2) {
		adj = 2;
#ifdef DEBUG
		if (si_debug & 2)
			printf("si_dma_start: adjusted up %d bytes\n", adj);
#endif
	}

	/* We have to frob the address on the "sw". */
	dh->dh_startingpa = (data_pa | 0xF00000);
	si->dma_addr = (int)(dh->dh_startingpa + adj);
	si->dma_count = (xlen - adj);

	/*
	 * Acknowledge the phase change.  (After DMA setup!)
	 * Put the SBIC into DMA mode, and start the transfer.
	 */
	if (dh->dh_flags & SIDH_OUT) {
		*ncr_sc->sci_tcmd = PHASE_DATA_OUT;
		if (adj) {
			adjlen = ncr5380_pio_out(ncr_sc, PHASE_DATA_OUT,
			    adj, dh->dh_addr);
			if (adjlen != adj)
				printf("%s: bad outgoing adj, %d != %d\n",
				    ncr_sc->sc_dev.dv_xname, adjlen, adj);
		}
		SCI_CLR_INTR(ncr_sc);
		*ncr_sc->sci_icmd = SCI_ICMD_DATA;
		*ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
		*ncr_sc->sci_dma_send = 0;	/* start it */
	} else {
		*ncr_sc->sci_tcmd = PHASE_DATA_IN;
		if (adj) {
			adjlen = ncr5380_pio_in(ncr_sc, PHASE_DATA_IN,
			    adj, dh->dh_addr);
			if (adjlen != adj)
				printf("%s: bad incoming adj, %d != %d\n",
				    ncr_sc->sc_dev.dv_xname, adjlen, adj);
		}
		SCI_CLR_INTR(ncr_sc);
		*ncr_sc->sci_icmd = 0;
		*ncr_sc->sci_mode |= (SCI_MODE_DMA | SCI_MODE_DMA_IE);
		*ncr_sc->sci_irecv = 0;		/* start it */
	}

	/* Let'er rip! */
	si->sw_csr |= SI_CSR_DMA_EN;

	ncr_sc->sc_state |= NCR_DOINGDMA;

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_start: started, flags=0x%x\n",
		    ncr_sc->sc_state);
	}
#endif
}

#if (defined(DEBUG) || defined(DIAGNOSTIC)) && !defined(COUNT_SW_LEFTOVERS)
#define COUNT_SW_LEFTOVERS
#endif
#ifdef COUNT_SW_LEFTOVERS
/*
 * Let's find out how often these occur.  Read these with DDB from time
 * to time.
 */
int	sw_3_leftover = 0;
int	sw_2_leftover = 0;
int	sw_1_leftover = 0;
int	sw_0_leftover = 0;
#endif

void
si_obio_dma_stop(ncr_sc)
	struct ncr5380_softc *ncr_sc;
{
	struct si_softc *sc = (struct si_softc *)ncr_sc;
	struct sci_req *sr = ncr_sc->sc_current;
	struct si_dma_handle *dh = sr->sr_dma_hand;
	volatile struct si_regs *si = sc->sc_regs;
	int ntrans = 0, Dma_addr;

	if ((ncr_sc->sc_state & NCR_DOINGDMA) == 0) {
#ifdef	DEBUG
		printf("si_dma_stop: dma not running\n");
#endif
		return;
	}
	ncr_sc->sc_state &= ~NCR_DOINGDMA;

	/* First, halt the DMA engine. */
	si->sw_csr &= ~SI_CSR_DMA_EN;

	/*
	 * XXX HARDWARE BUG!
	 * Apparently, some early 4/100 SCSI controllers had a hardware
	 * bug that caused the controller to do illegal memory access.
	 * We see this as SI_CSR_DMA_BUS_ERR (makes sense).  To work around
	 * this, we simply need to clean up after ourselves ... there will
	 * be as many as 3 bytes left over.  Since we clean up "left-over"
	 * bytes on every read anyway, we just continue to chug along
	 * if SI_CSR_DMA_BUS_ERR is asserted.  (This was probably worked
	 * around in hardware later with the "left-over byte" indicator
	 * in the VME controller.)
	 */
#if 0
	if (si->sw_csr & (SI_CSR_DMA_CONFLICT | SI_CSR_DMA_BUS_ERR)) {
#else
	if (si->sw_csr & (SI_CSR_DMA_CONFLICT)) {
#endif
		printf("sw: DMA error, csr=0x%x, reset\n", si->sw_csr);
		sr->sr_xs->error = XS_DRIVER_STUFFUP;
		ncr_sc->sc_state |= NCR_ABORTING;
		si_reset_adapter(ncr_sc);
	}

	/* Note that timeout may have set the error flag. */
	if (ncr_sc->sc_state & NCR_ABORTING)
		goto out;

	/*
	 * Now try to figure out how much actually transferred
	 *
	 * The "sw" doesn't have a FIFO or a bcr, so we've stored
	 * the starting PA of the transfer in the DMA handle,
	 * and subtract it from the ending PA left in the dma_addr
	 * register.
	 */
	Dma_addr = si->dma_addr;
	ntrans = (Dma_addr - dh->dh_startingpa);

#ifdef	DEBUG
	if (si_debug & 2) {
		printf("si_dma_stop: ntrans=0x%x\n", ntrans);
	}
#endif

	if (ntrans < MIN_DMA_LEN) {
		printf("sw: short transfer\n");
		ncr_sc->sc_state |= NCR_ABORTING;
		goto out;
	}

	if (ntrans > ncr_sc->sc_datalen)
		panic("si_dma_stop: excess transfer");

	/* Adjust data pointer */
	ncr_sc->sc_dataptr += ntrans;
	ncr_sc->sc_datalen -= ntrans;

	/*
	 * After a read, we may need to clean-up
	 * "Left-over bytes"  (yuck!)  The "sw" doesn't
	 * have a "left-over" indicator, so we have to so
	 * this no matter what.  Ick.
	 */
	if ((dh->dh_flags & SIDH_OUT) == 0) {
		char *cp = ncr_sc->sc_dataptr;

		switch (Dma_addr & 3) {
		case 3:
			cp[0] = (si->sw_bpr & 0xff000000) >> 24;
			cp[1] = (si->sw_bpr & 0x00ff0000) >> 16;
			cp[2] = (si->sw_bpr & 0x0000ff00) >> 8;
#ifdef COUNT_SW_LEFTOVERS
			++sw_3_leftover;
#endif
			break;

		case 2:
			cp[0] = (si->sw_bpr & 0xff000000) >> 24;
			cp[1] = (si->sw_bpr & 0x00ff0000) >> 16;
#ifdef COUNT_SW_LEFTOVERS
			++sw_2_leftover;
#endif
			break;

		case 1:
			cp[0] = (si->sw_bpr & 0xff000000) >> 24;
#ifdef COUNT_SW_LEFTOVERS
			++sw_1_leftover;
#endif
			break;

#ifdef COUNT_SW_LEFTOVERS
		default:
			++sw_0_leftover;
			break;
#endif
		}
	}

 out:
	si->dma_addr = 0;
	si->dma_count = 0;

	/* Put SBIC back in PIO mode. */
	*ncr_sc->sci_mode &= ~(SCI_MODE_DMA | SCI_MODE_DMA_IE);
	*ncr_sc->sci_icmd = 0;

#ifdef DEBUG
	if (si_debug & 2) {
		printf("si_dma_stop: ntrans=0x%x\n", ntrans);
	}
#endif
}


code:
/*	$OpenBSD: sireg.h,v 1.3 1997/08/08 08:25:30 downsj Exp $	*/
/*	$NetBSD: sireg.h,v 1.3 1996/01/01 22:40:58 thorpej Exp $	*/

/*
 * Register map for the Sun3 SCSI Interface (si)
 * The first part of this register map is an NCR5380
 * SCSI Bus Interface Controller (SBIC).  The rest is a
 * DMA controller and custom logic in one of two flavors,
 * one for the OBIO interface (3/50,3/60) and one for the
 * VME interface (3/160,3/260,etc.), where some registers
 * are implemented only on one or the other, some on both.
 *
 * Modified for Sun 4 systems by Jason R. Thorpe <thorpej@NetBSD.ORG>.
 */

/*
 * Note that the obio version on the 4/1xx (the so-called "SCSI Weird", or
 * "sw" controller) is laid out a bit differently, and hence the evilness
 * with unions.  Also, the "sw" doesn't appear to have a FIFO.
 */

/*
 * Am5380 Register map (no padding)
 */
struct ncr5380regs {
	volatile u_char sci_r0;
	volatile u_char sci_r1;
	volatile u_char sci_r2;
	volatile u_char sci_r3;
	volatile u_char sci_r4;
	volatile u_char sci_r5;
	volatile u_char sci_r6;
	volatile u_char sci_r7;
};

struct si_regs {
	struct ncr5380regs sci;

	/* DMA controller registers */
	union {
		struct {
			u_short	_Dma_addrh;	/* dma address (VME only) */
			u_short	_Dma_addrl;	/* (high word, low word)  */
		} _si_u1_s;
		u_int		_Dma_addr;	/* dma address (OBIO) */
	} _si_u1;
#define dma_addrh	_si_u1._si_u1_s._Dma_addrh
#define dma_addrl	_si_u1._si_u1_s._Dma_addrl
#define dma_addr	_si_u1._Dma_addr

	union {
		struct {
			u_short	_Dma_counth;	/* dma count   (VME only) */
			u_short	_Dma_countl;	/* (high word, low word)  */
		} _si_u2_s;
		u_int		_Dma_count;	/* dma count (OBIO) */
	} _si_u2;
#define dma_counth	_si_u2._si_u2_s._Dma_counth
#define dma_countl	_si_u2._si_u2_s._Dma_countl
#define dma_count	_si_u2._Dma_count

	u_int		si_pad0;		/* no-existent register */

	union {
		struct {
			u_short	_Fifo_data;	/* fifo data register */
			u_short	_Fifo_count;	/* fifo count register */
		} _si_u4_s;
		u_int		_Sw_csr;	/* sw control/status */
	} _si_u4;
#define fifo_data	_si_u4._si_u4_s._Fifo_data
#define fifo_count	_si_u4._si_u4_s._Fifo_count
#define sw_csr		_si_u4._Sw_csr

	union {
		struct {
			u_short	_Si_csr;	/* si control/status */
			u_short	_Bprh;		/* VME byte pack high */
		} _si_u5_s;
		u_int	_Bpr;			/* sw byte pack */
	} _si_u5;
#define si_csr		_si_u5._si_u5_s._Si_csr
#define si_bprh		_si_u5._si_u5_s._Bprh
#define sw_bpr		_si_u5._Bpr

	/* The rest of these are on the VME interface only: */
	u_short			si_bprl;	/* VME byte pack low */
	u_short			si_iv_am;	/* bits 0-7: intr vector */
				/* bits 8-13: addr modifier (VME only) */
						/* bits 14-15: unused */
	u_short			fifo_cnt_hi;	/* high part of fifo_count (VME only) */

	/* Whole thing repeats after 32 bytes. */
	u_short			_space[3];
};

/* possible values for the address modifier, vme version only */
#define VME_SUPV_DATA_24	0x3d00

/*
 * Status Register.
 * Note:
 *	(r)	indicates bit is read only.
 *	(rw)	indicates bit is read or write.
 *	(v)	vme host adaptor interface only.
 *	(o)	sun3/50 onboard host adaptor interface only.
 *	(b)	both vme and sun3/50 host adaptor interfaces.
 */
#define SI_CSR_DMA_ACTIVE	0x8000	/* (r,o) dma transfer active */
#define SI_CSR_DMA_CONFLICT	0x4000	/* (r,b) reg accessed while dmaing */
#define SI_CSR_DMA_BUS_ERR	0x2000	/* (r,b) bus error during dma */
#define SI_CSR_ID		0x1000	/* (r,b) 0 for 3/50, 1 for SCSI-3, */
					/* 0 if SCSI-3 unmodified */
#define SI_CSR_FIFO_FULL	0x0800	/* (r,b) fifo full */
#define SI_CSR_FIFO_EMPTY	0x0400	/* (r,b) fifo empty */
#define SI_CSR_SBC_IP		0x0200	/* (r,b) sbc interrupt pending */
#define SI_CSR_DMA_IP		0x0100	/* (r,b) dma interrupt pending */
#define SI_CSR_LOB		0x00c0	/* (r,v) number of leftover bytes */
#define SI_CSR_LOB_THREE	0x00c0	/* (r,v) three leftover bytes */
#define SI_CSR_LOB_TWO		0x0080	/* (r,v) two leftover bytes */
#define SI_CSR_LOB_ONE		0x0040	/* (r,v) one leftover byte */
#define SI_CSR_BPCON		0x0020	/* (rw,v) byte packing control */
					/* dma is in 0=longwords, 1=words */
#define SI_CSR_DMA_EN		0x0010	/* (rw,v) dma/interrupt enable */
#define SI_CSR_SEND		0x0008	/* (rw,b) dma dir, 1=to device */
#define SI_CSR_INTR_EN		0x0004	/* (rw,b) interrupts enable */
#define SI_CSR_FIFO_RES		0x0002	/* (rw,b) inits fifo, 0=reset */
#define SI_CSR_SCSI_RES		0x0001	/* (rw,b) reset sbc and udc, 0=reset */

license: unknown


code:
/*	$OpenBSD: spif.c,v 1.28 2015/03/28 19:07:07 miod Exp $	*/

/*
 * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
 * 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.
 */

/*
 * Driver for the SUNW,spif: 8 serial, 1 parallel sbus board
 * based heavily on Iain Hibbert's driver for the MAGMA cards
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/malloc.h>
#include <sys/tty.h>
#include <sys/conf.h>

#include <machine/autoconf.h>
#include <sparc/cpu.h>
#include <sparc/sparc/cpuvar.h>
#include <sparc/dev/spifreg.h>
#include <sparc/dev/spifvar.h>

int	spifmatch(struct device *, void *, void *);
void	spifattach(struct device *, struct device *, void *);

int	sttymatch(struct device *, void *, void *);
void	sttyattach(struct device *, struct device *, void *);
int	sttyopen(dev_t, int, int, struct proc *);
int	sttyclose(dev_t, int, int, struct proc *);
int	sttyread(dev_t, struct uio *, int);
int	sttywrite(dev_t, struct uio *, int);
int	sttyioctl(dev_t, u_long, caddr_t, int, struct proc *);
int	sttystop(struct tty *, int);

int	spifstcintr(void *);
int	spifstcintr_mx(struct spif_softc *, int *);
int	spifstcintr_tx(struct spif_softc *, int *);
int	spifstcintr_rx(struct spif_softc *, int *);
int	spifstcintr_rxexception(struct spif_softc *, int *);
void	spifsoftintr(void *);

int	stty_param(struct tty *, struct termios *);
struct tty *sttytty(dev_t);
int	stty_modem_control(struct stty_port *, int, int);
static __inline	void	stty_write_ccr(struct stcregs *, u_int8_t);
int	stty_compute_baud(speed_t, int, u_int8_t *, u_int8_t *);
void	stty_start(struct tty *);

int	sbppmatch(struct device *, void *, void *);
void	sbppattach(struct device *, struct device *, void *);
int	sbppopen(dev_t, int, int, struct proc *);
int	sbppclose(dev_t, int, int, struct proc *);
int	sbppread(dev_t, struct uio *, int);
int	sbppwrite(dev_t, struct uio *, int);
int	sbpp_rw(dev_t, struct uio *);
int	spifppcintr(void *);
int	sbpppoll(dev_t, int, struct proc *);
int	sbppioctl(dev_t, u_long, caddr_t, int, struct proc *);

struct cfattach spif_ca = {
	sizeof (struct spif_softc), spifmatch, spifattach
};

struct cfdriver spif_cd = {
	NULL, "spif", DV_DULL
};

struct cfattach stty_ca = {
	sizeof(struct stty_softc), sttymatch, sttyattach
};

struct cfdriver stty_cd = {
	NULL, "stty", DV_TTY
};

struct cfattach sbpp_ca = {
	sizeof(struct sbpp_softc), sbppmatch, sbppattach
};

struct cfdriver sbpp_cd = {
	NULL, "sbpp", DV_DULL
};

int
spifmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct cfdata *cf = vcf;
	struct confargs *ca = aux;
	register struct romaux *ra = &ca->ca_ra;

	if (strcmp(cf->cf_driver->cd_name, ra->ra_name) &&
	    strcmp("SUNW,spif", ra->ra_name))
		return (0);
	return (1);
}

void
spifattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct spif_softc *sc = (struct spif_softc *)self;
	struct confargs *ca = aux;
	int stcpri, ppcpri;

	if (ca->ca_ra.ra_nintr != 2) {
		printf(": expected 2 interrupts, got %d\n",
		    ca->ca_ra.ra_nintr);
		return;
	}
	stcpri = ca->ca_ra.ra_intr[SERIAL_INTR].int_pri;
	ppcpri = ca->ca_ra.ra_intr[PARALLEL_INTR].int_pri;

	if (ca->ca_ra.ra_nreg != 1) {
		printf(": expected %d registers, got %d\n",
		    1, ca->ca_ra.ra_nreg);
		return;
	}
	sc->sc_regs = mapiodev(ca->ca_ra.ra_reg, 0, ca->ca_ra.ra_len);

	sc->sc_node = ca->ca_ra.ra_node;

	sc->sc_rev = getpropint(sc->sc_node, "revlev", 0);

	sc->sc_osc = getpropint(sc->sc_node, "verosc", 0);
	switch (sc->sc_osc) {
	case SPIF_OSC10:
		sc->sc_osc = 10000000;
		break;
	case SPIF_OSC9:
	default:
		sc->sc_osc = 9830400;
		break;
	}

	sc->sc_nser = 8;
	sc->sc_npar = 1;

	sc->sc_rev2 = sc->sc_regs->stc.gfrcr;
	sc->sc_regs->stc.gsvr = 0;

	stty_write_ccr(&sc->sc_regs->stc,
	    CD180_CCR_CMD_RESET | CD180_CCR_RESETALL);
	while (sc->sc_regs->stc.gsvr != 0xff);
	while (sc->sc_regs->stc.gfrcr != sc->sc_rev2);

	sc->sc_regs->stc.pprh = CD180_PPRH;
	sc->sc_regs->stc.pprl = CD180_PPRL;
	sc->sc_regs->stc.msmr = SPIF_MSMR;
	sc->sc_regs->stc.tsmr = SPIF_TSMR;
	sc->sc_regs->stc.rsmr = SPIF_RSMR;
	sc->sc_regs->stc.gsvr = 0;
	sc->sc_regs->stc.gscr1 = 0;
	sc->sc_regs->stc.gscr2 = 0;
	sc->sc_regs->stc.gscr3 = 0;
	printf(": rev %x chiprev %x osc %sMHz stcpri %d ppcpri %d softpri %d\n",
	    sc->sc_rev, sc->sc_rev2, clockfreq(sc->sc_osc),
	    stcpri, ppcpri, IPL_TTY);

	(void)config_found(self, sttymatch, NULL);
	(void)config_found(self, sbppmatch, NULL);

	sc->sc_ppcih.ih_fun = spifppcintr;
	sc->sc_ppcih.ih_arg = sc;
	intr_establish(ppcpri, &sc->sc_ppcih, -1, self->dv_xname);

	sc->sc_stcih.ih_fun = spifstcintr;
	sc->sc_stcih.ih_arg = sc;
	intr_establish(stcpri, &sc->sc_stcih, -1, self->dv_xname);

	sc->sc_softih = softintr_establish(IPL_SOFTTTY, spifsoftintr, sc);
}

int
sttymatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct spif_softc *sc = (struct spif_softc *)parent;

	return (aux == sttymatch && sc->sc_ttys == NULL);
}

void
sttyattach(parent, dev, aux)
	struct device *parent, *dev;
	void *aux;
{
	struct spif_softc *sc = (struct spif_softc *)parent;
	struct stty_softc *ssc = (struct stty_softc *)dev;
	int port;

	sc->sc_ttys = ssc;

	for (port = 0; port < sc->sc_nser; port++) {
		struct stty_port *sp = &ssc->sc_port[port];
		struct tty *tp;

		sp->sp_dtr = 0;
		sc->sc_regs->dtrlatch[port] = 1;

		tp = ttymalloc(0);

		tp->t_oproc = stty_start;
		tp->t_param = stty_param;

		sp->sp_tty = tp;
		sp->sp_sc = sc;
		sp->sp_channel = port;

		sp->sp_rbuf = malloc(STTY_RBUF_SIZE, M_DEVBUF, M_NOWAIT);
		if(sp->sp_rbuf == NULL)
			break;

		sp->sp_rend = sp->sp_rbuf + STTY_RBUF_SIZE;
	}

	ssc->sc_nports = port;

	printf(": %d tty%s\n", port, port == 1 ? "" : "s");
}

int
sttyopen(dev, flags, mode, p)
	dev_t dev;
	int flags;
	int mode;
	struct proc *p;
{
	struct spif_softc *csc;
	struct stty_softc *sc;
	struct stty_port *sp;
	struct tty *tp;
	int card = SPIF_CARD(dev);
	int port = SPIF_PORT(dev);
	int s;

	if (card >= stty_cd.cd_ndevs || card >= spif_cd.cd_ndevs)
		return (ENXIO);

	sc = stty_cd.cd_devs[card];
	csc = spif_cd.cd_devs[card];
	if (sc == NULL)
		return (ENXIO);

	if (port >= sc->sc_nports)
		return (ENXIO);

	sp = &sc->sc_port[port];
	tp = sp->sp_tty;
	tp->t_dev = dev;

	if (!ISSET(tp->t_state, TS_ISOPEN)) {
		SET(tp->t_state, TS_WOPEN);

		ttychars(tp);
		tp->t_iflag = TTYDEF_IFLAG;
		tp->t_oflag = TTYDEF_OFLAG;
		tp->t_cflag = TTYDEF_CFLAG;
		if (ISSET(sp->sp_openflags, TIOCFLAG_CLOCAL))
			SET(tp->t_cflag, CLOCAL);
		if (ISSET(sp->sp_openflags, TIOCFLAG_CRTSCTS))
			SET(tp->t_cflag, CRTSCTS);
		if (ISSET(sp->sp_openflags, TIOCFLAG_MDMBUF))
			SET(tp->t_cflag, MDMBUF);
		tp->t_lflag = TTYDEF_LFLAG;
		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;

		sp->sp_rput = sp->sp_rget = sp->sp_rbuf;

		s = spltty();

		csc->sc_regs->stc.car = sp->sp_channel;
		stty_write_ccr(&csc->sc_regs->stc,
		    CD180_CCR_CMD_RESET | CD180_CCR_RESETCHAN);
		csc->sc_regs->stc.car = sp->sp_channel;

		stty_param(tp, &tp->t_termios);

		ttsetwater(tp);

		csc->sc_regs->stc.srer = CD180_SRER_CD | CD180_SRER_RXD;

		if (ISSET(sp->sp_openflags, TIOCFLAG_SOFTCAR) || sp->sp_carrier)
			SET(tp->t_state, TS_CARR_ON);
		else
			CLR(tp->t_state, TS_CARR_ON);
	}
	else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p, 0) != 0) {
		return (EBUSY);
	} else {
		s = spltty();
	}

	if (!ISSET(flags, O_NONBLOCK)) {
		while (!ISSET(tp->t_cflag, CLOCAL) &&
		    !ISSET(tp->t_state, TS_CARR_ON)) {
			int error;

			SET(tp->t_state, TS_WOPEN);
			error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
			    "sttycd", 0);
			if (error != 0) {
				splx(s);
				CLR(tp->t_state, TS_WOPEN);
				return (error);
			}
		}
	}

	splx(s);

	return ((*linesw[tp->t_line].l_open)(dev, tp, p));
}

int
sttyclose(dev, flags, mode, p)
	dev_t dev;
	int flags;
	int mode;
	struct proc *p;
{
	struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
	struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
	struct spif_softc *csc = sp->sp_sc;
	struct tty *tp = sp->sp_tty;
	int port = SPIF_PORT(dev);
	int s;

	(*linesw[tp->t_line].l_close)(tp, flags, p);
	s = spltty();

	if (ISSET(tp->t_cflag, HUPCL) || !ISSET(tp->t_state, TS_ISOPEN)) {
		stty_modem_control(sp, 0, DMSET);
		csc->sc_regs->stc.car = port;
		csc->sc_regs->stc.ccr =
		    CD180_CCR_CMD_RESET | CD180_CCR_RESETCHAN;
	}

	splx(s);
	ttyclose(tp);
	return (0);
}

int
sttyioctl(dev, cmd, data, flags, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flags;
	struct proc *p;
{
	struct stty_softc *stc = stty_cd.cd_devs[SPIF_CARD(dev)];
	struct stty_port *sp = &stc->sc_port[SPIF_PORT(dev)];
	struct spif_softc *sc = sp->sp_sc;
	struct tty *tp = sp->sp_tty;
	int error;

	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flags, p);
	if (error >= 0)
		return (error);

	error = ttioctl(tp, cmd, data, flags, p);
	if (error >= 0)
		return (error);

	error = 0;

	switch (cmd) {
	case TIOCSBRK:
		SET(sp->sp_flags, STTYF_SET_BREAK);
		sc->sc_regs->stc.car = sp->sp_channel;
		sc->sc_regs->stc.srer |= CD180_SRER_TXD;
		break;
	case TIOCCBRK:
		SET(sp->sp_flags, STTYF_CLR_BREAK);
		sc->sc_regs->stc.car = sp->sp_channel;
		sc->sc_regs->stc.srer |= CD180_SRER_TXD;
		break;
	case TIOCSDTR:
		stty_modem_control(sp, TIOCM_DTR, DMBIS);
		break;
	case TIOCCDTR:
		stty_modem_control(sp, TIOCM_DTR, DMBIC);
		break;
	case TIOCMBIS:
		stty_modem_control(sp, *((int *)data), DMBIS);
		break;
	case TIOCMBIC:
		stty_modem_control(sp, *((int *)data), DMBIC);
		break;
	case TIOCMGET:
		*((int *)data) = stty_modem_control(sp, 0, DMGET);
		break;
	case TIOCMSET:
		stty_modem_control(sp, *((int *)data), DMSET);
		break;
	case TIOCGFLAGS:
		*((int *)data) = sp->sp_openflags;
		break;
	case TIOCSFLAGS:
		if (suser(p, 0))
			error = EPERM;
		else
			sp->sp_openflags = *((int *)data) &
			    (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL |
			     TIOCFLAG_CRTSCTS | TIOCFLAG_MDMBUF);
		break;
	default:
		error = ENOTTY;
	}

	return (error);
}

int
stty_modem_control(sp, bits, how)
	struct stty_port *sp;
	int bits, how;
{
	struct spif_softc *csc = sp->sp_sc;
	struct tty *tp = sp->sp_tty;
	int s, msvr;

	s = spltty();
	csc->sc_regs->stc.car = sp->sp_channel;

	switch (how) {
	case DMGET:
		bits = TIOCM_LE;
		if (sp->sp_dtr)
			bits |= TIOCM_DTR;
		msvr = csc->sc_regs->stc.msvr;
		if (ISSET(msvr, CD180_MSVR_DSR))
			bits |= TIOCM_DSR;
		if (ISSET(msvr, CD180_MSVR_CD))
			bits |= TIOCM_CD;
		if (ISSET(msvr, CD180_MSVR_CTS))
			bits |= TIOCM_CTS;
		if (ISSET(msvr, CD180_MSVR_RTS))
			bits |= TIOCM_RTS;
		break;
	case DMSET:
		if (ISSET(bits, TIOCM_DTR)) {
			sp->sp_dtr = 1;
			csc->sc_regs->dtrlatch[sp->sp_channel] = 0;
		}
		else {
			sp->sp_dtr = 0;
			csc->sc_regs->dtrlatch[sp->sp_channel] = 1;
		}
		if (ISSET(bits, TIOCM_RTS))
			csc->sc_regs->stc.msvr &= ~CD180_MSVR_RTS;
		else
			csc->sc_regs->stc.msvr |= CD180_MSVR_RTS;
		break;
	case DMBIS:
		if (ISSET(bits, TIOCM_DTR)) {
			sp->sp_dtr = 1;
			csc->sc_regs->dtrlatch[sp->sp_channel] = 0;
		}
		if (ISSET(bits, TIOCM_RTS) && !ISSET(tp->t_cflag, CRTSCTS))
			csc->sc_regs->stc.msvr &= ~CD180_MSVR_RTS;
		break;
	case DMBIC:
		if (ISSET(bits, TIOCM_DTR)) {
			sp->sp_dtr = 0;
			csc->sc_regs->dtrlatch[sp->sp_channel] = 1;
		}
		if (ISSET(bits, TIOCM_RTS))
			csc->sc_regs->stc.msvr |= CD180_MSVR_RTS;
		break;
	}

	splx(s);
	return (bits);
}

int
stty_param(tp, t)
	struct tty *tp;
	struct termios *t;
{
	struct stty_softc *st = stty_cd.cd_devs[SPIF_CARD(tp->t_dev)];
	struct stty_port *sp = &st->sc_port[SPIF_PORT(tp->t_dev)];
	struct spif_softc *sc = sp->sp_sc;
	u_int8_t rbprl, rbprh, tbprl, tbprh;
	int s, opt;

	if (t->c_ospeed &&
	    stty_compute_baud(t->c_ospeed, sc->sc_osc, &tbprl, &tbprh))
		return (EINVAL);

	if (t->c_ispeed &&
	    stty_compute_baud(t->c_ispeed, sc->sc_osc, &rbprl, &rbprh))
		return (EINVAL);

	s = spltty();

	/* hang up line if ospeed is zero, otherwise raise DTR */
	stty_modem_control(sp, TIOCM_DTR,
	    (t->c_ospeed == 0 ? DMBIC : DMBIS));

	sc->sc_regs->stc.car = sp->sp_channel;

	opt = 0;
	if (ISSET(t->c_cflag, PARENB)) {
		opt |= CD180_COR1_PARMODE_NORMAL;
		opt |= (ISSET(t->c_cflag, PARODD) ?
				CD180_COR1_ODDPAR :
				CD180_COR1_EVENPAR);
	}
	else
		opt |= CD180_COR1_PARMODE_NO;

	if (!ISSET(t->c_iflag, INPCK))
		opt |= CD180_COR1_IGNPAR;

	if (ISSET(t->c_cflag, CSTOPB))
		opt |= CD180_COR1_STOP2;

	switch (t->c_cflag & CSIZE) {
	case CS5:
		opt |= CD180_COR1_CS5;
		break;
	case CS6:
		opt |= CD180_COR1_CS6;
		break;
	case CS7:
		opt |= CD180_COR1_CS7;
		break;
	default:
		opt |= CD180_COR1_CS8;
		break;
	}
	sc->sc_regs->stc.cor1 = opt;
	stty_write_ccr(&sc->sc_regs->stc, CD180_CCR_CMD_COR|CD180_CCR_CORCHG1);

	opt = CD180_COR2_ETC;
	if (ISSET(t->c_cflag, CRTSCTS))
		opt |= CD180_COR2_CTSAE;
	sc->sc_regs->stc.cor2 = opt;
	stty_write_ccr(&sc->sc_regs->stc, CD180_CCR_CMD_COR|CD180_CCR_CORCHG2);

	sc->sc_regs->stc.cor3 = STTY_RX_FIFO_THRESHOLD;
	stty_write_ccr(&sc->sc_regs->stc, CD180_CCR_CMD_COR|CD180_CCR_CORCHG3);

	sc->sc_regs->stc.schr1 = 0x11;
	sc->sc_regs->stc.schr2 = 0x13;
	sc->sc_regs->stc.schr3 = 0x11;
	sc->sc_regs->stc.schr4 = 0x13;
	sc->sc_regs->stc.rtpr = 0x12;

	sc->sc_regs->stc.mcor1 = CD180_MCOR1_CDZD | STTY_RX_DTR_THRESHOLD;
	sc->sc_regs->stc.mcor2 = CD180_MCOR2_CDOD;
	sc->sc_regs->stc.mcr = 0;

	if (t->c_ospeed) {
		sc->sc_regs->stc.tbprh = tbprh;
		sc->sc_regs->stc.tbprl = tbprl;
	}

	if (t->c_ispeed) {
		sc->sc_regs->stc.rbprh = rbprh;
		sc->sc_regs->stc.rbprl = rbprl;
	}

	stty_write_ccr(&sc->sc_regs->stc, CD180_CCR_CMD_CHAN |
	    CD180_CCR_CHAN_TXEN | CD180_CCR_CHAN_RXEN);

	sp->sp_carrier = sc->sc_regs->stc.msvr & CD180_MSVR_CD;

	splx(s);
	return (0);
}

int
sttyread(dev, uio, flags)
	dev_t dev;
	struct uio *uio;
	int flags;
{
	struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
	struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
	struct tty *tp = sp->sp_tty;

	return ((*linesw[tp->t_line].l_read)(tp, uio, flags));
}

int
sttywrite(dev, uio, flags)
	dev_t dev;
	struct uio *uio;
	int flags;
{
	struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
	struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];
	struct tty *tp = sp->sp_tty;

	return ((*linesw[tp->t_line].l_write)(tp, uio, flags));
}

struct tty *
sttytty(dev)
	dev_t dev;
{
	struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(dev)];
	struct stty_port *sp = &sc->sc_port[SPIF_PORT(dev)];

	return (sp->sp_tty);
}

int
sttystop(tp, flags)
	struct tty *tp;
	int flags;
{
	struct stty_softc *sc = stty_cd.cd_devs[SPIF_CARD(tp->t_dev)];
	struct stty_port *sp = &sc->sc_port[SPIF_PORT(tp->t_dev)];
	int s;

	s = spltty();
	if (ISSET(tp->t_state, TS_BUSY)) {
		if (!ISSET(tp->t_state, TS_TTSTOP))
			SET(tp->t_state, TS_FLUSH);
		SET(sp->sp_flags, STTYF_STOP);
	}
	splx(s);
	return (0);
}

void
stty_start(tp)
	struct tty *tp;
{
	struct stty_softc *stc = stty_cd.cd_devs[SPIF_CARD(tp->t_dev)];
	struct stty_port *sp = &stc->sc_port[SPIF_PORT(tp->t_dev)];
	struct spif_softc *sc = sp->sp_sc;
	int s;

	s = spltty();

	if (!ISSET(tp->t_state, TS_TTSTOP | TS_TIMEOUT | TS_BUSY)) {
		ttwakeupwr(tp);
		if (tp->t_outq.c_cc) {
			sp->sp_txc = ndqb(&tp->t_outq, 0);
			sp->sp_txp = tp->t_outq.c_cf;
			SET(tp->t_state, TS_BUSY);
			sc->sc_regs->stc.car = sp->sp_channel;
			sc->sc_regs->stc.srer |= CD180_SRER_TXD;
		}
	}

	splx(s);
}

int
spifstcintr_rxexception(sc, needsoftp)
	struct spif_softc *sc;
	int *needsoftp;
{
	struct stty_port *sp;
	u_int8_t channel, *ptr;

	channel = CD180_GSCR_CHANNEL(sc->sc_regs->stc.gscr1);
	sp = &sc->sc_ttys->sc_port[channel];
	ptr = sp->sp_rput;
	*ptr++ = sc->sc_regs->stc.rcsr;
	*ptr++ = sc->sc_regs->stc.rdr;
	if (ptr == sp->sp_rend)
		ptr = sp->sp_rbuf;
	if (ptr == sp->sp_rget) {
		if (ptr == sp->sp_rbuf)
			ptr = sp->sp_rend;
		ptr -= 2;
		SET(sp->sp_flags, STTYF_RING_OVERFLOW);
	}
	sc->sc_regs->stc.eosrr = 0;
	*needsoftp = 1;
	sp->sp_rput = ptr;
	return (1);
}

int
spifstcintr_rx(sc, needsoftp)
	struct spif_softc *sc;
	int *needsoftp;
{
	struct stty_port *sp;
	u_int8_t channel, *ptr, cnt, rcsr;
	int i;

	channel = CD180_GSCR_CHANNEL(sc->sc_regs->stc.gscr1);
	sp = &sc->sc_ttys->sc_port[channel];
	ptr = sp->sp_rput;
	cnt = sc->sc_regs->stc.rdcr;
	for (i = 0; i < cnt; i++) {
		*ptr++ = 0;
		rcsr = sc->sc_regs->stc.rcsr;
		*ptr++ = sc->sc_regs->stc.rdr;
		if (ptr == sp->sp_rend)
			ptr = sp->sp_rbuf;
		if (ptr == sp->sp_rget) {
			if (ptr == sp->sp_rbuf)
				ptr = sp->sp_rend;
			ptr -= 2;
			SET(sp->sp_flags, STTYF_RING_OVERFLOW);
			break;
		}
	}
	sc->sc_regs->stc.eosrr = 0;
	if (cnt) {
		*needsoftp = 1;
		sp->sp_rput = ptr;
	}
	return (1);
}

int
spifstcintr_tx(sc, needsoftp)
	struct spif_softc *sc;
	int *needsoftp;
{
	struct stty_port *sp;
	u_int8_t channel, ch;
	int cnt = 0;

	channel = CD180_GSCR_CHANNEL(sc->sc_regs->stc.gscr1);
	sp = &sc->sc_ttys->sc_port[channel];
	if (!ISSET(sp->sp_flags, STTYF_STOP)) {
		if (ISSET(sp->sp_flags, STTYF_SET_BREAK)) {
			sc->sc_regs->stc.tdr = 0;
			sc->sc_regs->stc.tdr = 0x81;
			CLR(sp->sp_flags, STTYF_SET_BREAK);
			cnt += 2;
		}
		if (ISSET(sp->sp_flags, STTYF_CLR_BREAK)) {
			sc->sc_regs->stc.tdr = 0;
			sc->sc_regs->stc.tdr = 0x83;
			CLR(sp->sp_flags, STTYF_CLR_BREAK);
			cnt += 2;
		}

		while (sp->sp_txc > 0 && cnt < (CD180_TX_FIFO_SIZE-1)) {
			ch = *sp->sp_txp;
			sp->sp_txc--;
			sp->sp_txp++;

			if (ch == 0) {
				sc->sc_regs->stc.tdr = ch;
				cnt++;
			}
			sc->sc_regs->stc.tdr = ch;
			cnt++;
		}
	}

	if (sp->sp_txc == 0 ||
	    ISSET(sp->sp_flags, STTYF_STOP)) {
		sc->sc_regs->stc.srer &= ~CD180_SRER_TXD;
		CLR(sp->sp_flags, STTYF_STOP);
		SET(sp->sp_flags, STTYF_DONE);
		*needsoftp = 1;
	}

	sc->sc_regs->stc.eosrr = 0;

	return (1);
}

int
spifstcintr_mx(sc, needsoftp)
	struct spif_softc *sc;
	int *needsoftp;
{
	struct stty_port *sp;
	u_int8_t channel, mcr;

	channel = CD180_GSCR_CHANNEL(sc->sc_regs->stc.gscr1);
	sp = &sc->sc_ttys->sc_port[channel];
	mcr = sc->sc_regs->stc.mcr;
	if (mcr & CD180_MCR_CD) {
		SET(sp->sp_flags, STTYF_CDCHG);
		*needsoftp = 1;
	}
	sc->sc_regs->stc.mcr = 0;
	sc->sc_regs->stc.eosrr = 0;
	return (1);
}

int
spifstcintr(vsc)
	void *vsc;
{
	struct spif_softc *sc = (struct spif_softc *)vsc;
	int needsoft = 0, r = 0, i;
	u_int8_t ar;

	for (i = 0; i < 8; i++) {
		ar = sc->sc_regs->istc.rrar & CD180_GSVR_IMASK;
		if (ar == CD180_GSVR_RXGOOD)
			r |= spifstcintr_rx(sc, &needsoft);
		else if (ar == CD180_GSVR_RXEXCEPTION)
			r |= spifstcintr_rxexception(sc, &needsoft);
	}

	for (i = 0; i < 8; i++) {
		ar = sc->sc_regs->istc.trar & CD180_GSVR_IMASK;
		if (ar == CD180_GSVR_TXDATA)
			r |= spifstcintr_tx(sc, &needsoft);
	}

	for (i = 0; i < 8; i++) {
		ar = sc->sc_regs->istc.mrar & CD180_GSVR_IMASK;
		if (ar == CD180_GSVR_STATCHG)
			r |= spifstcintr_mx(sc, &needsoft);
	}

	if (needsoft)
		softintr_schedule(sc->sc_softih);
	return (r);
}

void
spifsoftintr(vsc)
	void *vsc;
{
	struct spif_softc *sc = (struct spif_softc *)vsc;
	struct stty_softc *stc = sc->sc_ttys;
	int i, data, s, flags;
	u_int8_t stat, msvr;
	struct stty_port *sp;
	struct tty *tp;

	if (stc != NULL) {
		for (i = 0; i < stc->sc_nports; i++) {
			sp = &stc->sc_port[i];
			tp = sp->sp_tty;

			if (!ISSET(tp->t_state, TS_ISOPEN))
				continue;

			while (sp->sp_rget != sp->sp_rput) {
				stat = sp->sp_rget[0];
				data = sp->sp_rget[1];
				sp->sp_rget += 2;
				if (sp->sp_rget == sp->sp_rend)
					sp->sp_rget = sp->sp_rbuf;

				if (stat & (CD180_RCSR_BE | CD180_RCSR_FE))
					data |= TTY_FE;

				if (stat & CD180_RCSR_PE)
					data |= TTY_PE;

				(*linesw[tp->t_line].l_rint)(data, tp);
			}

			s = splhigh();
			flags = sp->sp_flags;
			CLR(sp->sp_flags, STTYF_DONE | STTYF_CDCHG |
			    STTYF_RING_OVERFLOW);
			splx(s);

			if (ISSET(flags, STTYF_CDCHG)) {
				s = spltty();
				sc->sc_regs->stc.car = i;
				msvr = sc->sc_regs->stc.msvr;
				splx(s);

				sp->sp_carrier = msvr & CD180_MSVR_CD;
				(*linesw[tp->t_line].l_modem)(tp,
				    sp->sp_carrier);
			}

			if (ISSET(flags, STTYF_RING_OVERFLOW)) {
				log(LOG_WARNING, "%s-%x: ring overflow\n",
					stc->sc_dev.dv_xname, i);
			}

			if (ISSET(flags, STTYF_DONE)) {
				ndflush(&tp->t_outq,
				    sp->sp_txp - tp->t_outq.c_cf);
				CLR(tp->t_state, TS_BUSY);
				(*linesw[tp->t_line].l_start)(tp);
			}
		}
	}
}

static __inline	void
stty_write_ccr(stc, val)
	struct stcregs *stc;
	u_int8_t val;
{
	int tries = 100000;

	while (stc->ccr && tries--);
	if (tries == 0)
		printf("CCR: timeout\n");
	stc->ccr = val;
}

int
stty_compute_baud(speed, clock, bprlp, bprhp)
	speed_t speed;
	int clock;
	u_int8_t *bprlp, *bprhp;
{
	u_int32_t rate;

	rate = (2 * clock) / (16 * speed);
	if (rate & 1)
		rate = (rate >> 1) + 1;
	else
		rate = rate >> 1;

	if (rate > 0xffff || rate == 0)
		return (1);

	*bprlp = rate & 0xff;
	*bprhp = (rate >> 8) & 0xff;
	return (0);
}

int
sbppmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct spif_softc *sc = (struct spif_softc *)parent;

	return (aux == sbppmatch && sc->sc_bpps == NULL);
}

void
sbppattach(parent, dev, aux)
	struct device *parent, *dev;
	void *aux;
{
	struct spif_softc *sc = (struct spif_softc *)parent;
	struct sbpp_softc *psc = (struct sbpp_softc *)dev;
	int port;

	sc->sc_bpps = psc;

	for (port = 0; port < sc->sc_npar; port++) {
	}

	psc->sc_nports = port;
	printf(": %d port%s\n", port, port == 1 ? "" : "s");
}

int
sbppopen(dev, flags, mode, p)
	dev_t dev;
	int flags;
	int mode;
	struct proc *p;
{
	return (ENXIO);
}

int
sbppclose(dev, flags, mode, p)
	dev_t dev;
	int flags;
	int mode;
	struct proc *p;
{
	return (ENXIO);
}

int
spifppcintr(v)
	void *v;
{
	return (0);
}

int
sbppread(dev, uio, flags)
	dev_t dev;
	struct uio *uio;
	int flags;
{
	return (sbpp_rw(dev, uio));
}

int
sbppwrite(dev, uio, flags)
	dev_t dev;
	struct uio *uio;
	int flags;
{
	return (sbpp_rw(dev, uio));
}

int
sbpp_rw(dev, uio)
	dev_t dev;
	struct uio *uio;
{
	return (ENXIO);
}

int
sbpppoll(dev, events, p)
	dev_t dev;
	int events;
	struct proc *p;
{
	return (seltrue(dev, events, p));
}

int
sbppioctl(dev, cmd, data, flags, p)
	dev_t dev;
	u_long cmd;
	caddr_t data;
	int flags;
	struct proc *p;
{
	int error;

	error = ENOTTY;

	return (error);
}


code:
/*	$OpenBSD: spifreg.h,v 1.10 2003/06/02 18:40:59 jason Exp $	*/

/*
 * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
 * 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.
 */

struct ppcregs {
	volatile u_int8_t	in_pdata;	/* input data reg */
	volatile u_int8_t	in_pstat;	/* input status reg */
	volatile u_int8_t	in_pctrl;	/* input control reg */
	volatile u_int8_t	in_pweird;	/* input weird reg */
	volatile u_int8_t	out_pdata;	/* output data reg */
	volatile u_int8_t	out_pstat;	/* output status reg */
	volatile u_int8_t	out_pctrl;	/* output control reg */
	volatile u_int8_t	out_pweird;	/* output weird reg */
	volatile u_int8_t	_unused[500];	/* unused space */
	volatile u_int8_t	iack_pdata;	/* intr-ack data reg */
	volatile u_int8_t	iack_pstat;	/* intr-ack status reg */
	volatile u_int8_t	iack_pctrl;	/* intr-ack control reg */
	volatile u_int8_t	iack_pweird;	/* intr-ack weird reg */
};

/* Parallel Status: read only */
#define	PPC_PSTAT_ERROR		0x08		/* error */
#define	PPC_PSTAT_SELECT	0x10		/* select */
#define	PPC_PSTAT_PAPER		0x20		/* paper out */
#define	PPC_PSTAT_ACK		0x40		/* ack */
#define	PPC_PSTAT_BUSY		0x80		/* busy */

/* Parallel Control: read/write */
#define	PPC_CTRL_STROBE		0x01		/* strobe, 1=drop strobe */
#define	PPC_CTRL_AFX		0x02		/* auto form-feed */
#define	PPC_CTRL_INIT		0x04		/* init, 1=enable printer */
#define	PPC_CTRL_SLCT		0x08		/* SLC, 1=select printer */
#define	PPC_CTRL_IRQE		0x10		/* IRQ, 1=enable intrs */
#define	PPC_CTRL_OUTPUT		0x20		/* direction: 1=ppc out */

/*
 * The 'stc' is a Cirrus Logic CL-CD180 (either revision B or revision C)
 */
struct stcregs {
	volatile u_int8_t	_unused0[1];	/* 0x00 unused */
	volatile u_int8_t	ccr;		/* channel command reg */
	volatile u_int8_t	srer;		/* service req enable reg */
	volatile u_int8_t	cor1;		/* channel option reg 1 */
	volatile u_int8_t	cor2;		/* channel option reg 2 */
	volatile u_int8_t	cor3;		/* channel option reg 3 */
	volatile u_int8_t	ccsr;		/* channel cntrl status reg */
	volatile u_int8_t	rdcr;		/* rx data count reg */
	volatile u_int8_t	_unused1[1];	/* 0x08 unused */
	volatile u_int8_t	schr1;		/* special char reg 1 */
	volatile u_int8_t	schr2;		/* special char reg 2 */
	volatile u_int8_t	schr3;		/* special char reg 3 */
	volatile u_int8_t	schr4;		/* special char reg 4 */
	volatile u_int8_t	_unused2[3];	/* 0x0d - 0x0f unused */

	volatile u_int8_t	mcor1;		/* modem change option reg 1 */
	volatile u_int8_t	mcor2;		/* modem change option reg 2 */
	volatile u_int8_t	mcr;		/* modem change reg */
	volatile u_int8_t	_unused3[5];	/* 0x13 - 0x17 unused */
	volatile u_int8_t	rtpr;		/* rx timeout period reg */
	volatile u_int8_t	_unused4[7];

	volatile u_int8_t	_unused5[8];	/* 0x19 - 0x27 unused */
	volatile u_int8_t	msvr;		/* modem signal value reg */
	volatile u_int8_t	msvrts;		/* modem sig value rts reg */
	volatile u_int8_t	msvdtr;		/* modem sig value dtr reg */
	volatile u_int8_t	_unused6[5];	/* 0x2b - 0x2f unused */

	volatile u_int8_t	_unused7[1];	/* 0x30 unused */
	volatile u_int8_t	rbprh;		/* rx bit rate period reg hi */
	volatile u_int8_t	rbprl;		/* rx bit rate period reg lo */
	volatile u_int8_t	rbr;		/* rx bit reg */
	volatile u_int8_t	_unused8[5];	/* 0x34 - 0x38 unused */
	volatile u_int8_t	tbprh;		/* tx bit rate period reg hi */
	volatile u_int8_t	tbprl;		/* tx bit rate period reg lo */
	volatile u_int8_t	_unused9[5];	/* 0x34 - 0x38 unused */

	volatile u_int8_t	gsvr;		/* global service vector reg */
	volatile u_int8_t	gscr1;		/* global service chan reg 1 */
	volatile u_int8_t	gscr2;		/* global service chan reg 2 */
	volatile u_int8_t	gscr3;		/* global service chan reg 3 */
	volatile u_int8_t	_unused10[12];	/* 0x44 - 0x4f unused */

	volatile u_int8_t	_unused11[16];	/* 0x50 - 0x5f unused */

	volatile u_int8_t	_unused12[1];	/* 0x60 unused */
	volatile u_int8_t	msmr;		/* modem service match reg */
	volatile u_int8_t	tsmr;		/* tx service match reg */
	volatile u_int8_t	rsmr;		/* rx service match reg */
	volatile u_int8_t	car;		/* channel access reg */
	volatile u_int8_t	srsr;		/* service request stat reg */
	volatile u_int8_t	srcr;		/* service request conf reg */
	volatile u_int8_t	_unused13[4];	/* 0x67 - 0x6a unused */
	volatile u_int8_t	gfrcr;		/* global firmwr rev code reg */
	volatile u_int8_t	_unused14[4];	/* 0x6c - 0x6f unused */

	volatile u_int8_t	pprh;		/* prescalar period reg hi */
	volatile u_int8_t	pprl;		/* prescalar period reg lo */
	volatile u_int8_t	_unused15[3];	/* 0x72 - 0x74 unused */
	volatile u_int8_t	mrar;		/* modem request ack reg */
	volatile u_int8_t	trar;		/* tx request ack reg */
	volatile u_int8_t	rrar;		/* rx request ack reg */
	volatile u_int8_t	rdr;		/* rx data reg */
	volatile u_int8_t	_unused16[1];	/* 0x79 unused */
	volatile u_int8_t	rcsr;		/* rx char status reg */
	volatile u_int8_t	tdr;		/* tx data reg */
	volatile u_int8_t	_unused17[3];	/* 0x7c - 0x7e unused */
	volatile u_int8_t	eosrr;		/* end of service req reg */
};

/* Global Firmware Revision Code Register (rw) */
#define	CD180_GFRCR_REV_B	0x81		/* CL-CD180B */
#define	CD180_GFRCR_REV_C	0x82		/* CL-CD180C */

/* Service Request Configuration Register (rw) (CD180C or higher) */
#define	CD180_SRCR_PKGTYP		0x80	/* pkg type,0=PLCC,1=PQFP */
#define	CD180_SRCR_REGACKEN		0x40	/* register ack enable */
#define	CD180_SRCR_DAISYEN		0x20	/* daisy chain enable */
#define	CD180_SRCR_GLOBPRI		0x10	/* global priority */
#define	CD180_SRCR_UNFAIR		0x08	/* use unfair interrupts */
#define	CD180_SRCR_AUTOPRI		0x02	/* automatic priority */
#define	CD180_SRCR_PRISEL		0x01	/* select rx/tx as high pri */

/* Prescalar Period Register High (rw) */
#define	CD180_PPRH	0xf0		/* high byte */
#define	CD180_PPRL	0x00		/* low byte */

/* Global Service Vector Register (rw) */
/* Modem Request Acknowledgement Register (ro) (and IACK equivalent) */
/* Receive Request Acknowledgement Register (ro) (and IACK equivalent) */
/* Transmit Request Acknowledgement Register (ro) (and IACK equivalent) */
#define	CD180_GSVR_USERMASK		0xf8	/* user defined bits */
#define	CD180_GSVR_IMASK		0x07	/* interrupt type mask */
#define	CD180_GSVR_NOREQUEST		0x00	/* no request pending */
#define	CD180_GSVR_STATCHG		0x01	/* modem signal change */
#define	CD180_GSVR_TXDATA		0x02	/* tx service request */
#define	CD180_GSVR_RXGOOD		0x03	/* rx service request */
#define	CD180_GSVR_reserved1		0x04	/* reserved */
#define	CD180_GSVR_reserved2		0x05	/* reserved */
#define	CD180_GSVR_reserved3		0x06	/* reserved */
#define	CD180_GSVR_RXEXCEPTION		0x07	/* rx exception request */

/* Service Request Status Register (ro) (CD180C and higher) */
#define	CD180_SRSR_MREQINT		0x01	/* modem request internal */
#define	CD180_SRSR_MREQEXT		0x02	/* modem request external */
#define	CD180_SRSR_TREQINT		0x04	/* tx request internal */
#define	CD180_SRSR_TREQEXT		0x08	/* tx request external */
#define	CD180_SRSR_RREQINT		0x10	/* rx request internal */
#define	CD180_SRSR_RREQEXT		0x20	/* rx request external */
#define	CD180_SRSR_ILV_MASK		0xc0	/* internal service context */
#define	CD180_SRSR_ILV_NONE		0x00	/* not in service context */
#define	CD180_SRSR_ILV_RX		0xc0	/* in rx service context */
#define	CD180_SRSR_ILV_TX		0x80	/* in tx service context */
#define	CD180_SRSR_ILV_MODEM		0x40	/* in modem service context */

/* Global Service Channel Register 1,2,3 (rw) */
#define	CD180_GSCR_CHANNEL(gscr)	(((gscr) >> 2) & 7)

/* Receive Data Count Register (ro) */
#define	CD180_RDCR_MASK			0x0f	/* mask for fifo length */

/* Receive Character Status Register (ro) */
#define	CD180_RCSR_TO			0x80	/* time out */
#define	CD180_RCSR_SCD2			0x40	/* special char detect 2 */
#define	CD180_RCSR_SCD1			0x20	/* special char detect 1 */
#define	CD180_RCSR_SCD0			0x10	/* special char detect 0 */
#define	CD180_RCSR_BE			0x08	/* break exception */
#define	CD180_RCSR_PE			0x04	/* parity exception */
#define	CD180_RCSR_FE			0x02	/* framing exception */
#define	CD180_RCSR_OE			0x01	/* overrun exception */

/* Service Request Enable Register (rw) */
#define	CD180_SRER_DSR			0x80	/* DSR service request */
#define	CD180_SRER_CD			0x40	/* CD service request */
#define	CD180_SRER_CTS			0x20	/* CTS service request */
#define	CD180_SRER_RXD			0x10	/* RXD service request */
#define	CD180_SRER_RXSCD		0x08	/* RX special char request */
#define	CD180_SRER_TXD			0x04	/* TX ready service request */
#define	CD180_SRER_TXE			0x02	/* TX empty service request */
#define	CD180_SRER_NNDT			0x01	/* No new data timeout req */

/* Channel Command Register (rw) */
/* Reset Channel Command */
#define	CD180_CCR_CMD_RESET		0x80	/* chip/channel reset */
#define CD180_CCR_RESETALL		0x01	/* global reset */
#define	CD180_CCR_RESETCHAN		0x00	/* current channel reset */
/* Channel Option Register Command */
#define	CD180_CCR_CMD_COR		0x40	/* channel opt reg changed */
#define	CD180_CCR_CORCHG1		0x02	/* cor1 has changed */
#define	CD180_CCR_CORCHG2		0x04	/* cor2 has changed */
#define	CD180_CCR_CORCHG3		0x08	/* cor3 has changed */
/* Send Special Character Command */
#define	CD180_CCR_CMD_SPC		0x20	/* send special chars changed */
#define	CD180_CCR_SSPC0			0x01	/* send special char 0 change */
#define	CD180_CCR_SSPC1			0x02	/* send special char 1 change */
#define	CD180_CCR_SSPC2			0x04	/* send special char 2 change */
/* Channel Control Command */
#define	CD180_CCR_CMD_CHAN		0x10	/* channel control command */
#define	CD180_CCR_CHAN_TXEN		0x08	/* enable channel tx */
#define	CD180_CCR_CHAN_TXDIS		0x04	/* disable channel tx */
#define	CD180_CCR_CHAN_RXEN		0x02	/* enable channel rx */
#define	CD180_CCR_CHAN_RXDIS		0x01	/* disable channel rx */

/* Channel Option Register 1 (rw) */
#define	CD180_COR1_EVENPAR		0x00	/* even parity */
#define	CD180_COR1_ODDPAR		0x80	/* odd parity */
#define	CD180_COR1_PARMODE_NO		0x00	/* no parity */
#define	CD180_COR1_PARMODE_FORCE	0x20	/* force (odd=1, even=0) */
#define CD180_COR1_PARMODE_NORMAL	0x40	/* normal parity mode */
#define	CD180_COR1_PARMODE_NA		0x60	/* notused */
#define	CD180_COR1_IGNPAR		0x10	/* ignore parity */
#define	CD180_COR1_STOP1		0x00	/* 1 stop bit */
#define	CD180_COR1_STOP15		0x04	/* 1.5 stop bits */
#define	CD180_COR1_STOP2		0x08	/* 2 stop bits */
#define	CD180_COR1_STOP25		0x0c	/* 2.5 stop bits */
#define	CD180_COR1_CS5			0x00	/* 5 bit characters */
#define	CD180_COR1_CS6			0x01	/* 6 bit characters */
#define	CD180_COR1_CS7			0x02	/* 7 bit characters */
#define	CD180_COR1_CS8			0x03	/* 8 bit characters */

/* Channel Option Register 2 (rw) */
#define	CD180_COR2_IXM			0x80	/* implied xon mode */
#define	CD180_COR2_TXIBE		0x40	/* tx in-band flow control */
#define	CD180_COR2_ETC			0x20	/* embedded tx command enbl */
#define	CD180_COR2_LLM			0x10	/* local loopback mode */
#define	CD180_COR2_RLM			0x08	/* remote loopback mode */
#define	CD180_COR2_RTSAO		0x04	/* RTS automatic output enbl */
#define	CD180_COR2_CTSAE		0x02	/* CTS automatic enable */
#define	CD180_COR2_DSRAE		0x01	/* DSR automatic enable */

/* Channel Option Register 3 (rw) */
#define	CD180_COR3_XON2			0x80	/* XON char in spc1&3 */
#define	CD180_COR3_XON1			0x00	/* XON char in spc1 */
#define	CD180_COR3_XOFF2		0x40	/* XOFF char in spc2&4 */
#define	CD180_COR3_XOFF1		0x00	/* XOFF char in spc2 */
#define	CD180_COR3_FCT			0x20	/* flow control transparency */
#define	CD180_COR3_SCDE			0x10	/* special char recognition */
#define	CD180_COR3_RXFIFO_MASK		0x0f	/* rx fifo threshold */

/* Channel Control Status Register (ro) */
#define	CD180_CCSR_RXEN			0x80	/* rx is enabled */
#define	CD180_CCSR_RXFLOFF		0x40	/* rx flow-off */
#define	CD180_CCSR_RXFLON		0x20	/* rx flow-on */
#define	CD180_CCSR_TXEN			0x08	/* tx is enabled */
#define	CD180_CCSR_TXFLOFF		0x04	/* tx flow-off */
#define	CD180_CCSR_TXFLON		0x02	/* tx flow-on */

/* Receiver Bit Register (ro) */
#define	CD180_RBR_RXD			0x40	/* state of RxD pin */
#define	CD180_RBR_STARTHUNT		0x20	/* looking for start bit */

/* Modem Change Register (rw) */
#define	CD180_MCR_DSR			0x80	/* DSR changed */
#define	CD180_MCR_CD			0x40	/* CD changed */
#define	CD180_MCR_CTS			0x20	/* CTS changed */

/* Modem Change Option Register 1 (rw) */
#define	CD180_MCOR1_DSRZD		0x80	/* catch 0->1 DSR changes */
#define	CD180_MCOR1_CDZD		0x40	/* catch 0->1 CD changes */
#define	CD180_MCOR1_CTSZD		0x40	/* catch 0->1 CTS changes */
#define	CD180_MCOR1_DTRTHRESH		0x0f	/* DTR threshold mask */

/* Modem Change Option Register 2 (rw) */
#define	CD180_MCOR2_DSROD		0x80	/* catch 1->0 DSR changes */
#define	CD180_MCOR2_CDOD		0x40	/* catch 1->0 CD changes */
#define	CD180_MCOR2_CTSOD		0x20	/* catch 1->0 CTS changes */

/* Modem Signal Value Register (rw) */
#define	CD180_MSVR_DSR			0x80	/* DSR input state */
#define	CD180_MSVR_CD			0x40	/* CD input state */
#define	CD180_MSVR_CTS			0x20	/* CTS input state */
#define	CD180_MSVR_DTR			0x02	/* DTR output state */
#define	CD180_MSVR_RTS			0x01	/* RTS output state */

/* Modem Signal Value Register - Request To Send (w) (CD180C and higher) */
#define	CD180_MSVRTS_RTS		0x01	/* RTS signal value */

/* Modem Signal Value Register - Data Terminal Ready (w) (CD180C and higher) */
#define	CD180_MSVDTR_DTR		0x02	/* DTR signal value */

/*
 * The register map for the SUNW,spif looks something like:
 *    Offset:		Function:
 *	0000 - 03ff	Boot ROM
 *	0400 - 0407	dtr latches (one per port)
 *	0408 - 07ff	unused
 *	0800 - 087f	CD180 registers (normal mapping)
 *	0880 - 0bff	unused
 *	0c00 - 0c7f	CD180 registers (*iack mapping)
 *	0c80 - 0dff	unused
 *	0e00 - 1fff	PPC registers
 *
 * One note about the DTR latches:  The values stored there are reversed.
 * By writing a 1 to the latch, DTR is lowered, and by writing a 0, DTR
 * is raised.  The latches cannot be read, and no other value can be written
 * there or the system will crash due to "excessive bus loading (see
 * SBus loading and capacitance spec)"
 *
 * The *iack registers are read/written with the IACK bit set.  When
 * the interrupt routine starts, it reads the MRAR, TRAR, and RRAR registers
 * from this mapping.  This signals an interrupt acknowledgement cycle.
 * (NOTE: these are not really the MRAR, TRAR, and RRAR... They are copies
 * of the GSVR, I just mapped them to the same location as the mrar, trar,
 * and rrar because it seemed appropriate).
 */
struct spifregs {
	volatile u_int8_t	_unused1[1024];	/* 0x000-0x3ff unused */
	volatile u_int8_t	dtrlatch[8];	/* per port dtr latch */
	volatile u_int8_t	_unused2[1016];	/* 0x408-0x7ff unused */
	struct stcregs		stc;		/* regular cd-180 regs */
	volatile u_int8_t	_unused3[896];	/* 0x880-0xbff unused */
	struct stcregs		istc;		/* *iack cd-180 regs */
	volatile u_int8_t	_unused4[384];	/* 0xc80-0xdff unused */
	struct ppcregs		ppc;		/* parallel port regs */
};

/*
 * The mapping of minor device number -> card and port is done as
 * follows by default:
 *
 *  +---+---+---+---+---+---+---+---+
 *  | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
 *  +---+---+---+---+---+---+---+---+
 *    |   |   |   |   |   |   |   |
 *    |   |   |   |   |   +---+---+---> port number
 *    |   |   |   |   |
 *    |   |   |   |   +---------------> unused
 *    |   |   |   |
 *    |   |   |   +-------------------> dialout (on tty ports)
 *    |   |   |
 *    |   |   +-----------------------> unused
 *    |   |
 *    +---+---------------------------> card number
 *
 */
#define SPIF_MAX_CARDS		4
#define SPIF_MAX_TTY		8
#define SPIF_MAX_BPP		1

/*
 * device selectors
 */
#define SPIF_CARD(x)	((minor(x) >> 6) & 0x03)
#define SPIF_PORT(x)	(minor(x) & 0x07)
#define STTY_DIALOUT(x) (minor(x) & 0x10)

#define	STTY_RX_FIFO_THRESHOLD	4
#define	STTY_RX_DTR_THRESHOLD	7
#define	CD180_TX_FIFO_SIZE	8		/* 8 chars of fifo */

/*
 * These are the offsets of the MRAR, TRAR, and RRAR in *IACK space.
 * The high bit must be set as per specs for the MSMR, TSMR, and RSMR.
 */
#define	SPIF_MSMR			0xf5	/* offset of MRAR | 0x80 */
#define	SPIF_TSMR			0xf6	/* offset of TRAR | 0x80 */
#define	SPIF_RSMR			0xf7	/* offset of RRAR | 0x80 */

/*
 * "verosc" node tells which oscillator we have.
 */
#define	SPIF_OSC9	1		/* 9.8304 MHz */
#define	SPIF_OSC10	2		/* 10MHz */

/*
 * There are two interrupts, serial gets interrupt[0], and parallel
 * gets interrupt[1]
 */
#define	SERIAL_INTR	0
#define	PARALLEL_INTR	1

/*
 * spif tty flags
 */
#define	STTYF_CDCHG		0x01		/* carrier changed */
#define	STTYF_RING_OVERFLOW	0x02		/* ring buffer overflowed */
#define	STTYF_DONE		0x04		/* done... flush buffers */
#define	STTYF_SET_BREAK		0x08		/* set break signal */
#define	STTYF_CLR_BREAK		0x10		/* clear break signal */
#define	STTYF_STOP		0x20		/* stopped */

#define	STTY_RBUF_SIZE		(2 * 512)


code:
/*	$OpenBSD: spifvar.h,v 1.5 2009/04/10 20:53:51 miod Exp $	*/

/*
 * Copyright (c) 1999 Jason L. Wright (jason@thought.net)
 * 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.
 */

#define	SPIF_MAX_SERIAL	8
#define SPIF_MAX_PARALLEL 1

struct stty_port {
	struct tty *sp_tty;		/* tty device */
	struct spif_softc *sp_sc;	/* pointer back to registers */
	int sp_channel;			/* channel number */
	u_char *sp_rbuf;		/* ring buffer start */
	u_char *sp_rend;		/* ring buffer end */
	u_char *sp_rget;		/* ring buffer read pointer */
	u_char *sp_rput;		/* ring buffer write pointer */
	u_char *sp_txp;			/* transmit character pointer */
	int sp_txc;			/* transmit character counter */

	int sp_openflags;		/* open flags */
	int sp_carrier;			/* software carrier status */
	int sp_flags;			/* software state */
	char sp_dtr;			/* software dtr status */
};

struct stty_softc {
	struct	device sc_dev;		/* base device */
	int	sc_nports;		/* number of serial ports */
	struct	stty_port sc_port[SPIF_MAX_SERIAL];
};

struct sbpp_softc {
	struct	device sc_dev;		/* base device */
	int	sc_nports;		/* number of parallel ports */
};

struct spif_softc {
	struct	device sc_dev;		/* base device */
	struct	intrhand sc_stcih;	/* stc interrupt vectoring */
	struct	intrhand sc_ppcih;	/* ppc interrupt vectoring */
	void	*sc_softih;		/* soft interrupt vectoring */
	int	sc_rev;			/* revision level */
	int	sc_osc;			/* oscillator speed (hz) */
	int	sc_node;		/* which sbus node */
	int	sc_nser;		/* number of serial ports */
	int	sc_npar;		/* number of parallel ports */
	int	sc_rev2;		/* cd180 chip revision */
	struct	spifregs *sc_regs;	/* registers */
	struct	stty_softc *sc_ttys;	/* our ttys */
	struct	sbpp_softc *sc_bpps;	/* our ttys */
};


code:
/*	$OpenBSD: stp_sbus.c,v 1.7 2008/06/26 05:42:13 ray Exp $	*/
/*	$NetBSD: stp4020.c,v 1.23 2002/06/01 23:51:03 lukem Exp $	*/

/*-
 * Copyright (c) 1998 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Paul Kranenburg.
 *
 * 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.
 */

/*
 * STP4020: SBus/PCMCIA bridge supporting one Type-3 PCMCIA card, or up to
 * two Type-1 and Type-2 PCMCIA cards..
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/extent.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/device.h>

#include <dev/pcmcia/pcmciareg.h>
#include <dev/pcmcia/pcmciavar.h>
#include <dev/pcmcia/pcmciachip.h>

#include <machine/bus.h>
#include <sparc/dev/sbusvar.h>

#include <dev/sbus/stp4020reg.h>
#include <dev/sbus/stp4020var.h>

struct stp4020_sbus_softc {
	struct stp4020_softc stp;
	struct rom_reg	sc_reg;
	struct rom_reg	sc_reg_le;  /* rev. copy for pcmcia bus_space access */
	struct intrhand	sc_ih[2];
};

int	stpmatch(struct device *, void *, void *);
void	stpattach(struct device *, struct device *, void *);

struct cfattach stp_sbus_ca = {
	sizeof(struct stp4020_sbus_softc), stpmatch, stpattach
};

int
stpmatch(parent, match, aux)
	struct device *parent;
	void *match;
	void *aux;
{
	struct confargs *ca = aux;

	return (strcmp("SUNW,pcmcia", ca->ca_ra.ra_name) == 0);
}

/*
 * Attach all the sub-devices we can find
 */
void
stpattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct confargs *ca = aux;
	struct stp4020_sbus_softc *ssc = (void *)self;
	struct stp4020_softc *sc = (void *)self;
	int node;
	int i;
	bus_space_handle_t bh;

	node = ca->ca_ra.ra_node;

	/* Transfer bus tags */
	ssc->sc_reg = ca->ca_ra.ra_reg[0];
	ssc->sc_reg_le = ca->ca_ra.ra_reg[0];
	SET_TAG_LITTLE_ENDIAN(&ssc->sc_reg_le);
	sc->sc_bustag = &ssc->sc_reg;

	/* Set up per-socket static initialization */
	sc->sc_socks[0].sc = sc->sc_socks[1].sc = sc;
	sc->sc_socks[0].tag = sc->sc_socks[1].tag = sc->sc_bustag;

	if (ca->ca_ra.ra_nreg < 8) {
		printf(": only %d register sets\n", ca->ca_ra.ra_nreg);
		return;
	}

	if (ca->ca_ra.ra_nintr != 2) {
		printf(": expect 2 interrupt SBus levels; got %d\n",
		    ca->ca_ra.ra_nintr);
		return;
	}

#define STP4020_BANK_PROM	0
#define STP4020_BANK_CTRL	4
	for (i = 0; i < 8; i++) {

		/*
		 * STP4020 Register address map:
		 *	bank  0:   Forth PROM
		 *	banks 1-3: socket 0, windows 0-2
		 *	bank  4:   control registers
		 *	banks 5-7: socket 1, windows 0-2
		 */

		if (i == STP4020_BANK_PROM)
			/* Skip the PROM */
			continue;

		if (bus_space_map(&ca->ca_ra.ra_reg[i], 0,
		    ca->ca_ra.ra_reg[i].rr_len, 0, &bh) != 0) {
			printf(": attach: cannot map registers\n");
			return;
		}

		if (i == STP4020_BANK_CTRL) {
			/*
			 * Copy tag and handle to both socket structures
			 * for easy access in control/status IO functions.
			 */
			sc->sc_socks[0].regs = sc->sc_socks[1].regs = bh;
		} else if (i < STP4020_BANK_CTRL) {
			/* banks 1-3 */
			sc->sc_socks[0].windows[i-1].winaddr = bh;
			sc->sc_socks[0].wintag = &ssc->sc_reg_le;
		} else {
			/* banks 5-7 */
			sc->sc_socks[1].windows[i-5].winaddr = bh;
			sc->sc_socks[1].wintag = &ssc->sc_reg_le;
		}
	}

	/*
	 * We get to use two SBus interrupt levels.
	 * The higher level we use for status change interrupts;
	 * the lower level for PC card I/O.
	 */
	ssc->sc_ih[1].ih_fun = stp4020_statintr;
	ssc->sc_ih[1].ih_arg = sc;
	intr_establish(ca->ca_ra.ra_intr[1].int_pri,
	    &ssc->sc_ih[1], -1, self->dv_xname);
	printf(" pri %d", ca->ca_ra.ra_intr[1].int_pri);

	ssc->sc_ih[0].ih_fun = stp4020_iointr;
	ssc->sc_ih[0].ih_arg = sc;
	intr_establish(ca->ca_ra.ra_intr[0].int_pri,
	    &ssc->sc_ih[0], -1, self->dv_xname);
	printf(" and %d", ca->ca_ra.ra_intr[0].int_pri);

	stpattach_common(sc, ((struct sbus_softc *)parent)->sc_clockfreq);
}