blob: 137fb1a37dd172c3f64bade86779689fdf3639fc [file] [log] [blame]
/*
* 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#202 $
*
* $FreeBSD$
*/
#ifdef __linux__
#include "aic79xx_osm.h"
#include "aic79xx_inline.h"
#include "aicasm/aicasm_insformat.h"
#else
#include <dev/aic7xxx/aic79xx_osm.h>
#include <dev/aic7xxx/aic79xx_inline.h>
#include <dev/aic7xxx/aicasm/aicasm_insformat.h>
#endif
/******************************** Globals *************************************/
struct ahd_softc_tailq ahd_tailq = TAILQ_HEAD_INITIALIZER(ahd_tailq);
/***************************** Lookup Tables **********************************/
char *ahd_chip_names[] =
{
"NONE",
"aic7901",
"aic7902",
"aic7901A"
};
static const u_int num_chip_names = NUM_ELEMENTS(ahd_chip_names);
/*
* Hardware error codes.
*/
struct ahd_hard_error_entry {
uint8_t errno;
char *errmesg;
};
static 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 = NUM_ELEMENTS(ahd_hard_errors);
static struct ahd_phase_table_entry ahd_phase_table[] =
{
{ P_DATAOUT, MSG_NOOP, "in Data-out phase" },
{ P_DATAIN, MSG_INITIATOR_DET_ERR, "in Data-in phase" },
{ P_DATAOUT_DT, MSG_NOOP, "in DT Data-out phase" },
{ P_DATAIN_DT, MSG_INITIATOR_DET_ERR, "in DT Data-in phase" },
{ P_COMMAND, MSG_NOOP, "in Command phase" },
{ P_MESGOUT, MSG_NOOP, "in Message-out phase" },
{ P_STATUS, MSG_INITIATOR_DET_ERR, "in Status phase" },
{ P_MESGIN, MSG_PARITY_ERROR, "in Message-in phase" },
{ P_BUSFREE, MSG_NOOP, "while idle" },
{ 0, MSG_NOOP, "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 = NUM_ELEMENTS(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 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 ahd_callback_t ahd_reset_poll;
static ahd_callback_t ahd_stat_timer;
#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,
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
/******************************** Private Inlines *****************************/
static __inline void ahd_assert_atn(struct ahd_softc *ahd);
static __inline int ahd_currently_packetized(struct ahd_softc *ahd);
static __inline int ahd_set_active_fifo(struct ahd_softc *ahd);
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 __inline 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);
}
}
/************************* Sequencer Execution Control ************************/
/*
* Restart the sequencer program from address zero
*/
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, MSG_NOOP); /* 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);
ahd_outb(ahd, SEQCTL0, FASTMODE|SEQRESET);
ahd_unpause(ahd);
}
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)
printf("%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.
*/
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);
/*
* Complete any SCBs that just finished being
* DMA'ed into the qoutfifo.
*/
ahd_run_qoutfifo(ahd);
/*
* Flush the good status FIFO for compelted 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_inb(ahd, GSFIFO+1) << 8)
| ahd_inb(ahd, GSFIFO);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL) {
printf("%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;
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);
/*
* Clearing this transaction in this FIFO may
* cause a CFG4DATA for this same transaction
* to assert in the other FIFO. Make sure we
* loop one more time and check the other FIFO.
*/
i = 0;
}
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, SCB_GET_TAG(scb));
comp_head = ahd_inw(ahd, COMPLETE_DMA_SCB_HEAD);
ahd_outw(ahd, SCB_NEXT_COMPLETE, comp_head);
if (SCBID_IS_NULL(comp_head))
ahd_outw(ahd, COMPLETE_DMA_SCB_HEAD,
SCB_GET_TAG(scb));
} 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 if 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);
}
if ((ccscbctl & CCSCBDIR) != 0)
ahd_outb(ahd, CCSCBCTL, ccscbctl & ~(CCARREN|CCSCBEN));
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) {
printf("%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);
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) {
printf("%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;
while (1) {
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. Clear
* the snapshot and continue.
*/
ahd_outb(ahd, DFFSXFRCTL, CLRCHN);
continue;
}
/*
* 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)
break;
} 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.
*/
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)
continue;
if ((ahd_inb(ahd, DFCNTRL) & HDMAENACK) == 0)
continue;
/*
* 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);
break;
} else if ((ahd_inb(ahd, DFSTATUS) & FIFOEMP) != 0) {
break;
}
ahd_delay(200);
}
/*
* 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);
}
void
ahd_run_qoutfifo(struct ahd_softc *ahd)
{
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);
while ((ahd->qoutfifo[ahd->qoutfifonext]
& QOUTFIFO_ENTRY_VALID_LE) == ahd->qoutfifonext_valid_tag) {
scb_index = ahd_le16toh(ahd->qoutfifo[ahd->qoutfifonext]
& ~QOUTFIFO_ENTRY_VALID_LE);
scb = ahd_lookup_scb(ahd, scb_index);
if (scb == NULL) {
printf("%s: WARNING no command for scb %d "
"(cmdcmplt)\nQOUTPOS = %d\n",
ahd_name(ahd), scb_index,
ahd->qoutfifonext);
ahd_dump_card_state(ahd);
} else
ahd_complete_scb(ahd, scb);
ahd->qoutfifonext = (ahd->qoutfifonext+1) & (AHD_QOUT_SIZE-1);
if (ahd->qoutfifonext == 0)
ahd->qoutfifonext_valid_tag ^= QOUTFIFO_ENTRY_VALID_LE;
}
ahd->flags &= ~AHD_RUNNING_QOUTFIFO;
}
/************************* Interrupt Handling *********************************/
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)
printf("%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);
}
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)
printf("%s: Handle Seqint Called for code %d\n",
ahd_name(ahd), seqintcode);
#endif
switch (seqintcode) {
case BAD_SCB_STATUS:
{
struct scb *scb;
u_int scbid;
int cmds_pending;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL) {
ahd_complete_scb(ahd, scb);
} else {
printf("%s: WARNING no command for scb %d "
"(bad status)\n", ahd_name(ahd), scbid);
ahd_dump_card_state(ahd);
}
cmds_pending = ahd_inw(ahd, CMDS_PENDING);
if (cmds_pending > 0)
ahd_outw(ahd, CMDS_PENDING, cmds_pending - 1);
break;
}
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)
printf("%s: Assuming LQIPHASE_NLQ with "
"P0 assertion\n", ahd_name(ahd));
#endif
}
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_RECOVERY) != 0)
printf("%s: Entering NONPACK\n", ahd_name(ahd));
#endif
break;
}
case INVALID_SEQINT:
printf("%s: Invalid Sequencer interrupt occurred.\n",
ahd_name(ahd));
ahd_dump_card_state(ahd);
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
printf("%s: ", ahd_name(ahd));
printf("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);
printf("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;
printf("%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);
printf("%s: Issued Bus Reset.\n", ahd_name(ahd));
break;
case P_COMMAND:
{
struct ahd_devinfo devinfo;
struct scb *scb;
struct ahd_initiator_tinfo *targ_info;
struct ahd_tmode_tstate *tstate;
struct ahd_transinfo *tinfo;
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) {
printf("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);
targ_info = ahd_fetch_transinfo(ahd,
devinfo.channel,
devinfo.our_scsiid,
devinfo.target,
&tstate);
tinfo = &targ_info->curr;
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);
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_CMDPHASE_ABORT;
ahd_freeze_devq(ahd, scb);
ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
ahd_freeze_scb(scb);
/*
* 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);
printf("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) {
printf("%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] = MSG_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) {
printf("%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
* transfered 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) {
printf("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);
printf("%s:%c:%d: no active SCB for reconnecting "
"target - issuing BUS DEVICE RESET\n",
ahd_name(ahd), 'A', ahd_inb(ahd, SELID) >> 4);
printf("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));
printf("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));
printf("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));
printf("SCSIBUS[0] == 0x%x, SCSISIGI == 0x%x\n",
ahd_inb(ahd, SCSIBUS), ahd_inb(ahd, SCSISIGI));
printf("SXFRCTL0 == 0x%x\n", ahd_inb(ahd, SXFRCTL0));
printf("SEQCTL0 == 0x%x\n", ahd_inb(ahd, SEQCTL0));
ahd_dump_card_state(ahd);
ahd->msgout_buf[0] = MSG_BUS_DEV_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);
printf("%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);
printf("%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);
printf("data overrun detected %s. Tag == 0x%x.\n",
ahd_lookup_phase_entry(lastphase)->phasemsg,
SCB_GET_TAG(scb));
ahd_print_path(ahd, scb);
printf("%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);
printf("%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);
printf("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);
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:
lun = scb->hscb->lun;
case SIU_TASKMGMT_TARGET_RESET:
{
struct ahd_devinfo devinfo;
ahd_scb_devinfo(ahd, &devinfo, scb);
error = CAM_BDR_SENT;
ahd_handle_devreset(ahd, &devinfo, lun,
CAM_BDR_SENT,
lun != CAM_LUN_WILDCARD
? "Lun Reset"
: "Target Reset",
/*verbose_level*/0);
break;
}
default:
panic("Unexpected TaskMgmt Func\n");
break;
}
}
break;
}
case TASKMGMT_CMD_CMPLT_OKAY:
{
u_int scbid;
struct scb *scb;
/*
* An ABORT TASK TMF failed to be delivered before
* the targeted command completed normally.
*/
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL) {
/*
* Remove the second instance of this SCB from
* the QINFIFO if it is still there.
*/
ahd_print_path(ahd, scb);
printf("SCB completes before TMF\n");
/*
* Handle losing the race. Wait until any
* current selection completes. We will then
* set the TMF back to zero in this SCB so that
* the sequencer doesn't bother to issue another
* sequencer interrupt for its completion.
*/
while ((ahd_inb(ahd, SCSISEQ0) & ENSELO) != 0
&& (ahd_inb(ahd, SSTAT0) & SELDO) == 0
&& (ahd_inb(ahd, SSTAT1) & SELTO) == 0)
;
ahd_outb(ahd, SCB_TASK_MANAGEMENT, 0);
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);
}
break;
}
case TRACEPOINT0:
case TRACEPOINT1:
case TRACEPOINT2:
case TRACEPOINT3:
printf("%s: Tracepoint %d\n", ahd_name(ahd),
seqintcode - TRACEPOINT0);
break;
case NO_SEQINT:
break;
case SAW_HWERR:
ahd_handle_hwerrint(ahd);
break;
default:
printf("%s: Unexpected SEQINTCODE %d\n", ahd_name(ahd),
seqintcode);
break;
}
/*
* The sequencer is paused immediately on
* a SEQINT, so we should restart it when
* we're done.
*/
ahd_unpause(ahd);
}
void
ahd_handle_scsiint(struct ahd_softc *ahd, u_int intstat)
{
struct scb *scb;
u_int status0;
u_int status3;
u_int status;
u_int lqistat1;
u_int lqostat0;
u_int scbid;
u_int busfreetime;
ahd_update_modes(ahd);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
status3 = ahd_inb(ahd, SSTAT3) & (NTRAMPERR|OSRAMPERR);
status0 = ahd_inb(ahd, SSTAT0) & (IOERR|OVERRUN|SELDI|SELDO);
status = ahd_inb(ahd, SSTAT1) & (SELTO|SCSIRSTI|BUSFREE|SCSIPERR);
lqistat1 = ahd_inb(ahd, LQISTAT1);
lqostat0 = ahd_inb(ahd, LQOSTAT0);
busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
if ((status0 & (SELDI|SELDO)) != 0) {
u_int simode0;
ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
simode0 = ahd_inb(ahd, SIMODE0);
status0 &= simode0 & (IOERR|OVERRUN|SELDI|SELDO);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
}
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL
&& (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
scb = NULL;
/* Make sure the sequencer is in a safe location. */
ahd_clear_critical_section(ahd);
if ((status0 & IOERR) != 0) {
u_int now_lvd;
now_lvd = ahd_inb(ahd, SBLKCTL) & ENAB40;
printf("%s: Transceiver State Has Changed to %s mode\n",
ahd_name(ahd), now_lvd ? "LVD" : "SE");
ahd_outb(ahd, CLRSINT0, CLRIOERR);
/*
* A change in I/O mode is equivalent to a bus reset.
*/
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
ahd_pause(ahd);
ahd_setup_iocell_workaround(ahd);
ahd_unpause(ahd);
} else if ((status0 & OVERRUN) != 0) {
printf("%s: SCSI offset overrun detected. Resetting bus.\n",
ahd_name(ahd));
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
} else if ((status & SCSIRSTI) != 0) {
printf("%s: Someone reset channel A\n", ahd_name(ahd));
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/FALSE);
} else if ((status & SCSIPERR) != 0) {
ahd_handle_transmission_error(ahd);
} else if (lqostat0 != 0) {
printf("%s: lqostat0 == 0x%x!\n", ahd_name(ahd), lqostat0);
ahd_outb(ahd, CLRLQOINT0, lqostat0);
if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
ahd_outb(ahd, CLRLQOINT1, 0);
}
} else if ((status & SELTO) != 0) {
u_int scbid;
/* Stop the selection */
ahd_outb(ahd, SCSISEQ0, 0);
/* No more pending messages */
ahd_clear_msg_state(ahd);
/* Clear interrupt state */
ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRBUSFREE|CLRSCSIPERR);
/*
* Although the driver does not care about the
* 'Selection in Progress' status bit, the busy
* LED does. SELINGO is only cleared by a sucessfull
* selection, so we must manually clear it to insure
* the LED turns off just incase no future successful
* selections occur (e.g. no devices on the bus).
*/
ahd_outb(ahd, CLRSINT0, CLRSELINGO);
scbid = ahd_inw(ahd, WAITING_TID_HEAD);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL) {
printf("%s: ahd_intr - referenced scb not "
"valid during SELTO scb(0x%x)\n",
ahd_name(ahd), scbid);
ahd_dump_card_state(ahd);
} else {
struct ahd_devinfo devinfo;
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_SELTO) != 0) {
ahd_print_path(ahd, scb);
printf("Saw Selection Timeout for SCB 0x%x\n",
scbid);
}
#endif
/*
* Force a renegotiation with this target just in
* case the cable was pulled and will later be
* re-attached. The target may forget its negotiation
* settings with us should it attempt to reselect
* during the interruption. The target will not issue
* a unit attention in this case, so we must always
* renegotiate.
*/
ahd_scb_devinfo(ahd, &devinfo, scb);
ahd_force_renegotiation(ahd, &devinfo);
ahd_set_transaction_status(scb, CAM_SEL_TIMEOUT);
ahd_freeze_devq(ahd, scb);
}
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_iocell_first_selection(ahd);
ahd_unpause(ahd);
} else if ((status0 & (SELDI|SELDO)) != 0) {
ahd_iocell_first_selection(ahd);
ahd_unpause(ahd);
} else if (status3 != 0) {
printf("%s: SCSI Cell parity error SSTAT3 == 0x%x\n",
ahd_name(ahd), status3);
ahd_outb(ahd, CLRSINT3, status3);
} else if ((lqistat1 & (LQIPHASE_LQ|LQIPHASE_NLQ)) != 0) {
ahd_handle_lqiphase_error(ahd, lqistat1);
} else if ((lqistat1 & LQICRCI_NLQ) != 0) {
/*
* This status can be delayed during some
* streaming operations. The SCSIPHASE
* handler has already dealt with this case
* so just clear the error.
*/
ahd_outb(ahd, CLRLQIINT1, CLRLQICRCI_NLQ);
} else if ((status & BUSFREE) != 0) {
u_int lqostat1;
int restart;
int clear_fifo;
int packetized;
u_int mode;
/*
* Clear our selection hardware as soon as possible.
* We may have an entry in the waiting Q for this target,
* that is affected by this busfree and we don't want to
* go about selecting the target while we handle the event.
*/
ahd_outb(ahd, SCSISEQ0, 0);
/*
* Determine what we were up to at the time of
* the busfree.
*/
mode = AHD_MODE_SCSI;
busfreetime = ahd_inb(ahd, SSTAT2) & BUSFREETIME;
lqostat1 = ahd_inb(ahd, LQOSTAT1);
switch (busfreetime) {
case BUSFREE_DFF0:
case BUSFREE_DFF1:
{
u_int scbid;
struct scb *scb;
mode = busfreetime == BUSFREE_DFF0
? AHD_MODE_DFF0 : AHD_MODE_DFF1;
ahd_set_modes(ahd, mode, mode);
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL) {
printf("%s: Invalid SCB %d in DFF%d "
"during unexpected busfree\n",
ahd_name(ahd), scbid, mode);
packetized = 0;
} else
packetized = (scb->flags & SCB_PACKETIZED) != 0;
clear_fifo = 1;
break;
}
case BUSFREE_LQO:
clear_fifo = 0;
packetized = 1;
break;
default:
clear_fifo = 0;
packetized = (lqostat1 & LQOBUSFREE) != 0;
if (!packetized
&& ahd_inb(ahd, LASTPHASE) == P_BUSFREE)
packetized = 1;
break;
}
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MISC) != 0)
printf("Saw Busfree. Busfreetime = 0x%x.\n",
busfreetime);
#endif
/*
* Busfrees that occur in non-packetized phases are
* handled by the nonpkt_busfree handler.
*/
if (packetized && ahd_inb(ahd, LASTPHASE) == P_BUSFREE) {
restart = ahd_handle_pkt_busfree(ahd, busfreetime);
} else {
packetized = 0;
restart = ahd_handle_nonpkt_busfree(ahd);
}
/*
* Clear the busfree interrupt status. The setting of
* the interrupt is a pulse, so in a perfect world, we
* would not need to muck with the ENBUSFREE logic. This
* would ensure that if the bus moves on to another
* connection, busfree protection is still in force. If
* BUSFREEREV is broken, however, we must manually clear
* the ENBUSFREE if the busfree occurred during a non-pack
* connection so that we don't get false positives during
* future, packetized, connections.
*/
ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
if (packetized == 0
&& (ahd->bugs & AHD_BUSFREEREV_BUG) != 0)
ahd_outb(ahd, SIMODE1,
ahd_inb(ahd, SIMODE1) & ~ENBUSFREE);
if (clear_fifo)
ahd_clear_fifo(ahd, mode);
ahd_clear_msg_state(ahd);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
if (restart) {
ahd_restart(ahd);
} else {
ahd_unpause(ahd);
}
} else {
printf("%s: Missing case in ahd_handle_scsiint. status = %x\n",
ahd_name(ahd), status);
ahd_dump_card_state(ahd);
ahd_clear_intstat(ahd);
ahd_unpause(ahd);
}
}
static void
ahd_handle_transmission_error(struct ahd_softc *ahd)
{
struct scb *scb;
u_int scbid;
u_int lqistat1;
u_int lqistat2;
u_int msg_out;
u_int curphase;
u_int lastphase;
u_int perrdiag;
u_int cur_col;
int silent;
scb = NULL;
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
lqistat1 = ahd_inb(ahd, LQISTAT1) & ~(LQIPHASE_LQ|LQIPHASE_NLQ);
lqistat2 = ahd_inb(ahd, LQISTAT2);
if ((lqistat1 & (LQICRCI_NLQ|LQICRCI_LQ)) == 0
&& (ahd->bugs & AHD_NLQICRC_DELAYED_BUG) != 0) {
u_int lqistate;
ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
lqistate = ahd_inb(ahd, LQISTATE);
if ((lqistate >= 0x1E && lqistate <= 0x24)
|| (lqistate == 0x29)) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_RECOVERY) != 0) {
printf("%s: NLQCRC found via LQISTATE\n",
ahd_name(ahd));
}
#endif
lqistat1 |= LQICRCI_NLQ;
}
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
}
ahd_outb(ahd, CLRLQIINT1, lqistat1);
lastphase = ahd_inb(ahd, LASTPHASE);
curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
perrdiag = ahd_inb(ahd, PERRDIAG);
msg_out = MSG_INITIATOR_DET_ERR;
ahd_outb(ahd, CLRSINT1, CLRSCSIPERR);
/*
* Try to find the SCB associated with this error.
*/
silent = FALSE;
if (lqistat1 == 0
|| (lqistat1 & LQICRCI_NLQ) != 0) {
if ((lqistat1 & (LQICRCI_NLQ|LQIOVERI_NLQ)) != 0)
ahd_set_active_fifo(ahd);
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL && SCB_IS_SILENT(scb))
silent = TRUE;
}
cur_col = 0;
if (silent == FALSE) {
printf("%s: Transmission error detected\n", ahd_name(ahd));
ahd_lqistat1_print(lqistat1, &cur_col, 50);
ahd_lastphase_print(lastphase, &cur_col, 50);
ahd_scsisigi_print(curphase, &cur_col, 50);
ahd_perrdiag_print(perrdiag, &cur_col, 50);
printf("\n");
ahd_dump_card_state(ahd);
}
if ((lqistat1 & (LQIOVERI_LQ|LQIOVERI_NLQ)) != 0) {
if (silent == FALSE) {
printf("%s: Gross protocol error during incoming "
"packet. lqistat1 == 0x%x. Resetting bus.\n",
ahd_name(ahd), lqistat1);
}
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
return;
} else if ((lqistat1 & LQICRCI_LQ) != 0) {
/*
* A CRC error has been detected on an incoming LQ.
* The bus is currently hung on the last ACK.
* Hit LQIRETRY to release the last ack, and
* wait for the sequencer to determine that ATNO
* is asserted while in message out to take us
* to our host message loop. No NONPACKREQ or
* LQIPHASE type errors will occur in this
* scenario. After this first LQIRETRY, the LQI
* manager will be in ISELO where it will
* happily sit until another packet phase begins.
* Unexpected bus free detection is enabled
* through any phases that occur after we release
* this last ack until the LQI manager sees a
* packet phase. This implies we may have to
* ignore a perfectly valid "unexected busfree"
* after our "initiator detected error" message is
* sent. A busfree is the expected response after
* we tell the target that it's L_Q was corrupted.
* (SPI4R09 10.7.3.3.3)
*/
ahd_outb(ahd, LQCTL2, LQIRETRY);
printf("LQIRetry for LQICRCI_LQ to release ACK\n");
} else if ((lqistat1 & LQICRCI_NLQ) != 0) {
/*
* We detected a CRC error in a NON-LQ packet.
* The hardware has varying behavior in this situation
* depending on whether this packet was part of a
* stream or not.
*
* PKT by PKT mode:
* The hardware has already acked the complete packet.
* If the target honors our outstanding ATN condition,
* we should be (or soon will be) in MSGOUT phase.
* This will trigger the LQIPHASE_LQ status bit as the
* hardware was expecting another LQ. Unexpected
* busfree detection is enabled. Once LQIPHASE_LQ is
* true (first entry into host message loop is much
* the same), we must clear LQIPHASE_LQ and hit
* LQIRETRY so the hardware is ready to handle
* a future LQ. NONPACKREQ will not be asserted again
* once we hit LQIRETRY until another packet is
* processed. The target may either go busfree
* or start another packet in response to our message.
*
* Read Streaming P0 asserted:
* If we raise ATN and the target completes the entire
* stream (P0 asserted during the last packet), the
* hardware will ack all data and return to the ISTART
* state. When the target reponds to our ATN condition,
* LQIPHASE_LQ will be asserted. We should respond to
* this with an LQIRETRY to prepare for any future
* packets. NONPACKREQ will not be asserted again
* once we hit LQIRETRY until another packet is
* processed. The target may either go busfree or
* start another packet in response to our message.
* Busfree detection is enabled.
*
* Read Streaming P0 not asserted:
* If we raise ATN and the target transitions to
* MSGOUT in or after a packet where P0 is not
* asserted, the hardware will assert LQIPHASE_NLQ.
* We should respond to the LQIPHASE_NLQ with an
* LQIRETRY. Should the target stay in a non-pkt
* phase after we send our message, the hardware
* will assert LQIPHASE_LQ. Recovery is then just as
* listed above for the read streaming with P0 asserted.
* Busfree detection is enabled.
*/
if (silent == FALSE)
printf("LQICRC_NLQ\n");
if (scb == NULL) {
printf("%s: No SCB valid for LQICRC_NLQ. "
"Resetting bus\n", ahd_name(ahd));
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
return;
}
} else if ((lqistat1 & LQIBADLQI) != 0) {
printf("Need to handle BADLQI!\n");
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
return;
} else if ((perrdiag & (PARITYERR|PREVPHASE)) == PARITYERR) {
if ((curphase & ~P_DATAIN_DT) != 0) {
/* Ack the byte. So we can continue. */
if (silent == FALSE)
printf("Acking %s to clear perror\n",
ahd_lookup_phase_entry(curphase)->phasemsg);
ahd_inb(ahd, SCSIDAT);
}
if (curphase == P_MESGIN)
msg_out = MSG_PARITY_ERROR;
}
/*
* 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.
*/
ahd->send_msg_perror = msg_out;
if (scb != NULL && msg_out == MSG_INITIATOR_DET_ERR)
scb->flags |= SCB_TRANSMISSION_ERROR;
ahd_outb(ahd, MSG_OUT, HOST_MSG);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_unpause(ahd);
}
static void
ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
{
/*
* Clear the sources of the interrupts.
*/
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_outb(ahd, CLRLQIINT1, lqistat1);
/*
* If the "illegal" phase changes were in response
* to our ATN to flag a CRC error, AND we ended up
* on packet boundaries, clear the error, restart the
* LQI manager as appropriate, and go on our merry
* way toward sending the message. Otherwise, reset
* the bus to clear the error.
*/
ahd_set_active_fifo(ahd);
if ((ahd_inb(ahd, SCSISIGO) & ATNO) != 0
&& (ahd_inb(ahd, MDFFSTAT) & DLZERO) != 0) {
if ((lqistat1 & LQIPHASE_LQ) != 0) {
printf("LQIRETRY for LQIPHASE_LQ\n");
ahd_outb(ahd, LQCTL2, LQIRETRY);
} else if ((lqistat1 & LQIPHASE_NLQ) != 0) {
printf("LQIRETRY for LQIPHASE_NLQ\n");
ahd_outb(ahd, LQCTL2, LQIRETRY);
} else
panic("ahd_handle_lqiphase_error: No phase errors\n");
ahd_dump_card_state(ahd);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_unpause(ahd);
} else {
printf("Reseting Channel for LQI Phase error\n");
ahd_dump_card_state(ahd);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
}
}
/*
* Packetized unexpected or expected busfree.
* Entered in mode based on busfreetime.
*/
static int
ahd_handle_pkt_busfree(struct ahd_softc *ahd, u_int busfreetime)
{
u_int lqostat1;
AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
lqostat1 = ahd_inb(ahd, LQOSTAT1);
if ((lqostat1 & LQOBUSFREE) != 0) {
struct scb *scb;
u_int scbid;
u_int saved_scbptr;
u_int waiting_h;
u_int waiting_t;
u_int next;
if ((busfreetime & BUSFREE_LQO) == 0)
printf("%s: Warning, BUSFREE time is 0x%x. "
"Expected BUSFREE_LQO.\n",
ahd_name(ahd), busfreetime);
/*
* The LQO manager detected an unexpected busfree
* either:
*
* 1) During an outgoing LQ.
* 2) After an outgoing LQ but before the first
* REQ of the command packet.
* 3) During an outgoing command packet.
*
* In all cases, CURRSCB is pointing to the
* SCB that encountered the failure. Clean
* up the queue, clear SELDO and LQOBUSFREE,
* and allow the sequencer to restart the select
* out at its lesure.
*/
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
scbid = ahd_inw(ahd, CURRSCB);
scb = ahd_lookup_scb(ahd, scbid);
if (scb == NULL)
panic("SCB not valid during LQOBUSFREE");
/*
* Clear the status.
*/
ahd_outb(ahd, CLRLQOINT1, CLRLQOBUSFREE);
if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0)
ahd_outb(ahd, CLRLQOINT1, 0);
ahd_outb(ahd, SCSISEQ0, ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
ahd_flush_device_writes(ahd);
ahd_outb(ahd, CLRSINT0, CLRSELDO);
/*
* Return the LQO manager to its idle loop. It will
* not do this automatically if the busfree occurs
* after the first REQ of either the LQ or command
* packet or between the LQ and command packet.
*/
ahd_outb(ahd, LQCTL2, ahd_inb(ahd, LQCTL2) | LQOTOIDLE);
/*
* Update the waiting for selection queue so
* we restart on the correct SCB.
*/
waiting_h = ahd_inw(ahd, WAITING_TID_HEAD);
saved_scbptr = ahd_get_scbptr(ahd);
if (waiting_h != scbid) {
ahd_outw(ahd, WAITING_TID_HEAD, scbid);
waiting_t = ahd_inw(ahd, WAITING_TID_TAIL);
if (waiting_t == waiting_h) {
ahd_outw(ahd, WAITING_TID_TAIL, scbid);
next = SCB_LIST_NULL;
} else {
ahd_set_scbptr(ahd, waiting_h);
next = ahd_inw_scbram(ahd, SCB_NEXT2);
}
ahd_set_scbptr(ahd, scbid);
ahd_outw(ahd, SCB_NEXT2, next);
}
ahd_set_scbptr(ahd, saved_scbptr);
if (scb->crc_retry_count < AHD_MAX_LQ_CRC_ERRORS) {
if (SCB_IS_SILENT(scb) == FALSE) {
ahd_print_path(ahd, scb);
printf("Probable outgoing LQ CRC error. "
"Retrying command\n");
}
scb->crc_retry_count++;
} else {
ahd_set_transaction_status(scb, CAM_UNCOR_PARITY);
ahd_freeze_scb(scb);
ahd_freeze_devq(ahd, scb);
}
/* Return unpausing the sequencer. */
return (0);
} else if ((ahd_inb(ahd, PERRDIAG) & PARITYERR) != 0) {
/*
* Ignore what are really parity errors that
* occur on the last REQ of a free running
* clock prior to going busfree. Some drives
* do not properly active negate just before
* going busfree resulting in a parity glitch.
*/
ahd_outb(ahd, CLRSINT1, CLRSCSIPERR|CLRBUSFREE);
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MASKED_ERRORS) != 0)
printf("%s: Parity on last REQ detected "
"during busfree phase.\n",
ahd_name(ahd));
#endif
/* Return unpausing the sequencer. */
return (0);
}
if (ahd->src_mode != AHD_MODE_SCSI) {
u_int scbid;
struct scb *scb;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
ahd_print_path(ahd, scb);
printf("Unexpected PKT busfree condition\n");
ahd_dump_card_state(ahd);
ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb), 'A',
SCB_GET_LUN(scb), SCB_GET_TAG(scb),
ROLE_INITIATOR, CAM_UNEXP_BUSFREE);
/* Return restarting the sequencer. */
return (1);
}
printf("%s: Unexpected PKT busfree condition\n", ahd_name(ahd));
ahd_dump_card_state(ahd);
/* Restart the sequencer. */
return (1);
}
/*
* Non-packetized unexpected or expected busfree.
*/
static int
ahd_handle_nonpkt_busfree(struct ahd_softc *ahd)
{
struct ahd_devinfo devinfo;
struct scb *scb;
u_int lastphase;
u_int saved_scsiid;
u_int saved_lun;
u_int target;
u_int initiator_role_id;
u_int scbid;
u_int ppr_busfree;
int printerror;
/*
* 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.
*/
lastphase = ahd_inb(ahd, LASTPHASE);
saved_scsiid = ahd_inb(ahd, SAVED_SCSIID);
saved_lun = ahd_inb(ahd, SAVED_LUN);
target = SCSIID_TARGET(ahd, saved_scsiid);
initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
ahd_compile_devinfo(&devinfo, initiator_role_id,
target, saved_lun, 'A', ROLE_INITIATOR);
printerror = 1;
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
if (scb != NULL
&& (ahd_inb(ahd, SEQ_FLAGS) & NOT_IDENTIFIED) != 0)
scb = NULL;
ppr_busfree = (ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0;
if (lastphase == P_MESGOUT) {
u_int tag;
tag = SCB_LIST_NULL;
if (ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT_TAG, TRUE)
|| ahd_sent_msg(ahd, AHDMSG_1B, MSG_ABORT, TRUE)) {
int found;
int sent_msg;
if (scb == NULL) {
ahd_print_devinfo(ahd, &devinfo);
printf("Abort for unidentified "
"connection completed.\n");
/* restart the sequencer. */
return (1);
}
sent_msg = ahd->msgout_buf[ahd->msgout_index - 1];
ahd_print_path(ahd, scb);
printf("SCB %d - Abort%s Completed.\n",
SCB_GET_TAG(scb),
sent_msg == MSG_ABORT_TAG ? "" : " Tag");
if (sent_msg == MSG_ABORT_TAG)
tag = SCB_GET_TAG(scb);
if ((scb->flags & SCB_CMDPHASE_ABORT) != 0) {
/*
* This abort is in response to an
* unexpected switch to command phase
* for a packetized connection. Since
* the identify message was never sent,
* "saved lun" is 0. We really want to
* abort only the SCB that encountered
* this error, which could have a different
* lun. The SCB will be retried so the OS
* will see the UA after renegotiating to
* packetized.
*/
tag = SCB_GET_TAG(scb);
saved_lun = scb->hscb->lun;
}
found = ahd_abort_scbs(ahd, target, 'A', saved_lun,
tag, ROLE_INITIATOR,
CAM_REQ_ABORTED);
printf("found == 0x%x\n", found);
printerror = 0;
} else if (ahd_sent_msg(ahd, AHDMSG_1B,
MSG_BUS_DEV_RESET, TRUE)) {
#ifdef __FreeBSD__
/*
* Don't mark the user's request for this BDR
* as completing with CAM_BDR_SENT. CAM3
* specifies CAM_REQ_CMP.
*/
if (scb != NULL
&& scb->io_ctx->ccb_h.func_code== XPT_RESET_DEV
&& ahd_match_scb(ahd, scb, target, 'A',
CAM_LUN_WILDCARD, SCB_LIST_NULL,
ROLE_INITIATOR))
ahd_set_transaction_status(scb, CAM_REQ_CMP);
#endif
ahd_handle_devreset(ahd, &devinfo, CAM_LUN_WILDCARD,
CAM_BDR_SENT, "Bus Device Reset",
/*verbose_level*/0);
printerror = 0;
} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_PPR, FALSE)
&& ppr_busfree == 0) {
struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
/*
* PPR Rejected. Try non-ppr negotiation
* and retry command.
*/
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("PPR negotiation rejected busfree.\n");
#endif
tinfo = ahd_fetch_transinfo(ahd, devinfo.channel,
devinfo.our_scsiid,
devinfo.target, &tstate);
tinfo->curr.transport_version = 2;
tinfo->goal.transport_version = 2;
tinfo->goal.ppr_options = 0;
ahd_qinfifo_requeue_tail(ahd, scb);
printerror = 0;
} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_WDTR, FALSE)
&& ppr_busfree == 0) {
/*
* Negotiation Rejected. Go-narrow and
* retry command.
*/
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("WDTR negotiation rejected busfree.\n");
#endif
ahd_set_width(ahd, &devinfo,
MSG_EXT_WDTR_BUS_8_BIT,
AHD_TRANS_CUR|AHD_TRANS_GOAL,
/*paused*/TRUE);
ahd_qinfifo_requeue_tail(ahd, scb);
printerror = 0;
} else if (ahd_sent_msg(ahd, AHDMSG_EXT, MSG_EXT_SDTR, FALSE)
&& ppr_busfree == 0) {
/*
* Negotiation Rejected. Go-async and
* retry command.
*/
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("SDTR negotiation rejected busfree.\n");
#endif
ahd_set_syncrate(ahd, &devinfo,
/*period*/0, /*offset*/0,
/*ppr_options*/0,
AHD_TRANS_CUR|AHD_TRANS_GOAL,
/*paused*/TRUE);
ahd_qinfifo_requeue_tail(ahd, scb);
printerror = 0;
} else if ((ahd->msg_flags & MSG_FLAG_EXPECT_IDE_BUSFREE) != 0
&& ahd_sent_msg(ahd, AHDMSG_1B,
MSG_INITIATOR_DET_ERR, TRUE)) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("Expected IDE Busfree\n");
#endif
printerror = 0;
} else if ((ahd->msg_flags & MSG_FLAG_EXPECT_QASREJ_BUSFREE)
&& ahd_sent_msg(ahd, AHDMSG_1B,
MSG_MESSAGE_REJECT, TRUE)) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("Expected QAS Reject Busfree\n");
#endif
printerror = 0;
}
}
/*
* The busfree required flag is honored at the end of
* the message phases. We check it last in case we
* had to send some other message that caused a busfree.
*/
if (printerror != 0
&& (lastphase == P_MESGIN || lastphase == P_MESGOUT)
&& ((ahd->msg_flags & MSG_FLAG_EXPECT_PPR_BUSFREE) != 0)) {
ahd_freeze_devq(ahd, scb);
ahd_set_transaction_status(scb, CAM_REQUEUE_REQ);
ahd_freeze_scb(scb);
if ((ahd->msg_flags & MSG_FLAG_IU_REQ_CHANGED) != 0) {
ahd_abort_scbs(ahd, SCB_GET_TARGET(ahd, scb),
SCB_GET_CHANNEL(ahd, scb),
SCB_GET_LUN(scb), SCB_LIST_NULL,
ROLE_INITIATOR, CAM_REQ_ABORTED);
} else {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("PPR Negotiation Busfree.\n");
#endif
ahd_done(ahd, scb);
}
printerror = 0;
}
if (printerror != 0) {
int aborted;
aborted = 0;
if (scb != NULL) {
u_int tag;
if ((scb->hscb->control & TAG_ENB) != 0)
tag = SCB_GET_TAG(scb);
else
tag = SCB_LIST_NULL;
ahd_print_path(ahd, scb);
aborted = ahd_abort_scbs(ahd, target, 'A',
SCB_GET_LUN(scb), tag,
ROLE_INITIATOR,
CAM_UNEXP_BUSFREE);
} else {
/*
* We had not fully identified this connection,
* so we cannot abort anything.
*/
printf("%s: ", ahd_name(ahd));
}
if (lastphase != P_BUSFREE)
ahd_force_renegotiation(ahd, &devinfo);
printf("Unexpected busfree %s, %d SCBs aborted, "
"PRGMCNT == 0x%x\n",
ahd_lookup_phase_entry(lastphase)->phasemsg,
aborted,
ahd_inb(ahd, PRGMCNT)
| (ahd_inb(ahd, PRGMCNT+1) << 8));
ahd_dump_card_state(ahd);
}
/* Always restart the sequencer. */
return (1);
}
static void
ahd_handle_proto_violation(struct ahd_softc *ahd)
{
struct ahd_devinfo devinfo;
struct scb *scb;
u_int scbid;
u_int seq_flags;
u_int curphase;
u_int lastphase;
int found;
ahd_fetch_devinfo(ahd, &devinfo);
scbid = ahd_get_scbptr(ahd);
scb = ahd_lookup_scb(ahd, scbid);
seq_flags = ahd_inb(ahd, SEQ_FLAGS);
curphase = ahd_inb(ahd, SCSISIGI) & PHASE_MASK;
lastphase = ahd_inb(ahd, LASTPHASE);
if ((seq_flags & NOT_IDENTIFIED) != 0) {
/*
* The reconnecting target either did not send an
* identify message, or did, but we didn't find an SCB
* to match.
*/
ahd_print_devinfo(ahd, &devinfo);
printf("Target did not send an IDENTIFY message. "
"LASTPHASE = 0x%x.\n", lastphase);
scb = NULL;
} else if (scb == NULL) {
/*
* We don't seem to have an SCB active for this
* transaction. Print an error and reset the bus.
*/
ahd_print_devinfo(ahd, &devinfo);
printf("No SCB found during protocol violation\n");
goto proto_violation_reset;
} else {
ahd_set_transaction_status(scb, CAM_SEQUENCE_FAIL);
if ((seq_flags & NO_CDB_SENT) != 0) {
ahd_print_path(ahd, scb);
printf("No or incomplete CDB sent to device.\n");
} else if ((ahd_inb_scbram(ahd, SCB_CONTROL)
& STATUS_RCVD) == 0) {
/*
* The target never bothered to provide status to
* us prior to completing the command. Since we don't
* know the disposition of this command, we must attempt
* to abort it. Assert ATN and prepare to send an abort
* message.
*/
ahd_print_path(ahd, scb);
printf("Completed command without status.\n");
} else {
ahd_print_path(ahd, scb);
printf("Unknown protocol violation.\n");
ahd_dump_card_state(ahd);
}
}
if ((lastphase & ~P_DATAIN_DT) == 0
|| lastphase == P_COMMAND) {
proto_violation_reset:
/*
* Target either went directly to data
* phase or didn't respond to our ATN.
* The only safe thing to do is to blow
* it away with a bus reset.
*/
found = ahd_reset_channel(ahd, 'A', TRUE);
printf("%s: Issued Channel %c Bus Reset. "
"%d SCBs aborted\n", ahd_name(ahd), 'A', found);
} else {
/*
* Leave the selection hardware off in case
* this abort attempt will affect yet to
* be sent commands.
*/
ahd_outb(ahd, SCSISEQ0,
ahd_inb(ahd, SCSISEQ0) & ~ENSELO);
ahd_assert_atn(ahd);
ahd_outb(ahd, MSG_OUT, HOST_MSG);
if (scb == NULL) {
ahd_print_devinfo(ahd, &devinfo);
ahd->msgout_buf[0] = MSG_ABORT_TASK;
ahd->msgout_len = 1;
ahd->msgout_index = 0;
ahd->msg_type = MSG_TYPE_INITIATOR_MSGOUT;
} else {
ahd_print_path(ahd, scb);
scb->flags |= SCB_ABORT;
}
printf("Protocol violation %s. Attempting to abort.\n",
ahd_lookup_phase_entry(curphase)->phasemsg);
}
}
/*
* Force renegotiation to occur the next time we initiate
* a command to the current device.
*/
static void
ahd_force_renegotiation(struct ahd_softc *ahd, struct ahd_devinfo *devinfo)
{
struct ahd_initiator_tinfo *targ_info;
struct ahd_tmode_tstate *tstate;
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
ahd_print_devinfo(ahd, devinfo);
printf("Forcing renegotiation\n");
}
#endif
targ_info = ahd_fetch_transinfo(ahd,
devinfo->channel,
devinfo->our_scsiid,
devinfo->target,
&tstate);
ahd_update_neg_request(ahd, devinfo, tstate,
targ_info, AHD_NEG_IF_NON_ASYNC);
}
#define AHD_MAX_STEPS 2000
void
ahd_clear_critical_section(struct ahd_softc *ahd)
{
ahd_mode_state saved_modes;
int stepping;
int steps;
int first_instr;
u_int simode0;
u_int simode1;
u_int simode3;
u_int lqimode0;
u_int lqimode1;
u_int lqomode0;
u_int lqomode1;
if (ahd->num_critical_sections == 0)
return;
stepping = FALSE;
steps = 0;
first_instr = 0;
simode0 = 0;
simode1 = 0;
simode3 = 0;
lqimode0 = 0;
lqimode1 = 0;
lqomode0 = 0;
lqomode1 = 0;
saved_modes = ahd_save_modes(ahd);
for (;;) {
struct cs *cs;
u_int seqaddr;
u_int i;
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
seqaddr = ahd_inb(ahd, CURADDR)
| (ahd_inb(ahd, CURADDR+1) << 8);
cs = ahd->critical_sections;
for (i = 0; i < ahd->num_critical_sections; i++, cs++) {
if (cs->begin < seqaddr && cs->end >= seqaddr)
break;
}
if (i == ahd->num_critical_sections)
break;
if (steps > AHD_MAX_STEPS) {
printf("%s: Infinite loop in critical section\n"
"%s: First Instruction 0x%x now 0x%x\n",
ahd_name(ahd), ahd_name(ahd), first_instr,
seqaddr);
ahd_dump_card_state(ahd);
panic("critical section loop");
}
steps++;
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MISC) != 0)
printf("%s: Single stepping at 0x%x\n", ahd_name(ahd),
seqaddr);
#endif
if (stepping == FALSE) {
first_instr = seqaddr;
ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
simode0 = ahd_inb(ahd, SIMODE0);
simode3 = ahd_inb(ahd, SIMODE3);
lqimode0 = ahd_inb(ahd, LQIMODE0);
lqimode1 = ahd_inb(ahd, LQIMODE1);
lqomode0 = ahd_inb(ahd, LQOMODE0);
lqomode1 = ahd_inb(ahd, LQOMODE1);
ahd_outb(ahd, SIMODE0, 0);
ahd_outb(ahd, SIMODE3, 0);
ahd_outb(ahd, LQIMODE0, 0);
ahd_outb(ahd, LQIMODE1, 0);
ahd_outb(ahd, LQOMODE0, 0);
ahd_outb(ahd, LQOMODE1, 0);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
simode1 = ahd_inb(ahd, SIMODE1);
/*
* We don't clear ENBUSFREE. Unfortunately
* we cannot re-enable busfree detection within
* the current connection, so we must leave it
* on while single stepping.
*/
ahd_outb(ahd, SIMODE1, simode1 & ENBUSFREE);
ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) | STEP);
stepping = TRUE;
}
ahd_outb(ahd, CLRSINT1, CLRBUSFREE);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode);
ahd_outb(ahd, HCNTRL, ahd->unpause);
while (!ahd_is_paused(ahd))
ahd_delay(200);
ahd_update_modes(ahd);
}
if (stepping) {
ahd_set_modes(ahd, AHD_MODE_CFG, AHD_MODE_CFG);
ahd_outb(ahd, SIMODE0, simode0);
ahd_outb(ahd, SIMODE3, simode3);
ahd_outb(ahd, LQIMODE0, lqimode0);
ahd_outb(ahd, LQIMODE1, lqimode1);
ahd_outb(ahd, LQOMODE0, lqomode0);
ahd_outb(ahd, LQOMODE1, lqomode1);
ahd_set_modes(ahd, AHD_MODE_SCSI, AHD_MODE_SCSI);
ahd_outb(ahd, SEQCTL0, ahd_inb(ahd, SEQCTL0) & ~STEP);
ahd_outb(ahd, SIMODE1, simode1);
/*
* SCSIINT seems to glitch occassionally when
* the interrupt masks are restored. Clear SCSIINT
* one more time so that only persistent errors
* are seen as a real interrupt.
*/
ahd_outb(ahd, CLRINT, CLRSCSIINT);
}
ahd_restore_modes(ahd, saved_modes);
}
/*
* Clear any pending interrupt status.
*/
void
ahd_clear_intstat(struct ahd_softc *ahd)
{
AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK),
~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK));
/* Clear any interrupt conditions this may have caused */
ahd_outb(ahd, CLRLQIINT0, CLRLQIATNQAS|CLRLQICRCT1|CLRLQICRCT2
|CLRLQIBADLQT|CLRLQIATNLQ|CLRLQIATNCMD);
ahd_outb(ahd, CLRLQIINT1, CLRLQIPHASE_LQ|CLRLQIPHASE_NLQ|CLRLIQABORT
|CLRLQICRCI_LQ|CLRLQICRCI_NLQ|CLRLQIBADLQI
|CLRLQIOVERI_LQ|CLRLQIOVERI_NLQ|CLRNONPACKREQ);
ahd_outb(ahd, CLRLQOINT0, CLRLQOTARGSCBPERR|CLRLQOSTOPT2|CLRLQOATNLQ
|CLRLQOATNPKT|CLRLQOTCRC);
ahd_outb(ahd, CLRLQOINT1, CLRLQOINITSCBPERR|CLRLQOSTOPI2|CLRLQOBADQAS
|CLRLQOBUSFREE|CLRLQOPHACHGINPKT);
if ((ahd->bugs & AHD_CLRLQO_AUTOCLR_BUG) != 0) {
ahd_outb(ahd, CLRLQOINT0, 0);
ahd_outb(ahd, CLRLQOINT1, 0);
}
ahd_outb(ahd, CLRSINT3, CLRNTRAMPERR|CLROSRAMPERR);
ahd_outb(ahd, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
|CLRBUSFREE|CLRSCSIPERR|CLRREQINIT);
ahd_outb(ahd, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO
|CLRIOERR|CLROVERRUN);
ahd_outb(ahd, CLRINT, CLRSCSIINT);
}
/**************************** Debugging Routines ******************************/
#ifdef AHD_DEBUG
uint32_t ahd_debug = AHD_DEBUG_OPTS;
#endif
void
ahd_print_scb(struct scb *scb)
{
struct hardware_scb *hscb;
int i;
hscb = scb->hscb;
printf("scb:%p control:0x%x scsiid:0x%x lun:%d cdb_len:%d\n",
(void *)scb,
hscb->control,
hscb->scsiid,
hscb->lun,
hscb->cdb_len);
printf("Shared Data: ");
for (i = 0; i < sizeof(hscb->shared_data.idata.cdb); i++)
printf("%#02x", hscb->shared_data.idata.cdb[i]);
printf(" dataptr:%#x%x datacnt:%#x sgptr:%#x tag:%#x\n",
(uint32_t)((ahd_le64toh(hscb->dataptr) >> 32) & 0xFFFFFFFF),
(uint32_t)(ahd_le64toh(hscb->dataptr) & 0xFFFFFFFF),
ahd_le32toh(hscb->datacnt),
ahd_le32toh(hscb->sgptr),
SCB_GET_TAG(scb));
ahd_dump_sglist(scb);
}
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;
uint32_t len;
addr = ahd_le64toh(sg_list[i].addr);
len = ahd_le32toh(sg_list[i].len);
printf("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);
printf("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" : "");
}
}
}
}
/************************* Transfer Negotiation *******************************/
/*
* Allocate per target mode instance (ID we respond to as a target)
* transfer negotiation data structures.
*/
static struct ahd_tmode_tstate *
ahd_alloc_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel)
{
struct ahd_tmode_tstate *master_tstate;
struct ahd_tmode_tstate *tstate;
int i;
master_tstate = ahd->enabled_targets[ahd->our_id];
if (ahd->enabled_targets[scsi_id] != NULL
&& ahd->enabled_targets[scsi_id] != master_tstate)
panic("%s: ahd_alloc_tstate - Target already allocated",
ahd_name(ahd));
tstate = malloc(sizeof(*tstate), M_DEVBUF, M_NOWAIT);
if (tstate == NULL)
return (NULL);
/*
* If we have allocated a master tstate, copy user settings from
* the master tstate (taken from SRAM or the EEPROM) for this
* channel, but reset our current and goal settings to async/narrow
* until an initiator talks to us.
*/
if (master_tstate != NULL) {
memcpy(tstate, master_tstate, sizeof(*tstate));
memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
for (i = 0; i < 16; i++) {
memset(&tstate->transinfo[i].curr, 0,
sizeof(tstate->transinfo[i].curr));
memset(&tstate->transinfo[i].goal, 0,
sizeof(tstate->transinfo[i].goal));
}
} else
memset(tstate, 0, sizeof(*tstate));
ahd->enabled_targets[scsi_id] = tstate;
return (tstate);
}
#ifdef AHD_TARGET_MODE
/*
* Free per target mode instance (ID we respond to as a target)
* transfer negotiation data structures.
*/
static void
ahd_free_tstate(struct ahd_softc *ahd, u_int scsi_id, char channel, int force)
{
struct ahd_tmode_tstate *tstate;
/*
* Don't clean up our "master" tstate.
* It has our default user settings.
*/
if (scsi_id == ahd->our_id
&& force == FALSE)
return;
tstate = ahd->enabled_targets[scsi_id];
if (tstate != NULL)
free(tstate, M_DEVBUF);
ahd->enabled_targets[scsi_id] = NULL;
}
#endif
/*
* Called when we have an active connection to a target on the bus,
* this function finds the nearest period to the input period limited
* by the capabilities of the bus connectivity of and sync settings for
* the target.
*/
void
ahd_devlimited_syncrate(struct ahd_softc *ahd,
struct ahd_initiator_tinfo *tinfo,
u_int *period, u_int *ppr_options, role_t role)
{
struct ahd_transinfo *transinfo;
u_int maxsync;
if ((ahd_inb(ahd, SBLKCTL) & ENAB40) != 0
&& (ahd_inb(ahd, SSTAT2) & EXP_ACTIVE) == 0) {
maxsync = AHD_SYNCRATE_PACED;
} else {
maxsync = AHD_SYNCRATE_ULTRA;
/* Can't do DT related options on an SE bus */
*ppr_options &= MSG_EXT_PPR_QAS_REQ;
}
/*
* Never allow a value higher than our current goal
* period otherwise we may allow a target initiated
* negotiation to go above the limit as set by the
* user. In the case of an initiator initiated
* sync negotiation, we limit based on the user
* setting. This allows the system to still accept
* incoming negotiations even if target initiated
* negotiation is not performed.
*/
if (role == ROLE_TARGET)
transinfo = &tinfo->user;
else
transinfo = &tinfo->goal;
*ppr_options &= (transinfo->ppr_options|MSG_EXT_PPR_PCOMP_EN);
if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
maxsync = MAX(maxsync, AHD_SYNCRATE_ULTRA2);
*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
}
if (transinfo->period == 0) {
*period = 0;
*ppr_options = 0;
} else {
*period = MAX(*period, transinfo->period);
ahd_find_syncrate(ahd, period, ppr_options, maxsync);
}
}
/*
* Look up the valid period to SCSIRATE conversion in our table.
* Return the period and offset that should be sent to the target
* if this was the beginning of an SDTR.
*/
void
ahd_find_syncrate(struct ahd_softc *ahd, u_int *period,
u_int *ppr_options, u_int maxsync)
{
if (*period < maxsync)
*period = maxsync;
if ((*ppr_options & MSG_EXT_PPR_DT_REQ) != 0
&& *period > AHD_SYNCRATE_MIN_DT)
*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
if (*period > AHD_SYNCRATE_MIN)
*period = 0;
/* Honor PPR option conformance rules. */
if (*period > AHD_SYNCRATE_PACED)
*ppr_options &= ~MSG_EXT_PPR_RTI;
if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0)
*ppr_options &= (MSG_EXT_PPR_DT_REQ|MSG_EXT_PPR_QAS_REQ);
if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0)
*ppr_options &= MSG_EXT_PPR_QAS_REQ;
/* Skip all PACED only entries if IU is not available */
if ((*ppr_options & MSG_EXT_PPR_IU_REQ) == 0
&& *period < AHD_SYNCRATE_DT)
*period = AHD_SYNCRATE_DT;
/* Skip all DT only entries if DT is not available */
if ((*ppr_options & MSG_EXT_PPR_DT_REQ) == 0
&& *period < AHD_SYNCRATE_ULTRA2)
*period = AHD_SYNCRATE_ULTRA2;
}
/*
* Truncate the given synchronous offset to a value the
* current adapter type and syncrate are capable of.
*/
void
ahd_validate_offset(struct ahd_softc *ahd,
struct ahd_initiator_tinfo *tinfo,
u_int period, u_int *offset, int wide,
role_t role)
{
u_int maxoffset;
/* Limit offset to what we can do */
if (period == 0)
maxoffset = 0;
else if (period <= AHD_SYNCRATE_PACED) {
if ((ahd->bugs & AHD_PACED_NEGTABLE_BUG) != 0)
maxoffset = MAX_OFFSET_PACED_BUG;
else
maxoffset = MAX_OFFSET_PACED;
} else
maxoffset = MAX_OFFSET_NON_PACED;
*offset = MIN(*offset, maxoffset);
if (tinfo != NULL) {
if (role == ROLE_TARGET)
*offset = MIN(*offset, tinfo->user.offset);
else
*offset = MIN(*offset, tinfo->goal.offset);
}
}
/*
* Truncate the given transfer width parameter to a value the
* current adapter type is capable of.
*/
void
ahd_validate_width(struct ahd_softc *ahd, struct ahd_initiator_tinfo *tinfo,
u_int *bus_width, role_t role)
{
switch (*bus_width) {
default:
if (ahd->features & AHD_WIDE) {
/* Respond Wide */
*bus_width = MSG_EXT_WDTR_BUS_16_BIT;
break;
}
/* FALLTHROUGH */
case MSG_EXT_WDTR_BUS_8_BIT:
*bus_width = MSG_EXT_WDTR_BUS_8_BIT;
break;
}
if (tinfo != NULL) {
if (role == ROLE_TARGET)
*bus_width = MIN(tinfo->user.width, *bus_width);
else
*bus_width = MIN(tinfo->goal.width, *bus_width);
}
}
/*
* Update the bitmask of targets for which the controller should
* negotiate with at the next convenient oportunity. This currently
* means the next time we send the initial identify messages for
* a new transaction.
*/
int
ahd_update_neg_request(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
struct ahd_tmode_tstate *tstate,
struct ahd_initiator_tinfo *tinfo, ahd_neg_type neg_type)
{
u_int auto_negotiate_orig;
auto_negotiate_orig = tstate->auto_negotiate;
if (neg_type == AHD_NEG_ALWAYS) {
/*
* Force our "current" settings to be
* unknown so that unless a bus reset
* occurs the need to renegotiate is
* recorded persistently.
*/
if ((ahd->features & AHD_WIDE) != 0)
tinfo->curr.width = AHD_WIDTH_UNKNOWN;
tinfo->curr.period = AHD_PERIOD_UNKNOWN;
tinfo->curr.offset = AHD_OFFSET_UNKNOWN;
}
if (tinfo->curr.period != tinfo->goal.period
|| tinfo->curr.width != tinfo->goal.width
|| tinfo->curr.offset != tinfo->goal.offset
|| tinfo->curr.ppr_options != tinfo->goal.ppr_options
|| (neg_type == AHD_NEG_IF_NON_ASYNC
&& (tinfo->goal.offset != 0
|| tinfo->goal.width != MSG_EXT_WDTR_BUS_8_BIT
|| tinfo->goal.ppr_options != 0)))
tstate->auto_negotiate |= devinfo->target_mask;
else
tstate->auto_negotiate &= ~devinfo->target_mask;
return (auto_negotiate_orig != tstate->auto_negotiate);
}
/*
* Update the user/goal/curr tables of synchronous negotiation
* parameters as well as, in the case of a current or active update,
* any data structures on the host controller. In the case of an
* active update, the specified target is currently talking to us on
* the bus, so the transfer parameter update must take effect
* immediately.
*/
void
ahd_set_syncrate(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
u_int period, u_int offset, u_int ppr_options,
u_int type, int paused)
{
struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
u_int old_period;
u_int old_offset;
u_int old_ppr;
int active;
int update_needed;
active = (type & AHD_TRANS_ACTIVE) == AHD_TRANS_ACTIVE;
update_needed = 0;
if (period == 0 || offset == 0) {
period = 0;
offset = 0;
}
tinfo = ahd_fetch_transinfo(ahd, devinfo->channel, devinfo->our_scsiid,
devinfo->target, &tstate);
if ((type & AHD_TRANS_USER) != 0) {
tinfo->user.period = period;
tinfo->user.offset = offset;
tinfo->user.ppr_options = ppr_options;
}
if ((type & AHD_TRANS_GOAL) != 0) {
tinfo->goal.period = period;
tinfo->goal.offset = offset;
tinfo->goal.ppr_options = ppr_options;
}
old_period = tinfo->curr.period;
old_offset = tinfo->curr.offset;
old_ppr = tinfo->curr.ppr_options;
if ((type & AHD_TRANS_CUR) != 0
&& (old_period != period
|| old_offset != offset
|| old_ppr != ppr_options)) {
update_needed++;
tinfo->curr.period = period;
tinfo->curr.offset = offset;
tinfo->curr.ppr_options = ppr_options;
ahd_send_async(ahd, devinfo->channel, devinfo->target,
CAM_LUN_WILDCARD, AC_TRANSFER_NEG, NULL);
if (bootverbose) {
if (offset != 0) {
int options;
printf("%s: target %d synchronous with "
"period = 0x%x, offset = 0x%x",
ahd_name(ahd), devinfo->target,
period, offset);
options = 0;
if ((ppr_options & MSG_EXT_PPR_RD_STRM) != 0) {
printf("(RDSTRM");
options++;
}
if ((ppr_options & MSG_EXT_PPR_DT_REQ) != 0) {
printf("%s", options ? "|DT" : "(DT");
options++;
}
if ((ppr_options & MSG_EXT_PPR_IU_REQ) != 0) {
printf("%s", options ? "|IU" : "(IU");
options++;
}
if ((ppr_options & MSG_EXT_PPR_RTI) != 0) {
printf("%s", options ? "|RTI" : "(RTI");
options++;
}
if ((ppr_options & MSG_EXT_PPR_QAS_REQ) != 0) {
printf("%s", options ? "|QAS" : "(QAS");
options++;
}
if (options != 0)
printf(")\n");
else
printf("\n");
} else {
printf("%s: target %d using "
"asynchronous transfers%s\n",
ahd_name(ahd), devinfo->target,
(ppr_options & MSG_EXT_PPR_QAS_REQ) != 0
? "(QAS)" : "");
}
}
}
/*
* Always refresh the neg-table to handle the case of the
* sequencer setting the ENATNO bit for a MK_MESSAGE request.
* We will always renegotiate in that case if this is a
* packetized request. Also manage the busfree expected flag
* from this common routine so that we catch changes due to
* WDTR or SDTR messages.
*/
if ((type & AHD_TRANS_CUR) != 0) {
if (!paused)
ahd_pause(ahd);
ahd_update_neg_table(ahd, devinfo, &tinfo->curr);
if (!paused)
ahd_unpause(ahd);
if (ahd->msg_type != MSG_TYPE_NONE) {
if ((old_ppr & MSG_EXT_PPR_IU_REQ)
!= (ppr_options & MSG_EXT_PPR_IU_REQ)) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0) {
ahd_print_devinfo(ahd, devinfo);
printf("Expecting IU Change busfree\n");
}
#endif
ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE
| MSG_FLAG_IU_REQ_CHANGED;
}
if ((old_ppr & MSG_EXT_PPR_IU_REQ) != 0) {
#ifdef AHD_DEBUG
if ((ahd_debug & AHD_SHOW_MESSAGES) != 0)
printf("PPR with IU_REQ outstanding\n");
#endif
ahd->msg_flags |= MSG_FLAG_EXPECT_PPR_BUSFREE;
}
}
}
update_needed += ahd_update_neg_request(ahd, devinfo, tstate,
tinfo, AHD_NEG_TO_GOAL);
if (update_needed && active)
ahd_update_pending_scbs(ahd);
}
/*
* Update the user/goal/curr tables of wide negotiation
* parameters as well as, in the case of a current or active update,
* any data structures on the host controller. In the case of an
* active update, the specified target is currently talking to us on
* the bus, so the transfer parameter update must take effect
* immediately.
*/
void
ahd_set_width(struct ahd_softc *ahd, struct ahd_devinfo *devinfo,
u_int width<