?? wd7000.c
字號:
/* $Id: wd7000.c,v 1.2 1994/01/15 06:02:32 drew Exp $
* linux/kernel/wd7000.c
*
* Copyright (C) 1992 Thomas Wuensche
* closely related to the aha1542 driver from Tommy Thorn
* ( as close as different hardware allows on a lowlevel-driver :-) )
*
* Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to
* accomodate Eric Youngdale's modifications to scsi.c. Nov 1992.
*
* Additional changes to support scatter/gather. Dec. 1992. tw/jb
*/
#include <stdarg.h>
#include <linux/kernel.h>
#include <linux/head.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <asm/system.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include "../block/blk.h"
#include "scsi.h"
#include "hosts.h"
/* #define DEBUG */
#include "wd7000.h"
#ifdef DEBUG
#define DEB(x) x
#else
#define DEB(x)
#endif
/*
Driver data structures:
- mb and scbs are required for interfacing with the host adapter.
An SCB has extra fields not visible to the adapter; mb's
_cannot_ do this, since the adapter assumes they are contiguous in
memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
to access them.
- An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each;
the additional bytes are used only by the driver.
- For now, a pool of SCBs are kept in global storage by this driver,
and are allocated and freed as needed.
The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
not when it has finished. Since the SCB must be around for completion,
problems arise when SCBs correspond to OGMBs, which may be reallocated
earlier (or delayed unnecessarily until a command completes).
Mailboxes are used as transient data structures, simply for
carrying SCB addresses to/from the 7000-FASST2.
Note also since SCBs are not "permanently" associated with mailboxes,
there is no need to keep a global list of Scsi_Cmnd pointers indexed
by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox
indices need not be involved.
*/
static struct {
struct wd_mailbox ogmb[OGMB_CNT];
struct wd_mailbox icmb[ICMB_CNT];
} mb;
static int next_ogmb = 0; /* to reduce contention at mailboxes */
static Scb scbs[MAX_SCBS];
static Scb *scbfree = NULL;
static int wd7000_host = 0;
static unchar controlstat = 0;
static unchar rev_1 = 0, rev_2 = 0; /* filled in by wd7000_revision */
#define wd7000_intr_ack() outb(0,INTR_ACK)
#define WAITnexttimeout 3000000
static inline void wd7000_enable_intr(void)
{
controlstat |= INT_EN;
outb(controlstat,CONTROL);
}
static inline void wd7000_enable_dma(void)
{
controlstat |= DMA_EN;
outb(controlstat,CONTROL);
set_dma_mode(DMA_CH, DMA_MODE_CASCADE);
enable_dma(DMA_CH);
}
#define WAIT(port, mask, allof, noneof) \
{ register WAITbits; \
register WAITtimeout = WAITnexttimeout; \
while (1) { \
WAITbits = inb(port) & (mask); \
if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
break; \
if (--WAITtimeout == 0) goto fail; \
} \
}
static inline void delay( unsigned how_long )
{
unsigned long time = jiffies + how_long;
while (jiffies < time);
}
static inline int command_out(unchar *cmdp, int len)
{
while (len--) {
WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
outb(*cmdp++, COMMAND);
}
return 1;
fail:
printk("wd7000_out WAIT failed(%d): ", len+1);
return 0;
}
static inline Scb *alloc_scb(void)
{
Scb *scb;
unsigned long flags;
save_flags(flags);
cli();
if (scbfree == NULL) {
panic("wd7000: can't allocate free SCB.\n");
restore_flags(flags);
return NULL;
}
scb = scbfree; scbfree = scb->next;
memset(scb, 0, sizeof(Scb)); scb->next = NULL;
restore_flags(flags);
return scb;
}
static inline void free_scb( Scb *scb )
{
unsigned long flags;
save_flags(flags);
cli();
memset(scb, 0, sizeof(Scb));
scb->next = scbfree; scbfree = scb;
restore_flags(flags);
}
static inline void init_scbs(void)
{
int i;
unsigned long flags;
save_flags(flags);
cli();
scbfree = &(scbs[0]);
for (i = 0; i < MAX_SCBS-1; i++) scbs[i].next = &(scbs[i+1]);
scbs[MAX_SCBS-1].next = NULL;
restore_flags(flags);
}
static int mail_out( Scb *scbptr )
/*
* Note: this can also be used for ICBs; just cast to the parm type.
*/
{
int i, ogmb;
unsigned long flags;
DEB(printk("wd7000_scb_out: %06x");)
/* We first look for a free outgoing mailbox */
save_flags(flags);
cli();
ogmb = next_ogmb;
for (i = 0; i < OGMB_CNT; i++) {
if (mb.ogmb[ogmb].status == 0) {
DEB(printk(" using OGMB %x",ogmb));
mb.ogmb[ogmb].status = 1;
any2scsi(mb.ogmb[ogmb].scbptr, scbptr);
next_ogmb = (ogmb+1) % OGMB_CNT;
break;
} else
ogmb = (++ogmb) % OGMB_CNT;
}
restore_flags(flags);
DEB(printk(", scb is %x",scbptr);)
if (i >= OGMB_CNT) {
DEB(printk(", no free OGMBs.\n");)
/* Alternatively, issue "interrupt on free OGMB", and sleep... */
return 0;
}
wd7000_enable_intr();
do {
WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
outb(START_OGMB|ogmb, COMMAND);
WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
} while (inb(ASC_STAT) & CMD_REJ);
DEB(printk(", awaiting interrupt.\n");)
return 1;
fail:
DEB(printk(", WAIT timed out.\n");)
return 0;
}
int make_code(unsigned hosterr, unsigned scsierr)
{
#ifdef DEBUG
int in_error = hosterr;
#endif
switch ((hosterr>>8)&0xff){
case 0: /* Reserved */
hosterr = DID_ERROR;
break;
case 1: /* Command Complete, no errors */
hosterr = DID_OK;
break;
case 2: /* Command complete, error logged in scb status (scsierr) */
hosterr = DID_OK;
break;
case 4: /* Command failed to complete - timeout */
hosterr = DID_TIME_OUT;
break;
case 5: /* Command terminated; Bus reset by external device */
hosterr = DID_RESET;
break;
case 6: /* Unexpected Command Received w/ host as target */
hosterr = DID_BAD_TARGET;
break;
case 80: /* Unexpected Reselection */
case 81: /* Unexpected Selection */
hosterr = DID_BAD_INTR;
break;
case 82: /* Abort Command Message */
hosterr = DID_ABORT;
break;
case 83: /* SCSI Bus Software Reset */
case 84: /* SCSI Bus Hardware Reset */
hosterr = DID_RESET;
break;
default: /* Reserved */
hosterr = DID_ERROR;
break;
}
#ifdef DEBUG
if (scsierr||hosterr)
printk("\nSCSI command error: SCSI %02x host %04x return %d",
scsierr,in_error,hosterr);
#endif
return scsierr | (hosterr << 16);
}
static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
{
DEB(printk("wd7000_scsi_done: %06x\n",SCpnt);)
SCpnt->SCp.phase = 0;
}
void wd7000_intr_handle(int irq)
{
int flag, icmb, errstatus, icmb_status;
int host_error, scsi_error;
Scb *scb; /* for SCSI commands */
unchar *icb; /* for host commands */
Scsi_Cmnd *SCpnt;
flag = inb(INTR_STAT);
DEB(printk("wd7000_intr_handle: intr stat = %02x",flag);)
if (!(inb(ASC_STAT)&0x80)){
DEB(printk("\nwd7000_intr_handle: phantom interrupt...\n");)
wd7000_intr_ack();
return;
}
/* check for an incoming mailbox */
if ((flag & 0x40) == 0) {
/* for a free OGMB - need code for this case... */
DEB(printk("wd7000_intr_handle: free outgoing mailbox\n");)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -