code:
/*	$OpenBSD: aic7xxx_cam.h,v 1.7 2015/07/17 21:42:49 krw Exp $	*/
/*	$NetBSD: aic7xxx_cam.h,v 1.3 2003/04/20 11:17:20 fvdl Exp $	*/

/*
 * Data structures and definitions for the CAM system.
 *
 * Copyright (c) 1997 Justin T. Gibbs.
 * Copyright (c) 2000 Adaptec Inc.
 * 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,
 *    without modification.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL").
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 */
/*
 * Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
 */

#ifndef _AIC7XXX_CAM_H
#define _AIC7XXX_CAM_H

#define SCSI_REV_2	2

#define	CAM_BUS_WILDCARD ((u_int)~0)
#define	CAM_TARGET_WILDCARD ((u_int)~0)
#define	CAM_LUN_WILDCARD -1

/*
 * Translate FreeBSD names for SCSI status byte values to OpenBSD names.
 */
#define SCSI_STATUS_OK                  SCSI_OK
#define SCSI_STATUS_CHECK_COND          SCSI_CHECK
#define SCSI_STATUS_COND_MET            SCSI_COND_MET
#define SCSI_STATUS_BUSY                SCSI_BUSY
#define SCSI_STATUS_INTERMED            SCSI_INTERM
#define SCSI_STATUS_INTERMED_COND_MET   SCSI_INTERMED_COND_MET
#define SCSI_STATUS_RESERV_CONFLICT     SCSI_RESERV_CONFLICT
#define SCSI_STATUS_CMD_TERMINATED      SCSI_TERMINATED
#define SCSI_STATUS_QUEUE_FULL          SCSI_QUEUE_FULL

#define XS_CTL_DATA_IN		SCSI_DATA_IN
#define XS_CTL_POLL		SCSI_POLL
#define XS_CTL_RESET		SCSI_RESET

#define MSG_EXT_PPR_QAS_REQ	MSG_EXT_PPR_PROT_QAS
#define MSG_EXT_PPR_IU_REQ	MSG_EXT_PPR_PROT_IUS
#define MSG_EXT_PPR_DT_REQ	MSG_EXT_PPR_PROT_DT
#define MSG_ORDERED_TASK	MSG_ORDERED_Q_TAG
#define MSG_SIMPLE_TASK		MSG_SIMPLE_Q_TAG
#define MSG_ABORT_TASK		MSG_ABORT_TAG

#define scsipi_channel		scsi_link
#define scsipi_xfer		scsi_xfer
#define scsipi_adapter		scsi_adapter
#define scsipi_sense_data	scsi_sense_data
#define scsipi_sense		scsi_sense
#define scsipi_periph		scsi_link

#define	callout_reset(timer, timeout, func, arg) do {	\
	if (!timeout_initialized((timer)))		\
		timeout_set((timer), (func), (arg));	\
	timeout_add((timer), (timeout));		\
} while (0)

#define aic_delay	DELAY

#define aic_htobe16(x) htobe16(x)
#define aic_htobe32(x) htobe32(x)
#define aic_htobe64(x) htobe64(x)
#define aic_htole16(x) htole16(x)
#define aic_htole32(x) htole32(x)
#define aic_htole64(x) htole64(x)

#define aic_be16toh(x) be16toh(x)
#define aic_be32toh(x) be32toh(x)
#define aic_be64toh(x) be64toh(x)
#define aic_le16toh(x) letoh16(x)
#define aic_le32toh(x) letoh32(x)
#define aic_le64toh(x) letoh64(x)

#define xs_control	flags
#define xs_callout	stimeout
#define xs_status	status

/* CAM Status field values */
typedef enum {
	CAM_REQ_INPROG,		/* CCB request is in progress */
	CAM_REQ_CMP,		/* CCB request completed without error */
	CAM_REQ_ABORTED,	/* CCB request aborted by the host */
	CAM_UA_ABORT,		/* Unable to abort CCB request */
	CAM_REQ_CMP_ERR,	/* CCB request completed with an error */
	CAM_BUSY,		/* CAM subsytem is busy */
	CAM_REQ_INVALID,	/* CCB request was invalid */
	CAM_PATH_INVALID,	/* Supplied Path ID is invalid */
	CAM_SEL_TIMEOUT,	/* Target Selection Timeout */
	CAM_CMD_TIMEOUT,	/* Command timeout */
	CAM_SCSI_STATUS_ERROR,	/* SCSI error, look at error code in CCB */
	CAM_SCSI_BUS_RESET,	/* SCSI Bus Reset Sent/Received */
	CAM_UNCOR_PARITY,	/* Uncorrectable parity error occurred */
	CAM_AUTOSENSE_FAIL,	/* Autosense: request sense cmd fail */
	CAM_NO_HBA,		/* No HBA Detected Error */
	CAM_DATA_RUN_ERR,	/* Data Overrun error */
	CAM_UNEXP_BUSFREE,	/* Unexpected Bus Free */
	CAM_SEQUENCE_FAIL,	/* Protocol Violation */
	CAM_CCB_LEN_ERR,	/* CCB length supplied is inadequate */
	CAM_PROVIDE_FAIL,	/* Unable to provide requested capability */
	CAM_BDR_SENT,		/* A SCSI BDR msg was sent to target */
	CAM_REQ_TERMIO,		/* CCB request terminated by the host */
	CAM_UNREC_HBA_ERROR,	/* Unrecoverable Host Bus Adapter Error */
	CAM_REQ_TOO_BIG,	/* The request was too large for this host */
	CAM_UA_TERMIO,		/* Unable to terminate I/O CCB request */
	CAM_MSG_REJECT_REC,	/* Message Reject Received */
	CAM_DEV_NOT_THERE,	/* SCSI Device Not Installed/there */
	CAM_RESRC_UNAVAIL,	/* Resource Unavailable */
	/*
	 * This request should be requeued to preserve
	 * transaction ordering.  This typically occurs
	 * when the SIM recognizes an error that should
	 * freeze the queue and must place additional
	 * requests for the target at the sim level
	 * back into the XPT queue.
	 */
	CAM_REQUEUE_REQ,
	CAM_DEV_QFRZN		= 0x40,

	CAM_STATUS_MASK		= 0x3F
} cam_status;

typedef enum {
	CAM_DIR_IN		= SCSI_DATA_IN,
	CAM_DIR_OUT		= SCSI_DATA_OUT
} ccb_flags;

typedef enum {
	AC_BUS_RESET            =       0x001,
	AC_UNSOL_RESEL          =       0x002,
	AC_SCSI_AEN             =       0x008,
	AC_SENT_BDR             =       0x010,
	AC_PATH_REGISTERED      =       0x020,
	AC_PATH_DEREGISTERED    =       0x040,
	AC_FOUND_DEVICE         =       0x080,
	AC_LOST_DEVICE          =       0x100,
	AC_TRANSFER_NEG         =       0x200,
	AC_INQ_CHANGED          =       0x400,
	AC_GETDEV_CHANGED       =       0x800
} ac_code;

#endif /* _AIC7XXX_CAM_H */

license: unknown


code:
/*	$OpenBSD: aic7xxx_inline.h,v 1.16 2015/07/17 21:42:49 krw Exp $	*/
/*	$NetBSD: aic7xxx_inline.h,v 1.4 2003/11/02 11:07:44 wiz Exp $	*/

/*
 * Inline routines shareable across OS platforms.
 *
 * Copyright (c) 1994-2001 Justin T. Gibbs.
 * Copyright (c) 2000-2001 Adaptec Inc.
 * 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,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
 *
 * //depot/aic7xxx/aic7xxx/aic7xxx_inline.h#39 $
 *
 * $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx_inline.h,v 1.20 2003/01/20 20:44:55 gibbs Exp $
 */
/*
 * Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
 */

#ifndef _AIC7XXX_INLINE_H_
#define _AIC7XXX_INLINE_H_

#ifdef SMALL_KERNEL
#define	IO_INLINE
#else
#define	IO_INLINE	static __inline
#define	IO_EXPAND
#endif

/************************* Sequencer Execution Control ************************/
IO_INLINE void ahc_pause_bug_fix(struct ahc_softc *ahc);
IO_INLINE int  ahc_is_paused(struct ahc_softc *ahc);
IO_INLINE void ahc_pause(struct ahc_softc *ahc);
IO_INLINE void ahc_unpause(struct ahc_softc *ahc);

#ifdef IO_EXPAND
/*
 * Work around any chip bugs related to halting sequencer execution.
 * On Ultra2 controllers, we must clear the CIOBUS stretch signal by
 * reading a register that will set this signal and deassert it.
 * Without this workaround, if the chip is paused, by an interrupt or
 * manual pause while accessing scb ram, accesses to certain registers
 * will hang the system (infinite pci retries).
 */
IO_INLINE void
ahc_pause_bug_fix(struct ahc_softc *ahc)
{
	if ((ahc->features & AHC_ULTRA2) != 0)
		(void)ahc_inb(ahc, CCSCBCTL);
}

/*
 * Determine whether the sequencer has halted code execution.
 * Returns non-zero status if the sequencer is stopped.
 */
IO_INLINE int
ahc_is_paused(struct ahc_softc *ahc)
{
	return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
}

/*
 * Request that the sequencer stop and wait, indefinitely, for it
 * to stop.  The sequencer will only acknowledge that it is paused
 * once it has reached an instruction boundary and PAUSEDIS is
 * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
 * for critical sections.
 */
IO_INLINE void
ahc_pause(struct ahc_softc *ahc)
{
	ahc_outb(ahc, HCNTRL, ahc->pause);

	/*
	 * Since the sequencer can disable pausing in a critical section, we
	 * must loop until it actually stops.
	 */
	while (ahc_is_paused(ahc) == 0)
		;

	ahc_pause_bug_fix(ahc);
}

/*
 * Allow the sequencer to continue program execution.
 * We check here to ensure that no additional interrupt
 * sources that would cause the sequencer to halt have been
 * asserted.  If, for example, a SCSI bus reset is detected
 * while we are fielding a different, pausing, interrupt type,
 * we don't want to release the sequencer before going back
 * into our interrupt handler and dealing with this new
 * condition.
 */
IO_INLINE void
ahc_unpause(struct ahc_softc *ahc)
{
	if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
		ahc_outb(ahc, HCNTRL, ahc->unpause);
}
#endif /* IO_EXPAND */

/*********************** Untagged Transaction Routines ************************/
IO_INLINE void	ahc_freeze_untagged_queues(struct ahc_softc *ahc);
IO_INLINE void	ahc_release_untagged_queues(struct ahc_softc *ahc);

#ifdef IO_EXPAND
/*
 * Block our completion routine from starting the next untagged
 * transaction for this target or target lun.
 */
IO_INLINE void
ahc_freeze_untagged_queues(struct ahc_softc *ahc)
{
	if ((ahc->flags & AHC_SCB_BTT) == 0)
		ahc->untagged_queue_lock++;
}

/*
 * Allow the next untagged transaction for this target or target lun
 * to be executed.  We use a counting semaphore to allow the lock
 * to be acquired recursively.  Once the count drops to zero, the
 * transaction queues will be run.
 */
IO_INLINE void
ahc_release_untagged_queues(struct ahc_softc *ahc)
{
	if ((ahc->flags & AHC_SCB_BTT) == 0) {
		ahc->untagged_queue_lock--;
		if (ahc->untagged_queue_lock == 0)
			ahc_run_untagged_queues(ahc);
	}
}
#endif /* IO_EXPAND */

/************************** Memory mapping routines ***************************/
IO_INLINE struct ahc_dma_seg *
			ahc_sg_bus_to_virt(struct scb *scb,
					   uint32_t sg_busaddr);
IO_INLINE uint32_t
			ahc_sg_virt_to_bus(struct scb *scb,
					   struct ahc_dma_seg *sg);
IO_INLINE uint32_t
			ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
IO_INLINE void		ahc_sync_scb(struct ahc_softc *ahc,
					   struct scb *scb, int op);
#ifdef AHC_TARGET_MODE
IO_INLINE uint32_t
			ahc_targetcmd_offset(struct ahc_softc *ahc,
					     u_int index);
#endif

#ifdef IO_EXPAND

IO_INLINE struct ahc_dma_seg *
ahc_sg_bus_to_virt(struct scb *scb, uint32_t sg_busaddr)
{
	int sg_index;

	sg_index = (sg_busaddr - scb->sg_list_phys)/sizeof(struct ahc_dma_seg);
	/* sg_list_phys points to entry 1, not 0 */
	sg_index++;

	return (&scb->sg_list[sg_index]);
}

IO_INLINE uint32_t
ahc_sg_virt_to_bus(struct scb *scb, struct ahc_dma_seg *sg)
{
	int sg_index;

	/* sg_list_phys points to entry 1, not 0 */
	sg_index = sg - &scb->sg_list[1];

	return (scb->sg_list_phys + (sg_index * sizeof(*scb->sg_list)));
}

IO_INLINE uint32_t
ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
{
	return (ahc->scb_data->hscb_busaddr
		+ (sizeof(struct hardware_scb) * index));
}

IO_INLINE void
ahc_sync_scb(struct ahc_softc *ahc, struct scb *scb, int op)
{
	ahc_dmamap_sync(ahc, ahc->parent_dmat,
			ahc->scb_data->hscb_dmamap,
			/*offset*/(scb->hscb - ahc->scb_data->hscbs) * sizeof(*scb->hscb),
			/*len*/sizeof(*scb->hscb), op);
}

#ifdef AHC_TARGET_MODE
IO_INLINE uint32_t
ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
{
	return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
}
#endif /* AHC_TARGET_MODE */
#endif /* IO_EXPAND */

/******************************** Debugging ***********************************/
static __inline char *ahc_name(struct ahc_softc *ahc);

static __inline char *
ahc_name(struct ahc_softc *ahc)
{
	return (ahc->name);
}

/*********************** Miscellaneous Support Functions ***********************/

IO_INLINE void	ahc_update_residual(struct ahc_softc *ahc,
					    struct scb *scb);
IO_INLINE struct ahc_initiator_tinfo *
			ahc_fetch_transinfo(struct ahc_softc *ahc,
					    char channel, u_int our_id,
					    u_int remote_id,
					    struct ahc_tmode_tstate **tstate);

IO_INLINE uint16_t
			ahc_inw(struct ahc_softc *ahc, u_int port);
IO_INLINE void	ahc_outw(struct ahc_softc *ahc, u_int port,
				 u_int value);
IO_INLINE uint32_t
			ahc_inl(struct ahc_softc *ahc, u_int port);
IO_INLINE void	ahc_outl(struct ahc_softc *ahc, u_int port,
				 uint32_t value);
IO_INLINE struct scb*
			ahc_get_scb(struct ahc_softc *ahc);
IO_INLINE void		ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
IO_INLINE struct scb *ahc_lookup_scb(struct ahc_softc *ahc, u_int tag);
IO_INLINE void		ahc_swap_with_next_hscb(struct ahc_softc *ahc,
						struct scb *scb);
IO_INLINE void		ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
IO_INLINE struct scsi_sense_data *
			ahc_get_sense_buf(struct ahc_softc *ahc,
					  struct scb *scb);
IO_INLINE uint32_t
			ahc_get_sense_bufaddr(struct ahc_softc *ahc,
					      struct scb *scb);

#ifdef IO_EXPAND

/*
 * Determine whether the sequencer reported a residual
 * for this SCB/transaction.
 */
IO_INLINE void
ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
{
	uint32_t sgptr;

	sgptr = aic_le32toh(scb->hscb->sgptr);
	if ((sgptr & SG_RESID_VALID) != 0)
		ahc_calc_residual(ahc, scb);
}

/*
 * Return pointers to the transfer negotiation information
 * for the specified our_id/remote_id pair.
 */
IO_INLINE struct ahc_initiator_tinfo *
ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
		    u_int remote_id, struct ahc_tmode_tstate **tstate)
{
	/*
	 * Transfer data structures are stored from the perspective
	 * of the target role.  Since the parameters for a connection
	 * in the initiator role to a given target are the same as
	 * when the roles are reversed, we pretend we are the target.
	 */
	if (channel == 'B')
		our_id += 8;
	*tstate = ahc->enabled_targets[our_id];
	return (&(*tstate)->transinfo[remote_id]);
}

IO_INLINE uint16_t
ahc_inw(struct ahc_softc *ahc, u_int port)
{
	return ((ahc_inb(ahc, port+1) << 8) | ahc_inb(ahc, port));
}

IO_INLINE void
ahc_outw(struct ahc_softc *ahc, u_int port, u_int value)
{
	ahc_outb(ahc, port, value & 0xFF);
	ahc_outb(ahc, port+1, (value >> 8) & 0xFF);
}

IO_INLINE uint32_t
ahc_inl(struct ahc_softc *ahc, u_int port)
{
	return ((ahc_inb(ahc, port))
	      | (ahc_inb(ahc, port+1) << 8)
	      | (ahc_inb(ahc, port+2) << 16)
	      | (ahc_inb(ahc, port+3) << 24));
}

IO_INLINE void
ahc_outl(struct ahc_softc *ahc, u_int port, uint32_t value)
{
	ahc_outb(ahc, port, (value) & 0xFF);
	ahc_outb(ahc, port+1, ((value) >> 8) & 0xFF);
	ahc_outb(ahc, port+2, ((value) >> 16) & 0xFF);
	ahc_outb(ahc, port+3, ((value) >> 24) & 0xFF);
}

/*
 * Get a free scb. If there are none, see if we can allocate a new SCB.
 */
IO_INLINE struct scb *
ahc_get_scb(struct ahc_softc *ahc)
{
	struct scb *scb;

	scb = SLIST_FIRST(&ahc->scb_data->free_scbs);

	if (scb != NULL)
		SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);

	return (scb);
}

/*
 * Return an SCB resource to the free list.
 */
IO_INLINE void
ahc_free_scb(struct ahc_softc *ahc, struct scb *scb)
{
	struct hardware_scb *hscb;

	hscb = scb->hscb;
	/* Clean up for the next user */
	ahc->scb_data->scbindex[hscb->tag] = NULL;
	scb->flags = SCB_FLAG_NONE;
	hscb->control = 0;

	SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links.sle);

	/* Notify the OSM that a resource is now available. */
	ahc_platform_scb_free(ahc, scb);
}

IO_INLINE struct scb *
ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
{
	struct scb* scb;

	scb = ahc->scb_data->scbindex[tag];
	if (scb != NULL)
		ahc_sync_scb(ahc, scb,
			     BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
	return (scb);
}

IO_INLINE void
ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
{
	struct hardware_scb *q_hscb;
	u_int  saved_tag;

	/*
	 * Our queuing method is a bit tricky.  The card
	 * knows in advance which HSCB to download, and we
	 * can't disappoint it.  To achieve this, the next
	 * SCB to download is saved off in ahc->next_queued_scb.
	 * When we are called to queue "an arbitrary scb",
	 * we copy the contents of the incoming HSCB to the one
	 * the sequencer knows about, swap HSCB pointers and
	 * finally assign the SCB to the tag indexed location
	 * in the scb_array.  This makes sure that we can still
	 * locate the correct SCB by SCB_TAG.
	 */
	q_hscb = ahc->next_queued_scb->hscb;
	saved_tag = q_hscb->tag;
	memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
	if ((scb->flags & SCB_CDB32_PTR) != 0) {
		q_hscb->shared_data.cdb_ptr =
		    aic_htole32(ahc_hscb_busaddr(ahc, q_hscb->tag)
			      + offsetof(struct hardware_scb, cdb32));
	}
	q_hscb->tag = saved_tag;
	q_hscb->next = scb->hscb->tag;

	/* Now swap HSCB pointers. */
	ahc->next_queued_scb->hscb = scb->hscb;
	scb->hscb = q_hscb;

	/* Now define the mapping from tag to SCB in the scbindex */
	ahc->scb_data->scbindex[scb->hscb->tag] = scb;
}

/*
 * Tell the sequencer about a new transaction to execute.
 */
IO_INLINE void
ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
{
	ahc_swap_with_next_hscb(ahc, scb);

	if (scb->hscb->tag == SCB_LIST_NULL
	 || scb->hscb->next == SCB_LIST_NULL)
		panic("Attempt to queue invalid SCB tag %x:%x",
		      scb->hscb->tag, scb->hscb->next);

	/*
	 * Setup data "oddness".
	 */
	scb->hscb->lun &= LID;
	if (ahc_get_transfer_length(scb) & 0x1)
		scb->hscb->lun |= SCB_XFERLEN_ODD;

	/*
	 * Keep a history of SCBs we've downloaded in the qinfifo.
	 */
	ahc->qinfifo[ahc->qinfifonext] = scb->hscb->tag;
	ahc_dmamap_sync(ahc, ahc->parent_dmat, ahc->shared_data_dmamap,
			/*offset*/ahc->qinfifonext+256, /*len*/1,
			BUS_DMASYNC_PREWRITE);
	ahc->qinfifonext++;

	/*
	 * Make sure our data is consistent from the
	 * perspective of the adapter.
	 */
	ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);

	/* Tell the adapter about the newly queued SCB */
	if ((ahc->features & AHC_QUEUE_REGS) != 0) {
		ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
	} else {
		if ((ahc->features & AHC_AUTOPAUSE) == 0)
			ahc_pause(ahc);
		ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
		if ((ahc->features & AHC_AUTOPAUSE) == 0)
			ahc_unpause(ahc);
	}
}

IO_INLINE struct scsi_sense_data *
ahc_get_sense_buf(struct ahc_softc *ahc, struct scb *scb)
{
	int offset;

	offset = scb - ahc->scb_data->scbarray;
	return (&ahc->scb_data->sense[offset]);
}

IO_INLINE uint32_t
ahc_get_sense_bufaddr(struct ahc_softc *ahc, struct scb *scb)
{
	int offset;

	offset = scb - ahc->scb_data->scbarray;
	return (ahc->scb_data->sense_busaddr
	      + (offset * sizeof(struct scsi_sense_data)));
}
#endif /* IO_EXPAND */

/************************** Interrupt Processing ******************************/
IO_INLINE void	ahc_sync_qoutfifo(struct ahc_softc *ahc, int op);
IO_INLINE void	ahc_sync_tqinfifo(struct ahc_softc *ahc, int op);
IO_INLINE u_int	ahc_check_cmdcmpltqueues(struct ahc_softc *ahc);
IO_INLINE int	ahc_intr(struct ahc_softc *ahc);

#ifdef IO_EXPAND
IO_INLINE void
ahc_sync_qoutfifo(struct ahc_softc *ahc, int op)
{
	ahc_dmamap_sync(ahc, ahc->parent_dmat, ahc->shared_data_dmamap,
			/*offset*/0, /*len*/256, op);
}

IO_INLINE void
ahc_sync_tqinfifo(struct ahc_softc *ahc, int op)
{
#ifdef AHC_TARGET_MODE
	if ((ahc->flags & AHC_TARGETROLE) != 0) {
	  ahc_dmamap_sync(ahc, ahc->parent_dmat /*shared_data_dmat*/,
				ahc->shared_data_dmamap,
				ahc_targetcmd_offset(ahc, 0),
				sizeof(struct target_cmd) * AHC_TMODE_CMDS,
				op);
	}
#endif
}

/*
 * See if the firmware has posted any completed commands
 * into our in-core command complete fifos.
 */
#define AHC_RUN_QOUTFIFO 0x1
#define AHC_RUN_TQINFIFO 0x2
IO_INLINE u_int
ahc_check_cmdcmpltqueues(struct ahc_softc *ahc)
{
	u_int retval;

	retval = 0;
	ahc_dmamap_sync(ahc, ahc->parent_dmat /*shared_data_dmat*/, ahc->shared_data_dmamap,
			/*offset*/ahc->qoutfifonext, /*len*/1,
			BUS_DMASYNC_POSTREAD);
	if (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL)
		retval |= AHC_RUN_QOUTFIFO;
#ifdef AHC_TARGET_MODE
	if ((ahc->flags & AHC_TARGETROLE) != 0
	    && (ahc->flags & AHC_TQINFIFO_BLOCKED) == 0) {
	  ahc_dmamap_sync(ahc, ahc->parent_dmat /*shared_data_dmat*/,
			  ahc->shared_data_dmamap,
			  ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
			  /*len*/sizeof(struct target_cmd),
			  BUS_DMASYNC_POSTREAD);
		if (ahc->targetcmds[ahc->tqinfifonext].cmd_valid != 0)
			retval |= AHC_RUN_TQINFIFO;
	}
#endif
	return (retval);
}

/*
 * Catch an interrupt from the adapter
 */
IO_INLINE int
ahc_intr(struct ahc_softc *ahc)
{
	u_int	intstat;

	if ((ahc->pause & INTEN) == 0) {
		/*
		 * Our interrupt is not enabled on the chip
		 * and may be disabled for re-entrancy reasons,
		 * so just return.  This is likely just a shared
		 * interrupt.
		 */
		return (0);
	}
	/*
	 * Instead of directly reading the interrupt status register,
	 * infer the cause of the interrupt by checking our in-core
	 * completion queues.  This avoids a costly PCI bus read in
	 * most cases.
	 */
	if ((ahc->flags & (AHC_ALL_INTERRUPTS|AHC_EDGE_INTERRUPT)) == 0
	    && (ahc_check_cmdcmpltqueues(ahc) != 0))
                intstat = CMDCMPLT;
        else {
                intstat = ahc_inb(ahc, INTSTAT);
        }

        if (intstat & CMDCMPLT) {
		ahc_outb(ahc, CLRINT, CLRCMDINT);

		/*
		 * Ensure that the chip sees that we've cleared
		 * this interrupt before we walk the output fifo.
		 * Otherwise, we may, due to posted bus writes,
		 * clear the interrupt after we finish the scan,
		 * and after the sequencer has added new entries
		 * and asserted the interrupt again.
		 */
		ahc_flush_device_writes(ahc);
		ahc_run_qoutfifo(ahc);
#ifdef AHC_TARGET_MODE
		if ((ahc->flags & AHC_TARGETROLE) != 0)
			ahc_run_tqinfifo(ahc, /*paused*/FALSE);
#endif
	}

	if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
		/* Hot eject */
		return 1;

	if ((intstat & INT_PEND) == 0) {
#if AHC_PCI_CONFIG > 0
		if (ahc->unsolicited_ints > 500) {
			ahc->unsolicited_ints = 0;
			if ((ahc->chip & AHC_PCI) != 0
			 && (ahc_inb(ahc, ERROR) & PCIERRSTAT) != 0)
				ahc->bus_intr(ahc);
		}
		ahc->unsolicited_ints++;
#endif
		return 0;
	}
	ahc->unsolicited_ints = 0;

	if (intstat & BRKADRINT) {
		ahc_handle_brkadrint(ahc);
		/* Fatal error, no more interrupts to handle. */
		return 1;
	}

	if ((intstat & (SEQINT|SCSIINT)) != 0)
		ahc_pause_bug_fix(ahc);

	if ((intstat & SEQINT) != 0)
		ahc_handle_seqint(ahc, intstat);

	if ((intstat & SCSIINT) != 0)
		ahc_handle_scsiint(ahc, intstat);

	return (1);
}

#endif	/* IO_EXPAND */

#endif  /* _AIC7XXX_INLINE_H_ */


code:
/*	$OpenBSD: aic7xxx_openbsd.c,v 1.54 2015/07/17 21:42:49 krw Exp $	*/
/*	$NetBSD: aic7xxx_osm.c,v 1.14 2003/11/02 11:07:44 wiz Exp $	*/

/*
 * Bus independent OpenBSD shim for the aic7xxx based adaptec SCSI controllers
 *
 * Copyright (c) 1994-2001 Justin T. Gibbs.
 * Copyright (c) 2001-2002 Steve Murphree, Jr.
 * 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,
 *    without modification.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU Public License ("GPL").
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 * //depot/aic7xxx/freebsd/dev/aic7xxx/aic7xxx_osm.c#12 $
 *
 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_osm.c,v 1.31 2002/11/30 19:08:58 scottl Exp $
 */
/*
 * Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
 */

#include <dev/ic/aic7xxx_openbsd.h>
#include <dev/ic/aic7xxx_inline.h>

#ifndef AHC_TMODE_ENABLE
#define AHC_TMODE_ENABLE 0
#endif

void	ahc_action(struct scsi_xfer *);
void	ahc_execute_scb(void *, bus_dma_segment_t *, int);
int	ahc_poll(struct ahc_softc *, int);
void	ahc_setup_data(struct ahc_softc *, struct scsi_xfer *, struct scb *);

void	ahc_minphys(struct buf *, struct scsi_link *);
void	ahc_adapter_req_set_xfer_mode(struct ahc_softc *, struct scb *);

struct cfdriver ahc_cd = {
	NULL, "ahc", DV_DULL
};

static struct scsi_adapter ahc_switch =
{
	ahc_action,
	ahc_minphys,
	0,
	0,
};

/*
 * Attach all the sub-devices we can find
 */
int
ahc_attach(struct ahc_softc *ahc)
{
	struct scsibus_attach_args saa;
	int s;

        s = splbio();

	/*
	 * fill in the prototype scsi_links.
	 */
	ahc->sc_channel.adapter_target = ahc->our_id;
	if (ahc->features & AHC_WIDE)
		ahc->sc_channel.adapter_buswidth = 16;
	ahc->sc_channel.adapter_softc = ahc;
	ahc->sc_channel.adapter = &ahc_switch;
	ahc->sc_channel.openings = 16;

	if (ahc->features & AHC_TWIN) {
		/* Configure the second scsi bus */
		ahc->sc_channel_b = ahc->sc_channel;
		ahc->sc_channel_b.adapter_target = ahc->our_id_b;
	}

#ifndef DEBUG
	if (bootverbose) {
		char ahc_info[256];
		ahc_controller_info(ahc, ahc_info, sizeof ahc_info);
		printf("%s: %s\n", ahc->sc_dev.dv_xname, ahc_info);
	}
#endif

	ahc_intr_enable(ahc, TRUE);

	if (ahc->flags & AHC_RESET_BUS_A)
		ahc_reset_channel(ahc, 'A', TRUE);
	if ((ahc->features & AHC_TWIN) && ahc->flags & AHC_RESET_BUS_B)
		ahc_reset_channel(ahc, 'B', TRUE);

	bzero(&saa, sizeof(saa));
	if ((ahc->flags & AHC_PRIMARY_CHANNEL) == 0) {
		saa.saa_sc_link = &ahc->sc_channel;
		ahc->sc_child = config_found((void *)&ahc->sc_dev,
		    &saa, scsiprint);
		if (ahc->features & AHC_TWIN) {
			saa.saa_sc_link = &ahc->sc_channel_b;
			ahc->sc_child_b = config_found((void *)&ahc->sc_dev,
			    &saa, scsiprint);
		}
	} else {
		if (ahc->features & AHC_TWIN) {
			saa.saa_sc_link = &ahc->sc_channel_b;
			ahc->sc_child = config_found((void *)&ahc->sc_dev,
			    &saa, scsiprint);
		}
		saa.saa_sc_link = &ahc->sc_channel;
		ahc->sc_child_b = config_found((void *)&ahc->sc_dev,
		    &saa, scsiprint);
	}

	splx(s);
	return (1);
}

/*
 * Catch an interrupt from the adapter
 */
int
ahc_platform_intr(void *arg)
{
	struct	ahc_softc *ahc = (struct ahc_softc *)arg;

	bus_dmamap_sync(ahc->parent_dmat, ahc->scb_data->hscb_dmamap,
	    0, ahc->scb_data->hscb_dmamap->dm_mapsize,
	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);

	return ahc_intr(ahc);
}

/*
 * We have an scb which has been processed by the
 * adaptor, now we look to see how the operation
 * went.
 */
void
ahc_done(struct ahc_softc *ahc, struct scb *scb)
{
	struct scsi_xfer *xs = scb->xs;
	int s;

	bus_dmamap_sync(ahc->parent_dmat, ahc->scb_data->hscb_dmamap,
	    0, ahc->scb_data->hscb_dmamap->dm_mapsize,
	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);

	LIST_REMOVE(scb, pending_links);
	if ((scb->flags & SCB_UNTAGGEDQ) != 0) {
		struct scb_tailq *untagged_q;
		int target_offset;

		target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
		untagged_q = &ahc->untagged_queues[target_offset];
		TAILQ_REMOVE(untagged_q, scb, links.tqe);
		scb->flags &= ~SCB_UNTAGGEDQ;
		ahc_run_untagged_queue(ahc, untagged_q);
	}

	timeout_del(&xs->stimeout);

	if (xs->datalen) {
		int op;

		if ((xs->flags & SCSI_DATA_IN) != 0)
			op = BUS_DMASYNC_POSTREAD;
		else
			op = BUS_DMASYNC_POSTWRITE;
		bus_dmamap_sync(ahc->parent_dmat, scb->dmamap, 0,
				scb->dmamap->dm_mapsize, op);
		bus_dmamap_unload(ahc->parent_dmat, scb->dmamap);
	}

	/* Translate the CAM status code to a SCSI error code. */
	switch (xs->error) {
	case CAM_SCSI_STATUS_ERROR:
	case CAM_REQ_INPROG:
	case CAM_REQ_CMP:
		switch (xs->status) {
		case SCSI_TASKSET_FULL:
		case SCSI_BUSY:
			xs->error = XS_BUSY;
			break;
		case SCSI_CHECK:
		case SCSI_TERMINATED:
			if ((scb->flags & SCB_SENSE) == 0) {
				/* CHECK on CHECK? */
				xs->error = XS_DRIVER_STUFFUP;
			} else
				xs->error = XS_NOERROR;
			break;
		default:
			xs->error = XS_NOERROR;
			break;
		}
		break;
	case CAM_REQUEUE_REQ:
	case CAM_BUSY:
		xs->error = XS_BUSY;
		break;
	case CAM_CMD_TIMEOUT:
		xs->error = XS_TIMEOUT;
		break;
	case CAM_BDR_SENT:
	case CAM_SCSI_BUS_RESET:
		xs->error = XS_RESET;
		break;
	case CAM_SEL_TIMEOUT:
		xs->error = XS_SELTIMEOUT;
		break;
	default:
		xs->error = XS_DRIVER_STUFFUP;
		break;
	}

	/* Don't clobber any existing error state */
	if (xs->error != XS_NOERROR) {
	  /* Don't clobber any existing error state */
	} else if ((scb->flags & SCB_SENSE) != 0) {
		/*
		 * We performed autosense retrieval.
		 *
		 * Zero any sense not transferred by the
		 * device.  The SCSI spec mandates that any
		 * untransferred data should be assumed to be
		 * zero.  Complete the 'bounce' of sense information
		 * through buffers accessible via bus-space by
		 * copying it into the clients csio.
		 */
		memset(&xs->sense, 0, sizeof(struct scsi_sense_data));
		memcpy(&xs->sense, ahc_get_sense_buf(ahc, scb),
		    aic_le32toh(scb->sg_list->len) & AHC_SG_LEN_MASK);
		xs->error = XS_SENSE;
	}

        s = splbio();
	ahc_free_scb(ahc, scb);
	scsi_done(xs);
        splx(s);
}

void
ahc_minphys(struct buf *bp, struct scsi_link *sl)
{
	/*
	 * Even though the card can transfer up to 16megs per command
	 * we are limited by the number of segments in the dma segment
	 * list that we can hold.  The worst case is that all pages are
	 * discontinuous physically, hence the "page per segment" limit
	 * enforced here.
	 */
	if (bp->b_bcount > ((AHC_NSEG - 1) * PAGE_SIZE)) {
		bp->b_bcount = ((AHC_NSEG - 1) * PAGE_SIZE);
	}
	minphys(bp);
}

void
ahc_action(struct scsi_xfer *xs)
{
	struct ahc_softc *ahc;
	struct scb *scb;
	struct hardware_scb *hscb;
	u_int target_id;
	u_int our_id;
	int s;

	SC_DEBUG(xs->sc_link, SDEV_DB3, ("ahc_action\n"));
	ahc = (struct ahc_softc *)xs->sc_link->adapter_softc;

	target_id = xs->sc_link->target;
	our_id = SCSI_SCSI_ID(ahc, xs->sc_link);

	/*
	 * get an scb to use.
	 */
	s = splbio();
	scb = ahc_get_scb(ahc);
	splx(s);
	if (scb == NULL) {
		xs->error = XS_NO_CCB;
		scsi_done(xs);
		return;
	}

	hscb = scb->hscb;

	SC_DEBUG(xs->sc_link, SDEV_DB3, ("start scb(%p)\n", scb));
	scb->xs = xs;
	timeout_set(&xs->stimeout, ahc_timeout, scb);

	/*
	 * Put all the arguments for the xfer in the scb
	 */
	hscb->control = 0;
	hscb->scsiid = BUILD_SCSIID(ahc, xs->sc_link, target_id, our_id);
	hscb->lun = xs->sc_link->lun;
	if (xs->xs_control & XS_CTL_RESET) {
		hscb->cdb_len = 0;
		scb->flags |= SCB_DEVICE_RESET;
		hscb->control |= MK_MESSAGE;
		ahc_execute_scb(scb, NULL, 0);
		return;
	}

	ahc_setup_data(ahc, xs, scb);
}

void
ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments)
{
	struct	scb *scb;
	struct	scsi_xfer *xs;
	struct	ahc_softc *ahc;
	struct	ahc_initiator_tinfo *tinfo;
	struct	ahc_tmode_tstate *tstate;

	u_int	mask;
	int	s;

	scb = (struct scb *)arg;
	xs = scb->xs;
	xs->error = CAM_REQ_INPROG;
	xs->status = 0;
	ahc = (struct ahc_softc *)xs->sc_link->adapter_softc;

	if (nsegments != 0) {
		struct	  ahc_dma_seg *sg;
		bus_dma_segment_t *end_seg;
		int op;

		end_seg = dm_segs + nsegments;

		/* Copy the segments into our SG list */
		sg = scb->sg_list;
		while (dm_segs < end_seg) {
			uint32_t len;

			sg->addr = aic_htole32(dm_segs->ds_addr);
			len = dm_segs->ds_len
			    | ((dm_segs->ds_addr >> 8) & 0x7F000000);
			sg->len = aic_htole32(len);
			sg++;
			dm_segs++;
		}

		/*
		 * Note where to find the SG entries in bus space.
		 * We also set the full residual flag which the
		 * sequencer will clear as soon as a data transfer
		 * occurs.
		 */
		scb->hscb->sgptr = aic_htole32(scb->sg_list_phys|SG_FULL_RESID);

		if ((xs->flags & SCSI_DATA_IN) != 0)
			op = BUS_DMASYNC_PREREAD;
		else
			op = BUS_DMASYNC_PREWRITE;

		bus_dmamap_sync(ahc->parent_dmat, scb->dmamap, 0,
				scb->dmamap->dm_mapsize, op);

		sg--;
		sg->len |= aic_htole32(AHC_DMA_LAST_SEG);

		bus_dmamap_sync(ahc->parent_dmat, scb->sg_map->sg_dmamap,
		    0, scb->sg_map->sg_dmamap->dm_mapsize,
		    BUS_DMASYNC_PREWRITE);

		/* Copy the first SG into the "current" data pointer area */
		scb->hscb->dataptr = scb->sg_list->addr;
		scb->hscb->datacnt = scb->sg_list->len;
	} else {
		scb->hscb->sgptr = aic_htole32(SG_LIST_NULL);
		scb->hscb->dataptr = 0;
		scb->hscb->datacnt = 0;
	}

	scb->sg_count = nsegments;

	s = splbio();

	tinfo = ahc_fetch_transinfo(ahc, SCSIID_CHANNEL(ahc, scb->hscb->scsiid),
				    SCSIID_OUR_ID(scb->hscb->scsiid),
				    SCSIID_TARGET(ahc, scb->hscb->scsiid),
				    &tstate);

	mask = SCB_GET_TARGET_MASK(ahc, scb);
	scb->hscb->scsirate = tinfo->scsirate;
	scb->hscb->scsioffset = tinfo->curr.offset;

	if ((tstate->ultraenb & mask) != 0)
		scb->hscb->control |= ULTRAENB;

	if ((tstate->discenable & mask) != 0)
		scb->hscb->control |= DISCENB;

	if ((tstate->auto_negotiate & mask) != 0) {
		scb->flags |= SCB_AUTO_NEGOTIATE;
		scb->hscb->control |= MK_MESSAGE;
	}

	if ((tstate->tagenable & mask) != 0)
		scb->hscb->control |= TAG_ENB;

	bus_dmamap_sync(ahc->parent_dmat, ahc->scb_data->hscb_dmamap,
	    0, ahc->scb_data->hscb_dmamap->dm_mapsize,
	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);

	LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);

	if (!(xs->flags & SCSI_POLL))
		timeout_add_msec(&xs->stimeout, xs->timeout);

	/*
	 * We only allow one untagged transaction
	 * per target in the initiator role unless
	 * we are storing a full busy target *lun*
	 * table in SCB space.
	 *
	 * This really should not be of any
	 * concern, as we take care to avoid this
	 * in ahc_done().  XXX smurph
	 */
	if ((scb->hscb->control & (TARGET_SCB|TAG_ENB)) == 0
	    && (ahc->flags & AHC_SCB_BTT) == 0) {
		struct scb_tailq *untagged_q;
		int target_offset;

		target_offset = SCB_GET_TARGET_OFFSET(ahc, scb);
		untagged_q = &(ahc->untagged_queues[target_offset]);
		TAILQ_INSERT_TAIL(untagged_q, scb, links.tqe);
		scb->flags |= SCB_UNTAGGEDQ;
		if (TAILQ_FIRST(untagged_q) != scb) {
			if (xs->flags & SCSI_POLL)
				goto poll;
			else {
				splx(s);
				return;
			}
		}
	}
	scb->flags |= SCB_ACTIVE;

	if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) {
		/* Define a mapping from our tag to the SCB. */
		ahc->scb_data->scbindex[scb->hscb->tag] = scb;
		ahc_pause(ahc);
		if ((ahc->flags & AHC_PAGESCBS) == 0)
			ahc_outb(ahc, SCBPTR, scb->hscb->tag);
		ahc_outb(ahc, TARG_IMMEDIATE_SCB, scb->hscb->tag);
		ahc_unpause(ahc);
	} else {
		ahc_queue_scb(ahc, scb);
	}

	if (!(xs->flags & SCSI_POLL)) {
		if (ahc->inited_target[xs->sc_link->target] == 0) {
			struct	ahc_devinfo devinfo;

			ahc_adapter_req_set_xfer_mode(ahc, scb);
			ahc_scb_devinfo(ahc, &devinfo, scb);
			ahc_update_neg_request(ahc, &devinfo, tstate, tinfo,
			    AHC_NEG_IF_NON_ASYNC);

			ahc->inited_target[xs->sc_link->target] = 1;
		}
		splx(s);
		return;
	}

	/*
	 * If we can't use interrupts, poll for completion
	 */
poll:
	SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_poll\n"));

	do {
		if (ahc_poll(ahc, xs->timeout)) {
			if (!(xs->flags & SCSI_SILENT))
				printf("cmd fail\n");
			ahc_timeout(scb);
			break;
		}
	} while (!(xs->flags & ITSDONE));

	splx(s);
}

int
ahc_poll(struct ahc_softc *ahc, int wait)
{
	while (--wait) {
		DELAY(1000);
		if (ahc_inb(ahc, INTSTAT) & INT_PEND)
			break;
	}

	if (wait == 0) {
		printf("%s: board is not responding\n", ahc_name(ahc));
		return (EIO);
	}

	ahc_intr((void *)ahc);
	return (0);
}

void
ahc_setup_data(struct ahc_softc *ahc, struct scsi_xfer *xs,
	       struct scb *scb)
{
	struct hardware_scb *hscb;
	int s;

	hscb = scb->hscb;
	xs->resid = xs->status = 0;
	xs->error = CAM_REQ_INPROG;

	hscb->cdb_len = xs->cmdlen;
	if (hscb->cdb_len > sizeof(hscb->cdb32)) {
		s = splbio();
		ahc_free_scb(ahc, scb);
		splx(s);
		xs->error = XS_DRIVER_STUFFUP;
		scsi_done(xs);
		return;
	}

	if (hscb->cdb_len > 12) {
		memcpy(hscb->cdb32, xs->cmd, hscb->cdb_len);
		scb->flags |= SCB_CDB32_PTR;
	} else {
		memcpy(hscb->shared_data.cdb, xs->cmd, hscb->cdb_len);
	}

	/* Only use S/G if there is a transfer */
	if (xs->datalen) {
		int error;

                error = bus_dmamap_load(ahc->parent_dmat,
					scb->dmamap, xs->data,
					xs->datalen, NULL,
					(xs->flags & SCSI_NOSLEEP) ?
					BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
		if (error) {
#ifdef AHC_DEBUG
                        printf("%s: in ahc_setup_data(): bus_dmamap_load() "
			       "= %d\n",
			       ahc_name(ahc), error);
#endif
			s = splbio();
			ahc_free_scb(ahc, scb);
			splx(s);
			xs->error = XS_DRIVER_STUFFUP;
			scsi_done(xs);
			return;
		}
		ahc_execute_scb(scb, scb->dmamap->dm_segs,
		    scb->dmamap->dm_nsegs);
	} else {
		ahc_execute_scb(scb, NULL, 0);
	}
}

void
ahc_timeout(void *arg)
{
	struct	scb *scb, *list_scb;
	struct	ahc_softc *ahc;
	int	s;
	int	found;
	char	channel;

	scb = (struct scb *)arg;
	ahc = (struct ahc_softc *)scb->xs->sc_link->adapter_softc;

	s = splbio();

#ifdef AHC_DEBUG
	printf("%s: SCB %d timed out\n", ahc_name(ahc), scb->hscb->tag);
	ahc_dump_card_state(ahc);
#endif

	ahc_pause(ahc);

	if (scb->flags & SCB_ACTIVE) {
		channel = SCB_GET_CHANNEL(ahc, scb);
		ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
		/*
		 * Go through all of our pending SCBs and remove
		 * any scheduled timeouts for them. They're about to be
		 * aborted so no need for them to timeout.
		 */
		LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) {
			if (list_scb->xs)
				timeout_del(&list_scb->xs->stimeout);
		}
		found = ahc_reset_channel(ahc, channel, /*Initiate Reset*/TRUE);
#ifdef AHC_DEBUG
		printf("%s: Issued Channel %c Bus Reset %d SCBs aborted\n",
		    ahc_name(ahc), channel, found);
#endif
	}

	ahc_unpause(ahc);
	splx(s);
}

void
ahc_platform_set_tags(struct ahc_softc *ahc,
		      struct ahc_devinfo *devinfo, int alg)
{
	struct ahc_tmode_tstate *tstate;

	ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
			    devinfo->target, &tstate);

	/* XXXX Need to check quirks before doing this! XXXX */

	switch (alg) {
	case AHC_QUEUE_BASIC:
	case AHC_QUEUE_TAGGED:
		tstate->tagenable |= devinfo->target_mask;
		break;
	case AHC_QUEUE_NONE:
		tstate->tagenable &= ~devinfo->target_mask;
		break;
	}
}

int
ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
{
	if (sizeof(struct ahc_platform_data) > 0) {
		ahc->platform_data = malloc(sizeof(struct ahc_platform_data),
		    M_DEVBUF, M_NOWAIT | M_ZERO);
		if (ahc->platform_data == NULL)
			return (ENOMEM);
	}

	return (0);
}

void
ahc_platform_free(struct ahc_softc *ahc)
{
	if (sizeof(struct ahc_platform_data) > 0)
		free(ahc->platform_data, M_DEVBUF, 0);
}

int
ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
{
	return (0);
}

void
ahc_send_async(struct ahc_softc *ahc, char channel, u_int target, u_int lun,
		ac_code code, void *opt_arg)
{
	/* Nothing to do here for OpenBSD */
}

void
ahc_adapter_req_set_xfer_mode(struct ahc_softc *ahc, struct scb *scb)
{
	struct ahc_initiator_tinfo *tinfo;
	struct ahc_tmode_tstate *tstate;
	struct ahc_syncrate *syncrate;
	struct ahc_devinfo devinfo;
	u_int16_t quirks;
	u_int width, ppr_options, period, offset;
	int s;

	s = splbio();

	ahc_scb_devinfo(ahc, &devinfo, scb);
	quirks = scb->xs->sc_link->quirks;
	tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
	    devinfo.our_scsiid, devinfo.target, &tstate);

	tstate->discenable |= (ahc->user_discenable & devinfo.target_mask);

	if (quirks & SDEV_NOTAGS)
		tstate->tagenable &= ~devinfo.target_mask;
	else if (ahc->user_tagenable & devinfo.target_mask)
		tstate->tagenable |= devinfo.target_mask;

	if (quirks & SDEV_NOWIDE)
		width = MSG_EXT_WDTR_BUS_8_BIT;
	else
		width = MSG_EXT_WDTR_BUS_16_BIT;

	ahc_validate_width(ahc, NULL, &width, ROLE_UNKNOWN);
	if (width > tinfo->user.width)
		width = tinfo->user.width;
	ahc_set_width(ahc, &devinfo, width, AHC_TRANS_GOAL, FALSE);

	if (quirks & SDEV_NOSYNC) {
		period = 0;
		offset = 0;
	} else {
		period = tinfo->user.period;
		offset = tinfo->user.offset;
	}

	/* XXX Look at saved INQUIRY flags for PPR capabilities XXX */
	ppr_options = tinfo->user.ppr_options;
	/* XXX Other reasons to avoid ppr? XXX */
	if (width < MSG_EXT_WDTR_BUS_16_BIT)
		ppr_options = 0;

	if ((tstate->discenable & devinfo.target_mask) == 0 ||
	    (tstate->tagenable & devinfo.target_mask) == 0)
		ppr_options &= ~MSG_EXT_PPR_PROT_IUS;

	syncrate = ahc_find_syncrate(ahc, &period, &ppr_options,
	    AHC_SYNCRATE_MAX);
	ahc_validate_offset(ahc, NULL, syncrate, &offset, width,
	    ROLE_UNKNOWN);

	if (offset == 0) {
		period = 0;
		ppr_options = 0;
	}

	if (ppr_options != 0 && tinfo->user.transport_version >= 3) {
		tinfo->goal.transport_version = tinfo->user.transport_version;
		tinfo->curr.transport_version = tinfo->user.transport_version;
	}

	ahc_set_syncrate(ahc, &devinfo, syncrate, period, offset, ppr_options,
	    AHC_TRANS_GOAL, FALSE);

	splx(s);
}


code:
/*	$OpenBSD: aic7xxx_openbsd.h,v 1.25 2015/07/17 21:42:49 krw Exp $	*/
/*	$NetBSD: aic7xxx_osm.h,v 1.7 2003/11/02 11:07:44 wiz Exp $	*/

/*
 * OpenBSD platform specific driver option settings, data structures,
 * function declarations and includes.
 *
 * Copyright (c) 1994-2001 Justin T. Gibbs.
 * Copyright (c) 2001-2002 Steve Murphree, Jr.
 * 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,
 *    without modification.
 * 2. The name of the author(s) may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU Public License ("GPL").
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
 *
 * //depot/aic7xxx/freebsd/dev/aic7xxx/aic7xxx_osm.h#14 $
 *
 * $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx_osm.h,v 1.20 2002/12/04 22:51:29 scottl Exp $
 */
/*
 * Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
 */

#ifndef _AIC7XXX_OPENBSD_H_
#define _AIC7XXX_OPENBSD_H_

#include "pci.h"		/* for config options */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/queue.h>

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

#include <machine/bus.h>
#include <machine/intr.h>

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

#include <uvm/uvm_extern.h>

#ifdef CAM_NEW_TRAN_CODE
#define AHC_NEW_TRAN_SETTINGS
#endif /* CAM_NEW_TRAN_CODE */

#if NPCI > 0
#define AHC_PCI_CONFIG 1
#endif

#if 0
#define AHC_DEBUG	AHC_SHOW_SENSE | AHC_SHOW_MISC | AHC_SHOW_CMDS
#endif

#ifdef DEBUG
#define bootverbose	1
#else
#define bootverbose	0
#endif
/****************************** Platform Macros *******************************/

#define	SCSI_IS_SCSIBUS_B(ahc, sc_link)	\
	((sc_link)->scsibus == (ahc)->sc_channel_b.scsibus)
#define	SCSI_SCSI_ID(ahc, sc_link)	\
	(SCSI_IS_SCSIBUS_B(ahc, sc_link) ? ahc->our_id_b : ahc->our_id)
#define	SCSI_CHANNEL(ahc, sc_link)	\
	(SCSI_IS_SCSIBUS_B(ahc, sc_link) ? 'B' : 'A')
#define BUILD_SCSIID(ahc, sc_link, target_id, our_id) \
        ((((target_id) << TID_SHIFT) & TID) | (our_id) \
        | (SCSI_IS_SCSIBUS_B(ahc, sc_link) ? TWIN_CHNLB : 0))

#ifndef offsetof
#define offsetof(type, member)  ((size_t)(&((type *)0)->member))
#endif
/************************* Forward Declarations *******************************/
typedef struct pci_attach_args * ahc_dev_softc_t;

/***************************** Bus Space/DMA **********************************/

/* XXX Need to update Bus DMA for partial map syncs */
#define ahc_dmamap_sync(ahc, dma_tag, dmamap, offset, len, op)		\
	bus_dmamap_sync(dma_tag, dmamap, offset, len, op)

/************************ Tunable Driver Parameters  **************************/
/*
 * The number of DMA segments supported.  The sequencer can handle any number
 * of physically contiguous S/G entries.  To reduce the driver's memory
 * consumption, we limit the number supported to be sufficient to handle
 * the largest mapping supported by the kernel, MAXPHYS.  Assuming the
 * transfer is as fragmented as possible and unaligned, this turns out to
 * be the number of paged sized transfers in MAXPHYS plus an extra element
 * to handle any unaligned residual.  The sequencer fetches SG elements
 * in cacheline sized chucks, so make the number per-transaction an even
 * multiple of 16 which should align us on even the largest of cacheline
 * boundaries.
 */
#define AHC_NSEG (roundup(atop(MAXPHYS) + 1, 16))

/* This driver supports target mode */
//#define AHC_TARGET_MODE 1

#include <dev/ic/aic7xxxvar.h>

/************************** Softc/SCB Platform Data ***************************/
struct ahc_platform_data {
};

struct scb_platform_data {
};

/*
 * Some ISA devices (e.g. on a VLB) can perform 32-bit DMA.  This
 * flag is passed to bus_dmamap_create() to indicate that fact.
 */
#ifndef ISABUS_DMA_32BIT
#define ISABUS_DMA_32BIT	BUS_DMA_BUS1
#endif

/************************** Timer DataStructures ******************************/
typedef struct timeout ahc_timer_t;

/***************************** Core Includes **********************************/
#if AHC_REG_PRETTY_PRINT
#define AIC_DEBUG_REGISTERS 1
#else
#define AIC_DEBUG_REGISTERS 0
#endif

/***************************** Timer Facilities *******************************/
void ahc_timeout(void*);

#define ahc_timer_init callout_init
#define ahc_timer_stop callout_stop

static __inline void
ahc_timer_reset(ahc_timer_t *timer, u_int usec, ahc_callback_t *func, void *arg)
{
	callout_reset(timer, (usec * hz)/1000000, func, arg);
}

static __inline void
ahc_scb_timer_reset(struct scb *scb, u_int usec)
{
	if (!(scb->xs->xs_control & XS_CTL_POLL)) {
		callout_reset(&scb->xs->xs_callout,
			      (usec * hz)/1000000, ahc_timeout, scb);
	}
}

/*************************** Device Access ************************************/
#define ahc_inb(ahc, port)				\
	bus_space_read_1((ahc)->tag, (ahc)->bsh, port)

#define ahc_outb(ahc, port, value)			\
	bus_space_write_1((ahc)->tag, (ahc)->bsh, port, value)

#define ahc_outsb(ahc, port, valp, count)		\
	bus_space_write_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count)

#define ahc_insb(ahc, port, valp, count)		\
	bus_space_read_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count)

static __inline void ahc_flush_device_writes(struct ahc_softc *);

static __inline void
ahc_flush_device_writes(struct ahc_softc *ahc)
{
	bus_space_barrier(ahc->tag, ahc->bsh, 0, 0x100,
	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
	ahc_inb(ahc, INTSTAT);
}

/**************************** Locking Primitives ******************************/

/****************************** OS Primitives *********************************/

/************************** Transaction Operations ****************************/
static __inline void ahc_set_transaction_status(struct scb *, uint32_t);
static __inline void ahc_set_scsi_status(struct scb *, uint32_t);
static __inline uint32_t ahc_get_transaction_status(struct scb *);
static __inline uint32_t ahc_get_scsi_status(struct scb *);
static __inline void ahc_set_transaction_tag(struct scb *, int, u_int);
static __inline u_long ahc_get_transfer_length(struct scb *);
static __inline int ahc_get_transfer_dir(struct scb *);
static __inline void ahc_set_residual(struct scb *, u_long);
static __inline void ahc_set_sense_residual(struct scb *, u_long);
static __inline u_long ahc_get_residual(struct scb *);
static __inline int ahc_perform_autosense(struct scb *);
static __inline uint32_t ahc_get_sense_bufsize(struct ahc_softc *,
    struct scb *);
static __inline void ahc_freeze_scb(struct scb *);
static __inline void ahc_platform_freeze_devq(struct ahc_softc *, struct scb *);
static __inline int  ahc_platform_abort_scbs(struct ahc_softc *, int, char,
    int, u_int, role_t, uint32_t);

static __inline
void ahc_set_transaction_status(struct scb *scb, uint32_t status)
{
	scb->xs->error = status;
}

static __inline
void ahc_set_scsi_status(struct scb *scb, uint32_t status)
{
	scb->xs->status = status;
}

static __inline
uint32_t ahc_get_transaction_status(struct scb *scb)
{
	if (scb->xs->flags & ITSDONE)
		return CAM_REQ_CMP;
	else
		return scb->xs->error;
}

static __inline
uint32_t ahc_get_scsi_status(struct scb *scb)
{
	return (scb->xs->status);
}

static __inline
void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
{
}

static __inline
u_long ahc_get_transfer_length(struct scb *scb)
{
	return (scb->xs->datalen);
}

static __inline
int ahc_get_transfer_dir(struct scb *scb)
{
	return (scb->xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT));
}

static __inline
void ahc_set_residual(struct scb *scb, u_long resid)
{
	scb->xs->resid = resid;
}

static __inline
void ahc_set_sense_residual(struct scb *scb, u_long resid)
{
	scb->xs->resid = resid;
}

static __inline
u_long ahc_get_residual(struct scb *scb)
{
	return (scb->xs->resid);
}

static __inline
int ahc_perform_autosense(struct scb *scb)
{
	/* Return true for OpenBSD */
	return (1);
}

static __inline uint32_t
ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
{
	return (sizeof(struct scsi_sense_data));
}

static __inline void
ahc_freeze_scb(struct scb *scb)
{
}

static __inline void
ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
{
}

static __inline int
ahc_platform_abort_scbs(struct ahc_softc *ahc, int target,
			char channel, int lun, u_int tag,
			role_t role, uint32_t status)
{
	return (0);
}

static __inline void
ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
{
	int s;

	s = splbio();

	if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0) {
		ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
	}

	timeout_del(&scb->xs->stimeout);

	splx(s);
}

/********************************** PCI ***************************************/
#ifdef AHC_PCI_CONFIG
static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t, int, int);
static __inline void	 ahc_pci_write_config(ahc_dev_softc_t, int, uint32_t,
    int);
static __inline int	 ahc_get_pci_function(ahc_dev_softc_t);
static __inline int	 ahc_get_pci_slot(ahc_dev_softc_t);
static __inline int	 ahc_get_pci_bus(ahc_dev_softc_t);

int			 ahc_pci_map_registers(struct ahc_softc *);
int			 ahc_pci_map_int(struct ahc_softc *);

static __inline uint32_t
ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
{
	return (pci_conf_read(pci->pa_pc, pci->pa_tag, reg));
}

static __inline void
ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
{
	pci_conf_write(pci->pa_pc, pci->pa_tag, reg, value);
}

static __inline int
ahc_get_pci_function(ahc_dev_softc_t pci)
{
	return (pci->pa_function);
}

static __inline int
ahc_get_pci_slot(ahc_dev_softc_t pci)
{
	return (pci->pa_device);
}

static __inline int
ahc_get_pci_bus(ahc_dev_softc_t pci)
{
	return (pci->pa_bus);
}

typedef enum
{
	AHC_POWER_STATE_D0,
	AHC_POWER_STATE_D1,
	AHC_POWER_STATE_D2,
	AHC_POWER_STATE_D3
} ahc_power_state;

void ahc_power_state_change(struct ahc_softc *, ahc_power_state);
#endif
/******************************** VL/EISA *************************************/
int aic7770_map_registers(struct ahc_softc *, u_int);
int aic7770_map_int(struct ahc_softc *, int);

/********************************* Debug **************************************/
static __inline void	ahc_print_path(struct ahc_softc *, struct scb *);
static __inline void	ahc_platform_dump_card_state(struct ahc_softc *);

static __inline void
ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
{
	sc_print_addr(scb->xs->sc_link);
}

static __inline void
ahc_platform_dump_card_state(struct ahc_softc *ahc)
{
	/* Nothing to do here for OpenBSD */
	printf("FEATURES = 0x%x, FLAGS = 0x%x, CHIP = 0x%x BUGS =0x%x\n",
	       ahc->features, ahc->flags, ahc->chip, ahc->bugs);
}
/**************************** Transfer Settings *******************************/
void	  ahc_notify_xfer_settings_change(struct ahc_softc *,
					  struct ahc_devinfo *);
void	  ahc_platform_set_tags(struct ahc_softc *, struct ahc_devinfo *, int);

/************************* Initialization/Teardown ****************************/
int	  ahc_platform_alloc(struct ahc_softc *, void *);
void	  ahc_platform_free(struct ahc_softc *);
int	  ahc_map_int(struct ahc_softc *);
int	  ahc_attach(struct ahc_softc *);
int	  ahc_softc_comp(struct ahc_softc *, struct ahc_softc *);
int	  ahc_detach(struct device *, int);

/****************************** Interrupts ************************************/
int                     ahc_platform_intr(void *);
static __inline void    ahc_platform_flushwork(struct ahc_softc *);
static __inline void
ahc_platform_flushwork(struct ahc_softc *ahc)
{
}

/************************ Misc Function Declarations **************************/
void	  ahc_done(struct ahc_softc *, struct scb *);
void	  ahc_send_async(struct ahc_softc *, char, u_int, u_int, ac_code,
    void *);

#endif  /* _AIC7XXX_OPENBSD_H_ */


code:
/*	$OpenBSD: aic7xxx_seeprom.c,v 1.7 2015/07/17 21:42:49 krw Exp $	*/
/*	$NetBSD: aic7xxx_seeprom.c,v 1.8 2003/05/02 19:12:19 dyoung Exp $	*/

/*
 * Product specific probe and attach routines for:
 *      3940, 2940, aic7895, aic7890, aic7880,
 *      aic7870, aic7860 and aic7850 SCSI controllers
 *
 * Copyright (c) 1994-2001 Justin T. Gibbs.
 * Copyright (c) 2000-2001 Adaptec Inc.
 * 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,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES.
 *
 * This file was originally split off from the PCI code by
 * Jason Thorpe <thorpej@netbsd.org>. This version was split off
 * from the FreeBSD source file aic7xxx_pci.c by Frank van der Linden
 * <fvdl@netbsd.org>
 *
 * $Id: aic7xxx_seeprom.c,v 1.7 2015/07/17 21:42:49 krw Exp $
 *
 * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.22 2003/01/20 20:44:55 gibbs Exp $
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <sys/reboot.h>		/* for AB_* needed by bootverbose */

#include <machine/intr.h>

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

#include <dev/ic/aic7xxx_openbsd.h>
#include <dev/ic/aic7xxx_inline.h>

#include <dev/ic/smc93cx6var.h>

#define DEVCONFIG	0x40
#define STPWLEVEL	0x00000002

static void configure_termination(struct ahc_softc *,
				  struct seeprom_descriptor *, u_int, u_int *);
static int verify_seeprom_cksum(struct seeprom_config *sc);

static void ahc_new_term_detect(struct ahc_softc *, int *, int *, int *,
				   int *, int *);
static void aic787X_cable_detect(struct ahc_softc *, int *, int *, int *,
				 int *);
static void aic785X_cable_detect(struct ahc_softc *, int *, int *, int *);
static void write_brdctl(struct ahc_softc *, u_int8_t);
static u_int8_t read_brdctl(struct ahc_softc *);
static void ahc_parse_pci_eeprom(struct ahc_softc *, struct seeprom_config *);

/*
 * Check the external port logic for a serial eeprom
 * and termination/cable detection contrls.
 */
void
ahc_check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
{
	struct	seeprom_descriptor sd;
	struct	seeprom_config *sc;
	int	have_seeprom;
	int	have_autoterm;

	sd.sd_tag = ahc->tag;
	sd.sd_bsh = ahc->bsh;
	sd.sd_regsize = 1;
	sd.sd_control_offset = SEECTL;
	sd.sd_status_offset = SEECTL;
	sd.sd_dataout_offset = SEECTL;
	sc = ahc->seep_config;

	/*
	 * For some multi-channel devices, the c46 is simply too
	 * small to work.  For the other controller types, we can
	 * get our information from either SEEPROM type.  Set the
	 * type to start our probe with accordingly.
	 */
	if (ahc->flags & AHC_LARGE_SEEPROM)
		sd.sd_chip = C56_66;
	else
		sd.sd_chip = C46;

	sd.sd_MS = SEEMS;
	sd.sd_RDY = SEERDY;
	sd.sd_CS = SEECS;
	sd.sd_CK = SEECK;
	sd.sd_DO = SEEDO;
	sd.sd_DI = SEEDI;

	have_seeprom = ahc_acquire_seeprom(ahc, &sd);
	if (have_seeprom) {

		if (bootverbose)
			printf("%s: Reading SEEPROM...", ahc_name(ahc));

		for (;;) {
			u_int start_addr;

			start_addr = 32 * (ahc->channel - 'A');
			have_seeprom = read_seeprom(&sd, (uint16_t *)sc,
							start_addr,
							sizeof(*sc)/2);

			if (have_seeprom)
				have_seeprom = verify_seeprom_cksum(sc);

			if (have_seeprom != 0 || sd.sd_chip == C56_66) {
				if (bootverbose) {
					if (have_seeprom == 0)
						printf ("checksum error\n");
					else
						printf ("done.\n");
				}
				break;
			}
			sd.sd_chip = C56_66;
		}
		ahc_release_seeprom(&sd);
	}

	if (!have_seeprom) {
		/*
		 * Pull scratch ram settings and treat them as
		 * if they are the contents of an seeprom if
		 * the 'ADPT' signature is found in SCB2.
		 * We manually compose the data as 16bit values
		 * to avoid endian issues.
		 */
		ahc_outb(ahc, SCBPTR, 2);
		if (ahc_inb(ahc, SCB_BASE) == 'A'
		 && ahc_inb(ahc, SCB_BASE + 1) == 'D'
		 && ahc_inb(ahc, SCB_BASE + 2) == 'P'
		 && ahc_inb(ahc, SCB_BASE + 3) == 'T') {
			uint16_t *sc_data;
			int	  i;

			sc_data = (uint16_t *)sc;
			for (i = 0; i < 32; i++, sc_data++) {
				int	j;

				j = i * 2;
				*sc_data = ahc_inb(ahc, SRAM_BASE + j)
					 | ahc_inb(ahc, SRAM_BASE + j + 1) << 8;
			}
			have_seeprom = verify_seeprom_cksum(sc);
			if (have_seeprom)
				ahc->flags |= AHC_SCB_CONFIG_USED;
		}
		/*
		 * Clear any SCB parity errors in case this data and
		 * its associated parity was not initialized by the BIOS
		 */
		ahc_outb(ahc, CLRINT, CLRPARERR);
		ahc_outb(ahc, CLRINT, CLRBRKADRINT);
	}

	if (!have_seeprom) {
		if (bootverbose)
			printf("%s: No SEEPROM available.\n", ahc_name(ahc));
		ahc->flags |= AHC_USEDEFAULTS | AHC_NO_BIOS_INIT;
		free(ahc->seep_config, M_DEVBUF, 0);
		ahc->seep_config = NULL;
		sc = NULL;
	} else {
		ahc_parse_pci_eeprom(ahc, sc);
	}

	/*
	 * Cards that have the external logic necessary to talk to
	 * a SEEPROM, are almost certain to have the remaining logic
	 * necessary for auto-termination control.  This assumption
	 * hasn't failed yet...
	 */
	have_autoterm = have_seeprom;

	/*
	 * Some low-cost chips have SEEPROM and auto-term control built
	 * in, instead of using a GAL.  They can tell us directly
	 * if the termination logic is enabled.
	 */
	if ((ahc->features & AHC_SPIOCAP) != 0) {
		if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
			have_autoterm = FALSE;
	}

	if (have_autoterm) {
		ahc_acquire_seeprom(ahc, &sd);
		configure_termination(ahc, &sd, sc->adapter_control, sxfrctl1);
		ahc_release_seeprom(&sd);
	} else if (have_seeprom) {
		*sxfrctl1 &= ~STPWEN;
		if ((sc->adapter_control & CFSTERM) != 0)
			*sxfrctl1 |= STPWEN;
		if (bootverbose)
			printf("%s: Low byte termination %sabled\n",
			       ahc_name(ahc),
			       (*sxfrctl1 & STPWEN) ? "en" : "dis");
	}
}

static void
ahc_parse_pci_eeprom(struct ahc_softc *ahc, struct seeprom_config *sc)
{
	/*
	 * Put the data we've collected down into SRAM
	 * where ahc_init will find it.
	 */
	int	 i;
	int	 max_targ = sc->max_targets & CFMAXTARG;
	u_int	 scsi_conf;
	uint16_t discenable;
	uint16_t ultraenb;

	discenable = 0;
	ultraenb = 0;
	if ((sc->adapter_control & CFULTRAEN) != 0) {
		/*
		 * Determine if this adapter has a "newstyle"
		 * SEEPROM format.
		 */
		for (i = 0; i < max_targ; i++) {
			if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0) {
				ahc->flags |= AHC_NEWEEPROM_FMT;
				break;
			}
		}
	}

	for (i = 0; i < max_targ; i++) {
		u_int     scsirate;
		uint16_t target_mask;

		target_mask = 0x01 << i;
		if (sc->device_flags[i] & CFDISC)
			discenable |= target_mask;
		if ((ahc->flags & AHC_NEWEEPROM_FMT) != 0) {
			if ((sc->device_flags[i] & CFSYNCHISULTRA) != 0)
				ultraenb |= target_mask;
		} else if ((sc->adapter_control & CFULTRAEN) != 0) {
			ultraenb |= target_mask;
		}
		if ((sc->device_flags[i] & CFXFER) == 0x04
		    && (ultraenb & target_mask) != 0) {
			/* Treat 10MHz as a non-ultra speed */
			sc->device_flags[i] &= ~CFXFER;
			ultraenb &= ~target_mask;
		}
		if ((ahc->features & AHC_ULTRA2) != 0) {
			u_int offset;

			if (sc->device_flags[i] & CFSYNCH)
				offset = MAX_OFFSET_ULTRA2;
			else
				offset = 0;
			ahc_outb(ahc, TARG_OFFSET + i, offset);

			/*
			 * The ultra enable bits contain the
			 * high bit of the ultra2 sync rate
			 * field.
			 */
			scsirate = (sc->device_flags[i] & CFXFER)
				 | ((ultraenb & target_mask) ? 0x8 : 0x0);
			if (sc->device_flags[i] & CFWIDEB)
				scsirate |= WIDEXFER;
		} else {
			scsirate = (sc->device_flags[i] & CFXFER) << 4;
			if (sc->device_flags[i] & CFSYNCH)
				scsirate |= SOFS;
			if (sc->device_flags[i] & CFWIDEB)
				scsirate |= WIDEXFER;
		}
		ahc_outb(ahc, TARG_SCSIRATE + i, scsirate);
	}
	ahc->our_id = sc->brtime_id & CFSCSIID;

	scsi_conf = (ahc->our_id & 0x7);
	if (sc->adapter_control & CFSPARITY)
		scsi_conf |= ENSPCHK;
	if (sc->adapter_control & CFRESETB)
		scsi_conf |= RESET_SCSI;

	ahc->flags |= (sc->adapter_control & CFBOOTCHAN) >> CFBOOTCHANSHIFT;

	if (sc->bios_control & CFEXTEND)
		ahc->flags |= AHC_EXTENDED_TRANS_A;

	if (sc->bios_control & CFBIOSEN)
		ahc->flags |= AHC_BIOS_ENABLED;
	if (ahc->features & AHC_ULTRA
	    && (ahc->flags & AHC_NEWEEPROM_FMT) == 0) {
		/* Should we enable Ultra mode? */
		if (!(sc->adapter_control & CFULTRAEN))
			/* Treat us as a non-ultra card */
			ultraenb = 0;
	}

	if (sc->signature == CFSIGNATURE
	    || sc->signature == CFSIGNATURE2) {
		uint32_t devconfig;

		/* Honor the STPWLEVEL settings */
		devconfig = pci_conf_read(ahc->bd->pc, ahc->bd->tag, DEVCONFIG);
		devconfig &= ~STPWLEVEL;
		if ((sc->bios_control & CFSTPWLEVEL) != 0)
			devconfig |= STPWLEVEL;
		pci_conf_write(ahc->bd->pc, ahc->bd->tag, DEVCONFIG,  devconfig);
	}
	/* Set SCSICONF info */
	ahc_outb(ahc, SCSICONF, scsi_conf);
	ahc_outb(ahc, DISC_DSB, ~(discenable & 0xff));
	ahc_outb(ahc, DISC_DSB + 1, ~((discenable >> 8) & 0xff));
	ahc_outb(ahc, ULTRA_ENB, ultraenb & 0xff);
	ahc_outb(ahc, ULTRA_ENB + 1, (ultraenb >> 8) & 0xff);
}

static void
configure_termination(struct ahc_softc *ahc,
		      struct seeprom_descriptor *sd,
		      u_int adapter_control,
		      u_int *sxfrctl1)
{
	uint8_t brddat;

	brddat = 0;

	/*
	 * Update the settings in sxfrctl1 to match the
	 * termination settings
	 */
	*sxfrctl1 = 0;

	/*
	 * SEECS must be on for the GALS to latch
	 * the data properly.  Be sure to leave MS
	 * on or we will release the seeprom.
	 */
	SEEPROM_OUTB(sd, sd->sd_MS | sd->sd_CS);
	if ((adapter_control & CFAUTOTERM) != 0
	 || (ahc->features & AHC_NEW_TERMCTL) != 0) {
		int internal50_present;
		int internal68_present;
		int externalcable_present;
		int eeprom_present;
		int enableSEC_low;
		int enableSEC_high;
		int enablePRI_low;
		int enablePRI_high;
		int sum;

		enableSEC_low = 0;
		enableSEC_high = 0;
		enablePRI_low = 0;
		enablePRI_high = 0;
		if ((ahc->features & AHC_NEW_TERMCTL) != 0) {
			ahc_new_term_detect(ahc, &enableSEC_low,
					    &enableSEC_high,
					    &enablePRI_low,
					    &enablePRI_high,
					    &eeprom_present);
			if ((adapter_control & CFSEAUTOTERM) == 0) {
				if (bootverbose)
					printf("%s: Manual SE Termination\n",
					       ahc_name(ahc));
				enableSEC_low = (adapter_control & CFSELOWTERM);
				enableSEC_high =
				    (adapter_control & CFSEHIGHTERM);
			}
			if ((adapter_control & CFAUTOTERM) == 0) {
				if (bootverbose)
					printf("%s: Manual LVD Termination\n",
					       ahc_name(ahc));
				enablePRI_low = (adapter_control & CFSTERM);
				enablePRI_high = (adapter_control & CFWSTERM);
			}
			/* Make the table calculations below happy */
			internal50_present = 0;
			internal68_present = 1;
			externalcable_present = 1;
		} else if ((ahc->features & AHC_SPIOCAP) != 0) {
			aic785X_cable_detect(ahc, &internal50_present,
					     &externalcable_present,
					     &eeprom_present);
			/* Can never support a wide connector. */
			internal68_present = 0;
		} else {
			aic787X_cable_detect(ahc, &internal50_present,
					     &internal68_present,
					     &externalcable_present,
					     &eeprom_present);
		}

		if ((ahc->features & AHC_WIDE) == 0)
			internal68_present = 0;

		if (bootverbose
		 && (ahc->features & AHC_ULTRA2) == 0) {
			printf("%s: internal 50 cable %s present",
			       ahc_name(ahc),
			       internal50_present ? "is":"not");

			if ((ahc->features & AHC_WIDE) != 0)
				printf(", internal 68 cable %s present",
				       internal68_present ? "is":"not");
			printf("\n%s: external cable %s present\n",
			       ahc_name(ahc),
			       externalcable_present ? "is":"not");
		}
		if (bootverbose)
			printf("%s: BIOS eeprom %s present\n",
			       ahc_name(ahc), eeprom_present ? "is" : "not");

		if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0) {
			/*
			 * The 50 pin connector is a separate bus,
			 * so force it to always be terminated.
			 * In the future, perform current sensing
			 * to determine if we are in the middle of
			 * a properly terminated bus.
			 */
			internal50_present = 0;
		}

		/*
		 * Now set the termination based on what
		 * we found.
		 * Flash Enable = BRDDAT7
		 * Secondary High Term Enable = BRDDAT6
		 * Secondary Low Term Enable = BRDDAT5 (7890)
		 * Primary High Term Enable = BRDDAT4 (7890)
		 */
		if ((ahc->features & AHC_ULTRA2) == 0
		 && (internal50_present != 0)
		 && (internal68_present != 0)
		 && (externalcable_present != 0)) {
			printf("%s: Illegal cable configuration!!. "
			       "Only two connectors on the "
			       "adapter may be used at a "
			       "time!\n", ahc_name(ahc));

			/*
			 * Pretend there are no cables in the hope
			 * that having all of the termination on
			 * gives us a more stable bus.
			 */
			internal50_present = 0;
			internal68_present = 0;
			externalcable_present = 0;
		}

		if ((ahc->features & AHC_WIDE) != 0
		 && ((externalcable_present == 0)
		  || (internal68_present == 0)
		  || (enableSEC_high != 0))) {
			brddat |= BRDDAT6;
			if (bootverbose) {
				if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
					printf("%s: 68 pin termination "
					       "Enabled\n", ahc_name(ahc));
				else
					printf("%s: %sHigh byte termination "
					       "Enabled\n", ahc_name(ahc),
					       enableSEC_high ? "Secondary "
							      : "");
			}
		}

		sum = internal50_present + internal68_present
		    + externalcable_present;
		if (sum < 2 || (enableSEC_low != 0)) {
			if ((ahc->features & AHC_ULTRA2) != 0)
				brddat |= BRDDAT5;
			else
				*sxfrctl1 |= STPWEN;
			if (bootverbose) {
				if ((ahc->flags & AHC_INT50_SPEEDFLEX) != 0)
					printf("%s: 50 pin termination "
					       "Enabled\n", ahc_name(ahc));
				else
					printf("%s: %sLow byte termination "
					       "Enabled\n", ahc_name(ahc),
					       enableSEC_low ? "Secondary "
							     : "");
			}
		}

		if (enablePRI_low != 0) {
			*sxfrctl1 |= STPWEN;
			if (bootverbose)
				printf("%s: Primary Low Byte termination "
				       "Enabled\n", ahc_name(ahc));
		}

		/*
		 * Setup STPWEN before setting up the rest of
		 * the termination per the tech note on the U160 cards.
		 */
		ahc_outb(ahc, SXFRCTL1, *sxfrctl1);

		if (enablePRI_high != 0) {
			brddat |= BRDDAT4;
			if (bootverbose)
				printf("%s: Primary High Byte "
				       "termination Enabled\n",
				       ahc_name(ahc));
		}

		write_brdctl(ahc, brddat);

	} else {
		if ((adapter_control & CFSTERM) != 0) {
			*sxfrctl1 |= STPWEN;

			if (bootverbose)
				printf("%s: %sLow byte termination Enabled\n",
				       ahc_name(ahc),
				       (ahc->features & AHC_ULTRA2) ? "Primary "
								    : "");
		}

		if ((adapter_control & CFWSTERM) != 0
		 && (ahc->features & AHC_WIDE) != 0) {
			brddat |= BRDDAT6;
			if (bootverbose)
				printf("%s: %sHigh byte termination Enabled\n",
				       ahc_name(ahc),
				       (ahc->features & AHC_ULTRA2)
				     ? "Secondary " : "");
		}

		/*
		 * Setup STPWEN before setting up the rest of
		 * the termination per the tech note on the U160 cards.
		 */
		ahc_outb(ahc, SXFRCTL1, *sxfrctl1);

		if ((ahc->features & AHC_WIDE) != 0)
			write_brdctl(ahc, brddat);
	}
	SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
}

static void
ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
		    int *enableSEC_high, int *enablePRI_low,
		    int *enablePRI_high, int *eeprom_present)
{
	uint8_t brdctl;

	/*
	 * BRDDAT7 = Eeprom
	 * BRDDAT6 = Enable Secondary High Byte termination
	 * BRDDAT5 = Enable Secondary Low Byte termination
	 * BRDDAT4 = Enable Primary high byte termination
	 * BRDDAT3 = Enable Primary low byte termination
	 */
	brdctl = read_brdctl(ahc);
	*eeprom_present = brdctl & BRDDAT7;
	*enableSEC_high = (brdctl & BRDDAT6);
	*enableSEC_low = (brdctl & BRDDAT5);
	*enablePRI_high = (brdctl & BRDDAT4);
	*enablePRI_low = (brdctl & BRDDAT3);
}

static void
aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
		     int *internal68_present, int *externalcable_present,
		     int *eeprom_present)
{
	uint8_t brdctl;

	/*
	 * First read the status of our cables.
	 * Set the rom bank to 0 since the
	 * bank setting serves as a multiplexor
	 * for the cable detection logic.
	 * BRDDAT5 controls the bank switch.
	 */
	write_brdctl(ahc, 0);

	/*
	 * Now read the state of the internal
	 * connectors.  BRDDAT6 is INT50 and
	 * BRDDAT7 is INT68.
	 */
	brdctl = read_brdctl(ahc);
	*internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
	*internal68_present = (brdctl & BRDDAT7) ? 0 : 1;

	/*
	 * Set the rom bank to 1 and determine
	 * the other signals.
	 */
	write_brdctl(ahc, BRDDAT5);

	/*
	 * Now read the state of the external
	 * connectors.  BRDDAT6 is EXT68 and
	 * BRDDAT7 is EPROMPS.
	 */
	brdctl = read_brdctl(ahc);
	*externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
	*eeprom_present = (brdctl & BRDDAT7) ? 1 : 0;
}

static void
aic785X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
		     int *externalcable_present, int *eeprom_present)
{
	uint8_t brdctl;
	uint8_t spiocap;

	spiocap = ahc_inb(ahc, SPIOCAP);
	spiocap &= ~SOFTCMDEN;
	spiocap |= EXT_BRDCTL;
	ahc_outb(ahc, SPIOCAP, spiocap);
	ahc_outb(ahc, BRDCTL, BRDRW|BRDCS);
	ahc_outb(ahc, BRDCTL, 0);
	brdctl = ahc_inb(ahc, BRDCTL);
	*internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
	*externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;

	*eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
}

int
ahc_acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
{
	int wait;

	if ((ahc->features & AHC_SPIOCAP) != 0
	    && (ahc_inb(ahc, SPIOCAP) & SEEPROM) == 0)
		return (0);

	/*
	 * Request access of the memory port.  When access is
	 * granted, SEERDY will go high.  We use a 1 second
	 * timeout which should be near 1 second more than
	 * is needed.  Reason: after the chip reset, there
	 * should be no contention.
	 */
	SEEPROM_OUTB(sd, sd->sd_MS);
	wait = 1000;  /* 1 second timeout in msec */
	while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
		aic_delay(1000);  /* delay 1 msec */
	}
	if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
		SEEPROM_OUTB(sd, 0);
		return (0);
	}
	return(1);
}

void
ahc_release_seeprom(struct seeprom_descriptor *sd)
{
	/* Release access to the memory port and the serial EEPROM. */
	SEEPROM_OUTB(sd, 0);
}

static void
write_brdctl(struct ahc_softc *ahc, uint8_t value)
{
	uint8_t brdctl;

	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
		brdctl = BRDSTB;
		if (ahc->channel == 'B')
			brdctl |= BRDCS;
	} else if ((ahc->features & AHC_ULTRA2) != 0) {
		brdctl = 0;
	} else {
		brdctl = BRDSTB|BRDCS;
	}
	ahc_outb(ahc, BRDCTL, brdctl);
	ahc_flush_device_writes(ahc);
	brdctl |= value;
	ahc_outb(ahc, BRDCTL, brdctl);
	ahc_flush_device_writes(ahc);
	if ((ahc->features & AHC_ULTRA2) != 0)
		brdctl |= BRDSTB_ULTRA2;
	else
		brdctl &= ~BRDSTB;
	ahc_outb(ahc, BRDCTL, brdctl);
	ahc_flush_device_writes(ahc);
	if ((ahc->features & AHC_ULTRA2) != 0)
		brdctl = 0;
	else
		brdctl &= ~BRDCS;
	ahc_outb(ahc, BRDCTL, brdctl);
}

static uint8_t
read_brdctl(ahc)
	struct	ahc_softc *ahc;
{
	uint8_t brdctl;
	uint8_t value;

	if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
		brdctl = BRDRW;
		if (ahc->channel == 'B')
			brdctl |= BRDCS;
	} else if ((ahc->features & AHC_ULTRA2) != 0) {
		brdctl = BRDRW_ULTRA2;
	} else {
		brdctl = BRDRW|BRDCS;
	}
	ahc_outb(ahc, BRDCTL, brdctl);
	ahc_flush_device_writes(ahc);
	value = ahc_inb(ahc, BRDCTL);
	ahc_outb(ahc, BRDCTL, 0);
	return (value);
}

static int
verify_seeprom_cksum(struct seeprom_config *sc)
{
	int i;
	int maxaddr;
	uint32_t checksum;
	uint16_t *scarray;

	maxaddr = (sizeof(*sc)/2) - 1;
	checksum = 0;
	scarray = (uint16_t *)sc;

	for (i = 0; i < maxaddr; i++)
		checksum = checksum + scarray[i];
	if (checksum == 0
	 || (checksum & 0xFFFF) != sc->checksum) {
		return (0);
	} else {
		return(1);
	}
}