NetBSD/sys/dev/ic/esiop.c
bouyer 98fae666e4 Some drives disconnect after the last data phase without a save data pointer
message. In such case we would not update resid with the proper value
(eventually resid would not be updated at all if there was only one data
phase). To fix this, have the script save the offset in the data tables at
disconnect time if there was a transfer, and use this to compute the resid
if the current offset is 0.
Problem reported and patch tested by edwin, Roy Bixler and YAMAMOTO Takashi.
Fix kern/31990 by YAMAMOTO Takashi.
2005-11-18 23:10:32 +00:00

2222 lines
66 KiB
C

/* $NetBSD: esiop.c,v 1.34 2005/11/18 23:10:32 bouyer Exp $ */
/*
* Copyright (c) 2002 Manuel Bouyer.
*
* 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.
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Manuel Bouyer.
* 4. 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 ``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 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.
*
*/
/* SYM53c7/8xx PCI-SCSI I/O Processors driver */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: esiop.c,v 1.34 2005/11/18 23:10:32 bouyer Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <sys/buf.h>
#include <sys/kernel.h>
#include <uvm/uvm_extern.h>
#include <machine/endian.h>
#include <machine/bus.h>
#include <dev/microcode/siop/esiop.out>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsi_message.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#include <dev/ic/siopreg.h>
#include <dev/ic/siopvar_common.h>
#include <dev/ic/esiopvar.h>
#include "opt_siop.h"
#ifndef DEBUG
#undef DEBUG
#endif
#undef SIOP_DEBUG
#undef SIOP_DEBUG_DR
#undef SIOP_DEBUG_INTR
#undef SIOP_DEBUG_SCHED
#undef DUMP_SCRIPT
#define SIOP_STATS
#ifndef SIOP_DEFAULT_TARGET
#define SIOP_DEFAULT_TARGET 7
#endif
/* number of cmd descriptors per block */
#define SIOP_NCMDPB (PAGE_SIZE / sizeof(struct esiop_xfer))
void esiop_reset(struct esiop_softc *);
void esiop_checkdone(struct esiop_softc *);
void esiop_handle_reset(struct esiop_softc *);
void esiop_scsicmd_end(struct esiop_cmd *, int);
void esiop_unqueue(struct esiop_softc *, int, int);
int esiop_handle_qtag_reject(struct esiop_cmd *);
static void esiop_start(struct esiop_softc *, struct esiop_cmd *);
void esiop_timeout(void *);
void esiop_scsipi_request(struct scsipi_channel *,
scsipi_adapter_req_t, void *);
void esiop_dump_script(struct esiop_softc *);
void esiop_morecbd(struct esiop_softc *);
void esiop_moretagtbl(struct esiop_softc *);
void siop_add_reselsw(struct esiop_softc *, int);
void esiop_target_register(struct esiop_softc *, u_int32_t);
void esiop_update_scntl3(struct esiop_softc *, struct siop_common_target *);
#ifdef SIOP_STATS
static int esiop_stat_intr = 0;
static int esiop_stat_intr_shortxfer = 0;
static int esiop_stat_intr_sdp = 0;
static int esiop_stat_intr_done = 0;
static int esiop_stat_intr_xferdisc = 0;
static int esiop_stat_intr_lunresel = 0;
static int esiop_stat_intr_qfull = 0;
void esiop_printstats(void);
#define INCSTAT(x) x++
#else
#define INCSTAT(x)
#endif
static __inline__ void esiop_script_sync(struct esiop_softc *, int);
static __inline__ void
esiop_script_sync(sc, ops)
struct esiop_softc *sc;
int ops;
{
if ((sc->sc_c.features & SF_CHIP_RAM) == 0)
bus_dmamap_sync(sc->sc_c.sc_dmat, sc->sc_c.sc_scriptdma, 0,
PAGE_SIZE, ops);
}
static __inline__ u_int32_t esiop_script_read(struct esiop_softc *, u_int);
static __inline__ u_int32_t
esiop_script_read(sc, offset)
struct esiop_softc *sc;
u_int offset;
{
if (sc->sc_c.features & SF_CHIP_RAM) {
return bus_space_read_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh,
offset * 4);
} else {
return le32toh(sc->sc_c.sc_script[offset]);
}
}
static __inline__ void esiop_script_write(struct esiop_softc *, u_int,
u_int32_t);
static __inline__ void
esiop_script_write(sc, offset, val)
struct esiop_softc *sc;
u_int offset;
u_int32_t val;
{
if (sc->sc_c.features & SF_CHIP_RAM) {
bus_space_write_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh,
offset * 4, val);
} else {
sc->sc_c.sc_script[offset] = htole32(val);
}
}
void
esiop_attach(sc)
struct esiop_softc *sc;
{
struct esiop_dsatbl *tagtbl_donering;
if (siop_common_attach(&sc->sc_c) != 0 )
return;
TAILQ_INIT(&sc->free_list);
TAILQ_INIT(&sc->cmds);
TAILQ_INIT(&sc->free_tagtbl);
TAILQ_INIT(&sc->tag_tblblk);
sc->sc_currschedslot = 0;
#ifdef SIOP_DEBUG
aprint_debug("%s: script size = %d, PHY addr=0x%x, VIRT=%p\n",
sc->sc_c.sc_dev.dv_xname, (int)sizeof(esiop_script),
(u_int32_t)sc->sc_c.sc_scriptaddr, sc->sc_c.sc_script);
#endif
sc->sc_c.sc_adapt.adapt_max_periph = ESIOP_NTAG;
sc->sc_c.sc_adapt.adapt_request = esiop_scsipi_request;
/*
* get space for the CMD done slot. For this we use a tag table entry.
* It's the same size and allows us to not waste 3/4 of a page
*/
#ifdef DIAGNOSTIC
if (ESIOP_NTAG != A_ndone_slots) {
aprint_error("%s: size of tag DSA table different from the done"
" ring\n", sc->sc_c.sc_dev.dv_xname);
return;
}
#endif
esiop_moretagtbl(sc);
tagtbl_donering = TAILQ_FIRST(&sc->free_tagtbl);
if (tagtbl_donering == NULL) {
aprint_error("%s: no memory for command done ring\n",
sc->sc_c.sc_dev.dv_xname);
return;
}
TAILQ_REMOVE(&sc->free_tagtbl, tagtbl_donering, next);
sc->sc_done_map = tagtbl_donering->tblblk->blkmap;
sc->sc_done_offset = tagtbl_donering->tbl_offset;
sc->sc_done_slot = &tagtbl_donering->tbl[0];
/* Do a bus reset, so that devices fall back to narrow/async */
siop_resetbus(&sc->sc_c);
/*
* siop_reset() will reset the chip, thus clearing pending interrupts
*/
esiop_reset(sc);
#ifdef DUMP_SCRIPT
esiop_dump_script(sc);
#endif
config_found((struct device*)sc, &sc->sc_c.sc_chan, scsiprint);
}
void
esiop_reset(sc)
struct esiop_softc *sc;
{
int i, j;
u_int32_t addr;
u_int32_t msgin_addr, sem_addr;
siop_common_reset(&sc->sc_c);
/*
* we copy the script at the beggining of RAM. Then there is 4 bytes
* for messages in, and 4 bytes for semaphore
*/
sc->sc_free_offset = sizeof(esiop_script) / sizeof(esiop_script[0]);
msgin_addr =
sc->sc_free_offset * sizeof(u_int32_t) + sc->sc_c.sc_scriptaddr;
sc->sc_free_offset += 1;
sc->sc_semoffset = sc->sc_free_offset;
sem_addr =
sc->sc_semoffset * sizeof(u_int32_t) + sc->sc_c.sc_scriptaddr;
sc->sc_free_offset += 1;
/* then we have the scheduler ring */
sc->sc_shedoffset = sc->sc_free_offset;
sc->sc_free_offset += A_ncmd_slots * CMD_SLOTSIZE;
/* then the targets DSA table */
sc->sc_target_table_offset = sc->sc_free_offset;
sc->sc_free_offset += sc->sc_c.sc_chan.chan_ntargets;
/* copy and patch the script */
if (sc->sc_c.features & SF_CHIP_RAM) {
bus_space_write_region_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh, 0,
esiop_script,
sizeof(esiop_script) / sizeof(esiop_script[0]));
for (j = 0; j <
(sizeof(E_tlq_offset_Used) / sizeof(E_tlq_offset_Used[0]));
j++) {
bus_space_write_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh,
E_tlq_offset_Used[j] * 4,
sizeof(struct siop_common_xfer));
}
for (j = 0; j <
(sizeof(E_saved_offset_offset_Used) /
sizeof(E_saved_offset_offset_Used[0]));
j++) {
bus_space_write_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh,
E_saved_offset_offset_Used[j] * 4,
sizeof(struct siop_common_xfer) + 4);
}
for (j = 0; j <
(sizeof(E_abs_msgin2_Used) / sizeof(E_abs_msgin2_Used[0]));
j++) {
bus_space_write_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh,
E_abs_msgin2_Used[j] * 4, msgin_addr);
}
for (j = 0; j <
(sizeof(E_abs_sem_Used) / sizeof(E_abs_sem_Used[0]));
j++) {
bus_space_write_4(sc->sc_c.sc_ramt, sc->sc_c.sc_ramh,
E_abs_sem_Used[j] * 4, sem_addr);
}
if (sc->sc_c.features & SF_CHIP_LED0) {
bus_space_write_region_4(sc->sc_c.sc_ramt,
sc->sc_c.sc_ramh,
Ent_led_on1, esiop_led_on,
sizeof(esiop_led_on) / sizeof(esiop_led_on[0]));
bus_space_write_region_4(sc->sc_c.sc_ramt,
sc->sc_c.sc_ramh,
Ent_led_on2, esiop_led_on,
sizeof(esiop_led_on) / sizeof(esiop_led_on[0]));
bus_space_write_region_4(sc->sc_c.sc_ramt,
sc->sc_c.sc_ramh,
Ent_led_off, esiop_led_off,
sizeof(esiop_led_off) / sizeof(esiop_led_off[0]));
}
} else {
for (j = 0;
j < (sizeof(esiop_script) / sizeof(esiop_script[0])); j++) {
sc->sc_c.sc_script[j] = htole32(esiop_script[j]);
}
for (j = 0; j <
(sizeof(E_tlq_offset_Used) / sizeof(E_tlq_offset_Used[0]));
j++) {
sc->sc_c.sc_script[E_tlq_offset_Used[j]] =
htole32(sizeof(struct siop_common_xfer));
}
for (j = 0; j <
(sizeof(E_saved_offset_offset_Used) /
sizeof(E_saved_offset_offset_Used[0]));
j++) {
sc->sc_c.sc_script[E_saved_offset_offset_Used[j]] =
htole32(sizeof(struct siop_common_xfer) + 4);
}
for (j = 0; j <
(sizeof(E_abs_msgin2_Used) / sizeof(E_abs_msgin2_Used[0]));
j++) {
sc->sc_c.sc_script[E_abs_msgin2_Used[j]] =
htole32(msgin_addr);
}
for (j = 0; j <
(sizeof(E_abs_sem_Used) / sizeof(E_abs_sem_Used[0]));
j++) {
sc->sc_c.sc_script[E_abs_sem_Used[j]] =
htole32(sem_addr);
}
if (sc->sc_c.features & SF_CHIP_LED0) {
for (j = 0; j < (sizeof(esiop_led_on) /
sizeof(esiop_led_on[0])); j++)
sc->sc_c.sc_script[
Ent_led_on1 / sizeof(esiop_led_on[0]) + j
] = htole32(esiop_led_on[j]);
for (j = 0; j < (sizeof(esiop_led_on) /
sizeof(esiop_led_on[0])); j++)
sc->sc_c.sc_script[
Ent_led_on2 / sizeof(esiop_led_on[0]) + j
] = htole32(esiop_led_on[j]);
for (j = 0; j < (sizeof(esiop_led_off) /
sizeof(esiop_led_off[0])); j++)
sc->sc_c.sc_script[
Ent_led_off / sizeof(esiop_led_off[0]) + j
] = htole32(esiop_led_off[j]);
}
}
/* get base of scheduler ring */
addr = sc->sc_c.sc_scriptaddr + sc->sc_shedoffset * sizeof(u_int32_t);
/* init scheduler */
for (i = 0; i < A_ncmd_slots; i++) {
esiop_script_write(sc,
sc->sc_shedoffset + i * CMD_SLOTSIZE, A_f_cmd_free);
}
sc->sc_currschedslot = 0;
bus_space_write_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCRATCHE, 0);
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCRATCHD, addr);
/*
* 0x78000000 is a 'move data8 to reg'. data8 is the second
* octet, reg offset is the third.
*/
esiop_script_write(sc, Ent_cmdr0 / 4,
0x78640000 | ((addr & 0x000000ff) << 8));
esiop_script_write(sc, Ent_cmdr1 / 4,
0x78650000 | ((addr & 0x0000ff00) ));
esiop_script_write(sc, Ent_cmdr2 / 4,
0x78660000 | ((addr & 0x00ff0000) >> 8));
esiop_script_write(sc, Ent_cmdr3 / 4,
0x78670000 | ((addr & 0xff000000) >> 16));
/* done ring */
for (i = 0; i < A_ndone_slots; i++)
sc->sc_done_slot[i] = 0;
bus_dmamap_sync(sc->sc_c.sc_dmat, sc->sc_done_map,
sc->sc_done_offset, A_ndone_slots * sizeof(u_int32_t),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
addr = sc->sc_done_map->dm_segs[0].ds_addr + sc->sc_done_offset;
sc->sc_currdoneslot = 0;
bus_space_write_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCRATCHE + 2, 0);
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCRATCHF, addr);
esiop_script_write(sc, Ent_doner0 / 4,
0x786c0000 | ((addr & 0x000000ff) << 8));
esiop_script_write(sc, Ent_doner1 / 4,
0x786d0000 | ((addr & 0x0000ff00) ));
esiop_script_write(sc, Ent_doner2 / 4,
0x786e0000 | ((addr & 0x00ff0000) >> 8));
esiop_script_write(sc, Ent_doner3 / 4,
0x786f0000 | ((addr & 0xff000000) >> 16));
/* set flags */
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCRATCHC, 0);
/* write pointer of base of target DSA table */
addr = (sc->sc_target_table_offset * sizeof(u_int32_t)) +
sc->sc_c.sc_scriptaddr;
esiop_script_write(sc, (Ent_load_targtable / 4) + 0,
esiop_script_read(sc,(Ent_load_targtable / 4) + 0) |
((addr & 0x000000ff) << 8));
esiop_script_write(sc, (Ent_load_targtable / 4) + 2,
esiop_script_read(sc,(Ent_load_targtable / 4) + 2) |
((addr & 0x0000ff00) ));
esiop_script_write(sc, (Ent_load_targtable / 4) + 4,
esiop_script_read(sc,(Ent_load_targtable / 4) + 4) |
((addr & 0x00ff0000) >> 8));
esiop_script_write(sc, (Ent_load_targtable / 4) + 6,
esiop_script_read(sc,(Ent_load_targtable / 4) + 6) |
((addr & 0xff000000) >> 16));
#ifdef SIOP_DEBUG
printf("%s: target table offset %d free offset %d\n",
sc->sc_c.sc_dev.dv_xname, sc->sc_target_table_offset,
sc->sc_free_offset);
#endif
/* register existing targets */
for (i = 0; i < sc->sc_c.sc_chan.chan_ntargets; i++) {
if (sc->sc_c.targets[i])
esiop_target_register(sc, i);
}
/* start script */
if ((sc->sc_c.features & SF_CHIP_RAM) == 0) {
bus_dmamap_sync(sc->sc_c.sc_dmat, sc->sc_c.sc_scriptdma, 0,
PAGE_SIZE, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSP,
sc->sc_c.sc_scriptaddr + Ent_reselect);
}
#if 0
#define CALL_SCRIPT(ent) do {\
printf ("start script DSA 0x%lx DSP 0x%lx\n", \
esiop_cmd->cmd_c.dsa, \
sc->sc_c.sc_scriptaddr + ent); \
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSP, sc->sc_c.sc_scriptaddr + ent); \
} while (0)
#else
#define CALL_SCRIPT(ent) do {\
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSP, sc->sc_c.sc_scriptaddr + ent); \
} while (0)
#endif
int
esiop_intr(v)
void *v;
{
struct esiop_softc *sc = v;
struct esiop_target *esiop_target;
struct esiop_cmd *esiop_cmd;
struct esiop_lun *esiop_lun;
struct scsipi_xfer *xs;
int istat, sist, sstat1, dstat = 0; /* XXX: gcc */
u_int32_t irqcode;
int need_reset = 0;
int offset, target, lun, tag;
u_int32_t tflags;
u_int32_t addr;
int freetarget = 0;
int slot;
int retval = 0;
again:
istat = bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_ISTAT);
if ((istat & (ISTAT_INTF | ISTAT_DIP | ISTAT_SIP)) == 0) {
return retval;
}
retval = 1;
INCSTAT(esiop_stat_intr);
esiop_checkdone(sc);
if (istat & ISTAT_INTF) {
bus_space_write_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_ISTAT, ISTAT_INTF);
goto again;
}
if ((istat &(ISTAT_DIP | ISTAT_SIP | ISTAT_ABRT)) ==
(ISTAT_DIP | ISTAT_ABRT)) {
/* clear abort */
bus_space_write_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_ISTAT, 0);
}
/* get CMD from T/L/Q */
tflags = bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_SCRATCHC);
#ifdef SIOP_DEBUG_INTR
printf("interrupt, istat=0x%x tflags=0x%x "
"DSA=0x%x DSP=0x%lx\n", istat, tflags,
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSA),
(u_long)(bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP) -
sc->sc_c.sc_scriptaddr));
#endif
target = (tflags & A_f_c_target) ? ((tflags >> 8) & 0xff) : -1;
if (target > sc->sc_c.sc_chan.chan_ntargets) target = -1;
lun = (tflags & A_f_c_lun) ? ((tflags >> 16) & 0xff) : -1;
if (lun > sc->sc_c.sc_chan.chan_nluns) lun = -1;
tag = (tflags & A_f_c_tag) ? ((tflags >> 24) & 0xff) : -1;
if (target >= 0 && lun >= 0) {
esiop_target = (struct esiop_target *)sc->sc_c.targets[target];
if (esiop_target == NULL) {
printf("esiop_target (target %d) not valid\n", target);
goto none;
}
esiop_lun = esiop_target->esiop_lun[lun];
if (esiop_lun == NULL) {
printf("esiop_lun (target %d lun %d) not valid\n",
target, lun);
goto none;
}
esiop_cmd =
(tag >= 0) ? esiop_lun->tactive[tag] : esiop_lun->active;
if (esiop_cmd == NULL) {
printf("esiop_cmd (target %d lun %d tag %d) not valid\n",
target, lun, tag);
goto none;
}
xs = esiop_cmd->cmd_c.xs;
#ifdef DIAGNOSTIC
if (esiop_cmd->cmd_c.status != CMDST_ACTIVE) {
printf("esiop_cmd (target %d lun %d) "
"not active (%d)\n", target, lun,
esiop_cmd->cmd_c.status);
goto none;
}
#endif
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
} else {
none:
xs = NULL;
esiop_target = NULL;
esiop_lun = NULL;
esiop_cmd = NULL;
}
if (istat & ISTAT_DIP) {
dstat = bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSTAT);
if (dstat & DSTAT_ABRT) {
/* was probably generated by a bus reset IOCTL */
if ((dstat & DSTAT_DFE) == 0)
siop_clearfifo(&sc->sc_c);
goto reset;
}
if (dstat & DSTAT_SSI) {
printf("single step dsp 0x%08x dsa 0x08%x\n",
(int)(bus_space_read_4(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_DSP) -
sc->sc_c.sc_scriptaddr),
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSA));
if ((dstat & ~(DSTAT_DFE | DSTAT_SSI)) == 0 &&
(istat & ISTAT_SIP) == 0) {
bus_space_write_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_DCNTL,
bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_DCNTL) | DCNTL_STD);
}
return 1;
}
if (dstat & ~(DSTAT_SIR | DSTAT_DFE | DSTAT_SSI)) {
printf("%s: DMA IRQ:", sc->sc_c.sc_dev.dv_xname);
if (dstat & DSTAT_IID)
printf(" Illegal instruction");
if (dstat & DSTAT_BF)
printf(" bus fault");
if (dstat & DSTAT_MDPE)
printf(" parity");
if (dstat & DSTAT_DFE)
printf(" DMA fifo empty");
else
siop_clearfifo(&sc->sc_c);
printf(", DSP=0x%x DSA=0x%x: ",
(int)(bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP) - sc->sc_c.sc_scriptaddr),
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSA));
if (esiop_cmd)
printf("T/L/Q=%d/%d/%d last msg_in=0x%x status=0x%x\n",
target, lun, tag, esiop_cmd->cmd_tables->msg_in[0],
le32toh(esiop_cmd->cmd_tables->status));
else
printf(" current T/L/Q invalid\n");
need_reset = 1;
}
}
if (istat & ISTAT_SIP) {
if (istat & ISTAT_DIP)
delay(10);
/*
* Can't read sist0 & sist1 independently, or we have to
* insert delay
*/
sist = bus_space_read_2(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_SIST0);
sstat1 = bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_SSTAT1);
#ifdef SIOP_DEBUG_INTR
printf("scsi interrupt, sist=0x%x sstat1=0x%x "
"DSA=0x%x DSP=0x%lx\n", sist, sstat1,
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSA),
(u_long)(bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP) -
sc->sc_c.sc_scriptaddr));
#endif
if (sist & SIST0_RST) {
esiop_handle_reset(sc);
/* no table to flush here */
return 1;
}
if (sist & SIST0_SGE) {
if (esiop_cmd)
scsipi_printaddr(xs->xs_periph);
else
printf("%s:", sc->sc_c.sc_dev.dv_xname);
printf("scsi gross error\n");
if (esiop_target)
esiop_target->target_c.flags &= ~TARF_DT;
#ifdef DEBUG
printf("DSA=0x%x DSP=0x%lx\n",
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSA),
(u_long)(bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP) -
sc->sc_c.sc_scriptaddr));
printf("SDID 0x%x SCNTL3 0x%x SXFER 0x%x SCNTL4 0x%x\n",
bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SDID),
bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCNTL3),
bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SXFER),
bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_SCNTL4));
#endif
goto reset;
}
if ((sist & SIST0_MA) && need_reset == 0) {
if (esiop_cmd) {
int scratchc0;
dstat = bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_DSTAT);
/*
* first restore DSA, in case we were in a S/G
* operation.
*/
bus_space_write_4(sc->sc_c.sc_rt,
sc->sc_c.sc_rh,
SIOP_DSA, esiop_cmd->cmd_c.dsa);
scratchc0 = bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHC);
switch (sstat1 & SSTAT1_PHASE_MASK) {
case SSTAT1_PHASE_STATUS:
/*
* previous phase may be aborted for any reason
* ( for example, the target has less data to
* transfer than requested). Compute resid and
* just go to status, the command should
* terminate.
*/
INCSTAT(esiop_stat_intr_shortxfer);
if (scratchc0 & A_f_c_data)
siop_ma(&esiop_cmd->cmd_c);
else if ((dstat & DSTAT_DFE) == 0)
siop_clearfifo(&sc->sc_c);
CALL_SCRIPT(Ent_status);
return 1;
case SSTAT1_PHASE_MSGIN:
/*
* target may be ready to disconnect
* Compute resid which would be used later
* if a save data pointer is needed.
*/
INCSTAT(esiop_stat_intr_xferdisc);
if (scratchc0 & A_f_c_data)
siop_ma(&esiop_cmd->cmd_c);
else if ((dstat & DSTAT_DFE) == 0)
siop_clearfifo(&sc->sc_c);
bus_space_write_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHC,
scratchc0 & ~A_f_c_data);
CALL_SCRIPT(Ent_msgin);
return 1;
}
printf("%s: unexpected phase mismatch %d\n",
sc->sc_c.sc_dev.dv_xname,
sstat1 & SSTAT1_PHASE_MASK);
} else {
printf("%s: phase mismatch without command\n",
sc->sc_c.sc_dev.dv_xname);
}
need_reset = 1;
}
if (sist & SIST0_PAR) {
/* parity error, reset */
if (esiop_cmd)
scsipi_printaddr(xs->xs_periph);
else
printf("%s:", sc->sc_c.sc_dev.dv_xname);
printf("parity error\n");
if (esiop_target)
esiop_target->target_c.flags &= ~TARF_DT;
goto reset;
}
if ((sist & (SIST1_STO << 8)) && need_reset == 0) {
/*
* selection time out, assume there's no device here
* We also have to update the ring pointer ourselve
*/
slot = bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHE);
esiop_script_sync(sc,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
#ifdef SIOP_DEBUG_SCHED
printf("sel timeout target %d, slot %d\n", target, slot);
#endif
/*
* mark this slot as free, and advance to next slot
*/
esiop_script_write(sc,
sc->sc_shedoffset + slot * CMD_SLOTSIZE,
A_f_cmd_free);
addr = bus_space_read_4(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHD);
if (slot < (A_ncmd_slots - 1)) {
bus_space_write_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHE, slot + 1);
addr = addr + sizeof(struct esiop_slot);
} else {
bus_space_write_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHE, 0);
addr = sc->sc_c.sc_scriptaddr +
sc->sc_shedoffset * sizeof(u_int32_t);
}
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_SCRATCHD, addr);
esiop_script_sync(sc,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
if (esiop_cmd) {
esiop_cmd->cmd_c.status = CMDST_DONE;
xs->error = XS_SELTIMEOUT;
freetarget = 1;
goto end;
} else {
printf("%s: selection timeout without "
"command, target %d (sdid 0x%x), "
"slot %d\n",
sc->sc_c.sc_dev.dv_xname, target,
bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SDID), slot);
need_reset = 1;
}
}
if (sist & SIST0_UDC) {
/*
* unexpected disconnect. Usually the target signals
* a fatal condition this way. Attempt to get sense.
*/
if (esiop_cmd) {
esiop_cmd->cmd_tables->status =
htole32(SCSI_CHECK);
goto end;
}
printf("%s: unexpected disconnect without "
"command\n", sc->sc_c.sc_dev.dv_xname);
goto reset;
}
if (sist & (SIST1_SBMC << 8)) {
/* SCSI bus mode change */
if (siop_modechange(&sc->sc_c) == 0 || need_reset == 1)
goto reset;
if ((istat & ISTAT_DIP) && (dstat & DSTAT_SIR)) {
/*
* we have a script interrupt, it will
* restart the script.
*/
goto scintr;
}
/*
* else we have to restart it ourselve, at the
* interrupted instruction.
*/
bus_space_write_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP,
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP) - 8);
return 1;
}
/* Else it's an unhandled exception (for now). */
printf("%s: unhandled scsi interrupt, sist=0x%x sstat1=0x%x "
"DSA=0x%x DSP=0x%x\n", sc->sc_c.sc_dev.dv_xname, sist,
bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_SSTAT1),
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSA),
(int)(bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSP) - sc->sc_c.sc_scriptaddr));
if (esiop_cmd) {
esiop_cmd->cmd_c.status = CMDST_DONE;
xs->error = XS_SELTIMEOUT;
goto end;
}
need_reset = 1;
}
if (need_reset) {
reset:
/* fatal error, reset the bus */
siop_resetbus(&sc->sc_c);
/* no table to flush here */
return 1;
}
scintr:
if ((istat & ISTAT_DIP) && (dstat & DSTAT_SIR)) { /* script interrupt */
irqcode = bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_DSPS);
#ifdef SIOP_DEBUG_INTR
printf("script interrupt 0x%x\n", irqcode);
#endif
/*
* no command, or an inactive command is only valid for a
* reselect interrupt
*/
if ((irqcode & 0x80) == 0) {
if (esiop_cmd == NULL) {
printf(
"%s: script interrupt (0x%x) with invalid DSA !!!\n",
sc->sc_c.sc_dev.dv_xname, irqcode);
goto reset;
}
if (esiop_cmd->cmd_c.status != CMDST_ACTIVE) {
printf("%s: command with invalid status "
"(IRQ code 0x%x current status %d) !\n",
sc->sc_c.sc_dev.dv_xname,
irqcode, esiop_cmd->cmd_c.status);
xs = NULL;
}
}
switch(irqcode) {
case A_int_err:
printf("error, DSP=0x%x\n",
(int)(bus_space_read_4(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_DSP) - sc->sc_c.sc_scriptaddr));
if (xs) {
xs->error = XS_SELTIMEOUT;
goto end;
} else {
goto reset;
}
case A_int_msgin:
{
int msgin = bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SFBR);
if (msgin == MSG_MESSAGE_REJECT) {
int msg, extmsg;
if (esiop_cmd->cmd_tables->msg_out[0] & 0x80) {
/*
* message was part of a identify +
* something else. Identify shouldn't
* have been rejected.
*/
msg =
esiop_cmd->cmd_tables->msg_out[1];
extmsg =
esiop_cmd->cmd_tables->msg_out[3];
} else {
msg =
esiop_cmd->cmd_tables->msg_out[0];
extmsg =
esiop_cmd->cmd_tables->msg_out[2];
}
if (msg == MSG_MESSAGE_REJECT) {
/* MSG_REJECT for a MSG_REJECT !*/
if (xs)
scsipi_printaddr(xs->xs_periph);
else
printf("%s: ",
sc->sc_c.sc_dev.dv_xname);
printf("our reject message was "
"rejected\n");
goto reset;
}
if (msg == MSG_EXTENDED &&
extmsg == MSG_EXT_WDTR) {
/* WDTR rejected, initiate sync */
if ((esiop_target->target_c.flags &
TARF_SYNC) == 0) {
esiop_target->target_c.status =
TARST_OK;
siop_update_xfer_mode(&sc->sc_c,
target);
/* no table to flush here */
CALL_SCRIPT(Ent_msgin_ack);
return 1;
}
esiop_target->target_c.status =
TARST_SYNC_NEG;
siop_sdtr_msg(&esiop_cmd->cmd_c, 0,
sc->sc_c.st_minsync,
sc->sc_c.maxoff);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
} else if (msg == MSG_EXTENDED &&
extmsg == MSG_EXT_SDTR) {
/* sync rejected */
esiop_target->target_c.offset = 0;
esiop_target->target_c.period = 0;
esiop_target->target_c.status =
TARST_OK;
siop_update_xfer_mode(&sc->sc_c,
target);
/* no table to flush here */
CALL_SCRIPT(Ent_msgin_ack);
return 1;
} else if (msg == MSG_EXTENDED &&
extmsg == MSG_EXT_PPR) {
/* PPR rejected */
esiop_target->target_c.offset = 0;
esiop_target->target_c.period = 0;
esiop_target->target_c.status =
TARST_OK;
siop_update_xfer_mode(&sc->sc_c,
target);
/* no table to flush here */
CALL_SCRIPT(Ent_msgin_ack);
return 1;
} else if (msg == MSG_SIMPLE_Q_TAG ||
msg == MSG_HEAD_OF_Q_TAG ||
msg == MSG_ORDERED_Q_TAG) {
if (esiop_handle_qtag_reject(
esiop_cmd) == -1)
goto reset;
CALL_SCRIPT(Ent_msgin_ack);
return 1;
}
if (xs)
scsipi_printaddr(xs->xs_periph);
else
printf("%s: ",
sc->sc_c.sc_dev.dv_xname);
if (msg == MSG_EXTENDED) {
printf("scsi message reject, extended "
"message sent was 0x%x\n", extmsg);
} else {
printf("scsi message reject, message "
"sent was 0x%x\n", msg);
}
/* no table to flush here */
CALL_SCRIPT(Ent_msgin_ack);
return 1;
}
if (msgin == MSG_IGN_WIDE_RESIDUE) {
/* use the extmsgdata table to get the second byte */
esiop_cmd->cmd_tables->t_extmsgdata.count =
htole32(1);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_get_extmsgdata);
return 1;
}
if (xs)
scsipi_printaddr(xs->xs_periph);
else
printf("%s: ", sc->sc_c.sc_dev.dv_xname);
printf("unhandled message 0x%x\n", msgin);
esiop_cmd->cmd_tables->msg_out[0] = MSG_MESSAGE_REJECT;
esiop_cmd->cmd_tables->t_msgout.count= htole32(1);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
}
case A_int_extmsgin:
#ifdef SIOP_DEBUG_INTR
printf("extended message: msg 0x%x len %d\n",
esiop_cmd->cmd_tables->msg_in[2],
esiop_cmd->cmd_tables->msg_in[1]);
#endif
if (esiop_cmd->cmd_tables->msg_in[1] >
sizeof(esiop_cmd->cmd_tables->msg_in) - 2)
printf("%s: extended message too big (%d)\n",
sc->sc_c.sc_dev.dv_xname,
esiop_cmd->cmd_tables->msg_in[1]);
esiop_cmd->cmd_tables->t_extmsgdata.count =
htole32(esiop_cmd->cmd_tables->msg_in[1] - 1);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_get_extmsgdata);
return 1;
case A_int_extmsgdata:
#ifdef SIOP_DEBUG_INTR
{
int i;
printf("extended message: 0x%x, data:",
esiop_cmd->cmd_tables->msg_in[2]);
for (i = 3; i < 2 + esiop_cmd->cmd_tables->msg_in[1];
i++)
printf(" 0x%x",
esiop_cmd->cmd_tables->msg_in[i]);
printf("\n");
}
#endif
if (esiop_cmd->cmd_tables->msg_in[0] ==
MSG_IGN_WIDE_RESIDUE) {
/* we got the second byte of MSG_IGN_WIDE_RESIDUE */
if (esiop_cmd->cmd_tables->msg_in[3] != 1)
printf("MSG_IGN_WIDE_RESIDUE: "
"bad len %d\n",
esiop_cmd->cmd_tables->msg_in[3]);
switch (siop_iwr(&esiop_cmd->cmd_c)) {
case SIOP_NEG_MSGOUT:
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
case SIOP_NEG_ACK:
CALL_SCRIPT(Ent_msgin_ack);
return 1;
default:
panic("invalid retval from "
"siop_iwr()");
}
return 1;
}
if (esiop_cmd->cmd_tables->msg_in[2] == MSG_EXT_PPR) {
switch (siop_ppr_neg(&esiop_cmd->cmd_c)) {
case SIOP_NEG_MSGOUT:
esiop_update_scntl3(sc,
esiop_cmd->cmd_c.siop_target);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
case SIOP_NEG_ACK:
esiop_update_scntl3(sc,
esiop_cmd->cmd_c.siop_target);
CALL_SCRIPT(Ent_msgin_ack);
return 1;
default:
panic("invalid retval from "
"siop_wdtr_neg()");
}
return 1;
}
if (esiop_cmd->cmd_tables->msg_in[2] == MSG_EXT_WDTR) {
switch (siop_wdtr_neg(&esiop_cmd->cmd_c)) {
case SIOP_NEG_MSGOUT:
esiop_update_scntl3(sc,
esiop_cmd->cmd_c.siop_target);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
case SIOP_NEG_ACK:
esiop_update_scntl3(sc,
esiop_cmd->cmd_c.siop_target);
CALL_SCRIPT(Ent_msgin_ack);
return 1;
default:
panic("invalid retval from "
"siop_wdtr_neg()");
}
return 1;
}
if (esiop_cmd->cmd_tables->msg_in[2] == MSG_EXT_SDTR) {
switch (siop_sdtr_neg(&esiop_cmd->cmd_c)) {
case SIOP_NEG_MSGOUT:
esiop_update_scntl3(sc,
esiop_cmd->cmd_c.siop_target);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD |
BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
case SIOP_NEG_ACK:
esiop_update_scntl3(sc,
esiop_cmd->cmd_c.siop_target);
CALL_SCRIPT(Ent_msgin_ack);
return 1;
default:
panic("invalid retval from "
"siop_wdtr_neg()");
}
return 1;
}
/* send a message reject */
esiop_cmd->cmd_tables->msg_out[0] = MSG_MESSAGE_REJECT;
esiop_cmd->cmd_tables->t_msgout.count = htole32(1);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_send_msgout);
return 1;
case A_int_disc:
INCSTAT(esiop_stat_intr_sdp);
offset = bus_space_read_1(sc->sc_c.sc_rt,
sc->sc_c.sc_rh, SIOP_SCRATCHA + 1);
#ifdef SIOP_DEBUG_DR
printf("disconnect offset %d\n", offset);
#endif
siop_sdp(&esiop_cmd->cmd_c, offset);
/* we start again with no offset */
ESIOP_XFER(esiop_cmd, saved_offset) =
htole32(SIOP_NOOFFSET);
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
CALL_SCRIPT(Ent_script_sched);
return 1;
case A_int_resfail:
printf("reselect failed\n");
CALL_SCRIPT(Ent_script_sched);
return 1;
case A_int_done:
if (xs == NULL) {
printf("%s: done without command\n",
sc->sc_c.sc_dev.dv_xname);
CALL_SCRIPT(Ent_script_sched);
return 1;
}
#ifdef SIOP_DEBUG_INTR
printf("done, DSA=0x%lx target id 0x%x last msg "
"in=0x%x status=0x%x\n", (u_long)esiop_cmd->cmd_c.dsa,
le32toh(esiop_cmd->cmd_tables->id),
esiop_cmd->cmd_tables->msg_in[0],
le32toh(esiop_cmd->cmd_tables->status));
#endif
INCSTAT(esiop_stat_intr_done);
esiop_cmd->cmd_c.status = CMDST_DONE;
goto end;
default:
printf("unknown irqcode %x\n", irqcode);
if (xs) {
xs->error = XS_SELTIMEOUT;
goto end;
}
goto reset;
}
return 1;
}
/* We just should't get there */
panic("siop_intr: I shouldn't be there !");
end:
/*
* restart the script now if command completed properly
* Otherwise wait for siop_scsicmd_end(), we may need to cleanup the
* queue
*/
xs->status = le32toh(esiop_cmd->cmd_tables->status);
#ifdef SIOP_DEBUG_INTR
printf("esiop_intr end: status %d\n", xs->status);
#endif
if (tag >= 0)
esiop_lun->tactive[tag] = NULL;
else
esiop_lun->active = NULL;
offset = bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_SCRATCHA + 1);
/*
* if we got a disconnect between the last data phase
* and the status phase, offset will be 0. In this
* case, cmd_tables->saved_offset will have the proper value
* if it got updated by the controller
*/
if (offset == 0 &&
ESIOP_XFER(esiop_cmd, saved_offset) != htole32(SIOP_NOOFFSET))
offset =
(le32toh(ESIOP_XFER(esiop_cmd, saved_offset)) >> 8) & 0xff;
esiop_scsicmd_end(esiop_cmd, offset);
if (freetarget && esiop_target->target_c.status == TARST_PROBING)
esiop_del_dev(sc, target, lun);
CALL_SCRIPT(Ent_script_sched);
return 1;
}
void
esiop_scsicmd_end(esiop_cmd, offset)
struct esiop_cmd *esiop_cmd;
int offset;
{
struct scsipi_xfer *xs = esiop_cmd->cmd_c.xs;
struct esiop_softc *sc = (struct esiop_softc *)esiop_cmd->cmd_c.siop_sc;
siop_update_resid(&esiop_cmd->cmd_c, offset);
switch(xs->status) {
case SCSI_OK:
xs->error = XS_NOERROR;
break;
case SCSI_BUSY:
xs->error = XS_BUSY;
break;
case SCSI_CHECK:
xs->error = XS_BUSY;
/* remove commands in the queue and scheduler */
esiop_unqueue(sc, xs->xs_periph->periph_target,
xs->xs_periph->periph_lun);
break;
case SCSI_QUEUE_FULL:
INCSTAT(esiop_stat_intr_qfull);
#ifdef SIOP_DEBUG
printf("%s:%d:%d: queue full (tag %d)\n",
sc->sc_c.sc_dev.dv_xname,
xs->xs_periph->periph_target,
xs->xs_periph->periph_lun, esiop_cmd->cmd_c.tag);
#endif
xs->error = XS_BUSY;
break;
case SCSI_SIOP_NOCHECK:
/*
* don't check status, xs->error is already valid
*/
break;
case SCSI_SIOP_NOSTATUS:
/*
* the status byte was not updated, cmd was
* aborted
*/
xs->error = XS_SELTIMEOUT;
break;
default:
scsipi_printaddr(xs->xs_periph);
printf("invalid status code %d\n", xs->status);
xs->error = XS_DRIVER_STUFFUP;
}
if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
bus_dmamap_sync(sc->sc_c.sc_dmat,
esiop_cmd->cmd_c.dmamap_data, 0,
esiop_cmd->cmd_c.dmamap_data->dm_mapsize,
(xs->xs_control & XS_CTL_DATA_IN) ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_c.sc_dmat,
esiop_cmd->cmd_c.dmamap_data);
}
bus_dmamap_unload(sc->sc_c.sc_dmat, esiop_cmd->cmd_c.dmamap_cmd);
callout_stop(&esiop_cmd->cmd_c.xs->xs_callout);
esiop_cmd->cmd_c.status = CMDST_FREE;
TAILQ_INSERT_TAIL(&sc->free_list, esiop_cmd, next);
#if 0
if (xs->resid != 0)
printf("resid %d datalen %d\n", xs->resid, xs->datalen);
#endif
scsipi_done (xs);
}
void
esiop_checkdone(sc)
struct esiop_softc *sc;
{
int target, lun, tag;
struct esiop_target *esiop_target;
struct esiop_lun *esiop_lun;
struct esiop_cmd *esiop_cmd;
u_int32_t slot;
int needsync = 0;
int status;
u_int32_t sem, offset;
esiop_script_sync(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
sem = esiop_script_read(sc, sc->sc_semoffset);
esiop_script_write(sc, sc->sc_semoffset, sem & ~A_sem_done);
if ((sc->sc_flags & SCF_CHAN_NOSLOT) && (sem & A_sem_start)) {
/*
* at last one command have been started,
* so we should have free slots now
*/
sc->sc_flags &= ~SCF_CHAN_NOSLOT;
scsipi_channel_thaw(&sc->sc_c.sc_chan, 1);
}
esiop_script_sync(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
if ((sem & A_sem_done) == 0) {
/* no pending done command */
return;
}
bus_dmamap_sync(sc->sc_c.sc_dmat, sc->sc_done_map,
sc->sc_done_offset, A_ndone_slots * sizeof(u_int32_t),
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
next:
if (sc->sc_done_slot[sc->sc_currdoneslot] == 0) {
if (needsync)
bus_dmamap_sync(sc->sc_c.sc_dmat, sc->sc_done_map,
sc->sc_done_offset,
A_ndone_slots * sizeof(u_int32_t),
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
return;
}
needsync = 1;
slot = htole32(sc->sc_done_slot[sc->sc_currdoneslot]);
sc->sc_done_slot[sc->sc_currdoneslot] = 0;
sc->sc_currdoneslot += 1;
if (sc->sc_currdoneslot == A_ndone_slots)
sc->sc_currdoneslot = 0;
target = (slot & A_f_c_target) ? (slot >> 8) & 0xff : -1;
lun = (slot & A_f_c_lun) ? (slot >> 16) & 0xff : -1;
tag = (slot & A_f_c_tag) ? (slot >> 24) & 0xff : -1;
esiop_target = (target >= 0) ?
(struct esiop_target *)sc->sc_c.targets[target] : NULL;
if (esiop_target == NULL) {
printf("esiop_target (target %d) not valid\n", target);
goto next;
}
esiop_lun = (lun >= 0) ? esiop_target->esiop_lun[lun] : NULL;
if (esiop_lun == NULL) {
printf("esiop_lun (target %d lun %d) not valid\n",
target, lun);
goto next;
}
esiop_cmd = (tag >= 0) ? esiop_lun->tactive[tag] : esiop_lun->active;
if (esiop_cmd == NULL) {
printf("esiop_cmd (target %d lun %d tag %d) not valid\n",
target, lun, tag);
goto next;
}
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
status = le32toh(esiop_cmd->cmd_tables->status);
#ifdef DIAGNOSTIC
if (status != SCSI_OK) {
printf("command for T/L/Q %d/%d/%d status %d\n",
target, lun, tag, status);
goto next;
}
#endif
/* Ok, this command has been handled */
esiop_cmd->cmd_c.xs->status = status;
if (tag >= 0)
esiop_lun->tactive[tag] = NULL;
else
esiop_lun->active = NULL;
/*
* scratcha was eventually saved in saved_offset by script.
* fetch offset from it
*/
offset = 0;
if (ESIOP_XFER(esiop_cmd, saved_offset) != htole32(SIOP_NOOFFSET))
offset =
(le32toh(ESIOP_XFER(esiop_cmd, saved_offset)) >> 8) & 0xff;
esiop_scsicmd_end(esiop_cmd, offset);
goto next;
}
void
esiop_unqueue(sc, target, lun)
struct esiop_softc *sc;
int target;
int lun;
{
int slot, tag;
u_int32_t slotdsa;
struct esiop_cmd *esiop_cmd;
struct esiop_lun *esiop_lun =
((struct esiop_target *)sc->sc_c.targets[target])->esiop_lun[lun];
/* first make sure to read valid data */
esiop_script_sync(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
for (tag = 0; tag < ESIOP_NTAG; tag++) {
/* look for commands in the scheduler, not yet started */
if (esiop_lun->tactive[tag] == NULL)
continue;
esiop_cmd = esiop_lun->tactive[tag];
for (slot = 0; slot < A_ncmd_slots; slot++) {
slotdsa = esiop_script_read(sc,
sc->sc_shedoffset + slot * CMD_SLOTSIZE);
/* if the slot has any flag, it won't match the DSA */
if (slotdsa == esiop_cmd->cmd_c.dsa) { /* found it */
/* Mark this slot as ignore */
esiop_script_write(sc,
sc->sc_shedoffset + slot * CMD_SLOTSIZE,
esiop_cmd->cmd_c.dsa | A_f_cmd_ignore);
/* ask to requeue */
esiop_cmd->cmd_c.xs->error = XS_REQUEUE;
esiop_cmd->cmd_c.xs->status = SCSI_SIOP_NOCHECK;
esiop_lun->tactive[tag] = NULL;
esiop_scsicmd_end(esiop_cmd, 0);
break;
}
}
}
esiop_script_sync(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
/*
* handle a rejected queue tag message: the command will run untagged,
* has to adjust the reselect script.
*/
int
esiop_handle_qtag_reject(esiop_cmd)
struct esiop_cmd *esiop_cmd;
{
struct esiop_softc *sc = (struct esiop_softc *)esiop_cmd->cmd_c.siop_sc;
int target = esiop_cmd->cmd_c.xs->xs_periph->periph_target;
int lun = esiop_cmd->cmd_c.xs->xs_periph->periph_lun;
int tag = esiop_cmd->cmd_tables->msg_out[2];
struct esiop_target *esiop_target =
(struct esiop_target*)sc->sc_c.targets[target];
struct esiop_lun *esiop_lun = esiop_target->esiop_lun[lun];
#ifdef SIOP_DEBUG
printf("%s:%d:%d: tag message %d (%d) rejected (status %d)\n",
sc->sc_c.sc_dev.dv_xname, target, lun, tag, esiop_cmd->cmd_c.tag,
esiop_cmd->cmd_c.status);
#endif
if (esiop_lun->active != NULL) {
printf("%s: untagged command already running for target %d "
"lun %d (status %d)\n", sc->sc_c.sc_dev.dv_xname,
target, lun, esiop_lun->active->cmd_c.status);
return -1;
}
/* clear tag slot */
esiop_lun->tactive[tag] = NULL;
/* add command to non-tagged slot */
esiop_lun->active = esiop_cmd;
esiop_cmd->cmd_c.flags &= ~CMDFL_TAG;
esiop_cmd->cmd_c.tag = -1;
/* update DSA table */
esiop_script_write(sc, esiop_target->lun_table_offset +
lun * 2 + A_target_luntbl / sizeof(u_int32_t),
esiop_cmd->cmd_c.dsa);
esiop_script_sync(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
return 0;
}
/*
* handle a bus reset: reset chip, unqueue all active commands, free all
* target struct and report lossage to upper layer.
* As the upper layer may requeue immediatly we have to first store
* all active commands in a temporary queue.
*/
void
esiop_handle_reset(sc)
struct esiop_softc *sc;
{
struct esiop_cmd *esiop_cmd;
struct esiop_lun *esiop_lun;
int target, lun, tag;
/*
* scsi bus reset. reset the chip and restart
* the queue. Need to clean up all active commands
*/
printf("%s: scsi bus reset\n", sc->sc_c.sc_dev.dv_xname);
/* stop, reset and restart the chip */
esiop_reset(sc);
if (sc->sc_flags & SCF_CHAN_NOSLOT) {
/* chip has been reset, all slots are free now */
sc->sc_flags &= ~SCF_CHAN_NOSLOT;
scsipi_channel_thaw(&sc->sc_c.sc_chan, 1);
}
/*
* Process all commands: first commands completes, then commands
* being executed
*/
esiop_checkdone(sc);
for (target = 0; target < sc->sc_c.sc_chan.chan_ntargets;
target++) {
struct esiop_target *esiop_target =
(struct esiop_target *)sc->sc_c.targets[target];
if (esiop_target == NULL)
continue;
for (lun = 0; lun < 8; lun++) {
esiop_lun = esiop_target->esiop_lun[lun];
if (esiop_lun == NULL)
continue;
for (tag = -1; tag <
((sc->sc_c.targets[target]->flags & TARF_TAG) ?
ESIOP_NTAG : 0);
tag++) {
if (tag >= 0)
esiop_cmd = esiop_lun->tactive[tag];
else
esiop_cmd = esiop_lun->active;
if (esiop_cmd == NULL)
continue;
scsipi_printaddr(esiop_cmd->cmd_c.xs->xs_periph);
printf("command with tag id %d reset\n", tag);
esiop_cmd->cmd_c.xs->error =
(esiop_cmd->cmd_c.flags & CMDFL_TIMEOUT) ?
XS_TIMEOUT : XS_RESET;
esiop_cmd->cmd_c.xs->status = SCSI_SIOP_NOCHECK;
if (tag >= 0)
esiop_lun->tactive[tag] = NULL;
else
esiop_lun->active = NULL;
esiop_cmd->cmd_c.status = CMDST_DONE;
esiop_scsicmd_end(esiop_cmd, 0);
}
}
sc->sc_c.targets[target]->status = TARST_ASYNC;
sc->sc_c.targets[target]->flags &= ~(TARF_ISWIDE | TARF_ISDT);
sc->sc_c.targets[target]->period =
sc->sc_c.targets[target]->offset = 0;
siop_update_xfer_mode(&sc->sc_c, target);
}
scsipi_async_event(&sc->sc_c.sc_chan, ASYNC_EVENT_RESET, NULL);
}
void
esiop_scsipi_request(chan, req, arg)
struct scsipi_channel *chan;
scsipi_adapter_req_t req;
void *arg;
{
struct scsipi_xfer *xs;
struct scsipi_periph *periph;
struct esiop_softc *sc = (void *)chan->chan_adapter->adapt_dev;
struct esiop_cmd *esiop_cmd;
struct esiop_target *esiop_target;
int s, error, i;
int target;
int lun;
switch (req) {
case ADAPTER_REQ_RUN_XFER:
xs = arg;
periph = xs->xs_periph;
target = periph->periph_target;
lun = periph->periph_lun;
s = splbio();
/*
* first check if there are pending complete commands.
* this can free us some resources (in the rings for example).
* we have to lock it to avoid recursion.
*/
if ((sc->sc_flags & SCF_CHAN_ADAPTREQ) == 0) {
sc->sc_flags |= SCF_CHAN_ADAPTREQ;
esiop_checkdone(sc);
sc->sc_flags &= ~SCF_CHAN_ADAPTREQ;
}
#ifdef SIOP_DEBUG_SCHED
printf("starting cmd for %d:%d tag %d(%d)\n", target, lun,
xs->xs_tag_type, xs->xs_tag_id);
#endif
esiop_cmd = TAILQ_FIRST(&sc->free_list);
if (esiop_cmd == NULL) {
xs->error = XS_RESOURCE_SHORTAGE;
scsipi_done(xs);
splx(s);
return;
}
TAILQ_REMOVE(&sc->free_list, esiop_cmd, next);
#ifdef DIAGNOSTIC
if (esiop_cmd->cmd_c.status != CMDST_FREE)
panic("siop_scsicmd: new cmd not free");
#endif
esiop_target = (struct esiop_target*)sc->sc_c.targets[target];
if (esiop_target == NULL) {
#ifdef SIOP_DEBUG
printf("%s: alloc siop_target for target %d\n",
sc->sc_c.sc_dev.dv_xname, target);
#endif
sc->sc_c.targets[target] =
malloc(sizeof(struct esiop_target),
M_DEVBUF, M_NOWAIT | M_ZERO);
if (sc->sc_c.targets[target] == NULL) {
printf("%s: can't malloc memory for "
"target %d\n", sc->sc_c.sc_dev.dv_xname,
target);
xs->error = XS_RESOURCE_SHORTAGE;
scsipi_done(xs);
splx(s);
return;
}
esiop_target =
(struct esiop_target*)sc->sc_c.targets[target];
esiop_target->target_c.status = TARST_PROBING;
esiop_target->target_c.flags = 0;
esiop_target->target_c.id =
sc->sc_c.clock_div << 24; /* scntl3 */
esiop_target->target_c.id |= target << 16; /* id */
/* esiop_target->target_c.id |= 0x0 << 8; scxfer is 0 */
for (i=0; i < 8; i++)
esiop_target->esiop_lun[i] = NULL;
esiop_target_register(sc, target);
}
if (esiop_target->esiop_lun[lun] == NULL) {
esiop_target->esiop_lun[lun] =
malloc(sizeof(struct esiop_lun), M_DEVBUF,
M_NOWAIT|M_ZERO);
if (esiop_target->esiop_lun[lun] == NULL) {
printf("%s: can't alloc esiop_lun for "
"target %d lun %d\n",
sc->sc_c.sc_dev.dv_xname, target, lun);
xs->error = XS_RESOURCE_SHORTAGE;
scsipi_done(xs);
splx(s);
return;
}
}
esiop_cmd->cmd_c.siop_target = sc->sc_c.targets[target];
esiop_cmd->cmd_c.xs = xs;
esiop_cmd->cmd_c.flags = 0;
esiop_cmd->cmd_c.status = CMDST_READY;
/* load the DMA maps */
error = bus_dmamap_load(sc->sc_c.sc_dmat,
esiop_cmd->cmd_c.dmamap_cmd,
xs->cmd, xs->cmdlen, NULL, BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to load cmd DMA map: %d\n",
sc->sc_c.sc_dev.dv_xname, error);
xs->error = XS_DRIVER_STUFFUP;
scsipi_done(xs);
splx(s);
return;
}
if (xs->xs_control & (XS_CTL_DATA_IN | XS_CTL_DATA_OUT)) {
error = bus_dmamap_load(sc->sc_c.sc_dmat,
esiop_cmd->cmd_c.dmamap_data, xs->data, xs->datalen,
NULL, BUS_DMA_NOWAIT | BUS_DMA_STREAMING |
((xs->xs_control & XS_CTL_DATA_IN) ?
BUS_DMA_READ : BUS_DMA_WRITE));
if (error) {
printf("%s: unable to load cmd DMA map: %d",
sc->sc_c.sc_dev.dv_xname, error);
xs->error = XS_DRIVER_STUFFUP;
scsipi_done(xs);
bus_dmamap_unload(sc->sc_c.sc_dmat,
esiop_cmd->cmd_c.dmamap_cmd);
splx(s);
return;
}
bus_dmamap_sync(sc->sc_c.sc_dmat,
esiop_cmd->cmd_c.dmamap_data, 0,
esiop_cmd->cmd_c.dmamap_data->dm_mapsize,
(xs->xs_control & XS_CTL_DATA_IN) ?
BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
}
bus_dmamap_sync(sc->sc_c.sc_dmat, esiop_cmd->cmd_c.dmamap_cmd,
0, esiop_cmd->cmd_c.dmamap_cmd->dm_mapsize,
BUS_DMASYNC_PREWRITE);
if (xs->xs_tag_type)
esiop_cmd->cmd_c.tag = xs->xs_tag_id;
else
esiop_cmd->cmd_c.tag = -1;
siop_setuptables(&esiop_cmd->cmd_c);
ESIOP_XFER(esiop_cmd, saved_offset) = htole32(SIOP_NOOFFSET);
ESIOP_XFER(esiop_cmd, tlq) = htole32(A_f_c_target | A_f_c_lun);
ESIOP_XFER(esiop_cmd, tlq) |=
htole32((target << 8) | (lun << 16));
if (esiop_cmd->cmd_c.flags & CMDFL_TAG) {
ESIOP_XFER(esiop_cmd, tlq) |= htole32(A_f_c_tag);
ESIOP_XFER(esiop_cmd, tlq) |=
htole32(esiop_cmd->cmd_c.tag << 24);
}
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
esiop_start(sc, esiop_cmd);
if (xs->xs_control & XS_CTL_POLL) {
/* poll for command completion */
while ((xs->xs_status & XS_STS_DONE) == 0) {
delay(1000);
esiop_intr(sc);
}
}
splx(s);
return;
case ADAPTER_REQ_GROW_RESOURCES:
#ifdef SIOP_DEBUG
printf("%s grow resources (%d)\n", sc->sc_c.sc_dev.dv_xname,
sc->sc_c.sc_adapt.adapt_openings);
#endif
esiop_morecbd(sc);
return;
case ADAPTER_REQ_SET_XFER_MODE:
{
struct scsipi_xfer_mode *xm = arg;
if (sc->sc_c.targets[xm->xm_target] == NULL)
return;
s = splbio();
if (xm->xm_mode & PERIPH_CAP_TQING) {
sc->sc_c.targets[xm->xm_target]->flags |= TARF_TAG;
/* allocate tag tables for this device */
for (lun = 0;
lun < sc->sc_c.sc_chan.chan_nluns; lun++) {
if (scsipi_lookup_periph(chan,
xm->xm_target, lun) != NULL)
esiop_add_dev(sc, xm->xm_target, lun);
}
}
if ((xm->xm_mode & PERIPH_CAP_WIDE16) &&
(sc->sc_c.features & SF_BUS_WIDE))
sc->sc_c.targets[xm->xm_target]->flags |= TARF_WIDE;
if (xm->xm_mode & PERIPH_CAP_SYNC)
sc->sc_c.targets[xm->xm_target]->flags |= TARF_SYNC;
if ((xm->xm_mode & PERIPH_CAP_DT) &&
(sc->sc_c.features & SF_CHIP_DT))
sc->sc_c.targets[xm->xm_target]->flags |= TARF_DT;
if ((xm->xm_mode &
(PERIPH_CAP_SYNC | PERIPH_CAP_WIDE16 | PERIPH_CAP_DT)) ||
sc->sc_c.targets[xm->xm_target]->status == TARST_PROBING)
sc->sc_c.targets[xm->xm_target]->status = TARST_ASYNC;
splx(s);
}
}
}
static void
esiop_start(sc, esiop_cmd)
struct esiop_softc *sc;
struct esiop_cmd *esiop_cmd;
{
struct esiop_lun *esiop_lun;
struct esiop_target *esiop_target;
int timeout;
int target, lun, slot;
/*
* first make sure to read valid data
*/
esiop_script_sync(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
/*
* We use a circular queue here. sc->sc_currschedslot points to a
* free slot, unless we have filled the queue. Check this.
*/
slot = sc->sc_currschedslot;
if ((esiop_script_read(sc, sc->sc_shedoffset + slot * CMD_SLOTSIZE) &
A_f_cmd_free) == 0) {
/*
* no more free slot, no need to continue. freeze the queue
* and requeue this command.
*/
scsipi_channel_freeze(&sc->sc_c.sc_chan, 1);
sc->sc_flags |= SCF_CHAN_NOSLOT;
esiop_script_write(sc, sc->sc_semoffset,
esiop_script_read(sc, sc->sc_semoffset) & ~A_sem_start);
esiop_script_sync(sc,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
esiop_cmd->cmd_c.xs->error = XS_REQUEUE;
esiop_cmd->cmd_c.xs->status = SCSI_SIOP_NOCHECK;
esiop_scsicmd_end(esiop_cmd, 0);
return;
}
/* OK, we can use this slot */
target = esiop_cmd->cmd_c.xs->xs_periph->periph_target;
lun = esiop_cmd->cmd_c.xs->xs_periph->periph_lun;
esiop_target = (struct esiop_target*)sc->sc_c.targets[target];
esiop_lun = esiop_target->esiop_lun[lun];
/* if non-tagged command active, panic: this shouldn't happen */
if (esiop_lun->active != NULL) {
panic("esiop_start: tagged cmd while untagged running");
}
#ifdef DIAGNOSTIC
/* sanity check the tag if needed */
if (esiop_cmd->cmd_c.flags & CMDFL_TAG) {
if (esiop_lun->tactive[esiop_cmd->cmd_c.tag] != NULL)
panic("esiop_start: tag not free");
if (esiop_cmd->cmd_c.tag >= ESIOP_NTAG ||
esiop_cmd->cmd_c.tag < 0) {
scsipi_printaddr(esiop_cmd->cmd_c.xs->xs_periph);
printf(": tag id %d\n", esiop_cmd->cmd_c.tag);
panic("esiop_start: invalid tag id");
}
}
#endif
#ifdef SIOP_DEBUG_SCHED
printf("using slot %d for DSA 0x%lx\n", slot,
(u_long)esiop_cmd->cmd_c.dsa);
#endif
/* mark command as active */
if (esiop_cmd->cmd_c.status == CMDST_READY)
esiop_cmd->cmd_c.status = CMDST_ACTIVE;
else
panic("esiop_start: bad status");
/* DSA table for reselect */
if (esiop_cmd->cmd_c.flags & CMDFL_TAG) {
esiop_lun->tactive[esiop_cmd->cmd_c.tag] = esiop_cmd;
/* DSA table for reselect */
esiop_lun->lun_tagtbl->tbl[esiop_cmd->cmd_c.tag] =
htole32(esiop_cmd->cmd_c.dsa);
bus_dmamap_sync(sc->sc_c.sc_dmat,
esiop_lun->lun_tagtbl->tblblk->blkmap,
esiop_lun->lun_tagtbl->tbl_offset,
sizeof(u_int32_t) * ESIOP_NTAG, BUS_DMASYNC_PREWRITE);
} else {
esiop_lun->active = esiop_cmd;
esiop_script_write(sc,
esiop_target->lun_table_offset +
lun * 2 + A_target_luntbl / sizeof(u_int32_t),
esiop_cmd->cmd_c.dsa);
}
/* scheduler slot: DSA */
esiop_script_write(sc, sc->sc_shedoffset + slot * CMD_SLOTSIZE,
esiop_cmd->cmd_c.dsa);
/* make sure SCRIPT processor will read valid data */
esiop_script_sync(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
/* handle timeout */
if ((esiop_cmd->cmd_c.xs->xs_control & XS_CTL_POLL) == 0) {
/* start exire timer */
timeout = mstohz(esiop_cmd->cmd_c.xs->timeout);
if (timeout == 0)
timeout = 1;
callout_reset( &esiop_cmd->cmd_c.xs->xs_callout,
timeout, esiop_timeout, esiop_cmd);
}
/* Signal script it has some work to do */
bus_space_write_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh,
SIOP_ISTAT, ISTAT_SIGP);
/* update the current slot, and wait for IRQ */
sc->sc_currschedslot++;
if (sc->sc_currschedslot >= A_ncmd_slots)
sc->sc_currschedslot = 0;
return;
}
void
esiop_timeout(v)
void *v;
{
struct esiop_cmd *esiop_cmd = v;
struct esiop_softc *sc =
(struct esiop_softc *)esiop_cmd->cmd_c.siop_sc;
int s;
#ifdef SIOP_DEBUG
int slot, slotdsa;
#endif
s = splbio();
esiop_table_sync(esiop_cmd,
BUS_DMASYNC_POSTREAD |
BUS_DMASYNC_POSTWRITE);
scsipi_printaddr(esiop_cmd->cmd_c.xs->xs_periph);
#ifdef SIOP_DEBUG
printf("command timeout (status %d)\n", le32toh(esiop_cmd->cmd_tables->status));
esiop_script_sync(sc, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
for (slot = 0; slot < A_ncmd_slots; slot++) {
slotdsa = esiop_script_read(sc,
sc->sc_shedoffset + slot * CMD_SLOTSIZE);
if ((slotdsa & 0x01) == 0)
printf("slot %d not free (0x%x)\n", slot, slotdsa);
}
printf("istat 0x%x ", bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_ISTAT));
printf("DSP 0x%lx DSA 0x%x\n",
(u_long)(bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSP) - sc->sc_c.sc_scriptaddr),
bus_space_read_4(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_DSA));
bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_CTEST2);
printf("istat 0x%x\n", bus_space_read_1(sc->sc_c.sc_rt, sc->sc_c.sc_rh, SIOP_ISTAT));
#else
printf("command timeout, CDB: ");
scsipi_print_cdb(esiop_cmd->cmd_c.xs->cmd);
printf("\n");
#endif
/* reset the scsi bus */
siop_resetbus(&sc->sc_c);
/* deactivate callout */
callout_stop(&esiop_cmd->cmd_c.xs->xs_callout);
/*
* mark command has being timed out and just return;
* the bus reset will generate an interrupt,
* it will be handled in siop_intr()
*/
esiop_cmd->cmd_c.flags |= CMDFL_TIMEOUT;
splx(s);
return;
}
void
esiop_dump_script(sc)
struct esiop_softc *sc;
{
int i;
for (i = 0; i < PAGE_SIZE / 4; i += 2) {
printf("0x%04x: 0x%08x 0x%08x", i * 4,
le32toh(sc->sc_c.sc_script[i]),
le32toh(sc->sc_c.sc_script[i+1]));
if ((le32toh(sc->sc_c.sc_script[i]) & 0xe0000000) ==
0xc0000000) {
i++;
printf(" 0x%08x", le32toh(sc->sc_c.sc_script[i+1]));
}
printf("\n");
}
}
void
esiop_morecbd(sc)
struct esiop_softc *sc;
{
int error, i, s;
bus_dma_segment_t seg;
int rseg;
struct esiop_cbd *newcbd;
struct esiop_xfer *xfer;
bus_addr_t dsa;
/* allocate a new list head */
newcbd = malloc(sizeof(struct esiop_cbd), M_DEVBUF, M_NOWAIT|M_ZERO);
if (newcbd == NULL) {
printf("%s: can't allocate memory for command descriptors "
"head\n", sc->sc_c.sc_dev.dv_xname);
return;
}
/* allocate cmd list */
newcbd->cmds = malloc(sizeof(struct esiop_cmd) * SIOP_NCMDPB,
M_DEVBUF, M_NOWAIT|M_ZERO);
if (newcbd->cmds == NULL) {
printf("%s: can't allocate memory for command descriptors\n",
sc->sc_c.sc_dev.dv_xname);
goto bad3;
}
error = bus_dmamem_alloc(sc->sc_c.sc_dmat, PAGE_SIZE, PAGE_SIZE, 0,
&seg, 1, &rseg, BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to allocate cbd DMA memory, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad2;
}
error = bus_dmamem_map(sc->sc_c.sc_dmat, &seg, rseg, PAGE_SIZE,
(caddr_t *)&newcbd->xfers, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
if (error) {
printf("%s: unable to map cbd DMA memory, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad2;
}
error = bus_dmamap_create(sc->sc_c.sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
BUS_DMA_NOWAIT, &newcbd->xferdma);
if (error) {
printf("%s: unable to create cbd DMA map, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad1;
}
error = bus_dmamap_load(sc->sc_c.sc_dmat, newcbd->xferdma,
newcbd->xfers, PAGE_SIZE, NULL, BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to load cbd DMA map, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad0;
}
#ifdef DEBUG
printf("%s: alloc newcdb at PHY addr 0x%lx\n", sc->sc_c.sc_dev.dv_xname,
(unsigned long)newcbd->xferdma->dm_segs[0].ds_addr);
#endif
for (i = 0; i < SIOP_NCMDPB; i++) {
error = bus_dmamap_create(sc->sc_c.sc_dmat, MAXPHYS, SIOP_NSG,
MAXPHYS, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
&newcbd->cmds[i].cmd_c.dmamap_data);
if (error) {
printf("%s: unable to create data DMA map for cbd: "
"error %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad0;
}
error = bus_dmamap_create(sc->sc_c.sc_dmat,
sizeof(struct scsipi_generic), 1,
sizeof(struct scsipi_generic), 0,
BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
&newcbd->cmds[i].cmd_c.dmamap_cmd);
if (error) {
printf("%s: unable to create cmd DMA map for cbd %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad0;
}
newcbd->cmds[i].cmd_c.siop_sc = &sc->sc_c;
newcbd->cmds[i].esiop_cbdp = newcbd;
xfer = &newcbd->xfers[i];
newcbd->cmds[i].cmd_tables = (struct siop_common_xfer *)xfer;
memset(newcbd->cmds[i].cmd_tables, 0,
sizeof(struct esiop_xfer));
dsa = newcbd->xferdma->dm_segs[0].ds_addr +
i * sizeof(struct esiop_xfer);
newcbd->cmds[i].cmd_c.dsa = dsa;
newcbd->cmds[i].cmd_c.status = CMDST_FREE;
xfer->siop_tables.t_msgout.count= htole32(1);
xfer->siop_tables.t_msgout.addr = htole32(dsa);
xfer->siop_tables.t_msgin.count= htole32(1);
xfer->siop_tables.t_msgin.addr = htole32(dsa +
offsetof(struct siop_common_xfer, msg_in));
xfer->siop_tables.t_extmsgin.count= htole32(2);
xfer->siop_tables.t_extmsgin.addr = htole32(dsa +
offsetof(struct siop_common_xfer, msg_in) + 1);
xfer->siop_tables.t_extmsgdata.addr = htole32(dsa +
offsetof(struct siop_common_xfer, msg_in) + 3);
xfer->siop_tables.t_status.count= htole32(1);
xfer->siop_tables.t_status.addr = htole32(dsa +
offsetof(struct siop_common_xfer, status));
s = splbio();
TAILQ_INSERT_TAIL(&sc->free_list, &newcbd->cmds[i], next);
splx(s);
#ifdef SIOP_DEBUG
printf("tables[%d]: in=0x%x out=0x%x status=0x%x "
"offset=0x%x\n", i,
le32toh(newcbd->cmds[i].cmd_tables->t_msgin.addr),
le32toh(newcbd->cmds[i].cmd_tables->t_msgout.addr),
le32toh(newcbd->cmds[i].cmd_tables->t_status.addr,
le32toh(newcbd->cmds[i].cmd_tables->t_offset.addr));
#endif
}
s = splbio();
TAILQ_INSERT_TAIL(&sc->cmds, newcbd, next);
sc->sc_c.sc_adapt.adapt_openings += SIOP_NCMDPB;
splx(s);
return;
bad0:
bus_dmamap_unload(sc->sc_c.sc_dmat, newcbd->xferdma);
bus_dmamap_destroy(sc->sc_c.sc_dmat, newcbd->xferdma);
bad1:
bus_dmamem_free(sc->sc_c.sc_dmat, &seg, rseg);
bad2:
free(newcbd->cmds, M_DEVBUF);
bad3:
free(newcbd, M_DEVBUF);
return;
}
void
esiop_moretagtbl(sc)
struct esiop_softc *sc;
{
int error, i, j, s;
bus_dma_segment_t seg;
int rseg;
struct esiop_dsatblblk *newtblblk;
struct esiop_dsatbl *newtbls;
u_int32_t *tbls;
/* allocate a new list head */
newtblblk = malloc(sizeof(struct esiop_dsatblblk),
M_DEVBUF, M_NOWAIT|M_ZERO);
if (newtblblk == NULL) {
printf("%s: can't allocate memory for tag DSA table block\n",
sc->sc_c.sc_dev.dv_xname);
return;
}
/* allocate tbl list */
newtbls = malloc(sizeof(struct esiop_dsatbl) * ESIOP_NTPB,
M_DEVBUF, M_NOWAIT|M_ZERO);
if (newtbls == NULL) {
printf("%s: can't allocate memory for command descriptors\n",
sc->sc_c.sc_dev.dv_xname);
goto bad3;
}
error = bus_dmamem_alloc(sc->sc_c.sc_dmat, PAGE_SIZE, PAGE_SIZE, 0,
&seg, 1, &rseg, BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to allocate tbl DMA memory, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad2;
}
error = bus_dmamem_map(sc->sc_c.sc_dmat, &seg, rseg, PAGE_SIZE,
(void *)&tbls, BUS_DMA_NOWAIT|BUS_DMA_COHERENT);
if (error) {
printf("%s: unable to map tbls DMA memory, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad2;
}
error = bus_dmamap_create(sc->sc_c.sc_dmat, PAGE_SIZE, 1, PAGE_SIZE, 0,
BUS_DMA_NOWAIT, &newtblblk->blkmap);
if (error) {
printf("%s: unable to create tbl DMA map, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad1;
}
error = bus_dmamap_load(sc->sc_c.sc_dmat, newtblblk->blkmap,
tbls, PAGE_SIZE, NULL, BUS_DMA_NOWAIT);
if (error) {
printf("%s: unable to load tbl DMA map, error = %d\n",
sc->sc_c.sc_dev.dv_xname, error);
goto bad0;
}
#ifdef DEBUG
printf("%s: alloc new tag DSA table at PHY addr 0x%lx\n",
sc->sc_c.sc_dev.dv_xname,
(unsigned long)newtblblk->blkmap->dm_segs[0].ds_addr);
#endif
for (i = 0; i < ESIOP_NTPB; i++) {
newtbls[i].tblblk = newtblblk;
newtbls[i].tbl = &tbls[i * ESIOP_NTAG];
newtbls[i].tbl_offset = i * ESIOP_NTAG * sizeof(u_int32_t);
newtbls[i].tbl_dsa = newtblblk->blkmap->dm_segs[0].ds_addr +
newtbls[i].tbl_offset;
for (j = 0; j < ESIOP_NTAG; j++)
newtbls[i].tbl[j] = j;
s = splbio();
TAILQ_INSERT_TAIL(&sc->free_tagtbl, &newtbls[i], next);
splx(s);
}
s = splbio();
TAILQ_INSERT_TAIL(&sc->tag_tblblk, newtblblk, next);
splx(s);
return;
bad0:
bus_dmamap_unload(sc->sc_c.sc_dmat, newtblblk->blkmap);
bus_dmamap_destroy(sc->sc_c.sc_dmat, newtblblk->blkmap);
bad1:
bus_dmamem_free(sc->sc_c.sc_dmat, &seg, rseg);
bad2:
free(newtbls, M_DEVBUF);
bad3:
free(newtblblk, M_DEVBUF);
return;
}
void
esiop_update_scntl3(sc, _siop_target)
struct esiop_softc *sc;
struct siop_common_target *_siop_target;
{
struct esiop_target *esiop_target = (struct esiop_target *)_siop_target;
esiop_script_write(sc, esiop_target->lun_table_offset,
esiop_target->target_c.id);
esiop_script_sync(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
void
esiop_add_dev(sc, target, lun)
struct esiop_softc *sc;
int target;
int lun;
{
struct esiop_target *esiop_target =
(struct esiop_target *)sc->sc_c.targets[target];
struct esiop_lun *esiop_lun = esiop_target->esiop_lun[lun];
if (esiop_lun->lun_tagtbl != NULL)
return; /* already allocated */
/* we need a tag DSA table */
esiop_lun->lun_tagtbl= TAILQ_FIRST(&sc->free_tagtbl);
if (esiop_lun->lun_tagtbl == NULL) {
esiop_moretagtbl(sc);
esiop_lun->lun_tagtbl= TAILQ_FIRST(&sc->free_tagtbl);
if (esiop_lun->lun_tagtbl == NULL) {
/* no resources, run untagged */
esiop_target->target_c.flags &= ~TARF_TAG;
return;
}
}
TAILQ_REMOVE(&sc->free_tagtbl, esiop_lun->lun_tagtbl, next);
/* Update LUN DSA table */
esiop_script_write(sc, esiop_target->lun_table_offset +
lun * 2 + A_target_luntbl_tag / sizeof(u_int32_t),
esiop_lun->lun_tagtbl->tbl_dsa);
esiop_script_sync(sc, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
void
esiop_del_dev(sc, target, lun)
struct esiop_softc *sc;
int target;
int lun;
{
struct esiop_target *esiop_target;
#ifdef SIOP_DEBUG
printf("%s:%d:%d: free lun sw entry\n",
sc->sc_c.sc_dev.dv_xname, target, lun);
#endif
if (sc->sc_c.targets[target] == NULL)
return;
esiop_target = (struct esiop_target *)sc->sc_c.targets[target];
free(esiop_target->esiop_lun[lun], M_DEVBUF);
esiop_target->esiop_lun[lun] = NULL;
}
void
esiop_target_register(sc, target)
struct esiop_softc *sc;
u_int32_t target;
{
struct esiop_target *esiop_target =
(struct esiop_target *)sc->sc_c.targets[target];
struct esiop_lun *esiop_lun;
int lun;
/* get a DSA table for this target */
esiop_target->lun_table_offset = sc->sc_free_offset;
sc->sc_free_offset += sc->sc_c.sc_chan.chan_nluns * 2 + 2;
#ifdef SIOP_DEBUG
printf("%s: lun table for target %d offset %d free offset %d\n",
sc->sc_c.sc_dev.dv_xname, target, esiop_target->lun_table_offset,
sc->sc_free_offset);
#endif
/* first 32 bytes are ID (for select) */
esiop_script_write(sc, esiop_target->lun_table_offset,
esiop_target->target_c.id);
/* Record this table in the target DSA table */
esiop_script_write(sc,
sc->sc_target_table_offset + target,
(esiop_target->lun_table_offset * sizeof(u_int32_t)) +
sc->sc_c.sc_scriptaddr);
/* if we have a tag table, register it */
for (lun = 0; lun < sc->sc_c.sc_chan.chan_nluns; lun++) {
esiop_lun = esiop_target->esiop_lun[lun];
if (esiop_lun == NULL)
continue;
if (esiop_lun->lun_tagtbl)
esiop_script_write(sc, esiop_target->lun_table_offset +
lun * 2 + A_target_luntbl_tag / sizeof(u_int32_t),
esiop_lun->lun_tagtbl->tbl_dsa);
}
esiop_script_sync(sc,
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
}
#ifdef SIOP_STATS
void
esiop_printstats()
{
printf("esiop_stat_intr %d\n", esiop_stat_intr);
printf("esiop_stat_intr_shortxfer %d\n", esiop_stat_intr_shortxfer);
printf("esiop_stat_intr_xferdisc %d\n", esiop_stat_intr_xferdisc);
printf("esiop_stat_intr_sdp %d\n", esiop_stat_intr_sdp);
printf("esiop_stat_intr_done %d\n", esiop_stat_intr_done);
printf("esiop_stat_intr_lunresel %d\n", esiop_stat_intr_lunresel);
printf("esiop_stat_intr_qfull %d\n", esiop_stat_intr_qfull);
}
#endif