/* * Core routines and tables shareable across OS platforms. * * Copyright (c) 1994-2002 Justin T. Gibbs. * Copyright (c) 2000-2002 Adaptec Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * 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/aic7xxx.c#155 $
*/
staticconststruct ahc_phase_table_entry ahc_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.
*/ staticconst u_int num_phases = ARRAY_SIZE(ahc_phase_table) - 1;
/* * Block our completion routine from starting the next untagged * transaction for this target or target lun.
*/ staticinlinevoid
ahc_freeze_untagged_queues(struct ahc_softc *ahc)
{ if ((ahc->flags & AHC_SCB_BTT) == 0)
ahc->untagged_queue_lock++;
}
/* * Allow the next untagged transaction for this target or target lun * to be executed. We use a counting semaphore to allow the lock * to be acquired recursively. Once the count drops to zero, the * transaction queues will be run.
*/ staticinlinevoid
ahc_release_untagged_queues(struct ahc_softc *ahc)
{ if ((ahc->flags & AHC_SCB_BTT) == 0) {
ahc->untagged_queue_lock--; if (ahc->untagged_queue_lock == 0)
ahc_run_untagged_queues(ahc);
}
}
/************************* Sequencer Execution Control ************************/ /* * Work around any chip bugs related to halting sequencer execution. * On Ultra2 controllers, we must clear the CIOBUS stretch signal by * reading a register that will set this signal and deassert it. * Without this workaround, if the chip is paused, by an interrupt or * manual pause while accessing scb ram, accesses to certain registers * will hang the system (infinite pci retries).
*/ staticvoid
ahc_pause_bug_fix(struct ahc_softc *ahc)
{ if ((ahc->features & AHC_ULTRA2) != 0)
(void)ahc_inb(ahc, CCSCBCTL);
}
/* * Determine whether the sequencer has halted code execution. * Returns non-zero status if the sequencer is stopped.
*/ int
ahc_is_paused(struct ahc_softc *ahc)
{ return ((ahc_inb(ahc, HCNTRL) & PAUSE) != 0);
}
/* * Request that the sequencer stop and wait, indefinitely, for it * to stop. The sequencer will only acknowledge that it is paused * once it has reached an instruction boundary and PAUSEDIS is * cleared in the SEQCTL register. The sequencer may use PAUSEDIS * for critical sections.
*/ void
ahc_pause(struct ahc_softc *ahc)
{
ahc_outb(ahc, HCNTRL, ahc->pause);
/* * Since the sequencer can disable pausing in a critical section, we * must loop until it actually stops.
*/ while (ahc_is_paused(ahc) == 0)
;
ahc_pause_bug_fix(ahc);
}
/* * Allow the sequencer to continue program execution. * We check here to ensure that no additional interrupt * sources that would cause the sequencer to halt have been * asserted. If, for example, a SCSI bus reset is detected * while we are fielding a different, pausing, interrupt type, * we don't want to release the sequencer before going back * into our interrupt handler and dealing with this new * condition.
*/ void
ahc_unpause(struct ahc_softc *ahc)
{ if ((ahc_inb(ahc, INTSTAT) & (SCSIINT | SEQINT | BRKADRINT)) == 0)
ahc_outb(ahc, HCNTRL, ahc->unpause);
}
/* * Return pointers to the transfer negotiation information * for the specified our_id/remote_id pair.
*/ struct ahc_initiator_tinfo *
ahc_fetch_transinfo(struct ahc_softc *ahc, char channel, u_int our_id,
u_int remote_id, struct ahc_tmode_tstate **tstate)
{ /* * Transfer data structures are stored from the perspective * of the target role. Since the parameters for a connection * in the initiator role to a given target are the same as * when the roles are reversed, we pretend we are the target.
*/ if (channel == 'B')
our_id += 8;
*tstate = ahc->enabled_targets[our_id]; return (&(*tstate)->transinfo[remote_id]);
}
uint16_t
ahc_inw(struct ahc_softc *ahc, u_int port)
{
uint16_t r = ahc_inb(ahc, port+1) << 8; return r | ahc_inb(ahc, port);
}
/* * Our queuing method is a bit tricky. The card * knows in advance which HSCB to download, and we * can't disappoint it. To achieve this, the next * SCB to download is saved off in ahc->next_queued_scb. * When we are called to queue "an arbitrary scb", * we copy the contents of the incoming HSCB to the one * the sequencer knows about, swap HSCB pointers and * finally assign the SCB to the tag indexed location * in the scb_array. This makes sure that we can still * locate the correct SCB by SCB_TAG.
*/
q_hscb = ahc->next_queued_scb->hscb;
saved_tag = q_hscb->tag;
memcpy(q_hscb, scb->hscb, sizeof(*scb->hscb)); if ((scb->flags & SCB_CDB32_PTR) != 0) {
q_hscb->shared_data.cdb_ptr =
ahc_htole32(ahc_hscb_busaddr(ahc, q_hscb->tag)
+ offsetof(struct hardware_scb, cdb32));
}
q_hscb->tag = saved_tag;
q_hscb->next = scb->hscb->tag;
/* Now define the mapping from tag to SCB in the scbindex */
ahc->scb_data->scbindex[scb->hscb->tag] = scb;
}
/* * Tell the sequencer about a new transaction to execute.
*/ void
ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
{
ahc_swap_with_next_hscb(ahc, scb);
if (scb->hscb->tag == SCB_LIST_NULL
|| scb->hscb->next == SCB_LIST_NULL)
panic("Attempt to queue invalid SCB tag %x:%x\n",
scb->hscb->tag, scb->hscb->next);
/* * Setup data "oddness".
*/
scb->hscb->lun &= LID; if (ahc_get_transfer_length(scb) & 0x1)
scb->hscb->lun |= SCB_XFERLEN_ODD;
/* * Keep a history of SCBs we've downloaded in the qinfifo.
*/
ahc->qinfifo[ahc->qinfifonext++] = scb->hscb->tag;
/* * Make sure our data is consistent from the * perspective of the adapter.
*/
ahc_sync_scb(ahc, scb, BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE);
/* Tell the adapter about the newly queued SCB */ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
ahc_outb(ahc, HNSCB_QOFF, ahc->qinfifonext);
} else { if ((ahc->features & AHC_AUTOPAUSE) == 0)
ahc_pause(ahc);
ahc_outb(ahc, KERNEL_QINPOS, ahc->qinfifonext); if ((ahc->features & AHC_AUTOPAUSE) == 0)
ahc_unpause(ahc);
}
}
/* * Catch an interrupt from the adapter
*/ int
ahc_intr(struct ahc_softc *ahc)
{
u_int intstat;
if ((ahc->pause & INTEN) == 0) { /* * Our interrupt is not enabled on the chip * and may be disabled for re-entrancy reasons, * so just return. This is likely just a shared * interrupt.
*/ return (0);
} /* * Instead of directly reading the interrupt status register, * infer the cause of the interrupt by checking our in-core * completion queues. This avoids a costly PCI bus read in * most cases.
*/ if ((ahc->flags & (AHC_ALL_INTERRUPTS|AHC_EDGE_INTERRUPT)) == 0
&& (ahc_check_cmdcmpltqueues(ahc) != 0))
intstat = CMDCMPLT; else {
intstat = ahc_inb(ahc, INTSTAT);
}
if (intstat & CMDCMPLT) {
ahc_outb(ahc, CLRINT, CLRCMDINT);
/* * Ensure that the chip sees that we've cleared * this interrupt before we walk the output fifo. * Otherwise, we may, due to posted bus writes, * clear the interrupt after we finish the scan, * and after the sequencer has added new entries * and asserted the interrupt again.
*/
ahc_flush_device_writes(ahc);
ahc_run_qoutfifo(ahc); #ifdef AHC_TARGET_MODE if ((ahc->flags & AHC_TARGETROLE) != 0)
ahc_run_tqinfifo(ahc, /*paused*/FALSE); #endif
}
/* * Handle statuses that may invalidate our cached * copy of INTSTAT separately.
*/ if (intstat == 0xFF && (ahc->features & AHC_REMOVABLE) != 0) { /* Hot eject. Do nothing */
} elseif (intstat & BRKADRINT) {
ahc_handle_brkadrint(ahc);
} elseif ((intstat & (SEQINT|SCSIINT)) != 0) {
ahc_pause_bug_fix(ahc);
if ((intstat & SEQINT) != 0)
ahc_handle_seqint(ahc, intstat);
/************************* Sequencer Execution Control ************************/ /* * Restart the sequencer program from address zero
*/ staticvoid
ahc_restart(struct ahc_softc *ahc)
{
uint8_t sblkctl;
ahc_pause(ahc);
/* No more pending messages. */
ahc_clear_msg_state(ahc);
/* * Ensure that the sequencer's idea of TQINPOS * matches our own. The sequencer increments TQINPOS * only after it sees a DMA complete and a reset could * occur before the increment leaving the kernel to believe * the command arrived but the sequencer to not.
*/
ahc_outb(ahc, TQINPOS, ahc->tqinfifonext);
/* Always allow reselection */
ahc_outb(ahc, SCSISEQ,
ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP)); if ((ahc->features & AHC_CMD_CHAN) != 0) { /* Ensure that no DMA operations are in progress */
ahc_outb(ahc, CCSCBCNT, 0);
ahc_outb(ahc, CCSGCTL, 0);
ahc_outb(ahc, CCSCBCTL, 0);
} /* * If we were in the process of DMA'ing SCB data into * an SCB, replace that SCB on the free list. This prevents * an SCB leak.
*/ if ((ahc_inb(ahc, SEQ_FLAGS2) & SCB_DMA) != 0) {
ahc_add_curscb_to_free_list(ahc);
ahc_outb(ahc, SEQ_FLAGS2,
ahc_inb(ahc, SEQ_FLAGS2) & ~SCB_DMA);
}
/* * Clear any pending sequencer interrupt. It is no * longer relevant since we're resetting the Program * Counter.
*/
ahc_outb(ahc, CLRINT, CLRSEQINT);
/* * Take the LED out of diagnostic mode on PM resume, too
*/
sblkctl = ahc_inb(ahc, SBLKCTL);
ahc_outb(ahc, SBLKCTL, (sblkctl & ~(DIAGLEDEN|DIAGLEDON)));
/* * Clear 32bits of QOUTFIFO at a time * so that we don't clobber an incoming * byte DMA to the array on architectures * that only support 32bit load and store * operations.
*/
modnext = ahc->qoutfifonext & ~0x3;
*((uint32_t *)(&ahc->qoutfifo[modnext])) = 0xFFFFFFFFUL;
ahc_dmamap_sync(ahc, ahc->shared_data_dmat,
ahc->shared_data_dmamap, /*offset*/modnext, /*len*/4,
BUS_DMASYNC_PREREAD);
}
ahc->qoutfifonext++;
scb = ahc_lookup_scb(ahc, scb_index); if (scb == NULL) {
printk("%s: WARNING no command for scb %d " "(cmdcmplt)\nQOUTPOS = %d\n",
ahc_name(ahc), scb_index,
(ahc->qoutfifonext - 1) & 0xFF); continue;
}
/* * Save off the residual * if there is one.
*/
ahc_update_residual(ahc, scb);
ahc_done(ahc, scb);
}
}
staticvoid
ahc_run_untagged_queues(struct ahc_softc *ahc)
{ int i;
for (i = 0; i < 16; i++)
ahc_run_untagged_queue(ahc, &ahc->untagged_queues[i]);
}
/************************* Interrupt Handling *********************************/ staticvoid
ahc_handle_brkadrint(struct ahc_softc *ahc)
{ /* * We upset the sequencer :-( * Lookup the error message
*/ int i; int error;
error = ahc_inb(ahc, ERROR); for (i = 0; error != 1 && i < num_errors; i++)
error >>= 1;
printk("%s: brkadrint, %s at seqaddr = 0x%x\n",
ahc_name(ahc), ahc_hard_errors[i].errmesg,
ahc_inb(ahc, SEQADDR0) |
(ahc_inb(ahc, SEQADDR1) << 8));
ahc_dump_card_state(ahc);
/* Tell everyone that this HBA is no longer available */
ahc_abort_scbs(ahc, CAM_TARGET_WILDCARD, ALL_CHANNELS,
CAM_LUN_WILDCARD, SCB_LIST_NULL, ROLE_UNKNOWN,
CAM_NO_HBA);
/* Disable all interrupt sources by resetting the controller */
ahc_shutdown(ahc);
}
/* * Clear the upper byte that holds SEQINT status * codes and clear the SEQINT bit. We will unpause * the sequencer, if appropriate, after servicing * the request.
*/
ahc_outb(ahc, CLRINT, CLRSEQINT); switch (intstat & SEQINT_MASK) { case BAD_STATUS:
{
u_int scb_index; struct hardware_scb *hscb;
/* * Set the default return value to 0 (don't * send sense). The sense code will change * this if needed.
*/
ahc_outb(ahc, RETURN_1, 0);
/* * The sequencer will notify us when a command * has an error that would be of interest to * the kernel. This allows us to leave the sequencer * running in the common case of command completes * without error. The sequencer will already have * dma'd the SCB back up to us, so we can reference * the in kernel copy directly.
*/
scb_index = ahc_inb(ahc, SCB_TAG);
scb = ahc_lookup_scb(ahc, scb_index); if (scb == NULL) {
ahc_print_devinfo(ahc, &devinfo);
printk("ahc_intr - referenced scb " "not valid during seqint 0x%x scb(%d)\n",
intstat, scb_index);
ahc_dump_card_state(ahc);
panic("for safety"); goto unpause;
}
hscb = scb->hscb;
/* Don't want to clobber the original sense code */ if ((scb->flags & SCB_SENSE) != 0) { /* * Clear the SCB_SENSE Flag and have * the sequencer do a normal command * complete.
*/
scb->flags &= ~SCB_SENSE;
ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL); break;
}
ahc_set_transaction_status(scb, CAM_SCSI_STATUS_ERROR); /* Freeze the queue until the client sees the error. */
ahc_freeze_devq(ahc, scb);
ahc_freeze_scb(scb);
ahc_set_scsi_status(scb, hscb->shared_data.status.scsi_status); switch (hscb->shared_data.status.scsi_status) { case SAM_STAT_GOOD:
printk("%s: Interrupted for status of 0???\n",
ahc_name(ahc)); break; case SAM_STAT_COMMAND_TERMINATED: case SAM_STAT_CHECK_CONDITION:
{ struct ahc_dma_seg *sg; struct scsi_sense *sc; struct ahc_initiator_tinfo *targ_info; struct ahc_tmode_tstate *tstate; struct ahc_transinfo *tinfo; #ifdef AHC_DEBUG if (ahc_debug & AHC_SHOW_SENSE) {
ahc_print_path(ahc, scb);
printk("SCB %d: requests Check Status\n",
scb->hscb->tag);
} #endif
if (ahc_perform_autosense(scb) == 0) break;
targ_info = ahc_fetch_transinfo(ahc,
devinfo.channel,
devinfo.our_scsiid,
devinfo.target,
&tstate);
tinfo = &targ_info->curr;
sg = scb->sg_list;
sc = (struct scsi_sense *)(&hscb->shared_data.cdb); /* * Save off the residual if there is one.
*/
ahc_update_residual(ahc, scb); #ifdef AHC_DEBUG if (ahc_debug & AHC_SHOW_SENSE) {
ahc_print_path(ahc, scb);
printk("Sending Sense\n");
} #endif
sg->addr = ahc_get_sense_bufaddr(ahc, scb);
sg->len = ahc_get_sense_bufsize(ahc, scb);
sg->len |= AHC_DMA_LAST_SEG;
/* * We can't allow the target to disconnect. * This will be an untagged transaction and * having the target disconnect will make this * transaction indestinguishable from outstanding * tagged transactions.
*/
hscb->control = 0;
/* * This request sense could be because the * the device lost power or in some other * way has lost our transfer negotiations. * Renegotiate if appropriate. Unit attention * errors will be reported before any data * phases occur.
*/ if (ahc_get_residual(scb)
== ahc_get_transfer_length(scb)) {
ahc_update_neg_request(ahc, &devinfo,
tstate, targ_info,
AHC_NEG_IF_NON_ASYNC);
} if (tstate->auto_negotiate & devinfo.target_mask) {
hscb->control |= MK_MESSAGE;
scb->flags &= ~SCB_NEGOTIATE;
scb->flags |= SCB_AUTO_NEGOTIATE;
}
hscb->cdb_len = sizeof(*sc);
hscb->dataptr = sg->addr;
hscb->datacnt = sg->len;
hscb->sgptr = scb->sg_list_phys | SG_FULL_RESID;
hscb->sgptr = ahc_htole32(hscb->sgptr);
scb->sg_count = 1;
scb->flags |= SCB_SENSE;
ahc_qinfifo_requeue_tail(ahc, scb);
ahc_outb(ahc, RETURN_1, SEND_SENSE); /* * Ensure we have enough time to actually * retrieve the sense.
*/
ahc_scb_timer_reset(scb, 5 * 1000000); break;
} default: break;
} break;
} case NO_MATCH:
{ /* Ensure we don't leave the selection hardware on */
ahc_outb(ahc, SCSISEQ,
ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
lastphase = ahc_inb(ahc, LASTPHASE);
printk("%s:%c:%d: unknown scsi bus phase %x, " "lastphase = 0x%x. Attempting to continue\n",
ahc_name(ahc), devinfo.channel, devinfo.target,
lastphase, ahc_inb(ahc, SCSISIGI)); break;
} case MISSED_BUSFREE:
{
u_int lastphase;
lastphase = ahc_inb(ahc, LASTPHASE);
printk("%s:%c:%d: Missed busfree. " "Lastphase = 0x%x, Curphase = 0x%x\n",
ahc_name(ahc), devinfo.channel, devinfo.target,
lastphase, ahc_inb(ahc, SCSISIGI));
ahc_restart(ahc); return;
} case HOST_MSG_LOOP:
{ /* * 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.
*/ if (ahc->msg_type == MSG_TYPE_NONE) { struct scb *scb;
u_int scb_index;
u_int bus_phase;
bus_phase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK; if (bus_phase != P_MESGIN
&& bus_phase != P_MESGOUT) {
printk("ahc_intr: HOST_MSG_LOOP bad " "phase 0x%x\n",
bus_phase); /* * Probably transitioned to bus free before * we got here. Just punt the message.
*/
ahc_clear_intstat(ahc);
ahc_restart(ahc); return;
}
scb_index = ahc_inb(ahc, SCB_TAG);
scb = ahc_lookup_scb(ahc, scb_index); if (devinfo.role == ROLE_INITIATOR) { if (bus_phase == P_MESGOUT) { if (scb == NULL)
panic("HOST_MSG_LOOP with " "invalid SCB %x\n",
scb_index);
ahc_handle_message_phase(ahc); break;
} case PERR_DETECTED:
{ /* * If we've cleared the parity error interrupt * but the sequencer still believes that SCSIPERR * is true, it must be that the parity error is * for the currently presented byte on the bus, * and we are not in a phase (data-in) where we will * eventually ack this byte. Ack the byte and * throw it away in the hope that the target will * take us to message out to deliver the appropriate * error message.
*/ if ((intstat & SCSIINT) == 0
&& (ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0) {
if ((ahc->features & AHC_DT) == 0) {
u_int curphase;
/* * The hardware will only let you ack bytes * if the expected phase in SCSISIGO matches * the current phase. Make sure this is * currently the case.
*/
curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
ahc_outb(ahc, LASTPHASE, curphase);
ahc_outb(ahc, SCSISIGO, curphase);
} if ((ahc_inb(ahc, SCSISIGI) & (CDI|MSGI)) == 0) { int wait;
/* * In a data phase. Faster to bitbucket * the data than to individually ack each * byte. This is also the only strategy * that will work with AUTOACK enabled.
*/
ahc_outb(ahc, SXFRCTL1,
ahc_inb(ahc, SXFRCTL1) | BITBUCKET);
wait = 5000; while (--wait != 0) { if ((ahc_inb(ahc, SCSISIGI)
& (CDI|MSGI)) != 0) break;
ahc_delay(100);
}
ahc_outb(ahc, SXFRCTL1,
ahc_inb(ahc, SXFRCTL1) & ~BITBUCKET); if (wait == 0) { struct scb *scb;
u_int scb_index;
ahc_print_devinfo(ahc, &devinfo);
printk("Unable to clear parity error. " "Resetting bus.\n");
scb_index = ahc_inb(ahc, SCB_TAG);
scb = ahc_lookup_scb(ahc, scb_index); if (scb != NULL)
ahc_set_transaction_status(scb,
CAM_UNCOR_PARITY);
ahc_reset_channel(ahc, devinfo.channel, /*init reset*/TRUE);
}
} else {
ahc_inb(ahc, SCSIDATL);
}
} break;
} case DATA_OVERRUN:
{ /* * When the sequencer detects an overrun, it * places the controller in "BITBUCKET" mode * and allows the target to complete its transfer. * Unfortunately, none of the counters get updated * when the controller is in this mode, so we have * no way of knowing how large the overrun was.
*/
u_int scbindex = ahc_inb(ahc, SCB_TAG);
u_int lastphase = ahc_inb(ahc, LASTPHASE);
u_int i;
scb = ahc_lookup_scb(ahc, scbindex); for (i = 0; i < num_phases; i++) { if (lastphase == ahc_phase_table[i].phase) break;
}
ahc_print_path(ahc, scb);
printk("data overrun detected %s." " Tag == 0x%x.\n",
ahc_phase_table[i].phasemsg,
scb->hscb->tag);
ahc_print_path(ahc, scb);
printk("%s seen Data Phase. Length = %ld. NumSGs = %d.\n",
ahc_inb(ahc, SEQ_FLAGS) & DPHASE ? "Have" : "Haven't",
ahc_get_transfer_length(scb), scb->sg_count); if (scb->sg_count > 0) { for (i = 0; i < scb->sg_count; i++) {
printk("sg[%d] - Addr 0x%x%x : Length %d\n",
i,
(ahc_le32toh(scb->sg_list[i].len) >> 24
& SG_HIGH_ADDR_BITS),
ahc_le32toh(scb->sg_list[i].addr),
ahc_le32toh(scb->sg_list[i].len)
& AHC_SG_LEN_MASK);
}
} /* * Set this and it will take effect when the * target does a command complete.
*/
ahc_freeze_devq(ahc, scb); if ((scb->flags & SCB_SENSE) == 0) {
ahc_set_transaction_status(scb, CAM_DATA_RUN_ERR);
} else {
scb->flags &= ~SCB_SENSE;
ahc_set_transaction_status(scb, CAM_AUTOSENSE_FAIL);
}
ahc_freeze_scb(scb);
if ((ahc->features & AHC_ULTRA2) != 0) { /* * Clear the channel in case we return * to data phase later.
*/
ahc_outb(ahc, SXFRCTL0,
ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
ahc_outb(ahc, SXFRCTL0,
ahc_inb(ahc, SXFRCTL0) | CLRSTCNT|CLRCHN);
} if ((ahc->flags & AHC_39BIT_ADDRESSING) != 0) {
u_int dscommand1;
if ((ahc->features & AHC_ULTRA2) != 0
&& (status0 & IOERR) != 0) { int now_lvd;
now_lvd = ahc_inb(ahc, SBLKCTL) & ENAB40;
printk("%s: Transceiver State Has Changed to %s mode\n",
ahc_name(ahc), now_lvd ? "LVD" : "SE");
ahc_outb(ahc, CLRSINT0, CLRIOERR); /* * When transitioning to SE mode, the reset line * glitches, triggering an arbitration bug in some * Ultra2 controllers. This bug is cleared when we * assert the reset line. Since a reset glitch has * already occurred with this transition and a * transceiver state change is handled just like * a bus reset anyway, asserting the reset line * ourselves is safe.
*/
ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/now_lvd == 0);
} elseif ((status & SCSIRSTI) != 0) {
printk("%s: Someone reset channel %c\n",
ahc_name(ahc), intr_channel); if (intr_channel != cur_channel)
ahc_outb(ahc, SBLKCTL, ahc_inb(ahc, SBLKCTL) ^ SELBUSB);
ahc_reset_channel(ahc, intr_channel, /*Initiate Reset*/FALSE);
} elseif ((status & SCSIPERR) != 0) { /* * Determine the bus phase and queue an appropriate message. * SCSIPERR is latched true as soon as a parity error * occurs. If the sequencer acked the transfer that * caused the parity error and the currently presented * transfer on the bus has correct parity, SCSIPERR will * be cleared by CLRSCSIPERR. Use this to determine if * we should look at the last phase the sequencer recorded, * or the current phase presented on the bus.
*/ struct ahc_devinfo devinfo;
u_int mesg_out;
u_int curphase;
u_int errorphase;
u_int lastphase;
u_int scsirate;
u_int i;
u_int sstat2; int silent;
lastphase = ahc_inb(ahc, LASTPHASE);
curphase = ahc_inb(ahc, SCSISIGI) & PHASE_MASK;
sstat2 = ahc_inb(ahc, SSTAT2);
ahc_outb(ahc, CLRSINT1, CLRSCSIPERR); /* * For all phases save DATA, the sequencer won't * automatically ack a byte that has a parity error * in it. So the only way that the current phase * could be 'data-in' is if the parity error is for * an already acked byte in the data phase. During * synchronous data-in transfers, we may actually * ack bytes before latching the current phase in * LASTPHASE, leading to the discrepancy between * curphase and lastphase.
*/ if ((ahc_inb(ahc, SSTAT1) & SCSIPERR) != 0
|| curphase == P_DATAIN || curphase == P_DATAIN_DT)
errorphase = curphase; else
errorphase = lastphase;
for (i = 0; i < num_phases; i++) { if (errorphase == ahc_phase_table[i].phase) break;
}
mesg_out = ahc_phase_table[i].mesg_out;
silent = FALSE; if (scb != NULL) { if (SCB_IS_SILENT(scb))
silent = TRUE; else
ahc_print_path(ahc, scb);
scb->flags |= SCB_TRANSMISSION_ERROR;
} else
printk("%s:%c:%d: ", ahc_name(ahc), intr_channel,
SCSIID_TARGET(ahc, ahc_inb(ahc, SAVED_SCSIID)));
scsirate = ahc_inb(ahc, SCSIRATE); if (silent == FALSE) {
printk("parity error detected %s. " "SEQADDR(0x%x) SCSIRATE(0x%x)\n",
ahc_phase_table[i].phasemsg,
ahc_inw(ahc, SEQADDR0),
scsirate); if ((ahc->features & AHC_DT) != 0) { if ((sstat2 & CRCVALERR) != 0)
printk("\tCRC Value Mismatch\n"); if ((sstat2 & CRCENDERR) != 0)
printk("\tNo terminal CRC packet " "received\n"); if ((sstat2 & CRCREQERR) != 0)
printk("\tIllegal CRC packet " "request\n"); if ((sstat2 & DUAL_EDGE_ERR) != 0)
printk("\tUnexpected %sDT Data Phase\n",
(scsirate & SINGLE_EDGE)
? "" : "non-");
}
}
if ((ahc->features & AHC_DT) != 0
&& (sstat2 & DUAL_EDGE_ERR) != 0) { /* * This error applies regardless of * data direction, so ignore the value * in the phase table.
*/
mesg_out = INITIATOR_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.
*/ if (mesg_out != NOP) { if (ahc->msg_type != MSG_TYPE_NONE)
ahc->send_msg_perror = TRUE; else
ahc_outb(ahc, MSG_OUT, mesg_out);
} /* * Force a renegotiation with this target just in * case we are out of sync for some external reason * unknown (or unreported) by the target.
*/
ahc_fetch_devinfo(ahc, &devinfo);
ahc_force_renegotiation(ahc, &devinfo);
/* * Although the driver does not care about the * 'Selection in Progress' status bit, the busy * LED does. SELINGO is only cleared by a successful * selection, so we must manually clear it to insure * the LED turns off just incase no future successful * selections occur (e.g. no devices on the bus).
*/
ahc_outb(ahc, CLRSINT0, CLRSELINGO);
/* * Cancel any pending transactions on the device * now that it seems to be missing. This will * also revert us to async/narrow transfers until * we can renegotiate with the device.
*/
ahc_handle_devreset(ahc, &devinfo,
CAM_SEL_TIMEOUT, "Selection Timeout", /*verbose_level*/1);
}
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_restart(ahc);
} elseif ((status & BUSFREE) != 0
&& (ahc_inb(ahc, SIMODE1) & ENBUSFREE) != 0) { struct ahc_devinfo devinfo;
u_int lastphase;
u_int saved_scsiid;
u_int saved_lun;
u_int target;
u_int initiator_role_id; char channel; int printerror;
/* * Clear our selection hardware as soon as possible. * We may have an entry in the waiting Q for this target, * that is affected by this busfree and we don't want to * go about selecting the target while we handle the event.
*/
ahc_outb(ahc, SCSISEQ,
ahc_inb(ahc, SCSISEQ) & (ENSELI|ENRSELI|ENAUTOATNP));
/* * Disable busfree interrupts and clear the busfree * interrupt status. We do this here so that several * bus transactions occur prior to clearing the SCSIINT * latch. It can take a bit for the clearing to take effect.
*/
ahc_outb(ahc, SIMODE1, ahc_inb(ahc, SIMODE1) & ~ENBUSFREE);
ahc_outb(ahc, CLRSINT1, CLRBUSFREE|CLRSCSIPERR);
/* * 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 = ahc_inb(ahc, LASTPHASE);
saved_scsiid = ahc_inb(ahc, SAVED_SCSIID);
saved_lun = ahc_inb(ahc, SAVED_LUN);
target = SCSIID_TARGET(ahc, saved_scsiid);
initiator_role_id = SCSIID_OUR_ID(saved_scsiid);
channel = SCSIID_CHANNEL(ahc, saved_scsiid);
ahc_compile_devinfo(&devinfo, initiator_role_id,
target, saved_lun, channel, ROLE_INITIATOR);
printerror = 1;
if ((scb->hscb->control & TAG_ENB) != 0)
tag = scb->hscb->tag; else
tag = SCB_LIST_NULL;
ahc_print_path(ahc, scb);
ahc_abort_scbs(ahc, target, channel,
SCB_GET_LUN(scb), tag,
ROLE_INITIATOR,
CAM_UNEXP_BUSFREE);
} else { /* * We had not fully identified this connection, * so we cannot abort anything.
*/
printk("%s: ", ahc_name(ahc));
} for (i = 0; i < num_phases; i++) { if (lastphase == ahc_phase_table[i].phase) break;
} if (lastphase != P_BUSFREE) { /* * Renegotiate with this device at the * next opportunity just in case this busfree * is due to a negotiation mismatch with the * device.
*/
ahc_force_renegotiation(ahc, &devinfo);
}
printk("Unexpected busfree %s\n" "SEQADDR == 0x%x\n",
ahc_phase_table[i].phasemsg,
ahc_inb(ahc, SEQADDR0)
| (ahc_inb(ahc, SEQADDR1) << 8));
}
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_restart(ahc);
} else {
printk("%s: Missing case in ahc_handle_scsiint. status = %x\n",
ahc_name(ahc), status);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
}
}
/* * Force renegotiation to occur the next time we initiate * a command to the current device.
*/ staticvoid
ahc_force_renegotiation(struct ahc_softc *ahc, struct ahc_devinfo *devinfo)
{ struct ahc_initiator_tinfo *targ_info; struct ahc_tmode_tstate *tstate;
/* * Seqaddr represents the next instruction to execute, * so we are really executing the instruction just * before it.
*/ if (seqaddr != 0)
seqaddr -= 1;
cs = ahc->critical_sections; for (i = 0; i < ahc->num_critical_sections; i++, cs++) { if (cs->begin < seqaddr && cs->end >= seqaddr) break;
}
if (i == ahc->num_critical_sections) break;
if (steps > AHC_MAX_STEPS) {
printk("%s: Infinite loop in critical section\n",
ahc_name(ahc));
ahc_dump_card_state(ahc);
panic("critical section loop");
}
steps++; if (stepping == FALSE) {
/* * Disable all interrupt sources so that the * sequencer will not be stuck by a pausing * interrupt condition while we attempt to * leave a critical section.
*/
simode0 = ahc_inb(ahc, SIMODE0);
ahc_outb(ahc, SIMODE0, 0);
simode1 = ahc_inb(ahc, SIMODE1); if ((ahc->features & AHC_DT) != 0) /* * On DT class controllers, we * use the enhanced busfree logic. * Unfortunately we cannot re-enable * busfree detection within the * current connection, so we must * leave it on while single stepping.
*/
ahc_outb(ahc, SIMODE1, simode1 & ENBUSFREE); else
ahc_outb(ahc, SIMODE1, 0);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_outb(ahc, SEQCTL, ahc->seqctl | STEP);
stepping = TRUE;
} if ((ahc->features & AHC_DT) != 0) {
ahc_outb(ahc, CLRSINT1, CLRBUSFREE);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
}
ahc_outb(ahc, HCNTRL, ahc->unpause); while (!ahc_is_paused(ahc))
ahc_delay(200);
} if (stepping) {
ahc_outb(ahc, SIMODE0, simode0);
ahc_outb(ahc, SIMODE1, simode1);
ahc_outb(ahc, SEQCTL, ahc->seqctl);
}
}
/* * Clear any pending interrupt status.
*/ staticvoid
ahc_clear_intstat(struct ahc_softc *ahc)
{ /* Clear any interrupt conditions this may have caused */
ahc_outb(ahc, CLRSINT1, CLRSELTIMEO|CLRATNO|CLRSCSIRSTI
|CLRBUSFREE|CLRSCSIPERR|CLRPHASECHG|
CLRREQINIT);
ahc_flush_device_writes(ahc);
ahc_outb(ahc, CLRSINT0, CLRSELDO|CLRSELDI|CLRSELINGO);
ahc_flush_device_writes(ahc);
ahc_outb(ahc, CLRINT, CLRSCSIINT);
ahc_flush_device_writes(ahc);
}
printk("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);
printk("Shared Data: "); for (i = 0; i < sizeof(hscb->shared_data.cdb); i++)
printk("%#02x", hscb->shared_data.cdb[i]);
printk(" dataptr:%#x datacnt:%#x sgptr:%#x tag:%#x\n",
ahc_le32toh(hscb->dataptr),
ahc_le32toh(hscb->datacnt),
ahc_le32toh(hscb->sgptr),
hscb->tag); if (scb->sg_count > 0) { for (i = 0; i < scb->sg_count; i++) {
printk("sg[%d] - Addr 0x%x%x : Length %d\n",
i,
(ahc_le32toh(scb->sg_list[i].len) >> 24
& SG_HIGH_ADDR_BITS),
ahc_le32toh(scb->sg_list[i].addr),
ahc_le32toh(scb->sg_list[i].len));
}
}
} #endif
/************************* Transfer Negotiation *******************************/ /* * Allocate per target mode instance (ID we respond to as a target) * transfer negotiation data structures.
*/ staticstruct ahc_tmode_tstate *
ahc_alloc_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel)
{ struct ahc_tmode_tstate *master_tstate; struct ahc_tmode_tstate *tstate; int i;
/* * If we have allocated a master tstate, copy user settings from * the master tstate (taken from SRAM or the EEPROM) for this * channel, but reset our current and goal settings to async/narrow * until an initiator talks to us.
*/ if (master_tstate != NULL) {
memcpy(tstate, master_tstate, sizeof(*tstate));
memset(tstate->enabled_luns, 0, sizeof(tstate->enabled_luns));
tstate->ultraenb = 0; for (i = 0; i < AHC_NUM_TARGETS; i++) {
memset(&tstate->transinfo[i].curr, 0, sizeof(tstate->transinfo[i].curr));
memset(&tstate->transinfo[i].goal, 0, sizeof(tstate->transinfo[i].goal));
}
} else
memset(tstate, 0, sizeof(*tstate));
ahc->enabled_targets[scsi_id] = tstate; return (tstate);
}
#ifdef AHC_TARGET_MODE /* * Free per target mode instance (ID we respond to as a target) * transfer negotiation data structures.
*/ staticvoid
ahc_free_tstate(struct ahc_softc *ahc, u_int scsi_id, char channel, int force)
{ struct ahc_tmode_tstate *tstate;
/* * Don't clean up our "master" tstate. * It has our default user settings.
*/ if (((channel == 'B' && scsi_id == ahc->our_id_b)
|| (channel == 'A' && scsi_id == ahc->our_id))
&& force == FALSE) return;
/* * Called when we have an active connection to a target on the bus, * this function finds the nearest syncrate to the input period limited * by the capabilities of the bus connectivity of and sync settings for * the target.
*/ staticconststruct ahc_syncrate *
ahc_devlimited_syncrate(struct ahc_softc *ahc, struct ahc_initiator_tinfo *tinfo,
u_int *period, u_int *ppr_options, role_t role)
{ struct ahc_transinfo *transinfo;
u_int maxsync;
if ((ahc->features & AHC_ULTRA2) != 0) { if ((ahc_inb(ahc, SBLKCTL) & ENAB40) != 0
&& (ahc_inb(ahc, SSTAT2) & EXP_ACTIVE) == 0) {
maxsync = AHC_SYNCRATE_DT;
} else {
maxsync = AHC_SYNCRATE_ULTRA; /* Can't do DT on an SE bus */
*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
}
} elseif ((ahc->features & AHC_ULTRA) != 0) {
maxsync = AHC_SYNCRATE_ULTRA;
} else {
maxsync = AHC_SYNCRATE_FAST;
} /* * Never allow a value higher than our current goal * period otherwise we may allow a target initiated * negotiation to go above the limit as set by the * user. In the case of an initiator initiated * sync negotiation, we limit based on the user * setting. This allows the system to still accept * incoming negotiations even if target initiated * negotiation is not performed.
*/ if (role == ROLE_TARGET)
transinfo = &tinfo->user; else
transinfo = &tinfo->goal;
*ppr_options &= transinfo->ppr_options; if (transinfo->width == MSG_EXT_WDTR_BUS_8_BIT) {
maxsync = max(maxsync, (u_int)AHC_SYNCRATE_ULTRA2);
*ppr_options &= ~MSG_EXT_PPR_DT_REQ;
} if (transinfo->period == 0) {
*period = 0;
*ppr_options = 0; return (NULL);
}
*period = max(*period, (u_int)transinfo->period); return (ahc_find_syncrate(ahc, period, ppr_options, maxsync));
}
/* * Look up the valid period to SCSIRATE conversion in our table. * Return the period and offset that should be sent to the target * if this was the beginning of an SDTR.
*/ conststruct ahc_syncrate *
ahc_find_syncrate(struct ahc_softc *ahc, u_int *period,
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
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.