1073 lines
27 KiB
C
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);
|
|
}
|