📄 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 3000000static 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 + -