Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/scsi/aic7xxx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 289 kB image not shown  

Quelle  aic79xx_core.c   Sprache: C

 
/*
 * Core routines and tables shareable across OS platforms.
 *
 * Copyright (c) 1994-2002 Justin T. Gibbs.
 * Copyright (c) 2000-2003 Adaptec Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer,
 *    without modification.
 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
 *    substantially similar to the "NO WARRANTY" disclaimer below
 *    ("Disclaimer") and any redistribution must be conditioned upon
 *    including a substantially similar Disclaimer requirement for further
 *    binary redistribution.
 * 3. Neither the names of the above-listed copyright holders nor the names
 *    of any contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * Alternatively, this software may be distributed under the terms of the
 * GNU General Public License ("GPL") version 2 as published by the Free
 * Software Foundation.
 *
 * NO WARRANTY
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * $Id: //depot/aic7xxx/aic7xxx/aic79xx.c#250 $
 */


#include "aic79xx_osm.h"
#include "aic79xx_inline.h"
#include "aicasm/aicasm_insformat.h"

/***************************** Lookup Tables **********************************/
static const char *const ahd_chip_names[] =
{
 "NONE",
 "aic7901",
 "aic7902",
 "aic7901A"
};

/*
 * Hardware error codes.
 */

struct ahd_hard_error_entry {
 uint8_t errno;
 const char *errmesg;
};

static const struct ahd_hard_error_entry ahd_hard_errors[] = {
 { DSCTMOUT, "Discard Timer has timed out" },
 { ILLOPCODE, "Illegal Opcode in sequencer program" },
 { SQPARERR, "Sequencer Parity Error" },
 { DPARERR, "Data-path Parity Error" },
 { MPARERR, "Scratch or SCB Memory Parity Error" },
 { CIOPARERR, "CIOBUS Parity Error" },
};
static const u_int num_errors = ARRAY_SIZE(ahd_hard_errors);

static const struct ahd_phase_table_entry ahd_phase_table[] =
{
 { P_DATAOUT, NOP,   "in Data-out phase" },
 { P_DATAIN, INITIATOR_ERROR, "in Data-in phase" },
 { P_DATAOUT_DT, NOP,   "in DT Data-out phase" },
 { P_DATAIN_DT, INITIATOR_ERROR, "in DT Data-in phase" },
 { P_COMMAND, NOP,   "in Command phase" },
 { P_MESGOUT, NOP,   "in Message-out phase" },
 { P_STATUS, INITIATOR_ERROR, "in Status phase" },
 { P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
 { P_BUSFREE, NOP,   "while idle"  },
 { 0,  NOP,   "in unknown phase" }
};

/*
 * In most cases we only wish to itterate over real phases, so
 * exclude the last element from the count.
 */

static const u_int num_phases = ARRAY_SIZE(ahd_phase_table) - 1;

/* Our Sequencer Program */
#include "aic79xx_seq.h"

/**************************** Function Declarations ***************************/
static void  ahd_handle_transmission_error(struct ahd_softc *ahd);
static void  ahd_handle_lqiphase_error(struct ahd_softc *ahd,
        u_int lqistat1);
static int  ahd_handle_pkt_busfree(struct ahd_softc *ahd,
            u_int busfreetime);
static int  ahd_handle_nonpkt_busfree(struct ahd_softc *ahd);
static void  ahd_handle_proto_violation(struct ahd_softc *ahd);
static void  ahd_force_renegotiation(struct ahd_softc *ahd,
      struct ahd_devinfo *devinfo);

static struct ahd_tmode_tstate*
   ahd_alloc_tstate(struct ahd_softc *ahd,
      u_int scsi_id, char channel);
#ifdef AHD_TARGET_MODE
static void  ahd_free_tstate(struct ahd_softc *ahd,
     u_int scsi_id, char channel, int force);
#endif
static void  ahd_devlimited_syncrate(struct ahd_softc *ahd,
      struct ahd_initiator_tinfo *,
      u_int *period,
      u_int *ppr_options,
      role_t role);
static void  ahd_update_neg_table(struct ahd_softc *ahd,
          struct ahd_devinfo *devinfo,
          struct ahd_transinfo *tinfo);
static void  ahd_update_pending_scbs(struct ahd_softc *ahd);
static void  ahd_fetch_devinfo(struct ahd_softc *ahd,
       struct ahd_devinfo *devinfo);
static void  ahd_scb_devinfo(struct ahd_softc *ahd,
     struct ahd_devinfo *devinfo,
     struct scb *scb);
static void  ahd_setup_initiator_msgout(struct ahd_softc *ahd,
         struct ahd_devinfo *devinfo,
         struct scb *scb);
static void  ahd_build_transfer_msg(struct ahd_softc *ahd,
            struct ahd_devinfo *devinfo);
static void  ahd_construct_sdtr(struct ahd_softc *ahd,
        struct ahd_devinfo *devinfo,
        u_int period, u_int offset);
static void  ahd_construct_wdtr(struct ahd_softc *ahd,
        struct ahd_devinfo *devinfo,
        u_int bus_width);
static void  ahd_construct_ppr(struct ahd_softc *ahd,
       struct ahd_devinfo *devinfo,
       u_int period, u_int offset,
       u_int bus_width, u_int ppr_options);
static void  ahd_clear_msg_state(struct ahd_softc *ahd);
static void  ahd_handle_message_phase(struct ahd_softc *ahd);
typedef enum {
 AHDMSG_1B,
 AHDMSG_2B,
 AHDMSG_EXT
} ahd_msgtype;
static int  ahd_sent_msg(struct ahd_softc *ahd, ahd_msgtype type,
         u_int msgval, int full);
static int  ahd_parse_msg(struct ahd_softc *ahd,
          struct ahd_devinfo *devinfo);
static int  ahd_handle_msg_reject(struct ahd_softc *ahd,
           struct ahd_devinfo *devinfo);
static void  ahd_handle_ign_wide_residue(struct ahd_softc *ahd,
      struct ahd_devinfo *devinfo);
static void  ahd_reinitialize_dataptrs(struct ahd_softc *ahd);
static void  ahd_handle_devreset(struct ahd_softc *ahd,
         struct ahd_devinfo *devinfo,
         u_int lun, cam_status status,
         char *message, int verbose_level);
#ifdef AHD_TARGET_MODE
static void  ahd_setup_target_msgin(struct ahd_softc *ahd,
            struct ahd_devinfo *devinfo,
            struct scb *scb);
#endif

static u_int  ahd_sglist_size(struct ahd_softc *ahd);
static u_int  ahd_sglist_allocsize(struct ahd_softc *ahd);
static bus_dmamap_callback_t
   ahd_dmamap_cb;
static void  ahd_initialize_hscbs(struct ahd_softc *ahd);
static int  ahd_init_scbdata(struct ahd_softc *ahd);
static void  ahd_fini_scbdata(struct ahd_softc *ahd);
static void  ahd_setup_iocell_workaround(struct ahd_softc *ahd);
static void  ahd_iocell_first_selection(struct ahd_softc *ahd);
static void  ahd_add_col_list(struct ahd_softc *ahd,
      struct scb *scb, u_int col_idx);
static void  ahd_rem_col_list(struct ahd_softc *ahd,
      struct scb *scb);
static void  ahd_chip_init(struct ahd_softc *ahd);
static void  ahd_qinfifo_requeue(struct ahd_softc *ahd,
         struct scb *prev_scb,
         struct scb *scb);
static int  ahd_qinfifo_count(struct ahd_softc *ahd);
static int  ahd_search_scb_list(struct ahd_softc *ahd, int target,
         char channel, int lun, u_int tag,
         role_t role, uint32_t status,
         ahd_search_action action,
         u_int *list_head, u_int *list_tail,
         u_int tid);
static void  ahd_stitch_tid_list(struct ahd_softc *ahd,
         u_int tid_prev, u_int tid_cur,
         u_int tid_next);
static void  ahd_add_scb_to_free_list(struct ahd_softc *ahd,
       u_int scbid);
static u_int  ahd_rem_wscb(struct ahd_softc *ahd, u_int scbid,
         u_int prev, u_int next, u_int tid);
static void  ahd_reset_current_bus(struct ahd_softc *ahd);
static void  ahd_stat_timer(struct timer_list *t);
#ifdef AHD_DUMP_SEQ
static void  ahd_dumpseq(struct ahd_softc *ahd);
#endif
static void  ahd_loadseq(struct ahd_softc *ahd);
static int  ahd_check_patch(struct ahd_softc *ahd,
     const struct patch **start_patch,
     u_int start_instr, u_int *skip_addr);
static u_int  ahd_resolve_seqaddr(struct ahd_softc *ahd,
         u_int address);
static void  ahd_download_instr(struct ahd_softc *ahd,
        u_int instrptr, uint8_t *dconsts);
static int  ahd_probe_stack_size(struct ahd_softc *ahd);
static int  ahd_scb_active_in_fifo(struct ahd_softc *ahd,
            struct scb *scb);
static void  ahd_run_data_fifo(struct ahd_softc *ahd,
       struct scb *scb);

#ifdef AHD_TARGET_MODE
static void  ahd_queue_lstate_event(struct ahd_softc *ahd,
            struct ahd_tmode_lstate *lstate,
            u_int initiator_id,
            u_int event_type,
            u_int event_arg);
static void  ahd_update_scsiid(struct ahd_softc *ahd,
       u_int targid_mask);
static int  ahd_handle_target_cmd(struct ahd_softc *ahd,
           struct target_cmd *cmd);
#endif

static int  ahd_abort_scbs(struct ahd_softc *ahd, int target,
           char channel, int lun, u_int tag,
           role_t role, uint32_t status);
static void  ahd_alloc_scbs(struct ahd_softc *ahd);
static void  ahd_busy_tcl(struct ahd_softc *ahd, u_int tcl,
         u_int scbid);
static void  ahd_calc_residual(struct ahd_softc *ahd,
       struct scb *scb);
static void  ahd_clear_critical_section(struct ahd_softc *ahd);
static void  ahd_clear_intstat(struct ahd_softc *ahd);
static void  ahd_enable_coalescing(struct ahd_softc *ahd,
           int enable);
static u_int  ahd_find_busy_tcl(struct ahd_softc *ahd, u_int tcl);
static void  ahd_freeze_devq(struct ahd_softc *ahd,
     struct scb *scb);
static void  ahd_handle_scb_status(struct ahd_softc *ahd,
           struct scb *scb);
static const struct ahd_phase_table_entry* ahd_lookup_phase_entry(int phase);
static void  ahd_shutdown(void *arg);
static void  ahd_update_coalescing_values(struct ahd_softc *ahd,
           u_int timer,
           u_int maxcmds,
           u_int mincmds);
static int  ahd_verify_vpd_cksum(struct vpd_config *vpd);
static int  ahd_wait_seeprom(struct ahd_softc *ahd);
static int  ahd_match_scb(struct ahd_softc *ahd, struct scb *scb,
          int target, char channel, int lun,
          u_int tag, role_t role);

static void  ahd_reset_cmds_pending(struct ahd_softc *ahd);

/*************************** Interrupt Services *******************************/
static void  ahd_run_qoutfifo(struct ahd_softc *ahd);
#ifdef AHD_TARGET_MODE
static void  ahd_run_tqinfifo(struct ahd_softc *ahd, int paused);
#endif
static void  ahd_handle_hwerrint(struct ahd_softc *ahd);
static void  ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat);
static void  ahd_handle_scsiint(struct ahd_softc *ahd,
        u_int intstat);

/************************ Sequencer Execution Control *************************/
void
ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst)
{
 if (ahd->src_mode == src && ahd->dst_mode == dst)
  return;
#ifdef AHD_DEBUG
 if (ahd->src_mode == AHD_MODE_UNKNOWN
  || ahd->dst_mode == AHD_MODE_UNKNOWN)
  panic("Setting mode prior to saving it.\n");
 if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
  printk("%s: Setting mode 0x%x\n", ahd_name(ahd),
         ahd_build_mode_state(ahd, src, dst));
#endif
 ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst));
 ahd->src_mode = src;
 ahd->dst_mode = dst;
}

static void
ahd_update_modes(struct ahd_softc *ahd)
{
 ahd_mode_state mode_ptr;
 ahd_mode src;
 ahd_mode dst;

 mode_ptr = ahd_inb(ahd, MODE_PTR);
#ifdef AHD_DEBUG
 if ((ahd_debug & AHD_SHOW_MODEPTR) != 0)
  printk("Reading mode 0x%x\n", mode_ptr);
#endif
 ahd_extract_mode_state(ahd, mode_ptr, &src, &dst);
 ahd_known_modes(ahd, src, dst);
}

static void
ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode,
   ahd_mode dstmode, const char *file, int line)
{
#ifdef AHD_DEBUG
 if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0
  || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) {
  panic("%s:%s:%d: Mode assertion failed.\n",
         ahd_name(ahd), file, line);
 }
#endif
}

#define AHD_ASSERT_MODES(ahd, source, dest) \
 ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__);

ahd_mode_state
ahd_save_modes(struct ahd_softc *ahd)
{
 if (ahd->src_mode == AHD_MODE_UNKNOWN
  || ahd->dst_mode == AHD_MODE_UNKNOWN)
  ahd_update_modes(ahd);

 return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode));
}

void
ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state)
{
 ahd_mode src;
 ahd_mode dst;

 ahd_extract_mode_state(ahd, state, &src, &dst);
 ahd_set_modes(ahd, src, dst);
}

/*
 * Determine whether the sequencer has halted code execution.
 * Returns non-zero status if the sequencer is stopped.
 */

int
ahd_is_paused(struct ahd_softc *ahd)
{
 return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0);
}

/*
 * Request that the sequencer stop and wait, indefinitely, for it
 * to stop.  The sequencer will only acknowledge that it is paused
 * once it has reached an instruction boundary and PAUSEDIS is
 * cleared in the SEQCTL register.  The sequencer may use PAUSEDIS
 * for critical sections.
 */

void
ahd_pause(struct ahd_softc *ahd)
{
 ahd_outb(ahd, HCNTRL, ahd->pause);

 /*
 * Since the sequencer can disable pausing in a critical section, we
 * must loop until it actually stops.
 */

 while (ahd_is_paused(ahd) == 0)
  ;
}

/*
 * Allow the sequencer to continue program execution.
 * We check here to ensure that no additional interrupt
 * sources that would cause the sequencer to halt have been
 * asserted.  If, for example, a SCSI bus reset is detected
 * while we are fielding a different, pausing, interrupt type,
 * we don't want to release the sequencer before going back
 * into our interrupt handler and dealing with this new
 * condition.
 */

void
ahd_unpause(struct ahd_softc *ahd)
{
 /*
 * Automatically restore our modes to those saved
 * prior to the first change of the mode.
 */

 if (ahd->saved_src_mode != AHD_MODE_UNKNOWN
  && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) {
  if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0)
   ahd_reset_cmds_pending(ahd);
  ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
 }

 if ((ahd_inb(ahd, INTSTAT) & ~CMDCMPLT) == 0)
  ahd_outb(ahd, HCNTRL, ahd->unpause);

 ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN);
}

/*********************** Scatter Gather List Handling *************************/
void *
ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb,
      void *sgptr, dma_addr_t addr, bus_size_t len, int last)
{
 scb->sg_count++;
 if (sizeof(dma_addr_t) > 4
  && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
  struct ahd_dma64_seg *sg;

  sg = (struct ahd_dma64_seg *)sgptr;
  sg->addr = ahd_htole64(addr);
  sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0));
  return (sg + 1);
 } else {
  struct ahd_dma_seg *sg;

  sg = (struct ahd_dma_seg *)sgptr;
  sg->addr = ahd_htole32(addr & 0xFFFFFFFF);
  sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000)
        | (last ? AHD_DMA_LAST_SEG : 0));
  return (sg + 1);
 }
}

static void
ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb)
{
 /* XXX Handle target mode SCBs. */
 scb->crc_retry_count = 0;
 if ((scb->flags & SCB_PACKETIZED) != 0) {
  /* XXX what about ACA??  It is type 4, but TAG_TYPE == 0x3. */
  scb->hscb->task_attribute = scb->hscb->control & SCB_TAG_TYPE;
 } else {
  if (ahd_get_transfer_length(scb) & 0x01)
   scb->hscb->task_attribute = SCB_XFERLEN_ODD;
  else
   scb->hscb->task_attribute = 0;
 }

 if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR
  || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0)
  scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr =
      ahd_htole32(scb->sense_busaddr);
}

static void
ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb)
{
 /*
 * Copy the first SG into the "current" data ponter area.
 */

 if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
  struct ahd_dma64_seg *sg;

  sg = (struct ahd_dma64_seg *)scb->sg_list;
  scb->hscb->dataptr = sg->addr;
  scb->hscb->datacnt = sg->len;
 } else {
  struct ahd_dma_seg *sg;
  uint32_t *dataptr_words;

  sg = (struct ahd_dma_seg *)scb->sg_list;
  dataptr_words = (uint32_t*)&scb->hscb->dataptr;
  dataptr_words[0] = sg->addr;
  dataptr_words[1] = 0;
  if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) {
   uint64_t high_addr;

   high_addr = ahd_le32toh(sg->len) & 0x7F000000;
   scb->hscb->dataptr |= ahd_htole64(high_addr << 8);
  }
  scb->hscb->datacnt = sg->len;
 }
 /*
 * Note where to find the SG entries in bus space.
 * We also set the full residual flag which the
 * sequencer will clear as soon as a data transfer
 * occurs.
 */

 scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID);
}

static void
ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb)
{
 scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL);
 scb->hscb->dataptr = 0;
 scb->hscb->datacnt = 0;
}

/************************** Memory mapping routines ***************************/
static void *
ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr)
{
 dma_addr_t sg_offset;

 /* sg_list_phys points to entry 1, not 0 */
 sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd));
 return ((uint8_t *)scb->sg_list + sg_offset);
}

static uint32_t
ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg)
{
 dma_addr_t sg_offset;

 /* sg_list_phys points to entry 1, not 0 */
 sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list)
    - ahd_sg_size(ahd);

 return (scb->sg_list_busaddr + sg_offset);
}

static void
ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op)
{
 ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat,
   scb->hscb_map->dmamap,
   /*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr,
   /*len*/sizeof(*scb->hscb), op);
}

void
ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op)
{
 if (scb->sg_count == 0)
  return;

 ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat,
   scb->sg_map->dmamap,
   /*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd),
   /*len*/ahd_sg_size(ahd) * scb->sg_count, op);
}

static void
ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op)
{
 ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat,
   scb->sense_map->dmamap,
   /*offset*/scb->sense_busaddr,
   /*len*/AHD_SENSE_BUFSIZE, op);
}

#ifdef AHD_TARGET_MODE
static uint32_t
ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index)
{
 return (((uint8_t *)&ahd->targetcmds[index])
        - (uint8_t *)ahd->qoutfifo);
}
#endif

/*********************** Miscellaneous Support Functions ***********************/
/*
 * Return pointers to the transfer negotiation information
 * for the specified our_id/remote_id pair.
 */

struct ahd_initiator_tinfo *
ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id,
      u_int remote_id, struct ahd_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 = ahd->enabled_targets[our_id];
 return (&(*tstate)->transinfo[remote_id]);
}

uint16_t
ahd_inw(struct ahd_softc *ahd, u_int port)
{
 /*
 * Read high byte first as some registers increment
 * or have other side effects when the low byte is
 * read.
 */

 uint16_t r = ahd_inb(ahd, port+1) << 8;
 return r | ahd_inb(ahd, port);
}

void
ahd_outw(struct ahd_softc *ahd, u_int port, u_int value)
{
 /*
 * Write low byte first to accommodate registers
 * such as PRGMCNT where the order maters.
 */

 ahd_outb(ahd, port, value & 0xFF);
 ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
}

uint32_t
ahd_inl(struct ahd_softc *ahd, u_int port)
{
 return ((ahd_inb(ahd, port))
       | (ahd_inb(ahd, port+1) << 8)
       | (ahd_inb(ahd, port+2) << 16)
       | (ahd_inb(ahd, port+3) << 24));
}

void
ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value)
{
 ahd_outb(ahd, port, (value) & 0xFF);
 ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF);
 ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF);
 ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF);
}

uint64_t
ahd_inq(struct ahd_softc *ahd, u_int port)
{
 return ((ahd_inb(ahd, port))
       | (ahd_inb(ahd, port+1) << 8)
       | (ahd_inb(ahd, port+2) << 16)
       | (ahd_inb(ahd, port+3) << 24)
       | (((uint64_t)ahd_inb(ahd, port+4)) << 32)
       | (((uint64_t)ahd_inb(ahd, port+5)) << 40)
       | (((uint64_t)ahd_inb(ahd, port+6)) << 48)
       | (((uint64_t)ahd_inb(ahd, port+7)) << 56));
}

void
ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value)
{
 ahd_outb(ahd, port, value & 0xFF);
 ahd_outb(ahd, port+1, (value >> 8) & 0xFF);
 ahd_outb(ahd, port+2, (value >> 16) & 0xFF);
 ahd_outb(ahd, port+3, (value >> 24) & 0xFF);
 ahd_outb(ahd, port+4, (value >> 32) & 0xFF);
 ahd_outb(ahd, port+5, (value >> 40) & 0xFF);
 ahd_outb(ahd, port+6, (value >> 48) & 0xFF);
 ahd_outb(ahd, port+7, (value >> 56) & 0xFF);
}

u_int
ahd_get_scbptr(struct ahd_softc *ahd)
{
 AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
    ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
 return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8));
}

void
ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr)
{
 AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
    ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
 ahd_outb(ahd, SCBPTR, scbptr & 0xFF);
 ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF);
}

#if 0 /* unused */
static u_int
ahd_get_hnscb_qoff(struct ahd_softc *ahd)
{
 return (ahd_inw_atomic(ahd, HNSCB_QOFF));
}
#endif

static void
ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value)
{
 ahd_outw_atomic(ahd, HNSCB_QOFF, value);
}

#if 0 /* unused */
static u_int
ahd_get_hescb_qoff(struct ahd_softc *ahd)
{
 return (ahd_inb(ahd, HESCB_QOFF));
}
#endif

static void
ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value)
{
 ahd_outb(ahd, HESCB_QOFF, value);
}

static u_int
ahd_get_snscb_qoff(struct ahd_softc *ahd)
{
 u_int oldvalue;

 AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
 oldvalue = ahd_inw(ahd, SNSCB_QOFF);
 ahd_outw(ahd, SNSCB_QOFF, oldvalue);
 return (oldvalue);
}

static void
ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value)
{
 AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
 ahd_outw(ahd, SNSCB_QOFF, value);
}

#if 0 /* unused */
static u_int
ahd_get_sescb_qoff(struct ahd_softc *ahd)
{
 AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
 return (ahd_inb(ahd, SESCB_QOFF));
}
#endif

static void
ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value)
{
 AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
 ahd_outb(ahd, SESCB_QOFF, value);
}

#if 0 /* unused */
static u_int
ahd_get_sdscb_qoff(struct ahd_softc *ahd)
{
 AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
 return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8));
}
#endif

static void
ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value)
{
 AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK);
 ahd_outb(ahd, SDSCB_QOFF, value & 0xFF);
 ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF);
}

u_int
ahd_inb_scbram(struct ahd_softc *ahd, u_int offset)
{
 u_int value;

 /*
 * Workaround PCI-X Rev A. hardware bug.
 * After a host read of SCB memory, the chip
 * may become confused into thinking prefetch
 * was required.  This starts the discard timer
 * running and can cause an unexpected discard
 * timer interrupt.  The work around is to read
 * a normal register prior to the exhaustion of
 * the discard timer.  The mode pointer register
 * has no side effects and so serves well for
 * this purpose.
 *
 * Razor #528
 */

 value = ahd_inb(ahd, offset);
 if ((ahd->bugs & AHD_PCIX_SCBRAM_RD_BUG) != 0)
  ahd_inb(ahd, MODE_PTR);
 return (value);
}

u_int
ahd_inw_scbram(struct ahd_softc *ahd, u_int offset)
{
 return (ahd_inb_scbram(ahd, offset)
       | (ahd_inb_scbram(ahd, offset+1) << 8));
}

static uint32_t
ahd_inl_scbram(struct ahd_softc *ahd, u_int offset)
{
 return (ahd_inw_scbram(ahd, offset)
       | (ahd_inw_scbram(ahd, offset+2) << 16));
}

static uint64_t
ahd_inq_scbram(struct ahd_softc *ahd, u_int offset)
{
 return (ahd_inl_scbram(ahd, offset)
       | ((uint64_t)ahd_inl_scbram(ahd, offset+4)) << 32);
}

struct scb *
ahd_lookup_scb(struct ahd_softc *ahd, u_int tag)
{
 struct scb* scb;

 if (tag >= AHD_SCB_MAX)
  return (NULL);
 scb = ahd->scb_data.scbindex[tag];
 if (scb != NULL)
  ahd_sync_scb(ahd, scb,
        BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
 return (scb);
}

static void
ahd_swap_with_next_hscb(struct ahd_softc *ahd, struct scb *scb)
{
 struct  hardware_scb *q_hscb;
 struct  map_node *q_hscb_map;
 uint32_t saved_hscb_busaddr;

 /*
 * Our queuing method is a bit tricky.  The card
 * knows in advance which HSCB (by address) to download,
 * and we can't disappoint it.  To achieve this, the next
 * HSCB to download is saved off in ahd->next_queued_hscb.
 * When we are called to queue "an arbitrary scb",
 * we copy the contents of the incoming HSCB to the one
 * the sequencer knows about, swap HSCB pointers and
 * finally assign the SCB to the tag indexed location
 * in the scb_array.  This makes sure that we can still
 * locate the correct SCB by SCB_TAG.
 */

 q_hscb = ahd->next_queued_hscb;
 q_hscb_map = ahd->next_queued_hscb_map;
 saved_hscb_busaddr = q_hscb->hscb_busaddr;
 memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb));
 q_hscb->hscb_busaddr = saved_hscb_busaddr;
 q_hscb->next_hscb_busaddr = scb->hscb->hscb_busaddr;

 /* Now swap HSCB pointers. */
 ahd->next_queued_hscb = scb->hscb;
 ahd->next_queued_hscb_map = scb->hscb_map;
 scb->hscb = q_hscb;
 scb->hscb_map = q_hscb_map;

 /* Now define the mapping from tag to SCB in the scbindex */
 ahd->scb_data.scbindex[SCB_GET_TAG(scb)] = scb;
}

/*
 * Tell the sequencer about a new transaction to execute.
 */

void
ahd_queue_scb(struct ahd_softc *ahd, struct scb *scb)
{
 ahd_swap_with_next_hscb(ahd, scb);

 if (SCBID_IS_NULL(SCB_GET_TAG(scb)))
  panic("Attempt to queue invalid SCB tag %x\n",
        SCB_GET_TAG(scb));

 /*
 * Keep a history of SCBs we've downloaded in the qinfifo.
 */

 ahd->qinfifo[AHD_QIN_WRAP(ahd->qinfifonext)] = SCB_GET_TAG(scb);
 ahd->qinfifonext++;

 if (scb->sg_count != 0)
  ahd_setup_data_scb(ahd, scb);
 else
  ahd_setup_noxfer_scb(ahd, scb);
 ahd_setup_scb_common(ahd, scb);

 /*
 * Make sure our data is consistent from the
 * perspective of the adapter.
 */

 ahd_sync_scb(ahd, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);

#ifdef AHD_DEBUG
 if ((ahd_debug & AHD_SHOW_QUEUE) != 0) {
  uint64_t host_dataptr;

  host_dataptr = ahd_le64toh(scb->hscb->dataptr);
  printk("%s: Queueing SCB %d:0x%x bus addr 0x%x - 0x%x%x/0x%x\n",
         ahd_name(ahd),
         SCB_GET_TAG(scb), scb->hscb->scsiid,
         ahd_le32toh(scb->hscb->hscb_busaddr),
         (u_int)((host_dataptr >> 32) & 0xFFFFFFFF),
         (u_int)(host_dataptr & 0xFFFFFFFF),
         ahd_le32toh(scb->hscb->datacnt));
 }
#endif
 /* Tell the adapter about the newly queued SCB */
 ahd_set_hnscb_qoff(ahd, ahd->qinfifonext);
}

/************************** Interrupt Processing ******************************/
static void
ahd_sync_qoutfifo(struct ahd_softc *ahd, int op)
{
 ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
   /*offset*/0,
   /*len*/AHD_SCB_MAX * sizeof(struct ahd_completion), op);
}

static void
ahd_sync_tqinfifo(struct ahd_softc *ahd, int op)
{
#ifdef AHD_TARGET_MODE
 if ((ahd->flags & AHD_TARGETROLE) != 0) {
  ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
    ahd->shared_data_map.dmamap,
    ahd_targetcmd_offset(ahd, 0),
    sizeof(struct target_cmd) * AHD_TMODE_CMDS,
    op);
 }
#endif
}

/*
 * See if the firmware has posted any completed commands
 * into our in-core command complete fifos.
 */

#define AHD_RUN_QOUTFIFO 0x1
#define AHD_RUN_TQINFIFO 0x2
static u_int
ahd_check_cmdcmpltqueues(struct ahd_softc *ahd)
{
 u_int retval;

 retval = 0;
 ahd_dmamap_sync(ahd, ahd->shared_data_dmat, ahd->shared_data_map.dmamap,
   /*offset*/ahd->qoutfifonext * sizeof(*ahd->qoutfifo),
   /*len*/sizeof(*ahd->qoutfifo), BUS_DMASYNC_POSTREAD);
 if (ahd->qoutfifo[ahd->qoutfifonext].valid_tag
   == ahd->qoutfifonext_valid_tag)
  retval |= AHD_RUN_QOUTFIFO;
#ifdef AHD_TARGET_MODE
 if ((ahd->flags & AHD_TARGETROLE) != 0
  && (ahd->flags & AHD_TQINFIFO_BLOCKED) == 0) {
  ahd_dmamap_sync(ahd, ahd->shared_data_dmat,
    ahd->shared_data_map.dmamap,
    ahd_targetcmd_offset(ahd, ahd->tqinfifofnext),
    /*len*/sizeof(struct target_cmd),
    BUS_DMASYNC_POSTREAD);
  if (ahd->targetcmds[ahd->tqinfifonext].cmd_valid != 0)
   retval |= AHD_RUN_TQINFIFO;
 }
#endif
 return (retval);
}

/*
 * Catch an interrupt from the adapter
 */

int
ahd_intr(struct ahd_softc *ahd)
{
 u_int intstat;

 if ((ahd->pause & INTEN) == 0) {
  /*
 * Our interrupt is not enabled on the chip
 * and may be disabled for re-entrancy reasons,
 * so just return.  This is likely just a shared
 * interrupt.
 */

  return (0);
 }

 /*
 * Instead of directly reading the interrupt status register,
 * infer the cause of the interrupt by checking our in-core
 * completion queues.  This avoids a costly PCI bus read in
 * most cases.
 */

 if ((ahd->flags & AHD_ALL_INTERRUPTS) == 0
  && (ahd_check_cmdcmpltqueues(ahd) != 0))
  intstat = CMDCMPLT;
 else
  intstat = ahd_inb(ahd, INTSTAT);

 if ((intstat & INT_PEND) == 0)
  return (0);

 if (intstat & CMDCMPLT) {
  ahd_outb(ahd, CLRINT, CLRCMDINT);

  /*
 * Ensure that the chip sees that we've cleared
 * this interrupt before we walk the output fifo.
 * Otherwise, we may, due to posted bus writes,
 * clear the interrupt after we finish the scan,
 * and after the sequencer has added new entries
 * and asserted the interrupt again.
 */

  if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
   if (ahd_is_paused(ahd)) {
    /*
 * Potentially lost SEQINT.
 * If SEQINTCODE is non-zero,
 * simulate the SEQINT.
 */

    if (ahd_inb(ahd, SEQINTCODE) != NO_SEQINT)
     intstat |= SEQINT;
   }
  } else {
   ahd_flush_device_writes(ahd);
  }
  ahd_run_qoutfifo(ahd);
  ahd->cmdcmplt_counts[ahd->cmdcmplt_bucket]++;
  ahd->cmdcmplt_total++;
#ifdef AHD_TARGET_MODE
  if ((ahd->flags & AHD_TARGETROLE) != 0)
   ahd_run_tqinfifo(ahd, /*paused*/FALSE);
#endif
 }

 /*
 * Handle statuses that may invalidate our cached
 * copy of INTSTAT separately.
 */

 if (intstat == 0xFF && (ahd->features & AHD_REMOVABLE) != 0) {
  /* Hot eject.  Do nothing */
 } else if (intstat & HWERRINT) {
  ahd_handle_hwerrint(ahd);
 } else if ((intstat & (PCIINT|SPLTINT)) != 0) {
  ahd->bus_intr(ahd);
 } else {

  if ((intstat & SEQINT) != 0)
   ahd_handle_seqint(ahd, intstat);

  if ((intstat & SCSIINT) != 0)
   ahd_handle_scsiint(ahd, intstat);
 }
 return (1);
}

/******************************** Private Inlines *****************************/
static inline void
ahd_assert_atn(struct ahd_softc *ahd)
{
 ahd_outb(ahd, SCSISIGO, ATNO);
}

/*
 * Determine if the current connection has a packetized
 * agreement.  This does not necessarily mean that we
 * are currently in a packetized transfer.  We could
 * just as easily be sending or receiving a message.
 */

static int
ahd_currently_packetized(struct ahd_softc *ahd)
{
 ahd_mode_state  saved_modes;
 int   packetized;

 saved_modes = ahd_save_modes(ahd);
 if ((ahd->bugs & AHD_PKTIZED_STATUS_BUG) != 0) {
  /*
 * The packetized bit refers to the last
 * connection, not the current one.  Check
 * for non-zero LQISTATE instead.
 */

  ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
  packetized = ahd_inb(ahd, LQISTATE) != 0;
 } else {
  ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
  packetized = ahd_inb(ahd, LQISTAT2) & PACKETIZED;
 }
 ahd_restore_modes(ahd, saved_modes);
 return (packetized);
}

static inline int
ahd_set_active_fifo(struct ahd_softc *ahd)
{
 u_int active_fifo;

 AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
 active_fifo = ahd_inb(ahd, DFFSTAT) & CURRFIFO;
 switch (active_fifo) {
 case 0:
 case 1:
  ahd_set_modes(ahd, active_fifo, active_fifo);
  return (1);
 default:
  return (0);
 }
}

static inline void
ahd_unbusy_tcl(struct ahd_softc *ahd, u_int tcl)
{
 ahd_busy_tcl(ahd, tcl, SCB_LIST_NULL);
}

/*
 * Determine whether the sequencer reported a residual
 * for this SCB/transaction.
 */

static inline void
ahd_update_residual(struct ahd_softc *ahd, struct scb *scb)
{
 uint32_t sgptr;

 sgptr = ahd_le32toh(scb->hscb->sgptr);
 if ((sgptr & SG_STATUS_VALID) != 0)
  ahd_calc_residual(ahd, scb);
}

static inline void
ahd_complete_scb(struct ahd_softc *ahd, struct scb *scb)
{
 uint32_t sgptr;

 sgptr = ahd_le32toh(scb->hscb->sgptr);
 if ((sgptr & SG_STATUS_VALID) != 0)
  ahd_handle_scb_status(ahd, scb);
 else
  ahd_done(ahd, scb);
}


/************************* Sequencer Execution Control ************************/
/*
 * Restart the sequencer program from address zero
 */

static void
ahd_restart(struct ahd_softc *ahd)
{

 ahd_pause(ahd);

 ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);

 /* No more pending messages */
 ahd_clear_msg_state(ahd);
 ahd_outb(ahd, SCSISIGO, 0);  /* De-assert BSY */
 ahd_outb(ahd, MSG_OUT, NOP); /* No message to send */
 ahd_outb(ahd, SXFRCTL1, ahd_inb(ahd, SXFRCTL1) & ~BITBUCKET);
 ahd_outb(ahd, SEQINTCTL, 0);
 ahd_outb(ahd, LASTPHASE, P_BUSFREE);
 ahd_outb(ahd, SEQ_FLAGS, 0);
 ahd_outb(ahd, SAVED_SCSIID, 0xFF);
 ahd_outb(ahd, SAVED_LUN, 0xFF);

 /*
 * Ensure that the sequencer's idea of TQINPOS
 * matches our own.  The sequencer increments TQINPOS
 * only after it sees a DMA complete and a reset could
 * occur before the increment leaving the kernel to believe
 * the command arrived but the sequencer to not.
 */

 ahd_outb(ahd, TQINPOS, ahd->tqinfifonext);

 /* Always allow reselection */
 ahd_outb(ahd, SCSISEQ1,
   ahd_inb(ahd, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
 ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);

 /*
 * Clear any pending sequencer interrupt.  It is no
 * longer relevant since we're resetting the Program
 * Counter.
 */

 ahd_outb(ahd, CLRINT, CLRSEQINT);

 ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
 ahd_unpause(ahd);
}

static void
ahd_clear_fifo(struct ahd_softc *ahd, u_int fifo)
{
 ahd_mode_state  saved_modes;

#ifdef AHD_DEBUG
 if ((ahd_debug & AHD_SHOW_FIFOS) != 0)
  printk("%s: Clearing FIFO %d\n", ahd_name(ahd), fifo);
#endif
 saved_modes = ahd_save_modes(ahd);
 ahd_set_modes(ahd, fifo, fifo);
 ahd_outb(ahd, DFFSXFRCTL, RSTCHN|CLRSHCNT);
 if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
  ahd_outb(ahd, CCSGCTL, CCSGRESET);
 ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
 ahd_outb(ahd, SG_STATE, 0);
 ahd_restore_modes(ahd, saved_modes);
}

/************************* Input/Output Queues ********************************/
/*
 * Flush and completed commands that are sitting in the command
 * complete queues down on the chip but have yet to be dma'ed back up.
 */

static void
ahd_flush_qoutfifo(struct ahd_softc *ahd)
{
 struct  scb *scb;
 ahd_mode_state saved_modes;
 u_int  saved_scbptr;
 u_int  ccscbctl;
 u_int  scbid;
 u_int  next_scbid;

 saved_modes = ahd_save_modes(ahd);

 /*
 * Flush the good status FIFO for completed packetized commands.
 */

 ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
 saved_scbptr = ahd_get_scbptr(ahd);
 while ((ahd_inb(ahd, LQISTAT2) & LQIGSAVAIL) != 0) {
  u_int fifo_mode;
  u_int i;

  scbid = ahd_inw(ahd, GSFIFO);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb == NULL) {
   printk("%s: Warning - GSFIFO SCB %d invalid\n",
          ahd_name(ahd), scbid);
   continue;
  }
  /*
 * Determine if this transaction is still active in
 * any FIFO.  If it is, we must flush that FIFO to
 * the host before completing the  command.
 */

  fifo_mode = 0;
rescan_fifos:
  for (i = 0; i < 2; i++) {
   /* Toggle to the other mode. */
   fifo_mode ^= 1;
   ahd_set_modes(ahd, fifo_mode, fifo_mode);

   if (ahd_scb_active_in_fifo(ahd, scb) == 0)
    continue;

   ahd_run_data_fifo(ahd, scb);

   /*
 * Running this FIFO may cause a CFG4DATA for
 * this same transaction to assert in the other
 * FIFO or a new snapshot SAVEPTRS interrupt
 * in this FIFO.  Even running a FIFO may not
 * clear the transaction if we are still waiting
 * for data to drain to the host. We must loop
 * until the transaction is not active in either
 * FIFO just to be sure.  Reset our loop counter
 * so we will visit both FIFOs again before
 * declaring this transaction finished.  We
 * also delay a bit so that status has a chance
 * to change before we look at this FIFO again.
 */

   ahd_delay(200);
   goto rescan_fifos;
  }
  ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
  ahd_set_scbptr(ahd, scbid);
  if ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_LIST_NULL) == 0
   && ((ahd_inb_scbram(ahd, SCB_SGPTR) & SG_FULL_RESID) != 0
    || (ahd_inb_scbram(ahd, SCB_RESIDUAL_SGPTR)
        & SG_LIST_NULL) != 0)) {
   u_int comp_head;

   /*
 * The transfer completed with a residual.
 * Place this SCB on the complete DMA list
 * so that we update our in-core copy of the
 * SCB before completing the command.
 */

   ahd_outb(ahd, SCB_SCSI_STATUS, 0);
   ahd_outb(ahd, SCB_SGPTR,
     ahd_inb_scbram(ahd, SCB_SGPTR)
     | SG_STATUS_VALID);
   ahd_outw(ahd, SCB_TAG, scbid);
   ahd_outw(ahd, SCB_NEXT_COMPLETE, SCB_LIST_NULL);
   comp_head = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
   if (SCBID_IS_NULL(comp_head)) {
    ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, scbid);
    ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, scbid);
   } else {
    u_int tail;

    tail = ahd_inw(ahd, COMPLETE_DMA_SCB_TAIL);
    ahd_set_scbptr(ahd, tail);
    ahd_outw(ahd, SCB_NEXT_COMPLETE, scbid);
    ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, scbid);
    ahd_set_scbptr(ahd, scbid);
   }
  } else
   ahd_complete_scb(ahd, scb);
 }
 ahd_set_scbptr(ahd, saved_scbptr);

 /*
 * Setup for command channel portion of flush.
 */

 ahd_set_modes(ahd, AHD_MODE_CCHAN, AHD_MODE_CCHAN);

 /*
 * Wait for any inprogress DMA to complete and clear DMA state
 * if this is for an SCB in the qinfifo.
 */

 while (((ccscbctl = ahd_inb(ahd, CCSCBCTL)) & (CCARREN|CCSCBEN)) != 0) {

  if ((ccscbctl & (CCSCBDIR|CCARREN)) == (CCSCBDIR|CCARREN)) {
   if ((ccscbctl & ARRDONE) != 0)
    break;
  } else if ((ccscbctl & CCSCBDONE) != 0)
   break;
  ahd_delay(200);
 }
 /*
 * We leave the sequencer to cleanup in the case of DMA's to
 * update the qoutfifo.  In all other cases (DMA's to the
 * chip or a push of an SCB from the COMPLETE_DMA_SCB list),
 * we disable the DMA engine so that the sequencer will not
 * attempt to handle the DMA completion.
 */

 if ((ccscbctl & CCSCBDIR) != 0 || (ccscbctl & ARRDONE) != 0)
  ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));

 /*
 * Complete any SCBs that just finished
 * being DMA'ed into the qoutfifo.
 */

 ahd_run_qoutfifo(ahd);

 saved_scbptr = ahd_get_scbptr(ahd);
 /*
 * Manually update/complete any completed SCBs that are waiting to be
 * DMA'ed back up to the host.
 */

 scbid = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
 while (!SCBID_IS_NULL(scbid)) {
  uint8_t *hscb_ptr;
  u_int  i;

  ahd_set_scbptr(ahd, scbid);
  next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb == NULL) {
   printk("%s: Warning - DMA-up and complete "
          "SCB %d invalid\n", ahd_name(ahd), scbid);
   continue;
  }
  hscb_ptr = (uint8_t *)scb->hscb;
  for (i = 0; i < sizeof(struct hardware_scb); i++)
   *hscb_ptr++ = ahd_inb_scbram(ahd, SCB_BASE + i);

  ahd_complete_scb(ahd, scb);
  scbid = next_scbid;
 }
 ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD, SCB_LIST_NULL);
 ahd_outw(ahd, COMPLETE_DMA_SCB_TAIL, SCB_LIST_NULL);

 scbid = ahd_inw(ahd, COMPLETE_ON_QFREEZE_HEAD);
 while (!SCBID_IS_NULL(scbid)) {

  ahd_set_scbptr(ahd, scbid);
  next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb == NULL) {
   printk("%s: Warning - Complete Qfrz SCB %d invalid\n",
          ahd_name(ahd), scbid);
   continue;
  }

  ahd_complete_scb(ahd, scb);
  scbid = next_scbid;
 }
 ahd_outw(ahd, COMPLETE_ON_QFREEZE_HEAD, SCB_LIST_NULL);

 scbid = ahd_inw(ahd, COMPLETE_SCB_HEAD);
 while (!SCBID_IS_NULL(scbid)) {

  ahd_set_scbptr(ahd, scbid);
  next_scbid = ahd_inw_scbram(ahd, SCB_NEXT_COMPLETE);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb == NULL) {
   printk("%s: Warning - Complete SCB %d invalid\n",
          ahd_name(ahd), scbid);
   continue;
  }

  ahd_complete_scb(ahd, scb);
  scbid = next_scbid;
 }
 ahd_outw(ahd, COMPLETE_SCB_HEAD, SCB_LIST_NULL);

 /*
 * Restore state.
 */

 ahd_set_scbptr(ahd, saved_scbptr);
 ahd_restore_modes(ahd, saved_modes);
 ahd->flags |= AHD_UPDATE_PEND_CMDS;
}

/*
 * Determine if an SCB for a packetized transaction
 * is active in a FIFO.
 */

static int
ahd_scb_active_in_fifo(struct ahd_softc *ahd, struct scb *scb)
{

 /*
 * The FIFO is only active for our transaction if
 * the SCBPTR matches the SCB's ID and the firmware
 * has installed a handler for the FIFO or we have
 * a pending SAVEPTRS or CFG4DATA interrupt.
 */

 if (ahd_get_scbptr(ahd) != SCB_GET_TAG(scb)
  || ((ahd_inb(ahd, LONGJMP_ADDR+1) & INVALID_ADDR) != 0
   && (ahd_inb(ahd, SEQINTSRC) & (CFG4DATA|SAVEPTRS)) == 0))
  return (0);

 return (1);
}

/*
 * Run a data fifo to completion for a transaction we know
 * has completed across the SCSI bus (good status has been
 * received).  We are already set to the correct FIFO mode
 * on entry to this routine.
 *
 * This function attempts to operate exactly as the firmware
 * would when running this FIFO.  Care must be taken to update
 * this routine any time the firmware's FIFO algorithm is
 * changed.
 */

static void
ahd_run_data_fifo(struct ahd_softc *ahd, struct scb *scb)
{
 u_int seqintsrc;

 seqintsrc = ahd_inb(ahd, SEQINTSRC);
 if ((seqintsrc & CFG4DATA) != 0) {
  uint32_t datacnt;
  uint32_t sgptr;

  /*
 * Clear full residual flag.
 */

  sgptr = ahd_inl_scbram(ahd, SCB_SGPTR) & ~SG_FULL_RESID;
  ahd_outb(ahd, SCB_SGPTR, sgptr);

  /*
 * Load datacnt and address.
 */

  datacnt = ahd_inl_scbram(ahd, SCB_DATACNT);
  if ((datacnt & AHD_DMA_LAST_SEG) != 0) {
   sgptr |= LAST_SEG;
   ahd_outb(ahd, SG_STATE, 0);
  } else
   ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
  ahd_outq(ahd, HADDR, ahd_inq_scbram(ahd, SCB_DATAPTR));
  ahd_outl(ahd, HCNT, datacnt & AHD_SG_LEN_MASK);
  ahd_outb(ahd, SG_CACHE_PRE, sgptr);
  ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);

  /*
 * Initialize Residual Fields.
 */

  ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, datacnt >> 24);
  ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr & SG_PTR_MASK);

  /*
 * Mark the SCB as having a FIFO in use.
 */

  ahd_outb(ahd, SCB_FIFO_USE_COUNT,
    ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) + 1);

  /*
 * Install a "fake" handler for this FIFO.
 */

  ahd_outw(ahd, LONGJMP_ADDR, 0);

  /*
 * Notify the hardware that we have satisfied
 * this sequencer interrupt.
 */

  ahd_outb(ahd, CLRSEQINTSRC, CLRCFG4DATA);
 } else if ((seqintsrc & SAVEPTRS) != 0) {
  uint32_t sgptr;
  uint32_t resid;

  if ((ahd_inb(ahd, LONGJMP_ADDR+1)&INVALID_ADDR) != 0) {
   /*
 * Snapshot Save Pointers.  All that
 * is necessary to clear the snapshot
 * is a CLRCHN.
 */

   goto clrchn;
  }

  /*
 * Disable S/G fetch so the DMA engine
 * is available to future users.
 */

  if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0)
   ahd_outb(ahd, CCSGCTL, 0);
  ahd_outb(ahd, SG_STATE, 0);

  /*
 * Flush the data FIFO.  Strickly only
 * necessary for Rev A parts.
 */

  ahd_outb(ahd, DFCNTRL, ahd_inb(ahd, DFCNTRL) | FIFOFLUSH);

  /*
 * Calculate residual.
 */

  sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
  resid = ahd_inl(ahd, SHCNT);
  resid |= ahd_inb_scbram(ahd, SCB_RESIDUAL_DATACNT+3) << 24;
  ahd_outl(ahd, SCB_RESIDUAL_DATACNT, resid);
  if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG) == 0) {
   /*
 * Must back up to the correct S/G element.
 * Typically this just means resetting our
 * low byte to the offset in the SG_CACHE,
 * but if we wrapped, we have to correct
 * the other bytes of the sgptr too.
 */

   if ((ahd_inb(ahd, SG_CACHE_SHADOW) & 0x80) != 0
    && (sgptr & 0x80) == 0)
    sgptr -= 0x100;
   sgptr &= ~0xFF;
   sgptr |= ahd_inb(ahd, SG_CACHE_SHADOW)
          & SG_ADDR_MASK;
   ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);
   ahd_outb(ahd, SCB_RESIDUAL_DATACNT + 3, 0);
  } else if ((resid & AHD_SG_LEN_MASK) == 0) {
   ahd_outb(ahd, SCB_RESIDUAL_SGPTR,
     sgptr | SG_LIST_NULL);
  }
  /*
 * Save Pointers.
 */

  ahd_outq(ahd, SCB_DATAPTR, ahd_inq(ahd, SHADDR));
  ahd_outl(ahd, SCB_DATACNT, resid);
  ahd_outl(ahd, SCB_SGPTR, sgptr);
  ahd_outb(ahd, CLRSEQINTSRC, CLRSAVEPTRS);
  ahd_outb(ahd, SEQIMODE,
    ahd_inb(ahd, SEQIMODE) | ENSAVEPTRS);
  /*
 * If the data is to the SCSI bus, we are
 * done, otherwise wait for FIFOEMP.
 */

  if ((ahd_inb(ahd, DFCNTRL) & DIRECTION) != 0)
   goto clrchn;
 } else if ((ahd_inb(ahd, SG_STATE) & LOADING_NEEDED) != 0) {
  uint32_t sgptr;
  uint64_t data_addr;
  uint32_t data_len;
  u_int  dfcntrl;

  /*
 * Disable S/G fetch so the DMA engine
 * is available to future users.  We won't
 * be using the DMA engine to load segments.
 */

  if ((ahd_inb(ahd, SG_STATE) & FETCH_INPROG) != 0) {
   ahd_outb(ahd, CCSGCTL, 0);
   ahd_outb(ahd, SG_STATE, LOADING_NEEDED);
  }

  /*
 * Wait for the DMA engine to notice that the
 * host transfer is enabled and that there is
 * space in the S/G FIFO for new segments before
 * loading more segments.
 */

  if ((ahd_inb(ahd, DFSTATUS) & PRELOAD_AVAIL) != 0
   && (ahd_inb(ahd, DFCNTRL) & HDMAENACK) != 0) {

   /*
 * Determine the offset of the next S/G
 * element to load.
 */

   sgptr = ahd_inl_scbram(ahd, SCB_RESIDUAL_SGPTR);
   sgptr &= SG_PTR_MASK;
   if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) {
    struct ahd_dma64_seg *sg;

    sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
    data_addr = sg->addr;
    data_len = sg->len;
    sgptr += sizeof(*sg);
   } else {
    struct ahd_dma_seg *sg;

    sg = ahd_sg_bus_to_virt(ahd, scb, sgptr);
    data_addr = sg->len & AHD_SG_HIGH_ADDR_MASK;
    data_addr <<= 8;
    data_addr |= sg->addr;
    data_len = sg->len;
    sgptr += sizeof(*sg);
   }

   /*
 * Update residual information.
 */

   ahd_outb(ahd, SCB_RESIDUAL_DATACNT+3, data_len >> 24);
   ahd_outl(ahd, SCB_RESIDUAL_SGPTR, sgptr);

   /*
 * Load the S/G.
 */

   if (data_len & AHD_DMA_LAST_SEG) {
    sgptr |= LAST_SEG;
    ahd_outb(ahd, SG_STATE, 0);
   }
   ahd_outq(ahd, HADDR, data_addr);
   ahd_outl(ahd, HCNT, data_len & AHD_SG_LEN_MASK);
   ahd_outb(ahd, SG_CACHE_PRE, sgptr & 0xFF);

   /*
 * Advertise the segment to the hardware.
 */

   dfcntrl = ahd_inb(ahd, DFCNTRL)|PRELOADEN|HDMAEN;
   if ((ahd->features & AHD_NEW_DFCNTRL_OPTS) != 0) {
    /*
 * Use SCSIENWRDIS so that SCSIEN
 * is never modified by this
 * operation.
 */

    dfcntrl |= SCSIENWRDIS;
   }
   ahd_outb(ahd, DFCNTRL, dfcntrl);
  }
 } else if ((ahd_inb(ahd, SG_CACHE_SHADOW) & LAST_SEG_DONE) != 0) {

  /*
 * Transfer completed to the end of SG list
 * and has flushed to the host.
 */

  ahd_outb(ahd, SCB_SGPTR,
    ahd_inb_scbram(ahd, SCB_SGPTR) | SG_LIST_NULL);
  goto clrchn;
 } else if ((ahd_inb(ahd, DFSTATUS) & FIFOEMP) != 0) {
clrchn:
  /*
 * Clear any handler for this FIFO, decrement
 * the FIFO use count for the SCB, and release
 * the FIFO.
 */

  ahd_outb(ahd, LONGJMP_ADDR + 1, INVALID_ADDR);
  ahd_outb(ahd, SCB_FIFO_USE_COUNT,
    ahd_inb_scbram(ahd, SCB_FIFO_USE_COUNT) - 1);
  ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
 }
}

/*
 * Look for entries in the QoutFIFO that have completed.
 * The valid_tag completion field indicates the validity
 * of the entry - the valid value toggles each time through
 * the queue. We use the sg_status field in the completion
 * entry to avoid referencing the hscb if the completion
 * occurred with no errors and no residual.  sg_status is
 * a copy of the first byte (little endian) of the sgptr
 * hscb field.
 */

static void
ahd_run_qoutfifo(struct ahd_softc *ahd)
{
 struct ahd_completion *completion;
 struct scb *scb;
 u_int  scb_index;

 if ((ahd->flags & AHD_RUNNING_QOUTFIFO) != 0)
  panic("ahd_run_qoutfifo recursion");
 ahd->flags |= AHD_RUNNING_QOUTFIFO;
 ahd_sync_qoutfifo(ahd, BUS_DMASYNC_POSTREAD);
 for (;;) {
  completion = &ahd->qoutfifo[ahd->qoutfifonext];

  if (completion->valid_tag != ahd->qoutfifonext_valid_tag)
   break;

  scb_index = ahd_le16toh(completion->tag);
  scb = ahd_lookup_scb(ahd, scb_index);
  if (scb == NULL) {
   printk("%s: WARNING no command for scb %d "
          "(cmdcmplt)\nQOUTPOS = %d\n",
          ahd_name(ahd), scb_index,
          ahd->qoutfifonext);
   ahd_dump_card_state(ahd);
  } else if ((completion->sg_status & SG_STATUS_VALID) != 0) {
   ahd_handle_scb_status(ahd, scb);
  } else {
   ahd_done(ahd, scb);
  }

  ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1);
  if (ahd->qoutfifonext == 0)
   ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID;
 }
 ahd->flags &= ~AHD_RUNNING_QOUTFIFO;
}

/************************* Interrupt Handling *********************************/
static void
ahd_handle_hwerrint(struct ahd_softc *ahd)
{
 /*
 * Some catastrophic hardware error has occurred.
 * Print it for the user and disable the controller.
 */

 int i;
 int error;

 error = ahd_inb(ahd, ERROR);
 for (i = 0; i < num_errors; i++) {
  if ((error & ahd_hard_errors[i].errno) != 0)
   printk("%s: hwerrint, %s\n",
          ahd_name(ahd), ahd_hard_errors[i].errmesg);
 }

 ahd_dump_card_state(ahd);
 panic("BRKADRINT");

 /* Tell everyone that this HBA is no longer available */
 ahd_abort_scbs(ahd, CAM_TARGET_WILDCARD, ALL_CHANNELS,
         CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
         CAM_NO_HBA);

 /* Tell the system that this controller has gone away. */
 ahd_free(ahd);
}

#ifdef AHD_DEBUG
static void
ahd_dump_sglist(struct scb *scb)
{
 int i;

 if (scb->sg_count > 0) {
  if ((scb->ahd_softc->flags & AHD_64BIT_ADDRESSING) != 0) {
   struct ahd_dma64_seg *sg_list;

   sg_list = (struct ahd_dma64_seg*)scb->sg_list;
   for (i = 0; i < scb->sg_count; i++) {
    uint64_t addr;

    addr = ahd_le64toh(sg_list[i].addr);
    printk("sg[%d] - Addr 0x%x%x : Length %d%s\n",
           i,
           (uint32_t)((addr >> 32) & 0xFFFFFFFF),
           (uint32_t)(addr & 0xFFFFFFFF),
           sg_list[i].len & AHD_SG_LEN_MASK,
           (sg_list[i].len & AHD_DMA_LAST_SEG)
         ? " Last" : "");
   }
  } else {
   struct ahd_dma_seg *sg_list;

   sg_list = (struct ahd_dma_seg*)scb->sg_list;
   for (i = 0; i < scb->sg_count; i++) {
    uint32_t len;

    len = ahd_le32toh(sg_list[i].len);
    printk("sg[%d] - Addr 0x%x%x : Length %d%s\n",
           i,
           (len & AHD_SG_HIGH_ADDR_MASK) >> 24,
           ahd_le32toh(sg_list[i].addr),
           len & AHD_SG_LEN_MASK,
           len & AHD_DMA_LAST_SEG ? " Last" : "");
   }
  }
 }
}
#endif  /*  AHD_DEBUG  */

static void
ahd_handle_seqint(struct ahd_softc *ahd, u_int intstat)
{
 u_int seqintcode;

 /*
 * Save the sequencer interrupt code and clear the SEQINT
 * bit. We will unpause the sequencer, if appropriate,
 * after servicing the request.
 */

 seqintcode = ahd_inb(ahd, SEQINTCODE);
 ahd_outb(ahd, CLRINT, CLRSEQINT);
 if ((ahd->bugs & AHD_INTCOLLISION_BUG) != 0) {
  /*
 * Unpause the sequencer and let it clear
 * SEQINT by writing NO_SEQINT to it.  This
 * will cause the sequencer to be paused again,
 * which is the expected state of this routine.
 */

  ahd_unpause(ahd);
  while (!ahd_is_paused(ahd))
   ;
  ahd_outb(ahd, CLRINT, CLRSEQINT);
 }
 ahd_update_modes(ahd);
#ifdef AHD_DEBUG
 if ((ahd_debug & AHD_SHOW_MISC) != 0)
  printk("%s: Handle Seqint Called for code %d\n",
         ahd_name(ahd), seqintcode);
#endif
 switch (seqintcode) {
 case ENTERING_NONPACK:
 {
  struct scb *scb;
  u_int scbid;

  AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
     ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
  scbid = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb == NULL) {
   /*
 * Somehow need to know if this
 * is from a selection or reselection.
 * From that, we can determine target
 * ID so we at least have an I_T nexus.
 */

  } else {
   ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
   ahd_outb(ahd, SAVED_LUN, scb->hscb->lun);
   ahd_outb(ahd, SEQ_FLAGS, 0x0);
  }
  if ((ahd_inb(ahd, LQISTAT2) & LQIPHASE_OUTPKT) != 0
   && (ahd_inb(ahd, SCSISIGO) & ATNO) != 0) {
   /*
 * Phase change after read stream with
 * CRC error with P0 asserted on last
 * packet.
 */

#ifdef AHD_DEBUG
   if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
    printk("%s: Assuming LQIPHASE_NLQ with "
           "P0 assertion\n", ahd_name(ahd));
#endif
  }
#ifdef AHD_DEBUG
  if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
   printk("%s: Entering NONPACK\n", ahd_name(ahd));
#endif
  break;
 }
 case INVALID_SEQINT:
  printk("%s: Invalid Sequencer interrupt occurred, "
         "resetting channel.\n",
         ahd_name(ahd));
#ifdef AHD_DEBUG
  if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
   ahd_dump_card_state(ahd);
#endif
  ahd_reset_channel(ahd, 'A'/*Initiate Reset*/TRUE);
  break;
 case STATUS_OVERRUN:
 {
  struct scb *scb;
  u_int scbid;

  scbid = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb != NULL)
   ahd_print_path(ahd, scb);
  else
   printk("%s: ", ahd_name(ahd));
  printk("SCB %d Packetized Status Overrun", scbid);
  ahd_dump_card_state(ahd);
  ahd_reset_channel(ahd, 'A'/*Initiate Reset*/TRUE);
  break;
 }
 case CFG4ISTAT_INTR:
 {
  struct scb *scb;
  u_int scbid;

  scbid = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb == NULL) {
   ahd_dump_card_state(ahd);
   printk("CFG4ISTAT: Free SCB %d referenced", scbid);
   panic("For safety");
  }
  ahd_outq(ahd, HADDR, scb->sense_busaddr);
  ahd_outw(ahd, HCNT, AHD_SENSE_BUFSIZE);
  ahd_outb(ahd, HCNT + 2, 0);
  ahd_outb(ahd, SG_CACHE_PRE, SG_LAST_SEG);
  ahd_outb(ahd, DFCNTRL, PRELOADEN|SCSIEN|HDMAEN);
  break;
 }
 case ILLEGAL_PHASE:
 {
  u_int bus_phase;

  bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
  printk("%s: ILLEGAL_PHASE 0x%x\n",
         ahd_name(ahd), bus_phase);

  switch (bus_phase) {
  case P_DATAOUT:
  case P_DATAIN:
  case P_DATAOUT_DT:
  case P_DATAIN_DT:
  case P_MESGOUT:
  case P_STATUS:
  case P_MESGIN:
   ahd_reset_channel(ahd, 'A'/*Initiate Reset*/TRUE);
   printk("%s: Issued Bus Reset.\n", ahd_name(ahd));
   break;
  case P_COMMAND:
  {
   struct ahd_devinfo devinfo;
   struct scb *scb;
   u_int scbid;

   /*
 * If a target takes us into the command phase
 * assume that it has been externally reset and
 * has thus lost our previous packetized negotiation
 * agreement.  Since we have not sent an identify
 * message and may not have fully qualified the
 * connection, we change our command to TUR, assert
 * ATN and ABORT the task when we go to message in
 * phase.  The OSM will see the REQUEUE_REQUEST
 * status and retry the command.
 */

   scbid = ahd_get_scbptr(ahd);
   scb = ahd_lookup_scb(ahd, scbid);
   if (scb == NULL) {
    printk("Invalid phase with no valid SCB. "
           "Resetting bus.\n");
    ahd_reset_channel(ahd, 'A',
        /*Initiate Reset*/TRUE);
    break;
   }
   ahd_compile_devinfo(&devinfo, SCB_GET_OUR_ID(scb),
         SCB_GET_TARGET(ahd, scb),
         SCB_GET_LUN(scb),
         SCB_GET_CHANNEL(ahd, scb),
         ROLE_INITIATOR);
   ahd_set_width(ahd, &devinfo, MSG_EXT_WDTR_BUS_8_BIT,
          AHD_TRANS_ACTIVE, /*paused*/TRUE);
   ahd_set_syncrate(ahd, &devinfo, /*period*/0,
      /*offset*/0, /*ppr_options*/0,
      AHD_TRANS_ACTIVE, /*paused*/TRUE);
   /* Hand-craft TUR command */
   ahd_outb(ahd, SCB_CDB_STORE, 0);
   ahd_outb(ahd, SCB_CDB_STORE+1, 0);
   ahd_outb(ahd, SCB_CDB_STORE+2, 0);
   ahd_outb(ahd, SCB_CDB_STORE+3, 0);
   ahd_outb(ahd, SCB_CDB_STORE+4, 0);
   ahd_outb(ahd, SCB_CDB_STORE+5, 0);
   ahd_outb(ahd, SCB_CDB_LEN, 6);
   scb->hscb->control &= ~(TAG_ENB|SCB_TAG_TYPE);
   scb->hscb->control |= MK_MESSAGE;
   ahd_outb(ahd, SCB_CONTROL, scb->hscb->control);
   ahd_outb(ahd, MSG_OUT, HOST_MSG);
   ahd_outb(ahd, SAVED_SCSIID, scb->hscb->scsiid);
   /*
 * The lun is 0, regardless of the SCB's lun
 * as we have not sent an identify message.
 */

   ahd_outb(ahd, SAVED_LUN, 0);
   ahd_outb(ahd, SEQ_FLAGS, 0);
   ahd_assert_atn(ahd);
   scb->flags &= ~SCB_PACKETIZED;
   scb->flags |= SCB_ABORT|SCB_EXTERNAL_RESET;
   ahd_freeze_devq(ahd, scb);
   ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
   ahd_freeze_scb(scb);

   /* Notify XPT */
   ahd_send_async(ahd, devinfo.channel, devinfo.target,
           CAM_LUN_WILDCARD, AC_SENT_BDR);

   /*
 * Allow the sequencer to continue with
 * non-pack processing.
 */

   ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
   ahd_outb(ahd, CLRLQOINT1, CLRLQOPHACHGINPKT);
   if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
    ahd_outb(ahd, CLRLQOINT1, 0);
   }
#ifdef AHD_DEBUG
   if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
    ahd_print_path(ahd, scb);
    printk("Unexpected command phase from "
           "packetized target\n");
   }
#endif
   break;
  }
  }
  break;
 }
 case CFG4OVERRUN:
 {
  struct scb *scb;
  u_int scb_index;

#ifdef AHD_DEBUG
  if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
   printk("%s: CFG4OVERRUN mode = %x\n", ahd_name(ahd),
          ahd_inb(ahd, MODE_PTR));
  }
#endif
  scb_index = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scb_index);
  if (scb == NULL) {
   /*
 * Attempt to transfer to an SCB that is
 * not outstanding.
 */

   ahd_assert_atn(ahd);
   ahd_outb(ahd, MSG_OUT, HOST_MSG);
   ahd->msgout_buf[0] = ABORT_TASK;
   ahd->msgout_len = 1;
   ahd->msgout_index = 0;
   ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
   /*
 * Clear status received flag to prevent any
 * attempt to complete this bogus SCB.
 */

   ahd_outb(ahd, SCB_CONTROL,
     ahd_inb_scbram(ahd, SCB_CONTROL)
     & ~STATUS_RCVD);
  }
  break;
 }
 case DUMP_CARD_STATE:
 {
  ahd_dump_card_state(ahd);
  break;
 }
 case PDATA_REINIT:
 {
#ifdef AHD_DEBUG
  if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
   printk("%s: PDATA_REINIT - DFCNTRL = 0x%x "
          "SG_CACHE_SHADOW = 0x%x\n",
          ahd_name(ahd), ahd_inb(ahd, DFCNTRL),
          ahd_inb(ahd, SG_CACHE_SHADOW));
  }
#endif
  ahd_reinitialize_dataptrs(ahd);
  break;
 }
 case HOST_MSG_LOOP:
 {
  struct ahd_devinfo devinfo;

  /*
 * 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
 * transferred so we can track bus phase changes.
 *
 * If this is the first time we've seen a HOST_MSG_LOOP
 * interrupt, initialize the state of the host message
 * loop.
 */

  ahd_fetch_devinfo(ahd, &devinfo);
  if (ahd->msg_type == MSG_TYPE_NONE) {
   struct scb *scb;
   u_int scb_index;
   u_int bus_phase;

   bus_phase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
   if (bus_phase != P_MESGIN
    && bus_phase != P_MESGOUT) {
    printk("ahd_intr: HOST_MSG_LOOP bad "
           "phase 0x%x\n", bus_phase);
    /*
 * Probably transitioned to bus free before
 * we got here.  Just punt the message.
 */

    ahd_dump_card_state(ahd);
    ahd_clear_intstat(ahd);
    ahd_restart(ahd);
    return;
   }

   scb_index = ahd_get_scbptr(ahd);
   scb = ahd_lookup_scb(ahd, scb_index);
   if (devinfo.role == ROLE_INITIATOR) {
    if (bus_phase == P_MESGOUT)
     ahd_setup_initiator_msgout(ahd,
           &devinfo,
           scb);
    else {
     ahd->msg_type =
         MSG_TYPE_INITIATOR_MSGIN;
     ahd->msgin_index = 0;
    }
   }
#ifdef AHD_TARGET_MODE
   else {
    if (bus_phase == P_MESGOUT) {
     ahd->msg_type =
         MSG_TYPE_TARGET_MSGOUT;
     ahd->msgin_index = 0;
    } else
     ahd_setup_target_msgin(ahd,
              &devinfo,
              scb);
   }
#endif
  }

  ahd_handle_message_phase(ahd);
  break;
 }
 case NO_MATCH:
 {
  /* Ensure we don't leave the selection hardware on */
  AHD_ASSERT_MODES(ahd, AHD_MODE_SCSI_MSK, AHD_MODE_SCSI_MSK);
  ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);

  printk("%s:%c:%d: no active SCB for reconnecting "
         "target - issuing BUS DEVICE RESET\n",
         ahd_name(ahd), 'A', ahd_inb(ahd, SELID) >> 4);
  printk("SAVED_SCSIID == 0x%x, SAVED_LUN == 0x%x, "
         "REG0 == 0x%x ACCUM = 0x%x\n",
         ahd_inb(ahd, SAVED_SCSIID), ahd_inb(ahd, SAVED_LUN),
         ahd_inw(ahd, REG0), ahd_inb(ahd, ACCUM));
  printk("SEQ_FLAGS == 0x%x, SCBPTR == 0x%x, BTT == 0x%x, "
         "SINDEX == 0x%x\n",
         ahd_inb(ahd, SEQ_FLAGS), ahd_get_scbptr(ahd),
         ahd_find_busy_tcl(ahd,
      BUILD_TCL(ahd_inb(ahd, SAVED_SCSIID),
         ahd_inb(ahd, SAVED_LUN))),
         ahd_inw(ahd, SINDEX));
  printk("SELID == 0x%x, SCB_SCSIID == 0x%x, SCB_LUN == 0x%x, "
         "SCB_CONTROL == 0x%x\n",
         ahd_inb(ahd, SELID), ahd_inb_scbram(ahd, SCB_SCSIID),
         ahd_inb_scbram(ahd, SCB_LUN),
         ahd_inb_scbram(ahd, SCB_CONTROL));
  printk("SCSIBUS[0] == 0x%x, SCSISIGI == 0x%x\n",
         ahd_inb(ahd, SCSIBUS), ahd_inb(ahd, SCSISIGI));
  printk("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
  printk("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
  ahd_dump_card_state(ahd);
  ahd->msgout_buf[0] = TARGET_RESET;
  ahd->msgout_len = 1;
  ahd->msgout_index = 0;
  ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
  ahd_outb(ahd, MSG_OUT, HOST_MSG);
  ahd_assert_atn(ahd);
  break;
 }
 case PROTO_VIOLATION:
 {
  ahd_handle_proto_violation(ahd);
  break;
 }
 case IGN_WIDE_RES:
 {
  struct ahd_devinfo devinfo;

  ahd_fetch_devinfo(ahd, &devinfo);
  ahd_handle_ign_wide_residue(ahd, &devinfo);
  break;
 }
 case BAD_PHASE:
 {
  u_int lastphase;

  lastphase = ahd_inb(ahd, LASTPHASE);
  printk("%s:%c:%d: unknown scsi bus phase %x, "
         "lastphase = 0x%x. Attempting to continue\n",
         ahd_name(ahd), 'A',
         SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
         lastphase, ahd_inb(ahd, SCSISIGI));
  break;
 }
 case MISSED_BUSFREE:
 {
  u_int lastphase;

  lastphase = ahd_inb(ahd, LASTPHASE);
  printk("%s:%c:%d: Missed busfree. "
         "Lastphase = 0x%x, Curphase = 0x%x\n",
         ahd_name(ahd), 'A',
         SCSIID_TARGET(ahd, ahd_inb(ahd, SAVED_SCSIID)),
         lastphase, ahd_inb(ahd, SCSISIGI));
  ahd_restart(ahd);
  return;
 }
 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.
 */

  struct scb *scb;
  u_int scbindex;
#ifdef AHD_DEBUG
  u_int lastphase;
#endif

  scbindex = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scbindex);
#ifdef AHD_DEBUG
  lastphase = ahd_inb(ahd, LASTPHASE);
  if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
   ahd_print_path(ahd, scb);
   printk("data overrun detected %s. Tag == 0x%x.\n",
          ahd_lookup_phase_entry(lastphase)->phasemsg,
          SCB_GET_TAG(scb));
   ahd_print_path(ahd, scb);
   printk("%s seen Data Phase. Length = %ld. "
          "NumSGs = %d.\n",
          ahd_inb(ahd, SEQ_FLAGS) & DPHASE
          ? "Have" : "Haven't",
          ahd_get_transfer_length(scb), scb->sg_count);
   ahd_dump_sglist(scb);
  }
#endif

  /*
 * Set this and it will take effect when the
 * target does a command complete.
 */

  ahd_freeze_devq(ahd, scb);
  ahd_set_transaction_status(scb, CAM_DATA_RUN_ERR);
  ahd_freeze_scb(scb);
  break;
 }
 case MKMSG_FAILED:
 {
  struct ahd_devinfo devinfo;
  struct scb *scb;
  u_int scbid;

  ahd_fetch_devinfo(ahd, &devinfo);
  printk("%s:%c:%d:%d: Attempt to issue message failed\n",
         ahd_name(ahd), devinfo.channel, devinfo.target,
         devinfo.lun);
  scbid = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb != NULL
   && (scb->flags & SCB_RECOVERY_SCB) != 0)
   /*
 * Ensure that we didn't put a second instance of this
 * SCB into the QINFIFO.
 */

   ahd_search_qinfifo(ahd, SCB_GET_TARGET(ahd, scb),
        SCB_GET_CHANNEL(ahd, scb),
        SCB_GET_LUN(scb), SCB_GET_TAG(scb),
        ROLE_INITIATOR, /*status*/0,
        SEARCH_REMOVE);
  ahd_outb(ahd, SCB_CONTROL,
    ahd_inb_scbram(ahd, SCB_CONTROL) & ~MK_MESSAGE);
  break;
 }
 case TASKMGMT_FUNC_COMPLETE:
 {
  u_int scbid;
  struct scb *scb;

  scbid = ahd_get_scbptr(ahd);
  scb = ahd_lookup_scb(ahd, scbid);
  if (scb != NULL) {
   u_int    lun;
   u_int    tag;
   cam_status error;

   ahd_print_path(ahd, scb);
   printk("Task Management Func 0x%x Complete\n",
          scb->hscb->task_management);
   lun = CAM_LUN_WILDCARD;
   tag = SCB_LIST_NULL;

   switch (scb->hscb->task_management) {
   case SIU_TASKMGMT_ABORT_TASK:
    tag = SCB_GET_TAG(scb);
    fallthrough;
   case SIU_TASKMGMT_ABORT_TASK_SET:
   case SIU_TASKMGMT_CLEAR_TASK_SET:
    lun = scb->hscb->lun;
    error = CAM_REQ_ABORTED;
    ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
            'A', lun, tag, ROLE_INITIATOR,
            error);
    break;
   case SIU_TASKMGMT_LUN_RESET:
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=95 H=93 G=93

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.20Bemerkung:  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.