📄 acornscsi.c
字号:
/* * linux/drivers/acorn/scsi/acornscsi.c * * Acorn SCSI 3 driver * By R.M.King. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Abandoned using the Select and Transfer command since there were * some nasty races between our software and the target devices that * were not easy to solve, and the device errata had a lot of entries * for this command, some of them quite nasty... * * Changelog: * 26-Sep-1997 RMK Re-jigged to use the queue module. * Re-coded state machine to be based on driver * state not scsi state. Should be easier to debug. * Added acornscsi_release to clean up properly. * Updated proc/scsi reporting. * 05-Oct-1997 RMK Implemented writing to SCSI devices. * 06-Oct-1997 RMK Corrected small (non-serious) bug with the connect/ * reconnect race condition causing a warning message. * 12-Oct-1997 RMK Added catch for re-entering interrupt routine. * 15-Oct-1997 RMK Improved handling of commands. * 27-Jun-1998 RMK Changed asm/delay.h to linux/delay.h. * 13-Dec-1998 RMK Better abort code and command handling. Extra state * transitions added to allow dodgy devices to work. */#define DEBUG_NO_WRITE 1#define DEBUG_QUEUES 2#define DEBUG_DMA 4#define DEBUG_ABORT 8#define DEBUG_DISCON 16#define DEBUG_CONNECT 32#define DEBUG_PHASES 64#define DEBUG_WRITE 128#define DEBUG_LINK 256#define DEBUG_MESSAGES 512#define DEBUG_RESET 1024#define DEBUG_ALL (DEBUG_RESET|DEBUG_MESSAGES|DEBUG_LINK|DEBUG_WRITE|\ DEBUG_PHASES|DEBUG_CONNECT|DEBUG_DISCON|DEBUG_ABORT|\ DEBUG_DMA|DEBUG_QUEUES)/* DRIVER CONFIGURATION * * SCSI-II Tagged queue support. * * I don't have any SCSI devices that support it, so it is totally untested * (except to make sure that it doesn't interfere with any non-tagging * devices). It is not fully implemented either - what happens when a * tagging device reconnects??? * * You can tell if you have a device that supports tagged queueing my * cating (eg) /proc/scsi/acornscsi/0 and see if the SCSI revision is reported * as '2 TAG'. * * Also note that CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE is normally set in the config * scripts, but disabled here. Once debugged, remove the #undef, otherwise to debug, * comment out the undef. */#undef CONFIG_SCSI_ACORNSCSI_TAGGED_QUEUE/* * SCSI-II Linked command support. * * The higher level code doesn't support linked commands yet, and so the option * is undef'd here. */#undef CONFIG_SCSI_ACORNSCSI_LINK/* * SCSI-II Synchronous transfer support. * * Tried and tested... * * SDTR_SIZE - maximum number of un-acknowledged bytes (0 = off, 12 = max) * SDTR_PERIOD - period of REQ signal (min=125, max=1020) * DEFAULT_PERIOD - default REQ period. */#define SDTR_SIZE 12#define SDTR_PERIOD 125#define DEFAULT_PERIOD 500/* * Debugging information * * DEBUG - bit mask from list above * DEBUG_TARGET - is defined to the target number if you want to debug * a specific target. [only recon/write/dma]. */#define DEBUG (DEBUG_RESET|DEBUG_WRITE|DEBUG_NO_WRITE)/* only allow writing to SCSI device 0 */#define NO_WRITE 0xFE/*#define DEBUG_TARGET 2*//* * Select timeout time (in 10ms units) * * This is the timeout used between the start of selection and the WD33C93 * chip deciding that the device isn't responding. */#define TIMEOUT_TIME 10/* * Define this if you want to have verbose explaination of SCSI * status/messages. */#undef CONFIG_ACORNSCSI_CONSTANTS/* * Define this if you want to use the on board DMAC [don't remove this option] * If not set, then use PIO mode (not currently supported). */#define USE_DMAC/* * ==================================================================================== */#ifdef DEBUG_TARGET#define DBG(cmd,xxx...) \ if (cmd->device->id == DEBUG_TARGET) { \ xxx; \ }#else#define DBG(cmd,xxx...) xxx#endif#ifndef STRINGIFY#define STRINGIFY(x) #x#endif#define STRx(x) STRINGIFY(x)#define NO_WRITE_STR STRx(NO_WRITE)#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/string.h>#include <linux/signal.h>#include <linux/errno.h>#include <linux/proc_fs.h>#include <linux/ioport.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/ecard.h>#include "../scsi.h"#include <scsi/scsi_host.h>#include "acornscsi.h"#include "msgqueue.h"#include "scsi.h"#include <scsi/scsicam.h>#define VER_MAJOR 2#define VER_MINOR 0#define VER_PATCH 6#ifndef ABORT_TAG#define ABORT_TAG 0xd#else#error "Yippee! ABORT TAG is now defined! Remove this error!"#endif#ifdef CONFIG_SCSI_ACORNSCSI_LINK#error SCSI2 LINKed commands not supported (yet)!#endif#ifdef USE_DMAC/* * DMAC setup parameters */ #define INIT_DEVCON0 (DEVCON0_RQL|DEVCON0_EXW|DEVCON0_CMP)#define INIT_DEVCON1 (DEVCON1_BHLD)#define DMAC_READ (MODECON_READ)#define DMAC_WRITE (MODECON_WRITE)#define INIT_SBICDMA (CTRL_DMABURST)#define scsi_xferred have_data_in/* * Size of on-board DMA buffer */#define DMAC_BUFFER_SIZE 65536#endif#define STATUS_BUFFER_TO_PRINT 24unsigned int sdtr_period = SDTR_PERIOD;unsigned int sdtr_size = SDTR_SIZE;static void acornscsi_done(AS_Host *host, Scsi_Cmnd **SCpntp, unsigned int result);static int acornscsi_reconnect_finish(AS_Host *host);static void acornscsi_dma_cleanup(AS_Host *host);static void acornscsi_abortcmd(AS_Host *host, unsigned char tag);/* ==================================================================================== * Miscellaneous */static inline voidsbic_arm_write(unsigned int io_port, int reg, int value){ __raw_writeb(reg, io_port); __raw_writeb(value, io_port + 4);}#define sbic_arm_writenext(io,val) \ __raw_writeb((val), (io) + 4)static inlineint sbic_arm_read(unsigned int io_port, int reg){ if(reg == SBIC_ASR) return __raw_readl(io_port) & 255; __raw_writeb(reg, io_port); return __raw_readl(io_port + 4) & 255;}#define sbic_arm_readnext(io) \ __raw_readb((io) + 4)#ifdef USE_DMAC#define dmac_read(io_port,reg) \ inb((io_port) + (reg))#define dmac_write(io_port,reg,value) \ ({ outb((value), (io_port) + (reg)); })#define dmac_clearintr(io_port) \ ({ outb(0, (io_port)); })static inlineunsigned int dmac_address(unsigned int io_port){ return dmac_read(io_port, DMAC_TXADRHI) << 16 | dmac_read(io_port, DMAC_TXADRMD) << 8 | dmac_read(io_port, DMAC_TXADRLO);}staticvoid acornscsi_dumpdma(AS_Host *host, char *where){ unsigned int mode, addr, len; mode = dmac_read(host->dma.io_port, DMAC_MODECON); addr = dmac_address(host->dma.io_port); len = dmac_read(host->dma.io_port, DMAC_TXCNTHI) << 8 | dmac_read(host->dma.io_port, DMAC_TXCNTLO); printk("scsi%d: %s: DMAC %02x @%06x+%04x msk %02x, ", host->host->host_no, where, mode, addr, (len + 1) & 0xffff, dmac_read(host->dma.io_port, DMAC_MASKREG)); printk("DMA @%06x, ", host->dma.start_addr); printk("BH @%p +%04x, ", host->scsi.SCp.ptr, host->scsi.SCp.this_residual); printk("DT @+%04x ST @+%04x", host->dma.transferred, host->scsi.SCp.scsi_xferred); printk("\n");}#endifstaticunsigned long acornscsi_sbic_xfcount(AS_Host *host){ unsigned long length; length = sbic_arm_read(host->scsi.io_port, SBIC_TRANSCNTH) << 16; length |= sbic_arm_readnext(host->scsi.io_port) << 8; length |= sbic_arm_readnext(host->scsi.io_port); return length;}static intacornscsi_sbic_wait(AS_Host *host, int stat_mask, int stat, int timeout, char *msg){ int asr; do { asr = sbic_arm_read(host->scsi.io_port, SBIC_ASR); if ((asr & stat_mask) == stat) return 0; udelay(1); } while (--timeout); printk("scsi%d: timeout while %s\n", host->host->host_no, msg); return -1;}staticint acornscsi_sbic_issuecmd(AS_Host *host, int command){ if (acornscsi_sbic_wait(host, ASR_CIP, 0, 1000, "issuing command")) return -1; sbic_arm_write(host->scsi.io_port, SBIC_CMND, command); return 0;}static voidacornscsi_csdelay(unsigned int cs){ unsigned long target_jiffies, flags; target_jiffies = jiffies + 1 + cs * HZ / 100; local_save_flags(flags); local_irq_enable(); while (time_before(jiffies, target_jiffies)) barrier(); local_irq_restore(flags);}staticvoid acornscsi_resetcard(AS_Host *host){ unsigned int i, timeout; /* assert reset line */ host->card.page_reg = 0x80; outb(host->card.page_reg, host->card.io_page); /* wait 3 cs. SCSI standard says 25ms. */ acornscsi_csdelay(3); host->card.page_reg = 0; outb(host->card.page_reg, host->card.io_page); /* * Should get a reset from the card */ timeout = 1000; do { if (inb(host->card.io_intr) & 8) break; udelay(1); } while (--timeout); if (timeout == 0) printk("scsi%d: timeout while resetting card\n", host->host->host_no); sbic_arm_read(host->scsi.io_port, SBIC_ASR); sbic_arm_read(host->scsi.io_port, SBIC_SSR); /* setup sbic - WD33C93A */ sbic_arm_write(host->scsi.io_port, SBIC_OWNID, OWNID_EAF | host->host->this_id); sbic_arm_write(host->scsi.io_port, SBIC_CMND, CMND_RESET); /* * Command should cause a reset interrupt */ timeout = 1000; do { if (inb(host->card.io_intr) & 8) break; udelay(1); } while (--timeout); if (timeout == 0) printk("scsi%d: timeout while resetting card\n", host->host->host_no); sbic_arm_read(host->scsi.io_port, SBIC_ASR); if (sbic_arm_read(host->scsi.io_port, SBIC_SSR) != 0x01) printk(KERN_CRIT "scsi%d: WD33C93A didn't give enhanced reset interrupt\n", host->host->host_no); sbic_arm_write(host->scsi.io_port, SBIC_CTRL, INIT_SBICDMA | CTRL_IDI); sbic_arm_write(host->scsi.io_port, SBIC_TIMEOUT, TIMEOUT_TIME); sbic_arm_write(host->scsi.io_port, SBIC_SYNCHTRANSFER, SYNCHTRANSFER_2DBA); sbic_arm_write(host->scsi.io_port, SBIC_SOURCEID, SOURCEID_ER | SOURCEID_DSP); host->card.page_reg = 0x40; outb(host->card.page_reg, host->card.io_page); /* setup dmac - uPC71071 */ dmac_write(host->dma.io_port, DMAC_INIT, 0);#ifdef USE_DMAC dmac_write(host->dma.io_port, DMAC_INIT, INIT_8BIT); dmac_write(host->dma.io_port, DMAC_CHANNEL, CHANNEL_0); dmac_write(host->dma.io_port, DMAC_DEVCON0, INIT_DEVCON0); dmac_write(host->dma.io_port, DMAC_DEVCON1, INIT_DEVCON1);#endif host->SCpnt = NULL; host->scsi.phase = PHASE_IDLE; host->scsi.disconnectable = 0; memset(host->busyluns, 0, sizeof(host->busyluns)); for (i = 0; i < 8; i++) { host->device[i].sync_state = SYNC_NEGOCIATE; host->device[i].disconnect_ok = 1; } /* wait 25 cs. SCSI standard says 250ms. */ acornscsi_csdelay(25);}/*============================================================================================= * Utility routines (eg. debug) */#ifdef CONFIG_ACORNSCSI_CONSTANTSstatic char *acornscsi_interrupttype[] = { "rst", "suc", "p/a", "3", "term", "5", "6", "7", "serv", "9", "a", "b", "c", "d", "e", "f"};static signed char acornscsi_map[] = { 0, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, 3, -1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, -1, -1, -1, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 15, 16, 17, 18, 19, -1, -1, 20, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 21, 22, -1, -1, -1, 23, -1, -1, 4, 5, 6, 7, 8, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; static char *acornscsi_interruptcode[] = { /* 0 */ "reset - normal mode", /* 00 */ "reset - advanced mode", /* 01 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -