📄 fas216.c
字号:
/* * linux/drivers/acorn/scsi/fas216.c * * Copyright (C) 1997-2003 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. */#include <linux/module.h>#include <linux/blkdev.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <linux/bitops.h>#include <linux/init.h>#include <linux/interrupt.h>#include <asm/dma.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/ecard.h>#include "../scsi.h"#include <scsi/scsi_dbg.h>#include <scsi/scsi_host.h>#include "fas216.h"#include "scsi.h"/* 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_TAG#undef DEBUG_CONNECT#undef DEBUG_MESSAGES#undef CHECK_STRUCTURE#define LOG_CONNECT (1 << 0)#define LOG_BUSSERVICE (1 << 1)#define LOG_FUNCTIONDONE (1 << 2)#define LOG_MESSAGES (1 << 3)#define LOG_BUFFER (1 << 4)#define LOG_ERROR (1 << 8)static int level_mask = LOG_ERROR;module_param(level_mask, int, 0644);static int __init fas216_log_setup(char *str){ char *s; level_mask = 0; while ((s = strsep(&str, ",")) != NULL) { switch (s[0]) { case 'a': if (strcmp(s, "all") == 0) level_mask |= -1; break; case 'b': if (strncmp(s, "bus", 3) == 0) level_mask |= LOG_BUSSERVICE; if (strncmp(s, "buf", 3) == 0) level_mask |= LOG_BUFFER; break; case 'c': level_mask |= LOG_CONNECT; break; case 'e': level_mask |= LOG_ERROR; break; case 'm': level_mask |= LOG_MESSAGES; break; case 'n': if (strcmp(s, "none") == 0) level_mask = 0; break; case 's': level_mask |= LOG_FUNCTIONDONE; break; } } return 1;}__setup("fas216_logging=", fas216_log_setup);static inline unsigned char fas216_readb(FAS216_Info *info, unsigned int reg){ unsigned int off = reg << info->scsi.io_shift; return readb(info->scsi.io_base + off);}static inline void fas216_writeb(FAS216_Info *info, unsigned int reg, unsigned int val){ unsigned int off = reg << info->scsi.io_shift; writeb(val, info->scsi.io_base + off);}static void fas216_dumpstate(FAS216_Info *info){ unsigned char is, stat, inst; is = fas216_readb(info, REG_IS); stat = fas216_readb(info, REG_STAT); inst = fas216_readb(info, REG_INST); printk("FAS216: CTCL=%02X CTCM=%02X CMD=%02X STAT=%02X" " INST=%02X IS=%02X CFIS=%02X", fas216_readb(info, REG_CTCL), fas216_readb(info, REG_CTCM), fas216_readb(info, REG_CMD), stat, inst, is, fas216_readb(info, REG_CFIS)); printk(" CNTL1=%02X CNTL2=%02X CNTL3=%02X CTCH=%02X\n", fas216_readb(info, REG_CNTL1), fas216_readb(info, REG_CNTL2), fas216_readb(info, REG_CNTL3), fas216_readb(info, REG_CTCH));}static void print_SCp(struct scsi_pointer *SCp, const char *prefix, const char *suffix){ printk("%sptr %p this_residual 0x%x buffer %p buffers_residual 0x%x%s", prefix, SCp->ptr, SCp->this_residual, SCp->buffer, SCp->buffers_residual, suffix);}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_shift=%X irq=%X cfg={ %X %X %X %X }\n", 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\n", info->scsi.type, info->scsi.phase); print_SCp(&info->scsi.SCp, " SCp={ ", " }\n"); 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]=%08lx 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){ static const char *phases[] = { [PHASE_IDLE] = "idle", [PHASE_SELECTION] = "selection", [PHASE_COMMAND] = "command", [PHASE_DATAOUT] = "data out", [PHASE_DATAIN] = "data in", [PHASE_MSGIN] = "message in", [PHASE_MSGIN_DISCONNECT]= "disconnect", [PHASE_MSGOUT_EXPECT] = "expect message out", [PHASE_MSGOUT] = "message out", [PHASE_STATUS] = "status", [PHASE_DONE] = "done", }; if (info->scsi.phase < ARRAY_SIZE(phases) && phases[info->scsi.phase]) return phases[info->scsi.phase]; return "???";}static char fas216_target(FAS216_Info *info){ if (info->SCpnt) return '0' + info->SCpnt->device->id; else return 'H';}static voidfas216_do_log(FAS216_Info *info, char target, char *fmt, va_list ap){ static char buf[1024]; vsnprintf(buf, sizeof(buf), fmt, ap); printk("scsi%d.%c: %s", info->host->host_no, target, buf);}static voidfas216_log_command(FAS216_Info *info, int level, Scsi_Cmnd *SCpnt, char *fmt, ...){ va_list args; if (level != 0 && !(level & level_mask)) return; va_start(args, fmt); fas216_do_log(info, '0' + SCpnt->device->id, fmt, args); va_end(args); printk(" CDB: "); __scsi_print_command(SCpnt->cmnd);}static voidfas216_log_target(FAS216_Info *info, int level, int target, char *fmt, ...){ va_list args; if (level != 0 && !(level & level_mask)) return; if (target < 0) target = 'H'; else target += '0'; va_start(args, fmt); fas216_do_log(info, target, fmt, args); va_end(args); printk("\n");}static void fas216_log(FAS216_Info *info, int level, char *fmt, ...){ va_list args; if (level != 0 && !(level & level_mask)) return; va_start(args, fmt); fas216_do_log(info, fas216_target(info), fmt, args); va_end(args); printk("\n");}#define PH_SIZE 32static struct { int stat, ssr, isr, ph; } ph_list[PH_SIZE];static int ph_ptr;static void add_debug_list(int stat, int ssr, int isr, int ph){ ph_list[ph_ptr].stat = stat; ph_list[ph_ptr].ssr = ssr; ph_list[ph_ptr].isr = isr; ph_list[ph_ptr].ph = ph; ph_ptr = (ph_ptr + 1) & (PH_SIZE-1);}static struct { int command; void *from; } cmd_list[8];static int cmd_ptr;static void fas216_cmd(FAS216_Info *info, unsigned int command){ cmd_list[cmd_ptr].command = command; cmd_list[cmd_ptr].from = __builtin_return_address(0); cmd_ptr = (cmd_ptr + 1) & 7; fas216_writeb(info, REG_CMD, command);}static void print_debug_list(void){ int i; i = ph_ptr; printk(KERN_ERR "SCSI IRQ trail\n"); do { printk(" %02x:%02x:%02x:%1x", ph_list[i].stat, ph_list[i].ssr, ph_list[i].isr, ph_list[i].ph); i = (i + 1) & (PH_SIZE - 1); if (((i ^ ph_ptr) & 7) == 0) printk("\n"); } while (i != ph_ptr); if ((i ^ ph_ptr) & 7) printk("\n"); i = cmd_ptr; printk(KERN_ERR "FAS216 commands: "); do { printk("%02x:%p ", cmd_list[i].command, cmd_list[i].from); i = (i + 1) & 7; } while (i != cmd_ptr); printk("\n");}static void fas216_done(FAS216_Info *info, unsigned int result);/** * fas216_get_last_msg - retrive last message from the list * @info: interface to search * @pos: current fifo position * * Retrieve a last message from the list, using position in fifo. */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]; } fas216_log(info, LOG_MESSAGES, "Message: %04x found at position %02x\n", packed_msg, pos); return packed_msg;}/** * fas216_syncperiod - calculate STP register value * @info: state structure for interface connected to device * @ns: period in ns (between subsequent bytes) * * Calculate value to be loaded into the STP register for a given period * in ns. Returns a value suitable for REG_STP. */static int fas216_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;}/** * fas216_set_sync - setup FAS216 chip for specified transfer period. * @info: state structure for interface connected to device * @target: target * * Correctly setup FAS216 chip for specified transfer period. * 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 void fas216_set_sync(FAS216_Info *info, int target){ unsigned int cntl3; fas216_writeb(info, REG_SOF, info->device[target].sof); fas216_writeb(info, REG_STP, info->device[target].stp); cntl3 = info->scsi.cfg[2]; if (info->device[target].period >= (200 / 4)) cntl3 = cntl3 & ~CNTL3_FASTSCSI; fas216_writeb(info, REG_CNTL3, cntl3);}/* 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -