/* esp.c: EnhancedScsiProcessor Sun SCSI driver code.
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/malloc.h>
#include <linux/blk.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include "scsi.h"
#include "hosts.h"
#include "esp.h"
#include <asm/sbus.h>
#include <asm/dma.h>
#include <asm/system.h>
#include <asm/idprom.h>
#include <asm/machines.h>
#include <asm/ptrace.h>
#include <asm/pgtable.h>
#include <asm/oplib.h>
#include <asm/vaddrs.h>
#include <asm/io.h>
#define DEBUG_ESP
/* #define DEBUG_ESP_SG */
#if defined(DEBUG_ESP)
#define ESPLOG(foo) printk foo
#else
#define ESPLOG(foo)
#endif /* (DEBUG_ESP) */
#define INTERNAL_ESP_ERROR \
(panic ("Internal ESP driver error in file %s, line %d\n", \
__FILE__, __LINE__))
#define INTERNAL_ESP_ERROR_NOPANIC \
(printk ("Internal ESP driver error in file %s, line %d\n", \
__FILE__, __LINE__))
/* This enum will be expanded when we have sync code written. */
enum {
not_issued = 0x01, /* Still in the issue_SC queue. */
in_selection = 0x02, /* ESP is arbitrating, awaiting IRQ */
in_datain = 0x04, /* Data is transferring over the bus */
in_dataout = 0x08, /* Data is transferring over the bus */
in_status = 0x10, /* Awaiting status/msg bytes from target */
in_finale = 0x11, /* Sent Msg ack, awaiting disconnect */
};
struct proc_dir_entry proc_scsi_esp = {
PROC_SCSI_ESP, 3, "esp",
S_IFDIR | S_IRUGO | S_IXUGO, 2
};
struct Sparc_ESP *espchain;
static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs);
static void esp_done(struct Sparc_ESP *esp, int error);
/* Debugging routines */
struct esp_cmdstrings {
unchar cmdchar;
char *text;
} esp_cmd_strings[] = {
/* Miscellaneous */
{ ESP_CMD_NULL, "ESP_NOP", },
{ ESP_CMD_FLUSH, "FIFO_FLUSH", },
{ ESP_CMD_RC, "RSTESP", },
{ ESP_CMD_RS, "RSTSCSI", },
/* Disconnected State Group */
{ ESP_CMD_RSEL, "RESLCTSEQ", },
{ ESP_CMD_SEL, "SLCTNATN", },
{ ESP_CMD_SELA, "SLCTATN", },
{ ESP_CMD_SELAS, "SLCTATNSTOP", },
{ ESP_CMD_ESEL, "ENSLCTRESEL", },
{ ESP_CMD_DSEL, "DISSELRESEL", },
{ ESP_CMD_SA3, "SLCTATN3", },
{ ESP_CMD_RSEL3, "RESLCTSEQ", },
/* Target State Group */
{ ESP_CMD_SMSG, "SNDMSG", },
{ ESP_CMD_SSTAT, "SNDSTATUS", },
{ ESP_CMD_SDATA, "SNDDATA", },
{ ESP_CMD_DSEQ, "DISCSEQ", },
{ ESP_CMD_TSEQ, "TERMSEQ", },
{ ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", },
{ ESP_CMD_DCNCT, "DISC", },
{ ESP_CMD_RMSG, "RCVMSG", },
{ ESP_CMD_RCMD, "RCVCMD", },
{ ESP_CMD_RDATA, "RCVDATA", },
{ ESP_CMD_RCSEQ, "RCVCMDSEQ", },
/* Initiator State Group */
{ ESP_CMD_TI, "TRANSINFO", },
{ ESP_CMD_ICCSEQ, "INICMDSEQCOMP", },
{ ESP_CMD_MOK, "MSGACCEPTED", },
{ ESP_CMD_TPAD, "TPAD", },
{ ESP_CMD_SATN, "SATN", },
{ ESP_CMD_RATN, "RATN", },
};
#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings)))
/* Print textual representation of an ESP command */
static inline void esp_print_cmd(unchar espcmd)
{
unchar dma_bit = espcmd & ESP_CMD_DMA;
int i;
espcmd &= ~dma_bit;
for(i=0; i<NUM_ESP_COMMANDS; i++)
if(esp_cmd_strings[i].cmdchar == espcmd)
break;
if(i==NUM_ESP_COMMANDS)
printk("ESP_Unknown");
else
printk("%s%s", esp_cmd_strings[i].text,
((dma_bit) ? "+DMA" : ""));
}
/* Print the status register's value */
static inline void esp_print_statreg(unchar statreg)
{
unchar phase;
printk("STATUS<");
phase = statreg & ESP_STAT_PMASK;
printk("%s,", (phase == ESP_DOP ? "DATA-OUT" :
(phase == ESP_DIP ? "DATA-IN" :
(phase == ESP_CMDP ? "COMMAND" :
(phase == ESP_STATP ? "STATUS" :
(phase == ESP_MOP ? "MSG-OUT" :
(phase == ESP_MIP ? "MSG_IN" :
"unknown")))))));
if(statreg & ESP_STAT_TDONE)
printk("TRANS_DONE,");
if(statreg & ESP_STAT_TCNT)
printk("TCOUNT_ZERO,");
if(statreg & ESP_STAT_PERR)
printk("P_ERROR,");
if(statreg & ESP_STAT_SPAM)
printk("SPAM,");
if(statreg & ESP_STAT_INTR)
printk("IRQ,");
printk(">");
}
/* Print the interrupt register's value */
static inline void esp_print_ireg(unchar intreg)
{
printk("INTREG< ");
if(intreg & ESP_INTR_S)
printk("SLCT_NATN ");
if(intreg & ESP_INTR_SATN)
printk("SLCT_ATN ");
if(intreg & ESP_INTR_RSEL)
printk("RSLCT ");
if(intreg & ESP_INTR_FDONE)
printk("FDONE ");
if(intreg & ESP_INTR_BSERV)
printk("BSERV ");
if(intreg & ESP_INTR_DC)
printk("DISCNCT ");
if(intreg & ESP_INTR_IC)
printk("ILL_CMD ");
if(intreg & ESP_INTR_SR)
printk("SCSI_BUS_RESET ");
printk(">");
}
/* Print the sequence step registers contents */
static inline void esp_print_seqreg(unchar stepreg)
{
stepreg &= ESP_STEP_VBITS;
printk("STEP<%s>",
(stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" :
(stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" :
(stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" :
(stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" :
(stepreg == ESP_STEP_FINI ? "CMD_SENT_OK" :
"UNKNOWN"))))));
}
/* Manipulation of the ESP command queues. Thanks to the aha152x driver
* and its author, Juergen E. Fischer, for the methods used here.
* Note that these are per-ESP queues, not global queues like
* the aha152x driver uses.
*/
static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
{
Scsi_Cmnd *end;
unsigned long flags;
save_flags(flags); cli();
new_SC->host_scribble = (unsigned char *) NULL;
if(!*SC)
*SC = new_SC;
else {
for(end=*SC;end->host_scribble;end=(Scsi_Cmnd *)end->host_scribble)
;
end->host_scribble = (unsigned char *) new_SC;
}
restore_flags(flags);
}
static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
{
Scsi_Cmnd *ptr;
unsigned long flags;
save_flags(flags); cli();
ptr = *SC;
if(ptr)
*SC = (Scsi_Cmnd *) (*SC)->host_scribble;
restore_flags(flags);
return ptr;
}
static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
{
Scsi_Cmnd *ptr, *prev;
unsigned long flags;
save_flags(flags); cli();
for(ptr = *SC, prev = NULL;
ptr && ((ptr->target != target) || (ptr->lun != lun));
prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
;
if(ptr) {
if(prev)
prev->host_scribble=ptr->host_scribble;
else
*SC=(Scsi_Cmnd *)ptr->host_scribble;
}
restore_flags(flags);
return ptr;
}
static inline void do_pause(unsigned amount)
{
unsigned long the_time = jiffies + amount;
while(jiffies < the_time)
barrier(); /* Not really needed, but... */
}
/* This places the ESP into a known state at boot time. */
static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
{
struct sparc_dma_registers *dregs = esp->dregs;
volatile unchar trash;
/* Punt the DVMA into a known state. */
dregs->cond_reg |= DMA_RST_SCSI;
do_pause(100);
dregs->cond_reg &= ~(DMA_RST_SCSI);
if(esp->dma->revision == dvmarev2)
if(esp->erev != esp100)
dregs->cond_reg |= DMA_3CLKS;
else if(esp->dma->revision == dvmarev3)
if(esp->erev == fas236 || esp->erev == fas100a) {
dregs->cond_reg &= ~(DMA_3CLKS);
dregs->cond_reg |= DMA_2CLKS;
}
else if(esp->dma->revision == dvmaesc1)
dregs->cond_reg |= DMA_ADD_ENABLE;
DMA_INTSON(dregs);
/* Now reset the ESP chip */
eregs->esp_cmd = ESP_CMD_RC;
eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA);
eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA); /* borken hardware... */
/* Reload the configuration registers */
eregs->esp_cfg1 = esp->config1;
eregs->esp_cfact = esp->cfact;
eregs->esp_stp = 0;
eregs->esp_soff = 0;
eregs->esp_timeo = esp->sync_defp;
if(esp->erev == esp100a || esp->erev == esp236)
eregs->esp_cfg2 = esp->config2;
if(esp->erev == esp236)
eregs->esp_cfg3 = esp->config3[0];
/* Eat any bitrot in the chip */
trash = eregs->esp_intrpt;
/* Reset the SCSI bus, but tell ESP not to generate an irq */
eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB;
eregs->esp_cmd = ESP_CMD_RS;
do_pause(200);
eregs->esp_cfg1 = esp->config1;
/* Eat any bitrot in the chip and we are done... */
trash = eregs->esp_intrpt;
}
/* Detecting ESP chips on the machine. This is the simple and easy
* version.
*/
int esp_detect(Scsi_Host_Template *tpnt)
{
struct Sparc_ESP *esp, *elink;
struct Scsi_Host *esp_host;
struct linux_sbus *sbus;
struct linux_sbus_device *esp_dev, *sbdev_iter;
struct Sparc_ESP_regs *eregs;
struct sparc_dma_registers *dregs;
struct Linux_SBus_DMA *dma, *dlink;
unsigned int fmhz;
unchar ccf, bsizes, bsizes_more;
int nesps = 0;
int esp_node;
espchain = 0;
if(!SBus_chain)
panic("No SBUS in esp_detect()");
for_each_sbus(sbus) {
for_each_sbusdev(sbdev_iter, sbus) {
/* Is it an esp sbus device? */
esp_dev = sbdev_iter;
if(strcmp(esp_dev->prom_name, "esp") &&
strcmp(esp_dev->prom_name, "SUNW,esp")) {
if(!esp_dev->child ||
strcmp(esp_dev->prom_name, "espdma"))
continue; /* nope... */
esp_dev = esp_dev->child;
if(strcmp(esp_dev->prom_name, "esp") &&
strcmp(esp_dev->prom_name, "SUNW,esp"))
continue; /* how can this happen? */
}
esp_host = scsi_register(tpnt, sizeof(struct Sparc_ESP));
if(!esp_host)
panic("Cannot register ESP SCSI host");
esp = (struct Sparc_ESP *) esp_host->hostdata;
if(!esp)
panic("No esp in hostdata");
esp->ehost = esp_host;
esp->edev = esp_dev;
/* Put into the chain of esp chips detected */
if(espchain) {
elink = espchain;
while(elink->next) elink = elink->next;
elink->next = esp;
} else {
espchain = esp;
}
esp->next = 0;
/* Get misc. prom information */
#define ESP_IS_MY_DVMA(esp, dma) \
((esp->edev->my_bus == dma->SBus_dev->my_bus) && \
(esp->edev->slot == dma->SBus_dev->slot) && \
(!strcmp(dma->SBus_dev->prom_name, "dma") || \
!strcmp(dma->SBus_dev->prom_name, "espdma")))
esp_node = esp_dev->prom_node;
prom_getstring(esp_node, "name", esp->prom_name,
sizeof(esp->prom_name));
esp->prom_node = esp_node;
for_each_dvma(dlink) {
if(ESP_IS_MY_DVMA(esp, dlink) && !dlink->allocated)
break;
}
#undef ESP_IS_MY_DVMA
/* If we don't know how to handle the dvma, do not use this device */
if(!dlink){
printk ("Cannot find dvma for ESP SCSI\n");
scsi_unregister (esp_host);
continue;
}
if (dlink->allocated){
printk ("esp: can't use my espdma\n");
scsi_unregister (esp_host);
continue;
}
dlink->allocated = 1;
dma = dlink;
esp->dma = dma;
esp->dregs = dregs = dma->regs;
/* Map in the ESP registers from I/O space */
prom_apply_sbus_ranges(esp->edev->reg_addrs, 1);
esp->eregs = eregs = (struct Sparc_ESP_regs *)
sparc_alloc_io(esp->edev->reg_addrs[0].phys_addr, 0,
PAGE_SIZE, "ESP Registers",
esp->edev->reg_addrs[0].which_io, 0x0);
if(!eregs)
panic("ESP registers unmappable");
esp->esp_command =
sparc_dvma_malloc(16, "ESP DVMA Cmd Block");
if(!esp->esp_command)
panic("ESP DVMA transport area unmappable");
/* Set up the irq's etc. */
esp->ehost->base = (unsigned char *) esp->eregs;
esp->ehost->io_port = (unsigned int) esp->eregs;
esp->ehost->n_io_port = (unsigned char)
esp->edev->reg_addrs[0].reg_size;
/* XXX The following may be different on sun4ms XXX */
esp->ehost->irq = esp->irq = esp->edev->irqs[0].pri;
/* Allocate the irq only if necessary */
for_each_esp(elink) {
if((elink != esp) && (esp->irq == elink->irq)) {
goto esp_irq_acquired; /* BASIC rulez */
}
}
/* XXX We have shared interrupts per level now, maybe
* XXX use them, maybe not...
*/
if(request_irq(esp->ehost->irq, esp_intr, SA_INTERRUPT,
"Sparc ESP SCSI", NULL))
panic("Cannot acquire ESP irq line");
esp_irq_acquired:
printk("esp%d: IRQ %d ", nesps, esp->ehost->irq);
/* Figure out our scsi ID on the bus */
esp->scsi_id = prom_getintdefault(esp->prom_node,
"initiator-id", -1);
if(esp->scsi_id == -1)
esp->scsi_id = prom_getintdefault(esp->prom_node,
"scsi-initiator-id", -1);
if(esp->scsi_id == -1)
esp->scsi_id =
prom_getintdefault(esp->edev->my_bus->prom_node,
"scsi-initiator-id", 7);
esp->ehost->this_id = esp->scsi_id;
esp->scsi_id_mask = (1 << esp->scsi_id);
/* Check for differential bus */
esp->diff = prom_getintdefault(esp->prom_node, "differential", -1);
esp->diff = (esp->diff == -1) ? 0 : 1;
/* Check out the clock properties of the chip */
fmhz = prom_getintdefault(esp->prom_node, "clock-frequency", -1);
if(fmhz==-1)
fmhz = prom_getintdefault(esp->edev->my_bus->prom_node,
"clock-frequency", -1);
if(fmhz <= (5000))
ccf = 0;
else
ccf = (((5000 - 1) + (fmhz))/(5000));
if(!ccf || ccf > 8) {
ccf = ESP_CCF_F4;
fmhz = (5000 * 4);
}
if(ccf==(ESP_CCF_F7+1))
esp->cfact = ESP_CCF_F0;
else if(ccf == ESP_CCF_NEVER)
esp->cfact = ESP_CCF_F2;
else
esp->cfact = ccf;
esp->cfreq = fmhz;
esp->ccycle = ((1000000000) / ((fmhz)/1000));
esp->ctick = ((7682 * esp->cfact * esp->ccycle)/1000);
esp->sync_defp = ((7682 + esp->ctick - 1) / esp->ctick);
/* XXX HACK HACK HACK XXX */
if (esp->sync_defp < 153)
esp->sync_defp = 153;
printk("SCSI ID %d Clock %d MHz Period %2x ", esp->scsi_id,
(fmhz / 1000), esp->sync_defp);
/* Find the burst sizes this dma supports. */
bsizes = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff);
bsizes_more = prom_getintdefault(esp->edev->my_bus->prom_node,
"burst-sizes", 0xff);
if(bsizes_more != 0xff) bsizes &= bsizes_more;
if(bsizes == 0xff || (bsizes & DMA_BURST16)==0 ||
(bsizes & DMA_BURST32)==0)
bsizes = (DMA_BURST32 - 1);
esp->bursts = bsizes;
/* Probe the revision of this esp */
esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7));
esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY);
esp->config3[0] = ESP_CONFIG3_TENB;
eregs->esp_cfg2 = esp->config2;
if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) !=
(ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) {
printk("NCR53C90(esp100) detected\n");
esp->erev = esp100;
} else {
eregs->esp_cfg2 = esp->config2 = 0;
eregs->esp_cfg3 = 0;
eregs->esp_cfg3 = esp->config3[0] = 5;
if(eregs->esp_cfg3 != 5) {
printk("NCR53C90A(esp100a) detected\n");
esp->erev = esp100a;
} else {
int target;
for(target=0; target<8; target++)
esp->config3[target] = 0;
eregs->esp_cfg3 = 0;
if(esp->cfact > ESP_CCF_F5) {
printk("NCR53C9XF(espfast) detected\n");
esp->erev = fast;
esp->config2 |= ESP_CONFIG2_FENAB;
eregs->esp_cfg2 = esp->config2;
} else {
printk("NCR53C9x(esp236) detected\n");
esp->erev = esp236;
eregs->esp_cfg2 = esp->config2 = 0;
}
}
}
/* Initialize the command queues */
esp->current_SC = 0;
esp->disconnected_SC = 0;
esp->issue_SC = 0;
/* Reset the thing before we try anything... */
esp_bootup_reset(esp, eregs);
nesps++;
#ifdef THREADED_ESP_DRIVER
kernel_thread(esp_kernel_thread, esp, 0);
#endif
} /* for each sbusdev */
} /* for each sbus */
return nesps;
}
/*
* The info function will return whatever useful
* information the developer sees fit. If not provided, then
* the name field will be used instead.
*/
const char *esp_info(struct Scsi_Host *host)
{
struct Sparc_ESP *esp;
esp = (struct Sparc_ESP *) host->hostdata;
switch(esp->erev) {
case esp100:
return "Sparc ESP100 (NCR53C90)";
case esp100a:
return "Sparc ESP100A (NCR53C90A)";
case esp236:
return "Sparc ESP236";
case fast:
return "Sparc ESP-FAST (236 or 100A)";
case fas236:
return "Sparc ESP236-FAST";
case fas100a:
return "Sparc ESP100A-FAST";
default:
panic("Bogon ESP revision");
};
}
/* Execute a SCSI command when the bus is free. All callers
* turn off all interrupts, so we don't need to explicitly do
* it here.
*/
static inline void esp_exec_cmd(struct Sparc_ESP *esp)
{
struct sparc_dma_registers *dregs;
struct Sparc_ESP_regs *eregs;
Scsi_Cmnd *SCptr;
int i;
eregs = esp->eregs;
dregs = esp->dregs;
/* Grab first member of the issue queue. */
SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC);
if(!SCptr)
goto bad;
SCptr->SCp.phase = in_selection;
/* NCR docs say:
* 1) Load select/reselect Bus ID register with target ID
* 2) Load select/reselect Timeout Reg with desired value
* 3) Load Synchronous offset register with zero (for
* asynchronous transfers).
* 4) Load Synchronous Transfer Period register (if
* synchronous)
* 5) Load FIFO with 6, 10, or 12 byte SCSI command
* 6) Issue SELECTION_WITHOUT_ATTENTION command
*
* They also mention that a DMA NOP command must be issued
* to the SCSI chip under many circumstances, plus it's
* also a good idea to flush out the fifo just in case.
*/
/* Load zeros into COUNTER via 2 DMA NOP chip commands
* due to flaky implementations of the 53C9x which don't
* get the idea the first time around.
*/
dregs->cond_reg = (DMA_INT_ENAB | DMA_FIFO_INV);
eregs->esp_tclow = 0;
eregs->esp_tcmed = 0;
eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA);
/* Flush the fifo of excess garbage. */
eregs->esp_cmd = ESP_CMD_FLUSH;
/* Load bus-id and timeout values. */
eregs->esp_busid = (SCptr->target & 7);
eregs->esp_timeo = esp->sync_defp;
eregs->esp_soff = 0; /* This means async transfer... */
eregs->esp_stp = 0;
/* Load FIFO with the actual SCSI command. */
for(i=0; i < SCptr->cmd_len; i++)
eregs->esp_fdata = SCptr->cmnd[i];
/* Make sure the dvma forwards the ESP interrupt. */
dregs->cond_reg = DMA_INT_ENAB;
/* Tell ESP to SELECT without asserting ATN. */
eregs->esp_cmd = ESP_CMD_SEL;
return;
bad:
panic("esp: daaarrrkk starrr crashesss....");
}
/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */
int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
struct Sparc_ESP *esp;
unsigned long flags;
save_flags(flags); cli();
/* Set up func ptr and initial driver cmd-phase. */
SCpnt->scsi_done = done;
SCpnt->SCp.phase = not_issued;
esp = (struct Sparc_ESP *) SCpnt->host->hostdata;
/* We use the scratch area. */
if(!SCpnt->use_sg) {
SCpnt->SCp.this_residual = SCpnt->request_bufflen;
SCpnt->SCp.buffer =
(struct scatterlist *) SCpnt->request_buffer;
SCpnt->SCp.buffers_residual = 0;
SCpnt->SCp.Status = CHECK_CONDITION;
SCpnt->SCp.Message = 0;
SCpnt->SCp.have_data_in = 0;
SCpnt->SCp.sent_command = 0;
SCpnt->SCp.ptr = mmu_get_scsi_one((char *)SCpnt->SCp.buffer,
SCpnt->SCp.this_residual,
esp->edev->my_bus);
} else {
#ifdef DEBUG_ESP_SG
printk("esp: sglist at %p with %d buffers\n",
SCpnt->buffer, SCpnt->use_sg);
#endif
SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer;
SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer,
SCpnt->SCp.buffers_residual,
esp->edev->my_bus);
SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->alt_address;
}
/* Place into our queue. */
append_SC(&esp->issue_SC, SCpnt);
/* Run it now if we can */
if(!esp->current_SC)
esp_exec_cmd(esp);
restore_flags(flags);
return 0;
}
/* Only queuing supported in this ESP driver. */
int esp_command(Scsi_Cmnd *SCpnt)
{
ESPLOG(("esp: esp_command() called...\n"));
return -1;
}
/* Abort a command. Those that are on the bus force a SCSI bus
* reset.
*/
int esp_abort(Scsi_Cmnd *SCpnt)
{
ESPLOG(("esp_abort: Not implemented yet\n"));
return SCSI_ABORT_ERROR;
}
/* Reset ESP chip, reset hanging bus, then kill active and
* disconnected commands for targets without soft reset.
*/
int esp_reset(Scsi_Cmnd *SCptr, unsigned int how)
{
ESPLOG(("esp_reset: Not implemented yet\n"));
return SCSI_RESET_ERROR;
}
/* Internal ESP done function. */
static inline void esp_done(struct Sparc_ESP *esp, int error)
{
unsigned long flags;
Scsi_Cmnd *done_SC;
if(esp->current_SC) {
/* Critical section... */
save_flags(flags); cli();
done_SC = esp->current_SC;
esp->current_SC = NULL;
/* Free dvma entry. */
if(!done_SC->use_sg) {
mmu_release_scsi_one(done_SC->SCp.ptr,
done_SC->SCp.this_residual,
esp->edev->my_bus);
} else {
struct scatterlist *scl = (struct scatterlist *)done_SC->buffer;
#ifdef DEBUG_ESP_SG
printk("esp: unmapping sg ");
#endif
mmu_release_scsi_sgl((struct mmu_sglist *) scl,
done_SC->use_sg - 1,
esp->edev->my_bus);
#ifdef DEBUG_ESP_SG
printk("done.\n");
#endif
}
done_SC->result = error;
if(done_SC->scsi_done)
done_SC->scsi_done(done_SC);
else
panic("esp: esp->current_SC->scsi_done() == NULL");
/* Bus is free, issue any commands in the queue. */
if(esp->issue_SC)
esp_exec_cmd(esp);
restore_flags(flags);
/* End of critical section... */
} else
panic("esp: done() called with NULL esp->current_SC");
}
#ifdef THREADED_ESP_DRIVER /* planning stage... */
/* With multiple lots of commands being processed I frequently
* see a situation where we see galloping esp herds. esp_done()
* wakes the entire world up and each interrupt causes a reschedule.
* This kernel thread fixes some of these unwanted effects during
* IO intensive activity.... I hope...
*/
static void esp_kernel_thread(void *opaque)
{
struct Sparc_ESP *esp = opaque;
for(;;) {
unsigned long flags;
while(esp->eatme_SC) {
struct Scsi_Cmnd *SCpnt;
SCpnt = remove_first_SC(esp->eatme_SC);
esp_done(esp, error, SCpnt);
}
sleep();
}
}
#endif
/* Read the interrupt status registers on this ESP board */
static inline void esp_updatesoft(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs)
{
/* Update our software copies of the three ESP status
* registers for this ESP. Be careful, reading the
* ESP interrupt register clears the status and sequence
* step registers (unlatches them, you get the idea).
* So read the interrupt register last.
*/
esp->seqreg = eregs->esp_sstep;
esp->sreg = eregs->esp_status;
/* Supposedly, the ESP100A and above assert the highest
* bit in the status register if an interrupt is pending.
* I've never seen this work properly, so let's clear it
* manually while we are here. If I see any esp chips
* for which this bit is reliable I will conditionalize
* this. However, I don't see what this extra bit can
* buy me with all the tests I'll have to place all over
* the code to actually use it when I 'can'. Plus the
* 'pending interrupt' condition can more than reliably
* be obtained from the DVMA control register.
*
* "Broken hardware" -Linus
*/
esp->sreg &= (~ESP_STAT_INTR);
esp->ireg = eregs->esp_intrpt; /* Must be last or we lose */
}
/* #define ESP_IRQ_TRACE */
#ifdef ESP_IRQ_TRACE
#define ETRACE(foo) printk foo
#else
#define ETRACE(foo)
#endif
static char last_fflags, last_status, last_msg;
/* Main interrupt handler for an esp adapter. */
static inline void esp_handle(struct Sparc_ESP *esp)
{
struct sparc_dma_registers *dregs;
struct Sparc_ESP_regs *eregs;
Scsi_Cmnd *SCptr;
eregs = esp->eregs;
dregs = esp->dregs;
SCptr = esp->current_SC;
DMA_IRQ_ENTRY(esp->dma, dregs);
esp_updatesoft(esp, eregs);
ETRACE(("ESPIRQ: <%2x,%2x,%2x> --> ", esp->ireg, esp->sreg, esp->seqreg));
/* Check for errors. */
if(!SCptr)
panic("esp_handle: current_SC == penguin within interrupt!");
/* At this point in time, this esp driver should not see
* scsibus resets, parity errors, or gross errors unless
* something truly terrible happens which we are not ready
* to properly recover from yet.
*/
if((esp->ireg & (ESP_INTR_SR | ESP_INTR_IC)) ||
(esp->sreg & (ESP_STAT_PERR | ESP_STAT_SPAM))) {
printk("esp: really bad error detected\n");
printk("esp: intr<%2x> stat<%2x> seq<%2x>",
esp->ireg, esp->sreg, esp->seqreg);
printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
panic("esp: cannot continue\n");
}
if(dregs->cond_reg & DMA_HNDL_ERROR) {
printk("esp: DMA shows an error cond_reg<%08lx> addr<%p>\n",
dregs->cond_reg, dregs->st_addr);
printk("esp: intr<%2x> stat<%2x> seq<%2x>",
esp->ireg, esp->sreg, esp->seqreg);
printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
panic("esp: cannot continue\n");
}
if(esp->sreg & ESP_STAT_PERR) {
printk("esp: SCSI bus parity error\n");
printk("esp: intr<%2x> stat<%2x> seq<%2x>",
esp->ireg, esp->sreg, esp->seqreg);
printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase);
panic("esp: cannot continue\n");
}
/* Service interrupt. */
switch(SCptr->SCp.phase) {
case not_issued:
panic("Unexpected ESP interrupt, current_SC not issued.");
break;
case in_selection:
if(esp->ireg & ESP_INTR_RSEL) {
/* XXX Some day XXX */
panic("ESP penguin reselected in async mode.");
} else if(esp->ireg & ESP_INTR_DC) {
/* Either we are scanning the bus and no-one
* lives at this target or it didn't respond.
*/
ETRACE(("DISCONNECT\n"));
#ifdef THREADED_ESP_DRIVER
append_SC(esp->eatme_SC, esp->current_SC);
esp->current_SC = 0;
wake_up(esp_kernel_thread);
#else
esp_done(esp, (DID_NO_CONNECT << 16));
#endif
goto esp_handle_done;
} else if((esp->ireg & (ESP_INTR_FDONE | ESP_INTR_BSERV)) ==
(ESP_INTR_FDONE | ESP_INTR_BSERV)) {
/* Selection successful, check the sequence step. */
/* XXX I know, I know... add error recovery. XXX */
switch(esp->seqreg & ESP_STEP_VBITS) {
case ESP_STEP_NCMD:
panic("esp: penguin didn't enter cmd phase.");
break;
case ESP_STEP_PPC:
panic("esp: penguin prematurely changed from cmd phase.");
break;
case ESP_STEP_FINI:
/* At the completion of every command
* or message-out phase, we _must_
* unlatch the fifo-flags register
* with an ESP nop command.
*/
eregs->esp_cmd = ESP_CMD_NULL;
/* Selection/Command sequence completed. We
* (at least for this driver) will be in
* either one of the data phases or status
* phase, check the status register to find
* out.
*/
switch(esp->sreg & ESP_STAT_PMASK) {
default:
printk("esp: Not datain/dataout/status.\n");
panic("esp: penguin phase transition after selection.");
break;
case ESP_DOP:
/* Data out phase. */
dregs->cond_reg |= DMA_FIFO_INV;
while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
barrier();
SCptr->SCp.phase = in_dataout;
#ifdef DEBUG_ESP_SG
if(SCptr->use_sg)
printk("esp: sg-start <%p,%d>",
SCptr->SCp.ptr,
SCptr->SCp.this_residual);
#endif
eregs->esp_tclow = SCptr->SCp.this_residual;
eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
/* This is either the one buffer dvma ptr,
* or the first one in the scatter gather
* list. Check out esp_queue to see how
* this is set up.
*/
dregs->st_addr = SCptr->SCp.ptr;
dregs->cond_reg &= ~(DMA_ST_WRITE);
dregs->cond_reg |= (DMA_ENABLE | DMA_INT_ENAB);
eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
ETRACE(("DATA_OUT\n"));
goto esp_handle_done;
case ESP_DIP:
/* Data in phase. */
dregs->cond_reg |= DMA_FIFO_INV;
while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
barrier();
SCptr->SCp.phase = in_datain;
#ifdef DEBUG_ESP_SG
if(SCptr->use_sg)
printk("esp: sg-start <%p,%d>",
SCptr->SCp.ptr,
SCptr->SCp.this_residual);
#endif
eregs->esp_tclow = SCptr->SCp.this_residual;
eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
/* This is either the one buffer dvma ptr,
* or the first one in the scatter gather
* list. Check out esp_queue to see how
* this is set up.
*/
dregs->st_addr = SCptr->SCp.ptr;
dregs->cond_reg |= (DMA_ENABLE | DMA_ST_WRITE | DMA_INT_ENAB);
eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
ETRACE(("DATA_IN\n"));
goto esp_handle_done;
case ESP_STATP:
/* Status phase. */
SCptr->SCp.phase = in_status;
eregs->esp_cmd = ESP_CMD_ICCSEQ;
ETRACE(("STATUS\n"));
goto esp_handle_done; /* Wait for message. */
};
};
} else if(esp->ireg & ESP_INTR_FDONE) {
/* I'd like to investigate why this happens... */
ESPLOG(("esp: This is weird, halfway through "));
ESPLOG(("selection, trying to continue anyways.\n"));
goto esp_handle_done;
} else {
panic("esp: Did not get bus service during selection.");
goto esp_handle_done;
}
panic("esp: Mr. Potatoe Head is on the loose!");
case in_datain:
/* Drain the fifo for writes to memory. */
switch(esp->dma->revision) {
case dvmarev0:
case dvmarev1:
case dvmarevplus:
case dvmarev2:
case dvmarev3:
/* Force a drain. */
dregs->cond_reg |= DMA_FIFO_STDRAIN;
/* fall through */
case dvmaesc1:
/* Wait for the fifo to drain completely. */
while(dregs->cond_reg & DMA_FIFO_ISDRAIN)
barrier();
break;
};
case in_dataout:
dregs->cond_reg &= ~DMA_ENABLE;
/* We may be pipelining an sg-list. */
if(SCptr->use_sg) {
if(SCptr->SCp.buffers_residual) {
/* If we do not see a BUS SERVICE interrupt
* at this point, or we see that we have left
* the current data phase, then we lose.
*/
if(!(esp->ireg & ESP_INTR_BSERV) ||
((esp->sreg & ESP_STAT_PMASK) > 1))
panic("esp: Aiee penguin on the SCSI-bus.");
++SCptr->SCp.buffer;
--SCptr->SCp.buffers_residual;
SCptr->SCp.this_residual = SCptr->SCp.buffer->length;
SCptr->SCp.ptr = SCptr->SCp.buffer->alt_address;
#ifdef DEBUG_ESP_SG
printk("<%p,%d> ", SCptr->SCp.ptr,
SCptr->SCp.this_residual);
#endif
/* Latch in new esp counters... */
eregs->esp_tclow = SCptr->SCp.this_residual;
eregs->esp_tcmed = (SCptr->SCp.this_residual>>8);
eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL);
/* Reload DVMA gate array with new vaddr and enab. */
dregs->st_addr = SCptr->SCp.ptr;
dregs->cond_reg |= DMA_ENABLE;
/* Tell the esp to start transferring. */
eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI);
goto esp_handle_done;
}
#ifdef DEBUG_ESP_SG
printk("done.\n");
#endif
}
/* Take a look at what happened. */
if(esp->ireg & ESP_INTR_DC) {
panic("esp: target disconnects during data transfer.");
goto esp_handle_done;
} else if(esp->ireg & ESP_INTR_BSERV) {
if((esp->sreg & ESP_STAT_PMASK) != ESP_STATP) {
panic("esp: Not status phase after data phase.");
goto esp_handle_done;
}
SCptr->SCp.phase = in_status;
eregs->esp_cmd = ESP_CMD_ICCSEQ;
ETRACE(("STATUS\n"));
goto esp_handle_done; /* Wait for message. */
} else {
printk("esp: did not get bus service after data transfer.");
printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n",
esp->ireg, esp->sreg, esp->seqreg);
panic("esp: penguin data transfer.");
goto esp_handle_done;
}
case in_status:
if(esp->ireg & ESP_INTR_DC) {
panic("esp: penguin disconnects in status phase.");
goto esp_handle_done;
} else if (esp->ireg & ESP_INTR_FDONE) {
/* Status and Message now sit in the fifo for us. */
last_fflags = eregs->esp_fflags;
SCptr->SCp.phase = in_finale;
last_status = SCptr->SCp.Status = eregs->esp_fdata;
last_msg = SCptr->SCp.Message = eregs->esp_fdata;
eregs->esp_cmd = ESP_CMD_MOK;
ETRACE(("FINALE\n"));
goto esp_handle_done;
} else {
panic("esp: penguin status phase.");
}
case in_finale:
if(esp->ireg & ESP_INTR_BSERV) {
panic("esp: penguin doesn't disconnect after status msg-ack.");
goto esp_handle_done;
} else if(esp->ireg & ESP_INTR_DC) {
/* Nexus is complete. */
#ifdef THREADED_ESP_DRIVER
append_SC(esp->eatme_SC, esp->current_SC);
esp->current_SC = 0;
wake_up(esp_kernel_thread);
#else
esp_done(esp, ((SCptr->SCp.Status & 0xff) |
((SCptr->SCp.Message & 0xff) << 8) |
(DID_OK << 16)));
#endif
ETRACE(("NEXUS_COMPLETE\n"));
goto esp_handle_done;
} else {
printk("esp: wacky state while in in_finale phase.\n");
printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n",
esp->ireg, esp->sreg, esp->seqreg);
panic("esp: penguin esp state.");
goto esp_handle_done;
}
default:
panic("esp: detected penguin phase.");
goto esp_handle_done;
}
panic("esp: Heading to the promised land.");
esp_handle_done:
DMA_IRQ_EXIT(esp->dma, dregs);
return;
}
static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs)
{
struct Sparc_ESP *esp;
/* Handle all ESP interrupts showing */
for_each_esp(esp) {
if(DMA_IRQ_P(esp->dregs)) {
esp_handle(esp);
}
}
}