📄 nsp_cs.c
字号:
/*====================================================================== NinjaSCSI-3 / NinjaSCSI-32Bi PCMCIA SCSI host adapter card driver By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp> Ver.2.8 Support 32bit MMIO mode Support Synchronous Data Transfer Request (SDTR) mode Ver.2.0 Support 32bit PIO mode Ver.1.1.2 Fix for scatter list buffer exceeds Ver.1.1 Support scatter list Ver.0.1 Initial version This software may be used and distributed according to the terms of the GNU General Public License.======================================================================*//*********************************************************************** This driver is for these PCcards. I-O DATA PCSC-F (Workbit NinjaSCSI-3) "WBT", "NinjaSCSI-3", "R1.0" I-O DATA CBSC-II (Workbit NinjaSCSI-32Bi in 16bit mode) "IO DATA", "CBSC16 ", "1"***********************************************************************//* $Id: nsp_cs.c,v 1.25 2003/09/24 10:38:18 elca Exp $ */#include <linux/version.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/major.h>#include <linux/blkdev.h>#include <linux/stat.h>#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))# include <linux/blk.h>#endif#include <asm/io.h>#include <asm/irq.h>#include <../drivers/scsi/scsi.h>#include <../drivers/scsi/hosts.h>#include <scsi/scsi.h>#include <scsi/scsi_ioctl.h>#include <pcmcia/version.h>#include <pcmcia/cs_types.h>#include <pcmcia/cs.h>#include <pcmcia/cistpl.h>#include <pcmcia/cisreg.h>#include <pcmcia/ds.h>#include "nsp_cs.h"MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>");MODULE_DESCRIPTION("WorkBit NinjaSCSI-3 / NinjaSCSI-32Bi(16bit) PCMCIA SCSI host adapter module $Revision: 1.25 $");MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");MODULE_LICENSE("GPL");#include "nsp_io.h"/*====================================================================*//* Parameters that can be set with 'insmod' */static unsigned int irq_mask = 0xffff;MODULE_PARM (irq_mask, "i");MODULE_PARM_DESC(irq_mask, "IRQ mask bits (default: 0xffff)");static int irq_list[4] = { -1 };MODULE_PARM (irq_list, "1-4i");MODULE_PARM_DESC(irq_list, "Use specified IRQ number. (default: auto select)");static int nsp_burst_mode = BURST_MEM32;MODULE_PARM (nsp_burst_mode, "i");MODULE_PARM_DESC(nsp_burst_mode, "Burst transfer mode (0=io8, 1=io32, 2=mem32(default))");/* Release IO ports after configuration? */static int free_ports = 0;MODULE_PARM (free_ports, "i");MODULE_PARM_DESC(free_ports, "Release IO ports after configuration? (default: 0 (=no))");/* /usr/src/linux/drivers/scsi/hosts.h */static Scsi_Host_Template nsp_driver_template = { .proc_name = "nsp_cs", .proc_info = nsp_proc_info, .name = "WorkBit NinjaSCSI-3/32Bi(16bit)",#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) .detect = nsp_detect_old, .release = nsp_release_old,#endif .info = nsp_info, .queuecommand = nsp_queuecommand,/* .eh_strategy_handler = nsp_eh_strategy,*//* .eh_abort_handler = nsp_eh_abort,*//* .eh_device_reset_handler = nsp_eh_device_reset,*/ .eh_bus_reset_handler = nsp_eh_bus_reset, .eh_host_reset_handler = nsp_eh_host_reset, .can_queue = 1, .this_id = NSP_INITIATOR_ID, .sg_tablesize = SG_ALL, .cmd_per_lun = 1, .use_clustering = DISABLE_CLUSTERING,#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2)) .use_new_eh_code = 1,#endif};static dev_link_t *dev_list = NULL;static dev_info_t dev_info = "nsp_cs";static nsp_hw_data nsp_data_base; /* attach <-> detect glue *//****************************************************************** * debug, error print */#ifdef NSP_DEBUG# include "nsp_debug.c"#endif /* NSP_DEBUG */#ifndef NSP_DEBUG# define NSP_DEBUG_MASK 0x000000# define nsp_msg(type, args...) nsp_cs_message("", 0, (type), args)# define nsp_dbg(mask, args...) /* */#else# define NSP_DEBUG_MASK 0xffffff# define nsp_msg(type, args...) \ nsp_cs_message (__FUNCTION__, __LINE__, (type), args)# define nsp_dbg(mask, args...) \ nsp_cs_dmessage(__FUNCTION__, __LINE__, (mask), args)#endif#define NSP_DEBUG_QUEUECOMMAND BIT(0)#define NSP_DEBUG_REGISTER BIT(1)#define NSP_DEBUG_AUTOSCSI BIT(2)#define NSP_DEBUG_INTR BIT(3)#define NSP_DEBUG_SGLIST BIT(4)#define NSP_DEBUG_BUSFREE BIT(5)#define NSP_DEBUG_CDB_CONTENTS BIT(6)#define NSP_DEBUG_RESELECTION BIT(7)#define NSP_DEBUG_MSGINOCCUR BIT(8)#define NSP_DEBUG_EEPROM BIT(9)#define NSP_DEBUG_MSGOUTOCCUR BIT(10)#define NSP_DEBUG_BUSRESET BIT(11)#define NSP_DEBUG_RESTART BIT(12)#define NSP_DEBUG_SYNC BIT(13)#define NSP_DEBUG_WAIT BIT(14)#define NSP_DEBUG_TARGETFLAG BIT(15)#define NSP_DEBUG_PROC BIT(16)#define NSP_DEBUG_INIT BIT(17)#define NSP_DEBUG_DATA_IO BIT(18)#define NSP_SPECIAL_PRINT_REGISTER BIT(20)#define NSP_DEBUG_BUF_LEN 150static void nsp_cs_message(const char *func, int line, char *type, char *fmt, ...){ va_list args; char buf[NSP_DEBUG_BUF_LEN]; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args);#ifndef NSP_DEBUG printk("%snsp_cs: %s\n", type, buf);#else printk("%snsp_cs: %s (%d): %s\n", type, func, line, buf);#endif}#ifdef NSP_DEBUGstatic void nsp_cs_dmessage(const char *func, int line, int mask, char *fmt, ...){ va_list args; char buf[NSP_DEBUG_BUF_LEN]; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); if (mask & NSP_DEBUG_MASK) { printk("nsp_cs-debug: 0x%x %s (%d): %s\n", mask, func, line, buf); }}#endif/***********************************************************//*==================================================== * Clenaup parameters and call done() functions. * You must be set SCpnt->result before call this function. */static void nsp_scsi_done(Scsi_Cmnd *SCpnt){ nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata; data->CurrentSC = NULL; SCpnt->scsi_done(SCpnt);}static int nsp_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){#ifdef NSP_DEBUG /*unsigned int host_id = SCpnt->device->host->this_id;*/ /*unsigned int base = SCpnt->device->host->io_port;*/ unsigned char target = SCpnt->device->id;#endif nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata; nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "SCpnt=0x%p target=%d lun=%d buff=0x%p bufflen=%d use_sg=%d", SCpnt, target, SCpnt->device->lun, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->use_sg); //nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "before CurrentSC=0x%p", data->CurrentSC); SCpnt->scsi_done = done; if (data->CurrentSC != NULL) { nsp_msg(KERN_WARNING, "CurrentSC!=NULL this can't be happen"); SCpnt->result = DID_BAD_TARGET << 16; nsp_scsi_done(SCpnt); return SCSI_MLQUEUE_HOST_BUSY; } show_command(SCpnt); data->CurrentSC = SCpnt; SCpnt->SCp.Status = CHECK_CONDITION; SCpnt->SCp.Message = 0; SCpnt->SCp.have_data_in = IO_UNKNOWN; SCpnt->SCp.sent_command = 0; SCpnt->SCp.phase = PH_UNDETERMINED; SCpnt->resid = SCpnt->request_bufflen; /* setup scratch area SCp.ptr : buffer pointer SCp.this_residual : buffer length SCp.buffer : next buffer SCp.buffers_residual : left buffers in list SCp.phase : current state of the command */ if (SCpnt->use_sg) { SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer; SCpnt->SCp.ptr = SG_ADDRESS(SCpnt->SCp.buffer); SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; } else { SCpnt->SCp.ptr = (char *) SCpnt->request_buffer; SCpnt->SCp.this_residual = SCpnt->request_bufflen; SCpnt->SCp.buffer = NULL; SCpnt->SCp.buffers_residual = 0; } if (nsphw_start_selection(SCpnt) == FALSE) { nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "selection fail"); SCpnt->result = DID_BUS_BUSY << 16; nsp_scsi_done(SCpnt); return SCSI_MLQUEUE_DEVICE_BUSY; } //nsp_dbg(NSP_DEBUG_QUEUECOMMAND, "out");#ifdef NSP_DEBUG data->CmdId++;#endif return 0;}/* * setup PIO FIFO transfer mode and enable/disable to data out */static void nsp_setup_fifo(nsp_hw_data *data, int enabled){ unsigned int base = data->BaseAddress; unsigned char transfer_mode_reg; //nsp_dbg(NSP_DEBUG_DATA_IO, "enabled=%d", enabled); if (enabled != FALSE) { transfer_mode_reg = TRANSFER_GO | BRAIND; } else { transfer_mode_reg = 0; } transfer_mode_reg |= data->TransferMode; nsp_index_write(base, TRANSFERMODE, transfer_mode_reg);}static void nsphw_init_sync(nsp_hw_data *data){ sync_data tmp_sync = { .SyncNegotiation = SYNC_NOT_YET, .SyncPeriod = 0, .SyncOffset = 0 }; int i; /* setup sync data */ for ( i = 0; i < NUMBER(data->Sync); i++ ) { data->Sync[i] = tmp_sync; }}/* * Initialize Ninja hardware */static int nsphw_init(nsp_hw_data *data){ unsigned int base = data->BaseAddress; nsp_dbg(NSP_DEBUG_INIT, "in base=0x%x", base); data->ScsiClockDiv = CLOCK_40M | FAST_20; data->CurrentSC = NULL; data->FifoCount = 0; data->TransferMode = MODE_IO8; nsphw_init_sync(data); /* block all interrupts */ nsp_write(base, IRQCONTROL, IRQCONTROL_ALL_CLEAR_AND_MASK); nsp_write(base, IFSELECT, 0); data->ChipRev = nsp_read(base, FIFOSTATUS); /* setup SCSI interface */ nsp_write(base, IFSELECT, IF_REGSEL); nsp_index_write(base, SCSIIRQMODE, 0); nsp_index_write(base, TRANSFERMODE, MODE_IO8); nsp_index_write(base, CLOCKDIV, data->ScsiClockDiv); nsp_index_write(base, PARITYCTRL, 0); nsp_index_write(base, POINTERCLR, POINTER_CLEAR | ACK_COUNTER_CLEAR | REQ_COUNTER_CLEAR | HOST_COUNTER_CLEAR); /* setup fifo asic */ nsp_write(base, IFSELECT, IF_REGSEL); nsp_index_write(base, TERMPWRCTRL, 0); if ((nsp_index_read(base, OTHERCONTROL) & TPWR_SENSE) == 0) { nsp_msg(KERN_INFO, "terminator power on"); nsp_index_write(base, TERMPWRCTRL, POWER_ON); } nsp_index_write(base, TIMERCOUNT, 0); nsp_index_write(base, TIMERCOUNT, 0); /* requires 2 times!! */ nsp_index_write(base, SYNCREG, 0); nsp_index_write(base, ACKWIDTH, 0); /* enable interrupts and ack them */ nsp_index_write(base, SCSIIRQMODE, SCSI_PHASE_CHANGE_EI | RESELECT_EI | SCSI_RESET_IRQ_EI ); nsp_write(base, IRQCONTROL, IRQCONTROL_ALL_CLEAR); nsp_setup_fifo(data, FALSE); return TRUE;}/* * Start selection phase */static int nsphw_start_selection(Scsi_Cmnd *SCpnt){ unsigned int host_id = SCpnt->device->host->this_id; unsigned int base = SCpnt->device->host->io_port; unsigned char target = SCpnt->device->id; nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata; int time_out; unsigned char phase, arbit; //nsp_dbg(NSP_DEBUG_RESELECTION, "in"); phase = nsp_index_read(base, SCSIBUSMON); if(phase != BUSMON_BUS_FREE) { //nsp_dbg(NSP_DEBUG_RESELECTION, "bus busy"); return FALSE; } /* start arbitration */ //nsp_dbg(NSP_DEBUG_RESELECTION, "start arbit"); SCpnt->SCp.phase = PH_ARBSTART; nsp_index_write(base, SETARBIT, ARBIT_GO); time_out = 1000; do { /* XXX: what a stupid chip! */ arbit = nsp_index_read(base, ARBITSTATUS); //nsp_dbg(NSP_DEBUG_RESELECTION, "arbit=%d, wait_count=%d", arbit, wait_count); udelay(1); /* hold 1.2us */ } while((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 && (time_out-- != 0)); if (!(arbit & ARBIT_WIN)) { //nsp_dbg(NSP_DEBUG_RESELECTION, "arbit fail"); nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR); return FALSE; } /* assert select line */ //nsp_dbg(NSP_DEBUG_RESELECTION, "assert SEL line"); SCpnt->SCp.phase = PH_SELSTART; udelay(3); /* wait 2.4us */ nsp_index_write(base, SCSIDATALATCH, BIT(host_id) | BIT(target)); nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_ATN); udelay(2); /* wait >1.2us */ nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_DATAOUT_ENB | SCSI_ATN); nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR); /*udelay(1);*/ /* wait >90ns */ nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_DATAOUT_ENB | SCSI_ATN); /* check selection timeout */ nsp_start_timer(SCpnt, 1000/51); data->SelectionTimeOut = 1; return TRUE;}/*********************************************************************** * Period/AckWidth speed conversion table * * Note: This period/ackwidth speed table must be in descending order. ***********************************************************************/struct nsp_sync_table { unsigned int chip_period; unsigned int ack_width; unsigned int min_period; unsigned int max_period;};static struct nsp_sync_table nsp_sync_table_40M[] = { /* {PNo, AW, SP, EP} Speed(MB/s) Period AckWidth */ {0x1, 0, 0x0c, 0x0c}, /* 20.0 : 50ns, 25ns */ {0x2, 0, 0x0d, 0x18}, /* 13.3 : 75ns, 25ns */ {0x3, 1, 0x19, 0x19}, /* 10.0 : 100ns, 50ns */ {0x4, 1, 0x1a, 0x1f}, /* 8.0 : 125ns, 50ns */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -