adv_pci.c:

adw_pci.c:
	Update comments describing supported chips/cards.

adwcam.c:
adwlib.c:
adwlib.h:
	Handle more error return codes from the firmware.

	Break out the bus reset code into its own function.

	Usa a constant for the bus reset hold delay.

	Fix an interrupt race problem in adw_idle_cmd_send by
	incorporating the poll loop for command completion.

Approved by:	jkh@FreeBSDorg
This commit is contained in:
Justin T. Gibbs 2000-03-02 00:08:35 +00:00
parent 1285d5557c
commit 58d246367e
5 changed files with 115 additions and 99 deletions

View file

@ -12,20 +12,21 @@
* ABP930 - Bus-Master PCI (16 CDB) *
* ABP930U - Bus-Master PCI Ultra (16 CDB)
* ABP930UA - Bus-Master PCI Ultra (16 CDB)
* ABP940UA/3940UA - Bus-Master PCI Ultra (240 CDB)
* ABP960 - Bus-Master PCI MAC/PC (16 CDB) **
* ABP960U - Bus-Master PCI MAC/PC Ultra (16 CDB)
* ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB)
* ABP3960UA - Bus-Master PCI MAC/PC (240 CDB)
* ABP960U - Bus-Master PCI MAC/PC (16 CDB) **
*
* Single Channel Products:
* ABP940 - Bus-Master PCI (240 CDB)
* ABP940U - Bus-Master PCI Ultra (240 CDB)
* ABP970 - Bus-Master PCI MAC/PC (240 CDB)
* ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB)
* ABP940 - Bus-Master PCI (240 CDB)
* ABP940U - Bus-Master PCI Ultra (240 CDB)
* ABP940UA/3940UA - Bus-Master PCI Ultra (240 CDB)
* ABP3960UA - Bus-Master PCI MAC/PC (240 CDB)
* ABP970 - Bus-Master PCI MAC/PC (240 CDB)
* ABP970U - Bus-Master PCI MAC/PC Ultra (240 CDB)
*
* Dual Channel Products:
* ABP950 - Dual Channel Bus-Master PCI (240 CDB Per Channel)
* ABP980 - Four Channel Bus-Master PCI (240 CDB Per Channel)
* ABP980U - Four Channel Bus-Master PCI Ultra (240 CDB Per Channel)
* ABP980UA/3980UA - Four Channel Bus-Master PCI Ultra (16 CDB Per Chan.)
*
* Footnotes:

View file

@ -4,7 +4,9 @@
*
* ABP[3]940UW - Bus-Master PCI Ultra-Wide (253 CDB)
* ABP950UW - Dual Channel Bus-Master PCI Ultra-Wide (253 CDB/Channel)
* ABP970UW - Bus-Master PCI Ultra-Wide (253 CDB)
* ABP3940U2W - Bus-Master PCI LVD/Ultra2-Wide (253 CDB)
* ABP3950U2W - Bus-Master PCI LVD/Ultra2-Wide (253 CDB)
*
* Copyright (c) 1998, 1999, 2000 Justin Gibbs.
* All rights reserved.

View file

@ -78,6 +78,7 @@
u_long adw_unit;
static __inline cam_status adwccbstatus(union ccb*);
static __inline struct acb* adwgetacb(struct adw_softc *adw);
static __inline void adwfreeacb(struct adw_softc *adw,
struct acb *acb);
@ -101,6 +102,12 @@ static void adw_handle_device_reset(struct adw_softc *adw,
static void adw_handle_bus_reset(struct adw_softc *adw,
int initiated);
static __inline cam_status
adwccbstatus(union ccb* ccb)
{
return (ccb->ccb_h.status & CAM_STATUS_MASK);
}
static __inline struct acb*
adwgetacb(struct adw_softc *adw)
{
@ -324,7 +331,7 @@ adwexecuteacb(void *arg, bus_dma_segment_t *dm_segs, int nseg, int error)
splx(s);
return;
}
acb->state |= ACB_ACTIVE;
ccb->ccb_h.status |= CAM_SIM_QUEUED;
LIST_INSERT_HEAD(&adw->pending_ccbs, &ccb->ccb_h, sim_links.le);
@ -393,7 +400,7 @@ adw_action(struct cam_sim *sim, union ccb *ccb)
if ((ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0) {
switch (csio->tag_action) {
case MSG_SIMPLE_Q_TAG:
acb->queue.scsi_cntl = 0;
acb->queue.scsi_cntl = ADW_QSC_SIMPLE_Q_TAG;
break;
case MSG_HEAD_OF_Q_TAG:
acb->queue.scsi_cntl = ADW_QSC_HEAD_OF_Q_TAG;
@ -401,6 +408,9 @@ adw_action(struct cam_sim *sim, union ccb *ccb)
case MSG_ORDERED_Q_TAG:
acb->queue.scsi_cntl = ADW_QSC_ORDERED_Q_TAG;
break;
default:
acb->queue.scsi_cntl = ADW_QSC_NO_TAGMSG;
break;
}
} else
acb->queue.scsi_cntl = ADW_QSC_NO_TAGMSG;
@ -498,9 +508,8 @@ adw_action(struct cam_sim *sim, union ccb *ccb)
{
adw_idle_cmd_status_t status;
adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET,
ccb->ccb_h.target_id);
status = adw_idle_cmd_wait(adw);
status = adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET,
ccb->ccb_h.target_id);
if (status == ADW_IDLE_CMD_SUCCESS) {
ccb->ccb_h.status = CAM_REQ_CMP;
if (bootverbose) {
@ -748,28 +757,17 @@ adw_action(struct cam_sim *sim, union ccb *ccb)
}
case XPT_RESET_BUS: /* Reset the specified SCSI bus */
{
adw_idle_cmd_status_t status;
int failure;
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_START,
/*param*/0);
status = adw_idle_cmd_wait(adw);
if (status != ADW_IDLE_CMD_SUCCESS) {
failure = adw_reset_bus(adw);
if (failure != 0) {
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
xpt_done(ccb);
break;
}
DELAY(100);
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_END, /*param*/0);
status = adw_idle_cmd_wait(adw);
if (status != ADW_IDLE_CMD_SUCCESS) {
ccb->ccb_h.status = CAM_REQ_CMP_ERR;
xpt_done(ccb);
break;
}
ccb->ccb_h.status = CAM_REQ_CMP;
if (bootverbose) {
xpt_print_path(adw->path);
printf("Bus Reset Delivered\n");
} else {
if (bootverbose) {
xpt_print_path(adw->path);
printf("Bus Reset Delivered\n");
}
ccb->ccb_h.status = CAM_REQ_CMP;
}
xpt_done(ccb);
break;
@ -1385,8 +1383,16 @@ adwprocesserror(struct adw_softc *adw, struct acb *acb)
case QHSTA_M_UNEXPECTED_BUS_FREE:
ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
break;
case QHSTA_M_SCSI_BUS_RESET:
case QHSTA_M_SCSI_BUS_RESET_UNSOL:
ccb->ccb_h.status = CAM_SCSI_BUS_RESET;
break;
case QHSTA_M_BUS_DEVICE_RESET:
ccb->ccb_h.status = CAM_BDR_SENT;
break;
case QHSTA_M_QUEUE_ABORTED:
/* BDR or Bus Reset */
printf("Saw Queue Aborted\n");
ccb->ccb_h.status = adw->last_reset;
break;
case QHSTA_M_SXFR_SDMA_ERR:
@ -1397,23 +1403,10 @@ adwprocesserror(struct adw_softc *adw, struct acb *acb)
case QHSTA_M_WTM_TIMEOUT:
case QHSTA_M_SXFR_WD_TMO:
{
adw_idle_cmd_status_t status;
/* The SCSI bus hung in a phase */
ccb->ccb_h.status = CAM_SEQUENCE_FAIL;
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_START,
/*param*/0);
status = adw_idle_cmd_wait(adw);
if (status != ADW_IDLE_CMD_SUCCESS)
panic("%s: Bus Reset during WD timeout failed",
adw_name(adw));
DELAY(100);
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_END,
/*param*/0);
status = adw_idle_cmd_wait(adw);
if (status != ADW_IDLE_CMD_SUCCESS)
panic("%s: Bus Reset during WD timeout failed",
adw_name(adw));
xpt_print_path(adw->path);
printf("Watch Dog timer expired. Reseting bus\n");
adw_reset_bus(adw);
break;
}
case QHSTA_M_SXFR_XFR_PH_ERR:
@ -1445,6 +1438,11 @@ adwprocesserror(struct adw_softc *adw, struct acb *acb)
/* NOTREACHED */
}
}
if ((acb->state & ACB_RECOVERY_ACB) != 0) {
if (ccb->ccb_h.status == CAM_SCSI_BUS_RESET
|| ccb->ccb_h.status == CAM_BDR_SENT)
ccb->ccb_h.status = CAM_CMD_TIMEOUT;
}
if (ccb->ccb_h.status != CAM_REQ_CMP) {
xpt_freeze_devq(ccb->ccb_h.path, /*count*/1);
ccb->ccb_h.status |= CAM_DEV_QFRZN;
@ -1460,6 +1458,7 @@ adwtimeout(void *arg)
union ccb *ccb;
struct adw_softc *adw;
adw_idle_cmd_status_t status;
int target_id;
int s;
acb = (struct acb *)arg;
@ -1478,29 +1477,21 @@ adwtimeout(void *arg)
return;
}
acb->state |= ACB_RECOVERY_ACB;
target_id = ccb->ccb_h.target_id;
/* Attempt a BDR first */
adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET,
ccb->ccb_h.target_id);
status = adw_idle_cmd_send(adw, ADW_IDLE_CMD_DEVICE_RESET,
ccb->ccb_h.target_id);
splx(s);
status = adw_idle_cmd_wait(adw);
if (status == ADW_IDLE_CMD_SUCCESS) {
printf("%s: BDR Delivered. No longer in timeout\n",
adw_name(adw));
adw_handle_device_reset(adw, ccb->ccb_h.target_id);
adw_handle_device_reset(adw, target_id);
} else {
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_START,
/*param*/0);
status = adw_idle_cmd_wait(adw);
if (status != ADW_IDLE_CMD_SUCCESS)
panic("%s: Bus Reset during timeout failed",
adw_name(adw));
DELAY(100);
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_END,
/*param*/0);
status = adw_idle_cmd_wait(adw);
if (status != ADW_IDLE_CMD_SUCCESS)
panic("%s: Bus Reset during timeout failed",
adw_name(adw));
adw_reset_bus(adw);
xpt_print_path(adw->path);
printf("Bus Reset Delivered. No longer in timeout\n");
}
}

View file

@ -189,6 +189,32 @@ adw_reset_chip(struct adw_softc *adw)
adw_inw(adw, ADW_SCSI_CFG1) & ~ADW_SCSI_CFG1_BIG_ENDIAN);
}
/*
* Reset the SCSI bus.
*/
int
adw_reset_bus(struct adw_softc *adw)
{
adw_idle_cmd_status_t status;
status =
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_START, /*param*/0);
if (status != ADW_IDLE_CMD_SUCCESS) {
xpt_print_path(adw->path);
printf("Bus Reset start attempt failed\n");
return (1);
}
DELAY(ADW_BUS_RESET_HOLD_DELAY_US);
status =
adw_idle_cmd_send(adw, ADW_IDLE_CMD_SCSI_RESET_END, /*param*/0);
if (status != ADW_IDLE_CMD_SUCCESS) {
xpt_print_path(adw->path);
printf("Bus Reset end attempt failed\n");
return (1);
}
return (0);
}
/*
* Read the specified EEPROM location
*/
@ -818,22 +844,22 @@ adw_hshk_cfg_period_factor(u_int tinfo)
}
/*
* Send an idle command to the chip and optionally wait for completion.
* Send an idle command to the chip and wait for completion.
*/
void
adw_idle_cmd_status_t
adw_idle_cmd_send(struct adw_softc *adw, adw_idle_cmd_t cmd, u_int parameter)
{
int s;
adw->idle_command_cmp = 0;
u_int timeout;
adw_idle_cmd_status_t status;
int s;
s = splcam();
if (adw->idle_cmd != ADW_IDLE_CMD_COMPLETED)
printf("%s: Warning! Overlapped Idle Commands Attempted\n",
adw_name(adw));
adw->idle_cmd = cmd;
adw->idle_cmd_param = parameter;
/*
* Clear the idle command status which is set by the microcode
* to a non-zero value to indicate when the command is completed.
*/
adw_lram_write_16(adw, ADW_MC_IDLE_CMD_STATUS, 0);
/*
* Write the idle command value after the idle command parameter
@ -841,37 +867,25 @@ adw_idle_cmd_send(struct adw_softc *adw, adw_idle_cmd_t cmd, u_int parameter)
* followed, the microcode may process the idle command before the
* parameters have been written to LRAM.
*/
adw_lram_write_16(adw, ADW_MC_IDLE_CMD_PARAMETER, parameter);
adw_lram_write_32(adw, ADW_MC_IDLE_CMD_PARAMETER, parameter);
adw_lram_write_16(adw, ADW_MC_IDLE_CMD, cmd);
/*
* Tickle the RISC to tell it to process the idle command.
*/
adw_tickle_risc(adw, ADW_TICKLE_B);
splx(s);
}
/* Wait for an idle command to complete */
adw_idle_cmd_status_t
adw_idle_cmd_wait(struct adw_softc *adw)
{
u_int timeout;
adw_idle_cmd_status_t status;
int s;
/* Wait for up to 10 seconds for the command to complete */
timeout = 10000;
timeout = 5000000;
while (--timeout) {
s = splcam();
status = adw_lram_read_16(adw, ADW_MC_IDLE_CMD_STATUS);
splx(s);
if (status != 0)
break;
DELAY(1000);
DELAY(20);
}
if (timeout == 0)
panic("%s: Idle Command Timed Out!\n", adw_name(adw));
adw->idle_cmd = ADW_IDLE_CMD_COMPLETED;
splx(s);
return (status);
}

View file

@ -342,11 +342,18 @@ typedef enum {
QHSTA_M_SXFR_DESELECTED = 0x22, /* Deselected */
QHSTA_M_SXFR_XFR_PH_ERR = 0x24, /* Transfer Phase Error */
QHSTA_M_SXFR_UNKNOWN_ERROR = 0x25, /* SXFR_STATUS Unknown Error */
QHSTA_M_SCSI_BUS_RESET = 0x30, /* Request aborted from SBR */
QHSTA_M_SCSI_BUS_RESET_UNSOL= 0x31, /* Request aborted from unsol. SBR*/
QHSTA_M_BUS_DEVICE_RESET = 0x32, /* Request aborted from BDR */
QHSTA_M_DIRECTION_ERR = 0x35, /* Data Phase mismatch */
QHSTA_M_DIRECTION_ERR_HUNG = 0x36, /* Data Phase mismatch - bus hang */
QHSTA_M_WTM_TIMEOUT = 0x41,
QHSTA_M_BAD_CMPL_STATUS_IN = 0x42,
QHSTA_M_NO_AUTO_REQ_SENSE = 0x43,
QHSTA_M_AUTO_REQ_SENSE_FAIL = 0x44,
QHSTA_M_INVALID_DEVICE = 0x45 /* Bad target ID */
QHSTA_M_INVALID_DEVICE = 0x45, /* Bad target ID */
QHSTA_M_FROZEN_TIDQ = 0x46, /* TID Queue frozen. */
QHSTA_M_SGBACKUP_ERROR = 0x47 /* Scatter-Gather backup error */
} host_status_t;
typedef enum {
@ -380,6 +387,7 @@ struct adw_scsi_req_q {
#define ADW_QSC_NO_SYNC 0x04
#define ADW_QSC_NO_WIDE 0x08
#define ADW_QSC_REDO_DTR 0x10 /* Renegotiate WDTR/SDTR */
#define ADW_QSC_SIMPLE_Q_TAG 0x00
#define ADW_QSC_HEAD_OF_Q_TAG 0x40
#define ADW_QSC_ORDERED_Q_TAG 0x80
u_int8_t done_status; /* Completion status. */
@ -405,7 +413,8 @@ struct adw_scsi_req_q {
typedef enum {
ACB_FREE = 0x00,
ACB_ACTIVE = 0x01,
ACB_RELEASE_SIMQ = 0x02
ACB_RELEASE_SIMQ = 0x02,
ACB_RECOVERY_ACB = 0x04
} acb_state;
struct acb {
@ -542,6 +551,8 @@ struct adw_eeprom
#define ADW_EEP_DVC_CTL_BEGIN (offsetof(struct adw_eeprom, oem_name)/2)
#define ADW_EEP_MAX_WORD_ADDR (sizeof(struct adw_eeprom)/2)
#define ADW_BUS_RESET_HOLD_DELAY_US 100
typedef enum {
ADW_CHIP_NONE,
ADW_CHIP_ASC3550, /* Ultra-Wide IC */
@ -636,9 +647,6 @@ struct adw_softc
char* name;
cam_status last_reset; /* Last reset type */
u_int16_t bios_ctrl;
adw_idle_cmd_t idle_cmd;
u_int idle_cmd_param;
volatile int idle_command_cmp;
u_int16_t user_wdtr;
u_int16_t user_sdtr[4]; /* A nibble per-device */
u_int16_t user_tagenb;
@ -806,6 +814,7 @@ carrierbtov(struct adw_softc *adw, u_int32_t baddr)
/* Intialization */
int adw_find_signature(struct adw_softc *adw);
void adw_reset_chip(struct adw_softc *adw);
int adw_reset_bus(struct adw_softc *adw);
u_int16_t adw_eeprom_read(struct adw_softc *adw, struct adw_eeprom *buf);
void adw_eeprom_write(struct adw_softc *adw, struct adw_eeprom *buf);
int adw_init_chip(struct adw_softc *adw, u_int term_scsicfg1);
@ -819,9 +828,8 @@ u_int adw_find_period(struct adw_softc *adw, u_int mc_sdtr);
u_int adw_hshk_cfg_period_factor(u_int tinfo);
/* Idle Commands */
void adw_idle_cmd_send(struct adw_softc *adw, u_int cmd,
adw_idle_cmd_status_t adw_idle_cmd_send(struct adw_softc *adw, u_int cmd,
u_int parameter);
adw_idle_cmd_status_t adw_idle_cmd_wait(struct adw_softc *adw);
/* SCSI Transaction Processing */
static __inline void adw_send_acb(struct adw_softc *adw, struct acb *acb,