1
0
Files
2022-09-29 17:59:04 +03:00

6112 lines
159 KiB
C

/** Edit history:
**
** 01/04/95 robertn started port to sgi. This driver is base on
** the Qlogic SCO driver
*/
/*
** For initial SN0 bring up we use the wd93 chip on the HUB's
** JUNK Bus. This means that we cannot link ql.o as we need to
** have only 1 set of routines like alloc_subchannel, doscsi etc.
*/
/******************************************************************************/
/**********************************************************
**
** Debug options
**
**********************************************************/
#if SN0
#define A64_BIT_OPERATION
#define SN0_PCI_64
#endif
#ifdef DEBUG
#define QL_DEBUG /* include debug code */
#define MBOX_DEBUG /* debug prints in the mbox routine */
#define QL_IOCTL /* include code to support ha ioctls */
#define DUMP_REGS /* dump ql registers to console */
/*#define DUMP_PARMS dump mail box type parms */
#define DUMP_IOCB /* dump iocb to console */
#endif
/**********************************************************
**
** Compile time options
*/
/*
** Some miscellaneous configuration parameters.
*/
#define ENABLE_BURST /* define to enable PCI bursting */
/*
** Some general run-time options.
*/
#define TAGGED_CMDS /* support tagged commands */
/*
** The following are initialization options.
** They are mainly used to assist anyone that is responsible for bringing
** up a ISP for the first time. Once the driver is running, most of these
** should be removed (including the code).
*/
/*#define CLEAR_INTR_FIRST clear the interrupt control register */
/*#define SOFT_RESET issue a soft reset to the chip */
/*#define CHECK_HCCR_FIRST check hccr before loading firmware */
#define LOAD_FIRMWARE /* define to load new firmware */
/*#define SWAP_DPROM swap the firmware array -- for dproms only */
#define MAILBOX_TEST /* test mailboxes (firmware running?) */
#define CHECKSUM_TEST /* verify the checksum we calulate with */
/* the checksum the ISP calulates */
#ifdef IP30
#undef VERIFY_TEST /* verify the RISC firmware word by word */
#undef USE_MAPPED_CONTROL_STREAM
#define SWAP_DATA_STREAM
#define LOAD_VIA_DMA /* load firmware with DMA else a word at a
* time */
#endif
#define SCSI_TIMEOUT_WAR
#if defined (SN0) && defined (SCSI_TIMEOUT_WAR)
#define SCSI_TO_MAX_RETRY 3
int first_adaptor = 1;
#endif
/*
** Leave normal_startup disabled and init_at_open enabled when first bringing
** up this driver. This will allow you to run the init routine by opening
** the host adapter device instead of letting the system do it at start up
** time. Doing it at system start up time can let debug text scroll off the
** screen and if it should fail, leave you with an unbootable system (yuck).
** I would also use poll mode. This prevents interrupts from being enabled
** and polls when ever an interrupt is expected. Once this is working
** successfully, enable the interrupts.
*/
#define NORMAL_STARTUP /* continue normal init */
/*
** The following define the way the mailbox registers are handled.
** Once this is stable, delete code associated with options that are
** commented out and delete conditionals of enabled options.
*/
#define WAIT_SEMA /*wait for semaphore (else wait on
* interrupt) */
/*
** Enabling these options generates multiple adapter requests for a
** single device driver request. The device driver's request is not
** returned until the last "copy" of the request is returned from the
** host adapter. This allows us to trash the adapter (firmware) with
** minimal system overhead. This also allows us to load up the adapter
** with a lot more requests than most systems could generate.
** XXX robertn - this has never been tested
*/
/*#define MAX_DUPS 32 issue 32 multiples of the same command */
/*
** Some hard coded parameters (use these to bring up driver).
*/
#define MEM_BASE_ADDRESS 0xbf500000
/**********************************************************
**
** Include files
*/
#include "sys/types.h"
#include <sys/debug.h>
#include "sys/param.h"
#include "sys/scsi.h"
#include "sys/newscsi.h"
#include <sys/scsimsgs.h> /* correct values of SC_NUMADDSENSE */
#include "sys/edt.h"
#include "sys/cpu.h"
#include "sys/PCI/PCI_defs.h"
#include "sys/time.h"
#include "sys/buf.h"
#include "sys/pfdat.h"
#include "sys/dkio.h"
#include <sys/invent.h>
#include <sys/mtio.h>
#include "sys/tpsc.h"
#include "sys/cmn_err.h"
#include "sys/sysmacros.h"
#include "sys/kmem.h"
#ifdef DEBUG
#include <sys/idbg.h>
#include <sys/debug.h>
#endif
#ifdef QL_IOCTL
#include "sys/dir.h"
#include "sys/xlate.h"
#include "sys/proc.h"
#endif
#include "sys/systm.h"
#include "sys/errno.h"
#include <arcs/hinv.h>
#include <arcs/cfgdata.h>
#include <sys/scsidev.h>
#include <sys/immu.h>
#include "sys/PCI/bridge.h"
#include <standcfg.h>
#ifdef SN0
#include <libkl.h>
#include <pgdrv.h>
#include <sys/SN/SN0/ip27config.h>
#include <sys/SN/addrs.h>
#include <sys/SN/kldiag.h>
#include <sys/SN/nvram.h>
#include <kllibc.h>
#if defined (SCSI_TIMEOUT_WAR)
#include <ksys/elsc.h>
#endif /* SCSI_TIMEOUT_WAR */
#endif
#include <libsc.h>
#include <libsk.h>
#include <sys/dmamap.h>
#if QL_MAXLUN > 8
#error too many luns for dev_init field!
#endif
#define initialized(ha,id,lun) (ha->dev_init[id] & (1<<lun))
#undef MAX_ADAPTERS /* standalone uses SC_MAXADAP */
union scsicfg
{
struct scsidisk_data disk;
struct floppy_data fd;
};
#ifdef SN0
void kl_qlogic_AddChild(COMPONENT*, union scsicfg *) ;
#endif
struct scsi_target_info *
ql_u_cmd(u_char, u_char, u_char,
u_char, u_char, u_char) ;
void cdrom_revert(u_char, u_char, u_char) ;
#include <sys/pci_intf.h>
#include "sys/ql_firmware_standalone.h"
#include "sys/ql_standalone.h" /* structure definitions for adapter */
/****************************************************************/
/* Function Prototypes */
/* */
void qlintr(pHA_INFO);
int ql_entry(scsi_request_t *);
struct scsi_target_info tinfo[SC_MAXADAP][QL_MAXTARG][QL_MAXLUN];
int qlreset(u_char adap);
struct scsi_target_info *qlinfo(u_char extctlr, u_char targ, u_char lun);
static void ql_set_defaults(pHA_INFO ha);
static int reqi_get_slot(pHA_INFO ha,int);
static int ql_init_board(int, u_short, caddr_t, paddr_t, void *);
static int ql_init_isp(pHA_INFO);
static void ql_service_interrupt(pHA_INFO);
static void ql_queue_cmd(pHA_INFO, scsi_request_t *);
static void ql_start_scsi(pHA_INFO);
static int
ql_mbox_cmd(pHA_INFO, u_short *, u_char, u_char,
u_short, u_short, u_short, u_short,
u_short, u_short, u_short, u_short);
void munge(u_char * pointer, int count);
int munge_bp(buf_t * bp, uint len);
int make_sg(scatter * scatter, u_char * addr, int len, pHA_INFO);
int ql_init(u_int slot, caddr_t config_addr, paddr_t mem_map_addr,
void *bus_base);
#if HEART_COHERENCY_WAR
/* flush both memory reads and writes */
#define MR_DCACHE_WB_INVAL(a,l) heart_dcache_wb_inval(a,l)
#define MRW_DCACHE_WB_INVAL(a,l) heart_write_dcache_wb_inval(a,l)
#define DCACHE_INVAL(a,l) heart_dcache_inval(a,l)
#else
#define MR_DCACHE_WB_INVAL(a,l)
#define MRW_DCACHE_WB_INVAL(a,l)
#define DCACHE_INVAL(a,l)
#endif /* HEART_COHERENCY_WAR || HEART_WRITE_COHERENCY_WAR */
void qlhinv(u_char * inv, u_char ctlr, u_char targ, u_char lun);
#ifdef DUMP_PARMS
static void dump_parameters(int board);
#endif
#ifdef DUMP_IOCB
static void dump_iocb(command_entry * q_ptr);
#endif
#ifdef QL_DEBUG
static void dump_iorb(status_entry * q_ptr);
static void dump_marker(marker_entry * q_ptr);
static void dump_continuation(continuation_entry * q_ptr);
void dump_area(char *ptr, int count);
#endif
void add_deltatime(struct timeval * current_time, struct timeval * old_time, struct timeval * add_to_this);
#if 0 /* not used right now */
static void dump_request(scsi_request_t * request);
#endif
#ifdef DUMP_REGS
static void dump_registers(pISP_REGS isp);
#endif
void freesubchannel(scsisubchan_t *);
void ql_clear_structs(void);
#if defined(IP30)
void qlconfigure(COMPONENT *,slotinfo_t *);
#elif defined(SN0)
#ifndef SN_PDI
void qlconfigure(COMPONENT *, char *, char *);
#else
void qlconfigure(pd_inst_hdl_t *pdih, pd_inst_parm_t *pdip) ;
#endif
#else
void qlconfigure(COMPONENT *);
#endif
#ifdef IP30
static void stand_qlhinv(COMPONENT *, slotinfo_t *);
#endif
static void ql_poll(void);
void doscsi(scsisubchan_t * scsisubchan);
static caddr_t scsi_ecmd(u_char, u_char, u_char, u_char *, int, void *, int);
static int scsi_rc(u_char, u_char, u_char, struct scsidisk_data *);
scsisubchan_t *allocsubchannel(int adap, int targ, int lun);
extern scsidev_t *scsidev;
extern int scsicnt; /* set to 1 in master.d/scsi, and changed in
* IP*.c for machines that * support more
* than one */
void setgioconfig(int slot, int flags);
u_char scsi_ha_id[SC_MAXADAP]; /* allows users in stand stuff to
* check if a particulary id is the
* host adapter, rather than
* embedding the id in every user of
* the driver. For now, host adapter
* id is always 0. */
/*
** Use these routines to print debug information.
** CMN_ERR information will print to the console but NOT immediately.
** This information is also printed into the system log.
** Use PRINTx for other (temporary) debug print statements.
** Check the initial value of the variables: ql_exterr and ql_debug.
** You can use a simple ioctl call to disable debug prints this way.
**
** Use PRINTD in initialization routines.
** PRINT1 will only print if the debug print level is 1.
** Use this to print a single character for the operation being perfomed.
** This prints a cryptic set of characters you can use to check flow
** of operations. If nothing else, you can stare at it for hours and
** impress your friends (talk about a GURU).
** Use PRINT2 for more information (use sparingly).
** I used PRINT3 and 4 only for the mailbox routines (in case its hanging).
*/
#ifdef QL_DEBUG
static int ql_exterr = 1;
#define CMN_ERR(x) if (ql_exterr > 0) cmn_err x
int ql_debug = 4;
int ql_dump_control_block;
static int ql_slot_number = 0; /* this is the number of slots in the
* system. */
/* incremented each call to ql_attach */
#define PRINTD(x) if (ql_debug) printf x
#define PRINT1(x) if (ql_debug > 0) printf x
#define PRINT2(x) if (ql_debug > 1) printf x
#define PRINT3(x) if (ql_debug > 2) printf x
#define PRINT4(x) if (ql_debug > 3) printf x
#define PRINT5(x) if (ql_debug > 4) printf x
#else /* !QL_DEBUG */
#define ql_debug 0
#define CMN_ERR(x)
#define PRINTD(x)
#define PRINT1(x)
#define PRINT2(x)
#define PRINT3(x)
#define PRINT4(x)
#define PRINT5(x)
#endif /* QL_DEBUG */
int do_verify; /* keep in .bss for IP30 prom */
#if defined(IP30)
#define MAKE_DIRECT_MAPPED_2GIG(x) kv_to_bridge32_dirmap((ha->bus_base),x)
#define CLEAR_DIRECT_MAPPED_2GIG(x) bridge32_dirmap_to_phys((ha->bus_base),x)
#elif defined(SN0)
#define MAKE_DIRECT_MAPPED_2GIG(x) ((x) | 0x80000000 )
#define CLEAR_DIRECT_MAPPED_2GIG(x) ((x) & 0x7fffffff )
#else
#define MAKE_DIRECT_MAPPED_2GIG(x) (x)
#endif
/**********************************************************
**
** Global Kernel Information
*/
/**********************************************************
**
** Local Information
*/
#if IP30
/* Keep malloc size down when we have numberous many xtalk cards, which
* is reasonable if SC_MAXADAP < 32 or so
*/
#define _STATIC_HAINFO 1
static HA_INFO _ha_info[SC_MAXADAP];
#endif
pHA_INFO ha_info[SC_MAXADAP];
pHA_INFO ha_list;
/* holding place for sense data. */
/* auto sense data is placed here */
/* it is valid only for one command */
/* we can get away with this becuase the prom is single threaded */
static u_char last_sense[REQ_SENSE_DATA_LENGTH];
#ifdef SN0
/*
* This is a data structure parallel to ha_info. It maintains
* hardware specific info for SN0 to 'map' DMA and PIOs for
* multiple Qlogic adapters. This info is filled in
* during 'install'.
*/
static ULONG ha_sn0_info[SC_MAXADAP];
int dksc_map[SC_MAXADAP] ;
int dksc_inv_map[SC_MAXADAP] ;
#endif
#define INIT_SPECIFIC 0
#define INIT_NEXT 1
int sc_inq_stat; /* status from scsi_inq() */
/******************************************************************************/
/* Entry-Point
** Function: ql_init
**
** Description: Initialize all of the ISP's on the PCI bus.
*/
int
ql_init(u_int slot, caddr_t config_addr, paddr_t mem_map_addr, void *bus_base)
{
static int inited;
int cnt;
#ifdef QL_DEBUG
PRINT1(("ql_init: entered slot %x config addr %x mem map addr %x\n",
slot, config_addr, mem_map_addr));
#endif
/*
** Start with no adapters in the system.
*/
PRINT3(("ha_info %x\n", ha_info));
if (!inited)
{
PRINT4(("initializing ha_info\n"));
for (cnt = 0; cnt < SC_MAXADAP; cnt++)
{
ha_info[cnt] = NULL;
}
ha_list = NULL;
inited = 1;
}
/* go configured */
/* * If this Adapter has already been configured in, then ignore * the
* request. This will take care of duplicates. */
if (!ha_info[slot])
{
/* * Initialize this board. * If this fails, then it won't be marked
* as valid (ignore). */
if (ql_init_board(INIT_SPECIFIC,
slot,
config_addr, /* confiuration space address */
mem_map_addr,
bus_base))
{
cmn_err(CE_WARN, "Encountered a fatal error while initializing QL SCSI controller\n");
return (1);
}
}
#ifdef QL_DEBUG
PRINT3(("ql_init: exiting normally\n")); /* XXX */
PRINT3(("ql_init: ha_info[0] %x ha_info[1] %x \n", ha_info[0], ha_info[1]));
#endif
return (0);
}
/* this function was added because the malloc pool is erased at the end of */
/* the ponmore diags */
void
ql_clear_structs(void)
{
int cnt;
#ifdef QL_DEBUG1
ql_slot_number = 0; /* clear out the number of slots */
#endif
for (cnt = 0; cnt < SC_MAXADAP; cnt++)
{
PRINT1(("ql_clear_structs: CLEARING ha_info[%x]\n", cnt));
#ifndef _STATIC_HAINFO
if (ha_info[cnt])
free(ha_info[cnt]);
#endif
ha_info[cnt] = NULL;
}
ha_list = NULL;
}
/* Function: ql_init_board
**
** Description: Initialize specific ISP board.
** Returns: 0 = success
** 1 = failure
*/
static int
ql_init_board(int init_type,
u_short ha_index,
caddr_t config_addr,
paddr_t mem_map_addr,
void *bus_base)
{
pHA_INFO ha = NULL;
paddr_t Base_Address;
#ifdef QL_DEBUG
PRINT3(("ql_init_board: init_type=%s, ha_index=%d\n",
(init_type == INIT_SPECIFIC) ? "SPECIFIC" : "NEXT", ha_index));
PRINT1(("ql_init_board: config addr=0x%x, mem addr=0x%x bus_base=0x%x\n",
config_addr, mem_map_addr, bus_base)); /* XXX */
#endif
/*
** Make sure the adapter number is valid.
*/
if (ha_index >= SC_MAXADAP)
{
#ifdef QL_DEBUG
CMN_ERR((CE_WARN,
"ql_init - Invalid adapter number specified [%d]",
ha_index));
#endif
return (1);
}
/*
** If they gave us an address, make sure its our device.
** If not, find the next board.
*/
switch (init_type)
{
/*
** Determine if the specified device is present.
*/
case INIT_SPECIFIC:
{
pISP_REGS isp = (pISP_REGS) config_addr;
u_short vendor_id = 0;
u_short dev_id = 0;
/*
** Make sure this is an ISP.
*/
vendor_id = PCI_INH(&isp->bus_id_low);
dev_id = PCI_INH(&isp->bus_id_high);
#ifdef QL_DEBUG
PRINT3(("Data at isp address 0x%x: Vendor id=0x%x, Bus id=0x%x\n",
isp, vendor_id, dev_id));
#endif
/* * If this device is not ours, just leave. */
if (vendor_id != QLogic_VENDOR_ID ||
dev_id != QLogic_DEVICE_ID)
{
/* * Not our board! */
return (1);
}
/* * Use the addresses they specified. * Note: this doesn't care
* whether this is a memory address * or an I/O address (its
* whatever they specified). */
Base_Address = mem_map_addr;
}
break;
}
/*
** Allocate and init memory for the adapter structure.
*/
{
u_int id, lun;
u_int req_cnt;
pREQ_INFO r_ptr;
#ifndef _STATIC_HAINFO
PRINT1(("allocating ha struct for adapter adapter %x size %x\n", ha_index, sizeof(HA_INFO)));
ha = (pHA_INFO) malloc(sizeof(HA_INFO));
#else
ha = &_ha_info[ha_index];
#endif
bzero(ha, sizeof(HA_INFO));
ha_info[ha_index] = ha;
ha->controller_number = ha_index;
ha->ha_base = (caddr_t) Base_Address;
PRINT3(("base address set up ha_base is %x\n", ha->ha_base));
ha->ha_config_base = (caddr_t) config_addr;
#ifdef IP30
ha->bus_base = bus_base;
#endif
/*
** Clear the state flags.
*/
ha->flags = 0;
/*
** Put this adapter on the ha_list.
*/
ha->next_ha = (ha_list) ? ha_list : NULL;
ha_list = ha;
/*
** Init the raw request queue to empty.
*/
ha->req_forw = NULL;
ha->req_back = NULL;
/*
** Clear the device flags for all target lun combinations.
*/
for (id = 0; id < QL_MAXTARG; id++)
{
ha->dev_init[id] = 0; /* clears all luns */
r_ptr = &ha->reqi_block[id][0];
ha->reqi_cntr[id] = 1;
for (req_cnt = 0; req_cnt <= (MAX_REQ_INFO); req_cnt++, r_ptr++)
{
r_ptr->req = NULL;
}
}
/*
** Initialize the Req_Info.
*/
for (id = 0; id < QL_MAXTARG; id++) {
}
/*
** Note: if device is to be memory mapped, then we have to enable the
** memory (i.e. map it into the available address space).
** Either way, save the address for later accesses.
*/
}
/*
** Read in the default parameters.
** Provide the memory to store the novram parameters.
*/
ql_set_defaults(ha);
/*
** Go initialize the ISP.
*/
if (ql_init_isp(ha))
{
/*
** Failed to initialize the ISP!
*/
cmn_err(CE_WARN, "QL SCSI controller initialization failed\n");
#ifndef _STATIC_HAINFO
free(ha);
#endif
ha_info[ha_index] = NULL;
return (1); /* fail */
}
/*
** We added a parameter that can be used to LOGICALLY
** disable the board. If they set this flag, then we shouldn't
** publish this adapter board to the SCSI drivers. However,
** we do need to be able to open the device in order to update
** the parameters to re-enable this board.
** So say we failed the initialization, and check whether the
** actual initialization of the adapter failed (with INITIALIZED
** flag) in open routine.
*/
if (!ha->ql_defaults.HA_Enable)
{
return (1);
}
/*
** Indicate we found the board
*/
PRINTD(("Out of ql_init_board\n"));
return (0);
}
/* Function: ql_init_isp()
**
** Description: Initialize the ISP chip on a specific board.
** Returns: 0 = success
** 1 = failure
*/
static int
ql_init_isp(pHA_INFO ha)
{
u_short mbox_sts[8];
pISP_REGS isp = (pISP_REGS) ha->ha_base;
pPCI_REG isp_config = (pPCI_REG) ha->ha_config_base;
paddr_t p_ptr;
u_short checksum;
u_short mailbox0;
int i;
#ifdef IP30
int tmp;
#endif
int count;
/*REFERENCED*/
int mem_map = 0;
/*REFERENCED*/
int command = 0;
#ifdef IP30
bridge_t *bridge = BRIDGE_K1PTR;
ate_t ate;
#endif
u_short *w_ptr;
PCI_OUTH(&isp->bus_icr, 0); /* disable interrupts */
PCI_OUTH(&isp->hccr, HCCR_CMD_PAUSE);
PCI_OUTH(&isp->bus_icr, ICR_SOFT_RESET);
flushbus();
DELAY(2);
PCI_OUTH(&isp->control_dma_control, DMA_CON_RESET_INT|DMA_CON_CLEAR_CHAN);
PCI_OUTH(&isp->data_dma_control, DMA_CON_RESET_INT|DMA_CON_CLEAR_CHAN);
for (i = 0;i < 1000; i++) {
mailbox0 = PCI_INH(&isp->mailbox0);
if (mailbox0 != MBOX_STS_BUSY)
break;
DELAY(1000);
}
/*
** Setup the busy and idle timers
*/
#ifdef QL_DEBUG
PRINT3(("ql_init_isp: isp=0x%x\n", isp)); /* XXX */
PRINT3(("ql_init_isp: host adapter structure address %x\n", ha));
PRINT3(("ql_init_isp: host adapter base address %x\n", ha->ha_base));
PRINT3(("ql_init_isp: host adapter configuration base address %x\n",
isp_config));
PRINT3(("ql_init_isp: isp addresses at %x\n", isp));
#endif
#ifndef EVEREST
/* write the mapping register, enable mapping */
#ifndef IP30
PCI_OUTW(&isp_config->Memory_Base_Address,
(int) ((kvtophys) (ha->ha_base)));
#endif
#endif /* EVEREST */
mem_map = PCI_INW(&isp_config->Memory_Base_Address);
PRINT1(("memory mapped to %x at %x\n", mem_map,
&isp_config->Memory_Base_Address));
PCI_OUTH(&isp_config->Command, MEMORY_SPACE_ENABLE | BUS_MASTER_ENABLE);
command = PCI_INH(&isp_config->Command);
PRINT3(("Command reg set to %x at memory address %x\n",
command, &isp_config->Command));
/* set data directions, burst enable, interrupt enable */
/* PRINT2(("data_dma_config at %x\n",&isp->data_dma_config)); */
/* PCI_OUTH(&isp->data_dma_config,DMA_DATA_DIRECTION|BURST_ENABLE|INTERRUPT_ENABLE);*/
/* data_dma = PCI_INH(&isp->data_dma_config); */
PCI_OUTB(&isp_config->Latency_Timer, 0x40);
PCI_OUTB(&isp_config->Cache_Line_Size, 0x40);
PRINT3(("&isp_config->Latency_Timer %x\n", &isp_config->Latency_Timer));
PCI_OUTH(&isp->bus_icr, ICR_ENABLE_RISC_INT | ICR_SOFT_RESET);
DELAY(1000);
PRINTD(("reset the controller\n"));
PCI_OUTH(&isp->hccr, HCCR_CMD_RESET);
PCI_OUTH(&isp->hccr, HCCR_CMD_RELEASE);
PCI_OUTH(&isp->hccr, HCCR_WRITE_BIOS_ENABLE);
#ifdef CLEAR_INTR_FIRST
{
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Disabling hardware interrupts ...\n"));
#endif
/*
** Clear the interrupt allows (don't want any interrupts yet).
** We do this by only enabling the RISC interrupts (and not
** enabling the other interrupts).
*/
PCI_OUTH(&isp->bus_icr, ICR_ENABLE_RISC_INT);
}
#endif /* CLEAR_INTR_FIRST */
#ifdef SOFT_RESET
/*
** Perform a soft reset of the ISP chip.
** If there is any firmware running, the soft reset will stop
** its execution (this would be firmware loaded at POST time).
*/
{
u_short mailbox0;
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Issuing a soft reset ...\n"));
#endif
/*
** To do a soft reset we reset the interface,
** reset the RISC, and then release the RISC.
** We also have to disable the BIOS (manual not clear about this).
*/
PCI_OUTH(&isp->bus_icr, ICR_ENABLE_RISC_INT | ICR_SOFT_RESET);
/* XXX need delay here */
PRINTD(("enabled risc interrupt and soft reset %x\n",
PCI_INH(&isp->bus_icr)));
PCI_OUTH(&isp->hccr, HCCR_CMD_RESET);
PCI_OUTH(&isp->hccr, HCCR_CMD_RELEASE);
PCI_OUTH(&isp->hccr, HCCR_WRITE_BIOS_ENABLE);
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: ISP reset ...\n"));
#endif
/*
** Wait for mailbox 0 to clear.
** This means that the self test code has completed
** and its ready to accept mailbox commands.
*/
do
{
/*
** Get a copy of the mailbox0.
*/
mailbox0 = PCI_INH(&isp->mailbox0);
} while (mailbox0);
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Starting firmware ...\n"));
#endif
/*
** Start ISP firmware.
*/
if (ql_mbox_cmd(ha, mbox_sts, 2, 1,
MBOX_CMD_EXECUTE_FIRMWARE,
risc_code_addr01,
0, 0, 0, 0, 0, 0))
{
#ifdef QL_DEBUG
CMN_ERR((CE_WARN,
"ql_init_isp - EXECUTE FIRMWARE Command Failed\n"));
#endif
return (1);
}
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Firmware running ...\n"));
#endif
}
#else /* SOFT_RESET */
/*
** If we're NOT going to do a soft reset, then we need to
** load up some new firmware of our own. This is the perferred
** choice in that we can update the firmware by shipping a new
** software driver (i.e. the new firmware is imbedded in this
** code).
*/
#ifdef CHECK_HCCR_FIRST
/*
** Check the HCCR to see if there is any firmware running.
** I'm not sure if this is really necessary.
*/
{
u_short hccr;
/*
** Get a copy of the Host Command and Config register.
** Is there firmware running?
*/
hccr = PCI_INH(&isp->hccr);
/*
** If the RISC is not paused and not reset, then its running.
*/
if (!(hccr & (HCCR_RESET | HCCR_PAUSE)))
{
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: RISC running, stopping ...\n"));
#endif
/*
** Stop the current ISP firmware and begin execution
** in the internal ISP ROM firmware.
*/
PRINT1(("about to stop firmware\n"));
(void) ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_STOP_FIRMWARE,
0, 0, 0, 0, 0, 0, 0);
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Firmware stopped [%x, %x]\n",
mbox_sts[1], mbox_sts[2]));
#endif
}
}
#endif /* CHECK_HCCR_FIRST */
#ifdef LOAD_FIRMWARE
/*
** If we're going to load our own firmware, we need to
** stop any currently running firmware (if any).
*/
{
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Stopping firmware if running any...\n"));
PRINT2(("ql_init_isp: Error will occur if no firmware is running\n"));
#endif
#ifdef MAILBOX_TEST
PRINTD(("doing the mbox test: "));
if (ql_mbox_cmd(ha, mbox_sts, 8, 8,
MBOX_CMD_MAILBOX_REGISTER_TEST,
(ushort) 0x1111,
(ushort) 0x2222,
(ushort) 0x3333,
(ushort) 0x4444,
(ushort) 0x5555,
(ushort) 0x6666,
(ushort) 0x7777))
{
CMN_ERR((CE_WARN,
"ql_init_isp - MAILBOX REGISTER TEST Command Failed"));
return (1);
} else
{
/*
** Do they match?
*/
if ( mbox_sts[1] != (ushort)0x1111 ||
mbox_sts[2] != (ushort)0x2222 ||
mbox_sts[3] != (ushort)0x3333 ||
mbox_sts[4] != (ushort)0x4444 ||
mbox_sts[5] != (ushort)0x5555 ||
mbox_sts[6] != (ushort)0x6666 )
{
PRINTD(("qlIS_init_isp - mailbox compare error\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
return(1);
}
PRINTD(("qlIS_init_isp - mailbox compare test passed!!\n"));
}
#endif
/*
** Stop the current ISP firmware and begin execution
** in the internal ISP ROM firmware.
*/
/* (void)ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_STOP_FIRMWARE,
0,0,0,0,0,0,0);
*/
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Firmware stopped [%x, %x]\n",
mbox_sts[1], mbox_sts[2]));
#endif
}
#endif /* LOAD_FIRMWARE */
#endif /* SOFT_RESET */
/*
** At this point, the RISC is running, but not running any loaded firmware.
** We can now either load new firmware or restart the previously loaded
** firmware.
*/
#ifdef DUMP_REGS
/* dump_registers(isp); XXX */
#endif
/*
** Initialize the Control Parameter configuration.
*/
{
unsigned int Config_Reg;
/*
** Build the value for the ISP Configuration 1 Register.
*/
Config_Reg = ha->ql_defaults.Fifo_Threshold;
#ifdef ENABLE_BURST
/*
** always set burst enable
*/
Config_Reg |= CONF_1_BURST_ENABLE;
#endif /* ENABLE_BURST */
#ifdef QL_DEBUG
PRINT3(("ql_init_isp: Initializing the hardware registers ...\n"));
#endif
PCI_OUTH(&isp->hccr, HCCR_CMD_PAUSE); /* do I need to do this? */
PCI_OUTH(&isp->bus_config1, Config_Reg);
PRINT3(("set config for burst i think config=%x at %x\n", Config_Reg, &isp->bus_config1));
/* PCI_OUTH(&isp->bus_icr, 0); XXX */
PCI_OUTH(&isp->bus_sema, 0);
PCI_OUTH(&isp->hccr, HCCR_CMD_RELEASE); /* and this? */
}
#ifdef LOAD_FIRMWARE
/*
** Okay, we're ready to load our own firmware.
** We have two ways of doing this: DMA or word by word.
** I've left both version in because it may help to bring
** up a a new system.
*/
{
#ifdef QL_DEBUG
PRINTD(("ql_init_isp: Loading firmware ...\n"));
#endif
#ifdef LOAD_VIA_DMA
{
/*XXX for emulation - load the code from the prom into physical memory */
ushort *firmware, *prom_firmware;
/*REFERENCED*/
ushort *tmp;
ushort count;
#ifdef SWAP_DATA_STREAM
u_char *charp;
ushort holder;
#endif
#ifdef SWAP_DPROM
static int swapped = 0;
ushort temp;
PRINT1(("before swap %x \n", *((int *) risc_code01)));
firmware = (ushort *) risc_code01;
tmp = firmware;
tmp++;
PRINT1(("prom_array = %x sizeof(risc code) %x\n",
firmware, sizeof(risc_code01)));
for (count = 0; count < sizeof(risc_code01) / 2; count++)
{
if (swapped)
continue;
temp = *firmware;
*firmware = *tmp;
*tmp = temp;
firmware++;
firmware++;
tmp++;
tmp++;
}
swapped = 1;
tmp = risc_code01;
PRINT1(("after swap %x \n", *((int *) risc_code01)));
firmware = risc_code01;
#else /* copy it locally and then swap it */
prom_firmware = (ushort *) risc_code01;
firmware = (ushort *) dmabuf_malloc(4 + sizeof(risc_code01));
tmp = firmware;
PRINT1(("risc_code %x firmware %x tmp %x prom_f %x count %x\n",
risc_code01, firmware, tmp, prom_firmware,
risc_code_length01 / sizeof(ushort)));
#ifndef SWAP_DATA_STREAM
for (count = 0; count < 2 + risc_code_length01 / sizeof(ushort); count++)
{
*(tmp + 1) = *(prom_firmware);
*(tmp) = *(prom_firmware + 1);
tmp++;
tmp++;
prom_firmware++;
prom_firmware++;
}
#else
charp = (u_char *) firmware;
for (count = 0; count < 2 + risc_code_length01; count++)
{
holder = *(prom_firmware++);
*charp++ = (holder & 0xff);
*charp++ = ((holder >> 8) & 0xff);
}
#endif /* SWAP_DATA_STREAM */
tmp = firmware;
PRINT1(("\n"));
for (count = 0; count < 24; count++)
PRINT1(("%x ", *((ushort *) tmp++)));
PRINT1(("\ncopy complete, first word dma copy %x prom copy %x\n",
*(int *) firmware, *(int *) risc_code01));
tmp = firmware;
#endif
MR_DCACHE_WB_INVAL(firmware, 4 + sizeof(risc_code01));
/*
** Load my version of the executable firmware by DMA.
*/
p_ptr = MAKE_DIRECT_MAPPED_2GIG(kvtophys(firmware));
#if 0
#ifdef SN0_PCI_64
p_ptr = get_pci64_dma_addr(firmware,
ha_sn0_info[ha->controller_number]);
#endif
#endif
PRINTD(("loading via DMA at %x\n", p_ptr));
#ifdef A64_BIT_OPERATION1
if (ql_mbox_cmd(ha, mbox_sts, 5, 5,
MBOX_CMD_LOAD_RAM_64,
(short) risc_code_addr01,
(short) ((u_long) p_ptr >> 16),
(short) ((u_long) p_ptr & 0xFFFF),
risc_code_length01,
0,
(short) ((u_long) p_ptr >> 48),
(short) ((u_long) p_ptr >> 32)))
#else
if (ql_mbox_cmd(ha, mbox_sts, 5, 5,
MBOX_CMD_LOAD_RAM,
(short) risc_code_addr01,
(short) ((u_int) p_ptr >> 16),
(short) ((u_int) p_ptr & 0xFFFF),
risc_code_length01,
0, 0, 0))
#endif /* A64_BIT_OPERATION */
{
CMN_ERR((CE_WARN,
"ql_init_isp - LOAD RAM Command Failed"));
debug("mailbox command for dma load");
return (1);
} else
PRINT1(("DMA of code complete\n"));
PRINT1(("after dma\n"));
free(firmware);
}
/* go get the first 10 and print them out */
if(ql_debug) for (count = 0; count < 10; count++)
{
if (ql_mbox_cmd(ha, mbox_sts, 2, 3,
MBOX_CMD_READ_RAM_WORD,
(u_short) (risc_code_addr01 + count), /* risc addr */
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - READ RAM WORD Command Failed [%x]",
risc_code_addr01 + count));
break;
} else
PRINTD(("%x:%x ", count, mbox_sts[2]));
}
PRINTD(("\n"));
#else /* LOAD_VIA_DMA */
/*
** Load my version of the executable firmware one word at a time.
*/
w_ptr = &risc_code01[0];
checksum = 0;
PRINT3(("load one word at a time\n"));
for (count = 0; count < risc_code_length01; count++)
{
/*XXX for emulation */
#ifdef QL_DEBUG
if (!(count % 0x100))
PRINT1(("%x ", count));
/* PRINTD(("%x\r", count)); */
#endif
if (ql_mbox_cmd(ha, mbox_sts, 3, 3,
MBOX_CMD_WRITE_RAM_WORD,
(u_short) (risc_code_addr01 + count), /* risc addr */
*w_ptr, /* risc data */
0, 0, 0, 0, 0))
{
printf("ql_init_isp - WRITE RAM WORD Command Failed [%x]:%x",
risc_code_addr01 + count, *w_ptr);
return (1);
}
/*
** Total up the checksum as we go.
*/
checksum += *w_ptr++;
}
#endif /* LOAD_VIA_DMA */
#ifdef QL_DEBUG
PRINTD(("\nql_init_isp: Firmware loaded ...\n"));
PRINTD(("ql_init_isp: Starting firmware ...\n"));
#endif
/* dump_registers(isp); */
/*
** Start ISP firmware.
*/
if (ql_mbox_cmd(ha, mbox_sts, 2, 1,
MBOX_CMD_EXECUTE_FIRMWARE,
risc_code_addr01,
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - EXECUTE FIRMWARE Command Failed"));
return (1);
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 3,
MBOX_CMD_ABOUT_FIRMWARE,
0, 0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - EXECUTE FIRMWARE Command Failed"));
}
PRINTD(("\nql_init_isp: Firmware running : version %d.%d\n",
mbox_sts[1], mbox_sts[2]));
}
#endif /* LOAD_FIRMWARE */
#if 0
{
ushort *firmware, *tmp;
paddr_t p_ptr;
int count;
firmware = (ushort *) dmabuf_malloc(4 + sizeof(risc_code01));
DCACHE_INVAL(firmware, 4 + sizeof(risc_code01));
p_ptr = MAKE_DIRECT_MAPPED_2GIG(kvtophys(firmware));
#ifdef SN0_PCI_64
p_ptr = get_pci64_dma_addr(firmware,ha_info[ha->controller_number]);
#endif
PRINTD(("getting firmware via DMA at %x\n", p_ptr));
#ifdef A64_BIT_OPERATION
if (ql_mbox_cmd(ha, mbox_sts, 5, 5,
MBOX_CMD_DUMP_RAM,
(short) risc_code_addr01,
(short) ((u_long) p_ptr >> 16),
(short) ((u_long) p_ptr & 0xFFFF),
risc_code_length01,
0,
(short) ((u_long) p_ptr >> 48),
(short) ((u_long) p_ptr >> 32)))
#else
if (ql_mbox_cmd(ha, mbox_sts, 5, 5,
MBOX_CMD_DUMP_RAM,
(short) risc_code_addr01,
(short) ((u_int) p_ptr >> 16),
(short) ((u_int) p_ptr & 0xFFFF),
risc_code_length01,
0, 0, 0))
#endif /* A64_BIT_OPERATION */
{
CMN_ERR((CE_WARN,
"ql_init_isp - DUMP RAM Command Failed"));
return (1);
} else
PRINT1(("DMA DUMP of code complete\n"));
tmp = firmware;
for (count = 0; count < 2 + sizeof(risc_code01) / sizeof(short); count++)
PRINT1(("%x ", *((ushort *) tmp++)));
}
#endif
#ifdef MAILBOX_TEST
/*
** This is a test to be used to bring up a new implementation.
** It determines if we can play with the mailbox registers.
**
** Can we write something into the mailbox registers and
** get the (now running) firmware to copyit back?
*/
{
if (ql_mbox_cmd(ha, mbox_sts, 8, 8,
MBOX_CMD_MAILBOX_REGISTER_TEST,
(ushort) 0x1111,
(ushort) 0x2222,
(ushort) 0x3333,
(ushort) 0x4444,
(ushort) 0x5555,
(ushort) 0x6666,
(ushort) 0x0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - MAILBOX REGISTER TEST Command Failed"));
return (1);
} else
{
/*
** Do they match?
*/
if (mbox_sts[1] != (ushort) 0x1111 ||
mbox_sts[2] != (ushort) 0x2222 ||
mbox_sts[3] != (ushort) 0x3333 ||
mbox_sts[4] != (ushort) 0x4444 ||
mbox_sts[5] != (ushort) 0x5555 ||
mbox_sts[6] != (ushort) 0x6666 )
{
printf("ql_init_isp - mailbox compare error\n");
printf(" %x, %x, %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5],
mbox_sts[6]);
return(1);
}
PRINTD(("ql_init_isp - mailbox compare test passed!!\n"));
}
}
#endif /* MAILBOX_TEST */
#ifdef CHECKSUM_TEST
/* calculate the checksum and compare it with what the controller */
/* thinks its calculated checksum is. */
/* if it is not the same then check the firmware word by word until */
/* a mismatch is found */
/* */
/* I suppose that it is conceivable that a word by word check could */
/* succeed and the MBOX_CMD_VERIFY_CHECKSUM command could fail */
/* in this case believe the word by word check and continue without */
/* failing the controller setup */
{
w_ptr = &risc_code01[0];
checksum = 0;
count = 0;
for (count = 0; count < risc_code_length01; count++)
{
checksum += *w_ptr++;
}
/*
** Get the current checksum value.
*/
if (ql_mbox_cmd(ha, mbox_sts, 2, 3,
MBOX_CMD_VERIFY_CHECKSUM,
0x1000, /* start at location 0 */
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - VERIFY CHECKSUM Command Failed"));
return(1);
}
if( mbox_sts[2] != checksum)
{
printf("ISP Firmware Checksum mismatch: theirs %x, mine = %x\n",
mbox_sts[2], checksum);
do_verify = 1;
}
else
{
PRINTD(("PRINTD checksum is equal theirs %x, mine = %x\n",
mbox_sts[2], checksum));
}
}
#endif /* CHECKSUM_TEST */
#if defined(VERIFY_TEST) || defined(CHECKSUM_TEST)
/*
** This test can used to determine if the firmware loaded
** matches what we tried to load.
** This would be used to bring up a new implementation.
*/
#ifndef VERIFY_TEST
if(do_verify)
#endif
{
/*
** Dump the executable firmware.
*/
w_ptr = &risc_code01[0];
count = 0;
PRINTD(("VERIFY THE FIRWARE WITH LOCAL COPY\n"));
for (count = 0; count < risc_code_length01; count++)
{
if (ql_mbox_cmd(ha, mbox_sts, 2, 3,
MBOX_CMD_READ_RAM_WORD,
(u_short) (risc_code_addr01 + count), /* risc addr */
0, 0, 0, 0, 0, 0))
{
#ifdef QL_DEBUG
CMN_ERR((CE_WARN,
"ql_init_isp - READ RAM WORD Command Failed [%x]",
risc_code_addr01 + count));
#endif
break;
}
if (mbox_sts[2] != *w_ptr++) {
printf("Firmware Data Mismatch [0x%x]: his = %x, mine = %x\n", count, mbox_sts[2], *w_ptr);
return(1);
}
}
}
#endif /* VERIFY_TEST */
/*
** At this point, the firmware is up and running and we're ready to
** initialze for normal startup. If you are bringing up the ISP
** device for the first time, leave normal_startup undefined until
** you have worked out the bugs of getting the firmware loaded and
** running.
*/
#ifdef NORMAL_STARTUP
/*
** Set Bus Control Parameters.
*/
if (ql_mbox_cmd(ha,
mbox_sts, 3, 3,
MBOX_CMD_SET_BUS_CONTROL_PARAMETERS,
(u_short) (ha->ql_defaults.Capability.bits.Data_DMA_Burst_Enable << 1),
(u_short) (ha->ql_defaults.Capability.bits.Command_DMA_Burst_Enable << 1),
0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - SET BUS CONTROL PARAMETERS Command Failed"));
return (1);
}
/* Set the Clock Rate for the board */
{
u_short clock_rate;
clock_rate = FAST20_CLOCK_RATE;
PRINTD(("ql_init_isp - Informing board of %d Mhz CLOCK RATE \n",
clock_rate));
if (ql_mbox_cmd(ha,
mbox_sts, 2, 2,
MBOX_CMD_SET_CLOCK_RATE,
clock_rate,
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - SET CLOCK RATE Command Failed"));
return (1);
}
}
/*
** Initialize response queue indexes.
*/
#ifdef USE_MAPPED_CONTROL_STREAM
#define BUFFER_MALLOC(size) align_malloc(size,MIN(size,1024*16))
#else
#define BUFFER_MALLOC(size) dmabuf_malloc(size)
#endif
ha->response_base = (queue_entry *)
BUFFER_MALLOC(sizeof(queue_entry) * RESPONSE_QUEUE_DEPTH);
if (ha->response_base == NULL)
{
cmn_err(CE_WARN, "could not malloc space for response queue\n");
return (1); /* FAIL */
}
PRINT1(("response_base is %x\n", ha->response_base));
ha->response_ptr = ha->response_base;
PRINTD(("response queue base %x phys is %x\n", ha->response_base,
kvtophys(ha->response_base))); /* XXX */
ha->response_out = 0;
#ifdef USE_MAPPED_CONTROL_STREAM
/* XXX hack for emulation */
/* this is for programming the bridge to put the control stream in mapped
** space
** step 1: make p_ptr an address that will access mapped area
** step 2: make the Address Translation Entry (ATE) and put it in first entry
** note: this is a hack for now because no routines are written yet.
** this is designed to work on the emulator (one ql card)
*/
/* put the ATE entry number ored in with the start of mapped space */
#ifdef IP30 /* XXX this is only for emulation and should
* be taken out */
/* XXX as soon at the PCI infratructure stuff goes in */
#define MAKE_PMU_ADDRESS(entry_number) ((entry_number<<14) | 0x40000000)
#define MAKE_ATE(mode,pfn) ((mode)|(pfn<<12)|(0x8<<8))
#endif /* IP30 */
#ifndef SN0
p_ptr = MAKE_PMU_ADDRESS(0x0);
PRINT1(("response base for controller is %x\n", p_ptr));
PRINT1(("setting up ATE for response buffer\n"));
{
volatile bridge_ate_t *wrp = &bridge->b_int_ate_ram[0].wr;
#if 0
volatile bridgereg_t *lop = &bridge->b_int_ate_ram_lo[0].rd;
volatile bridgereg_t *hip = &bridge->b_int_ate_ram[0].hi.rd;
PRINT1(("Value (at 0x%x and 0x%x) before writing:", hip, lop));
PRINT1((" hi=0x%x lo=0x%x\n", *hip, *lop));
PRINT1(("about to write ATE at 0x%x\n", wrp));
#endif
*wrp = MAKE_ATE(ATE_V | ATE_CO, kvtophys(ha->response_base) >> 12);
#if 0
PRINT1(("Wrote ATE at 0x%x; now reading back from 0x%x and 0x%x:", wrp, hip, lop));
PRINT1((" hi=0x%x lo=0x%x\n", *hip, *lop));
#endif
}
*/
#else
p_ptr = MAKE_DIRECT_MAPPED_2GIG(kvtophys(ha->response_ptr));
#endif /* SN0 */
#else
p_ptr = MAKE_DIRECT_MAPPED_2GIG(kvtophys(ha->response_ptr));
#ifdef SN0_PCI_64
p_ptr = (paddr_t) get_pci64_dma_addr((__psunsigned_t) ha->response_ptr,
ha_sn0_info[ha->controller_number]);
#endif
PRINTD(("response q base : %x\n", p_ptr));
#endif /* IP30 */
PRINTD(("response queue base %x, kvtophys %x\n", ha->response_base, p_ptr));
bzero(ha->response_base, RESPONSE_QUEUE_DEPTH * sizeof(queue_entry));
MRW_DCACHE_WB_INVAL(ha->response_base,
RESPONSE_QUEUE_DEPTH * sizeof(queue_entry));
/*
** Initialize response queue.
*/
#ifdef A64_BIT_OPERATION
if (ql_mbox_cmd(ha, mbox_sts, 8, 8,
MBOX_CMD_INIT_RESPONSE_QUEUE_64,
RESPONSE_QUEUE_DEPTH,
(short) ((u_long) p_ptr >> 16),
(short) ((u_long) p_ptr & 0xFFFF),
0,
ha->response_out,
(short) ((u_long) p_ptr >> 48),
(short) ((u_long) p_ptr >> 32)))
#else
if (ql_mbox_cmd(ha, mbox_sts, 6, 6,
MBOX_CMD_INIT_RESPONSE_QUEUE,
RESPONSE_QUEUE_DEPTH,
(short) ((u_int) p_ptr >> 16),
(short) ((u_int) p_ptr & 0xFFFF),
0,
ha->response_out, 0, 0))
#endif /* A64_BIT_OPERATION */
{
CMN_ERR((CE_WARN,
"ql_init_isp - INIT RESPONSE QUEUE Command Failed"));
return (1);
}
#ifdef QL_DEBUG
PRINTD(("ql_init_isp - INIT RESPONSE QUEUE results\n"));
PRINTD((" %x, %x, %x, %x, %x, %x %x %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3],
mbox_sts[4], mbox_sts[5], mbox_sts[6], mbox_sts[7])); /* XXX */
#endif
/*
** Initialize request queue indexes.
*/
ha->queue_space = 0;
ha->request_in = 0;
ha->request_out = 0;
ha->request_base = (queue_entry *)
BUFFER_MALLOC(sizeof(queue_entry) * REQUEST_QUEUE_DEPTH);
if (ha->request_base == NULL)
{
cmn_err(CE_WARN, "could not malloc space for request queue\n");
return (1); /* FAIL */
}
PRINT1(("request_base %x\n", ha->request_base));
ha->request_ptr = ha->request_base;
PRINT1(("request queue base %x phys is %x\n", ha->request_base,
kvtophys(ha->request_base))); /* XXX */
ha->request_out = 0;
#if defined(USE_MAPPED_CONTROL_STREAM) && defined(IP30)
/* XXX hack for emulation */
/* this is for programming the bridge to put the control stream in mapped
** space
** step 1: make p_ptr an address that will access mapped area
** step 2: make the Address Translation Entry (ATE) and put it in first entry
** note: this is a hack for now because no routines are written yet.
** this is designed to work on the emulator (one ql card)
*/
/* put the ATE entry number ored in with the start of mapped space */
p_ptr = MAKE_PMU_ADDRESS(0x1);
PRINT1(("request base for controller is %x\n", p_ptr));
PRINT1(("setting up ATE for request buffer\n"));
{
volatile bridge_ate_t *wrp = &bridge->b_int_ate_ram[1].wr;
#if 0
volatile bridgereg_t *lop = &bridge->b_int_ate_ram_lo[1].rd;
volatile bridgereg_t *hip = &bridge->b_int_ate_ram[1].hi.rd;
PRINT1(("Value (at 0x%x and 0x%x) before writing:\n", hip, lop));
PRINT1(("\thi=0x%x lo=0x%x\n", *hip, *lop));
PRINT1(("about to write ATE at 0x%x\n", wrp));
#endif
*wrp = MAKE_ATE(ATE_V | ATE_CO, kvtophys(ha->request_base) >> 12);;
#if 0
PRINT1(("Wrote ATE at 0x%x; now reading back from 0x%x and 0x%x:\n", wrp, hip, lop));
PRINT1(("\thi=0x%x lo=0x%x\n", *hip, *lop));
#endif
}
#else
p_ptr = MAKE_DIRECT_MAPPED_2GIG(kvtophys(ha->request_base));
#ifdef SN0_PCI_64
p_ptr = (paddr_t) get_pci64_dma_addr((__psunsigned_t) ha->request_base,
ha_sn0_info[ha->controller_number]);
#endif
PRINTD(("mapped request q base : %x\n", p_ptr));
#endif /* IP30 */
PRINT3(("request queue base %x, kvtophys %x\n", ha->request_base, p_ptr));
bzero(ha->request_base, REQUEST_QUEUE_DEPTH * sizeof(queue_entry));
/*
** Initialize request queue.
*/
#ifdef A64_BIT_OPERATION
if (ql_mbox_cmd(ha, mbox_sts, 8, 8,
MBOX_CMD_INIT_REQUEST_QUEUE_64,
REQUEST_QUEUE_DEPTH,
(short) ((u_int) p_ptr >> 16),
(short) ((u_int) p_ptr & 0xFFFF),
0,
ha->request_in,
(short) ((u_long) p_ptr >> 48),
(short) ((u_long) p_ptr >> 32)))
#else
if (ql_mbox_cmd(ha, mbox_sts, 5, 6,
MBOX_CMD_INIT_REQUEST_QUEUE,
REQUEST_QUEUE_DEPTH,
(short) ((u_long) p_ptr >> 16),
(short) ((u_long) p_ptr & 0xFFFF),
ha->request_in,
0,
(short) ((u_long) p_ptr >> 48),
(short) ((u_long) p_ptr >> 32)))
#endif /* A64_BIT_OPERATION */
{
CMN_ERR((CE_WARN,
"ql_init_isp - INIT REQUEST QUEUE Command Failed"));
return (1);
}
#ifdef QL_DEBUG
PRINTD(("ql_init_isp - INIT REQUEST QUEUE results\n"));
PRINTD((" %x, %x, %x, %x, %x, %x %x %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3],
mbox_sts[4], mbox_sts[5], mbox_sts[6], mbox_sts[7])); /* XXX */
#endif
/*
** Set Initiator SCSI ID.
*/
if (ql_mbox_cmd(ha, mbox_sts, 2, 2,
MBOX_CMD_SET_INITIATOR_SCSI_ID,
(u_short) ha->ql_defaults.Initiator_SCSI_Id,
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - SET INITIATOR SCSI ID Command Failed"));
return (1);
}
/*
** Set Selection Timeout.
*/
if (ql_mbox_cmd(ha, mbox_sts, 2, 2,
MBOX_CMD_SET_SELECTION_TIMEOUT,
(u_short) ha->ql_defaults.Selection_Timeout,
0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_init_isp - SET_SELECTION_TIMEOUT Command Failed");
} else
{
PRINTD(("scsi_seltimeout: selection timeout adapter %d is now %dmS, was %dmS\n", adap, timeout, mbox_sts[1]));
}
/*
** Set RETRY limits.
*/
if (ql_mbox_cmd(ha, mbox_sts, 3, 3,
MBOX_CMD_SET_RETRY_COUNT,
ha->ql_defaults.Retry_Count,
ha->ql_defaults.Retry_Delay,
0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - SET RETRY COUNT Command Failed"));
return (1);
}
#ifdef TAGGED_CMDS
/*
** Set Tag Age Limits.
*/
if (ql_mbox_cmd(ha, mbox_sts, 2, 2,
MBOX_CMD_SET_TAG_AGE_LIMIT,
ha->ql_defaults.Tag_Age_Limit,
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - SET TAG AGE LIMIT Command Failed"));
return (1);
}
#endif /* TAGGED_CMDS */
/*
** Set Active Negation.
*/
if (ql_mbox_cmd(ha,
mbox_sts, 2, 2,
MBOX_CMD_SET_ACTIVE_NEGATION_STATE,
(u_short) ((ha->ql_defaults.Capability.bits.REQ_ACK_Active_Negation << 5)
| (ha->ql_defaults.Capability.bits.DATA_Line_Active_Negation << 4)),
0, 0, 0, 0, 0, 0))
{
#ifdef QL_DEBUG
CMN_ERR((CE_WARN,
"ql_init_isp - SET ACTIVE NEGATION STATE Command Failed"));
#endif
return (1);
}
/*
** Set Asynchronous Data Setup Time.
*/
if (ql_mbox_cmd(ha,
mbox_sts, 2, 2,
MBOX_CMD_SET_ASYNC_DATA_SETUP_TIME,
(u_short) ha->ql_defaults.Capability.bits.ASync_Data_Setup_Time,
0, 0, 0, 0, 0, 0))
{
CMN_ERR((CE_WARN,
"ql_init_isp - SET ASYNC DATA SETUP TIME Command Failed"));
return (1);
}
#else /* NORMAL_STARTUP */
/*
** Indicate that we did NOT perform a full initialization.
*/
return (1);
#endif /* NORMAL_STARTUP */
#ifdef DUMP_REGS
/* dump_registers(isp); XXX */
#endif
/*
** Indicate we successfully initialized this ISP!
*/
PRINTD(("Out of ql_init_isp\n"));
return (0);
}
#if defined (SN0) && defined (SCSI_TIMEOUT_WAR)
int
ql_to_war_reset(void)
{
char times;
int r;
char ll ;
times = get_scsi_war_value();
if (times > SCSI_TO_MAX_RETRY)
times = 0;
if (times == SCSI_TO_MAX_RETRY)
return -1;
if (times)
printf("\nSCSI adaptor timeout: resetting machine (attempt %d).\n",
times);
else
printf("\nSCSI adaptor timeout: resetting machine.\n");
times++;
set_scsi_war_value(times);
/*
* Bug PV 464448
* We need to remember additional state - the value
* of the reboot variable. The value of this register
* is clobbered when we do a hard reset from here.
*/
ll = diag_get_reboot() ;
set_scsi_to_ll_value(ll);
if (elsc_system_reset(get_elsc())) {
printf("SCSI adaptor timeout: Unable to reset system\n");
return -1;
}
return 0;
}
void
ql_to_war_fine(void)
{
char times;
times = get_scsi_war_value();
if (times) {
set_scsi_war_value(0);
}
}
#endif /* defined (SN0) && defined (SCSI_TIMEOUT_WAR) */
/* Entry-Point
** Function: ql_entry
**
** Description: Execute a SCSI request.
**
** Returns: 0 = success (i.e. I understood the request)
** -1 = failure (what the heck was that?)
*/
int
ql_entry(scsi_request_t * request)
{
int i = 0;
unsigned long tm0, tm1, timeout;
u_short id = request->sr_target;
u_short lun = request->sr_lun;
pHA_INFO ha = ha_info[request->sr_ctlr];
PRINT3(("sr_ctlr %x\n", request->sr_ctlr));
PRINT2(("ha_info[0] %x ha_info[1] %x\n", ha_info[0], ha_info[1]));
#ifdef QL_DEBUG
PRINT2(("ql_entry: request=0x%x, id=%x,lun=%x, cmd=%x\n",
request, id, lun, *request->sr_command));
#endif
/*
** Before processing the request, assume bad status and change when we know
** the command was successfully completed
*/
request->sr_status = SC_HARDERR;
request->sr_scsi_status = 0;
/*
** Error if the adapter specified is invalid.
*/
if (ha == NULL)
{
#ifdef QL_DEBUG
cmn_err(CE_WARN, "ql_entry: Invalid device specified");
#endif
/*
** Tell them that they specified an illegal host adapter.
*/
request->sr_status = SC_REQUEST;
/*
** call back
*/
request->sr_notify(request);
return (0);
}
if(ha->flags & AFLG_SENSE_HELD) {
ha->flags &= ~AFLG_SENSE_HELD; /* reset the flag */
if (request->sr_command[0] == 0x03) {
PRINTD(("did get sense command\n"));
bcopy(last_sense, request->sr_buffer,REQ_SENSE_DATA_LENGTH);
request->sr_status=SC_GOOD;
request->sr_scsi_status=ST_GOOD;
return(0);
}
else {
PRINTD(("did not get sense command after autosense\n"));
}
}
/*
** Check that the device has been or is being initialized.
*/
if (!initialized(ha,id,lun))
{
#ifdef QL_DEBUG
PRINT3(("ql_entry: Uninitialized device - initializing adapter %x, target %x\n", request->sr_ctlr, id));
#endif
#ifdef QL_DEBUG
PRINT3(("ql_entry: SCSI_INIT ha=%d, id=%d, lun=%d\n",
request->sr_ctlr, id, lun)); /* XXX */
#endif
/*
** If the device hasn't been initialized, do it now!
*/
if (!initialized(ha,id,lun))
{
u_short mbox_sts[8];
/*
** Set Device Queue Parameters.
*/
if (ql_mbox_cmd(ha, mbox_sts, 4, 1,
MBOX_CMD_SET_DEVICE_QUEUE_PARAMETERS,
(u_short) ((id << 8) + lun),
ha->ql_defaults.Max_Queue_Depth,
ha->ql_defaults.Id[id].Throttle,
0, 0, 0, 0))
{
#ifdef QL_DEBUG
cmn_err(CE_WARN,
"ql_entry - SET DEVICE QUEUE PARAMETERS Command Failed");
#endif
}
/*
** Set Target Parameters.
*/
PRINT5(("MBOX_CMD_SET_TARGET_PARAMETERS 0:%x 1:%x 2:%x 3:%x\n", MBOX_CMD_SET_TARGET_PARAMETERS, (u_short) (id
<< 8), (u_short) (ha->ql_defaults.Id[id].Capability.byte << 8), (u_short) ((ha->ql_defaults.Id[id].Sync_Offset
<< 8) |
(ha->ql_defaults.Id[id].Sync_Period))));
if (ql_mbox_cmd(
ha, mbox_sts, 4, 4,
MBOX_CMD_SET_TARGET_PARAMETERS,
(u_short) (id << 8),
(u_short) (ha->ql_defaults.Id[id].Capability.byte << 8),
(u_short) ((ha->ql_defaults.Id[id].Sync_Offset << 8) |
(ha->ql_defaults.Id[id].Sync_Period)),
0, 0, 0, 0))
{
#ifdef QL_DEBUG
cmn_err(CE_WARN,
"ql_entry - SET TARGET PARAMETERS Command Failed");
#endif
}
/*
** Device has now been initialized!
*/
ha->dev_init[id] |= (1<<lun);
}
}
/*
** Go handle the request.
*/
/*
** Queue a SCSI request.
** A scatter/gather request is processed the same here
** but different when it is actually issued to the adapter.
*/
/*
** Indicate that we're waiting for a fake interrupt.
*/
ha->flags |= AFLG_POLL_ISR;
/*
** Issue the new command.
*/
#ifdef QL_DEBUG1
PRINTD(("queue commands at 0x%x\n", request));
#endif
ql_queue_cmd(ha, request);
/*
** If we're polling, then wait for it to complete.
*/
timeout = request->sr_timeout / HZ + 1;
#ifdef HEART1_TIMEOUT_WAR
/* Try to avoid heart PIO timeout bug when reading the clock */
if (heart_rev() == HEART_REV_A) {
tm0 = timeout * 1000;
tm1 = 0;
}
else
#endif
tm0 = get_tod();
do
{
ql_poll();
#ifdef HEART1_TIMEOUT_WAR
if (heart_rev() == HEART_REV_A) {
us_delay(1000);
tm0--;
if (tm0 <= 0)
break;
}
else
#endif
{
tm1 = get_tod();
if (tm1 - tm0 >= timeout) {
break;
}
}
us_delay(500) ; /* DELAY for .5 millisecond */
} while (ha->flags & AFLG_POLL_ISR);
#if 0
/*
* This section of code was used to debug 464448.
* It will be around till we are confident that
* the problem is fixed.
* If you define a nvram variable 'checkto', and
* set it to 1 in the ioprom, and boot unix from the net, it
* will 'create' a scsi to war reset when a reboot
* is done. This happens only once.
*/
{
char *y ;
if (y = getenv("checkto")) {
if (*y != '0') {
ha->flags |= AFLG_POLL_ISR ;
first_adaptor = 1 ;
}
setenv("checkto", "0") ;
}
}
#endif
if (ha->flags & AFLG_POLL_ISR)
{
#if defined (SN0) && defined (SCSI_TIMEOUT_WAR)
if (first_adaptor) ql_to_war_reset();
#endif /* defined (SN0) && defined (SCSI_TIMEOUT_WAR) */
printf("timeout on adapter %x target %x\n", request->sr_ctlr, request->sr_target);
printf(" tm0=0x%x, tm1=0x%x, timeout=0x%x\n", tm0, tm1, timeout);
request->sr_status = SC_HARDERR;
/*dump_registers((pISP_REGS) ha->ha_base);*/
}
#if defined (SN0) && defined (SCSI_TIMEOUT_WAR)
else {
first_adaptor = 0;
ql_to_war_fine();
}
#endif /* defined (SN0) && defined (SCSI_TIMEOUT_WAR) */
/*
** Return a status to indicate we understood the request.
*/
return (0);
}
/* Entry-Point
** Function: qlintr
**
** Description: This is the interrupt service routine. This is called
** by the system to process a hardware interrupt for our
** board, or by one of our internal routines that need to
** check for an interrupt by polling. These routines MUST
** lock out interrupts (splx) before calling here.
*/
void
qlintr(pHA_INFO ha)
{
PRINT3(("qlintr: pHA_INFO %x\n", ha));
{
pISP_REGS isp = (pISP_REGS) ha->ha_base;
u_short isr = 0;
/*
** Determine if this board caused the (an) interrupt.
*/
isr = PCI_INH(&isp->bus_isr);
if (isr & BUS_ISR_RISC_INT)
{
/*
** We have the interrupter...try to service the request.
*/
PRINT3(("service int with ha at %x \n", ha));
ql_service_interrupt(ha);
}
}
}
/*
** Function: ql_poll
**
** Description: This is an alternative version of the interrupt service
** routine. It is for debug only in that its static so
** we don't have to worry about anyone but us calling it.
** It helps to bring up new implementations (Unixes) to
** not worry about interrupts while trying to get a command
** executed. This is nearly identical to the real interrupt
** service routine -- it just makes it easier to "play" with
** the code here. Understand?
*/
static void
ql_poll(void)
{
int interrupt_seen;
#if 0
printf("enter ql_poll loop. isp=0x%x\n", (pISP_REGS) ha_list->ha_base);
{
pISP_REGS isp = (pISP_REGS) ha_list->ha_base;
printf("isr=0x%x\n", PCI_INH(&isp->bus_isr));
}
#endif
PRINT5(("+"));
do
{
pHA_INFO ha = ha_list;
/*
** Assume that we won't find an interrupt.
*/
interrupt_seen = 0;
/*
** Always try all boards to locate the interrupt.
*/
while (ha != 0)
{
pISP_REGS isp = (pISP_REGS) ha->ha_base;
u_short isr = 0;
/*
** Determine if this board caused the interrupt.
*/
isr = PCI_INH(&isp->bus_isr);
if (isr & (BUS_ISR_RISC_INT))
{
/*
** We have the interrupter...try to service the request.
*/
PRINT3(("service from ql \n"));
ql_service_interrupt(ha);
/*
** If we saw one interrupt, maybe theres another.
*/
interrupt_seen = 1;
}
/*
** Try the next board.
*/
ha = ha->next_ha;
}
} while (interrupt_seen);
}
/*
** Function: ql_service_interrupt
**
** Description: This routine actually services the interrupt.
** There are two kinds of interrupts: 1) Responses available
** and 2) a Mailbox request completed. ALWAYS service the
** Mailbox interrupts first. Then process all responses we
** have (may be more than one). Do this by pulling off all
** the completed responses and putting them in a linked list.
** Then after we have them all, tell the adapter we have them
** and do completion processing on the list. This limits
** the amount of time the adapter has to wait to post new
** responses.
*/
static void
ql_service_interrupt(pHA_INFO ha)
{
status_entry *q_ptr;
status_entry tmp_status_entry;
scsi_request_t *request;
pREQ_INFO r_ptr;
pISP_REGS isp = (pISP_REGS) ha->ha_base;
scsi_request_t *cmp_forw = NULL;
scsi_request_t *cmp_back = NULL;
int tgt,idx;
u_short isr = 0;
/*
** Process all the mailbox interrupts and all responses.
*/
do
{
u_short response_in = 0;
u_short bus_sema = 0;
u_short mailbox0 = 0;
PRINT5(("ql_service_interrupt: got one\n"));
/*
** Get the current "response in" pointer.
** I will use this after I check the mailbox.
*/
response_in = PCI_INH(&isp->mailbox5);
PRINT3(("ql_service_interrupt: response_in %x\n", response_in));
/*
** If we weren't issuing a MAILBOX command,
** then check for an asynchronous mailbox interrupt.
*/
#if 0
if (!(ha->flags & AFLG_MAIL_BOX_EXPECTED))
{
PRINT3(("not AFLG_MAIL_BOX_EXPECTED in interrupt service\n"));
#endif
/*
** Get a copy of the semaphore and mailbox 0 registers.
*/
bus_sema = PCI_INH(&isp->bus_sema);
mailbox0 = PCI_INH(&isp->mailbox0);
PRINT3(("bus_sema %x\t mailbox0 %x\n", bus_sema, mailbox0));
/*
** Check for async mailbox events.
*/
if (bus_sema & BUS_SEMA_LOCK)
{
if ((mailbox0 >= 0x4000) && (mailbox0 <= 0x7fff) ) {
} else {
/*
** Determine what mailbox request is here.
*/
switch (mailbox0)
{
/*
** A system error? WOW! What can I say.
*/
case MBOX_ASTS_SYSTEM_ERROR:
{
#ifdef QL_DEBUG
u_short mailbox1 = 0;
mailbox1 = PCI_INH(&isp->mailbox1);
cmn_err(CE_WARN,
"ql_isr - ISP Fatal Firmware Error: ");
cmn_err(CE_WARN,
"ql_isr - Mailbox 0 = 0x%x, Mailbox 1 = 0x%x",
mailbox0, mailbox1);
dump_registers(isp);
#endif
}
break;
/*
** Some joker reset the SCSI bus.
** We need to issue a Marker before the next
** request or the adapter will not execute it.
*/
case MBOX_ASTS_SCSI_BUS_RESET:
{
ha->flags |= AFLG_SEND_MARKER;
PRINTD(("SCSI bus was reset. Send marker before next command\n"));
}
break;
/*
** What the heck was that interrupt?
*/
default:
{
#ifdef QL_DEBUG
cmn_err(CE_WARN,
"ql_isr - Unexpected MailBox Response (mailbox0 = 0x%x)",
mailbox0);
#endif
}
} /* case */
} /* if */
/*
** Clear the semaphore lock.
*/
PCI_OUTH(&isp->bus_sema, 0);
PRINT3(("cleared the bus_sema\n"));
}
#if 0
/*
** Clear the RISC interrupt. I clear it here, but will check
** it again in case another response gets posted.
*/
PRINT3(("clear the risk interrupt %x %x\n",
PCI_INH(&isp->hccr), &isp->hccr));
PCI_OUTH(&isp->hccr, HCCR_CMD_CLEAR_RISC_INT);
#endif
/*
** If there aren't any completed responses then don't do any
** of the following. i.e. it was just a mailbox interrupt or spurious becuase right now I poll on mailbox commands.
*/
if (response_in == ha->response_out)
{
/*
** We can do one of three things here:
** continue - that will try for another interrupt
** jump out with a goto
** or break and perform completion processing.
** I left them all here because I'm not sure which I want yet.
*/
PRINTD(("spurious interrupt\n"));
/* continue; XXX */
/* goto no_responses; XXX */
/* break; */
PRINT3(("clear the risk interrupt %x %x\n",
PCI_INH(&isp->hccr), &isp->hccr));
PCI_OUTH(&isp->hccr, HCCR_CMD_CLEAR_RISC_INT);
return;
}
/*
** Remove all of the responses from the queue.
*/
while (ha->response_out != response_in)
{
/*
** Get a pointer to the next response entry.
*/
tmp_status_entry = *(status_entry *)ha->response_ptr;
q_ptr = &tmp_status_entry;
DCACHE_INVAL(
(void *)((__psunsigned_t)ha->response_ptr & CACHE_SLINE_MASK),
CACHE_SLINE_SIZE);
#if defined(IP30) && !defined(USE_MAPPED_CONTROL_STREAM)
PRINT3(("MUNGING IORB\n"));
munge((u_char *) q_ptr, 64);
#endif
#ifdef QL_DEBUG
if (ql_dump_control_block)
dump_iorb((status_entry *) q_ptr);
if (ql_debug >= 3)
{
dump_iorb((status_entry *) q_ptr);
}
#endif
/*
** Move the pointers for the next entry.
*/
if (ha->response_out == (RESPONSE_QUEUE_DEPTH - 1))
{
ha->response_out = 0;
ha->response_ptr = ha->response_base;
PRINT5(("wrapped the response pointer\n"));
} else
{
ha->response_out++;
ha->response_ptr++;
}
/* assert that the IORB that we are processing is really a request */
ASSERT((q_ptr->handle > 0) && (q_ptr->handle <= MAX_HANDLE_NO));
/* Determine which request it was. */
tgt = q_ptr->handle / (MAX_REQ_INFO +1);
idx = q_ptr->handle % (MAX_REQ_INFO +1) ;
r_ptr = &ha->reqi_block[tgt][idx];
PRINT3(("r_ptr %x\n", r_ptr));
/*
** Make sure the driver issued the command right.
*/
if (q_ptr->hdr.entry_type != ET_STATUS ||
q_ptr->hdr.flags & EF_ERROR_MASK)
{
printf("\nql_isr - Invalid driver command issued ");
printf("entry_cnt=0x%x, entry_type=0x%x\n",
q_ptr->hdr.entry_cnt, q_ptr->hdr.entry_type);
printf("flags=0x%x, sys_def_1=0x%x\n",
q_ptr->hdr.flags, q_ptr->hdr.sys_def_1);
PCI_OUTH(&isp->mailbox5, ha->response_out);
return;
}
request = r_ptr->req;
/*
** Put the request info structure back on the free list.
*/
r_ptr->req = NULL;
/* start out with good status */
/* it will be changed if required */
request->sr_status = SC_GOOD;
/* pass on scsi status to the dksc driver */
request->sr_scsi_status = q_ptr->scsi_status;
/* pass on the residual count */
request->sr_resid = q_ptr->residual;
PRINT3(("residual count %d\n", request->sr_resid));
PRINT3(("sr_buflen %d, buffer: 0x%lx\n", request->sr_buflen, request->sr_buffer));
#ifdef QL_DEBUG
/* what do we report to dksc under these conditions */
/*
case SCS_INCOMPLETE: SC_HARDERR
case SCS_DMA_ERROR: SC_HARDERR
case SCS_TRANSPORT_ERROR: SC_HARDERR
case SCS_RESET_OCCURRED: SC_ATTN
case SCS_ABORTED: SC_ATTN
case SCS_TIMEOUT: SC_CMDTIME
case SCS_DATA_OVERRUN: SC_HARDERR
case SCS_COMMAND_OVERRUN: SC_HARDERR
case SCS_STATUS_OVERRUN: SC_HARDERR
case SCS_BAD_MESSAGE: SC_HARDERR
case SCS_NO_MESSAGE_OUT: SC_HARDERR
case SCS_IDE_MSG_FAILED: SC_HARDERR
case SCS_ABORT_MSG_FAILED: SC_HARDERR
case SCS_REJECT_MSG_FAILED: SC_HARDERR
case SCS_NOP_MSG_FAILED: SC_HARDERR
case SCS_PARITY_ERROR_MSG_FAILED: SC_HARDERR
case SCS_ID_MSG_FAILED: SC_HARDERR
case SCS_UNEXPECTED_BUS_FREE: SC_HARDERR
case SCS_DATA_UNDERRUN: SC_HARDERR happens all the time with mode sense
for now don't report them
*/
/*
** Print out any status errors.
*/
/* if (q_ptr->completion_status) */
/* do this all the time for now */
#endif /* QL_DEBUG */
{
PRINT3(("ql_service_interrupt - completion status error [0x%x]\n",
q_ptr->completion_status));
PRINTD(("scsi status[%x], state flags[%x], status flags[%x], completion_status[%x]\n",
q_ptr->scsi_status,
q_ptr->state_flags,
q_ptr->status_flags,
q_ptr->completion_status));
switch (q_ptr->completion_status)
{
case SCS_COMPLETE:
{
PRINT1(("completion status: GOOD\n"));
break;
}
case SCS_INCOMPLETE:
{
PRINT1(("completion status: INCOMPLETE\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_DMA_ERROR:
{
PRINT1(("completion status: DMA_ERROR\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_TRANSPORT_ERROR:
{
PRINT1(("completion status: SCS_TRANSPORT_ERROR\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_RESET_OCCURRED:
{
PRINT1(("completion status: SCS_RESET_OCCURRED\n"));
request->sr_status = SC_ATTN;
break;
}
case SCS_ABORTED:
{
PRINT1(("completion status: SCS_ABORTED\n"));
request->sr_status = SC_ATTN;
break;
}
case SCS_TIMEOUT:
{
PRINT1(("completion status: SCS_TIMEOUT\n"));
request->sr_status = SC_CMDTIME;
break;
}
case SCS_DATA_OVERRUN:
{
PRINT1(("completion status: SCS_DATA_OVERRUN\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_COMMAND_OVERRUN:
{
PRINT1(("completion status: SCS_COMMAND_OVERRUN\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_STATUS_OVERRUN:
{
PRINT1(("completion status: SCS_STATUS_OVERRUN\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_BAD_MESSAGE:
{
PRINT1(("completion status: SCS_BAD_MESSAGE\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_NO_MESSAGE_OUT:
{
PRINT1(("completion status: SCS_NO_MESSAGE_OUT\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_EXT_ID_FAILED:
{
PRINT1(("completion status: SCS_EXT_ID_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_IDE_MSG_FAILED:
{
PRINT1(("completion status: SCS_IDE_MSG_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_ABORT_MSG_FAILED:
{
PRINT1(("completion status: SCS_ABORT_MSG_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_REJECT_MSG_FAILED:
{
PRINT1(("completion status: SCS_REJECT_MSG_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_NOP_MSG_FAILED:
{
PRINT1(("completion status: SCS_NOP_MSG_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_PARITY_ERROR_MSG_FAILED:
{
PRINT1(("completion status: SCS_DEVICE_RESET_MSG_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_ID_MSG_FAILED:
{
PRINT1(("completion status: SCS_ID_MSG_FAILED\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_UNEXPECTED_BUS_FREE:
{
PRINT1(("completion status: SCS_UNEXPECTED_BUS_FREE\n"));
request->sr_status = SC_HARDERR;
break;
}
case SCS_DATA_UNDERRUN:
/* this happens all the time when we ask for for mode sense than the drive
can offer
*/
{
PRINT1(("completion status: SCS_DATA_UNDERRUN\n"));
/* request->sr_status=SC_HARDERR; */
/* should reset bus too */
break;
}
default:
PRINTD(("DONT KNOW THE COMPLETION STATUS\n"));
}
if (q_ptr->completion_status)
{
if (q_ptr->state_flags & SS_GOT_BUS)
PRINT3(("state_flag: SS_GOT_BUS\n"));
if (q_ptr->state_flags & SS_GOT_TARGET)
PRINT3(("state_flag: SS_GOT_TARGET\n"));
if (q_ptr->state_flags & SS_SENT_CDB)
PRINT3(("state_flag: SS_SENT_CDB\n"));
if (q_ptr->state_flags & SS_TRANSFERRED_DATA)
PRINT3(("state_flag: SS_TRANSFERRED_DATA\n"));
if (q_ptr->state_flags & SS_GOT_STATUS)
PRINT3(("state_flag: SS_GOT_STATUS\n"));
if (q_ptr->state_flags & SS_GOT_SENSE)
PRINT3(("state_flag: SS_GOT_SENSE\n"));
if (q_ptr->state_flags & SS_TRANSFER_COMPLETE)
PRINT3(("state_flag: SS_TRANSFER_COMPLETE\n"));
if (q_ptr->status_flags & SST_DISCONNECT)
PRINT3(("status_flag: SST_DISCONNECT\n"));
if (q_ptr->status_flags & SST_SYNCHRONOUS)
PRINT3(("status_flag: SST_SYNCHRONOUS\n"));
if (q_ptr->status_flags & SST_PARITY_ERROR)
{
request->sr_status |= SC_PARITY;
PRINT3(("status_flag: SST_PARITY_ERROR\n"));
}
if (q_ptr->status_flags & SST_BUS_RESET)
{
/* request->sr_status|=SC_ATTN;*/
PRINT3(("status_flag: SST_BUS_RESET\n"));
}
if (q_ptr->status_flags & SST_DEVICE_RESET)
PRINT3(("status_flag: SST_DEVICE_RESET\n"));
if (q_ptr->status_flags & SST_ABORTED)
PRINT3(("status_flag: SST_ABORTED\n"));
if (q_ptr->status_flags & SST_TIMEOUT)
{
PRINT3(("status_flag: SST_TIMEOUT\n"));
request->sr_status |= SC_TIMEOUT;
}
if (q_ptr->status_flags & SST_NEGOTIATION)
PRINT3(("status_flag: SST_NEGOTIATION\n"));
}
#ifdef DUMP_IOCB
/* dump_iocb(q_ptr); XXX */
#endif
}
PRINT2(("ql_service_interrupt: IORB %x request %x buffer %x bufferlen %x\n", q_ptr, request, request->sr_buffer, request->sr_buflen));
PRINT4(("completion status %x scsi status[%x], state flags[%x], status flags[%x]\n", q_ptr->completion_status,
q_ptr->scsi_status,
q_ptr->state_flags,
q_ptr->status_flags));
if (request->sr_buflen)
{
{
if ((request->sr_command[0] != READ_CMD_28) && (request->sr_command[0] != WRITE_CMD_2A))
{
#ifndef SWAP_DATA_STREAM
munge(request->sr_buffer, request->sr_buflen);
PRINT2(("munged data from command %x\n", request->sr_command[0]));
#endif
}
}
if (request->sr_command[0] == 0x28)
{
int i;
/*REFERENCED*/
u_char *p;
p = request->sr_command;
for (i = 0; i < 10; i++)
PRINT4(("%x ", *(p++)));
PRINT4(("\n"));
}
/* if (q_ptr->completion_status)debug("status error");*/
}
if (q_ptr->completion_status == SCS_DATA_UNDERRUN)
{
/* allow underruns for now. This happens all the time with mode sense */
PRINT1(("saw underrun.... not reporting it though\n"));
request->sr_status = 0;
}
/* send zero status for now... this should fix the underrun condition */
/* on mode sense commands */
if (request->sr_status != 0)
PRINT1(("sr_status not zero -- %x \n", request->sr_status));
request->sr_scsi_status = q_ptr->scsi_status;
/*
** If a SCSI bus reset occurred, then we need to
** issue a marker before issuing any new requests.
*/
if (q_ptr->completion_status == SCS_RESET_OCCURRED)
{
ha->flags |= AFLG_SEND_MARKER;
/* when this happens the command never got executed */
/* mark this a SC_BUS_RESET so the command will be retried */
request->sr_status = SC_BUS_RESET;
PRINT2(("SCS_RESET_OCCURRED occured\n"));
}
/*
** Save any request sense info.
*/
if (q_ptr->state_flags & SS_GOT_SENSE)
{
u_char cnt;
u_char *dest;
#ifdef QL_DEBUG
if (ql_debug)
dump_iorb((status_entry *) q_ptr);
#endif
if (request->sr_sense == NULL)
{
PRINTD(("no place to put the sense data in the scsi request struct\n"));
munge(&q_ptr->req_sense_data[0], REQ_SENSE_DATA_LENGTH);
#ifdef QL_DEBUG
dump_area((char *) &q_ptr->req_sense_data[0], REQ_SENSE_DATA_LENGTH);
#endif
bcopy(q_ptr->req_sense_data,last_sense,REQ_SENSE_DATA_LENGTH);
ha->flags |= AFLG_SENSE_HELD;
PRINTD(("AFLG_SENSE_HELD\n"));
} else
{
dest = &request->sr_sense[0];
PRINTD(("sense: adapter %x, target %x", request->sr_ctlr, request->sr_target));
for (cnt = 0; cnt < REQ_SENSE_DATA_LENGTH; cnt++)
{
*dest++ = q_ptr->req_sense_data[cnt];
}
#ifndef SWAP_DATA_STREAM
munge(&request->sr_sense[0], REQ_SENSE_DATA_LENGTH);
#endif
#ifdef QL_DEBUG
dump_area((char *) &request->sr_sense[0], REQ_SENSE_DATA_LENGTH);
#endif
request->sr_sensegotten = REQ_SENSE_DATA_LENGTH;
bcopy(request->sr_sense, &tinfo[request->sr_ctlr][request->sr_target][request->sr_lun].si_sense, request->sr_sensegotten);
}
}
/*
** Link this request and return them later.
*/
if (cmp_forw)
{
cmp_back->req_forw = request;
cmp_back = request;
request->req_forw = NULL;
} else
{
cmp_forw = request;
cmp_back = request;
}
request->req_forw = NULL;
}
/*
** I think I have all of the responses. Update the ISP.
*/
PRINT3(("retrieved all reponses from controller. put %x ha->response_out\n", ha->response_out));
PCI_OUTH(&isp->mailbox5, ha->response_out);
PCI_OUTH(&isp->hccr, HCCR_CMD_CLEAR_RISC_INT);
/*
** Get a new copy of the ISR.
*/
isr = PCI_INH(&isp->bus_isr);
PRINT3(("Get the isr again. it is %x mask = %x\n", isr, BUS_ISR_RISC_INT));
} while (isr & BUS_ISR_RISC_INT); /* XXX */
/*no_responses:*/
/*
** Send all completion information back to the host.
*/
while (request = cmp_forw)
{
cmp_forw = request->req_forw;
/* zero out the buffer length */
request->sr_buflen = 0;
request->sr_flags = 0;
request->sr_buffer = 0;
/* callback now */
#ifdef QL_DEBUG
PRINT3(("ql_service_interrupt: completed request=0x%x\n", request));
#endif
}
/*
** If we're in polling mode, then turn this flag off.
*/
{
PRINT3(("clearing AFLG_POLL_ISR\n"));
ha->flags &= ~AFLG_POLL_ISR;
}
/*
** Some resources may have become available.
** See if there are any requests to send.
*/
ql_start_scsi(ha);
}
/* Function: ql_queue_cmd
**
** Description: This routine queues up a new request on our internal
** queue list. We then call start to see if it can start
** the new request. If not, it'll get back to it later.
*/
static void
ql_queue_cmd(pHA_INFO ha, scsi_request_t * request)
{
PRINT3(("ql_queue_cmd: ha %x scsi_request %x\n", ha, request));
CRITICAL_BEGIN
{
/*
** Link the request onto the pending queue.
*/
if (ha->req_forw)
{
ha->req_back->req_forw = request;
} else
{
ha->req_forw = request;
}
ha->req_back = request;
request->req_forw = NULL;
/*
** Start the command (if possible).
*/
ql_start_scsi(ha);
}
CRITICAL_END
}
/* Function: ql_start_scsi
**
** Description: This routine tries to start up any new requests we have
** pending on our internal queue. This may be new commands
** or it may be ones that we couldn't start berfore because
** the adapter queues were full.
*/
static void
ql_start_scsi(pHA_INFO ha)
{
pREQ_INFO r_ptr;
scsi_request_t *request;
pISP_REGS isp = (pISP_REGS) ha->ha_base;
p_scatter scat = &ha->scatter_base;
int entries = 0, index, ri_slot;
#if SN0 && HUB1_II_TIMEOUT_WAR
/* Fix for HUB TIMEOUT BUG This routine needs to be called max 20 secs
* before a DMA is done. It is here right now but it can be called later
* in this routine if required. */
hub_dmatimeout_kick((__psunsigned_t) ha->ha_base);
#endif /* SN0 && HUB1_II_TIMEOUT_WAR */
#ifdef QLREVA_STALE_RRB_WAR
/* The ql controller sometimes asks for things it doesn't consume, which
* can lead the bridge to be stuck with stale buffers in the current
* bridge non-prefetch non-percise mode. This is fixed in rev 0x4.
*/
{
void bridge_inval_rrbs(__psunsigned_t);
if (ha->revision < REV_ISP1040B)
bridge_inval_rrbs((__psunsigned_t)ha->ha_base);
}
#endif
/*
** If there is nothing on the list for us to do,
** then get out now.
*/
PRINT3(("\n\n\nABOUT TO SEND COMMAND\n"));
if ((request = ha->req_forw) == NULL)
{
#ifdef QL_DEBUG
PRINT3(("ql_start_scsi: Nothing to start\n"));
#endif
return;
}
PRINT3(("ql_start_scsi: request: 0x%x ha: 0x%x\n", request, ha));
/*
** If we're going to do a scatter/gather operation we may need
** to use more than one queue entry (one for the request and one
** or more for the scatter/gather table). So, if we have less than
** two queue slots available (might need one for a marker too)
** then zap the number of queue entries so we'll go look again.
** This is kindof a kludge, but ... we's does whats we needs to do.
*/
if (ha->queue_space <= 2)
{
ha->queue_space = 0; /* go look for "number queue slots" */
}
/*
** Determine how many entries are available on the request queue.
**
** This is kinda neat -- instead of always checking if there are
** any new slots RIGHT NOW, we keep track of how many slots there
** were the last time we checked. Since we're the only one filling
** these, there will always be AT LEAST that many still available.
** Once we reach that number, theres no sense in issuing any more
** until he's taken some. So, when we filled up the number of slots
** available on our last check, THEN we check again (to see if there
** is anymore slots available).
**
** If we filled all the slots on the last pass, then check to see
** how many slots there are NOW!
*/
/* took this out because sometimes we have huge request and run out of entries*/
/* if (ha->queue_space == 0) */
{
u_short mailbox4 = 0;
mailbox4 = PCI_INH(&isp->mailbox4);
ha->request_out = mailbox4;
/*
** If the pointers are the same, then ALL the slots are available.
** We have to account for wrap condition, so check both conditions.
*/
if (ha->request_in == ha->request_out)
{
ha->queue_space = REQUEST_QUEUE_DEPTH - 1;
} else if (ha->request_in > ha->request_out)
{
ha->queue_space = ((REQUEST_QUEUE_DEPTH - 1) -
(ha->request_in - ha->request_out));
} else
{
ha->queue_space = (ha->request_out - ha->request_in) - 1;
}
}
{
/*REFERENCED*/
u_short mailbox4 = 0;
/*REFERENCED*/
u_short mailbox5 = 0;
mailbox4 = PCI_INH(&isp->mailbox4);
mailbox5 = PCI_INH(&isp->mailbox5);
PRINT4(("\nmbox 4 %d mbox 5 %d \n", mailbox4, mailbox5));
}
/*
** If we need to send a Marker do it now...
** We only have to send MARKER TO ALL type requests.
*/
if (ha->queue_space != 0 && ha->flags & AFLG_SEND_MARKER)
{
marker_entry *q_ptr;
/*
** Reset the flag so we don't send another marker.
*/
ha->flags &= ~AFLG_SEND_MARKER;
/*
** Get a pointer to the queue entry for the marker.
*/
q_ptr = (marker_entry *) ha->request_ptr;
/*
** Move the internal pointers for the request queue.
*/
if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1))
{
ha->request_in = 0;
ha->request_ptr = ha->request_base;
PRINT4(("wrapped the request pointer\n"));
} else
{
ha->request_in++;
ha->request_ptr++;
}
bzero(q_ptr, sizeof(marker_entry));
/*
** Put the marker in the request queue.
** NOTE: Always use modifier of "2" to marker all devices.
*/
q_ptr->hdr.entry_type = ET_MARKER;
q_ptr->hdr.entry_cnt = 1;
q_ptr->hdr.flags = 0;
q_ptr->hdr.sys_def_1 = ha->request_in;
q_ptr->reserved0 = 0; /* just in case */
q_ptr->target_id = 0;
q_ptr->target_lun = 0;
q_ptr->reserved1 = 0; /* just in case */
q_ptr->modifier = 2;
#ifdef QL_DEBUG
dump_marker(q_ptr);
#endif
#if defined(IP30) && !defined(USE_MAPPED_CONTROL_STREAM)
munge((u_char *) q_ptr, 64);
#endif
/* flush the entry out of the cache */
MR_DCACHE_WB_INVAL(q_ptr, sizeof(marker_entry));
#ifdef QL_DEBUG
PRINT2(("ql_start_scsi: Marker command given to adapter\n"));
#endif
/*
** Tell isp it's got a new I/O request...
*/
PRINT2(("writing %x to mbox 4\n", ha->request_in));
PCI_OUTH(&isp->mailbox4, ha->request_in);
/*
** One less I/O slot.
*/
ha->queue_space--;
}
/*
** See if we can start one or more requests.
** This means there's room in the queue, we have another request
** to start, and we have a request structure for the request.
*/
while (ha->queue_space != 0 &&
(request = ha->req_forw) != NULL &&
(ri_slot = reqi_get_slot(ha,(int)request->sr_target)) != 0)
{
int i;
command_entry *q_ptr;
r_ptr = &ha->reqi_block[request->sr_target][ri_slot % (MAX_REQ_INFO +1 )];
/*
** In order to perform a scatter/gather request, we need to
** determine how many items there are in the scatter list
** We need to do this first because this is the only case where
** we might need more than one queue entry (one for the request
** and more for the continuation of the scatter list).
** If there won't be enough queue entries to process this
** request, then we can't issue the request now.
** We'll just drop out now and let the next interrupt start us
** up again (which should work because we'll re-check the
** number of queue entries).
**
** data_len specifies the total bytes to transfer.
** data_ptr points to an array of address values.
** link_ptr points to an array of length values.
*/
if (request->sr_buflen)
{
/*REFERENCED*/
uint len;
len = make_sg(scat, request->sr_buffer, request->sr_buflen, ha);
entries = scat->num_pages;
#ifdef QL_DEBUG
PRINT3(("ql_start_scsi: return from make_sg or make_sgbp, len = %x, num_pages = %x\n", len, scat->num_pages)); /* XXX */
PRINT3(("ql_start_scsi: S/G xfer has %d entries\n", entries)); /* XXX */
PRINT3(("ql_start_scsi: queue_space is %d\n", ha->queue_space)); /* XXX */
#endif
/*
** If there aren't enough queue entries to support this
** command then bye bye ... Clear the number of entries
** in the queue so we'll check with the controller on
** the next call to this routine.
** There is room for four entries in with the command.
** There is room for 7 entries in each continuation entry.
** So, excluding the four in the command entry, divide by
** seven to determine the number of continuations. Add
** one to count the command entry.
**
** Note: remember that there MUST be some command outstanding
** or the number of queue entries would be greater than 2
** (at the top of this function we ensure that there is
** more than two entries).
*/
if ((((entries - IOCB_SEGS) / CONTINUATION_SEGS) + 1) >= ha->queue_space)
{
ha->queue_space = 0;
#ifdef QL_DEBUG
cmn_err(CE_WARN, "ql_start_scsi: No room for continue pkt\n");
#endif
break;
}
}
/*
** If we got this far, then we have enough entries in the queue
** to satisfy the request. All we need to do is grab the next
** entry, fill it in, and tell the controller that there are
** new entries.
*/
/*
** Unhook the request and the request info structure.
*/
ha->req_forw = request->req_forw;
/*
** Initialize the request info structure for this request.
*/
r_ptr->req = request;
PRINT1(("r_ptr = 0x%x request is %x\n", r_ptr, request));
request->sr_status = 0; /* this is counted down on retrun */
request->sr_scsi_status = 0; /* this is how many we issued */
/*
** Get a pointer to the queue entry for the command.
*/
q_ptr = (command_entry *) ha->request_ptr;
/*
** Move the internal pointers for the request queue.
*/
if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1))
{
ha->request_in = 0;
ha->request_ptr = ha->request_base;
PRINT3(("wrapped the request pointer\n"));
} else
{
ha->request_in++;
ha->request_ptr++;
}
/* bzero out the entry. makes it easier for debug. might get ride
* of it later */
bzero(q_ptr, sizeof(queue_entry));
/*
** Fill in the command header.
*/
q_ptr->hdr.entry_type = ET_COMMAND;
q_ptr->hdr.entry_cnt = 1;
q_ptr->hdr.flags = 0;
q_ptr->hdr.sys_def_1 = ha->request_in;
/*
** Fill in the reset of the command data.
*/
q_ptr->handle = ri_slot;
q_ptr->target_id = request->sr_target;
ASSERT(request->sr_target != ha->ql_defaults.Initiator_SCSI_Id);
q_ptr->target_lun = request->sr_lun;
q_ptr->control_flags = 0;
q_ptr->reserved = 0;
/* do not let the conroller timeout commands */
/* this driver will poll on command completion and timeout */
/* based on sr_timeout */
q_ptr->time_out = 0;
#ifdef QL_DEBUG
for (i = 0; i < request->sr_cmdlen; i++)
PRINT2(("%x ", request->sr_command[i]));
if (request->sr_command[0] == READ_CMD_28)
{
PRINT2(("READ %x bytes\n", request->sr_buflen));
} else if (request->sr_command[0] == WRITE_CMD_2A)
{
PRINT2(("WRITE %x bytes\n", request->sr_buflen));
} else
{
int i;
/* dump the cdb */
PRINT2(("\nnot a READ or a WRITE command "));
PRINT2(("request->sr_command %x :", request->sr_command));
for (i = 0; i < request->sr_cmdlen; i++)
{
PRINT2((" %x", request->sr_command[i]));
}
PRINT2((" sr_flags %x\n", q_ptr->control_flags));
}
#endif
/*
** Copy over the requests SCSI cdb.
*/
q_ptr->cdb_length = request->sr_cmdlen;
switch (request->sr_cmdlen)
{
case SC_CLASS2_SZ:
q_ptr->cdb11 = request->sr_command[11];
q_ptr->cdb10 = request->sr_command[10];
case SC_CLASS1_SZ:
q_ptr->cdb9 = request->sr_command[9];
q_ptr->cdb8 = request->sr_command[8];
q_ptr->cdb7 = request->sr_command[7];
q_ptr->cdb6 = request->sr_command[6];
case SC_CLASS0_SZ:
q_ptr->cdb5 = request->sr_command[5];
q_ptr->cdb4 = request->sr_command[4];
q_ptr->cdb3 = request->sr_command[3];
q_ptr->cdb2 = request->sr_command[2];
q_ptr->cdb1 = request->sr_command[1];
q_ptr->cdb0 = request->sr_command[0];
}
PRINT1(("SCSI COMMAND: %x %x %x %x %x %x byte count %x for targ %x lun %x\n",
request->sr_command[0],
request->sr_command[1],
request->sr_command[2],
request->sr_command[3],
request->sr_command[4],
request->sr_command[5],
request->sr_buflen,
request->sr_target,
request->sr_lun));
/*
** Setup to transfer data.
*/
if (q_ptr->dseg[0].count = request->sr_buflen)
{
q_ptr->segment_cnt = 1;
q_ptr->dseg[0].base_low_32 = (u_int) MAKE_DIRECT_MAPPED_2GIG(kvtophys(request->sr_buffer));
#ifdef SN0_PCI_64
{
paddr_t p_ptr;
p_ptr = get_pci64_dma_addr(
(__psunsigned_t) request->sr_buffer,
ha_sn0_info[ha->controller_number]);
q_ptr->dseg[0].base_low_32 = p_ptr;
q_ptr->dseg[0].base_high_32 = p_ptr>> 32;
/*printf("l32 %x h32 %x\n",q_ptr->dseg[0].base_low_32,q_ptr->dseg[0].base_high_32);*/
}
#endif
if (!(request->sr_flags & SRF_DIR_IN))
{
q_ptr->control_flags |= CF_WRITE;
PRINT3(("WRITE to memory direction\n"));
if (request->sr_buflen)
{
{
if ((request->sr_command[0] != READ_CMD_28) && (request->sr_command[0] != WRITE_CMD_2A))
{
#ifndef SWAP_DATA_STREAM
munge(request->sr_buffer, request->sr_buflen);
PRINT3(("munge before controller DMA reads\n"));
#endif
}
if (request->sr_buffer)
MR_DCACHE_WB_INVAL(request->sr_buffer, request->sr_buflen);
}
}
} else
{
q_ptr->control_flags |= CF_READ;
if (request->sr_buffer)
DCACHE_INVAL(request->sr_buffer, request->sr_buflen);
}
} else
{
q_ptr->segment_cnt = 0;
}
/*
** If this is a scatter/gather request, then we need to adjust the
** data address and lengths for the scatter entries.
*/
/*
** If there are more than IOCB_SEGS entries in the scatter
** gather list, then we're going to be using some contiuation
** queue entries. So, tell the command entry how many
** continuations to expect. Add two to include the command.
*/
if (entries > IOCB_SEGS)
{
int i = 1;
if ((entries - IOCB_SEGS) % CONTINUATION_SEGS)
i = 2;
q_ptr->hdr.entry_cnt = ((entries - IOCB_SEGS) / CONTINUATION_SEGS) + i;
}
/*
** Also, update the segment count with the number of entries
** in the scatter/gather list.
*/
q_ptr->segment_cnt = entries;
/*
** The command queue entry has room for four scatter/gather
** entries. Fill them in.
*/
for (i = 0; i < IOCB_SEGS && entries; i++, entries--)
{
q_ptr->dseg[i].base_low_32 = scat->dseg[i].base;
#ifdef SN0_PCI_64
q_ptr->dseg[i].base_high_32 = scat->dseg[i].base >> 32;
#endif
q_ptr->dseg[i].count = scat->dseg[i].count;
}
/*
** If there is still more entries in the scatter/gather
** list, then we need to use some continuation queue entries.
**
** While there are more entries in the scatter/gather
** table, allocate a continuation queue entry and fill it in.
*/
index = IOCB_SEGS; /* set the index for the */
/* next sg element pair */
while (entries)
{
continuation_entry *c_ptr;
/*
** Get a pointer to the next queue entry for the
** continuation of the command.
*/
c_ptr = (continuation_entry *) ha->request_ptr;
/*
** Move the internal pointers for the request queue.
*/
if (ha->request_in == (REQUEST_QUEUE_DEPTH - 1))
{
ha->request_in = 0;
ha->request_ptr = ha->request_base;
PRINT3(("wrapped the request pointer\n"));
} else
{
ha->request_in++;
ha->request_ptr++;
}
/*
** Fill in the continuation header.
*/
/*zero out the entry. might take out later */
bzero(c_ptr, sizeof(continuation_entry));
c_ptr->hdr.entry_type = ET_CONTINUATION;
c_ptr->hdr.entry_cnt = 1;
c_ptr->hdr.flags = 0;
c_ptr->hdr.sys_def_1 = 0;
#ifndef SN0_PCI_64
c_ptr->reserved = 0;
#endif
/*
** Fill in the scatter entries for this continuation.
*/
for (i = 0; i < CONTINUATION_SEGS && entries; i++, entries--)
{
c_ptr->dseg[i].base_low_32 = scat->dseg[index].base;
#ifdef SN0_PCI_64
c_ptr->dseg[i].base_high_32 = scat->dseg[index].base >> 32;
#endif
c_ptr->dseg[i].count = scat->dseg[index].count;
index++;
}
/*
** One less I/O slot.
*/
ha->queue_space--;
/* flush the entry out of the cache */
#if defined(IP30) && !defined(USE_MAPPED_CONTROL_STREAM)
munge((u_char *) c_ptr, 64); /* XXX for emulation */
#endif
#ifdef QL_DEBUG
if (ql_debug >= 3)
{
dump_continuation((continuation_entry *) c_ptr);
}
#endif
MR_DCACHE_WB_INVAL(c_ptr, sizeof(continuation_entry));
PRINT3(("continuation entry\n"));
}
#ifdef QL_DEBUG
PRINT3(("ql_start_scsi: New command given to adapter\n"));
if (ql_debug >= 3)
{
dump_iocb((command_entry *) q_ptr); /* XXX */
}
#endif
#ifdef DUMP_IOCB
if (ql_dump_control_block)
dump_iocb((command_entry *) q_ptr); /* XXX */
#endif
#if defined(IP30) && !defined(USE_MAPPED_CONTROL_STREAM)
PRINT3(("MUNGING IOCB\n"));
munge((u_char *) q_ptr, 64);
#endif
MR_DCACHE_WB_INVAL((void *)q_ptr, 64);
/*
** Tell isp it's got a new I/O request...
** Wake up
*/
PRINT2(("writing %x to mbox4 at %x\n", ha->request_in,
&isp->mailbox4));
PCI_OUTH(&isp->mailbox4, ha->request_in);
/*
** One less I/O slot.
*/
ha->queue_space--;
}
}
/* Function: ql_mbox_cmd()
**
** Return: 0 no error
** non-0 error
**
** Description: This routine issues a mailbox command.
** We're passed a list of mailboxes and the number of
** mailboxes to read and the number to write.
**
** Note: I've added a special case (that may not be any problem)
** to skip loading mailbox 4 if the command is INITIALIZE RESPONSE QUEUE.
*/
static int
ql_mbox_cmd(pHA_INFO ha,
u_short * mbox_sts,
u_char out_cnt, u_char in_cnt,
u_short reg0, u_short reg1, u_short reg2, u_short reg3,
u_short reg4, u_short reg5, u_short reg6, u_short reg7)
{
pISP_REGS isp = (pISP_REGS) ha->ha_base;
int complete = 0;
int retry_cnt = 1;
int timeout_error = 0;
/*XXX a guess at a timeout value */
#define MBOX_TIMEOUT_VALUE 20000000
long timeout = 1;
/*
** Copies of ISP registers (for register accesses).
*/
u_short hccr = 0;
u_short icr = 0;
u_short bus_sema = 0;
int disable = 0;
if ((reg0 == MBOX_CMD_WRITE_RAM_WORD) || (reg0 == MBOX_CMD_READ_RAM_WORD))
disable = 1;
#ifdef MBOX_DEBUG
/* dump_registers(isp); */
if (!disable)
PRINT5(("\n\nql_mbox_cmd - new cmd 0x%x (out=%d, in=%d)\n",
reg0, out_cnt, in_cnt));
if (!disable)
PRINT3(("out %x: [%x %x %x %x %x %x %x %x] ",
out_cnt, reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7)); /* XXX */
#endif
#ifdef MBOX_DEBUG
if (!disable)
PRINT5(("P1 ")); /* XXX */
#endif
/*
** Just for grins, clear out ALL the return status.
** This might catch a bug later where I try to use a return
** parameter that I wasn't given (would be left over from previous).
*/
mbox_sts[0] = 0;
mbox_sts[1] = 0;
mbox_sts[2] = 0;
mbox_sts[3] = 0;
mbox_sts[4] = 0;
mbox_sts[5] = 0;
mbox_sts[6] = 0;
mbox_sts[7] = 0;
/*
** Host interrupt should be clear unless we just started.
** Wait for the bit to clear.
** Note: Later, be sure to timeout (in case of dead hardware).
*/
do
{
/*
** Get a new copy of the HCCR.
*/
hccr = PCI_INH(&isp->hccr);
if (!disable)
PRINT5(("hccr %x\n", hccr));
} while (hccr & HCCR_HOST_INT);
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P2 ")); /* XXX */
#endif
/* CRITICAL_BEGIN XXX */
{
/*
** Get a copy of the ICR. Are interrupts enabled (yet)?
** We'll use it later to determine if we need to clear
** the interrupt status.
*/
icr = PCI_INH(&isp->bus_icr);
if (!disable)
PRINT5(("icr is %x\n", icr));
/*
** Okay, now try to send the command.
*/
do
{
/*
** Move the data to the mailbox registers.
*/
if (!disable)
PRINT5(("out_cnt is %x\n", out_cnt));
switch (out_cnt)
{
case 8:
PCI_OUTH(&isp->mailbox7, reg7);
case 7:
PCI_OUTH(&isp->mailbox6, reg6);
case 6:
PCI_OUTH(&isp->mailbox5, reg5);
case 5:
if (reg0 != 0x11)
PCI_OUTH(&isp->mailbox4, reg4);
case 4:
PCI_OUTH(&isp->mailbox3, reg3);
case 3:
PCI_OUTH(&isp->mailbox2, reg2);
case 2:
PCI_OUTH(&isp->mailbox1, reg1);
case 1:
PCI_OUTH(&isp->mailbox0, reg0);
if (!disable)
PRINT3(("all mailbox registers loaded cmd:%x %x %x %x %x %x %x %x\n", reg0, reg1, reg2, reg3, reg4, reg5, reg6, reg7));
}
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P3 ")); /* XXX */
#endif
#ifdef DUMP_REGS
/* dump_registers(isp); XXX */
#endif
/*
** Let the interrupt service routine know we were
** expecting an interrupt, so okay to ignore it.
*/
ha->flags |= AFLG_MAIL_BOX_EXPECTED;
if (!disable)
PRINT3(("set AFLG_MAIL_BOX_EXPECTED\n"));
/*
** Wake up the isp.
*/
PCI_OUTH(&isp->hccr, HCCR_CMD_SET_HOST_INT);
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P4 ")); /* XXX */
#endif
#ifdef WAIT_SEMA
/*
** Wait for the command to complete
** Note: Later, be sure to timeout (in case of dead hardware).
*/
if (!disable)
PRINT5(("waiting for sema\n"));
do
{
/*
** Get a new copy of the semaphore register.
*/
bus_sema = PCI_INH(&isp->bus_sema);
if (!(timeout % 100))
{
PRINT3(("reading sema %x timeout %d\n", bus_sema, timeout));
/* dump_registers(isp); */
}
if (++timeout >= MBOX_TIMEOUT_VALUE)
{
PRINTD(("mbox_cmd timeout waiting for sema at %x=%x\n", &isp->bus_sema, PCI_INH(&isp->bus_sema)));
timeout_error = 1;
break;
}
} while (!(bus_sema & BUS_SEMA_LOCK));
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P5A ")); /* XXX */
#endif
#else /* WAIT_SEMA */
/*
** Wait for the RISC INTERRUPT to arrive.
*/
PRINT5(("waiting for interrupt\n"));
do
{
/*
** Get a new copy of the ISR.
*/
isr = PCI_INH(&isp->bus_isr);
if (!(++timeout % 100))
{
PRINTD(("reading isr %x timeout %d\n", isr, timeout));
/* dump_registers(isp); */
}
} while (!(isr & BUS_ISR_RISC_INT));
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P5B ")); /* XXX */
#endif
#endif /* WAIT_SEMA */
/*
** Let the interrupt service routine know we
** aren't expecting an interrut anymore.
*/
ha->flags &= ~AFLG_MAIL_BOX_EXPECTED;
if (!disable)
PRINT3(("cleared AFLG_MAIL_BOX_EXPECTED\n"));
/*
** Process the mailbox response.
*/
if (timeout_error)
goto finishup;
{
u_short mailbox0 = 0;
/*
** Get a new copy of mailbox 0.
*/
mailbox0 = PCI_INH(&isp->mailbox0);
/*
** Get the response to our command.
*/
switch (mailbox0)
{
case MBOX_STS_BUSY:
{
/*
** Retry forever on BUSY.
*/
retry_cnt++;
/*
** Print a message every 10 retry.
*/
if (retry_cnt % 10 == 0)
{
#ifdef QL_DEBUG
cmn_err(CE_WARN,
"ql_mbox_cmd - %d Retries", retry_cnt);
#endif
}
}
break;
case MBOX_ASTS_SYSTEM_ERROR:
{
#ifdef QL_DEBUG
u_short mailbox1 = 0;
u_short mailbox2 = 0;
mailbox1 = PCI_INH(&isp->mailbox1);
mailbox2 = PCI_INH(&isp->mailbox2);
cmn_err(CE_WARN,
"ql_mbox_cmd - ISP Fatal Firmware Error");
cmn_err(CE_WARN,
"ql_mbox_cmd - mailbox: 0=0x%x, 1=0x%x, 2=0x%x\n",
mailbox0, mailbox1, mailbox2);
#endif
}
break;
case MBOX_ASTS_SCSI_BUS_RESET:
{
ha->flags |= AFLG_SEND_MARKER;
cmn_err(CE_NOTE,
"ql_mbox_cmd - ISP SCSI bus RESET seen");
}
break;
case MBOX_STS_INVALID_COMMAND:
{
PRINT2(("ql_mbox_cmd - ISP BOGUS Command Error cmd %d\n", reg0));
PRINT2(("ql_mbox_cmd - mailbox: 0=0x%x, \n", mailbox0));
}
break;
default:
{
/*
** Save away status registers.
*/
switch (in_cnt)
{
case 8:
mbox_sts[7] = PCI_INH(&isp->mailbox7);
case 7:
mbox_sts[6] = PCI_INH(&isp->mailbox6);
case 6:
mbox_sts[5] = PCI_INH(&isp->mailbox5);
case 5:
if (reg0 != 0x11)
mbox_sts[4] = PCI_INH(&isp->mailbox4);
case 4:
mbox_sts[3] = PCI_INH(&isp->mailbox3);
case 3:
mbox_sts[2] = PCI_INH(&isp->mailbox2);
case 2:
mbox_sts[1] = PCI_INH(&isp->mailbox1);
case 1:
mbox_sts[0] = PCI_INH(&isp->mailbox0);
}
/*
** Indicate that we're done processing.
*/
complete = 1;
}
}
}
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P6 ")); /* XXX */
#endif
/*
** Clear the semaphore lock.
*/
finishup:
PCI_OUTH(&isp->bus_sema, 0);
/*
** If interrupts aren't enabled, then we need to
** clear the interrupt ourselves (interrupt service
** won't ever get it). This is in INIT stage.
*/
if (!(icr & (ICR_ENABLE_ALL_INTS | ICR_ENABLE_RISC_INT)))
{
/*
** Clear the risc interrupt.
*/
PCI_OUTH(&isp->hccr, HCCR_CMD_CLEAR_RISC_INT);
}
} while ((!complete) && (retry_cnt-- > 0));
}
/* CRITICAL_END XXX */
#ifdef QL_DEBUG
if (!disable)
PRINT5(("P7\n ")); /* XXX */
#endif
#ifdef MBOX_DEBUG
if (!disable)
PRINT3(("in %d: [(%x) %x %x %x %x %x %x %x]\n",
in_cnt, mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6], mbox_sts[7])); /* XXX */
#endif
#ifndef WAIT_SEMA
/*
** If we weren't waiting on the semaphore flag,
** I may have cleared an interrupt,
** so call the service routine just in case.
** THIS MAY CAUSE STATISTIC TO BE WEIRD - we count calls to qlintr
*/
PRINT2(("calling qlintr from ql\n"));
PRINTD(("extra call to qlintr"));
qlintr(ha);
#endif /* WAIT_SEMA */
return (!complete);
}
#ifdef QL_DEBUG
#ifdef DUMP_REGS
/*
** Dump the registers for this ISP.
*/
static void
dump_registers(pISP_REGS isp)
{
u_short bus_id_low, bus_id_high, bus_config0, bus_config1;
u_short bus_icr = 0;
u_short bus_isr = 0;
u_short bus_sema;
u_short hccr = 0;
u_short risc_pc;
u_short mbox0, mbox1, mbox2, mbox3, mbox4, mbox5;
u_short control_dma_control;
u_short control_dma_config;
u_short control_dma_fifo_status;
u_short control_dma_status;
u_short control_dma_counter;
u_short control_dma_address_counter_1;
u_short control_dma_address_counter_0;
u_short control_dma_address_counter_3;
u_short control_dma_address_counter_2;
u_short data_dma_control;
u_short data_dma_config;
u_short data_dma_fifo_status;
u_short data_dma_status;
u_short data_dma_xfer_counter_high;
u_short data_dma_xfer_counter_low;
u_short data_dma_address_counter_1;
u_short data_dma_address_counter_0;
u_short data_dma_address_counter_3;
u_short data_dma_address_counter_2;
/* Get copies of the registers. */
mbox0 = PCI_INH(&isp->mailbox0);
mbox1 = PCI_INH(&isp->mailbox1);
mbox2 = PCI_INH(&isp->mailbox2);
mbox3 = PCI_INH(&isp->mailbox3);
mbox4 = PCI_INH(&isp->mailbox4);
mbox5 = PCI_INH(&isp->mailbox5);
bus_id_low = PCI_INH(&isp->bus_id_low);
bus_id_high = PCI_INH(&isp->bus_id_high);
bus_config0 = PCI_INH(&isp->bus_config0);
bus_config1 = PCI_INH(&isp->bus_config1);
bus_icr = PCI_INH(&isp->bus_icr);
bus_isr = PCI_INH(&isp->bus_isr);
bus_sema = PCI_INH(&isp->bus_sema);
hccr = PCI_INH(&isp->hccr);
PCI_OUTH(&isp->hccr, HCCR_CMD_PAUSE);
control_dma_control = PCI_INH(&isp->control_dma_control);
control_dma_config = PCI_INH(&isp->control_dma_config);
control_dma_fifo_status = PCI_INH(&isp->control_dma_fifo_status);
control_dma_status = PCI_INH(&isp->control_dma_status);
control_dma_counter = PCI_INH(&isp->control_dma_counter);
control_dma_address_counter_1 = PCI_INH(&isp->control_dma_address_counter_1);
control_dma_address_counter_0 = PCI_INH(&isp->control_dma_address_counter_0);
control_dma_address_counter_3 = PCI_INH(&isp->control_dma_address_counter_3);
control_dma_address_counter_2 = PCI_INH(&isp->control_dma_address_counter_2);
data_dma_config = PCI_INH(&isp->data_dma_config);
data_dma_fifo_status = PCI_INH(&isp->data_dma_fifo_status);
data_dma_status = PCI_INH(&isp->data_dma_status);
data_dma_control = PCI_INH(&isp->data_dma_control);
data_dma_xfer_counter_high = PCI_INH(&isp->data_dma_xfer_counter_high);
data_dma_xfer_counter_low = PCI_INH(&isp->data_dma_xfer_counter_low);
data_dma_address_counter_1 = PCI_INH(&isp->data_dma_address_counter_1);
data_dma_address_counter_0 = PCI_INH(&isp->data_dma_address_counter_0);
data_dma_address_counter_3 = PCI_INH(&isp->data_dma_address_counter_3);
data_dma_address_counter_2 = PCI_INH(&isp->data_dma_address_counter_1);
risc_pc = PCI_INH(&isp->risc_pc);
PCI_OUTH(&isp->hccr, HCCR_CMD_RELEASE);
printf("\ndump_regs: mbox0 = %x, mbox1 = %x\n", mbox0, mbox1);
printf("dump_regs: mbox2 = %x, mbox3 = %x\n", mbox2, mbox3);
printf("dump_regs: mbox4 = %x, mbox5 = %x\n", mbox4, mbox5);
printf("dump_regs: BUS_ID_LOW=%x, BUS_ID_HIGH=%x\n",
bus_id_low, bus_id_high);
printf("dump_regs: BUS_CONFIG0=%x, BUS_CONFIG1=%x\n",
bus_config0, bus_config1);
printf("dump_regs: BUS_ICR=%x, BUS_ISR=%x, BUS_SEMA=%x\n",
bus_icr, bus_isr, bus_sema);
printf("dump_regs: HCCR=%x: %x \n", &isp->hccr, hccr);
printf("risc PC : %x\n", risc_pc);
printf("dump_regs: control_dma_control %x: %x\n",
&isp->control_dma_control, control_dma_control);
printf("dump_regs: control_dma_config %x: %x\n",
&isp->control_dma_config, control_dma_config);
printf("dump_regs: control_dma_fifo_status %x: %x\n",
&isp->control_dma_fifo_status, control_dma_fifo_status);
printf("dump_regs: control_dma_status %x: %x\n",
&isp->control_dma_status, control_dma_status);
printf("dump_regs: control_dma_counter %x: %x\n",
&isp->control_dma_counter, control_dma_counter);
printf("dump_regs: control_dma_address_counter_1 %x: %x\n",
&isp->control_dma_address_counter_1, control_dma_address_counter_1);
printf("dump_regs: control_dma_address_counter_0 %x: %x\n",
&isp->control_dma_address_counter_0, control_dma_address_counter_0);
printf("dump_regs: control_dma_address_counter_3 %x: %x\n",
&isp->control_dma_address_counter_3, control_dma_address_counter_3);
printf("dump_regs: control_dma_address_counter_0 %x: %x\n",
&isp->control_dma_address_counter_2, control_dma_address_counter_2);
printf("dump_regs: data_dma_config %x: %x\n",
&isp->data_dma_config, data_dma_config);
printf("dump_regs: data_dma_status %x: %x\n",
&isp->data_dma_status, data_dma_status);
printf("dump_regs: data_dma_fifo_status %x: %x\n",
&isp->data_dma_fifo_status, data_dma_fifo_status);
printf("dump_regs: data_dma_control %x: %x\n",
&isp->data_dma_control, data_dma_control);
printf("dump_regs: data_dma_xfer_counter_high %x: %x\n",
&isp->data_dma_xfer_counter_high, data_dma_xfer_counter_high);
printf("dump_regs: data_dma_xfer_counter_low %x: %x\n",
&isp->data_dma_xfer_counter_low, data_dma_xfer_counter_low);
printf("dump_regs: data_dma_address_counter_1 %x: %x\n",
&isp->data_dma_address_counter_1,
data_dma_address_counter_1);
printf("dump_regs: data_dma_address_counter_0 %x: %x\n",
&isp->data_dma_address_counter_0,
data_dma_address_counter_0);
printf("dump_regs: data_dma_address_counter_3 %x: %x\n",
&isp->data_dma_address_counter_3,
data_dma_address_counter_3);
printf("dump_regs: data_dma_address_counter_2 %x: %x\n",
&isp->data_dma_address_counter_2,
data_dma_address_counter_2);
}
#endif /* DUMP_REGS */
#ifdef DUMP_PARMS
/*
** Dump FIRMWARE parameters.
*/
static void
dump_parameters(int board)
{
pHA_INFO ha = ha_info[board];
u_short mbox_sts[8];
if(!ha) {
printf("no host adapter structure for board %x\n",board);
return;
}
PRINTD(("dump_parameters for board %x\n",board));
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_FIRMWARE_STATUS,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_FIRMWARE_STATUS Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_FIRMWARE_STATUS\n"));
PRINTD((" %x, %x, %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2], mbox_sts[3],
mbox_sts[4], mbox_sts[5], mbox_sts[6], mbox_sts[7]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_INITIATOR_SCSI_ID,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_INITIATOR_SCSI_ID Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_INITIATOR_SCSI_ID\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_SELECTION_TIMEOUT,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_SELECTION_TIMEOUT Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_SELECTION_TIMEOUT\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_RETRY_COUNT,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_RETRY_COUNT Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_RETRY_COUNT\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_TAG_AGE_LIMIT,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_TAG_AGE_LIMIT Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_TAG_AGE_LIMIT\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_CLOCK_RATE,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_CLOCK_RATE Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_CLOCK_RATE\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_ACTIVE_NEGATION_STATE,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_ACTIVE_NEGATION_STATE Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_ACTIVE_NEGATION_STATE\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_ASYNC_DATA_SETUP_TIME,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_ASYNC_DATA_SETUP_TIME Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_ASYNC_DATA_SETUP_TIME\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 1, 6,
MBOX_CMD_GET_BUS_CONTROL_PARAMETERS,
0, 0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_BUS_CONTROL_PARAMETERS Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_BUS_CONTROL_PARAMETERS\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 2, 6,
MBOX_CMD_GET_TARGET_PARAMETERS,
0, /* SCSI id */
0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_TARGET_PARAMETERS Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_TARGET_PARAMETERS\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
if (ql_mbox_cmd(ha, mbox_sts, 2, 6,
MBOX_CMD_GET_DEVICE_QUEUE_PARAMETERS,
0, /* SCSI id */
0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"ql_dump_parameters - GET_DEVICE_QUEUE_PARAMETERS Command Failed");
} else
{
PRINTD(("ql_dump_parameters - GET_DEVICE_QUEUE_PARAMETERS\n"));
PRINTD((" %x, %x, %x, %x, %x, %x\n",
mbox_sts[0], mbox_sts[1], mbox_sts[2],
mbox_sts[3], mbox_sts[4], mbox_sts[5]));
}
}
#endif /* DUMP_PARMS */
#ifdef DUMP_IOCB
static void
dump_iocb(command_entry * q_ptr)
{
int i;
char *ptr;
ptr = (char *) q_ptr;
printf("RAW data: ");
for (i = 0; i <= 64; i++)
printf(" %x", *ptr++);
printf("\n");
printf("------------- IOCB ------------- %x\n", q_ptr);
printf("\n");
printf("Entry Count\t:\t%x\n", q_ptr->hdr.entry_cnt);
printf("Entry Type\t:\t%x\n", q_ptr->hdr.entry_type);
printf("Flags\t\t: \t%x\n", q_ptr->hdr.flags);
printf("System Defined\t:\t%x\n", q_ptr->hdr.sys_def_1);
printf("Handle\t\t:\t%x\n", q_ptr->handle);
printf("Target ID\t:\t%x\n", q_ptr->target_id);
printf("LUN\t\t:\t%x\n", q_ptr->target_lun);
printf("CDB Length\t:\t%x\n", q_ptr->cdb_length);
printf("Control Flags\t:\t%x\n", q_ptr->control_flags);
printf("Timeout\t\t:\t%x\n", q_ptr->time_out);
printf("Data Seg Count\t:\t%x\n", q_ptr->segment_cnt);
switch (q_ptr->cdb_length)
{
case SC_CLASS0_SZ:
printf("CDB\t:\t%x %x %x %x %x %x\n", q_ptr->cdb0, q_ptr->cdb1, q_ptr->cdb2, q_ptr->cdb3, q_ptr->cdb4, q_ptr->cdb5);
break;
case SC_CLASS2_SZ:
printf("CDB\t:\t%x %x %x %x %x %x %x %x\n", q_ptr->cdb0, q_ptr->cdb1, q_ptr->cdb2, q_ptr->cdb3, q_ptr->cdb4, q_ptr->cdb5, q_ptr->cdb6, q_ptr->cdb7);
break;
case SC_CLASS1_SZ:
printf("CDB\t:\t%x %x %x %x %x %x %x %x %x %x %x %x\n", q_ptr->cdb0, q_ptr->cdb1, q_ptr->cdb2, q_ptr->cdb3, q_ptr->cdb4, q_ptr->cdb5, q_ptr->cdb6, q_ptr->cdb7, q_ptr->cdb8, q_ptr->cdb9, q_ptr->cdb10, q_ptr->cdb11);
break;
}
printf("Data Segment 1: Address %x\tCount %x\n", q_ptr->dseg[0].base_low_32, q_ptr->dseg[0].count);
printf("Data Segment 2: Address %x\tCount %x\n", q_ptr->dseg[1].base_low_32, q_ptr->dseg[1].count);
printf("Data Segment 3: Address %x\tCount %x\n", q_ptr->dseg[2].base_low_32, q_ptr->dseg[2].count);
printf("Data Segment 4: Address %x\tCount %x\n", q_ptr->dseg[3].base_low_32, q_ptr->dseg[3].count);
}
#endif
#ifdef QL_DEBUG
static void
dump_iorb(status_entry * q_ptr)
{
int i;
char *ptr;
ptr = (char *) q_ptr;
printf("RAW data: ");
for (i = 0; i <= 64; i++)
printf(" %x", *ptr++);
printf("\n");
printf("------------- IORB ------------- %x\n", q_ptr);
printf("Entry Count\t:\t%x\n", q_ptr->hdr.entry_cnt);
printf("Entry Type\t:\t%x\n", q_ptr->hdr.entry_type);
printf("Flags\t\t:\t%x\n", q_ptr->hdr.flags);
printf("System Defined\t:\t%x\n", q_ptr->hdr.sys_def_1);
printf("Handle\t\t:\t%x\n", q_ptr->handle);
printf("Scsi Status\t:\t%x\n", q_ptr->scsi_status);
printf("Comp. Status\t:\t%x\n", q_ptr->completion_status);
printf("State Flags\t:\t%x\n", q_ptr->state_flags);
printf("Status Flags\t:\t%x\n", q_ptr->status_flags);
printf("Time\t\t:\t%x\n", q_ptr->time);
printf("Sense Length\t:\t%x\n", q_ptr->req_sense_length);
printf("Timeout\t\t:\t%x\n", q_ptr->time);
printf("Residual\t:\t%x\n", q_ptr->residual);
printf("Request Sense : %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", q_ptr->req_sense_data[3], q_ptr->req_sense_data[2], q_ptr->req_sense_data[1], q_ptr->req_sense_data[0], q_ptr->req_sense_data[7], q_ptr->req_sense_data[6], q_ptr->req_sense_data[5], q_ptr->req_sense_data[4], q_ptr->req_sense_data[11], q_ptr->req_sense_data[10], q_ptr->req_sense_data[9], q_ptr->req_sense_data[8], q_ptr->req_sense_data[15], q_ptr->req_sense_data[14], q_ptr->req_sense_data[13], q_ptr->req_sense_data[12]);
printf("Request Sense : %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n", q_ptr->req_sense_data[19], q_ptr->req_sense_data[18], q_ptr->req_sense_data[17], q_ptr->req_sense_data[16], q_ptr->req_sense_data[23], q_ptr->req_sense_data[22], q_ptr->req_sense_data[21], q_ptr->req_sense_data[20], q_ptr->req_sense_data[27], q_ptr->req_sense_data[26], q_ptr->req_sense_data[25], q_ptr->req_sense_data[24], q_ptr->req_sense_data[31], q_ptr->req_sense_data[30], q_ptr->req_sense_data[29], q_ptr->req_sense_data[28]);
}
static void
dump_continuation(continuation_entry * q_ptr)
{
int i;
char *ptr;
ptr = (char *) q_ptr;
PRINT3(("RAW data: "));
for (i = 0; i <= 64; i++)
PRINT3((" %x", *ptr++));
PRINT3(("\n"));
PRINT3(("------------- CONTINUATION ------------- %x\n", q_ptr));
PRINT3(("Entry Count\t:\t%x\n", q_ptr->hdr.entry_cnt));
PRINT3(("Entry Type\t:\t%x\n", q_ptr->hdr.entry_type));
PRINT3(("Flags\t\t:\t%x\n", q_ptr->hdr.flags));
PRINT3(("System Defined\t:\t%x\n", q_ptr->hdr.sys_def_1));
PRINT3(("Data Segment 1: Address %x\tCount %x\n", q_ptr->dseg[0].base_low_32, q_ptr->dseg[0].count));
PRINT3(("Data Segment 2: Address %x\tCount %x\n", q_ptr->dseg[1].base_low_32, q_ptr->dseg[1].count));
PRINT3(("Data Segment 3: Address %x\tCount %x\n", q_ptr->dseg[2].base_low_32, q_ptr->dseg[2].count));
PRINT3(("Data Segment 4: Address %x\tCount %x\n", q_ptr->dseg[3].base_low_32, q_ptr->dseg[3].count));
PRINT3(("Data Segment 5: Address %x\tCount %x\n", q_ptr->dseg[4].base_low_32, q_ptr->dseg[4].count));
PRINT3(("Data Segment 6: Address %x\tCount %x\n", q_ptr->dseg[5].base_low_32, q_ptr->dseg[5].count));
PRINT3(("Data Segment 7: Address %x\tCount %x\n", q_ptr->dseg[6].base_low_32, q_ptr->dseg[6].count));
}
static void
dump_marker(marker_entry * q_ptr)
{
PRINT3(("------------- MARKER ------------- %x\n", q_ptr));
PRINT3(("Entry Count\t:\t%x\n", q_ptr->hdr.entry_cnt));
PRINT3(("Entry Type\t:\t%x\n", q_ptr->hdr.entry_type));
PRINT3(("Flags\t\t:\t%x\n", q_ptr->hdr.flags));
PRINT3(("System Defined\t:\t%x\n", q_ptr->hdr.sys_def_1));
PRINT3(("Target\t\t:\t%x\n", q_ptr->target_id));
PRINT3(("Lun\t\t:\t%x\n", q_ptr->target_lun));
PRINT3(("Modifier\t:\t%x\n", q_ptr->modifier));
}
#endif /* DUMP_IOCB */
#if 0 /* not used right now. avoid compiler
* warning */
static void
dump_request(scsi_request_t * request)
{
int i;
char *ptr;
printf("------------- SCSI REQUEST ------------- %x\n", request);
printf("Controller\t:\t%x\n", request->sr_ctlr);
printf("Target\t\t:\t%x\n", request->sr_target);
printf("Lun\t\t:\t\t%x\n", request->sr_lun);
printf("Tag\t\t:\t%x\n", request->sr_tag);
printf("Command\t\t:\t");
ptr = (char *) request->sr_command;
for (i = 0; i < request->sr_cmdlen; i++)
printf("%x ", *ptr++);
printf("\n");
printf("Command Length\t:\t%x\n", request->sr_cmdlen);
printf("Flags\t\t:\t%x\n", request->sr_flags);
printf("Timeout\t\t:\t%x\n", request->sr_timeout);
printf("Buffer pointer\t:\t%x\n", request->sr_buffer);
printf("Buffer length\t:\t%x\n", request->sr_buflen);
printf("Sense pointer\t:\t%x\n", request->sr_sense);
printf("Sense length\t:\t%x\n", request->sr_senselen);
printf("buf_t pointer\t:\t%x\n", request->sr_bp);
}
#endif /* 0 */
#endif /* QL_DEBUG */
void
qlcommand(struct scsi_request * req)
{
/* zero out the status flags and sensegotten stuff */
/* this fixes the case when we do retries and these fields are */
/* left from the previous IO */
req->sr_status = SC_HARDERR;
req->sr_scsi_status = req->sr_ha_flags = req->sr_sensegotten = 0;
req->sr_spare = NULL;
req->sr_ha = NULL;
PRINT3(("qlcommand: request for slot %x\n", req->sr_ctlr));
ql_entry(req);
}
struct scsi_target_info *
qlinfo(u_char extctlr, u_char targ, u_char lun)
{
struct scsi_request *req;
int retry = 1;
static u_char scsi[6];
char name[9];
struct scsi_target_info *retval;
scsi_target_info_t *info;
u_char board;
board = extctlr;
PRINT3(("qlinfo: extctlr %x, targ %x, lun %x\n", extctlr, targ, lun));
info = &tinfo[board][targ][lun];
if (info->si_inq == NULL)
{
info->si_inq = (u_char *) dmabuf_malloc(SCSI_INQUIRY_LEN);
info->si_sense = (u_char *) dmabuf_malloc(SCSI_SENSE_LEN);
PRINT3(("\ninquiry and sense allocated for board %x, targ %x lun %x inquire %x sense %x\n", board, targ, lun, info->si_inq, info->si_sense));
}
bzero(info->si_inq, SCSI_INQUIRY_LEN);
req = malloc(sizeof(*req));
retryit:
bzero(req, sizeof(*req));
scsi[0] = 0x12; /* Inquiry command */
scsi[1] = scsi[2] = scsi[3] = scsi[5] = 0;
scsi[4] = SCSI_INQUIRY_LEN;
req->sr_ctlr = extctlr;
req->sr_target = targ;
req->sr_lun = lun;
req->sr_command = scsi;
req->sr_cmdlen = 6; /* Inquiry command length */
req->sr_flags = SRF_DIR_IN | SRF_FLUSH | SRF_AEN_ACK;
req->sr_timeout = 10 * HZ;
req->sr_buffer = info->si_inq;
req->sr_buflen = SCSI_INQUIRY_LEN;
req->sr_sense = NULL;
req->sr_dev = 0;
DCACHE_INVAL(info->si_inq, SCSI_INQUIRY_LEN);
qlcommand(req);
if (req->sr_status == SC_GOOD && req->sr_scsi_status == ST_GOOD)
{
PRINT3(("qlinfo: good status on inquiry sr_status %x sr_scsi_status %x\n", req->sr_status, req->sr_scsi_status));
retval = info;
PRINTD(("qlinfo: REGISTER device inquire data %x controller %x target %x\n", info->si_inq, extctlr, targ));
} else
{
PRINT3(("qlinfo failed: sr_status %x, sr_scsi_status %x\n",
req->sr_status, req->sr_scsi_status));
if (retry-- && req->sr_status == SC_BUS_RESET)
{
PRINT2(("qlinfo: retrying because of bus reset\n"));
goto retryit;
}
retval = NULL;
}
free(req);
return retval;
}
int
qlalloc(u_char extctlr, u_char targ, u_char lun, int opt,
void (*cb) ())
{
int adap = extctlr;
int retval = SCSIALLOCOK;
pHA_INFO ha = ha_info[adap];
PRINT3(("qlalloc : controller %x, target %x, lun %x, option %x, callback %x\n", extctlr, targ, lun, opt, cb));
if (qlinfo(extctlr, targ, lun) == NULL)
{
PRINTD(("qlalloc: qlinfo returned null -- FAIL\n"));
return 0;
}
if (adap >= SC_MAXADAP || targ >= QL_MAXTARG || lun >= QL_MAXLUN ||
ha == NULL ||
targ == ha->ql_defaults.Initiator_SCSI_Id)
return ENODEV;
#if 0 /* XXX ref_count never decremented. No need for stand exclusive
* open now either, so ref_count has been removed, and dev_flags
* has just been made a flag for initialized...(jeffs)
*/
if (ha->dev_flags[targ][lun] & DFLG_EXCLUSIVE)
{
retval = EBUSY;
PRINTD(("ha->dev_flags[targ][lun] %x\n", ha->dev_flags[targ][lun]));
goto _allocout;
}
if (opt & SCSIALLOC_EXCLUSIVE)
{
if (ha->ref_count[targ][lun] != 0)
{
retval = EBUSY;
PRINTD(("qlalloc: ref_count is %x\n", ha->ref_count[targ][lun]));
goto _allocout;
} else
ha->dev_flags[targ][lun] |= DFLG_EXCLUSIVE;
}
ha->ref_count[targ][lun]++;
#endif
_allocout:
PRINT3(("qlalloc: extctlr %x, targ %x, lun %x opt %x,callback %x\n", extctlr, targ, lun, opt, cb));
return retval;
}
int
qlreset(u_char adap)
{
pHA_INFO ha = ha_info[adap];
u_short mbox_sts[8];
PRINT1(("qlreset: about to issue a bus reset for adapter %x\n", adap));
if (ha == NULL)
return (0); /* fail */
if (ql_mbox_cmd(ha, mbox_sts, 2, 2,
MBOX_CMD_BUS_RESET, ha->ql_defaults.Delay_after_reset
,0, 0, 0, 0, 0, 0))
{
cmn_err(CE_WARN,
"qlreset - SCSI BUS RESET Command Failed");
return (0); /* fail */
} else
{
PRINTD(("qlreset - SCSI BUS RESET FIRMWARE Command succeeded\n"));
return (1); /* success */
}
}
#ifdef IP30
int
qlinstall(COMPONENT *c,slotinfo_t *slot)
{
bridge_t *bridge;
bridgereg_t dev_reg;
__uint32_t mem_base;
volatile __uint32_t *mem_base_ptr;
int tmp;
#ifndef SWAP_DATA_STREAM
#define DEV_ATTRIBUTES (BRIDGE_DEV_PREF | \
BRIDGE_DEV_COH | \
BRIDGE_DEV_DEV_IO_MEM)
#else
#define DEV_ATTRIBUTES (BRIDGE_DEV_PREF | \
BRIDGE_DEV_SWAP_DIR | \
BRIDGE_DEV_COH | \
BRIDGE_DEV_DEV_IO_MEM)
#endif /* SWAP_DATA_STREAM */
#define NUMBER_OF_RRB 2
#if BASEIO_PCI_BUS_ONLY
{
__psunsigned_t bus_base_phys;
/*
* While our base addresses have been made
* available in the slot structure, they are
* ignored and the rest of the driver uses
* hardwired addresses corresponding to the PCI
* bus on the IP30 motherboard. If someone plugs
* in a PCI bus that has a QL on it (like an IO6
* card), the driver does the wrong thing.
* Until this is made to work, we ignore any QL
* controllers found that are not on the baseio
* PCI bus.
*
* The dependency is due to the use of
* BRIDGE_BASE in qlconfigure.
*/
/* UNKNOWN_BUS_BASE is an arbitrary value
* that we know will never really be a
* physical address for bus_base.
* If we start getting "unable to translate"
* we should extend the cascaded IFs to
* handle whatever virtual address is coming in.
*/
#define UNKNOWN_BUS_BASE 0xDEADBEEF
/*
* PHYS_TO_XTALK_SLOT tries to change a physical
* address into the crosstalk slot number.
* Currently, it knows that physical addresses
* in the second 128MB map 16M each for slots
* eight through fifteen.
* Tune this, of course, as necessary.
*/
#define PHYS_TO_XTALK_SLOT(a) ((((a)>>27)==3) ? (((a)>>24)&0xF) : -1)
/* XXX can we simplify this code on IP30? */
if (IS_COMPAT_PHYS(slot->bus_base))
bus_base_phys = COMPAT_TO_PHYS(slot->bus_base);
#ifdef IS_XKPHYS
else if (IS_XKPHYS(slot->bus_base))
bus_base_phys = XKPHYS_TO_PHYS(slot->bus_base);
#endif
else if (IS_KSEG0(slot->bus_base))
bus_base_phys = K0_TO_PHYS(slot->bus_base);
else if (IS_KSEG1(slot->bus_base))
bus_base_phys = K1_TO_PHYS(slot->bus_base);
else if (IS_KSEG2(slot->bus_base))
bus_base_phys = kvtophys(slot->bus_base);
else if (IS_KPHYS(slot->bus_base))
bus_base_phys = (__psunsigned_t)(slot->bus_base);
else
bus_base_phys = UNKNOWN_BUS_BASE;
if (bus_base_phys != BRIDGE_BASE) {
if (bus_base_phys == UNKNOWN_BUS_BASE)
printf("ql_install WARNING:"
" unable to support QL in slot %d of PCI Bus"
" at KVA 0x%X\n",
slot->bus_slot, slot->bus_base);
#ifdef PHYS_TO_XTALK_SLOT
else if (PHYS_TO_XTALK_SLOT(bus_base_phys) != -1)
printf("ql_install WARNING:"
" unable to support QL in slot %d of PCI Bus"
" in XTalk port %X\n",
slot->bus_slot, PHYS_TO_XTALK_SLOT(bus_base_phys));
#endif
else
printf("ql_install WARNING:"
" unable to support QL in slot %d of PCI Bus"
" at PHYS 0x%X\n",
slot->bus_slot, bus_base_phys);
return 1;
}
}
#endif
PRINT1(("ql_install: QL SCSI controller found in PCI slot %d\n",
slot->bus_slot));
PRINT2(("DEV_ATTRIBUTES are %x\n",DEV_ATTRIBUTES));
/* use the mem address set up by init_bridge_pci() earlier */
mem_base_ptr = (volatile __uint32_t *) slot->cfg_base +
PCI_CFG_BASE_ADDR_1 / sizeof(__uint32_t);
mem_base = (*mem_base_ptr & ~0xfffff) >> 20;
bridge = (bridge_t *) slot->bus_base;
dev_reg = bridge->b_device[slot->bus_slot].reg & ~BRIDGE_DEV_OFF_MASK;
dev_reg |= mem_base | DEV_ATTRIBUTES;
bridge->b_device[slot->bus_slot].reg = dev_reg;
tmp = alloc_bridge_rrb((void *) bridge, slot->bus_slot, 0, NUMBER_OF_RRB);
if (tmp != NUMBER_OF_RRB) {
((tmp == 0) ? panic : (void(*)(char *,...))printf)
("Cannot allocate %d RRBs for ql %d\n",NUMBER_OF_RRB,
slot->bus_slot);
}
stand_qlhinv(c,slot);
return 0;
}
static slotdrvr_id_t qlids[] = {
{BUS_PCI, QLogic_VENDOR_ID, QLogic_DEVICE_ID, REV_ANY},
{BUS_NONE, 0, 0, 0}
};
drvrinfo_t ql_drvrinfo = { qlids, "ql" };
#endif /* IP30 */
/* if we get to this routine then the Adaptor has been probed succesfully */
/* first register the adaptor then go walk that bus looking for devices */
static COMPONENT scsiadaptmpl = {
AdapterClass, /* Class */
SCSIAdapter, /* Type */
0, /* Flags */
SGI_ARCS_VERS, /* Version */
SGI_ARCS_REV, /* Revision */
0, /* Key */
0x01, /* Affinity */
0, /* ConfigurationDataSize */
10, /* IdentifierLength */
"QLISP1040" /* Identifier */
};
static COMPONENT scsitmpl, scsictrltmpl;
#ifdef IP30
static void
stand_qlhinv(COMPONENT *c, slotinfo_t *slot)
{
static int adap; /* incremented every call to this routine */
PRINT2(("stand_qlhinv: registering host adaptor %x\n", adap));
c = AddChild(c, &scsiadaptmpl, (void *) NULL);
if (c == (COMPONENT *) NULL)
cpu_acpanic("scsi adapter");
c->Key = adap;
/* fill in unchanged parts of templates */
bzero(&scsictrltmpl, sizeof(COMPONENT));
scsictrltmpl.Class = ControllerClass;
scsictrltmpl.Version = SGI_ARCS_VERS;
scsictrltmpl.Revision = SGI_ARCS_REV;
scsitmpl.Class = PeripheralClass;
scsitmpl.Version = SGI_ARCS_VERS;
scsitmpl.Revision = SGI_ARCS_REV;
scsitmpl.AffinityMask = 0x01;
PRINT2(("calling bus walk for adapter %x\n", c->Key));
qlconfigure(c,slot);
adap++;
}
#endif
#ifdef SN0 /* for MSCSI bringup only */
static void
mscsi_debug(int adap, __psunsigned_t ql_register_base)
{
short *sp ;
short stmp ;
sp = (short *)ql_register_base ;
printf("A = %d, HA = %x, \n", adap, sp) ;
us_delay(10000) ;
*(sp + 0xc2/2) = 0x2000 ; /* pause risc */
printf("C2 %x ", *(sp + 0xc2/2)) ;
us_delay(10000) ;
*(sp + 0x4/2) = 0x4c ; /* select SXP */
printf("4 %x ", *(sp + 0x4/2)) ;
us_delay(10000) ;
stmp = *(sp + 0xf4/2) ;
printf("\nDiff Ctrl Reg = %x, ", stmp) ;
us_delay(10000) ;
stmp = *(sp + 0xB4/2) ;
printf("GPIO Enable = %x, ", stmp) ;
us_delay(10000) ;
stmp = *(sp + 0xB6/2) ;
printf("GPIO Data = %x\n", stmp) ;
us_delay(10000) ;
*(sp + 0x4/2) = 0x44 ; /* select SXP */
us_delay(10000) ;
*(sp + 0xc2/2) = 0 ; /* pause risc */
us_delay(10000) ;
}
#endif /* for MSCSI bringup only */
void
#if defined(IP30)
qlconfigure(COMPONENT * c,slotinfo_t *slot)
#elif defined(SN0)
#ifdef SN_PDI
qlconfigure(pd_inst_hdl_t *pdih, pd_inst_parm_t *pdip)
#else /* SN_PDI */
qlconfigure(COMPONENT * c, char *hwgpath, char *nicstr)
#endif /* SN_PDI */
#else
qlconfigure(COMPONENT * c)
#endif
{
#ifdef SN_PDI
int adap ;
char *hwgpath ;
char *nicstr = NULL ;
#else
int adap = (int) (c->Key & 0xff);
#endif
int num_disks = 0;
int type;
int max_targets= QL_MAXTARG;
char *extra, *inv;
union scsicfg cfgd;
char id[26]; /* 8 + ' ' + 16 + null */
u_char targ, lu;
COMPONENT *tmp, *tmp2;
pHA_INFO ha;
pPCI_REG isp_config;
scsi_target_info_t *info;
__psunsigned_t ql_config_base, /* PCI Config space for this device */
ql_register_base; /* PIO space for qlogic registers */
#ifdef SN0
int i, rc;
lboard_t *brd_ptr;
klinfo_t *comp_ptr = NULL;
ULONG Key ;
#ifdef SN_PDI
hwgpath = pdipHwg(pdip) ;
nicstr = get_nicstr_from_devinfo(pdihPdi(pdih)->pdi_lb) ;
#endif
#ifndef SN_PDI
Key = c->Key ;
#else
Key = pdipDmaParm(pdip) ;
#endif
if (brd_ptr = brd_from_key(Key))
comp_ptr = find_first_component(brd_ptr, KLSTRUCT_SCSI);
/* Run SCSI diags before configure devices */
rc = diag_scsi_mem(diag_get_mode(), GET_WIDBASE_FROM_KEY(Key),
GET_PCIID_FROM_KEY(Key));
/*
* This complex bitshifting is here to record failures for as many pci
* ids as possible. Currently NUM_SCSI_FAIL_CODES is 3 and diagval is
* 16 bits. The 16 bits are used as follows:
* Bits 0-2 contain diag results for pci id 0
* - bit 0 is result of mem test
* - bit 1 is result of dma test
* - bit 2 is result of self test
* Bits 3-5 contain diag results for pci id 1 in the same fashion
* Bits 6-8 contain diag results for pci id 2
* Bits 9-11 contain diag results for pci id 3
* Bits 12-14 contain diag results for pci id 4
* Anything with a pci id > 4 is discarded.
* This is fine for now because we have a max of 4 pci ids on any
* board (MSCSI board has 4). If the size of the diagval field or the
* number of SCSI diagnostic tests changes, the recorded results will
* change accordingly.
*/
if (comp_ptr && rc)
if (GET_PCIID_FROM_KEY(Key) < (sizeof(comp_ptr->diagval) * NBBY /
NUM_SCSI_FAIL_CODES))
comp_ptr->diagval |= SCSI_MEM_FAIL_MASK <<
(GET_PCIID_FROM_KEY(Key) * NUM_SCSI_FAIL_CODES);
if (brd_ptr && (rc != KLDIAG_PASSED))
printf("diag_scsi_mem: /hw/module/%d/slot/io%d/ql: FAILED\n",
brd_ptr->brd_module, SLOTNUM_GETSLOT(brd_ptr->brd_slot));
rc = diag_scsi_dma(diag_get_mode(), GET_WIDBASE_FROM_KEY(Key),
GET_PCIID_FROM_KEY(Key));
if (comp_ptr && rc)
if (GET_PCIID_FROM_KEY(Key) < (sizeof(comp_ptr->diagval) * NBBY /
NUM_SCSI_FAIL_CODES))
comp_ptr->diagval |= SCSI_DMA_FAIL_MASK <<
(GET_PCIID_FROM_KEY(Key) * NUM_SCSI_FAIL_CODES);
if (brd_ptr && (rc != KLDIAG_PASSED))
printf("diag_scsi_dma: /hw/module/%d/slot/io%d/ql: FAILED\n",
brd_ptr->brd_module, SLOTNUM_GETSLOT(brd_ptr->brd_slot));
rc = diag_scsi_selftest(diag_get_mode(), GET_WIDBASE_FROM_KEY(Key),
GET_PCIID_FROM_KEY(Key));
if (comp_ptr && rc)
if (GET_PCIID_FROM_KEY(Key) < (sizeof(comp_ptr->diagval) * NBBY /
NUM_SCSI_FAIL_CODES))
comp_ptr->diagval |= SCSI_STEST_FAIL_MASK <<
(GET_PCIID_FROM_KEY(Key) * NUM_SCSI_FAIL_CODES);
if (brd_ptr && (rc != KLDIAG_PASSED))
printf("diag_scsi_selftest: /hw/module/%d/slot/io%d/ql: FAILED\n",
brd_ptr->brd_module, SLOTNUM_GETSLOT(brd_ptr->brd_slot));
#ifndef SN_PDI
/* Get the qlogic base address from the c->Key field */
ql_config_base = GET_PCICFGBASE_FROM_KEY(c->Key);
ql_register_base = GET_PCIBASE_FROM_KEY(c->Key);
/* store the TARGET_ID field required for DMA in ha_sn0_dma */
ha_sn0_info[adap] = c->Key;
#else
adap = pdipCtrlId(pdip) ;
ql_config_base = pdipCfgBase(pdip) ;
ql_register_base = pdipMemBase(pdip) ;
ha_sn0_info[adap] = pdipDmaParm(pdip) ;
#endif /* SN_PDI */
PRINTD(("QL will be initted with \
config = %x, registers = %x, hubwid = %d, hstnasid = %d\n",
ql_config_base, ql_register_base,
GET_HUBWID_FROM_KEY(ha_sn0_info[adap]),
GET_HSTNASID_FROM_KEY(ha_sn0_info[adap])));
scsicnt = SCSI_MAXCTLR;
#endif
#if IP30
ql_config_base = (__psunsigned_t)slot->cfg_base;
ql_register_base = (__psunsigned_t)slot->mem_base;
PRINTD(("slot %x\tql_config_base %x\tql_register_base %x\n",slot->bus_slot, ql_config_base, ql_register_base));
if (ql_init(adap, (caddr_t)ql_config_base, (paddr_t)ql_register_base,
slot->bus_base))
PRINTD(("Initialization at slot %x failed\n", 0x0));
#endif /* IP30 */
#ifdef SN0
#if 0 /* Use this when u need to debug mscsi boards */
mscsi_debug(adap, ql_register_base) ;
#endif
if (ql_init(adap, (caddr_t) ql_config_base, ql_register_base,0))
{
PRINTD(("SN0: QL init at slot %d failed\n", adap));
return;
}
#endif /* SN0 */
/* now walk the bus and register the devices found */
ha = ha_info[adap];
if (ha == NULL)
{
cmn_err(CE_WARN, "qlconfigure: failed to configure adapter %x\n", adap);
return;
}
#ifdef SN0
if (SN00) {
/*REFERENCED*/
u_short gpio_val;
int pci_slot;
pISP_REGS isp = (pISP_REGS) ha->ha_base;
gpio_val = PCI_INH(&isp->b4);
if (gpio_val & 0x000c) {
pci_slot = GET_PCIID_FROM_KEY(ha_sn0_info[adap]) ;
if (pci_slot == 1) {
ha->flags |= AFLG_FAST_NARROW;
max_targets = 8;
ql_set_defaults(ha);
}
}
PCI_OUTH(&isp->b6,ENABLE_WRITE_SN00);
PCI_OUTH(&isp->b4, 0);
DELAY(100000);
}
#endif
/* Lego doesn't use slotinfo_t so just go read the rev_id from config space */
isp_config = (pPCI_REG) ha->ha_config_base;
ha->revision = PCI_INB(&isp_config->Rev_Id);
if (!qlreset(adap))
{
cmn_err(CE_WARN, "qlconfigure: Unable to reset bus %d\n", adap);
} else
{
DELAY(1000);
}
#if IP30 && VERBOSE
printf("\nWalking SCSI Adapter %d\n", adap);
#endif
#ifdef SN0
#if 0
if (nicstr)
printf("%s\n", nicstr) ;
#endif
if ((adap == 0) || (adap == 1))
printf("\nWalking SCSI Adapter %d (%s), (pci id %d)\n",
adap, hwgpath, GET_PCIID_FROM_KEY(Key));
else
printf("\nWalking SCSI Adapter %s (pci id %d)\n",
hwgpath, GET_PCIID_FROM_KEY(Key));
#endif
if (ha->ql_defaults.Initiator_SCSI_Id != INITIATOR_SCSI_ID)
printf("Non-default scsihostid is %d\n",
ha->ql_defaults.Initiator_SCSI_Id);
#ifdef IP30 /* only three targets (1,2,3) on internal controller */
/* for speedracer */
if( adap == 0) max_targets = 4;
#endif
for (lu = targ = 0; targ < max_targets; targ++)
{
if (ha->ql_defaults.Initiator_SCSI_Id == targ)
continue;
#if SN0 || VERBOSE
printf("%d", targ);
#endif
if (!(inv = (char *) scsi_inq(adap, targ, lu)) ||
(inv[0] & 0x10) == 0x10)
{
#if SN0 || VERBOSE
printf("- ");
#endif
continue; /* LUN not supported, or not attached */
} else
PRINT2(("found dksc(%d,%d)\n", adap, targ));
#if SN0 || VERBOSE
printf("+ ");
num_disks++;
#endif
/* do not keep data for all scsi targets around as they are
* not likely to be used in standalone. If the drive is used
* again we will reallocate the space.
*/
info = &tinfo[adap][targ][lu];
if (info->si_inq != NULL) {
dmabuf_free(info->si_inq);
info->si_inq = NULL;
}
if (info->si_sense != NULL) {
dmabuf_free(info->si_sense);
info->si_sense = NULL;
}
/* ARCS Identifier is "Vendor Product" */
strncpy(id, inv + 8, 8);
id[8] = '\0';
for (extra = id + strlen(id) - 1; *extra == ' '; *extra-- = '\0');
strcat(id, " ");
strncat(id, inv + 16, 16);
id[25] = '\0';
for (extra = id + strlen(id) - 1; *extra == ' '; *extra-- = '\0');
scsictrltmpl.IdentifierLength = strlen(id);
scsictrltmpl.Identifier = id;
if ((inv[0] == 0) &&
((!strncmp(id, "TOSHIBA CD", 10)) ||
(!strncmp(id, "SONY CD", 10)) ||
(!strncmp(id, "LMS CD", 10)))) {
cdrom_revert(adap, targ, 0) ;
if (!(inv = (char *) scsi_inq(adap, targ, lu)) ||
(inv[0] & 0x10) == 0x10) {
PRINTD(("second inq on cd failed\n")) ;
}
#if 0 /* Debug */
for (i=0;i<25;i++)
printf("0x%x ", inv[i]) ;
printf("\n") ;
#endif
/* THis is a copy of the same lines above.
Got to change this to a function call. */
/* ARCS Identifier is "Vendor Product" */
strncpy(id, inv + 8, 8);
id[8] = '\0';
for (extra = id + strlen(id) - 1; *extra == ' '; *extra-- = '\0');
strcat(id, " ");
strncat(id, inv + 16, 16);
id[25] = '\0';
for (extra = id + strlen(id) - 1; *extra == ' '; *extra-- = '\0');
scsictrltmpl.IdentifierLength = strlen(id);
scsictrltmpl.Identifier = id;
}
switch (inv[0])
{
case 0: /* hard disk */
extra = "disk";
scsictrltmpl.Type = DiskController;
scsictrltmpl.Key = targ;
#ifndef SN0
tmp = AddChild(c, &scsictrltmpl, (void *) NULL);
PRINT3(("ADDED CHILD scsictrltmpl\n"));
if (tmp == (COMPONENT *) NULL)
cpu_acpanic("disk ctrl");
#endif
if (inv[1] & 0x80)
{
scsitmpl.Type = FloppyDiskPeripheral;
scsitmpl.Flags = Removable | Input | Output;
scsitmpl.ConfigurationDataSize =
sizeof(struct floppy_data);
bzero(&cfgd.fd, sizeof(struct floppy_data));
cfgd.fd.Version = SGI_ARCS_VERS;
cfgd.fd.Revision = SGI_ARCS_REV;
cfgd.fd.Type = FLOPPY_TYPE;
} else
{
scsitmpl.Type = DiskPeripheral;
scsitmpl.Flags = Input | Output;
scsitmpl.ConfigurationDataSize =
sizeof(struct scsidisk_data);
bzero(&cfgd.disk, sizeof(struct scsidisk_data));
scsi_rc(adap, targ, lu, &cfgd.disk);
cfgd.disk.Version = SGI_ARCS_VERS;
cfgd.disk.Revision = SGI_ARCS_REV;
cfgd.disk.Type = SCSIDISK_TYPE;
}
scsitmpl.Key = lu;
#ifndef SN0
tmp = AddChild(tmp, &scsitmpl, (void *) &cfgd);
PRINT3(("ADDED CHILD scsitmpl\n"));
if (tmp == (COMPONENT *) NULL)
cpu_acpanic("scsi disk");
RegisterDriverStrategy(tmp, dksc_strat);
#endif
break;
case 1: /* sequential == tape */
type = tpsctapetype(inv, 0);
extra = inv;
sprintf(inv, " type %d", type);
scsictrltmpl.Type = TapeController;
scsictrltmpl.Key = targ;
#ifndef SN0
tmp = AddChild(c, &scsictrltmpl, (void *) NULL);
if (tmp == (COMPONENT *) NULL)
cpu_acpanic("tape ctrl");
#endif
scsitmpl.Type = TapePeripheral;
scsitmpl.Flags = Removable | Input | Output;
scsitmpl.ConfigurationDataSize = 0;
scsitmpl.Key = lu;
#ifndef SN0
tmp = AddChild(tmp, &scsitmpl, (void *) NULL);
if (tmp == (COMPONENT *) NULL)
cpu_acpanic("scsi tape");
RegisterDriverStrategy(tmp, tpsc_strat);
#endif
break;
default:
type = inv[1] & INV_REMOVE;
/* ANSI level: SCSI 1, 2, etc. */
type |= inv[2] & INV_SCSI_MASK;
if ((inv[0] & 0x1f) == INV_CDROM)
{
scsictrltmpl.Type = CDROMController;
scsitmpl.Type = DiskPeripheral;
scsitmpl.Flags = ReadOnly | Removable | Input;
} else
{
scsictrltmpl.Type = OtherController;
scsitmpl.Type = OtherPeripheral;
scsitmpl.Flags = 0;
}
scsitmpl.ConfigurationDataSize = 0;
scsictrltmpl.Key = targ;
#ifndef SN0
tmp = AddChild(c, &scsictrltmpl, (void *) NULL);
if (tmp == (COMPONENT *) NULL)
cpu_acpanic("other ctrl");
#endif
scsitmpl.Key = lu;
#ifndef SN0
tmp2 = AddChild(tmp, &scsitmpl, (void *) NULL);
if (tmp2 == (COMPONENT *) NULL)
cpu_acpanic("scsi device");
if (tmp->Type == CDROMController)
RegisterDriverStrategy(tmp2, dksc_strat);
#endif
extra = "";
break;
}
#ifdef SN0 /* && SN0_USE_HWGRAPH */
{
#ifdef SN_PDI
COMPONENT *c ;
c = &pdipDevC(pdip) ;
#endif /* SN_PDI */
/* fill c->* with all relevant info, call kl_AddChild */
c->IdentifierLength = scsictrltmpl.IdentifierLength;
c->Identifier = scsictrltmpl.Identifier;
if (scsictrltmpl.Type == CDROMController)
c->Type = CDROMController ; /* XXX define CDROMperipheral ? */
else
c->Type = scsitmpl.Type;
c->Flags = scsitmpl.Flags;
c->Key = (adap << 8) | scsictrltmpl.Key;
c->ConfigurationDataSize = scsitmpl.ConfigurationDataSize;
#ifdef SN_PDI
pdipDevUnit(pdip) = scsictrltmpl.Key ;
pdipDevLun(pdip) = 0 ;
snAddChild(pdih, pdip) ; /* XXX check cfgd */
#else
kl_qlogic_AddChild(c, &cfgd);
#endif
}
#endif
PRINT3(("LEAVING qlinstall\n"));
}
#if SN0 || VERBOSE
printf("= %d device(s)\n\n", num_disks);
#endif
}
/*
* This function is used to select between "short" and "long"
* selection timeouts. QL has only a limited number of selection
* timeouts to choose from. The next shorter value below 250 mS is 100
* uS and some devices have problems with such a short value. Hence
* this function is essentially a NOP and is provided only to avoid
* link errors since scsi.c makes calls to it.
*/
/* ARGSUSED */
void
scsi_seltimeout(u_char adap, int makeshort)
{
return;
}
static int
scsi_rc(u_char adap, u_char targ, u_char lun, struct scsidisk_data * d)
{
unsigned int *dc;
u_char read_capacity[10];
PRINT3(("scsi_rc: read capacity for target %x on adapter %x\n", targ, adap));
bzero(read_capacity, 10);
read_capacity[0] = 0x25;
dc = dmabuf_malloc(sizeof(unsigned int) * 2);
scsi_ecmd(adap, targ, lun, read_capacity, 10, dc, 8);
#ifdef _MIPSEL
swapl(dc, 2);
#endif
d->MaxBlocks = dc[0];
d->BlockSize = dc[1];
dmabuf_free(dc);
return (0);
}
/* scsi early command */
static caddr_t
scsi_ecmd(u_char adap, u_char targ, u_char lun,
u_char * inquiry, int il,
void *data, int dl)
{
register scsisubchan_t *sp;
int first = 2;
buf_t *bp;
caddr_t ret;
PRINT3(("scsi_ecmd: adap %x, targ %x, lun %x, inquiry* %x, il %x, data %x, dl %x\n", adap, targ, lun, inquiry, il, data, dl));
if ((sp = allocsubchannel(adap, targ, lun)) == NULL ||
(bp = (buf_t *) malloc(sizeof(*bp))) == NULL)
{
PRINTD(("bailing on scsi_ecmd because allocsubchannel was null, sp %x - bp %x\n", sp, bp));
return NULL;
}
PRINT1(("scsi_ecmd: subchannel block is at %x\n", sp));
sp->s_notify = NULL;
sp->s_cmd = inquiry;
sp->s_len = il;
sp->s_buf = bp;
/* SCSI 2 spec strongly recommends all devices be able to respond
* successfully to inq within 2 secs of power on. some ST4 1200N (1.2)
* drives occasionally accept the cmd and disc, but don't come back
* within 1 second, even when motor start jumper is on, at powerup.
* Only seen on IP12, probably because it gets to the diag faster.
*/
sp->s_timeoutval = 2 * HZ;
again:
/* So we don't keep data left from an earlier call. Also insures null
* termination. */
bzero(data, dl);
bp->b_dmaaddr = (caddr_t) data;
bp->b_bcount = dl;
bp->b_flags = B_READ | B_BUSY;
PRINTD(("calling doscsi\n"));
doscsi(sp);
PRINTD(("back from doscsi\n"));
if (sp->s_error == SC_GOOD && sp->s_status == ST_GOOD)
{
PRINT3(("scsi_ecmd: returning good status\n"));
ret = (caddr_t) data;
} else if (first-- && sp->s_error != SC_TIMEOUT)
{
/* CMDTIME can also indicate a bus reset; could be bus problems, *
* or sync vs async wd problem; at least it responded to selection,
* so retry it. */
if (sp->s_error == SC_HARDERR || sp->s_error == SC_CMDTIME || sp->s_error == SC_BUS_RESET)
{
PRINTD(("RETRY\n"));
goto again; /* one retry, if it not a timeout. */
}
} else
{
ret = NULL;
}
sc_inq_stat = sp->s_error; /* mainly for standalone ide */
freesubchannel(sp);
free((caddr_t) bp);
return (ret);
}
/*
* Map `len' bytes from starting address `addr' for a SCSI DMA op.
* Returns the actual number of bytes mapped.
*/
int
make_sg(scatter * scatter, u_char * addr, int len, pHA_INFO ha)
{
unsigned incr, bytes, dotrans, npages, partial, index;
pde_t *pde;
PRINT3(("make_sg: *scatter %x addr %x len %x\n", scatter, addr, len));
/* can only deal with word aligned addresses */
if ((ulong) addr & 0x3)
{
PRINTD(("make_sg: not word alligned\n"));
return (0);
}
if (bytes = (int) poff(addr))
{
if ((bytes = (NBPC - bytes)) > len)
{
bytes = len;
npages = 1;
PRINT3(("make_sg: bytes = poff(addr)\n"));
} else
npages = (int) btoc(len - bytes) + 1;
} else
{
bytes = 0;
npages = (int) btoc(len);
PRINT3(("make_sg: not bytes = poff(addr)\n"));
}
if (bytes == len)
{
partial = bytes; /* only one descr, and that is part of a page */
PRINT3(("partial in one page\n"));
} else
{
partial = (len - bytes) % NBPC; /* partial page in last descr? */
PRINT3(("partial in the last page \n"));
}
/* We either have KSEG2 or KSEG0/1/Phys, figure out now */
{
/* We either have KSEG0/1 or Physical */
if (IS_KSEGDM(addr))
{
addr = (u_char *) KDM_TO_PHYS(addr);
}
dotrans = 0;
}
/* put the number of pages needed into the structure */
scatter->num_pages = npages;
PRINT3(("number of pages %x\n", npages));
/* Generate SCSI dma descriptors for this request */
index = 0;
if (bytes)
{
scatter->dseg[index].base =
#if IP30
MAKE_DIRECT_MAPPED_2GIG(kvtophys(addr));
#elif SN0
#ifdef SN0_PCI_64
get_pci64_dma_addr((__psunsigned_t) addr,
ha_sn0_info[ha->controller_number]);
#else
MAKE_DIRECT_MAPPED_2GIG((int) kvtophys(addr));
#endif /* SN0_PCI_64 */
#else
KDM_TO_PHYS(addr);
#endif
scatter->dseg[index].count = bytes;
PRINT3(("index %x base %x count %x\n", index, scatter->dseg[index].base, scatter->dseg[index].count));
addr += bytes;
npages--;
++index;
}
while (npages--)
{
scatter->dseg[index].base = dotrans ? (int) pdetophys(pde++) :
#if IP30
MAKE_DIRECT_MAPPED_2GIG(kvtophys(addr));
#elif SN0
#ifdef SN0_PCI_64
get_pci64_dma_addr((__psunsigned_t) addr,
ha_sn0_info[ha->controller_number]);
#else
MAKE_DIRECT_MAPPED_2GIG((int) kvtophys(addr));
#endif /* SN0_PCI_64 */
#else
KDM_TO_PHYS(addr);
#endif
incr = (!npages && partial) ? partial : NBPC;
addr += incr;
scatter->dseg[index].count = incr;
PRINT3(("index %x base %x count %x\n", index, scatter->dseg[index].base, scatter->dseg[index].count));
++index;
}
return len;
}
void
munge(u_char * pointer, int count)
{
/* munge 4 bytes at a time */
int i;
u_char *cpointer, *copyptr;
int temp;
if (count % 4)
{
PRINT2(("munge: dont do non word aligned stuff\n"));
PRINT2(("munge: but I'll do it anyway %x\n", pointer));
}
cpointer = copyptr = pointer;
PRINT4(("munge: %x bytes from address %x : ", count, cpointer));
for (i = 0; i < 16; i++)
PRINT4(("%x ", *cpointer++));
PRINT4(("\n"));
cpointer = pointer;
copyptr = pointer;
for (i = 0; i < count / 4; i++)
{
temp = *((int *) copyptr);
cpointer = (u_char *) & temp;
*(copyptr + 3) = *(cpointer + 0);
*(copyptr + 2) = *(cpointer + 1);
*(copyptr + 1) = *(cpointer + 2);
*(copyptr + 0) = *(cpointer + 3);
copyptr = copyptr + 4;
}
cpointer = pointer;
PRINT4(("munge: %x bytes from address %x : ", count, cpointer));
for (i = 0; i < 16; i++)
PRINT4(("%x ", *cpointer++));
PRINT4(("\n"));
}
/*
** Function: ql_set_defaults
**
** Description: Sets the default parameters.
*/
static void
ql_set_defaults(pHA_INFO ha)
{
Default_parameters *n_ptr = &ha->ql_defaults;
int index;
/* * Use the defaults! */
n_ptr->Fifo_Threshold = BURST_128;
n_ptr->HA_Enable = HA_ENABLE;
n_ptr->Initiator_SCSI_Id = INITIATOR_SCSI_ID;
#ifndef EVEREST
{ char *cp;
#ifdef SN0
if ((cp = getenv("scsihostid")) != NULL) {
int scsi_id = INITIATOR_SCSI_ID ;
if (isxdigit(*cp)) {
if (isdigit(*cp))
scsi_id = *cp - '0' ;
else
scsi_id = 10 + tolower(*cp) - 'a' ;
}
n_ptr->Initiator_SCSI_Id = scsi_id ;
}
#else
if ((cp = getenv("scsihostid")) != NULL)
n_ptr->Initiator_SCSI_Id = atoi(cp);
#endif
if (n_ptr->Initiator_SCSI_Id > 7)
n_ptr->Initiator_SCSI_Id = INITIATOR_SCSI_ID;
}
#endif
n_ptr->Bus_Reset_Delay = BUS_RESET_DELAY;
#ifdef SN0
n_ptr->Retry_Count = 0;
/* this was tried in the kernel and walking the scsi bus is pretty fast. */
#else
n_ptr->Retry_Count = RETRY_COUNT;
#endif
n_ptr->Retry_Delay = RETRY_DELAY;
n_ptr->Capability.bits.ASync_Data_Setup_Time =
ASYNC_DATA_SETUP_TIME;
n_ptr->Capability.bits.REQ_ACK_Active_Negation =
REQ_ACK_ACTIVE_NEGATION;
n_ptr->Capability.bits.DATA_Line_Active_Negation =
DATA_ACTIVE_NEGATION;
n_ptr->Capability.bits.Data_DMA_Burst_Enable =
DATA_DMA_BURST_ENABLE;
n_ptr->Capability.bits.Command_DMA_Burst_Enable =
CMD_DMA_BURST_ENABLE;
n_ptr->Tag_Age_Limit = TAG_AGE_LIMIT;
n_ptr->Selection_Timeout = SELECTION_TIMEOUT;
n_ptr->Max_Queue_Depth = MAX_QUEUE_DEPTH;
n_ptr->Delay_after_reset = DELAY_AFTER_RESET;
for (index = 0; index < QL_MAXTARG; index++)
{
n_ptr->Id[index].Capability.bits.Renegotiate_on_Error =
RENEGOTIATE_ON_ERROR;
n_ptr->Id[index].Capability.bits.Stop_Queue_on_Check =
STOP_QUEUE_ON_CHECK;
n_ptr->Id[index].Capability.bits.Auto_Request_Sense =
AUTO_REQUEST_SENSE;
#ifdef TAGGED_CMDS
n_ptr->Id[index].Capability.bits.Tagged_Queuing =
TAGGED_QUEUING;
#endif
n_ptr->Id[index].Capability.bits.Sync_Data_Transfers =
SYNC_DATA_TRANSFERS;
n_ptr->Id[index].Capability.bits.Parity_Checking =
PARITY_CHECKING;
n_ptr->Id[index].Capability.bits.Disconnect_Allowed =
DISCONNECT_ALLOWED;
if (ha->flags & AFLG_FAST_NARROW) {
n_ptr->Id[index].Sync_Period = SYNC_PERIOD;
} else {
n_ptr->Id[index].Capability.bits.Wide_Data_Transfers =
WIDE_DATA_TRANSFERS;
n_ptr->Id[index].Sync_Period = SYNC_PERIOD_FAST20;
}
n_ptr->Id[index].Throttle = EXECUTION_THROTTLE;
n_ptr->Id[index].Sync_Offset = SYNC_OFFSET;
#if 0
/* * If sync or wide transfers, make sure to renegotiate on errors. */
if (n_ptr->Id[index].Sync_Data_Transfers ||
n_ptr->Id[index].Wide_Data_Transfers)
{
/* * Override the default. */
n_ptr->Id[index].Capability.bits.Renegotiate_on_Error = 1;
}
#endif
}
PRINTD(("FAST/FAST20 period set to %d offset %x\n", n_ptr->Id[index - 1].Sync_Period, n_ptr->Id[index - 1].Sync_Offset));
}
/* this is the command entry point for the standalone driver */
/* make a scsi_request structure from a scsisubchan sturcutre */
/* this is in hopes of using the same code for the kernel driver */
/* and the standalone driver */
void
doscsi(scsisubchan_t * scsisubchan)
{
static scsi_request_t s_request;
scsi_request_t *scsi_request;
scsi_request = &s_request;
PRINT3(("doscsi: scsisubchan is at %x, scsi_request is at %x\n", scsisubchan, scsi_request));
bzero(scsi_request, sizeof(scsi_request_t));
scsi_request->sr_ctlr = scsisubchan->s_adap;;
scsi_request->sr_target = scsisubchan->s_target;
scsi_request->sr_lun = scsisubchan->s_lu;
scsi_request->sr_command = scsisubchan->s_cmd;
scsi_request->sr_cmdlen = scsisubchan->s_len;
/* flags are weird. in standalone they are in the buf stucture */
/* need to preserve the B_BUSY flag and pass on the B_READ */
/* which becomes SRF_DIR_IN */
/* remember to not kill the B_BUSY on the way back */
scsi_request->sr_flags = (scsisubchan->s_buf->b_flags) & SRF_DIR_IN;
scsi_request->sr_timeout = scsisubchan->s_timeoutval;
scsi_request->sr_buffer = (u_char *) scsisubchan->s_buf->b_dmaaddr;
scsi_request->sr_buflen = scsisubchan->s_buf->b_bcount;
/* too many places don't flush, so in standalone, do the flush here. */
if (scsisubchan->s_buf->b_bcount) {
if (scsisubchan->s_buf->b_flags & B_READ)
DCACHE_INVAL(scsisubchan->s_buf->b_dmaaddr,
scsisubchan->s_buf->b_bcount);
else
MRW_DCACHE_WB_INVAL(scsisubchan->s_buf->b_dmaaddr,
scsisubchan->s_buf->b_bcount);
}
ql_entry(scsi_request);
scsisubchan->s_error = scsi_request->sr_status;
scsisubchan->s_status = scsi_request->sr_scsi_status;
PRINT3(("s_error is %x and s_status is %x\n", scsisubchan->s_error, scsisubchan->s_status));
if (scsisubchan->s_notify)
(*scsisubchan->s_notify) (scsisubchan);
}
scsisubchan_t *subchan[SCSI_MAXCTLR][QL_MAXTARG];
scsisubchan_t *
allocsubchannel(int adap, int target, int lu)
{
scsisubchan_t *sp;
pHA_INFO ha = ha_info[adap];
PRINT3(("inside allocsubchannel, adap %x target %x lun %x\n", adap, target, lu));
/* abort allocating subchannel if no ha configured for this adaptor */
if (!ha)
{
PRINTD(("cannot allocate subchannel for target %x, no adaptor %x\n", target, adap));
return NULL;
}
if (adap >= SCSI_MAXCTLR || target >= QL_MAXTARG || lu != 0)
{
PRINTD(("failed to allocate subchannel\nadap >= SCSI_MAXCTLR || target >= QL_MAXTARG || lu != 0\n"));
return NULL;
}
if (subchan[adap][target] != NULL)
{
PRINTD(("failed to allocate subchannel\nsubchan[adap][target] != NULL\n"));
return NULL;
}
if (qlalloc(adap, target, lu, 1, NULL) == 0)
{
PRINTD(("failed to allocate subchannel\nqlalloc(adap, target, lu, 1, NULL) == 0\n"));
return NULL;
}
subchan[adap][target] = malloc(sizeof(scsisubchan_t));
PRINT3(("allocated subachan address is %x\n", &subchan[adap][target]));
sp = subchan[adap][target];
sp->s_adap = adap;
sp->s_target = target;
sp->s_lu = lu;
PRINT3(("subchannel succesfully allocated\n"));
return sp;
}
/* this routine is used to do the hardware inventory, etc. It
* returns NULL on errors, else a ptr to 37 bytes of null
* terminated inquiry data, which is enough for type info, and mfg
* name, product name, and rev. Only valid to call at init time,
* when no driver open routines have yet been called. It isn't
* static, because ide uses it also.
*/
#define SCSI_IDATA_SIZE 37
u_char *
scsi_inq(u_char adap, u_char targ, u_char lun)
{
static u_char idata[SCSI_IDATA_SIZE];
static u_char inquiry[6]; /* can't initialize for PROM */
u_char *buf;
PRINT3(("inside scsi_inq\n"));
buf = dmabuf_malloc(SCSI_IDATA_SIZE);
bzero(buf, SCSI_IDATA_SIZE);
inquiry[0] = 0x12;
inquiry[1] = lun << 5;
inquiry[4] = SCSI_IDATA_SIZE - 1;
if (scsi_ecmd(adap, targ, lun, inquiry, 6, buf, SCSI_IDATA_SIZE - 1) != 0)
{
bcopy(buf, idata, SCSI_IDATA_SIZE);
dmabuf_free(buf);
return idata;
}
dmabuf_free(buf);
return 0;
}
void
scsi_setsyncmode(scsisubchan_t * sp, int enable)
{
PRINT3(("scsi_setsyncmode called: does nothing - subchannel %x enable %x\n", sp, enable));
}
void
freesubchannel(register scsisubchan_t * sp)
{
PRINT3(("FREEing Subchannel adap %x, targ %x, lun %x\n", sp->s_adap, sp->s_target, sp->s_lu));
subchan[sp->s_adap][sp->s_target] = NULL;
free(sp);
}
/*
* NOTE:
* The defines SC_NUMSENSE and SC_NUMADDSENSE are in scsi.h but they
* are overridden by new values in newscsi.h. These arrays are
* defined in controller specific files like ql.c, wd95.c which
* use values in newscsi.h. Including newscsi.h in dksc.c may cause other
* problems. So only these controller file dependent information
* is changed. If any of these change in newscsi.h it should
* be reflected in include/sys/scsimsgs.h
*/
char *scsi_key_msgtab[SC_NUMSENSE] = {
"No sense", /* 0x0 */
"Recovered Error", /* 0x1 */
"Drive not ready", /* 0x2 */
"Media error", /* 0x3 */
"Unrecoverable device error", /* 0x4 */
"Illegal request", /* 0x5 */
"Unit Attention", /* 0x6 */
"Data protect error", /* 0x7 */
"Unexpected blank media", /* 0x8 */
"Vendor Unique error", /* 0x9 */
"Copy Aborted", /* 0xa */
"Aborted command", /* 0xb */
"Search data successful", /* 0xc */
"Volume overflow", /* 0xd */
"Reserved (0xE)", /* 0xe */
"Reserved (0xF)", /* 0xf */
};
char *scsi_addit_msgtab[SC_NUMADDSENSE] = {
NULL, /* 0x00 */
"No index/sector signal", /* 0x01 */
"No seek complete", /* 0x02 */
"Write fault", /* 0x03 */
"Driver not ready", /* 0x04 */
"Drive not selected", /* 0x05 */
"No track 0", /* 0x06 */
"Multiple drives selected", /* 0x07 */
"LUN communication error", /* 0x08 */
"Track error", /* 0x09 */
"Error log overflow", /* 0x0a */
NULL, /* 0x0b */
"Write error", /* 0x0c */
NULL, /* 0x0d */
NULL, /* 0x0e */
NULL, /* 0x0f */
"ID CRC or ECC error", /* 0x10 */
"Unrecovered data block read error", /* 0x11 */
"No addr mark found in ID field", /* 0x12 */
"No addr mark found in Data field", /* 0x13 */
"No record found", /* 0x14 */
"Seek position error", /* 0x15 */
"Data sync mark error", /* 0x16 */
"Read data recovered with retries", /* 0x17 */
"Read data recovered with ECC", /* 0x18 */
"Defect list error", /* 0x19 */
"Parameter overrun", /* 0x1a */
"Synchronous transfer error", /* 0x1b */
"Defect list not found", /* 0x1c */
"Compare error", /* 0x1d */
"Recovered ID with ECC", /* 0x1e */
NULL, /* 0x1f */
"Invalid command code", /* 0x20 */
"Illegal logical block address", /* 0x21 */
"Illegal function", /* 0x22 */
NULL, /* 0x23 */
"Illegal field in CDB", /* 0x24 */
"Invalid LUN", /* 0x25 */
"Invalid field in parameter list", /* 0x26 */
"Media write protected", /* 0x27 */
"Media change", /* 0x28 */
"Device reset", /* 0x29 */
"Log parameters changed", /* 0x2a */
"Copy requires disconnect", /* 0x2b */
"Command sequence error", /* 0x2c */
"Update in place error", /* 0x2d */
NULL, /* 0x2e */
"Tagged commands cleared", /* 0x2f */
"Incompatible media", /* 0x30 */
"Media format corrupted", /* 0x31 */
"No defect spare location available", /* 0x32 */
"Media length error", /* 0x33 (tape only) */
NULL, /* 0x34 */
NULL, /* 0x35 */
"Toner/ink error", /* 0x36 */
"Parameter rounded", /* 0x37 */
NULL, /* 0x38 */
"Saved parameters not supported", /* 0x39 */
"Medium not present", /* 0x3a */
"Forms error", /* 0x3b */
NULL, /* 0x3c */
"Invalid ID msg", /* 0x3d */
"Self config in progress", /* 0x3e */
"Device config has changed",/* 0x3f */
"RAM failure", /* 0x40 */
"Data path diagnostic failure", /* 0x41 */
"Power on diagnostic failure", /* 0x42 */
"Message reject error", /* 0x43 */
"Internal controller error",/* 0x44 */
"Select/Reselect failed", /* 0x45 */
"Soft reset failure", /* 0x46 */
"SCSI interface parity error", /* 0x47 */
"Initiator detected error", /* 0x48 */
"Inappropriate/Illegal message", /* 0x49 */
};
#ifdef QL_DEBUG
void
dump_area(char *ptr, int count)
{
int i;
for (i = 0; i < count; i++)
{
if (!(i % 0x20))
PRINTD(("\n%x: ", ptr));
PRINTD(("%x ", *ptr++));
}
PRINTD(("\n"));
}
#endif
static int
reqi_get_slot(pHA_INFO ha,int tgt)
{
int i = ha->reqi_cntr[tgt];
pREQ_INFO r_ptr = &ha->reqi_block[tgt][i];
int slot;
int found = 0;
do {
i++;
if (i > MAX_REQ_INFO ) {
i = 1;
}
r_ptr = &ha->reqi_block[tgt][i];
if (r_ptr->req == NULL) {
found = 1;
break;
}
} while (i != ha->reqi_cntr[tgt]);
if (found) {
ha->reqi_cntr[tgt] = i;
slot = (tgt * (MAX_REQ_INFO +1)) | i;
} else {
slot = found;
}
return slot;
}
#ifdef IP30
qldebug_cmd(int argc, char **argv)
{
#ifdef QL_DEBUG
int new;
if (argc == 2) {
new = atoi(argv[1]);
if (new >= 0 && new <= 5)
ql_debug = new;
else
printf("invalid level %d\n",new);
}
printf("current ql_debug level %d\n",ql_debug);
#else
printf("QL_DEBUG is not enabled.\n");
#endif
return 0;
}
#endif
/* execute a user command given code, cdblen, and datalen */
struct scsi_target_info *
ql_u_cmd(u_char extctlr, u_char targ, u_char lun,
u_char cmd,u_char clen, u_char dlen)
{
struct scsi_request *req;
int retry = 1;
static u_char scsi[10];
char name[9];
struct scsi_target_info *retval;
scsi_target_info_t *info;
u_char board;
board = extctlr;
PRINT3(("ql_u_cmd: extctlr %x, targ %x, lun %x\n", extctlr, targ, lun));
info = &tinfo[board][targ][lun];
if ((info->si_inq == NULL) && (dlen))
{
info->si_inq = (u_char *) dmabuf_malloc(dlen);
info->si_sense = (u_char *) dmabuf_malloc(SCSI_SENSE_LEN);
PRINT3(("\ninquiry and sense allocated for board %x, targ %x lun %x inquire %x sense %x\n", board, targ, lun, info->si_inq, info->si_sense));
bzero(info->si_inq, dlen);
}
req = malloc(sizeof(*req));
retryit:
bzero(req, sizeof(*req));
scsi[0] = cmd;
scsi[1] = scsi[2] = scsi[3] = scsi[5] = 0;
scsi[4] = dlen;
req->sr_ctlr = extctlr;
req->sr_target = targ;
req->sr_lun = lun;
req->sr_command = scsi;
req->sr_cmdlen = clen; /* command length */
req->sr_flags = SRF_DIR_IN | SRF_FLUSH | SRF_AEN_ACK;
req->sr_timeout = 10 * HZ;
req->sr_buffer = info->si_inq;
req->sr_buflen = dlen;
req->sr_sense = NULL;
req->sr_dev = 0;
DCACHE_INVAL(info->si_inq, clen);
qlcommand(req);
if (req->sr_status == SC_GOOD && req->sr_scsi_status == ST_GOOD)
{
PRINT3(("qlinfo: good status on inquiry sr_status %x sr_scsi_status %x\n", req->sr_status, req->sr_scsi_status));
retval = info;
PRINTD(("qlinfo: REGISTER device inquire data %x controller %x target %x\n", info->si_inq, extctlr, targ));
} else
{
PRINT3(("ql_u_cmd failed: sr_status %x, sr_scsi_status %x\n",
req->sr_status, req->sr_scsi_status));
if (retry-- && req->sr_status == SC_BUS_RESET)
{
PRINT2(("ql_u_cmd: retrying because of bus reset\n"));
goto retryit;
}
retval = NULL;
}
free(req);
return retval;
}
void
cdrom_revert(u_char adap, u_char unit, u_char lun)
{
/* request sense, code, cdblen, datalen */
ql_u_cmd(adap, unit, lun, 3, 6, SCSI_SENSE_LEN) ;
/* TOSHIBA CD vendor specific command */
ql_u_cmd(adap, unit, lun, 0xc9, 10, 0) ;
}