summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
authorfvdl <fvdl@NetBSD.org>2003-04-19 19:33:29 +0000
committerfvdl <fvdl@NetBSD.org>2003-04-19 19:33:29 +0000
commitccadc2cbd333c9334b40eaf2f90ab7fa2d92eccc (patch)
treef134ddcc0f1dc5eef8568542fd0592fe55b7dae3 /sys/dev
parent8a26def233cbea5720c0754cc4af7c347de19b62 (diff)
Newer adaptec 7xxx driver. Written by Justin Gibbs for FreeBSD. Ported to
NetBSD by Pascal Renauld at Network Storage Solutions, Inc. Some modifications by me.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/ic/aic77xx.c9
-rw-r--r--sys/dev/ic/aic7xxx.c9361
-rw-r--r--sys/dev/ic/aic7xxx_cam.h125
-rw-r--r--sys/dev/ic/aic7xxx_inline.h667
-rw-r--r--sys/dev/ic/aic7xxx_osm.c1042
-rw-r--r--sys/dev/ic/aic7xxx_osm.h568
-rw-r--r--sys/dev/ic/aic7xxx_seeprom.c550
-rw-r--r--sys/dev/ic/aic7xxxvar.h1577
8 files changed, 9497 insertions, 4402 deletions
diff --git a/sys/dev/ic/aic77xx.c b/sys/dev/ic/aic77xx.c
index 224e6fa2171..94aed32ac88 100644
--- a/sys/dev/ic/aic77xx.c
+++ b/sys/dev/ic/aic77xx.c
@@ -1,4 +1,4 @@
-/* $NetBSD: aic77xx.c,v 1.2 2001/11/13 13:14:33 lukem Exp $ */
+/* $NetBSD: aic77xx.c,v 1.3 2003/04/19 19:33:29 fvdl Exp $ */
/*
* Common routines for AHA-27/284X and aic7770 motherboard SCSI controllers.
@@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: aic77xx.c,v 1.2 2001/11/13 13:14:33 lukem Exp $");
+__KERNEL_RCSID(0, "$NetBSD: aic77xx.c,v 1.3 2003/04/19 19:33:29 fvdl Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -44,8 +44,8 @@ __KERNEL_RCSID(0, "$NetBSD: aic77xx.c,v 1.2 2001/11/13 13:14:33 lukem Exp $");
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
-#include <dev/microcode/aic7xxx/aic7xxx_reg.h>
-#include <dev/ic/aic7xxxvar.h>
+#include <dev/ic/aic7xxx_osm.h>
+#include <dev/ic/aic7xxx_inline.h>
#include <dev/ic/aic77xxreg.h>
#include <dev/ic/aic77xxvar.h>
@@ -138,4 +138,3 @@ ahc_aic77xx_attach(ahc)
return 0;
}
-
diff --git a/sys/dev/ic/aic7xxx.c b/sys/dev/ic/aic7xxx.c
index 2844ab39cf3..f81ce9223bf 100644
--- a/sys/dev/ic/aic7xxx.c
+++ b/sys/dev/ic/aic7xxx.c
@@ -1,14 +1,8 @@
-/* $NetBSD: aic7xxx.c,v 1.95 2003/01/31 00:26:27 thorpej Exp $ */
-
/*
- * Generic driver for the aic7xxx based adaptec SCSI controllers
- * Product specific probe and attach routines can be found in:
- * i386/eisa/ahc_eisa.c 27/284X and aic7770 motherboard controllers
- * pci/ahc_pci.c 3985, 3980, 3940, 2940, aic7895, aic7890,
- * aic7880, aic7870, aic7860, and aic7850 controllers
- * cardbus/ahc_cardbus.c aic7860 cardbus controllers
+ * Core routines and tables shareable across OS platforms.
*
- * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 Justin T. Gibbs.
+ * Copyright (c) 1994-2002 Justin T. Gibbs.
+ * Copyright (c) 2000-2002 Adaptec Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -17,142 +11,51 @@
* 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.
+ * 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
- * the GNU Public License ("GPL").
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
*
- * 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
+ * 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 DAMAGE.
- *
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.42 2000/03/18 22:28:18 gibbs Exp $
- */
-/*
- * A few notes on features of the driver.
+ * 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.
*
- * SCB paging takes advantage of the fact that devices stay disconnected
- * from the bus a relatively long time and that while they're disconnected,
- * having the SCBs for these transactions down on the host adapter is of
- * little use. Instead of leaving this idle SCB down on the card we copy
- * it back up into kernel memory and reuse the SCB slot on the card to
- * schedule another transaction. This can be a real payoff when doing random
- * I/O to tagged queueing devices since there are more transactions active at
- * once for the device to sort for optimal seek reduction. The algorithm goes
- * like this...
+ * $Id: aic7xxx.c,v 1.96 2003/04/19 19:33:29 fvdl Exp $
*
- * The sequencer maintains two lists of its hardware SCBs. The first is the
- * singly linked free list which tracks all SCBs that are not currently in
- * use. The second is the doubly linked disconnected list which holds the
- * SCBs of transactions that are in the disconnected state sorted most
- * recently disconnected first. When the kernel queues a transaction to
- * the card, a hardware SCB to "house" this transaction is retrieved from
- * either of these two lists. If the SCB came from the disconnected list,
- * a check is made to see if any data transfer or SCB linking (more on linking
- * in a bit) information has been changed since it was copied from the host
- * and if so, DMAs the SCB back up before it can be used. Once a hardware
- * SCB has been obtained, the SCB is DMAed from the host. Before any work
- * can begin on this SCB, the sequencer must ensure that either the SCB is
- * for a tagged transaction or the target is not already working on another
- * non-tagged transaction. If a conflict arises in the non-tagged case, the
- * sequencer finds the SCB for the active transactions and sets the SCB_LINKED
- * field in that SCB to this next SCB to execute. To facilitate finding
- * active non-tagged SCBs, the last four bytes of up to the first four hardware
- * SCBs serve as a storage area for the currently active SCB ID for each
- * target.
- *
- * When a device reconnects, a search is made of the hardware SCBs to find
- * the SCB for this transaction. If the search fails, a hardware SCB is
- * pulled from either the free or disconnected SCB list and the proper
- * SCB is DMAed from the host. If the MK_MESSAGE control bit is set
- * in the control byte of the SCB while it was disconnected, the sequencer
- * will assert ATN and attempt to issue a message to the host.
- *
- * When a command completes, a check for non-zero status and residuals is
- * made. If either of these conditions exists, the SCB is DMAed back up to
- * the host so that it can interpret this information. Additionally, in the
- * case of bad status, the sequencer generates a special interrupt and pauses
- * itself. This allows the host to setup a request sense command if it
- * chooses for this target synchronously with the error so that sense
- * information isn't lost.
+ * //depot/aic7xxx/aic7xxx/aic7xxx.c#112 $
*
+ * $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx.c,v 1.88 2003/01/20 20:44:55 gibbs Exp $
+ */
+/*
+ * Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
*/
-#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: aic7xxx.c,v 1.95 2003/01/31 00:26:27 thorpej Exp $");
-
-#include "opt_ddb.h"
-#include "opt_ahc.h"
-
-#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/proc.h>
-#include <sys/scsiio.h>
-
-#include <machine/bus.h>
-#include <machine/intr.h>
-
-#include <dev/scsipi/scsi_all.h>
-#include <dev/scsipi/scsipi_all.h>
-#include <dev/scsipi/scsi_message.h>
-#include <dev/scsipi/scsipi_debug.h>
-#include <dev/scsipi/scsiconf.h>
-
-#include <uvm/uvm_extern.h>
+#include <dev/ic/aic7xxx_osm.h>
+#include <dev/ic/aic7xxx_inline.h>
+#include <dev/ic/aic7xxx_cam.h>
-#include <dev/ic/aic7xxxvar.h>
-#include <dev/microcode/aic7xxx/sequencer.h>
-#include <dev/microcode/aic7xxx/aic7xxx_reg.h>
-#include <dev/microcode/aic7xxx/aic7xxx_seq.h>
+/****************************** Softc Data ************************************/
+struct ahc_softc_tailq ahc_tailq = TAILQ_HEAD_INITIALIZER(ahc_tailq);
-#define ALL_CHANNELS '\0'
-#define ALL_TARGETS_MASK 0xFFFF
-#define INITIATOR_WILDCARD (~0)
-
-#define SIM_IS_SCSIBUS_B(ahc, periph) \
- ((periph)->periph_channel->chan_channel == 1)
-#define SIM_CHANNEL(ahc, periph) \
- (SIM_IS_SCSIBUS_B(ahc, periph) ? 'B' : 'A')
-#define SIM_SCSI_ID(ahc, periph) \
- (SIM_IS_SCSIBUS_B(ahc, periph) ? ahc->our_id_b : ahc->our_id)
-#define SCB_IS_SCSIBUS_B(scb) \
- (((scb)->hscb->tcl & SELBUSB) != 0)
-#define SCB_TARGET(scb) \
- (((scb)->hscb->tcl & TID) >> 4)
-#define SCB_CHANNEL(scb) \
- (SCB_IS_SCSIBUS_B(scb) ? 'B' : 'A')
-#define SCB_LUN(scb) \
- ((scb)->hscb->tcl & LID)
-#define SCB_TARGET_OFFSET(scb) \
- (SCB_TARGET(scb) + (SCB_IS_SCSIBUS_B(scb) ? 8 : 0))
-#define SCB_TARGET_MASK(scb) \
- (0x01 << (SCB_TARGET_OFFSET(scb)))
-#define TCL_CHANNEL(ahc, tcl) \
- ((((ahc)->features & AHC_TWIN) && ((tcl) & SELBUSB)) ? 'B' : 'A')
-#define TCL_SCSI_ID(ahc, tcl) \
- (TCL_CHANNEL((ahc), (tcl)) == 'B' ? (ahc)->our_id_b : (ahc)->our_id)
-#define TCL_TARGET(tcl) (((tcl) & TID) >> TCL_TARGET_SHIFT)
-#define TCL_LUN(tcl) ((tcl) & LID)
-
-#define XS_TCL(ahc, xs) \
- ((((xs)->xs_periph->periph_target << 4) & 0xF0) \
- | (SIM_IS_SCSIBUS_B((ahc), (xs)->xs_periph) ? SELBUSB : 0) \
- | ((xs)->xs_periph->periph_lun & 0x07))
-
-const char * const ahc_chip_names[] =
+/***************************** Lookup Tables **********************************/
+char *ahc_chip_names[] =
{
"NONE",
"aic7770",
@@ -162,593 +65,24 @@ const char * const ahc_chip_names[] =
"aic7860",
"aic7870",
"aic7880",
- "aic7890/91",
- "aic7892",
"aic7895",
+ "aic7895C",
+ "aic7890/91",
"aic7896/97",
+ "aic7892",
"aic7899"
};
-
-typedef enum {
- ROLE_UNKNOWN,
- ROLE_INITIATOR,
- ROLE_TARGET
-} role_t;
-
-struct ahc_devinfo {
- int our_scsiid;
- int target_offset;
- u_int16_t target_mask;
- u_int8_t target;
- u_int8_t lun;
- char channel;
- role_t role; /*
- * Only guaranteed to be correct if not
- * in the busfree state.
- */
-};
-
-typedef enum {
- SEARCH_COMPLETE,
- SEARCH_COUNT,
- SEARCH_REMOVE
-} ahc_search_action;
-
-#ifdef AHC_DEBUG
-static int ahc_debug = AHC_DEBUG;
-#endif
-
-static int ahcinitscbdata(struct ahc_softc *);
-static void ahcfiniscbdata(struct ahc_softc *);
-
-#if UNUSED
-static void ahc_dump_targcmd(struct target_cmd *);
-#endif
-static void ahc_shutdown(void *arg);
-static void ahc_action(struct scsipi_channel *,
- scsipi_adapter_req_t, void *);
-static int ahc_ioctl(struct scsipi_channel *, u_long, caddr_t, int,
- struct proc *);
-static void ahc_execute_scb(void *, bus_dma_segment_t *, int);
-static int ahc_poll(struct ahc_softc *, int);
-static void ahc_setup_data(struct ahc_softc *, struct scsipi_xfer *,
- struct scb *);
-static void ahc_freeze_devq(struct ahc_softc *, struct scsipi_periph *);
-static void ahcallocscbs(struct ahc_softc *);
-#if UNUSED
-static void ahc_scb_devinfo(struct ahc_softc *, struct ahc_devinfo *,
- struct scb *);
-#endif
-static void ahc_fetch_devinfo(struct ahc_softc *, struct ahc_devinfo *);
-static void ahc_compile_devinfo(struct ahc_devinfo *, u_int, u_int, u_int,
- char, role_t);
-static u_int ahc_abort_wscb(struct ahc_softc *, u_int, u_int);
-static void ahc_done(struct ahc_softc *, struct scb *);
-static struct tmode_tstate *
- ahc_alloc_tstate(struct ahc_softc *, u_int, char);
-#if UNUSED
-static void ahc_free_tstate(struct ahc_softc *, u_int, char, int);
-#endif
-static void ahc_handle_seqint(struct ahc_softc *, u_int);
-static void ahc_handle_scsiint(struct ahc_softc *, u_int);
-static void ahc_build_transfer_msg(struct ahc_softc *,
- struct ahc_devinfo *);
-static void ahc_setup_initiator_msgout(struct ahc_softc *,
- struct ahc_devinfo *,
- struct scb *);
-static void ahc_setup_target_msgin(struct ahc_softc *,
- struct ahc_devinfo *);
-static void ahc_clear_msg_state(struct ahc_softc *);
-static void ahc_handle_message_phase(struct ahc_softc *,
- struct scsipi_periph *);
-static int ahc_sent_msg(struct ahc_softc *, u_int, int);
-
-static int ahc_parse_msg(struct ahc_softc *, struct scsipi_periph *,
- struct ahc_devinfo *);
-static void ahc_handle_ign_wide_residue(struct ahc_softc *,
- struct ahc_devinfo *);
-static void ahc_handle_devreset(struct ahc_softc *, struct ahc_devinfo *,
- int, char *, int);
-#ifdef AHC_DUMP_SEQ
-static void ahc_dumpseq(struct ahc_softc *);
-#endif
-static void ahc_loadseq(struct ahc_softc *);
-static int ahc_check_patch(struct ahc_softc *, const struct patch **,
- int, int *);
-static void ahc_download_instr(struct ahc_softc *, int, u_int8_t *);
-static int ahc_match_scb(struct scb *, int, char, int, u_int, role_t);
-#if defined(AHC_DEBUG)
-static void ahc_print_scb(struct scb *);
-#endif
-static int ahc_search_qinfifo(struct ahc_softc *, int, char, int, u_int,
- role_t, scb_flag, ahc_search_action);
-static int ahc_reset_channel(struct ahc_softc *, char, int);
-static int ahc_abort_scbs(struct ahc_softc *, int, char, int, u_int,
- role_t, int);
-static int ahc_search_disc_list(struct ahc_softc *, int,
- char, int, u_int, int, int, int);
-static u_int ahc_rem_scb_from_disc_list(struct ahc_softc *, u_int, u_int);
-static void ahc_add_curscb_to_free_list(struct ahc_softc *);
-static void ahc_clear_intstat(struct ahc_softc *);
-static void ahc_reset_current_bus(struct ahc_softc *);
-static const struct ahc_syncrate *
- ahc_devlimited_syncrate(struct ahc_softc *, u_int *);
-static const struct ahc_syncrate *
- ahc_find_syncrate(struct ahc_softc *, u_int *, u_int);
-static u_int ahc_find_period(struct ahc_softc *, u_int, u_int);
-static void ahc_validate_offset(struct ahc_softc *,
- const struct ahc_syncrate *, u_int *, int);
-static void ahc_update_target_msg_request(struct ahc_softc *,
- struct ahc_devinfo *,
- struct ahc_initiator_tinfo *,
- int, int);
-static void ahc_set_syncrate(struct ahc_softc *, struct ahc_devinfo *,
- const struct ahc_syncrate *, u_int, u_int,
- u_int, int, int);
-static void ahc_set_width(struct ahc_softc *, struct ahc_devinfo *,
- u_int, u_int, int, int);
-static void ahc_set_tags(struct ahc_softc *, struct ahc_devinfo *, int);
-static void ahc_update_xfer_mode(struct ahc_softc *, struct ahc_devinfo *);
-static void ahc_construct_sdtr(struct ahc_softc *, u_int, u_int);
-
-static void ahc_construct_wdtr(struct ahc_softc *, u_int);
-
-static void ahc_calc_residual(struct scb *);
-
-static void ahc_update_pending_syncrates(struct ahc_softc *);
-
-static void ahc_set_recoveryscb(struct ahc_softc *, struct scb *);
-
-static void ahc_timeout (void *);
-static __inline int sequencer_paused(struct ahc_softc *);
-static __inline void pause_sequencer(struct ahc_softc *);
-static __inline void unpause_sequencer(struct ahc_softc *);
-static void restart_sequencer(struct ahc_softc *);
-static __inline u_int ahc_index_busy_tcl(struct ahc_softc *, u_int, int);
-
-static __inline void ahc_busy_tcl(struct ahc_softc *, struct scb *);
-static __inline int ahc_isbusy_tcl(struct ahc_softc *, struct scb *);
-
-static __inline void ahc_freeze_ccb(struct scb *);
-static __inline void ahcsetccbstatus(struct scsipi_xfer *, int);
-static void ahc_run_qoutfifo(struct ahc_softc *);
-
-static __inline struct ahc_initiator_tinfo *
- ahc_fetch_transinfo(struct ahc_softc *,
- char, u_int, u_int,
- struct tmode_tstate **);
-static void ahcfreescb(struct ahc_softc *, struct scb *);
-static __inline struct scb *ahcgetscb(struct ahc_softc *);
-
-static int ahc_createdmamem(bus_dma_tag_t, int, int, bus_dmamap_t *,
- caddr_t *, bus_addr_t *, bus_dma_segment_t *,
- int *, const char *, const char *);
-static void ahc_freedmamem(bus_dma_tag_t, int, bus_dmamap_t,
- caddr_t, bus_dma_segment_t *, int);
-static void ahcminphys(struct buf *);
-
-static __inline void ahc_swap_hscb(struct hardware_scb *);
-static __inline void ahc_swap_sg(struct ahc_dma_seg *);
-static int ahc_istagged_device(struct ahc_softc *, struct scsipi_xfer *, int);
-
-#if defined(AHC_DEBUG) && 0
-static void ahc_dumptinfo(struct ahc_softc *, struct ahc_initiator_tinfo *);
-#endif
-
-static __inline void
-ahc_swap_hscb(struct hardware_scb *hscb)
-{
- hscb->SG_pointer = htole32(hscb->SG_pointer);
- hscb->data = htole32(hscb->data);
- hscb->datalen = htole32(hscb->datalen);
- /*
- * No need to swap cmdpointer; it's either 0 or set to
- * cmdstore_busaddr, which is already swapped.
- */
-}
-
-static __inline void
-ahc_swap_sg(struct ahc_dma_seg *sg)
-{
- sg->addr = htole32(sg->addr);
- sg->len = htole32(sg->len);
-}
-
-static void
-ahcminphys(bp)
- struct buf *bp;
-{
-/*
- * 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, hense the "page per segment" limit
- * enforced here.
- */
- if (bp->b_bcount > AHC_MAXTRANSFER_SIZE) {
- bp->b_bcount = AHC_MAXTRANSFER_SIZE;
- }
- minphys(bp);
-}
-
-
-static __inline u_int32_t
-ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
-{
- return (ahc->scb_data->hscb_busaddr
- + (sizeof(struct hardware_scb) * index));
-}
-
-#define AHC_BUSRESET_DELAY 25 /* Reset delay in us */
-
-static __inline int
-sequencer_paused(struct ahc_softc *ahc)
-{
- return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
-}
-
-static __inline void
-pause_sequencer(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 (sequencer_paused(ahc) == 0)
- ;
-}
-
-static __inline void
-unpause_sequencer(struct ahc_softc *ahc)
-{
- if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
- ahc_outb(ahc, HCNTRL, ahc->unpause);
-}
-
-/*
- * Restart the sequencer program from address zero
- */
-static void
-restart_sequencer(struct ahc_softc *ahc)
-{
- u_int i;
-
- pause_sequencer(ahc);
-
- /*
- * Everytime we restart the sequencer, there
- * is the possiblitity that we have restarted
- * within a three instruction window where an
- * SCB has been marked free but has not made it
- * onto the free list. Since SCSI events(bus reset,
- * unexpected bus free) will always freeze the
- * sequencer, we cannot close this window. To
- * avoid losing an SCB, we reconsitute the free
- * list every time we restart the sequencer.
- */
- ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
- for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
-
- ahc_outb(ahc, SCBPTR, i);
- if (ahc_inb(ahc, SCB_TAG) == SCB_LIST_NULL)
- ahc_add_curscb_to_free_list(ahc);
- }
- ahc_outb(ahc, SEQCTL, FASTMODE|SEQRESET);
- unpause_sequencer(ahc);
-}
-
-static __inline u_int
-ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl, int unbusy)
-{
- u_int scbid;
-
- scbid = ahc->untagged_scbs[tcl];
- if (unbusy) {
- ahc->untagged_scbs[tcl] = SCB_LIST_NULL;
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap,
- UNTAGGEDSCB_OFFSET * 256, 256, BUS_DMASYNC_PREWRITE);
- }
-
- return (scbid);
-}
-
-static __inline void
-ahc_busy_tcl(struct ahc_softc *ahc, struct scb *scb)
-{
- ahc->untagged_scbs[scb->hscb->tcl] = scb->hscb->tag;
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap,
- UNTAGGEDSCB_OFFSET * 256, 256, BUS_DMASYNC_PREWRITE);
-}
-
-static __inline int
-ahc_isbusy_tcl(struct ahc_softc *ahc, struct scb *scb)
-{
- return ahc->untagged_scbs[scb->hscb->tcl] != SCB_LIST_NULL;
-}
-
-static __inline void
-ahc_freeze_ccb(struct scb *scb)
-{
- struct scsipi_xfer *xs = scb->xs;
-
- if (!(scb->flags & SCB_FREEZE_QUEUE)) {
- scsipi_periph_freeze(xs->xs_periph, 1);
- scb->flags |= SCB_FREEZE_QUEUE;
- }
-}
-
-static __inline void
-ahcsetccbstatus(struct scsipi_xfer *xs, int status)
-{
- xs->error = status;
-}
-
-static __inline struct ahc_initiator_tinfo *
-ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
- u_int remote_id, struct 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]);
-}
-
-static void
-ahc_run_qoutfifo(struct ahc_softc *ahc)
-{
- struct scb *scb;
- u_int scb_index;
-
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap, 0,
- 256, BUS_DMASYNC_POSTREAD);
-
- while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
- scb_index = ahc->qoutfifo[ahc->qoutfifonext];
- ahc->qoutfifo[ahc->qoutfifonext++] = SCB_LIST_NULL;
-
- scb = &ahc->scb_data->scbarray[scb_index];
- if (scb_index >= ahc->scb_data->numscbs
- || (scb->flags & SCB_ACTIVE) == 0) {
- printf("%s: WARNING no command for scb %d "
- "(cmdcmplt)\nQOUTPOS = %d\n",
- ahc_name(ahc), scb_index,
- ahc->qoutfifonext - 1);
- continue;
- }
-
- /*
- * Save off the residual
- * if there is one.
- */
- if (scb->hscb->residual_SG_count != 0)
- ahc_calc_residual(scb);
- else
- scb->xs->resid = 0;
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSCBS) {
- scsipi_printaddr(scb->xs->xs_periph);
- printf("run_qoutfifo: SCB %x complete\n",
- scb->hscb->tag);
- }
-#endif
- ahc_done(ahc, scb);
- }
-}
-
-
-/*
- * An scb (and hence an scb entry on the board) is put onto the
- * free list.
- */
-static void
-ahcfreescb(struct ahc_softc *ahc, struct scb *scb)
-{
- struct hardware_scb *hscb;
- int opri;
-
- hscb = scb->hscb;
-
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSCBALLOC)
- printf("%s: free SCB tag %x\n", ahc_name(ahc), hscb->tag);
-#endif
-
- opri = splbio();
-
- if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0 ||
- (scb->flags & SCB_RECOVERY_SCB) != 0) {
- ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
- scsipi_channel_thaw(&ahc->sc_channel, 1);
- if (ahc->features & AHC_TWIN)
- scsipi_channel_thaw(&ahc->sc_channel_b, 1);
- }
-
- /* Clean up for the next user */
- scb->flags = SCB_FREE;
- hscb->control = 0;
- hscb->status = 0;
-
- SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, scb, links);
-
- splx(opri);
-}
+static const u_int num_chip_names = NUM_ELEMENTS(ahc_chip_names);
/*
- * Get a free scb, either one already assigned to a hardware slot
- * on the adapter or one that will require an SCB to be paged out before
- * use. If there are none, see if we can allocate a new SCB. Otherwise
- * either return an error or sleep.
+ * Hardware error codes.
*/
-static __inline struct scb *
-ahcgetscb(struct ahc_softc *ahc)
-{
- struct scb *scbp;
- int opri;
-
- opri = splbio();
- if ((scbp = SLIST_FIRST(&ahc->scb_data->free_scbs))) {
- SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links);
- } else {
- ahcallocscbs(ahc);
- scbp = SLIST_FIRST(&ahc->scb_data->free_scbs);
- if (scbp != NULL)
- SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links);
- }
-
- splx(opri);
-
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSCBALLOC) {
- if (scbp != NULL)
- printf("%s: new SCB, tag %x\n", ahc_name(ahc),
- scbp->hscb->tag);
- else
- printf("%s: failed to allocate new SCB\n",
- ahc_name(ahc));
- }
-#endif
-
- return (scbp);
-}
-
-static int
-ahc_createdmamem(tag, size, flags, mapp, vaddr, baddr, seg, nseg, myname, what)
- bus_dma_tag_t tag;
- int size;
- int flags;
- bus_dmamap_t *mapp;
- caddr_t *vaddr;
- bus_addr_t *baddr;
- bus_dma_segment_t *seg;
- int *nseg;
- const char *myname, *what;
-{
- int error, level = 0;
-
- if ((error = bus_dmamem_alloc(tag, size, PAGE_SIZE, 0,
- seg, 1, nseg, BUS_DMA_NOWAIT)) != 0) {
- aprint_error(
- "%s: failed to allocate DMA mem for %s, error = %d\n",
- myname, what, error);
- goto out;
- }
- level++;
-
- if ((error = bus_dmamem_map(tag, seg, *nseg, size, vaddr,
- BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
- aprint_error("%s: failed to map DMA mem for %s, error = %d\n",
- myname, what, error);
- goto out;
- }
- level++;
-
- if ((error = bus_dmamap_create(tag, size, 1, size, 0,
- BUS_DMA_NOWAIT | flags, mapp)) != 0) {
- aprint_error(
- "%s: failed to create DMA map for %s, error = %d\n",
- myname, what, error);
- goto out;
- }
- level++;
-
- if ((error = bus_dmamap_load(tag, *mapp, *vaddr, size, NULL,
- BUS_DMA_NOWAIT)) != 0) {
- aprint_error("%s: failed to load DMA map for %s, error = %d\n",
- myname, what, error);
- goto out;
- }
-
- *baddr = (*mapp)->dm_segs[0].ds_addr;
-
-#ifdef AHC_DEBUG
- aprint_debug(
- "%s: dmamem for %s at busaddr %lx virt %lx nseg %d size %d\n",
- myname, what, (unsigned long)*baddr, (unsigned long)*vaddr,
- *nseg, size);
-#endif
-
- return 0;
-out:
- switch (level) {
- case 3:
- bus_dmamap_destroy(tag, *mapp);
- /* FALLTHROUGH */
- case 2:
- bus_dmamem_unmap(tag, *vaddr, size);
- /* FALLTHROUGH */
- case 1:
- bus_dmamem_free(tag, seg, *nseg);
- break;
- default:
- break;
- }
-
- return error;
-}
-
-static void
-ahc_freedmamem(tag, size, map, vaddr, seg, nseg)
- bus_dma_tag_t tag;
- int size;
- bus_dmamap_t map;
- caddr_t vaddr;
- bus_dma_segment_t *seg;
- int nseg;
-{
-
- bus_dmamap_unload(tag, map);
- bus_dmamap_destroy(tag, map);
- bus_dmamem_unmap(tag, vaddr, size);
- bus_dmamem_free(tag, seg, nseg);
-}
-
-char *
-ahc_name(struct ahc_softc *ahc)
-{
- return (ahc->sc_dev.dv_xname);
-}
-
-#ifdef AHC_DEBUG
-static void
-ahc_print_scb(struct scb *scb)
-{
- struct hardware_scb *hscb = scb->hscb;
-
- printf("scb:%p tag %x control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
- scb,
- hscb->tag,
- hscb->control,
- hscb->tcl,
- hscb->cmdlen,
- (unsigned long)le32toh(hscb->cmdpointer));
- printf(" datlen:%u data:0x%lx segs:0x%x segp:0x%lx\n",
- le32toh(hscb->datalen),
- (unsigned long)(le32toh(hscb->data)),
- hscb->SG_count,
- (unsigned long)(le32toh(hscb->SG_pointer)));
- printf(" sg_addr:%lx sg_len:%lu\n",
- (unsigned long)(le32toh(scb->sg_list[0].addr)),
- (unsigned long)(le32toh(scb->sg_list[0].len)));
- printf(" cdb:%x %x %x %x %x %x %x %x %x %x %x %x\n",
- hscb->cmdstore[0], hscb->cmdstore[1], hscb->cmdstore[2],
- hscb->cmdstore[3], hscb->cmdstore[4], hscb->cmdstore[5],
- hscb->cmdstore[6], hscb->cmdstore[7], hscb->cmdstore[8],
- hscb->cmdstore[9], hscb->cmdstore[10], hscb->cmdstore[11]);
-}
-#endif
+struct ahc_hard_error_entry {
+ uint8_t errno;
+ char *errmesg;
+};
-static const struct {
- u_int8_t errno;
- const char *errmesg;
-} hard_error[] = {
+static struct ahc_hard_error_entry ahc_hard_errors[] = {
{ ILLHADDR, "Illegal Host Access" },
{ ILLSADDR, "Illegal Sequencer Address referrenced" },
{ ILLOPCODE, "Illegal Opcode in sequencer program" },
@@ -758,15 +92,14 @@ static const struct {
{ PCIERRSTAT, "PCI Error detected" },
{ CIOPARERR, "CIOBUS Parity Error" },
};
-static const int num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
+static const u_int num_errors = NUM_ELEMENTS(ahc_hard_errors);
-static const struct {
- u_int8_t phase;
- u_int8_t mesg_out; /* Message response to parity errors */
- const char *phasemsg;
-} phase_table[] = {
+static struct ahc_phase_table_entry ahc_phase_table[] =
+{
{ P_DATAOUT, MSG_NOOP, "in Data-out phase" },
{ P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
+ { P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
+ { P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
{ P_COMMAND, MSG_NOOP, "in Command phase" },
{ P_MESGOUT, MSG_NOOP, "in Message-out phase" },
{ P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
@@ -774,18 +107,20 @@ static const struct {
{ P_BUSFREE, MSG_NOOP, "while idle" },
{ 0, MSG_NOOP, "in unknown phase" }
};
-static const int num_phases = (sizeof(phase_table)/sizeof(phase_table[0])) - 1;
+
+/*
+ * In most cases we only wish to itterate over real phases, so
+ * exclude the last element from the count.
+ */
+static const u_int num_phases = NUM_ELEMENTS(ahc_phase_table) - 1;
/*
* Valid SCSIRATE values. (p. 3-17)
- * Provides a mapping of transfer periods in ns to the proper value to
- * stick in the scsiscfr reg to use that transfer rate.
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsixfer reg.
*/
-#define AHC_SYNCRATE_DT 0
-#define AHC_SYNCRATE_ULTRA2 1
-#define AHC_SYNCRATE_ULTRA 3
-#define AHC_SYNCRATE_FAST 6
-static const struct ahc_syncrate ahc_syncrates[] = {
+static struct ahc_syncrate ahc_syncrates[] =
+{
/* ultra2 fast/ultra period rate */
{ 0x42, 0x000, 9, "80.0" },
{ 0x03, 0x000, 10, "40.0" },
@@ -804,850 +139,288 @@ static const struct ahc_syncrate ahc_syncrates[] = {
{ 0x00, 0x000, 0, NULL }
};
-/*
- * Allocate a controller structure for a new device and initialize it.
- */
-int
-ahc_alloc(struct ahc_softc *ahc, bus_space_handle_t sh, bus_space_tag_t st,
- bus_dma_tag_t parent_dmat, ahc_chip chip, ahc_feature features,
- ahc_flag flags)
-{
- struct scb_data *scb_data;
-
- scb_data = malloc(sizeof (struct scb_data), M_DEVBUF, M_NOWAIT|M_ZERO);
- if (scb_data == NULL) {
- aprint_error("%s: cannot malloc softc!\n", ahc_name(ahc));
- return -1;
- }
- LIST_INIT(&ahc->pending_ccbs);
- ahc->tag = st;
- ahc->bsh = sh;
- ahc->parent_dmat = parent_dmat;
- ahc->chip = chip;
- ahc->features = features;
- ahc->flags = flags;
- ahc->scb_data = scb_data;
-
- ahc->unpause = (ahc_inb(ahc, HCNTRL) & IRQMS) | INTEN;
- /* The IRQMS bit is only valid on VL and EISA chips */
- if ((ahc->chip & AHC_PCI) != 0)
- ahc->unpause &= ~IRQMS;
- ahc->pause = ahc->unpause | PAUSE;
- return (0);
-}
-
-void
-ahc_free(ahc)
- struct ahc_softc *ahc;
-{
- ahcfiniscbdata(ahc);
- if (ahc->init_level != 0)
- ahc_freedmamem(ahc->parent_dmat, ahc->shared_data_size,
- ahc->shared_data_dmamap, ahc->qoutfifo,
- &ahc->shared_data_seg, ahc->shared_data_nseg);
-
- if (ahc->scb_data != NULL)
- free(ahc->scb_data, M_DEVBUF);
- if (ahc->bus_data != NULL)
- free(ahc->bus_data, M_DEVBUF);
- return;
-}
-
-static int
-ahcinitscbdata(struct ahc_softc *ahc)
-{
- struct scb_data *scb_data;
- int i;
-
- scb_data = ahc->scb_data;
- SLIST_INIT(&scb_data->free_scbs);
- SLIST_INIT(&scb_data->sg_maps);
-
- /* Allocate SCB resources */
- scb_data->scbarray =
- (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX,
- M_DEVBUF, M_NOWAIT|M_ZERO);
- if (scb_data->scbarray == NULL)
- return (ENOMEM);
-
- /* Determine the number of hardware SCBs and initialize them */
-
- scb_data->maxhscbs = ahc_probe_scbs(ahc);
- /* SCB 0 heads the free list */
- ahc_outb(ahc, FREE_SCBH, 0);
- for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
- ahc_outb(ahc, SCBPTR, i);
-
- /* Clear the control byte. */
- ahc_outb(ahc, SCB_CONTROL, 0);
-
- /* Set the next pointer */
- ahc_outb(ahc, SCB_NEXT, i+1);
-
- /* Make the tag number invalid */
- ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
- }
-
- /* Make sure that the last SCB terminates the free list */
- ahc_outb(ahc, SCBPTR, i-1);
- ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
-
- /* Ensure we clear the 0 SCB's control byte. */
- ahc_outb(ahc, SCBPTR, 0);
- ahc_outb(ahc, SCB_CONTROL, 0);
-
- scb_data->maxhscbs = i;
-
- if (ahc->scb_data->maxhscbs == 0)
- panic("%s: No SCB space found", ahc_name(ahc));
-
- /*
- * Create our DMA tags. These tags define the kinds of device
- * accessible memory allocations and memory mappings we will
- * need to perform during normal operation.
- *
- * Unless we need to further restrict the allocation, we rely
- * on the restrictions of the parent dmat, hence the common
- * use of MAXADDR and MAXSIZE.
- */
-
- if (ahc_createdmamem(ahc->parent_dmat,
- AHC_SCB_MAX * sizeof(struct hardware_scb), ahc->sc_dmaflags,
- &scb_data->hscb_dmamap,
- (caddr_t *)&scb_data->hscbs, &scb_data->hscb_busaddr,
- &scb_data->hscb_seg, &scb_data->hscb_nseg, ahc_name(ahc),
- "hardware SCB structures") < 0)
- goto error_exit;
-
- scb_data->init_level++;
-
- if (ahc_createdmamem(ahc->parent_dmat,
- AHC_SCB_MAX * sizeof(struct scsipi_sense_data), ahc->sc_dmaflags,
- &scb_data->sense_dmamap, (caddr_t *)&scb_data->sense,
- &scb_data->sense_busaddr, &scb_data->sense_seg,
- &scb_data->sense_nseg, ahc_name(ahc), "sense buffers") < 0)
- goto error_exit;
-
- scb_data->init_level++;
-
- /* Perform initial CCB allocation */
- memset(scb_data->hscbs, 0, AHC_SCB_MAX * sizeof(struct hardware_scb));
- ahcallocscbs(ahc);
-
- if (scb_data->numscbs == 0) {
- printf("%s: ahc_init_scb_data - "
- "Unable to allocate initial scbs\n",
- ahc_name(ahc));
- goto error_exit;
- }
-
- scb_data->init_level++;
-
- /*
- * Note that we were successful
- */
- return 0;
-
-error_exit:
-
- return ENOMEM;
-}
-
-static void
-ahcfiniscbdata(struct ahc_softc *ahc)
-{
- struct scb_data *scb_data;
-
- scb_data = ahc->scb_data;
-
- switch (scb_data->init_level) {
- default:
- case 3:
- {
- struct sg_map_node *sg_map;
-
- while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
- SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
- ahc_freedmamem(ahc->parent_dmat, PAGE_SIZE,
- sg_map->sg_dmamap, (caddr_t)sg_map->sg_vaddr,
- &sg_map->sg_dmasegs, sg_map->sg_nseg);
- free(sg_map, M_DEVBUF);
- }
- }
- /*FALLTHROUGH*/
- case 2:
- ahc_freedmamem(ahc->parent_dmat,
- AHC_SCB_MAX * sizeof(struct scsipi_sense_data),
- scb_data->sense_dmamap, (caddr_t)scb_data->sense,
- &scb_data->sense_seg, scb_data->sense_nseg);
- /*FALLTHROUGH*/
- case 1:
- ahc_freedmamem(ahc->parent_dmat,
- AHC_SCB_MAX * sizeof(struct hardware_scb),
- scb_data->hscb_dmamap, (caddr_t)scb_data->hscbs,
- &scb_data->hscb_seg, scb_data->hscb_nseg);
- /*FALLTHROUGH*/
- }
- if (scb_data->scbarray != NULL)
- free(scb_data->scbarray, M_DEVBUF);
-}
+/* Our Sequencer Program */
+#include <dev/microcode/aic7xxx/aic7xxx_seq.h>
-int
-ahc_reset(struct ahc_softc *ahc)
-{
- u_int sblkctl;
- int wait;
+/**************************** Function Declarations ***************************/
+static void ahc_force_renegotiation(struct ahc_softc *ahc);
+static struct ahc_tmode_tstate*
+ ahc_alloc_tstate(struct ahc_softc *ahc,
+ u_int scsi_id, char channel);
+#ifdef AHC_TARGET_MODE
+static void ahc_free_tstate(struct ahc_softc *ahc,
+ u_int scsi_id, char channel, int force);
+#endif
+static struct ahc_syncrate*
+ ahc_devlimited_syncrate(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *,
+ u_int *period,
+ u_int *ppr_options,
+ role_t role);
+static void ahc_update_pending_scbs(struct ahc_softc *ahc);
+static void ahc_fetch_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_scb_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+static void ahc_assert_atn(struct ahc_softc *ahc);
+static void ahc_setup_initiator_msgout(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+static void ahc_build_transfer_msg(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_construct_sdtr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int period, u_int offset);
+static void ahc_construct_wdtr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int bus_width);
+static void ahc_construct_ppr(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int period, u_int offset,
+ u_int bus_width, u_int ppr_options);
+static void ahc_clear_msg_state(struct ahc_softc *ahc);
+static void ahc_handle_proto_violation(struct ahc_softc *ahc);
+static void ahc_handle_message_phase(struct ahc_softc *ahc);
+typedef enum {
+ AHCMSG_1B,
+ AHCMSG_2B,
+ AHCMSG_EXT
+} ahc_msgtype;
+static int ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type,
+ u_int msgval, int full);
+static int ahc_parse_msg(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static int ahc_handle_msg_reject(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_handle_ign_wide_residue(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo);
+static void ahc_reinitialize_dataptrs(struct ahc_softc *ahc);
+static void ahc_handle_devreset(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ cam_status status, char *message,
+ int verbose_level);
+#if AHC_TARGET_MODE
+static void ahc_setup_target_msgin(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb);
+#endif
+//static bus_dmamap_callback_t ahc_dmamap_cb;
+static void ahc_build_free_scb_list(struct ahc_softc *ahc);
+static int ahc_init_scbdata(struct ahc_softc *ahc);
+static void ahc_fini_scbdata(struct ahc_softc *ahc);
+static void ahc_qinfifo_requeue(struct ahc_softc *ahc,
+ struct scb *prev_scb,
+ struct scb *scb);
+static int ahc_qinfifo_count(struct ahc_softc *ahc);
+static u_int ahc_rem_scb_from_disc_list(struct ahc_softc *ahc,
+ u_int prev, u_int scbptr);
+static void ahc_add_curscb_to_free_list(struct ahc_softc *ahc);
+static u_int ahc_rem_wscb(struct ahc_softc *ahc,
+ u_int scbpos, u_int prev);
+static void ahc_reset_current_bus(struct ahc_softc *ahc);
#ifdef AHC_DUMP_SEQ
- if (ahc->init_level == 0)
- ahc_dumpseq(ahc);
+static void ahc_dumpseq(struct ahc_softc *ahc);
+#endif
+static void ahc_loadseq(struct ahc_softc *ahc);
+static int ahc_check_patch(struct ahc_softc *ahc,
+ struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr);
+static void ahc_download_instr(struct ahc_softc *ahc,
+ u_int instrptr, uint8_t *dconsts);
+#ifdef AHC_TARGET_MODE
+static void ahc_queue_lstate_event(struct ahc_softc *ahc,
+ struct ahc_tmode_lstate *lstate,
+ u_int initiator_id,
+ u_int event_type,
+ u_int event_arg);
+static void ahc_update_scsiid(struct ahc_softc *ahc,
+ u_int targid_mask);
+static int ahc_handle_target_cmd(struct ahc_softc *ahc,
+ struct target_cmd *cmd);
#endif
- ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
- /*
- * Ensure that the reset has finished
- */
- wait = 1000;
- do {
- DELAY(1000);
- } while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
-
- if (wait == 0) {
- printf("%s: WARNING - Failed chip reset! "
- "Trying to initialize anyway.\n", ahc_name(ahc));
- }
- ahc_outb(ahc, HCNTRL, ahc->pause);
-
- /* Determine channel configuration */
- sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
- /* No Twin Channel PCI cards */
- if ((ahc->chip & AHC_PCI) != 0)
- sblkctl &= ~SELBUSB;
- switch (sblkctl) {
- case 0:
- /* Single Narrow Channel */
- break;
- case 2:
- /* Wide Channel */
- ahc->features |= AHC_WIDE;
- break;
- case 8:
- /* Twin Channel */
- ahc->features |= AHC_TWIN;
- break;
- default:
- printf(" Unsupported adapter type. Ignoring\n");
- return(-1);
- }
-
- return (0);
-}
-
-/*
- * Called when we have an active connection to a target on the bus,
- * this function finds the nearest syncrate to the input period limited
- * by the capabilities of the bus connectivity of the target.
- */
-static const struct ahc_syncrate *
-ahc_devlimited_syncrate(struct ahc_softc *ahc, u_int *period) {
- u_int maxsync;
-
- if ((ahc->features & AHC_ULTRA2) != 0) {
- if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
- && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
- maxsync = AHC_SYNCRATE_ULTRA2;
- } else {
- maxsync = AHC_SYNCRATE_ULTRA;
- }
- } else if ((ahc->features & AHC_ULTRA) != 0) {
- maxsync = AHC_SYNCRATE_ULTRA;
- } else {
- maxsync = AHC_SYNCRATE_FAST;
- }
- return (ahc_find_syncrate(ahc, period, maxsync));
-}
+/************************** Added for porting to NetBSD ***********************/
+static int ahc_createdmamem(bus_dma_tag_t tag,
+ int size,
+ int flags,
+ bus_dmamap_t *mapp,
+ caddr_t *vaddr,
+ bus_addr_t *baddr,
+ bus_dma_segment_t *seg,
+ int *nseg,
+ const char *myname, const char *what);
+static void ahc_freedmamem(bus_dma_tag_t tag,
+ int size,
+ bus_dmamap_t map,
+ caddr_t vaddr,
+ bus_dma_segment_t *seg,
+ int nseg);
+
+/************************* Sequencer Execution Control ************************/
/*
- * Look up the valid period to SCSIRATE conversion in our table.
- * Return the period and offset that should be sent to the target
- * if this was the beginning of an SDTR.
+ * Restart the sequencer program from address zero
*/
-static const struct ahc_syncrate *
-ahc_find_syncrate(struct ahc_softc *ahc, u_int *period, u_int maxsync)
-{
- const struct ahc_syncrate *syncrate;
-
- syncrate = &ahc_syncrates[maxsync];
- while ((syncrate->rate != NULL)
- && ((ahc->features & AHC_ULTRA2) == 0
- || (syncrate->sxfr_u2 != 0))) {
-
- if (*period <= syncrate->period) {
- /*
- * When responding to a target that requests
- * sync, the requested rate may fall between
- * two rates that we can output, but still be
- * a rate that we can receive. Because of this,
- * we want to respond to the target with
- * the same rate that it sent to us even
- * if the period we use to send data to it
- * is lower. Only lower the response period
- * if we must.
- */
- if (syncrate == &ahc_syncrates[maxsync])
- *period = syncrate->period;
- break;
- }
- syncrate++;
- }
-
- if ((*period == 0)
- || (syncrate->rate == NULL)
- || ((ahc->features & AHC_ULTRA2) != 0
- && (syncrate->sxfr_u2 == 0))) {
- /* Use asynchronous transfers. */
- *period = 0;
- syncrate = NULL;
- }
- return (syncrate);
-}
-
-static u_int
-ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+void
+ahc_restart(struct ahc_softc *ahc)
{
- const struct ahc_syncrate *syncrate;
-
- if ((ahc->features & AHC_ULTRA2) != 0)
- scsirate &= SXFR_ULTRA2;
- else
- scsirate &= SXFR;
- syncrate = &ahc_syncrates[maxsync];
- while (syncrate->rate != NULL) {
+ ahc_pause(ahc);
- if ((ahc->features & AHC_ULTRA2) != 0) {
- if (syncrate->sxfr_u2 == 0)
- break;
- else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
- return (syncrate->period);
- } else if (scsirate == (syncrate->sxfr & SXFR)) {
- return (syncrate->period);
- }
- syncrate++;
- }
- return (0); /* async */
-}
+ /* No more pending messages. */
+ ahc_clear_msg_state(ahc);
-static void
-ahc_validate_offset(struct ahc_softc *ahc, const struct ahc_syncrate *syncrate,
- u_int *offset, int wide)
-{
- u_int maxoffset;
+ ahc_outb(ahc, SCSISIGO, 0); /* De-assert BSY */
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP); /* No message to send */
+ ahc_outb(ahc, SXFRCTL1, ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ ahc_outb(ahc, LASTPHASE, P_BUSFREE);
+ ahc_outb(ahc, SAVED_SCSIID, 0xFF);
+ ahc_outb(ahc, SAVED_LUN, 0xFF);
- /* Limit offset to what we can do */
- if (syncrate == NULL) {
- maxoffset = 0;
- } else if ((ahc->features & AHC_ULTRA2) != 0) {
- maxoffset = MAX_OFFSET_ULTRA2;
- } else {
- if (wide)
- maxoffset = MAX_OFFSET_16BIT;
- else
- maxoffset = MAX_OFFSET_8BIT;
- }
- *offset = MIN(*offset, maxoffset);
-}
-
-static void
-ahc_update_target_msg_request(struct ahc_softc *ahc,
- struct ahc_devinfo *devinfo,
- struct ahc_initiator_tinfo *tinfo,
- int force, int paused)
-{
- u_int targ_msg_req_orig;
-
- targ_msg_req_orig = ahc->targ_msg_req;
- if (tinfo->current.period != tinfo->goal.period
- || tinfo->current.width != tinfo->goal.width
- || tinfo->current.offset != tinfo->goal.offset
- || (force
- && (tinfo->goal.period != 0
- || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT))) {
- ahc->targ_msg_req |= devinfo->target_mask;
- } else {
- ahc->targ_msg_req &= ~devinfo->target_mask;
+ /*
+ * Ensure that the sequencer's idea of TQINPOS
+ * matches our own. The sequencer increments TQINPOS
+ * only after it sees a DMA complete and a reset could
+ * occur before the increment leaving the kernel to believe
+ * the command arrived but the sequencer to not.
+ */
+ ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+
+ /* Always allow reselection */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if ((ahc->features & AHC_CMD_CHAN) != 0) {
+ /* Ensure that no DMA operations are in progress */
+ ahc_outb(ahc, CCSCBCNT, 0);
+ ahc_outb(ahc, CCSGCTL, 0);
+ ahc_outb(ahc, CCSCBCTL, 0);
}
-
- if (ahc->targ_msg_req != targ_msg_req_orig) {
- /* Update the message request bit for this target */
- if ((ahc->features & AHC_HS_MAILBOX) != 0) {
- if (paused) {
- ahc_outb(ahc, TARGET_MSG_REQUEST,
- ahc->targ_msg_req & 0xFF);
- ahc_outb(ahc, TARGET_MSG_REQUEST + 1,
- (ahc->targ_msg_req >> 8) & 0xFF);
- } else {
- ahc_outb(ahc, HS_MAILBOX,
- 0x01 << HOST_MAILBOX_SHIFT);
- }
- } else {
- if (!paused)
- pause_sequencer(ahc);
-
- ahc_outb(ahc, TARGET_MSG_REQUEST,
- ahc->targ_msg_req & 0xFF);
- ahc_outb(ahc, TARGET_MSG_REQUEST + 1,
- (ahc->targ_msg_req >> 8) & 0xFF);
-
- if (!paused)
- unpause_sequencer(ahc);
- }
+ /*
+ * If we were in the process of DMA'ing SCB data into
+ * an SCB, replace that SCB on the free list. This prevents
+ * an SCB leak.
+ */
+ if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
+ ahc_add_curscb_to_free_list(ahc);
+ ahc_outb(ahc, SEQ_FLAGS2,
+ ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
}
+ ahc_outb(ahc, MWI_RESIDUAL, 0);
+ ahc_outb(ahc, SEQCTL, FASTMODE);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+ ahc_unpause(ahc);
}
-static void
-ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
- const struct ahc_syncrate *syncrate,
- u_int period, u_int offset, u_int type, int paused, int done)
+/************************* Input/Output Queues ********************************/
+void
+ahc_run_qoutfifo(struct ahc_softc *ahc)
{
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
- u_int old_period;
- u_int old_offset;
- int active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
-
- if (syncrate == NULL) {
- period = 0;
- offset = 0;
- }
-
- tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
- devinfo->target, &tstate);
- old_period = tinfo->current.period;
- old_offset = tinfo->current.offset;
-
- if ((type & AHC_TRANS_CUR) != 0
- && (old_period != period || old_offset != offset)) {
- u_int scsirate;
-
- scsirate = tinfo->scsirate;
- if ((ahc->features & AHC_ULTRA2) != 0) {
+ struct scb *scb;
+ u_int scb_index;
- /* XXX */
- /* Force single edge until DT is fully implemented */
- scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
- if (syncrate != NULL)
- scsirate |= syncrate->sxfr_u2|SINGLE_EDGE;
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+ while (ahc->qoutfifo[ahc->qoutfifonext] != SCB_LIST_NULL) {
- if (active)
- ahc_outb(ahc, SCSIOFFSET, offset);
- } else {
+ scb_index = ahc->qoutfifo[ahc->qoutfifonext];
+ if ((ahc->qoutfifonext & 0x03) == 0x03) {
+ u_int modnext;
- scsirate &= ~(SXFR|SOFS);
/*
- * Ensure Ultra mode is set properly for
- * this target.
+ * Clear 32bits of QOUTFIFO at a time
+ * so that we don't clobber an incoming
+ * byte DMA to the array on architectures
+ * that only support 32bit load and store
+ * operations.
*/
- tstate->ultraenb &= ~devinfo->target_mask;
- if (syncrate != NULL) {
- if (syncrate->sxfr & ULTRA_SXFR) {
- tstate->ultraenb |=
- devinfo->target_mask;
- }
- scsirate |= syncrate->sxfr & SXFR;
- scsirate |= offset & SOFS;
- }
- if (active) {
- u_int sxfrctl0;
-
- sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
- sxfrctl0 &= ~FAST20;
- if (tstate->ultraenb & devinfo->target_mask)
- sxfrctl0 |= FAST20;
- ahc_outb(ahc, SXFRCTL0, sxfrctl0);
- }
+ modnext = ahc->qoutfifonext & ~0x3;
+ *((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
+ ahc_dmamap_sync(ahc, ahc->parent_dmat /*shared_data_dmat*/,
+ ahc->shared_data_dmamap,
+ /*offset*/modnext, /*len*/4,
+ BUS_DMASYNC_PREREAD);
}
- if (active)
- ahc_outb(ahc, SCSIRATE, scsirate);
-
- tinfo->scsirate = scsirate;
- tinfo->current.period = period;
- tinfo->current.offset = offset;
+ ahc->qoutfifonext++;
- /* Update the syncrates in any pending scbs */
- ahc_update_pending_syncrates(ahc);
- }
-
- if ((type & AHC_TRANS_GOAL) != 0) {
- tinfo->goal.period = period;
- tinfo->goal.offset = offset;
- }
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s: WARNING no command for scb %d "
+ "(cmdcmplt)\nQOUTPOS = %d\n",
+ ahc_name(ahc), scb_index,
+ (ahc->qoutfifonext - 1) & 0xFF);
+ continue;
+ }
- if ((type & AHC_TRANS_USER) != 0) {
- tinfo->user.period = period;
- tinfo->user.offset = offset;
+ /*
+ * Save off the residual
+ * if there is one.
+ */
+ ahc_update_residual(ahc, scb);
+ ahc_done(ahc, scb);
}
-
- ahc_update_target_msg_request(ahc, devinfo, tinfo,
- /*force*/FALSE,
- paused);
}
-static void
-ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
- u_int width, u_int type, int paused, int done)
-{
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
- u_int oldwidth;
- int active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
-
- tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
- devinfo->target, &tstate);
- oldwidth = tinfo->current.width;
-
- if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
- u_int scsirate;
-
- scsirate = tinfo->scsirate;
- scsirate &= ~WIDEXFER;
- if (width == MSG_EXT_WDTR_BUS_16_BIT)
- scsirate |= WIDEXFER;
-
- tinfo->scsirate = scsirate;
-
- if (active)
- ahc_outb(ahc, SCSIRATE, scsirate);
-
- tinfo->current.width = width;
- }
-
- if ((type & AHC_TRANS_GOAL) != 0)
- tinfo->goal.width = width;
- if ((type & AHC_TRANS_USER) != 0)
- tinfo->user.width = width;
-
- ahc_update_target_msg_request(ahc, devinfo, tinfo,
- /*force*/FALSE, paused);
-}
-
-static void
-ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo, int enable)
+void
+ahc_run_untagged_queues(struct ahc_softc *ahc)
{
-#ifndef AHC_NO_TAGS
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
-
- tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
- devinfo->target, &tstate);
+ int i;
- if (enable) {
- tstate->tagenable |= devinfo->target_mask;
- } else {
- tstate->tagenable &= ~devinfo->target_mask;
- tstate->tagdisable |= devinfo->target_mask;
- }
-#endif
+ for (i = 0; i < 16; i++)
+ ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
}
-static void
-ahc_update_xfer_mode(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+void
+ahc_run_untagged_queue(struct ahc_softc *ahc, struct scb_tailq *queue)
{
- struct scsipi_xfer_mode xm;
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
+ struct scb *scb;
- if (ahc->inited_targets[devinfo->target] != 2)
+ if (ahc->untagged_queue_lock != 0)
return;
- tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
- devinfo->target, &tstate);
-
- xm.xm_target = devinfo->target;
- xm.xm_mode = 0;
- xm.xm_period = tinfo->current.period;
- xm.xm_offset = tinfo->current.offset;
- if (tinfo->current.width == 1)
- xm.xm_mode |= PERIPH_CAP_WIDE16;
- if (tinfo->current.period)
- xm.xm_mode |= PERIPH_CAP_SYNC;
- if (tstate->tagenable & devinfo->target_mask)
- xm.xm_mode |= PERIPH_CAP_TQING;
- scsipi_async_event(
- devinfo->channel == 'B' ? &ahc->sc_channel_b : &ahc->sc_channel,
- ASYNC_EVENT_XFER_MODE, &xm);
-}
-
-/*
- * Attach all the sub-devices we can find
- */
-int
-ahc_attach(struct ahc_softc *ahc)
-{
- ahc->sc_adapter.adapt_dev = &ahc->sc_dev;
- ahc->sc_adapter.adapt_nchannels = (ahc->features & AHC_TWIN) ? 2 : 1;
- if (ahc->flags & AHC_PAGESCBS) {
- ahc->sc_adapter.adapt_openings = AHC_SCB_MAX;
- ahc->sc_adapter.adapt_max_periph = 16;
- } else {
- ahc->sc_adapter.adapt_openings = ahc->scb_data->maxhscbs;
- if (ahc->scb_data->maxhscbs >= 16)
- ahc->sc_adapter.adapt_max_periph = 16;
- else
- ahc->sc_adapter.adapt_max_periph = 4;
- }
- ahc->sc_adapter.adapt_ioctl = ahc_ioctl;
- ahc->sc_adapter.adapt_minphys = ahcminphys;
- ahc->sc_adapter.adapt_request = ahc_action;
-
- ahc->sc_channel.chan_adapter = &ahc->sc_adapter;
- ahc->sc_channel.chan_bustype = &scsi_bustype;
- ahc->sc_channel.chan_channel = 0;
- ahc->sc_channel.chan_ntargets = (ahc->features & AHC_WIDE) ? 16 : 8;
- ahc->sc_channel.chan_nluns = 8;
- ahc->sc_channel.chan_id = ahc->our_id;
-
- if (ahc->features & AHC_TWIN) {
- ahc->sc_channel_b = ahc->sc_channel;
- ahc->sc_channel_b.chan_id = ahc->our_id_b;
- ahc->sc_channel_b.chan_channel = 1;
+ if ((scb = TAILQ_FIRST(queue)) != NULL
+ && (scb->flags & SCB_ACTIVE) == 0) {
+ scb->flags |= SCB_ACTIVE;
+ ahc_queue_scb(ahc, scb);
}
-
- if ((ahc->flags & AHC_CHANNEL_B_PRIMARY) == 0) {
- ahc->child = config_found((void *)ahc, &ahc->sc_channel, scsiprint);
- if (ahc->features & AHC_TWIN)
- ahc->child = config_found((void *)ahc, &ahc->sc_channel_b,
- scsiprint);
- } else {
- ahc->child = config_found((void *)ahc, &ahc->sc_channel_b, scsiprint);
- ahc->child = config_found((void *)ahc, &ahc->sc_channel, scsiprint);
- }
- return 1;
}
-int
-ahc_detach(struct ahc_softc *ahc, int flags)
-{
- int rv = 0;
-
- if (ahc->child != NULL)
- rv = config_detach(ahc->child, flags);
-
- ahc_free(ahc);
-
- return (rv);
-}
-
-int
-ahc_activate(struct device *self, enum devact act)
-{
- struct ahc_softc *ahc = (void *) self;
- int s, rv = 0;
-
- s = splhigh();
- switch (act) {
- case DVACT_ACTIVATE:
- rv = EOPNOTSUPP;
- break;
-
- case DVACT_DEACTIVATE:
- if (ahc->child != NULL)
- rv = config_deactivate(ahc->child);
- break;
- }
- splx(s);
-
- return (rv);
-}
-
-static void
-ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
- u_int saved_tcl;
- role_t role;
- int our_id;
-
- if (ahc_inb(ahc, SSTAT0) & TARGET)
- role = ROLE_TARGET;
- else
- role = ROLE_INITIATOR;
-
- if (role == ROLE_TARGET
- && (ahc->features & AHC_MULTI_TID) != 0
- && (ahc_inb(ahc, SEQ_FLAGS) & CMDPHASE_PENDING) != 0) {
- /* We were selected, so pull our id from TARGIDIN */
- our_id = ahc_inb(ahc, TARGIDIN) & OID;
- } else if ((ahc->features & AHC_ULTRA2) != 0)
- our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
- else
- our_id = ahc_inb(ahc, SCSIID) & OID;
-
- saved_tcl = ahc_inb(ahc, SAVED_TCL);
- ahc_compile_devinfo(devinfo, our_id, TCL_TARGET(saved_tcl),
- TCL_LUN(saved_tcl), TCL_CHANNEL(ahc, saved_tcl),
- role);
-}
-
-static void
-ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
- u_int lun, char channel, role_t role)
-{
- devinfo->our_scsiid = our_id;
- devinfo->target = target;
- devinfo->lun = lun;
- devinfo->target_offset = target;
- devinfo->channel = channel;
- devinfo->role = role;
- if (channel == 'B')
- devinfo->target_offset += 8;
- devinfo->target_mask = (0x01 << devinfo->target_offset);
-}
-
-/*
- * Catch an interrupt from the adapter
- */
-int
-ahc_intr(void *arg)
+/************************* Interrupt Handling *********************************/
+void
+ahc_handle_brkadrint(struct ahc_softc *ahc)
{
- struct ahc_softc *ahc;
- u_int intstat;
-
- ahc = (struct ahc_softc *)arg;
-
- intstat = ahc_inb(ahc, INTSTAT);
-
/*
- * Any interrupts to process?
+ * We upset the sequencer :-(
+ * Lookup the error message
*/
- if ((intstat & INT_PEND) == 0) {
- if (ahc->bus_intr && ahc->bus_intr(ahc)) {
-#ifdef AHC_DEBUG
- printf("%s: bus intr: CCHADDR %x HADDR %x SEQADDR %x\n",
- ahc_name(ahc),
- ahc_inb(ahc, CCHADDR) |
- (ahc_inb(ahc, CCHADDR+1) << 8)
- | (ahc_inb(ahc, CCHADDR+2) << 16)
- | (ahc_inb(ahc, CCHADDR+3) << 24),
- ahc_inb(ahc, HADDR) | (ahc_inb(ahc, HADDR+1) << 8)
- | (ahc_inb(ahc, HADDR+2) << 16)
- | (ahc_inb(ahc, HADDR+3) << 24),
- ahc_inb(ahc, SEQADDR0) |
- (ahc_inb(ahc, SEQADDR1) << 8));
-#endif
- return 1;
- }
- return 0;
- }
-
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWINTR) {
- printf("%s: intstat %x\n", ahc_name(ahc), intstat);
- }
-#endif
-
- if (intstat & CMDCMPLT) {
- ahc_outb(ahc, CLRINT, CLRCMDINT);
- ahc_run_qoutfifo(ahc);
- }
- if (intstat & BRKADRINT) {
- /*
- * We upset the sequencer :-(
- * Lookup the error message
- */
- int i, error;
-
- error = ahc_inb(ahc, ERROR);
- for (i = 0; error != 1 && i < num_errors; i++)
- error >>= 1;
- panic("%s: brkadrint, %s at seqaddr = 0x%x",
- ahc_name(ahc), hard_error[i].errmesg,
- ahc_inb(ahc, SEQADDR0) |
- (ahc_inb(ahc, SEQADDR1) << 8));
-
- /* Tell everyone that this HBA is no longer available */
- ahc_abort_scbs(ahc, AHC_TARGET_WILDCARD, ALL_CHANNELS,
- AHC_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
- XS_DRIVER_STUFFUP);
- }
- if (intstat & SEQINT)
- ahc_handle_seqint(ahc, intstat);
-
- if (intstat & SCSIINT)
- ahc_handle_scsiint(ahc, intstat);
-
- return 1;
-}
-
-static struct tmode_tstate *
-ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
-{
- struct tmode_tstate *master_tstate;
- struct tmode_tstate *tstate;
- int i, s;
-
- master_tstate = ahc->enabled_targets[ahc->our_id];
- if (channel == 'B') {
- scsi_id += 8;
- master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
- }
- if (ahc->enabled_targets[scsi_id] != NULL
- && ahc->enabled_targets[scsi_id] != master_tstate)
- panic("%s: ahc_alloc_tstate - Target already allocated",
- ahc_name(ahc));
- tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
- if (tstate == NULL)
- return (NULL);
+ int i;
+ int error;
- /*
- * If we have allocated a master tstate, copy user settings from
- * the master tstate (taken from SRAM or the EEPROM) for this
- * channel, but reset our current and goal settings to async/narrow
- * until an initiator talks to us.
- */
- if (master_tstate != NULL) {
- memcpy(tstate, master_tstate, sizeof(*tstate));
- tstate->ultraenb = 0;
- for (i = 0; i < 16; i++) {
- memset(&tstate->transinfo[i].current, 0,
- sizeof(tstate->transinfo[i].current));
- memset(&tstate->transinfo[i].goal, 0,
- sizeof(tstate->transinfo[i].goal));
- }
- } else
- memset(tstate, 0, sizeof(*tstate));
- s = splbio();
- ahc->enabled_targets[scsi_id] = tstate;
- splx(s);
- return (tstate);
-}
+ error = ahc_inb(ahc, ERROR);
+ for (i = 0; error != 1 && i < num_errors; i++)
+ error >>= 1;
+ printf("%s: brkadrint, %s at seqaddr = 0x%x\n",
+ ahc_name(ahc), ahc_hard_errors[i].errmesg,
+ ahc_inb(ahc, SEQADDR0) |
+ (ahc_inb(ahc, SEQADDR1) << 8));
-#if UNUSED
-static void
-ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
-{
- struct tmode_tstate *tstate;
+ ahc_dump_card_state(ahc);
- /* Don't clean up the entry for our initiator role */
- if ((ahc->flags & AHC_INITIATORMODE) != 0
- && ((channel == 'B' && scsi_id == ahc->our_id_b)
- || (channel == 'A' && scsi_id == ahc->our_id))
- && force == FALSE)
- return;
+ /* Tell everyone that this HBA is no longer availible */
+ ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
+ CAM_NO_HBA);
- if (channel == 'B')
- scsi_id += 8;
- tstate = ahc->enabled_targets[scsi_id];
- if (tstate != NULL)
- free(tstate, M_DEVBUF);
- ahc->enabled_targets[scsi_id] = NULL;
+ /* Disable all interrupt sources by resetting the controller */
+ ahc_shutdown(ahc);
}
-#endif
-static void
+void
ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
{
struct scb *scb;
struct ahc_devinfo devinfo;
-
+
ahc_fetch_devinfo(ahc, &devinfo);
/*
@@ -1658,86 +431,18 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
*/
ahc_outb(ahc, CLRINT, CLRSEQINT);
switch (intstat & SEQINT_MASK) {
- case NO_MATCH:
+ case BAD_STATUS:
{
- /* Ensure we don't leave the selection hardware on */
- ahc_outb(ahc, SCSISEQ,
- ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ u_int scb_index;
+ struct hardware_scb *hscb;
- printf("%s:%c:%d: no active SCB for reconnecting "
- "target - issuing BUS DEVICE RESET\n",
- ahc_name(ahc), devinfo.channel, devinfo.target);
- printf("SAVED_TCL == 0x%x, ARG_1 == 0x%x, SEQ_FLAGS == 0x%x\n",
- ahc_inb(ahc, SAVED_TCL), ahc_inb(ahc, ARG_1),
- ahc_inb(ahc, SEQ_FLAGS));
- ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
- ahc->msgout_len = 1;
- ahc->msgout_index = 0;
- ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
- ahc_outb(ahc, MSG_OUT, HOST_MSG);
- ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, LASTPHASE) | ATNO);
- break;
- }
- case UPDATE_TMSG_REQ:
- ahc_outb(ahc, TARGET_MSG_REQUEST, ahc->targ_msg_req & 0xFF);
- ahc_outb(ahc, TARGET_MSG_REQUEST + 1,
- (ahc->targ_msg_req >> 8) & 0xFF);
- ahc_outb(ahc, HS_MAILBOX, 0);
- break;
- case SEND_REJECT:
- {
- u_int rejbyte = ahc_inb(ahc, ACCUM);
- printf("%s:%c:%d: Warning - unknown message received from "
- "target (0x%x). Rejecting\n",
- ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
- break;
- }
- case NO_IDENT:
- {
/*
- * The reconnecting target either did not send an identify
- * message, or did, but we didn't find and SCB to match and
- * before it could respond to our ATN/abort, it hit a dataphase.
- * The only safe thing to do is to blow it away with a bus
- * reset.
+ * Set the default return value to 0 (don't
+ * send sense). The sense code will change
+ * this if needed.
*/
- int found;
-
- printf("%s:%c:%d: Target did not send an IDENTIFY message. "
- "LASTPHASE = 0x%x, SAVED_TCL == 0x%x\n",
- ahc_name(ahc), devinfo.channel, devinfo.target,
- ahc_inb(ahc, LASTPHASE), ahc_inb(ahc, SAVED_TCL));
- found = ahc_reset_channel(ahc, devinfo.channel,
- /*initiate reset*/TRUE);
- printf("%s: Issued Channel %c Bus Reset. "
- "%d SCBs aborted\n", ahc_name(ahc), devinfo.channel,
- found);
- return;
- }
- case BAD_PHASE:
- {
- u_int lastphase;
+ ahc_outb(ahc, RETURN_1, 0);
- lastphase = ahc_inb(ahc, LASTPHASE);
- if (lastphase == P_BUSFREE) {
- printf("%s:%c:%d: Missed busfree. Curphase = 0x%x\n",
- ahc_name(ahc), devinfo.channel, devinfo.target,
- ahc_inb(ahc, SCSISIGI));
- restart_sequencer(ahc);
- return;
- } else {
- printf("%s:%c:%d: unknown scsi bus phase %x. "
- "Attempting to continue\n",
- ahc_name(ahc), devinfo.channel, devinfo.target,
- ahc_inb(ahc, SCSISIGI));
- }
- break;
- }
- case BAD_STATUS:
- {
- u_int scb_index;
- struct hardware_scb *hscb;
- struct scsipi_xfer *xs;
/*
* The sequencer will notify us when a command
* has an error that would be of interest to
@@ -1748,27 +453,18 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
* the in kernel copy directly.
*/
scb_index = ahc_inb(ahc, SCB_TAG);
- scb = &ahc->scb_data->scbarray[scb_index];
-
- /* ahc_print_scb(scb); */
-
- /*
- * Set the default return value to 0 (don't
- * send sense). The sense code will change
- * this if needed.
- */
- ahc_outb(ahc, RETURN_1, 0);
- if (!(scb_index < ahc->scb_data->numscbs
- && (scb->flags & SCB_ACTIVE) != 0)) {
- printf("%s:%c:%d: ahc_intr - referenced scb "
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("ahc_intr - referenced scb "
"not valid during seqint 0x%x scb(%d)\n",
- ahc_name(ahc), devinfo.channel,
- devinfo.target, intstat, scb_index);
+ intstat, scb_index);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
goto unpause;
}
- hscb = scb->hscb;
- xs = scb->xs;
+ hscb = scb->hscb;
/* Don't want to clobber the original sense code */
if ((scb->flags & SCB_SENSE) != 0) {
@@ -1778,146 +474,201 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
* complete.
*/
scb->flags &= ~SCB_SENSE;
- ahcsetccbstatus(xs, XS_DRIVER_STUFFUP);
break;
}
- /* Freeze the queue unit the client sees the error. */
- ahc_freeze_devq(ahc, xs->xs_periph);
- ahc_freeze_ccb(scb);
- xs->status = hscb->status;
- switch (hscb->status) {
+ /* Freeze the queue until the client sees the error. */
+ ahc_freeze_devq(ahc, scb);
+ ahc_freeze_scb(scb);
+ ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status);
+ switch (hscb->shared_data.status.scsi_status) {
case SCSI_STATUS_OK:
- printf("%s: Interrupted for status of 0???\n",
+ printf("%s: Interrupted for status of 0 (???)\n",
ahc_name(ahc));
break;
case SCSI_STATUS_CMD_TERMINATED:
case SCSI_STATUS_CHECK_COND:
-#if defined(AHC_DEBUG)
- if (ahc_debug & AHC_SHOWSENSE) {
- scsipi_printaddr(xs->xs_periph);
- printf("Check Status, resid %d datalen %d\n",
- xs->resid, xs->datalen);
+ {
+ struct ahc_dma_seg *sg;
+ struct scsipi_sense *sc;
+ struct ahc_initiator_tinfo *targ_info;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_transinfo *tinfo;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOW_SENSE) {
+ ahc_print_path(ahc, scb);
+ printf("SCB %d: requests Check Status\n",
+ scb->hscb->tag);
}
#endif
- if (xs->error == XS_NOERROR &&
- !(scb->flags & SCB_SENSE)) {
- struct ahc_dma_seg *sg;
- struct scsipi_sense *sc;
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
-
- sg = scb->sg_list;
- sc = (struct scsipi_sense *)(&hscb->cmdstore);
- /*
- * Save off the residual if there is one.
- */
- if (hscb->residual_SG_count != 0)
- ahc_calc_residual(scb);
- else
- xs->resid = 0;
+ if (ahc_perform_autosense(scb) == 0)
+ break;
+ targ_info = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo = &targ_info->curr;
+ sg = scb->sg_list;
+ sc = (struct scsipi_sense *)(&hscb->shared_data.cdb);
+ /*
+ * Save off the residual if there is one.
+ */
+ ahc_update_residual(ahc, scb);
#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSENSE) {
- scsipi_printaddr(xs->xs_periph);
- printf("Sending Sense\n");
- }
+ if (ahc_debug & AHC_SHOW_SENSE) {
+ ahc_print_path(ahc, scb);
+ printf("Sending Sense\n");
+ }
#endif
- sg->addr = ahc->scb_data->sense_busaddr +
- (hscb->tag*sizeof(struct scsipi_sense_data));
- sg->len = sizeof (struct scsipi_sense_data);
+ sg->addr = ahc_get_sense_bufaddr(ahc, scb);
+ sg->len = ahc_get_sense_bufsize(ahc, scb);
+ sg->len |= AHC_DMA_LAST_SEG;
+
+ /* Fixup byte order */
+ sg->addr = ahc_htole32(sg->addr);
+ sg->len = ahc_htole32(sg->len);
+
+ sc->opcode = REQUEST_SENSE;
+ sc->byte2 = 0;
+ if (tinfo->protocol_version <= SCSI_REV_2
+ && SCB_GET_LUN(scb) < 8)
+ sc->byte2 = SCB_GET_LUN(scb) << 5;
+ sc->unused[0] = 0;
+ sc->unused[1] = 0;
+ sc->length = sg->len;
+ sc->control = 0;
- sc->opcode = REQUEST_SENSE;
- sc->byte2 = SCB_LUN(scb) << 5;
- sc->unused[0] = 0;
- sc->unused[1] = 0;
- sc->length = sg->len;
- sc->control = 0;
-
- /*
- * Would be nice to preserve DISCENB here,
- * but due to the way we page SCBs, we can't.
- */
- hscb->control = 0;
-
- /*
- * This request sense could be because the
- * the device lost power or in some other
- * way has lost our transfer negotiations.
- * Renegotiate if appropriate. Unit attention
- * errors will be reported before any data
- * phases occur.
- */
- ahc_calc_residual(scb);
-#if defined(AHC_DEBUG)
- if (ahc_debug & AHC_SHOWSENSE) {
- scsipi_printaddr(xs->xs_periph);
- printf("Sense: datalen %d resid %d"
- "chan %d id %d targ %d\n",
- xs->datalen, xs->resid,
- devinfo.channel, devinfo.our_scsiid,
- devinfo.target);
- }
-#endif
- if (xs->datalen > 0 &&
- xs->resid == xs->datalen) {
- tinfo = ahc_fetch_transinfo(ahc,
- devinfo.channel,
- devinfo.our_scsiid,
- devinfo.target,
- &tstate);
- ahc_update_target_msg_request(ahc,
- &devinfo,
- tinfo,
- /*force*/TRUE,
- /*paused*/TRUE);
- }
- hscb->status = 0;
- hscb->SG_count = 1;
- hscb->SG_pointer = scb->sg_list_phys;
- hscb->data = sg->addr;
- hscb->datalen = sg->len;
- hscb->cmdpointer = hscb->cmdstore_busaddr;
- hscb->cmdlen = sizeof(*sc);
- scb->sg_count = hscb->SG_count;
- ahc_swap_hscb(hscb);
- ahc_swap_sg(scb->sg_list);
- scb->flags |= SCB_SENSE;
- /*
- * Ensure the target is busy since this
- * will be an untagged request.
- */
- ahc_busy_tcl(ahc, scb);
- ahc_outb(ahc, RETURN_1, SEND_SENSE);
+ /*
+ * We can't allow the target to disconnect.
+ * This will be an untagged transaction and
+ * having the target disconnect will make this
+ * transaction indestinguishable from outstanding
+ * tagged transactions.
+ */
+ hscb->control = 0;
- /*
- * Ensure we have enough time to actually
- * retrieve the sense.
- */
- if (!(scb->xs->xs_control & XS_CTL_POLL)) {
- callout_reset(&scb->xs->xs_callout,
- 5 * hz, ahc_timeout, scb);
- }
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate. Unit attention
+ * errors will be reported before any data
+ * phases occur.
+ */
+ if (ahc_get_residual(scb)
+ == ahc_get_transfer_length(scb)) {
+ ahc_update_neg_request(ahc, &devinfo,
+ tstate, targ_info,
+ AHC_NEG_IF_NON_ASYNC);
}
+ if (tstate->auto_negotiate & devinfo.target_mask) {
+ hscb->control |= MK_MESSAGE;
+ scb->flags &= ~SCB_NEGOTIATE;
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ }
+ hscb->cdb_len = sizeof(*sc);
+ hscb->dataptr = sg->addr;
+ hscb->datacnt = sg->len;
+ hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
+ hscb->sgptr = ahc_htole32(hscb->sgptr);
+ scb->sg_count = 1;
+ scb->flags |= SCB_SENSE;
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ ahc_outb(ahc, RETURN_1, SEND_SENSE);
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ ahc_scb_timer_reset(scb, 5 * 1000000);
break;
- case SCSI_STATUS_QUEUE_FULL:
- case SCSI_STATUS_BUSY:
- xs->error = XS_BUSY;
+ }
+ default:
break;
}
break;
}
- case TRACE_POINT:
+ case NO_MATCH:
{
- printf("SSTAT2 = 0x%x DFCNTRL = 0x%x\n", ahc_inb(ahc, SSTAT2),
- ahc_inb(ahc, DFCNTRL));
- printf("SSTAT3 = 0x%x DSTATUS = 0x%x\n", ahc_inb(ahc, SSTAT3),
- ahc_inb(ahc, DFSTATUS));
- printf("SSTAT0 = 0x%x, SCB_DATACNT = 0x%x\n",
- ahc_inb(ahc, SSTAT0),
- ahc_inb(ahc, SCB_DATACNT));
+ /* Ensure we don't leave the selection hardware on */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ printf("%s:%c:%d: no active SCB for reconnecting "
+ "target - issuing BUS DEVICE RESET\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target);
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+ ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n",
+ ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+ ahc_index_busy_tcl(ahc,
+ BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+ ahc_inb(ahc, SAVED_LUN))),
+ ahc_inb(ahc, SINDEX));
+ printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+ ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+ ahc_inb(ahc, SCB_CONTROL));
+ printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+ ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+ printf("SXFRCTL0 == 0x%x\n", ahc_inb(ahc, SXFRCTL0));
+ printf("SEQCTL == 0x%x\n", ahc_inb(ahc, SEQCTL));
+ ahc_dump_card_state(ahc);
+ ahc->msgout_buf[0] = MSG_BUS_DEV_RESET;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_assert_atn(ahc);
break;
}
+ case SEND_REJECT:
+ {
+ u_int rejbyte = ahc_inb(ahc, ACCUM);
+ printf("%s:%c:%d: Warning - unknown message received from "
+ "target (0x%x). Rejecting\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target, rejbyte);
+ break;
+ }
+ case PROTO_VIOLATION:
+ {
+ ahc_handle_proto_violation(ahc);
+ break;
+ }
+ case IGN_WIDE_RES:
+ ahc_handle_ign_wide_residue(ahc, &devinfo);
+ break;
+ case PDATA_REINIT:
+ ahc_reinitialize_dataptrs(ahc);
+ break;
+ case BAD_PHASE:
+ {
+ u_int lastphase;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ printf("%s:%c:%d: unknown scsi bus phase %x, "
+ "lastphase = 0x%x. Attempting to continue\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ lastphase, ahc_inb(ahc, SCSISIGI));
+ break;
+ }
+ case MISSED_BUSFREE:
+ {
+ u_int lastphase;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ printf("%s:%c:%d: Missed busfree. "
+ "Lastphase = 0x%x, Curphase = 0x%x\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ lastphase, ahc_inb(ahc, SCSISIGI));
+ ahc_restart(ahc);
+ return;
+ }
case HOST_MSG_LOOP:
{
/*
@@ -1925,12 +676,15 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
* that requires host assistance for completion.
* While handling the message phase(s), we will be
* notified by the sequencer after each byte is
- * transferred so we can track bus phases.
+ * transfered so we can track bus phase changes.
*
- * If this is the first time we've seen a HOST_MSG_LOOP,
- * initialize the state of the host message loop.
+ * If this is the first time we've seen a HOST_MSG_LOOP
+ * interrupt, initialize the state of the host message
+ * loop.
*/
if (ahc->msg_type == MSG_TYPE_NONE) {
+ struct scb *scb;
+ u_int scb_index;
u_int bus_phase;
bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
@@ -1944,15 +698,16 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
* we got here. Just punt the message.
*/
ahc_clear_intstat(ahc);
- restart_sequencer(ahc);
+ ahc_restart(ahc);
+ return;
}
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
if (devinfo.role == ROLE_INITIATOR) {
- struct scb *scb;
- u_int scb_index;
-
- scb_index = ahc_inb(ahc, SCB_TAG);
- scb = &ahc->scb_data->scbarray[scb_index];
+ if (scb == NULL)
+ panic("HOST_MSG_LOOP with "
+ "invalid SCB %x\n", scb_index);
if (bus_phase == P_MESGOUT)
ahc_setup_initiator_msgout(ahc,
@@ -1963,19 +718,23 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
MSG_TYPE_INITIATOR_MSGIN;
ahc->msgin_index = 0;
}
- } else {
+ }
+#if AHC_TARGET_MODE
+ else {
if (bus_phase == P_MESGOUT) {
ahc->msg_type =
MSG_TYPE_TARGET_MSGOUT;
ahc->msgin_index = 0;
- } else
- /* XXX Ever executed??? */
- ahc_setup_target_msgin(ahc, &devinfo);
+ }
+ else
+ ahc_setup_target_msgin(ahc,
+ &devinfo,
+ scb);
}
+#endif
}
- /* Pass a NULL path so that handlers generate their own */
- ahc_handle_message_phase(ahc, /*path*/NULL);
+ ahc_handle_message_phase(ahc);
break;
}
case PERR_DETECTED:
@@ -1993,18 +752,58 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
*/
if ((intstat & SCSIINT) == 0
&& (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
- u_int curphase;
- /*
- * The hardware will only let you ack bytes
- * if the expected phase in SCSISIGO matches
- * the current phase. Make sure this is
- * currently the case.
- */
- curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
- ahc_outb(ahc, LASTPHASE, curphase);
- ahc_outb(ahc, SCSISIGO, curphase);
- ahc_inb(ahc, SCSIDATL);
+ if ((ahc->features & AHC_DT) == 0) {
+ u_int curphase;
+
+ /*
+ * The hardware will only let you ack bytes
+ * if the expected phase in SCSISIGO matches
+ * the current phase. Make sure this is
+ * currently the case.
+ */
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ ahc_outb(ahc, LASTPHASE, curphase);
+ ahc_outb(ahc, SCSISIGO, curphase);
+ }
+ if ((ahc_inb(ahc, SCSISIGI) & (CDI|MSGI)) == 0) {
+ int wait;
+
+ /*
+ * In a data phase. Faster to bitbucket
+ * the data than to individually ack each
+ * byte. This is also the only strategy
+ * that will work with AUTOACK enabled.
+ */
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) | BITBUCKET);
+ wait = 5000;
+ while (--wait != 0) {
+ if ((ahc_inb(ahc, SCSISIGI)
+ & (CDI|MSGI)) != 0)
+ break;
+ ahc_delay(100);
+ }
+ ahc_outb(ahc, SXFRCTL1,
+ ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET);
+ if (wait == 0) {
+ struct scb *scb;
+ u_int scb_index;
+
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Unable to clear parity error. "
+ "Resetting bus.\n");
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL)
+ ahc_set_transaction_status(scb,
+ CAM_UNCOR_PARITY);
+ ahc_reset_channel(ahc, devinfo.channel,
+ /*init reset*/TRUE);
+ }
+ } else {
+ ahc_inb(ahc, SCSIDATL);
+ }
}
break;
}
@@ -2020,140 +819,223 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
*/
u_int scbindex = ahc_inb(ahc, SCB_TAG);
u_int lastphase = ahc_inb(ahc, LASTPHASE);
- int i;
+ u_int i;
- scb = &ahc->scb_data->scbarray[scbindex];
+ scb = ahc_lookup_scb(ahc, scbindex);
for (i = 0; i < num_phases; i++) {
- if (lastphase == phase_table[i].phase)
+ if (lastphase == ahc_phase_table[i].phase)
break;
}
- scsipi_printaddr(scb->xs->xs_periph);
+ ahc_print_path(ahc, scb);
printf("data overrun detected %s."
" Tag == 0x%x.\n",
- phase_table[i].phasemsg,
+ ahc_phase_table[i].phasemsg,
scb->hscb->tag);
- scsipi_printaddr(scb->xs->xs_periph);
- printf("%s seen Data Phase. Length = %d. NumSGs = %d.\n",
+ ahc_print_path(ahc, scb);
+ printf("%s seen Data Phase. Length = %ld. NumSGs = %d.\n",
ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
- scb->xs->datalen, scb->sg_count);
+ ahc_get_transfer_length(scb), scb->sg_count);
if (scb->sg_count > 0) {
for (i = 0; i < scb->sg_count; i++) {
- printf("sg[%d] - Addr 0x%x : Length %d\n",
+
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
i,
- le32toh(scb->sg_list[i].addr),
- le32toh(scb->sg_list[i].len));
+ (ahc_le32toh(scb->sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahc_le32toh(scb->sg_list[i].addr),
+ ahc_le32toh(scb->sg_list[i].len)
+ & AHC_SG_LEN_MASK);
}
}
/*
- * Set this and it will take affect when the
+ * Set this and it will take effect when the
* target does a command complete.
*/
- ahc_freeze_devq(ahc, scb->xs->xs_periph);
- ahcsetccbstatus(scb->xs, XS_DRIVER_STUFFUP);
- ahc_freeze_ccb(scb);
+ ahc_freeze_devq(ahc, scb);
+ if ((scb->flags & SCB_SENSE) == 0) {
+ ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
+ } else {
+ scb->flags &= ~SCB_SENSE;
+ ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
+ }
+ ahc_freeze_scb(scb);
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ /*
+ * Clear the channel in case we return
+ * to data phase later.
+ */
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
+ }
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ u_int dscommand1;
+
+ /* Ensure HHADDR is 0 for future DMA operations. */
+ dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+ ahc_outb(ahc, HADDR, 0);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1);
+ }
break;
}
- case TRACEPOINT:
+ case MKMSG_FAILED:
{
- printf("TRACEPOINT: RETURN_1 = %d\n", ahc_inb(ahc, RETURN_1));
- printf("TRACEPOINT: RETURN_2 = %d\n", ahc_inb(ahc, RETURN_2));
- printf("TRACEPOINT: ARG_1 = %d\n", ahc_inb(ahc, ARG_1));
- printf("TRACEPOINT: ARG_2 = %d\n", ahc_inb(ahc, ARG_2));
- printf("TRACEPOINT: CCHADDR = %x\n",
- ahc_inb(ahc, CCHADDR) | (ahc_inb(ahc, CCHADDR+1) << 8)
- | (ahc_inb(ahc, CCHADDR+2) << 16)
- | (ahc_inb(ahc, CCHADDR+3) << 24));
-#if 0
- printf("SSTAT1 == 0x%x\n", ahc_inb(ahc, SSTAT1));
- printf("SSTAT0 == 0x%x\n", ahc_inb(ahc, SSTAT0));
- printf(", SCSISIGI == 0x%x\n", ahc_inb(ahc, SCSISIGI));
- printf("TRACEPOINT: CCHCNT = %d, SG_COUNT = %d\n",
- ahc_inb(ahc, CCHCNT), ahc_inb(ahc, SG_COUNT));
- printf("TRACEPOINT: SCB_TAG = %d\n", ahc_inb(ahc, SCB_TAG));
- printf("TRACEPOINT1: CCHADDR = %d, CCHCNT = %d, SCBPTR = %d\n",
- ahc_inb(ahc, CCHADDR)
- | (ahc_inb(ahc, CCHADDR+1) << 8)
- | (ahc_inb(ahc, CCHADDR+2) << 16)
- | (ahc_inb(ahc, CCHADDR+3) << 24),
- ahc_inb(ahc, CCHCNT)
- | (ahc_inb(ahc, CCHCNT+1) << 8)
- | (ahc_inb(ahc, CCHCNT+2) << 16),
- ahc_inb(ahc, SCBPTR));
- printf("TRACEPOINT: WAITING_SCBH = %d\n", ahc_inb(ahc, WAITING_SCBH));
- printf("TRACEPOINT: SCB_TAG = %d\n", ahc_inb(ahc, SCB_TAG));
-#ifdef DDB
- cpu_Debugger();
-#endif
-#endif
+ u_int scbindex;
+
+ printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ devinfo.lun);
+ scbindex = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbindex);
+ if (scb != NULL
+ && (scb->flags & SCB_RECOVERY_SCB) != 0)
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
+ break;
+ }
+ case NO_FREE_SCB:
+ {
+ printf("%s: No free or disconnected SCBs\n", ahc_name(ahc));
+ ahc_dump_card_state(ahc);
+ panic("for safety");
break;
}
-#if NOT_YET
- /* XXX Fill these in later */
- case MESG_BUFFER_BUSY:
+ case SCB_MISMATCH:
+ {
+ u_int scbptr;
+
+ scbptr = ahc_inb(ahc, SCBPTR);
+ printf("Bogus TAG after DMA. SCBPTR %d, tag %d, our tag %d\n",
+ scbptr, ahc_inb(ahc, ARG_1),
+ ahc->scb_data->hscbs[scbptr].tag);
+ ahc_dump_card_state(ahc);
+ panic("for saftey");
break;
- case MSGIN_PHASEMIS:
+ }
+ case OUT_OF_RANGE:
+ {
+ printf("%s: BTT calculation out of range\n", ahc_name(ahc));
+ printf("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
+ "ARG_1 == 0x%x ACCUM = 0x%x\n",
+ ahc_inb(ahc, SAVED_SCSIID), ahc_inb(ahc, SAVED_LUN),
+ ahc_inb(ahc, ARG_1), ahc_inb(ahc, ACCUM));
+ printf("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
+ "SINDEX == 0x%x\n, A == 0x%x\n",
+ ahc_inb(ahc, SEQ_FLAGS), ahc_inb(ahc, SCBPTR),
+ ahc_index_busy_tcl(ahc,
+ BUILD_TCL(ahc_inb(ahc, SAVED_SCSIID),
+ ahc_inb(ahc, SAVED_LUN))),
+ ahc_inb(ahc, SINDEX),
+ ahc_inb(ahc, ACCUM));
+ printf("SCSIID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
+ "SCB_TAG == 0x%x, SCB_CONTROL == 0x%x\n",
+ ahc_inb(ahc, SCSIID), ahc_inb(ahc, SCB_SCSIID),
+ ahc_inb(ahc, SCB_LUN), ahc_inb(ahc, SCB_TAG),
+ ahc_inb(ahc, SCB_CONTROL));
+ printf("SCSIBUSL == 0x%x, SCSISIGI == 0x%x\n",
+ ahc_inb(ahc, SCSIBUSL), ahc_inb(ahc, SCSISIGI));
+ ahc_dump_card_state(ahc);
+ panic("for safety");
break;
-#endif
+ }
default:
printf("ahc_intr: seqint, "
"intstat == 0x%x, scsisigi = 0x%x\n",
intstat, ahc_inb(ahc, SCSISIGI));
break;
}
-
unpause:
/*
* The sequencer is paused immediately on
* a SEQINT, so we should restart it when
* we're done.
*/
- unpause_sequencer(ahc);
+ ahc_unpause(ahc);
}
-static void
+void
ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
{
u_int scb_index;
+ u_int status0;
u_int status;
struct scb *scb;
char cur_channel;
char intr_channel;
if ((ahc->features & AHC_TWIN) != 0
- && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
+ && ((ahc_inb(ahc, SBLKCTL) & SELBUSB) != 0))
cur_channel = 'B';
else
cur_channel = 'A';
intr_channel = cur_channel;
- status = ahc_inb(ahc, SSTAT1);
- if (status == 0) {
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ status0 = ahc_inb(ahc, SSTAT0) & IOERR;
+ else
+ status0 = 0;
+ status = ahc_inb(ahc, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
+ if (status == 0 && status0 == 0) {
if ((ahc->features & AHC_TWIN) != 0) {
/* Try the other channel */
ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
- status = ahc_inb(ahc, SSTAT1);
- ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ status = ahc_inb(ahc, SSTAT1)
+ & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
intr_channel = (cur_channel == 'A') ? 'B' : 'A';
}
if (status == 0) {
printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_unpause(ahc);
return;
}
}
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
+
scb_index = ahc_inb(ahc, SCB_TAG);
- if (scb_index < ahc->scb_data->numscbs) {
- scb = &ahc->scb_data->scbarray[scb_index];
- if ((scb->flags & SCB_ACTIVE) == 0
- || (ahc_inb(ahc, SEQ_FLAGS) & IDENTIFY_SEEN) == 0)
- scb = NULL;
- } else
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb != NULL
+ && (ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
scb = NULL;
- if ((status & SCSIRSTI) != 0) {
+ if ((ahc->features & AHC_ULTRA2) != 0
+ && (status0 & IOERR) != 0) {
+ int now_lvd;
+
+ now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
+ printf("%s: Transceiver State Has Changed to %s mode\n",
+ ahc_name(ahc), now_lvd ? "LVD" : "SE");
+ ahc_outb(ahc, CLRSINT0, CLRIOERR);
+ /*
+ * When transitioning to SE mode, the reset line
+ * glitches, triggering an arbitration bug in some
+ * Ultra2 controllers. This bug is cleared when we
+ * assert the reset line. Since a reset glitch has
+ * already occurred with this transition and a
+ * transceiver state change is handled just like
+ * a bus reset anyway, asserting the reset line
+ * ourselves is safe.
+ */
+ ahc_reset_channel(ahc, intr_channel,
+ /*Initiate Reset*/now_lvd == 0);
+ } else if ((status & SCSIRSTI) != 0) {
printf("%s: Someone reset channel %c\n",
ahc_name(ahc), intr_channel);
- ahc_reset_channel(ahc, intr_channel, /* Initiate Reset */FALSE);
+ if (intr_channel != cur_channel)
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
+ ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
} else if ((status & SCSIPERR) != 0) {
/*
* Determine the bus phase and queue an appropriate message.
@@ -2169,10 +1051,14 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
u_int curphase;
u_int errorphase;
u_int lastphase;
- int i;
+ u_int scsirate;
+ u_int i;
+ u_int sstat2;
+ int silent;
lastphase = ahc_inb(ahc, LASTPHASE);
curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ sstat2 = ahc_inb(ahc, SSTAT2);
ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
/*
* For all phases save DATA, the sequencer won't
@@ -2186,32 +1072,62 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
* curphase and lastphase.
*/
if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
- || curphase == P_DATAIN)
+ || curphase == P_DATAIN || curphase == P_DATAIN_DT)
errorphase = curphase;
else
errorphase = lastphase;
for (i = 0; i < num_phases; i++) {
- if (errorphase == phase_table[i].phase)
+ if (errorphase == ahc_phase_table[i].phase)
break;
}
- mesg_out = phase_table[i].mesg_out;
- if (scb != NULL)
- scsipi_printaddr(scb->xs->xs_periph);
- else
- printf("%s:%c:%d: ", ahc_name(ahc),
- intr_channel,
- TCL_TARGET(ahc_inb(ahc, SAVED_TCL)));
+ mesg_out = ahc_phase_table[i].mesg_out;
+ silent = FALSE;
+ if (scb != NULL) {
+ if (SCB_IS_SILENT(scb))
+ silent = TRUE;
+ else
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_TRANSMISSION_ERROR;
+ } else
+ printf("%s:%c:%d: ", ahc_name(ahc), intr_channel,
+ SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
+ scsirate = ahc_inb(ahc, SCSIRATE);
+ if (silent == FALSE) {
+ printf("parity error detected %s. "
+ "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
+ ahc_phase_table[i].phasemsg,
+ ahc_inw(ahc, SEQADDR0),
+ scsirate);
+ if ((ahc->features & AHC_DT) != 0) {
+ if ((sstat2 & CRCVALERR) != 0)
+ printf("\tCRC Value Mismatch\n");
+ if ((sstat2 & CRCENDERR) != 0)
+ printf("\tNo terminal CRC packet "
+ "recevied\n");
+ if ((sstat2 & CRCREQERR) != 0)
+ printf("\tIllegal CRC packet "
+ "request\n");
+ if ((sstat2 & DUAL_EDGE_ERR) != 0)
+ printf("\tUnexpected %sDT Data Phase\n",
+ (scsirate & SINGLE_EDGE)
+ ? "" : "non-");
+ }
+ }
- printf("parity error detected %s. "
- "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
- phase_table[i].phasemsg,
- ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8),
- ahc_inb(ahc, SCSIRATE));
+ if ((ahc->features & AHC_DT) != 0
+ && (sstat2 & DUAL_EDGE_ERR) != 0) {
+ /*
+ * This error applies regardless of
+ * data direction, so ignore the value
+ * in the phase table.
+ */
+ mesg_out = MSG_INITIATOR_DET_ERR;
+ }
/*
- * We've set the hardware to assert ATN if we
- * get a parity error on "in" phases, so all we
+ * We've set the hardware to assert ATN if we
+ * get a parity error on "in" phases, so all we
* need to do is stuff the message buffer with
* the appropriate message. "In" phases have set
* mesg_out to something other than MSG_NOP.
@@ -2222,77 +1138,203 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
else
ahc_outb(ahc, MSG_OUT, mesg_out);
}
+ /*
+ * Force a renegotiation with this target just in
+ * case we are out of sync for some external reason
+ * unknown (or unreported) by the target.
+ */
+ ahc_force_renegotiation(ahc);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
- unpause_sequencer(ahc);
+ ahc_unpause(ahc);
+ } else if ((status & SELTO) != 0) {
+ u_int scbptr;
+
+ /* Stop the selection */
+ ahc_outb(ahc, SCSISEQ, 0);
+
+ /* No more pending messages */
+ ahc_clear_msg_state(ahc);
+
+ /* Clear interrupt state */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+
+ /*
+ * Although the driver does not care about the
+ * 'Selection in Progress' status bit, the busy
+ * LED does. SELINGO is only cleared by a sucessfull
+ * selection, so we must manually clear it to insure
+ * the LED turns off just incase no future successful
+ * selections occur (e.g. no devices on the bus).
+ */
+ ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+ scbptr = ahc_inb(ahc, WAITING_SCBH);
+ ahc_outb(ahc, SCBPTR, scbptr);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("%s: ahc_intr - referenced scb not "
+ "valid during SELTO scb(%d, %d)\n",
+ ahc_name(ahc), scbptr, scb_index);
+ ahc_dump_card_state(ahc);
+ } else {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_SELTO) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Saw Selection Timeout for SCB 0x%x\n",
+ scb_index);
+ }
+#endif
+ /*
+ * Force a renegotiation with this target just in
+ * case the cable was pulled and will later be
+ * re-attached. The target may forget its negotiation
+ * settings with us should it attempt to reselect
+ * during the interruption. The target will not issue
+ * a unit attention in this case, so we must always
+ * renegotiate.
+ */
+ ahc_force_renegotiation(ahc);
+ ahc_set_transaction_status(scb, CAM_SEL_TIMEOUT);
+ ahc_freeze_devq(ahc, scb);
+ }
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_restart(ahc);
} else if ((status & BUSFREE) != 0
&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+ u_int lastphase;
+ u_int saved_scsiid;
+ u_int saved_lun;
+ u_int target;
+ u_int initiator_role_id;
+ char channel;
+ int printerror;
+
+ /*
+ * Clear our selection hardware as soon as possible.
+ * We may have an entry in the waiting Q for this target,
+ * that is affected by this busfree and we don't want to
+ * go about selecting the target while we handle the event.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+
+ /*
+ * Disable busfree interrupts and clear the busfree
+ * interrupt status. We do this here so that several
+ * bus transactions occur prior to clearing the SCSIINT
+ * latch. It can take a bit for the clearing to take effect.
+ */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
+
/*
- * First look at what phase we were last in.
+ * Look at what phase we were last in.
* If its message out, chances are pretty good
* that the busfree was in response to one of
* our abort requests.
*/
- u_int lastphase = ahc_inb(ahc, LASTPHASE);
- u_int saved_tcl = ahc_inb(ahc, SAVED_TCL);
- u_int target = TCL_TARGET(saved_tcl);
- u_int initiator_role_id = TCL_SCSI_ID(ahc, saved_tcl);
- char channel = TCL_CHANNEL(ahc, saved_tcl);
- int printerror = 1;
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ saved_lun = ahc_inb(ahc, SAVED_LUN);
+ target = SCSIID_TARGET(ahc, saved_scsiid);
+ initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
+ channel = SCSIID_CHANNEL(ahc, saved_scsiid);
+ printerror = 1;
- ahc_outb(ahc, SCSISEQ,
- ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
if (lastphase == P_MESGOUT) {
- u_int message;
+ struct ahc_devinfo devinfo;
u_int tag;
- message = ahc->msgout_buf[ahc->msgout_index - 1];
+ ahc_fetch_devinfo(ahc, &devinfo);
tag = SCB_LIST_NULL;
- switch (message) {
- case MSG_ABORT_TAG:
+ if (ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT_TAG, TRUE)
+ || ahc_sent_msg(ahc, AHCMSG_1B, MSG_ABORT, TRUE)) {
+ if (ahc->msgout_buf[ahc->msgout_index - 1]
+ == MSG_ABORT_TAG)
tag = scb->hscb->tag;
- /* FALLTRHOUGH */
- case MSG_ABORT:
- scsipi_printaddr(scb->xs->xs_periph);
- printf("SCB %x - Abort %s Completed.\n",
+ ahc_print_path(ahc, scb);
+ printf("SCB %d - Abort%s Completed.\n",
scb->hscb->tag, tag == SCB_LIST_NULL ?
- "" : "Tag");
+ "" : " Tag");
ahc_abort_scbs(ahc, target, channel,
- TCL_LUN(saved_tcl), tag,
+ saved_lun, tag,
ROLE_INITIATOR,
- XS_DRIVER_STUFFUP);
+ CAM_REQ_ABORTED);
printerror = 0;
- break;
- case MSG_BUS_DEV_RESET:
- {
- struct ahc_devinfo devinfo;
-
- if (scb != NULL &&
- (scb->xs->xs_control & XS_CTL_RESET)
- && ahc_match_scb(scb, target, channel,
- TCL_LUN(saved_tcl),
+ } else if (ahc_sent_msg(ahc, AHCMSG_1B,
+ MSG_BUS_DEV_RESET, TRUE)) {
+#ifdef __FreeBSD__
+ /*
+ * Don't mark the user's request for this BDR
+ * as completing with CAM_BDR_SENT. CAM3
+ * specifies CAM_REQ_CMP.
+ */
+ if (scb != NULL
+ && scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
+ && ahc_match_scb(ahc, scb, target, channel,
+ CAM_LUN_WILDCARD,
SCB_LIST_NULL,
ROLE_INITIATOR)) {
- ahcsetccbstatus(scb->xs, XS_NOERROR);
+ ahc_set_transaction_status(scb, CAM_REQ_CMP);
}
+#endif
ahc_compile_devinfo(&devinfo,
initiator_role_id,
target,
- TCL_LUN(saved_tcl),
+ CAM_LUN_WILDCARD,
channel,
ROLE_INITIATOR);
ahc_handle_devreset(ahc, &devinfo,
- XS_RESET,
+ CAM_BDR_SENT,
"Bus Device Reset",
/*verbose_level*/0);
printerror = 0;
- break;
- }
- default:
- break;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_PPR, FALSE)) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ /*
+ * PPR Rejected. Try non-ppr negotiation
+ * and retry command.
+ */
+ tinfo = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->goal.ppr_options = 0;
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_WDTR, FALSE)
+ || ahc_sent_msg(ahc, AHCMSG_EXT,
+ MSG_EXT_SDTR, FALSE)) {
+ /*
+ * Negotiation Rejected. Go-async and
+ * retry command.
+ */
+ ahc_set_width(ahc, &devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_set_syncrate(ahc, &devinfo,
+ /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0,
+ AHC_TRANS_CUR|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ printerror = 0;
}
}
if (printerror != 0) {
- int i;
+ u_int i;
if (scb != NULL) {
u_int tag;
@@ -2301,11 +1343,11 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
tag = scb->hscb->tag;
else
tag = SCB_LIST_NULL;
+ ahc_print_path(ahc, scb);
ahc_abort_scbs(ahc, target, channel,
- SCB_LUN(scb), tag,
+ SCB_GET_LUN(scb), tag,
ROLE_INITIATOR,
- XS_DRIVER_STUFFUP);
- scsipi_printaddr(scb->xs->xs_periph);
+ CAM_UNEXP_BUSFREE);
} else {
/*
* We had not fully identified this connection,
@@ -2314,120 +1356,917 @@ ahc_handle_scsiint(struct ahc_softc *ahc, u_int intstat)
printf("%s: ", ahc_name(ahc));
}
for (i = 0; i < num_phases; i++) {
- if (lastphase == phase_table[i].phase)
+ if (lastphase == ahc_phase_table[i].phase)
break;
}
+ /*
+ * Renegotiate with this device at the
+ * next oportunity just in case this busfree
+ * is due to a negotiation mismatch with the
+ * device.
+ */
+ ahc_force_renegotiation(ahc);
printf("Unexpected busfree %s\n"
"SEQADDR == 0x%x\n",
- phase_table[i].phasemsg, ahc_inb(ahc, SEQADDR0)
+ ahc_phase_table[i].phasemsg,
+ ahc_inb(ahc, SEQADDR0)
| (ahc_inb(ahc, SEQADDR1) << 8));
}
- ahc_clear_msg_state(ahc);
- ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
- ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
- restart_sequencer(ahc);
- } else if ((status & SELTO) != 0) {
- u_int scbptr;
+ ahc_restart(ahc);
+ } else {
+ printf("%s: Missing case in ahc_handle_scsiint. status = %x\n",
+ ahc_name(ahc), status);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
+}
- scbptr = ahc_inb(ahc, WAITING_SCBH);
- ahc_outb(ahc, SCBPTR, scbptr);
- scb_index = ahc_inb(ahc, SCB_TAG);
+/*
+ * Force renegotiation to occur the next time we initiate
+ * a command to the current device.
+ */
+static void
+ahc_force_renegotiation(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
+ struct ahc_initiator_tinfo *targ_info;
+ struct ahc_tmode_tstate *tstate;
- if (scb_index < ahc->scb_data->numscbs) {
- scb = &ahc->scb_data->scbarray[scb_index];
- if ((scb->flags & SCB_ACTIVE) == 0)
- scb = NULL;
- } else
- scb = NULL;
+ ahc_fetch_devinfo(ahc, &devinfo);
+ targ_info = ahc_fetch_transinfo(ahc,
+ devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target,
+ &tstate);
+ ahc_update_neg_request(ahc, &devinfo, tstate,
+ targ_info, AHC_NEG_IF_NON_ASYNC);
+}
- if (scb == NULL) {
- printf("%s: ahc_intr - referenced scb not "
- "valid during SELTO scb(%d, %d)\n",
- ahc_name(ahc), scbptr, scb_index);
- } else {
- u_int tag;
+#define AHC_MAX_STEPS 2000
+void
+ahc_clear_critical_section(struct ahc_softc *ahc)
+{
+ int stepping;
+ int steps;
+ u_int simode0;
+ u_int simode1;
- tag = SCB_LIST_NULL;
- if ((scb->hscb->control & TAG_ENB) != 0)
- tag = scb->hscb->tag;
+ if (ahc->num_critical_sections == 0)
+ return;
- ahc_abort_scbs(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb),
- SCB_LUN(scb), tag,
- ROLE_INITIATOR, XS_SELTIMEOUT);
- }
- /* Stop the selection */
- ahc_outb(ahc, SCSISEQ, 0);
+ stepping = FALSE;
+ steps = 0;
+ simode0 = 0;
+ simode1 = 0;
+ for (;;) {
+ struct cs *cs;
+ u_int seqaddr;
+ u_int i;
- /* No more pending messages */
- ahc_clear_msg_state(ahc);
+ seqaddr = ahc_inb(ahc, SEQADDR0)
+ | (ahc_inb(ahc, SEQADDR1) << 8);
/*
- * Although the driver does not care about the
- * 'Selection in Progress' status bit, the busy
- * LED does. SELINGO is only cleared by a successful
- * selection, so we must manually clear it to ensure
- * the LED turns off just incase no future successful
- * selections occur (e.g. no devices on the bus).
+ * Seqaddr represents the next instruction to execute,
+ * so we are really executing the instruction just
+ * before it.
*/
- ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+ if (seqaddr != 0)
+ seqaddr -= 1;
+ cs = ahc->critical_sections;
+ for (i = 0; i < ahc->num_critical_sections; i++, cs++) {
+
+ if (cs->begin < seqaddr && cs->end >= seqaddr)
+ break;
+ }
- /* Clear interrupt state */
- ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
- ahc_outb(ahc, CLRINT, CLRSCSIINT);
- restart_sequencer(ahc);
- } else {
- scsipi_printaddr(scb->xs->xs_periph);
- printf("Unknown SCSIINT. Status = 0x%x\n", status);
- ahc_outb(ahc, CLRSINT1, status);
- ahc_outb(ahc, CLRINT, CLRSCSIINT);
- unpause_sequencer(ahc);
+ if (i == ahc->num_critical_sections)
+ break;
+
+ if (steps > AHC_MAX_STEPS) {
+ printf("%s: Infinite loop in critical section\n",
+ ahc_name(ahc));
+ ahc_dump_card_state(ahc);
+ panic("critical section loop");
+ }
+
+ steps++;
+ if (stepping == FALSE) {
+
+ /*
+ * Disable all interrupt sources so that the
+ * sequencer will not be stuck by a pausing
+ * interrupt condition while we attempt to
+ * leave a critical section.
+ */
+ simode0 = ahc_inb(ahc, SIMODE0);
+ ahc_outb(ahc, SIMODE0, 0);
+ simode1 = ahc_inb(ahc, SIMODE1);
+ if ((ahc->features & AHC_DT) != 0)
+ /*
+ * On DT class controllers, we
+ * use the enhanced busfree logic.
+ * Unfortunately we cannot re-enable
+ * busfree detection within the
+ * current connection, so we must
+ * leave it on while single stepping.
+ */
+ ahc_outb(ahc, SIMODE1, ENBUSFREE);
+ else
+ ahc_outb(ahc, SIMODE1, 0);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) | STEP);
+ stepping = TRUE;
+ }
+ if ((ahc->features & AHC_DT) != 0) {
+ ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ }
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+ while (!ahc_is_paused(ahc))
+ ahc_delay(200);
+ }
+ if (stepping) {
+ ahc_outb(ahc, SIMODE0, simode0);
+ ahc_outb(ahc, SIMODE1, simode1);
+ ahc_outb(ahc, SEQCTL, ahc_inb(ahc, SEQCTL) & ~STEP);
}
}
+/*
+ * Clear any pending interrupt status.
+ */
+void
+ahc_clear_intstat(struct ahc_softc *ahc)
+{
+ /* Clear any interrupt conditions this may have caused */
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
+ |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
+ CLRREQINIT);
+ ahc_flush_device_writes(ahc);
+ ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
+ ahc_flush_device_writes(ahc);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ ahc_flush_device_writes(ahc);
+}
+
+/**************************** Debugging Routines ******************************/
+#ifdef AHC_DEBUG
+uint32_t ahc_debug = 0; /* AHC_SHOW_MISC|AHC_SHOW_SENSE|AHC_DEBUG_OPTS;*/
+#endif
+
+void
+ahc_print_scb(struct scb *scb)
+{
+ int i;
+
+ struct hardware_scb *hscb = scb->hscb;
+
+ printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
+ (void *)scb,
+ hscb->control,
+ hscb->scsiid,
+ hscb->lun,
+ hscb->cdb_len);
+ printf("Shared Data: ");
+ for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
+ printf("%#02x", hscb->shared_data.cdb[i]);
+ printf(" dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
+ ahc_le32toh(hscb->dataptr),
+ ahc_le32toh(hscb->datacnt),
+ ahc_le32toh(hscb->sgptr),
+ hscb->tag);
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+ printf("sg[%d] - Addr 0x%x%x : Length %d\n",
+ i,
+ (ahc_le32toh(scb->sg_list[i].len) >> 24
+ & SG_HIGH_ADDR_BITS),
+ ahc_le32toh(scb->sg_list[i].addr),
+ ahc_le32toh(scb->sg_list[i].len));
+ }
+ }
+}
+
+/************************* Transfer Negotiation *******************************/
+/*
+ * Allocate per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
+static struct ahc_tmode_tstate *
+ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
+{
+ struct ahc_tmode_tstate *master_tstate;
+ struct ahc_tmode_tstate *tstate;
+ int i;
+
+ master_tstate = ahc->enabled_targets[ahc->our_id];
+ if (channel == 'B') {
+ scsi_id += 8;
+ master_tstate = ahc->enabled_targets[ahc->our_id_b + 8];
+ }
+ if (ahc->enabled_targets[scsi_id] != NULL
+ && ahc->enabled_targets[scsi_id] != master_tstate)
+ panic("%s: ahc_alloc_tstate - Target already allocated",
+ ahc_name(ahc));
+ tstate = (struct ahc_tmode_tstate*)malloc(sizeof(*tstate),
+ M_DEVBUF, M_NOWAIT);
+ if (tstate == NULL)
+ return (NULL);
+
+ /*
+ * If we have allocated a master tstate, copy user settings from
+ * the master tstate (taken from SRAM or the EEPROM) for this
+ * channel, but reset our current and goal settings to async/narrow
+ * until an initiator talks to us.
+ */
+ if (master_tstate != NULL) {
+ memcpy(tstate, master_tstate, sizeof(*tstate));
+ memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
+ tstate->ultraenb = 0;
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ memset(&tstate->transinfo[i].curr, 0,
+ sizeof(tstate->transinfo[i].curr));
+ memset(&tstate->transinfo[i].goal, 0,
+ sizeof(tstate->transinfo[i].goal));
+ }
+ } else
+ memset(tstate, 0, sizeof(*tstate));
+ ahc->enabled_targets[scsi_id] = tstate;
+ return (tstate);
+}
+
+#ifdef AHC_TARGET_MODE
+/*
+ * Free per target mode instance (ID we respond to as a target)
+ * transfer negotiation data structures.
+ */
static void
-ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
{
+ struct ahc_tmode_tstate *tstate;
+
/*
- * We need to initiate transfer negotiations.
- * If our current and goal settings are identical,
- * we want to renegotiate due to a check condition.
+ * Don't clean up our "master" tstate.
+ * It has our default user settings.
*/
+ if (((channel == 'B' && scsi_id == ahc->our_id_b)
+ || (channel == 'A' && scsi_id == ahc->our_id))
+ && force == FALSE)
+ return;
+
+ if (channel == 'B')
+ scsi_id += 8;
+ tstate = ahc->enabled_targets[scsi_id];
+ if (tstate != NULL)
+ free(tstate, M_DEVBUF);
+ ahc->enabled_targets[scsi_id] = NULL;
+}
+#endif
+
+/*
+ * Called when we have an active connection to a target on the bus,
+ * this function finds the nearest syncrate to the input period limited
+ * by the capabilities of the bus connectivity of and sync settings for
+ * the target.
+ */
+struct ahc_syncrate *
+ahc_devlimited_syncrate(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ u_int *period, u_int *ppr_options, role_t role)
+{
+ struct ahc_transinfo *transinfo;
+ u_int maxsync;
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
+ && (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
+ maxsync = AHC_SYNCRATE_DT;
+ } else {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ /* Can't do DT on an SE bus */
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ } else if ((ahc->features & AHC_ULTRA) != 0) {
+ maxsync = AHC_SYNCRATE_ULTRA;
+ } else {
+ maxsync = AHC_SYNCRATE_FAST;
+ }
+ /*
+ * Never allow a value higher than our current goal
+ * period otherwise we may allow a target initiated
+ * negotiation to go above the limit as set by the
+ * user. In the case of an initiator initiated
+ * sync negotiation, we limit based on the user
+ * setting. This allows the system to still accept
+ * incoming negotiations even if target initiated
+ * negotiation is not performed.
+ */
+ if (role == ROLE_TARGET)
+ transinfo = &tinfo->user;
+ else
+ transinfo = &tinfo->goal;
+ *ppr_options &= transinfo->ppr_options;
+ if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
+ maxsync = MAX(maxsync, AHC_SYNCRATE_ULTRA2);
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ if (transinfo->period == 0) {
+ *period = 0;
+ *ppr_options = 0;
+ return (NULL);
+ }
+ *period = MAX(*period, transinfo->period);
+ return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
+}
+
+/*
+ * Look up the valid period to SCSIRATE conversion in our table.
+ * Return the period and offset that should be sent to the target
+ * if this was the beginning of an SDTR.
+ */
+struct ahc_syncrate *
+ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+ u_int *ppr_options, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_DT) == 0)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ /* Skip all DT only entries if DT is not available */
+ if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && maxsync < AHC_SYNCRATE_ULTRA2)
+ maxsync = AHC_SYNCRATE_ULTRA2;
+
+ for (syncrate = &ahc_syncrates[maxsync];
+ syncrate->rate != NULL;
+ syncrate++) {
+
+ /*
+ * The Ultra2 table doesn't go as low
+ * as for the Fast/Ultra cards.
+ */
+ if ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))
+ break;
+
+ if (*period <= syncrate->period) {
+ /*
+ * When responding to a target that requests
+ * sync, the requested rate may fall between
+ * two rates that we can output, but still be
+ * a rate that we can receive. Because of this,
+ * we want to respond to the target with
+ * the same rate that it sent to us even
+ * if the period we use to send data to it
+ * is lower. Only lower the response period
+ * if we must.
+ */
+ if (syncrate == &ahc_syncrates[maxsync])
+ *period = syncrate->period;
+
+ /*
+ * At some speeds, we only support
+ * ST transfers.
+ */
+ if ((syncrate->sxfr_u2 & ST_SXFR) != 0)
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ break;
+ }
+ }
+
+ if ((*period == 0)
+ || (syncrate->rate == NULL)
+ || ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))) {
+ /* Use asynchronous transfers. */
+ *period = 0;
+ syncrate = NULL;
+ *ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+ }
+ return (syncrate);
+}
+
+/*
+ * Convert from an entry in our syncrate table to the SCSI equivalent
+ * sync "period" factor.
+ */
+u_int
+ahc_find_period(struct ahc_softc *ahc, u_int scsirate, u_int maxsync)
+{
+ struct ahc_syncrate *syncrate;
+
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ scsirate &= SXFR_ULTRA2;
+ else
+ scsirate &= SXFR;
+
+ syncrate = &ahc_syncrates[maxsync];
+ while (syncrate->rate != NULL) {
+
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ if (syncrate->sxfr_u2 == 0)
+ break;
+ else if (scsirate == (syncrate->sxfr_u2 & SXFR_ULTRA2))
+ return (syncrate->period);
+ } else if (scsirate == (syncrate->sxfr & SXFR)) {
+ return (syncrate->period);
+ }
+ syncrate++;
+ }
+ return (0); /* async */
+}
+
+/*
+ * Truncate the given synchronous offset to a value the
+ * current adapter type and syncrate are capable of.
+ */
+void
+ahc_validate_offset(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide, role_t role)
+{
+ u_int maxoffset;
+
+ /* Limit offset to what we can do */
+ if (syncrate == NULL) {
+ maxoffset = 0;
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ maxoffset = MAX_OFFSET_ULTRA2;
+ } else {
+ if (wide)
+ maxoffset = MAX_OFFSET_16BIT;
+ else
+ maxoffset = MAX_OFFSET_8BIT;
+ }
+ *offset = MIN(*offset, maxoffset);
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *offset = MIN(*offset, tinfo->user.offset);
+ else
+ *offset = MIN(*offset, tinfo->goal.offset);
+ }
+}
+
+/*
+ * Truncate the given transfer width parameter to a value the
+ * current adapter type is capable of.
+ */
+void
+ahc_validate_width(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
+ u_int *bus_width, role_t role)
+{
+ switch (*bus_width) {
+ default:
+ if (ahc->features & AHC_WIDE) {
+ /* Respond Wide */
+ *bus_width = MSG_EXT_WDTR_BUS_16_BIT;
+ break;
+ }
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ *bus_width = MSG_EXT_WDTR_BUS_8_BIT;
+ break;
+ }
+ if (tinfo != NULL) {
+ if (role == ROLE_TARGET)
+ *bus_width = MIN(tinfo->user.width, *bus_width);
+ else
+ *bus_width = MIN(tinfo->goal.width, *bus_width);
+ }
+}
+
+/*
+ * Update the bitmask of targets for which the controller should
+ * negotiate with at the next convenient oportunity. This currently
+ * means the next time we send the initial identify messages for
+ * a new transaction.
+ */
+int
+ahc_update_neg_request(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct ahc_tmode_tstate *tstate,
+ struct ahc_initiator_tinfo *tinfo, ahc_neg_type neg_type)
+{
+ u_int auto_negotiate_orig;
+
+ auto_negotiate_orig = tstate->auto_negotiate;
+ if (neg_type == AHC_NEG_ALWAYS) {
+ /*
+ * Force our "current" settings to be
+ * unknown so that unless a bus reset
+ * occurs the need to renegotiate is
+ * recorded persistently.
+ */
+ if ((ahc->features & AHC_WIDE) != 0)
+ tinfo->curr.width = AHC_WIDTH_UNKNOWN;
+ tinfo->curr.period = AHC_PERIOD_UNKNOWN;
+ tinfo->curr.offset = AHC_OFFSET_UNKNOWN;
+ }
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options
+ || (neg_type == AHC_NEG_IF_NON_ASYNC
+ && (tinfo->goal.offset != 0
+ || tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
+ || tinfo->goal.ppr_options != 0)))
+ tstate->auto_negotiate |= devinfo->target_mask;
+ else
+ tstate->auto_negotiate &= ~devinfo->target_mask;
+
+ return (auto_negotiate_orig != tstate->auto_negotiate);
+}
+
+/*
+ * Update the user/goal/curr tables of synchronous negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_syncrate(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate, u_int period,
+ u_int offset, u_int ppr_options, u_int type, int paused)
+{
struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
- int dowide;
- int dosync;
+ struct ahc_tmode_tstate *tstate;
+ u_int old_period;
+ u_int old_offset;
+ u_int old_ppr;
+ int active;
+ int update_needed;
+
+ active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+ update_needed = 0;
+
+ if (syncrate == NULL) {
+ period = 0;
+ offset = 0;
+ }
tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
devinfo->target, &tstate);
- dowide = tinfo->current.width != tinfo->goal.width;
- dosync = tinfo->current.period != tinfo->goal.period;
- if (!dowide && !dosync) {
- dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
- dosync = tinfo->goal.period != 0;
+ if ((type & AHC_TRANS_USER) != 0) {
+ tinfo->user.period = period;
+ tinfo->user.offset = offset;
+ tinfo->user.ppr_options = ppr_options;
+ }
+
+ if ((type & AHC_TRANS_GOAL) != 0) {
+ tinfo->goal.period = period;
+ tinfo->goal.offset = offset;
+ tinfo->goal.ppr_options = ppr_options;
}
- if (dowide) {
- ahc_construct_wdtr(ahc, tinfo->goal.width);
- } else if (dosync) {
- const struct ahc_syncrate *rate;
- u_int period;
- u_int offset;
+ old_period = tinfo->curr.period;
+ old_offset = tinfo->curr.offset;
+ old_ppr = tinfo->curr.ppr_options;
- period = tinfo->goal.period;
- rate = ahc_devlimited_syncrate(ahc, &period);
- offset = tinfo->goal.offset;
- ahc_validate_offset(ahc, rate, &offset,
- tinfo->current.width);
- ahc_construct_sdtr(ahc, period, offset);
+ if ((type & AHC_TRANS_CUR) != 0
+ && (old_period != period
+ || old_offset != offset
+ || old_ppr != ppr_options)) {
+ u_int scsirate;
+
+ update_needed++;
+ scsirate = tinfo->scsirate;
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+
+ scsirate &= ~(SXFR_ULTRA2|SINGLE_EDGE|ENABLE_CRC);
+ if (syncrate != NULL) {
+ scsirate |= syncrate->sxfr_u2;
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0)
+ scsirate |= ENABLE_CRC;
+ else
+ scsirate |= SINGLE_EDGE;
+ }
+ } else {
+
+ scsirate &= ~(SXFR|SOFS);
+ /*
+ * Ensure Ultra mode is set properly for
+ * this target.
+ */
+ tstate->ultraenb &= ~devinfo->target_mask;
+ if (syncrate != NULL) {
+ if (syncrate->sxfr & ULTRA_SXFR) {
+ tstate->ultraenb |=
+ devinfo->target_mask;
+ }
+ scsirate |= syncrate->sxfr & SXFR;
+ scsirate |= offset & SOFS;
+ }
+ if (active) {
+ u_int sxfrctl0;
+
+ sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ sxfrctl0 &= ~FAST20;
+ if (tstate->ultraenb & devinfo->target_mask)
+ sxfrctl0 |= FAST20;
+ ahc_outb(ahc, SXFRCTL0, sxfrctl0);
+ }
+ }
+ if (active) {
+ ahc_outb(ahc, SCSIRATE, scsirate);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIOFFSET, offset);
+ }
+
+ tinfo->scsirate = scsirate;
+ tinfo->curr.period = period;
+ tinfo->curr.offset = offset;
+ tinfo->curr.ppr_options = ppr_options;
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ if (offset != 0) {
+ printf("%s: target %d synchronous at %sMHz%s, "
+ "offset = 0x%x\n", ahc_name(ahc),
+ devinfo->target, syncrate->rate,
+ (ppr_options & MSG_EXT_PPR_DT_REQ)
+ ? " DT" : "", offset);
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers\n",
+ ahc_name(ahc), devinfo->target);
+ }
+ }
+ }
+
+ update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, AHC_NEG_TO_GOAL);
+
+ if (update_needed)
+ ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the user/goal/curr tables of wide negotiation
+ * parameters as well as, in the case of a current or active update,
+ * any data structures on the host controller. In the case of an
+ * active update, the specified target is currently talking to us on
+ * the bus, so the transfer parameter update must take effect
+ * immediately.
+ */
+void
+ahc_set_width(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int oldwidth;
+ int active;
+ int update_needed;
+
+ active = (type & AHC_TRANS_ACTIVE) == AHC_TRANS_ACTIVE;
+ update_needed = 0;
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if ((type & AHC_TRANS_USER) != 0)
+ tinfo->user.width = width;
+
+ if ((type & AHC_TRANS_GOAL) != 0)
+ tinfo->goal.width = width;
+
+ oldwidth = tinfo->curr.width;
+ if ((type & AHC_TRANS_CUR) != 0 && oldwidth != width) {
+ u_int scsirate;
+
+ update_needed++;
+ scsirate = tinfo->scsirate;
+ scsirate &= ~WIDEXFER;
+ if (width == MSG_EXT_WDTR_BUS_16_BIT)
+ scsirate |= WIDEXFER;
+
+ tinfo->scsirate = scsirate;
+
+ if (active)
+ ahc_outb(ahc, SCSIRATE, scsirate);
+
+ tinfo->curr.width = width;
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
+ if (bootverbose) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << width));
+ }
+ }
+
+ update_needed += ahc_update_neg_request(ahc, devinfo, tstate,
+ tinfo, AHC_NEG_TO_GOAL);
+ if (update_needed)
+ ahc_update_pending_scbs(ahc);
+}
+
+/*
+ * Update the current state of tagged queuing for a given target.
+ */
+void
+ahc_set_tags(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg)
+{
+ ahc_platform_set_tags(ahc, devinfo, alg);
+}
+
+/*
+ * When the transfer settings for a connection change, update any
+ * in-transit SCBs to contain the new data so the hardware will
+ * be set correctly during future (re)selections.
+ */
+static void
+ahc_update_pending_scbs(struct ahc_softc *ahc)
+{
+ struct scb *pending_scb;
+ int pending_scb_count;
+ int i;
+ int paused;
+ u_int saved_scbptr;
+
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings.
+ */
+ pending_scb_count = 0;
+ LIST_FOREACH(pending_scb, &ahc->pending_scbs, pending_links) {
+ struct ahc_devinfo devinfo;
+ struct hardware_scb *pending_hscb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ ahc_scb_devinfo(ahc, &devinfo, pending_scb);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
+ devinfo.our_scsiid,
+ devinfo.target, &tstate);
+ pending_hscb = pending_scb->hscb;
+ pending_hscb->control &= ~ULTRAENB;
+ if ((tstate->ultraenb & devinfo.target_mask) != 0)
+ pending_hscb->control |= ULTRAENB;
+ pending_hscb->scsirate = tinfo->scsirate;
+ pending_hscb->scsioffset = tinfo->curr.offset;
+ if ((tstate->auto_negotiate & devinfo.target_mask) == 0
+ && (pending_scb->flags & SCB_AUTO_NEGOTIATE) != 0) {
+ pending_scb->flags &= ~SCB_AUTO_NEGOTIATE;
+ pending_hscb->control &= ~MK_MESSAGE;
+ }
+ ahc_sync_scb(ahc, pending_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ pending_scb_count++;
+ }
+
+ if (pending_scb_count == 0)
+ return;
+
+ if (ahc_is_paused(ahc)) {
+ paused = 1;
} else {
- panic("ahc_intr: AWAITING_MSG for negotiation, "
- "but no negotiation needed");
+ paused = 0;
+ ahc_pause(ahc);
+ }
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ /* Ensure that the hscbs down on the card match the new information */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ struct hardware_scb *pending_hscb;
+ u_int control;
+ u_int scb_tag;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scb_tag = ahc_inb(ahc, SCB_TAG);
+ pending_scb = ahc_lookup_scb(ahc, scb_tag);
+ if (pending_scb == NULL)
+ continue;
+
+ pending_hscb = pending_scb->hscb;
+ control = ahc_inb(ahc, SCB_CONTROL);
+ control &= ~(ULTRAENB|MK_MESSAGE);
+ control |= pending_hscb->control & (ULTRAENB|MK_MESSAGE);
+ ahc_outb(ahc, SCB_CONTROL, control);
+ ahc_outb(ahc, SCB_SCSIRATE, pending_hscb->scsirate);
+ ahc_outb(ahc, SCB_SCSIOFFSET, pending_hscb->scsioffset);
+ }
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+
+ if (paused == 0)
+ ahc_unpause(ahc);
+}
+
+/**************************** Pathing Information *****************************/
+static void
+ahc_fetch_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ u_int saved_scsiid;
+ role_t role;
+ int our_id;
+
+ if (ahc_inb(ahc, SSTAT0) & TARGET)
+ role = ROLE_TARGET;
+ else
+ role = ROLE_INITIATOR;
+
+ if (role == ROLE_TARGET
+ && (ahc->features & AHC_MULTI_TID) != 0
+ && (ahc_inb(ahc, SEQ_FLAGS)
+ & (CMDPHASE_PENDING|TARG_CMD_PENDING|NO_DISCONNECT)) != 0) {
+ /* We were selected, so pull our id from TARGIDIN */
+ our_id = ahc_inb(ahc, TARGIDIN) & OID;
+ } else if ((ahc->features & AHC_ULTRA2) != 0)
+ our_id = ahc_inb(ahc, SCSIID_ULTRA2) & OID;
+ else
+ our_id = ahc_inb(ahc, SCSIID) & OID;
+
+ saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
+ ahc_compile_devinfo(devinfo,
+ our_id,
+ SCSIID_TARGET(ahc, saved_scsiid),
+ ahc_inb(ahc, SAVED_LUN),
+ SCSIID_CHANNEL(ahc, saved_scsiid),
+ role);
+}
+
+struct ahc_phase_table_entry*
+ahc_lookup_phase_entry(int phase)
+{
+ struct ahc_phase_table_entry *entry;
+ struct ahc_phase_table_entry *last_entry;
+
+ /*
+ * num_phases doesn't include the default entry which
+ * will be returned if the phase doesn't match.
+ */
+ last_entry = &ahc_phase_table[num_phases];
+ for (entry = ahc_phase_table; entry < last_entry; entry++) {
+ if (phase == entry->phase)
+ break;
}
+ return (entry);
+}
+
+void
+ahc_compile_devinfo(struct ahc_devinfo *devinfo, u_int our_id, u_int target,
+ u_int lun, char channel, role_t role)
+{
+ devinfo->our_scsiid = our_id;
+ devinfo->target = target;
+ devinfo->lun = lun;
+ devinfo->target_offset = target;
+ devinfo->channel = channel;
+ devinfo->role = role;
+ if (channel == 'B')
+ devinfo->target_offset += 8;
+ devinfo->target_mask = (0x01 << devinfo->target_offset);
+}
+
+void
+ahc_print_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ printf("%s:%c:%d:%d: ", ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
}
static void
+ahc_scb_devinfo(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
+{
+ role_t role;
+ int our_id;
+
+ our_id = SCSIID_OUR_ID(scb->hscb->scsiid);
+ role = ROLE_INITIATOR;
+ if ((scb->flags & SCB_TARGET_SCB) != 0)
+ role = ROLE_TARGET;
+ ahc_compile_devinfo(devinfo, our_id, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_LUN(scb), SCB_GET_CHANNEL(ahc, scb), role);
+}
+
+
+/************************ Message Phase Processing ****************************/
+static void
+ahc_assert_atn(struct ahc_softc *ahc)
+{
+ u_int scsisigo;
+
+ scsisigo = ATNO;
+ if ((ahc->features & AHC_DT) == 0)
+ scsisigo |= ahc_inb(ahc, SCSISIGI);
+ ahc_outb(ahc, SCSISIGO, scsisigo);
+}
+
+/*
+ * When an initiator transaction with the MK_MESSAGE flag either reconnects
+ * or enters the initial message out phase, we are interrupted. Fill our
+ * outgoing message buffer with the appropriate message and beging handing
+ * the message phase(s) manually.
+ */
+static void
ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
struct scb *scb)
{
@@ -2443,7 +2282,7 @@ ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
&& ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
u_int identify_msg;
- identify_msg = MSG_IDENTIFYFLAG | SCB_LUN(scb);
+ identify_msg = MSG_IDENTIFYFLAG | SCB_GET_LUN(scb);
if ((scb->hscb->control & DISCENB) != 0)
identify_msg |= MSG_IDENTIFY_DISCFLAG;
ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
@@ -2451,7 +2290,7 @@ ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
if ((scb->hscb->control & TAG_ENB) != 0) {
ahc->msgout_buf[ahc->msgout_index++] =
- scb->xs->xs_tag_type;
+ scb->hscb->control & (TAG_ENB|SCB_TAG_TYPE);
ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
ahc->msgout_len += 2;
}
@@ -2460,21 +2299,40 @@ ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
if (scb->flags & SCB_DEVICE_RESET) {
ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
ahc->msgout_len++;
- scsipi_printaddr(scb->xs->xs_periph);
+ ahc_print_path(ahc, scb);
printf("Bus Device Reset Message Sent\n");
- } else if (scb->flags & SCB_ABORT) {
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else if ((scb->flags & SCB_ABORT) != 0) {
if ((scb->hscb->control & TAG_ENB) != 0)
ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT_TAG;
else
ahc->msgout_buf[ahc->msgout_index++] = MSG_ABORT;
ahc->msgout_len++;
- scsipi_printaddr(scb->xs->xs_periph);
- printf("Abort Message Sent\n");
- } else if ((ahc->targ_msg_req & devinfo->target_mask) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Abort%s Message Sent\n",
+ (scb->hscb->control & TAG_ENB) != 0 ? " Tag" : "");
+ /*
+ * Clear our selection hardware in advance of
+ * the busfree. We may have an entry in the waiting
+ * Q for this target, and we don't want to go about
+ * selecting while we handle the busfree and blow it
+ * away.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else if ((scb->flags & (SCB_AUTO_NEGOTIATE|SCB_NEGOTIATE)) != 0) {
ahc_build_transfer_msg(ahc, devinfo);
} else {
printf("ahc_intr: AWAITING_MSG for an SCB that "
- "does not have a waiting message");
+ "does not have a waiting message\n");
+ printf("SCSIID = %x, target_mask = %x\n", scb->hscb->scsiid,
+ devinfo->target_mask);
panic("SCB = %d, SCB Control = %x, MSG_OUT = %x "
"SCB flags = %x", scb->hscb->tag, scb->hscb->control,
ahc_inb(ahc, MSG_OUT), scb->flags);
@@ -2485,150 +2343,301 @@ ahc_setup_initiator_msgout(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
* asked to send this message again.
*/
ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+ scb->hscb->control &= ~MK_MESSAGE;
ahc->msgout_index = 0;
ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
}
+/*
+ * Build an appropriate transfer negotiation message for the
+ * currently active target.
+ */
static void
-ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+ahc_build_transfer_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{
/*
- * To facilitate adding multiple messages together,
- * each routine should increment the index and len
- * variables instead of setting them explicitly.
+ * We need to initiate transfer negotiations.
+ * If our current and goal settings are identical,
+ * we want to renegotiate due to a check condition.
*/
- ahc->msgout_index = 0;
- ahc->msgout_len = 0;
-
- if ((ahc->targ_msg_req & devinfo->target_mask) != 0)
- ahc_build_transfer_msg(ahc, devinfo);
- else
- panic("ahc_intr: AWAITING target message with no message");
-
- ahc->msgout_index = 0;
- ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
-}
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_syncrate *rate;
+ int dowide;
+ int dosync;
+ int doppr;
+ u_int period;
+ u_int ppr_options;
+ u_int offset;
-static int
-ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
-{
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
/*
- * What we care about here is if we had an
- * outstanding SDTR or WDTR message for this
- * target. If we did, this is a signal that
- * the target is refusing negotiation.
+ * Filter our period based on the current connection.
+ * If we can't perform DT transfers on this segment (not in LVD
+ * mode for instance), then our decision to issue a PPR message
+ * may change.
*/
- struct scb *scb;
- u_int scb_index;
- u_int last_msg;
- int response = 0;
-
- scb_index = ahc_inb(ahc, SCB_TAG);
- scb = &ahc->scb_data->scbarray[scb_index];
-
- /* Might be necessary */
- last_msg = ahc_inb(ahc, LAST_MSG);
+ period = tinfo->goal.period;
+ ppr_options = tinfo->goal.ppr_options;
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ ppr_options = 0;
+ rate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options, devinfo->role);
+ dowide = tinfo->curr.width != tinfo->goal.width;
+ dosync = tinfo->curr.period != period;
+ /*
+ * Only use PPR if we have options that need it, even if the device
+ * claims to support it. There might be an expander in the way
+ * that doesn't.
+ */
+ doppr = ppr_options != 0;
- if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/FALSE)) {
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
+ if (!dowide && !dosync && !doppr) {
+ dowide = tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT;
+ dosync = tinfo->goal.offset != 0;
+ }
-#ifdef AHC_DEBUG_NEG
- /* note 8bit xfers */
- printf("%s:%c:%d: refuses WIDE negotiation. Using "
- "8bit transfers\n", ahc_name(ahc),
- devinfo->channel, devinfo->target);
-#endif
- ahc_set_width(ahc, devinfo,
- MSG_EXT_WDTR_BUS_8_BIT,
- AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
- /*paused*/TRUE, /*done*/TRUE);
+ if (!dowide && !dosync && !doppr) {
/*
- * No need to clear the sync rate. If the target
- * did not accept the command, our syncrate is
- * unaffected. If the target started the negotiation,
- * but rejected our response, we already cleared the
- * sync rate before sending our WDTR.
+ * Force async with a WDTR message if we have a wide bus,
+ * or just issue an SDTR with a 0 offset.
*/
- tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
- devinfo->our_scsiid,
- devinfo->target, &tstate);
- if (tinfo->goal.period) {
- u_int period;
+ if ((ahc->features & AHC_WIDE) != 0)
+ dowide = 1;
+ else
+ dosync = 1;
- /* Start the sync negotiation */
- period = tinfo->goal.period;
- ahc_devlimited_syncrate(ahc, &period);
- ahc->msgout_index = 0;
- ahc->msgout_len = 0;
- ahc_construct_sdtr(ahc, period, tinfo->goal.offset);
- ahc->msgout_index = 0;
- response = 1;
- } else
- ahc_update_xfer_mode(ahc, devinfo);
- } else if (ahc_sent_msg(ahc, MSG_EXT_SDTR, /*full*/FALSE)) {
- /* note asynch xfers and clear flag */
- ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
- /*offset*/0, AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
- /*paused*/TRUE, /*done*/TRUE);
-#ifdef AHC_DEBUG_NEG
- printf("%s:%c:%d: refuses synchronous negotiation. "
- "Using asynchronous transfers\n",
- ahc_name(ahc),
- devinfo->channel, devinfo->target);
-#endif
- ahc_update_xfer_mode(ahc, devinfo);
- } else if ((scb->hscb->control & TAG_ENB) != 0) {
- printf("%s:%c:%d: refuses tagged commands. Performing "
- "non-tagged I/O\n", ahc_name(ahc),
- devinfo->channel, devinfo->target);
+ if (bootverbose) {
+ ahc_print_devinfo(ahc, devinfo);
+ printf("Ensuring async\n");
+ }
+ }
- ahc_set_tags(ahc, devinfo, FALSE);
- ahc_update_xfer_mode(ahc, devinfo);
+ /* Target initiated PPR is not allowed in the SCSI spec */
+ if (devinfo->role == ROLE_TARGET)
+ doppr = 0;
- /*
- * Resend the identify for this CCB as the target
- * may believe that the selection is invalid otherwise.
- */
- ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL)
- & ~MSG_SIMPLE_Q_TAG);
- scb->hscb->control &= ~MSG_SIMPLE_Q_TAG;
- ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
- ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+ /*
+ * Both the PPR message and SDTR message require the
+ * goal syncrate to be limited to what the target device
+ * is capable of handling (based on whether an LVD->SE
+ * expander is on the bus), so combine these two cases.
+ * Regardless, guarantee that if we are using WDTR and SDTR
+ * messages that WDTR comes first.
+ */
+ if (doppr || (dosync && !dowide)) {
- /*
- * Requeue all tagged commands for this target
- * currently in our posession so they can be
- * converted to untagged commands.
- */
- ahc_search_qinfifo(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb),
- SCB_LUN(scb), /*tag*/SCB_LIST_NULL,
- ROLE_INITIATOR, SCB_REQUEUE,
- SEARCH_COMPLETE);
+ offset = tinfo->goal.offset;
+ ahc_validate_offset(ahc, tinfo, rate, &offset,
+ doppr ? tinfo->goal.width
+ : tinfo->curr.width,
+ devinfo->role);
+ if (doppr) {
+ ahc_construct_ppr(ahc, devinfo, period, offset,
+ tinfo->goal.width, ppr_options);
+ } else {
+ ahc_construct_sdtr(ahc, devinfo, period, offset);
+ }
} else {
- /*
- * Otherwise, we ignore it.
- */
- printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahc_construct_wdtr(ahc, devinfo, tinfo->goal.width);
+ }
+}
+
+/*
+ * Build a synchronous negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_sdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int period, u_int offset)
+{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
+ ahc->msgout_buf[ahc->msgout_index++] = period;
+ ahc->msgout_buf[ahc->msgout_index++] = offset;
+ ahc->msgout_len += 5;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending SDTR period %x, offset %x\n",
ahc_name(ahc), devinfo->channel, devinfo->target,
- last_msg);
+ devinfo->lun, period, offset);
}
- return (response);
}
+/*
+ * Build a wide negotiation message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_wdtr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int bus_width)
+{
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
+ ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+ ahc->msgout_len += 4;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending WDTR %x\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ devinfo->lun, bus_width);
+ }
+}
+
+/*
+ * Build a parallel protocol request message in our message
+ * buffer based on the input parameters.
+ */
+static void
+ahc_construct_ppr(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ u_int period, u_int offset, u_int bus_width,
+ u_int ppr_options)
+{
+ if (offset == 0)
+ period = AHC_ASYNC_XFER_PERIOD;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR_LEN;
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_PPR;
+ ahc->msgout_buf[ahc->msgout_index++] = period;
+ ahc->msgout_buf[ahc->msgout_index++] = 0;
+ ahc->msgout_buf[ahc->msgout_index++] = offset;
+ ahc->msgout_buf[ahc->msgout_index++] = bus_width;
+ ahc->msgout_buf[ahc->msgout_index++] = ppr_options;
+ ahc->msgout_len += 8;
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Sending PPR bus_width %x, period %x, "
+ "offset %x, ppr_options %x\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun,
+ bus_width, period, offset, ppr_options);
+ }
+}
+
+/*
+ * Clear any active message state.
+ */
static void
ahc_clear_msg_state(struct ahc_softc *ahc)
{
ahc->msgout_len = 0;
ahc->msgin_index = 0;
ahc->msg_type = MSG_TYPE_NONE;
+ if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0) {
+ /*
+ * The target didn't care to respond to our
+ * message request, so clear ATN.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ }
ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+ ahc_outb(ahc, SEQ_FLAGS2,
+ ahc_inb(ahc, SEQ_FLAGS2) & ~TARGET_MSG_PENDING);
}
static void
-ahc_handle_message_phase(struct ahc_softc *ahc, struct scsipi_periph *periph)
+ahc_handle_proto_violation(struct ahc_softc *ahc)
{
struct ahc_devinfo devinfo;
+ struct scb *scb;
+ u_int scbid;
+ u_int seq_flags;
+ u_int curphase;
+ u_int lastphase;
+ int found;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbid);
+ seq_flags = ahc_inb(ahc, SEQ_FLAGS);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ if ((seq_flags & NOT_IDENTIFIED) != 0) {
+
+ /*
+ * The reconnecting target either did not send an
+ * identify message, or did, but we didn't find an SCB
+ * to match.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Target did not send an IDENTIFY message. "
+ "LASTPHASE = 0x%x.\n", lastphase);
+ scb = NULL;
+ } else if (scb == NULL) {
+ /*
+ * We don't seem to have an SCB active for this
+ * transaction. Print an error and reset the bus.
+ */
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("No SCB found during protocol violation\n");
+ goto proto_violation_reset;
+ } else {
+ ahc_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
+ if ((seq_flags & NO_CDB_SENT) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("No or incomplete CDB sent to device.\n");
+ } else if ((ahc_inb(ahc, SCB_CONTROL) & STATUS_RCVD) == 0) {
+ /*
+ * The target never bothered to provide status to
+ * us prior to completing the command. Since we don't
+ * know the disposition of this command, we must attempt
+ * to abort it. Assert ATN and prepare to send an abort
+ * message.
+ */
+ ahc_print_path(ahc, scb);
+ printf("Completed command without status.\n");
+ } else {
+ ahc_print_path(ahc, scb);
+ printf("Unknown protocol violation.\n");
+ ahc_dump_card_state(ahc);
+ }
+ }
+ if ((lastphase & ~P_DATAIN_DT) == 0
+ || lastphase == P_COMMAND) {
+proto_violation_reset:
+ /*
+ * Target either went directly to data/command
+ * phase or didn't respond to our ATN.
+ * The only safe thing to do is to blow
+ * it away with a bus reset.
+ */
+ found = ahc_reset_channel(ahc, 'A', TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), 'A', found);
+ } else {
+ /*
+ * Leave the selection hardware off in case
+ * this abort attempt will affect yet to
+ * be sent commands.
+ */
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_assert_atn(ahc);
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ if (scb == NULL) {
+ ahc_print_devinfo(ahc, &devinfo);
+ ahc->msgout_buf[0] = MSG_ABORT_TASK;
+ ahc->msgout_len = 1;
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ } else {
+ ahc_print_path(ahc, scb);
+ scb->flags |= SCB_ABORT;
+ }
+ printf("Protocol violation %s. Attempting to abort.\n",
+ ahc_lookup_phase_entry(curphase)->phasemsg);
+ }
+}
+
+/*
+ * Manual message loop handler.
+ */
+static void
+ahc_handle_message_phase(struct ahc_softc *ahc)
+{
+ struct ahc_devinfo devinfo;
u_int bus_phase;
int end_session;
@@ -2645,10 +2654,23 @@ reswitch:
int msgdone;
if (ahc->msgout_len == 0)
- panic("REQINIT interrupt with no active message");
+ panic("HOST_MSG_LOOP interrupt with no active message");
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("INITIATOR_MSG_OUT");
+ }
+#endif
phasemis = bus_phase != P_MESGOUT;
if (phasemis) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahc_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
if (bus_phase == P_MESGIN) {
/*
* Change gears and see if
@@ -2669,6 +2691,10 @@ reswitch:
if (ahc->send_msg_perror) {
ahc_outb(ahc, CLRSINT1, CLRATNO);
ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n", ahc->send_msg_perror);
+#endif
ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
break;
}
@@ -2681,7 +2707,7 @@ reswitch:
* 0, and try again.
*/
ahc->msgout_index = 0;
- ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+ ahc_assert_atn(ahc);
}
lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
@@ -2695,6 +2721,11 @@ reswitch:
* the next byte on the bus.
*/
ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahc->msgout_buf[ahc->msgout_index]);
+#endif
ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
break;
}
@@ -2703,9 +2734,21 @@ reswitch:
int phasemis;
int message_done;
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("INITIATOR_MSG_IN");
+ }
+#endif
phasemis = bus_phase != P_MESGIN;
-
if (phasemis) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ printf(" PHASEMIS %s\n",
+ ahc_lookup_phase_entry(bus_phase)
+ ->phasemsg);
+ }
+#endif
ahc->msgin_index = 0;
if (bus_phase == P_MESGOUT
&& (ahc->send_msg_perror == TRUE
@@ -2720,8 +2763,13 @@ reswitch:
/* Pull the byte in without acking it */
ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0)
+ printf(" byte 0x%x\n",
+ ahc->msgin_buf[ahc->msgin_index]);
+#endif
- message_done = ahc_parse_msg(ahc, periph, &devinfo);
+ message_done = ahc_parse_msg(ahc, &devinfo);
if (message_done) {
/*
@@ -2735,15 +2783,25 @@ reswitch:
* assert ATN so the target takes us to the
* message out phase.
*/
- if (ahc->msgout_len != 0)
- ahc_outb(ahc, SCSISIGO,
- ahc_inb(ahc, SCSISIGO) | ATNO);
- } else
+ if (ahc->msgout_len != 0) {
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MESSAGES) != 0) {
+ ahc_print_devinfo(ahc, &devinfo);
+ printf("Asserting ATN for response\n");
+ }
+#endif
+ ahc_assert_atn(ahc);
+ }
+ } else
ahc->msgin_index++;
- /* Ack the byte */
- ahc_outb(ahc, CLRSINT1, CLRREQINIT);
- ahc_inb(ahc, SCSIDATL);
+ if (message_done == MSGLOOP_TERMINATED) {
+ end_session = TRUE;
+ } else {
+ /* Ack the byte */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_inb(ahc, SCSIDATL);
+ }
break;
}
case MSG_TYPE_TARGET_MSGIN:
@@ -2812,12 +2870,12 @@ reswitch:
/*
* Read the latched byte, but turn off SPIOEN first
- * so that we don't inadvertantly cause a REQ for the
+ * so that we don't inadvertently cause a REQ for the
* next byte.
*/
ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIDATL);
- msgdone = ahc_parse_msg(ahc, periph, &devinfo);
+ msgdone = ahc_parse_msg(ahc, &devinfo);
if (msgdone == MSGLOOP_TERMINATED) {
/*
* The message is *really* done in that it caused
@@ -2827,7 +2885,7 @@ reswitch:
*/
return;
}
-
+
ahc->msgin_index++;
/*
@@ -2874,54 +2932,62 @@ reswitch:
/*
* See if we sent a particular extended message to the target.
- * If "full" is true, the target saw the full message.
- * If "full" is false, the target saw at least the first
- * byte of the message.
+ * If "full" is true, return true only if the target saw the full
+ * message. If "full" is false, return true if the target saw at
+ * least the first byte of the message.
*/
static int
-ahc_sent_msg(struct ahc_softc *ahc, u_int msgtype, int full)
+ahc_sent_msg(struct ahc_softc *ahc, ahc_msgtype type, u_int msgval, int full)
{
int found;
- int index;
+ u_int index;
found = FALSE;
index = 0;
while (index < ahc->msgout_len) {
if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+ u_int end_index;
- /* Found a candidate */
- if (ahc->msgout_buf[index+2] == msgtype) {
- u_int end_index;
+ end_index = index + 1 + ahc->msgout_buf[index + 1];
+ if (ahc->msgout_buf[index+2] == msgval
+ && type == AHCMSG_EXT) {
- end_index = index + 1
- + ahc->msgout_buf[index + 1];
if (full) {
if (ahc->msgout_index > end_index)
found = TRUE;
} else if (ahc->msgout_index > index)
found = TRUE;
}
- break;
- } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_Q_TAG
+ index = end_index;
+ } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_TASK
&& ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
/* Skip tag type and tag id or residue param*/
index += 2;
} else {
/* Single byte message */
+ if (type == AHCMSG_1B
+ && ahc->msgout_buf[index] == msgval
+ && ahc->msgout_index > index)
+ found = TRUE;
index++;
}
+
+ if (found)
+ break;
}
return (found);
}
+/*
+ * Wait for a complete incoming message, parse it, and respond accordingly.
+ */
static int
-ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
- struct ahc_devinfo *devinfo)
+ahc_parse_msg(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{
struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
+ struct ahc_tmode_tstate *tstate;
int reject;
int done;
int response;
@@ -2935,9 +3001,9 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
targ_scsirate = tinfo->scsirate;
/*
- * Parse as much of the message as is available,
+ * Parse as much of the message as is availible,
* rejecting it if we don't support it. When
- * the entire message is available and has been
+ * the entire message is availible and has been
* handled, return MSGLOOP_MSGCOMPLETE, indicating
* that we have parsed an entire message.
*
@@ -2946,25 +3012,23 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
* extended message type.
*/
switch (ahc->msgin_buf[0]) {
+ case MSG_DISCONNECT:
+ case MSG_SAVEDATAPOINTER:
+ case MSG_CMDCOMPLETE:
+ case MSG_RESTOREPOINTERS:
+ case MSG_IGN_WIDE_RESIDUE:
+ /*
+ * End our message loop as these are messages
+ * the sequencer handles on its own.
+ */
+ done = MSGLOOP_TERMINATED;
+ break;
case MSG_MESSAGE_REJECT:
response = ahc_handle_msg_reject(ahc, devinfo);
/* FALLTHROUGH */
case MSG_NOOP:
done = MSGLOOP_MSGCOMPLETE;
break;
- case MSG_IGN_WIDE_RESIDUE:
- {
- /* Wait for the whole message */
- if (ahc->msgin_index >= 1) {
- if (ahc->msgin_buf[1] != 1
- || tinfo->current.width == MSG_EXT_WDTR_BUS_8_BIT) {
- reject = TRUE;
- done = MSGLOOP_MSGCOMPLETE;
- } else
- ahc_handle_ign_wide_residue(ahc, devinfo);
- }
- break;
- }
case MSG_EXTENDED:
{
/* Wait for enough of the message to begin validation */
@@ -2973,11 +3037,12 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
switch (ahc->msgin_buf[2]) {
case MSG_EXT_SDTR:
{
- const struct ahc_syncrate *syncrate;
+ struct ahc_syncrate *syncrate;
u_int period;
+ u_int ppr_options;
u_int offset;
u_int saved_offset;
-
+
if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
reject = TRUE;
break;
@@ -2994,22 +3059,35 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
break;
period = ahc->msgin_buf[3];
+ ppr_options = 0;
saved_offset = offset = ahc->msgin_buf[4];
- syncrate = ahc_devlimited_syncrate(ahc, &period);
- ahc_validate_offset(ahc, syncrate, &offset,
- targ_scsirate & WIDEXFER);
- ahc_set_syncrate(ahc, devinfo,
- syncrate, period, offset,
+ syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options,
+ devinfo->role);
+ ahc_validate_offset(ahc, tinfo, syncrate, &offset,
+ targ_scsirate & WIDEXFER,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received "
+ "SDTR period %x, offset %x\n\t"
+ "Filtered to period %x, offset %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ ahc->msgin_buf[3], saved_offset,
+ period, offset);
+ }
+ ahc_set_syncrate(ahc, devinfo,
+ syncrate, period,
+ offset, ppr_options,
AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
- /*paused*/TRUE, /*done*/TRUE);
- ahc_update_xfer_mode(ahc, devinfo);
+ /*paused*/TRUE);
/*
* See if we initiated Sync Negotiation
* and didn't have to fall down to async
* transfers.
*/
- if (ahc_sent_msg(ahc, MSG_EXT_SDTR, /*full*/TRUE)) {
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, TRUE)) {
/* We started it */
if (saved_offset != offset) {
/* Went too low - force async */
@@ -3019,9 +3097,17 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
/*
* Send our own SDTR in reply
*/
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated SDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
ahc->msgout_index = 0;
ahc->msgout_len = 0;
- ahc_construct_sdtr(ahc, period, offset);
+ ahc_construct_sdtr(ahc, devinfo,
+ period, offset);
ahc->msgout_index = 0;
response = TRUE;
}
@@ -3030,8 +3116,9 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
}
case MSG_EXT_WDTR:
{
- u_int bus_width;
- u_int sending_reply;
+ u_int bus_width;
+ u_int saved_width;
+ u_int sending_reply;
sending_reply = FALSE;
if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
@@ -3050,88 +3137,188 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
break;
bus_width = ahc->msgin_buf[3];
- if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/TRUE)) {
+ saved_width = bus_width;
+ ahc_validate_width(ahc, tinfo, &bus_width,
+ devinfo->role);
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received WDTR "
+ "%x filtered to %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, bus_width);
+ }
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, TRUE)) {
/*
* Don't send a WDTR back to the
* target, since we asked first.
+ * If the width went higher than our
+ * request, reject it.
*/
- switch (bus_width){
- default:
- /*
- * How can we do anything greater
- * than 16bit transfers on a 16bit
- * bus?
- */
+ if (saved_width > bus_width) {
reject = TRUE;
- printf("%s: target %d requested %dBit "
+ printf("(%s:%c:%d:%d): requested %dBit "
"transfers. Rejecting...\n",
- ahc_name(ahc), devinfo->target,
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
8 * (0x01 << bus_width));
- /* FALLTHROUGH */
- case MSG_EXT_WDTR_BUS_8_BIT:
- bus_width = MSG_EXT_WDTR_BUS_8_BIT;
- break;
- case MSG_EXT_WDTR_BUS_16_BIT:
- break;
+ bus_width = 0;
}
} else {
/*
* Send our own WDTR in reply
*/
- switch (bus_width) {
- default:
- if (ahc->features & AHC_WIDE) {
- /* Respond Wide */
- bus_width =
- MSG_EXT_WDTR_BUS_16_BIT;
- break;
- }
- /* FALLTHROUGH */
- case MSG_EXT_WDTR_BUS_8_BIT:
- bus_width = MSG_EXT_WDTR_BUS_8_BIT;
- break;
+ if (bootverbose
+ && devinfo->role == ROLE_INITIATOR) {
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated WDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
}
ahc->msgout_index = 0;
ahc->msgout_len = 0;
- ahc_construct_wdtr(ahc, bus_width);
+ ahc_construct_wdtr(ahc, devinfo, bus_width);
ahc->msgout_index = 0;
response = TRUE;
sending_reply = TRUE;
}
ahc_set_width(ahc, devinfo, bus_width,
AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
- /*paused*/TRUE, /*done*/TRUE);
-
+ /*paused*/TRUE);
/* After a wide message, we are async */
ahc_set_syncrate(ahc, devinfo,
/*syncrate*/NULL, /*period*/0,
- /*offset*/0, AHC_TRANS_ACTIVE,
- /*paused*/TRUE, /*done*/FALSE);
+ /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_ACTIVE, /*paused*/TRUE);
if (sending_reply == FALSE && reject == FALSE) {
- if (tinfo->goal.period) {
- const struct ahc_syncrate *rate;
- u_int period;
- u_int offset;
-
- /* Start the sync negotiation */
- period = tinfo->goal.period;
- rate = ahc_devlimited_syncrate(ahc,
- &period);
- offset = tinfo->goal.offset;
- ahc_validate_offset(ahc, rate, &offset,
- tinfo->current.width);
+ if (tinfo->goal.offset) {
ahc->msgout_index = 0;
ahc->msgout_len = 0;
- ahc_construct_sdtr(ahc, period, offset);
+ ahc_build_transfer_msg(ahc, devinfo);
ahc->msgout_index = 0;
response = TRUE;
- } else
- ahc_update_xfer_mode(ahc, devinfo);
+ }
}
done = MSGLOOP_MSGCOMPLETE;
break;
}
+ case MSG_EXT_PPR:
+ {
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int offset;
+ u_int bus_width;
+ u_int ppr_options;
+ u_int saved_width;
+ u_int saved_offset;
+ u_int saved_ppr_options;
+
+ if (ahc->msgin_buf[1] != MSG_EXT_PPR_LEN) {
+ reject = TRUE;
+ break;
+ }
+
+ /*
+ * Wait until we have all args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_PPR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_PPR_LEN + 1))
+ break;
+
+ period = ahc->msgin_buf[3];
+ offset = ahc->msgin_buf[5];
+ bus_width = ahc->msgin_buf[6];
+ saved_width = bus_width;
+ ppr_options = ahc->msgin_buf[7];
+ /*
+ * According to the spec, a DT only
+ * period factor with no DT option
+ * set implies async.
+ */
+ if ((ppr_options & MSG_EXT_PPR_DT_REQ) == 0
+ && period == 9)
+ offset = 0;
+ saved_ppr_options = ppr_options;
+ saved_offset = offset;
+
+ /*
+ * Mask out any options we don't support
+ * on any controller. Transfer options are
+ * only available if we are negotiating wide.
+ */
+ ppr_options &= MSG_EXT_PPR_DT_REQ;
+ if (bus_width == 0)
+ ppr_options = 0;
+
+ ahc_validate_width(ahc, tinfo, &bus_width,
+ devinfo->role);
+ syncrate = ahc_devlimited_syncrate(ahc, tinfo, &period,
+ &ppr_options,
+ devinfo->role);
+ ahc_validate_offset(ahc, tinfo, syncrate,
+ &offset, bus_width,
+ devinfo->role);
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, TRUE)) {
+ /*
+ * If we are unable to do any of the
+ * requested options (we went too low),
+ * then we'll have to reject the message.
+ */
+ if (saved_width > bus_width
+ || saved_offset != offset
+ || saved_ppr_options != ppr_options) {
+ reject = TRUE;
+ period = 0;
+ offset = 0;
+ bus_width = 0;
+ ppr_options = 0;
+ syncrate = NULL;
+ }
+ } else {
+ if (devinfo->role != ROLE_TARGET)
+ printf("(%s:%c:%d:%d): Target "
+ "Initiated PPR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ else
+ printf("(%s:%c:%d:%d): Initiator "
+ "Initiated PPR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_ppr(ahc, devinfo, period, offset,
+ bus_width, ppr_options);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): Received PPR width %x, "
+ "period %x, offset %x,options %x\n"
+ "\tFiltered to width %x, period %x, "
+ "offset %x, options %x\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ saved_width, ahc->msgin_buf[3],
+ saved_offset, saved_ppr_options,
+ bus_width, period, offset, ppr_options);
+ }
+ ahc_set_width(ahc, devinfo, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ ahc_set_syncrate(ahc, devinfo,
+ syncrate, period,
+ offset, ppr_options,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ done = MSGLOOP_MSGCOMPLETE;
+ break;
+ }
default:
/* Unknown extended message. Reject it. */
reject = TRUE;
@@ -3139,47 +3326,51 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
}
break;
}
+#ifdef AHC_TARGET_MODE
case MSG_BUS_DEV_RESET:
ahc_handle_devreset(ahc, devinfo,
- XS_RESET, "Bus Device Reset Received",
+ CAM_BDR_SENT,
+ "Bus Device Reset Received",
/*verbose_level*/0);
- restart_sequencer(ahc);
+ ahc_restart(ahc);
done = MSGLOOP_TERMINATED;
break;
case MSG_ABORT_TAG:
case MSG_ABORT:
case MSG_CLEAR_QUEUE:
+ {
+ int tag;
+
/* Target mode messages */
if (devinfo->role != ROLE_TARGET) {
reject = TRUE;
break;
}
-#if AHC_TARGET_MODE
+ tag = SCB_LIST_NULL;
+ if (ahc->msgin_buf[0] == MSG_ABORT_TAG)
+ tag = ahc_inb(ahc, INITIATOR_TAG);
ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
- devinfo->lun,
- ahc->msgin_buf[0] == MSG_ABORT_TAG
- ? SCB_LIST_NULL
- : ahc_inb(ahc, INITIATOR_TAG),
- ROLE_TARGET, XS_DRIVER_STUFFUP);
+ devinfo->lun, tag, ROLE_TARGET,
+ CAM_REQ_ABORTED);
tstate = ahc->enabled_targets[devinfo->our_scsiid];
if (tstate != NULL) {
- struct tmode_lstate* lstate;
+ struct ahc_tmode_lstate* lstate;
lstate = tstate->enabled_luns[devinfo->lun];
if (lstate != NULL) {
ahc_queue_lstate_event(ahc, lstate,
devinfo->our_scsiid,
ahc->msgin_buf[0],
- /*arg*/0);
+ /*arg*/tag);
ahc_send_lstate_events(ahc, lstate);
}
}
- done = MSGLOOP_MSGCOMPLETE;
-#else
- panic("ahc: got target mode message");
-#endif
+ ahc_restart(ahc);
+ done = MSGLOOP_TERMINATED;
break;
+ }
+#endif
case MSG_TERM_IO_PROC:
default:
reject = TRUE;
@@ -3204,6 +3395,168 @@ ahc_parse_msg(struct ahc_softc *ahc, struct scsipi_periph *periph,
return (done);
}
+/*
+ * Process a message reject message.
+ */
+static int
+ahc_handle_msg_reject(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
+{
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+ struct scb *scb;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+ u_int scb_index;
+ u_int last_msg;
+ int response = 0;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ /* Might be necessary */
+ last_msg = ahc_inb(ahc, LAST_MSG);
+
+ if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_PPR, /*full*/FALSE)) {
+ /*
+ * Target does not support the PPR message.
+ * Attempt to negotiate SPI-2 style.
+ */
+ if (bootverbose) {
+ printf("(%s:%c:%d:%d): PPR Rejected. "
+ "Trying WDTR/SDTR\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ }
+ tinfo->goal.ppr_options = 0;
+ tinfo->curr.transport_version = 2;
+ tinfo->goal.transport_version = 2;
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = 1;
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_WDTR, /*full*/FALSE)) {
+
+ /* note 8bit xfers */
+ if (bootverbose)
+ printf("(%s:%c:%d:%d): refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target, devinfo->lun);
+ ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ /*
+ * No need to clear the sync rate. If the target
+ * did not accept the command, our syncrate is
+ * unaffected. If the target started the negotiation,
+ * but rejected our response, we already cleared the
+ * sync rate before sending our WDTR.
+ */
+ if (tinfo->goal.offset != tinfo->curr.offset) {
+
+ /* Start the sync negotiation */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_build_transfer_msg(ahc, devinfo);
+ ahc->msgout_index = 0;
+ response = 1;
+ }
+ } else if (ahc_sent_msg(ahc, AHCMSG_EXT, MSG_EXT_SDTR, /*full*/FALSE)) {
+ /* note asynch xfers and clear flag */
+ ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE);
+ if (bootverbose)
+ printf("(%s:%c:%d:%d): refuses synchronous negotiation."
+ " Using asynchronous transfers\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ } else if ((scb->hscb->control & MSG_SIMPLE_TASK) != 0) {
+ int tag_type;
+ int mask;
+
+ tag_type = (scb->hscb->control & MSG_SIMPLE_TASK);
+
+ if (tag_type == MSG_SIMPLE_TASK) {
+ if (bootverbose)
+ printf("(%s:%c:%d:%d): refuses tagged commands."
+ " Performing non-tagged I/O\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun);
+ ahc_set_tags(ahc, devinfo, AHC_QUEUE_NONE);
+ mask = ~0x23;
+ } else {
+ if (bootverbose)
+ printf("(%s:%c:%d:%d): refuses %s tagged "
+ "commands. Performing simple queue "
+ "tagged I/O only\n",
+ ahc_name(ahc), devinfo->channel,
+ devinfo->target, devinfo->lun,
+ tag_type == MSG_ORDERED_TASK
+ ? "ordered" : "head of queue");
+ ahc_set_tags(ahc, devinfo, AHC_QUEUE_BASIC);
+ mask = ~0x03;
+ }
+
+ /*
+ * Resend the identify for this CCB as the target
+ * may believe that the selection is invalid otherwise.
+ */
+ ahc_outb(ahc, SCB_CONTROL,
+ ahc_inb(ahc, SCB_CONTROL) & mask);
+ scb->hscb->control &= mask;
+ ahc_set_transaction_tag(scb, /*enabled*/FALSE,
+ /*type*/MSG_SIMPLE_TASK);
+ ahc_outb(ahc, MSG_OUT, MSG_IDENTIFYFLAG);
+ ahc_assert_atn(ahc);
+
+ /*
+ * This transaction is now at the head of
+ * the untagged queue for this target.
+ */
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
+ struct scb_tailq *untagged_q;
+
+ untagged_q =
+ &(ahc->untagged_queues[devinfo->target_offset]);
+ TAILQ_INSERT_HEAD(untagged_q, scb, links.tqe);
+ scb->flags |= SCB_UNTAGGEDQ;
+ }
+ ahc_busy_tcl(ahc, BUILD_TCL(scb->hscb->scsiid, devinfo->lun),
+ scb->hscb->tag);
+
+ /*
+ * Requeue all tagged commands for this target
+ * currently in our posession so they can be
+ * converted to untagged commands.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), /*tag*/SCB_LIST_NULL,
+ ROLE_INITIATOR, CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+ if (bootverbose)
+ printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ last_msg);
+ }
+ return (response);
+}
+
+/*
+ * Process an ingnore wide residue message.
+ */
static void
ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{
@@ -3211,9 +3564,13 @@ ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
struct scb *scb;
scb_index = ahc_inb(ahc, SCB_TAG);
- scb = &ahc->scb_data->scbarray[scb_index];
+ scb = ahc_lookup_scb(ahc, scb_index);
+ /*
+ * XXX Actually check data direction in the sequencer?
+ * Perhaps add datadir to some spare bits in the hscb?
+ */
if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
- || !(scb->xs->xs_control & XS_CTL_DATA_IN)) {
+ || ahc_get_transfer_dir(scb) != CAM_DIR_IN) {
/*
* Ignore the message if we haven't
* seen an appropriate data phase yet.
@@ -3226,10 +3583,10 @@ ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* nothing. Otherwise, subtract a byte
* and update the residual count accordingly.
*/
- u_int resid_sgcnt;
+ uint32_t sgptr;
- resid_sgcnt = ahc_inb(ahc, SCB_RESID_SGCNT);
- if (resid_sgcnt == 0
+ sgptr = ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+ if ((sgptr & SG_LIST_NULL) != 0
&& ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
/*
* If the residual occurred on the last
@@ -3238,13 +3595,20 @@ ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
* nothing.
*/
} else {
- u_int data_cnt;
- u_int32_t data_addr;
- u_int sg_index;
-
- data_cnt = (ahc_inb(ahc, SCB_RESID_DCNT + 2) << 16)
- | (ahc_inb(ahc, SCB_RESID_DCNT + 1) << 8)
- | (ahc_inb(ahc, SCB_RESID_DCNT));
+ struct ahc_dma_seg *sg;
+ uint32_t data_cnt;
+ uint32_t data_addr;
+ uint32_t sglen;
+
+ /* Pull in the rest of the sgptr */
+ sgptr |= (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8);
+ sgptr &= SG_PTR_MASK;
+ data_cnt = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT+1) << 8)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT));
data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
| (ahc_inb(ahc, SHADDR + 2) << 16)
@@ -3254,192 +3618,478 @@ ahc_handle_ign_wide_residue(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
data_cnt += 1;
data_addr -= 1;
- sg_index = scb->sg_count - resid_sgcnt;
-
- if (sg_index != 0
- && (le32toh(scb->sg_list[sg_index].len) < data_cnt)) {
- u_int32_t sg_addr;
+ sg = ahc_sg_bus_to_virt(scb, sgptr);
+ /*
+ * The residual sg ptr points to the next S/G
+ * to load so we must go back one.
+ */
+ sg--;
+ sglen = ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+ if (sg != scb->sg_list
+ && sglen < (data_cnt & AHC_SG_LEN_MASK)) {
- sg_index--;
- data_cnt = 1;
- data_addr = le32toh(scb->sg_list[sg_index].addr)
- + le32toh(scb->sg_list[sg_index].len)
- - 1;
+ sg--;
+ sglen = ahc_le32toh(sg->len);
+ /*
+ * Preserve High Address and SG_LIST bits
+ * while setting the count to 1.
+ */
+ data_cnt = 1 | (sglen & (~AHC_SG_LEN_MASK));
+ data_addr = ahc_le32toh(sg->addr)
+ + (sglen & AHC_SG_LEN_MASK) - 1;
/*
- * The physical address base points to the
- * second entry as it is always used for
- * calculating the "next S/G pointer".
+ * Increment sg so it points to the
+ * "next" sg.
*/
- sg_addr = scb->sg_list_phys
- + (sg_index* sizeof(*scb->sg_list));
- ahc_outb(ahc, SG_NEXT + 3, sg_addr >> 24);
- ahc_outb(ahc, SG_NEXT + 2, sg_addr >> 16);
- ahc_outb(ahc, SG_NEXT + 1, sg_addr >> 8);
- ahc_outb(ahc, SG_NEXT, sg_addr);
+ sg++;
+ sgptr = ahc_sg_virt_to_bus(scb, sg);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 3,
+ sgptr >> 24);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 2,
+ sgptr >> 16);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR + 1,
+ sgptr >> 8);
+ ahc_outb(ahc, SCB_RESIDUAL_SGPTR, sgptr);
}
- ahc_outb(ahc, SCB_RESID_DCNT + 2, data_cnt >> 16);
- ahc_outb(ahc, SCB_RESID_DCNT + 1, data_cnt >> 8);
- ahc_outb(ahc, SCB_RESID_DCNT, data_cnt);
-
- ahc_outb(ahc, SHADDR + 3, data_addr >> 24);
- ahc_outb(ahc, SHADDR + 2, data_addr >> 16);
- ahc_outb(ahc, SHADDR + 1, data_addr >> 8);
- ahc_outb(ahc, SHADDR, data_addr);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 3, data_cnt >> 24);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 2, data_cnt >> 16);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT + 1, data_cnt >> 8);
+ ahc_outb(ahc, SCB_RESIDUAL_DATACNT, data_cnt);
}
}
}
+
+/*
+ * Reinitialize the data pointers for the active transfer
+ * based on its current residual.
+ */
+static void
+ahc_reinitialize_dataptrs(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+ struct ahc_dma_seg *sg;
+ u_int scb_index;
+ uint32_t sgptr;
+ uint32_t resid;
+ uint32_t dataptr;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scb_index);
+ sgptr = (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 3) << 24)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_SGPTR + 1) << 8)
+ | ahc_inb(ahc, SCB_RESIDUAL_SGPTR);
+
+ sgptr &= SG_PTR_MASK;
+ sg = ahc_sg_bus_to_virt(scb, sgptr);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ resid = (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESIDUAL_DATACNT + 1) << 8)
+ | ahc_inb(ahc, SCB_RESIDUAL_DATACNT);
+
+ dataptr = ahc_le32toh(sg->addr)
+ + (ahc_le32toh(sg->len) & AHC_SG_LEN_MASK)
+ - resid;
+ if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
+ u_int dscommand1;
+
+ dscommand1 = ahc_inb(ahc, DSCOMMAND1);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1 | HADDLDSEL0);
+ ahc_outb(ahc, HADDR,
+ (ahc_le32toh(sg->len) >> 24) & SG_HIGH_ADDR_BITS);
+ ahc_outb(ahc, DSCOMMAND1, dscommand1);
+ }
+ ahc_outb(ahc, HADDR + 3, dataptr >> 24);
+ ahc_outb(ahc, HADDR + 2, dataptr >> 16);
+ ahc_outb(ahc, HADDR + 1, dataptr >> 8);
+ ahc_outb(ahc, HADDR, dataptr);
+ ahc_outb(ahc, HCNT + 2, resid >> 16);
+ ahc_outb(ahc, HCNT + 1, resid >> 8);
+ ahc_outb(ahc, HCNT, resid);
+ if ((ahc->features & AHC_ULTRA2) == 0) {
+ ahc_outb(ahc, STCNT + 2, resid >> 16);
+ ahc_outb(ahc, STCNT + 1, resid >> 8);
+ ahc_outb(ahc, STCNT, resid);
+ }
+}
+
+/*
+ * Handle the effects of issuing a bus device reset message.
+ */
static void
ahc_handle_devreset(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
- int status, char *message,
- int verbose_level)
+ cam_status status, char *message, int verbose_level)
{
+#ifdef AHC_TARGET_MODE
+ struct ahc_tmode_tstate* tstate;
+ u_int lun;
+#endif
int found;
found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
- AHC_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL, devinfo->role,
status);
+#ifdef AHC_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target mord peripheral
+ * drivers affected by this action.
+ */
+ tstate = ahc->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahc_queue_lstate_event(ahc, lstate, devinfo->our_scsiid,
+ MSG_BUS_DEV_RESET, /*arg*/0);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+#endif
+
/*
* Go back to async/narrow transfers and renegotiate.
- * ahc_set_width and ahc_set_syncrate can cope with NULL
- * paths.
*/
ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
- AHC_TRANS_CUR, /*paused*/TRUE, /*done*/FALSE);
+ AHC_TRANS_CUR, /*paused*/TRUE);
ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
- /*period*/0, /*offset*/0, AHC_TRANS_CUR,
- /*paused*/TRUE, /*done*/FALSE);
- ahc_update_xfer_mode(ahc, devinfo);
+ /*period*/0, /*offset*/0, /*ppr_options*/0,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+
+ ahc_send_async(ahc, devinfo->channel, devinfo->target,
+ CAM_LUN_WILDCARD, AC_SENT_BDR, NULL);
- if (message != NULL && (verbose_level <= 0))
+ if (message != NULL
+ && (verbose_level <= bootverbose))
printf("%s: %s on %c:%d. %d SCBs aborted\n", ahc_name(ahc),
message, devinfo->channel, devinfo->target, found);
}
-/*
- * We have an scb which has been processed by the
- * adaptor, now we look to see how the operation
- * went.
- */
+#ifdef AHC_TARGET_MODE
static void
-ahc_done(struct ahc_softc *ahc, struct scb *scb)
+ahc_setup_target_msgin(struct ahc_softc *ahc, struct ahc_devinfo *devinfo,
+ struct scb *scb)
{
- struct scsipi_xfer *xs;
- struct scsipi_periph *periph;
- int requeue = 0;
- int target;
-
- xs = scb->xs;
- periph = xs->xs_periph;
- LIST_REMOVE(scb, plinks);
+ /*
+ * To facilitate adding multiple messages together,
+ * each routine should increment the index and len
+ * variables instead of setting them explicitly.
+ */
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
- callout_stop(&scb->xs->xs_callout);
+ if (scb != NULL && (scb->flags & SCB_AUTO_NEGOTIATE) != 0)
+ ahc_build_transfer_msg(ahc, devinfo);
+ else
+ panic("ahc_intr: AWAITING target message with no message");
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWCMDS) {
- scsipi_printaddr(periph);
- printf("ahc_done opcode %d tag %x\n", xs->cmdstore.opcode,
- scb->hscb->tag);
- }
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
#endif
- target = periph->periph_target;
-
- if (xs->datalen) {
- int op;
+int
+ahc_softc_init(struct ahc_softc *ahc)
+{
- if (xs->xs_control & XS_CTL_DATA_IN)
- 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);
+ /* The IRQMS bit is only valid on VL and EISA chips */
+ if ((ahc->chip & AHC_PCI) == 0)
+ ahc->unpause = ahc_inb(ahc, HCNTRL) & IRQMS;
+ else
+ ahc->unpause = 0;
+ ahc->pause = ahc->unpause | PAUSE;
+ /* XXX The shared scb data stuff should be deprecated */
+ if (ahc->scb_data == NULL) {
+ ahc->scb_data = malloc(sizeof(*ahc->scb_data),
+ M_DEVBUF, M_NOWAIT);
+ if (ahc->scb_data == NULL)
+ return (ENOMEM);
+ memset(ahc->scb_data, 0, sizeof(*ahc->scb_data));
}
+ return (0);
+}
+
+void
+ahc_softc_insert(struct ahc_softc *ahc)
+{
+ struct ahc_softc *list_ahc;
+
+#if AHC_PCI_CONFIG > 0
/*
- * Unbusy this target/channel/lun.
- * XXX if we are holding two commands per lun,
- * send the next command.
+ * Second Function PCI devices need to inherit some
+ * settings from function 0.
*/
- if (!(scb->hscb->control & TAG_ENB))
- ahc_index_busy_tcl(ahc, scb->hscb->tcl, /*unbusy*/TRUE);
+ if ((ahc->chip & AHC_BUS_MASK) == AHC_PCI
+ && (ahc->features & AHC_MULTI_FUNC) != 0) {
+ TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+ ahc_dev_softc_t list_pci;
+ ahc_dev_softc_t pci;
+
+ list_pci = list_ahc->dev_softc;
+ pci = ahc->dev_softc;
+ if (ahc_get_pci_slot(list_pci) == ahc_get_pci_slot(pci)
+ && ahc_get_pci_bus(list_pci) == ahc_get_pci_bus(pci)) {
+ struct ahc_softc *master;
+ struct ahc_softc *slave;
+
+ if (ahc_get_pci_function(list_pci) == 0) {
+ master = list_ahc;
+ slave = ahc;
+ } else {
+ master = ahc;
+ slave = list_ahc;
+ }
+ slave->flags &= ~AHC_BIOS_ENABLED;
+ slave->flags |=
+ master->flags & AHC_BIOS_ENABLED;
+ slave->flags &= ~AHC_PRIMARY_CHANNEL;
+ slave->flags |=
+ master->flags & AHC_PRIMARY_CHANNEL;
+ break;
+ }
+ }
+ }
+#endif
/*
- * If the recovery SCB completes, we have to be
- * out of our timeout.
+ * Insertion sort into our list of softcs.
*/
- if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ list_ahc = TAILQ_FIRST(&ahc_tailq);
+ while (list_ahc != NULL
+ && ahc_softc_comp(list_ahc, ahc) <= 0)
+ list_ahc = TAILQ_NEXT(list_ahc, links);
+ if (list_ahc != NULL)
+ TAILQ_INSERT_BEFORE(list_ahc, ahc, links);
+ else
+ TAILQ_INSERT_TAIL(&ahc_tailq, ahc, links);
+ ahc->init_level++;
+}
+
+/*
+ * Verify that the passed in softc pointer is for a
+ * controller that is still configured.
+ */
+struct ahc_softc *
+ahc_find_softc(struct ahc_softc *ahc)
+{
+ struct ahc_softc *list_ahc;
- struct scb *scbp;
+ TAILQ_FOREACH(list_ahc, &ahc_tailq, links) {
+ if (list_ahc == ahc)
+ return (ahc);
+ }
+ return (NULL);
+}
- /*
- * We were able to complete the command successfully,
- * so reinstate the timeouts for all other pending
- * commands.
- */
- scbp = ahc->pending_ccbs.lh_first;
- while (scbp != NULL) {
- struct scsipi_xfer *txs = scbp->xs;
-
- if (!(txs->xs_control & XS_CTL_POLL)) {
- callout_reset(&scbp->xs->xs_callout,
- mstohz(scbp->xs->timeout),
- ahc_timeout, scbp);
+void
+ahc_set_unit(struct ahc_softc *ahc, int unit)
+{
+ ahc->unit = unit;
+}
+
+void
+ahc_set_name(struct ahc_softc *ahc, char *name)
+{
+ if (ahc->name != NULL)
+ free(ahc->name, M_DEVBUF);
+ ahc->name = name;
+}
+
+void
+ahc_free(struct ahc_softc *ahc)
+{
+ int i;
+
+ ahc_fini_scbdata(ahc);
+ switch (ahc->init_level) {
+ default:
+ case 2:
+ ahc_shutdown(ahc);
+ TAILQ_REMOVE(&ahc_tailq, ahc, links);
+ /* FALLTHROUGH */
+ case 1:
+ bus_dmamap_unload(ahc->parent_dmat, ahc->shared_data_dmamap);
+ bus_dmamap_destroy(ahc->parent_dmat, ahc->shared_data_dmamap);
+ bus_dmamem_unmap(ahc->parent_dmat, (caddr_t)ahc->qoutfifo, ahc->shared_data_size);
+ bus_dmamem_free(ahc->parent_dmat, &ahc->shared_data_seg, ahc->shared_data_nseg);
+ break;
+ case 0:
+ break;
+ }
+
+ ahc_platform_free(ahc);
+ for (i = 0; i < AHC_NUM_TARGETS; i++) {
+ struct ahc_tmode_tstate *tstate;
+
+ tstate = ahc->enabled_targets[i];
+ if (tstate != NULL) {
+#if AHC_TARGET_MODE
+ int j;
+
+ for (j = 0; j < AHC_NUM_LUNS; j++) {
+ struct ahc_tmode_lstate *lstate;
+
+ lstate = tstate->enabled_luns[j];
+ if (lstate != NULL) {
+ /*xpt_free_path(lstate->path);*/
+ free(lstate, M_DEVBUF);
+ }
}
- scbp = LIST_NEXT(scbp, plinks);
+#endif
+ free(tstate, M_DEVBUF);
}
+ }
+#if AHC_TARGET_MODE
+ if (ahc->black_hole != NULL) {
+ /*xpt_free_path(ahc->black_hole->path);*/
+ free(ahc->black_hole, M_DEVBUF);
+ }
+#endif
+ if (ahc->name != NULL)
+ free(ahc->name, M_DEVBUF);
+ if (ahc->seep_config != NULL)
+ free(ahc->seep_config, M_DEVBUF);
+#ifndef __FreeBSD__
+ free(ahc, M_DEVBUF);
+#endif
+ return;
+}
+
+void
+ahc_shutdown(void *arg)
+{
+ struct ahc_softc *ahc;
+ int i;
+
+ ahc = (struct ahc_softc *)arg;
+ /* This will reset most registers to 0, but not all */
+ ahc_reset(ahc);
+ ahc_outb(ahc, SCSISEQ, 0);
+ ahc_outb(ahc, SXFRCTL0, 0);
+ ahc_outb(ahc, DSPCISTATUS, 0);
+
+ for (i = TARG_SCSIRATE; i < SCSICONF; i++)
+ ahc_outb(ahc, i, 0);
+}
+
+/*
+ * Reset the controller and record some information about it
+ * that is only available just after a reset.
+ */
+int
+ahc_reset(struct ahc_softc *ahc)
+{
+ u_int sblkctl;
+ u_int sxfrctl1_a, sxfrctl1_b;
+ int wait;
+
+ /*
+ * Preserve the value of the SXFRCTL1 register for all channels.
+ * It contains settings that affect termination and we don't want
+ * to disturb the integrity of the bus.
+ */
+ ahc_pause(ahc);
+ if ((ahc_inb(ahc, HCNTRL) & CHIPRST) != 0) {
/*
- * Ensure that we didn't put a second instance of this
- * SCB into the QINFIFO.
+ * The chip has not been initialized since
+ * PCI/EISA/VLB bus reset. Don't trust
+ * "left over BIOS data".
*/
- ahc_search_qinfifo(ahc, SCB_TARGET(scb), SCB_CHANNEL(scb),
- SCB_LUN(scb), scb->hscb->tag,
- ROLE_INITIATOR, /*status*/0,
- SEARCH_REMOVE);
- if (xs->error != XS_NOERROR)
- ahcsetccbstatus(xs, XS_TIMEOUT);
- scsipi_printaddr(xs->xs_periph);
- printf("no longer in timeout, status = %x\n", xs->status);
- }
-
- if (xs->error != XS_NOERROR) {
- /* Don't clobber any existing error state */
- } else if ((scb->flags & SCB_SENSE) != 0) {
+ ahc->flags |= AHC_NO_BIOS_INIT;
+ }
+ sxfrctl1_b = 0;
+ if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7770) {
+ u_int sblkctl;
+
/*
- * We performed autosense retrieval.
- *
- * zero the sense data before having
- * the drive fill it. 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.
+ * Save channel B's settings in case this chip
+ * is setup for TWIN channel operation.
*/
- memset(&xs->sense.scsi_sense, 0, sizeof(xs->sense.scsi_sense));
- memcpy(&xs->sense.scsi_sense,
- &ahc->scb_data->sense[scb->hscb->tag],
- le32toh(scb->sg_list->len));
- xs->error = XS_SENSE;
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+ sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
+ ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+ }
+ sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+
+ ahc_outb(ahc, HCNTRL, CHIPRST | ahc->pause);
+
+ /*
+ * Ensure that the reset has finished. We delay 1000us
+ * prior to reading the register to make sure the chip
+ * has sufficiently completed its reset to handle register
+ * accesses.
+ */
+ wait = 1000;
+ do {
+ ahc_delay(1000);
+ } while (--wait && !(ahc_inb(ahc, HCNTRL) & CHIPRSTACK));
+
+ if (wait == 0) {
+ printf("%s: WARNING - Failed chip reset! "
+ "Trying to initialize anyway.\n", ahc_name(ahc));
}
- if (scb->flags & SCB_FREEZE_QUEUE) {
- scsipi_periph_thaw(periph, 1);
- scb->flags &= ~SCB_FREEZE_QUEUE;
+ ahc_outb(ahc, HCNTRL, ahc->pause);
+
+ /* Determine channel configuration */
+ sblkctl = ahc_inb(ahc, SBLKCTL) & (SELBUSB|SELWIDE);
+ /* No Twin Channel PCI cards */
+ if ((ahc->chip & AHC_PCI) != 0)
+ sblkctl &= ~SELBUSB;
+ switch (sblkctl) {
+ case 0:
+ /* Single Narrow Channel */
+ break;
+ case 2:
+ /* Wide Channel */
+ ahc->features |= AHC_WIDE;
+ break;
+ case 8:
+ /* Twin Channel */
+ ahc->features |= AHC_TWIN;
+ break;
+ default:
+ printf(" Unsupported adapter type (0x%x). Ignoring\n", sblkctl);
+ return(-1);
}
- requeue = scb->flags & SCB_REQUEUE;
- ahcfreescb(ahc, scb);
+ /*
+ * Reload sxfrctl1.
+ *
+ * We must always initialize STPWEN to 1 before we
+ * restore the saved values. STPWEN is initialized
+ * to a tri-state condition which can only be cleared
+ * by turning it on.
+ */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ u_int sblkctl;
- if (requeue) {
- xs->error = XS_REQUEUE;
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
+ ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
+ ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
}
- scsipi_done(xs);
+ ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+
+#ifdef AHC_DUMP_SEQ
+ if (ahc->init_level == 0)
+ ahc_dumpseq(ahc);
+#endif
+
+ return (0);
}
/*
@@ -3450,33 +4100,343 @@ ahc_probe_scbs(struct ahc_softc *ahc) {
int i;
for (i = 0; i < AHC_SCB_MAX; i++) {
+
ahc_outb(ahc, SCBPTR, i);
- ahc_outb(ahc, SCB_CONTROL, i);
- if (ahc_inb(ahc, SCB_CONTROL) != i)
+ ahc_outb(ahc, SCB_BASE, i);
+ if (ahc_inb(ahc, SCB_BASE) != i)
break;
ahc_outb(ahc, SCBPTR, 0);
- if (ahc_inb(ahc, SCB_CONTROL) != 0)
+ if (ahc_inb(ahc, SCB_BASE) != 0)
break;
}
return (i);
}
+#if 0
+static void
+ahc_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+{
+ bus_addr_t *baddr;
+
+ baddr = (bus_addr_t *)arg;
+ *baddr = segs->ds_addr;
+}
+#endif
+
+static void
+ahc_build_free_scb_list(struct ahc_softc *ahc)
+{
+ int scbsize;
+ int i;
+
+ scbsize = 32;
+ if ((ahc->flags & AHC_LSCBS_ENABLED) != 0)
+ scbsize = 64;
+
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ int j;
+
+ ahc_outb(ahc, SCBPTR, i);
+
+ /*
+ * Touch all SCB bytes to avoid parity errors
+ * should one of our debugging routines read
+ * an otherwise uninitiatlized byte.
+ */
+ for (j = 0; j < scbsize; j++)
+ ahc_outb(ahc, SCB_BASE+j, 0xFF);
+
+ /* Clear the control byte. */
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ /* Set the next pointer */
+ if ((ahc->flags & AHC_PAGESCBS) != 0)
+ ahc_outb(ahc, SCB_NEXT, i+1);
+ else
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+
+ /* Make the tag number, SCSIID, and lun invalid */
+ ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
+ ahc_outb(ahc, SCB_SCSIID, 0xFF);
+ ahc_outb(ahc, SCB_LUN, 0xFF);
+ }
+
+ /* Make sure that the last SCB terminates the free list */
+ ahc_outb(ahc, SCBPTR, i-1);
+ ahc_outb(ahc, SCB_NEXT, SCB_LIST_NULL);
+}
+
+static int
+ahc_init_scbdata(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+
+ scb_data = ahc->scb_data;
+ SLIST_INIT(&scb_data->free_scbs);
+ SLIST_INIT(&scb_data->sg_maps);
+
+ /* Allocate SCB resources */
+ scb_data->scbarray =
+ (struct scb *)malloc(sizeof(struct scb) * AHC_SCB_MAX_ALLOC,
+ M_DEVBUF, M_NOWAIT);
+ if (scb_data->scbarray == NULL)
+ return (ENOMEM);
+ memset(scb_data->scbarray, 0, sizeof(struct scb) * AHC_SCB_MAX_ALLOC);
+
+ /* Determine the number of hardware SCBs and initialize them */
+
+ scb_data->maxhscbs = ahc_probe_scbs(ahc);
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ /* SCB 0 heads the free list */
+ ahc_outb(ahc, FREE_SCBH, 0);
+ } else {
+ ahc_outb(ahc, FREE_SCBH, SCB_LIST_NULL);
+ }
+
+ if (ahc->scb_data->maxhscbs == 0) {
+ printf("%s: No SCB space found\n", ahc_name(ahc));
+ return (ENXIO);
+ }
+
+ ahc_build_free_scb_list(ahc);
+
+ /*
+ * Create our DMA tags. These tags define the kinds of device
+ * accessible memory allocations and memory mappings we will
+ * need to perform during normal operation.
+ *
+ * Unless we need to further restrict the allocation, we rely
+ * on the restrictions of the parent dmat, hence the common
+ * use of MAXADDR and MAXSIZE.
+ */
+
+ if (ahc_createdmamem(ahc->parent_dmat,
+ AHC_SCB_MAX * sizeof(struct hardware_scb), ahc->sc_dmaflags,
+ &scb_data->hscb_dmamap,
+ (caddr_t *)&scb_data->hscbs, &scb_data->hscb_busaddr,
+ &scb_data->hscb_seg, &scb_data->hscb_nseg, ahc_name(ahc),
+ "hardware SCB structures") < 0)
+ goto error_exit;
+
+ scb_data->init_level++;
+
+ if (ahc_createdmamem(ahc->parent_dmat,
+ AHC_SCB_MAX * sizeof(struct scsipi_sense_data), ahc->sc_dmaflags,
+ &scb_data->sense_dmamap, (caddr_t *)&scb_data->sense,
+ &scb_data->sense_busaddr, &scb_data->sense_seg,
+ &scb_data->sense_nseg, ahc_name(ahc), "sense buffers") < 0)
+ goto error_exit;
+
+ scb_data->init_level++;
+
+ /* Perform initial CCB allocation */
+ memset(scb_data->hscbs, 0,
+ AHC_SCB_MAX_ALLOC * sizeof(struct hardware_scb));
+ ahc_alloc_scbs(ahc);
+
+ if (scb_data->numscbs == 0) {
+ printf("%s: ahc_init_scbdata - "
+ "Unable to allocate initial scbs\n",
+ ahc_name(ahc));
+ goto error_exit;
+ }
+
+ /*
+ * Tell the sequencer which SCB will be the next one it receives.
+ */
+ ahc->next_queued_scb = ahc_get_scb(ahc);
+ ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
+
+ /*
+ * Note that we were successfull
+ */
+ return (0);
+
+error_exit:
+
+ return (ENOMEM);
+}
+
+static void
+ahc_fini_scbdata(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+
+ scb_data = ahc->scb_data;
+ if (scb_data == NULL)
+ return;
+
+ switch (scb_data->init_level) {
+ default:
+ case 3:
+ {
+ struct sg_map_node *sg_map;
+
+ while ((sg_map = SLIST_FIRST(&scb_data->sg_maps))!= NULL) {
+ SLIST_REMOVE_HEAD(&scb_data->sg_maps, links);
+ ahc_freedmamem(ahc->parent_dmat, PAGE_SIZE,
+ sg_map->sg_dmamap, (caddr_t)sg_map->sg_vaddr,
+ &sg_map->sg_dmasegs, sg_map->sg_nseg);
+ free(sg_map, M_DEVBUF);
+ }
+ }
+ /*FALLTHROUGH*/
+ case 2:
+ ahc_freedmamem(ahc->parent_dmat,
+ AHC_SCB_MAX * sizeof(struct scsipi_sense_data),
+ scb_data->sense_dmamap, (caddr_t)scb_data->sense,
+ &scb_data->sense_seg, scb_data->sense_nseg);
+ /*FALLTHROUGH*/
+ case 1:
+ ahc_freedmamem(ahc->parent_dmat,
+ AHC_SCB_MAX * sizeof(struct hardware_scb),
+ scb_data->hscb_dmamap, (caddr_t)scb_data->hscbs,
+ &scb_data->hscb_seg, scb_data->hscb_nseg);
+ /*FALLTHROUGH*/
+ }
+ if (scb_data->scbarray != NULL)
+ free(scb_data->scbarray, M_DEVBUF);
+}
+
+void
+ahc_alloc_scbs(struct ahc_softc *ahc)
+{
+ struct scb_data *scb_data;
+ struct scb *next_scb;
+ struct sg_map_node *sg_map;
+ bus_addr_t physaddr;
+ struct ahc_dma_seg *segs;
+ int newcount;
+ int i;
+
+ scb_data = ahc->scb_data;
+ if (scb_data->numscbs >= AHC_SCB_MAX_ALLOC)
+ /* Can't allocate any more */
+ return;
+
+ next_scb = &scb_data->scbarray[scb_data->numscbs];
+
+ sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
+
+ if (sg_map == NULL)
+ return;
+
+ /* Allocate S/G space for the next batch of SCBS */
+ if (ahc_createdmamem(ahc->parent_dmat, PAGE_SIZE, ahc->sc_dmaflags,
+ &sg_map->sg_dmamap,
+ (caddr_t *)&sg_map->sg_vaddr, &sg_map->sg_physaddr,
+ &sg_map->sg_dmasegs, &sg_map->sg_nseg, ahc_name(ahc),
+ "SG space") < 0) {
+ free(sg_map, M_DEVBUF);
+ return;
+ }
+
+ SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
+
+ segs = sg_map->sg_vaddr;
+ physaddr = sg_map->sg_physaddr;
+
+ newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
+ newcount = MIN(newcount, (AHC_SCB_MAX_ALLOC - scb_data->numscbs));
+ for (i = 0; i < newcount; i++) {
+ struct scb_platform_data *pdata;
+ int error;
+
+ pdata = (struct scb_platform_data *)malloc(sizeof(*pdata),
+ M_DEVBUF, M_NOWAIT);
+ if (pdata == NULL)
+ break;
+ next_scb->platform_data = pdata;
+ next_scb->sg_map = sg_map;
+ next_scb->sg_list = segs;
+ /*
+ * The sequencer always starts with the second entry.
+ * The first entry is embedded in the scb.
+ */
+ next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+ next_scb->ahc_softc = ahc;
+ next_scb->flags = SCB_FREE;
+
+ error = bus_dmamap_create(ahc->parent_dmat,
+ AHC_MAXTRANSFER_SIZE, AHC_NSEG, MAXBSIZE, 0,
+ BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW,
+ &next_scb->dmamap);
+ if (error != 0)
+ break;
+
+ next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
+ next_scb->hscb->tag = ahc->scb_data->numscbs;
+ SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs,
+ next_scb, links.sle);
+ segs += AHC_NSEG;
+ physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
+ next_scb++;
+ ahc->scb_data->numscbs++;
+ }
+}
+
+void
+ahc_controller_info(struct ahc_softc *ahc, char *buf)
+{
+ int len;
+
+ len = sprintf(buf, "%s: ", ahc_chip_names[ahc->chip & AHC_CHIPID_MASK]);
+ buf += len;
+ if ((ahc->features & AHC_TWIN) != 0)
+ len = sprintf(buf, "Twin Channel, A SCSI Id=%d, "
+ "B SCSI Id=%d, primary %c, ",
+ ahc->our_id, ahc->our_id_b,
+ (ahc->flags & AHC_PRIMARY_CHANNEL) + 'A');
+ else {
+ const char *speed;
+ const char *type;
+
+ speed = "";
+ if ((ahc->features & AHC_ULTRA) != 0) {
+ speed = "Ultra ";
+ } else if ((ahc->features & AHC_DT) != 0) {
+ speed = "Ultra160 ";
+ } else if ((ahc->features & AHC_ULTRA2) != 0) {
+ speed = "Ultra2 ";
+ }
+ if ((ahc->features & AHC_WIDE) != 0) {
+ type = "Wide";
+ } else {
+ type = "Single";
+ }
+ len = sprintf(buf, "%s%s Channel %c, SCSI Id=%d, ",
+ speed, type, ahc->channel, ahc->our_id);
+ }
+ buf += len;
+
+ if ((ahc->flags & AHC_PAGESCBS) != 0)
+ sprintf(buf, "%d/%d SCBs",
+ ahc->scb_data->maxhscbs, AHC_MAX_QUEUE);
+ else
+ sprintf(buf, "%d SCBs", ahc->scb_data->maxhscbs);
+}
+
/*
* Start the board, ready for normal operation
*/
int
ahc_init(struct ahc_softc *ahc)
{
- int max_targ = 15;
- int i;
- int term;
- u_int scsi_conf;
- u_int scsiseq_template;
- u_int ultraenb;
- u_int discenable;
- u_int tagenable;
- size_t driver_data_size;
- u_int32_t physaddr;
+ int max_targ;
+ int i;
+ int term;
+ u_int scsi_conf;
+ u_int scsiseq_template;
+ u_int ultraenb;
+ u_int discenable;
+ u_int tagenable;
+ size_t driver_data_size;
+ uint32_t physaddr;
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_DEBUG_SEQUENCER) != 0)
+ ahc->flags |= AHC_SEQUENCER_DEBUG;
+#endif
#ifdef AHC_PRINT_SRAM
printf("Scratch Ram:");
@@ -3495,56 +4455,81 @@ ahc_init(struct ahc_softc *ahc)
}
}
printf ("\n");
+ /*
+ * Reading uninitialized scratch ram may
+ * generate parity errors.
+ */
+ ahc_outb(ahc, CLRINT, CLRPARERR);
+ ahc_outb(ahc, CLRINT, CLRBRKADRINT);
#endif
+ max_targ = 15;
/*
* Assume we have a board at this stage and it has been reset.
*/
if ((ahc->flags & AHC_USEDEFAULTS) != 0)
ahc->our_id = ahc->our_id_b = 7;
-
+
/*
* Default to allowing initiator operations.
*/
- ahc->flags |= AHC_INITIATORMODE;
+ ahc->flags |= AHC_INITIATORROLE;
+
+ /*
+ * Only allow target mode features if this unit has them enabled.
+ */
+ //if ((AHC_TMODE_ENABLE & (0x1 << ahc->unit)) == 0)
+ ahc->features &= ~AHC_TARGETMODE;
/*
* DMA tag for our command fifos and other data in system memory
* the card's sequencer must be able to access. For initiator
- * roles, we need to allocate space for the qinfifo, qoutfifo,
- * and untagged_scb arrays each of which are composed of 256
- * 1 byte elements. When providing for the target mode role,
- * we additionally must provide space for the incoming target
- * command fifo.
+ * roles, we need to allocate space for the qinfifo and qoutfifo.
+ * The qinfifo and qoutfifo are composed of 256 1 byte elements.
+ * When providing for the target mode role, we must additionally
+ * provide space for the incoming target command fifo and an extra
+ * byte to deal with a dma bug in some chip versions.
*/
- driver_data_size = 3 * 256 * sizeof(u_int8_t);
+ driver_data_size = 2 * 256 * sizeof(uint8_t);
+ if ((ahc->features & AHC_TARGETMODE) != 0)
+ driver_data_size += AHC_TMODE_CMDS * sizeof(struct target_cmd)
+ + /*DMA WideOdd Bug Buffer*/1;
if (ahc_createdmamem(ahc->parent_dmat, driver_data_size,
- ahc->sc_dmaflags,
- &ahc->shared_data_dmamap, (caddr_t *)&ahc->qoutfifo,
- &ahc->shared_data_busaddr, &ahc->shared_data_seg,
- &ahc->shared_data_nseg, ahc_name(ahc), "shared data") < 0)
+ ahc->sc_dmaflags,
+ &ahc->shared_data_dmamap, (caddr_t *)&ahc->qoutfifo,
+ &ahc->shared_data_busaddr, &ahc->shared_data_seg,
+ &ahc->shared_data_nseg, ahc_name(ahc), "shared data") < 0)
return (ENOMEM);
ahc->init_level++;
- /* Allocate SCB data now that parent_dmat is initialized */
- if (ahc->scb_data->maxhscbs == 0)
- if (ahcinitscbdata(ahc) != 0)
- return (ENOMEM);
-
+ if ((ahc->features & AHC_TARGETMODE) != 0) {
+ ahc->targetcmds = (struct target_cmd *)ahc->qoutfifo;
+ ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[AHC_TMODE_CMDS];
+ ahc->dma_bug_buf = ahc->shared_data_busaddr
+ + driver_data_size - 1;
+ /* All target command blocks start out invalid. */
+ for (i = 0; i < AHC_TMODE_CMDS; i++)
+ ahc->targetcmds[i].cmd_valid = 0;
+ ahc_sync_tqinfifo(ahc, BUS_DMASYNC_PREREAD);
+ ahc->tqinfifonext = 1;
+ ahc_outb(ahc, KERNEL_TQINPOS, ahc->tqinfifonext - 1);
+ ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
+ ahc->qoutfifo = (uint8_t *)&ahc->targetcmds[256];
+ }
ahc->qinfifo = &ahc->qoutfifo[256];
- ahc->untagged_scbs = &ahc->qinfifo[256];
- /* There are no untagged SCBs active yet. */
- for (i = 0; i < 256; i++)
- ahc->untagged_scbs[i] = SCB_LIST_NULL;
- /* All of our queues are empty */
- for (i = 0; i < 256; i++)
- ahc->qoutfifo[i] = SCB_LIST_NULL;
+ ahc->init_level++;
+
+ /* Allocate SCB data now that buffer_dmat is initialized */
+ if (ahc->scb_data->maxhscbs == 0)
+ if (ahc_init_scbdata(ahc) != 0)
+ return (ENOMEM);
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap, 0,
- driver_data_size, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
+ if (bootverbose)
+ printf("%s: found %d SCBs\n", ahc_name(ahc),
+ ahc->scb_data->maxhscbs);
/*
* Allocate a tstate to house information for our
@@ -3552,51 +4537,36 @@ ahc_init(struct ahc_softc *ahc)
* data for any target mode initiator.
*/
if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
- aprint_error("%s: unable to allocate tmode_tstate. "
+ printf("%s: unable to allocate ahc_tmode_tstate. "
"Failing attach\n", ahc_name(ahc));
- return (-1);
+ return (ENOMEM);
}
if ((ahc->features & AHC_TWIN) != 0) {
if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
- aprint_error("%s: unable to allocate tmode_tstate. "
+ printf("%s: unable to allocate ahc_tmode_tstate. "
"Failing attach\n", ahc_name(ahc));
- return (-1);
- }
- aprint_normal(
- "Twin Channel, A SCSI Id=%d, B SCSI Id=%d, primary %c, ",
- ahc->our_id, ahc->our_id_b,
- ahc->flags & AHC_CHANNEL_B_PRIMARY? 'B': 'A');
- } else {
- if ((ahc->features & AHC_WIDE) != 0) {
- aprint_normal("Wide ");
- } else {
- aprint_normal("Single ");
+ return (ENOMEM);
}
- aprint_normal("Channel %c, SCSI Id=%d, ", ahc->channel,
- ahc->our_id);
}
ahc_outb(ahc, SEQ_FLAGS, 0);
+ ahc_outb(ahc, SEQ_FLAGS2, 0);
- if (ahc->scb_data->maxhscbs < AHC_SCB_MAX) {
+ if (ahc->scb_data->maxhscbs < AHC_SCB_MAX_ALLOC) {
ahc->flags |= AHC_PAGESCBS;
- aprint_normal("%d/%d SCBs\n", ahc->scb_data->maxhscbs,
- AHC_SCB_MAX);
} else {
ahc->flags &= ~AHC_PAGESCBS;
- aprint_normal("%d SCBs\n", ahc->scb_data->maxhscbs);
}
#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWMISC) {
- aprint_debug(
- "%s: hardware scb %lu bytes; kernel scb %lu bytes; "
- "ahc_dma %lu bytes\n",
+ if (ahc_debug & AHC_SHOW_MISC) {
+ printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
+ "ahc_dma %d bytes\n",
ahc_name(ahc),
- (unsigned long) sizeof(struct hardware_scb),
- (unsigned long) sizeof(struct scb),
- (unsigned long) sizeof(struct ahc_dma_seg));
+ sizeof(struct hardware_scb),
+ sizeof(struct scb),
+ sizeof(struct ahc_dma_seg));
}
#endif /* AHC_DEBUG */
@@ -3607,24 +4577,25 @@ ahc_init(struct ahc_softc *ahc)
* The device is gated to channel B after a chip reset,
* so set those values first
*/
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
term = (ahc->flags & AHC_TERM_ENB_B) != 0 ? STPWEN : 0;
- if ((ahc->features & AHC_ULTRA2) != 0)
- ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id_b);
- else
- ahc_outb(ahc, SCSIID, ahc->our_id_b);
+ ahc_outb(ahc, SCSIID, ahc->our_id_b);
scsi_conf = ahc_inb(ahc, SCSICONF + 1);
ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
- |term|ENSTIMER|ACTNEGEN);
+ |term|ahc->seltime_b|ENSTIMER|ACTNEGEN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
if ((scsi_conf & RESET_SCSI) != 0
- && (ahc->flags & AHC_INITIATORMODE) != 0)
+ && (ahc->flags & AHC_INITIATORROLE) != 0)
ahc->flags |= AHC_RESET_BUS_B;
/* Select Channel A */
ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
}
+
term = (ahc->flags & AHC_TERM_ENB_A) != 0 ? STPWEN : 0;
if ((ahc->features & AHC_ULTRA2) != 0)
ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
@@ -3632,27 +4603,28 @@ ahc_init(struct ahc_softc *ahc)
ahc_outb(ahc, SCSIID, ahc->our_id);
scsi_conf = ahc_inb(ahc, SCSICONF);
ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
- |term
+ |term|ahc->seltime
|ENSTIMER|ACTNEGEN);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SIMODE0, ahc_inb(ahc, SIMODE0)|ENIOERR);
ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
if ((scsi_conf & RESET_SCSI) != 0
- && (ahc->flags & AHC_INITIATORMODE) != 0)
+ && (ahc->flags & AHC_INITIATORROLE) != 0)
ahc->flags |= AHC_RESET_BUS_A;
/*
* Look at the information that board initialization or
* the board bios has left us.
*/
- ultraenb = 0;
+ ultraenb = 0;
tagenable = ALL_TARGETS_MASK;
/* Grab the disconnection disable table and invert it for our needs */
- if (ahc->flags & AHC_USEDEFAULTS) {
- aprint_normal(
- "%s: Host Adapter Bios disabled. Using default SCSI "
- "device parameters\n", ahc_name(ahc));
+ if ((ahc->flags & AHC_USEDEFAULTS) != 0) {
+ printf("%s: Host Adapter Bios disabled. Using default SCSI "
+ "device parameters\n", ahc_name(ahc));
ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
AHC_TERM_ENB_A|AHC_TERM_ENB_B;
discenable = ALL_TARGETS_MASK;
@@ -3671,7 +4643,7 @@ ahc_init(struct ahc_softc *ahc)
for (i = 0; i <= max_targ; i++) {
struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
+ struct ahc_tmode_tstate *tstate;
u_int our_id;
u_int target_id;
char channel;
@@ -3700,7 +4672,7 @@ ahc_init(struct ahc_softc *ahc)
tinfo->user.offset = ~0;
} else {
u_int scsirate;
- u_int16_t mask;
+ uint16_t mask;
/* Take the settings leftover in scratch RAM. */
scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
@@ -3721,6 +4693,9 @@ ahc_init(struct ahc_softc *ahc)
offset = MAX_OFFSET_ULTRA2;
} else
offset = ahc_inb(ahc, TARG_OFFSET + i);
+ if ((scsirate & ~WIDEXFER) == 0 && offset != 0)
+ /* Set to the lowest sync rate, 5MHz */
+ scsirate |= 0x1c;
maxsync = AHC_SYNCRATE_ULTRA2;
if ((ahc->features & AHC_DT) != 0)
maxsync = AHC_SYNCRATE_DT;
@@ -3730,8 +4705,18 @@ ahc_init(struct ahc_softc *ahc)
tinfo->user.period = 0;
else
tinfo->user.offset = ~0;
+ if ((scsirate & SXFR_ULTRA2) <= 8/*10MHz*/
+ && (ahc->features & AHC_DT) != 0)
+ tinfo->user.ppr_options =
+ MSG_EXT_PPR_DT_REQ;
} else if ((scsirate & SOFS) != 0) {
- tinfo->user.period =
+ if ((scsirate & SXFR) == 0x40
+ && (ultraenb & mask) != 0) {
+ /* Treat 10MHz as a non-ultra speed */
+ scsirate &= ~SXFR;
+ ultraenb &= ~mask;
+ }
+ tinfo->user.period =
ahc_find_period(ahc, scsirate,
(ultraenb & mask)
? AHC_SYNCRATE_ULTRA
@@ -3739,19 +4724,56 @@ ahc_init(struct ahc_softc *ahc)
if (tinfo->user.period != 0)
tinfo->user.offset = ~0;
}
+ if (tinfo->user.period == 0)
+ tinfo->user.offset = 0;
if ((scsirate & WIDEXFER) != 0
&& (ahc->features & AHC_WIDE) != 0)
tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+ tinfo->user.protocol_version = 4;
+ if ((ahc->features & AHC_DT) != 0)
+ tinfo->user.transport_version = 3;
+ else
+ tinfo->user.transport_version = 2;
+ tinfo->goal.protocol_version = 2;
+ tinfo->goal.transport_version = 2;
+ tinfo->curr.protocol_version = 2;
+ tinfo->curr.transport_version = 2;
}
- tinfo->goal = tinfo->user; /* force negotiation */
- tstate->ultraenb = ultraenb;
+ tstate->ultraenb = 0;
tstate->discenable = discenable;
- tstate->tagenable = 0; /* Wait until the XPT says its okay */
- tstate->tagdisable = 0;
}
ahc->user_discenable = discenable;
ahc->user_tagenable = tagenable;
+ /* There are no untagged SCBs active yet. */
+ for (i = 0; i < 16; i++) {
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, 0));
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ int lun;
+
+ /*
+ * The SCB based BTT allows an entry per
+ * target and lun pair.
+ */
+ for (lun = 1; lun < AHC_NUM_LUNS; lun++)
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, lun));
+ }
+ }
+
+ /* All of our queues are empty */
+ for (i = 0; i < 256; i++)
+ ahc->qoutfifo[i] = SCB_LIST_NULL;
+
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_PREREAD);
+
+ for (i = 0; i < 256; i++)
+ ahc->qinfifo[i] = SCB_LIST_NULL;
+
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ ahc_outb(ahc, TARGID, 0);
+ ahc_outb(ahc, TARGID + 1, 0);
+ }
+
/*
* Tell the sequencer where it can find our arrays in memory.
*/
@@ -3762,17 +4784,10 @@ ahc_init(struct ahc_softc *ahc)
ahc_outb(ahc, HSCB_ADDR + 3, (physaddr >> 24) & 0xFF);
physaddr = ahc->shared_data_busaddr;
- ahc_outb(ahc, SCBID_ADDR, physaddr & 0xFF);
- ahc_outb(ahc, SCBID_ADDR + 1, (physaddr >> 8) & 0xFF);
- ahc_outb(ahc, SCBID_ADDR + 2, (physaddr >> 16) & 0xFF);
- ahc_outb(ahc, SCBID_ADDR + 3, (physaddr >> 24) & 0xFF);
-
- /* Target mode incomding command fifo */
- physaddr += 3 * 256 * sizeof(u_int8_t);
- ahc_outb(ahc, TMODE_CMDADDR, physaddr & 0xFF);
- ahc_outb(ahc, TMODE_CMDADDR + 1, (physaddr >> 8) & 0xFF);
- ahc_outb(ahc, TMODE_CMDADDR + 2, (physaddr >> 16) & 0xFF);
- ahc_outb(ahc, TMODE_CMDADDR + 3, (physaddr >> 24) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 2, (physaddr >> 16) & 0xFF);
+ ahc_outb(ahc, SHARED_DATA_ADDR + 3, (physaddr >> 24) & 0xFF);
/*
* Initialize the group code to command length table.
@@ -3787,22 +4802,12 @@ ahc_init(struct ahc_softc *ahc)
ahc_outb(ahc, CMDSIZE_TABLE + 5, 11);
ahc_outb(ahc, CMDSIZE_TABLE + 6, 0);
ahc_outb(ahc, CMDSIZE_TABLE + 7, 0);
-
+
/* Tell the sequencer of our initial queue positions */
ahc_outb(ahc, KERNEL_QINPOS, 0);
ahc_outb(ahc, QINPOS, 0);
ahc_outb(ahc, QOUTPOS, 0);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWMISC)
- aprint_debug("DISCENABLE == 0x%x\nULTRAENB == 0x%x\n",
- discenable, ultraenb);
-#endif
-
- /* Don't have any special messages to send to targets */
- ahc_outb(ahc, TARGET_MSG_REQUEST, 0);
- ahc_outb(ahc, TARGET_MSG_REQUEST + 1, 0);
-
/*
* Use the built in queue management registers
* if they are available.
@@ -3826,1216 +4831,752 @@ ahc_init(struct ahc_softc *ahc)
/*
* Setup the allowed SCSI Sequences based on operational mode.
- * If we are a target, we'll enable select in operations once
+ * If we are a target, we'll enalbe select in operations once
* we've had a lun enabled.
*/
scsiseq_template = ENSELO|ENAUTOATNO|ENAUTOATNP;
- if ((ahc->flags & AHC_INITIATORMODE) != 0)
+ if ((ahc->flags & AHC_INITIATORROLE) != 0)
scsiseq_template |= ENRSELI;
ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
/*
* Load the Sequencer program and Enable the adapter
* in "fast" mode.
- */
-#ifdef AHC_DEBUG
- aprint_debug("%s: Downloading Sequencer Program...",
- ahc_name(ahc));
-#endif
+ */
+ if (bootverbose)
+ printf("%s: Downloading Sequencer Program...",
+ ahc_name(ahc));
ahc_loadseq(ahc);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int wait;
+
+ /*
+ * Wait for up to 500ms for our transceivers
+ * to settle. If the adapter does not have
+ * a cable attached, the tranceivers may
+ * never settle, so don't complain if we
+ * fail here.
+ */
+ ahc_pause(ahc);
+ for (wait = 5000;
+ (ahc_inb(ahc, SBLKCTL) & (ENAB40|ENAB20)) == 0 && wait;
+ wait--)
+ ahc_delay(100);
+ ahc_unpause(ahc);
+ }
+
/* We have to wait until after any system dumps... */
- shutdownhook_establish(ahc_shutdown, ahc);
+ ahc->shutdown_hook = shutdownhook_establish(ahc_shutdown, ahc);
return (0);
}
-static int
-ahc_ioctl(struct scsipi_channel *channel, u_long cmd, caddr_t addr, int flag,
- struct proc *p)
+void
+ahc_intr_enable(struct ahc_softc *ahc, int enable)
{
- struct ahc_softc *ahc = (void *)channel->chan_adapter->adapt_dev;
- int s, ret = ENOTTY;
-
- switch (cmd) {
- case SCBUSIORESET:
- s = splbio();
- ahc_reset_channel(ahc, channel->chan_channel == 1 ? 'B' : 'A',
- TRUE);
- splx(s);
- ret = 0;
- break;
- default:
- break;
- }
+ u_int hcntrl;
- return ret;
+ hcntrl = ahc_inb(ahc, HCNTRL);
+ hcntrl &= ~INTEN;
+ ahc->pause &= ~INTEN;
+ ahc->unpause &= ~INTEN;
+ if (enable) {
+ hcntrl |= INTEN;
+ ahc->pause |= INTEN;
+ ahc->unpause |= INTEN;
+ }
+ ahc_outb(ahc, HCNTRL, hcntrl);
}
-
/*
- * XXX fvdl the busy_tcl checks and settings should only be done
- * for the non-tagged queueing case, but we don't do tagged queueing
- * yet, so..
+ * Ensure that the card is paused in a location
+ * outside of all critical sections and that all
+ * pending work is completed prior to returning.
+ * This routine should only be called from outside
+ * an interrupt context.
*/
-static void
-ahc_action(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg)
+void
+ahc_pause_and_flushwork(struct ahc_softc *ahc)
{
- struct scsipi_xfer *xs;
- struct scsipi_periph *periph;
- struct ahc_softc *ahc = (void *)chan->chan_adapter->adapt_dev;
- struct scb *scb;
- struct hardware_scb *hscb;
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
- u_int target_id;
- u_int our_id;
- int s, tcl;
- u_int16_t mask;
- char channel;
-
- switch (req) {
- case ADAPTER_REQ_RUN_XFER:
- xs = arg;
- periph = xs->xs_periph;
-
- SC_DEBUG(xs->xs_periph, SCSIPI_DB3, ("ahc_action\n"));
+ int intstat;
+ int maxloops;
+ int paused;
+
+ maxloops = 1000;
+ ahc->flags |= AHC_ALL_INTERRUPTS;
+ intstat = 0;
+ paused = FALSE;
+ do {
+ if (paused)
+ ahc_unpause(ahc);
+ ahc_intr(ahc);
+ ahc_pause(ahc);
+ paused = TRUE;
+ ahc_outb(ahc, SCSISEQ, ahc_inb(ahc, SCSISEQ) & ~ENSELO);
+ ahc_clear_critical_section(ahc);
+ if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0)
+ break;
+ } while (--maxloops
+ && (((intstat = ahc_inb(ahc, INTSTAT)) & INT_PEND) != 0
+ || (ahc_inb(ahc, SSTAT0) & (SELDO|SELINGO))));
+ if (maxloops == 0) {
+ printf("Infinite interrupt loop, INTSTAT = %x",
+ ahc_inb(ahc, INTSTAT));
+ }
+ ahc_platform_flushwork(ahc);
+ ahc->flags &= ~AHC_ALL_INTERRUPTS;
+}
- /* must protect the queue */
- s = splbio();
+int
+ahc_suspend(struct ahc_softc *ahc)
+{
+ uint8_t *ptr;
+ int i;
- tcl = XS_TCL(ahc, xs);
+ ahc_pause_and_flushwork(ahc);
- if (!ahc_istagged_device(ahc, xs, 0) &&
- ahc_index_busy_tcl(ahc, tcl, FALSE) != SCB_LIST_NULL) {
- panic("ahc_action: not tagged and device busy");
- }
+ if (LIST_FIRST(&ahc->pending_scbs) != NULL)
+ return (EBUSY);
+#if AHC_TARGET_MODE
+ /*
+ * XXX What about ATIOs that have not yet been serviced?
+ * Perhaps we should just refuse to be suspended if we
+ * are acting in a target role.
+ */
+ if (ahc->pending_device != NULL)
+ return (EBUSY);
+#endif
- target_id = periph->periph_target;
- our_id = SIM_SCSI_ID(ahc, periph);
+ /* Save volatile registers */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+ ahc->suspend_state.channel[1].scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc->suspend_state.channel[1].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ ahc->suspend_state.channel[1].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+ ahc->suspend_state.channel[1].simode0 = ahc_inb(ahc, SIMODE0);
+ ahc->suspend_state.channel[1].simode1 = ahc_inb(ahc, SIMODE1);
+ ahc->suspend_state.channel[1].seltimer = ahc_inb(ahc, SELTIMER);
+ ahc->suspend_state.channel[1].seqctl = ahc_inb(ahc, SEQCTL);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
+ }
+ ahc->suspend_state.channel[0].scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc->suspend_state.channel[0].sxfrctl0 = ahc_inb(ahc, SXFRCTL0);
+ ahc->suspend_state.channel[0].sxfrctl1 = ahc_inb(ahc, SXFRCTL1);
+ ahc->suspend_state.channel[0].simode0 = ahc_inb(ahc, SIMODE0);
+ ahc->suspend_state.channel[0].simode1 = ahc_inb(ahc, SIMODE1);
+ ahc->suspend_state.channel[0].seltimer = ahc_inb(ahc, SELTIMER);
+ ahc->suspend_state.channel[0].seqctl = ahc_inb(ahc, SEQCTL);
+
+ if ((ahc->chip & AHC_PCI) != 0) {
+ ahc->suspend_state.dscommand0 = ahc_inb(ahc, DSCOMMAND0);
+ ahc->suspend_state.dspcistatus = ahc_inb(ahc, DSPCISTATUS);
+ }
- /*
- * get an scb to use.
- */
- if ((scb = ahcgetscb(ahc)) == NULL) {
- xs->error = XS_RESOURCE_SHORTAGE;
- scsipi_done(xs);
- splx(s);
- return;
- }
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
- tcl = XS_TCL(ahc, xs);
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc->suspend_state.optionmode = ahc_inb(ahc, OPTIONMODE);
+ ahc_outb(ahc, SFUNCT, sfunct);
+ ahc->suspend_state.crccontrol1 = ahc_inb(ahc, CRCCONTROL1);
+ }
-#ifdef DIAGNOSTIC
- if (!ahc_istagged_device(ahc, xs, 0) &&
- ahc_index_busy_tcl(ahc, tcl, FALSE) != SCB_LIST_NULL)
- panic("ahc: queuing for busy target");
-#endif
+ if ((ahc->features & AHC_MULTI_FUNC) != 0)
+ ahc->suspend_state.scbbaddr = ahc_inb(ahc, SCBBADDR);
- scb->xs = xs;
- hscb = scb->hscb;
- hscb->tcl = tcl;
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc->suspend_state.dff_thrsh = ahc_inb(ahc, DFF_THRSH);
- if (xs->xs_tag_type) {
-#ifdef DIAGNOSTIC
- if (ahc_istagged_device(ahc, xs, 0) == 0)
- panic("ahc_action: taggged command for untagged device");
-#endif
- scb->hscb->control |= TAG_ENB;
- } else
- ahc_busy_tcl(ahc, scb);
-
- splx(s);
-
- channel = SIM_CHANNEL(ahc, periph);
- if (ahc->inited_channels[channel - 'A'] == 0) {
- if ((channel == 'A' &&
- (ahc->flags & AHC_RESET_BUS_A)) ||
- (channel == 'B' &&
- (ahc->flags & AHC_RESET_BUS_B))) {
- s = splbio();
- ahc_reset_channel(ahc, channel, TRUE);
- splx(s);
- }
- ahc->inited_channels[channel - 'A'] = 1;
- }
+ ptr = ahc->suspend_state.scratch_ram;
+ for (i = 0; i < 64; i++)
+ *ptr++ = ahc_inb(ahc, SRAM_BASE + i);
- /*
- * Put all the arguments for the xfer in the scb
- */
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0; i < 16; i++)
+ *ptr++ = ahc_inb(ahc, TARG_OFFSET + i);
+ }
- mask = SCB_TARGET_MASK(scb);
- tinfo = ahc_fetch_transinfo(ahc,
- SIM_CHANNEL(ahc, periph),
- our_id, target_id, &tstate);
- if (ahc->inited_targets[target_id] == 0) {
- struct ahc_devinfo devinfo;
- s = splbio();
- ahc_compile_devinfo(&devinfo, our_id, target_id,
- periph->periph_lun,
- SIM_CHANNEL(ahc, periph),
- ROLE_INITIATOR);
- ahc_update_target_msg_request(ahc, &devinfo, tinfo,
- TRUE, FALSE);
- ahc->inited_targets[target_id] = 1;
- splx(s);
- }
- hscb->scsirate = tinfo->scsirate;
- hscb->scsioffset = tinfo->current.offset;
- if ((tstate->ultraenb & mask) != 0)
- hscb->control |= ULTRAENB;
-
- if ((tstate->discenable & mask) != 0)
- hscb->control |= DISCENB;
-
- if (xs->xs_control & XS_CTL_RESET) {
- hscb->cmdpointer = 0;
- scb->flags |= SCB_DEVICE_RESET;
- hscb->control |= MK_MESSAGE;
- ahc_execute_scb(scb, NULL, 0);
- }
+ ptr = ahc->suspend_state.btt;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ for (i = 0;i < AHC_NUM_TARGETS; i++) {
+ int j;
- ahc_setup_data(ahc, xs, scb);
- return;
- case ADAPTER_REQ_GROW_RESOURCES:
- /* XXX not supported */
- return;
- case ADAPTER_REQ_SET_XFER_MODE:
- {
- struct scsipi_xfer_mode *xm = arg;
- struct ahc_devinfo devinfo;
- int target_id, our_id;
- char channel;
+ for (j = 0;j < AHC_NUM_LUNS; j++) {
+ u_int tcl;
- target_id = xm->xm_target;
- our_id = chan->chan_id;
- channel = (chan->chan_channel == 1) ? 'B' : 'A';
- s = splbio();
- tinfo = ahc_fetch_transinfo(ahc, channel, our_id, target_id,
- &tstate);
- ahc_compile_devinfo(&devinfo, our_id, target_id,
- 0, channel, ROLE_INITIATOR);
- ahc->inited_targets[target_id] = 2;
- if (xm->xm_mode & PERIPH_CAP_TQING &&
- (tstate->tagdisable & devinfo.target_mask) == 0) {
- ahc_set_tags(ahc, &devinfo, TRUE);
+ tcl = BUILD_TCL(i << 4, j);
+ *ptr = ahc_index_busy_tcl(ahc, tcl);
+ }
}
- splx(s);
- ahc_update_xfer_mode(ahc, &devinfo);
- }
}
+ ahc_shutdown(ahc);
+ return (0);
}
-static void
-ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments)
+int
+ahc_resume(struct ahc_softc *ahc)
{
- struct scb *scb;
- struct scsipi_xfer *xs;
- struct ahc_softc *ahc;
- int s;
+ uint8_t *ptr;
+ int i;
- scb = (struct scb *)arg;
- xs = scb->xs;
- ahc = (void *)xs->xs_periph->periph_channel->chan_adapter->adapt_dev;
-
-
- if (nsegments != 0) {
- struct ahc_dma_seg *sg;
- bus_dma_segment_t *end_seg;
- int op;
-
- end_seg = dm_segs + nsegments;
-
- /* Copy the first SG into the data pointer area */
- scb->hscb->data = dm_segs->ds_addr;
- scb->hscb->datalen = dm_segs->ds_len;
-
- /* Copy the segments into our SG list */
- sg = scb->sg_list;
- while (dm_segs < end_seg) {
- sg->addr = dm_segs->ds_addr;
- sg->len = dm_segs->ds_len;
- ahc_swap_sg(sg);
- sg++;
- dm_segs++;
- }
-
- /* Note where to find the SG entries in bus space */
- scb->hscb->SG_pointer = scb->sg_list_phys;
-
- if (xs->xs_control & XS_CTL_DATA_IN)
- op = BUS_DMASYNC_PREREAD;
- else
- op = BUS_DMASYNC_PREWRITE;
+ ahc_reset(ahc);
- bus_dmamap_sync(ahc->parent_dmat, scb->dmamap, 0,
- scb->dmamap->dm_mapsize, op);
+ ahc_build_free_scb_list(ahc);
- } else {
- scb->hscb->SG_pointer = 0;
- scb->hscb->data = 0;
- scb->hscb->datalen = 0;
+ /* Restore volatile registers */
+ if ((ahc->features & AHC_TWIN) != 0) {
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) | SELBUSB);
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+ ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[1].scsiseq);
+ ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[1].sxfrctl0);
+ ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[1].sxfrctl1);
+ ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[1].simode0);
+ ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[1].simode1);
+ ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[1].seltimer);
+ ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[1].seqctl);
+ ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) & ~SELBUSB);
}
+ ahc_outb(ahc, SCSISEQ, ahc->suspend_state.channel[0].scsiseq);
+ ahc_outb(ahc, SXFRCTL0, ahc->suspend_state.channel[0].sxfrctl0);
+ ahc_outb(ahc, SXFRCTL1, ahc->suspend_state.channel[0].sxfrctl1);
+ ahc_outb(ahc, SIMODE0, ahc->suspend_state.channel[0].simode0);
+ ahc_outb(ahc, SIMODE1, ahc->suspend_state.channel[0].simode1);
+ ahc_outb(ahc, SELTIMER, ahc->suspend_state.channel[0].seltimer);
+ ahc_outb(ahc, SEQCTL, ahc->suspend_state.channel[0].seqctl);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, ahc->our_id);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id);
- scb->sg_count = scb->hscb->SG_count = nsegments;
-
- s = splbio();
-
- /*
- * Last time we need to check if this SCB needs to
- * be aborted.
- */
- if (xs->xs_status & XS_STS_DONE) {
- if (!ahc_istagged_device(ahc, xs, 0))
- ahc_index_busy_tcl(ahc, scb->hscb->tcl, TRUE);
- if (nsegments != 0)
- bus_dmamap_unload(ahc->parent_dmat, scb->dmamap);
- ahcfreescb(ahc, scb);
- splx(s);
- return;
+ if ((ahc->chip & AHC_PCI) != 0) {
+ ahc_outb(ahc, DSCOMMAND0, ahc->suspend_state.dscommand0);
+ ahc_outb(ahc, DSPCISTATUS, ahc->suspend_state.dspcistatus);
}
-#ifdef DIAGNOSTIC
- if (scb->sg_count > 255)
- panic("ahc bad sg_count");
-#endif
-
- ahc_swap_hscb(scb->hscb);
+ if ((ahc->features & AHC_DT) != 0) {
+ u_int sfunct;
- LIST_INSERT_HEAD(&ahc->pending_ccbs, scb, plinks);
+ sfunct = ahc_inb(ahc, SFUNCT) & ~ALT_MODE;
+ ahc_outb(ahc, SFUNCT, sfunct | ALT_MODE);
+ ahc_outb(ahc, OPTIONMODE, ahc->suspend_state.optionmode);
+ ahc_outb(ahc, SFUNCT, sfunct);
+ ahc_outb(ahc, CRCCONTROL1, ahc->suspend_state.crccontrol1);
+ }
- scb->flags |= SCB_ACTIVE;
+ if ((ahc->features & AHC_MULTI_FUNC) != 0)
+ ahc_outb(ahc, SCBBADDR, ahc->suspend_state.scbbaddr);
- if (!(xs->xs_control & XS_CTL_POLL))
- callout_reset(&scb->xs->xs_callout, mstohz(xs->timeout),
- ahc_timeout, scb);
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, DFF_THRSH, ahc->suspend_state.dff_thrsh);
- if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) {
-#if 0
- printf("Continueing Immediate Command %d:%d\n",
- xs->xs_periph->periph_target,
- xs->xs_periph->periph_lun);
-#endif
- pause_sequencer(ahc);
- if ((ahc->flags & AHC_PAGESCBS) == 0)
- ahc_outb(ahc, SCBPTR, scb->hscb->tag);
- ahc_outb(ahc, SCB_TAG, scb->hscb->tag);
- ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
- unpause_sequencer(ahc);
- } else {
+ ptr = ahc->suspend_state.scratch_ram;
+ for (i = 0; i < 64; i++)
+ ahc_outb(ahc, SRAM_BASE + i, *ptr++);
-#if 0
- printf("tag %x at qpos %u vaddr %p paddr 0x%lx\n",
- scb->hscb->tag, ahc->qinfifonext,
- &ahc->qinfifo[ahc->qinfifonext],
- ahc->shared_data_busaddr + 1024 + ahc->qinfifonext);
-#endif
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0; i < 16; i++)
+ ahc_outb(ahc, TARG_OFFSET + i, *ptr++);
+ }
- ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+ ptr = ahc->suspend_state.btt;
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ for (i = 0;i < AHC_NUM_TARGETS; i++) {
+ int j;
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap,
- QINFIFO_OFFSET * 256, 256, BUS_DMASYNC_PREWRITE);
+ for (j = 0;j < AHC_NUM_LUNS; j++) {
+ u_int tcl;
- if ((ahc->features & AHC_QUEUE_REGS) != 0) {
- ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
- } else {
- pause_sequencer(ahc);
- ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
- unpause_sequencer(ahc);
+ tcl = BUILD_TCL(i << 4, j);
+ ahc_busy_tcl(ahc, tcl, *ptr);
+ }
}
}
+ return (0);
+}
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWCMDS) {
- scsi_print_addr(xs->xs_periph);
- printf("opcode %d tag %x len %d flags %x control %x fpos %u"
- " rate %x\n",
- xs->cmdstore.opcode, scb->hscb->tag, scb->hscb->datalen,
- scb->flags, scb->hscb->control, ahc->qinfifonext,
- scb->hscb->scsirate);
+/************************** Busy Target Table *********************************/
+/*
+ * Return the untagged transaction id for a given target/channel lun.
+ * Optionally, clear the entry.
+ */
+u_int
+ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl)
+{
+ u_int scbid;
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ scbid = ahc_inb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl));
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ scbid = ahc_inb(ahc, BUSY_TARGETS + target_offset);
}
-#endif
- if (!(xs->xs_control & XS_CTL_POLL)) {
- splx(s);
- return;
- }
- /*
- * If we can't use interrupts, poll for completion
- */
- SC_DEBUG(xs->xs_periph, SCSIPI_DB3, ("cmd_poll\n"));
- do {
- if (ahc_poll(ahc, xs->timeout)) {
- if (!(xs->xs_control & XS_CTL_SILENT))
- printf("cmd fail\n");
- ahc_timeout(scb);
- break;
- }
- } while (!(xs->xs_status & XS_STS_DONE));
- splx(s);
- return;
+ return (scbid);
}
-static int
-ahc_poll(struct ahc_softc *ahc, int wait)
+void
+ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl)
{
- while (--wait) {
- DELAY(1000);
- if (ahc_inb(ahc, INTSTAT) & INT_PEND)
- break;
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ ahc_outb(ahc, SCB_64_BTT+TCL_TARGET_OFFSET(tcl), SCB_LIST_NULL);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ ahc_outb(ahc, BUSY_TARGETS + target_offset, SCB_LIST_NULL);
}
+}
- if (wait == 0) {
- printf("%s: board is not responding\n", ahc_name(ahc));
- return (EIO);
+void
+ahc_busy_tcl(struct ahc_softc *ahc, u_int tcl, u_int scbid)
+{
+ u_int target_offset;
+
+ if ((ahc->flags & AHC_SCB_BTT) != 0) {
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, TCL_LUN(tcl));
+ ahc_outb(ahc, SCB_64_BTT + TCL_TARGET_OFFSET(tcl), scbid);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ } else {
+ target_offset = TCL_TARGET_OFFSET(tcl);
+ ahc_outb(ahc, BUSY_TARGETS + target_offset, scbid);
}
-
- ahc_intr((void *)ahc);
- return (0);
}
-static void
-ahc_setup_data(struct ahc_softc *ahc, struct scsipi_xfer *xs,
- struct scb *scb)
+/************************** SCB and SCB queue management **********************/
+int
+ahc_match_scb(struct ahc_softc *ahc, struct scb *scb, int target,
+ char channel, int lun, u_int tag, role_t role)
{
- struct hardware_scb *hscb;
-
- hscb = scb->hscb;
- xs->resid = xs->status = 0;
-
- hscb->cmdlen = xs->cmdlen;
- memcpy(hscb->cmdstore, xs->cmd, xs->cmdlen);
- hscb->cmdpointer = hscb->cmdstore_busaddr;
-
- /* Only use S/G if there is a transfer */
- if (xs->datalen) {
- int error;
+ int targ = SCB_GET_TARGET(ahc, scb);
+ char chan = SCB_GET_CHANNEL(ahc, scb);
+ int slun = SCB_GET_LUN(scb);
+ int match;
- error = bus_dmamap_load(ahc->parent_dmat,
- scb->dmamap, xs->data,
- xs->datalen, NULL,
- ((xs->xs_control & XS_CTL_NOSLEEP) ?
- BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
- BUS_DMA_STREAMING |
- ((xs->xs_control & XS_CTL_DATA_IN) ?
- BUS_DMA_READ : BUS_DMA_WRITE));
- if (error) {
-#ifdef AHC_DEBUG
- printf("%s: in ahc_setup_data(): bus_dmamap_load() "
- "= %d\n",
- ahc_name(ahc),
- error);
-#endif
- if (!ahc_istagged_device(ahc, xs, 0))
- ahc_index_busy_tcl(ahc, hscb->tcl, TRUE);
- xs->error = XS_RESOURCE_SHORTAGE; /* XXX fvdl */
- scsipi_done(xs);
- return;
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == CAM_TARGET_WILDCARD));
+ if (match != 0)
+ match = ((lun == slun) || (lun == CAM_LUN_WILDCARD));
+ if (match != 0) {
+#if 0
+#if AHC_TARGET_MODE
+ int group;
+
+ group = XPT_FC_GROUP(scb->io_ctx->ccb_h.func_code);
+ if (role == ROLE_INITIATOR) {
+ match = (group != XPT_FC_GROUP_TMODE)
+ && ((tag == scb->hscb->tag)
+ || (tag == SCB_LIST_NULL));
+ } else if (role == ROLE_TARGET) {
+ match = (group == XPT_FC_GROUP_TMODE)
+ && ((tag == scb->io_ctx->csio.tag_id)
+ || (tag == SCB_LIST_NULL));
}
- ahc_execute_scb(scb,
- scb->dmamap->dm_segs,
- scb->dmamap->dm_nsegs);
- } else {
- ahc_execute_scb(scb, NULL, 0);
+#else /* !AHC_TARGET_MODE */
+ match = ((tag == scb->hscb->tag) || (tag == SCB_LIST_NULL));
+#endif /* AHC_TARGET_MODE */
+#endif
}
+
+ return match;
}
-static void
-ahc_freeze_devq(struct ahc_softc *ahc, struct scsipi_periph *periph)
+void
+ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
{
int target;
char channel;
int lun;
- target = periph->periph_target;
- lun = periph->periph_lun;
- channel = periph->periph_channel->chan_channel;
-
+ target = SCB_GET_TARGET(ahc, scb);
+ lun = SCB_GET_LUN(scb);
+ channel = SCB_GET_CHANNEL(ahc, scb);
+
ahc_search_qinfifo(ahc, target, channel, lun,
/*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
- SCB_REQUEUE, SEARCH_COMPLETE);
-}
-
-static void
-ahcallocscbs(struct ahc_softc *ahc)
-{
- struct scb_data *scb_data;
- struct scb *next_scb;
- struct sg_map_node *sg_map;
- bus_addr_t physaddr;
- struct ahc_dma_seg *segs;
- int newcount;
- int i;
+ CAM_REQUEUE_REQ, SEARCH_COMPLETE);
- scb_data = ahc->scb_data;
- if (scb_data->numscbs >= AHC_SCB_MAX)
- /* Can't allocate any more */
- return;
-
- next_scb = &scb_data->scbarray[scb_data->numscbs];
-
- sg_map = malloc(sizeof(*sg_map), M_DEVBUF, M_NOWAIT);
-
- if (sg_map == NULL)
- return;
-
- if (ahc_createdmamem(ahc->parent_dmat, PAGE_SIZE, ahc->sc_dmaflags,
- &sg_map->sg_dmamap,
- (caddr_t *)&sg_map->sg_vaddr, &sg_map->sg_physaddr,
- &sg_map->sg_dmasegs, &sg_map->sg_nseg, ahc_name(ahc),
- "SG space") < 0) {
- free(sg_map, M_DEVBUF);
- return;
- }
-
- SLIST_INSERT_HEAD(&scb_data->sg_maps, sg_map, links);
-
- segs = sg_map->sg_vaddr;
- physaddr = sg_map->sg_physaddr;
-
- newcount = (PAGE_SIZE / (AHC_NSEG * sizeof(struct ahc_dma_seg)));
- for (i = 0; scb_data->numscbs < AHC_SCB_MAX && i < newcount; i++) {
- int error;
-
- next_scb->sg_list = segs;
- /*
- * The sequencer always starts with the second entry.
- * The first entry is embedded in the scb.
- */
- next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
- next_scb->flags = SCB_FREE;
- error = bus_dmamap_create(ahc->parent_dmat,
- AHC_MAXTRANSFER_SIZE, AHC_NSEG, MAXBSIZE, 0,
- BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW|ahc->sc_dmaflags,
- &next_scb->dmamap);
- if (error != 0)
- break;
- next_scb->hscb = &scb_data->hscbs[scb_data->numscbs];
- next_scb->hscb->tag = ahc->scb_data->numscbs;
- next_scb->hscb->cmdstore_busaddr =
- ahc_hscb_busaddr(ahc, next_scb->hscb->tag)
- + offsetof(struct hardware_scb, cmdstore);
- next_scb->hscb->cmdstore_busaddr =
- htole32(next_scb->hscb->cmdstore_busaddr);
- SLIST_INSERT_HEAD(&ahc->scb_data->free_scbs, next_scb, links);
- segs += AHC_NSEG;
- physaddr += (AHC_NSEG * sizeof(struct ahc_dma_seg));
- next_scb++;
- ahc->scb_data->numscbs++;
- }
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSCBALLOC)
- printf("%s: allocated %d new SCBs count now %d\n",
- ahc_name(ahc), i - 1, ahc->scb_data->numscbs);
-#endif
+ ahc_platform_freeze_devq(ahc, scb);
}
-#ifdef AHC_DUMP_SEQ
-static void
-ahc_dumpseq(struct ahc_softc* ahc)
+void
+ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
{
- int i;
- int max_prog;
+ struct scb *prev_scb;
- if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
- max_prog = 448;
- else if ((ahc->features & AHC_ULTRA2) != 0)
- max_prog = 768;
- else
- max_prog = 512;
+ prev_scb = NULL;
+ if (ahc_qinfifo_count(ahc) != 0) {
+ u_int prev_tag;
+ uint8_t prev_pos;
- ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
- ahc_outb(ahc, SEQADDR0, 0);
- ahc_outb(ahc, SEQADDR1, 0);
- for (i = 0; i < max_prog; i++) {
- u_int8_t ins_bytes[4];
-
- ahc_insb(ahc, SEQRAM, ins_bytes, 4);
- printf("0x%08x\n", ins_bytes[0] << 24
- | ins_bytes[1] << 16
- | ins_bytes[2] << 8
- | ins_bytes[3]);
+ prev_pos = ahc->qinfifonext - 1;
+ prev_tag = ahc->qinfifo[prev_pos];
+ prev_scb = ahc_lookup_scb(ahc, prev_tag);
+ }
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
}
}
-#endif
static void
-ahc_loadseq(struct ahc_softc *ahc)
+ahc_qinfifo_requeue(struct ahc_softc *ahc, struct scb *prev_scb,
+ struct scb *scb)
{
- const struct patch *cur_patch;
- int i;
- int downloaded;
- int skip_addr;
- u_int8_t download_consts[4];
-
- /* Setup downloadable constant table */
-#if 0
- /* No downloaded constants are currently defined. */
- download_consts[TMODE_NUMCMDS] = ahc->num_targetcmds;
-#endif
-
- cur_patch = patches;
- downloaded = 0;
- skip_addr = 0;
- ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
- ahc_outb(ahc, SEQADDR0, 0);
- ahc_outb(ahc, SEQADDR1, 0);
-
- for (i = 0; i < sizeof(seqprog)/4; i++) {
- if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
- /*
- * Don't download this instruction as it
- * is in a patch that was removed.
- */
- continue;
- }
- ahc_download_instr(ahc, i, download_consts);
- downloaded++;
+ if (prev_scb == NULL) {
+ ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+ } else {
+ prev_scb->hscb->next = scb->hscb->tag;
+ ahc_sync_scb(ahc, prev_scb,
+ BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
- ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
- restart_sequencer(ahc);
-
-#ifdef AHC_DEBUG
- printf(" %d instructions downloaded\n", downloaded);
-#endif
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+ scb->hscb->next = ahc->next_queued_scb->hscb->tag;
+ ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
}
static int
-ahc_check_patch(struct ahc_softc *ahc, const struct patch **start_patch,
- int start_instr, int *skip_addr)
+ahc_qinfifo_count(struct ahc_softc *ahc)
{
- const struct patch *cur_patch;
- const struct patch *last_patch;
- int num_patches;
+ uint8_t qinpos;
+ uint8_t diff;
- num_patches = sizeof(patches)/sizeof(struct patch);
- last_patch = &patches[num_patches];
- cur_patch = *start_patch;
-
- while (cur_patch < last_patch && start_instr == cur_patch->begin) {
-
- if (cur_patch->patch_func(ahc) == 0) {
-
- /* Start rejecting code */
- *skip_addr = start_instr + cur_patch->skip_instr;
- cur_patch += cur_patch->skip_patch;
- } else {
- /* Accepted this patch. Advance to the next
- * one and wait for our intruction pointer to
- * hit this point.
- */
- cur_patch++;
- }
- }
-
- *start_patch = cur_patch;
- if (start_instr < *skip_addr)
- /* Still skipping */
- return (0);
-
- return (1);
-}
-
-static void
-ahc_download_instr(struct ahc_softc *ahc, int instrptr, u_int8_t *dconsts)
-{
- union ins_formats instr;
- struct ins_format1 *fmt1_ins;
- struct ins_format3 *fmt3_ins;
- u_int opcode;
-
- /* Structure copy */
- memcpy(&instr, &seqprog[instrptr * 4], sizeof instr);
-
- instr.integer = le32toh(instr.integer);
-
- fmt1_ins = &instr.format1;
- fmt3_ins = NULL;
-
- /* Pull the opcode */
- opcode = instr.format1.opcode;
- switch (opcode) {
- case AIC_OP_JMP:
- case AIC_OP_JC:
- case AIC_OP_JNC:
- case AIC_OP_CALL:
- case AIC_OP_JNE:
- case AIC_OP_JNZ:
- case AIC_OP_JE:
- case AIC_OP_JZ:
- {
- const struct patch *cur_patch;
- int address_offset;
- u_int address;
- int skip_addr;
- int i;
-
- fmt3_ins = &instr.format3;
- address_offset = 0;
- address = fmt3_ins->address;
- cur_patch = patches;
- skip_addr = 0;
-
- for (i = 0; i < address;) {
-
- ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
-
- if (skip_addr > i) {
- int end_addr;
-
- end_addr = MIN(address, skip_addr);
- address_offset += end_addr - i;
- i = skip_addr;
- } else {
- i++;
- }
- }
- address -= address_offset;
- fmt3_ins->address = address;
- /* FALLTHROUGH */
- }
- case AIC_OP_OR:
- case AIC_OP_AND:
- case AIC_OP_XOR:
- case AIC_OP_ADD:
- case AIC_OP_ADC:
- case AIC_OP_BMOV:
- if (fmt1_ins->parity != 0) {
- fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
- }
- fmt1_ins->parity = 0;
- /* FALLTHROUGH */
- case AIC_OP_ROL:
- if ((ahc->features & AHC_ULTRA2) != 0) {
- int i, count;
-
- /* Calculate odd parity for the instruction */
- for (i = 0, count = 0; i < 31; i++) {
- u_int32_t mask;
-
- mask = 0x01 << i;
- if ((instr.integer & mask) != 0)
- count++;
- }
- if ((count & 0x01) == 0)
- instr.format1.parity = 1;
- } else {
- /* Compress the instruction for older sequencers */
- if (fmt3_ins != NULL) {
- instr.integer =
- fmt3_ins->immediate
- | (fmt3_ins->source << 8)
- | (fmt3_ins->address << 16)
- | (fmt3_ins->opcode << 25);
- } else {
- instr.integer =
- fmt1_ins->immediate
- | (fmt1_ins->source << 8)
- | (fmt1_ins->destination << 16)
- | (fmt1_ins->ret << 24)
- | (fmt1_ins->opcode << 25);
- }
- }
- instr.integer = htole32(instr.integer);
- ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
- break;
- default:
- panic("Unknown opcode encountered in seq program");
- break;
- }
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ qinpos = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinpos);
+ } else
+ qinpos = ahc_inb(ahc, QINPOS);
+ diff = ahc->qinfifonext - qinpos;
+ return (diff);
}
-static void
-ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb)
+int
+ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status,
+ ahc_search_action action)
{
+ struct scb *scb;
+ struct scb *prev_scb;
+ uint8_t qinstart;
+ uint8_t qinpos;
+ uint8_t qintail;
+ uint8_t next;
+ uint8_t prev;
+ uint8_t curscbptr;
+ int found;
+ int have_qregs;
- if ((scb->flags & SCB_RECOVERY_SCB) == 0) {
- struct scb *scbp;
-
- scb->flags |= SCB_RECOVERY_SCB;
-
- /*
- * Take all queued, but not sent SCBs out of the equation.
- * Also ensure that no new CCBs are queued to us while we
- * try to fix this problem.
- */
- scsipi_channel_freeze(&ahc->sc_channel, 1);
- if (ahc->features & AHC_TWIN)
- scsipi_channel_freeze(&ahc->sc_channel_b, 1);
+ qintail = ahc->qinfifonext;
+ have_qregs = (ahc->features & AHC_QUEUE_REGS) != 0;
+ if (have_qregs) {
+ qinstart = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinstart);
+ } else
+ qinstart = ahc_inb(ahc, QINPOS);
+ qinpos = qinstart;
+ found = 0;
+ prev_scb = NULL;
+ if (action == SEARCH_COMPLETE) {
/*
- * Go through all of our pending SCBs and remove
- * any scheduled timeouts for them. We will reschedule
- * them after we've successfully fixed this problem.
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
*/
- scbp = ahc->pending_ccbs.lh_first;
- while (scbp != NULL) {
- callout_stop(&scbp->xs->xs_callout);
- scbp = scbp->plinks.le_next;
- }
- }
-}
-
-static void
-ahc_timeout(void *arg)
-{
- struct scb *scb;
- struct ahc_softc *ahc;
- int s, found;
- u_int last_phase;
- int target;
- int lun;
- int i;
- char channel;
-
- scb = (struct scb *)arg;
- ahc =
- (void *)scb->xs->xs_periph->periph_channel->chan_adapter->adapt_dev;
-
- s = splbio();
-
- /*
- * Ensure that the card doesn't do anything
- * behind our back. Also make sure that we
- * didn't "just" miss an interrupt that would
- * affect this timeout.
- */
- do {
- ahc_intr(ahc);
- pause_sequencer(ahc);
- } while (ahc_inb(ahc, INTSTAT) & INT_PEND);
-
- if ((scb->flags & SCB_ACTIVE) == 0) {
- /* Previous timeout took care of me already */
- printf("Timedout SCB handled by another timeout\n");
- unpause_sequencer(ahc);
- splx(s);
- return;
+ ahc_freeze_untagged_queues(ahc);
}
- target = SCB_TARGET(scb);
- channel = SCB_CHANNEL(scb);
- lun = SCB_LUN(scb);
-
- scsipi_printaddr(scb->xs->xs_periph);
- printf("SCB %x - timed out ", scb->hscb->tag);
/*
- * Take a snapshot of the bus state and print out
- * some information so we can track down driver bugs.
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
*/
- last_phase = ahc_inb(ahc, LASTPHASE);
-
- for (i = 0; i < num_phases; i++) {
- if (last_phase == phase_table[i].phase)
- break;
- }
- printf("%s", phase_table[i].phasemsg);
-
- printf(", SEQADDR == 0x%x\n",
- ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
- printf("SCSIRATE == 0x%x\n", ahc_inb(ahc, SCSIRATE));
-
-#ifdef AHC_DEBUG
- ahc_print_scb(scb);
-#endif
+ ahc->qinfifonext = qinpos;
+ ahc_outb(ahc, NEXT_QUEUED_SCB, ahc->next_queued_scb->hscb->tag);
-#if 0
- printf("SSTAT1 == 0x%x\n", ahc_inb(ahc, SSTAT1));
- printf("SSTAT3 == 0x%x\n", ahc_inb(ahc, SSTAT3));
- printf("SCSIPHASE == 0x%x\n", ahc_inb(ahc, SCSIPHASE));
- printf("SCSIOFFSET == 0x%x\n", ahc_inb(ahc, SCSIOFFSET));
- printf("SEQ_FLAGS == 0x%x\n", ahc_inb(ahc, SEQ_FLAGS));
- printf("SCB_DATAPTR == 0x%x\n", ahc_inb(ahc, SCB_DATAPTR)
- | ahc_inb(ahc, SCB_DATAPTR + 1) << 8
- | ahc_inb(ahc, SCB_DATAPTR + 2) << 16
- | ahc_inb(ahc, SCB_DATAPTR + 3) << 24);
- printf("SCB_DATACNT == 0x%x\n", ahc_inb(ahc, SCB_DATACNT)
- | ahc_inb(ahc, SCB_DATACNT + 1) << 8
- | ahc_inb(ahc, SCB_DATACNT + 2) << 16);
- printf("SCB_SGCOUNT == 0x%x\n", ahc_inb(ahc, SCB_SGCOUNT));
- printf("CCSCBCTL == 0x%x\n", ahc_inb(ahc, CCSCBCTL));
- printf("CCSCBCNT == 0x%x\n", ahc_inb(ahc, CCSCBCNT));
- printf("DFCNTRL == 0x%x\n", ahc_inb(ahc, DFCNTRL));
- printf("DFSTATUS == 0x%x\n", ahc_inb(ahc, DFSTATUS));
- printf("CCHCNT == 0x%x\n", ahc_inb(ahc, CCHCNT));
- if (scb->sg_count > 0) {
- for (i = 0; i < scb->sg_count; i++) {
- printf("sg[%d] - Addr 0x%x : Length %d\n",
- i,
- le32toh(scb->sg_list[i].addr),
- le32toh(scb->sg_list[i].len));
+ while (qinpos != qintail) {
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+ if (scb == NULL) {
+ printf("qinpos = %d, SCB index = %d\n",
+ qinpos, ahc->qinfifo[qinpos]);
+ panic("Loop 1\n");
}
- }
-#endif
- if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) {
- /*
- * Been down this road before.
- * Do a full bus reset.
- */
-bus_reset:
- ahcsetccbstatus(scb->xs, XS_TIMEOUT);
- found = ahc_reset_channel(ahc, channel, /*Initiate Reset*/TRUE);
- printf("%s: Issued Channel %c Bus Reset. "
- "%d SCBs aborted\n", ahc_name(ahc), channel, found);
- } else {
- /*
- * If we are a target, transition to bus free and report
- * the timeout.
- *
- * The target/initiator that is holding up the bus may not
- * be the same as the one that triggered this timeout
- * (different commands have different timeout lengths).
- * If the bus is idle and we are actiing as the initiator
- * for this request, queue a BDR message to the timed out
- * target. Otherwise, if the timed out transaction is
- * active:
- * Initiator transaction:
- * Stuff the message buffer with a BDR message and assert
- * ATN in the hopes that the target will let go of the bus
- * and go to the mesgout phase. If this fails, we'll
- * get another timeout 2 seconds later which will attempt
- * a bus reset.
- *
- * Target transaction:
- * Transition to BUS FREE and report the error.
- * It's good to be the target!
- */
- u_int active_scb_index;
-
- active_scb_index = ahc_inb(ahc, SCB_TAG);
-
- if (last_phase != P_BUSFREE
- && (active_scb_index < ahc->scb_data->numscbs)) {
- struct scb *active_scb;
+ if (ahc_match_scb(ahc, scb, target, channel, lun, tag, role)) {
/*
- * If the active SCB is not from our device,
- * assume that another device is hogging the bus
- * and wait for it's timeout to expire before
- * taking additional action.
+ * We found an scb that needs to be acted on.
*/
- active_scb = &ahc->scb_data->scbarray[active_scb_index];
- if (active_scb->hscb->tcl != scb->hscb->tcl) {
- u_int newtimeout;
-
- scsipi_printaddr(scb->xs->xs_periph);
- printf("Other SCB Timeout%s",
- (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0
- ? " again\n" : "\n");
- scb->flags |= SCB_OTHERTCL_TIMEOUT;
- newtimeout = MAX(active_scb->xs->timeout,
- scb->xs->timeout);
- callout_reset(&scb->xs->xs_callout,
- mstohz(newtimeout), ahc_timeout, scb);
- splx(s);
- return;
- }
-
- /* It's us */
- if ((scb->hscb->control & TARGET_SCB) != 0) {
-
- /*
- * Send back any queued up transactions
- * and properly record the error condition.
- */
- ahc_freeze_devq(ahc, scb->xs->xs_periph);
- ahcsetccbstatus(scb->xs, XS_TIMEOUT);
- ahc_freeze_ccb(scb);
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in qinfifo\n");
ahc_done(ahc, scb);
- /* Will clear us from the bus */
- restart_sequencer(ahc);
- splx(s);
- return;
- }
-
- ahc_set_recoveryscb(ahc, active_scb);
- ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET);
- ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
- scsipi_printaddr(active_scb->xs->xs_periph);
- printf("BDR message in message buffer\n");
- active_scb->flags |= SCB_DEVICE_RESET;
- callout_reset(&active_scb->xs->xs_callout,
- 2 * hz, ahc_timeout, active_scb);
- unpause_sequencer(ahc);
- } else {
- int disconnected;
-
- /* XXX Shouldn't panic. Just punt instead */
- if ((scb->hscb->control & TARGET_SCB) != 0)
- panic("Timed-out target SCB but bus idle");
-
- if (last_phase != P_BUSFREE
- && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) {
- /* XXX What happened to the SCB? */
- /* Hung target selection. Goto busfree */
- printf("%s: Hung target selection\n",
- ahc_name(ahc));
- restart_sequencer(ahc);
- splx(s);
- return;
+ /* FALLTHROUGH */
}
-
- if (ahc_search_qinfifo(ahc, target, channel, lun,
- scb->hscb->tag, ROLE_INITIATOR,
- /*status*/0, SEARCH_COUNT) > 0) {
- disconnected = FALSE;
- } else {
- disconnected = TRUE;
+ case SEARCH_REMOVE:
+ break;
+ case SEARCH_COUNT:
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ prev_scb = scb;
+ break;
}
+ } else {
+ ahc_qinfifo_requeue(ahc, prev_scb, scb);
+ prev_scb = scb;
+ }
+ qinpos++;
+ }
- if (disconnected) {
- u_int active_scb;
-
- ahc_set_recoveryscb(ahc, scb);
- /*
- * Simply set the MK_MESSAGE control bit.
- */
- scb->hscb->control |= MK_MESSAGE;
- scb->flags |= SCB_QUEUED_MSG
- | SCB_DEVICE_RESET;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
+ }
- /*
- * Mark the cached copy of this SCB in the
- * disconnected list too, so that a reconnect
- * at this point causes a BDR or abort.
- */
- active_scb = ahc_inb(ahc, SCBPTR);
- if (ahc_search_disc_list(ahc, target,
- channel, lun,
- scb->hscb->tag,
- /*stop_on_first*/TRUE,
- /*remove*/FALSE,
- /*save_state*/FALSE)) {
- u_int scb_control;
-
- scb_control = ahc_inb(ahc, SCB_CONTROL);
- scb_control |= MK_MESSAGE;
- ahc_outb(ahc, SCB_CONTROL, scb_control);
- }
- ahc_outb(ahc, SCBPTR, active_scb);
- ahc_index_busy_tcl(ahc, scb->hscb->tcl,
- /*unbusy*/TRUE);
+ if (action != SEARCH_COUNT
+ && (found != 0)
+ && (qinstart != ahc->qinfifonext)) {
+ /*
+ * The sequencer may be in the process of dmaing
+ * down the SCB at the beginning of the queue.
+ * This could be problematic if either the first,
+ * or the second SCB is removed from the queue
+ * (the first SCB includes a pointer to the "next"
+ * SCB to dma). If we have removed any entries, swap
+ * the first element in the queue with the next HSCB
+ * so the sequencer will notice that NEXT_QUEUED_SCB
+ * has changed during its dma attempt and will retry
+ * the DMA.
+ */
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinstart]);
- /*
- * Actually re-queue this SCB in case we can
- * select the device before it reconnects.
- * Clear out any entries in the QINFIFO first
- * so we are the next SCB for this target
- * to run.
- */
- ahc_search_qinfifo(ahc, SCB_TARGET(scb),
- channel, SCB_LUN(scb),
- SCB_LIST_NULL,
- ROLE_INITIATOR,
- SCB_REQUEUE,
- SEARCH_COMPLETE);
- scsipi_printaddr(scb->xs->xs_periph);
- printf("Queuing a BDR SCB\n");
- ahc->qinfifo[ahc->qinfifonext++] =
- scb->hscb->tag;
-
- bus_dmamap_sync(ahc->parent_dmat,
- ahc->shared_data_dmamap,
- QINFIFO_OFFSET * 256, 256,
- BUS_DMASYNC_PREWRITE);
-
- if ((ahc->features & AHC_QUEUE_REGS) != 0) {
- ahc_outb(ahc, HNSCB_QOFF,
- ahc->qinfifonext);
- } else {
- ahc_outb(ahc, KERNEL_QINPOS,
- ahc->qinfifonext);
- }
- callout_reset(&scb->xs->xs_callout, 2 * hz,
- ahc_timeout, scb);
- unpause_sequencer(ahc);
- } else {
- /* Go "immediatly" to the bus reset */
- /* This shouldn't happen */
- ahc_set_recoveryscb(ahc, scb);
- scsipi_printaddr(scb->xs->xs_periph);
- printf("SCB %x: Immediate reset. "
- "Flags = 0x%x\n", scb->hscb->tag,
- scb->flags);
- goto bus_reset;
- }
+ if (scb == NULL) {
+ printf("found = %d, qinstart = %d, qinfifionext = %d\n",
+ found, qinstart, ahc->qinfifonext);
+ panic("First/Second Qinfifo fixup\n");
}
+ /*
+ * ahc_swap_with_next_hscb forces our next pointer to
+ * point to the reserved SCB for future commands. Save
+ * and restore our original next pointer to maintain
+ * queue integrity.
+ */
+ next = scb->hscb->next;
+ ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+ ahc_swap_with_next_hscb(ahc, scb);
+ scb->hscb->next = next;
+ ahc->qinfifo[qinstart] = scb->hscb->tag;
+
+ /* Tell the card about the new head of the qinfifo. */
+ ahc_outb(ahc, NEXT_QUEUED_SCB, scb->hscb->tag);
+
+ /* Fixup the tail "next" pointer. */
+ qintail = ahc->qinfifonext - 1;
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qintail]);
+ scb->hscb->next = ahc->next_queued_scb->hscb->tag;
}
- splx(s);
-}
-
-static int
-ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
- int lun, u_int tag, role_t role, scb_flag status,
- ahc_search_action action)
-{
- struct scb *scbp;
- u_int8_t qinpos;
- u_int8_t qintail;
- int found;
-
- qinpos = ahc_inb(ahc, QINPOS);
- qintail = ahc->qinfifonext;
- found = 0;
/*
- * Start with an empty queue. Entries that are not chosen
- * for removal will be re-added to the queue as we go.
+ * Search waiting for selection list.
*/
- ahc->qinfifonext = qinpos;
+ curscbptr = ahc_inb(ahc, SCBPTR);
+ next = ahc_inb(ahc, WAITING_SCBH); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap,
- QINFIFO_OFFSET * 256, 256, BUS_DMASYNC_POSTREAD);
+ while (next != SCB_LIST_NULL) {
+ uint8_t scb_index;
- while (qinpos != qintail) {
- scbp = &ahc->scb_data->scbarray[ahc->qinfifo[qinpos]];
- if (ahc_match_scb(scbp, target, channel, lun, tag, role)) {
+ ahc_outb(ahc, SCBPTR, next);
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ if (scb_index >= ahc->scb_data->numscbs) {
+ printf("Waiting List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ }
+ scb = ahc_lookup_scb(ahc, scb_index);
+ if (scb == NULL) {
+ printf("scb_index = %d, next = %d\n",
+ scb_index, next);
+ panic("Waiting List traversal\n");
+ }
+ if (ahc_match_scb(ahc, scb, target, channel,
+ lun, SCB_LIST_NULL, role)) {
/*
- * We found an scb that needs to be removed.
+ * We found an scb that needs to be acted on.
*/
+ found++;
switch (action) {
case SEARCH_COMPLETE:
- if (!(scbp->xs->xs_status & XS_STS_DONE)) {
- scbp->flags |= status;
- scbp->xs->error = XS_NOERROR;
- }
- ahc_freeze_ccb(scbp);
- ahc_done(ahc, scbp);
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in Waiting List\n");
+ ahc_done(ahc, scb);
+ /* FALLTHROUGH */
+ }
+ case SEARCH_REMOVE:
+ next = ahc_rem_wscb(ahc, next, prev);
break;
case SEARCH_COUNT:
- ahc->qinfifo[ahc->qinfifonext++] =
- scbp->hscb->tag;
- break;
- case SEARCH_REMOVE:
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
break;
}
- found++;
} else {
- ahc->qinfifo[ahc->qinfifonext++] = scbp->hscb->tag;
+
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
}
- qinpos++;
}
+ ahc_outb(ahc, SCBPTR, curscbptr);
- bus_dmamap_sync(ahc->parent_dmat, ahc->shared_data_dmamap,
- QINFIFO_OFFSET * 256, 256, BUS_DMASYNC_PREWRITE);
-
- if ((ahc->features & AHC_QUEUE_REGS) != 0) {
- ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
- } else {
- ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext);
- }
+ found += ahc_search_untagged_queues(ahc, /*ahc_io_ctx_t*/NULL, target,
+ channel, lun, status, action);
+ if (action == SEARCH_COMPLETE)
+ ahc_release_untagged_queues(ahc);
return (found);
}
-/*
- * Abort all SCBs that match the given description (target/channel/lun/tag),
- * setting their status to the passed in status if the status has not already
- * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
- * is paused before it is called.
- */
-static int
-ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
- int lun, u_int tag, role_t role, int status)
+int
+ahc_search_untagged_queues(struct ahc_softc *ahc, struct scsipi_xfer *xs, /*ahc_io_ctx_t ctx,*/
+ int target, char channel, int lun, uint32_t status,
+ ahc_search_action action)
{
- struct scb *scbp;
- u_int active_scb;
- int i;
+ struct scb *scb;
+ int maxtarget;
int found;
+ int i;
- /* restore this when we're done */
- active_scb = ahc_inb(ahc, SCBPTR);
-
- found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
- role, SCB_REQUEUE, SEARCH_COMPLETE);
-
- /*
- * Search waiting for selection list.
- */
- {
- u_int8_t next, prev;
-
- next = ahc_inb(ahc, WAITING_SCBH); /* Start at head of list. */
- prev = SCB_LIST_NULL;
-
- while (next != SCB_LIST_NULL) {
- u_int8_t scb_index;
+ if (action == SEARCH_COMPLETE) {
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+ }
- ahc_outb(ahc, SCBPTR, next);
- scb_index = ahc_inb(ahc, SCB_TAG);
- if (scb_index >= ahc->scb_data->numscbs) {
- panic("Waiting List inconsistency. "
- "SCB index == %d, yet numscbs == %d.",
- scb_index, ahc->scb_data->numscbs);
- }
- scbp = &ahc->scb_data->scbarray[scb_index];
- if (ahc_match_scb(scbp, target, channel,
- lun, SCB_LIST_NULL, role)) {
+ found = 0;
+ i = 0;
+ if ((ahc->flags & AHC_SCB_BTT) == 0) {
- next = ahc_abort_wscb(ahc, next, prev);
- } else {
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
- prev = next;
- next = ahc_inb(ahc, SCB_NEXT);
- }
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
}
+ } else {
+ maxtarget = 0;
}
- /*
- * Go through the disconnected list and remove any entries we
- * have queued for completion, 0'ing their control byte too.
- * We save the active SCB and restore it ourselves, so there
- * is no reason for this search to restore it too.
- */
- ahc_search_disc_list(ahc, target, channel, lun, tag,
- /*stop_on_first*/FALSE, /*remove*/TRUE,
- /*save_state*/FALSE);
- /*
- * Go through the hardware SCB array looking for commands that
- * were active but not on any list.
- */
- for(i = 0; i < ahc->scb_data->maxhscbs; i++) {
- u_int scbid;
+ for (; i < maxtarget; i++) {
+ struct scb_tailq *untagged_q;
+ struct scb *next_scb;
- ahc_outb(ahc, SCBPTR, i);
- scbid = ahc_inb(ahc, SCB_TAG);
- scbp = &ahc->scb_data->scbarray[scbid];
- if (scbid < ahc->scb_data->numscbs
- && ahc_match_scb(scbp, target, channel, lun, tag, role))
- ahc_add_curscb_to_free_list(ahc);
- }
+ untagged_q = &(ahc->untagged_queues[i]);
+ next_scb = TAILQ_FIRST(untagged_q);
+ while (next_scb != NULL) {
- /*
- * Go through the pending CCB list and look for
- * commands for this target that are still active.
- * These are other tagged commands that were
- * disconnected when the reset occurred.
- */
- {
- struct scb *scb;
+ scb = next_scb;
+ next_scb = TAILQ_NEXT(scb, links.tqe);
+
+ /*
+ * The head of the list may be the currently
+ * active untagged command for a device.
+ * We're only searching for commands that
+ * have not been started. A transaction
+ * marked active but still in the qinfifo
+ * is removed by the qinfifo scanning code
+ * above.
+ */
+ if ((scb->flags & SCB_ACTIVE) != 0)
+ continue;
- scb = ahc->pending_ccbs.lh_first;
- while (scb != NULL) {
- scbp = scb;
- scb = scb->plinks.le_next;
- if (ahc_match_scb(scbp, target, channel,
- lun, tag, role)) {
- if (!(scbp->xs->xs_status & XS_STS_DONE))
- ahcsetccbstatus(scbp->xs, status);
- ahc_freeze_ccb(scbp);
- ahc_done(ahc, scbp);
- found++;
+ if (ahc_match_scb(ahc, scb, target, channel, lun,
+ SCB_LIST_NULL, ROLE_INITIATOR) == 0
+ /*|| (ctx != NULL && ctx != scb->io_ctx)*/)
+ continue;
+
+ /*
+ * We found an scb that needs to be acted on.
+ */
+ found++;
+ switch (action) {
+ case SEARCH_COMPLETE:
+ {
+ cam_status ostat;
+ cam_status cstat;
+
+ ostat = ahc_get_transaction_status(scb);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scb, status);
+ cstat = ahc_get_transaction_status(scb);
+ if (cstat != CAM_REQ_CMP)
+ ahc_freeze_scb(scb);
+ if ((scb->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB in untaggedQ\n");
+ ahc_done(ahc, scb);
+ break;
+ }
+ case SEARCH_REMOVE:
+ scb->flags &= ~SCB_UNTAGGEDQ;
+ TAILQ_REMOVE(untagged_q, scb, links.tqe);
+ break;
+ case SEARCH_COUNT:
+ break;
}
}
}
- ahc_outb(ahc, SCBPTR, active_scb);
- return found;
+
+ if (action == SEARCH_COMPLETE)
+ ahc_release_untagged_queues(ahc);
+ return (found);
}
-static int
+int
ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
int lun, u_int tag, int stop_on_first, int remove,
int save_state)
@@ -5063,12 +5604,20 @@ ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
ahc_outb(ahc, SCBPTR, next);
scb_index = ahc_inb(ahc, SCB_TAG);
if (scb_index >= ahc->scb_data->numscbs) {
- panic("Disconnected List inconsistency. "
- "SCB index == %d, yet numscbs == %d.",
- scb_index, ahc->scb_data->numscbs);
+ printf("Disconnected List inconsistency. "
+ "SCB index == %d, yet numscbs == %d.",
+ scb_index, ahc->scb_data->numscbs);
+ ahc_dump_card_state(ahc);
+ panic("for safety");
+ }
+
+ if (next == prev) {
+ panic("Disconnected List Loop. "
+ "cur SCBPTR == %x, prev SCBPTR == %x.",
+ next, prev);
}
- scbp = &ahc->scb_data->scbarray[scb_index];
- if (ahc_match_scb(scbp, target, channel, lun,
+ scbp = ahc_lookup_scb(ahc, scb_index);
+ if (ahc_match_scb(ahc, scbp, target, channel, lun,
tag, ROLE_INITIATOR)) {
count++;
if (remove) {
@@ -5090,6 +5639,10 @@ ahc_search_disc_list(struct ahc_softc *ahc, int target, char channel,
return (count);
}
+/*
+ * Remove an SCB from the on chip list of disconnected transactions.
+ * This is empty/unused if we are not performing SCB paging.
+ */
static u_int
ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
{
@@ -5111,14 +5664,24 @@ ahc_rem_scb_from_disc_list(struct ahc_softc *ahc, u_int prev, u_int scbptr)
return (next);
}
+/*
+ * Add the SCB as selected by SCBPTR onto the on chip list of
+ * free hardware SCBs. This list is empty/unused if we are not
+ * performing SCB paging.
+ */
static void
ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
{
- /* Invalidate the tag so that ahc_find_scb doesn't think it's active */
+ /*
+ * Invalidate the tag so that our abort
+ * routines don't think it's active.
+ */
ahc_outb(ahc, SCB_TAG, SCB_LIST_NULL);
- ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
- ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+ if ((ahc->flags & AHC_PAGESCBS) != 0) {
+ ahc_outb(ahc, SCB_NEXT, ahc_inb(ahc, FREE_SCBH));
+ ahc_outb(ahc, FREE_SCBH, ahc_inb(ahc, SCBPTR));
+ }
}
/*
@@ -5126,8 +5689,8 @@ ahc_add_curscb_to_free_list(struct ahc_softc *ahc)
* scb that follows the one that we remove.
*/
static u_int
-ahc_abort_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
-{
+ahc_rem_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
+{
u_int curscb, next;
/*
@@ -5146,7 +5709,7 @@ ahc_abort_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
/* update the waiting list */
if (prev == SCB_LIST_NULL) {
/* First in the list */
- ahc_outb(ahc, WAITING_SCBH, next);
+ ahc_outb(ahc, WAITING_SCBH, next);
/*
* Ensure we aren't attempting to perform
@@ -5155,7 +5718,7 @@ ahc_abort_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
} else {
/*
- * Select the scb that pointed to us
+ * Select the scb that pointed to us
* and update its next pointer.
*/
ahc_outb(ahc, SCBPTR, prev);
@@ -5169,26 +5732,155 @@ ahc_abort_wscb(struct ahc_softc *ahc, u_int scbpos, u_int prev)
return next;
}
-static void
-ahc_clear_intstat(struct ahc_softc *ahc)
+/******************************** Error Handling ******************************/
+/*
+ * Abort all SCBs that match the given description (target/channel/lun/tag),
+ * setting their status to the passed in status if the status has not already
+ * been modified from CAM_REQ_INPROG. This routine assumes that the sequencer
+ * is paused before it is called.
+ */
+int
+ahc_abort_scbs(struct ahc_softc *ahc, int target, char channel,
+ int lun, u_int tag, role_t role, uint32_t status)
{
- /* Clear any interrupt conditions this may have caused */
- ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
- ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
- |CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
- CLRREQINIT);
- ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ struct scb *scbp;
+ struct scb *scbp_next;
+ u_int active_scb;
+ int i, j;
+ int maxtarget;
+ int minlun;
+ int maxlun;
+
+ int found;
+
+ /*
+ * Don't attempt to run any queued untagged transactions
+ * until we are done with the abort process.
+ */
+ ahc_freeze_untagged_queues(ahc);
+
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+
+ found = ahc_search_qinfifo(ahc, target, channel, lun, SCB_LIST_NULL,
+ role, CAM_REQUEUE_REQ, SEARCH_COMPLETE);
+
+ /*
+ * Clean out the busy target table for any untagged commands.
+ */
+ i = 0;
+ maxtarget = 16;
+ if (target != CAM_TARGET_WILDCARD) {
+ i = target;
+ if (channel == 'B')
+ i += 8;
+ maxtarget = i + 1;
+ }
+
+ if (lun == CAM_LUN_WILDCARD) {
+
+ /*
+ * Unless we are using an SCB based
+ * busy targets table, there is only
+ * one table entry for all luns of
+ * a target.
+ */
+ minlun = 0;
+ maxlun = 1;
+ if ((ahc->flags & AHC_SCB_BTT) != 0)
+ maxlun = AHC_NUM_LUNS;
+ } else {
+ minlun = lun;
+ maxlun = lun + 1;
+ }
+
+ if (role != ROLE_TARGET) {
+ for (;i < maxtarget; i++) {
+ for (j = minlun;j < maxlun; j++) {
+ u_int scbid;
+ u_int tcl;
+
+ tcl = BUILD_TCL(i << 4, j);
+ scbid = ahc_index_busy_tcl(ahc, tcl);
+ scbp = ahc_lookup_scb(ahc, scbid);
+ if (scbp == NULL
+ || ahc_match_scb(ahc, scbp, target, channel,
+ lun, tag, role) == 0)
+ continue;
+ ahc_unbusy_tcl(ahc, BUILD_TCL(i << 4, j));
+ }
+ }
+
+ /*
+ * Go through the disconnected list and remove any entries we
+ * have queued for completion, 0'ing their control byte too.
+ * We save the active SCB and restore it ourselves, so there
+ * is no reason for this search to restore it too.
+ */
+ ahc_search_disc_list(ahc, target, channel, lun, tag,
+ /*stop_on_first*/FALSE, /*remove*/TRUE,
+ /*save_state*/FALSE);
+ }
+
+ /*
+ * Go through the hardware SCB array looking for commands that
+ * were active but not on any list. In some cases, these remnants
+ * might not still have mappings in the scbindex array (e.g. unexpected
+ * bus free with the same scb queued for an abort). Don't hold this
+ * against them.
+ */
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
+ u_int scbid;
+
+ ahc_outb(ahc, SCBPTR, i);
+ scbid = ahc_inb(ahc, SCB_TAG);
+ scbp = ahc_lookup_scb(ahc, scbid);
+ if ((scbp == NULL && scbid != SCB_LIST_NULL)
+ || (scbp != NULL
+ && ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)))
+ ahc_add_curscb_to_free_list(ahc);
+ }
+
+ /*
+ * Go through the pending CCB list and look for
+ * commands for this target that are still active.
+ * These are other tagged commands that were
+ * disconnected when the reset occurred.
+ */
+ scbp_next = LIST_FIRST(&ahc->pending_scbs);
+ while (scbp_next != NULL) {
+ scbp = scbp_next;
+ scbp_next = LIST_NEXT(scbp, pending_links);
+ if (ahc_match_scb(ahc, scbp, target, channel, lun, tag, role)) {
+ cam_status ostat;
+
+ ostat = ahc_get_transaction_status(scbp);
+ if (ostat == CAM_REQ_INPROG)
+ ahc_set_transaction_status(scbp, status);
+ if (ahc_get_transaction_status(scbp) != CAM_REQ_CMP)
+ ahc_freeze_scb(scbp);
+ if ((scbp->flags & SCB_ACTIVE) == 0)
+ printf("Inactive SCB on pending list\n");
+ ahc_done(ahc, scbp);
+ found++;
+ }
+ }
+ ahc_outb(ahc, SCBPTR, active_scb);
+ ahc_platform_abort_scbs(ahc, target, channel, lun, tag, role, status);
+ ahc_release_untagged_queues(ahc);
+ return found;
}
static void
ahc_reset_current_bus(struct ahc_softc *ahc)
{
- u_int8_t scsiseq;
+ uint8_t scsiseq;
ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
scsiseq = ahc_inb(ahc, SCSISEQ);
ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
- DELAY(AHC_BUSRESET_DELAY);
+ ahc_flush_device_writes(ahc);
+ ahc_delay(AHC_BUSRESET_DELAY);
/* Turn off the bus reset */
ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
@@ -5198,19 +5890,29 @@ ahc_reset_current_bus(struct ahc_softc *ahc)
ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
}
-static int
+int
ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
{
+ struct ahc_devinfo devinfo;
u_int initiator, target, max_scsiid;
u_int sblkctl;
- u_int our_id;
+ u_int scsiseq;
+ u_int simode1;
int found;
int restart_needed;
char cur_channel;
ahc->pending_device = NULL;
- pause_sequencer(ahc);
+ ahc_compile_devinfo(&devinfo,
+ CAM_TARGET_WILDCARD,
+ CAM_TARGET_WILDCARD,
+ CAM_LUN_WILDCARD,
+ channel, ROLE_UNKNOWN);
+ ahc_pause(ahc);
+
+ /* Make sure the sequencer is in a safe location. */
+ ahc_clear_critical_section(ahc);
/*
* Run our command complete fifos to ensure that we perform
@@ -5218,6 +5920,21 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
* before the reset occurred.
*/
ahc_run_qoutfifo(ahc);
+#if AHC_TARGET_MODE
+ /*
+ * XXX - In Twin mode, the tqinfifo may have commands
+ * for an unaffected channel in it. However, if
+ * we have run out of ATIO resources to drain that
+ * queue, we may not get them all out here. Further,
+ * the blocked transactions for the reset channel
+ * should just be killed off, irrespecitve of whether
+ * we are blocked on ATIO resources. Write a routine
+ * to compact the tqinfifo appropriately.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0) {
+ ahc_run_tqinfifo(ahc, /*paused*/TRUE);
+ }
+#endif
/*
* Reset the bus if we are initiating this reset
@@ -5227,40 +5944,47 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
if ((ahc->features & AHC_TWIN) != 0
&& ((sblkctl & SELBUSB) != 0))
cur_channel = 'B';
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
if (cur_channel != channel) {
/* Case 1: Command for another bus is active
* Stealthily reset the other bus without
* upsetting the current bus.
*/
ahc_outb(ahc, SBLKCTL, sblkctl ^ SELBUSB);
- ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
- ahc_outb(ahc, SCSISEQ,
- ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#if AHC_TARGET_MODE
+ /*
+ * Bus resets clear ENSELI, so we cannot
+ * defer re-enabling bus reset interrupts
+ * if we are in target mode.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ simode1 |= ENSCSIRST;
+#endif
+ ahc_outb(ahc, SIMODE1, simode1);
if (initiate_reset)
ahc_reset_current_bus(ahc);
ahc_clear_intstat(ahc);
+ ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
ahc_outb(ahc, SBLKCTL, sblkctl);
restart_needed = FALSE;
} else {
/* Case 2: A command from this bus is active or we're idle */
- ahc_clear_msg_state(ahc);
- ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
- ahc_outb(ahc, SCSISEQ,
- ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ simode1 = ahc_inb(ahc, SIMODE1) & ~(ENBUSFREE|ENSCSIRST);
+#if AHC_TARGET_MODE
+ /*
+ * Bus resets clear ENSELI, so we cannot
+ * defer re-enabling bus reset interrupts
+ * if we are in target mode.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) != 0)
+ simode1 |= ENSCSIRST;
+#endif
+ ahc_outb(ahc, SIMODE1, simode1);
if (initiate_reset)
ahc_reset_current_bus(ahc);
ahc_clear_intstat(ahc);
-
- /*
- * Since we are going to restart the sequencer, avoid
- * a race in the sequencer that could cause corruption
- * of our Q pointers by starting over from index 0.
- */
- ahc->qoutfifonext = 0;
- if ((ahc->features & AHC_QUEUE_REGS) != 0)
- ahc_outb(ahc, SDSCB_QOFF, 0);
- else
- ahc_outb(ahc, QOUTPOS, 0);
+ ahc_outb(ahc, SCSISEQ, scsiseq & (ENSELI|ENRSELI|ENAUTOATNP));
restart_needed = TRUE;
}
@@ -5268,17 +5992,37 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
* Clean up all the state information for the
* pending transactions on this bus.
*/
- found = ahc_abort_scbs(ahc, AHC_TARGET_WILDCARD, channel,
- AHC_LUN_WILDCARD, SCB_LIST_NULL,
- ROLE_UNKNOWN, XS_RESET);
- if (channel == 'B') {
- our_id = ahc->our_id_b;
- } else {
- our_id = ahc->our_id;
- }
+ found = ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, channel,
+ CAM_LUN_WILDCARD, SCB_LIST_NULL,
+ ROLE_UNKNOWN, CAM_SCSI_BUS_RESET);
max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+#ifdef AHC_TARGET_MODE
+ /*
+ * Send an immediate notify ccb to all target more peripheral
+ * drivers affected by this action.
+ */
+ for (target = 0; target <= max_scsiid; target++) {
+ struct ahc_tmode_tstate* tstate;
+ u_int lun;
+
+ tstate = ahc->enabled_targets[target];
+ if (tstate == NULL)
+ continue;
+ for (lun = 0; lun < AHC_NUM_LUNS; lun++) {
+ struct ahc_tmode_lstate* lstate;
+
+ lstate = tstate->enabled_luns[lun];
+ if (lstate == NULL)
+ continue;
+
+ ahc_queue_lstate_event(ahc, lstate, CAM_TARGET_WILDCARD,
+ EVENT_TYPE_BUS_RESET, /*arg*/0);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+#endif
/*
* Revert to async/narrow transfers until we renegotiate.
*/
@@ -5290,331 +6034,1402 @@ ahc_reset_channel(struct ahc_softc *ahc, char channel, int initiate_reset)
struct ahc_devinfo devinfo;
ahc_compile_devinfo(&devinfo, target, initiator,
- AHC_LUN_WILDCARD,
+ CAM_LUN_WILDCARD,
channel, ROLE_UNKNOWN);
- ahc_set_width(ahc, &devinfo,
- MSG_EXT_WDTR_BUS_8_BIT,
- AHC_TRANS_CUR, /*paused*/TRUE, FALSE);
- ahc_set_syncrate(ahc, &devinfo,
- /*syncrate*/NULL, /*period*/0,
- /*offset*/0, AHC_TRANS_CUR,
- /*paused*/TRUE, FALSE);
- ahc_update_xfer_mode(ahc, &devinfo);
+ ahc_set_width(ahc, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR, /*paused*/TRUE);
+ ahc_set_syncrate(ahc, &devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0,
+ /*ppr_options*/0, AHC_TRANS_CUR,
+ /*paused*/TRUE);
}
}
if (restart_needed)
- restart_sequencer(ahc);
+ ahc_restart(ahc);
else
- unpause_sequencer(ahc);
+ ahc_unpause(ahc);
return found;
}
-static int
-ahc_match_scb(struct scb *scb, int target, char channel,
- int lun, u_int tag, role_t role)
+
+/***************************** Residual Processing ****************************/
+/*
+ * Calculate the residual for a just completed SCB.
+ */
+void
+ahc_calc_residual(struct ahc_softc *ahc, struct scb *scb)
{
- int targ = SCB_TARGET(scb);
- char chan = SCB_CHANNEL(scb);
- int slun = SCB_LUN(scb);
- int match;
+ struct hardware_scb *hscb;
+ struct status_pkt *spkt;
+ uint32_t sgptr;
+ uint32_t resid_sgptr;
+ uint32_t resid;
- match = ((chan == channel) || (channel == ALL_CHANNELS));
- if (match != 0)
- match = ((targ == target) || (target == AHC_TARGET_WILDCARD));
- if (match != 0)
- match = ((lun == slun) || (lun == AHC_LUN_WILDCARD));
+ /*
+ * 5 cases.
+ * 1) No residual.
+ * SG_RESID_VALID clear in sgptr.
+ * 2) Transferless command
+ * 3) Never performed any transfers.
+ * sgptr has SG_FULL_RESID set.
+ * 4) No residual but target did not
+ * save data pointers after the
+ * last transfer, so sgptr was
+ * never updated.
+ * 5) We have a partial residual.
+ * Use residual_sgptr to determine
+ * where we are.
+ */
- return match;
+ hscb = scb->hscb;
+ sgptr = ahc_le32toh(hscb->sgptr);
+ if ((sgptr & SG_RESID_VALID) == 0)
+ /* Case 1 */
+ return;
+ sgptr &= ~SG_RESID_VALID;
+
+ if ((sgptr & SG_LIST_NULL) != 0)
+ /* Case 2 */
+ return;
+
+ spkt = &hscb->shared_data.status;
+ resid_sgptr = ahc_le32toh(spkt->residual_sg_ptr);
+ if ((sgptr & SG_FULL_RESID) != 0) {
+ /* Case 3 */
+ resid = ahc_get_transfer_length(scb);
+ } else if ((resid_sgptr & SG_LIST_NULL) != 0) {
+ /* Case 4 */
+ return;
+ } else if ((resid_sgptr & ~SG_PTR_MASK) != 0) {
+ panic("Bogus resid sgptr value 0x%x\n", resid_sgptr);
+ } else {
+ struct ahc_dma_seg *sg;
+
+ /*
+ * Remainder of the SG where the transfer
+ * stopped.
+ */
+ resid = ahc_le32toh(spkt->residual_datacnt) & AHC_SG_LEN_MASK;
+ sg = ahc_sg_bus_to_virt(scb, resid_sgptr & SG_PTR_MASK);
+
+ /* The residual sg_ptr always points to the next sg */
+ sg--;
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ while ((ahc_le32toh(sg->len) & AHC_DMA_LAST_SEG) == 0) {
+ sg++;
+ resid += ahc_le32toh(sg->len) & AHC_SG_LEN_MASK;
+ }
+ }
+ if ((scb->flags & SCB_SENSE) == 0)
+ ahc_set_residual(scb, resid);
+ else
+ ahc_set_sense_residual(scb, resid);
+
+#ifdef AHC_DEBUG
+ if ((ahc_debug & AHC_SHOW_MISC) != 0) {
+ ahc_print_path(ahc, scb);
+ printf("Handled %sResidual of %d bytes\n",
+ (scb->flags & SCB_SENSE) ? "Sense " : "", resid);
+ }
+#endif
}
+/******************************* Target Mode **********************************/
+#ifdef AHC_TARGET_MODE
+/*
+ * Add a target mode event to this lun's queue
+ */
static void
-ahc_construct_sdtr(struct ahc_softc *ahc, u_int period, u_int offset)
+ahc_queue_lstate_event(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate,
+ u_int initiator_id, u_int event_type, u_int event_arg)
{
- ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
- ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR_LEN;
- ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_SDTR;
- ahc->msgout_buf[ahc->msgout_index++] = period;
- ahc->msgout_buf[ahc->msgout_index++] = offset;
- ahc->msgout_len += 5;
+ struct ahc_tmode_event *event;
+ int pending;
+
+ xpt_freeze_devq(lstate->path, /*count*/1);
+ if (lstate->event_w_idx >= lstate->event_r_idx)
+ pending = lstate->event_w_idx - lstate->event_r_idx;
+ else
+ pending = AHC_TMODE_EVENT_BUFFER_SIZE + 1
+ - (lstate->event_r_idx - lstate->event_w_idx);
+
+ if (event_type == EVENT_TYPE_BUS_RESET
+ || event_type == MSG_BUS_DEV_RESET) {
+ /*
+ * Any earlier events are irrelevant, so reset our buffer.
+ * This has the effect of allowing us to deal with reset
+ * floods (an external device holding down the reset line)
+ * without losing the event that is really interesting.
+ */
+ lstate->event_r_idx = 0;
+ lstate->event_w_idx = 0;
+ xpt_release_devq(lstate->path, pending, /*runqueue*/FALSE);
+ }
+
+ if (pending == AHC_TMODE_EVENT_BUFFER_SIZE) {
+ xpt_print_path(lstate->path);
+ printf("immediate event %x:%x lost\n",
+ lstate->event_buffer[lstate->event_r_idx].event_type,
+ lstate->event_buffer[lstate->event_r_idx].event_arg);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ xpt_release_devq(lstate->path, /*count*/1, /*runqueue*/FALSE);
+ }
+
+ event = &lstate->event_buffer[lstate->event_w_idx];
+ event->initiator_id = initiator_id;
+ event->event_type = event_type;
+ event->event_arg = event_arg;
+ lstate->event_w_idx++;
+ if (lstate->event_w_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_w_idx = 0;
}
-static void
-ahc_construct_wdtr(struct ahc_softc *ahc, u_int bus_width)
+/*
+ * Send any target mode events queued up waiting
+ * for immediate notify resources.
+ */
+void
+ahc_send_lstate_events(struct ahc_softc *ahc, struct ahc_tmode_lstate *lstate)
{
- ahc->msgout_buf[ahc->msgout_index++] = MSG_EXTENDED;
- ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR_LEN;
- ahc->msgout_buf[ahc->msgout_index++] = MSG_EXT_WDTR;
- ahc->msgout_buf[ahc->msgout_index++] = bus_width;
- ahc->msgout_len += 4;
+ struct ccb_hdr *ccbh;
+ struct ccb_immed_notify *inot;
+
+ while (lstate->event_r_idx != lstate->event_w_idx
+ && (ccbh = SLIST_FIRST(&lstate->immed_notifies)) != NULL) {
+ struct ahc_tmode_event *event;
+
+ event = &lstate->event_buffer[lstate->event_r_idx];
+ SLIST_REMOVE_HEAD(&lstate->immed_notifies, sim_links.sle);
+ inot = (struct ccb_immed_notify *)ccbh;
+ switch (event->event_type) {
+ case EVENT_TYPE_BUS_RESET:
+ ccbh->status = CAM_SCSI_BUS_RESET|CAM_DEV_QFRZN;
+ break;
+ default:
+ ccbh->status = CAM_MESSAGE_RECV|CAM_DEV_QFRZN;
+ inot->message_args[0] = event->event_type;
+ inot->message_args[1] = event->event_arg;
+ break;
+ }
+ inot->initiator_id = event->initiator_id;
+ inot->sense_len = 0;
+ xpt_done((union ccb *)inot);
+ lstate->event_r_idx++;
+ if (lstate->event_r_idx == AHC_TMODE_EVENT_BUFFER_SIZE)
+ lstate->event_r_idx = 0;
+ }
}
+#endif
-static void
-ahc_calc_residual(struct scb *scb)
+/******************** Sequencer Program Patching/Download *********************/
+
+#ifdef AHC_DUMP_SEQ
+void
+ahc_dumpseq(struct ahc_softc* ahc)
{
- struct hardware_scb *hscb;
+ int i;
+ int max_prog;
- hscb = scb->hscb;
+ if ((ahc->chip & AHC_BUS_MASK) < AHC_PCI)
+ max_prog = 448;
+ else if ((ahc->features & AHC_ULTRA2) != 0)
+ max_prog = 768;
+ else
+ max_prog = 512;
+
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+ for (i = 0; i < max_prog; i++) {
+ uint8_t ins_bytes[4];
+
+ ahc_insb(ahc, SEQRAM, ins_bytes, 4);
+ printf("0x%08x\n", ins_bytes[0] << 24
+ | ins_bytes[1] << 16
+ | ins_bytes[2] << 8
+ | ins_bytes[3]);
+ }
+}
+#endif
+
+static void
+ahc_loadseq(struct ahc_softc *ahc)
+{
+ struct cs cs_table[num_critical_sections];
+ u_int begin_set[num_critical_sections];
+ u_int end_set[num_critical_sections];
+ struct patch *cur_patch;
+ u_int cs_count;
+ u_int cur_cs;
+ u_int i;
+ int downloaded;
+ u_int skip_addr;
+ u_int sg_prefetch_cnt;
+ uint8_t download_consts[7];
/*
- * If the disconnected flag is still set, this is bogus
- * residual information left over from a sequencer
- * pagin/pageout, so ignore this case.
+ * Start out with 0 critical sections
+ * that apply to this firmware load.
*/
- if ((scb->hscb->control & DISCONNECTED) == 0) {
- u_int32_t resid;
- int resid_sgs;
- int sg;
+ cs_count = 0;
+ cur_cs = 0;
+ memset(begin_set, 0, sizeof(begin_set));
+ memset(end_set, 0, sizeof(end_set));
- /*
- * Remainder of the SG where the transfer
- * stopped.
- */
- resid = (hscb->residual_data_count[2] << 16)
- | (hscb->residual_data_count[1] <<8)
- | (hscb->residual_data_count[0]);
+ /* Setup downloadable constant table */
+ download_consts[QOUTFIFO_OFFSET] = 0;
+ if (ahc->targetcmds != NULL)
+ download_consts[QOUTFIFO_OFFSET] += 32;
+ download_consts[QINFIFO_OFFSET] = download_consts[QOUTFIFO_OFFSET] + 1;
+ download_consts[CACHESIZE_MASK] = ahc->pci_cachesize - 1;
+ download_consts[INVERTED_CACHESIZE_MASK] = ~(ahc->pci_cachesize - 1);
+ sg_prefetch_cnt = ahc->pci_cachesize;
+ if (sg_prefetch_cnt < (2 * sizeof(struct ahc_dma_seg)))
+ sg_prefetch_cnt = 2 * sizeof(struct ahc_dma_seg);
+ download_consts[SG_PREFETCH_CNT] = sg_prefetch_cnt;
+ download_consts[SG_PREFETCH_ALIGN_MASK] = ~(sg_prefetch_cnt - 1);
+ download_consts[SG_PREFETCH_ADDR_MASK] = (sg_prefetch_cnt - 1);
+ cur_patch = patches;
+ downloaded = 0;
+ skip_addr = 0;
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE|LOADRAM);
+ ahc_outb(ahc, SEQADDR0, 0);
+ ahc_outb(ahc, SEQADDR1, 0);
+
+ for (i = 0; i < sizeof(seqprog)/4; i++) {
+ if (ahc_check_patch(ahc, &cur_patch, i, &skip_addr) == 0) {
+ /*
+ * Don't download this instruction as it
+ * is in a patch that was removed.
+ */
+ continue;
+ }
/*
- * Add up the contents of all residual
- * SG segments that are after the SG where
- * the transfer stopped.
+ * Move through the CS table until we find a CS
+ * that might apply to this instruction.
*/
- resid_sgs = scb->hscb->residual_SG_count - 1/*current*/;
- sg = scb->sg_count - resid_sgs;
- while (resid_sgs > 0) {
+ for (; cur_cs < num_critical_sections; cur_cs++) {
+ if (critical_sections[cur_cs].end <= i) {
+ if (begin_set[cs_count] == TRUE
+ && end_set[cs_count] == FALSE) {
+ cs_table[cs_count].end = downloaded;
+ end_set[cs_count] = TRUE;
+ cs_count++;
+ }
+ continue;
+ }
+ if (critical_sections[cur_cs].begin <= i
+ && begin_set[cs_count] == FALSE) {
+ cs_table[cs_count].begin = downloaded;
+ begin_set[cs_count] = TRUE;
+ }
+ break;
+ }
+ ahc_download_instr(ahc, i, download_consts);
+ downloaded++;
+ }
- resid += le32toh(scb->sg_list[sg].len);
- sg++;
- resid_sgs--;
+ ahc->num_critical_sections = cs_count;
+ if (cs_count != 0) {
+
+ cs_count *= sizeof(struct cs);
+ ahc->critical_sections = malloc(cs_count, M_DEVBUF, M_NOWAIT);
+ if (ahc->critical_sections == NULL)
+ panic("ahc_loadseq: Could not malloc");
+ memcpy(ahc->critical_sections, cs_table, cs_count);
+ }
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+ ahc_restart(ahc);
+
+ if (bootverbose) {
+ printf(" %d instructions downloaded\n", downloaded);
+ printf("%s: Features 0x%x, Bugs 0x%x, Flags 0x%x\n",
+ ahc_name(ahc), ahc->features, ahc->bugs, ahc->flags);
+ }
+}
+
+static int
+ahc_check_patch(struct ahc_softc *ahc, struct patch **start_patch,
+ u_int start_instr, u_int *skip_addr)
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ u_int num_patches;
+
+ num_patches = sizeof(patches)/sizeof(struct patch);
+ last_patch = &patches[num_patches];
+ cur_patch = *start_patch;
+
+ while (cur_patch < last_patch && start_instr == cur_patch->begin) {
+
+ if (cur_patch->patch_func(ahc) == 0) {
+
+ /* Start rejecting code */
+ *skip_addr = start_instr + cur_patch->skip_instr;
+ cur_patch += cur_patch->skip_patch;
+ } else {
+ /* Accepted this patch. Advance to the next
+ * one and wait for our intruction pointer to
+ * hit this point.
+ */
+ cur_patch++;
}
- scb->xs->resid = resid;
}
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
+}
+
+static void
+ahc_download_instr(struct ahc_softc *ahc, u_int instrptr, uint8_t *dconsts)
+{
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ u_int opcode;
+
/*
- * Clean out the residual information in this SCB for its
- * next consumer.
+ * The firmware is always compiled into a little endian format.
*/
- hscb->residual_SG_count = 0;
+ instr.integer = ahc_le32toh(*(uint32_t*)&seqprog[instrptr * 4]);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWMISC) {
- scsipi_printaddr(scb->xs->xs_periph);
- printf("Handled Residual of %ld bytes\n" ,(long)scb->xs->resid);
+ fmt1_ins = &instr.format1;
+ fmt3_ins = NULL;
+
+ /* Pull the opcode */
+ opcode = instr.format1.opcode;
+ switch (opcode) {
+ case AIC_OP_JMP:
+ case AIC_OP_JC:
+ case AIC_OP_JNC:
+ case AIC_OP_CALL:
+ case AIC_OP_JNE:
+ case AIC_OP_JNZ:
+ case AIC_OP_JE:
+ case AIC_OP_JZ:
+ {
+ struct patch *cur_patch;
+ int address_offset;
+ u_int address;
+ u_int skip_addr;
+ u_int i;
+
+ fmt3_ins = &instr.format3;
+ address_offset = 0;
+ address = fmt3_ins->address;
+ cur_patch = patches;
+ skip_addr = 0;
+ for (i = 0; i < address;) {
+ ahc_check_patch(ahc, &cur_patch, i, &skip_addr);
+
+ if (skip_addr > i) {
+ int end_addr;
+
+ end_addr = MIN(address, skip_addr);
+ address_offset += end_addr - i;
+ i = skip_addr;
+ } else {
+ i++;
+ }
+ }
+ address -= address_offset;
+ fmt3_ins->address = address;
+ /* FALLTHROUGH */
+ }
+ case AIC_OP_OR:
+ case AIC_OP_AND:
+ case AIC_OP_XOR:
+ case AIC_OP_ADD:
+ case AIC_OP_ADC:
+ case AIC_OP_BMOV:
+ if (fmt1_ins->parity != 0) {
+ fmt1_ins->immediate = dconsts[fmt1_ins->immediate];
+ }
+ fmt1_ins->parity = 0;
+ if ((ahc->features & AHC_CMD_CHAN) == 0
+ && opcode == AIC_OP_BMOV) {
+ /*
+ * Block move was added at the same time
+ * as the command channel. Verify that
+ * this is only a move of a single element
+ * and convert the BMOV to a MOV
+ * (AND with an immediate of FF).
+ */
+ if (fmt1_ins->immediate != 1)
+ panic("%s: BMOV not supported\n",
+ ahc_name(ahc));
+ fmt1_ins->opcode = AIC_OP_AND;
+ fmt1_ins->immediate = 0xff;
+ }
+ /* FALLTHROUGH */
+ case AIC_OP_ROL:
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ int i, count;
+
+ /* Calculate odd parity for the instruction */
+ for (i = 0, count = 0; i < 31; i++) {
+ uint32_t mask;
+
+ mask = 0x01 << i;
+ if ((instr.integer & mask) != 0)
+ count++;
+ }
+ if ((count & 0x01) == 0)
+ instr.format1.parity = 1;
+ } else {
+ /* Compress the instruction for older sequencers */
+ if (fmt3_ins != NULL) {
+ instr.integer =
+ fmt3_ins->immediate
+ | (fmt3_ins->source << 8)
+ | (fmt3_ins->address << 16)
+ | (fmt3_ins->opcode << 25);
+ } else {
+ instr.integer =
+ fmt1_ins->immediate
+ | (fmt1_ins->source << 8)
+ | (fmt1_ins->destination << 16)
+ | (fmt1_ins->ret << 24)
+ | (fmt1_ins->opcode << 25);
+ }
+ }
+ /* The sequencer is a little endian cpu */
+ instr.integer = ahc_htole32(instr.integer);
+ ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+ break;
+ default:
+ panic("Unknown opcode encountered in seq program");
+ break;
}
-#endif
}
-static void
-ahc_update_pending_syncrates(struct ahc_softc *ahc)
+int
+ahc_print_register(ahc_reg_parse_entry_t *table, u_int num_entries,
+ const char *name, u_int address, u_int value,
+ u_int *cur_column, u_int wrap_point)
+{
+ int printed;
+ u_int printed_mask;
+ char line[2028];
+
+ memset(line, '\0', 2048);
+
+ if (cur_column != NULL && *cur_column >= wrap_point) {
+ printf("\n");
+ *cur_column = 0;
+ }
+ sprintf(line+strlen(line), "%s[0x%x]", name, value);
+ if (table == NULL) {
+ sprintf(line+strlen(line), " ");
+ printed = strlen(line);
+ printf(line);
+ *cur_column += printed;
+ return (printed);
+ }
+ printed_mask = 0;
+ while (printed_mask != 0xFF) {
+ int entry;
+
+ for (entry = 0; entry < num_entries; entry++) {
+ if (((value & table[entry].mask)
+ != table[entry].value)
+ || ((printed_mask & table[entry].mask)
+ == table[entry].mask))
+ continue;
+
+ sprintf(line+strlen(line), "%s%s",
+ printed_mask == 0 ? ":(" : "|",
+ table[entry].name);
+ printed_mask |= table[entry].mask;
+
+ break;
+ }
+ if (entry >= num_entries)
+ break;
+ }
+ if (printed_mask != 0)
+ sprintf(line+strlen(line), ") ");
+ else
+ sprintf(line+strlen(line), " ");
+ printed = strlen(line);
+ if (cur_column != NULL)
+ *cur_column += printed;
+ printf(line);
+
+ return (printed);
+}
+
+void
+ahc_dump_card_state(struct ahc_softc *ahc)
{
struct scb *scb;
- int pending_ccb_count;
+ struct scb_tailq *untagged_q;
+ u_int cur_col;
+ int paused;
+ int target;
+ int maxtarget;
int i;
- u_int saved_scbptr;
+ uint8_t last_phase;
+ uint8_t qinpos;
+ uint8_t qintail;
+ uint8_t qoutpos;
+ uint8_t scb_index;
+ uint8_t saved_scbptr;
+
+ if (ahc_is_paused(ahc)) {
+ paused = 1;
+ } else {
+ paused = 0;
+ ahc_pause(ahc);
+ }
- /*
- * Traverse the pending SCB list and ensure that all of the
- * SCBs there have the proper settings.
- */
- scb = LIST_FIRST(&ahc->pending_ccbs);
- pending_ccb_count = 0;
- while (scb != NULL) {
- struct ahc_devinfo devinfo;
- struct scsipi_xfer *xs;
- struct scb *pending_scb;
- struct hardware_scb *pending_hscb;
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
- u_int our_id, remote_id;
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ last_phase = ahc_inb(ahc, LASTPHASE);
+ printf(">>>>>>>>>>>>>>>>>> Dump Card State Begins <<<<<<<<<<<<<<<<<\n"
+ "%s: Dumping Card State %s, at SEQADDR 0x%x\n",
+ ahc_name(ahc), ahc_lookup_phase_entry(last_phase)->phasemsg,
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+ if (paused)
+ printf("Card was paused\n");
+ printf("ACCUM = 0x%x, SINDEX = 0x%x, DINDEX = 0x%x, ARG_2 = 0x%x\n",
+ ahc_inb(ahc, ACCUM), ahc_inb(ahc, SINDEX), ahc_inb(ahc, DINDEX),
+ ahc_inb(ahc, ARG_2));
+ printf("HCNT = 0x%x SCBPTR = 0x%x\n", ahc_inb(ahc, HCNT),
+ ahc_inb(ahc, SCBPTR));
+ cur_col = 0;
+ if ((ahc->features & AHC_DT) != 0)
+ ahc_scsiphase_print(ahc_inb(ahc, SCSIPHASE), &cur_col, 50);
+ ahc_scsisigi_print(ahc_inb(ahc, SCSISIGI), &cur_col, 50);
+ ahc_error_print(ahc_inb(ahc, ERROR), &cur_col, 50);
+ ahc_scsibusl_print(ahc_inb(ahc, SCSIBUSL), &cur_col, 50);
+ ahc_lastphase_print(ahc_inb(ahc, LASTPHASE), &cur_col, 50);
+ ahc_scsiseq_print(ahc_inb(ahc, SCSISEQ), &cur_col, 50);
+ ahc_sblkctl_print(ahc_inb(ahc, SBLKCTL), &cur_col, 50);
+ ahc_scsirate_print(ahc_inb(ahc, SCSIRATE), &cur_col, 50);
+ ahc_seqctl_print(ahc_inb(ahc, SEQCTL), &cur_col, 50);
+ ahc_seq_flags_print(ahc_inb(ahc, SEQ_FLAGS), &cur_col, 50);
+ ahc_sstat0_print(ahc_inb(ahc, SSTAT0), &cur_col, 50);
+ ahc_sstat1_print(ahc_inb(ahc, SSTAT1), &cur_col, 50);
+ ahc_sstat2_print(ahc_inb(ahc, SSTAT2), &cur_col, 50);
+ ahc_sstat3_print(ahc_inb(ahc, SSTAT3), &cur_col, 50);
+ ahc_simode0_print(ahc_inb(ahc, SIMODE0), &cur_col, 50);
+ ahc_simode1_print(ahc_inb(ahc, SIMODE1), &cur_col, 50);
+ ahc_sxfrctl0_print(ahc_inb(ahc, SXFRCTL0), &cur_col, 50);
+ ahc_dfcntrl_print(ahc_inb(ahc, DFCNTRL), &cur_col, 50);
+ ahc_dfstatus_print(ahc_inb(ahc, DFSTATUS), &cur_col, 50);
+ if (cur_col != 0)
+ printf("\n");
+ printf("STACK:");
+ for (i = 0; i < STACK_SIZE; i++)
+ printf(" 0x%x", ahc_inb(ahc, STACK)|(ahc_inb(ahc, STACK) << 8));
+ printf("\nSCB count = %d\n", ahc->scb_data->numscbs);
+ printf("Kernel NEXTQSCB = %d\n", ahc->next_queued_scb->hscb->tag);
+ printf("Card NEXTQSCB = %d\n", ahc_inb(ahc, NEXT_QUEUED_SCB));
+ /* QINFIFO */
+ printf("QINFIFO entries: ");
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ qinpos = ahc_inb(ahc, SNSCB_QOFF);
+ ahc_outb(ahc, SNSCB_QOFF, qinpos);
+ } else
+ qinpos = ahc_inb(ahc, QINPOS);
+ qintail = ahc->qinfifonext;
+ while (qinpos != qintail) {
+ printf("%d ", ahc->qinfifo[qinpos]);
+ qinpos++;
+ }
+ printf("\n");
- xs = scb->xs;
- pending_scb = scb;
- pending_hscb = pending_scb->hscb;
- our_id = SCB_IS_SCSIBUS_B(pending_scb)
- ? ahc->our_id_b : ahc->our_id;
- remote_id = xs->xs_periph->periph_target;
- ahc_compile_devinfo(&devinfo, our_id, remote_id,
- SCB_LUN(pending_scb),
- SCB_CHANNEL(pending_scb),
- ROLE_UNKNOWN);
- tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
- our_id, remote_id, &tstate);
- pending_hscb->control &= ~ULTRAENB;
- if ((tstate->ultraenb & devinfo.target_mask) != 0)
- pending_hscb->control |= ULTRAENB;
- pending_hscb->scsirate = tinfo->scsirate;
- pending_hscb->scsioffset = tinfo->current.offset;
- pending_ccb_count++;
- scb = LIST_NEXT(scb, plinks);
+ printf("Waiting Queue entries: ");
+ scb_index = ahc_inb(ahc, WAITING_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+ scb_index = ahc_inb(ahc, SCB_NEXT);
}
+ printf("\n");
- if (pending_ccb_count == 0)
- return;
+ printf("Disconnected Queue entries: ");
+ scb_index = ahc_inb(ahc, DISCONNECTED_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d:%d ", scb_index, ahc_inb(ahc, SCB_TAG));
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+
+ ahc_sync_qoutfifo(ahc, BUS_DMASYNC_POSTREAD);
+ printf("QOUTFIFO entries: ");
+ qoutpos = ahc->qoutfifonext;
+ i = 0;
+ while (ahc->qoutfifo[qoutpos] != SCB_LIST_NULL && i++ < 256) {
+ printf("%d ", ahc->qoutfifo[qoutpos]);
+ qoutpos++;
+ }
+ printf("\n");
- saved_scbptr = ahc_inb(ahc, SCBPTR);
- /* Ensure that the hscbs down on the card match the new information */
- for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
- u_int scb_tag;
+ printf("Sequencer Free SCB List: ");
+ scb_index = ahc_inb(ahc, FREE_SCBH);
+ i = 0;
+ while (scb_index != SCB_LIST_NULL && i++ < 256) {
+ ahc_outb(ahc, SCBPTR, scb_index);
+ printf("%d ", scb_index);
+ scb_index = ahc_inb(ahc, SCB_NEXT);
+ }
+ printf("\n");
+ printf("Sequencer SCB Info: ");
+ for (i = 0; i < ahc->scb_data->maxhscbs; i++) {
ahc_outb(ahc, SCBPTR, i);
- scb_tag = ahc_inb(ahc, SCB_TAG);
- if (scb_tag != SCB_LIST_NULL) {
- struct ahc_devinfo devinfo;
- struct scb *pending_scb;
- struct scsipi_xfer *xs;
- struct hardware_scb *pending_hscb;
- struct ahc_initiator_tinfo *tinfo;
- struct tmode_tstate *tstate;
- u_int our_id, remote_id;
- u_int control;
-
- pending_scb = &ahc->scb_data->scbarray[scb_tag];
- if (pending_scb->flags == SCB_FREE)
- continue;
- pending_hscb = pending_scb->hscb;
- xs = pending_scb->xs;
- our_id = SCB_IS_SCSIBUS_B(pending_scb)
- ? ahc->our_id_b : ahc->our_id;
- remote_id = xs->xs_periph->periph_target;
- ahc_compile_devinfo(&devinfo, our_id, remote_id,
- SCB_LUN(pending_scb),
- SCB_CHANNEL(pending_scb),
- ROLE_UNKNOWN);
- tinfo = ahc_fetch_transinfo(ahc, devinfo.channel,
- our_id, remote_id, &tstate);
- control = ahc_inb(ahc, SCB_CONTROL);
- control &= ~ULTRAENB;
- if ((tstate->ultraenb & devinfo.target_mask) != 0)
- control |= ULTRAENB;
- ahc_outb(ahc, SCB_CONTROL, control);
- ahc_outb(ahc, SCB_SCSIRATE, tinfo->scsirate);
- ahc_outb(ahc, SCB_SCSIOFFSET, tinfo->current.offset);
+ /*cur_col =*/ printf("\n%3d ", i);
+
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL), &cur_col, 60);
+ ahc_scb_scsiid_print(ahc_inb(ahc, SCB_SCSIID), &cur_col, 60);
+ ahc_scb_lun_print(ahc_inb(ahc, SCB_LUN), &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ }
+ printf("\n");
+
+ printf("Pending list: ");
+ i = 0;
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ if (i++ > 256)
+ break;
+ /*cur_col =*/ printf("\n%3d ", scb->hscb->tag);
+ ahc_scb_control_print(scb->hscb->control, &cur_col, 60);
+ ahc_scb_scsiid_print(scb->hscb->scsiid, &cur_col, 60);
+ ahc_scb_lun_print(scb->hscb->lun, &cur_col, 60);
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+ printf("(");
+ ahc_scb_control_print(ahc_inb(ahc, SCB_CONTROL),
+ &cur_col, 60);
+ ahc_scb_tag_print(ahc_inb(ahc, SCB_TAG), &cur_col, 60);
+ printf(")");
}
}
+ printf("\n");
+
+ printf("Kernel Free SCB list: ");
+ i = 0;
+ SLIST_FOREACH(scb, &ahc->scb_data->free_scbs, links.sle) {
+ if (i++ > 256)
+ break;
+ printf("%d ", scb->hscb->tag);
+ }
+ printf("\n");
+
+ maxtarget = (ahc->features & (AHC_WIDE|AHC_TWIN)) ? 15 : 7;
+ for (target = 0; target <= maxtarget; target++) {
+ untagged_q = &ahc->untagged_queues[target];
+ if (TAILQ_FIRST(untagged_q) == NULL)
+ continue;
+ printf("Untagged Q(%d): ", target);
+ i = 0;
+ TAILQ_FOREACH(scb, untagged_q, links.tqe) {
+ if (i++ > 256)
+ break;
+ printf("%d ", scb->hscb->tag);
+ }
+ printf("\n");
+ }
+
+ ahc_platform_dump_card_state(ahc);
+ printf("\n<<<<<<<<<<<<<<<<< Dump Card State Ends >>>>>>>>>>>>>>>>>>\n");
ahc_outb(ahc, SCBPTR, saved_scbptr);
+ if (paused == 0)
+ ahc_unpause(ahc);
}
-#if UNUSED
-static void
-ahc_dump_targcmd(struct target_cmd *cmd)
+/************************* Target Mode ****************************************/
+#ifdef AHC_TARGET_MODE
+cam_status
+ahc_find_tmode_devs(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb,
+ struct ahc_tmode_tstate **tstate,
+ struct ahc_tmode_lstate **lstate,
+ int notfound_failure)
{
- u_int8_t *byte;
- u_int8_t *last_byte;
- int i;
- byte = &cmd->initiator_channel;
- /* Debugging info for received commands */
- last_byte = &cmd[1].initiator_channel;
+ if ((ahc->features & AHC_TARGETMODE) == 0)
+ return (CAM_REQ_INVALID);
- i = 0;
- while (byte < last_byte) {
- if (i == 0)
- printf("\t");
- printf("%#x", *byte++);
- i++;
- if (i == 8) {
- printf("\n");
- i = 0;
+ /*
+ * Handle the 'black hole' device that sucks up
+ * requests to unattached luns on enabled targets.
+ */
+ if (ccb->ccb_h.target_id == CAM_TARGET_WILDCARD
+ && ccb->ccb_h.target_lun == CAM_LUN_WILDCARD) {
+ *tstate = NULL;
+ *lstate = ahc->black_hole;
+ } else {
+ u_int max_id;
+
+ max_id = (ahc->features & AHC_WIDE) ? 15 : 7;
+ if (ccb->ccb_h.target_id > max_id)
+ return (CAM_TID_INVALID);
+
+ if (ccb->ccb_h.target_lun >= AHC_NUM_LUNS)
+ return (CAM_LUN_INVALID);
+
+ *tstate = ahc->enabled_targets[ccb->ccb_h.target_id];
+ *lstate = NULL;
+ if (*tstate != NULL)
+ *lstate =
+ (*tstate)->enabled_luns[ccb->ccb_h.target_lun];
+ }
+
+ if (notfound_failure != 0 && *lstate == NULL)
+ return (CAM_PATH_INVALID);
+
+ return (CAM_REQ_CMP);
+}
+
+void
+ahc_handle_en_lun(struct ahc_softc *ahc, struct cam_sim *sim, union ccb *ccb)
+{
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_tmode_lstate *lstate;
+ struct ccb_en_lun *cel;
+ cam_status status;
+ u_int target;
+ u_int lun;
+ u_int target_mask;
+ u_int our_id;
+ u_long s;
+ char channel;
+
+ status = ahc_find_tmode_devs(ahc, sim, ccb, &tstate, &lstate,
+ /*notfound_failure*/FALSE);
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ if (cam_sim_bus(sim) == 0)
+ our_id = ahc->our_id;
+ else
+ our_id = ahc->our_id_b;
+
+ if (ccb->ccb_h.target_id != our_id) {
+ /*
+ * our_id represents our initiator ID, or
+ * the ID of the first target to have an
+ * enabled lun in target mode. There are
+ * two cases that may preclude enabling a
+ * target id other than our_id.
+ *
+ * o our_id is for an active initiator role.
+ * Since the hardware does not support
+ * reselections to the initiator role at
+ * anything other than our_id, and our_id
+ * is used by the hardware to indicate the
+ * ID to use for both select-out and
+ * reselect-out operations, the only target
+ * ID we can support in this mode is our_id.
+ *
+ * o The MULTARGID feature is not available and
+ * a previous target mode ID has been enabled.
+ */
+ if ((ahc->features & AHC_MULTIROLE) != 0) {
+
+ if ((ahc->features & AHC_MULTI_TID) != 0
+ && (ahc->flags & AHC_INITIATORROLE) != 0) {
+ /*
+ * Only allow additional targets if
+ * the initiator role is disabled.
+ * The hardware cannot handle a re-select-in
+ * on the initiator id during a re-select-out
+ * on a different target id.
+ */
+ status = CAM_TID_INVALID;
+ } else if ((ahc->flags & AHC_INITIATORROLE) != 0
+ || ahc->enabled_luns > 0) {
+ /*
+ * Only allow our target id to change
+ * if the initiator role is not configured
+ * and there are no enabled luns which
+ * are attached to the currently registered
+ * scsi id.
+ */
+ status = CAM_TID_INVALID;
+ }
+ } else if ((ahc->features & AHC_MULTI_TID) == 0
+ && ahc->enabled_luns > 0) {
+
+ status = CAM_TID_INVALID;
+ }
+ }
+
+ if (status != CAM_REQ_CMP) {
+ ccb->ccb_h.status = status;
+ return;
+ }
+
+ /*
+ * We now have an id that is valid.
+ * If we aren't in target mode, switch modes.
+ */
+ if ((ahc->flags & AHC_TARGETROLE) == 0
+ && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+ u_long s;
+
+ printf("Configuring Target Mode\n");
+ ahc_lock(ahc, &s);
+ if (LIST_FIRST(&ahc->pending_scbs) != NULL) {
+ ccb->ccb_h.status = CAM_BUSY;
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ ahc->flags |= AHC_TARGETROLE;
+ if ((ahc->features & AHC_MULTIROLE) == 0)
+ ahc->flags &= ~AHC_INITIATORROLE;
+ ahc_pause(ahc);
+ ahc_loadseq(ahc);
+ ahc_unlock(ahc, &s);
+ }
+ cel = &ccb->cel;
+ target = ccb->ccb_h.target_id;
+ lun = ccb->ccb_h.target_lun;
+ channel = SIM_CHANNEL(ahc, sim);
+ target_mask = 0x01 << target;
+ if (channel == 'B')
+ target_mask <<= 8;
+
+ if (cel->enable != 0) {
+ u_int scsiseq;
+
+ /* Are we already enabled?? */
+ if (lstate != NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun already enabled\n");
+ ccb->ccb_h.status = CAM_LUN_ALRDY_ENA;
+ return;
+ }
+
+ if (cel->grp6_len != 0
+ || cel->grp7_len != 0) {
+ /*
+ * Don't (yet?) support vendor
+ * specific commands.
+ */
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ printf("Non-zero Group Codes\n");
+ return;
+ }
+
+ /*
+ * Seems to be okay.
+ * Setup our data structures.
+ */
+ if (target != CAM_TARGET_WILDCARD && tstate == NULL) {
+ tstate = ahc_alloc_tstate(ahc, target, channel);
+ if (tstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate tstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ }
+ lstate = malloc(sizeof(*lstate), M_DEVBUF, M_NOWAIT);
+ if (lstate == NULL) {
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate lstate\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ memset(lstate, 0, sizeof(*lstate));
+ status = xpt_create_path(&lstate->path, /*periph*/NULL,
+ xpt_path_path_id(ccb->ccb_h.path),
+ xpt_path_target_id(ccb->ccb_h.path),
+ xpt_path_lun_id(ccb->ccb_h.path));
+ if (status != CAM_REQ_CMP) {
+ free(lstate, M_DEVBUF);
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Couldn't allocate path\n");
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ return;
+ }
+ SLIST_INIT(&lstate->accept_tios);
+ SLIST_INIT(&lstate->immed_notifies);
+ ahc_lock(ahc, &s);
+ ahc_pause(ahc);
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = lstate;
+ ahc->enabled_luns++;
+
+ if ((ahc->features & AHC_MULTI_TID) != 0) {
+ u_int targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1) << 8);
+
+ targid_mask |= target_mask;
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1, (targid_mask >> 8));
+
+ ahc_update_scsiid(ahc, targid_mask);
+ } else {
+ u_int our_id;
+ char channel;
+
+ channel = SIM_CHANNEL(ahc, sim);
+ our_id = SIM_SCSI_ID(ahc, sim);
+
+ /*
+ * This can only happen if selections
+ * are not enabled
+ */
+ if (target != our_id) {
+ u_int sblkctl;
+ char cur_channel;
+ int swap;
+
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = (sblkctl & SELBUSB)
+ ? 'B' : 'A';
+ if ((ahc->features & AHC_TWIN) == 0)
+ cur_channel = 'A';
+ swap = cur_channel != channel;
+ if (channel == 'A')
+ ahc->our_id = target;
+ else
+ ahc->our_id_b = target;
+
+ if (swap)
+ ahc_outb(ahc, SBLKCTL,
+ sblkctl ^ SELBUSB);
+
+ ahc_outb(ahc, SCSIID, target);
+
+ if (swap)
+ ahc_outb(ahc, SBLKCTL, sblkctl);
+ }
+ }
+ } else
+ ahc->black_hole = lstate;
+ /* Allow select-in operations */
+ if (ahc->black_hole != NULL && ahc->enabled_luns > 0) {
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq |= ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+ }
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Lun now enabled for target mode\n");
+ } else {
+ struct scb *scb;
+ int i, empty;
+
+ if (lstate == NULL) {
+ ccb->ccb_h.status = CAM_LUN_INVALID;
+ return;
+ }
+
+ ahc_lock(ahc, &s);
+
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ LIST_FOREACH(scb, &ahc->pending_scbs, pending_links) {
+ struct ccb_hdr *ccbh;
+
+ ccbh = &scb->io_ctx->ccb_h;
+ if (ccbh->func_code == XPT_CONT_TARGET_IO
+ && !xpt_path_comp(ccbh->path, ccb->ccb_h.path)){
+ printf("CTIO pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ ahc_unlock(ahc, &s);
+ return;
+ }
+ }
+
+ if (SLIST_FIRST(&lstate->accept_tios) != NULL) {
+ printf("ATIOs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (SLIST_FIRST(&lstate->immed_notifies) != NULL) {
+ printf("INOTs pending\n");
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ }
+
+ if (ccb->ccb_h.status != CAM_REQ_CMP) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ xpt_print_path(ccb->ccb_h.path);
+ printf("Target mode disabled\n");
+ xpt_free_path(lstate->path);
+ free(lstate, M_DEVBUF);
+
+ ahc_pause(ahc);
+ /* Can we clean up the target too? */
+ if (target != CAM_TARGET_WILDCARD) {
+ tstate->enabled_luns[lun] = NULL;
+ ahc->enabled_luns--;
+ for (empty = 1, i = 0; i < 8; i++)
+ if (tstate->enabled_luns[i] != NULL) {
+ empty = 0;
+ break;
+ }
+
+ if (empty) {
+ ahc_free_tstate(ahc, target, channel,
+ /*force*/FALSE);
+ if (ahc->features & AHC_MULTI_TID) {
+ u_int targid_mask;
+
+ targid_mask = ahc_inb(ahc, TARGID)
+ | (ahc_inb(ahc, TARGID + 1)
+ << 8);
+
+ targid_mask &= ~target_mask;
+ ahc_outb(ahc, TARGID, targid_mask);
+ ahc_outb(ahc, TARGID+1,
+ (targid_mask >> 8));
+ ahc_update_scsiid(ahc, targid_mask);
+ }
+ }
} else {
- printf(", ");
+
+ ahc->black_hole = NULL;
+
+ /*
+ * We can't allow selections without
+ * our black hole device.
+ */
+ empty = TRUE;
}
+ if (ahc->enabled_luns == 0) {
+ /* Disallow select-in */
+ u_int scsiseq;
+
+ scsiseq = ahc_inb(ahc, SCSISEQ_TEMPLATE);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ scsiseq &= ~ENSELI;
+ ahc_outb(ahc, SCSISEQ, scsiseq);
+
+ if ((ahc->features & AHC_MULTIROLE) == 0) {
+ printf("Configuring Initiator Mode\n");
+ ahc->flags &= ~AHC_TARGETROLE;
+ ahc->flags |= AHC_INITIATORROLE;
+ ahc_pause(ahc);
+ ahc_loadseq(ahc);
+ }
+ }
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
}
}
-#endif
static void
-ahc_shutdown(void *arg)
+ahc_update_scsiid(struct ahc_softc *ahc, u_int targid_mask)
{
- struct ahc_softc *ahc;
- int i;
- u_int sxfrctl1_a, sxfrctl1_b;
+ u_int scsiid_mask;
+ u_int scsiid;
- ahc = (struct ahc_softc *)arg;
-
- pause_sequencer(ahc);
+ if ((ahc->features & AHC_MULTI_TID) == 0)
+ panic("ahc_update_scsiid called on non-multitid unit\n");
/*
- * Preserve the value of the SXFRCTL1 register for all channels.
- * It contains settings that affect termination and we don't want
- * to disturb the integrity of the bus during shutdown in case
- * we are in a multi-initiator setup.
+ * Since we will rely on the TARGID mask
+ * for selection enables, ensure that OID
+ * in SCSIID is not set to some other ID
+ * that we don't want to allow selections on.
*/
- sxfrctl1_b = 0;
- if ((ahc->features & AHC_TWIN) != 0) {
- u_int sblkctl;
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ scsiid = ahc_inb(ahc, SCSIID_ULTRA2);
+ else
+ scsiid = ahc_inb(ahc, SCSIID);
+ scsiid_mask = 0x1 << (scsiid & OID);
+ if ((targid_mask & scsiid_mask) == 0) {
+ u_int our_id;
- sblkctl = ahc_inb(ahc, SBLKCTL);
- ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
- sxfrctl1_b = ahc_inb(ahc, SXFRCTL1);
- ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
+ /* ffs counts from 1 */
+ our_id = ffs(targid_mask);
+ if (our_id == 0)
+ our_id = ahc->our_id;
+ else
+ our_id--;
+ scsiid &= TID;
+ scsiid |= our_id;
}
+ if ((ahc->features & AHC_ULTRA2) != 0)
+ ahc_outb(ahc, SCSIID_ULTRA2, scsiid);
+ else
+ ahc_outb(ahc, SCSIID, scsiid);
+}
- sxfrctl1_a = ahc_inb(ahc, SXFRCTL1);
+void
+ahc_run_tqinfifo(struct ahc_softc *ahc, int paused)
+{
+ struct target_cmd *cmd;
- /* This will reset most registers to 0, but not all */
- ahc_reset(ahc);
+ /*
+ * If the card supports auto-access pause,
+ * we can access the card directly regardless
+ * of whether it is paused or not.
+ */
+ if ((ahc->features & AHC_AUTOPAUSE) != 0)
+ paused = TRUE;
- if ((ahc->features & AHC_TWIN) != 0) {
- u_int sblkctl;
+ ahc_sync_tqinfifo(ahc, BUS_DMASYNC_POSTREAD);
+ while ((cmd = &ahc->targetcmds[ahc->tqinfifonext])->cmd_valid != 0) {
- sblkctl = ahc_inb(ahc, SBLKCTL);
- ahc_outb(ahc, SBLKCTL, sblkctl | SELBUSB);
- ahc_outb(ahc, SXFRCTL1, sxfrctl1_b);
- ahc_outb(ahc, SBLKCTL, sblkctl & ~SELBUSB);
- }
- ahc_outb(ahc, SXFRCTL1, sxfrctl1_a);
+ /*
+ * Only advance through the queue if we
+ * have the resources to process the command.
+ */
+ if (ahc_handle_target_cmd(ahc, cmd) != 0)
+ break;
- ahc_outb(ahc, SCSISEQ, 0);
- ahc_outb(ahc, SXFRCTL0, 0);
- ahc_outb(ahc, DSPCISTATUS, 0);
+ cmd->cmd_valid = 0;
+ ahc_dmamap_sync(ahc, ahc->parent_dmat/*shared_data_dmat*/,
+ ahc->shared_data_dmamap,
+ ahc_targetcmd_offset(ahc, ahc->tqinfifonext),
+ sizeof(struct target_cmd),
+ BUS_DMASYNC_PREREAD);
+ ahc->tqinfifonext++;
- for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
- ahc_outb(ahc, i, 0);
+ /*
+ * Lazily update our position in the target mode incoming
+ * command queue as seen by the sequencer.
+ */
+ if ((ahc->tqinfifonext & (HOST_TQINPOS - 1)) == 1) {
+ if ((ahc->features & AHC_HS_MAILBOX) != 0) {
+ u_int hs_mailbox;
+
+ hs_mailbox = ahc_inb(ahc, HS_MAILBOX);
+ hs_mailbox &= ~HOST_TQINPOS;
+ hs_mailbox |= ahc->tqinfifonext & HOST_TQINPOS;
+ ahc_outb(ahc, HS_MAILBOX, hs_mailbox);
+ } else {
+ if (!paused)
+ ahc_pause(ahc);
+ ahc_outb(ahc, KERNEL_TQINPOS,
+ ahc->tqinfifonext & HOST_TQINPOS);
+ if (!paused)
+ ahc_unpause(ahc);
+ }
+ }
+ }
}
-#if defined(AHC_DEBUG) && 0
-static void
-ahc_dumptinfo(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo)
+static int
+ahc_handle_target_cmd(struct ahc_softc *ahc, struct target_cmd *cmd)
{
- printf("%s: tinfo: rate %u\n", ahc_name(ahc), tinfo->scsirate);
-
- printf("\tcurrent:\n");
- printf("\t\twidth %u period %u offset %u flags %x\n",
- tinfo->current.width, tinfo->current.period,
- tinfo->current.offset, tinfo->current.ppr_flags);
-
- printf("\tgoal:\n");
- printf("\t\twidth %u period %u offset %u flags %x\n",
- tinfo->goal.width, tinfo->goal.period,
- tinfo->goal.offset, tinfo->goal.ppr_flags);
-
- printf("\tuser:\n");
- printf("\t\twidth %u period %u offset %u flags %x\n",
- tinfo->user.width, tinfo->user.period,
- tinfo->user.offset, tinfo->user.ppr_flags);
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_tmode_lstate *lstate;
+ struct ccb_accept_tio *atio;
+ uint8_t *byte;
+ int initiator;
+ int target;
+ int lun;
+
+ initiator = SCSIID_TARGET(ahc, cmd->scsiid);
+ target = SCSIID_OUR_ID(cmd->scsiid);
+ lun = (cmd->identify & MSG_IDENTIFY_LUNMASK);
+
+ byte = cmd->bytes;
+ tstate = ahc->enabled_targets[target];
+ lstate = NULL;
+ if (tstate != NULL)
+ lstate = tstate->enabled_luns[lun];
+
+ /*
+ * Commands for disabled luns go to the black hole driver.
+ */
+ if (lstate == NULL)
+ lstate = ahc->black_hole;
+
+ atio = (struct ccb_accept_tio*)SLIST_FIRST(&lstate->accept_tios);
+ if (atio == NULL) {
+ ahc->flags |= AHC_TQINFIFO_BLOCKED;
+ /*
+ * Wait for more ATIOs from the peripheral driver for this lun.
+ */
+ if (bootverbose)
+ printf("%s: ATIOs exhausted\n", ahc_name(ahc));
+ return (1);
+ } else
+ ahc->flags &= ~AHC_TQINFIFO_BLOCKED;
+#if 0
+ printf("Incoming command from %d for %d:%d%s\n",
+ initiator, target, lun,
+ lstate == ahc->black_hole ? "(Black Holed)" : "");
+#endif
+ SLIST_REMOVE_HEAD(&lstate->accept_tios, sim_links.sle);
+
+ if (lstate == ahc->black_hole) {
+ /* Fill in the wildcards */
+ atio->ccb_h.target_id = target;
+ atio->ccb_h.target_lun = lun;
+ }
+
+ /*
+ * Package it up and send it off to
+ * whomever has this lun enabled.
+ */
+ atio->sense_len = 0;
+ atio->init_id = initiator;
+ if (byte[0] != 0xFF) {
+ /* Tag was included */
+ atio->tag_action = *byte++;
+ atio->tag_id = *byte++;
+ atio->ccb_h.flags = CAM_TAG_ACTION_VALID;
+ } else {
+ atio->ccb_h.flags = 0;
+ }
+ byte++;
+
+ /* Okay. Now determine the cdb size based on the command code */
+ switch (*byte >> CMD_GROUP_CODE_SHIFT) {
+ case 0:
+ atio->cdb_len = 6;
+ break;
+ case 1:
+ case 2:
+ atio->cdb_len = 10;
+ break;
+ case 4:
+ atio->cdb_len = 16;
+ break;
+ case 5:
+ atio->cdb_len = 12;
+ break;
+ case 3:
+ default:
+ /* Only copy the opcode. */
+ atio->cdb_len = 1;
+ printf("Reserved or VU command code type encountered\n");
+ break;
+ }
+
+ memcpy(atio->cdb_io.cdb_bytes, byte, atio->cdb_len);
+
+ atio->ccb_h.status |= CAM_CDB_RECVD;
+
+ if ((cmd->identify & MSG_IDENTIFY_DISCFLAG) == 0) {
+ /*
+ * We weren't allowed to disconnect.
+ * We're hanging on the bus until a
+ * continue target I/O comes in response
+ * to this accept tio.
+ */
+#if 0
+ printf("Received Immediate Command %d:%d:%d - %p\n",
+ initiator, target, lun, ahc->pending_device);
+#endif
+ ahc->pending_device = lstate;
+ ahc_freeze_ccb((union ccb *)atio);
+ atio->ccb_h.flags |= CAM_DIS_DISCONNECT;
+ }
+ xpt_done((union ccb*)atio);
+ return (0);
}
#endif
static int
-ahc_istagged_device(struct ahc_softc *ahc, struct scsipi_xfer *xs,
- int nocmdcheck)
+ahc_createdmamem(tag, size, flags, mapp, vaddr, baddr, seg, nseg, myname, what)
+ bus_dma_tag_t tag;
+ int size;
+ int flags;
+ bus_dmamap_t *mapp;
+ caddr_t *vaddr;
+ bus_addr_t *baddr;
+ bus_dma_segment_t *seg;
+ int *nseg;
+ const char *myname, *what;
{
-#ifdef AHC_NO_TAGS
+ int error, level = 0;
+
+ if ((error = bus_dmamem_alloc(tag, size, PAGE_SIZE, 0,
+ seg, 1, nseg, BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: failed to allocate DMA mem for %s, error = %d\n",
+ myname, what, error);
+ goto out;
+ }
+ level++;
+
+ if ((error = bus_dmamem_map(tag, seg, *nseg, size, vaddr,
+ BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
+ printf("%s: failed to map DMA mem for %s, error = %d\n",
+ myname, what, error);
+ goto out;
+ }
+ level++;
+
+ if ((error = bus_dmamap_create(tag, size, 1, size, 0,
+ BUS_DMA_NOWAIT | flags, mapp)) != 0) {
+ printf("%s: failed to create DMA map for %s, error = %d\n",
+ myname, what, error);
+ goto out;
+ }
+ level++;
+
+
+ if ((error = bus_dmamap_load(tag, *mapp, *vaddr, size, NULL,
+ BUS_DMA_NOWAIT)) != 0) {
+ printf("%s: failed to load DMA map for %s, error = %d\n",
+ myname, what, error);
+ goto out;
+ }
+
+ *baddr = (*mapp)->dm_segs[0].ds_addr;
+
return 0;
-#else
- char channel;
- u_int our_id, target;
- struct tmode_tstate *tstate;
- struct ahc_devinfo devinfo;
+out:
+ printf("ahc_createdmamem error (%d)\n", level);
+ switch (level) {
+ case 3:
+ bus_dmamap_destroy(tag, *mapp);
+ /* FALLTHROUGH */
+ case 2:
+ bus_dmamem_unmap(tag, *vaddr, size);
+ /* FALLTHROUGH */
+ case 1:
+ bus_dmamem_free(tag, seg, *nseg);
+ break;
+ default:
+ break;
+ }
- channel = SIM_CHANNEL(ahc, xs->xs_periph);
- our_id = SIM_SCSI_ID(ahc, xs->xs_periph);
- target = xs->xs_periph->periph_target;
- (void)ahc_fetch_transinfo(ahc, channel, our_id, target, &tstate);
+ return error;
+}
- ahc_compile_devinfo(&devinfo, our_id, target,
- xs->xs_periph->periph_lun, channel, ROLE_INITIATOR);
+static void
+ahc_freedmamem(tag, size, map, vaddr, seg, nseg)
+ bus_dma_tag_t tag;
+ int size;
+ bus_dmamap_t map;
+ caddr_t vaddr;
+ bus_dma_segment_t *seg;
+ int nseg;
+{
- return (tstate->tagenable & devinfo.target_mask);
-#endif
+ bus_dmamap_unload(tag, map);
+ bus_dmamap_destroy(tag, map);
+ bus_dmamem_unmap(tag, vaddr, size);
+ bus_dmamem_free(tag, seg, nseg);
}
diff --git a/sys/dev/ic/aic7xxx_cam.h b/sys/dev/ic/aic7xxx_cam.h
new file mode 100644
index 00000000000..be4404914c7
--- /dev/null
+++ b/sys/dev/ic/aic7xxx_cam.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ *
+ * $Id: aic7xxx_cam.h,v 1.1 2003/04/19 19:33:30 fvdl Exp $
+ */
+/*
+ * 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 AHC_LUN_WILDCARD
+
+/*
+ * XXX translate FreeBSD SCSI status byte values to NetBSD, and define
+ * a few more.
+ */
+#define SCSI_STATUS_OK SCSI_OK
+#define SCSI_STATUS_CHECK_COND SCSI_CHECK
+#define SCSI_STATUS_COND_MET 0x04
+#define SCSI_STATUS_BUSY SCSI_BUSY
+#define SCSI_STATUS_INTERMED SCSI_INTERM
+#define SCSI_STATUS_INTERMED_COND_MET 0x14
+#define SCSI_STATUS_RESERV_CONFLICT 0x18
+#define SCSI_STATUS_CMD_TERMINATED SCSI_TERMINATED
+#define SCSI_STATUS_QUEUE_FULL SCSI_QUEUE_FULL
+
+/* CAM Status field values */
+typedef enum {
+ CAM_REQ_INPROG = XS_STS_DONE, /* CCB request is in progress */
+ CAM_REQ_CMP = XS_NOERROR, /* CCB request completed without error */
+ CAM_REQ_ABORTED = XS_DRIVER_STUFFUP, /* CCB request aborted by the host */
+ CAM_UA_ABORT, /* Unable to abort CCB request */
+ CAM_REQ_CMP_ERR = XS_DRIVER_STUFFUP, /* CCB request completed with an error */
+ CAM_BUSY = XS_BUSY, /* CAM subsytem is busy */
+ CAM_REQ_INVALID = XS_DRIVER_STUFFUP, /* CCB request was invalid */
+ CAM_PATH_INVALID, /* Supplied Path ID is invalid */
+ CAM_SEL_TIMEOUT = XS_SELTIMEOUT, /* Target Selection Timeout */
+ CAM_CMD_TIMEOUT, /* Command timeout */
+ CAM_SCSI_STATUS_ERROR, /* SCSI error, look at error code in CCB */
+ CAM_SCSI_BUS_RESET = XS_RESET, /* SCSI Bus Reset Sent/Received */
+ CAM_UNCOR_PARITY = XS_DRIVER_STUFFUP, /* Uncorrectable parity error occurred */
+ CAM_AUTOSENSE_FAIL = XS_DRIVER_STUFFUP, /* Autosense: request sense cmd fail */
+ CAM_NO_HBA = XS_DRIVER_STUFFUP, /* No HBA Detected Error */
+ CAM_DATA_RUN_ERR = XS_DRIVER_STUFFUP, /* Data Overrun error */
+ CAM_UNEXP_BUSFREE = XS_DRIVER_STUFFUP, /* Unexpected Bus Free */
+ CAM_SEQUENCE_FAIL = XS_DRIVER_STUFFUP, /* Protocol Violation */
+ CAM_CCB_LEN_ERR, /* CCB length supplied is inadequate */
+ CAM_PROVIDE_FAIL, /* Unable to provide requested capability */
+ CAM_BDR_SENT = XS_RESET, /* 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 = XS_REQUEUE,
+ CAM_DEV_QFRZN = 0x40,
+
+ CAM_STATUS_MASK = 0x3F
+} cam_status;
+
+typedef enum {
+ CAM_DIR_IN = XS_CTL_DATA_IN,
+ CAM_DIR_OUT = XS_CTL_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 */
diff --git a/sys/dev/ic/aic7xxx_inline.h b/sys/dev/ic/aic7xxx_inline.h
new file mode 100644
index 00000000000..2ea87218ebe
--- /dev/null
+++ b/sys/dev/ic/aic7xxx_inline.h
@@ -0,0 +1,667 @@
+/*
+ * 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.
+ *
+ * $Id: aic7xxx_inline.h,v 1.1 2003/04/19 19:33:30 fvdl Exp $
+ *
+ * //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_
+
+/************************* Sequencer Execution Control ************************/
+static __inline void ahc_pause_bug_fix(struct ahc_softc *ahc);
+static __inline int ahc_is_paused(struct ahc_softc *ahc);
+static __inline void ahc_pause(struct ahc_softc *ahc);
+static __inline void ahc_unpause(struct ahc_softc *ahc);
+
+/*
+ * 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).
+ */
+static __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.
+ */
+static __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.
+ */
+static __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.
+ */
+static __inline void
+ahc_unpause(struct ahc_softc *ahc)
+{
+ if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
+}
+
+/*********************** Untagged Transaction Routines ************************/
+static __inline void ahc_freeze_untagged_queues(struct ahc_softc *ahc);
+static __inline void ahc_release_untagged_queues(struct ahc_softc *ahc);
+
+/*
+ * Block our completion routine from starting the next untagged
+ * transaction for this target or target lun.
+ */
+static __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.
+ */
+static __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);
+ }
+}
+
+/************************** Memory mapping routines ***************************/
+static __inline struct ahc_dma_seg *
+ ahc_sg_bus_to_virt(struct scb *scb,
+ uint32_t sg_busaddr);
+static __inline uint32_t
+ ahc_sg_virt_to_bus(struct scb *scb,
+ struct ahc_dma_seg *sg);
+static __inline uint32_t
+ ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index);
+static __inline void ahc_sync_scb(struct ahc_softc *ahc,
+ struct scb *scb, int op);
+static __inline void ahc_sync_sglist(struct ahc_softc *ahc,
+ struct scb *scb, int op);
+static __inline uint32_t
+ ahc_targetcmd_offset(struct ahc_softc *ahc,
+ u_int index);
+
+static __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]);
+}
+
+static __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)));
+}
+
+static __inline uint32_t
+ahc_hscb_busaddr(struct ahc_softc *ahc, u_int index)
+{
+ return (ahc->scb_data->hscb_busaddr
+ + (sizeof(struct hardware_scb) * index));
+}
+
+static __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);
+}
+
+static __inline void
+ahc_sync_sglist(struct ahc_softc *ahc, struct scb *scb, int op)
+{
+ if (scb->sg_count == 0)
+ return;
+
+ ahc_dmamap_sync(ahc, ahc->parent_dmat, scb->sg_map->sg_dmamap,
+ /*offset*/(scb->sg_list - scb->sg_map->sg_vaddr)
+ * sizeof(struct ahc_dma_seg),
+ /*len*/sizeof(struct ahc_dma_seg) * scb->sg_count, op);
+}
+
+static __inline uint32_t
+ahc_targetcmd_offset(struct ahc_softc *ahc, u_int index)
+{
+ return (((uint8_t *)&ahc->targetcmds[index]) - ahc->qoutfifo);
+}
+
+/******************************** Debugging ***********************************/
+static __inline char *ahc_name(struct ahc_softc *ahc);
+
+static __inline char *
+ahc_name(struct ahc_softc *ahc)
+{
+ return (ahc->name);
+}
+
+/*********************** Miscelaneous Support Functions ***********************/
+
+static __inline void ahc_update_residual(struct ahc_softc *ahc,
+ struct scb *scb);
+static __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);
+static __inline uint16_t
+ ahc_inw(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outw(struct ahc_softc *ahc, u_int port,
+ u_int value);
+static __inline uint32_t
+ ahc_inl(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outl(struct ahc_softc *ahc, u_int port,
+ uint32_t value);
+static __inline uint64_t
+ ahc_inq(struct ahc_softc *ahc, u_int port);
+static __inline void ahc_outq(struct ahc_softc *ahc, u_int port,
+ uint64_t value);
+static __inline struct scb*
+ ahc_get_scb(struct ahc_softc *ahc);
+static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void ahc_swap_with_next_hscb(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline struct scsipi_sense_data *
+ ahc_get_sense_buf(struct ahc_softc *ahc,
+ struct scb *scb);
+static __inline uint32_t
+ ahc_get_sense_bufaddr(struct ahc_softc *ahc,
+ struct scb *scb);
+
+/*
+ * Determine whether the sequencer reported a residual
+ * for this SCB/transaction.
+ */
+static __inline void
+ahc_update_residual(struct ahc_softc *ahc, struct scb *scb)
+{
+ uint32_t sgptr;
+
+ sgptr = ahc_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.
+ */
+static __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]);
+}
+
+static __inline uint16_t
+ahc_inw(struct ahc_softc *ahc, u_int port)
+{
+ return ((ahc_inb(ahc, port+1) << 8) | ahc_inb(ahc, port));
+}
+
+static __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);
+}
+
+static __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));
+}
+
+static __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);
+}
+
+static __inline uint64_t
+ahc_inq(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)
+ | (((uint64_t)ahc_inb(ahc, port+4)) << 32)
+ | (((uint64_t)ahc_inb(ahc, port+5)) << 40)
+ | (((uint64_t)ahc_inb(ahc, port+6)) << 48)
+ | (((uint64_t)ahc_inb(ahc, port+7)) << 56));
+}
+
+static __inline void
+ahc_outq(struct ahc_softc *ahc, u_int port, uint64_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);
+ ahc_outb(ahc, port+4, (value >> 32) & 0xFF);
+ ahc_outb(ahc, port+5, (value >> 40) & 0xFF);
+ ahc_outb(ahc, port+6, (value >> 48) & 0xFF);
+ ahc_outb(ahc, port+7, (value >> 56) & 0xFF);
+}
+
+/*
+ * Get a free scb. If there are none, see if we can allocate a new SCB.
+ */
+static __inline struct scb *
+ahc_get_scb(struct ahc_softc *ahc)
+{
+ struct scb *scb;
+
+ if ((scb = SLIST_FIRST(&ahc->scb_data->free_scbs)) == NULL) {
+ ahc_alloc_scbs(ahc);
+ scb = SLIST_FIRST(&ahc->scb_data->free_scbs);
+ if (scb == NULL)
+ return (NULL);
+ }
+ SLIST_REMOVE_HEAD(&ahc->scb_data->free_scbs, links.sle);
+ return (scb);
+}
+
+/*
+ * Return an SCB resource to the free list.
+ */
+static __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_FREE;
+ 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);
+}
+
+static __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);
+}
+
+static __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 =
+ ahc_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.
+ */
+static __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\n",
+ scb->hscb->tag, scb->hscb->next);
+ /*
+ * Keep a history of SCBs we've downloaded in the qinfifo.
+ */
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+ /*
+ * Make sure our data is consistant 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);
+ }
+}
+
+static __inline struct scsipi_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]);
+}
+
+static __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 scsipi_sense_data)));
+}
+
+/************************** Interrupt Processing ******************************/
+static __inline void ahc_sync_qoutfifo(struct ahc_softc *ahc, int op);
+static __inline void ahc_sync_tqinfifo(struct ahc_softc *ahc, int op);
+static __inline u_int ahc_check_cmdcmpltqueues(struct ahc_softc *ahc);
+static __inline int ahc_intr(void *arg);
+static __inline void ahc_minphys(struct buf *bp);
+
+static __inline void
+ahc_minphys(bp)
+ struct buf *bp;
+{
+/*
+ * 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, hense the "page per segment" limit
+ * enforced here.
+ */
+ if (bp->b_bcount > AHC_MAXTRANSFER_SIZE) {
+ bp->b_bcount = AHC_MAXTRANSFER_SIZE;
+ }
+ minphys(bp);
+}
+
+static __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);
+}
+
+static __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
+static __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
+ */
+static __inline int
+ahc_intr(void *arg)
+{
+ struct ahc_softc *ahc = (struct ahc_softc*)arg;
+ 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 1;
+ }
+ /*
+ * 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);
+ scsipi_channel_freeze(ahc->channel == 'A' ? &ahc->sc_channel : &ahc->sc_channel_b, 1);
+ ahc_run_qoutfifo(ahc);
+ scsipi_channel_thaw(ahc->channel == 'A' ? &ahc->sc_channel : &ahc->sc_channel_b, 1);
+#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);
+ }
+#endif
+ ahc->unsolicited_ints++;
+ return 1;
+ }
+ 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 /* _AIC7XXX_INLINE_H_ */
diff --git a/sys/dev/ic/aic7xxx_osm.c b/sys/dev/ic/aic7xxx_osm.c
new file mode 100644
index 00000000000..67164be21ea
--- /dev/null
+++ b/sys/dev/ic/aic7xxx_osm.c
@@ -0,0 +1,1042 @@
+/*
+ * Bus independent FreeBSD shim for the aic7xxx based adaptec SCSI controllers
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * 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.
+ *
+ * $Id: aic7xxx_osm.c,v 1.1 2003/04/19 19:33:30 fvdl Exp $
+ *
+ * //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_osm.h>
+#include <dev/ic/aic7xxx_inline.h>
+
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+
+
+static void ahc_action(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg);
+static void ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments);
+static int ahc_poll(struct ahc_softc *ahc, int wait);
+static void ahc_setup_data(struct ahc_softc *ahc,
+ struct scsipi_xfer *xs, struct scb *scb);
+static void ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb);
+static int ahc_ioctl(struct scsipi_channel *channel, u_long cmd, caddr_t addr, int flag,
+ struct proc *p);
+
+
+
+/*
+ * Attach all the sub-devices we can find
+ */
+int
+ahc_attach(struct ahc_softc *ahc)
+{
+ u_long s;
+ int i;
+ char ahc_info[256];
+
+ LIST_INIT(&ahc->pending_scbs);
+ for (i = 0; i < AHC_NUM_TARGETS; i++)
+ TAILQ_INIT(&ahc->untagged_queues[i]);
+
+ ahc_lock(ahc, &s);
+
+ ahc->sc_adapter.adapt_dev = &ahc->sc_dev;
+ ahc->sc_adapter.adapt_nchannels = (ahc->features & AHC_TWIN) ? 2 : 1;
+
+ ahc->sc_adapter.adapt_openings = AHC_MAX_QUEUE;
+ ahc->sc_adapter.adapt_max_periph = 16;
+
+ ahc->sc_adapter.adapt_ioctl = ahc_ioctl;
+ ahc->sc_adapter.adapt_minphys = ahc_minphys;
+ ahc->sc_adapter.adapt_request = ahc_action;
+
+ ahc->sc_channel.chan_adapter = &ahc->sc_adapter;
+ ahc->sc_channel.chan_bustype = &scsi_bustype;
+ ahc->sc_channel.chan_channel = 0;
+ ahc->sc_channel.chan_ntargets = (ahc->features & AHC_WIDE) ? 16 : 8;
+ ahc->sc_channel.chan_nluns = 8 /*AHC_NUM_LUNS*/;
+ ahc->sc_channel.chan_id = ahc->our_id;
+
+ if (ahc->features & AHC_TWIN) {
+ ahc->sc_channel_b = ahc->sc_channel;
+ ahc->sc_channel_b.chan_id = ahc->our_id_b;
+ ahc->sc_channel_b.chan_channel = 1;
+ }
+
+ ahc_controller_info(ahc, ahc_info);
+ printf("%s: %s\n", ahc->sc_dev.dv_xname, ahc_info);
+
+ if ((ahc->flags & AHC_PRIMARY_CHANNEL) == 0) {
+ ahc->sc_child = config_found((void *)&ahc->sc_dev,
+ &ahc->sc_channel, scsiprint);
+ if (ahc->features & AHC_TWIN)
+ ahc->sc_child_b = config_found((void *)&ahc->sc_dev,
+ &ahc->sc_channel_b, scsiprint);
+ } else {
+ ahc->sc_child = config_found((void *)&ahc->sc_dev,
+ &ahc->sc_channel_b, scsiprint);
+ ahc->sc_child_b = config_found((void *)&ahc->sc_dev,
+ &ahc->sc_channel, scsiprint);
+ }
+
+ ahc_intr_enable(ahc, TRUE);
+
+ ahc_unlock(ahc, &s);
+ return (1);
+}
+
+/*
+ * Catch an interrupt from the adapter
+ */
+void
+ahc_platform_intr(void *arg)
+{
+ struct ahc_softc *ahc;
+
+ ahc = (struct ahc_softc *)arg;
+ 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 scsipi_xfer *xs;
+ struct scsipi_periph *periph;
+ u_long s;
+
+ xs = scb->xs;
+ periph = xs->xs_periph;
+ 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);
+ }
+
+ callout_stop(&scb->xs->xs_callout);
+
+ if (xs->datalen) {
+ int op;
+
+ if (xs->xs_control & XS_CTL_DATA_IN)
+ 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);
+ }
+
+ /*
+ * If the recovery SCB completes, we have to be
+ * out of our timeout.
+ */
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+ struct scb *list_scb;
+
+ /*
+ * We were able to complete the command successfully,
+ * so reinstate the timeouts for all other pending
+ * commands.
+ */
+ LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) {
+ struct scsipi_xfer *xs = list_scb->xs;
+
+ if (!(xs->xs_control & XS_CTL_POLL)) {
+ callout_reset(&list_scb->xs->xs_callout,
+ (list_scb->xs->timeout > 1000000) ?
+ (list_scb->xs->timeout / 1000) * hz :
+ (list_scb->xs->timeout * hz) / 1000,
+ ahc_timeout, list_scb);
+ }
+ }
+
+ if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
+ || ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ scsipi_printaddr(xs->xs_periph);
+ printf("%s: no longer in timeout, status = %x\n",
+ ahc_name(ahc), xs->status);
+ }
+
+ /* 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
+ * untransfered 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.scsi_sense, 0, sizeof(xs->sense.scsi_sense));
+ memcpy(&xs->sense.scsi_sense,
+ ahc_get_sense_buf(ahc, scb),
+ sizeof(xs->sense.scsi_sense));
+ xs->error = XS_SENSE;
+ }
+ if (scb->flags & SCB_FREEZE_QUEUE) {
+ scsipi_periph_thaw(periph, 1);
+ scb->flags &= ~SCB_FREEZE_QUEUE;
+ }
+
+ ahc_lock(ahc, &s);
+ ahc_free_scb(ahc, scb);
+ ahc_unlock(ahc, &s);
+
+ scsipi_done(xs);
+}
+
+static int
+ahc_ioctl(struct scsipi_channel *channel, u_long cmd, caddr_t addr, int flag,
+ struct proc *p)
+{
+ struct ahc_softc *ahc = (void *)channel->chan_adapter->adapt_dev;
+ int s, ret = ENOTTY;
+
+ switch (cmd) {
+ case SCBUSIORESET:
+ s = splbio();
+ ahc_reset_channel(ahc, ahc->channel, TRUE);
+ splx(s);
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static void
+ahc_action(struct scsipi_channel *chan, scsipi_adapter_req_t req, void *arg)
+{
+ struct ahc_softc *ahc;
+ int s;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ ahc = (void *)chan->chan_adapter->adapt_dev;
+
+ switch (req) {
+
+ case ADAPTER_REQ_RUN_XFER:
+ {
+ struct scsipi_xfer *xs;
+ struct scsipi_periph *periph;
+ struct scb *scb;
+ struct hardware_scb *hscb;
+ u_int target_id;
+ u_int our_id;
+ u_long s;
+
+ xs = arg;
+ periph = xs->xs_periph;
+
+ target_id = periph->periph_target;
+ our_id = ahc->our_id;
+
+ SC_DEBUG(xs->xs_periph, SCSIPI_DB3, ("ahc_action\n"));
+
+ /*
+ * get an scb to use.
+ */
+ ahc_lock(ahc, &s);
+ if ((scb = ahc_get_scb(ahc)) == NULL) {
+ xs->error = XS_RESOURCE_SHORTAGE;
+ ahc_unlock(ahc, &s);
+ scsipi_done(xs);
+ return;
+ }
+ ahc_unlock(ahc, &s);
+
+ hscb = scb->hscb;
+
+ SC_DEBUG(periph, SCSIPI_DB3, ("start scb(%p)\n", scb));
+ scb->xs = xs;
+
+ /*
+ * Put all the arguments for the xfer in the scb
+ */
+ hscb->control = 0;
+ hscb->scsiid = BUILD_SCSIID(ahc, 0, target_id, our_id);
+ hscb->lun = periph->periph_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);
+ }
+
+ ahc_setup_data(ahc, xs, scb);
+
+ break;
+ }
+ case ADAPTER_REQ_GROW_RESOURCES:
+ printf("%s: ADAPTER_REQ_GROW_RESOURCES\n", ahc_name(ahc));
+ return;
+
+ case ADAPTER_REQ_SET_XFER_MODE:
+ {
+ struct scsipi_xfer_mode *xm = arg;
+ struct ahc_devinfo devinfo;
+ int target_id, our_id, first;
+ u_int width;
+ char channel;
+
+ target_id = xm->xm_target;
+ our_id = chan->chan_id;
+ channel = (chan->chan_channel == 1) ? 'B' : 'A';
+ s = splbio();
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id, target_id,
+ &tstate);
+ ahc_compile_devinfo(&devinfo, our_id, target_id,
+ 0, channel, ROLE_INITIATOR);
+
+ /*
+ * XXX since the period and offset are not provided here,
+ * fake things by forcing a renegotiation using the user
+ * settings if this is called for the first time (i.e.
+ * during probe). Also, cap various values at the user
+ * values, assuming that the user set it up that way.
+ */
+ if (ahc->inited_target[target_id] == 0) {
+ tinfo->goal = tinfo->user;
+ tstate->tagenable |=
+ (ahc->user_tagenable & devinfo.target_mask);
+ tstate->discenable |=
+ (ahc->user_discenable & devinfo.target_mask);
+ ahc->inited_target[target_id] = 1;
+ first = 1;
+ } else
+ first = 0;
+
+ if (xm->xm_mode & PERIPH_CAP_WIDE16)
+ width = MSG_EXT_WDTR_BUS_16_BIT;
+ else
+ width = MSG_EXT_WDTR_BUS_8_BIT;
+
+ ahc_validate_width(ahc, NULL, &width, ROLE_UNKNOWN);
+ if (width > tinfo->user.width)
+ width = tinfo->user.width;
+ tinfo->goal.width = width;
+
+ if (!(xm->xm_mode & PERIPH_CAP_SYNC)) {
+ tinfo->goal.period = 0;
+ tinfo->goal.offset = 0;
+ tinfo->goal.ppr_options = 0;
+ }
+
+ if ((xm->xm_mode & PERIPH_CAP_DT) &&
+ (tinfo->user.ppr_options & MSG_EXT_PPR_DT_REQ))
+ tinfo->goal.ppr_options |= MSG_EXT_PPR_DT_REQ;
+ else
+ tinfo->goal.ppr_options &= ~MSG_EXT_PPR_DT_REQ;
+
+ if ((xm->xm_mode & PERIPH_CAP_TQING) &&
+ (ahc->user_tagenable & devinfo.target_mask))
+ tstate->tagenable |= devinfo.target_mask;
+ else
+ tstate->tagenable &= ~devinfo.target_mask;
+
+ /*
+ * If this is the first request, and no negotiation is
+ * needed, just confirm the state to the scsipi layer,
+ * so that it can print a message.
+ */
+ if (!ahc_update_neg_request(ahc, &devinfo, tstate,
+ tinfo, AHC_NEG_IF_NON_ASYNC) && first)
+ scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, xm);
+ splx(s);
+ }
+ }
+
+ return;
+}
+
+static void
+ahc_execute_scb(void *arg, bus_dma_segment_t *dm_segs, int nsegments)
+{
+ struct scb *scb;
+ struct scsipi_xfer *xs;
+ struct ahc_softc *ahc;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ u_int mask;
+ long s;
+
+ scb = (struct scb *)arg;
+ xs = scb->xs;
+ xs->error = 0;
+ xs->status = 0;
+ xs->xs_status = 0;
+ ahc = (void *)xs->xs_periph->periph_channel->chan_adapter->adapt_dev;
+
+ 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 = ahc_htole32(dm_segs->ds_addr);
+ len = dm_segs->ds_len
+ | ((dm_segs->ds_addr >> 8) & 0x7F000000);
+ sg->len = ahc_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 = ahc_htole32(scb->sg_list_phys|SG_FULL_RESID);
+
+ if (xs->xs_control & XS_CTL_DATA_IN)
+ 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 |= ahc_htole32(AHC_DMA_LAST_SEG);
+
+ /* 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 = ahc_htole32(SG_LIST_NULL);
+ scb->hscb->dataptr = 0;
+ scb->hscb->datacnt = 0;
+ }
+
+ scb->sg_count = nsegments;
+
+ ahc_lock(ahc, &s);
+
+ /*
+ * Last time we need to check if this SCB needs to
+ * be aborted.
+ */
+ if (xs->xs_status & XS_STS_DONE) {
+ if (nsegments != 0)
+ bus_dmamap_unload(ahc->buffer_dmat, scb->dmamap);
+ ahc_free_scb(ahc, scb);
+ ahc_unlock(ahc, &s);
+ scsipi_done(xs);
+ return;
+ }
+
+ tinfo = ahc_fetch_transinfo(ahc, ahc->channel,
+ 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 (xs->xs_tag_type)
+ scb->hscb->control |= xs->xs_tag_type;
+
+ if ((xs->xs_control & XS_CTL_DISCOVERY) && (tinfo->goal.width == 0
+ && tinfo->goal.offset == 0
+ && tinfo->goal.ppr_options == 0)) {
+ scb->flags |= SCB_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
+ } else if ((tstate->auto_negotiate & mask) != 0) {
+ scb->flags |= SCB_AUTO_NEGOTIATE;
+ scb->hscb->control |= MK_MESSAGE;
+ }
+
+ LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pending_links);
+
+ if (!(xs->xs_control & XS_CTL_POLL)) {
+ callout_reset(&scb->xs->xs_callout, xs->timeout > 1000000 ?
+ (xs->timeout / 1000) * hz : (xs->timeout * hz) / 1000,
+ ahc_timeout, scb);
+ }
+
+ /*
+ * 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.
+ */
+ 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) {
+ ahc_unlock(ahc, &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->xs_control & XS_CTL_POLL)) {
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ /*
+ * If we can't use interrupts, poll for completion
+ */
+ SC_DEBUG(xs->xs_periph, SCSIPI_DB3, ("cmd_poll\n"));
+ do {
+ if (ahc_poll(ahc, xs->timeout)) {
+ if (!(xs->xs_control & XS_CTL_SILENT))
+ printf("cmd fail\n");
+ ahc_timeout(scb);
+ break;
+ }
+ } while (!(xs->xs_status & XS_STS_DONE));
+ ahc_unlock(ahc, &s);
+
+ return;
+}
+
+static 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);
+}
+
+static void
+ahc_setup_data(struct ahc_softc *ahc, struct scsipi_xfer *xs,
+ struct scb *scb)
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+ xs->resid = xs->status = 0;
+
+ hscb->cdb_len = xs->cmdlen;
+ if (hscb->cdb_len > sizeof(hscb->cdb32)) {
+ u_long s;
+
+ ahc_set_transaction_status(scb, CAM_REQ_INVALID);
+ ahc_lock(ahc, &s);
+ ahc_free_scb(ahc, scb);
+ ahc_unlock(ahc, &s);
+ scsipi_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->xs_control & XS_CTL_NOSLEEP) ?
+ BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
+ BUS_DMA_STREAMING |
+ ((xs->xs_control & XS_CTL_DATA_IN) ?
+ BUS_DMA_READ : BUS_DMA_WRITE));
+ if (error) {
+#ifdef AHC_DEBUG
+ printf("%s: in ahc_setup_data(): bus_dmamap_load() "
+ "= %d\n",
+ ahc_name(ahc), error);
+#endif
+ xs->error = XS_RESOURCE_SHORTAGE;
+ scsipi_done(xs);
+ return;
+ }
+ ahc_execute_scb(scb,
+ scb->dmamap->dm_segs,
+ scb->dmamap->dm_nsegs);
+ } else {
+ ahc_execute_scb(scb, NULL, 0);
+ }
+}
+
+static void
+ahc_set_recoveryscb(struct ahc_softc *ahc, struct scb *scb) {
+
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0) {
+ struct scb *list_scb;
+
+ scb->flags |= SCB_RECOVERY_SCB;
+
+ /*
+ * Take all queued, but not sent SCBs out of the equation.
+ * Also ensure that no new CCBs are queued to us while we
+ * try to fix this problem.
+ */
+ scsipi_channel_freeze(&ahc->sc_channel, 1);
+ if (ahc->features & AHC_TWIN)
+ scsipi_channel_freeze(&ahc->sc_channel_b, 1);
+
+ /*
+ * Go through all of our pending SCBs and remove
+ * any scheduled timeouts for them. We will reschedule
+ * them after we've successfully fixed this problem.
+ */
+ LIST_FOREACH(list_scb, &ahc->pending_scbs, pending_links) {
+ callout_stop(&list_scb->xs->xs_callout);
+ }
+ }
+}
+
+void
+ahc_timeout(void *arg)
+{
+ struct scb *scb;
+ struct ahc_softc *ahc;
+ long s;
+ int found;
+ u_int last_phase;
+ int target;
+ int lun;
+ int i;
+ char channel;
+
+ scb = (struct scb *)arg;
+ ahc = (struct ahc_softc *)scb->ahc_softc;
+
+ ahc_lock(ahc, &s);
+
+ ahc_pause_and_flushwork(ahc);
+
+ if ((scb->flags & SCB_ACTIVE) == 0) {
+ /* Previous timeout took care of me already */
+ printf("%s: Timedout SCB already complete. "
+ "Interrupts may not be functioning.\n", ahc_name(ahc));
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ target = SCB_GET_TARGET(ahc, scb);
+ channel = SCB_GET_CHANNEL(ahc, scb);
+ lun = SCB_GET_LUN(scb);
+
+ ahc_print_path(ahc, scb);
+ printf("SCB 0x%x - timed out\n", scb->hscb->tag);
+ ahc_dump_card_state(ahc);
+ last_phase = ahc_inb(ahc, LASTPHASE);
+ if (scb->sg_count > 0) {
+ for (i = 0; i < scb->sg_count; i++) {
+ printf("sg[%d] - Addr 0x%x : Length %d\n",
+ i,
+ scb->sg_list[i].addr,
+ scb->sg_list[i].len & AHC_SG_LEN_MASK);
+ }
+ }
+ if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) {
+ /*
+ * Been down this road before.
+ * Do a full bus reset.
+ */
+bus_reset:
+ ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
+ found = ahc_reset_channel(ahc, channel, /*Initiate Reset*/TRUE);
+ printf("%s: Issued Channel %c Bus Reset. "
+ "%d SCBs aborted\n", ahc_name(ahc), channel, found);
+ } else {
+ /*
+ * If we are a target, transition to bus free and report
+ * the timeout.
+ *
+ * The target/initiator that is holding up the bus may not
+ * be the same as the one that triggered this timeout
+ * (different commands have different timeout lengths).
+ * If the bus is idle and we are actiing as the initiator
+ * for this request, queue a BDR message to the timed out
+ * target. Otherwise, if the timed out transaction is
+ * active:
+ * Initiator transaction:
+ * Stuff the message buffer with a BDR message and assert
+ * ATN in the hopes that the target will let go of the bus
+ * and go to the mesgout phase. If this fails, we'll
+ * get another timeout 2 seconds later which will attempt
+ * a bus reset.
+ *
+ * Target transaction:
+ * Transition to BUS FREE and report the error.
+ * It's good to be the target!
+ */
+ u_int active_scb_index;
+ u_int saved_scbptr;
+
+ saved_scbptr = ahc_inb(ahc, SCBPTR);
+ active_scb_index = ahc_inb(ahc, SCB_TAG);
+
+ if ((ahc_inb(ahc, SEQ_FLAGS) & NOT_IDENTIFIED) == 0
+ && (active_scb_index < ahc->scb_data->numscbs)) {
+ struct scb *active_scb;
+
+ /*
+ * If the active SCB is not us, assume that
+ * the active SCB has a longer timeout than
+ * the timedout SCB, and wait for the active
+ * SCB to timeout.
+ */
+ active_scb = ahc_lookup_scb(ahc, active_scb_index);
+ if (active_scb != scb) {
+ uint64_t newtimeout;
+
+ ahc_print_path(ahc, scb);
+ printf("Other SCB Timeout%s",
+ (scb->flags & SCB_OTHERTCL_TIMEOUT) != 0
+ ? " again\n" : "\n");
+ scb->flags |= SCB_OTHERTCL_TIMEOUT;
+ newtimeout = MAX(active_scb->xs->timeout,
+ scb->xs->timeout);
+ callout_reset(&scb->xs->xs_callout,
+ newtimeout > 1000000 ?
+ (newtimeout / 1000) * hz :
+ (newtimeout * hz) / 1000,
+ ahc_timeout, scb);
+ ahc_unpause(ahc);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ /* It's us */
+ if ((scb->flags & SCB_TARGET_SCB) != 0) {
+
+ /*
+ * Send back any queued up transactions
+ * and properly record the error condition.
+ */
+ ahc_abort_scbs(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb),
+ scb->hscb->tag,
+ ROLE_TARGET,
+ CAM_CMD_TIMEOUT);
+
+ /* Will clear us from the bus */
+ ahc_restart(ahc);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ ahc_set_recoveryscb(ahc, active_scb);
+ ahc_outb(ahc, MSG_OUT, HOST_MSG);
+ ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+ ahc_print_path(ahc, active_scb);
+ printf("BDR message in message buffer\n");
+ active_scb->flags |= SCB_DEVICE_RESET;
+ callout_reset(&active_scb->xs->xs_callout,
+ 2 * hz, ahc_timeout, active_scb);
+ ahc_unpause(ahc);
+ } else {
+ int disconnected;
+
+ /* XXX Shouldn't panic. Just punt instead? */
+ if ((scb->flags & SCB_TARGET_SCB) != 0)
+ panic("Timed-out target SCB but bus idle");
+
+ if (last_phase != P_BUSFREE
+ && (ahc_inb(ahc, SSTAT0) & TARGET) != 0) {
+ /* XXX What happened to the SCB? */
+ /* Hung target selection. Goto busfree */
+ printf("%s: Hung target selection\n",
+ ahc_name(ahc));
+ ahc_restart(ahc);
+ ahc_unlock(ahc, &s);
+ return;
+ }
+
+ if (ahc_search_qinfifo(ahc, target, channel, lun,
+ scb->hscb->tag, ROLE_INITIATOR,
+ /*status*/0, SEARCH_COUNT) > 0) {
+ disconnected = FALSE;
+ } else {
+ disconnected = TRUE;
+ }
+
+ if (disconnected) {
+
+ ahc_set_recoveryscb(ahc, scb);
+ /*
+ * Actually re-queue this SCB in an attempt
+ * to select the device before it reconnects.
+ * In either case (selection or reselection),
+ * we will now issue a target reset to the
+ * timed-out device.
+ *
+ * Set the MK_MESSAGE control bit indicating
+ * that we desire to send a message. We
+ * also set the disconnected flag since
+ * in the paging case there is no guarantee
+ * that our SCB control byte matches the
+ * version on the card. We don't want the
+ * sequencer to abort the command thinking
+ * an unsolicited reselection occurred.
+ */
+ scb->hscb->control |= MK_MESSAGE|DISCONNECTED;
+ scb->flags |= SCB_DEVICE_RESET;
+
+ /*
+ * Remove any cached copy of this SCB in the
+ * disconnected list in preparation for the
+ * queuing of our abort SCB. We use the
+ * same element in the SCB, SCB_NEXT, for
+ * both the qinfifo and the disconnected list.
+ */
+ ahc_search_disc_list(ahc, target, channel,
+ lun, scb->hscb->tag,
+ /*stop_on_first*/TRUE,
+ /*remove*/TRUE,
+ /*save_state*/FALSE);
+
+ /*
+ * In the non-paging case, the sequencer will
+ * never re-reference the in-core SCB.
+ * To make sure we are notified during
+ * reslection, set the MK_MESSAGE flag in
+ * the card's copy of the SCB.
+ */
+ if ((ahc->flags & AHC_PAGESCBS) == 0) {
+ ahc_outb(ahc, SCBPTR, scb->hscb->tag);
+ ahc_outb(ahc, SCB_CONTROL,
+ ahc_inb(ahc, SCB_CONTROL)
+ | MK_MESSAGE);
+ }
+
+ /*
+ * Clear out any entries in the QINFIFO first
+ * so we are the next SCB for this target
+ * to run.
+ */
+ ahc_search_qinfifo(ahc,
+ SCB_GET_TARGET(ahc, scb),
+ channel, SCB_GET_LUN(scb),
+ SCB_LIST_NULL,
+ ROLE_INITIATOR,
+ CAM_REQUEUE_REQ,
+ SEARCH_COMPLETE);
+ ahc_print_path(ahc, scb);
+ printf("Queuing a BDR SCB\n");
+ ahc_qinfifo_requeue_tail(ahc, scb);
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
+ callout_reset(&scb->xs->xs_callout, 2 * hz,
+ ahc_timeout, scb);
+ ahc_unpause(ahc);
+ } else {
+ /* Go "immediatly" to the bus reset */
+ /* This shouldn't happen */
+ ahc_set_recoveryscb(ahc, scb);
+ ahc_print_path(ahc, scb);
+ printf("SCB %d: Immediate reset. "
+ "Flags = 0x%x\n", scb->hscb->tag,
+ scb->flags);
+ goto bus_reset;
+ }
+ }
+ }
+ ahc_unlock(ahc, &s);
+}
+
+void
+ahc_platform_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo, int enable)
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_tmode_tstate *tstate;
+
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+
+ if (enable)
+ tstate->tagenable |= devinfo->target_mask;
+ else
+ tstate->tagenable &= ~devinfo->target_mask;
+}
+
+int
+ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg)
+{
+ if (sizeof(struct ahc_platform_data) == 0)
+ return 0;
+ ahc->platform_data = malloc(sizeof(struct ahc_platform_data), M_DEVBUF,
+ M_NOWAIT);
+ if (ahc->platform_data == NULL)
+ return (ENOMEM);
+ return (0);
+}
+
+void
+ahc_platform_free(struct ahc_softc *ahc)
+{
+ if (sizeof(struct ahc_platform_data) == 0)
+ return;
+ free(ahc->platform_data, M_DEVBUF);
+}
+
+int
+ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc)
+{
+ return (0);
+}
+
+int
+ahc_detach(struct device *self, int flags)
+{
+ int rv = 0;
+
+ struct ahc_softc *ahc = (struct ahc_softc*)self;
+
+ ahc_intr_enable(ahc, FALSE);
+ if (ahc->sc_child != NULL)
+ rv = config_detach(ahc->sc_child, flags);
+ if (rv == 0 && ahc->sc_child_b != NULL)
+ rv = config_detach(ahc->sc_child_b, flags);
+
+ ahc_free(ahc);
+
+ shutdownhook_disestablish(ahc->shutdown_hook);
+
+ return (rv);
+}
+
+
+void
+ahc_send_async(struct ahc_softc *ahc, char channel, u_int target, u_int lun,
+ ac_code code, void *opt_arg)
+{
+ struct ahc_tmode_tstate *tstate;
+ struct ahc_initiator_tinfo *tinfo;
+ struct ahc_devinfo devinfo;
+ struct scsipi_channel *chan;
+ struct scsipi_xfer_mode xm;
+
+ chan = channel == 'B' ? &ahc->sc_channel_b : &ahc->sc_channel;
+ switch (code) {
+ case AC_TRANSFER_NEG:
+ tinfo = ahc_fetch_transinfo(ahc, channel, ahc->our_id, target,
+ &tstate);
+ ahc_compile_devinfo(&devinfo, ahc->our_id, target, lun,
+ channel, ROLE_UNKNOWN);
+ /*
+ * Don't bother if negotiating. XXX?
+ */
+ if (tinfo->curr.period != tinfo->goal.period
+ || tinfo->curr.width != tinfo->goal.width
+ || tinfo->curr.offset != tinfo->goal.offset
+ || tinfo->curr.ppr_options != tinfo->goal.ppr_options)
+ break;
+ xm.xm_target = target;
+ xm.xm_mode = 0;
+ xm.xm_period = tinfo->curr.period;
+ xm.xm_offset = tinfo->curr.offset;
+ if (tinfo->curr.width == MSG_EXT_WDTR_BUS_16_BIT)
+ xm.xm_mode |= PERIPH_CAP_WIDE16;
+ if (tinfo->curr.period)
+ xm.xm_mode |= PERIPH_CAP_SYNC;
+ if (tstate->tagenable & devinfo.target_mask)
+ xm.xm_mode |= PERIPH_CAP_TQING;
+ scsipi_async_event(chan, ASYNC_EVENT_XFER_MODE, &xm);
+ break;
+ case AC_BUS_RESET:
+ scsipi_async_event(chan, ASYNC_EVENT_RESET, NULL);
+ case AC_SENT_BDR:
+ default:
+ break;
+ }
+}
diff --git a/sys/dev/ic/aic7xxx_osm.h b/sys/dev/ic/aic7xxx_osm.h
new file mode 100644
index 00000000000..f7d4d4a527e
--- /dev/null
+++ b/sys/dev/ic/aic7xxx_osm.h
@@ -0,0 +1,568 @@
+/*
+ * FreeBSD platform specific driver option settings, data structures,
+ * function declarations and includes.
+ *
+ * Copyright (c) 1994-2001 Justin T. Gibbs.
+ * 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.
+ *
+ * $Id: aic7xxx_osm.h,v 1.1 2003/04/19 19:33:30 fvdl Exp $
+ *
+ * //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_NETBSD_H_
+#define _AIC7XXX_NETBSD_H_
+
+#include "opt_ahc.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/proc.h>
+#include <sys/scsiio.h>
+#include <sys/reboot.h>
+#include <sys/kthread.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/scsipi/scsi_all.h>
+#include <dev/scsipi/scsipi_all.h>
+#include <dev/scsipi/scsi_message.h>
+#include <dev/scsipi/scsipi_debug.h>
+#include <dev/scsipi/scsiconf.h>
+#include <dev/scsipi/scsi_iu.h>
+
+#include <uvm/uvm_extern.h>
+
+#ifdef CAM_NEW_TRAN_CODE
+#define AHC_NEW_TRAN_SETTINGS
+#endif /* CAM_NEW_TRAN_CODE */
+
+
+/****************************** Platform Macros *******************************/
+#define SIM_IS_SCSIBUS_B(ahc, sim) \
+ ((sim) == ahc->platform_data->sim_b)
+#define SIM_CHANNEL(ahc, sim) \
+ (((sim) == ahc->platform_data->sim_b) ? 'B' : 'A')
+#define SIM_SCSI_ID(ahc, sim) \
+ (((sim) == ahc->platform_data->sim_b) ? ahc->our_id_b : ahc->our_id)
+#define SIM_PATH(ahc, sim) \
+ (((sim) == ahc->platform_data->sim_b) ? ahc->platform_data->path_b \
+ : ahc->platform_data->path)
+#define BUILD_SCSIID(ahc, sim, target_id, our_id) \
+ ((((target_id) << TID_SHIFT) & TID) | (our_id))
+
+#define SCB_GET_SIM(ahc, scb) \
+ (SCB_GET_CHANNEL(ahc, scb) == 'A' ? (ahc)->platform_data->sim \
+ : (ahc)->platform_data->sim_b)
+
+#ifndef offsetof
+#define offsetof(type, member) ((size_t)(&((type *)0)->member))
+#endif
+/************************* Forward Declarations *******************************/
+typedef pcireg_t ahc_dev_softc_t;
+
+/***************************** Bus Space/DMA **********************************/
+#define ahc_dma_tag_create(ahc, parent_tag, alignment, boundary, \
+ lowaddr, highaddr, filter, filterarg, \
+ maxsize, nsegments, maxsegsz, flags, \
+ dma_tagp) \
+ bus_dma_tag_create(parent_tag, alignment, boundary, \
+ lowaddr, highaddr, filter, filterarg, \
+ maxsize, nsegments, maxsegsz, flags, \
+ dma_tagp)
+
+#define ahc_dma_tag_destroy(ahc, tag) \
+ bus_dma_tag_destroy(tag)
+
+#define ahc_dmamem_alloc(ahc, dmat, vaddr, flags, mapp) \
+ bus_dmamem_alloc(dmat, vaddr, flags, mapp)
+
+#define ahc_dmamem_free(ahc, dmat, vaddr, map) \
+ bus_dmamem_free(dmat, vaddr, map)
+
+#define ahc_dmamap_create(ahc, tag, flags, mapp) \
+ bus_dmamap_create(tag, flags, mapp)
+
+#define ahc_dmamap_destroy(ahc, tag, map) \
+ bus_dmamap_destroy(tag, map)
+
+#define ahc_dmamap_load(ahc, dmat, map, addr, buflen, callback, \
+ callback_arg, flags) \
+ bus_dmamap_load(dmat, map, addr, buflen, callback, callback_arg, flags)
+
+#define ahc_dmamap_unload(ahc, tag, map) \
+ bus_dmamap_unload(tag, map)
+
+/* 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 entrys. 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(btoc(MAXPHYS) + 1, 16))
+
+/* This driver supports target mode */
+//#define AHC_TARGET_MODE 1
+
+/************************** Softc/SCB Platform Data ***************************/
+struct ahc_platform_data {
+ /*
+ * Hooks into the XPT.
+ */
+#if 0
+ struct cam_sim *sim;
+ struct cam_sim *sim_b;
+ struct cam_path *path;
+ struct cam_path *path_b;
+
+ int regs_res_type;
+ int regs_res_id;
+ int irq_res_type;
+ struct resource *regs;
+ struct resource *irq;
+ void *ih;
+ eventhandler_tag eh;
+#endif
+};
+
+struct scb_platform_data {
+};
+
+/********************************* Byte Order *********************************/
+#if __FreeBSD_version >= 500000
+#define ahc_htobe16(x) htobe16(x)
+#define ahc_htobe32(x) htobe32(x)
+#define ahc_htobe64(x) htobe64(x)
+#define ahc_htole16(x) htole16(x)
+#define ahc_htole32(x) htole32(x)
+#define ahc_htole64(x) htole64(x)
+
+#define ahc_be16toh(x) be16toh(x)
+#define ahc_be32toh(x) be32toh(x)
+#define ahc_be64toh(x) be64toh(x)
+#define ahc_le16toh(x) le16toh(x)
+#define ahc_le32toh(x) le32toh(x)
+#define ahc_le64toh(x) le64toh(x)
+#else
+#define ahc_htobe16(x) (x)
+#define ahc_htobe32(x) (x)
+#define ahc_htobe64(x) (x)
+#define ahc_htole16(x) (x)
+#define ahc_htole32(x) (x)
+#define ahc_htole64(x) (x)
+
+#define ahc_be16toh(x) (x)
+#define ahc_be32toh(x) (x)
+#define ahc_be64toh(x) (x)
+#define ahc_le16toh(x) (x)
+#define ahc_le32toh(x) (x)
+#define ahc_le64toh(x) (x)
+#endif
+
+/************************** Timer DataStructures ******************************/
+typedef struct callout ahc_timer_t;
+
+/***************************** Core Includes **********************************/
+#if AHC_REG_PRETTY_PRINT
+#define AIC_DEBUG_REGISTERS 1
+#else
+#define AIC_DEBUG_REGISTERS 0
+#endif
+
+#include <dev/ic/aic7xxxvar.h>
+
+/***************************** Timer Facilities *******************************/
+void ahc_timeout(void*);
+
+#if __FreeBSD_version >= 500000
+#define ahc_timer_init(timer) callout_init(timer, /*mpsafe*/0)
+#else
+#define ahc_timer_init callout_init
+#endif
+#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)
+{
+ /* XXX Is this sufficient for all architectures??? */
+ ahc_inb(ahc, INTSTAT);
+}
+
+/**************************** Locking Primitives ******************************/
+/* Lock protecting internal data structures */
+static __inline void ahc_lockinit(struct ahc_softc *);
+static __inline void ahc_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void ahc_done_lockinit(struct ahc_softc *);
+static __inline void ahc_done_lock(struct ahc_softc *, unsigned long *flags);
+static __inline void ahc_done_unlock(struct ahc_softc *, unsigned long *flags);
+
+/* Lock held during ahc_list manipulation and ahc softc frees */
+static __inline void ahc_list_lockinit(void);
+static __inline void ahc_list_lock(unsigned long *flags);
+static __inline void ahc_list_unlock(unsigned long *flags);
+
+static __inline void
+ahc_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ *flags = splbio();
+}
+
+static __inline void
+ahc_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+ splx(*flags);
+}
+
+/* Lock held during command compeletion to the upper layer */
+static __inline void
+ahc_done_lockinit(struct ahc_softc *ahc)
+{
+}
+
+static __inline void
+ahc_done_lock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+
+static __inline void
+ahc_done_unlock(struct ahc_softc *ahc, unsigned long *flags)
+{
+}
+
+/* Lock held during ahc_list manipulation and ahc softc frees */
+static __inline void
+ahc_list_lockinit()
+{
+}
+
+static __inline void
+ahc_list_lock(unsigned long *flags)
+{
+}
+
+static __inline void
+ahc_list_unlock(unsigned long *flags)
+{
+}
+/****************************** OS Primitives *********************************/
+#define ahc_delay DELAY
+
+/************************** 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 *scb);
+static __inline void ahc_platform_freeze_devq(struct ahc_softc *, struct 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);
+
+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->xs_status = status;
+}
+
+static __inline
+uint32_t ahc_get_transaction_status(struct scb *scb)
+{
+ return (scb->xs->error);
+}
+
+static __inline
+uint32_t ahc_get_scsi_status(struct scb *scb)
+{
+ return (scb->xs->xs_status);
+}
+
+static __inline
+void ahc_set_transaction_tag(struct scb *scb, int enabled, u_int type)
+{
+ scb->xs->xs_tag_type = 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->xs_control & (XS_CTL_DATA_IN|XS_CTL_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->io_ctx->csio.sense_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 (!(scb->flags & SCB_SENSE));
+}
+
+static __inline uint32_t
+ahc_get_sense_bufsize(struct ahc_softc *ahc, struct scb *scb)
+{
+ return (sizeof(struct scsipi_sense_data));
+}
+
+static __inline void
+ahc_freeze_scb(struct scb *scb)
+{
+ struct scsipi_xfer *xs = scb->xs;
+
+ if (!(scb->flags & SCB_FREEZE_QUEUE)) {
+ scsipi_periph_freeze(xs->xs_periph, 1);
+ scb->flags |= SCB_FREEZE_QUEUE;
+ }
+}
+
+static __inline void
+ahc_platform_freeze_devq(struct ahc_softc *ahc, struct scb *scb)
+{
+ /* Nothing to do here for FreeBSD */
+}
+
+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)
+{
+ /* Nothing to do here for FreeBSD */
+ return (0);
+}
+
+static __inline void
+ahc_platform_scb_free(struct ahc_softc *ahc, struct scb *scb)
+{
+#ifdef _FreeBSD_
+ /* What do we do to generically handle driver resource shortages??? */
+ if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0
+ && scb->io_ctx != NULL
+ && (scb->io_ctx->ccb_h.status & CAM_RELEASE_SIMQ) == 0) {
+ scb->io_ctx->ccb_h.status |= CAM_RELEASE_SIMQ;
+ ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+ }
+ scb->io_ctx = NULL;
+#endif
+}
+
+/********************************** PCI ***************************************/
+#ifdef AHC_PCI_CONFIG
+static __inline uint32_t ahc_pci_read_config(ahc_dev_softc_t pci,
+ int reg, int width);
+static __inline void ahc_pci_write_config(ahc_dev_softc_t pci,
+ int reg, uint32_t value,
+ int width);
+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 *ahc);
+int ahc_pci_map_int(struct ahc_softc *ahc);
+
+static __inline uint32_t
+ahc_pci_read_config(ahc_dev_softc_t pci, int reg, int width)
+{
+ return (pci_read_config(pci, reg, width));
+}
+
+static __inline void
+ahc_pci_write_config(ahc_dev_softc_t pci, int reg, uint32_t value, int width)
+{
+ pci_write_config(pci, reg, value, width);
+}
+
+static __inline int
+ahc_get_pci_function(ahc_dev_softc_t pci)
+{
+ return (pci_get_function(pci));
+}
+
+static __inline int
+ahc_get_pci_slot(ahc_dev_softc_t pci)
+{
+ return (pci_get_slot(pci));
+}
+
+static __inline int
+ahc_get_pci_bus(ahc_dev_softc_t pci)
+{
+ return (pci_get_bus(pci));
+}
+
+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,
+ ahc_power_state new_state);
+#endif
+/******************************** VL/EISA *************************************/
+int aic7770_map_registers(struct ahc_softc *ahc, u_int port);
+int aic7770_map_int(struct ahc_softc *ahc, int irq);
+
+/********************************* Debug **************************************/
+static __inline void ahc_print_path(struct ahc_softc *, struct scb *);
+static __inline void ahc_platform_dump_card_state(struct ahc_softc *ahc);
+
+static __inline void
+ahc_print_path(struct ahc_softc *ahc, struct scb *scb)
+{
+ printf("%s:", ahc->sc_dev.dv_xname);
+}
+
+static __inline void
+ahc_platform_dump_card_state(struct ahc_softc *ahc)
+{
+ /* Nothing to do here for FreeBSD */
+}
+/**************************** 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 /*enable*/);
+
+/************************* Initialization/Teardown ****************************/
+int ahc_platform_alloc(struct ahc_softc *ahc, void *platform_arg);
+void ahc_platform_free(struct ahc_softc *ahc);
+int ahc_map_int(struct ahc_softc *ahc);
+int ahc_attach(struct ahc_softc *);
+int ahc_softc_comp(struct ahc_softc *lahc, struct ahc_softc *rahc);
+int ahc_detach(struct device *device, int flags);
+
+/****************************** Interrupts ************************************/
+void ahc_platform_intr(void *);
+static __inline void ahc_platform_flushwork(struct ahc_softc *ahc);
+static __inline void
+ahc_platform_flushwork(struct ahc_softc *ahc)
+{
+}
+
+/************************ Misc Function Declarations **************************/
+void ahc_done(struct ahc_softc *ahc, struct scb *scb);
+void ahc_send_async(struct ahc_softc *, char /*channel*/,
+ u_int /*target*/, u_int /*lun*/, u_int /*ac_code*/, void *arg);
+#endif /* _AIC7XXX_NETBSD_H_ */
diff --git a/sys/dev/ic/aic7xxx_seeprom.c b/sys/dev/ic/aic7xxx_seeprom.c
index 51ee6384c01..def6c7edd85 100644
--- a/sys/dev/ic/aic7xxx_seeprom.c
+++ b/sys/dev/ic/aic7xxx_seeprom.c
@@ -1,14 +1,12 @@
-/* $NetBSD: aic7xxx_seeprom.c,v 1.6 2001/11/13 13:14:34 lukem Exp $ */
+/* $NetBSD: aic7xxx_seeprom.c,v 1.7 2003/04/19 19:33:30 fvdl Exp $ */
/*
* Product specific probe and attach routines for:
* 3940, 2940, aic7895, aic7890, aic7880,
* aic7870, aic7860 and aic7850 SCSI controllers
*
- * These are the SEEPROM-reading functions only. They were split off from
- * the PCI-specific support by Jason R. Thorpe <thorpej@netbsd.org>.
- *
- * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 Justin T. Gibbs.
+ * 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
@@ -17,30 +15,44 @@
* 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.
+ * 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
- * the GNU Public License ("GPL").
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
*
- * 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
+ * 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 DAMAGE.
+ * 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 2003/04/19 19:33:30 fvdl Exp $
*
- * $FreeBSD: src/sys/dev/aic7xxx/ahc_pci.c,v 1.27 2000/01/10 01:47:51 gibbs Exp
-$
- */
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx_pci.c,v 1.22 2003/01/20 20:44:55 gibbs Exp $
+ */
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: aic7xxx_seeprom.c,v 1.6 2001/11/13 13:14:34 lukem Exp $");
+__KERNEL_RCSID(0, "$NetBSD: aic7xxx_seeprom.c,v 1.7 2003/04/19 19:33:30 fvdl Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@@ -57,42 +69,45 @@ __KERNEL_RCSID(0, "$NetBSD: aic7xxx_seeprom.c,v 1.6 2001/11/13 13:14:34 lukem Ex
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
-#include <dev/microcode/aic7xxx/aic7xxx_reg.h>
-#include <dev/ic/aic7xxxvar.h>
+#include <dev/ic/aic7xxx_osm.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 int acquire_seeprom(struct ahc_softc *, struct seeprom_descriptor *);
-static void release_seeprom(struct seeprom_descriptor *);
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
-check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
+ahc_check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
{
- struct seeprom_descriptor sd;
- struct seeprom_config sc;
- u_int scsi_conf;
- u_int adapter_control;
- int have_seeprom;
- int have_autoterm;
+ 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_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
@@ -112,145 +127,82 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
sd.sd_DO = SEEDO;
sd.sd_DI = SEEDI;
- have_seeprom = acquire_seeprom(ahc, &sd);
+ have_seeprom = ahc_acquire_seeprom(ahc, &sd);
if (have_seeprom) {
if (bootverbose)
printf("%s: Reading SEEPROM...", ahc_name(ahc));
for (;;) {
- bus_size_t start_addr;
+ 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);
- have_seeprom = read_seeprom(&sd, (u_int16_t *)&sc,
- start_addr, sizeof(sc)/2);
-
- if (have_seeprom) {
- /* Check checksum */
- int i;
- int maxaddr;
- u_int32_t checksum;
- u_int16_t *scarray;
-
- maxaddr = (sizeof(sc)/2) - 1;
- checksum = 0;
- scarray = (u_int16_t *)&sc;
-
- for (i = 0; i < maxaddr; i++)
- checksum = checksum + scarray[i];
- if (checksum == 0
- || (checksum & 0xFFFF) != sc.checksum) {
- if (bootverbose && sd.sd_chip == C56_66)
+ if (have_seeprom != 0 || sd.sd_chip == C56_66) {
+ if (bootverbose) {
+ if (have_seeprom == 0)
printf ("checksum error\n");
- have_seeprom = 0;
- } else {
- if (bootverbose)
- printf("done.\n");
- break;
+ else
+ printf ("done.\n");
}
- }
-
- if (sd.sd_chip == C56_66)
break;
+ }
sd.sd_chip = C56_66;
}
+ ahc_release_seeprom(&sd);
}
if (!have_seeprom) {
- if (bootverbose)
- printf("%s: No SEEPROM available\n", ahc_name(ahc));
- ahc->flags |= AHC_USEDEFAULTS;
- } else {
/*
- * Put the data we've collected down into SRAM
- * where ahc_init will find it.
+ * 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.
*/
- int i;
- int max_targ = sc.max_targets & CFMAXTARG;
- u_int16_t discenable;
- u_int16_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;
- }
+ 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);
+ }
- for (i = 0; i < max_targ; i++) {
- u_int scsirate;
- u_int16_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);
-
- 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;
-
- if (sc.bios_control & CFEXTEND)
- ahc->flags |= AHC_EXTENDED_TRANS_A;
- 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;
- }
- /* 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);
+ if (!have_seeprom) {
+ if (bootverbose)
+ printf("%s: No SEEPROM available.\n", ahc_name(ahc));
+ ahc->flags |= AHC_USEDEFAULTS;
+ free(ahc->seep_config, M_DEVBUF);
+ ahc->seep_config = NULL;
+ sc = NULL;
+ } else {
+ ahc_parse_pci_eeprom(ahc, sc);
}
/*
@@ -260,10 +212,6 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
* hasn't failed yet...
*/
have_autoterm = have_seeprom;
- if (have_seeprom)
- adapter_control = sc.adapter_control;
- else
- adapter_control = CFAUTOTERM;
/*
* Some low-cost chips have SEEPROM and auto-term control built
@@ -271,16 +219,139 @@ check_extport(struct ahc_softc *ahc, u_int *sxfrctl1)
* if the termination logic is enabled.
*/
if ((ahc->features & AHC_SPIOCAP) != 0) {
- if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) != 0)
- have_autoterm = TRUE;
- else
+ if ((ahc_inb(ahc, SPIOCAP) & SSPIOCPS) == 0)
have_autoterm = FALSE;
}
- if (have_autoterm)
- configure_termination(ahc, &sd, adapter_control, sxfrctl1);
+ 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;
- release_seeprom(&sd);
+ /* 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
@@ -289,7 +360,7 @@ configure_termination(struct ahc_softc *ahc,
u_int adapter_control,
u_int *sxfrctl1)
{
- u_int8_t brddat;
+ uint8_t brddat;
brddat = 0;
@@ -315,6 +386,7 @@ configure_termination(struct ahc_softc *ahc,
int enableSEC_high;
int enablePRI_low;
int enablePRI_high;
+ int sum;
enableSEC_low = 0;
enableSEC_high = 0;
@@ -322,23 +394,24 @@ configure_termination(struct ahc_softc *ahc,
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);
+ &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 & CFSTERM);
- enableSEC_high = (adapter_control & CFWSTERM);
+ 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 = enablePRI_high =
- (adapter_control & CFLVDSTERM);
+ enablePRI_low = (adapter_control & CFSTERM);
+ enablePRI_high = (adapter_control & CFWSTERM);
}
/* Make the table calculations below happy */
internal50_present = 0;
@@ -348,6 +421,8 @@ configure_termination(struct ahc_softc *ahc,
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,
@@ -358,21 +433,22 @@ configure_termination(struct ahc_softc *ahc,
if ((ahc->features & AHC_WIDE) == 0)
internal68_present = 0;
- if (bootverbose) {
- if ((ahc->features & AHC_ULTRA2) == 0) {
- printf("%s: internal 50 cable %s present, "
- "internal 68 cable %s present\n",
- ahc_name(ahc),
- internal50_present ? "is":"not",
- internal68_present ? "is":"not");
+ if (bootverbose
+ && (ahc->features & AHC_ULTRA2) == 0) {
+ printf("%s: internal 50 cable %s present",
+ ahc_name(ahc),
+ internal50_present ? "is":"not");
- printf("%s: external cable %s present\n",
- ahc_name(ahc),
- externalcable_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) {
/*
@@ -394,13 +470,22 @@ configure_termination(struct ahc_softc *ahc,
* Primary High Term Enable = BRDDAT4 (7890)
*/
if ((ahc->features & AHC_ULTRA2) == 0
- && (internal50_present != 0)
- && (internal68_present != 0)
- && (externalcable_present != 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
@@ -420,10 +505,9 @@ configure_termination(struct ahc_softc *ahc,
}
}
- if (((internal50_present ? 1 : 0)
- + (internal68_present ? 1 : 0)
- + (externalcable_present ? 1 : 0)) <= 1
- || (enableSEC_low != 0)) {
+ sum = internal50_present + internal68_present
+ + externalcable_present;
+ if (sum < 2 || (enableSEC_low != 0)) {
if ((ahc->features & AHC_ULTRA2) != 0)
brddat |= BRDDAT5;
else
@@ -474,7 +558,8 @@ configure_termination(struct ahc_softc *ahc,
: "");
}
- if ((adapter_control & CFWSTERM) != 0) {
+ if ((adapter_control & CFWSTERM) != 0
+ && (ahc->features & AHC_WIDE) != 0) {
brddat |= BRDDAT6;
if (bootverbose)
printf("%s: %sHigh byte termination Enabled\n",
@@ -489,7 +574,8 @@ configure_termination(struct ahc_softc *ahc,
*/
ahc_outb(ahc, SXFRCTL1, *sxfrctl1);
- write_brdctl(ahc, brddat);
+ if ((ahc->features & AHC_WIDE) != 0)
+ write_brdctl(ahc, brddat);
}
SEEPROM_OUTB(sd, sd->sd_MS); /* Clear CS */
}
@@ -499,7 +585,7 @@ ahc_new_term_detect(struct ahc_softc *ahc, int *enableSEC_low,
int *enableSEC_high, int *enablePRI_low,
int *enablePRI_high, int *eeprom_present)
{
- u_int8_t brdctl;
+ uint8_t brdctl;
/*
* BRDDAT7 = Eeprom
@@ -521,7 +607,7 @@ aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
int *internal68_present, int *externalcable_present,
int *eeprom_present)
{
- u_int8_t brdctl;
+ uint8_t brdctl;
/*
* First read the status of our cables.
@@ -538,8 +624,8 @@ aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
* BRDDAT7 is INT68.
*/
brdctl = read_brdctl(ahc);
- *internal50_present = !(brdctl & BRDDAT6);
- *internal68_present = !(brdctl & BRDDAT7);
+ *internal50_present = (brdctl & BRDDAT6) ? 0 : 1;
+ *internal68_present = (brdctl & BRDDAT7) ? 0 : 1;
/*
* Set the rom bank to 1 and determine
@@ -553,45 +639,50 @@ aic787X_cable_detect(struct ahc_softc *ahc, int *internal50_present,
* BRDDAT7 is EPROMPS.
*/
brdctl = read_brdctl(ahc);
- *externalcable_present = !(brdctl & BRDDAT6);
- *eeprom_present = brdctl & BRDDAT7;
+ *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)
{
- u_int8_t brdctl;
+ 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);
- *externalcable_present = !(brdctl & BRDDAT6);
+ *internal50_present = (brdctl & BRDDAT5) ? 0 : 1;
+ *externalcable_present = (brdctl & BRDDAT6) ? 0 : 1;
- *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) != 0;
+ *eeprom_present = (ahc_inb(ahc, SPIOCAP) & EEPROM) ? 1 : 0;
}
-static int
-acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
+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)
+ && (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 100 msec
- * timeout which should be near 100 msecs more than
+ * 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 = 100; /* 100 msec timeout */
+ wait = 1000; /* 1 second timeout in msec */
while (--wait && ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0)) {
- DELAY(1000); /* delay 1 msec */
+ ahc_delay(1000); /* delay 1 msec */
}
if ((SEEPROM_STATUS_INB(sd) & sd->sd_RDY) == 0) {
SEEPROM_OUTB(sd, 0);
@@ -600,17 +691,17 @@ acquire_seeprom(struct ahc_softc *ahc, struct seeprom_descriptor *sd)
return(1);
}
-static void
-release_seeprom(struct seeprom_descriptor *sd)
+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, u_int8_t value)
+write_brdctl(struct ahc_softc *ahc, uint8_t value)
{
- u_int8_t brdctl;
+ uint8_t brdctl;
if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
brdctl = BRDSTB;
@@ -622,16 +713,16 @@ write_brdctl(struct ahc_softc *ahc, u_int8_t value)
brdctl = BRDSTB|BRDCS;
}
ahc_outb(ahc, BRDCTL, brdctl);
- DELAY(20);
+ ahc_flush_device_writes(ahc);
brdctl |= value;
ahc_outb(ahc, BRDCTL, brdctl);
- DELAY(20);
+ ahc_flush_device_writes(ahc);
if ((ahc->features & AHC_ULTRA2) != 0)
brdctl |= BRDSTB_ULTRA2;
else
brdctl &= ~BRDSTB;
ahc_outb(ahc, BRDCTL, brdctl);
- DELAY(20);
+ ahc_flush_device_writes(ahc);
if ((ahc->features & AHC_ULTRA2) != 0)
brdctl = 0;
else
@@ -639,11 +730,12 @@ write_brdctl(struct ahc_softc *ahc, u_int8_t value)
ahc_outb(ahc, BRDCTL, brdctl);
}
-static u_int8_t
-read_brdctl(struct ahc_softc *ahc)
+static uint8_t
+read_brdctl(ahc)
+ struct ahc_softc *ahc;
{
- u_int8_t brdctl;
- u_int8_t value;
+ uint8_t brdctl;
+ uint8_t value;
if ((ahc->chip & AHC_CHIPID_MASK) == AHC_AIC7895) {
brdctl = BRDRW;
@@ -655,8 +747,30 @@ read_brdctl(struct ahc_softc *ahc)
brdctl = BRDRW|BRDCS;
}
ahc_outb(ahc, BRDCTL, brdctl);
- DELAY(20);
+ 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);
+ }
+}
diff --git a/sys/dev/ic/aic7xxxvar.h b/sys/dev/ic/aic7xxxvar.h
index 02aa8a3b39a..8620370507a 100644
--- a/sys/dev/ic/aic7xxxvar.h
+++ b/sys/dev/ic/aic7xxxvar.h
@@ -1,11 +1,8 @@
-/* $NetBSD: aic7xxxvar.h,v 1.32 2002/12/07 01:40:24 ichiro Exp $ */
-
/*
- * Interface to the generic driver for the aic7xxx based adaptec
- * SCSI controllers. This is used to implement product specific
- * probe and attach routines.
+ * Core definitions and data structures shareable across OS platforms.
*
- * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 Justin T. Gibbs.
+ * 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
@@ -14,74 +11,272 @@
* 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.
+ * 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
- * the GNU Public License ("GPL").
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
*
- * 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
+ * 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 DAMAGE.
+ * 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.
+ *
+ * $Id: aic7xxxvar.h,v 1.33 2003/04/19 19:33:30 fvdl Exp $
*
- * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.h,v 1.16 2000/02/09 21:24:59 gibbs Exp $
+ * $FreeBSD: /repoman/r/ncvs/src/sys/dev/aic7xxx/aic7xxx.h,v 1.44 2003/01/20 20:44:55 gibbs Exp $
+ */
+/*
+ * Ported from FreeBSD by Pascal Renauld, Network Storage Solutions, Inc. - April 2003
*/
-#ifndef _AIC7XXX_H_
-#define _AIC7XXX_H_
+#ifndef _AIC7XXXVAR_H_
+#define _AIC7XXXVAR_H_
+
+#define AHC_DEBUG
+
+/* Register Definitions */
+#include <dev/microcode/aic7xxx/aic7xxx_reg.h>
+
+#include <dev/ic/aic7xxx_cam.h>
+
+#define AIC_OP_OR 0x0
+#define AIC_OP_AND 0x1
+#define AIC_OP_XOR 0x2
+#define AIC_OP_ADD 0x3
+#define AIC_OP_ADC 0x4
+#define AIC_OP_ROL 0x5
+#define AIC_OP_BMOV 0x6
+
+#define AIC_OP_JMP 0x8
+#define AIC_OP_JC 0x9
+#define AIC_OP_JNC 0xa
+#define AIC_OP_CALL 0xb
+#define AIC_OP_JNE 0xc
+#define AIC_OP_JNZ 0xd
+#define AIC_OP_JE 0xe
+#define AIC_OP_JZ 0xf
+
+/* Pseudo Ops */
+#define AIC_OP_SHL 0x10
+#define AIC_OP_SHR 0x20
+#define AIC_OP_ROR 0x30
+
+struct ins_format1 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ immediate : 8;
+#endif
+};
-#ifndef FALSE
-#define FALSE 0
+struct ins_format2 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t shift_control : 8,
+ source : 9,
+ destination : 9,
+ ret : 1,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ ret : 1,
+ destination : 9,
+ source : 9,
+ shift_control : 8;
+#endif
+};
+
+struct ins_format3 {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ uint32_t immediate : 8,
+ source : 9,
+ address : 10,
+ opcode : 4,
+ parity : 1;
+#else
+ uint32_t parity : 1,
+ opcode : 4,
+ address : 10,
+ source : 9,
+ immediate : 8;
+#endif
+};
+
+union ins_formats {
+ struct ins_format1 format1;
+ struct ins_format2 format2;
+ struct ins_format3 format3;
+ uint8_t bytes[4];
+ uint32_t integer;
+};
+
+#define AHC_LUN_WILDCARD -1
+
+/************************* Forward Declarations *******************************/
+struct ahc_platform_data;
+struct scb_platform_data;
+struct seeprom_descriptor;
+
+/****************************** Useful Macros *********************************/
+#ifndef MAX
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#endif
+
+#ifndef MIN
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifndef TRUE
#define TRUE 1
#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
+
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD (~0)
+
+#define SCSIID_TARGET(ahc, scsiid) \
+ (((scsiid) & ((((ahc)->features & AHC_TWIN) != 0) ? TWIN_TID : TID)) \
+ >> TID_SHIFT)
+#define SCSIID_OUR_ID(scsiid) \
+ ((scsiid) & OID)
+#define SCSIID_CHANNEL(ahc, scsiid) \
+ ((((ahc)->features & AHC_TWIN) != 0) \
+ ? ((((scsiid) & TWIN_CHNLB) != 0) ? 'B' : 'A') \
+ : 'A')
+#define SCB_IS_SCSIBUS_B(ahc, scb) \
+ (SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid) == 'B')
+#define SCB_GET_OUR_ID(scb) \
+ SCSIID_OUR_ID((scb)->hscb->scsiid)
+#define SCB_GET_TARGET(ahc, scb) \
+ SCSIID_TARGET((ahc), (scb)->hscb->scsiid)
+#define SCB_GET_CHANNEL(ahc, scb) \
+ SCSIID_CHANNEL(ahc, (scb)->hscb->scsiid)
+#define SCB_GET_LUN(scb) \
+ ((scb)->hscb->lun)
+#define SCB_GET_TARGET_OFFSET(ahc, scb) \
+ (SCB_GET_TARGET(ahc, scb))
+#define SCB_GET_TARGET_MASK(ahc, scb) \
+ (0x01 << (SCB_GET_TARGET_OFFSET(ahc, scb)))
+#ifdef AHC_DEBUG
+#define SCB_IS_SILENT(scb) \
+ ((ahc_debug & AHC_SHOW_MASKED_ERRORS) == 0 \
+ && (((scb)->flags & SCB_SILENT) != 0))
+#else
+#define SCB_IS_SILENT(scb) \
+ (((scb)->flags & SCB_SILENT) != 0)
+#endif
+#define TCL_TARGET_OFFSET(tcl) \
+ ((((tcl) >> 4) & TID) >> 4)
+#define TCL_LUN(tcl) \
+ (tcl & (AHC_NUM_LUNS - 1))
+#define BUILD_TCL(scsiid, lun) \
+ ((lun) | (((scsiid) & TID) << 4))
+
+#ifndef AHC_TARGET_MODE
+#undef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+
+/**************************** Driver Constants ********************************/
+/*
+ * The maximum number of supported targets.
+ */
+#define AHC_NUM_TARGETS 16
+
+/*
+ * The maximum number of supported luns.
+ * The identify message only supports 64 luns in SPI3.
+ * You can have 2^64 luns when information unit transfers are enabled,
+ * but it is doubtful this driver will ever support IUTs.
+ */
+#define AHC_NUM_LUNS 64
/*
* The maximum transfer per S/G segment.
*/
-#define AHC_MAXTRANSFER_SIZE ctob(AHC_NSEG - 1)
+#define AHC_MAXTRANSFER_SIZE 0x00ffffff /* limited by 24bit counter */
/*
- * The number of dma segments supported. The current implementation limits
- * us to 255 S/G entries (this may change to be unlimited at some point).
- * To reduce the driver's memory consumption, we further 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 maximum amount of SCB storage in hardware on a controller.
+ * This value represents an upper bound. Controllers vary in the number
+ * they actually support.
*/
-#define AHC_NSEG (MIN(btoc(MAXPHYS) + 1, 255))
+#define AHC_SCB_MAX 255
-#define AHC_SCB_MAX 255 /*
- * Up to 255 SCBs on some types of aic7xxx
- * based boards. The aic7870 have 16 internal
- * SCBs, but external SRAM bumps this to 255.
- * The aic7770 family have only 4, and the
- * aic7850 has only 3.
- */
+/*
+ * The maximum number of concurrent transactions supported per driver instance.
+ * Sequencer Control Blocks (SCBs) store per-transaction information. Although
+ * the space for SCBs on the host adapter varies by model, the driver will
+ * page the SCBs between host and controller memory as needed. We are limited
+ * to 253 because:
+ * 1) The 8bit nature of the RISC engine holds us to an 8bit value.
+ * 2) We reserve one value, 255, to represent the invalid element.
+ * 3) Our input queue scheme requires one SCB to always be reserved
+ * in advance of queuing any SCBs. This takes us down to 254.
+ * 4) To handle our output queue correctly on machines that only
+ * support 32bit stores, we must clear the array 4 bytes at a
+ * time. To avoid colliding with a DMA write from the sequencer,
+ * we must be sure that 4 slots are empty when we write to clear
+ * the queue. This reduces us to 253 SCBs: 1 that just completed
+ * and the known three additional empty slots in the queue that
+ * precede it.
+ */
+#define AHC_MAX_QUEUE 253
-#define AHC_TMODE_CMDS 256 /*
- * Ring Buffer of incoming target commands.
- * We allocate 256 to simplify the logic
- * in the sequencer by using the natural
- * wrap point of an 8bit counter.
- */
+/*
+ * The maximum amount of SCB storage we allocate in host memory. This
+ * number should reflect the 1 additional SCB we require to handle our
+ * qinfifo mechanism.
+ */
+#define AHC_SCB_MAX_ALLOC (AHC_MAX_QUEUE+1)
-struct ahc_dma_seg {
- u_int32_t addr;
- u_int32_t len;
-};
+/*
+ * Ring Buffer of incoming target commands.
+ * We allocate 256 to simplify the logic in the sequencer
+ * by using the natural wrap point of an 8bit counter.
+ */
+#define AHC_TMODE_CMDS 256
+
+/* Reset line assertion time in us */
+#define AHC_BUSRESET_DELAY 25
+/******************* Chip Characteristics/Operating Settings *****************/
+/*
+ * Chip Type
+ * The chip order is from least sophisticated to most sophisticated.
+ */
typedef enum {
AHC_NONE = 0x0000,
AHC_CHIPID_MASK = 0x00FF,
@@ -92,93 +287,348 @@ typedef enum {
AHC_AIC7860 = 0x0005,
AHC_AIC7870 = 0x0006,
AHC_AIC7880 = 0x0007,
- AHC_AIC7890 = 0x0008,
- AHC_AIC7892 = 0x0009,
- AHC_AIC7895 = 0x000a,
+ AHC_AIC7895 = 0x0008,
+ AHC_AIC7895C = 0x0009,
+ AHC_AIC7890 = 0x000a,
AHC_AIC7896 = 0x000b,
- AHC_AIC7899 = 0x000c,
+ AHC_AIC7892 = 0x000c,
+ AHC_AIC7899 = 0x000d,
AHC_VL = 0x0100, /* Bus type VL */
AHC_EISA = 0x0200, /* Bus type EISA */
AHC_PCI = 0x0400, /* Bus type PCI */
AHC_BUS_MASK = 0x0F00
} ahc_chip;
-extern const char * const ahc_chip_names[];
-
+/*
+ * Features available in each chip type.
+ */
typedef enum {
- AHC_FENONE = 0x0000,
- AHC_ULTRA = 0x0001, /* Supports 20MHz Transfers */
- AHC_ULTRA2 = 0x0002, /* Supports 40MHz Transfers */
- AHC_WIDE = 0x0004, /* Wide Channel */
- AHC_TWIN = 0x0008, /* Twin Channel */
- AHC_MORE_SRAM = 0x0010, /* 80 bytes instead of 64 */
- AHC_CMD_CHAN = 0x0020, /* Has a Command DMA Channel */
- AHC_QUEUE_REGS = 0x0040, /* Has Queue management registers */
- AHC_SG_PRELOAD = 0x0080, /* Can perform auto-SG preload */
- AHC_SPIOCAP = 0x0100, /* Has a Serial Port I/O Cap Register */
- AHC_MULTI_TID = 0x0200, /* Has bitmask of TIDs for select-in */
- AHC_HS_MAILBOX = 0x0400, /* Has HS_MAILBOX register */
- AHC_DT = 0x0800, /* Double Transition transfers */
- AHC_NEW_TERMCTL = 0x1000,
- AHC_MULTI_FUNC = 0x2000, /* Multi-Function Twin Channel Device */
- AHC_TARG_DMABUG = 0x4000, /* WideOdd Data-In bug in TMODE */
- AHC_AIC7770_FE = AHC_TARG_DMABUG,
- AHC_AIC7850_FE = AHC_TARG_DMABUG|AHC_SPIOCAP,
- AHC_AIC7855_FE = AHC_AIC7850_FE,
- AHC_AIC7859_FE = AHC_AIC7850_FE|AHC_ULTRA,
- AHC_AIC7860_FE = AHC_AIC7859_FE,
- AHC_AIC7870_FE = AHC_TARG_DMABUG,
- AHC_AIC7880_FE = AHC_TARG_DMABUG|AHC_ULTRA,
- AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2|AHC_QUEUE_REGS
- |AHC_SG_PRELOAD|AHC_MULTI_TID|AHC_HS_MAILBOX
- |AHC_NEW_TERMCTL,
- AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_DT,
- AHC_AIC7895_FE = AHC_AIC7880_FE|AHC_MORE_SRAM
- |AHC_CMD_CHAN|AHC_MULTI_FUNC,
+ AHC_FENONE = 0x00000,
+ AHC_ULTRA = 0x00001, /* Supports 20MHz Transfers */
+ AHC_ULTRA2 = 0x00002, /* Supports 40MHz Transfers */
+ AHC_WIDE = 0x00004, /* Wide Channel */
+ AHC_TWIN = 0x00008, /* Twin Channel */
+ AHC_MORE_SRAM = 0x00010, /* 80 bytes instead of 64 */
+ AHC_CMD_CHAN = 0x00020, /* Has a Command DMA Channel */
+ AHC_QUEUE_REGS = 0x00040, /* Has Queue management registers */
+ AHC_SG_PRELOAD = 0x00080, /* Can perform auto-SG preload */
+ AHC_SPIOCAP = 0x00100, /* Has a Serial Port I/O Cap Register */
+ AHC_MULTI_TID = 0x00200, /* Has bitmask of TIDs for select-in */
+ AHC_HS_MAILBOX = 0x00400, /* Has HS_MAILBOX register */
+ AHC_DT = 0x00800, /* Double Transition transfers */
+ AHC_NEW_TERMCTL = 0x01000, /* Newer termination scheme */
+ AHC_MULTI_FUNC = 0x02000, /* Multi-Function Twin Channel Device */
+ AHC_LARGE_SCBS = 0x04000, /* 64byte SCBs */
+ AHC_AUTORATE = 0x08000, /* Automatic update of SCSIRATE/OFFSET*/
+ AHC_AUTOPAUSE = 0x10000, /* Automatic pause on register access */
+ AHC_TARGETMODE = 0x20000, /* Has tested target mode support */
+ AHC_MULTIROLE = 0x40000, /* Space for two roles at a time */
+ AHC_REMOVABLE = 0x80000, /* Hot-Swap supported */
+ AHC_AIC7770_FE = AHC_FENONE,
+ /*
+ * The real 7850 does not support Ultra modes, but there are
+ * several cards that use the generic 7850 PCI ID even though
+ * they are using an Ultra capable chip (7859/7860). We start
+ * out with the AHC_ULTRA feature set and then check the DEVSTATUS
+ * register to determine if the capability is really present.
+ */
+ AHC_AIC7850_FE = AHC_SPIOCAP|AHC_AUTOPAUSE|AHC_TARGETMODE|AHC_ULTRA,
+ AHC_AIC7860_FE = AHC_AIC7850_FE,
+ AHC_AIC7870_FE = AHC_TARGETMODE,
+ AHC_AIC7880_FE = AHC_AIC7870_FE|AHC_ULTRA,
+ /*
+ * Although we have space for both the initiator and
+ * target roles on ULTRA2 chips, we currently disable
+ * the initiator role to allow multi-scsi-id target mode
+ * configurations. We can only respond on the same SCSI
+ * ID as our initiator role if we allow initiator operation.
+ * At some point, we should add a configuration knob to
+ * allow both roles to be loaded.
+ */
+ AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2
+ |AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_MULTI_TID
+ |AHC_HS_MAILBOX|AHC_NEW_TERMCTL|AHC_LARGE_SCBS
+ |AHC_TARGETMODE,
+ AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_DT|AHC_AUTORATE|AHC_AUTOPAUSE,
+ AHC_AIC7895_FE = AHC_AIC7880_FE|AHC_MORE_SRAM|AHC_AUTOPAUSE
+ |AHC_CMD_CHAN|AHC_MULTI_FUNC|AHC_LARGE_SCBS,
AHC_AIC7895C_FE = AHC_AIC7895_FE|AHC_MULTI_TID,
AHC_AIC7896_FE = AHC_AIC7890_FE|AHC_MULTI_FUNC,
AHC_AIC7899_FE = AHC_AIC7892_FE|AHC_MULTI_FUNC
} ahc_feature;
+/*
+ * Bugs in the silicon that we work around in software.
+ */
+typedef enum {
+ AHC_BUGNONE = 0x00,
+ /*
+ * On all chips prior to the U2 product line,
+ * the WIDEODD S/G segment feature does not
+ * work during scsi->HostBus transfers.
+ */
+ AHC_TMODE_WIDEODD_BUG = 0x01,
+ /*
+ * On the aic7890/91 Rev 0 chips, the autoflush
+ * feature does not work. A manual flush of
+ * the DMA FIFO is required.
+ */
+ AHC_AUTOFLUSH_BUG = 0x02,
+ /*
+ * On many chips, cacheline streaming does not work.
+ */
+ AHC_CACHETHEN_BUG = 0x04,
+ /*
+ * On the aic7896/97 chips, cacheline
+ * streaming must be enabled.
+ */
+ AHC_CACHETHEN_DIS_BUG = 0x08,
+ /*
+ * PCI 2.1 Retry failure on non-empty data fifo.
+ */
+ AHC_PCI_2_1_RETRY_BUG = 0x10,
+ /*
+ * Controller does not handle cacheline residuals
+ * properly on S/G segments if PCI MWI instructions
+ * are allowed.
+ */
+ AHC_PCI_MWI_BUG = 0x20,
+ /*
+ * An SCB upload using the SCB channel's
+ * auto array entry copy feature may
+ * corrupt data. This appears to only
+ * occur on 66MHz systems.
+ */
+ AHC_SCBCHAN_UPLOAD_BUG = 0x40
+} ahc_bug;
+
+/*
+ * Configuration specific settings.
+ * The driver determines these settings by probing the
+ * chip/controller's configuration.
+ */
typedef enum {
- AHC_FNONE = 0x000,
- AHC_PAGESCBS = 0x001,/* Enable SCB paging */
- AHC_CHANNEL_B_PRIMARY = 0x002,/*
- * On twin channel adapters, probe
- * channel B first since it is the
- * primary bus.
+ AHC_FNONE = 0x000,
+ AHC_PRIMARY_CHANNEL = 0x003, /*
+ * The channel that should
+ * be probed first.
*/
- AHC_USEDEFAULTS = 0x004,/*
+ AHC_USEDEFAULTS = 0x004, /*
* For cards without an seeprom
* or a BIOS to initialize the chip's
* SRAM, we use the default target
* settings.
*/
- AHC_SHARED_SRAM = 0x010,
- AHC_LARGE_SEEPROM = 0x020,/* Uses C56_66 not C46 */
- AHC_RESET_BUS_A = 0x040,
- AHC_RESET_BUS_B = 0x080,
- AHC_EXTENDED_TRANS_A = 0x100,
- AHC_EXTENDED_TRANS_B = 0x200,
- AHC_TERM_ENB_A = 0x400,
- AHC_TERM_ENB_B = 0x800,
- AHC_INITIATORMODE = 0x1000,/*
+ AHC_SEQUENCER_DEBUG = 0x008,
+ AHC_SHARED_SRAM = 0x010,
+ AHC_LARGE_SEEPROM = 0x020, /* Uses C56_66 not C46 */
+ AHC_RESET_BUS_A = 0x040,
+ AHC_RESET_BUS_B = 0x080,
+ AHC_EXTENDED_TRANS_A = 0x100,
+ AHC_EXTENDED_TRANS_B = 0x200,
+ AHC_TERM_ENB_A = 0x400,
+ AHC_TERM_ENB_B = 0x800,
+ AHC_INITIATORROLE = 0x1000, /*
* Allow initiator operations on
* this controller.
*/
- AHC_TARGETMODE = 0x2000,/*
+ AHC_TARGETROLE = 0x2000, /*
* Allow target operations on this
* controller.
*/
- AHC_NEWEEPROM_FMT = 0x4000,
- AHC_RESOURCE_SHORTAGE = 0x8000,
- AHC_TQINFIFO_BLOCKED = 0x10000,/* Blocked waiting for ATIOs */
- AHC_INT50_SPEEDFLEX = 0x20000 /*
+ AHC_NEWEEPROM_FMT = 0x4000,
+ AHC_RESOURCE_SHORTAGE = 0x8000,
+ AHC_TQINFIFO_BLOCKED = 0x10000, /* Blocked waiting for ATIOs */
+ AHC_INT50_SPEEDFLEX = 0x20000, /*
* Internal 50pin connector
* sits behind an aic3860
*/
+ AHC_SCB_BTT = 0x40000, /*
+ * The busy targets table is
+ * stored in SCB space rather
+ * than SRAM.
+ */
+ AHC_BIOS_ENABLED = 0x80000,
+ AHC_ALL_INTERRUPTS = 0x100000,
+ AHC_PAGESCBS = 0x400000, /* Enable SCB paging */
+ AHC_EDGE_INTERRUPT = 0x800000, /* Device uses edge triggered ints */
+ AHC_39BIT_ADDRESSING = 0x1000000, /* Use 39 bit addressing scheme. */
+ AHC_LSCBS_ENABLED = 0x2000000, /* 64Byte SCBs enabled */
+ AHC_SCB_CONFIG_USED = 0x4000000, /* No SEEPROM but SCB2 had info. */
+ AHC_NO_BIOS_INIT = 0x8000000, /* No BIOS left over settings. */
+ AHC_DISABLE_PCI_PERR = 0x10000000
} ahc_flag;
+/************************* Hardware SCB Definition ***************************/
+
+/*
+ * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
+ * consists of a "hardware SCB" mirroring the fields availible on the card
+ * and additional information the kernel stores for each transaction.
+ *
+ * To minimize space utilization, a portion of the hardware scb stores
+ * different data during different portions of a SCSI transaction.
+ * As initialized by the host driver for the initiator role, this area
+ * contains the SCSI cdb (or a pointer to the cdb) to be executed. After
+ * the cdb has been presented to the target, this area serves to store
+ * residual transfer information and the SCSI status byte.
+ * For the target role, the contents of this area do not change, but
+ * still serve a different purpose than for the initiator role. See
+ * struct target_data for details.
+ */
+
+/*
+ * Status information embedded in the shared poriton of
+ * an SCB after passing the cdb to the target. The kernel
+ * driver will only read this data for transactions that
+ * complete abnormally (non-zero status byte).
+ */
+struct status_pkt {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sg_ptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* Standard SCSI status byte */
+};
+
+/*
+ * Target mode version of the shared data SCB segment.
+ */
+struct target_data {
+ uint32_t residual_datacnt; /* Residual in the current S/G seg */
+ uint32_t residual_sg_ptr; /* The next S/G for this transfer */
+ uint8_t scsi_status; /* SCSI status to give to initiator */
+ uint8_t target_phases; /* Bitmap of phases to execute */
+ uint8_t data_phase; /* Data-In or Data-Out */
+ uint8_t initiator_tag; /* Initiator's transaction tag */
+};
+
+struct hardware_scb {
+/*0*/ union {
+ /*
+ * If the cdb is 12 bytes or less, we embed it directly
+ * in the SCB. For longer cdbs, we embed the address
+ * of the cdb payload as seen by the chip and a DMA
+ * is used to pull it in.
+ */
+ uint8_t cdb[12];
+ uint32_t cdb_ptr;
+ struct status_pkt status;
+ struct target_data tdata;
+ } shared_data;
+/*
+ * A word about residuals.
+ * The scb is presented to the sequencer with the dataptr and datacnt
+ * fields initialized to the contents of the first S/G element to
+ * transfer. The sgptr field is initialized to the bus address for
+ * the S/G element that follows the first in the in core S/G array
+ * or'ed with the SG_FULL_RESID flag. Sgptr may point to an invalid
+ * S/G entry for this transfer (single S/G element transfer with the
+ * first elements address and length preloaded in the dataptr/datacnt
+ * fields). If no transfer is to occur, sgptr is set to SG_LIST_NULL.
+ * The SG_FULL_RESID flag ensures that the residual will be correctly
+ * noted even if no data transfers occur. Once the data phase is entered,
+ * the residual sgptr and datacnt are loaded from the sgptr and the
+ * datacnt fields. After each S/G element's dataptr and length are
+ * loaded into the hardware, the residual sgptr is advanced. After
+ * each S/G element is expired, its datacnt field is checked to see
+ * if the LAST_SEG flag is set. If so, SG_LIST_NULL is set in the
+ * residual sg ptr and the transfer is considered complete. If the
+ * sequencer determines that there is a residual in the tranfer, it
+ * will set the SG_RESID_VALID flag in sgptr and dma the scb back into
+ * host memory. To sumarize:
+ *
+ * Sequencer:
+ * o A residual has occurred if SG_FULL_RESID is set in sgptr,
+ * or residual_sgptr does not have SG_LIST_NULL set.
+ *
+ * o We are transfering the last segment if residual_datacnt has
+ * the SG_LAST_SEG flag set.
+ *
+ * Host:
+ * o A residual has occurred if a completed scb has the
+ * SG_RESID_VALID flag set.
+ *
+ * o residual_sgptr and sgptr refer to the "next" sg entry
+ * and so may point beyond the last valid sg entry for the
+ * transfer.
+ */
+/*12*/ uint32_t dataptr;
+/*16*/ uint32_t datacnt; /*
+ * Byte 3 (numbered from 0) of
+ * the datacnt is really the
+ * 4th byte in that data address.
+ */
+/*20*/ uint32_t sgptr;
+#define SG_PTR_MASK 0xFFFFFFF8
+/*24*/ uint8_t control; /* See SCB_CONTROL in aic7xxx.reg for details */
+/*25*/ uint8_t scsiid; /* what to load in the SCSIID register */
+/*26*/ uint8_t lun;
+/*27*/ uint8_t tag; /*
+ * Index into our kernel SCB array.
+ * Also used as the tag for tagged I/O
+ */
+/*28*/ uint8_t cdb_len;
+/*29*/ uint8_t scsirate; /* Value for SCSIRATE register */
+/*30*/ uint8_t scsioffset; /* Value for SCSIOFFSET register */
+/*31*/ uint8_t next; /*
+ * Used for threading SCBs in the
+ * "Waiting for Selection" and
+ * "Disconnected SCB" lists down
+ * in the sequencer.
+ */
+/*32*/ uint8_t cdb32[32]; /*
+ * CDB storage for cdbs of size
+ * 13->32. We store them here
+ * because hardware scbs are
+ * allocated from DMA safe
+ * memory so we are guaranteed
+ * the controller can access
+ * this data.
+ */
+};
+
+/************************ Kernel SCB Definitions ******************************/
+/*
+ * Some fields of the SCB are OS dependent. Here we collect the
+ * definitions for elements that all OS platforms need to include
+ * in there SCB definition.
+ */
+
+/*
+ * Definition of a scatter/gather element as transfered to the controller.
+ * The aic7xxx chips only support a 24bit length. We use the top byte of
+ * the length to store additional address bits and a flag to indicate
+ * that a given segment terminates the transfer. This gives us an
+ * addressable range of 512GB on machines with 64bit PCI or with chips
+ * that can support dual address cycles on 32bit PCI busses.
+ */
+struct ahc_dma_seg {
+ uint32_t addr;
+ uint32_t len;
+#define AHC_DMA_LAST_SEG 0x80000000
+#define AHC_SG_HIGH_ADDR_MASK 0x7F000000
+#define AHC_SG_LEN_MASK 0x00FFFFFF
+};
+
+struct sg_map_node {
+ bus_dmamap_t sg_dmamap;
+ bus_addr_t sg_physaddr;
+ bus_dma_segment_t sg_dmasegs;
+ int sg_nseg;
+ struct ahc_dma_seg* sg_vaddr;
+ SLIST_ENTRY(sg_map_node) links;
+};
+
+struct ahc_pci_busdata {
+ pci_chipset_tag_t pc;
+ pcitag_t tag;
+ u_int dev;
+ u_int func;
+ pcireg_t class;
+};
+
+/*
+ * The current state of this SCB.
+ */
typedef enum {
SCB_FREE = 0x0000,
SCB_OTHERTCL_TIMEOUT = 0x0002,/*
@@ -191,95 +641,122 @@ typedef enum {
*/
SCB_DEVICE_RESET = 0x0004,
SCB_SENSE = 0x0008,
- SCB_FREEZE_QUEUE = 0x0010,
- SCB_REQUEUE = 0x0020,
- SCB_RECOVERY_SCB = 0x0040,
- SCB_ABORT = 0x1000,
- SCB_QUEUED_MSG = 0x2000,
- SCB_ACTIVE = 0x4000,
- SCB_TARGET_IMMEDIATE = 0x8000
+ SCB_CDB32_PTR = 0x0010,
+ SCB_RECOVERY_SCB = 0x0020,
+ SCB_AUTO_NEGOTIATE = 0x0040,/* Negotiate to achieve goal. */
+ SCB_NEGOTIATE = 0x0080,/* Negotiation forced for command. */
+ SCB_ABORT = 0x0100,
+ SCB_UNTAGGEDQ = 0x0200,
+ SCB_ACTIVE = 0x0400,
+ SCB_TARGET_IMMEDIATE = 0x0800,
+ SCB_TRANSMISSION_ERROR = 0x1000,/*
+ * We detected a parity or CRC
+ * error that has effected the
+ * payload of the command. This
+ * flag is checked when normal
+ * status is returned to catch
+ * the case of a target not
+ * responding to our attempt
+ * to report the error.
+ */
+ SCB_TARGET_SCB = 0x2000,
+ SCB_SILENT = 0x4000,/*
+ * Be quiet about transmission type
+ * errors. They are expected and we
+ * don't want to upset the user. This
+ * flag is typically used during DV.
+ */
+ SCB_FREEZE_QUEUE = 0x8000
} scb_flag;
-typedef enum {
- MSGLOOP_IN_PROG,
- MSGLOOP_MSGCOMPLETE,
- MSGLOOP_TERMINATED
-} msg_loop_stat;
+struct scb {
+ struct hardware_scb *hscb;
+ union {
+ SLIST_ENTRY(scb) sle;
+ TAILQ_ENTRY(scb) tqe;
+ } links;
+ LIST_ENTRY(scb) pending_links;
+
+ struct scsipi_xfer *xs;
+ struct ahc_softc *ahc_softc;
+ scb_flag flags;
+#ifndef __linux__
+ bus_dmamap_t dmamap;
+#endif
+ struct scb_platform_data *platform_data;
+ struct sg_map_node *sg_map;
+ struct ahc_dma_seg *sg_list;
+ bus_addr_t sg_list_phys;
+ u_int sg_count;/* How full ahc_dma_seg is */
+};
-/*
- * The driver keeps up to MAX_SCB scb structures per card in memory. The SCB
- * consists of a "hardware SCB" mirroring the fields available on the card
- * and additional information the kernel stores for each transaction.
- */
-struct hardware_scb {
-/*0*/ u_int8_t control;
-/*1*/ u_int8_t tcl; /* 4/1/3 bits */
-/*2*/ u_int8_t status;
-/*3*/ u_int8_t SG_count;
-/*4*/ u_int32_t SG_pointer;
-/*8*/ u_int8_t residual_SG_count;
-/*9*/ u_int8_t residual_data_count[3];
-/*12*/ u_int32_t data;
-/*16*/ u_int32_t datalen; /* Really only three bytes, but its
- * faster to treat it as a long on
- * a quad boundary.
- */
-/*20*/ u_int32_t cmdpointer;
-/*24*/ u_int8_t cmdlen;
-/*25*/ u_int8_t tag; /* Index into our kernel SCB array.
- * Also used as the tag for tagged I/O
- */
-/*26*/ u_int8_t next; /* Used for threading SCBs in the
- * "Waiting for Selection" and
- * "Disconnected SCB" lists down
- * in the sequencer.
- */
-/*27*/ u_int8_t scsirate; /* Value for SCSIRATE register */
-/*28*/ u_int8_t scsioffset; /* Value for SCSIOFFSET register */
-/*29*/ u_int8_t spare[3]; /*
- * Spare space available on
- * all controller types.
- */
-/*32*/ u_int8_t cmdstore[16]; /*
- * CDB storage for controllers
- * supporting 64 byte SCBs.
+struct scb_data {
+ SLIST_HEAD(, scb) free_scbs; /*
+ * Pool of SCBs ready to be assigned
+ * commands to execute.
*/
-/*48*/ u_int32_t cmdstore_busaddr; /*
- * Address of command store for
- * 32byte SCB adapters
+ struct scb *scbindex[256]; /*
+ * Mapping from tag to SCB.
+ * As tag identifiers are an
+ * 8bit value, we provide space
+ * for all possible tag values.
+ * Any lookups to entries at or
+ * above AHC_SCB_MAX_ALLOC will
+ * always fail.
*/
-/*48*/ u_int8_t spare_64[12]; /*
- * Pad to 64 bytes.
+ struct hardware_scb *hscbs; /* Array of hardware SCBs */
+ struct scb *scbarray; /* Array of kernel SCBs */
+ struct scsipi_sense_data *sense; /* Per SCB sense data */
+
+ /*
+ * "Bus" addresses of our data structures.
+ */
+ bus_dmamap_t hscb_dmamap;
+ bus_addr_t hscb_busaddr;
+ bus_dma_segment_t hscb_seg;
+ int hscb_nseg;
+ int hscb_size;
+
+ bus_dmamap_t sense_dmamap;
+ bus_addr_t sense_busaddr;
+ bus_dma_segment_t sense_seg;
+ int sense_nseg;
+ int sense_size;
+
+ SLIST_HEAD(, sg_map_node) sg_maps;
+ uint8_t numscbs;
+ uint8_t maxhscbs; /* Number of SCBs on the card */
+ uint8_t init_level; /*
+ * How far we've initialized
+ * this structure.
*/
};
-struct scb {
- struct hardware_scb *hscb;
- SLIST_ENTRY(scb) links; /* for chaining */
- LIST_ENTRY(scb) plinks; /* pending chain */
- struct scsipi_xfer *xs;
- scb_flag flags;
- bus_dmamap_t dmamap;
- struct ahc_dma_seg *sg_list;
- bus_addr_t sg_list_phys;
- u_int sg_count;/* How full ahc_dma_seg is */
-};
+/************************ Target Mode Definitions *****************************/
/*
* Connection desciptor for select-in requests in target mode.
- * The first byte is the connecting target, followed by identify
- * message and optional tag information, terminated by 0xFF. The
- * remainder is the command to execute. The cmd_valid byte is on
- * an 8 byte boundary to simplify setting it on aic7880 hardware
- * which only has limited direct access to the DMA FIFO.
*/
struct target_cmd {
- u_int8_t initiator_channel;
- u_int8_t targ_id; /* Target ID we were selected at */
- u_int8_t identify; /* Identify message */
- u_int8_t bytes[21];
- u_int8_t cmd_valid;
- u_int8_t pad[7];
+ uint8_t scsiid; /* Our ID and the initiator's ID */
+ uint8_t identify; /* Identify message */
+ uint8_t bytes[22]; /*
+ * Bytes contains any additional message
+ * bytes terminated by 0xFF. The remainder
+ * is the cdb to execute.
+ */
+ uint8_t cmd_valid; /*
+ * When a command is complete, the firmware
+ * will set cmd_valid to all bits set.
+ * After the host has seen the command,
+ * the bits are cleared. This allows us
+ * to just peek at host memory to determine
+ * if more work is complete. cmd_valid is on
+ * an 8 byte boundary to simplify setting
+ * it on aic7880 hardware which only has
+ * limited direct access to the DMA FIFO.
+ */
+ uint8_t pad[7];
};
/*
@@ -288,95 +765,131 @@ struct target_cmd {
*/
#define AHC_TMODE_EVENT_BUFFER_SIZE 8
struct ahc_tmode_event {
- u_int8_t initiator_id;
- u_int8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */
+ uint8_t initiator_id;
+ uint8_t event_type; /* MSG type or EVENT_TYPE_BUS_RESET */
#define EVENT_TYPE_BUS_RESET 0xFF
- u_int8_t event_arg;
+ uint8_t event_arg;
};
/*
- * Per lun target mode state including accept TIO CCB
- * and immediate notify CCB pools.
+ * Per enabled lun target mode state.
+ * As this state is directly influenced by the host OS'es target mode
+ * environment, we let the OS module define it. Forward declare the
+ * structure here so we can store arrays of them, etc. in OS neutral
+ * data structures.
*/
-struct tmode_lstate {
-#ifdef maybe_later
+#ifdef AHC_TARGET_MODE
+struct ahc_tmode_lstate {
+#if 0
struct cam_path *path;
struct ccb_hdr_slist accept_tios;
struct ccb_hdr_slist immed_notifies;
+#endif
struct ahc_tmode_event event_buffer[AHC_TMODE_EVENT_BUFFER_SIZE];
- u_int8_t event_r_idx;
- u_int8_t event_w_idx;
+ uint8_t event_r_idx;
+ uint8_t event_w_idx;
+};
#else
- u_int8_t dummy;
+struct ahc_tmode_lstate;
#endif
-};
+/******************** Transfer Negotiation Datastructures *********************/
#define AHC_TRANS_CUR 0x01 /* Modify current neogtiation status */
-#define AHC_TRANS_ACTIVE 0x03 /* Assume this is the active target */
+#define AHC_TRANS_ACTIVE 0x03 /* Assume this target is on the bus */
#define AHC_TRANS_GOAL 0x04 /* Modify negotiation goal */
#define AHC_TRANS_USER 0x08 /* Modify user negotiation settings */
+#define AHC_WIDTH_UNKNOWN 0xFF
+#define AHC_PERIOD_UNKNOWN 0xFF
+#define AHC_OFFSET_UNKNOWN 0x0
+#define AHC_PPR_OPTS_UNKNOWN 0xFF
+
+/*
+ * Transfer Negotiation Information.
+ */
struct ahc_transinfo {
- u_int8_t width;
- u_int8_t period;
- u_int8_t offset;
- u_int8_t ppr_flags;
+ uint8_t protocol_version; /* SCSI Revision level */
+ uint8_t transport_version; /* SPI Revision level */
+ uint8_t width; /* Bus width */
+ uint8_t period; /* Sync rate factor */
+ uint8_t offset; /* Sync offset */
+ uint8_t ppr_options; /* Parallel Protocol Request options */
};
+/*
+ * Per-initiator current, goal and user transfer negotiation information. */
struct ahc_initiator_tinfo {
- u_int8_t scsirate;
- struct ahc_transinfo current;
+ uint8_t scsirate; /* Computed value for SCSIRATE reg */
+ struct ahc_transinfo curr;
struct ahc_transinfo goal;
struct ahc_transinfo user;
};
/*
- * Per target mode enabled target state. Esentially just an array of
- * pointers to lun target state as well as sync/wide negotiation information
- * for each initiator<->target mapping (including the mapping for when we
- * are the initiator).
+ * Per enabled target ID state.
+ * Pointers to lun target state as well as sync/wide negotiation information
+ * for each initiator<->target mapping. For the initiator role we pretend
+ * that we are the target and the targets are the initiators since the
+ * negotiation is the same regardless of role.
*/
-struct tmode_tstate {
-#if 0
- struct tmode_lstate* enabled_luns[8];
-#endif
- struct ahc_initiator_tinfo transinfo[16];
+struct ahc_tmode_tstate {
+ struct ahc_tmode_lstate* enabled_luns[AHC_NUM_LUNS];
+ struct ahc_initiator_tinfo transinfo[AHC_NUM_TARGETS];
/*
* Per initiator state bitmasks.
*/
- u_int16_t ultraenb; /* Using ultra sync rate */
- u_int16_t discenable; /* Disconnection allowed */
- u_int16_t tagenable; /* Tagged Queuing allowed */
- u_int16_t tagdisable; /* TQ explicitly disallowed */
+ uint16_t auto_negotiate;/* Auto Negotiation Required */
+ uint16_t ultraenb; /* Using ultra sync rate */
+ uint16_t discenable; /* Disconnection allowed */
+ uint16_t tagenable; /* Tagged Queuing allowed */
};
-#define AHC_TARGET_WILDCARD -1
-#define AHC_LUN_WILDCARD -1
-
/*
- * XXX translate FreeBSD SCSI status byte values to NetBSD, and define
- * a few more.
+ * Data structure for our table of allowed synchronous transfer rates.
*/
-#define SCSI_STATUS_OK SCSI_OK
-#define SCSI_STATUS_CHECK_COND SCSI_CHECK
-#define SCSI_STATUS_COND_MET 0x04
-#define SCSI_STATUS_BUSY SCSI_BUSY
-#define SCSI_STATUS_INTERMED SCSI_INTERM
-#define SCSI_STATUS_INTERMED_COND_MET 0x14
-#define SCSI_STATUS_RESERV_CONFLICT 0x18
-#define SCSI_STATUS_CMD_TERMINATED 0x22
-#define SCSI_STATUS_QUEUE_FULL SCSI_QUEUE_FULL
+struct ahc_syncrate {
+ u_int sxfr_u2; /* Value of the SXFR parameter for Ultra2+ Chips */
+ u_int sxfr; /* Value of the SXFR parameter for <= Ultra Chips */
+#define ULTRA_SXFR 0x100 /* Rate Requires Ultra Mode set */
+#define ST_SXFR 0x010 /* Rate Single Transition Only */
+#define DT_SXFR 0x040 /* Rate Double Transition Only */
+ uint8_t period; /* Period to send to SCSI target */
+ char *rate;
+};
+/* Safe and valid period for async negotiations. */
+#define AHC_ASYNC_XFER_PERIOD 0x45
+#define AHC_ULTRA2_XFER_PERIOD 0x0a
+
+/*
+ * Indexes into our table of syncronous transfer rates.
+ */
+#define AHC_SYNCRATE_DT 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA 3
+#define AHC_SYNCRATE_FAST 6
+#define AHC_SYNCRATE_MAX AHC_SYNCRATE_DT
+#define AHC_SYNCRATE_MIN 13
+
+/***************************** Lookup Tables **********************************/
/*
- * Define the format of the aic7XXX SEEPROM registers (16 bits).
+ * Phase -> name and message out response
+ * to parity errors in each phase table.
*/
+struct ahc_phase_table_entry {
+ uint8_t phase;
+ uint8_t mesg_out; /* Message response to parity errors */
+ char *phasemsg;
+};
+
+/************************** Serial EEPROM Format ******************************/
struct seeprom_config {
/*
- * SCSI ID Configuration Flags
+ * Per SCSI ID Configuration Flags
*/
- u_int16_t device_flags[16]; /* words 0-15 */
+ uint16_t device_flags[16]; /* words 0-15 */
#define CFXFER 0x0007 /* synchronous transfer rate */
#define CFSYNCH 0x0008 /* enable synchronous transfer */
#define CFDISC 0x0010 /* enable disconnection */
@@ -386,28 +899,36 @@ struct seeprom_config {
#define CFSTART 0x0100 /* send start unit SCSI command */
#define CFINCBIOS 0x0200 /* include in BIOS scan */
#define CFRNFOUND 0x0400 /* report even if not found */
-#define CFMULTILUN 0x0800 /* Probe multiple luns in BIOS scan */
+#define CFMULTILUNDEV 0x0800 /* Probe multiple luns in BIOS scan */
#define CFWBCACHEENB 0x4000 /* Enable W-Behind Cache on disks */
#define CFWBCACHENOP 0xc000 /* Don't touch W-Behind Cache */
/*
* BIOS Control Bits
*/
- u_int16_t bios_control; /* word 16 */
+ uint16_t bios_control; /* word 16 */
#define CFSUPREM 0x0001 /* support all removeable drives */
#define CFSUPREMB 0x0002 /* support removeable boot drives */
#define CFBIOSEN 0x0004 /* BIOS enabled */
-/* UNUSED 0x0008 */
+#define CFBIOS_BUSSCAN 0x0008 /* Have the BIOS Scan the Bus */
#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CFSTPWLEVEL 0x0010 /* Termination level control */
#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
-/* UNUSED 0x0040 */
+#define CFCTRL_A 0x0020 /* BIOS displays Ctrl-A message */
+#define CFTERM_MENU 0x0040 /* BIOS displays termination menu */
#define CFEXTEND 0x0080 /* extended translation enabled */
+#define CFSCAMEN 0x0100 /* SCAM enable */
+#define CFMSG_LEVEL 0x0600 /* BIOS Message Level */
+#define CFMSG_VERBOSE 0x0000
+#define CFMSG_SILENT 0x0200
+#define CFMSG_DIAG 0x0400
+#define CFBOOTCD 0x0800 /* Support Bootable CD-ROM */
/* UNUSED 0xff00 */
/*
* Host Adapter Control Bits
*/
- u_int16_t adapter_control; /* word 17 */
+ uint16_t adapter_control; /* word 17 */
#define CFAUTOTERM 0x0001 /* Perform Auto termination */
#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable */
#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
@@ -416,16 +937,20 @@ struct seeprom_config {
#define CFWSTERM 0x0008 /* SCSI high byte termination */
#define CFSPARITY 0x0010 /* SCSI parity */
#define CF284XSTERM 0x0020 /* SCSI low byte term (284x cards) */
+#define CFMULTILUN 0x0020
#define CFRESETB 0x0040 /* reset SCSI bus at boot */
-#define CFCHNLBPRIMARY 0x0100 /* aic7895 probe B channel first */
-#define CFSEAUTOTERM 0x0400 /* aic7890 Perform SE Auto Termination*/
-#define CFLVDSTERM 0x0800 /* aic7890 LVD Termination */
-/* UNUSED 0xf280 */
+#define CFCLUSTERENB 0x0080 /* Cluster Enable */
+#define CFBOOTCHAN 0x0300 /* probe this channel first */
+#define CFBOOTCHANSHIFT 8
+#define CFSEAUTOTERM 0x0400 /* Ultra2 Perform secondary Auto Term*/
+#define CFSELOWTERM 0x0800 /* Ultra2 secondary low term */
+#define CFSEHIGHTERM 0x1000 /* Ultra2 secondary high term */
+#define CFENABLEDV 0x4000 /* Perform Domain Validation*/
/*
- * Bus Release, Host Adapter ID
+ * Bus Release Time, Host Adapter ID
*/
- u_int16_t brtime_id; /* word 18 */
+ uint16_t brtime_id; /* word 18 */
#define CFSCSIID 0x000f /* host adapter SCSI ID */
/* UNUSED 0x00f0 */
#define CFBRTIME 0xff00 /* bus release time */
@@ -433,23 +958,18 @@ struct seeprom_config {
/*
* Maximum targets
*/
- u_int16_t max_targets; /* word 19 */
+ uint16_t max_targets; /* word 19 */
#define CFMAXTARG 0x00ff /* maximum targets */
-/* UNUSED 0xff00 */
- u_int16_t res_1[11]; /* words 20-30 */
- u_int16_t checksum; /* word 31 */
-};
-
-struct ahc_syncrate {
- int sxfr_u2;
- int sxfr;
- /* Rates in Ultra mode have bit 8 of sxfr set */
-#define ULTRA_SXFR 0x100
-#define ST_SXFR 0x010
- u_int8_t period; /* Period to send to SCSI target */
- char *rate;
+#define CFBOOTLUN 0x0f00 /* Lun to boot from */
+#define CFBOOTID 0xf000 /* Target to boot from */
+ uint16_t res_1[10]; /* words 20-29 */
+ uint16_t signature; /* Signature == 0x250 */
+#define CFSIGNATURE 0x250
+#define CFSIGNATURE2 0x300
+ uint16_t checksum; /* word 31 */
};
+/**************************** Message Buffer *********************************/
typedef enum {
MSG_TYPE_NONE = 0x00,
MSG_TYPE_INITIATOR_MSGOUT = 0x01,
@@ -458,71 +978,99 @@ typedef enum {
MSG_TYPE_TARGET_MSGIN = 0x04
} ahc_msg_type;
-struct sg_map_node {
- bus_dmamap_t sg_dmamap;
- bus_addr_t sg_physaddr;
- bus_dma_segment_t sg_dmasegs;
- int sg_nseg;
- struct ahc_dma_seg* sg_vaddr;
- SLIST_ENTRY(sg_map_node) links;
-};
-
-struct scb_data {
- struct hardware_scb *hscbs; /* Array of hardware SCBs */
- struct scb *scbarray; /* Array of kernel SCBs */
- SLIST_HEAD(, scb) free_scbs; /*
- * Pool of SCBs ready to be assigned
- * commands to execute.
- */
- struct scsipi_sense_data *sense; /* Per SCB sense data */
-
- /*
- * "Bus" addresses of our data structures.
- */
- bus_dmamap_t hscb_dmamap;
- bus_addr_t hscb_busaddr;
- bus_dma_segment_t hscb_seg;
- int hscb_nseg;
- int hscb_size;
+typedef enum {
+ MSGLOOP_IN_PROG,
+ MSGLOOP_MSGCOMPLETE,
+ MSGLOOP_TERMINATED
+} msg_loop_stat;
- bus_dmamap_t sense_dmamap;
- bus_addr_t sense_busaddr;
- bus_dma_segment_t sense_seg;
- int sense_nseg;
- int sense_size;
+/*********************** Software Configuration Structure *********************/
+TAILQ_HEAD(scb_tailq, scb);
+
+struct ahc_suspend_channel_state {
+ uint8_t scsiseq;
+ uint8_t sxfrctl0;
+ uint8_t sxfrctl1;
+ uint8_t simode0;
+ uint8_t simode1;
+ uint8_t seltimer;
+ uint8_t seqctl;
+};
- SLIST_HEAD(, sg_map_node) sg_maps;
- u_int8_t numscbs;
- u_int8_t maxhscbs; /* Number of SCBs on the card */
- u_int8_t init_level; /*
- * How far we've initialized
- * this structure.
- */
+struct ahc_suspend_state {
+ struct ahc_suspend_channel_state channel[2];
+ uint8_t optionmode;
+ uint8_t dscommand0;
+ uint8_t dspcistatus;
+ /* hsmailbox */
+ uint8_t crccontrol1;
+ uint8_t scbbaddr;
+ /* Host and sequencer SCB counts */
+ uint8_t dff_thrsh;
+ uint8_t *scratch_ram;
+ uint8_t *btt;
};
+typedef void (*ahc_bus_intr_t)(struct ahc_softc *);
+typedef void ahc_callback_t (void *);
+
struct ahc_softc {
- struct device sc_dev;
+ struct device sc_dev;
+
+ struct scsipi_channel sc_channel;
+ struct scsipi_channel sc_channel_b;
+ struct device * sc_child;
+ struct device * sc_child_b;
+ struct scsipi_adapter sc_adapter;
- struct scsipi_channel sc_channel;
- struct scsipi_channel sc_channel_b;
- struct scsipi_adapter sc_adapter;
+ bus_space_tag_t tag;
+ bus_space_handle_t bsh;
- bus_space_tag_t tag;
- bus_space_handle_t bsh;
- struct scb_data *scb_data;
+ scsipi_adapter_req_t sc_req;
-#define AHC_NEG_PENDING 0x01
-#define AHC_NEG_SDTRDONE 0x02
-#define AHC_NEG_WDTRDONE 0x04
- u_int8_t inited_targets[16];
- u_int8_t inited_channels[2];
+#ifndef __linux__
+ bus_dma_tag_t buffer_dmat; /* dmat for buffer I/O */
+#endif
+ struct scb_data *scb_data;
+
+ struct scb *next_queued_scb;
+
+ /*
+ * SCBs that have been sent to the controller
+ */
+ LIST_HEAD(, scb) pending_scbs;
+
+ /*
+ * Counting lock for deferring the release of additional
+ * untagged transactions from the untagged_queues. When
+ * the lock is decremented to 0, all queues in the
+ * untagged_queues array are run.
+ */
+ u_int untagged_queue_lock;
+
+ /*
+ * Per-target queue of untagged-transactions. The
+ * transaction at the head of the queue is the
+ * currently pending untagged transaction for the
+ * target. The driver only allows a single untagged
+ * transaction per target.
+ */
+ struct scb_tailq untagged_queues[AHC_NUM_TARGETS];
- struct device *child;
+ /*
+ * Platform specific data.
+ */
+ struct ahc_platform_data *platform_data;
/*
- * SCBs that have been send to the controller
+ * Platform specific device information.
*/
- LIST_HEAD(, scb) pending_ccbs;
+ /* ahc_dev_softc_t dev_softc; */
+
+ /*
+ * Bus specific device information.
+ */
+ ahc_bus_intr_t bus_intr;
/*
* Target mode related state kept on a per enabled lun basis.
@@ -530,152 +1078,369 @@ struct ahc_softc {
* As an initiator, we keep one target entry for our initiator
* ID to store our sync/wide transfer settings.
*/
- struct tmode_tstate* enabled_targets[16];
+ struct ahc_tmode_tstate *enabled_targets[AHC_NUM_TARGETS];
+
+ char inited_target[AHC_NUM_TARGETS];
/*
* The black hole device responsible for handling requests for
* disabled luns on enabled targets.
*/
- struct tmode_lstate* black_hole;
+ struct ahc_tmode_lstate *black_hole;
/*
* Device instance currently on the bus awaiting a continue TIO
* for a command that was not given the disconnect priveledge.
*/
- struct tmode_lstate* pending_device;
+ struct ahc_tmode_lstate *pending_device;
/*
* Card characteristics
*/
- ahc_chip chip;
- ahc_feature features;
- ahc_flag flags;
+ ahc_chip chip;
+ ahc_feature features;
+ ahc_bug bugs;
+ ahc_flag flags;
+ struct seeprom_config *seep_config;
/* Values to store in the SEQCTL register for pause and unpause */
- u_int8_t unpause;
- u_int8_t pause;
+ uint8_t unpause;
+ uint8_t pause;
/* Command Queues */
- u_int8_t qoutfifonext;
- u_int8_t qinfifonext;
- u_int8_t *qoutfifo;
- u_int8_t *qinfifo;
+ uint8_t qoutfifonext;
+ uint8_t qinfifonext;
+ uint8_t *qoutfifo;
+ uint8_t *qinfifo;
- /*
- * 256 byte array storing the SCBID of outstanding
- * untagged SCBs indexed by TCL.
- */
- u_int8_t *untagged_scbs;
+ /* Critical Section Data */
+ struct cs *critical_sections;
+ u_int num_critical_sections;
+
+ /* Links for chaining softcs */
+ TAILQ_ENTRY(ahc_softc) links;
/* Channel Names ('A', 'B', etc.) */
- char channel;
- char channel_b;
+ char channel;
/* Initiator Bus ID */
- u_int8_t our_id;
- u_int8_t our_id_b;
-
- /* Targets that need negotiation messages */
- u_int16_t targ_msg_req;
+ uint8_t our_id;
+ uint8_t our_id_b;
/*
- * PCI error detection and data for running the
- * PCI error interrupt handler.
+ * PCI error detection.
*/
- int unsolicited_ints;
+ int unsolicited_ints;
/*
* Target incoming command FIFO.
*/
- struct target_cmd *targetcmds;
- u_int8_t tqinfifonext;
+ struct target_cmd *targetcmds;
+ uint8_t tqinfifonext;
/*
* Incoming and outgoing message handling.
*/
- u_int8_t send_msg_perror;
- ahc_msg_type msg_type;
- u_int8_t msgout_buf[8]; /* Message we are sending */
- u_int8_t msgin_buf[8]; /* Message we are receiving */
- u_int msgout_len; /* Length of message to send */
- u_int msgout_index; /* Current index in msgout */
- u_int msgin_index; /* Current index in msgin */
-
- void *ih;
- bus_dma_tag_t parent_dmat;
- int sc_dmaflags;
- bus_dmamap_t shared_data_dmamap;
- bus_addr_t shared_data_busaddr;
- bus_dma_segment_t shared_data_seg;
- int shared_data_nseg;
- int shared_data_size;
- bus_addr_t dma_bug_buf;
+ uint8_t send_msg_perror;
+ ahc_msg_type msg_type;
+ uint8_t msgout_buf[12];/* Message we are sending */
+ uint8_t msgin_buf[12];/* Message we are receiving */
+ u_int msgout_len; /* Length of message to send */
+ u_int msgout_index; /* Current index in msgout */
+ u_int msgin_index; /* Current index in msgin */
- /* Number of enabled target mode device on this card */
- u_int enabled_luns;
+ /* Interrupt routine */
+ void *ih;
- /* Initialization level of this data structure */
- u_int init_level;
+ /*
+ * Mapping information for data structures shared
+ * between the sequencer and kernel.
+ */
+ bus_dma_tag_t parent_dmat;
+ bus_dmamap_t shared_data_dmamap;
+ bus_addr_t shared_data_busaddr;
- u_int16_t user_discenable;/* Disconnection allowed */
- u_int16_t user_tagenable;/* Tagged Queuing allowed */
+ bus_dma_segment_t shared_data_seg;
+ int shared_data_nseg;
+ int shared_data_size;
+ int sc_dmaflags;
- void *bus_data;
- int (*bus_intr)(struct ahc_softc *);
-};
+ /*
+ * Bus address of the one byte buffer used to
+ * work-around a DMA bug for chips <= aic7880
+ * in target mode.
+ */
+ bus_addr_t dma_bug_buf;
-struct full_ahc_softc {
- struct ahc_softc softc;
- struct scb_data scb_data_storage;
-};
+ /* Information saved through suspend/resume cycles */
+ struct ahc_suspend_state suspend_state;
-/* #define AHC_DEBUG 0x019f */
+ /* Number of enabled target mode device on this card */
+ u_int enabled_luns;
-#ifdef AHC_DEBUG
-/* Different debugging levels used when AHC_DEBUG is defined */
-#define AHC_SHOWMISC 0x0001
-#define AHC_SHOWCMDS 0x0002
-#define AHC_SHOWSCBS 0x0004
-#define AHC_SHOWABORTS 0x0008
-#define AHC_SHOWSENSE 0x0010
-#define AHC_SHOWSCBCNT 0x0020
-#define AHC_SHOWSCBALLOC 0x0040
-#define AHC_SHOWINTR 0x0080
-#define AHC_SHOWMSG 0x0100
-
-extern int ahc_debug; /* Initialized in i386/scsi/aic7xxx.c */
-#endif
+ /* Initialization level of this data structure */
+ u_int init_level;
-char *ahc_name(struct ahc_softc *ahc);
+ /* PCI cacheline size. */
+ u_int pci_cachesize;
-int ahc_alloc(struct ahc_softc *ahc, bus_space_handle_t sh,
- bus_space_tag_t st, bus_dma_tag_t parent_dmat,
- ahc_chip chip, ahc_feature features, ahc_flag flags);
-int ahc_reset(struct ahc_softc *ahc);
-void ahc_free(struct ahc_softc *);
-int ahc_probe_scbs(struct ahc_softc *);
-int ahc_init(struct ahc_softc *);
-int ahc_attach(struct ahc_softc *);
-int ahc_detach(struct ahc_softc *, int);
-int ahc_activate(struct device *, enum devact);
-int ahc_intr(void *arg);
+ u_int stack_size;
-/*
- * SEEPROM related functions.
- */
-void check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+ /* Per-Unit descriptive information */
+ const char *description;
+ char *name;
+ int unit;
+ /* Selection Timer settings */
+ int seltime;
+ int seltime_b;
-#define ahc_inb(ahc, port) \
- bus_space_read_1((ahc)->tag, (ahc)->bsh, port)
+ uint16_t user_discenable;/* Disconnection allowed */
+ uint16_t user_tagenable;/* Tagged Queuing allowed */
-#define ahc_outb(ahc, port, value) \
- bus_space_write_1((ahc)->tag, (ahc)->bsh, port, value)
+ struct ahc_pci_busdata *bd;
-#define ahc_outsb(ahc, port, valp, count) \
- bus_space_write_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count)
+ void *shutdown_hook;
+};
-#define ahc_insb(ahc, port, valp, count) \
- bus_space_read_multi_1((ahc)->tag, (ahc)->bsh, port, valp, count)
+TAILQ_HEAD(ahc_softc_tailq, ahc_softc);
+extern struct ahc_softc_tailq ahc_tailq;
-#endif /* _AIC7XXX_H_ */
+/************************ Active Device Information ***************************/
+typedef enum {
+ ROLE_UNKNOWN,
+ ROLE_INITIATOR,
+ ROLE_TARGET
+} role_t;
+
+struct ahc_devinfo {
+ int our_scsiid;
+ int target_offset;
+ uint16_t target_mask;
+ u_int target;
+ u_int lun;
+ char channel;
+ role_t role; /*
+ * Only guaranteed to be correct if not
+ * in the busfree state.
+ */
+};
+
+/****************************** PCI Structures ********************************/
+typedef int (ahc_device_setup_t)(struct ahc_softc *);
+
+struct ahc_pci_identity {
+ uint64_t full_id;
+ uint64_t id_mask;
+ char *name;
+ ahc_device_setup_t *setup;
+};
+extern struct ahc_pci_identity ahc_pci_ident_table [];
+extern const u_int ahc_num_pci_devs;
+
+/***************************** VL/EISA Declarations ***************************/
+struct aic7770_identity {
+ uint32_t full_id;
+ uint32_t id_mask;
+ char *name;
+ ahc_device_setup_t *setup;
+};
+extern struct aic7770_identity aic7770_ident_table [];
+extern const int ahc_num_aic7770_devs;
+
+#define AHC_EISA_SLOT_OFFSET 0xc00
+#define AHC_EISA_IOSIZE 0x100
+
+/*************************** Function Declarations ****************************/
+/******************************************************************************/
+u_int ahc_index_busy_tcl(struct ahc_softc *ahc, u_int tcl);
+void ahc_unbusy_tcl(struct ahc_softc *ahc, u_int tcl);
+void ahc_busy_tcl(struct ahc_softc *ahc,
+ u_int tcl, u_int busyid);
+
+/***************************** PCI Front End *********************************/
+const struct ahc_pci_identity *ahc_find_pci_device(pcireg_t id, pcireg_t subid, u_int func);
+int ahc_pci_config(struct ahc_softc *,
+ struct ahc_pci_identity *);
+int ahc_pci_test_register_access(struct ahc_softc *);
+
+/*************************** EISA/VL Front End ********************************/
+struct aic7770_identity *aic7770_find_device(uint32_t);
+int aic7770_config(struct ahc_softc *ahc,
+ struct aic7770_identity *,
+ u_int port);
+
+/************************** SCB and SCB queue management **********************/
+int ahc_probe_scbs(struct ahc_softc *);
+void ahc_run_untagged_queues(struct ahc_softc *ahc);
+void ahc_run_untagged_queue(struct ahc_softc *ahc,
+ struct scb_tailq *queue);
+void ahc_qinfifo_requeue_tail(struct ahc_softc *ahc,
+ struct scb *scb);
+int ahc_match_scb(struct ahc_softc *ahc, struct scb *scb,
+ int target, char channel, int lun,
+ u_int tag, role_t role);
+
+/****************************** Initialization ********************************/
+int ahc_softc_init(struct ahc_softc *);
+void ahc_controller_info(struct ahc_softc *ahc, char *buf);
+int ahc_init(struct ahc_softc *ahc);
+void ahc_intr_enable(struct ahc_softc *ahc, int enable);
+void ahc_pause_and_flushwork(struct ahc_softc *ahc);
+int ahc_suspend(struct ahc_softc *ahc);
+int ahc_resume(struct ahc_softc *ahc);
+void ahc_softc_insert(struct ahc_softc *);
+struct ahc_softc *ahc_find_softc(struct ahc_softc *ahc);
+void ahc_set_unit(struct ahc_softc *, int);
+void ahc_set_name(struct ahc_softc *, char *);
+void ahc_alloc_scbs(struct ahc_softc *ahc);
+void ahc_free(struct ahc_softc *ahc);
+int ahc_reset(struct ahc_softc *ahc);
+void ahc_shutdown(void *arg);
+
+/*************************** Interrupt Services *******************************/
+void ahc_pci_intr(struct ahc_softc *ahc);
+void ahc_clear_intstat(struct ahc_softc *ahc);
+void ahc_run_qoutfifo(struct ahc_softc *ahc);
+#ifdef AHC_TARGET_MODE
+void ahc_run_tqinfifo(struct ahc_softc *ahc, int paused);
+#endif
+void ahc_handle_brkadrint(struct ahc_softc *ahc);
+void ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat);
+void ahc_handle_scsiint(struct ahc_softc *ahc,
+ u_int intstat);
+void ahc_clear_critical_section(struct ahc_softc *ahc);
+
+/***************************** Error Recovery *********************************/
+typedef enum {
+ SEARCH_COMPLETE,
+ SEARCH_COUNT,
+ SEARCH_REMOVE
+} ahc_search_action;
+int ahc_search_qinfifo(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status,
+ ahc_search_action action);
+int ahc_search_untagged_queues(struct ahc_softc *ahc,
+ struct scsipi_xfer *xs, /*ahc_io_ctx_t ctx*/
+ int target, char channel,
+ int lun, uint32_t status,
+ ahc_search_action action);
+int ahc_search_disc_list(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ int stop_on_first, int remove,
+ int save_state);
+void ahc_freeze_devq(struct ahc_softc *ahc, struct scb *scb);
+int ahc_reset_channel(struct ahc_softc *ahc, char channel,
+ int initiate_reset);
+int ahc_abort_scbs(struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, uint32_t status);
+void ahc_restart(struct ahc_softc *ahc);
+void ahc_calc_residual(struct ahc_softc *ahc,
+ struct scb *scb);
+/*************************** Utility Functions ********************************/
+struct ahc_phase_table_entry*
+ ahc_lookup_phase_entry(int phase);
+void ahc_compile_devinfo(struct ahc_devinfo *devinfo,
+ u_int our_id, u_int target,
+ u_int lun, char channel,
+ role_t role);
+/************************** Transfer Negotiation ******************************/
+struct ahc_syncrate* ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
+ u_int *ppr_options, u_int maxsync);
+u_int ahc_find_period(struct ahc_softc *ahc,
+ u_int scsirate, u_int maxsync);
+void ahc_validate_offset(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide,
+ role_t role);
+void ahc_validate_width(struct ahc_softc *ahc,
+ struct ahc_initiator_tinfo *tinfo,
+ u_int *bus_width,
+ role_t role);
+/*
+ * Negotiation types. These are used to qualify if we should renegotiate
+ * even if our goal and current transport parameters are identical.
+ */
+typedef enum {
+ AHC_NEG_TO_GOAL, /* Renegotiate only if goal and curr differ. */
+ AHC_NEG_IF_NON_ASYNC, /* Renegotiate so long as goal is non-async. */
+ AHC_NEG_ALWAYS /* Renegotiat even if goal is async. */
+} ahc_neg_type;
+int ahc_update_neg_request(struct ahc_softc*,
+ struct ahc_devinfo*,
+ struct ahc_tmode_tstate*,
+ struct ahc_initiator_tinfo*,
+ ahc_neg_type);
+void ahc_set_width(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused);
+void ahc_set_syncrate(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate,
+ u_int period, u_int offset,
+ u_int ppr_options,
+ u_int type, int paused);
+typedef enum {
+ AHC_QUEUE_NONE,
+ AHC_QUEUE_BASIC,
+ AHC_QUEUE_TAGGED
+} ahc_queue_alg;
+
+void ahc_set_tags(struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ ahc_queue_alg alg);
+
+/**************************** Target Mode *************************************/
+#ifdef AHC_TARGET_MODE
+void ahc_send_lstate_events(struct ahc_softc *,
+ struct ahc_tmode_lstate *);
+void ahc_handle_en_lun(struct ahc_softc *ahc,
+ struct scsipi_xfer *xs
+ /*struct cam_sim *sim, union ccb *ccb*/);
+cam_status ahc_find_tmode_devs(struct ahc_softc *ahc,
+ /*struct cam_sim *sim, union ccb *ccb,*/
+ struct ahc_tmode_tstate **tstate,
+ struct ahc_tmode_lstate **lstate,
+ int notfound_failure);
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
+#endif
+#endif
+/******************************* Debug ***************************************/
+#ifdef AHC_DEBUG
+extern uint32_t ahc_debug;
+#define AHC_SHOW_MISC 0x0001
+#define AHC_SHOW_SENSE 0x0002
+#define AHC_DUMP_SEEPROM 0x0004
+#define AHC_SHOW_TERMCTL 0x0008
+#define AHC_SHOW_MEMORY 0x0010
+#define AHC_SHOW_MESSAGES 0x0020
+#define AHC_SHOW_DV 0x0040
+#define AHC_SHOW_SELTO 0x0080
+#define AHC_SHOW_QFULL 0x0200
+#define AHC_SHOW_QUEUE 0x0400
+#define AHC_SHOW_TQIN 0x0800
+#define AHC_SHOW_MASKED_ERRORS 0x1000
+#define AHC_DEBUG_SEQUENCER 0x2000
+#endif
+void ahc_print_scb(struct scb *scb);
+void ahc_print_devinfo(struct ahc_softc *ahc,
+ struct ahc_devinfo *dev);
+void ahc_dump_card_state(struct ahc_softc *ahc);
+int ahc_print_register(ahc_reg_parse_entry_t *table,
+ u_int num_entries,
+ const char *name,
+ u_int address,
+ u_int value,
+ u_int *cur_column,
+ u_int wrap_point);
+/******************************* SEEPROM *************************************/
+int ahc_acquire_seeprom(struct ahc_softc *ahc,
+ struct seeprom_descriptor *sd);
+void ahc_release_seeprom(struct seeprom_descriptor *sd);
+
+void ahc_check_extport(struct ahc_softc *ahc, u_int *sxfrctl1);
+#endif /* _AIC7XXXVAR_H_ */