freebsd-skq/sys/dev/advansys/advlib.c
ken 2bb789d7a3 Add a number of interrelated CAM feature enhancements and bug fixes.
NOTE:  These changes will require recompilation of any userland
applications, like cdrecord, xmcd, etc., that use the CAM passthrough
interface.  A make world is recommended.

camcontrol.[c8]:
 - We now support two new commands, "tags" and "negotiate".

	- The tags commands allows users to view the number of tagged
	  openings for a device as well as a number of other related
	  parameters, and it allows users to set tagged openings for
	  a device.

	- The negotiate command allows users to enable and disable
	  disconnection and tagged queueing, set sync rates, offsets
	  and bus width.  Note that not all of those features are
	  available for all controllers.  Only the adv, ahc, and ncr
	  drivers fully support all of the features at this point.
	  Some cards do not allow the setting of sync rates, offsets and
	  the like, and some of the drivers don't have any facilities to
	  do so.  Some drivers, like the adw driver, only support enabling
	  or disabling sync negotiation, but do not support setting sync
	  rates.

 - new description in the camcontrol man page of how to format a disk
 - cleanup of the camcontrol inquiry command
 - add support in the 'devlist' command for skipping unconfigured devices if
   -v was not specified on the command line.
 - make use of the new base_transfer_speed in the path inquiry CCB.
 - fix CCB bzero cases

cam_xpt.c, cam_sim.[ch], cam_ccb.h:

 - new flags on many CCB function codes to designate whether they're
   non-immediate, use a user-supplied CCB, and can only be passed from
   userland programs via the xpt device.  Use these flags in the transport
   layer and pass driver to categorize CCBs.

 - new flag in the transport layer device matching code for device nodes
   that indicates whether a device is unconfigured

 - bump the CAM version from 0x10 to 0x11

 - Change the CAM ioctls to use the version as their group code, so we can
   force users to recompile code even when the CCB size doesn't change.

 - add + fill in a new value in the path inquiry CCB, base_transfer_speed.
   Remove a corresponding field from the cam_sim structure, and add code to
   every SIM to set this field to the proper value.

 - Fix the set transfer settings code in the transport layer.

scsi_cd.c:

 - make some variables volatile instead of just casting them in various
   places
 - fix a race condition in the changer code
 - attach unless we get a "logical unit not supported" error.  This should
   fix all of the cases where people have devices that return weird errors
   when they don't have media in the drive.

scsi_da.c:

 - attach unless we get a "logical unit not supported" error

scsi_pass.c:

 - for immediate CCBs, just malloc a CCB to send the user request in.  This
   gets rid of the 'held' count problem in camcontrol tags.

scsi_pass.h:

 - change the CAM ioctls to use the CAM version as their group code.

adv driver:

 - Allow changing the sync rate and offset separately.

adw driver

 - Allow changing the sync rate and offset separately.

aha driver:

 - Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.

ahc driver:

 - Allow setting offset and sync rate separately

bt driver:

 - Don't return CAM_REQ_CMP for SET_TRAN_SETTINGS CCBs.

NCR driver:

 - Fix the ultra/ultra 2 negotiation bug
 - allow setting both the sync rate and offset separately

Other HBA drivers:
 - Put code in to set the base_transfer_speed field for
   XPT_GET_TRAN_SETTINGS CCBs.

Reviewed by:	gibbs, mjacob (isp), imp (aha)
1999-05-06 20:16:39 +00:00

2047 lines
52 KiB
C

/*
* Low level routines for the Advanced Systems Inc. SCSI controllers chips
*
* Copyright (c) 1996-1997 Justin Gibbs.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions, and the following disclaimer,
* without modification, immediately at the beginning of the file.
* 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.
*
* $Id: advlib.c,v 1.12 1999/04/19 21:27:36 gibbs Exp $
*/
/*
* Ported from:
* advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
*
* Copyright (c) 1995-1996 Advanced System Products, Inc.
* All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
* code retain the above copyright notice and this comment without
* modification.
*/
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <machine/bus_pio.h>
#include <machine/bus.h>
#include <machine/clock.h>
#include <cam/cam.h>
#include <cam/cam_ccb.h>
#include <cam/cam_sim.h>
#include <cam/cam_xpt_sim.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
#include <cam/scsi/scsi_da.h>
#include <cam/scsi/scsi_cd.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/pmap.h>
#include <dev/advansys/advansys.h>
#include <dev/advansys/advmcode.h>
struct adv_quirk_entry {
struct scsi_inquiry_pattern inq_pat;
u_int8_t quirks;
#define ADV_QUIRK_FIX_ASYN_XFER_ALWAYS 0x01
#define ADV_QUIRK_FIX_ASYN_XFER 0x02
};
static struct adv_quirk_entry adv_quirk_table[] =
{
{
{ T_CDROM, SIP_MEDIA_REMOVABLE, "HP", "*", "*" },
ADV_QUIRK_FIX_ASYN_XFER_ALWAYS|ADV_QUIRK_FIX_ASYN_XFER
},
{
{ T_CDROM, SIP_MEDIA_REMOVABLE, "NEC", "CD-ROM DRIVE", "*" },
0
},
{
{
T_SEQUENTIAL, SIP_MEDIA_REMOVABLE,
"TANDBERG", " TDC 36", "*"
},
0
},
{
{ T_SEQUENTIAL, SIP_MEDIA_REMOVABLE, "WANGTEK", "*", "*" },
0
},
{
{
T_PROCESSOR, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
"*", "*", "*"
},
0
},
{
{
T_SCANNER, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
"*", "*", "*"
},
0
},
{
/* Default quirk entry */
{
T_ANY, SIP_MEDIA_REMOVABLE|SIP_MEDIA_FIXED,
/*vendor*/"*", /*product*/"*", /*revision*/"*"
},
ADV_QUIRK_FIX_ASYN_XFER,
}
};
/*
* Allowable periods in ns
*/
static u_int8_t adv_sdtr_period_tbl[] =
{
25,
30,
35,
40,
50,
60,
70,
85
};
static u_int8_t adv_sdtr_period_tbl_ultra[] =
{
12,
19,
25,
32,
38,
44,
50,
57,
63,
69,
75,
82,
88,
94,
100,
107
};
struct ext_msg {
u_int8_t msg_type;
u_int8_t msg_len;
u_int8_t msg_req;
union {
struct {
u_int8_t sdtr_xfer_period;
u_int8_t sdtr_req_ack_offset;
} sdtr;
struct {
u_int8_t wdtr_width;
} wdtr;
struct {
u_int8_t mdp[4];
} mdp;
} u_ext_msg;
u_int8_t res;
};
#define xfer_period u_ext_msg.sdtr.sdtr_xfer_period
#define req_ack_offset u_ext_msg.sdtr.sdtr_req_ack_offset
#define wdtr_width u_ext_msg.wdtr.wdtr_width
#define mdp_b3 u_ext_msg.mdp_b3
#define mdp_b2 u_ext_msg.mdp_b2
#define mdp_b1 u_ext_msg.mdp_b1
#define mdp_b0 u_ext_msg.mdp_b0
/*
* Some of the early PCI adapters have problems with
* async transfers. Instead use an offset of 1.
*/
#define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41
/* LRAM routines */
static void adv_read_lram_16_multi(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *buffer, int count);
static void adv_write_lram_16_multi(struct adv_softc *adv,
u_int16_t s_addr, u_int16_t *buffer,
int count);
static void adv_mset_lram_16(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t set_value, int count);
static u_int32_t adv_msum_lram_16(struct adv_softc *adv, u_int16_t s_addr,
int count);
static int adv_write_and_verify_lram_16(struct adv_softc *adv,
u_int16_t addr, u_int16_t value);
static u_int32_t adv_read_lram_32(struct adv_softc *adv, u_int16_t addr);
static void adv_write_lram_32(struct adv_softc *adv, u_int16_t addr,
u_int32_t value);
static void adv_write_lram_32_multi(struct adv_softc *adv,
u_int16_t s_addr, u_int32_t *buffer,
int count);
/* EEPROM routines */
static u_int16_t adv_read_eeprom_16(struct adv_softc *adv, u_int8_t addr);
static u_int16_t adv_write_eeprom_16(struct adv_softc *adv, u_int8_t addr,
u_int16_t value);
static int adv_write_eeprom_cmd_reg(struct adv_softc *adv,
u_int8_t cmd_reg);
static int adv_set_eeprom_config_once(struct adv_softc *adv,
struct adv_eeprom_config *eeconfig);
/* Initialization */
static u_int32_t adv_load_microcode(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *mcode_buf, u_int16_t mcode_size);
static void adv_reinit_lram(struct adv_softc *adv);
static void adv_init_lram(struct adv_softc *adv);
static int adv_init_microcode_var(struct adv_softc *adv);
static void adv_init_qlink_var(struct adv_softc *adv);
/* Interrupts */
static void adv_disable_interrupt(struct adv_softc *adv);
static void adv_enable_interrupt(struct adv_softc *adv);
static void adv_toggle_irq_act(struct adv_softc *adv);
/* Chip Control */
static int adv_stop_chip(struct adv_softc *adv);
static int adv_host_req_chip_halt(struct adv_softc *adv);
static void adv_set_chip_ih(struct adv_softc *adv, u_int16_t ins_code);
#if UNUSED
static u_int8_t adv_get_chip_scsi_ctrl(struct adv_softc *adv);
#endif
/* Queue handling and execution */
static __inline int
adv_sgcount_to_qcount(int sgcount);
static __inline int
adv_sgcount_to_qcount(int sgcount)
{
int n_sg_list_qs;
n_sg_list_qs = ((sgcount - 1) / ADV_SG_LIST_PER_Q);
if (((sgcount - 1) % ADV_SG_LIST_PER_Q) != 0)
n_sg_list_qs++;
return (n_sg_list_qs + 1);
}
static void adv_get_q_info(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *inbuf, int words);
static u_int adv_get_num_free_queues(struct adv_softc *adv, u_int8_t n_qs);
static u_int8_t adv_alloc_free_queues(struct adv_softc *adv,
u_int8_t free_q_head, u_int8_t n_free_q);
static u_int8_t adv_alloc_free_queue(struct adv_softc *adv,
u_int8_t free_q_head);
static int adv_send_scsi_queue(struct adv_softc *adv,
struct adv_scsi_q *scsiq,
u_int8_t n_q_required);
static void adv_put_ready_sg_list_queue(struct adv_softc *adv,
struct adv_scsi_q *scsiq,
u_int q_no);
static void adv_put_ready_queue(struct adv_softc *adv,
struct adv_scsi_q *scsiq, u_int q_no);
static void adv_put_scsiq(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *buffer, int words);
/* Messages */
static void adv_handle_extmsg_in(struct adv_softc *adv,
u_int16_t halt_q_addr, u_int8_t q_cntl,
target_bit_vector target_id,
int tid);
static void adv_msgout_sdtr(struct adv_softc *adv, u_int8_t sdtr_period,
u_int8_t sdtr_offset);
static void adv_set_sdtr_reg_at_id(struct adv_softc *adv, int id,
u_int8_t sdtr_data);
/* Exported functions first */
void
advasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg)
{
struct adv_softc *adv;
adv = (struct adv_softc *)callback_arg;
switch (code) {
case AC_FOUND_DEVICE:
{
struct ccb_getdev *cgd;
target_bit_vector target_mask;
int num_entries;
caddr_t match;
struct adv_quirk_entry *entry;
struct adv_target_transinfo* tinfo;
cgd = (struct ccb_getdev *)arg;
target_mask = ADV_TID_TO_TARGET_MASK(cgd->ccb_h.target_id);
num_entries = sizeof(adv_quirk_table)/sizeof(*adv_quirk_table);
match = cam_quirkmatch((caddr_t)&cgd->inq_data,
(caddr_t)adv_quirk_table,
num_entries, sizeof(*adv_quirk_table),
scsi_inquiry_match);
if (match == NULL)
panic("advasync: device didn't match wildcard entry!!");
entry = (struct adv_quirk_entry *)match;
if (adv->bug_fix_control & ADV_BUG_FIX_ASYN_USE_SYN) {
if ((entry->quirks & ADV_QUIRK_FIX_ASYN_XFER_ALWAYS)!=0)
adv->fix_asyn_xfer_always |= target_mask;
else
adv->fix_asyn_xfer_always &= ~target_mask;
/*
* We start out life with all bits set and clear them
* after we've determined that the fix isn't necessary.
* It may well be that we've already cleared a target
* before the full inquiry session completes, so don't
* gratuitously set a target bit even if it has this
* quirk. But, if the quirk exonerates a device, clear
* the bit now.
*/
if ((entry->quirks & ADV_QUIRK_FIX_ASYN_XFER) == 0)
adv->fix_asyn_xfer &= ~target_mask;
}
/*
* Reset our sync settings now that we've determined
* what quirks are in effect for the device.
*/
tinfo = &adv->tinfo[cgd->ccb_h.target_id];
adv_set_syncrate(adv, cgd->ccb_h.path,
cgd->ccb_h.target_id,
tinfo->current.period,
tinfo->current.offset,
ADV_TRANS_CUR);
break;
}
case AC_LOST_DEVICE:
{
u_int target_mask;
if (adv->bug_fix_control & ADV_BUG_FIX_ASYN_USE_SYN) {
target_mask = 0x01 << xpt_path_target_id(path);
adv->fix_asyn_xfer |= target_mask;
}
/*
* Revert to async transfers
* for the next device.
*/
adv_set_syncrate(adv, /*path*/NULL,
xpt_path_target_id(path),
/*period*/0,
/*offset*/0,
ADV_TRANS_GOAL|ADV_TRANS_CUR);
}
default:
break;
}
}
void
adv_set_bank(struct adv_softc *adv, u_int8_t bank)
{
u_int8_t control;
/*
* Start out with the bank reset to 0
*/
control = ADV_INB(adv, ADV_CHIP_CTRL)
& (~(ADV_CC_SINGLE_STEP | ADV_CC_TEST
| ADV_CC_DIAG | ADV_CC_SCSI_RESET
| ADV_CC_CHIP_RESET | ADV_CC_BANK_ONE));
if (bank == 1) {
control |= ADV_CC_BANK_ONE;
} else if (bank == 2) {
control |= ADV_CC_DIAG | ADV_CC_BANK_ONE;
}
ADV_OUTB(adv, ADV_CHIP_CTRL, control);
}
u_int8_t
adv_read_lram_8(struct adv_softc *adv, u_int16_t addr)
{
u_int8_t byte_data;
u_int16_t word_data;
/*
* LRAM is accessed on 16bit boundaries.
*/
ADV_OUTW(adv, ADV_LRAM_ADDR, addr & 0xFFFE);
word_data = ADV_INW(adv, ADV_LRAM_DATA);
if (addr & 1) {
#if BYTE_ORDER == BIG_ENDIAN
byte_data = (u_int8_t)(word_data & 0xFF);
#else
byte_data = (u_int8_t)((word_data >> 8) & 0xFF);
#endif
} else {
#if BYTE_ORDER == BIG_ENDIAN
byte_data = (u_int8_t)((word_data >> 8) & 0xFF);
#else
byte_data = (u_int8_t)(word_data & 0xFF);
#endif
}
return (byte_data);
}
void
adv_write_lram_8(struct adv_softc *adv, u_int16_t addr, u_int8_t value)
{
u_int16_t word_data;
word_data = adv_read_lram_16(adv, addr & 0xFFFE);
if (addr & 1) {
word_data &= 0x00FF;
word_data |= (((u_int8_t)value << 8) & 0xFF00);
} else {
word_data &= 0xFF00;
word_data |= ((u_int8_t)value & 0x00FF);
}
adv_write_lram_16(adv, addr & 0xFFFE, word_data);
}
u_int16_t
adv_read_lram_16(struct adv_softc *adv, u_int16_t addr)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
return (ADV_INW(adv, ADV_LRAM_DATA));
}
void
adv_write_lram_16(struct adv_softc *adv, u_int16_t addr, u_int16_t value)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
ADV_OUTW(adv, ADV_LRAM_DATA, value);
}
/*
* Determine if there is a board at "iobase" by looking
* for the AdvanSys signatures. Return 1 if a board is
* found, 0 otherwise.
*/
int
adv_find_signature(bus_space_tag_t tag, bus_space_handle_t bsh)
{
u_int16_t signature;
if (bus_space_read_1(tag, bsh, ADV_SIGNATURE_BYTE) == ADV_1000_ID1B) {
signature = bus_space_read_2(tag, bsh, ADV_SIGNATURE_WORD);
if ((signature == ADV_1000_ID0W)
|| (signature == ADV_1000_ID0W_FIX))
return (1);
}
return (0);
}
void
adv_lib_init(struct adv_softc *adv)
{
if ((adv->type & ADV_ULTRA) != 0) {
adv->sdtr_period_tbl = adv_sdtr_period_tbl_ultra;
adv->sdtr_period_tbl_size = sizeof(adv_sdtr_period_tbl_ultra);
} else {
adv->sdtr_period_tbl = adv_sdtr_period_tbl;
adv->sdtr_period_tbl_size = sizeof(adv_sdtr_period_tbl);
}
}
u_int16_t
adv_get_eeprom_config(struct adv_softc *adv, struct
adv_eeprom_config *eeprom_config)
{
u_int16_t sum;
u_int16_t *wbuf;
u_int8_t cfg_beg;
u_int8_t cfg_end;
u_int8_t s_addr;
wbuf = (u_int16_t *)eeprom_config;
sum = 0;
for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
*wbuf = adv_read_eeprom_16(adv, s_addr);
sum += *wbuf;
}
if (adv->type & ADV_VL) {
cfg_beg = ADV_EEPROM_CFG_BEG_VL;
cfg_end = ADV_EEPROM_MAX_ADDR_VL;
} else {
cfg_beg = ADV_EEPROM_CFG_BEG;
cfg_end = ADV_EEPROM_MAX_ADDR;
}
for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
*wbuf = adv_read_eeprom_16(adv, s_addr);
sum += *wbuf;
#if ADV_DEBUG_EEPROM
printf("Addr 0x%x: 0x%04x\n", s_addr, *wbuf);
#endif
}
*wbuf = adv_read_eeprom_16(adv, s_addr);
return (sum);
}
int
adv_set_eeprom_config(struct adv_softc *adv,
struct adv_eeprom_config *eeprom_config)
{
int retry;
retry = 0;
while (1) {
if (adv_set_eeprom_config_once(adv, eeprom_config) == 0) {
break;
}
if (++retry > ADV_EEPROM_MAX_RETRY) {
break;
}
}
return (retry > ADV_EEPROM_MAX_RETRY);
}
int
adv_reset_chip_and_scsi_bus(struct adv_softc *adv)
{
adv_stop_chip(adv);
ADV_OUTB(adv, ADV_CHIP_CTRL,
ADV_CC_CHIP_RESET | ADV_CC_SCSI_RESET | ADV_CC_HALT);
DELAY(200 * 1000);
adv_set_chip_ih(adv, ADV_INS_RFLAG_WTM);
adv_set_chip_ih(adv, ADV_INS_HALT);
ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_CHIP_RESET | ADV_CC_HALT);
ADV_OUTB(adv, ADV_CHIP_CTRL, ADV_CC_HALT);
DELAY(200 * 1000);
return (adv_is_chip_halted(adv));
}
int
adv_test_external_lram(struct adv_softc* adv)
{
u_int16_t q_addr;
u_int16_t saved_value;
int success;
success = 0;
q_addr = ADV_QNO_TO_QADDR(241);
saved_value = adv_read_lram_16(adv, q_addr);
if (adv_write_and_verify_lram_16(adv, q_addr, 0x55AA) == 0) {
success = 1;
adv_write_lram_16(adv, q_addr, saved_value);
}
return (success);
}
int
adv_init_lram_and_mcode(struct adv_softc *adv)
{
u_int32_t retval;
adv_disable_interrupt(adv);
adv_init_lram(adv);
retval = adv_load_microcode(adv, 0, (u_int16_t *)adv_mcode,
adv_mcode_size);
if (retval != adv_mcode_chksum) {
printf("adv%d: Microcode download failed checksum!\n",
adv->unit);
return (1);
}
if (adv_init_microcode_var(adv) != 0)
return (1);
adv_enable_interrupt(adv);
return (0);
}
u_int8_t
adv_get_chip_irq(struct adv_softc *adv)
{
u_int16_t cfg_lsw;
u_int8_t chip_irq;
cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW);
if ((adv->type & ADV_VL) != 0) {
chip_irq = (u_int8_t)(((cfg_lsw >> 2) & 0x07));
if ((chip_irq == 0) ||
(chip_irq == 4) ||
(chip_irq == 7)) {
return (0);
}
return (chip_irq + (ADV_MIN_IRQ_NO - 1));
}
chip_irq = (u_int8_t)(((cfg_lsw >> 2) & 0x03));
if (chip_irq == 3)
chip_irq += 2;
return (chip_irq + ADV_MIN_IRQ_NO);
}
u_int8_t
adv_set_chip_irq(struct adv_softc *adv, u_int8_t irq_no)
{
u_int16_t cfg_lsw;
if ((adv->type & ADV_VL) != 0) {
if (irq_no != 0) {
if ((irq_no < ADV_MIN_IRQ_NO)
|| (irq_no > ADV_MAX_IRQ_NO)) {
irq_no = 0;
} else {
irq_no -= ADV_MIN_IRQ_NO - 1;
}
}
cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW) & 0xFFE3;
cfg_lsw |= 0x0010;
ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
adv_toggle_irq_act(adv);
cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW) & 0xFFE0;
cfg_lsw |= (irq_no & 0x07) << 2;
ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
adv_toggle_irq_act(adv);
} else if ((adv->type & ADV_ISA) != 0) {
if (irq_no == 15)
irq_no -= 2;
irq_no -= ADV_MIN_IRQ_NO;
cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW) & 0xFFF3;
cfg_lsw |= (irq_no & 0x03) << 2;
ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
}
return (adv_get_chip_irq(adv));
}
void
adv_set_chip_scsiid(struct adv_softc *adv, int new_id)
{
u_int16_t cfg_lsw;
cfg_lsw = ADV_INW(adv, ADV_CONFIG_LSW);
if (ADV_CONFIG_SCSIID(cfg_lsw) == new_id)
return;
cfg_lsw &= ~ADV_CFG_LSW_SCSIID;
cfg_lsw |= (new_id & ADV_MAX_TID) << ADV_CFG_LSW_SCSIID_SHIFT;
ADV_OUTW(adv, ADV_CONFIG_LSW, cfg_lsw);
}
int
adv_execute_scsi_queue(struct adv_softc *adv, struct adv_scsi_q *scsiq,
u_int32_t datalen)
{
struct adv_target_transinfo* tinfo;
u_int32_t *p_data_addr;
u_int32_t *p_data_bcount;
int disable_syn_offset_one_fix;
int retval;
u_int n_q_required;
u_int32_t addr;
u_int8_t sg_entry_cnt;
u_int8_t target_ix;
u_int8_t sg_entry_cnt_minus_one;
u_int8_t tid_no;
scsiq->q1.q_no = 0;
retval = 1; /* Default to error case */
target_ix = scsiq->q2.target_ix;
tid_no = ADV_TIX_TO_TID(target_ix);
tinfo = &adv->tinfo[tid_no];
if (scsiq->cdbptr[0] == REQUEST_SENSE) {
/* Renegotiate if appropriate. */
adv_set_syncrate(adv, /*struct cam_path */NULL,
tid_no, /*period*/0, /*offset*/0,
ADV_TRANS_CUR);
if (tinfo->current.period != tinfo->goal.period) {
adv_msgout_sdtr(adv, tinfo->goal.period,
tinfo->goal.offset);
scsiq->q1.cntl |= (QC_MSG_OUT | QC_URGENT);
}
}
if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
sg_entry_cnt = scsiq->sg_head->entry_cnt;
sg_entry_cnt_minus_one = sg_entry_cnt - 1;
#ifdef DIAGNOSTIC
if (sg_entry_cnt <= 1)
panic("adv_execute_scsi_queue: Queue "
"with QC_SG_HEAD set but %d segs.", sg_entry_cnt);
if (sg_entry_cnt > ADV_MAX_SG_LIST)
panic("adv_execute_scsi_queue: "
"Queue with too many segs.");
if (adv->type & (ADV_ISA | ADV_VL | ADV_EISA)) {
int i;
for (i = 0; i < sg_entry_cnt_minus_one; i++) {
addr = scsiq->sg_head->sg_list[i].addr +
scsiq->sg_head->sg_list[i].bytes;
if ((addr & 0x0003) != 0)
panic("adv_execute_scsi_queue: SG "
"with odd address or byte count");
}
}
#endif
p_data_addr =
&scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].addr;
p_data_bcount =
&scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].bytes;
n_q_required = adv_sgcount_to_qcount(sg_entry_cnt);
scsiq->sg_head->queue_cnt = n_q_required - 1;
} else {
p_data_addr = &scsiq->q1.data_addr;
p_data_bcount = &scsiq->q1.data_cnt;
n_q_required = 1;
}
disable_syn_offset_one_fix = FALSE;
if ((adv->fix_asyn_xfer & scsiq->q1.target_id) != 0
&& (adv->fix_asyn_xfer_always & scsiq->q1.target_id) == 0) {
if (datalen != 0) {
if (datalen < 512) {
disable_syn_offset_one_fix = TRUE;
} else {
if (scsiq->cdbptr[0] == INQUIRY
|| scsiq->cdbptr[0] == REQUEST_SENSE
|| scsiq->cdbptr[0] == READ_CAPACITY
|| scsiq->cdbptr[0] == MODE_SELECT_6
|| scsiq->cdbptr[0] == MODE_SENSE_6
|| scsiq->cdbptr[0] == MODE_SENSE_10
|| scsiq->cdbptr[0] == MODE_SELECT_10
|| scsiq->cdbptr[0] == READ_TOC) {
disable_syn_offset_one_fix = TRUE;
}
}
}
}
if (disable_syn_offset_one_fix) {
scsiq->q2.tag_code &=
~(MSG_SIMPLE_Q_TAG|MSG_HEAD_OF_Q_TAG|MSG_ORDERED_Q_TAG);
scsiq->q2.tag_code |= (ADV_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX
| ADV_TAG_FLAG_DISABLE_DISCONNECT);
}
if ((adv->bug_fix_control & ADV_BUG_FIX_IF_NOT_DWB) != 0
&& (scsiq->cdbptr[0] == READ_10 || scsiq->cdbptr[0] == READ_6)) {
u_int8_t extra_bytes;
addr = *p_data_addr + *p_data_bcount;
extra_bytes = addr & 0x0003;
if (extra_bytes != 0
&& ((scsiq->q1.cntl & QC_SG_HEAD) != 0
|| (scsiq->q1.data_cnt & 0x01FF) == 0)) {
scsiq->q2.tag_code |= ADV_TAG_FLAG_EXTRA_BYTES;
scsiq->q1.extra_bytes = extra_bytes;
*p_data_bcount -= extra_bytes;
}
}
if ((adv_get_num_free_queues(adv, n_q_required) >= n_q_required)
|| ((scsiq->q1.cntl & QC_URGENT) != 0))
retval = adv_send_scsi_queue(adv, scsiq, n_q_required);
return (retval);
}
u_int8_t
adv_copy_lram_doneq(struct adv_softc *adv, u_int16_t q_addr,
struct adv_q_done_info *scsiq, u_int32_t max_dma_count)
{
u_int16_t val;
u_int8_t sg_queue_cnt;
adv_get_q_info(adv, q_addr + ADV_SCSIQ_DONE_INFO_BEG,
(u_int16_t *)scsiq,
(sizeof(scsiq->d2) + sizeof(scsiq->d3)) / 2);
#if BYTE_ORDER == BIG_ENDIAN
adv_adj_endian_qdone_info(scsiq);
#endif
val = adv_read_lram_16(adv, q_addr + ADV_SCSIQ_B_STATUS);
scsiq->q_status = val & 0xFF;
scsiq->q_no = (val >> 8) & 0XFF;
val = adv_read_lram_16(adv, q_addr + ADV_SCSIQ_B_CNTL);
scsiq->cntl = val & 0xFF;
sg_queue_cnt = (val >> 8) & 0xFF;
val = adv_read_lram_16(adv,q_addr + ADV_SCSIQ_B_SENSE_LEN);
scsiq->sense_len = val & 0xFF;
scsiq->extra_bytes = (val >> 8) & 0xFF;
/*
* XXX
* Due to a bug in accessing LRAM on the 940UA, we only pull
* the low 16bits of residual information. In the future, we'll
* want to allow transfers larger than 64K, but hopefully we'll
* get a new firmware revision from AdvanSys that address this
* problem before we up the transfer size.
*/
scsiq->remain_bytes =
adv_read_lram_16(adv, q_addr + ADV_SCSIQ_DW_REMAIN_XFER_CNT);
/*
* XXX Is this just a safeguard or will the counter really
* have bogus upper bits?
*/
scsiq->remain_bytes &= max_dma_count;
return (sg_queue_cnt);
}
int
adv_start_chip(struct adv_softc *adv)
{
ADV_OUTB(adv, ADV_CHIP_CTRL, 0);
if ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_HALTED) != 0)
return (0);
return (1);
}
int
adv_stop_execution(struct adv_softc *adv)
{
int count;
count = 0;
if (adv_read_lram_8(adv, ADV_STOP_CODE_B) == 0) {
adv_write_lram_8(adv, ADV_STOP_CODE_B,
ADV_STOP_REQ_RISC_STOP);
do {
if (adv_read_lram_8(adv, ADV_STOP_CODE_B) &
ADV_STOP_ACK_RISC_STOP) {
return (1);
}
DELAY(1000);
} while (count++ < 20);
}
return (0);
}
int
adv_is_chip_halted(struct adv_softc *adv)
{
if ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_HALTED) != 0) {
if ((ADV_INB(adv, ADV_CHIP_CTRL) & ADV_CC_HALT) != 0) {
return (1);
}
}
return (0);
}
/*
* XXX The numeric constants and the loops in this routine
* need to be documented.
*/
void
adv_ack_interrupt(struct adv_softc *adv)
{
u_int8_t host_flag;
u_int8_t risc_flag;
int loop;
loop = 0;
do {
risc_flag = adv_read_lram_8(adv, ADVV_RISC_FLAG_B);
if (loop++ > 0x7FFF) {
break;
}
} while ((risc_flag & ADV_RISC_FLAG_GEN_INT) != 0);
host_flag = adv_read_lram_8(adv, ADVV_HOST_FLAG_B);
adv_write_lram_8(adv, ADVV_HOST_FLAG_B,
host_flag | ADV_HOST_FLAG_ACK_INT);
ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_INT_ACK);
loop = 0;
while (ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_INT_PENDING) {
ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_INT_ACK);
if (loop++ > 3) {
break;
}
}
adv_write_lram_8(adv, ADVV_HOST_FLAG_B, host_flag);
}
/*
* Handle all conditions that may halt the chip waiting
* for us to intervene.
*/
void
adv_isr_chip_halted(struct adv_softc *adv)
{
u_int16_t int_halt_code;
u_int16_t halt_q_addr;
target_bit_vector target_mask;
target_bit_vector scsi_busy;
u_int8_t halt_qp;
u_int8_t target_ix;
u_int8_t q_cntl;
u_int8_t tid_no;
int_halt_code = adv_read_lram_16(adv, ADVV_HALTCODE_W);
halt_qp = adv_read_lram_8(adv, ADVV_CURCDB_B);
halt_q_addr = ADV_QNO_TO_QADDR(halt_qp);
target_ix = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_TARGET_IX);
q_cntl = adv_read_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL);
tid_no = ADV_TIX_TO_TID(target_ix);
target_mask = ADV_TID_TO_TARGET_MASK(tid_no);
if (int_halt_code == ADV_HALT_DISABLE_ASYN_USE_SYN_FIX) {
/*
* Temporarily disable the async fix by removing
* this target from the list of affected targets,
* setting our async rate, and then putting us
* back into the mask.
*/
adv->fix_asyn_xfer &= ~target_mask;
adv_set_syncrate(adv, /*struct cam_path */NULL,
tid_no, /*period*/0, /*offset*/0,
ADV_TRANS_ACTIVE);
adv->fix_asyn_xfer |= target_mask;
} else if (int_halt_code == ADV_HALT_ENABLE_ASYN_USE_SYN_FIX) {
adv_set_syncrate(adv, /*struct cam_path */NULL,
tid_no, /*period*/0, /*offset*/0,
ADV_TRANS_ACTIVE);
} else if (int_halt_code == ADV_HALT_EXTMSG_IN) {
adv_handle_extmsg_in(adv, halt_q_addr, q_cntl,
target_mask, tid_no);
} else if (int_halt_code == ADV_HALT_CHK_CONDITION) {
struct adv_target_transinfo* tinfo;
union ccb *ccb;
u_int8_t tag_code;
u_int8_t q_status;
tinfo = &adv->tinfo[tid_no];
q_cntl |= QC_REQ_SENSE;
/* Renegotiate if appropriate. */
adv_set_syncrate(adv, /*struct cam_path */NULL,
tid_no, /*period*/0, /*offset*/0,
ADV_TRANS_CUR);
if (tinfo->current.period != tinfo->goal.period) {
adv_msgout_sdtr(adv, tinfo->goal.period,
tinfo->goal.offset);
q_cntl |= QC_MSG_OUT;
}
adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl);
/* Don't tag request sense commands */
tag_code = adv_read_lram_8(adv,
halt_q_addr + ADV_SCSIQ_B_TAG_CODE);
tag_code &=
~(MSG_SIMPLE_Q_TAG|MSG_HEAD_OF_Q_TAG|MSG_ORDERED_Q_TAG);
if ((adv->fix_asyn_xfer & target_mask) != 0
&& (adv->fix_asyn_xfer_always & target_mask) == 0) {
tag_code |= (ADV_TAG_FLAG_DISABLE_DISCONNECT
| ADV_TAG_FLAG_DISABLE_ASYN_USE_SYN_FIX);
}
adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_TAG_CODE,
tag_code);
q_status = adv_read_lram_8(adv,
halt_q_addr + ADV_SCSIQ_B_STATUS);
q_status |= (QS_READY | QS_BUSY);
adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_STATUS,
q_status);
/*
* Freeze the devq until we can handle the sense condition.
*/
ccb = (union ccb *) adv_read_lram_32(adv, halt_q_addr
+ ADV_SCSIQ_D_CCBPTR);
xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
ccb->ccb_h.status |= CAM_DEV_QFRZN;
adv_abort_ccb(adv, tid_no, ADV_TIX_TO_LUN(target_ix),
/*ccb*/NULL, CAM_REQUEUE_REQ,
/*queued_only*/TRUE);
scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B);
scsi_busy &= ~target_mask;
adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy);
/*
* Ensure we have enough time to actually
* retrieve the sense.
*/
untimeout(adv_timeout, (caddr_t)ccb, ccb->ccb_h.timeout_ch);
ccb->ccb_h.timeout_ch =
timeout(adv_timeout, (caddr_t)ccb, 5 * hz);
} else if (int_halt_code == ADV_HALT_SDTR_REJECTED) {
struct ext_msg out_msg;
adv_read_lram_16_multi(adv, ADVV_MSGOUT_BEG,
(u_int16_t *) &out_msg,
sizeof(out_msg)/2);
if ((out_msg.msg_type == MSG_EXTENDED)
&& (out_msg.msg_len == MSG_EXT_SDTR_LEN)
&& (out_msg.msg_req == MSG_EXT_SDTR)) {
/* Revert to Async */
adv_set_syncrate(adv, /*struct cam_path */NULL,
tid_no, /*period*/0, /*offset*/0,
ADV_TRANS_GOAL|ADV_TRANS_ACTIVE);
}
q_cntl &= ~QC_MSG_OUT;
adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl);
} else if (int_halt_code == ADV_HALT_SS_QUEUE_FULL) {
u_int8_t scsi_status;
union ccb *ccb;
scsi_status = adv_read_lram_8(adv, halt_q_addr
+ ADV_SCSIQ_SCSI_STATUS);
ccb = (union ccb *) adv_read_lram_32(adv, halt_q_addr
+ ADV_SCSIQ_D_CCBPTR);
xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
ccb->ccb_h.status |= CAM_DEV_QFRZN|CAM_SCSI_STATUS_ERROR;
ccb->csio.scsi_status = SCSI_STATUS_QUEUE_FULL;
adv_abort_ccb(adv, tid_no, ADV_TIX_TO_LUN(target_ix),
/*ccb*/NULL, CAM_REQUEUE_REQ,
/*queued_only*/TRUE);
scsi_busy = adv_read_lram_8(adv, ADVV_SCSIBUSY_B);
scsi_busy &= ~target_mask;
adv_write_lram_8(adv, ADVV_SCSIBUSY_B, scsi_busy);
}
adv_write_lram_16(adv, ADVV_HALTCODE_W, 0);
}
void
adv_sdtr_to_period_offset(struct adv_softc *adv,
u_int8_t sync_data, u_int8_t *period,
u_int8_t *offset, int tid)
{
if (adv->fix_asyn_xfer & ADV_TID_TO_TARGET_MASK(tid)
&& (sync_data == ASYN_SDTR_DATA_FIX_PCI_REV_AB)) {
*period = *offset = 0;
} else {
*period = adv->sdtr_period_tbl[((sync_data >> 4) & 0xF)];
*offset = sync_data & 0xF;
}
}
void
adv_set_syncrate(struct adv_softc *adv, struct cam_path *path,
u_int tid, u_int period, u_int offset, u_int type)
{
struct adv_target_transinfo* tinfo;
u_int old_period;
u_int old_offset;
u_int8_t sdtr_data;
tinfo = &adv->tinfo[tid];
/* Filter our input */
sdtr_data = adv_period_offset_to_sdtr(adv, &period,
&offset, tid);
old_period = tinfo->current.period;
old_offset = tinfo->current.offset;
if ((type & ADV_TRANS_CUR) != 0
&& ((old_period != period || old_offset != offset)
|| period == 0 || offset == 0) /*Changes in asyn fix settings*/) {
int s;
int halted;
s = splcam();
halted = adv_is_chip_halted(adv);
if (halted == 0)
/* Must halt the chip first */
adv_host_req_chip_halt(adv);
/* Update current hardware settings */
adv_set_sdtr_reg_at_id(adv, tid, sdtr_data);
/*
* If a target can run in sync mode, we don't need
* to check it for sync problems.
*/
if (offset != 0)
adv->fix_asyn_xfer &= ~ADV_TID_TO_TARGET_MASK(tid);
if (halted == 0)
/* Start the chip again */
adv_start_chip(adv);
splx(s);
tinfo->current.period = period;
tinfo->current.offset = offset;
if (path != NULL) {
/*
* Tell the SCSI layer about the
* new transfer parameters.
*/
struct ccb_trans_settings neg;
neg.sync_period = period;
neg.sync_offset = offset;
neg.valid = CCB_TRANS_SYNC_RATE_VALID
| CCB_TRANS_SYNC_OFFSET_VALID;
xpt_setup_ccb(&neg.ccb_h, path, /*priority*/1);
xpt_async(AC_TRANSFER_NEG, path, &neg);
}
}
if ((type & ADV_TRANS_GOAL) != 0) {
tinfo->goal.period = period;
tinfo->goal.offset = offset;
}
if ((type & ADV_TRANS_USER) != 0) {
tinfo->user.period = period;
tinfo->user.offset = offset;
}
}
u_int8_t
adv_period_offset_to_sdtr(struct adv_softc *adv, u_int *period,
u_int *offset, int tid)
{
u_int i;
u_int dummy_offset;
u_int dummy_period;
if (offset == NULL) {
dummy_offset = 0;
offset = &dummy_offset;
}
if (period == NULL) {
dummy_period = 0;
period = &dummy_period;
}
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
*offset = MIN(ADV_SYN_MAX_OFFSET, *offset);
if (*period != 0 && *offset != 0) {
for (i = 0; i < adv->sdtr_period_tbl_size; i++) {
if (*period <= adv->sdtr_period_tbl[i]) {
/*
* When responding to a target that requests
* sync, the requested rate may fall between
* two rates that we can output, but still be
* a rate that we can receive. Because of this,
* we want to respond to the target with
* the same rate that it sent to us even
* if the period we use to send data to it
* is lower. Only lower the response period
* if we must.
*/
if (i == 0 /* Our maximum rate */)
*period = adv->sdtr_period_tbl[0];
return ((i << 4) | *offset);
}
}
}
/* Must go async */
*period = 0;
*offset = 0;
if (adv->fix_asyn_xfer & ADV_TID_TO_TARGET_MASK(tid))
return (ASYN_SDTR_DATA_FIX_PCI_REV_AB);
return (0);
}
/* Internal Routines */
static void
adv_read_lram_16_multi(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *buffer, int count)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
ADV_INSW(adv, ADV_LRAM_DATA, buffer, count);
}
static void
adv_write_lram_16_multi(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *buffer, int count)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
ADV_OUTSW(adv, ADV_LRAM_DATA, buffer, count);
}
static void
adv_mset_lram_16(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t set_value, int count)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
bus_space_set_multi_2(adv->tag, adv->bsh, ADV_LRAM_DATA,
set_value, count);
}
static u_int32_t
adv_msum_lram_16(struct adv_softc *adv, u_int16_t s_addr, int count)
{
u_int32_t sum;
int i;
sum = 0;
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
for (i = 0; i < count; i++)
sum += ADV_INW(adv, ADV_LRAM_DATA);
return (sum);
}
static int
adv_write_and_verify_lram_16(struct adv_softc *adv, u_int16_t addr,
u_int16_t value)
{
int retval;
retval = 0;
ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
ADV_OUTW(adv, ADV_LRAM_DATA, value);
DELAY(10000);
ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
if (value != ADV_INW(adv, ADV_LRAM_DATA))
retval = 1;
return (retval);
}
static u_int32_t
adv_read_lram_32(struct adv_softc *adv, u_int16_t addr)
{
u_int16_t val_low, val_high;
ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
#if BYTE_ORDER == BIG_ENDIAN
val_high = ADV_INW(adv, ADV_LRAM_DATA);
val_low = ADV_INW(adv, ADV_LRAM_DATA);
#else
val_low = ADV_INW(adv, ADV_LRAM_DATA);
val_high = ADV_INW(adv, ADV_LRAM_DATA);
#endif
return (((u_int32_t)val_high << 16) | (u_int32_t)val_low);
}
static void
adv_write_lram_32(struct adv_softc *adv, u_int16_t addr, u_int32_t value)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, addr);
#if BYTE_ORDER == BIG_ENDIAN
ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)((value >> 16) & 0xFFFF));
ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)(value & 0xFFFF));
#else
ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)(value & 0xFFFF));
ADV_OUTW(adv, ADV_LRAM_DATA, (u_int16_t)((value >> 16) & 0xFFFF));
#endif
}
static void
adv_write_lram_32_multi(struct adv_softc *adv, u_int16_t s_addr,
u_int32_t *buffer, int count)
{
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
ADV_OUTSW(adv, ADV_LRAM_DATA, (u_int16_t *)buffer, count * 2);
}
static u_int16_t
adv_read_eeprom_16(struct adv_softc *adv, u_int8_t addr)
{
u_int16_t read_wval;
u_int8_t cmd_reg;
adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE_DISABLE);
DELAY(1000);
cmd_reg = addr | ADV_EEPROM_CMD_READ;
adv_write_eeprom_cmd_reg(adv, cmd_reg);
DELAY(1000);
read_wval = ADV_INW(adv, ADV_EEPROM_DATA);
DELAY(1000);
return (read_wval);
}
static u_int16_t
adv_write_eeprom_16(struct adv_softc *adv, u_int8_t addr, u_int16_t value)
{
u_int16_t read_value;
read_value = adv_read_eeprom_16(adv, addr);
if (read_value != value) {
adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE_ENABLE);
DELAY(1000);
ADV_OUTW(adv, ADV_EEPROM_DATA, value);
DELAY(1000);
adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE | addr);
DELAY(20 * 1000);
adv_write_eeprom_cmd_reg(adv, ADV_EEPROM_CMD_WRITE_DISABLE);
DELAY(1000);
read_value = adv_read_eeprom_16(adv, addr);
}
return (read_value);
}
static int
adv_write_eeprom_cmd_reg(struct adv_softc *adv, u_int8_t cmd_reg)
{
u_int8_t read_back;
int retry;
retry = 0;
while (1) {
ADV_OUTB(adv, ADV_EEPROM_CMD, cmd_reg);
DELAY(1000);
read_back = ADV_INB(adv, ADV_EEPROM_CMD);
if (read_back == cmd_reg) {
return (1);
}
if (retry++ > ADV_EEPROM_MAX_RETRY) {
return (0);
}
}
}
static int
adv_set_eeprom_config_once(struct adv_softc *adv,
struct adv_eeprom_config *eeprom_config)
{
int n_error;
u_int16_t *wbuf;
u_int16_t sum;
u_int8_t s_addr;
u_int8_t cfg_beg;
u_int8_t cfg_end;
wbuf = (u_int16_t *)eeprom_config;
n_error = 0;
sum = 0;
for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
sum += *wbuf;
if (*wbuf != adv_write_eeprom_16(adv, s_addr, *wbuf)) {
n_error++;
}
}
if (adv->type & ADV_VL) {
cfg_beg = ADV_EEPROM_CFG_BEG_VL;
cfg_end = ADV_EEPROM_MAX_ADDR_VL;
} else {
cfg_beg = ADV_EEPROM_CFG_BEG;
cfg_end = ADV_EEPROM_MAX_ADDR;
}
for (s_addr = cfg_beg; s_addr <= (cfg_end - 1); s_addr++, wbuf++) {
sum += *wbuf;
if (*wbuf != adv_write_eeprom_16(adv, s_addr, *wbuf)) {
n_error++;
}
}
*wbuf = sum;
if (sum != adv_write_eeprom_16(adv, s_addr, sum)) {
n_error++;
}
wbuf = (u_int16_t *)eeprom_config;
for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
if (*wbuf != adv_read_eeprom_16(adv, s_addr)) {
n_error++;
}
}
for (s_addr = cfg_beg; s_addr <= cfg_end; s_addr++, wbuf++) {
if (*wbuf != adv_read_eeprom_16(adv, s_addr)) {
n_error++;
}
}
return (n_error);
}
static u_int32_t
adv_load_microcode(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *mcode_buf, u_int16_t mcode_size)
{
u_int32_t chksum;
u_int16_t mcode_lram_size;
u_int16_t mcode_chksum;
mcode_lram_size = mcode_size >> 1;
/* XXX Why zero the memory just before you write the whole thing?? */
adv_mset_lram_16(adv, s_addr, 0, mcode_lram_size);
adv_write_lram_16_multi(adv, s_addr, mcode_buf, mcode_lram_size);
chksum = adv_msum_lram_16(adv, s_addr, mcode_lram_size);
mcode_chksum = (u_int16_t)adv_msum_lram_16(adv, ADV_CODE_SEC_BEG,
((mcode_size - s_addr
- ADV_CODE_SEC_BEG) >> 1));
adv_write_lram_16(adv, ADVV_MCODE_CHKSUM_W, mcode_chksum);
adv_write_lram_16(adv, ADVV_MCODE_SIZE_W, mcode_size);
return (chksum);
}
static void
adv_reinit_lram(struct adv_softc *adv) {
adv_init_lram(adv);
adv_init_qlink_var(adv);
}
static void
adv_init_lram(struct adv_softc *adv)
{
u_int8_t i;
u_int16_t s_addr;
adv_mset_lram_16(adv, ADV_QADR_BEG, 0,
(((adv->max_openings + 2 + 1) * 64) >> 1));
i = ADV_MIN_ACTIVE_QNO;
s_addr = ADV_QADR_BEG + ADV_QBLK_SIZE;
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, i + 1);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, adv->max_openings);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, i);
i++;
s_addr += ADV_QBLK_SIZE;
for (; i < adv->max_openings; i++, s_addr += ADV_QBLK_SIZE) {
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, i + 1);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, i - 1);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, i);
}
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, ADV_QLINK_END);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, adv->max_openings - 1);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, adv->max_openings);
i++;
s_addr += ADV_QBLK_SIZE;
for (; i <= adv->max_openings + 3; i++, s_addr += ADV_QBLK_SIZE) {
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_FWD, i);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_BWD, i);
adv_write_lram_8(adv, s_addr + ADV_SCSIQ_B_QNO, i);
}
}
static int
adv_init_microcode_var(struct adv_softc *adv)
{
int i;
for (i = 0; i <= ADV_MAX_TID; i++) {
/* Start out async all around */
adv_set_syncrate(adv, /*path*/NULL,
i, 0, 0,
ADV_TRANS_GOAL|ADV_TRANS_CUR);
}
adv_init_qlink_var(adv);
adv_write_lram_8(adv, ADVV_DISC_ENABLE_B, adv->disc_enable);
adv_write_lram_8(adv, ADVV_HOSTSCSI_ID_B, 0x01 << adv->scsi_id);
adv_write_lram_32(adv, ADVV_OVERRUN_PADDR_D, adv->overrun_physbase);
adv_write_lram_32(adv, ADVV_OVERRUN_BSIZE_D, ADV_OVERRUN_BSIZE);
ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR);
if (ADV_INW(adv, ADV_REG_PROG_COUNTER) != ADV_MCODE_START_ADDR) {
printf("adv%d: Unable to set program counter. Aborting.\n",
adv->unit);
return (1);
}
return (0);
}
static void
adv_init_qlink_var(struct adv_softc *adv)
{
int i;
u_int16_t lram_addr;
adv_write_lram_8(adv, ADVV_NEXTRDY_B, 1);
adv_write_lram_8(adv, ADVV_DONENEXT_B, adv->max_openings);
adv_write_lram_16(adv, ADVV_FREE_Q_HEAD_W, 1);
adv_write_lram_16(adv, ADVV_DONE_Q_TAIL_W, adv->max_openings);
adv_write_lram_8(adv, ADVV_BUSY_QHEAD_B,
(u_int8_t)((int) adv->max_openings + 1));
adv_write_lram_8(adv, ADVV_DISC1_QHEAD_B,
(u_int8_t)((int) adv->max_openings + 2));
adv_write_lram_8(adv, ADVV_TOTAL_READY_Q_B, adv->max_openings);
adv_write_lram_16(adv, ADVV_ASCDVC_ERR_CODE_W, 0);
adv_write_lram_16(adv, ADVV_HALTCODE_W, 0);
adv_write_lram_8(adv, ADVV_STOP_CODE_B, 0);
adv_write_lram_8(adv, ADVV_SCSIBUSY_B, 0);
adv_write_lram_8(adv, ADVV_WTM_FLAG_B, 0);
adv_write_lram_8(adv, ADVV_Q_DONE_IN_PROGRESS_B, 0);
lram_addr = ADV_QADR_BEG;
for (i = 0; i < 32; i++, lram_addr += 2)
adv_write_lram_16(adv, lram_addr, 0);
}
static void
adv_disable_interrupt(struct adv_softc *adv)
{
u_int16_t cfg;
cfg = ADV_INW(adv, ADV_CONFIG_LSW);
ADV_OUTW(adv, ADV_CONFIG_LSW, cfg & ~ADV_CFG_LSW_HOST_INT_ON);
}
static void
adv_enable_interrupt(struct adv_softc *adv)
{
u_int16_t cfg;
cfg = ADV_INW(adv, ADV_CONFIG_LSW);
ADV_OUTW(adv, ADV_CONFIG_LSW, cfg | ADV_CFG_LSW_HOST_INT_ON);
}
static void
adv_toggle_irq_act(struct adv_softc *adv)
{
ADV_OUTW(adv, ADV_CHIP_STATUS, ADV_CIW_IRQ_ACT);
ADV_OUTW(adv, ADV_CHIP_STATUS, 0);
}
void
adv_start_execution(struct adv_softc *adv)
{
if (adv_read_lram_8(adv, ADV_STOP_CODE_B) != 0) {
adv_write_lram_8(adv, ADV_STOP_CODE_B, 0);
}
}
static int
adv_stop_chip(struct adv_softc *adv)
{
u_int8_t cc_val;
cc_val = ADV_INB(adv, ADV_CHIP_CTRL)
& (~(ADV_CC_SINGLE_STEP | ADV_CC_TEST | ADV_CC_DIAG));
ADV_OUTB(adv, ADV_CHIP_CTRL, cc_val | ADV_CC_HALT);
adv_set_chip_ih(adv, ADV_INS_HALT);
adv_set_chip_ih(adv, ADV_INS_RFLAG_WTM);
if ((ADV_INW(adv, ADV_CHIP_STATUS) & ADV_CSW_HALTED) == 0) {
return (0);
}
return (1);
}
static int
adv_host_req_chip_halt(struct adv_softc *adv)
{
int count;
u_int8_t saved_stop_code;
if (adv_is_chip_halted(adv))
return (1);
count = 0;
saved_stop_code = adv_read_lram_8(adv, ADVV_STOP_CODE_B);
adv_write_lram_8(adv, ADVV_STOP_CODE_B,
ADV_STOP_HOST_REQ_RISC_HALT | ADV_STOP_REQ_RISC_STOP);
while (adv_is_chip_halted(adv) == 0
&& count++ < 2000)
;
adv_write_lram_8(adv, ADVV_STOP_CODE_B, saved_stop_code);
return (count < 2000);
}
static void
adv_set_chip_ih(struct adv_softc *adv, u_int16_t ins_code)
{
adv_set_bank(adv, 1);
ADV_OUTW(adv, ADV_REG_IH, ins_code);
adv_set_bank(adv, 0);
}
#if UNUSED
static u_int8_t
adv_get_chip_scsi_ctrl(struct adv_softc *adv)
{
u_int8_t scsi_ctrl;
adv_set_bank(adv, 1);
scsi_ctrl = ADV_INB(adv, ADV_REG_SC);
adv_set_bank(adv, 0);
return (scsi_ctrl);
}
#endif
/*
* XXX Looks like more padding issues in this routine as well.
* There has to be a way to turn this into an insw.
*/
static void
adv_get_q_info(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *inbuf, int words)
{
int i;
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
for (i = 0; i < words; i++, inbuf++) {
if (i == 5) {
continue;
}
*inbuf = ADV_INW(adv, ADV_LRAM_DATA);
}
}
static u_int
adv_get_num_free_queues(struct adv_softc *adv, u_int8_t n_qs)
{
u_int cur_used_qs;
u_int cur_free_qs;
cur_used_qs = adv->cur_active + ADV_MIN_FREE_Q;
if ((cur_used_qs + n_qs) <= adv->max_openings) {
cur_free_qs = adv->max_openings - cur_used_qs;
return (cur_free_qs);
}
adv->openings_needed = n_qs;
return (0);
}
static u_int8_t
adv_alloc_free_queues(struct adv_softc *adv, u_int8_t free_q_head,
u_int8_t n_free_q)
{
int i;
for (i = 0; i < n_free_q; i++) {
free_q_head = adv_alloc_free_queue(adv, free_q_head);
if (free_q_head == ADV_QLINK_END)
break;
}
return (free_q_head);
}
static u_int8_t
adv_alloc_free_queue(struct adv_softc *adv, u_int8_t free_q_head)
{
u_int16_t q_addr;
u_int8_t next_qp;
u_int8_t q_status;
next_qp = ADV_QLINK_END;
q_addr = ADV_QNO_TO_QADDR(free_q_head);
q_status = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_STATUS);
if ((q_status & QS_READY) == 0)
next_qp = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_FWD);
return (next_qp);
}
static int
adv_send_scsi_queue(struct adv_softc *adv, struct adv_scsi_q *scsiq,
u_int8_t n_q_required)
{
u_int8_t free_q_head;
u_int8_t next_qp;
u_int8_t tid_no;
u_int8_t target_ix;
int retval;
retval = 1;
target_ix = scsiq->q2.target_ix;
tid_no = ADV_TIX_TO_TID(target_ix);
free_q_head = adv_read_lram_16(adv, ADVV_FREE_Q_HEAD_W) & 0xFF;
if ((next_qp = adv_alloc_free_queues(adv, free_q_head, n_q_required))
!= ADV_QLINK_END) {
scsiq->q1.q_no = free_q_head;
/*
* Now that we know our Q number, point our sense
* buffer pointer to a bus dma mapped area where
* we can dma the data to.
*/
scsiq->q1.sense_addr = adv->sense_physbase
+ ((free_q_head - 1) * sizeof(struct scsi_sense_data));
adv_put_ready_sg_list_queue(adv, scsiq, free_q_head);
adv_write_lram_16(adv, ADVV_FREE_Q_HEAD_W, next_qp);
adv->cur_active += n_q_required;
retval = 0;
}
return (retval);
}
static void
adv_put_ready_sg_list_queue(struct adv_softc *adv, struct adv_scsi_q *scsiq,
u_int q_no)
{
u_int8_t sg_list_dwords;
u_int8_t sg_index, i;
u_int8_t sg_entry_cnt;
u_int8_t next_qp;
u_int16_t q_addr;
struct adv_sg_head *sg_head;
struct adv_sg_list_q scsi_sg_q;
sg_head = scsiq->sg_head;
if (sg_head) {
sg_entry_cnt = sg_head->entry_cnt - 1;
#ifdef DIAGNOSTIC
if (sg_entry_cnt == 0)
panic("adv_put_ready_sg_list_queue: ScsiQ with "
"a SG list but only one element");
if ((scsiq->q1.cntl & QC_SG_HEAD) == 0)
panic("adv_put_ready_sg_list_queue: ScsiQ with "
"a SG list but QC_SG_HEAD not set");
#endif
q_addr = ADV_QNO_TO_QADDR(q_no);
sg_index = 1;
scsiq->q1.sg_queue_cnt = sg_head->queue_cnt;
scsi_sg_q.sg_head_qp = q_no;
scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
for (i = 0; i < sg_head->queue_cnt; i++) {
u_int8_t segs_this_q;
if (sg_entry_cnt > ADV_SG_LIST_PER_Q)
segs_this_q = ADV_SG_LIST_PER_Q;
else {
/* This will be the last segment then */
segs_this_q = sg_entry_cnt;
scsi_sg_q.cntl |= QCSG_SG_XFER_END;
}
scsi_sg_q.seq_no = i + 1;
sg_list_dwords = segs_this_q << 1;
if (i == 0) {
scsi_sg_q.sg_list_cnt = segs_this_q;
scsi_sg_q.sg_cur_list_cnt = segs_this_q;
} else {
scsi_sg_q.sg_list_cnt = segs_this_q - 1;
scsi_sg_q.sg_cur_list_cnt = segs_this_q - 1;
}
next_qp = adv_read_lram_8(adv, q_addr + ADV_SCSIQ_B_FWD);
scsi_sg_q.q_no = next_qp;
q_addr = ADV_QNO_TO_QADDR(next_qp);
adv_write_lram_16_multi(adv,
q_addr + ADV_SCSIQ_SGHD_CPY_BEG,
(u_int16_t *)&scsi_sg_q,
sizeof(scsi_sg_q) >> 1);
adv_write_lram_32_multi(adv, q_addr + ADV_SGQ_LIST_BEG,
(u_int32_t *)&sg_head->sg_list[sg_index],
sg_list_dwords);
sg_entry_cnt -= segs_this_q;
sg_index += ADV_SG_LIST_PER_Q;
}
}
adv_put_ready_queue(adv, scsiq, q_no);
}
static void
adv_put_ready_queue(struct adv_softc *adv, struct adv_scsi_q *scsiq,
u_int q_no)
{
struct adv_target_transinfo* tinfo;
u_int q_addr;
u_int tid_no;
tid_no = ADV_TIX_TO_TID(scsiq->q2.target_ix);
tinfo = &adv->tinfo[tid_no];
if ((tinfo->current.period != tinfo->goal.period)
|| (tinfo->current.offset != tinfo->goal.offset)) {
adv_msgout_sdtr(adv, tinfo->goal.period, tinfo->goal.offset);
scsiq->q1.cntl |= QC_MSG_OUT;
}
q_addr = ADV_QNO_TO_QADDR(q_no);
scsiq->q1.status = QS_FREE;
adv_write_lram_16_multi(adv, q_addr + ADV_SCSIQ_CDB_BEG,
(u_int16_t *)scsiq->cdbptr,
scsiq->q2.cdb_len >> 1);
#if BYTE_ORDER == BIG_ENDIAN
adv_adj_scsiq_endian(scsiq);
#endif
adv_put_scsiq(adv, q_addr + ADV_SCSIQ_CPY_BEG,
(u_int16_t *) &scsiq->q1.cntl,
((sizeof(scsiq->q1) + sizeof(scsiq->q2)) / 2) - 1);
#if CC_WRITE_IO_COUNT
adv_write_lram_16(adv, q_addr + ADV_SCSIQ_W_REQ_COUNT,
adv->req_count);
#endif
#if CC_CLEAR_DMA_REMAIN
adv_write_lram_32(adv, q_addr + ADV_SCSIQ_DW_REMAIN_XFER_ADDR, 0);
adv_write_lram_32(adv, q_addr + ADV_SCSIQ_DW_REMAIN_XFER_CNT, 0);
#endif
adv_write_lram_16(adv, q_addr + ADV_SCSIQ_B_STATUS,
(scsiq->q1.q_no << 8) | QS_READY);
}
static void
adv_put_scsiq(struct adv_softc *adv, u_int16_t s_addr,
u_int16_t *buffer, int words)
{
int i;
/*
* XXX This routine makes *gross* assumptions
* about padding in the data structures.
* Either the data structures should have explicit
* padding members added, or they should have padding
* turned off via compiler attributes depending on
* which yields better overall performance. My hunch
* would be that turning off padding would be the
* faster approach as an outsw is much faster than
* this crude loop and accessing un-aligned data
* members isn't *that* expensive. The other choice
* would be to modify the ASC script so that the
* the adv_scsiq_1 structure can be re-arranged so
* padding isn't required.
*/
ADV_OUTW(adv, ADV_LRAM_ADDR, s_addr);
for (i = 0; i < words; i++, buffer++) {
if (i == 2 || i == 10) {
continue;
}
ADV_OUTW(adv, ADV_LRAM_DATA, *buffer);
}
}
static void
adv_handle_extmsg_in(struct adv_softc *adv, u_int16_t halt_q_addr,
u_int8_t q_cntl, target_bit_vector target_mask,
int tid_no)
{
struct ext_msg ext_msg;
adv_read_lram_16_multi(adv, ADVV_MSGIN_BEG, (u_int16_t *) &ext_msg,
sizeof(ext_msg) >> 1);
if ((ext_msg.msg_type == MSG_EXTENDED)
&& (ext_msg.msg_req == MSG_EXT_SDTR)
&& (ext_msg.msg_len == MSG_EXT_SDTR_LEN)) {
union ccb *ccb;
struct adv_target_transinfo* tinfo;
u_int period;
u_int offset;
int sdtr_accept;
u_int8_t orig_offset;
ccb = (union ccb *) adv_read_lram_32(adv, halt_q_addr
+ ADV_SCSIQ_D_CCBPTR);
tinfo = &adv->tinfo[tid_no];
sdtr_accept = TRUE;
orig_offset = ext_msg.req_ack_offset;
if (ext_msg.xfer_period < tinfo->goal.period) {
sdtr_accept = FALSE;
ext_msg.xfer_period = tinfo->goal.period;
}
/* Perform range checking */
period = ext_msg.xfer_period;
offset = ext_msg.req_ack_offset;
adv_period_offset_to_sdtr(adv, &period, &offset, tid_no);
ext_msg.xfer_period = period;
ext_msg.req_ack_offset = offset;
/* Record our current sync settings */
adv_set_syncrate(adv, ccb->ccb_h.path,
tid_no, ext_msg.xfer_period,
ext_msg.req_ack_offset,
ADV_TRANS_GOAL|ADV_TRANS_ACTIVE);
/* Offset too high or large period forced async */
if (orig_offset != ext_msg.req_ack_offset)
sdtr_accept = FALSE;
if (sdtr_accept && (q_cntl & QC_MSG_OUT)) {
/* Valid response to our requested negotiation */
q_cntl &= ~QC_MSG_OUT;
} else {
/* Must Respond */
q_cntl |= QC_MSG_OUT;
adv_msgout_sdtr(adv, ext_msg.xfer_period,
ext_msg.req_ack_offset);
}
} else if (ext_msg.msg_type == MSG_EXTENDED
&& ext_msg.msg_req == MSG_EXT_WDTR
&& ext_msg.msg_len == MSG_EXT_WDTR_LEN) {
ext_msg.wdtr_width = 0;
adv_write_lram_16_multi(adv, ADVV_MSGOUT_BEG,
(u_int16_t *)&ext_msg,
sizeof(ext_msg) >> 1);
q_cntl |= QC_MSG_OUT;
} else {
ext_msg.msg_type = MSG_MESSAGE_REJECT;
adv_write_lram_16_multi(adv, ADVV_MSGOUT_BEG,
(u_int16_t *)&ext_msg,
sizeof(ext_msg) >> 1);
q_cntl |= QC_MSG_OUT;
}
adv_write_lram_8(adv, halt_q_addr + ADV_SCSIQ_B_CNTL, q_cntl);
}
static void
adv_msgout_sdtr(struct adv_softc *adv, u_int8_t sdtr_period,
u_int8_t sdtr_offset)
{
struct ext_msg sdtr_buf;
sdtr_buf.msg_type = MSG_EXTENDED;
sdtr_buf.msg_len = MSG_EXT_SDTR_LEN;
sdtr_buf.msg_req = MSG_EXT_SDTR;
sdtr_buf.xfer_period = sdtr_period;
sdtr_offset &= ADV_SYN_MAX_OFFSET;
sdtr_buf.req_ack_offset = sdtr_offset;
adv_write_lram_16_multi(adv, ADVV_MSGOUT_BEG,
(u_int16_t *) &sdtr_buf,
sizeof(sdtr_buf) / 2);
}
int
adv_abort_ccb(struct adv_softc *adv, int target, int lun, union ccb *ccb,
u_int32_t status, int queued_only)
{
u_int16_t q_addr;
u_int8_t q_no;
struct adv_q_done_info scsiq_buf;
struct adv_q_done_info *scsiq;
u_int8_t target_ix;
int count;
scsiq = &scsiq_buf;
target_ix = ADV_TIDLUN_TO_IX(target, lun);
count = 0;
for (q_no = ADV_MIN_ACTIVE_QNO; q_no <= adv->max_openings; q_no++) {
q_addr = ADV_QNO_TO_QADDR(q_no);
adv_copy_lram_doneq(adv, q_addr, scsiq, adv->max_dma_count);
if (((scsiq->q_status & QS_READY) != 0)
&& ((scsiq->q_status & QS_ABORTED) == 0)
&& ((scsiq->cntl & QCSG_SG_XFER_LIST) == 0)
&& (scsiq->d2.target_ix == target_ix)
&& (queued_only == 0
|| !(scsiq->q_status & (QS_DISC1|QS_DISC2|QS_BUSY|QS_DONE)))
&& (ccb == NULL || (ccb == (union ccb *)scsiq->d2.ccb_ptr))) {
union ccb *aborted_ccb;
struct adv_ccb_info *cinfo;
scsiq->q_status |= QS_ABORTED;
adv_write_lram_8(adv, q_addr + ADV_SCSIQ_B_STATUS,
scsiq->q_status);
aborted_ccb = (union ccb *)scsiq->d2.ccb_ptr;
/* Don't clobber earlier error codes */
if ((aborted_ccb->ccb_h.status & CAM_STATUS_MASK)
== CAM_REQ_INPROG)
aborted_ccb->ccb_h.status |= status;
cinfo = (struct adv_ccb_info *)
aborted_ccb->ccb_h.ccb_cinfo_ptr;
cinfo->state |= ACCB_ABORT_QUEUED;
count++;
}
}
return (count);
}
int
adv_reset_bus(struct adv_softc *adv)
{
int count;
int i;
union ccb *ccb;
adv_reset_chip_and_scsi_bus(adv);
adv_reinit_lram(adv);
for (i = 0; i <= ADV_MAX_TID; i++) {
if (adv->fix_asyn_xfer & (0x01 << i))
adv_set_sdtr_reg_at_id(adv, i,
ASYN_SDTR_DATA_FIX_PCI_REV_AB);
}
ADV_OUTW(adv, ADV_REG_PROG_COUNTER, ADV_MCODE_START_ADDR);
/* Tell the XPT layer that a bus reset occured */
if (adv->path != NULL)
xpt_async(AC_BUS_RESET, adv->path, NULL);
count = 0;
while ((ccb = (union ccb *)LIST_FIRST(&adv->pending_ccbs)) != NULL) {
if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG)
ccb->ccb_h.status |= CAM_SCSI_BUS_RESET;
adv_done(adv, ccb, QD_ABORTED_BY_HOST, 0, 0, 0);
count++;
}
adv_start_chip(adv);
return (count);
}
static void
adv_set_sdtr_reg_at_id(struct adv_softc *adv, int tid, u_int8_t sdtr_data)
{
int orig_id;
adv_set_bank(adv, 1);
orig_id = ffs(ADV_INB(adv, ADV_HOST_SCSIID)) - 1;
ADV_OUTB(adv, ADV_HOST_SCSIID, tid);
if (ADV_INB(adv, ADV_HOST_SCSIID) == (0x01 << tid)) {
adv_set_bank(adv, 0);
ADV_OUTB(adv, ADV_SYN_OFFSET, sdtr_data);
}
adv_set_bank(adv, 1);
ADV_OUTB(adv, ADV_HOST_SCSIID, orig_id);
adv_set_bank(adv, 0);
}