NetBSD/sys/dev/ic/isp.c
mjacob 68d184dc19 Hmm- seriously funny and sad bug: you need to directly establish the
clock rate for this board on Alpha/PCI systems. Under x86/PCI, the
board f/w will correctly tell you "I'm running at 60Mhz", so the code
that preserved that across a board reset (which would drop the chip
back to 40Mhz) worked fine. On the 8200, the chip was saying "I'm 40Mhz"-
which wasn't true. This turned out to be okay as long as you didn't have
any FAST or UltraFast targets- In fact, setting the chip to 40Mhz allowed
you to run up to 8Mhz SCSI. Unfortunately you die bigtime on the devices
that go faster than that. The fix here is to only use what the chip tells
you the clock rate is in the cases you don't really know (sbus is the
only case where this could be different, although with 66Mhz PCI coming up,
this may change).
1997-06-22 19:57:06 +00:00

1073 lines
27 KiB
C

/* $NetBSD: isp.c,v 1.8 1997/06/22 19:57:06 mjacob Exp $ */
/*
* Machine Independent (well, as best as possible)
* code for the Qlogic ISP SCSI adapters.
*
* Specific probe attach and support routines for Qlogic ISP SCSI adapters.
*
* Copyright (c) 1997 by Matthew Jacob
* NASA AMES Research Center.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Inspiration and ideas about this driver are from Erik Moe's Linux driver
* (qlogicisp.c) and Dave Miller's SBus version of same (qlogicisp.c)
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
#include <scsi/scsi_message.h>
#include <scsi/scsi_debug.h>
#include <scsi/scsiconf.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <dev/ic/ispreg.h>
#include <dev/ic/ispvar.h>
#include <dev/ic/ispmbox.h>
#define MBOX_DELAY_COUNT 1000000 / 100
struct cfdriver isp_cd = {
NULL, "isp", DV_DULL
};
static void ispminphys __P((struct buf *));
static int32_t ispscsicmd __P((struct scsi_xfer *xs));
static int isp_mboxcmd __P((struct ispsoftc *, mbreg_t *));
static struct scsi_adapter isp_switch = {
ispscsicmd, ispminphys, 0, 0
};
static struct scsi_device isp_dev = { NULL, NULL, NULL, NULL };
static int isp_poll __P((struct ispsoftc *, struct scsi_xfer *, int));
static int isp_parse_status __P((struct ispsoftc *, ispstatusreq_t *));
static void isp_lostcmd __P((struct ispsoftc *, struct scsi_xfer *));
/*
* Reset Hardware.
*
* Only looks at sc_dev.dv_xname, sc_iot and sc_ioh fields.
*/
void
isp_reset(isp)
struct ispsoftc *isp;
{
mbreg_t mbs;
int loops, i;
u_int8_t clock;
isp->isp_state = ISP_NILSTATE;
/*
* Do MD specific pre initialization
*/
ISP_RESET0(isp);
/*
* Try and get old clock rate out before we hit the
* chip over the head- but if and only if we don't
* know our desired clock rate.
*/
clock = isp->isp_mdvec->dv_clock;
if (clock == 0) {
mbs.param[0] = MBOX_GET_CLOCK_RATE;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
clock = mbs.param[1];
printf("%s: using board clock 0x%x\n",
isp->isp_name, clock);
} else {
clock = 0;
}
}
/*
* Hit the chip over the head with hammer.
*/
ISP_WRITE(isp, BIU_ICR, BIU_ICR_SOFT_RESET);
/*
* Give the ISP a chance to recover...
*/
delay(100);
/*
* Clear data && control DMA engines.
*/
ISP_WRITE(isp, CDMA_CONTROL,
DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT);
ISP_WRITE(isp, DDMA_CONTROL,
DMA_CNTRL_CLEAR_CHAN | DMA_CNTRL_RESET_INT);
/*
* Wait for ISP to be ready to go...
*/
loops = MBOX_DELAY_COUNT;
while ((ISP_READ(isp, BIU_ICR) & BIU_ICR_SOFT_RESET) != 0) {
delay(100);
if (--loops < 0) {
printf("%s: chip reset timed out\n", isp->isp_name);
return;
}
}
/*
* More initialization
*/
ISP_WRITE(isp, BIU_CONF1, 0);
ISP_WRITE(isp, HCCR, HCCR_CMD_RESET);
delay(100);
if (isp->isp_mdvec->dv_conf1) {
ISP_SETBITS(isp, BIU_CONF1, isp->isp_mdvec->dv_conf1);
if (isp->isp_mdvec->dv_conf1 & BIU_BURST_ENABLE) {
ISP_SETBITS(isp, CDMA_CONF, DMA_ENABLE_BURST);
ISP_SETBITS(isp, DDMA_CONF, DMA_ENABLE_BURST);
}
} else {
ISP_WRITE(isp, BIU_CONF1, 0);
}
#if 0
ISP_WRITE(isp, RISC_MTR, 0x1212); /* FM */
#endif
ISP_WRITE(isp, HCCR, HCCR_CMD_RELEASE); /* release paused processor */
/*
* Do MD specific post initialization
*/
ISP_RESET1(isp);
/*
* Enable interrupts
*/
ISP_WRITE(isp, BIU_ICR,
BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS);
/*
* Do some sanity checking.
*/
mbs.param[0] = MBOX_NO_OP;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: NOP test failed\n", isp->isp_name);
return;
}
mbs.param[0] = MBOX_MAILBOX_REG_TEST;
mbs.param[1] = 0xdead;
mbs.param[2] = 0xbeef;
mbs.param[3] = 0xffff;
mbs.param[4] = 0x1111;
mbs.param[5] = 0xa5a5;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: Mailbox Register test didn't complete\n",
isp->isp_name);
return;
}
i = 0;
if (mbs.param[1] != 0xdead) {
printf("%s: Register Test Failed @reg %d (got %x)\n",
isp->isp_name, 1, mbs.param[1]);
i++;
}
if (mbs.param[2] != 0xbeef) {
printf("%s: Register Test Failed @reg %d (got %x)\n",
isp->isp_name, 2, mbs.param[2]);
i++;
}
if (mbs.param[3] != 0xffff) {
printf("%s: Register Test Failed @reg %d (got %x)\n",
isp->isp_name, 3, mbs.param[3]);
i++;
}
if (mbs.param[4] != 0x1111) {
printf("%s: Register Test Failed @reg %d (got %x)\n",
isp->isp_name, 4, mbs.param[4]);
i++;
}
if (mbs.param[5] != 0xa5a5) {
printf("%s: Register Test Failed @reg %d (got %x)\n",
isp->isp_name, 5, mbs.param[5]);
i++;
}
if (i) {
return;
}
/*
* Download new Firmware
*/
for (i = 0; i < isp->isp_mdvec->dv_fwlen; i++) {
mbs.param[0] = MBOX_WRITE_RAM_WORD;
mbs.param[1] = isp->isp_mdvec->dv_codeorg + i;
mbs.param[2] = isp->isp_mdvec->dv_ispfw[i];
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: f/w download failed\n", isp->isp_name);
return;
}
}
/*
* Verify that it downloaded correctly.
*/
mbs.param[0] = MBOX_VERIFY_CHECKSUM;
mbs.param[1] = isp->isp_mdvec->dv_codeorg;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: ram checksum failure\n", isp->isp_name);
return;
}
/*
* Now start it rolling...
*/
mbs.param[0] = MBOX_EXEC_FIRMWARE;
mbs.param[1] = isp->isp_mdvec->dv_codeorg;
(void) isp_mboxcmd(isp, &mbs);
/*
* Set CLOCK RATE
*/
if (clock) {
mbs.param[0] = MBOX_SET_CLOCK_RATE;
mbs.param[1] = clock;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: failed to set CLOCKRATE\n", isp->isp_name);
return;
}
}
mbs.param[0] = MBOX_ABOUT_FIRMWARE;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: ABOUT FIRMWARE command failed\n", isp->isp_name);
return;
}
printf("%s: F/W Revision %d.%d\n", isp->isp_name,
mbs.param[1], mbs.param[2]);
isp->isp_state = ISP_RESETSTATE;
}
/*
* Initialize Hardware to known state
*/
void
isp_init(isp)
struct ispsoftc *isp;
{
mbreg_t mbs;
int s, i, l;
/*
* Set Default Host Adapter Parameters
* XXX: Should try and get them out of NVRAM
*/
isp->isp_adapter_enabled = 1;
isp->isp_req_ack_active_neg = 1;
isp->isp_data_line_active_neg = 1;
isp->isp_cmd_dma_burst_enable = 1;
isp->isp_data_dma_burst_enabl = 1;
isp->isp_fifo_threshold = 2;
isp->isp_initiator_id = 7;
isp->isp_async_data_setup = 6;
isp->isp_selection_timeout = 250;
isp->isp_max_queue_depth = 256;
isp->isp_tag_aging = 8;
isp->isp_bus_reset_delay = 3;
isp->isp_retry_count = 0;
isp->isp_retry_delay = 1;
for (i = 0; i < MAX_TARGETS; i++) {
isp->isp_devparam[i].dev_flags = DPARM_DEFAULT;
isp->isp_devparam[i].exc_throttle = 16;
isp->isp_devparam[i].sync_period = 25;
isp->isp_devparam[i].sync_offset = 12;
isp->isp_devparam[i].dev_enable = 1;
}
s = splbio();
mbs.param[0] = MBOX_SET_INIT_SCSI_ID;
mbs.param[1] = isp->isp_initiator_id;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set initiator id\n", isp->isp_name);
return;
}
mbs.param[0] = MBOX_SET_RETRY_COUNT;
mbs.param[1] = isp->isp_retry_count;
mbs.param[2] = isp->isp_retry_delay;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set retry count and delay\n",
isp->isp_name);
return;
}
mbs.param[0] = MBOX_SET_ASYNC_DATA_SETUP_TIME;
mbs.param[1] = isp->isp_async_data_setup;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set async data setup time\n",
isp->isp_name);
return;
}
mbs.param[0] = MBOX_SET_ACTIVE_NEG_STATE;
mbs.param[1] =
(isp->isp_req_ack_active_neg << 4) |
(isp->isp_data_line_active_neg << 5);
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set active negation state\n",
isp->isp_name);
return;
}
mbs.param[0] = MBOX_SET_TAG_AGE_LIMIT;
mbs.param[1] = isp->isp_tag_aging;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set tag age limit\n", isp->isp_name);
return;
}
mbs.param[0] = MBOX_SET_SELECT_TIMEOUT;
mbs.param[1] = isp->isp_selection_timeout;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set selection timeout\n", isp->isp_name);
return;
}
for (i = 0; i < MAX_TARGETS; i++) {
if (isp->isp_devparam[i].dev_enable == 0)
continue;
mbs.param[0] = MBOX_SET_TARGET_PARAMS;
mbs.param[1] = i << 8;
mbs.param[2] = isp->isp_devparam[i].dev_flags << 8;
mbs.param[3] =
(isp->isp_devparam[i].sync_offset << 8) |
(isp->isp_devparam[i].sync_period);
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set target parameters\n",
isp->isp_name);
return;
}
for (l = 0; l < MAX_LUNS; l++) {
mbs.param[0] = MBOX_SET_DEV_QUEUE_PARAMS;
mbs.param[1] = (i << 8) | l;
mbs.param[2] = isp->isp_max_queue_depth;
mbs.param[3] = isp->isp_devparam[i].exc_throttle;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: failed to set device queue "
"parameters\n", isp->isp_name);
return;
}
}
}
/*
* Set up DMA for the request and result mailboxes.
*/
if (ISP_MBOXDMASETUP(isp)) {
(void) splx(s);
printf("%s: can't setup DMA for mailboxes\n", isp->isp_name);
return;
}
mbs.param[0] = MBOX_INIT_RES_QUEUE;
mbs.param[1] = RESULT_QUEUE_LEN;
mbs.param[2] = (u_int16_t) (isp->isp_result_dma >> 16);
mbs.param[3] = (u_int16_t) (isp->isp_result_dma & 0xffff);
mbs.param[4] = 0;
mbs.param[5] = 0;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: set of response queue failed\n", isp->isp_name);
return;
}
isp->isp_residx = 0;
mbs.param[0] = MBOX_INIT_REQ_QUEUE;
mbs.param[1] = RQUEST_QUEUE_LEN;
mbs.param[2] = (u_int16_t) (isp->isp_rquest_dma >> 16);
mbs.param[3] = (u_int16_t) (isp->isp_rquest_dma & 0xffff);
mbs.param[4] = 0;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: set of request queue failed\n", isp->isp_name);
return;
}
isp->isp_reqidx = 0;
/*
* Unfortunately, this is the only way right now for
* forcing a sync renegotiation. If we boot off of
* an Alpha, it's put the chip in SYNC mode, but we
* haven't necessarily set up the parameters the
* same, so we'll have to yank the reset line to
* get everyone to renegotiate.
*/
mbs.param[0] = MBOX_BUS_RESET;
mbs.param[1] = 2;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
(void) splx(s);
printf("%s: SCSI bus reset failed\n", isp->isp_name);
}
isp->isp_sendmarker = 1;
(void) splx(s);
isp->isp_state = ISP_INITSTATE;
}
/*
* Complete attachment of Hardware, include subdevices.
*/
void
isp_attach(isp)
struct ispsoftc *isp;
{
isp->isp_state = ISP_RUNSTATE;
isp->isp_link.channel = SCSI_CHANNEL_ONLY_ONE;
isp->isp_link.adapter_softc = isp;
isp->isp_link.adapter_target = isp->isp_initiator_id;
isp->isp_link.adapter = &isp_switch;
isp->isp_link.device = &isp_dev;
isp->isp_link.openings = RQUEST_QUEUE_LEN / (MAX_TARGETS - 1);
isp->isp_link.max_target = MAX_TARGETS-1;
config_found((void *)isp, &isp->isp_link, scsiprint);
}
/*
* Free any associated resources prior to decommissioning.
*/
void
isp_uninit(isp)
struct ispsoftc *isp;
{
}
/*
* minphys our xfers
*/
static void
ispminphys(bp)
struct buf *bp;
{
/*
* XX: Only the 1020 has a 24 bit limit.
*/
if (bp->b_bcount >= (1 << 24)) {
bp->b_bcount = (1 << 24) - 1;
}
minphys(bp);
}
/*
* start an xfer
*/
static int32_t
ispscsicmd(xs)
struct scsi_xfer *xs;
{
struct ispsoftc *isp;
u_int8_t iptr, optr;
ispreq_t *req;
int s, i;
isp = xs->sc_link->adapter_softc;
optr = ISP_READ(isp, OUTMAILBOX4);
iptr = isp->isp_reqidx;
req = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr);
iptr = (iptr + 1) & (RQUEST_QUEUE_LEN - 1);
if (iptr == optr) {
printf("%s: Request Queue Overflow\n", isp->isp_name);
xs->error = XS_DRIVER_STUFFUP;
return (TRY_AGAIN_LATER);
}
s = splbio();
if (isp->isp_sendmarker) {
ipsmarkreq_t *marker = (ipsmarkreq_t *) req;
bzero((void *) marker, sizeof (*marker));
marker->req_header.rqs_entry_count = 1;
marker->req_header.rqs_entry_type = RQSTYPE_MARKER;
marker->req_modifier = SYNC_ALL;
isp->isp_sendmarker = 0;
if (((iptr + 1) & (RQUEST_QUEUE_LEN - 1)) == optr) {
ISP_WRITE(isp, INMAILBOX4, iptr);
isp->isp_reqidx = iptr;
(void) splx(s);
printf("%s: Request Queue Overflow+\n", isp->isp_name);
xs->error = XS_DRIVER_STUFFUP;
return (TRY_AGAIN_LATER);
}
req = (ispreq_t *) ISP_QUEUE_ENTRY(isp->isp_rquest, iptr);
iptr = (iptr + 1) & (RQUEST_QUEUE_LEN - 1);
}
bzero((void *) req, sizeof (*req));
req->req_header.rqs_entry_count = 1;
req->req_header.rqs_entry_type = RQSTYPE_REQUEST;
req->req_header.rqs_flags = 0;
req->req_header.rqs_seqno = isp->isp_seqno++;
for (i = 0; i < RQUEST_QUEUE_LEN; i++) {
if (isp->isp_xflist[i] == NULL)
break;
}
if (i == RQUEST_QUEUE_LEN) {
panic("%s: ran out of xflist pointers\n", isp->isp_name);
/* NOTREACHED */
} else {
isp->isp_xflist[i] = xs;
req->req_handle = i;
}
req->req_flags = 0;
req->req_lun_trn = xs->sc_link->lun;
req->req_target = xs->sc_link->target;
req->req_cdblen = xs->cmdlen;
bcopy((void *)xs->cmd, req->req_cdb, xs->cmdlen);
#if 0
printf("%s(%d.%d): START%d cmd 0x%x datalen %d\n", isp->isp_name,
xs->sc_link->target, xs->sc_link->lun,
req->req_header.rqs_seqno, *(u_char *) xs->cmd, xs->datalen);
#endif
req->req_time = xs->timeout / 1000;
req->req_seg_count = 0;
if (ISP_DMASETUP(isp, xs, req, &iptr, optr)) {
(void) splx(s);
xs->error = XS_DRIVER_STUFFUP;
return (COMPLETE);
}
xs->error = 0;
ISP_WRITE(isp, INMAILBOX4, iptr);
isp->isp_reqidx = iptr;
(void) splx(s);
if ((xs->flags & SCSI_POLL) == 0) {
return (SUCCESSFULLY_QUEUED);
}
/*
* If we can't use interrupts, poll on completion.
*/
if (isp_poll(isp, xs, xs->timeout)) {
#if 0
/* XXX try to abort it, or whatever */
if (isp_poll(isp, xs, xs->timeout) {
/* XXX really nuke it */
}
#endif
/*
* If no other error occurred but we didn't finish,
* something bad happened.
*/
if ((xs->flags & ITSDONE) == 0 && xs->error == XS_NOERROR) {
isp_lostcmd(isp, xs);
xs->error = XS_DRIVER_STUFFUP;
}
}
return (COMPLETE);
}
/*
* Interrupt Service Routine(s)
*/
int
isp_poll(isp, xs, mswait)
struct ispsoftc *isp;
struct scsi_xfer *xs;
int mswait;
{
while (mswait) {
/* Try the interrupt handling routine */
(void)isp_intr((void *)isp);
/* See if the xs is now done */
if (xs->flags & ITSDONE)
return (0);
delay(1000); /* wait one millisecond */
mswait--;
}
return (1);
}
int
isp_intr(arg)
void *arg;
{
struct scsi_xfer *xs;
struct ispsoftc *isp = arg;
u_int16_t iptr, optr, isr;
isr = ISP_READ(isp, BIU_ISR);
if (isr == 0 || (isr & BIU_ISR_RISC_INT) == 0) {
#if 0
if (isr) {
printf("%s: isp_intr isr=%x\n", isp->isp_name, isr);
}
#endif
return (0);
}
optr = isp->isp_residx;
iptr = ISP_READ(isp, OUTMAILBOX5);
ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
ISP_WRITE(isp, BIU_ICR,
BIU_ICR_ENABLE_RISC_INT | BIU_ICR_ENABLE_ALL_INTS);
if (ISP_READ(isp, BIU_SEMA) & 1) {
u_int16_t mbox0 = ISP_READ(isp, OUTMAILBOX0);
switch (mbox0) {
case ASYNC_BUS_RESET:
case ASYNC_TIMEOUT_RESET:
printf("%s: bus or timeout reset\n", isp->isp_name);
isp->isp_sendmarker = 1;
break;
default:
printf("%s: async %x\n", isp->isp_name, mbox0);
break;
}
ISP_WRITE(isp, BIU_SEMA, 0);
#if 0
} else {
if (optr == iptr) {
printf("why'd we interrupt? isr %x iptr %x optr %x\n",
isr, optr, iptr);
}
#endif
}
while (optr != iptr) {
ispstatusreq_t *sp;
int buddaboom = 0;
sp = (ispstatusreq_t *) ISP_QUEUE_ENTRY(isp->isp_result, optr);
optr = (optr + 1) & (RESULT_QUEUE_LEN-1);
if (sp->req_header.rqs_entry_type != RQSTYPE_RESPONSE) {
printf("%s: not RESPONSE in RESPONSE Queue (0x%x)\n",
isp->isp_name, sp->req_header.rqs_entry_type);
if (sp->req_header.rqs_entry_type != RQSTYPE_REQUEST) {
ISP_WRITE(isp, INMAILBOX5, optr);
continue;
}
buddaboom = 1;
}
if (sp->req_header.rqs_flags & 0xf) {
if (sp->req_header.rqs_flags & RQSFLAG_CONTINUATION) {
ISP_WRITE(isp, INMAILBOX5, optr);
continue;
}
printf("%s: rqs_flags=%x\n", isp->isp_name,
sp->req_header.rqs_flags & 0xf);
}
if (sp->req_handle >= RQUEST_QUEUE_LEN) {
printf("%s: bad request handle %d\n", isp->isp_name,
sp->req_handle);
ISP_WRITE(isp, INMAILBOX5, optr);
continue;
}
xs = (struct scsi_xfer *) isp->isp_xflist[sp->req_handle];
if (xs == NULL) {
printf("%s: NULL xs in xflist\n", isp->isp_name);
ISP_WRITE(isp, INMAILBOX5, optr);
continue;
}
isp->isp_xflist[sp->req_handle] = NULL;
if (sp->req_status_flags & RQSTF_BUS_RESET) {
isp->isp_sendmarker = 1;
}
if (buddaboom) {
xs->error = XS_DRIVER_STUFFUP;
}
if (sp->req_state_flags & RQSF_GOT_SENSE) {
bcopy(sp->req_sense_data, &xs->sense,
sizeof (xs->sense));
xs->error = XS_SENSE;
}
xs->status = sp->req_scsi_status;
if (xs->error == 0 && xs->status == SCSI_BUSY)
xs->error = XS_BUSY;
if (sp->req_header.rqs_entry_type == RQSTYPE_RESPONSE) {
if (xs->error == 0)
xs->error = isp_parse_status(isp, sp);
} else {
printf("%s: unknown return %x\n", isp->isp_name,
sp->req_header.rqs_entry_type);
if (xs->error == 0)
xs->error = XS_DRIVER_STUFFUP;
}
xs->resid = sp->req_resid;
xs->flags |= ITSDONE;
if (xs->datalen) {
ISP_DMAFREE(isp, xs, sp->req_handle);
}
#if 0
printf("%s(%d.%d): FINISH%d cmd 0x%x resid %d STS %x",
isp->isp_name, xs->sc_link->target, xs->sc_link->lun,
sp->req_header.rqs_seqno, *(u_char *) xs->cmd,
xs->resid, xs->status);
if (sp->req_state_flags & RQSF_GOT_SENSE) {
printf(" Skey: %x", xs->sense.flags);
if (xs->error != XS_SENSE) {
printf(" BUT NOT SET");
}
}
printf(" xs->error %d\n", xs->error);
#endif
ISP_WRITE(isp, INMAILBOX5, optr);
scsi_done(xs);
}
isp->isp_residx = optr;
return (1);
}
/*
* Support routines.
*/
static int
isp_parse_status(isp, sp)
struct ispsoftc *isp;
ispstatusreq_t *sp;
{
switch (sp->req_completion_status) {
case RQCS_COMPLETE:
return (XS_NOERROR);
break;
case RQCS_INCOMPLETE:
if ((sp->req_state_flags & RQSF_GOT_TARGET) == 0) {
return (XS_SELTIMEOUT);
}
printf("%s: incomplete, state %x\n",
isp->isp_name, sp->req_state_flags);
break;
case RQCS_DATA_UNDERRUN:
return (XS_NOERROR);
case RQCS_TIMEOUT:
return (XS_TIMEOUT);
case RQCS_RESET_OCCURRED:
printf("%s: reset occurred\n", isp->isp_name);
isp->isp_sendmarker = 1;
break;
case RQCS_ABORTED:
printf("%s: command aborted\n", isp->isp_name);
isp->isp_sendmarker = 1;
break;
default:
printf("%s: comp status %x\n", isp->isp_name,
sp->req_completion_status);
break;
}
return (XS_DRIVER_STUFFUP);
}
#define HINIB(x) ((x) >> 0x4)
#define LONIB(x) ((x) & 0xf)
#define MAKNIB(a, b) (((a) << 4) | (b))
static u_int8_t mbpcnt[] = {
MAKNIB(1, 1), /* MBOX_NO_OP */
MAKNIB(5, 5), /* MBOX_LOAD_RAM */
MAKNIB(2, 0), /* MBOX_EXEC_FIRMWARE */
MAKNIB(5, 5), /* MBOX_DUMP_RAM */
MAKNIB(3, 3), /* MBOX_WRITE_RAM_WORD */
MAKNIB(2, 3), /* MBOX_READ_RAM_WORD */
MAKNIB(6, 6), /* MBOX_MAILBOX_REG_TEST */
MAKNIB(2, 3), /* MBOX_VERIFY_CHECKSUM */
MAKNIB(1, 3), /* MBOX_ABOUT_FIRMWARE */
MAKNIB(0, 0), /* 0x0009 */
MAKNIB(0, 0), /* 0x000a */
MAKNIB(0, 0), /* 0x000b */
MAKNIB(0, 0), /* 0x000c */
MAKNIB(0, 0), /* 0x000d */
MAKNIB(1, 2), /* MBOX_CHECK_FIRMWARE */
MAKNIB(0, 0), /* 0x000f */
MAKNIB(5, 5), /* MBOX_INIT_REQ_QUEUE */
MAKNIB(6, 6), /* MBOX_INIT_RES_QUEUE */
MAKNIB(4, 4), /* MBOX_EXECUTE_IOCB */
MAKNIB(2, 2), /* MBOX_WAKE_UP */
MAKNIB(1, 6), /* MBOX_STOP_FIRMWARE */
MAKNIB(4, 4), /* MBOX_ABORT */
MAKNIB(2, 2), /* MBOX_ABORT_DEVICE */
MAKNIB(3, 3), /* MBOX_ABORT_TARGET */
MAKNIB(2, 2), /* MBOX_BUS_RESET */
MAKNIB(2, 3), /* MBOX_STOP_QUEUE */
MAKNIB(2, 3), /* MBOX_START_QUEUE */
MAKNIB(2, 3), /* MBOX_SINGLE_STEP_QUEUE */
MAKNIB(2, 3), /* MBOX_ABORT_QUEUE */
MAKNIB(2, 4), /* MBOX_GET_DEV_QUEUE_STATUS */
MAKNIB(0, 0), /* 0x001e */
MAKNIB(1, 3), /* MBOX_GET_FIRMWARE_STATUS */
MAKNIB(1, 2), /* MBOX_GET_INIT_SCSI_ID */
MAKNIB(1, 2), /* MBOX_GET_SELECT_TIMEOUT */
MAKNIB(1, 3), /* MBOX_GET_RETRY_COUNT */
MAKNIB(1, 2), /* MBOX_GET_TAG_AGE_LIMIT */
MAKNIB(1, 2), /* MBOX_GET_CLOCK_RATE */
MAKNIB(1, 2), /* MBOX_GET_ACT_NEG_STATE */
MAKNIB(1, 2), /* MBOX_GET_ASYNC_DATA_SETUP_TIME */
MAKNIB(1, 3), /* MBOX_GET_PCI_PARAMS */
MAKNIB(2, 4), /* MBOX_GET_TARGET_PARAMS */
MAKNIB(2, 4), /* MBOX_GET_DEV_QUEUE_PARAMS */
MAKNIB(0, 0), /* 0x002a */
MAKNIB(0, 0), /* 0x002b */
MAKNIB(0, 0), /* 0x002c */
MAKNIB(0, 0), /* 0x002d */
MAKNIB(0, 0), /* 0x002e */
MAKNIB(0, 0), /* 0x002f */
MAKNIB(2, 2), /* MBOX_SET_INIT_SCSI_ID */
MAKNIB(2, 2), /* MBOX_SET_SELECT_TIMEOUT */
MAKNIB(3, 3), /* MBOX_SET_RETRY_COUNT */
MAKNIB(2, 2), /* MBOX_SET_TAG_AGE_LIMIT */
MAKNIB(2, 2), /* MBOX_SET_CLOCK_RATE */
MAKNIB(2, 2), /* MBOX_SET_ACTIVE_NEG_STATE */
MAKNIB(2, 2), /* MBOX_SET_ASYNC_DATA_SETUP_TIME */
MAKNIB(3, 3), /* MBOX_SET_PCI_CONTROL_PARAMS */
MAKNIB(4, 4), /* MBOX_SET_TARGET_PARAMS */
MAKNIB(4, 4), /* MBOX_SET_DEV_QUEUE_PARAMS */
MAKNIB(0, 0), /* 0x003a */
MAKNIB(0, 0), /* 0x003b */
MAKNIB(0, 0), /* 0x003c */
MAKNIB(0, 0), /* 0x003d */
MAKNIB(0, 0), /* 0x003e */
MAKNIB(0, 0), /* 0x003f */
MAKNIB(1, 2), /* MBOX_RETURN_BIOS_BLOCK_ADDR */
MAKNIB(6, 1), /* MBOX_WRITE_FOUR_RAM_WORDS */
MAKNIB(2, 3) /* MBOX_EXEC_BIOS_IOCB */
};
#define NMBCOM (sizeof (mbpcnt) / sizeof (mbpcnt[0]))
static int
isp_mboxcmd(isp, mbp)
struct ispsoftc *isp;
mbreg_t *mbp;
{
int outparam, inparam;
int loops;
if (mbp->param[0] > NMBCOM) {
printf("%s: bad command %x\n", isp->isp_name, mbp->param[0]);
return (-1);
}
inparam = HINIB(mbpcnt[mbp->param[0]]);
outparam = LONIB(mbpcnt[mbp->param[0]]);
if (inparam == 0 && outparam == 0) {
printf("%s: no parameters for %x\n", isp->isp_name,
mbp->param[0]);
return (-1);
}
/*
* Make sure we can send some words..
*/
loops = MBOX_DELAY_COUNT;
while ((ISP_READ(isp, HCCR) & HCCR_HOST_INT) != 0) {
delay(100);
if (--loops < 0) {
printf("%s: isp_mboxcmd timeout #1\n", isp->isp_name);
return (-1);
}
}
/*
* Write input parameters
*/
switch (inparam) {
case 6: ISP_WRITE(isp, INMAILBOX5, mbp->param[5]); mbp->param[5] = 0;
case 5: ISP_WRITE(isp, INMAILBOX4, mbp->param[4]); mbp->param[4] = 0;
case 4: ISP_WRITE(isp, INMAILBOX3, mbp->param[3]); mbp->param[3] = 0;
case 3: ISP_WRITE(isp, INMAILBOX2, mbp->param[2]); mbp->param[2] = 0;
case 2: ISP_WRITE(isp, INMAILBOX1, mbp->param[1]); mbp->param[1] = 0;
case 1: ISP_WRITE(isp, INMAILBOX0, mbp->param[0]); mbp->param[0] = 0;
}
/*
* Clear semaphore on mailbox registers
*/
ISP_WRITE(isp, BIU_SEMA, 0);
/*
* Clear RISC int condition.
*/
ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
/*
* Set Host Interrupt condition so that RISC will pick up mailbox regs.
*/
ISP_WRITE(isp, HCCR, HCCR_CMD_SET_HOST_INT);
/*
* Wait until RISC int is set
*/
loops = MBOX_DELAY_COUNT;
while ((ISP_READ(isp, BIU_ISR) & BIU_ISR_RISC_INT) != 0) {
delay(100);
if (--loops < 0) {
printf("%s: isp_mboxcmd timeout #2\n", isp->isp_name);
return (-1);
}
}
/*
* Check to make sure that the semaphore has been set.
*/
loops = MBOX_DELAY_COUNT;
while ((ISP_READ(isp, BIU_SEMA) & 1) == 0) {
delay(100);
if (--loops < 0) {
printf("%s: isp_mboxcmd timeout #3\n", isp->isp_name);
return (-1);
}
}
/*
* Make sure that the MBOX_BUSY has gone away
*/
loops = MBOX_DELAY_COUNT;
while (ISP_READ(isp, OUTMAILBOX0) == MBOX_BUSY) {
delay(100);
if (--loops < 0) {
printf("%s: isp_mboxcmd timeout #4\n", isp->isp_name);
return (-1);
}
}
/*
* Pick up output parameters.
*/
switch (outparam) {
case 6: mbp->param[5] = ISP_READ(isp, OUTMAILBOX5);
case 5: mbp->param[4] = ISP_READ(isp, OUTMAILBOX4);
case 4: mbp->param[3] = ISP_READ(isp, OUTMAILBOX3);
case 3: mbp->param[2] = ISP_READ(isp, OUTMAILBOX2);
case 2: mbp->param[1] = ISP_READ(isp, OUTMAILBOX1);
case 1: mbp->param[0] = ISP_READ(isp, OUTMAILBOX0);
}
/*
* Clear RISC int.
*/
ISP_WRITE(isp, HCCR, HCCR_CMD_CLEAR_RISC_INT);
/*
* Release semaphore on mailbox registers
*/
ISP_WRITE(isp, BIU_SEMA, 0);
return (0);
}
static void
isp_lostcmd(struct ispsoftc *isp, struct scsi_xfer *xs)
{
mbreg_t mbs;
mbs.param[0] = MBOX_GET_FIRMWARE_STATUS;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: couldn't GET FIRMWARE STATUS\n", isp->isp_name);
return;
}
printf("%s: lost command, %d commands active of total %d\n",
isp->isp_name, mbs.param[1], mbs.param[2]);
if (xs == NULL || xs->sc_link == NULL)
return;
mbs.param[0] = MBOX_GET_DEV_QUEUE_STATUS;
mbs.param[1] = xs->sc_link->target << 8 | xs->sc_link->lun;
(void) isp_mboxcmd(isp, &mbs);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
printf("%s: couldn't GET DEVICE STATUS\n", isp->isp_name);
return;
}
printf("%s: lost command, target %d lun %d, State: %x\n",
isp->isp_name, mbs.param[1] >> 8, mbs.param[1] & 0x7,
mbs.param[2] & 0xff);
}