📄 fas216.c
字号:
/* * linux/arch/arm/drivers/scsi/fas216.c * * Copyright (C) 1997-2000 Russell 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. * * Based on information in qlogicfas.c by Tom Zerucha, Michael Griffith, and * other sources, including: * the AMD Am53CF94 data sheet * the AMD Am53C94 data sheet * * This is a generic driver. To use it, have a look at cumana_2.c. You * should define your own structure that overlays FAS216_Info, eg: * struct my_host_data { * FAS216_Info info; * ... my host specific data ... * }; * * Changelog: * 30-08-1997 RMK Created * 14-09-1997 RMK Started disconnect support * 08-02-1998 RMK Corrected real DMA support * 15-02-1998 RMK Started sync xfer support * 06-04-1998 RMK Tightened conditions for printing incomplete * transfers * 02-05-1998 RMK Added extra checks in fas216_reset * 24-05-1998 RMK Fixed synchronous transfers with period >= 200ns * 27-06-1998 RMK Changed asm/delay.h to linux/delay.h * 26-08-1998 RMK Improved message support wrt MESSAGE_REJECT * 02-04-2000 RMK Converted to use the new error handling, and * automatically request sense data upon check * condition status from targets. * * Todo: * - allow individual devices to enable sync xfers. */#include <linux/module.h>#include <linux/blk.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/unistd.h>#include <linux/stat.h>#include <linux/delay.h>#include <linux/init.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/ecard.h>#define FAS216_C#include "../../scsi/scsi.h"#include "../../scsi/hosts.h"#include "fas216.h"#define VER_MAJOR 0#define VER_MINOR 0#define VER_PATCH 5/* NOTE: SCSI2 Synchronous transfers *require* DMA according to * the data sheet. This restriction is crazy, especially when * you only want to send 16 bytes! What were the guys who * designed this chip on at that time? Did they read the SCSI2 * spec at all? The following sections are taken from the SCSI2 * standard (s2r10) concerning this: * * > IMPLEMENTORS NOTES: * > (1) Re-negotiation at every selection is not recommended, since a * > significant performance impact is likely. * * > The implied synchronous agreement shall remain in effect until a BUS DEVICE * > RESET message is received, until a hard reset condition occurs, or until one * > of the two SCSI devices elects to modify the agreement. The default data * > transfer mode is asynchronous data transfer mode. The default data transfer * > mode is entered at power on, after a BUS DEVICE RESET message, or after a hard * > reset condition. * * In total, this means that once you have elected to use synchronous * transfers, you must always use DMA. * * I was thinking that this was a good chip until I found this restriction ;( */#define SCSI2_SYNC#undef SCSI2_WIDE#undef SCSI2_TAG#undef DEBUG_CONNECT#undef DEBUG_BUSSERVICE#undef DEBUG_FUNCTIONDONE#undef DEBUG_MESSAGES#undef CHECK_STRUCTUREstatic struct { int stat, ssr, isr, ph; } list[8];static int ptr;static void fas216_dumpstate(FAS216_Info *info){ unsigned char is, stat, inst; is = inb(REG_IS(info)); stat = inb(REG_STAT(info)); inst = inb(REG_INST(info)); printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" " INST=%02X IS=%02X CFIS=%02X", inb(REG_CTCL(info)), inb(REG_CTCM(info)), inb(REG_CMD(info)), stat, inst, is, inb(REG_CFIS(info))); printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", inb(REG_CNTL1(info)), inb(REG_CNTL2(info)), inb(REG_CNTL3(info)), inb(REG_CTCH(info)));}static void fas216_dumpinfo(FAS216_Info *info){ static int used = 0; int i; if (used++) return; printk("FAS216_Info=\n"); printk(" { magic_start=%lX host=%p SCpnt=%p origSCpnt=%p\n", info->magic_start, info->host, info->SCpnt, info->origSCpnt); printk(" scsi={ io_port=%X io_shift=%X irq=%X cfg={ %X %X %X %X }\n", info->scsi.io_port, info->scsi.io_shift, info->scsi.irq, info->scsi.cfg[0], info->scsi.cfg[1], info->scsi.cfg[2], info->scsi.cfg[3]); printk(" type=%p phase=%X reconnected={ target=%d lun=%d tag=%d }\n", info->scsi.type, info->scsi.phase, info->scsi.reconnected.target, info->scsi.reconnected.lun, info->scsi.reconnected.tag); printk(" SCp={ ptr=%p this_residual=%X buffer=%p buffers_residual=%X }\n", info->scsi.SCp.ptr, info->scsi.SCp.this_residual, info->scsi.SCp.buffer, info->scsi.SCp.buffers_residual); printk(" msgs async_stp=%X disconnectable=%d aborting=%d }\n", info->scsi.async_stp, info->scsi.disconnectable, info->scsi.aborting); printk(" stats={ queues=%X removes=%X fins=%X reads=%X writes=%X miscs=%X\n" " disconnects=%X aborts=%X bus_resets=%X host_resets=%X}\n", info->stats.queues, info->stats.removes, info->stats.fins, info->stats.reads, info->stats.writes, info->stats.miscs, info->stats.disconnects, info->stats.aborts, info->stats.bus_resets, info->stats.host_resets); printk(" ifcfg={ clockrate=%X select_timeout=%X asyncperiod=%X sync_max_depth=%X }\n", info->ifcfg.clockrate, info->ifcfg.select_timeout, info->ifcfg.asyncperiod, info->ifcfg.sync_max_depth); for (i = 0; i < 8; i++) { printk(" busyluns[%d]=%X dev[%d]={ disconnect_ok=%d stp=%X sof=%X sync_state=%X }\n", i, info->busyluns[i], i, info->device[i].disconnect_ok, info->device[i].stp, info->device[i].sof, info->device[i].sync_state); } printk(" dma={ transfer_type=%X setup=%p pseudo=%p stop=%p }\n", info->dma.transfer_type, info->dma.setup, info->dma.pseudo, info->dma.stop); printk(" internal_done=%X magic_end=%lX }\n", info->internal_done, info->magic_end);}#ifdef CHECK_STRUCTUREstatic void __fas216_checkmagic(FAS216_Info *info, const char *func){ int corruption = 0; if (info->magic_start != MAGIC) { printk(KERN_CRIT "FAS216 Error: magic at start corrupted\n"); corruption++; } if (info->magic_end != MAGIC) { printk(KERN_CRIT "FAS216 Error: magic at end corrupted\n"); corruption++; } if (corruption) { fas216_dumpinfo(info); panic("scsi memory space corrupted in %s", func); }}#define fas216_checkmagic(info) __fas216_checkmagic((info), __FUNCTION__)#else#define fas216_checkmagic(info)#endifstatic const char *fas216_bus_phase(int stat){ static const char *phases[] = { "DATA OUT", "DATA IN", "COMMAND", "STATUS", "MISC OUT", "MISC IN", "MESG OUT", "MESG IN" }; return phases[stat & STAT_BUSMASK];}static const char *fas216_drv_phase(FAS216_Info *info){ switch (info->scsi.phase) { case PHASE_IDLE: return "idle"; case PHASE_SELECTION: return "selection"; case PHASE_COMMAND: return "command"; case PHASE_RECONNECTED: return "reconnected"; case PHASE_DATAOUT: return "data out"; case PHASE_DATAIN: return "data in"; case PHASE_MSGIN: return "message in"; case PHASE_MSGIN_DISCONNECT: return "disconnect"; case PHASE_MSGOUT_EXPECT: return "expect message out"; case PHASE_MSGOUT: return "message out"; case PHASE_STATUS: return "status"; case PHASE_DONE: return "done"; default: return "???"; }}static char fas216_target(FAS216_Info *info){ if (info->SCpnt) return '0' + info->SCpnt->target; else return 'H';}static void add_debug_list(int stat, int ssr, int isr, int ph){ list[ptr].stat = stat; list[ptr].ssr = ssr; list[ptr].isr = isr; list[ptr].ph = ph; ptr = (ptr + 1) & 7;}static void print_debug_list(void){ int i; i = ptr; printk(KERN_ERR "SCSI IRQ trail: "); do { printk("%02X:%02X:%02X:%1X ", list[i].stat, list[i].ssr, list[i].isr, list[i].ph); i = (i + 1) & 7; } while (i != ptr); printk("\n");}static void fas216_done(FAS216_Info *info, unsigned int result);/* Function: int fas216_clockrate(unsigned int clock) * Purpose : calculate correct value to be written into clock conversion * factor register. * Params : clock - clock speed in MHz * Returns : CLKF_ value */static int fas216_clockrate(int clock){ if (clock <= 10 || clock > 40) { printk(KERN_CRIT "fas216: invalid clock rate: check your driver!\n"); clock = -1; } else clock = ((clock - 1) / 5 + 1) & 7; return clock;}/* Function: unsigned short fas216_get_last_msg(FAS216_Info *info, int pos) * Purpose : retrieve a last message from the list, using position in fifo * Params : info - interface to search * : pos - current fifo position */static inline unsigned shortfas216_get_last_msg(FAS216_Info *info, int pos){ unsigned short packed_msg = NOP; struct message *msg; int msgnr = 0; while ((msg = msgqueue_getmsg(&info->scsi.msgs, msgnr++)) != NULL) { if (pos >= msg->fifo) break; } if (msg) { if (msg->msg[0] == EXTENDED_MESSAGE) packed_msg = EXTENDED_MESSAGE | msg->msg[2] << 8; else packed_msg = msg->msg[0]; }#ifdef DEBUG_MESSAGES printk("Message: %04X found at position %02X\n", packed_msg, pos);#endif return packed_msg;}/* Function: int fas216_syncperiod(FAS216_Info *info, int ns) * Purpose : Calculate value to be loaded into the STP register * for a given period in ns * Params : info - state structure for interface connected to device * : ns - period in ns (between subsequent bytes) * Returns : Value suitable for REG_STP */static intfas216_syncperiod(FAS216_Info *info, int ns){ int value = (info->ifcfg.clockrate * ns) / 1000; fas216_checkmagic(info); if (value < 4) value = 4; else if (value > 35) value = 35; return value & 31;}/* Function: void fas216_set_sync(FAS216_Info *info, int target) * Purpose : Correctly setup FAS216 chip for specified transfer period. * Params : info - state structure for interface * : target - target * Notes : we need to switch the chip out of FASTSCSI mode if we have * a transfer period >= 200ns - otherwise the chip will violate * the SCSI timings. */static voidfas216_set_sync(FAS216_Info *info, int target){ outb(info->device[target].sof, REG_SOF(info)); outb(info->device[target].stp, REG_STP(info)); if (info->device[target].period >= (200 / 4)) outb(info->scsi.cfg[2] & ~CNTL3_FASTSCSI, REG_CNTL3(info)); else outb(info->scsi.cfg[2], REG_CNTL3(info));}/* Synchronous transfer support * * Note: The SCSI II r10 spec says (5.6.12): * * (2) Due to historical problems with early host adapters that could * not accept an SDTR message, some targets may not initiate synchronous * negotiation after a power cycle as required by this standard. Host * adapters that support synchronous mode may avoid the ensuing failure * modes when the target is independently power cycled by initiating a * synchronous negotiation on each REQUEST SENSE and INQUIRY command. * This approach increases the SCSI bus overhead and is not recommended * for new implementations. The correct method is to respond to an * SDTR message with a MESSAGE REJECT message if the either the * initiator or target devices does not support synchronous transfers * or does not want to negotiate for synchronous transfers at the time. * Using the correct method assures compatibility with wide data * transfers and future enhancements. * * We will always initiate a synchronous transfer negociation request on * every INQUIRY or REQUEST SENSE message, unless the target itself has * at some point performed a synchronous transfer negociation request, or * we have synchronous transfers disabled for this device. *//* Function: void fas216_handlesync(FAS216_Info *info, char *msg) * Purpose : Handle a synchronous transfer message from the target * Params : info - state structure for interface * : msg - message from target */static voidfas216_handlesync(FAS216_Info *info, char *msg){ struct fas216_device *dev = &info->device[info->SCpnt->target]; enum { sync, async, none, reject } res = none;#ifdef SCSI2_SYNC switch (msg[0]) { case MESSAGE_REJECT: /* Synchronous transfer request failed. * Note: SCSI II r10: * * SCSI devices that are capable of synchronous * data transfers shall not respond to an SDTR * message with a MESSAGE REJECT message. * * Hence, if we get this condition, we disable * negociation for this device. */ if (dev->sync_state == neg_inprogress) { dev->sync_state = neg_invalid; res = async; } break; case EXTENDED_MESSAGE: switch (dev->sync_state) { /* We don't accept synchronous transfer requests. * Respond with a MESSAGE_REJECT to prevent a * synchronous transfer agreement from being reached. */ case neg_invalid: res = reject; break; /* We were not negociating a synchronous transfer, * but the device sent us a negociation request. * Honour the request by sending back a SDTR * message containing our capability, limited by * the targets capability. */ default: outb(CMD_SETATN, REG_CMD(info)); if (msg[4] > info->ifcfg.sync_max_depth) msg[4] = info->ifcfg.sync_max_depth; if (msg[3] < 1000 / info->ifcfg.clockrate) msg[3] = 1000 / info->ifcfg.clockrate; msgqueue_flush(&info->scsi.msgs); msgqueue_addmsg(&info->scsi.msgs, 5, EXTENDED_MESSAGE, 3, EXTENDED_SDTR, msg[3], msg[4]); info->scsi.phase = PHASE_MSGOUT_EXPECT; /* This is wrong. The agreement is not in effect * until this message is accepted by the device */ dev->sync_state = neg_targcomplete; res = sync; break; /* We initiated the synchronous transfer negociation, * and have successfully received a response from the * target. The synchronous transfer agreement has been * reached. Note: if the values returned are out of our * bounds, we must reject the message. */ case neg_inprogress: res = reject; if (msg[4] <= info->ifcfg.sync_max_depth && msg[3] >= 1000 / info->ifcfg.clockrate) { dev->sync_state = neg_complete; res = sync; } break; } }#else res = reject;#endif switch (res) { case sync: dev->period = msg[3]; dev->sof = msg[4]; dev->stp = fas216_syncperiod(info, msg[3] * 4); fas216_set_sync(info, info->SCpnt->target);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -