summaryrefslogtreecommitdiff
path: root/sys/dev/ic/aic7xxx.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ic/aic7xxx.c')
-rw-r--r--sys/dev/ic/aic7xxx.c7603
1 files changed, 4918 insertions, 2685 deletions
diff --git a/sys/dev/ic/aic7xxx.c b/sys/dev/ic/aic7xxx.c
index 33ef8990af7..782a7a2e61d 100644
--- a/sys/dev/ic/aic7xxx.c
+++ b/sys/dev/ic/aic7xxx.c
@@ -1,27 +1,25 @@
-/* $OpenBSD: aic7xxx.c,v 1.18 1999/01/11 05:12:15 millert Exp $ */
-/* $NetBSD: aic7xxx.c,v 1.17 1996/10/21 22:34:04 thorpej Exp $ */
-
/*
* Generic driver for the aic7xxx based adaptec SCSI controllers
* Product specific probe and attach routines can be found in:
- * i386/eisa/aic7770.c 27/284X and aic7770 motherboard controllers
- * pci/aic7870.c 3940, 2940, aic7880, aic7870 and aic7850 controllers
+ * 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
*
- * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
+ * Copyright (c) 1994, 1995, 1996, 1997, 1998, 1999, 2000 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 immediately at the beginning of the file, without modification,
- * this list of conditions, and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
+ * 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
+ * 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
@@ -34,95 +32,68 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * from Id: aic7xxx.c,v 1.75 1996/06/23 20:02:37 gibbs Exp
+ * $FreeBSD: src/sys/dev/aic7xxx/aic7xxx.c,v 1.40 2000/01/07 23:08:17 gibbs Exp $
+ * $OpenBSD: aic7xxx.c,v 1.19 2000/03/22 02:48:47 smurph Exp $
*/
/*
- * TODO:
- * Implement Target Mode
- *
- * A few notes on how SCB paging works...
+ * A few notes on features of the driver.
*
* 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 that device down on the host adapter is of little use.
- * Instead we copy the SCB 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...
- *
- * At the sequencer level:
- * 1) Disconnected SCBs are threaded onto a doubly linked list, headed by
- * DISCONNECTED_SCBH using the SCB_NEXT and SCB_PREV fields. The most
- * recently disconnected device is always at the head.
- *
- * 2) The SCB has an added field SCB_TAG that corresponds to the kernel
- * SCB number (ie 0-254).
- *
- * 3) When a command is queued, the hardware index of the SCB it was downloaded
- * into is placed into the QINFIFO for easy indexing by the sequencer.
- *
- * 4) The tag field is used as the tag for tagged-queueing, for determining
- * the related kernel SCB, and is the value put into the QOUTFIFO
- * so the kernel doesn't have to upload the SCB to determine the kernel SCB
- * that completed on command completes.
+ * 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...
*
- * 5) When a reconnect occurs, the sequencer must scan the SCB array (even
- * in the tag case) looking for the appropriate SCB and if it can't find
- * it, it interrupts the kernel so it can page the SCB in.
+ * 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.
*
- * 6) If the sequencer is successful in finding the SCB, it removes it from
- * the doubly linked list of disconnected SCBS.
+ * 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.
*
- * At the kernel level:
- * 1) There are four queues that a kernel SCB may reside on:
- * free_scbs - SCBs that are not in use and have a hardware slot assigned
- * to them.
- * page_scbs - SCBs that are not in use and need to have a hardware slot
- * assigned to them (i.e. they will most likely cause a page
- * out event).
- * waiting_scbs - SCBs that are active, don't have an assigned hardware
- * slot assigned to them and are waiting for either a
- * disconnection or a command complete to free up a slot.
- * assigned_scbs - SCBs that were in the waiting_scbs queue, but were
- * assigned a slot by ahc_free_scb.
+ * 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.
*
- * 2) When a new request comes in, an SCB is allocated from the free_scbs or
- * page_scbs queue with preference to SCBs on the free_scbs queue.
- *
- * 3) If there are no free slots (we retrieved the SCB off of the page_scbs
- * queue), the SCB is inserted onto the tail of the waiting_scbs list and
- * we attempt to run this queue down.
- *
- * 4) ahc_run_waiting_queues() looks at both the assigned_scbs and waiting_scbs
- * queues. In the case of the assigned_scbs, the commands are immediately
- * downloaded and started. For waiting_scbs, we page in all that we can
- * ensuring we don't create a resource deadlock (see comments in
- * ahc_run_waiting_queues()).
- *
- * 5) After we handle a bunch of command completes, we also try running the
- * queues since many SCBs may have disconnected since the last command
- * was started and we have at least one free slot on the card.
- *
- * 6) ahc_free_scb looks at the waiting_scbs queue for a transaction
- * requiring a slot and moves it to the assigned_scbs queue if it
- * finds one. Otherwise it puts the current SCB onto the free_scbs
- * queue for later use.
- *
- * 7) The driver handles page-in requests from the sequencer in response to
- * the NO_MATCH sequencer interrupt. For tagged commands, the appropriate
- * SCB is easily found since the tag is a direct index into our kernel SCB
- * array. For non-tagged commands, we keep a separate array of 16 pointers
- * that point to the single possible SCB that was paged out for that target.
*/
#include <sys/param.h>
#include <sys/systm.h>
-#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <sys/device.h>
#include <machine/bus.h>
#include <machine/intr.h>
-#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
#include <sys/malloc.h>
#include <sys/buf.h>
@@ -130,495 +101,1372 @@
#include <scsi/scsi_all.h>
#include <scsi/scsi_message.h>
-#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <scsi/scsi_debug.h>
-#endif
#include <scsi/scsiconf.h>
-#if defined(__FreeBSD__)
-#include <machine/clock.h>
-#endif
-
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
-#if defined(__FreeBSD__)
-#include <i386/scsi/aic7xxx.h>
-
-#include <dev/aic7xxx/aic7xxx_reg.h>
-#endif /* defined(__FreeBSD__) */
-
-#if defined(__NetBSD__) || defined(__OpenBSD__)
#include <dev/ic/aic7xxxreg.h>
#include <dev/ic/aic7xxxvar.h>
+#include <dev/microcode/aic7xxx/aic7xxx_seq.h>
+#include <dev/microcode/aic7xxx/sequencer.h>
+#include "pci.h"
-#define bootverbose 1
+/*
+ * Some ISA devices (e.g. on a VLB) can perform 32-bit DMA. This
+ * flag is passed to bus_dmamap_create() to indicate that fact.
+ */
+#ifndef ISABUS_DMA_32BIT
+#define ISABUS_DMA_32BIT BUS_DMA_BUS1
+#endif
-#define DEBUGTARG DEBUGTARGET
-#if DEBUGTARG < 0 /* Negative numbers for disabling cause warnings */
-#undef DEBUGTARG
-#define DEBUGTARG 17
+#ifndef AHC_TMODE_ENABLE
+#define AHC_TMODE_ENABLE 0
#endif
-#ifdef alpha /* XXX */
-/* XXX XXX NEED REAL DMA MAPPING SUPPORT XXX XXX */
-extern vm_offset_t alpha_XXX_dmamap(vm_offset_t);
-#undef vtophys
-#define vtophys(va) alpha_XXX_dmamap((vm_offset_t) va)
-#endif /* alpha */
-#endif /* defined(__NetBSD__) || defined(__OpenBSD__) */
-
#include <sys/kernel.h>
-#define KVTOPHYS(x) vtophys(x)
+#define offsetof(s, e) ((char *)&((s *)0)->e - (char *)((s *)0))
-#define MIN(a,b) ((a < b) ? a : b)
-#define ALL_TARGETS -1
+#define IS_SCSIBUS_B(ahc, sc_link) \
+ ((sc_link)->scsibus == (ahc)->sc_link_b.scsibus)
+#define ALL_CHANNELS '\0'
+#define ALL_TARGETS_MASK 0xFFFF
+#define INITIATOR_WILDCARD (~0)
-#if defined(__FreeBSD__)
-u_long ahc_unit = 0;
+#define SIM_IS_SCSIBUS_B(ahc, sc_link) \
+ ((sc_link)->scsibus == (ahc)->sc_link_b.scsibus)
+#define SIM_CHANNEL(ahc, sc_link) \
+ (SIM_IS_SCSIBUS_B(ahc, sc_link) ? 'B' : 'A')
+#define SIM_SCSI_ID(ahc, sc_link) \
+ (SIM_IS_SCSIBUS_B(ahc, sc_link) ? 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)->sc_link->target << 4) & 0xF0) \
+ | (SIM_IS_SCSIBUS_B((ahc), (xs)->sc_link) ? SELBUSB : 0) \
+ | ((xs)->sc_link->lun & 0x07))
+
+/*
+ * Under normal circumstances, these messages are unnecessary
+ * and not terribly cosmetic.
+ */
+#ifdef DEBUG
+#define bootverbose 1
+#define STATIC
+#define INLINE
+#else
+#define bootverbose 0
+#define STATIC static
+#define INLINE __inline
#endif
+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
-#ifdef AHC_BROKEN_CACHE
-int ahc_broken_cache = 1;
-
-/*
- * "wbinvd" cause writing back whole cache (both CPU internal & external)
- * to memory, so that the instruction takes a lot of time.
- * This makes machine slow.
- */
-#define INVALIDATE_CACHE() __asm __volatile("wbinvd")
+#if NPCI > 0
+void ahc_pci_intr(struct ahc_softc *ahc);
#endif
-/**** bit definitions for SCSIDEF ****/
-#define HSCSIID 0x07 /* our SCSI ID */
-#define HWSCSIID 0x0f /* our SCSI ID if Wide Bus */
+STATIC int ahcinitscbdata(struct ahc_softc *ahc);
+STATIC void ahcfiniscbdata(struct ahc_softc *ahc);
+
+STATIC int ahc_poll __P((struct ahc_softc *ahc, int wait));
+STATIC void ahc_shutdown __P((void *arg));
+STATIC int ahc_execute_scb __P((void *arg, bus_dma_segment_t *dm_segs,
+ int nsegments));
+STATIC int ahc_setup_data __P((struct ahc_softc *ahc,
+ struct scsi_xfer *xs, struct scb *scb));
+STATIC void ahc_freeze_devq __P((struct ahc_softc *ahc,
+ struct scsi_link *sc_link));
+STATIC void ahcallocscbs __P((struct ahc_softc *ahc));
+STATIC void ahc_fetch_devinfo __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo));
+STATIC void ahc_compile_devinfo __P((struct ahc_devinfo *devinfo,
+ u_int our_id, u_int target,
+ u_int lun, char channel,
+ role_t role));
+STATIC u_int ahc_abort_wscb __P((struct ahc_softc *ahc,
+ u_int scbpos, u_int prev));
+STATIC void ahc_done __P((struct ahc_softc *ahc, struct scb *scbp));
+STATIC struct tmode_tstate *
+ ahc_alloc_tstate __P((struct ahc_softc *ahc, u_int scsi_id,
+ char channel));
+STATIC void ahc_handle_seqint __P((struct ahc_softc *ahc, u_int intstat));
+STATIC void ahc_handle_scsiint __P((struct ahc_softc *ahc, u_int intstat));
+STATIC void ahc_build_transfer_msg __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo));
+STATIC void ahc_setup_initiator_msgout __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct scb *scb));
+STATIC void ahc_setup_target_msgin __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo));
+STATIC int ahc_handle_msg_reject __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo));
+STATIC void ahc_clear_msg_state __P((struct ahc_softc *ahc));
+STATIC void ahc_handle_message_phase __P((struct ahc_softc *ahc,
+ struct scsi_link *sc_link));
+STATIC int ahc_sent_msg __P((struct ahc_softc *ahc, u_int msgtype,
+ int full));
+
+typedef enum {
+ MSGLOOP_IN_PROG,
+ MSGLOOP_MSGCOMPLETE,
+ MSGLOOP_TERMINATED
+} msg_loop_stat;
+
+STATIC int ahc_parse_msg __P((struct ahc_softc *ahc,
+ struct scsi_link *sc_link,
+ struct ahc_devinfo *devinfo));
+STATIC void ahc_handle_ign_wide_residue __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo));
+STATIC void ahc_handle_devreset __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ int status, char *message,
+ int verbose_level));
+#ifdef AHC_DUMP_SEQ
+STATIC void ahc_dumpseq __P((struct ahc_softc *ahc));
+#endif
+STATIC void ahc_loadseq __P((struct ahc_softc *ahc));
+STATIC int ahc_check_patch __P((struct ahc_softc *ahc,
+ struct patch **start_patch,
+ int start_instr, int *skip_addr));
+STATIC void ahc_download_instr __P((struct ahc_softc *ahc,
+ int instrptr, u_int8_t *dconsts));
+STATIC int ahc_match_scb __P((struct scb *scb, int target, char channel,
+ int lun, u_int tag, role_t role));
+#ifdef AHC_DEBUG
+STATIC void ahc_print_scb __P((struct scb *scb));
+#endif
+STATIC int ahc_search_qinfifo __P((struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, u_int32_t status,
+ ahc_search_action action));
+STATIC int ahc_reset_channel __P((struct ahc_softc *ahc, char channel,
+ int initiate_reset));
+STATIC int ahc_abort_scbs __P((struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ role_t role, u_int32_t status));
+STATIC int ahc_search_disc_list __P((struct ahc_softc *ahc, int target,
+ char channel, int lun, u_int tag,
+ int stop_on_first, int remove,
+ int save_state));
+STATIC u_int ahc_rem_scb_from_disc_list __P((struct ahc_softc *ahc,
+ u_int prev, u_int scbptr));
+STATIC void ahc_add_curscb_to_free_list __P((struct ahc_softc *ahc));
+STATIC void ahc_clear_intstat __P((struct ahc_softc *ahc));
+STATIC void ahc_reset_current_bus __P((struct ahc_softc *ahc));
+STATIC struct ahc_syncrate *
+ ahc_devlimited_syncrate __P((struct ahc_softc *ahc, u_int *period));
+STATIC struct ahc_syncrate *
+ ahc_find_syncrate __P((struct ahc_softc *ahc, u_int *period,
+ u_int maxsync));
+STATIC u_int ahc_find_period __P((struct ahc_softc *ahc, u_int scsirate,
+ u_int maxsync));
+STATIC void ahc_validate_offset __P((struct ahc_softc *ahc,
+ struct ahc_syncrate *syncrate,
+ u_int *offset, int wide));
+STATIC void ahc_update_target_msg_request __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct ahc_initiator_tinfo *tinfo,
+ int force, int paused));
+STATIC void ahc_set_syncrate __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ struct ahc_syncrate *syncrate,
+ u_int period, u_int offset,
+ u_int type, int paused, int done));
+STATIC void ahc_set_width __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,
+ u_int width, u_int type, int paused, int done));
+STATIC void ahc_set_tags __P((struct ahc_softc *ahc,
+ struct ahc_devinfo *devinfo,int enable));
+STATIC void ahc_construct_sdtr __P((struct ahc_softc *ahc,
+ u_int period, u_int offset));
+STATIC void ahc_construct_wdtr __P((struct ahc_softc *ahc, u_int bus_width));
+
+STATIC void ahc_calc_residual __P((struct scb *scb));
+
+STATIC void ahc_update_pending_syncrates __P((struct ahc_softc *ahc));
+
+STATIC void ahc_set_recoveryscb __P((struct ahc_softc *ahc,
+ struct scb *scb));
+STATIC void ahc_timeout __P((void *));
+
+static __inline int sequencer_paused __P((struct ahc_softc *ahc));
+static __inline void pause_sequencer __P((struct ahc_softc *ahc));
+static __inline void unpause_sequencer __P((struct ahc_softc *ahc));
+STATIC void restart_sequencer __P((struct ahc_softc *ahc));
+static __inline u_int ahc_index_busy_tcl __P((struct ahc_softc *ahc,
+ u_int tcl, int unbusy));
+
+static __inline void ahc_busy_tcl __P((struct ahc_softc *ahc,
+ struct scb *scb));
+static __inline int ahc_isbusy_tcl __P((struct ahc_softc *ahc,
+ struct scb *scb));
+static __inline void ahc_freeze_ccb __P((struct scb* scb));
+static __inline void ahcsetccbstatus __P((struct scsi_xfer *xs, int status));
+STATIC void ahc_run_qoutfifo __P((struct ahc_softc *ahc));
+
+static __inline struct ahc_initiator_tinfo *
+ ahc_fetch_transinfo __P((struct ahc_softc *ahc, char channel,
+ u_int our_id, u_int target,
+ struct tmode_tstate **tstate));
+STATIC void ahcfreescb __P((struct ahc_softc *ahc, struct scb *scb));
+static __inline struct scb *ahcgetscb __P((struct ahc_softc *ahc));
+int ahc_createdmamem __P((struct ahc_softc *ahc, int size,
+ bus_dmamap_t *mapp, caddr_t *vaddr,
+ bus_addr_t *baddr, bus_dma_segment_t *segs,
+ int *nseg, const char *what));
+STATIC void ahc_freedmamem __P((bus_dma_tag_t tag, int size,
+ bus_dmamap_t map, caddr_t vaddr,
+ bus_dma_segment_t *seg, int nseg));
+STATIC void ahcminphys __P((struct buf *bp));
+
+STATIC INLINE struct scsi_xfer *ahc_first_xs __P((struct ahc_softc *));
+STATIC INLINE void ahc_list_insert_before __P((struct ahc_softc *ahc,
+ struct scsi_xfer *xs,
+ struct scsi_xfer *next_xs));
+STATIC INLINE void ahc_list_insert_head __P((struct ahc_softc *ahc,
+ struct scsi_xfer *xs));
+STATIC INLINE void ahc_list_insert_tail __P((struct ahc_softc *ahc,
+ struct scsi_xfer *xs));
+STATIC INLINE void ahc_list_remove __P((struct ahc_softc *ahc,
+ struct scsi_xfer *xs));
+STATIC INLINE struct scsi_xfer *ahc_list_next __P((struct ahc_softc *ahc,
+ struct scsi_xfer *xs));
+
+STATIC int32_t ahc_scsi_cmd __P((struct scsi_xfer *xs));
-static void ahcminphys __P((struct buf *bp));
-static int32_t ahc_scsi_cmd __P((struct scsi_xfer *xs));
-static inline void pause_sequencer __P((struct ahc_data *ahc));
-static inline void unpause_sequencer __P((struct ahc_data *ahc,
- int unpause_always));
-static inline void restart_sequencer __P((struct ahc_data *ahc));
+struct cfdriver ahc_cd = {
+ NULL, "ahc", DV_DULL
+};
static struct scsi_adapter ahc_switch =
{
- ahc_scsi_cmd,
- ahcminphys,
- 0,
- 0,
-#if defined(__FreeBSD__)
- 0,
- "ahc",
- { 0, 0 }
-#endif
+ ahc_scsi_cmd,
+ ahcminphys,
+ 0,
+ 0,
};
/* the below structure is so we have a default dev struct for our link struct */
static struct scsi_device ahc_dev =
{
- NULL, /* Use default error handler */
- NULL, /* have a queue, served by this */
- NULL, /* have no async handler */
- NULL, /* Use default 'done' routine */
-#if defined(__FreeBSD__)
- "ahc",
- 0,
- { 0, 0 }
-#endif
+ NULL, /* Use default error handler */
+ NULL, /* have a queue, served by this */
+ NULL, /* have no async handler */
+ NULL, /* Use default 'done' routine */
};
-static inline void
+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_NSEG - 1) * PAGE_SIZE)) {
+ bp->b_bcount = ((AHC_NSEG - 1) * PAGE_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(ahc)
+ struct ahc_softc *ahc;
+{
+ return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
+}
+
+static __inline void
pause_sequencer(ahc)
- struct ahc_data *ahc;
+ struct ahc_softc *ahc;
{
- AHC_OUTB(ahc, HCNTRL, ahc->pause);
+ 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_INB(ahc, HCNTRL) & PAUSE) == 0)
+ while (sequencer_paused(ahc) == 0)
;
}
-static inline void
-unpause_sequencer(ahc, unpause_always)
- struct ahc_data *ahc;
- int unpause_always;
+static __inline void
+unpause_sequencer(ahc)
+ struct ahc_softc *ahc;
{
- if (unpause_always
- ||(AHC_INB(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
- AHC_OUTB(ahc, HCNTRL, ahc->unpause);
+ if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
+ ahc_outb(ahc, HCNTRL, ahc->unpause);
}
/*
* Restart the sequencer program from address zero
*/
-static inline void
+STATIC void
restart_sequencer(ahc)
- struct ahc_data *ahc;
+ struct ahc_softc *ahc;
{
- do {
- AHC_OUTB(ahc, SEQCTL, SEQRESET|FASTMODE);
- } while((AHC_INB(ahc, SEQADDR0) != 0)
- || (AHC_INB(ahc, SEQADDR1) != 0));
+ 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(ahc, tcl, unbusy)
+ 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->sc_dmat, ahc->shared_data_dmamap,
+ BUS_DMASYNC_PREWRITE);
+ }
- unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ return (scbid);
+}
+
+static __inline void
+ahc_busy_tcl(ahc, scb)
+ struct ahc_softc *ahc;
+ struct scb *scb;
+{
+ ahc->untagged_scbs[scb->hscb->tcl] = scb->hscb->tag;
+ bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap,
+ BUS_DMASYNC_PREWRITE);
}
-#if defined(__NetBSD__) || defined(__OpenBSD__)
+static __inline int
+ahc_isbusy_tcl(ahc, scb)
+ struct ahc_softc *ahc;
+ struct scb *scb;
+{
+ return ahc->untagged_scbs[scb->hscb->tcl] != SCB_LIST_NULL;
+}
+
+static __inline void
+ahc_freeze_ccb(scb)
+ struct scb *scb;
+{
+ struct scsi_xfer *xs = scb->xs;
+ struct ahc_softc *ahc = (struct ahc_softc *)xs->sc_link->adapter_softc;
+ int target;
+
+ target = xs->sc_link->target;
+ if (!(scb->flags & SCB_FREEZE_QUEUE)) {
+ ahc->devqueue_blocked[target]++;
+ scb->flags |= SCB_FREEZE_QUEUE;
+ }
+}
+
+static __inline void
+ahcsetccbstatus(xs, status)
+ struct scsi_xfer *xs;
+ int status;
+{
+ xs->error = status;
+}
+
+static __inline struct ahc_initiator_tinfo *
+ahc_fetch_transinfo(ahc, channel, our_id, remote_id, tstate)
+ 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(ahc)
+ struct ahc_softc *ahc;
+{
+ struct scb *scb;
+ u_int scb_index;
+
+ bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap,
+ 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;
+ ahc_done(ahc, scb);
+ }
+}
+
+
/*
- * Is device which is pointed by sc_link connected on second scsi bus ?
+ * An scb (and hence an scb entry on the board) is put onto the
+ * free list.
*/
-#define IS_SCSIBUS_B(ahc, sc_link) \
- ((sc_link)->scsibus == (ahc)->sc_link_b.scsibus)
+STATIC void
+ahcfreescb(ahc, scb)
+ struct ahc_softc *ahc;
+ struct scb *scb;
+{
+ struct hardware_scb *hscb;
+ int opri;
+
+ hscb = scb->hscb;
+ opri = splbio();
+
+ if ((ahc->flags & AHC_RESOURCE_SHORTAGE) != 0 ||
+ (scb->flags & SCB_RECOVERY_SCB) != 0) {
+ ahc->flags &= ~AHC_RESOURCE_SHORTAGE;
+ ahc->queue_blocked = 0;
+ }
+
+ /* 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);
+}
/*
- * convert FreeBSD's SCSI symbols to Net- & OpenBSD's
+ * 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.
*/
-#define SCSI_NOMASK SCSI_POLL
-#define opennings openings
-#endif
+static __inline struct scb *
+ahcgetscb(ahc)
+ struct ahc_softc *ahc;
+{
+ struct scb *scbp;
+ int opri;
-static u_char ahc_abort_wscb __P((struct ahc_data *ahc, struct scb *scbp,
- u_char prev,
- u_char timedout_scb, u_int32_t xs_error));
-static void ahc_add_waiting_scb __P((struct ahc_data *ahc,
- struct scb *scb));
-static void ahc_done __P((struct ahc_data *ahc, struct scb *scbp));
-static void ahc_free_scb __P((struct ahc_data *ahc, struct scb *scb,
- int flags));
-static inline void ahc_send_scb __P((struct ahc_data *ahc, struct scb *scb));
-static inline void ahc_fetch_scb __P((struct ahc_data *ahc, struct scb *scb));
-static inline void ahc_page_scb __P((struct ahc_data *ahc, struct scb *out_scb,
- struct scb *in_scb));
-static inline void ahc_run_waiting_queues __P((struct ahc_data *ahc));
-static void ahc_handle_seqint __P((struct ahc_data *ahc, u_int8_t intstat));
-static struct scb *
- ahc_get_scb __P((struct ahc_data *ahc, int flags));
-static void ahc_loadseq __P((struct ahc_data *ahc));
-static int ahc_match_scb __P((struct scb *scb, int target, char channel));
-static int ahc_poll __P((struct ahc_data *ahc, int wait));
-#ifdef AHC_DEBUG
-static void ahc_print_scb __P((struct scb *scb));
-#endif
-static int ahc_reset_channel __P((struct ahc_data *ahc, char channel,
- u_char timedout_scb, u_int32_t xs_error,
- u_char initiate_reset));
-static int ahc_reset_device __P((struct ahc_data *ahc, int target,
- char channel, u_char timedout_scb,
- u_int32_t xs_error));
-static void ahc_reset_current_bus __P((struct ahc_data *ahc));
-static void ahc_run_done_queue __P((struct ahc_data *ahc));
-static void ahc_scsirate __P((struct ahc_data* ahc, u_int8_t *scsirate,
- u_int8_t *period, u_int8_t *offset,
- char channel, int target));
-#if defined(__FreeBSD__)
-static timeout_t
- ahc_timeout;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
-static void ahc_timeout __P((void *));
-#endif
-static void ahc_busy_target __P((struct ahc_data *ahc,
- int target, char channel));
-static void ahc_unbusy_target __P((struct ahc_data *ahc,
- int target, char channel));
-static void ahc_construct_sdtr __P((struct ahc_data *ahc, int start_byte,
- u_int8_t period, u_int8_t offset));
-static void ahc_construct_wdtr __P((struct ahc_data *ahc, int start_byte,
- u_int8_t bus_width));
-
-#if defined(__NetBSD__) || defined(__OpenBSD__) /* XXX */
-static void ahc_xxx_enqueue __P((struct ahc_data *ahc,
- struct scsi_xfer *xs, int infront));
-static struct scsi_xfer *ahc_xxx_dequeue __P((struct ahc_data *ahc));
-#endif
+ 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);
+ }
-#if defined(__FreeBSD__)
+ splx(opri);
+
+ return (scbp);
+}
-char *ahc_name(ahc)
- struct ahc_data *ahc;
+int
+ahc_createdmamem(ahc, size, mapp, vaddr, baddr, seg, nseg, what)
+ struct ahc_softc *ahc;
+ int size;
+ bus_dmamap_t *mapp;
+ caddr_t *vaddr;
+ bus_addr_t *baddr;
+ bus_dma_segment_t *seg;
+ int *nseg;
+ const char *what;
{
- static char name[10];
+ int error, rseg, level = 0;
+ int dma_flags = BUS_DMA_NOWAIT;
+ bus_dma_tag_t tag = ahc->sc_dmat;
+ const char *myname = ahc_name(ahc);
+ if ((ahc->chip & AHC_VL) !=0)
+ dma_flags |= ISABUS_DMA_32BIT;
+
+ if ((error = bus_dmamem_alloc(tag, size, NBPG, 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,
+ dma_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 = seg[0].ds_addr;
+
+ if (bootverbose)
+ printf("%s: dmamem for %s at phys %lx virt %lx nseg %d size %d\n",
+ myname, what, (unsigned long)*baddr,
+ (unsigned long)*vaddr, *nseg, size);
+ 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, rseg);
+ break;
+ default:
+ break;
+ }
- sprintf(name, "ahc%d", ahc->unit);
- return (name);
+ return error;
}
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
-struct cfdriver ahc_cd = {
- NULL, "ahc", DV_DULL
-};
-#endif
+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);
+}
#ifdef AHC_DEBUG
-static void
+STATIC void
ahc_print_scb(scb)
- struct scb *scb;
-{
- printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n"
- ,scb
- ,scb->control
- ,scb->tcl
- ,scb->cmdlen
- ,scb->cmdpointer );
- printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n"
- ,scb->datalen
- ,scb->data
- ,scb->SG_segment_count
- ,scb->SG_list_pointer);
- printf(" sg_addr:%lx sg_len:%ld\n"
- ,scb->ahc_dma[0].addr
- ,scb->ahc_dma[0].len);
+ struct scb *scb;
+{
+ struct hardware_scb *hscb = scb->hscb;
+
+ printf("scb:%p control:0x%x tcl:0x%x cmdlen:%d cmdpointer:0x%lx\n",
+ scb,
+ hscb->control,
+ hscb->tcl,
+ hscb->cmdlen,
+ hscb->cmdpointer );
+ printf(" datlen:%d data:0x%lx segs:0x%x segp:0x%lx\n",
+ hscb->datalen,
+ hscb->data,
+ hscb->SG_count,
+ hscb->SG_pointer);
+ printf(" sg_addr:%lx sg_len:%ld\n",
+ scb->sg_list[0].addr,
+ 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
static struct {
- u_char errno;
+ u_int8_t errno;
char *errmesg;
} hard_error[] = {
- { ILLHADDR, "Illegal Host Access" },
- { ILLSADDR, "Illegal Sequencer Address referenced" },
- { ILLOPCODE, "Illegal Opcode in sequencer program" },
- { PARERR, "Sequencer Ram Parity Error" }
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referrenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { SQPARERR, "Sequencer Parity Error" },
+ { DPARERR, "Data-path Parity Error" },
+ { MPARERR, "Scratch or SCB Memory Parity Error" },
+ { PCIERRSTAT, "PCI Error detected" },
+ { CIOPARERR, "CIOBUS Parity Error" },
};
+static const int num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
+static struct {
+ u_int8_t phase;
+ u_int8_t mesg_out; /* Message response to parity errors */
+ char *phasemsg;
+} phase_table[] = {
+ { P_DATAOUT, MSG_NOOP, "in Data-out phase" },
+ { P_DATAIN, MSG_INITIATOR_DET_ERR, "in 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" },
+ { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
+ { 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;
/*
* Valid SCSIRATE values. (p. 3-17)
* Provides a mapping of tranfer periods in ns to the proper value to
* stick in the scsiscfr reg to use that transfer rate.
*/
-static struct {
- short sxfr;
- /* Rates in Ultra mode have bit 8 of sxfr set */
-#define ULTRA_SXFR 0x100
- int period; /* in ns/4 */
- char *rate;
-} ahc_syncrates[] = {
- { 0x100, 12, "20.0" },
- { 0x110, 15, "16.0" },
- { 0x120, 18, "13.4" },
- { 0x000, 25, "10.0" },
- { 0x010, 31, "8.0" },
- { 0x020, 37, "6.67" },
- { 0x030, 43, "5.7" },
- { 0x040, 50, "5.0" },
- { 0x050, 56, "4.4" },
- { 0x060, 62, "4.0" },
- { 0x070, 68, "3.6" }
+#define AHC_SYNCRATE_DT 0
+#define AHC_SYNCRATE_ULTRA2 1
+#define AHC_SYNCRATE_ULTRA 2
+#define AHC_SYNCRATE_FAST 5
+static struct ahc_syncrate ahc_syncrates[] = {
+ /* ultra2 fast/ultra period rate */
+ { 0x42, 0x000, 9, "80.0" },
+ { 0x03, 0x000, 10, "40.0" },
+ { 0x04, 0x000, 11, "33.0" },
+ { 0x05, 0x100, 12, "20.0" },
+ { 0x06, 0x110, 15, "16.0" },
+ { 0x07, 0x120, 18, "13.4" },
+ { 0x08, 0x000, 25, "10.0" },
+ { 0x19, 0x010, 31, "8.0" },
+ { 0x1a, 0x020, 37, "6.67" },
+ { 0x1b, 0x030, 43, "5.7" },
+ { 0x1c, 0x040, 50, "5.0" },
+ { 0x00, 0x050, 56, "4.4" },
+ { 0x00, 0x060, 62, "4.0" },
+ { 0x00, 0x070, 68, "3.6" },
+ { 0x00, 0x000, 0, NULL }
};
-static int ahc_num_syncrates =
- sizeof(ahc_syncrates) / sizeof(ahc_syncrates[0]);
-
/*
* Allocate a controller structure for a new device and initialize it.
* ahc_reset should be called before now since we assume that the card
* is paused.
*/
-#if defined(__FreeBSD__)
-struct ahc_data *
-ahc_alloc(unit, iobase, type, flags)
- int unit;
- u_long iobase;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
void
-ahc_construct(ahc, iot, ioh, type, flags)
- struct ahc_data *ahc;
+ahc_construct(ahc, iot, ioh, chip, flags, features, channel)
+ struct ahc_softc *ahc;
bus_space_tag_t iot;
bus_space_handle_t ioh;
-#endif
- ahc_type type;
+ ahc_chip chip;
ahc_flag flags;
+ ahc_feature features;
+ u_char channel;
{
-
/*
* find unit and check we have that many defined
*/
-
-#if defined(__FreeBSD__)
- struct ahc_data *ahc;
-
- /*
- * Allocate a storage area for us
- */
-
- ahc = malloc(sizeof(struct ahc_data), M_TEMP, M_NOWAIT);
- if (!ahc) {
- printf("ahc%d: cannot malloc!\n", unit);
- return NULL;
- }
- bzero(ahc, sizeof(struct ahc_data));
-#endif
- STAILQ_INIT(&ahc->free_scbs);
- STAILQ_INIT(&ahc->page_scbs);
- STAILQ_INIT(&ahc->waiting_scbs);
- STAILQ_INIT(&ahc->assigned_scbs);
-#if defined(__FreeBSD__)
- ahc->unit = unit;
-#endif
-#if defined(__FreeBSD__)
- ahc->baseport = iobase;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ LIST_INIT(&ahc->pending_scbs);
ahc->sc_iot = iot;
ahc->sc_ioh = ioh;
-#endif
- ahc->type = type;
+ ahc->chip = chip;
ahc->flags = flags;
- ahc->unpause = (AHC_INB(ahc, HCNTRL) & IRQMS) | INTEN;
+ ahc->features = features;
+ ahc->channel = channel;
+ ahc->scb_data = NULL;
+ ahc->pci_intr_func = NULL;
+
+ 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;
-
-#if defined(__FreeBSD__)
- return (ahc);
-#endif
}
void
ahc_free(ahc)
- struct ahc_data *ahc;
+ struct ahc_softc *ahc;
{
-#if defined(__FreeBSD__)
- free(ahc, M_DEVBUF);
+ ahcfiniscbdata(ahc);
+ if (ahc->init_level != 0)
+ ahc_freedmamem(ahc->sc_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->pci_data != NULL)
+ free(ahc->pci_data, M_DEVBUF);
return;
-#endif
+}
+
+STATIC int
+ahcinitscbdata(ahc)
+ 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);
+ if (scb_data->scbarray == NULL)
+ return (ENOMEM);
+ bzero(scb_data->scbarray, sizeof(struct scb) * AHC_SCB_MAX);
+
+ /* 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
+ * accessable 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,
+ AHC_SCB_MAX * sizeof(struct hardware_scb),
+ &scb_data->hscb_dmamap, (caddr_t *)&scb_data->hscbs,
+ &scb_data->hscb_busaddr, &scb_data->hscb_seg,
+ &scb_data->hscb_nseg, "hardware SCB structures") < 0)
+ goto error_exit;
+
+ scb_data->init_level++;
+
+ if (ahc_createdmamem(ahc,
+ AHC_SCB_MAX * sizeof(struct scsi_sense_data),
+ &scb_data->sense_dmamap, (caddr_t *)&scb_data->sense,
+ &scb_data->sense_busaddr, &scb_data->sense_seg,
+ &scb_data->sense_nseg, "sense buffers") < 0)
+ goto error_exit;
+
+ scb_data->init_level++;
+
+ /* Perform initial CCB allocation */
+ bzero(scb_data->hscbs, 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 successfull
+ */
+ return 0;
+
+error_exit:
+
+ return ENOMEM;
+}
+
+STATIC void
+ahcfiniscbdata(ahc)
+ 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->sc_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->sc_dmat,
+ AHC_SCB_MAX * sizeof(struct scsi_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->sc_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
-#if defined(__FreeBSD__)
-ahc_reset(iobase)
- u_long iobase;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
-ahc_reset(devname, iot, ioh)
+ahc_xxx_reset(devname, iot, ioh)
char *devname;
bus_space_tag_t iot;
bus_space_handle_t ioh;
-#endif
{
- u_char hcntrl;
+ u_char hcntrl;
int wait;
+#ifdef AHC_DUMP_SEQ
+ ahc_dumpseq(ahc);
+#endif
/* Retain the IRQ type accross the chip reset */
-#if defined(__FreeBSD__)
- hcntrl = (inb(HCNTRL + iobase) & IRQMS) | INTEN;
-
- outb(HCNTRL + iobase, CHIPRST | PAUSE);
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
hcntrl = (bus_space_read_1(iot, ioh, HCNTRL) & IRQMS) | INTEN;
bus_space_write_1(iot, ioh, HCNTRL, CHIPRST | PAUSE);
-#endif
/*
* Ensure that the reset has finished
*/
wait = 1000;
-#if defined(__FreeBSD__)
- while (--wait && !(inb(HCNTRL + iobase) & CHIPRSTACK))
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
while (--wait && !(bus_space_read_1(iot, ioh, HCNTRL) & CHIPRSTACK))
-#endif
DELAY(1000);
- if(wait == 0) {
-#if defined(__FreeBSD__)
- printf("ahc at 0x%lx: WARNING - Failed chip reset! "
- "Trying to initialize anyway.\n", iobase);
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ if (wait == 0) {
printf("%s: WARNING - Failed chip reset! "
- "Trying to initialize anyway.\n", devname);
-#endif
+ "Trying to initialize anyway.\n", devname);
}
-#if defined(__FreeBSD__)
- outb(HCNTRL + iobase, hcntrl | PAUSE);
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
bus_space_write_1(iot, ioh, HCNTRL, hcntrl | PAUSE);
+}
+
+int
+ahc_reset(ahc)
+ struct ahc_softc *ahc;
+{
+ u_int sblkctl;
+ int wait;
+
+#ifdef AHC_DUMP_SEQ
+ if (ahc->init_level == 0)
+ ahc_dumpseq(ahc);
#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 struct ahc_syncrate *
+ahc_devlimited_syncrate(ahc, period)
+ 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));
}
/*
* 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.
*/
-static void
-ahc_scsirate(ahc, scsirate, period, offset, channel, target )
- struct ahc_data *ahc;
- u_int8_t *scsirate;
- u_int8_t *period;
- u_int8_t *offset;
- char channel;
- int target;
+STATIC struct ahc_syncrate *
+ahc_find_syncrate(ahc, period, maxsync)
+ struct ahc_softc *ahc;
+ u_int *period;
+ u_int maxsync;
{
- int i;
- u_int32_t ultra_enb_addr;
- u_int8_t sxfrctl0;
- u_int8_t ultra_enb;
+ struct ahc_syncrate *syncrate;
- i = ahc_num_syncrates; /* Default to async */
-
- if (*period >= ahc_syncrates[0].period && *offset != 0) {
- for (i = 0; i < ahc_num_syncrates; i++) {
+ syncrate = &ahc_syncrates[maxsync];
+ while ((syncrate->rate != NULL)
+ && ((ahc->features & AHC_ULTRA2) == 0
+ || (syncrate->sxfr_u2 != 0))) {
- if (*period <= ahc_syncrates[i].period) {
- /*
- * Watch out for Ultra speeds when ultra is not
- * enabled and vice-versa.
- */
- if(!(ahc->type & AHC_ULTRA)
- && (ahc_syncrates[i].sxfr & ULTRA_SXFR)) {
- /*
- * This should only happen if the
- * drive is the first to negotiate
- * and chooses a high rate. We'll
- * just move down the table util
- * we hit a non ultra speed.
- */
- continue;
- }
- *scsirate = (ahc_syncrates[i].sxfr & 0xF0)
- | (*offset & 0x0f);
- *period = ahc_syncrates[i].period;
-
- if(bootverbose) {
- printf("%s: target %d synchronous at %sMHz,"
- " offset = 0x%x\n",
- ahc_name(ahc), target,
- ahc_syncrates[i].rate, *offset );
- }
- 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;
+ break;
}
+ syncrate++;
}
- if (i >= ahc_num_syncrates) {
+
+ if ((*period == 0)
+ || (syncrate->rate == NULL)
+ || ((ahc->features & AHC_ULTRA2) != 0
+ && (syncrate->sxfr_u2 == 0))) {
/* Use asynchronous transfers. */
- *scsirate = 0;
*period = 0;
- *offset = 0;
- if (bootverbose)
- printf("%s: target %d using asynchronous transfers\n",
- ahc_name(ahc), target );
+ syncrate = NULL;
+ }
+ return (syncrate);
+}
+
+STATIC u_int
+ahc_find_period(ahc, scsirate, maxsync)
+ 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 */
+}
+
+STATIC void
+ahc_validate_offset(ahc, syncrate, offset, wide)
+ struct ahc_softc *ahc;
+ struct ahc_syncrate *syncrate;
+ u_int *offset;
+ int wide;
+{
+ 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);
+}
+
+STATIC void
+ahc_update_target_msg_request(ahc, devinfo, tinfo, force, paused)
+ 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;
+
+ 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);
+ }
+ }
+}
+
+STATIC void
+ahc_set_syncrate(ahc, devinfo, syncrate, period, offset, type, paused, done)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int offset;
+ u_int type;
+ int paused;
+ int done;
+{
+ 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) {
+
+ /* 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;
+
+ if (active)
+ ahc_outb(ahc, SCSIOFFSET, offset);
+ } 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);
+
+ tinfo->scsirate = scsirate;
+ tinfo->current.period = period;
+ tinfo->current.offset = offset;
+
+ /* Update the syncrates in any pending scbs */
+ ahc_update_pending_syncrates(ahc);
+ }
+
/*
- * Ensure Ultra mode is set properly for
- * this target.
+ * Print messages if we're verbose and at the end of a negotiation
+ * cycle.
*/
- ultra_enb_addr = ULTRA_ENB;
- if(channel == 'B' || target > 7)
- ultra_enb_addr++;
- ultra_enb = AHC_INB(ahc, ultra_enb_addr);
- sxfrctl0 = AHC_INB(ahc, SXFRCTL0);
- if (*scsirate != 0 && ahc_syncrates[i].sxfr & ULTRA_SXFR) {
- ultra_enb |= 0x01 << (target & 0x07);
- sxfrctl0 |= ULTRAEN;
+ if (done && bootverbose) {
+ if (offset != 0) {
+ printf("%s: target %d synchronous at %sMHz, "
+ "offset = 0x%x\n", ahc_name(ahc),
+ devinfo->target, syncrate->rate, offset);
+ } else {
+ printf("%s: target %d using "
+ "asynchronous transfers\n",
+ ahc_name(ahc), devinfo->target);
+ }
+ }
+
+ if ((type & AHC_TRANS_GOAL) != 0) {
+ tinfo->goal.period = period;
+ tinfo->goal.offset = offset;
+ }
+
+ if ((type & AHC_TRANS_USER) != 0) {
+ tinfo->user.period = period;
+ tinfo->user.offset = offset;
+ }
+
+ ahc_update_target_msg_request(ahc, devinfo, tinfo,
+ /*force*/FALSE,
+ paused);
+}
+
+STATIC void
+ahc_set_width(ahc, devinfo, width, type, paused, done)
+ 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;
}
- else {
- ultra_enb &= ~(0x01 << (target & 0x07));
- sxfrctl0 &= ~ULTRAEN;
+
+ if (done) {
+ printf("%s: target %d using %dbit transfers\n",
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << width));
}
- AHC_OUTB(ahc, ultra_enb_addr, ultra_enb);
- AHC_OUTB(ahc, SXFRCTL0, sxfrctl0);
+
+ 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(ahc, devinfo, enable)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
+ int enable;
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct 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;
}
/*
@@ -626,18 +1474,12 @@ ahc_scsirate(ahc, scsirate, period, offset, channel, target )
*/
int
ahc_attach(ahc)
- struct ahc_data *ahc;
+ struct ahc_softc *ahc;
{
-#if defined(__FreeBSD__)
- struct scsibus_data *scbus;
-#endif
-
-#if defined(__NetBSD__) || defined(__OpenBSD__) /* XXX */
/*
* Initialize the software queue.
*/
LIST_INIT(&ahc->sc_xxxq);
-#endif
#ifdef AHC_BROKEN_CACHE
if (cpu_class == CPUCLASS_386) /* doesn't have "wbinvd" instruction */
@@ -646,88 +1488,24 @@ ahc_attach(ahc)
/*
* fill in the prototype scsi_links.
*/
-#if defined(__FreeBSD__)
- ahc->sc_link.adapter_unit = ahc->unit;
- ahc->sc_link.adapter_targ = ahc->our_id;
- ahc->sc_link.fordriver = 0;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
ahc->sc_link.adapter_target = ahc->our_id;
-#ifdef __OpenBSD__
- if(ahc->type & AHC_WIDE)
+ if (ahc->features & AHC_WIDE)
ahc->sc_link.adapter_buswidth = 16;
-#endif
-#ifndef __OpenBSD__
- ahc->sc_link.channel = 0;
-#endif
-#endif
ahc->sc_link.adapter_softc = ahc;
ahc->sc_link.adapter = &ahc_switch;
- ahc->sc_link.opennings = 2;
+ ahc->sc_link.openings = 2;
ahc->sc_link.device = &ahc_dev;
-#ifdef __OpenBSD__
ahc->sc_link.flags = SCSIDEBUG_LEVEL;
-#else
- ahc->sc_link.flags = DEBUGLEVEL;
-#endif
-
- if(ahc->type & AHC_TWIN) {
+
+ if (ahc->features & AHC_TWIN) {
/* Configure the second scsi bus */
ahc->sc_link_b = ahc->sc_link;
-#if defined(__FreeBSD__)
- ahc->sc_link_b.adapter_targ = ahc->our_id_b;
- ahc->sc_link_b.adapter_bus = 1;
- ahc->sc_link_b.fordriver = (void *)SELBUSB;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
ahc->sc_link_b.adapter_target = ahc->our_id_b;
-#ifdef __OpenBSD__
- if(ahc->type & AHC_WIDE)
+ if (ahc->features & AHC_WIDE)
ahc->sc_link.adapter_buswidth = 16;
-#endif
-#ifndef __OpenBSD__
- ahc->sc_link_b.channel = 1;
-#endif
-#endif
}
-
-#if defined(__FreeBSD__)
- /*
- * Prepare the scsibus_data area for the upperlevel
- * scsi code.
- */
- scbus = scsi_alloc_bus();
- if(!scbus)
- return 0;
- scbus->adapter_link = (ahc->flags & AHC_CHANNEL_B_PRIMARY) ?
- &ahc->sc_link_b : &ahc->sc_link;
- if(ahc->type & AHC_WIDE)
- scbus->maxtarg = 15;
-
- /*
- * ask the adapter what subunits are present
- */
- if(bootverbose)
- printf("ahc%d: Probing channel %c\n", ahc->unit,
- (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? 'B' : 'A');
- scsi_attachdevs(scbus);
- scbus = NULL; /* Upper-level SCSI code owns this now */
-
- if(ahc->type & AHC_TWIN) {
- scbus = scsi_alloc_bus();
- if(!scbus)
- return 0;
- scbus->adapter_link = (ahc->flags & AHC_CHANNEL_B_PRIMARY) ?
- &ahc->sc_link : &ahc->sc_link_b;
- if(ahc->type & AHC_WIDE)
- scbus->maxtarg = 15;
- if(bootverbose)
- printf("ahc%d: Probing Channel %c\n", ahc->unit,
- (ahc->flags & AHC_CHANNEL_B_PRIMARY) ? 'A': 'B');
- scsi_attachdevs(scbus);
- scbus = NULL; /* Upper-level SCSI code owns this now */
- }
-#elif defined(__NetBSD__) || defined (__OpenBSD__)
- /*
+/*
* ask the adapter what subunits are present
*/
if ((ahc->flags & AHC_CHANNEL_B_PRIMARY) == 0) {
@@ -735,7 +1513,7 @@ ahc_attach(ahc)
ahc->sc_link_b.scsibus = 0xff;
config_found((void *)ahc, &ahc->sc_link, scsiprint);
- if (ahc->type & AHC_TWIN)
+ if (ahc->features & AHC_TWIN)
config_found((void *)ahc, &ahc->sc_link_b, scsiprint);
} else {
/*
@@ -744,1283 +1522,1997 @@ ahc_attach(ahc)
* is needed, here.
*/
- /* assert(ahc->type & AHC_TWIN); */
+ /* assert(ahc->features & AHC_TWIN); */
config_found((void *)ahc, &ahc->sc_link_b, scsiprint);
config_found((void *)ahc, &ahc->sc_link, scsiprint);
}
-#endif
return 1;
}
-/*
- * Send an SCB down to the card via PIO.
- * We assume that the proper SCB is already selected in SCBPTR.
- */
-static inline void
-ahc_send_scb(ahc, scb)
- struct ahc_data *ahc;
- struct scb *scb;
-{
- AHC_OUTB(ahc, SCBCNT, SCBAUTO);
- if( ahc->type == AHC_284 )
- /* Can only do 8bit PIO */
- AHC_OUTSB(ahc, SCBARRAY, scb, SCB_PIO_TRANSFER_SIZE);
+STATIC void
+ahc_fetch_devinfo(ahc, 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
- AHC_OUTSL(ahc, SCBARRAY, scb,
- (SCB_PIO_TRANSFER_SIZE + 3) / 4);
- AHC_OUTB(ahc, SCBCNT, 0);
+ 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(devinfo, our_id, target, lun, channel, role)
+ 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);
}
/*
- * Retrieve an SCB from the card via PIO.
- * We assume that the proper SCB is already selected in SCBPTR.
+ * Catch an interrupt from the adapter
*/
-static inline void
-ahc_fetch_scb(ahc, scb)
- struct ahc_data *ahc;
- struct scb *scb;
+int
+ahc_intr(void *arg)
{
- AHC_OUTB(ahc, SCBCNT, 0x80); /* SCBAUTO */
+ struct ahc_softc *ahc;
+ u_int intstat;
- /* Can only do 8bit PIO for reads */
- AHC_INSB(ahc, SCBARRAY, scb, SCB_PIO_TRANSFER_SIZE);
+ ahc = (struct ahc_softc *)arg;
- AHC_OUTB(ahc, SCBCNT, 0);
-}
+ intstat = ahc_inb(ahc, INTSTAT);
-/*
- * Swap in_scbp for out_scbp down in the cards SCB array.
- * We assume that the SCB for out_scbp is already selected in SCBPTR.
- */
-static inline void
-ahc_page_scb(ahc, out_scbp, in_scbp)
- struct ahc_data *ahc;
- struct scb *out_scbp;
- struct scb *in_scbp;
-{
- /* Page-out */
- ahc_fetch_scb(ahc, out_scbp);
- out_scbp->flags |= SCB_PAGED_OUT;
- if(!(out_scbp->control & TAG_ENB))
- {
- /* Stick in non-tagged array */
- int index = (out_scbp->tcl >> 4)
- | (out_scbp->tcl & SELBUSB);
- ahc->pagedout_ntscbs[index] = out_scbp;
+ /*
+ * Any interrupts to process?
+ */
+ if ((intstat & INT_PEND) == 0) {
+ if (ahc->pci_intr_func && ahc->pci_intr_func(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;
+ }
+
+ 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, num_errors;
- /* Page-in */
- in_scbp->position = out_scbp->position;
- out_scbp->position = SCB_LIST_NULL;
- ahc_send_scb(ahc, in_scbp);
- in_scbp->flags &= ~SCB_PAGED_OUT;
+ error = ahc_inb(ahc, ERROR);
+ num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
+ for (i = 0; error != 1 && i < num_errors; i++)
+ error >>= 1;
+ panic("%s: brkadrint, %s at seqaddr = 0x%x\n",
+ ahc_name(ahc), hard_error[i].errmesg,
+ ahc_inb(ahc, SEQADDR0) |
+ (ahc_inb(ahc, SEQADDR1) << 8));
+
+ /* Tell everyone that this HBA is no longer availible */
+ ahc_abort_scbs(ahc, ALL_TARGETS, ALL_CHANNELS,
+ ALL_LUNS, 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 inline void
-ahc_run_waiting_queues(ahc)
- struct ahc_data *ahc;
+STATIC struct tmode_tstate *
+ahc_alloc_tstate(ahc, scsi_id, channel)
+ struct ahc_softc *ahc;
+ u_int scsi_id;
+ char channel;
{
- struct scb* scb;
- u_char cur_scb;
+ 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);
- if(!(ahc->assigned_scbs.stqh_first || ahc->waiting_scbs.stqh_first))
- return;
+ /*
+ * 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) {
+ bcopy(master_tstate, tstate, sizeof(*tstate));
+ tstate->ultraenb = 0;
+ for (i = 0; i < 16; i++) {
+ bzero(&tstate->transinfo[i].current,
+ sizeof(tstate->transinfo[i].current));
+ bzero(&tstate->transinfo[i].goal,
+ sizeof(tstate->transinfo[i].goal));
+ }
+ } else
+ bzero(tstate, sizeof(*tstate));
+ s = splbio();
+ ahc->enabled_targets[scsi_id] = tstate;
+ splx(s);
+ return (tstate);
+}
- pause_sequencer(ahc);
- cur_scb = AHC_INB(ahc, SCBPTR);
+STATIC void
+ahc_handle_seqint(ahc, intstat)
+ struct ahc_softc *ahc;
+ u_int intstat;
+{
+ struct scb *scb;
+ struct ahc_devinfo devinfo;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
/*
- * First handle SCBs that are waiting but have been
- * assigned a slot.
+ * Clear the upper byte that holds SEQINT status
+ * codes and clear the SEQINT bit. We will unpause
+ * the sequencer, if appropriate, after servicing
+ * the request.
*/
- while((scb = ahc->assigned_scbs.stqh_first) != NULL) {
- STAILQ_REMOVE_HEAD(&ahc->assigned_scbs, links);
- AHC_OUTB(ahc, SCBPTR, scb->position);
- ahc_send_scb(ahc, scb);
-
- /* Mark this as an active command */
- scb->flags ^= SCB_ASSIGNEDQ|SCB_ACTIVE;
+ ahc_outb(ahc, CLRINT, CLRSEQINT);
+ switch (intstat & SEQINT_MASK) {
+ case NO_MATCH:
+ {
+ /* 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_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.
+ */
+ 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, QINFIFO, scb->position);
- if (!(scb->xs->flags & SCSI_NOMASK)) {
- timeout(ahc_timeout, (caddr_t)scb,
- (scb->xs->timeout * hz) / 1000);
+ 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));
}
- SC_DEBUG(scb->xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
+ break;
}
- /* Now deal with SCBs that require paging */
- if((scb = ahc->waiting_scbs.stqh_first) != NULL) {
- u_char disc_scb = AHC_INB(ahc, DISCONNECTED_SCBH);
- u_char active = AHC_INB(ahc, FLAGS) & (SELECTED|IDENTIFY_SEEN);
- int count = 0;
+ case BAD_STATUS:
+ {
+ u_int scb_index;
+ struct hardware_scb *hscb;
+ struct scsi_xfer *xs;
+ /*
+ * The sequencer will notify us when a command
+ * has an error that would be of interest to
+ * the kernel. This allows us to leave the sequencer
+ * running in the common case of command completes
+ * without error. The sequencer will already have
+ * dma'd the SCB back up to us, so we can reference
+ * the in kernel copy directly.
+ */
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = &ahc->scb_data->scbarray[scb_index];
- do {
- u_char next_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 "
+ "not valid during seqint 0x%x scb(%d)\n",
+ ahc_name(ahc), devinfo.channel,
+ devinfo.target, intstat, scb_index);
+ goto unpause;
+ }
- /* Attempt to page this SCB in */
- if(disc_scb == SCB_LIST_NULL)
- break;
+ hscb = scb->hscb;
+ xs = scb->xs;
+ /* Don't want to clobber the original sense code */
+ if ((scb->flags & SCB_SENSE) != 0) {
/*
- * Check the next SCB on in the list.
+ * Clear the SCB_SENSE Flag and have
+ * the sequencer do a normal command
+ * complete.
*/
- AHC_OUTB(ahc, SCBPTR, disc_scb);
- next_scb = AHC_INB(ahc, SCB_NEXT);
+ scb->flags &= ~SCB_SENSE;
+ ahcsetccbstatus(xs, XS_DRIVER_STUFFUP);
+ break;
+ }
+ /* Freeze the queue unit the client sees the error. */
+ ahc_freeze_devq(ahc, xs->sc_link);
+ ahc_freeze_ccb(scb);
+ xs->status = hscb->status;
+ switch (hscb->status) {
+ case SCSI_OK:
+ printf("%s: Interrupted for staus of 0???\n",
+ ahc_name(ahc));
+ break;
+ case SCSI_CHECK:
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ sc_print_addr(scb->xs->sc_link);
+ 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 scsi_sense *sc;
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+
+ sg = scb->sg_list;
+ sc = (struct scsi_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;
- /*
- * We have to be careful about when we allow
- * an SCB to be paged out. There must always
- * be at least one slot available for a
- * reconnecting target in case it references
- * an SCB that has been paged out. Our
- * heuristic is that either the disconnected
- * list has at least two entries in it or
- * there is one entry and the sequencer is
- * actively working on an SCB which implies that
- * it will either complete or disconnect before
- * another reconnection can occur.
- */
- if((next_scb != SCB_LIST_NULL) || active)
- {
- u_char out_scbi;
- struct scb* out_scbp;
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ sc_print_addr(scb->xs->sc_link);
+ printf("Sending Sense\n");
+ }
+#endif
+ sg->addr = ahc->scb_data->sense_busaddr +
+ (hscb->tag*sizeof(struct scsi_sense_data));
+
+ sg->len = sizeof(struct scsi_sense_data);
- STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links);
+ 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;
/*
- * Find the in-core SCB for the one
- * we're paging out.
+ * Would be nice to preserve DISCENB here,
+ * but due to the way we page SCBs, we can't.
*/
- out_scbi = AHC_INB(ahc, SCB_TAG);
- out_scbp = ahc->scbarray[out_scbi];
+ hscb->control = 0;
- /* Do the page out */
- ahc_page_scb(ahc, out_scbp, scb);
-
- /* Mark this as an active command */
- scb->flags ^= SCB_WAITINGQ|SCB_ACTIVE;
+ /*
+ * This request sense could be because the
+ * the device lost power or in some other
+ * way has lost our transfer negotiations.
+ * Renegotiate if appropriate.
+ */
+ ahc_calc_residual(scb);
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWSENSE) {
+ sc_print_addr(xs->sc_link);
+ 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;
+ 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);
- /* Queue the command */
- AHC_OUTB(ahc, QINFIFO, scb->position);
- if (!(scb->xs->flags & SCSI_NOMASK)) {
+ /*
+ * Ensure we have enough time to actually
+ * retrieve the sense.
+ */
+ if (!(scb->xs->flags & SCSI_POLL)) {
+ untimeout(ahc_timeout, (caddr_t)scb);
timeout(ahc_timeout, (caddr_t)scb,
- (scb->xs->timeout * hz) / 1000);
+ 5 * hz);
}
- SC_DEBUG(scb->xs->sc_link, SDEV_DB3,
- ("cmd_paged-in\n"));
- count++;
+ }
+ break;
+ case SCSI_BUSY:
+ /*
+ * Requeue any transactions that haven't been
+ * sent yet.
+ */
+ ahc_freeze_devq(ahc, xs->sc_link);
+ ahc_freeze_ccb(scb);
+ break;
+ }
+ break;
+ }
+ case TRACE_POINT:
+ {
+ 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));
+ break;
+ }
+ case HOST_MSG_LOOP:
+ {
+ /*
+ * The sequencer has encountered a message phase
+ * that requires host assistance for completion.
+ * While handling the message phase(s), we will be
+ * notified by the sequencer after each byte is
+ * transfered so we can track bus phases.
+ *
+ * If this is the first time we've seen a HOST_MSG_LOOP,
+ * initialize the state of the host message loop.
+ */
+ if (ahc->msg_type == MSG_TYPE_NONE) {
+ u_int bus_phase;
+
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ if (bus_phase != P_MESGIN
+ && bus_phase != P_MESGOUT) {
+ printf("ahc_intr: HOST_MSG_LOOP bad "
+ "phase 0x%x\n",
+ bus_phase);
+ /*
+ * Probably transitioned to bus free before
+ * we got here. Just punt the message.
+ */
+ ahc_clear_intstat(ahc);
+ restart_sequencer(ahc);
+ }
- /* Advance to the next disconnected SCB */
- disc_scb = next_scb;
+ 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 (bus_phase == P_MESGOUT)
+ ahc_setup_initiator_msgout(ahc,
+ &devinfo,
+ scb);
+ else {
+ ahc->msg_type =
+ MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msgin_index = 0;
+ }
+ } 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);
+ }
}
+
+ /* Pass a NULL path so that handlers generate their own */
+ ahc_handle_message_phase(ahc, /*path*/NULL);
+ break;
+ }
+ case PERR_DETECTED:
+ {
+ /*
+ * If we've cleared the parity error interrupt
+ * but the sequencer still believes that SCSIPERR
+ * is true, it must be that the parity error is
+ * for the currently presented byte on the bus,
+ * and we are not in a phase (data-in) where we will
+ * eventually ack this byte. Ack the byte and
+ * throw it away in the hope that the target will
+ * take us to message out to deliver the appropriate
+ * error message.
+ */
+ 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);
+ }
+ break;
+ }
+ case DATA_OVERRUN:
+ {
+ /*
+ * When the sequencer detects an overrun, it
+ * places the controller in "BITBUCKET" mode
+ * and allows the target to complete its transfer.
+ * Unfortunately, none of the counters get updated
+ * when the controller is in this mode, so we have
+ * no way of knowing how large the overrun was.
+ */
+ u_int scbindex = ahc_inb(ahc, SCB_TAG);
+ u_int lastphase = ahc_inb(ahc, LASTPHASE);
+ int i;
+
+ scb = &ahc->scb_data->scbarray[scbindex];
+ for (i = 0; i < num_phases; i++) {
+ if (lastphase == phase_table[i].phase)
+ break;
+ }
+ sc_print_addr(scb->xs->sc_link);
+ printf("data overrun detected %s."
+ " Tag == 0x%x.\n",
+ phase_table[i].phasemsg,
+ scb->hscb->tag);
+ sc_print_addr(scb->xs->sc_link);
+ printf("%s seen Data Phase. Length = %d. NumSGs = %d.\n",
+ ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
+ scb->xs->datalen, 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",
+ i,
+ scb->sg_list[i].addr,
+ scb->sg_list[i].len);
+ }
+ }
+ /*
+ * Set this and it will take affect when the
+ * target does a command complete.
+ */
+ ahc_freeze_devq(ahc, scb->xs->sc_link);
+ ahcsetccbstatus(scb->xs, XS_DRIVER_STUFFUP);
+ ahc_freeze_ccb(scb);
+ break;
+ }
+ case TRACEPOINT:
+ {
+ printf("TRACEPOINT: RETURN_2 = %d\n", ahc_inb(ahc, RETURN_2));
+#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));
+#endif
+ break;
+ }
+#if NOT_YET
+ /* XXX Fill these in later */
+ case MESG_BUFFER_BUSY:
+ break;
+ case MSGIN_PHASEMIS:
+ 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);
+}
+
+STATIC void
+ahc_handle_scsiint(ahc, intstat)
+ struct ahc_softc *ahc;
+ u_int intstat;
+{
+ u_int scb_index;
+ u_int status;
+ struct scb *scb;
+ char cur_channel;
+ char intr_channel;
+
+ if ((ahc->features & AHC_TWIN) != 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_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);
+ intr_channel = (cur_channel == 'A') ? 'B' : 'A';
+ }
+ if (status == 0) {
+ printf("%s: Spurious SCSI interrupt\n", ahc_name(ahc));
+ return;
+ }
+ }
+
+ 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 = NULL;
+
+ 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);
+ } else if ((status & SCSIPERR) != 0) {
+ /*
+ * Determine the bus phase and queue an appropriate message.
+ * SCSIPERR is latched true as soon as a parity error
+ * occurs. If the sequencer acked the transfer that
+ * caused the parity error and the currently presented
+ * transfer on the bus has correct parity, SCSIPERR will
+ * be cleared by CLRSCSIPERR. Use this to determine if
+ * we should look at the last phase the sequencer recorded,
+ * or the current phase presented on the bus.
+ */
+ u_int mesg_out;
+ u_int curphase;
+ u_int errorphase;
+ u_int lastphase;
+ int i;
+
+ lastphase = ahc_inb(ahc, LASTPHASE);
+ curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+ ahc_outb(ahc, CLRSINT1, CLRSCSIPERR);
+ /*
+ * For all phases save DATA, the sequencer won't
+ * automatically ack a byte that has a parity error
+ * in it. So the only way that the current phase
+ * could be 'data-in' is if the parity error is for
+ * an already acked byte in the data phase. During
+ * synchronous data-in transfers, we may actually
+ * ack bytes before latching the current phase in
+ * LASTPHASE, leading to the discrepancy between
+ * curphase and lastphase.
+ */
+ if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
+ || curphase == P_DATAIN)
+ errorphase = curphase;
+ else
+ errorphase = lastphase;
+
+ for (i = 0; i < num_phases; i++) {
+ if (errorphase == phase_table[i].phase)
+ break;
+ }
+ mesg_out = phase_table[i].mesg_out;
+ if (scb != NULL)
+ sc_print_addr(scb->xs->sc_link);
+ else
+ printf("%s:%c:%d: ", ahc_name(ahc),
+ intr_channel,
+ TCL_TARGET(ahc_inb(ahc, SAVED_TCL)));
+
+ 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));
+
+ /*
+ * 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.
+ */
+ if (mesg_out != MSG_NOOP) {
+ if (ahc->msg_type != MSG_TYPE_NONE)
+ ahc->send_msg_perror = TRUE;
else
+ ahc_outb(ahc, MSG_OUT, mesg_out);
+ }
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ unpause_sequencer(ahc);
+ } else if ((status & BUSFREE) != 0
+ && (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) {
+ /*
+ * First 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;
+
+ ahc_outb(ahc, SCSISEQ,
+ ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
+ if (lastphase == P_MESGOUT) {
+ u_int message;
+ u_int tag;
+
+ message = ahc->msgout_buf[ahc->msgout_index - 1];
+ tag = SCB_LIST_NULL;
+ switch (message) {
+ case MSG_ABORT_TAG:
+ tag = scb->hscb->tag;
+ /* FALLTRHOUGH */
+ case MSG_ABORT:
+ sc_print_addr(scb->xs->sc_link);
+ printf("SCB %d - Abort %s Completed.\n",
+ scb->hscb->tag, tag == SCB_LIST_NULL ?
+ "" : "Tag");
+ ahc_abort_scbs(ahc, target, channel,
+ TCL_LUN(saved_tcl), tag,
+ ROLE_INITIATOR,
+ XS_DRIVER_STUFFUP);
+ printerror = 0;
+ break;
+ case MSG_BUS_DEV_RESET:
+ {
+ struct ahc_devinfo devinfo;
+
+ if (scb != NULL &&
+ (scb->xs->flags & SCSI_RESET)
+ && ahc_match_scb(scb, target, channel,
+ TCL_LUN(saved_tcl),
+ SCB_LIST_NULL,
+ ROLE_INITIATOR)) {
+ ahcsetccbstatus(scb->xs, XS_NOERROR);
+ }
+ ahc_compile_devinfo(&devinfo,
+ initiator_role_id,
+ target,
+ TCL_LUN(saved_tcl),
+ channel,
+ ROLE_INITIATOR);
+ ahc_handle_devreset(ahc, &devinfo,
+ XS_RESET,
+ "Bus Device Reset",
+ /*verbose_level*/0);
+ printerror = 0;
+ break;
+ }
+ default:
break;
- } while((scb = ahc->waiting_scbs.stqh_first) != NULL);
+ }
+ }
+ if (printerror != 0) {
+ int i;
- if(count) {
- /*
- * Update the head of the disconnected list.
- */
- AHC_OUTB(ahc, DISCONNECTED_SCBH, disc_scb);
- if(disc_scb != SCB_LIST_NULL) {
- AHC_OUTB(ahc, SCBPTR, disc_scb);
- AHC_OUTB(ahc, SCB_PREV, SCB_LIST_NULL);
+ if (scb != NULL) {
+ u_int tag;
+
+ if ((scb->hscb->control & TAG_ENB) != 0)
+ tag = scb->hscb->tag;
+ else
+ tag = SCB_LIST_NULL;
+ ahc_abort_scbs(ahc, target, channel,
+ SCB_LUN(scb), tag,
+ ROLE_INITIATOR,
+ XS_DRIVER_STUFFUP);
+ } else {
+ /*
+ * We had not fully identified this connection,
+ * so we cannot abort anything.
+ */
+ printf("%s: ", ahc_name(ahc));
+ }
+ for (i = 0; i < num_phases; i++) {
+ if (lastphase == phase_table[i].phase)
+ break;
}
+ printf("Unexpected busfree %s\n"
+ "SEQADDR == 0x%x\n",
+ 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;
+
+ scbptr = ahc_inb(ahc, WAITING_SCBH);
+ ahc_outb(ahc, SCBPTR, scbptr);
+ 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)
+ scb = NULL;
+ } else
+ scb = NULL;
+
+ 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;
+
+ tag = SCB_LIST_NULL;
+ if ((scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0)
+ tag = scb->hscb->tag;
+
+ 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);
+
+ /* No more pending messages */
+ ahc_clear_msg_state(ahc);
+
+ /*
+ * Although the driver does not care about the
+ * 'Selection in Progress' status bit, the busy
+ * LED does. SELINGO is only cleared by a sucessful
+ * 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).
+ */
+ ahc_outb(ahc, CLRSINT0, CLRSELINGO);
+
+ /* Clear interrupt state */
+ ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ restart_sequencer(ahc);
+ } else {
+ sc_print_addr(scb->xs->sc_link);
+ printf("Unknown SCSIINT. Status = 0x%x\n", status);
+ ahc_outb(ahc, CLRSINT1, status);
+ ahc_outb(ahc, CLRINT, CLRSCSIINT);
+ unpause_sequencer(ahc);
}
- /* Restore old position */
- AHC_OUTB(ahc, SCBPTR, cur_scb);
- unpause_sequencer(ahc, /*unpause_always*/FALSE);
}
-/*
- * Add this SCB to the head of the "waiting for selection" list.
- */
-static
-void ahc_add_waiting_scb(ahc, scb)
- struct ahc_data *ahc;
- struct scb *scb;
+STATIC void
+ahc_build_transfer_msg(ahc, devinfo)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
{
- u_char next;
- u_char curscb;
+ /*
+ * We need to initiate transfer negotiations.
+ * If our current and goal settings are identical,
+ * we want to renegotiate due to a check condition.
+ */
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+ int dowide;
+ int dosync;
+
+ 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;
+ }
- curscb = AHC_INB(ahc, SCBPTR);
- next = AHC_INB(ahc, WAITING_SCBH);
+ if (dowide) {
+ ahc_construct_wdtr(ahc, tinfo->goal.width);
+ } else if (dosync) {
+ struct ahc_syncrate *rate;
+ u_int period;
+ u_int offset;
+
+ 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);
+ } else {
+ panic("ahc_intr: AWAITING_MSG for negotiation, "
+ "but no negotiation needed\n");
+ }
+}
- AHC_OUTB(ahc, SCBPTR, scb->position);
- AHC_OUTB(ahc, SCB_NEXT, next);
- AHC_OUTB(ahc, WAITING_SCBH, scb->position);
+STATIC void
+ahc_setup_initiator_msgout(ahc, devinfo, scb)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
+ struct scb *scb;
+{
+ /*
+ * 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;
+
+ if ((scb->flags & SCB_DEVICE_RESET) == 0
+ && ahc_inb(ahc, MSG_OUT) == MSG_IDENTIFYFLAG) {
+ u_int identify_msg;
+
+ identify_msg = MSG_IDENTIFYFLAG | SCB_LUN(scb);
+ if ((scb->hscb->control & DISCENB) != 0)
+ identify_msg |= MSG_IDENTIFY_DISCFLAG;
+ ahc->msgout_buf[ahc->msgout_index++] = identify_msg;
+ ahc->msgout_len++;
+
+ if ((scb->hscb->control & TAG_ENB) != 0) {
+ /* XXX fvdl FreeBSD has tag action passed down */
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_SIMPLE_Q_TAG;
+ ahc->msgout_buf[ahc->msgout_index++] = scb->hscb->tag;
+ ahc->msgout_len += 2;
+ }
+ }
+
+ if (scb->flags & SCB_DEVICE_RESET) {
+ ahc->msgout_buf[ahc->msgout_index++] = MSG_BUS_DEV_RESET;
+ ahc->msgout_len++;
+
+ sc_print_addr(scb->xs->sc_link);
+ printf("Bus Device Reset Message Sent\n");
+ } else if (scb->flags & SCB_ABORT) {
+ 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++;
+ sc_print_addr(scb->xs->sc_link);
+ printf("Abort Message Sent\n");
+ } else if ((ahc->targ_msg_req & devinfo->target_mask) != 0) {
+ ahc_build_transfer_msg(ahc, devinfo);
+ } else {
+ printf("ahc_intr: AWAITING_MSG for an SCB that "
+ "does not have a waiting message");
+ 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);
+ }
- AHC_OUTB(ahc, SCBPTR, curscb);
+ /*
+ * Clear the MK_MESSAGE flag from the SCB so we aren't
+ * asked to send this message again.
+ */
+ ahc_outb(ahc, SCB_CONTROL, ahc_inb(ahc, SCB_CONTROL) & ~MK_MESSAGE);
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
}
-/*
- * Catch an interrupt from the adapter
- */
-#if defined(__FreeBSD__)
-void
-#elif defined (__NetBSD__) || defined (__OpenBSD__)
-int
-#endif
-ahc_intr(arg)
- void *arg;
+STATIC void
+ahc_setup_target_msgin(ahc, devinfo)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
{
- int intstat;
- u_char status;
- struct scb *scb;
- struct scsi_xfer *xs;
- struct ahc_data *ahc = (struct ahc_data *)arg;
+ /*
+ * 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;
+
+ 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");
- intstat = AHC_INB(ahc, INTSTAT);
+ ahc->msgout_index = 0;
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+}
+
+STATIC int
+ahc_handle_msg_reject(ahc, devinfo)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
+{
/*
- * Is this interrupt for me? or for
- * someone who is sharing my interrupt?
+ * 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.
*/
- if (!(intstat & INT_PEND))
-#if defined(__FreeBSD__)
- return;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
- return 0;
-#endif
+ 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);
+
+ if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/FALSE)) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+
+ /* note 8bit xfers */
+ printf("%s:%c:%d: refuses WIDE negotiation. Using "
+ "8bit transfers\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target);
+ ahc_set_width(ahc, devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE, /*done*/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.
+ */
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel,
+ devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ if (tinfo->goal.period) {
+ u_int period;
+
+ /* 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 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);
+ printf("%s:%c:%d: refuses synchronous negotiation. "
+ "Using asynchronous transfers\n",
+ ahc_name(ahc),
+ devinfo->channel, devinfo->target);
+ } else if ((scb->hscb->control & MSG_SIMPLE_Q_TAG) != 0) {
+ printf("%s:%c:%d: refuses tagged commands. Performing "
+ "non-tagged I/O\n", ahc_name(ahc),
+ devinfo->channel, devinfo->target);
+
+ ahc_set_tags(ahc, devinfo, FALSE);
- if (intstat & BRKADRINT) {
- /* We upset the sequencer :-( */
+ /*
+ * 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);
- /* Lookup the error message */
- int i, error = AHC_INB(ahc, ERROR);
- int num_errors = sizeof(hard_error)/sizeof(hard_error[0]);
- 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, SEQADDR1) << 8) |
- AHC_INB(ahc, SEQADDR0));
- }
- if (intstat & SEQINT)
- ahc_handle_seqint(ahc, intstat);
+ /*
+ * 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);
+ } else {
+ /*
+ * Otherwise, we ignore it.
+ */
+ printf("%s:%c:%d: Message reject for %x -- ignored\n",
+ ahc_name(ahc), devinfo->channel, devinfo->target,
+ last_msg);
+ }
+ return (response);
+}
- if (intstat & SCSIINT) {
-
- int scb_index = AHC_INB(ahc, SCB_TAG);
- status = AHC_INB(ahc, SSTAT1);
- scb = ahc->scbarray[scb_index];
-
- if (status & SCSIRSTI) {
- char channel;
- channel = AHC_INB(ahc, SBLKCTL);
- channel = channel & SELBUSB ? 'B' : 'A';
- printf("%s: Someone reset channel %c\n",
- ahc_name(ahc), channel);
- ahc_reset_channel(ahc,
- channel,
- SCB_LIST_NULL,
- XS_BUSY,
- /* Initiate Reset */FALSE);
- scb = NULL;
+STATIC void
+ahc_clear_msg_state(ahc)
+ struct ahc_softc *ahc;
+{
+ ahc->msgout_len = 0;
+ ahc->msgin_index = 0;
+ ahc->msg_type = MSG_TYPE_NONE;
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+}
+
+STATIC void
+ahc_handle_message_phase(ahc, sc_link)
+ struct ahc_softc *ahc;
+ struct scsi_link *sc_link;
+{
+ struct ahc_devinfo devinfo;
+ u_int bus_phase;
+ int end_session;
+
+ ahc_fetch_devinfo(ahc, &devinfo);
+ end_session = FALSE;
+ bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
+
+reswitch:
+ switch (ahc->msg_type) {
+ case MSG_TYPE_INITIATOR_MSGOUT:
+ {
+ int lastbyte;
+ int phasemis;
+ int msgdone;
+
+ if (ahc->msgout_len == 0)
+ panic("REQINIT interrupt with no active message");
+
+ phasemis = bus_phase != P_MESGOUT;
+ if (phasemis) {
+ if (bus_phase == P_MESGIN) {
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ ahc->send_msg_perror = FALSE;
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGIN;
+ ahc->msgin_index = 0;
+ goto reswitch;
+ }
+ end_session = TRUE;
+ break;
}
- else if (!(scb && (scb->flags & SCB_ACTIVE))){
- printf("%s: ahc_intr - referenced scb not "
- "valid during scsiint 0x%x scb(%d)\n",
- ahc_name(ahc), status, scb_index);
- AHC_OUTB(ahc, CLRSINT1, status);
- unpause_sequencer(ahc, /*unpause_always*/TRUE);
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
- scb = NULL;
+
+ if (ahc->send_msg_perror) {
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_outb(ahc, SCSIDATL, MSG_PARITY_ERROR);
+ break;
}
- else if (status & SCSIPERR) {
+
+ msgdone = ahc->msgout_index == ahc->msgout_len;
+ if (msgdone) {
/*
- * Determine the bus phase and
- * queue an appropriate message
+ * The target has requested a retry.
+ * Re-assert ATN, reset our message index to
+ * 0, and try again.
*/
- char *phase;
- u_char mesg_out = MSG_NOOP;
- u_char lastphase = AHC_INB(ahc, LASTPHASE);
+ ahc->msgout_index = 0;
+ ahc_outb(ahc, SCSISIGO, ahc_inb(ahc, SCSISIGO) | ATNO);
+ }
- xs = scb->xs;
- sc_print_addr(xs->sc_link);
+ lastbyte = ahc->msgout_index == (ahc->msgout_len - 1);
+ if (lastbyte) {
+ /* Last byte is signified by dropping ATN */
+ ahc_outb(ahc, CLRSINT1, CLRATNO);
+ }
- switch(lastphase) {
- case P_DATAOUT:
- phase = "Data-Out";
- break;
- case P_DATAIN:
- phase = "Data-In";
- mesg_out = MSG_INITIATOR_DET_ERR;
- break;
- case P_COMMAND:
- phase = "Command";
- break;
- case P_MESGOUT:
- phase = "Message-Out";
- break;
- case P_STATUS:
- phase = "Status";
- mesg_out = MSG_INITIATOR_DET_ERR;
- break;
- case P_MESGIN:
- phase = "Message-In";
- mesg_out = MSG_PARITY_ERROR;
- break;
- default:
- phase = "unknown";
- break;
+ /*
+ * Clear our interrupt status and present
+ * the next byte on the bus.
+ */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+ break;
+ }
+ case MSG_TYPE_INITIATOR_MSGIN:
+ {
+ int phasemis;
+ int message_done;
+
+ phasemis = bus_phase != P_MESGIN;
+
+ if (phasemis) {
+ ahc->msgin_index = 0;
+ if (bus_phase == P_MESGOUT
+ && (ahc->send_msg_perror == TRUE
+ || (ahc->msgout_len != 0
+ && ahc->msgout_index == 0))) {
+ ahc->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
+ goto reswitch;
}
- printf("parity error during %s phase.\n", phase);
+ end_session = TRUE;
+ break;
+ }
+
+ /* Pull the byte in without acking it */
+ ahc->msgin_buf[ahc->msgin_index] = ahc_inb(ahc, SCSIBUSL);
+
+ message_done = ahc_parse_msg(ahc, sc_link, &devinfo);
+ if (message_done) {
/*
- * 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.
+ * Clear our incoming message buffer in case there
+ * is another message following this one.
*/
- if(mesg_out != MSG_NOOP) {
- AHC_OUTB(ahc, MSG0, mesg_out);
- AHC_OUTB(ahc, MSG_LEN, 1);
- }
- else
- /*
- * Should we allow the target to make
- * this decision for us?
- */
- xs->error = XS_DRIVER_STUFFUP;
- }
- else if (status & SELTO) {
- u_char waiting;
- u_char flags;
+ ahc->msgin_index = 0;
- xs = scb->xs;
- xs->error = XS_SELTIMEOUT;
/*
- * Clear any pending messages for the timed out
- * target, and mark the target as free
+ * If this message illicited a response,
+ * assert ATN so the target takes us to the
+ * message out phase.
*/
- flags = AHC_INB(ahc, FLAGS);
- AHC_OUTB(ahc, MSG_LEN, 0);
- ahc_unbusy_target(ahc, xs->sc_link->target,
-#if defined(__FreeBSD__)
- ((long)xs->sc_link->fordriver & SELBUSB)
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
- IS_SCSIBUS_B(ahc, xs->sc_link)
-#endif
- ? 'B' : 'A');
- /* Stop the selection */
- AHC_OUTB(ahc, SCSISEQ, 0);
-
- AHC_OUTB(ahc, SCB_CONTROL, 0);
+ if (ahc->msgout_len != 0)
+ ahc_outb(ahc, SCSISIGO,
+ ahc_inb(ahc, SCSISIGO) | ATNO);
+ } else
+ ahc->msgin_index++;
+
+ /* Ack the byte */
+ ahc_outb(ahc, CLRSINT1, CLRREQINIT);
+ ahc_inb(ahc, SCSIDATL);
+ break;
+ }
+ case MSG_TYPE_TARGET_MSGIN:
+ {
+ int msgdone;
+ int msgout_request;
- AHC_OUTB(ahc, CLRSINT1, CLRSELTIMEO);
+ if (ahc->msgout_len == 0)
+ panic("Target MSGIN with no active message");
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
+ /*
+ * If we interrupted a mesgout session, the initiator
+ * will not know this until our first REQ. So, we
+ * only honor mesgout requests after we've sent our
+ * first byte.
+ */
+ if ((ahc_inb(ahc, SCSISIGI) & ATNI) != 0
+ && ahc->msgout_index > 0)
+ msgout_request = TRUE;
+ else
+ msgout_request = FALSE;
- /* Shift the waiting for selection queue forward */
- waiting = AHC_INB(ahc, WAITING_SCBH);
- AHC_OUTB(ahc, SCBPTR, waiting);
- waiting = AHC_INB(ahc, SCB_NEXT);
- AHC_OUTB(ahc, WAITING_SCBH, waiting);
+ if (msgout_request) {
- restart_sequencer(ahc);
- }
- else if (!(status & BUSFREE)) {
- sc_print_addr(scb->xs->sc_link);
- printf("Unknown SCSIINT. Status = 0x%x\n", status);
- AHC_OUTB(ahc, CLRSINT1, status);
- unpause_sequencer(ahc, /*unpause_always*/TRUE);
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
- scb = NULL;
- }
- if(scb != NULL) {
- /* We want to process the command */
- untimeout(ahc_timeout, (caddr_t)scb);
- ahc_done(ahc, scb);
+ /*
+ * Change gears and see if
+ * this messages is of interest to
+ * us or should be passed back to
+ * the sequencer.
+ */
+ ahc->msg_type = MSG_TYPE_TARGET_MSGOUT;
+ ahc_outb(ahc, SCSISIGO, P_MESGOUT | BSYO);
+ ahc->msgin_index = 0;
+ /* Dummy read to REQ for first byte */
+ ahc_inb(ahc, SCSIDATL);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ break;
}
- }
- if (intstat & CMDCMPLT) {
- int scb_index;
-
- do {
- scb_index = AHC_INB(ahc, QOUTFIFO);
- scb = ahc->scbarray[scb_index];
- if (!scb || !(scb->flags & SCB_ACTIVE)) {
- printf("%s: WARNING "
- "no command for scb %d (cmdcmplt)\n"
- "QOUTCNT == %d\n",
- ahc_name(ahc), scb_index,
- AHC_INB(ahc, QOUTCNT));
- AHC_OUTB(ahc, CLRINT, CLRCMDINT);
- continue;
- }
- AHC_OUTB(ahc, CLRINT, CLRCMDINT);
- untimeout(ahc_timeout, (caddr_t)scb);
- ahc_done(ahc, scb);
- } while (AHC_INB(ahc, QOUTCNT) & ahc->qcntmask);
+ msgdone = ahc->msgout_index == ahc->msgout_len;
+ if (msgdone) {
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) & ~SPIOEN);
+ end_session = TRUE;
+ break;
+ }
- ahc_run_waiting_queues(ahc);
+ /*
+ * Present the next byte on the bus.
+ */
+ ahc_outb(ahc, SXFRCTL0, ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ ahc_outb(ahc, SCSIDATL, ahc->msgout_buf[ahc->msgout_index++]);
+ break;
}
-#if defined(__NetBSD__) || defined(__OpenBSD__)
- return 1;
-#endif
-}
-
-static void
-ahc_handle_seqint(ahc, intstat)
- struct ahc_data *ahc;
- u_int8_t intstat;
-{
- struct scb *scb;
- u_short targ_mask;
- u_char target = (AHC_INB(ahc, SCSIID) >> 4) & 0x0f;
- u_char scratch_offset = target;
- char channel = AHC_INB(ahc, SBLKCTL) & SELBUSB ? 'B': 'A';
-
- if (channel == 'B')
- scratch_offset += 8;
- targ_mask = (0x01 << scratch_offset);
+ case MSG_TYPE_TARGET_MSGOUT:
+ {
+ int lastbyte;
+ int msgdone;
- switch (intstat & SEQINT_MASK) {
- case NO_MATCH:
- if (ahc->flags & AHC_PAGESCBS) {
- /* SCB Page-in request */
- u_char tag;
- u_char next;
- u_char disc_scb;
- struct scb *outscb;
- u_char arg_1 = AHC_INB(ahc, ARG_1);
+ /*
+ * The initiator signals that this is
+ * the last byte by dropping ATN.
+ */
+ lastbyte = (ahc_inb(ahc, SCSISIGI) & ATNI) == 0;
+ /*
+ * Read the latched byte, but turn off SPIOEN first
+ * so that we don't inadvertantly 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, sc_link, &devinfo);
+ if (msgdone == MSGLOOP_TERMINATED) {
/*
- * We should succeed, so set this now.
- * If we don't, and one of the methods
- * we use to aquire an SCB calls ahc_done,
- * we may wind up in our start routine
- * and unpause the adapter without giving
- * it the correct return value, which will
- * cause a hang.
+ * The message is *really* done in that it caused
+ * us to go to bus free. The sequencer has already
+ * been reset at this point, so pull the ejection
+ * handle.
*/
- AHC_OUTB(ahc, RETURN_1, SCB_PAGEDIN);
+ return;
+ }
+
+ ahc->msgin_index++;
- if (arg_1 == SCB_LIST_NULL) {
- /* Non-tagged command */
- int index;
-
- index = target|(channel == 'B' ? SELBUSB : 0);
- scb = ahc->pagedout_ntscbs[index];
- } else
- scb = ahc->scbarray[arg_1];
+ /*
+ * XXX Read spec about initiator dropping ATN too soon
+ * and use msgdone to detect it.
+ */
+ if (msgdone == MSGLOOP_MSGCOMPLETE) {
+ ahc->msgin_index = 0;
- if (!(scb->flags & SCB_PAGED_OUT))
- panic("%s: Request to page in a non paged out "
- "SCB.", ahc_name(ahc));
/*
- * Now to pick the SCB to page out.
- * Either take a free SCB, an assigned SCB,
- * an SCB that just completed, the first
- * one on the disconnected SCB list, or
- * as a last resort a queued SCB.
+ * If this message illicited a response, transition
+ * to the Message in phase and send it.
*/
- if (ahc->free_scbs.stqh_first) {
- outscb = ahc->free_scbs.stqh_first;
- STAILQ_REMOVE_HEAD(&ahc->free_scbs, links);
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- STAILQ_INSERT_HEAD(&ahc->page_scbs, outscb,
- links);
- AHC_OUTB(ahc, SCBPTR, scb->position);
- ahc_send_scb(ahc, scb);
- scb->flags &= ~SCB_PAGED_OUT;
- goto pagein_done;
- }
- if (intstat & CMDCMPLT) {
- int scb_index;
-
- AHC_OUTB(ahc, CLRINT, CLRCMDINT);
- scb_index = AHC_INB(ahc, QOUTFIFO);
- if (!(AHC_INB(ahc, QOUTCNT) & ahc->qcntmask))
- intstat &= ~CMDCMPLT;
-
- outscb = ahc->scbarray[scb_index];
- if (!outscb || !(outscb->flags & SCB_ACTIVE)) {
- printf("%s: WARNING no command for "
- "scb %d (cmdcmplt)\n",
- ahc_name(ahc),
- scb_index);
- /*
- * Fall through in hopes of finding
- * another SCB
- */
- } else {
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- AHC_OUTB(ahc, SCBPTR, scb->position);
- ahc_send_scb(ahc, scb);
- scb->flags &= ~SCB_PAGED_OUT;
- untimeout(ahc_timeout,
- (caddr_t)outscb);
- ahc_done(ahc, outscb);
- goto pagein_done;
- }
+ if (ahc->msgout_len != 0) {
+ ahc_outb(ahc, SCSISIGO, P_MESGIN | BSYO);
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ ahc->msg_type = MSG_TYPE_TARGET_MSGIN;
+ ahc->msgin_index = 0;
+ break;
}
- disc_scb = AHC_INB(ahc, DISCONNECTED_SCBH);
- if (disc_scb != SCB_LIST_NULL) {
- AHC_OUTB(ahc, SCBPTR, disc_scb);
- tag = AHC_INB(ahc, SCB_TAG);
- outscb = ahc->scbarray[tag];
- next = AHC_INB(ahc, SCB_NEXT);
- if (next != SCB_LIST_NULL) {
- AHC_OUTB(ahc, SCBPTR, next);
- AHC_OUTB(ahc, SCB_PREV,
- SCB_LIST_NULL);
- AHC_OUTB(ahc, SCBPTR, disc_scb);
- }
- AHC_OUTB(ahc, DISCONNECTED_SCBH, next);
- ahc_page_scb(ahc, outscb, scb);
- } else if (AHC_INB(ahc, QINCNT) & ahc->qcntmask) {
- /*
- * Pull one of our queued commands
- * as a last resort
- */
- disc_scb = AHC_INB(ahc, QINFIFO);
- AHC_OUTB(ahc, SCBPTR, disc_scb);
- tag = AHC_INB(ahc, SCB_TAG);
- outscb = ahc->scbarray[tag];
- if ((outscb->control & 0x23) != TAG_ENB) {
- /*
- * This is not a simple tagged command
- * so its position in the queue
- * matters. Take the command at the
- * end of the queue instead.
- */
- int i;
- u_char saved_queue[AHC_SCB_MAX];
- u_char queued = AHC_INB(ahc, QINCNT)
- & ahc->qcntmask;
+ }
- /*
- * Count the command we removed
- * already
- */
- saved_queue[0] = disc_scb;
- queued++;
+ if (lastbyte)
+ end_session = TRUE;
+ else {
+ /* Ask for the next byte. */
+ ahc_outb(ahc, SXFRCTL0,
+ ahc_inb(ahc, SXFRCTL0) | SPIOEN);
+ }
- /* Empty the input queue */
- for (i = 1; i < queued; i++)
- saved_queue[i] = AHC_INB(ahc, QINFIFO);
+ break;
+ }
+ default:
+ panic("Unknown REQINIT message type");
+ }
- /*
- * Put everyone back but the
- * last entry
- */
- queued--;
- for (i = 0; i < queued; i++)
- AHC_OUTB(ahc, QINFIFO,
- saved_queue[i]);
-
- AHC_OUTB(ahc, SCBPTR,
- saved_queue[queued]);
- tag = AHC_INB(ahc, SCB_TAG);
- outscb = ahc->scbarray[tag];
- }
- untimeout(ahc_timeout, (caddr_t)outscb);
- scb->position = outscb->position;
- outscb->position = SCB_LIST_NULL;
- STAILQ_INSERT_HEAD(&ahc->waiting_scbs,
- outscb, links);
- outscb->flags |= SCB_WAITINGQ;
- ahc_send_scb(ahc, scb);
- scb->flags &= ~SCB_PAGED_OUT;
- }
- else {
- panic("Page-in request with no candidates");
- AHC_OUTB(ahc, RETURN_1, 0);
+ if (end_session) {
+ ahc_clear_msg_state(ahc);
+ ahc_outb(ahc, RETURN_1, EXIT_MSG_LOOP);
+ } else
+ ahc_outb(ahc, RETURN_1, CONT_MSG_LOOP);
+}
+
+/*
+ * 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.
+ */
+STATIC int
+ahc_sent_msg(ahc, msgtype, full)
+ struct ahc_softc *ahc;
+ u_int msgtype;
+ int full;
+{
+ int found;
+ int index;
+
+ found = FALSE;
+ index = 0;
+
+ while (index < ahc->msgout_len) {
+ if (ahc->msgout_buf[index] == MSG_EXTENDED) {
+
+ /* Found a candidate */
+ if (ahc->msgout_buf[index+2] == msgtype) {
+ u_int end_index;
+
+ 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;
}
- pagein_done:
+ break;
+ } else if (ahc->msgout_buf[index] >= MSG_SIMPLE_Q_TAG
+ && ahc->msgout_buf[index] <= MSG_IGN_WIDE_RESIDUE) {
+
+ /* Skip tag type and tag id or residue param*/
+ index += 2;
} else {
- printf("%s:%c:%d: no active SCB for reconnecting "
- "target - issuing ABORT\n",
- ahc_name(ahc), channel, target);
- printf("SAVED_TCL == 0x%x\n",
- AHC_INB(ahc, SAVED_TCL));
- ahc_unbusy_target(ahc, target, channel);
- AHC_OUTB(ahc, SCB_CONTROL, 0);
- AHC_OUTB(ahc, CLRSINT1, CLRSELTIMEO);
- AHC_OUTB(ahc, RETURN_1, 0);
+ /* Single byte message */
+ index++;
}
+ }
+ return (found);
+}
+
+STATIC int
+ahc_parse_msg(ahc, sc_link, devinfo)
+ struct ahc_softc *ahc;
+ struct scsi_link *sc_link;
+ struct ahc_devinfo *devinfo;
+{
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+ int reject;
+ int done;
+ int response;
+ u_int targ_scsirate;
+
+ done = MSGLOOP_IN_PROG;
+ response = FALSE;
+ reject = FALSE;
+ tinfo = ahc_fetch_transinfo(ahc, devinfo->channel, devinfo->our_scsiid,
+ devinfo->target, &tstate);
+ targ_scsirate = tinfo->scsirate;
+
+ /*
+ * Parse as much of the message as is availible,
+ * rejecting it if we don't support it. When
+ * the entire message is availible and has been
+ * handled, return MSGLOOP_MSGCOMPLETE, indicating
+ * that we have parsed an entire message.
+ *
+ * In the case of extended messages, we accept the length
+ * byte outright and perform more checking once we know the
+ * extended message type.
+ */
+ switch (ahc->msgin_buf[0]) {
+ case MSG_MESSAGE_REJECT:
+ response = ahc_handle_msg_reject(ahc, devinfo);
+ /* FALLTHROUGH */
+ case MSG_NOOP:
+ done = MSGLOOP_MSGCOMPLETE;
break;
- case SEND_REJECT:
+ case MSG_IGN_WIDE_RESIDUE:
{
- u_char rejbyte = AHC_INB(ahc, REJBYTE);
- printf("%s:%c:%d: Warning - unknown message received from "
- "target (0x%x). Rejecting\n",
- ahc_name(ahc), channel, target, rejbyte);
- break;
- }
- case NO_IDENT:
- panic("%s:%c:%d: Target did not send an IDENTIFY message. "
- "SAVED_TCL == 0x%x\n",
- ahc_name(ahc), channel, target,
- AHC_INB(ahc, SAVED_TCL));
+ /* 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 BAD_PHASE:
- printf("%s:%c:%d: unknown scsi bus phase. Attempting to "
- "continue\n", ahc_name(ahc), channel, target);
- break;
- case EXTENDED_MSG:
+ }
+ case MSG_EXTENDED:
{
- u_int8_t message_length;
- u_int8_t message_code;
-
- message_length = AHC_INB(ahc, MSGIN_EXT_LEN);
- message_code = AHC_INB(ahc, MSGIN_EXT_OPCODE);
- switch(message_code) {
+ /* Wait for enough of the message to begin validation */
+ if (ahc->msgin_index < 2)
+ break;
+ switch (ahc->msgin_buf[2]) {
case MSG_EXT_SDTR:
{
- u_int8_t period;
- u_int8_t offset;
- u_int8_t saved_offset;
- u_int8_t targ_scratch;
- u_int8_t maxoffset;
- u_int8_t rate;
+ struct ahc_syncrate *syncrate;
+ u_int period;
+ u_int offset;
+ u_int saved_offset;
- if (message_length != MSG_EXT_SDTR_LEN) {
- AHC_OUTB(ahc, RETURN_1, SEND_REJ);
- ahc->sdtrpending &= ~targ_mask;
+ if (ahc->msgin_buf[1] != MSG_EXT_SDTR_LEN) {
+ reject = TRUE;
break;
}
- period = AHC_INB(ahc, MSGIN_EXT_BYTE0);
- saved_offset = AHC_INB(ahc, MSGIN_EXT_BYTE1);
- targ_scratch = AHC_INB(ahc, TARG_SCRATCH
- + scratch_offset);
- if (targ_scratch & WIDEXFER)
- maxoffset = MAX_OFFSET_16BIT;
- else
- maxoffset = MAX_OFFSET_8BIT;
- offset = MIN(saved_offset, maxoffset);
- ahc_scsirate(ahc, &rate, &period, &offset,
- channel, target);
- /* Preserve the WideXfer flag */
- targ_scratch = rate | (targ_scratch & WIDEXFER);
/*
- * Update both the target scratch area and the
- * current SCSIRATE.
+ * Wait until we have both args before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_SDTR_LEN to account for
+ * the extended message preamble.
*/
- AHC_OUTB(ahc, TARG_SCRATCH + scratch_offset,
- targ_scratch);
- AHC_OUTB(ahc, SCSIRATE, targ_scratch);
+ if (ahc->msgin_index < (MSG_EXT_SDTR_LEN + 1))
+ break;
+
+ period = ahc->msgin_buf[3];
+ 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,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE, /*done*/TRUE);
/*
* See if we initiated Sync Negotiation
* and didn't have to fall down to async
* transfers.
*/
- if ((ahc->sdtrpending & targ_mask) != 0
- && (saved_offset == offset)) {
- /*
- * Don't send an SDTR back to
- * the target
- */
- AHC_OUTB(ahc, RETURN_1, 0);
- ahc->needsdtr &= ~targ_mask;
- ahc->sdtrpending &= ~targ_mask;
+ if (ahc_sent_msg(ahc, MSG_EXT_SDTR, /*full*/TRUE)) {
+ /* We started it */
+ if (saved_offset != offset) {
+ /* Went too low - force async */
+ reject = TRUE;
+ }
} else {
/*
* Send our own SDTR in reply
*/
-#ifdef AHC_DEBUG
- if(ahc_debug & AHC_SHOWMISC)
- printf("Sending SDTR!!\n");
-#endif
- ahc_construct_sdtr(ahc, /*start_byte*/0,
- period, offset);
- AHC_OUTB(ahc, RETURN_1, SEND_MSG);
-
- /*
- * If we aren't starting a re-negotiation
- * because we had to go async in response
- * to a "too low" response from the target
- * clear the needsdtr flag for this target.
- */
- if ((ahc->sdtrpending & targ_mask) == 0)
- ahc->needsdtr &= ~targ_mask;
- else
- ahc->sdtrpending |= targ_mask;
+ if (bootverbose)
+ printf("Sending SDTR!\n");
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_sdtr(ahc, period, offset);
+ ahc->msgout_index = 0;
+ response = TRUE;
}
+ done = MSGLOOP_MSGCOMPLETE;
break;
}
case MSG_EXT_WDTR:
{
- u_int8_t scratch, bus_width;
+ u_int bus_width;
+ u_int sending_reply;
- if (message_length != MSG_EXT_WDTR_LEN) {
- AHC_OUTB(ahc, RETURN_1, SEND_REJ);
- ahc->wdtrpending &= ~targ_mask;
+ sending_reply = FALSE;
+ if (ahc->msgin_buf[1] != MSG_EXT_WDTR_LEN) {
+ reject = TRUE;
break;
}
- bus_width = AHC_INB(ahc, MSGIN_EXT_BYTE0);
- scratch = AHC_INB(ahc, TARG_SCRATCH
- + scratch_offset);
+ /*
+ * Wait until we have our arg before validating
+ * and acting on this message.
+ *
+ * Add one to MSG_EXT_WDTR_LEN to account for
+ * the extended message preamble.
+ */
+ if (ahc->msgin_index < (MSG_EXT_WDTR_LEN + 1))
+ break;
- if (ahc->wdtrpending & targ_mask) {
+ bus_width = ahc->msgin_buf[3];
+ if (ahc_sent_msg(ahc, MSG_EXT_WDTR, /*full*/TRUE)) {
/*
* Don't send a WDTR back to the
* target, since we asked first.
*/
- AHC_OUTB(ahc, RETURN_1, 0);
- switch(bus_width){
- case BUS_8_BIT:
- scratch &= 0x7f;
- break;
- case BUS_16_BIT:
- if(bootverbose)
- printf("%s: target %d using "
- "16Bit transfers\n",
- ahc_name(ahc), target);
- scratch |= WIDEXFER;
- break;
- case BUS_32_BIT:
+ switch (bus_width){
+ default:
/*
- * How can we do 32bit transfers
- * on a 16bit bus?
+ * How can we do anything greater
+ * than 16bit transfers on a 16bit
+ * bus?
*/
- AHC_OUTB(ahc, RETURN_1, SEND_REJ);
- printf("%s: target %d requested 32Bit "
+ reject = TRUE;
+ printf("%s: target %d requested %dBit "
"transfers. Rejecting...\n",
- ahc_name(ahc), target);
+ ahc_name(ahc), devinfo->target,
+ 8 * (0x01 << bus_width));
+ /* FALLTHROUGH */
+ case MSG_EXT_WDTR_BUS_8_BIT:
+ bus_width = MSG_EXT_WDTR_BUS_8_BIT;
break;
- default:
+ case MSG_EXT_WDTR_BUS_16_BIT:
break;
}
} else {
/*
* Send our own WDTR in reply
*/
- switch(bus_width) {
- case BUS_8_BIT:
- scratch &= 0x7f;
- break;
- case BUS_32_BIT:
- case BUS_16_BIT:
- if(ahc->type & AHC_WIDE) {
- /* Negotiate 16_BITS */
- bus_width = BUS_16_BIT;
- if(bootverbose)
- printf("%s: target %d "
- "using 16Bit "
- "transfers\n",
- ahc_name(ahc),
- target);
- scratch |= WIDEXFER;
- } else
- bus_width = BUS_8_BIT;
- break;
+ if (bootverbose)
+ printf("Sending WDTR!\n");
+ 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;
}
- ahc_construct_wdtr(ahc, /*start_byte*/0,
- bus_width);
- AHC_OUTB(ahc, RETURN_1, SEND_MSG);
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_wdtr(ahc, bus_width);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ sending_reply = TRUE;
}
-
- ahc->needwdtr &= ~targ_mask;
- ahc->wdtrpending &= ~targ_mask;
- AHC_OUTB(ahc, TARG_SCRATCH + scratch_offset, scratch);
- AHC_OUTB(ahc, SCSIRATE, scratch);
+ ahc_set_width(ahc, devinfo, bus_width,
+ AHC_TRANS_ACTIVE|AHC_TRANS_GOAL,
+ /*paused*/TRUE, /*done*/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);
+ if (sending_reply == FALSE && reject == FALSE) {
+
+ if (tinfo->goal.period) {
+ 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);
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 0;
+ ahc_construct_sdtr(ahc, period, offset);
+ ahc->msgout_index = 0;
+ response = TRUE;
+ }
+ }
+ done = MSGLOOP_MSGCOMPLETE;
break;
}
default:
/* Unknown extended message. Reject it. */
- AHC_OUTB(ahc, RETURN_1, SEND_REJ);
+ reject = TRUE;
+ break;
}
+ break;
}
- case REJECT_MSG:
- {
- /*
- * 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.
- */
-
- u_char targ_scratch;
-
- targ_scratch = AHC_INB(ahc, TARG_SCRATCH
- + scratch_offset);
-
- if (ahc->wdtrpending & targ_mask){
- /* note 8bit xfers and clear flag */
- targ_scratch &= 0x7f;
- ahc->needwdtr &= ~targ_mask;
- ahc->wdtrpending &= ~targ_mask;
-#if !(defined(__NetBSD__) || defined(__OpenBSD__)) || defined(DEBUG)
- printf("%s:%c:%d: refuses WIDE negotiation. Using "
- "8bit transfers\n", ahc_name(ahc),
- channel, target);
-#endif
- } else if(ahc->sdtrpending & targ_mask){
- /* note asynch xfers and clear flag */
- targ_scratch &= 0xf0;
- ahc->needsdtr &= ~targ_mask;
- ahc->sdtrpending &= ~targ_mask;
-#if !(defined(__NetBSD__) || defined(__OpenBSD__)) || defined(DEBUG)
- printf("%s:%c:%d: refuses synchronous negotiation. "
- "Using asynchronous transfers\n",
- ahc_name(ahc),
- channel, target);
-#endif
- } else {
- /*
- * Otherwise, we ignore it.
- */
-#ifdef AHC_DEBUG
- if(ahc_debug & AHC_SHOWMISC)
- printf("%s:%c:%d: Message reject -- ignored\n",
- ahc_name(ahc), channel, target);
-#endif
+ case MSG_BUS_DEV_RESET:
+ ahc_handle_devreset(ahc, devinfo,
+ XS_RESET, "Bus Device Reset Received",
+ /*verbose_level*/0);
+ restart_sequencer(ahc);
+ done = MSGLOOP_TERMINATED;
+ break;
+ case MSG_ABORT_TAG:
+ case MSG_ABORT:
+ case MSG_CLEAR_QUEUE:
+ /* Target mode messages */
+ if (devinfo->role != ROLE_TARGET) {
+ reject = TRUE;
break;
}
- AHC_OUTB(ahc, TARG_SCRATCH + scratch_offset, targ_scratch);
- AHC_OUTB(ahc, SCSIRATE, targ_scratch);
+#if AHC_TARGET_MODE
+ 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);
+
+ tstate = ahc->enabled_targets[devinfo->our_scsiid];
+ if (tstate != NULL) {
+ struct 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);
+ ahc_send_lstate_events(ahc, lstate);
+ }
+ }
+ done = MSGLOOP_MSGCOMPLETE;
+#else
+ panic("ahc: got target mode message");
+#endif
+ break;
+ case MSG_TERM_IO_PROC:
+ default:
+ reject = TRUE;
break;
}
- case BAD_STATUS:
- {
- int scb_index;
- struct scsi_xfer *xs;
- /* The sequencer will notify us when a command
- * has an error that would be of interest to
- * the kernel. This allows us to leave the sequencer
- * running in the common case of command completes
- * without error.
+ if (reject) {
+ /*
+ * Setup to reject the message.
*/
+ ahc->msgout_index = 0;
+ ahc->msgout_len = 1;
+ ahc->msgout_buf[0] = MSG_MESSAGE_REJECT;
+ done = MSGLOOP_MSGCOMPLETE;
+ response = TRUE;
+ }
- scb_index = AHC_INB(ahc, SCB_TAG);
- scb = ahc->scbarray[scb_index];
+ if (done != MSGLOOP_IN_PROG && !response)
+ /* Clear the outgoing message buffer */
+ ahc->msgout_len = 0;
+ return (done);
+}
+
+STATIC void
+ahc_handle_ign_wide_residue(ahc, devinfo)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
+{
+ u_int scb_index;
+ struct scb *scb;
+
+ scb_index = ahc_inb(ahc, SCB_TAG);
+ scb = &ahc->scb_data->scbarray[scb_index];
+ if ((ahc_inb(ahc, SEQ_FLAGS) & DPHASE) == 0
+ || !(scb->xs->flags & SCSI_DATA_IN)) {
/*
- * Set the default return value to 0 (don't
- * send sense). The sense code will change
- * this if needed and this reduces code
- * duplication.
+ * Ignore the message if we haven't
+ * seen an appropriate data phase yet.
*/
- AHC_OUTB(ahc, RETURN_1, 0);
- if (!(scb && (scb->flags & SCB_ACTIVE))) {
- printf("%s:%c:%d: ahc_intr - referenced scb "
- "not valid during seqint 0x%x scb(%d)\n",
- ahc_name(ahc),
- channel, target, intstat,
- scb_index);
- goto clear;
- }
+ } else {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing. Otherwise, subtract a byte
+ * and update the residual count accordingly.
+ */
+ u_int resid_sgcnt;
- xs = scb->xs;
+ resid_sgcnt = ahc_inb(ahc, SCB_RESID_SGCNT);
+ if (resid_sgcnt == 0
+ && ahc_inb(ahc, DATA_COUNT_ODD) == 1) {
+ /*
+ * If the residual occurred on the last
+ * transfer and the transfer request was
+ * expected to end on an odd count, do
+ * nothing.
+ */
+ } else {
+ u_int data_cnt;
+ u_int data_addr;
+ u_int sg_index;
- scb->status = AHC_INB(ahc, SCB_TARGET_STATUS);
+ data_cnt = (ahc_inb(ahc, SCB_RESID_DCNT + 2) << 16)
+ | (ahc_inb(ahc, SCB_RESID_DCNT + 1) << 8)
+ | (ahc_inb(ahc, SCB_RESID_DCNT));
-#ifdef AHC_DEBUG
- if((ahc_debug & AHC_SHOWSCBS)
- && xs->sc_link->target == DEBUGTARGET)
- ahc_print_scb(scb);
-#endif
- xs->status = scb->status;
- switch(scb->status){
- case SCSI_OK:
- printf("%s: Interrupted for staus of"
- " 0???\n", ahc_name(ahc));
- break;
- case SCSI_CHECK:
-#ifdef AHC_DEBUG
- if(ahc_debug & AHC_SHOWSENSE)
- {
+ data_addr = (ahc_inb(ahc, SHADDR + 3) << 24)
+ | (ahc_inb(ahc, SHADDR + 2) << 16)
+ | (ahc_inb(ahc, SHADDR + 1) << 8)
+ | (ahc_inb(ahc, SHADDR));
- sc_print_addr(xs->sc_link);
- printf("requests Check Status\n");
- }
-#endif
+ data_cnt += 1;
+ data_addr -= 1;
- if ((xs->error == XS_NOERROR)
- && !(scb->flags & SCB_SENSE)) {
- struct ahc_dma_seg *sg = scb->ahc_dma;
- struct scsi_sense *sc = &(scb->sense_cmd);
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWSENSE)
- {
- sc_print_addr(xs->sc_link);
- printf("Sending Sense\n");
- }
-#endif
-#if defined(__FreeBSD__)
- sc->op_code = REQUEST_SENSE;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
- sc->opcode = REQUEST_SENSE;
-#endif
- sc->byte2 = xs->sc_link->lun << 5;
- sc->length = sizeof(struct scsi_sense_data);
- sc->control = 0;
- sg->addr = KVTOPHYS(&xs->sense);
- sg->len = sizeof(struct scsi_sense_data);
+ sg_index = scb->sg_count - resid_sgcnt;
- scb->control &= DISCENB;
- scb->status = 0;
- scb->SG_segment_count = 1;
- scb->SG_list_pointer = KVTOPHYS(sg);
- scb->data = sg->addr;
- scb->datalen = sg->len;
-#ifdef AHC_BROKEN_CACHE
- if (ahc_broken_cache)
- INVALIDATE_CACHE();
-#endif
- scb->cmdpointer = KVTOPHYS(sc);
- scb->cmdlen = sizeof(*sc);
-
- scb->flags |= SCB_SENSE;
- ahc_send_scb(ahc, scb);
- /*
- * Ensure that the target is "BUSY"
- * so we don't get overlapping
- * commands if we happen to be doing
- * tagged I/O.
- */
- ahc_busy_target(ahc, target, channel);
+ if (sg_index != 0
+ && (scb->sg_list[sg_index].len < data_cnt)) {
+ u_int sg_addr;
+ sg_index--;
+ data_cnt = 1;
+ data_addr = scb->sg_list[sg_index].addr
+ + scb->sg_list[sg_index].len - 1;
+
/*
- * Make us the next command to run
+ * The physical address base points to the
+ * second entry as it is always used for
+ * calculating the "next S/G pointer".
*/
- ahc_add_waiting_scb(ahc, scb);
- AHC_OUTB(ahc, RETURN_1, SEND_SENSE);
- break;
+ 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);
}
- /*
- * Clear the SCB_SENSE Flag and have
- * the sequencer do a normal command
- * complete with either a "DRIVER_STUFFUP"
- * error or whatever other error condition
- * we already had.
- */
- scb->flags &= ~SCB_SENSE;
- if (xs->error == XS_NOERROR)
- xs->error = XS_DRIVER_STUFFUP;
- break;
- case SCSI_BUSY:
- xs->error = XS_BUSY;
- sc_print_addr(xs->sc_link);
- printf("Target Busy\n");
- break;
- case SCSI_QUEUE_FULL:
- /*
- * The upper level SCSI code will someday
- * handle this properly.
- */
- sc_print_addr(xs->sc_link);
- printf("Queue Full\n");
- scb->flags |= SCB_ASSIGNEDQ;
- STAILQ_INSERT_TAIL(&ahc->assigned_scbs,scb, links);
- AHC_OUTB(ahc, RETURN_1, SEND_SENSE);
- break;
- default:
- sc_print_addr(xs->sc_link);
- printf("unexpected targ_status: %x\n", scb->status);
- xs->error = XS_DRIVER_STUFFUP;
- break;
- }
- break;
- }
- case RESIDUAL:
- {
- int scb_index;
- struct scsi_xfer *xs;
- scb_index = AHC_INB(ahc, SCB_TAG);
- scb = ahc->scbarray[scb_index];
- xs = scb->xs;
- /*
- * Don't clobber valid resid info with
- * a resid coming from a check sense
- * operation.
- */
- if (!(scb->flags & SCB_SENSE)) {
- int resid_sgs;
-
- /*
- * Remainder of the SG where the transfer
- * stopped.
- */
- xs->resid = (AHC_INB(ahc, SCB_RESID_DCNT2)<<16) |
- (AHC_INB(ahc, SCB_RESID_DCNT1)<<8) |
- AHC_INB(ahc, SCB_RESID_DCNT0);
-
- /*
- * Add up the contents of all residual
- * SG segments that are after the SG where
- * the transfer stopped.
- */
- resid_sgs = AHC_INB(ahc, SCB_RESID_SGCNT) - 1;
- while (resid_sgs > 0) {
- int sg;
-
- sg = scb->SG_segment_count - resid_sgs;
- xs->resid += scb->ahc_dma[sg].len;
- resid_sgs--;
- }
+ 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);
-#if defined(__FreeBSD__)
- xs->flags |= SCSI_RESID_VALID;
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
- /* XXX - Update to do this right */
-#endif
-#ifdef AHC_DEBUG
- if (ahc_debug & AHC_SHOWMISC) {
- sc_print_addr(xs->sc_link);
- printf("Handled Residual of %ld bytes\n"
- ,xs->resid);
- }
-#endif
+ 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);
}
- break;
}
- case ABORT_TAG:
- {
- int scb_index;
- struct scsi_xfer *xs;
+}
- scb_index = AHC_INB(ahc, SCB_TAG);
- scb = ahc->scbarray[scb_index];
- xs = scb->xs;
- /*
- * We didn't recieve a valid tag back from
- * the target on a reconnect.
- */
- sc_print_addr(xs->sc_link);
- printf("invalid tag received -- sending ABORT_TAG\n");
- xs->error = XS_DRIVER_STUFFUP;
- untimeout(ahc_timeout, (caddr_t)scb);
- ahc_done(ahc, scb);
- break;
- }
- case AWAITING_MSG:
- {
- int scb_index;
- scb_index = AHC_INB(ahc, SCB_TAG);
- scb = ahc->scbarray[scb_index];
- /*
- * This SCB had a zero length command, informing
- * the sequencer that we wanted to send a special
- * message to this target. We only do this for
- * BUS_DEVICE_RESET messages currently.
- */
- if (scb->flags & SCB_DEVICE_RESET) {
- AHC_OUTB(ahc, MSG0,
- MSG_BUS_DEV_RESET);
- AHC_OUTB(ahc, MSG_LEN, 1);
- printf("Bus Device Reset Message Sent\n");
- } else if (scb->flags & SCB_MSGOUT_WDTR) {
- ahc_construct_wdtr(ahc, AHC_INB(ahc, MSG_LEN),
- BUS_16_BIT);
- } else if (scb->flags & SCB_MSGOUT_SDTR) {
- u_int8_t target_scratch;
- u_int8_t ultraenable;
- int sxfr;
- int i;
+STATIC void
+ahc_handle_devreset(ahc, devinfo, status, message, verbose_level)
+ struct ahc_softc *ahc;
+ struct ahc_devinfo *devinfo;
+ int status;
+ char *message;
+ int verbose_level;
+{
+ int found;
- /* Pull the user defined setting */
- target_scratch = AHC_INB(ahc, TARG_SCRATCH
- + scratch_offset);
-
- sxfr = target_scratch & SXFR;
- if (scratch_offset < 8)
- ultraenable = AHC_INB(ahc, ULTRA_ENB);
- else
- ultraenable = AHC_INB(ahc, ULTRA_ENB + 1);
-
- if (ultraenable & targ_mask)
- /* Want an ultra speed in the table */
- sxfr |= 0x100;
-
- for (i = 0; i < ahc_num_syncrates; i++)
- if (sxfr == ahc_syncrates[i].sxfr)
- break;
-
- ahc_construct_sdtr(ahc, AHC_INB(ahc, MSG_LEN),
- ahc_syncrates[i].period,
- target_scratch & WIDEXFER ?
- MAX_OFFSET_16BIT : MAX_OFFSET_8BIT);
- } else
- panic("ahc_intr: AWAITING_MSG for an SCB that "
- "does not have a waiting message");
- break;
- }
- case IMMEDDONE:
- {
- /*
- * Take care of device reset messages
- */
- u_char scbindex = AHC_INB(ahc, SCB_TAG);
- scb = ahc->scbarray[scbindex];
- if (scb->flags & SCB_DEVICE_RESET) {
- u_char targ_scratch;
- int found;
- /*
- * Go back to async/narrow transfers and
- * renegotiate.
- */
- ahc_unbusy_target(ahc, target, channel);
- ahc->needsdtr |= ahc->needsdtr_orig & targ_mask;
- ahc->needwdtr |= ahc->needwdtr_orig & targ_mask;
- ahc->sdtrpending &= ~targ_mask;
- ahc->wdtrpending &= ~targ_mask;
- targ_scratch = AHC_INB(ahc, TARG_SCRATCH
- + scratch_offset);
- targ_scratch &= SXFR;
- AHC_OUTB(ahc, TARG_SCRATCH + scratch_offset,
- targ_scratch);
- found = ahc_reset_device(ahc, target,
- channel, SCB_LIST_NULL,
- XS_NOERROR);
- sc_print_addr(scb->xs->sc_link);
- printf("Bus Device Reset delivered. "
- "%d SCBs aborted\n", found);
- ahc->in_timeout = FALSE;
- ahc_run_done_queue(ahc);
- } else
- panic("ahc_intr: Immediate complete for "
- "unknown operation.");
- break;
- }
- case DATA_OVERRUN:
- {
- /*
- * When the sequencer detects an overrun, it
- * sets STCNT to 0x00ffffff and allows the
- * target to complete its transfer in
- * BITBUCKET mode.
- */
- u_char scbindex = AHC_INB(ahc, SCB_TAG);
- u_int32_t overrun;
- scb = ahc->scbarray[scbindex];
- overrun = AHC_INB(ahc, STCNT0)
- | (AHC_INB(ahc, STCNT1) << 8)
- | (AHC_INB(ahc, STCNT2) << 16);
- overrun = 0x00ffffff - overrun;
- sc_print_addr(scb->xs->sc_link);
- printf("data overrun of %d bytes detected."
- " Forcing a retry.\n", overrun);
- /*
- * Set this and it will take affect when the
- * target does a command complete.
- */
- scb->xs->error = XS_DRIVER_STUFFUP;
- break;
- }
-#if NOT_YET
- /* XXX Fill these in later */
- case MESG_BUFFER_BUSY:
- break;
- case MSGIN_PHASEMIS:
- break;
-#endif
- default:
- printf("ahc_intr: seqint, "
- "intstat == 0x%x, scsisigi = 0x%x\n",
- intstat, AHC_INB(ahc, SCSISIGI));
- break;
- }
+ found = ahc_abort_scbs(ahc, devinfo->target, devinfo->channel,
+ ALL_LUNS, SCB_LIST_NULL, devinfo->role,
+ status);
-clear:
/*
- * Clear the upper byte that holds SEQINT status
- * codes and clear the SEQINT bit.
+ * Go back to async/narrow transfers and renegotiate.
+ * ahc_set_width and ahc_set_syncrate can cope with NULL
+ * paths.
*/
- AHC_OUTB(ahc, CLRINT, CLRSEQINT);
-
- /*
- * The sequencer is paused immediately on
- * a SEQINT, so we should restart it when
- * we're done.
- */
- unpause_sequencer(ahc, /*unpause_always*/TRUE);
+ ahc_set_width(ahc, devinfo, MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR, /*paused*/TRUE, /*done*/FALSE);
+ ahc_set_syncrate(ahc, devinfo, /*syncrate*/NULL,
+ /*period*/0, /*offset*/0, AHC_TRANS_CUR,
+ /*paused*/TRUE, /*done*/FALSE);
+
+ 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 a scb which has been processed by the
+ * We have an scb which has been processed by the
* adaptor, now we look to see how the operation
* went.
*/
-static void
+STATIC void
ahc_done(ahc, scb)
- struct ahc_data *ahc;
+ struct ahc_softc *ahc;
struct scb *scb;
{
struct scsi_xfer *xs = scb->xs;
+ struct scsi_link *sc_link = xs->sc_link;
+ int requeue = 0;
+ int target;
SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahc_done\n"));
+
+ LIST_REMOVE(scb, pend_links);
+
+ untimeout(ahc_timeout, (caddr_t)scb);
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWCMDS) {
+ sc_print_addr(sc_link);
+ printf("ahc_done opcode %d tag %x\n", xs->cmdstore.opcode,
+ scb->hscb->tag);
+ }
+#endif
+
+ target = sc_link->target;
+
+ if (xs->datalen) {
+ int op;
+
+ if ((xs->flags & SCSI_DATA_IN) != 0)
+ op = BUS_DMASYNC_POSTREAD;
+ else
+ op = BUS_DMASYNC_POSTWRITE;
+ bus_dmamap_sync(ahc->sc_dmat, scb->dmamap, op);
+ bus_dmamap_unload(ahc->sc_dmat, scb->dmamap);
+ }
+
/*
- * Put the results of the operation
- * into the xfer and call whoever started it
+ * Unbusy this target/channel/lun.
+ * XXX if we are holding two commands per lun,
+ * send the next command.
*/
-#if defined(__NetBSD__) || defined(__OpenBSD__)
- if (xs->error != XS_NOERROR) {
- /* Don't override the error value. */
- } else if (scb->flags & SCB_ABORTED) {
- xs->error = XS_DRIVER_STUFFUP;
- } else
-#endif
- if(scb->flags & SCB_SENSE)
- xs->error = XS_SENSE;
- if(scb->flags & SCB_SENTORDEREDTAG)
- ahc->in_timeout = FALSE;
-#if defined(__FreeBSD__)
- if ((xs->flags & SCSI_ERR_OK) && !(xs->error == XS_SENSE)) {
- /* All went correctly OR errors expected */
- xs->error = XS_NOERROR;
- }
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
+ ahc_index_busy_tcl(ahc, scb->hscb->tcl, /*unbusy*/TRUE);
+
/*
- * Since NetBSD nor OpenBSD doesn't have error ignoring operation mode
- * (SCSI_ERR_OK in FreeBSD), we don't have to care this case.
+ * If the recovery SCB completes, we have to be
+ * out of our timeout.
*/
-#endif
- xs->flags |= ITSDONE;
-#ifdef AHC_TAGENABLE
- if(xs->cmd->opcode == INQUIRY && xs->error == XS_NOERROR)
- {
- struct scsi_inquiry_data *inq_data;
- u_short mask = 0x01 << (xs->sc_link->target |
- (scb->tcl & 0x08));
+ if ((scb->flags & SCB_RECOVERY_SCB) != 0) {
+
+ struct scb *scbp;
+
/*
- * Sneak a look at the results of the SCSI Inquiry
- * command and see if we can do Tagged queing. This
- * should really be done by the higher level drivers.
+ * We were able to complete the command successfully,
+ * so reinstate the timeouts for all other pending
+ * commands.
*/
- inq_data = (struct scsi_inquiry_data *)xs->data;
- if((inq_data->flags & SID_CmdQue) && !(ahc->tagenable & mask))
- {
- printf("%s: target %d Tagged Queuing Device\n",
- ahc_name(ahc), xs->sc_link->target);
- ahc->tagenable |= mask;
- if(ahc->maxhscbs >= 16 || (ahc->flags & AHC_PAGESCBS)) {
- /* Default to 8 tags */
- xs->sc_link->opennings += 6;
- }
- else
- {
- /*
- * Default to 4 tags on whimpy
- * cards that don't have much SCB
- * space and can't page. This prevents
- * a single device from hogging all
- * slots. We should really have a better
- * way of providing fairness.
- */
- xs->sc_link->opennings += 2;
+ scbp = ahc->pending_scbs.lh_first;
+ while (scbp != NULL) {
+ struct scsi_xfer *txs = scbp->xs;
+
+ if (!(txs->flags & SCSI_POLL)) {
+ timeout(ahc_timeout, scbp,
+ (scbp->xs->timeout * hz)/1000);
}
+ scbp = LIST_NEXT(scbp, pend_links);
}
+
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ 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);
+ sc_print_addr(xs->sc_link);
+ 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) {
+ /*
+ * We performed autosense retrieval.
+ *
+ * bzero the sense data before having
+ * the drive fill it. 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.
+ */
+ bzero(&xs->sense, sizeof(struct scsi_sense));
+ bcopy(&ahc->scb_data->sense[scb->hscb->tag],
+ &xs->sense, scb->sg_list->len);
+ xs->error = XS_SENSE;
+ }
+ if (scb->flags & SCB_FREEZE_QUEUE) {
+ ahc->devqueue_blocked[target]--;
+ scb->flags &= ~SCB_FREEZE_QUEUE;
+ }
+
+ requeue = scb->flags & SCB_REQUEUE;
+ ahcfreescb(ahc, scb);
+
+ if (requeue) {
+ /*
+ * Re-insert at the front of the private queue to
+ * preserve order.
+ */
+ int s;
+
+ s = splbio();
+ /* TAILQ_INSERT_HEAD(&ahc->sc_q, xs, adapter_q); */
+ ahc_list_insert_head(ahc, xs);
+ splx(s);
+ } else {
+ xs->flags |= ITSDONE;
+ scsi_done(xs);
}
-#endif
- ahc_free_scb(ahc, scb, xs->flags);
- scsi_done(xs);
-#if defined(__NetBSD__) || defined(__OpenBSD__) /* XXX */
/*
* If there are entries in the software queue, try to
* run the first one. We should be more or less guaranteed
@@ -2029,9 +3521,30 @@ ahc_done(ahc, scb)
* NOTE: ahc_scsi_cmd() relies on our calling it with
* the first entry in the queue.
*/
- if (ahc->sc_xxxq.lh_first != NULL)
- (void) ahc_scsi_cmd(ahc->sc_xxxq.lh_first);
-#endif /* __NetBSD__ || __OpenBSD__ */
+ if ((xs = ahc->sc_xxxq.lh_first) != NULL)
+ (void) ahc_scsi_cmd(xs);
+}
+
+/*
+ * Determine the number of SCBs available on the controller
+ */
+int
+ahc_probe_scbs(ahc)
+ 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)
+ break;
+ ahc_outb(ahc, SCBPTR, 0);
+ if (ahc_inb(ahc, SCB_CONTROL) != 0)
+ break;
+ }
+
+ return (i);
}
/*
@@ -2039,435 +3552,555 @@ ahc_done(ahc, scb)
*/
int
ahc_init(ahc)
- struct ahc_data *ahc;
+ struct ahc_softc *ahc;
{
- u_int8_t scsi_conf, sblkctl, i;
- u_int16_t ultraenable = 0;
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;
+ struct scb_data *scb_data = NULL;
+
+#ifdef AHC_PRINT_SRAM
+ printf("Scratch Ram:");
+ for (i = 0x20; i < 0x5f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ if ((ahc->features & AHC_MORE_SRAM) != 0) {
+ for (i = 0x70; i < 0x7f; i++) {
+ if (((i % 8) == 0) && (i != 0)) {
+ printf ("\n ");
+ }
+ printf (" 0x%x", ahc_inb(ahc, i));
+ }
+ }
+ printf ("\n");
+#endif
+
+ if (ahc->scb_data == NULL) {
+ scb_data = malloc(sizeof (struct scb_data), M_DEVBUF, M_NOWAIT);
+ if (scb_data == NULL) {
+ printf("%s: cannot malloc scb_data!\n", ahc_name(ahc));
+ return (ENOMEM);
+ }
+ bzero(scb_data, sizeof(struct scb_data));
+ ahc->scb_data = scb_data;
+ }
/*
* 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;
+
+ /*
+ * 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.
+ */
+ driver_data_size = 3 * 256 * sizeof(u_int8_t);
- /* Handle the SCBPAGING option */
-#ifndef AHC_SCBPAGING_ENABLE
- ahc->flags &= ~AHC_PAGESCBS;
-#endif
+ if (ahc_createdmamem(ahc, driver_data_size,
+ &ahc->shared_data_dmamap, (caddr_t *)&ahc->qoutfifo,
+ &ahc->shared_data_busaddr, &ahc->shared_data_seg,
+ &ahc->shared_data_nseg, "shared data") < 0)
+ return (ENOMEM);
- /* Determine channel configuration and who we are on the scsi bus. */
- switch ( (sblkctl = AHC_INB(ahc, SBLKCTL) & 0x0a) ) {
- case 0:
- ahc->our_id = (AHC_INB(ahc, SCSICONF) & HSCSIID);
- ahc->flags &= ~AHC_CHANNEL_B_PRIMARY;
- if(ahc->type == AHC_394)
- printf("Channel %c, SCSI Id=%d, ",
- ahc->flags & AHC_CHNLB ? 'B' : 'A',
- ahc->our_id);
- else
- printf("Single Channel, SCSI Id=%d, ", ahc->our_id);
- AHC_OUTB(ahc, FLAGS, SINGLE_BUS | (ahc->flags & AHC_PAGESCBS));
- break;
- case 2:
- ahc->our_id = (AHC_INB(ahc, SCSICONF + 1) & HWSCSIID);
- ahc->flags &= ~AHC_CHANNEL_B_PRIMARY;
- if(ahc->type == AHC_394)
- printf("Wide Channel %c, SCSI Id=%d, ",
- ahc->flags & AHC_CHNLB ? 'B' : 'A',
- ahc->our_id);
- else
- printf("Wide Channel, SCSI Id=%d, ", ahc->our_id);
- ahc->type |= AHC_WIDE;
- AHC_OUTB(ahc, FLAGS, WIDE_BUS | (ahc->flags & AHC_PAGESCBS));
- break;
- case 8:
- ahc->our_id = (AHC_INB(ahc, SCSICONF) & HSCSIID);
- ahc->our_id_b = (AHC_INB(ahc, SCSICONF + 1) & HSCSIID);
- printf("Twin Channel, A SCSI Id=%d, B SCSI Id=%d, ",
- ahc->our_id, ahc->our_id_b);
- ahc->type |= AHC_TWIN;
- AHC_OUTB(ahc, FLAGS, TWIN_BUS | (ahc->flags & AHC_PAGESCBS));
- break;
- default:
- printf(" Unsupported adapter type. Ignoring\n");
- return(-1);
- }
+ ahc->init_level++;
- /* Determine the number of SCBs */
+ /* Allocate SCB data now that sc_dmat is initialized */
+ if (ahc->scb_data->maxhscbs == 0)
+ if (ahcinitscbdata(ahc) != 0)
+ return (ENOMEM);
- {
- AHC_OUTB(ahc, SCBPTR, 0);
- AHC_OUTB(ahc, SCB_CONTROL, 0);
- for(i = 1; i < AHC_SCB_MAX; i++) {
- AHC_OUTB(ahc, SCBPTR, i);
- AHC_OUTB(ahc, SCB_CONTROL, i);
- if(AHC_INB(ahc, SCB_CONTROL) != i)
- break;
- AHC_OUTB(ahc, SCBPTR, 0);
- if(AHC_INB(ahc, SCB_CONTROL) != 0)
- break;
- /* Clear the control byte. */
- AHC_OUTB(ahc, SCBPTR, i);
- AHC_OUTB(ahc, SCB_CONTROL, 0);
+ 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;
- ahc->qcntmask |= i; /* Update the count mask. */
- }
+ /* All of our queues are empty */
+ for (i = 0; i < 256; i++)
+ ahc->qoutfifo[i] = SCB_LIST_NULL;
- /* Ensure we clear the 0 SCB's control byte. */
- AHC_OUTB(ahc, SCBPTR, 0);
- AHC_OUTB(ahc, SCB_CONTROL, 0);
+ /*
+ * Allocate a tstate to house information for our
+ * initiator presence on the bus as well as the user
+ * data for any target mode initiator.
+ */
+ if (ahc_alloc_tstate(ahc, ahc->our_id, 'A') == NULL) {
+ printf("%s: unable to allocate tmode_tstate. "
+ "Failing attach\n", ahc_name(ahc));
+ return (-1);
+ }
- ahc->qcntmask |= i;
- ahc->maxhscbs = i;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ if (ahc_alloc_tstate(ahc, ahc->our_id_b, 'B') == NULL) {
+ printf("%s: unable to allocate tmode_tstate. "
+ "Failing attach\n", ahc_name(ahc));
+ return (-1);
+ }
+ printf("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) {
+ printf("Wide ");
+ } else {
+ printf("Single ");
+ }
+ printf("Channel %c, SCSI Id=%d, ", ahc->channel, ahc->our_id);
}
- if((ahc->maxhscbs < AHC_SCB_MAX) && (ahc->flags & AHC_PAGESCBS))
- ahc->maxscbs = AHC_SCB_MAX;
- else {
- ahc->maxscbs = ahc->maxhscbs;
+ ahc_outb(ahc, SEQ_FLAGS, 0);
+
+ if (ahc->scb_data->maxhscbs < AHC_SCB_MAX) {
+ ahc->flags |= AHC_PAGESCBS;
+ printf("%d/%d SCBs\n", ahc->scb_data->maxhscbs, AHC_SCB_MAX);
+ } else {
ahc->flags &= ~AHC_PAGESCBS;
+ printf("%d SCBs\n", ahc->scb_data->maxhscbs);
}
- printf("%d SCBs\n", ahc->maxhscbs);
-
#ifdef AHC_DEBUG
- if(ahc_debug & AHC_SHOWMISC) {
- struct scb test;
- printf("%s: hardware scb %ld bytes; kernel scb; "
+ if (ahc_debug & AHC_SHOWMISC) {
+ printf("%s: hardware scb %d bytes; kernel scb %d bytes; "
"ahc_dma %d bytes\n",
ahc_name(ahc),
- (u_long)&(test.next) - (u_long)(&test),
- sizeof(test),
+ sizeof(struct hardware_scb),
+ sizeof(struct scb),
sizeof(struct ahc_dma_seg));
}
#endif /* AHC_DEBUG */
- /* Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels*/
- if(ahc->type & AHC_TWIN)
- {
+ /* Set the SCSI Id,SXFRCTL0,SXFRCTL1, and SIMODE1, for both channels*/
+ if (ahc->features & AHC_TWIN) {
+
/*
* The device is gated to channel B after a chip reset,
* so set those values first
*/
- AHC_OUTB(ahc, SCSIID, ahc->our_id_b);
- scsi_conf = AHC_INB(ahc, SCSICONF + 1);
- AHC_OUTB(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
- | ENSTIMER|ACTNEGEN|STPWEN);
- AHC_OUTB(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
- if(ahc->type & AHC_ULTRA)
- AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN|ULTRAEN);
+ 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, SXFRCTL0, DFON|SPIOEN);
+ 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);
+ ahc_outb(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
+ ahc_outb(ahc, SXFRCTL0, DFON|SPIOEN);
- if(scsi_conf & RESET_SCSI) {
- /* Reset the bus */
-#if (!defined(__NetBSD__) && !defined(__OpenBSD__)) || defined(DEBUG)
- if(bootverbose)
- printf("%s: Resetting Channel B\n",
- ahc_name(ahc));
-#endif
- AHC_OUTB(ahc, SCSISEQ, SCSIRSTO);
- DELAY(1000);
- AHC_OUTB(ahc, SCSISEQ, 0);
-
- /* Ensure we don't get a RSTI interrupt from this */
- AHC_OUTB(ahc, CLRSINT1, CLRSCSIRSTI);
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
- }
+ if ((scsi_conf & RESET_SCSI) != 0
+ && (ahc->flags & AHC_INITIATORMODE) != 0)
+ ahc->flags |= AHC_RESET_BUS_B;
/* Select Channel A */
- AHC_OUTB(ahc, SBLKCTL, 0);
- }
- AHC_OUTB(ahc, SCSIID, ahc->our_id);
- scsi_conf = AHC_INB(ahc, SCSICONF);
- AHC_OUTB(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
- | ENSTIMER|ACTNEGEN|STPWEN);
- AHC_OUTB(ahc, SIMODE1, ENSELTIMO|ENSCSIRST|ENSCSIPERR);
- if(ahc->type & AHC_ULTRA)
- AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN|ULTRAEN);
- else
- AHC_OUTB(ahc, SXFRCTL0, DFON|SPIOEN);
-
- if(scsi_conf & RESET_SCSI) {
- /* Reset the bus */
-#if (!defined(__NetBSD__) && !defined(__OpenBSD__)) || defined(DEBUG)
- if(bootverbose)
- printf("%s: Resetting Channel A\n", ahc_name(ahc));
-#endif
-
- AHC_OUTB(ahc, SCSISEQ, SCSIRSTO);
- DELAY(1000);
- AHC_OUTB(ahc, SCSISEQ, 0);
-
- /* Ensure we don't get a RSTI interrupt from this */
- AHC_OUTB(ahc, CLRSINT1, CLRSCSIRSTI);
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
+ 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);
+ else
+ ahc_outb(ahc, SCSIID, ahc->our_id);
+ scsi_conf = ahc_inb(ahc, SCSICONF);
+ ahc_outb(ahc, SXFRCTL1, (scsi_conf & (ENSPCHK|STIMESEL))
+ |term
+ |ENSTIMER|ACTNEGEN);
+ 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_RESET_BUS_A;
/*
* Look at the information that board initialization or
- * the board bios has left us. In the lower four bits of each
- * target's scratch space any value other than 0 indicates
- * that we should initiate synchronous transfers. If it's zero,
- * the user or the BIOS has decided to disable synchronous
- * negotiation to that target so we don't activate the needsdtr
- * flag.
+ * the board bios has left us.
*/
- ahc->needsdtr_orig = 0;
- ahc->needwdtr_orig = 0;
+ ultraenb = 0;
+ tagenable = ALL_TARGETS_MASK;
/* Grab the disconnection disable table and invert it for our needs */
- if(ahc->flags & AHC_USEDEFAULTS) {
+ if (ahc->flags & AHC_USEDEFAULTS) {
printf("%s: Host Adapter Bios disabled. Using default SCSI "
"device parameters\n", ahc_name(ahc));
- ahc->discenable = 0xff;
+ ahc->flags |= AHC_EXTENDED_TRANS_A|AHC_EXTENDED_TRANS_B|
+ AHC_TERM_ENB_A|AHC_TERM_ENB_B;
+ discenable = ALL_TARGETS_MASK;
+ if ((ahc->features & AHC_ULTRA) != 0)
+ ultraenb = ALL_TARGETS_MASK;
+ } else {
+ discenable = ~((ahc_inb(ahc, DISC_DSB + 1) << 8)
+ | ahc_inb(ahc, DISC_DSB));
+ if ((ahc->features & (AHC_ULTRA|AHC_ULTRA2)) != 0)
+ ultraenb = (ahc_inb(ahc, ULTRA_ENB + 1) << 8)
+ | ahc_inb(ahc, ULTRA_ENB);
}
- else
- ahc->discenable = ~((AHC_INB(ahc, DISC_DSB + 1) << 8)
- | AHC_INB(ahc, DISC_DSB));
- if(!(ahc->type & (AHC_WIDE|AHC_TWIN)))
+ if ((ahc->features & (AHC_WIDE|AHC_TWIN)) == 0)
max_targ = 7;
- for(i = 0; i <= max_targ; i++){
- u_char target_settings;
- if (ahc->flags & AHC_USEDEFAULTS) {
- target_settings = 0; /* 10MHz */
- ahc->needsdtr_orig |= (0x01 << i);
- ahc->needwdtr_orig |= (0x01 << i);
+ for (i = 0; i <= max_targ; i++) {
+ struct ahc_initiator_tinfo *tinfo;
+ struct tmode_tstate *tstate;
+ u_int our_id;
+ u_int target_id;
+ char channel;
+
+ channel = 'A';
+ our_id = ahc->our_id;
+ target_id = i;
+ if (i > 7 && (ahc->features & AHC_TWIN) != 0) {
+ channel = 'B';
+ our_id = ahc->our_id_b;
+ target_id = i % 8;
}
- else {
+ tinfo = ahc_fetch_transinfo(ahc, channel, our_id,
+ target_id, &tstate);
+ /* Default to async narrow across the board */
+ bzero(tinfo, sizeof(*tinfo));
+ if (ahc->flags & AHC_USEDEFAULTS) {
+ if ((ahc->features & AHC_WIDE) != 0)
+ tinfo->user.width = MSG_EXT_WDTR_BUS_16_BIT;
+
+ /*
+ * These will be truncated when we determine the
+ * connection type we have with the target.
+ */
+ tinfo->user.period = ahc_syncrates->period;
+ tinfo->user.offset = ~0;
+ } else {
+ u_int scsirate;
+ u_int16_t mask;
+
/* Take the settings leftover in scratch RAM. */
- target_settings = AHC_INB(ahc, TARG_SCRATCH + i);
+ scsirate = ahc_inb(ahc, TARG_SCSIRATE + i);
+ mask = (0x01 << i);
+ if ((ahc->features & AHC_ULTRA2) != 0) {
+ u_int offset;
+ u_int maxsync;
- if(target_settings & 0x0f){
- ahc->needsdtr_orig |= (0x01 << i);
- /*Default to asynchronous transfers(0 offset)*/
- target_settings &= 0xf0;
- }
- if(target_settings & 0x80){
- ahc->needwdtr_orig |= (0x01 << i);
- /*
- * We'll set the Wide flag when we
- * are successful with Wide negotiation.
- * Turn it off for now so we aren't
- * confused.
- */
- target_settings &= 0x7f;
- }
- if(ahc->type & AHC_ULTRA) {
- /*
- * Enable Ultra for any target that
- * has a valid ultra syncrate setting.
- */
- u_char rate = target_settings & 0x70;
- if(rate == 0x00 || rate == 0x10 ||
- rate == 0x20 || rate == 0x40) {
- if(rate == 0x40) {
- /* Treat 10MHz specially */
- target_settings &= ~0x70;
- }
- else
- ultraenable |= (0x01 << i);
- }
+ if ((scsirate & SOFS) == 0x0F) {
+ /*
+ * Haven't negotiated yet,
+ * so the format is different.
+ */
+ scsirate = (scsirate & SXFR) >> 4
+ | (ultraenb & mask)
+ ? 0x08 : 0x0
+ | (scsirate & WIDEXFER);
+ offset = MAX_OFFSET_ULTRA2;
+ } else
+ offset = ahc_inb(ahc, TARG_OFFSET + i);
+ maxsync = AHC_SYNCRATE_ULTRA2;
+ if ((ahc->features & AHC_DT) != 0)
+ maxsync = AHC_SYNCRATE_DT;
+ tinfo->user.period =
+ ahc_find_period(ahc, scsirate, maxsync);
+ if (offset == 0)
+ tinfo->user.period = 0;
+ else
+ tinfo->user.offset = ~0;
+ } else if ((scsirate & SOFS) != 0) {
+ tinfo->user.period =
+ ahc_find_period(ahc, scsirate,
+ (ultraenb & mask)
+ ? AHC_SYNCRATE_ULTRA
+ : AHC_SYNCRATE_FAST);
+ 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;
}
- AHC_OUTB(ahc, TARG_SCRATCH+i,target_settings);
+ tinfo->goal = tinfo->user; /* force negotiation */
+ tstate->ultraenb = ultraenb;
+ tstate->discenable = discenable;
+ tstate->tagenable = 0; /* Wait until the XPT says its okay */
}
+ ahc->user_discenable = discenable;
+ ahc->user_tagenable = tagenable;
+
+ /*
+ * Tell the sequencer where it can find our arrays in memory.
+ */
+ physaddr = ahc->scb_data->hscb_busaddr;
+ ahc_outb(ahc, HSCB_ADDR, physaddr & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 1, (physaddr >> 8) & 0xFF);
+ ahc_outb(ahc, HSCB_ADDR + 2, (physaddr >> 16) & 0xFF);
+ 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);
+
/*
- * If we are not a WIDE device, forget WDTR. This
- * makes the driver work on some cards that don't
- * leave these fields cleared when the BIOS is not
- * installed.
+ * Initialize the group code to command length table.
+ * This overrides the values in TARG_SCSIRATE, so only
+ * setup the table after we have processed that information.
*/
- if(!(ahc->type & AHC_WIDE))
- ahc->needwdtr_orig = 0;
- ahc->needsdtr = ahc->needsdtr_orig;
- ahc->needwdtr = ahc->needwdtr_orig;
- ahc->sdtrpending = 0;
- ahc->wdtrpending = 0;
- ahc->tagenable = 0;
- ahc->orderedtag = 0;
-
- AHC_OUTB(ahc, ULTRA_ENB, ultraenable & 0xff);
- AHC_OUTB(ahc, ULTRA_ENB + 1, (ultraenable >> 8) & 0xff);
+ ahc_outb(ahc, CMDSIZE_TABLE, 5);
+ ahc_outb(ahc, CMDSIZE_TABLE + 1, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 2, 9);
+ ahc_outb(ahc, CMDSIZE_TABLE + 3, 0);
+ ahc_outb(ahc, CMDSIZE_TABLE + 4, 15);
+ 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
- /* How did we do? */
- if(ahc_debug & AHC_SHOWMISC)
+ if (ahc_debug & AHC_SHOWMISC)
printf("NEEDSDTR == 0x%x\nNEEDWDTR == 0x%x\n"
- "DISCENABLE == 0x%x\n", ahc->needsdtr,
- ahc->needwdtr, ahc->discenable);
+ "DISCENABLE == 0x%x\nULTRAENB == 0x%x\n",
+ ahc->needsdtr_orig, ahc->needwdtr_orig,
+ discenable, ultraenb);
#endif
- /*
- * Set the number of available SCBs
- */
- AHC_OUTB(ahc, SCBCOUNT, ahc->maxhscbs);
- /*
- * 2's compliment of maximum tag value
- */
- i = ahc->maxscbs;
- AHC_OUTB(ahc, COMP_SCBCOUNT, -i & 0xff);
+ /* 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);
/*
- * QCount mask to deal with broken aic7850s that
- * sporadically get garbage in the upper bits of
- * their QCount registers.
+ * Use the built in queue management registers
+ * if they are available.
*/
- AHC_OUTB(ahc, QCNTMASK, ahc->qcntmask);
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, QOFF_CTLSTA, SCB_QSIZE_256);
+ ahc_outb(ahc, SDSCB_QOFF, 0);
+ ahc_outb(ahc, SNSCB_QOFF, 0);
+ ahc_outb(ahc, HNSCB_QOFF, 0);
+ }
- /* We don't have any busy targets right now */
- AHC_OUTB(ahc, ACTIVE_A, 0);
- AHC_OUTB(ahc, ACTIVE_B, 0);
/* We don't have any waiting selections */
- AHC_OUTB(ahc, WAITING_SCBH, SCB_LIST_NULL);
+ ahc_outb(ahc, WAITING_SCBH, SCB_LIST_NULL);
/* Our disconnection list is empty too */
- AHC_OUTB(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
+ ahc_outb(ahc, DISCONNECTED_SCBH, SCB_LIST_NULL);
/* Message out buffer starts empty */
- AHC_OUTB(ahc, MSG_LEN, 0x00);
+ ahc_outb(ahc, MSG_OUT, MSG_NOOP);
+
+ /*
+ * Setup the allowed SCSI Sequences based on operational mode.
+ * 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)
+ scsiseq_template |= ENRSELI;
+ ahc_outb(ahc, SCSISEQ_TEMPLATE, scsiseq_template);
/*
* Load the Sequencer program and Enable the adapter
* in "fast" mode.
*/
-#if (!defined(__NetBSD__) && !defined(__OpenBSD__)) || defined(DEBUG)
- if(bootverbose)
+ if (bootverbose)
printf("%s: Downloading Sequencer Program...",
ahc_name(ahc));
-#endif
ahc_loadseq(ahc);
-#if (!defined(__NetBSD__) && !defined(__OpenBSD__)) || defined(DEBUG)
- if(bootverbose)
- printf("Done\n");
-#endif
-
- AHC_OUTB(ahc, SEQCTL, FASTMODE);
-
- unpause_sequencer(ahc, /*unpause_always*/TRUE);
-
- /*
- * Note that we are going and return (to probe)
- */
- ahc->flags |= AHC_INIT;
+ /* We have to wait until after any system dumps... */
+ shutdownhook_establish(ahc_shutdown, ahc);
return (0);
}
-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.
+ * Routines to manage a scsi_xfer into the software queue.
+ * We overload xs->free_list to to ensure we don't run into a queue
+ * resource shortage, and keep a pointer to the last entry around
+ * to make insertion O(C).
*/
- if (bp->b_bcount > ((AHC_NSEG - 1) * PAGE_SIZE)) {
- bp->b_bcount = ((AHC_NSEG - 1) * PAGE_SIZE);
- }
-#if defined(__NetBSD__) || defined(__OpenBSD__)
- minphys(bp);
-#endif
+STATIC INLINE void
+ahc_list_insert_before(ahc, xs, next_xs)
+ struct ahc_softc *ahc;
+ struct scsi_xfer *xs;
+ struct scsi_xfer *next_xs;
+{
+ LIST_INSERT_BEFORE(xs, next_xs, free_list);
+
}
-#if defined(__NetBSD__) || defined(__OpenBSD__) /* XXX */
-/*
- * Insert a scsi_xfer into the software queue. We overload xs->free_list
- * to to ensure we don't run into a queue resource shortage, and keep
- * a pointer to the last entry around to make insertion O(C).
- */
-static void
-ahc_xxx_enqueue(ahc, xs, infront)
- struct ahc_data *ahc;
+STATIC INLINE void
+ahc_list_insert_head(ahc, xs)
+ struct ahc_softc *ahc;
struct scsi_xfer *xs;
- int infront;
{
+ if (ahc->sc_xxxq.lh_first == NULL)
+ ahc->sc_xxxqlast = xs;
+ LIST_INSERT_HEAD(&ahc->sc_xxxq, xs, free_list);
+ return;
+}
- if (infront || ahc->sc_xxxq.lh_first == NULL) {
- if (ahc->sc_xxxq.lh_first == NULL)
- ahc->sc_xxxqlast = xs;
+STATIC INLINE void
+ahc_list_insert_tail(ahc, xs)
+ struct ahc_softc *ahc;
+ struct scsi_xfer *xs;
+{
+ if (ahc->sc_xxxq.lh_first == NULL){
+ ahc->sc_xxxqlast = xs;
LIST_INSERT_HEAD(&ahc->sc_xxxq, xs, free_list);
return;
}
-
LIST_INSERT_AFTER(ahc->sc_xxxqlast, xs, free_list);
ahc->sc_xxxqlast = xs;
}
-/*
- * Pull a scsi_xfer off the front of the software queue. When we
- * pull the last one off, we need to clear the pointer to the last
- * entry.
- */
-static struct scsi_xfer *
-ahc_xxx_dequeue(ahc)
- struct ahc_data *ahc;
-{
+STATIC INLINE void
+ahc_list_remove(ahc, xs)
+ struct ahc_softc *ahc;
struct scsi_xfer *xs;
-
- xs = ahc->sc_xxxq.lh_first;
+{
+ struct scsi_xfer *lxs;
+ if (xs == ahc->sc_xxxqlast) {
+ lxs = ahc->sc_xxxq.lh_first;
+ while (lxs != NULL) {
+ if (LIST_NEXT(lxs, free_list) == ahc->sc_xxxqlast) {
+ ahc->sc_xxxqlast = lxs;
+ break;
+ }
+ lxs = LIST_NEXT(xs, free_list);
+ }
+ }
+
LIST_REMOVE(xs, free_list);
-
if (ahc->sc_xxxq.lh_first == NULL)
ahc->sc_xxxqlast = NULL;
+}
- return (xs);
+STATIC INLINE struct scsi_xfer *
+ahc_list_next(ahc, xs)
+ struct ahc_softc *ahc;
+ struct scsi_xfer *xs;
+{
+ return(LIST_NEXT(xs, free_list));
}
-#endif
/*
- * start a scsi operation given the command and
- * the data address, target, and lun all of which
- * are stored in the scsi_xfer struct
+ * Pick the first xs for a non-blocked target.
*/
-static int32_t
-ahc_scsi_cmd(xs)
- struct scsi_xfer *xs;
+STATIC INLINE struct scsi_xfer *
+ahc_first_xs(struct ahc_softc *ahc)
{
- struct scb *scb;
- struct ahc_dma_seg *sg;
- int seg; /* scatter gather seg being worked on */
- unsigned long thiskv, nextkv;
- physaddr thisphys, nextphys;
- int bytes_this_seg, bytes_this_page, datalen, flags;
- struct ahc_data *ahc;
- u_short mask;
- int s;
-#if defined(__NetBSD__) || defined(__OpenBSD__) /* XXX */
- int dontqueue = 0, fromqueue = 0;
-#endif
+ int target;
+ struct scsi_xfer *xs = ahc->sc_xxxq.lh_first;
- ahc = (struct ahc_data *)xs->sc_link->adapter_softc;
- mask = (0x01 << (xs->sc_link->target
-#if defined(__FreeBSD__)
- | ((u_long)xs->sc_link->fordriver & 0x08)));
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
- | (IS_SCSIBUS_B(ahc, xs->sc_link) ? SELBUSB : 0) ));
-#endif
+ if (ahc->queue_blocked)
+ return NULL;
- SC_DEBUG(xs->sc_link, SDEV_DB2, ("ahc_scsi_cmd\n"));
+ while (xs != NULL) {
+ target = xs->sc_link->target;
+ if (ahc->devqueue_blocked[target] == 0 &&
+ ahc_index_busy_tcl(ahc, XS_TCL(ahc, xs), FALSE) ==
+ SCB_LIST_NULL)
+ break;
+ xs = LIST_NEXT(xs, free_list);
+ }
+
+ return xs;
+}
+
+STATIC int32_t
+ahc_scsi_cmd(xs)
+ struct scsi_xfer *xs;
+{
+ struct scsi_xfer *first_xs, *next_xs = NULL;
+ struct ahc_softc *ahc;
+ 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;
+ int dontqueue = 0, fromqueue = 0;
+
+ SC_DEBUG(xs->sc_link, SDEV_DB3, ("ahc_scsi_cmd\n"));
+ ahc = (struct ahc_softc *)xs->sc_link->adapter_softc;
-#if defined(__NetBSD__) || (__OpenBSD__) /* XXX */
/* must protect the queue */
s = splbio();
- /*
- * If we're running the queue from ahc_done(), we're called
- * with the first entry in the queue as our argument.
- * Pull it off; if we can't run the job, it will get placed
- * back at the front.
- */
if (xs == ahc->sc_xxxq.lh_first) {
- xs = ahc_xxx_dequeue(ahc);
+ /*
+ * Called from ahc_done. Calling with the first entry in
+ * the queue is really just a way of seeing where we're
+ * called from. Now, find the first eligible SCB to send,
+ * e.g. one which will be accepted immediately.
+ */
+
+ if (ahc->queue_blocked) {
+ splx(s);
+ return (TRY_AGAIN_LATER);
+ }
+
+ xs = ahc_first_xs(ahc);
+ if (xs == NULL) {
+ splx(s);
+ return (TRY_AGAIN_LATER);
+ }
+
+ next_xs = ahc_list_next(ahc, xs);
+ ahc_list_remove(ahc, xs);
fromqueue = 1;
goto get_scb;
}
+ /*
+ * If no new requests are accepted, just insert into the
+ * private queue to wait for our turn.
+ */
+ tcl = XS_TCL(ahc, xs);
+
+ if (ahc->queue_blocked ||
+ ahc->devqueue_blocked[xs->sc_link->target] ||
+ ahc_index_busy_tcl(ahc, tcl, FALSE) != SCB_LIST_NULL) {
+ if (dontqueue) {
+ splx(s);
+ xs->error = XS_DRIVER_STUFFUP;
+ return TRY_AGAIN_LATER;
+ }
+ ahc_list_insert_tail(ahc, xs);
+ splx(s);
+ return SUCCESSFULLY_QUEUED;
+ }
+
+ first_xs = ahc_first_xs(ahc);
+
/* determine safety of software queueing */
dontqueue = xs->flags & SCSI_POLL;
@@ -2475,7 +4108,7 @@ ahc_scsi_cmd(xs)
* Handle situations where there's already entries in the
* queue.
*/
- if (ahc->sc_xxxq.lh_first != NULL) {
+ if (first_xs != NULL) {
/*
* If we can't queue, we have to abort, since
* we have to preserve order.
@@ -2489,32 +4122,24 @@ ahc_scsi_cmd(xs)
/*
* Swap with the first queue entry.
*/
- ahc_xxx_enqueue(ahc, xs, 0);
- xs = ahc_xxx_dequeue(ahc);
+ ahc_list_insert_tail(ahc, xs);
+ xs = first_xs;
+ next_xs = ahc_list_next(ahc, xs);
+ ahc_list_remove(ahc, xs);
fromqueue = 1;
+
}
- get_scb:
-#endif /* __NetBSD__ || __OpenBSD__ */
+get_scb:
+
+ target_id = xs->sc_link->target;
+ our_id = SIM_SCSI_ID(ahc, xs->sc_link);
+
/*
- * get an scb to use. If the transfer
- * is from a buf (possibly from interrupt time)
- * then we can't allow it to sleep
+ * get an scb to use.
*/
- flags = xs->flags;
- if (flags & ITSDONE) {
- printf("%s: Already done?", ahc_name(ahc));
- xs->flags &= ~ITSDONE;
- }
- if (!(flags & INUSE)) {
- printf("%s: Not in use?", ahc_name(ahc));
- xs->flags |= INUSE;
- }
- if (!(scb = ahc_get_scb(ahc, flags))) {
-#if defined(__NetBSD__) || defined(__OpenBSD__) /* XXX */
- /*
- * If we can't queue, we lose.
- */
+ if ((scb = ahcgetscb(ahc)) == NULL) {
+
if (dontqueue) {
splx(s);
xs->error = XS_DRIVER_STUFFUP;
@@ -2523,182 +4148,189 @@ ahc_scsi_cmd(xs)
/*
* If we were pulled off the queue, put ourselves
- * back in the front, otherwise tack ourselves onto
- * the end.
+ * back to where we came from, otherwise tack ourselves
+ * onto the end.
*/
- ahc_xxx_enqueue(ahc, xs, fromqueue);
+ if (fromqueue && next_xs != NULL)
+ ahc_list_insert_before(ahc, xs, next_xs);
+ else
+ ahc_list_insert_tail(ahc, xs);
splx(s);
return (SUCCESSFULLY_QUEUED);
-#else
- xs->error = XS_DRIVER_STUFFUP;
- return (TRY_AGAIN_LATER);
-#endif /* __NetBSD__ || __OpenBSD__ */
}
-#if defined(__NetBSD__) || defined(__OpenBSD__)
- /* we're done playing with the queue */
- splx(s);
-#endif
+ tcl = XS_TCL(ahc, xs);
- SC_DEBUG(xs->sc_link, SDEV_DB3, ("start scb(%p)\n", scb));
+#ifdef DIAGNOSTIC
+ if (ahc_index_busy_tcl(ahc, tcl, FALSE) != SCB_LIST_NULL)
+ panic("ahc: queuing for busy target");
+#endif
+
scb->xs = xs;
- if (flags & SCSI_RESET) {
- scb->flags |= SCB_DEVICE_RESET|SCB_IMMED;
- scb->control |= MK_MESSAGE;
- }
+ hscb = scb->hscb;
+ hscb->tcl = tcl;
+
+ ahc_busy_tcl(ahc, scb);
+
+ splx(s);
+
/*
* Put all the arguments for the xfer in the scb
*/
- if(ahc->tagenable & mask) {
- scb->control |= TAG_ENB;
- if(ahc->orderedtag & mask) {
- printf("Ordered Tag sent\n");
- scb->control |= 0x02;
- ahc->orderedtag &= ~mask;
- }
+ mask = SCB_TARGET_MASK(scb);
+ tinfo = ahc_fetch_transinfo(ahc, SIM_CHANNEL(ahc, xs->sc_link), 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,
+ xs->sc_link->lun, SIM_CHANNEL(ahc, xs->sc_link),
+ ROLE_INITIATOR);
+ ahc_update_target_msg_request(ahc, &devinfo, tinfo, TRUE,
+ FALSE);
+ ahc->inited_targets[target_id] = 1;
+ splx(s);
}
- if(ahc->discenable & mask)
- scb->control |= DISCENB;
- if((ahc->needwdtr & mask) && !(ahc->wdtrpending & mask))
- {
- scb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_WDTR;
- ahc->wdtrpending |= mask;
+
+ 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->flags & SCSI_RESET) {
+ hscb->cmdpointer = NULL;
+ scb->flags |= SCB_DEVICE_RESET;
+ hscb->control |= MK_MESSAGE;
+ return ahc_execute_scb(scb, NULL, 0);
}
- else if((ahc->needsdtr & mask) && !(ahc->sdtrpending & mask))
- {
- scb->control |= MK_MESSAGE;
- scb->flags |= SCB_MSGOUT_SDTR;
- ahc->sdtrpending |= mask;
- }
- scb->tcl = ((xs->sc_link->target << 4) & 0xF0) |
-#if defined(__FreeBSD__)
- ((u_long)xs->sc_link->fordriver & 0x08) |
-#elif defined(__NetBSD__) || defined(__OpenBSD__)
- (IS_SCSIBUS_B(ahc,xs->sc_link)? SELBUSB : 0)|
-#endif
- (xs->sc_link->lun & 0x07);
- scb->cmdlen = xs->cmdlen;
- scb->cmdpointer = KVTOPHYS(xs->cmd);
- xs->resid = 0;
- xs->status = 0;
- if (xs->datalen) { /* should use S/G only if not zero length */
- scb->SG_list_pointer = KVTOPHYS(scb->ahc_dma);
- sg = scb->ahc_dma;
- seg = 0;
- /*
- * Set up the scatter gather block
- */
- SC_DEBUG(xs->sc_link, SDEV_DB4,
- ("%ld @%p:- ", xs->datalen, xs->data));
- datalen = xs->datalen;
- thiskv = (unsigned long) xs->data;
- thisphys = KVTOPHYS(thiskv);
- while ((datalen) && (seg < AHC_NSEG)) {
- bytes_this_seg = 0;
+ return ahc_setup_data(ahc, xs, scb);
+}
- /* put in the base address */
- sg->addr = thisphys;
+STATIC int
+ahc_execute_scb(arg, dm_segs, nsegments)
+ void *arg;
+ bus_dma_segment_t *dm_segs;
+ int nsegments;
+{
+ struct scb *scb;
+ struct scsi_xfer *xs;
+ struct ahc_softc *ahc;
+ int s;
- SC_DEBUGN(xs->sc_link, SDEV_DB4, ("0x%lx", thisphys));
+ scb = (struct scb *)arg;
+ xs = scb->xs;
+ ahc = (struct ahc_softc *)xs->sc_link->adapter_softc;
- /* do it at least once */
- nextphys = thisphys;
- while ((datalen) && (thisphys == nextphys)) {
- /*
- * This page is contiguous (physically)
- * with the the last, just extend the
- * length
- */
- /* how far to the end of the page */
- nextphys = (thisphys & (~(PAGE_SIZE- 1)))
- + PAGE_SIZE;
- bytes_this_page = nextphys - thisphys;
- /**** or the data ****/
- bytes_this_page = min(bytes_this_page, datalen);
- bytes_this_seg += bytes_this_page;
- datalen -= bytes_this_page;
-
- /* get more ready for the next page */
- nextkv = thiskv;
- nextkv &= ~((unsigned long) PAGE_SIZE - 1);
- nextkv += PAGE_SIZE;
- if (datalen)
- thisphys = KVTOPHYS(nextkv);
- thiskv = nextkv;
- }
- /*
- * next page isn't contiguous, finish the seg
- */
- SC_DEBUGN(xs->sc_link, SDEV_DB4,
- ("(0x%x)", bytes_this_seg));
- sg->len = bytes_this_seg;
- sg++;
- seg++;
- }
- scb->SG_segment_count = seg;
+
+ if (nsegments != 0) {
+ struct ahc_dma_seg *sg;
+ bus_dma_segment_t *end_seg;
+ bus_dmasync_op_t op;
+
+ end_seg = dm_segs + nsegments;
/* Copy the first SG into the data pointer area */
- scb->data = scb->ahc_dma->addr;
- scb->datalen = scb->ahc_dma->len;
- SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n"));
- if (datalen) {
- /* there's still data, must have run out of segs! */
- printf("%s: ahc_scsi_cmd: more than %d DMA segs\n",
- ahc_name(ahc), AHC_NSEG);
- xs->error = XS_DRIVER_STUFFUP;
- ahc_free_scb(ahc, scb, flags);
- return (COMPLETE);
+ 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;
+ sg++;
+ dm_segs++;
}
-#ifdef AHC_BROKEN_CACHE
- if (ahc_broken_cache)
- INVALIDATE_CACHE();
-#endif
+
+ /* Note where to find the SG entries in bus space */
+ scb->hscb->SG_pointer = scb->sg_list_phys;
+ if ((scb->xs->flags & SCSI_DATA_IN) != 0)
+ op = BUS_DMASYNC_PREREAD;
+ else
+ op = BUS_DMASYNC_PREWRITE;
+ bus_dmamap_sync(ahc->sc_dmat, scb->dmamap, op);
+ } else {
+ scb->hscb->SG_pointer = 0;
+ scb->hscb->data = 0;
+ scb->hscb->datalen = 0;
}
- else {
- /*
- * No data xfer, use non S/G values
- */
- scb->SG_segment_count = 0;
- scb->SG_list_pointer = 0;
- scb->data = 0;
- scb->datalen = 0;
+
+ 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->flags & ITSDONE) {
+ ahc_index_busy_tcl(ahc, scb->hscb->tcl, TRUE);
+ if (nsegments != 0)
+ bus_dmamap_unload(ahc->sc_dmat, scb->dmamap);
+ ahcfreescb(ahc, scb);
+ splx(s);
+ return (COMPLETE);
}
-#ifdef AHC_DEBUG
- if((ahc_debug & AHC_SHOWSCBS) && (xs->sc_link->target == DEBUGTARG))
- ahc_print_scb(scb);
+#ifdef DIAGNOSTIC
+ if (scb->sg_count > 255)
+ panic("ahc bad sg_count");
#endif
- s = splbio();
+
+ LIST_INSERT_HEAD(&ahc->pending_scbs, scb, pend_links);
- if( scb->position != SCB_LIST_NULL )
- {
- /* We already have a valid slot */
- u_char curscb;
+ scb->flags |= SCB_ACTIVE;
+
+ if (!(xs->flags & SCSI_POLL))
+ timeout(ahc_timeout, (caddr_t)scb,
+ (xs->timeout * hz) / 1000);
+ if ((scb->flags & SCB_TARGET_IMMEDIATE) != 0) {
+#if 0
+ printf("Continueing Immediate Command %d:%d\n",
+ xs->sc_link->target,
+ xs->sc_link->lun);
+#endif
pause_sequencer(ahc);
- curscb = AHC_INB(ahc, SCBPTR);
- AHC_OUTB(ahc, SCBPTR, scb->position);
- ahc_send_scb(ahc, scb);
- AHC_OUTB(ahc, SCBPTR, curscb);
- AHC_OUTB(ahc, QINFIFO, scb->position);
- unpause_sequencer(ahc, /*unpause_always*/FALSE);
- scb->flags |= SCB_ACTIVE;
- if (!(flags & SCSI_NOMASK)) {
- timeout(ahc_timeout, (caddr_t)scb,
- (xs->timeout * hz) / 1000);
- }
- SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n"));
- }
- else {
- scb->flags |= SCB_WAITINGQ;
- STAILQ_INSERT_TAIL(&ahc->waiting_scbs, scb, links);
- ahc_run_waiting_queues(ahc);
- }
- if (!(flags & SCSI_NOMASK)) {
+ 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 {
+
+ ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
+
+ bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap,
+ BUS_DMASYNC_PREWRITE);
+
+ 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);
+ }
+ }
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWCMDS) {
+ 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);
+ }
+#endif
+
+ if (!(xs->flags & SCSI_POLL)) {
splx(s);
return (SUCCESSFULLY_QUEUED);
}
@@ -2713,797 +4345,1398 @@ ahc_scsi_cmd(xs)
ahc_timeout(scb);
break;
}
- } while (!(xs->flags & ITSDONE)); /* a non command complete intr */
- splx(s);
+ } while (!(xs->flags & ITSDONE));
+ splx(s);
return (COMPLETE);
}
+STATIC int
+ahc_poll(ahc, wait)
+ struct ahc_softc *ahc;
+ int wait; /* in msec */
+{
+ while (--wait) {
+ DELAY(1000);
+ if (ahc_inb(ahc, INTSTAT) & INT_PEND)
+ break;
+ }
-/*
- * A scb (and hence an scb entry on the board) is put onto the
- * free list.
- */
-static void
-ahc_free_scb(ahc, scb, flags)
- struct ahc_data *ahc;
- int flags;
- struct scb *scb;
+ if (wait == 0) {
+ printf("%s: board is not responding\n", ahc_name(ahc));
+ return (EIO);
+ }
+
+ ahc_intr((void *)ahc);
+ return (0);
+}
+
+STATIC int
+ahc_setup_data(ahc, xs, scb)
+ struct ahc_softc *ahc;
+ struct scsi_xfer *xs;
+ struct scb *scb;
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+ xs->resid = xs->status = 0;
+
+ hscb->cmdlen = xs->cmdlen;
+ bcopy(xs->cmd, hscb->cmdstore, xs->cmdlen);
+ hscb->cmdpointer = hscb->cmdstore_busaddr;
+
+ /* Only use S/G if there is a transfer */
+ if (xs->datalen) {
+ int error;
+
+ error = bus_dmamap_load(ahc->sc_dmat,
+ scb->dmamap, xs->data,
+ xs->datalen, NULL,
+ (xs->flags & SCSI_NOSLEEP) ?
+ BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+ if (error) {
+ ahc_index_busy_tcl(ahc, hscb->tcl, TRUE);
+ return (TRY_AGAIN_LATER); /* XXX fvdl */
+ }
+ error = ahc_execute_scb(scb,
+ scb->dmamap->dm_segs,
+ scb->dmamap->dm_nsegs);
+ return error;
+ } else {
+ return ahc_execute_scb(scb, NULL, 0);
+ }
+}
+
+STATIC void
+ahc_freeze_devq(ahc, sc_link)
+ struct ahc_softc *ahc;
+ struct scsi_link *sc_link;
{
- struct scb *wscb;
- unsigned int opri;
+ int target;
+ char channel;
+ int lun;
- opri = splbio();
+ target = sc_link->target;
+ lun = sc_link->lun;
+ channel = SIM_CHANNEL(ahc, sc_link);
+
+ ahc_search_qinfifo(ahc, target, channel, lun,
+ /*tag*/SCB_LIST_NULL, ROLE_UNKNOWN,
+ SCB_REQUEUE, SEARCH_COMPLETE);
+}
- /* Clean up for the next user */
- scb->flags = SCB_FREE;
- scb->control = 0;
- scb->status = 0;
+STATIC void
+ahcallocscbs(ahc)
+ 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;
+ int dma_flags = 0;
- if(scb->position == SCB_LIST_NULL) {
- STAILQ_INSERT_HEAD(&ahc->page_scbs, scb, links);
- if(!scb->links.stqe_next && !ahc->free_scbs.stqh_first)
- /*
- * If there were no SCBs available, wake anybody waiting
- * for one to come free.
- */
- wakeup((caddr_t)&ahc->free_scbs);
+ 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;
+ bzero(sg_map, sizeof(struct sg_map_node));
+
+ if (ahc_createdmamem(ahc, PAGE_SIZE, &sg_map->sg_dmamap,
+ (caddr_t *)&sg_map->sg_vaddr, &sg_map->sg_physaddr,
+ &sg_map->sg_dmasegs, &sg_map->sg_nseg, "SG space") < 0) {
+ free(sg_map, M_DEVBUF);
+ return;
}
- /*
- * If there are any SCBS on the waiting queue,
- * assign the slot of this "freed" SCB to the first
- * one. We'll run the waiting queues after all command
- * completes for a particular interrupt are completed
- * or when we start another command.
- */
- else if((wscb = ahc->waiting_scbs.stqh_first) != NULL) {
- STAILQ_REMOVE_HEAD(&ahc->waiting_scbs, links);
- wscb->position = scb->position;
- STAILQ_INSERT_TAIL(&ahc->assigned_scbs, wscb, links);
- wscb->flags ^= SCB_WAITINGQ|SCB_ASSIGNEDQ;
-
- /*
- * The "freed" SCB will need to be assigned a slot
- * before being used, so put it in the page_scbs
- * queue.
+
+ 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.
*/
- scb->position = SCB_LIST_NULL;
- STAILQ_INSERT_HEAD(&ahc->page_scbs, scb, links);
- if(!scb->links.stqe_next && !ahc->free_scbs.stqh_first)
+ next_scb->sg_list_phys = physaddr + sizeof(struct ahc_dma_seg);
+ next_scb->flags = SCB_FREE;
+
+ /* set up AHA-284x right. */
+ dma_flags = ((ahc->chip & AHC_VL) !=0) ?
+ BUS_DMA_NOWAIT|ISABUS_DMA_32BIT :
+ BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW;
+
+ error = bus_dmamap_create(ahc->sc_dmat,
+ AHC_MAXTRANSFER_SIZE, AHC_NSEG, MAXBSIZE, 0,
+ dma_flags, &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);
+ 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_DUMP_SEQ
+STATIC void
+ahc_dumpseq(ahc)
+ struct ahc_softc* ahc;
+{
+ int i;
+ int max_prog;
+
+ 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++) {
+ 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]);
+ }
+}
+#endif
+
+STATIC void
+ahc_loadseq(ahc)
+ struct ahc_softc* ahc;
+{
+ 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) {
/*
- * If there were no SCBs available, wake anybody waiting
- * for one to come free.
+ * Don't download this instruction as it
+ * is in a patch that was removed.
*/
- wakeup((caddr_t)&ahc->free_scbs);
+ continue;
+ }
+ ahc_download_instr(ahc, i, download_consts);
+ downloaded++;
}
- else {
- STAILQ_INSERT_HEAD(&ahc->free_scbs, scb, links);
- if(!scb->links.stqe_next && !ahc->page_scbs.stqh_first)
- /*
- * If there were no SCBs available, wake anybody waiting
- * for one to come free.
+ ahc_outb(ahc, SEQCTL, PERRORDIS|FAILDIS|FASTMODE);
+ restart_sequencer(ahc);
+
+ if (bootverbose)
+ printf(" %d instructions downloaded\n", downloaded);
+}
+
+STATIC int
+ahc_check_patch(ahc, start_patch, start_instr,skip_addr)
+ struct ahc_softc *ahc;
+ struct patch **start_patch;
+ int start_instr;
+ int *skip_addr;
+{
+ struct patch *cur_patch;
+ struct patch *last_patch;
+ 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.
*/
- wakeup((caddr_t)&ahc->free_scbs);
+ cur_patch++;
+ }
}
-#ifdef AHC_DEBUG
- ahc->activescbs--;
-#endif
- splx(opri);
+
+ *start_patch = cur_patch;
+ if (start_instr < *skip_addr)
+ /* Still skipping */
+ return (0);
+
+ return (1);
}
-/*
- * 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.
- */
-static struct scb *
-ahc_get_scb(ahc, flags)
- struct ahc_data *ahc;
- int flags;
+STATIC void
+ahc_download_instr(ahc, instrptr, dconsts)
+ struct ahc_softc *ahc;
+ int instrptr;
+ u_int8_t *dconsts;
{
- unsigned opri;
- struct scb *scbp;
+ union ins_formats instr;
+ struct ins_format1 *fmt1_ins;
+ struct ins_format3 *fmt3_ins;
+ u_int opcode;
+
+ /* Structure copy */
+ instr = *(union ins_formats*)&seqprog[instrptr * 4];
+
+ 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;
+ int skip_addr;
+ int i;
- opri = splbio();
- /*
- * If we can and have to, sleep waiting for one to come free
- * but only if we can't allocate a new one.
- */
- while (1) {
- if((scbp = ahc->free_scbs.stqh_first)) {
- STAILQ_REMOVE_HEAD(&ahc->free_scbs, links);
- }
- else if((scbp = ahc->page_scbs.stqh_first)) {
- STAILQ_REMOVE_HEAD(&ahc->page_scbs, links);
- }
- else if(ahc->numscbs < ahc->maxscbs) {
- scbp = (struct scb *) malloc(sizeof(struct scb),
- M_TEMP, M_NOWAIT);
- if (scbp) {
- bzero(scbp, sizeof(struct scb));
- scbp->tag = ahc->numscbs;
- if( ahc->numscbs < ahc->maxhscbs )
- scbp->position = ahc->numscbs;
- else
- scbp->position = SCB_LIST_NULL;
- ahc->numscbs++;
- /*
- * Place in the scbarray
- * Never is removed.
- */
- ahc->scbarray[scbp->tag] = scbp;
- }
- else {
- printf("%s: Can't malloc SCB\n",
- ahc_name(ahc));
+ 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++;
}
}
- else {
- if (!(flags & SCSI_NOSLEEP)) {
- tsleep((caddr_t)&ahc->free_scbs, PRIBIO,
- "ahcscb", 0);
- continue;
+ 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);
}
}
+ ahc_outsb(ahc, SEQRAM, instr.bytes, 4);
+ break;
+ default:
+ panic("Unknown opcode encountered in seq program");
break;
}
-
-#ifdef AHC_DEBUG
- if (scbp) {
- ahc->activescbs++;
- if((ahc_debug & AHC_SHOWSCBCNT)
- && (ahc->activescbs == ahc->maxhscbs))
- printf("%s: Max SCBs active\n", ahc_name(ahc));
- }
-#endif
-
- splx(opri);
-
- return (scbp);
}
-static void ahc_loadseq(ahc)
- struct ahc_data *ahc;
+STATIC void
+ahc_set_recoveryscb(ahc, scb)
+ struct ahc_softc *ahc;
+ struct scb *scb;
{
- static u_char seqprog[] = {
-#if defined(__FreeBSD__)
-# include "aic7xxx_seq.h"
-#endif
-#if defined(__NetBSD__) || defined(__OpenBSD__)
-# include <dev/microcode/aic7xxx/aic7xxx_seq.h>
-#endif
- };
- AHC_OUTB(ahc, SEQCTL, PERRORDIS|SEQRESET|LOADRAM);
+ if ((scb->flags & SCB_RECOVERY_SCB) == 0) {
+ struct scb *scbp;
- AHC_OUTSB(ahc, SEQRAM, seqprog, sizeof(seqprog));
+ scb->flags |= SCB_RECOVERY_SCB;
- do {
- AHC_OUTB(ahc, SEQCTL, SEQRESET|FASTMODE);
- } while((AHC_INB(ahc, SEQADDR0) != 0)
- || (AHC_INB(ahc, SEQADDR1) != 0));
-}
+ /*
+ * 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.
+ */
+ ahc->queue_blocked = 1;
-/*
- * Function to poll for command completion when
- * interrupts are disabled (crash dumps)
- */
-static int
-ahc_poll(ahc, wait)
- struct ahc_data *ahc;
- int wait; /* in msec */
-{
- 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);
+ /*
+ * 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.
+ */
+ scbp = ahc->pending_scbs.lh_first;
+ while (scbp != NULL) {
+ untimeout(ahc_timeout, scbp);
+ scbp = scbp->pend_links.le_next;
+ }
}
- ahc_intr((void *)ahc);
- return (0);
}
-static void
-ahc_timeout(arg)
- void *arg;
+STATIC void
+ahc_timeout(void *arg)
{
- struct scb *scb = (struct scb *)arg;
- struct ahc_data *ahc;
+ struct scb *scb;
+ struct ahc_softc *ahc;
int s, found;
- u_char bus_state;
+ u_int last_phase;
+ int target;
+ int lun;
+ int i;
char channel;
+ scb = (struct scb *)arg;
+ ahc = (struct ahc_softc *)scb->xs->sc_link->adapter_softc;
+
s = splbio();
- if (!(scb->flags & SCB_ACTIVE)) {
+ /*
+ * 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 = (struct ahc_data *)scb->xs->sc_link->adapter_softc;
-
- if (ahc->in_timeout) {
- /*
- * Some other SCB has started a recovery operation
- * and is still working on cleaning things up.
- */
- if (scb->flags & SCB_TIMEDOUT) {
- /*
- * This SCB has been here before and is not the
- * recovery SCB. Cut our losses and panic. Its
- * better to do this than trash a filesystem.
- */
- panic("%s: Timed-out command times out "
- "again\n", ahc_name(ahc));
- }
- else if (!(scb->flags & SCB_ABORTED))
- {
- /*
- * This is not the SCB that started this timeout
- * processing. Give this scb another lifetime so
- * that it can continue once we deal with the
- * timeout.
- */
- scb->flags |= SCB_TIMEDOUT;
- timeout(ahc_timeout, (caddr_t)scb,
- (scb->xs->timeout * hz) / 1000);
- splx(s);
- return;
- }
- }
- ahc->in_timeout = TRUE;
-
- /*
- * Ensure that the card doesn't do anything
- * behind our back.
- */
- pause_sequencer(ahc);
+ target = SCB_TARGET(scb);
+ channel = SCB_CHANNEL(scb);
+ lun = SCB_LUN(scb);
sc_print_addr(scb->xs->sc_link);
- printf("timed out ");
+ printf("SCB 0x%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.
*/
- bus_state = AHC_INB(ahc, LASTPHASE);
+ last_phase = ahc_inb(ahc, LASTPHASE);
- switch(bus_state & PHASE_MASK)
- {
- case P_DATAOUT:
- printf("in dataout phase");
- break;
- case P_DATAIN:
- printf("in datain phase");
- break;
- case P_COMMAND:
- printf("in command phase");
- break;
- case P_MESGOUT:
- printf("in message out phase");
- break;
- case P_STATUS:
- printf("in status phase");
- break;
- case P_MESGIN:
- printf("in message in phase");
- break;
- default:
- printf("while idle, LASTPHASE == 0x%x",
- bus_state);
- /*
- * We aren't in a valid phase, so assume we're
- * idle.
- */
- bus_state = 0;
+ for (i = 0; i < num_phases; i++) {
+ if (last_phase == phase_table[i].phase)
break;
}
-
- printf(", SCSISIGI == 0x%x\n", AHC_INB(ahc, SCSISIGI));
-
- /* Decide our course of action */
-
- if(scb->flags & SCB_ABORTED)
- {
+ printf("%s", phase_table[i].phasemsg);
+
+ printf(", SEQADDR == 0x%x\n",
+ ahc_inb(ahc, SEQADDR0) | (ahc_inb(ahc, SEQADDR1) << 8));
+#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("SCSIRATE == 0x%x\n", ahc_inb(ahc, SCSIRATE));
+ 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,
+ scb->sg_list[i].addr,
+ scb->sg_list[i].len);
+ }
+ }
+#endif
+ if (scb->flags & (SCB_DEVICE_RESET|SCB_ABORT)) {
/*
* Been down this road before.
* Do a full bus reset.
*/
- char channel = (scb->tcl & SELBUSB)
- ? 'B': 'A';
- found = ahc_reset_channel(ahc, channel, scb->tag,
- XS_TIMEOUT, /*Initiate Reset*/TRUE);
- printf("%s: Issued Channel %c Bus Reset #1. "
+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);
- ahc->in_timeout = FALSE;
- }
- else if(scb->control & TAG_ENB) {
- /*
- * We could be starving this command
- * try sending an ordered tag command
- * to the target we come from.
- */
- scb->flags |= SCB_ABORTED|SCB_SENTORDEREDTAG;
- ahc->orderedtag |= 0xFF;
- timeout(ahc_timeout, (caddr_t)scb, (5 * hz));
- unpause_sequencer(ahc, /*unpause_always*/FALSE);
- printf("Ordered Tag queued\n");
- goto done;
- }
- else {
+ } else {
/*
- * Send a Bus Device Reset Message:
- * The target that is holding up the bus may not
+ * 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).
- * It is also impossible to get a message to a target
- * if we are in a "frozen" data transfer phase. Our
- * strategy here is to queue a bus device reset message
- * to the timed out target if it is disconnected.
- * Otherwise, if we have an active target we stuff the
- * message buffer with a bus device reset message and
- * assert ATN in the hopes that the target will let go
- * of the bus and finally disconnect. If this fails,
- * we'll get another timeout 2 seconds later which will
- * cause a bus reset.
+ * 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.
*
- * XXX If the SCB is paged out, we simply reset the
- * bus. We should probably queue a new command
- * instead.
+ * Target transaction:
+ * Transition to BUS FREE and report the error.
+ * It's good to be the target!
*/
+ u_int active_scb_index;
- /* Test to see if scb is disconnected */
- if( !(scb->flags & SCB_PAGED_OUT ) ){
- u_char active_scb;
- struct scb *active_scbp;
+ active_scb_index = ahc_inb(ahc, SCB_TAG);
- active_scb = AHC_INB(ahc, SCBPTR);
- active_scbp = ahc->scbarray[AHC_INB(ahc, SCB_TAG)];
- AHC_OUTB(ahc, SCBPTR, scb->position);
+ if (last_phase != P_BUSFREE
+ && (active_scb_index < ahc->scb_data->numscbs)) {
+ struct scb *active_scb;
+
+ /*
+ * 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.
+ */
+ active_scb = &ahc->scb_data->scbarray[active_scb_index];
+ if (active_scb->hscb->tcl != scb->hscb->tcl) {
+ u_int newtimeout;
- if(AHC_INB(ahc, SCB_CONTROL) & DISCONNECTED) {
- if(ahc->flags & AHC_PAGESCBS) {
- /*
- * Pull this SCB out of the
- * disconnected list.
- */
- u_char prev = AHC_INB(ahc, SCB_PREV);
- u_char next = AHC_INB(ahc, SCB_NEXT);
- if(prev == SCB_LIST_NULL) {
- /* At the head */
- AHC_OUTB(ahc, DISCONNECTED_SCBH,
- next );
- }
- else {
- AHC_OUTB(ahc, SCBPTR, prev);
- AHC_OUTB(ahc, SCB_NEXT, next);
- if(next != SCB_LIST_NULL) {
- AHC_OUTB(ahc, SCBPTR,
- next);
- AHC_OUTB(ahc, SCB_PREV,
- prev);
- }
- AHC_OUTB(ahc, SCBPTR,
- scb->position);
- }
- }
- scb->flags |= SCB_DEVICE_RESET|SCB_ABORTED;
- scb->control &= DISCENB;
- scb->control |= MK_MESSAGE;
- scb->cmdlen = 0;
- scb->SG_segment_count = 0;
- scb->SG_list_pointer = 0;
- scb->data = 0;
- scb->datalen = 0;
- ahc_send_scb(ahc, scb);
- ahc_add_waiting_scb(ahc, scb);
- timeout(ahc_timeout, (caddr_t)scb, (2 * hz));
sc_print_addr(scb->xs->sc_link);
- printf("BUS DEVICE RESET message queued.\n");
- AHC_OUTB(ahc, SCBPTR, active_scb);
- unpause_sequencer(ahc, /*unpause_always*/FALSE);
- goto done;
+ 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);
+ timeout(ahc_timeout, scb,
+ (newtimeout * hz) / 1000);
+ 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->sc_link);
+ ahcsetccbstatus(scb->xs, XS_TIMEOUT);
+ ahc_freeze_ccb(scb);
+ ahc_done(ahc, scb);
+
+ /* Will clear us from the bus */
+ restart_sequencer(ahc);
+ return;
+ }
+
+ ahc_set_recoveryscb(ahc, active_scb);
+ ahc_outb(ahc, MSG_OUT, MSG_BUS_DEV_RESET);
+ ahc_outb(ahc, SCSISIGO, last_phase|ATNO);
+ sc_print_addr(active_scb->xs->sc_link);
+ printf("BDR message in message buffer\n");
+ active_scb->flags |= SCB_DEVICE_RESET;
+ timeout(ahc_timeout, (caddr_t)active_scb, 2 * hz);
+ } 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);
+ return;
}
- /* Is the active SCB really active? */
- else if((active_scbp->flags & SCB_ACTIVE) && bus_state){
- AHC_OUTB(ahc, MSG_LEN, 1);
- AHC_OUTB(ahc, MSG0, MSG_BUS_DEV_RESET);
- AHC_OUTB(ahc, SCSISIGO, bus_state|ATNO);
- sc_print_addr(active_scbp->xs->sc_link);
- printf("asserted ATN - device reset in "
- "message buffer\n");
- active_scbp->flags |= SCB_DEVICE_RESET
- | SCB_ABORTED;
- if(active_scbp != scb) {
- untimeout(ahc_timeout,
- (caddr_t)active_scbp);
- /* Give scb a new lease on life */
- timeout(ahc_timeout, (caddr_t)scb,
- (scb->xs->timeout * hz) / 1000);
+
+ 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) {
+ 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;
+
+ /*
+ * 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);
+
+ /*
+ * 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);
+ sc_print_addr(scb->xs->sc_link);
+ printf("Queuing a BDR SCB\n");
+ ahc->qinfifo[ahc->qinfifonext++] =
+ scb->hscb->tag;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ ahc_outb(ahc, HNSCB_QOFF,
+ ahc->qinfifonext);
+ } else {
+ ahc_outb(ahc, KERNEL_QINPOS,
+ ahc->qinfifonext);
}
- timeout(ahc_timeout, (caddr_t)active_scbp,
- (2 * hz));
- AHC_OUTB(ahc, SCBPTR, active_scb);
- unpause_sequencer(ahc, /*unpause_always*/FALSE);
- goto done;
+ timeout(ahc_timeout, (caddr_t)scb, 2 * hz);
+ unpause_sequencer(ahc);
+ } else {
+ /* Go "immediatly" to the bus reset */
+ /* This shouldn't happen */
+ ahc_set_recoveryscb(ahc, scb);
+ sc_print_addr(scb->xs->sc_link);
+ printf("SCB %d: Immediate reset. "
+ "Flags = 0x%x\n", scb->hscb->tag,
+ scb->flags);
+ goto bus_reset;
}
}
- /*
- * No active target or a paged out SCB.
- * Try resetting the bus.
- */
- channel = (scb->tcl & SELBUSB) ? 'B': 'A';
- found = ahc_reset_channel(ahc, channel, scb->tag,
- XS_TIMEOUT,
- /*Initiate Reset*/TRUE);
- printf("%s: Issued Channel %c Bus Reset #2. "
- "%d SCBs aborted\n", ahc_name(ahc), channel,
- found);
- ahc->in_timeout = FALSE;
- }
-done:
+ }
splx(s);
}
-
-/*
- * The device at the given target/channel has been reset. Abort
- * all active and queued scbs for that target/channel.
- */
-static int
-ahc_reset_device(ahc, target, channel, timedout_scb, xs_error)
- struct ahc_data *ahc;
+STATIC int
+ahc_search_qinfifo(ahc, target, channel, lun, tag, role, status, action)
+ struct ahc_softc *ahc;
int target;
char channel;
- u_char timedout_scb;
- u_int32_t xs_error;
+ int lun;
+ u_int tag;
+ role_t role;
+ u_int32_t status;
+ ahc_search_action action;
{
- struct scb *scbp;
- u_char active_scb;
- int i = 0;
- int found = 0;
+ struct scb *scbp;
+ u_int8_t qinpos;
+ u_int8_t qintail;
+ int found;
- /* restore this when we're done */
- active_scb = AHC_INB(ahc, SCBPTR);
+ qinpos = ahc_inb(ahc, QINPOS);
+ qintail = ahc->qinfifonext;
+ found = 0;
/*
- * Search the QINFIFO.
+ * Start with an empty queue. Entries that are not chosen
+ * for removal will be re-added to the queue as we go.
*/
- {
- u_char saved_queue[AHC_SCB_MAX];
- u_char queued = AHC_INB(ahc, QINCNT) & ahc->qcntmask;
-
- for (i = 0; i < (queued - found); i++) {
- saved_queue[i] = AHC_INB(ahc, QINFIFO);
- AHC_OUTB(ahc, SCBPTR, saved_queue[i]);
- scbp = ahc->scbarray[AHC_INB(ahc, SCB_TAG)];
- if (ahc_match_scb (scbp, target, channel)){
- /*
- * We found an scb that needs to be aborted.
- */
- scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE;
- scbp->xs->error |= xs_error;
- if(scbp->position != timedout_scb)
- untimeout(ahc_timeout, (caddr_t)scbp);
- AHC_OUTB(ahc, SCB_CONTROL, 0);
- i--;
- found++;
+ ahc->qinfifonext = qinpos;
+ bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap,
+ BUS_DMASYNC_POSTREAD);
+
+ while (qinpos != qintail) {
+ scbp = &ahc->scb_data->scbarray[ahc->qinfifo[qinpos]];
+ if (ahc_match_scb(scbp, target, channel, lun, tag, role)) {
+ /*
+ * We found an scb that needs to be removed.
+ */
+ switch (action) {
+ case SEARCH_COMPLETE:
+ if (!(scbp->xs->flags & ITSDONE)) {
+ scbp->flags |= status;
+ scbp->xs->error = XS_NOERROR;
+ }
+ ahc_freeze_ccb(scbp);
+ ahc_done(ahc, scbp);
+ break;
+ case SEARCH_COUNT:
+ ahc->qinfifo[ahc->qinfifonext++] =
+ scbp->hscb->tag;
+ break;
+ case SEARCH_REMOVE:
+ break;
}
+ found++;
+ } else {
+ ahc->qinfifo[ahc->qinfifonext++] = scbp->hscb->tag;
}
- /* Now put the saved scbs back. */
- for (queued = 0; queued < i; queued++) {
- AHC_OUTB(ahc, QINFIFO, saved_queue[queued]);
- }
+ qinpos++;
}
+ bus_dmamap_sync(ahc->sc_dmat, ahc->shared_data_dmamap,
+ 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);
+ }
+
+ 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(ahc, target, channel, lun, tag, role, status)
+ struct ahc_softc *ahc;
+ int target;
+ char channel;
+ int lun;
+ u_int tag;
+ role_t role;
+ u_int32_t status;
+{
+ struct scb *scbp;
+ u_int active_scb;
+ int i;
+ int found;
+
+ /* 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_char next, prev;
-
- next = AHC_INB(ahc, WAITING_SCBH); /* Start at head of list. */
+ u_int8_t next, prev;
+ /* Start at head of list. */
+ next = ahc_inb(ahc, WAITING_SCBH);
prev = SCB_LIST_NULL;
while (next != SCB_LIST_NULL) {
- AHC_OUTB(ahc, SCBPTR, next);
- scbp = ahc->scbarray[AHC_INB(ahc, SCB_TAG)];
- /*
- * Select the SCB.
- */
- if (ahc_match_scb(scbp, target, channel)) {
- next = ahc_abort_wscb(ahc, scbp, prev,
- timedout_scb, xs_error);
- found++;
+ u_int8_t scb_index;
+
+ 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);
}
- else {
+ scbp = &ahc->scb_data->scbarray[scb_index];
+ if (ahc_match_scb(scbp, target, channel,
+ lun, SCB_LIST_NULL, role)) {
+
+ next = ahc_abort_wscb(ahc, next, prev);
+ } else {
+
prev = next;
- next = AHC_INB(ahc, SCB_NEXT);
+ next = ahc_inb(ahc, SCB_NEXT);
}
}
}
/*
- * Go through the entire SCB array now and look for
- * commands for this target that are active. These
- * are other (most likely tagged) commands that
- * were disconnected when the reset occured.
+ * 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.
*/
- for(i = 0; i < ahc->numscbs; i++) {
- scbp = ahc->scbarray[i];
- if((scbp->flags & SCB_ACTIVE)
- && ahc_match_scb(scbp, target, channel)) {
- /* Ensure the target is "free" */
- ahc_unbusy_target(ahc, target, channel);
- if( !(scbp->flags & SCB_PAGED_OUT) )
- {
- AHC_OUTB(ahc, SCBPTR, scbp->position);
- AHC_OUTB(ahc, SCB_CONTROL, 0);
+ 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;
+
+ 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);
+ }
+
+ /*
+ * 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 occured.
+ */
+ {
+ struct scb *scb;
+
+ scb = ahc->pending_scbs.lh_first;
+ while (scb != NULL) {
+ scbp = scb;
+ scb = scb->pend_links.le_next;
+ if (ahc_match_scb(scbp, target, channel,
+ lun, tag, role)) {
+ if (!(scbp->xs->flags & ITSDONE))
+ ahcsetccbstatus(scbp->xs, status);
+ ahc_freeze_ccb(scbp);
+ ahc_done(ahc, scbp);
+ found++;
}
- scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE;
- scbp->xs->error |= xs_error;
- if(scbp->tag != timedout_scb)
- untimeout(ahc_timeout, (caddr_t)scbp);
- found++;
}
- }
- AHC_OUTB(ahc, SCBPTR, active_scb);
+ }
+ ahc_outb(ahc, SCBPTR, active_scb);
return found;
}
+STATIC int
+ahc_search_disc_list(ahc, target, channel, lun, tag, stop_on_first,
+ remove, save_state)
+ struct ahc_softc *ahc;
+ int target;
+ char channel;
+ int lun;
+ u_int tag;
+ int stop_on_first;
+ int remove;
+ int save_state;
+{
+ struct scb *scbp;
+ u_int next;
+ u_int prev;
+ u_int count;
+ u_int active_scb;
+
+ count = 0;
+ next = ahc_inb(ahc, DISCONNECTED_SCBH);
+ prev = SCB_LIST_NULL;
+
+ if (save_state) {
+ /* restore this when we're done */
+ active_scb = ahc_inb(ahc, SCBPTR);
+ } else
+ /* Silence compiler */
+ active_scb = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL) {
+ u_int scb_index;
+
+ 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);
+ }
+ scbp = &ahc->scb_data->scbarray[scb_index];
+ if (ahc_match_scb(scbp, target, channel, lun,
+ tag, ROLE_INITIATOR)) {
+ count++;
+ if (remove) {
+ next =
+ ahc_rem_scb_from_disc_list(ahc, prev, next);
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ if (stop_on_first)
+ break;
+ } else {
+ prev = next;
+ next = ahc_inb(ahc, SCB_NEXT);
+ }
+ }
+ if (save_state)
+ ahc_outb(ahc, SCBPTR, active_scb);
+ return (count);
+}
+
+STATIC u_int
+ahc_rem_scb_from_disc_list(ahc, prev, scbptr)
+ struct ahc_softc *ahc;
+ u_int prev;
+ u_int scbptr;
+{
+ u_int next;
+
+ ahc_outb(ahc, SCBPTR, scbptr);
+ next = ahc_inb(ahc, SCB_NEXT);
+
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
+
+ if (prev != SCB_LIST_NULL) {
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
+ } else
+ ahc_outb(ahc, DISCONNECTED_SCBH, next);
+
+ return (next);
+}
+
+STATIC void
+ahc_add_curscb_to_free_list(ahc)
+ struct ahc_softc *ahc;
+{
+ /* Invalidate the tag so that ahc_find_scb doesn'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));
+}
+
/*
* Manipulate the waiting for selection list and return the
* scb that follows the one that we remove.
*/
-static u_char
-ahc_abort_wscb (ahc, scbp, prev, timedout_scb, xs_error)
- struct ahc_data *ahc;
- struct scb *scbp;
- u_char prev;
- u_char timedout_scb;
- u_int32_t xs_error;
+STATIC u_int
+ahc_abort_wscb(ahc, scbpos, prev)
+ struct ahc_softc *ahc;
+ u_int scbpos;
+ u_int prev;
{
- u_char curscbp, next;
- int target = ((scbp->tcl >> 4) & 0x0f);
- char channel = (scbp->tcl & SELBUSB) ? 'B' : 'A';
+ u_int curscb, next;
+
/*
* Select the SCB we want to abort and
* pull the next pointer out of it.
*/
- curscbp = AHC_INB(ahc, SCBPTR);
- AHC_OUTB(ahc, SCBPTR, scbp->position);
- next = AHC_INB(ahc, SCB_NEXT);
+ curscb = ahc_inb(ahc, SCBPTR);
+ ahc_outb(ahc, SCBPTR, scbpos);
+ next = ahc_inb(ahc, SCB_NEXT);
/* Clear the necessary fields */
- AHC_OUTB(ahc, SCB_CONTROL, 0);
- AHC_OUTB(ahc, SCB_NEXT, SCB_LIST_NULL);
- ahc_unbusy_target(ahc, target, channel);
+ ahc_outb(ahc, SCB_CONTROL, 0);
+
+ ahc_add_curscb_to_free_list(ahc);
/* update the waiting list */
- if( prev == SCB_LIST_NULL )
+ if (prev == SCB_LIST_NULL) {
/* First in the list */
- AHC_OUTB(ahc, WAITING_SCBH, next);
- else {
+ ahc_outb(ahc, WAITING_SCBH, next);
+
+ /*
+ * Ensure we aren't attempting to perform
+ * selection for this entry.
+ */
+ ahc_outb(ahc, SCSISEQ, (ahc_inb(ahc, SCSISEQ) & ~ENSELO));
+ } else {
/*
* Select the scb that pointed to us
* and update its next pointer.
*/
- AHC_OUTB(ahc, SCBPTR, prev);
- AHC_OUTB(ahc, SCB_NEXT, next);
+ ahc_outb(ahc, SCBPTR, prev);
+ ahc_outb(ahc, SCB_NEXT, next);
}
+
/*
- * Point us back at the original scb position
- * and inform the SCSI system that the command
- * has been aborted.
+ * Point us back at the original scb position.
*/
- AHC_OUTB(ahc, SCBPTR, curscbp);
- scbp->flags = SCB_ABORTED|SCB_QUEUED_FOR_DONE;
- scbp->xs->error |= xs_error;
- if(scbp->tag != timedout_scb)
- untimeout(ahc_timeout, (caddr_t)scbp);
+ ahc_outb(ahc, SCBPTR, curscb);
return next;
}
-static void
-ahc_busy_target(ahc, target, channel)
- struct ahc_data *ahc;
- u_char target;
- char channel;
+STATIC void
+ahc_clear_intstat(ahc)
+ struct ahc_softc *ahc;
{
- u_char active;
- u_long active_port = ACTIVE_A;
-
- if(target > 0x07 || channel == 'B') {
- /*
- * targets on the Second channel or
- * above id 7 store info in byte two
- * of HA_ACTIVE
- */
- active_port++;
- }
- active = AHC_INB(ahc, active_port);
- active |= (0x01 << (target & 0x07));
- AHC_OUTB(ahc, active_port, active);
+ /* 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);
}
-static void
-ahc_unbusy_target(ahc, target, channel)
- struct ahc_data *ahc;
- u_char target;
- char channel;
+STATIC void
+ahc_reset_current_bus(ahc)
+ struct ahc_softc *ahc;
{
- u_char active;
- u_long active_port = ACTIVE_A;
+ u_int8_t scsiseq;
- if(target > 0x07 || channel == 'B') {
- /*
- * targets on the Second channel or
- * above id 7 store info in byte two
- * of HA_ACTIVE
- */
- active_port++;
- }
- active = AHC_INB(ahc, active_port);
- active &= ~(0x01 << (target & 0x07));
- AHC_OUTB(ahc, active_port, active);
-}
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENSCSIRST);
+ scsiseq = ahc_inb(ahc, SCSISEQ);
+ ahc_outb(ahc, SCSISEQ, scsiseq | SCSIRSTO);
+ DELAY(AHC_BUSRESET_DELAY);
+ /* Turn off the bus reset */
+ ahc_outb(ahc, SCSISEQ, scsiseq & ~SCSIRSTO);
-static void
-ahc_reset_current_bus(ahc)
- struct ahc_data *ahc;
-{
- AHC_OUTB(ahc, SCSISEQ, SCSIRSTO);
- DELAY(1000);
- AHC_OUTB(ahc, SCSISEQ, 0);
+ ahc_clear_intstat(ahc);
+
+ /* Re-enable reset interrupts */
+ ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) | ENSCSIRST);
}
-static int
-ahc_reset_channel(ahc, channel, timedout_scb, xs_error, initiate_reset)
- struct ahc_data *ahc;
- char channel;
- u_char timedout_scb;
- u_int32_t xs_error;
- u_char initiate_reset;
+STATIC int
+ahc_reset_channel(ahc, channel, initiate_reset)
+ struct ahc_softc *ahc;
+ char channel;
+ int initiate_reset;
{
- u_char sblkctl;
- char cur_channel;
- u_long offset, offset_max;
- int found;
+ u_int initiator, target, max_scsiid;
+ u_int sblkctl;
+ u_int our_id;
+ int found;
+ int restart_needed;
+ char cur_channel;
+
+ ahc->pending_device = NULL;
+
+ pause_sequencer(ahc);
/*
- * Clean up all the state information for the
- * pending transactions on this bus.
+ * Run our command complete fifos to ensure that we perform
+ * completion processing on any commands that 'completed'
+ * before the reset occurred.
+ */
+ ahc_run_qoutfifo(ahc);
+
+ /*
+ * Reset the bus if we are initiating this reset
*/
- found = ahc_reset_device(ahc, ALL_TARGETS, channel,
- timedout_scb, xs_error);
- if(channel == 'B'){
- ahc->needsdtr |= (ahc->needsdtr_orig & 0xff00);
- ahc->sdtrpending &= 0x00ff;
- AHC_OUTB(ahc, ACTIVE_B, 0);
- offset = TARG_SCRATCH + 8;
- offset_max = TARG_SCRATCH + 16;
- }
- else if (ahc->type & AHC_WIDE){
- ahc->needsdtr = ahc->needsdtr_orig;
- ahc->needwdtr = ahc->needwdtr_orig;
- ahc->sdtrpending = 0;
- ahc->wdtrpending = 0;
- AHC_OUTB(ahc, ACTIVE_A, 0);
- AHC_OUTB(ahc, ACTIVE_B, 0);
- offset = TARG_SCRATCH;
- offset_max = TARG_SCRATCH + 16;
- }
- else{
- ahc->needsdtr |= (ahc->needsdtr_orig & 0x00ff);
- ahc->sdtrpending &= 0xff00;
- AHC_OUTB(ahc, ACTIVE_A, 0);
- offset = TARG_SCRATCH;
- offset_max = TARG_SCRATCH + 8;
- }
- for(;offset < offset_max;offset++) {
+ sblkctl = ahc_inb(ahc, SBLKCTL);
+ cur_channel = 'A';
+ if ((ahc->features & AHC_TWIN) != 0
+ && ((sblkctl & SELBUSB) != 0))
+ cur_channel = 'B';
+ 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));
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+ 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));
+ if (initiate_reset)
+ ahc_reset_current_bus(ahc);
+ ahc_clear_intstat(ahc);
+
/*
- * Revert to async/narrow transfers
- * until we renegotiate.
+ * 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.
*/
- u_char targ_scratch;
+ ahc->qoutfifonext = 0;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0)
+ ahc_outb(ahc, SDSCB_QOFF, 0);
+ else
+ ahc_outb(ahc, QOUTPOS, 0);
+ restart_needed = TRUE;
+ }
- targ_scratch = AHC_INB(ahc, offset);
- targ_scratch &= SXFR;
- AHC_OUTB(ahc, offset, targ_scratch);
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = ahc_abort_scbs(ahc, ALL_TARGETS, channel,
+ ALL_LUNS, SCB_LIST_NULL,
+ ROLE_UNKNOWN, XS_RESET);
+ if (channel == 'B') {
+ our_id = ahc->our_id_b;
+ } else {
+ our_id = ahc->our_id;
}
+ max_scsiid = (ahc->features & AHC_WIDE) ? 15 : 7;
+
/*
- * Reset the bus if we are initiating this reset and
- * restart/unpause the sequencer
+ * Revert to async/narrow transfers until we renegotiate.
*/
- /* Case 1: Command for another bus is active */
- sblkctl = AHC_INB(ahc, SBLKCTL);
- cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
- if(cur_channel != channel)
- {
- /*
- * Stealthily reset the other bus
- * without upsetting the current bus
- */
- AHC_OUTB(ahc, SBLKCTL, sblkctl ^ SELBUSB);
- if( initiate_reset )
- {
- ahc_reset_current_bus(ahc);
+ for (target = 0; target <= max_scsiid; target++) {
+
+ if (ahc->enabled_targets[target] == NULL)
+ continue;
+ for (initiator = 0; initiator <= max_scsiid; initiator++) {
+ struct ahc_devinfo devinfo;
+
+ ahc_compile_devinfo(&devinfo, target, initiator,
+ ALL_LUNS,
+ channel, ROLE_UNKNOWN);
+ ahc_set_width(ahc, &devinfo,
+ MSG_EXT_WDTR_BUS_8_BIT,
+ AHC_TRANS_CUR,
+ /*paused*/TRUE,
+ /*done*/FALSE);
+ ahc_set_syncrate(ahc, &devinfo,
+ /*syncrate*/NULL, /*period*/0,
+ /*offset*/0, AHC_TRANS_CUR,
+ /*paused*/TRUE,
+ /*done*/FALSE);
}
- AHC_OUTB(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO);
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
- AHC_OUTB(ahc, SBLKCTL, sblkctl);
- unpause_sequencer(ahc, /*unpause_always*/TRUE);
}
- /* Case 2: A command from this bus is active or we're idle */
- else {
- if( initiate_reset )
- {
- ahc_reset_current_bus(ahc);
- }
- AHC_OUTB(ahc, CLRSINT1, CLRSCSIRSTI|CLRSELTIMEO);
- AHC_OUTB(ahc, CLRINT, CLRSCSIINT);
+
+ if (restart_needed)
restart_sequencer(ahc);
- }
- ahc_run_done_queue(ahc);
+ else
+ unpause_sequencer(ahc);
return found;
}
-void
-ahc_run_done_queue(ahc)
- struct ahc_data *ahc;
+STATIC int
+ahc_match_scb(scb, target, channel, lun, role, tag)
+ struct scb *scb;
+ int target;
+ char channel;
+ int lun;
+ role_t role;
+ u_int tag;
{
- int i;
- struct scb *scbp;
-
- for(i = 0; i < ahc->numscbs; i++) {
- scbp = ahc->scbarray[i];
- if(scbp->flags & SCB_QUEUED_FOR_DONE)
- ahc_done(ahc, scbp);
- }
+ int targ = SCB_TARGET(scb);
+ char chan = SCB_CHANNEL(scb);
+ int slun = SCB_LUN(scb);
+ int match;
+
+ match = ((chan == channel) || (channel == ALL_CHANNELS));
+ if (match != 0)
+ match = ((targ == target) || (target == ALL_TARGETS));
+ if (match != 0)
+ match = ((lun == slun) || (lun == ALL_LUNS));
+ return match;
}
-
-static int
-ahc_match_scb (scb, target, channel)
- struct scb *scb;
- int target;
- char channel;
+
+STATIC void
+ahc_construct_sdtr(ahc, period, offset)
+ struct ahc_softc *ahc;
+ u_int period;
+ u_int offset;
{
- int targ = (scb->tcl >> 4) & 0x0f;
- char chan = (scb->tcl & SELBUSB) ? 'B' : 'A';
+ 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 (target == ALL_TARGETS)
- return (chan == channel);
- else
- return ((chan == channel) && (targ == target));
+STATIC void
+ahc_construct_wdtr(ahc, bus_width)
+ struct ahc_softc *ahc;
+ 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;
}
+STATIC void
+ahc_calc_residual(scb)
+ struct scb *scb;
+{
+ struct hardware_scb *hscb;
+
+ hscb = scb->hscb;
+
+ /*
+ * If the disconnected flag is still set, this is bogus
+ * residual information left over from a sequencer
+ * pagin/pageout, so ignore this case.
+ */
+ if ((scb->hscb->control & DISCONNECTED) == 0) {
+ u_int32_t resid;
+ int resid_sgs;
+ int sg;
+
+ /*
+ * 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]);
+
+ /*
+ * Add up the contents of all residual
+ * SG segments that are after the SG where
+ * the transfer stopped.
+ */
+ resid_sgs = scb->hscb->residual_SG_count - 1/*current*/;
+ sg = scb->sg_count - resid_sgs;
+ while (resid_sgs > 0) {
+
+ resid += scb->sg_list[sg].len;
+ sg++;
+ resid_sgs--;
+ }
+ scb->xs->resid = resid;
+ }
+
+ /*
+ * Clean out the residual information in this SCB for its
+ * next consumer.
+ */
+ hscb->residual_SG_count = 0;
+
+#ifdef AHC_DEBUG
+ if (ahc_debug & AHC_SHOWMISC) {
+ sc_print_addr(scb->xs->sc_link);
+ printf("Handled Residual of %ld bytes\n" ,scb->xs->resid);
+ }
+#endif
+}
-static void
-ahc_construct_sdtr(ahc, start_byte, period, offset)
- struct ahc_data *ahc;
- int start_byte;
- u_int8_t period;
- u_int8_t offset;
+STATIC void
+ahc_update_pending_syncrates(ahc)
+ struct ahc_softc *ahc;
{
- AHC_OUTB(ahc, MSG0 + start_byte, MSG_EXTENDED);
- AHC_OUTB(ahc, MSG1 + start_byte, MSG_EXT_SDTR_LEN);
- AHC_OUTB(ahc, MSG2 + start_byte, MSG_EXT_SDTR);
- AHC_OUTB(ahc, MSG3 + start_byte, period);
- AHC_OUTB(ahc, MSG4 + start_byte, offset);
- AHC_OUTB(ahc, MSG_LEN, start_byte + 5);
+ struct scb *scb;
+ int pending_scb_count;
+ int i;
+ u_int saved_scbptr;
+
+ /*
+ * Traverse the pending SCB list and ensure that all of the
+ * SCBs there have the proper settings.
+ */
+ scb = LIST_FIRST(&ahc->pending_scbs);
+ pending_scb_count = 0;
+ while (scb != NULL) {
+ struct ahc_devinfo devinfo;
+ struct scsi_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;
+
+ 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->sc_link->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_scb_count++;
+ scb = LIST_NEXT(scb, pend_links);
+ }
+
+ if (pending_scb_count == 0)
+ return;
+
+ 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;
+
+ 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 scsi_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->sc_link->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);
+ }
+ }
+ ahc_outb(ahc, SCBPTR, saved_scbptr);
}
-static void
-ahc_construct_wdtr(ahc, start_byte, bus_width)
- struct ahc_data *ahc;
- int start_byte;
- u_int8_t bus_width;
+STATIC void
+ahc_shutdown(void *arg)
{
- AHC_OUTB(ahc, MSG0 + start_byte, MSG_EXTENDED);
- AHC_OUTB(ahc, MSG1 + start_byte, MSG_EXT_WDTR_LEN);
- AHC_OUTB(ahc, MSG2 + start_byte, MSG_EXT_WDTR);
- AHC_OUTB(ahc, MSG3 + start_byte, bus_width);
- AHC_OUTB(ahc, MSG_LEN, start_byte + 4);
+ struct ahc_softc *ahc;
+ int i;
+ u_int sxfrctl1_a, sxfrctl1_b;
+
+ ahc = (struct ahc_softc *)arg;
+
+ pause_sequencer(ahc);
+
+ /*
+ * 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.
+ */
+ sxfrctl1_b = 0;
+ if ((ahc->features & AHC_TWIN) != 0) {
+ u_int sblkctl;
+
+ 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);
+
+ /* This will reset most registers to 0, but not all */
+ ahc_reset(ahc);
+
+ if ((ahc->features & AHC_TWIN) != 0) {
+ u_int sblkctl;
+
+ 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);
+
+ ahc_outb(ahc, SCSISEQ, 0);
+ ahc_outb(ahc, SXFRCTL0, 0);
+ ahc_outb(ahc, DSPCISTATUS, 0);
+
+ for (i = TARG_SCSIRATE; i < HA_274_BIOSCTRL; i++)
+ ahc_outb(ahc, i, 0);
}