📄 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.23 2003/08/18 11:09:19 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/major.h>#include <linux/blkdev.h>#include <linux/stat.h>#include <asm/io.h>#include <asm/irq.h>#include <../drivers/scsi/scsi.h>#include <scsi/scsi_host.h>#include <scsi/scsi.h>#include <scsi/scsi_ioctl.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.23 $");MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");#ifdef MODULE_LICENSEMODULE_LICENSE("GPL");#endif#include "nsp_io.h"/*====================================================================*//* Parameters that can be set with 'insmod' */static int nsp_burst_mode = BURST_MEM32;module_param(nsp_burst_mode, int, 0);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_param(free_ports, bool, 0);MODULE_PARM_DESC(free_ports, "Release IO ports after configuration? (default: 0 (=no))");/* /usr/src/linux/drivers/scsi/hosts.h */static struct 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_abort_handler = nsp_eh_abort,*/ .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 */#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 = scmd_id(SCpnt);#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_DEBUG, "CurrentSC!=NULL this can't be happen"); SCpnt->result = DID_BAD_TARGET << 16; nsp_scsi_done(SCpnt); return 0; }#if 0 /* XXX: pcmcia-cs generates SCSI command with "scsi_info" utility. This makes kernel crash when suspending... */ if (data->ScsiInfo->stop != 0) { nsp_msg(KERN_INFO, "suspending device. reject command."); SCpnt->result = DID_BAD_TARGET << 16; nsp_scsi_done(SCpnt); return SCSI_MLQUEUE_HOST_BUSY; }#endif 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 = BUFFER_ADDR; 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 0; } //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 < ARRAY_SIZE(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_ALLMASK); /* setup SCSI interface */ nsp_write(base, IFSELECT, IF_IFSEL); 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_ALLCLEAR); 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 = scmd_id(SCpnt); 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;}struct nsp_sync_table { unsigned int min_period; unsigned int max_period; unsigned int chip_period; unsigned int ack_width;};static struct nsp_sync_table nsp_sync_table_40M[] = { {0x0c, 0x0c, 0x1, 0}, /* 20MB 50ns*/ {0x19, 0x19, 0x3, 1}, /* 10MB 100ns*/ {0x1a, 0x25, 0x5, 2}, /* 7.5MB 150ns*/ {0x26, 0x32, 0x7, 3}, /* 5MB 200ns*/ { 0, 0, 0, 0},};static struct nsp_sync_table nsp_sync_table_20M[] = { {0x19, 0x19, 0x1, 0}, /* 10MB 100ns*/ {0x1a, 0x25, 0x2, 0}, /* 7.5MB 150ns*/ {0x26, 0x32, 0x3, 1}, /* 5MB 200ns*/ { 0, 0, 0, 0},};/* * setup synchronous data transfer mode */static int nsp_analyze_sdtr(Scsi_Cmnd *SCpnt){ unsigned char target = scmd_id(SCpnt);// unsigned char lun = SCpnt->device->lun; nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata; sync_data *sync = &(data->Sync[target]); struct nsp_sync_table *sync_table; unsigned int period, offset; int i; nsp_dbg(NSP_DEBUG_SYNC, "in"); period = sync->SyncPeriod; offset = sync->SyncOffset; nsp_dbg(NSP_DEBUG_SYNC, "period=0x%x, offset=0x%x", period, offset); if ((data->ScsiClockDiv & (BIT(0)|BIT(1))) == CLOCK_20M) { sync_table = nsp_sync_table_20M; } else { sync_table = nsp_sync_table_40M; } for ( i = 0; sync_table->max_period != 0; i++, sync_table++) { if ( period >= sync_table->min_period && period <= sync_table->max_period ) { break; } } if (period != 0 && sync_table->max_period == 0) { /* * No proper period/offset found */ nsp_dbg(NSP_DEBUG_SYNC, "no proper period/offset"); sync->SyncPeriod = 0; sync->SyncOffset = 0; sync->SyncRegister = 0; sync->AckWidth = 0; return FALSE; } sync->SyncRegister = (sync_table->chip_period << SYNCREG_PERIOD_SHIFT) | (offset & SYNCREG_OFFSET_MASK); sync->AckWidth = sync_table->ack_width; nsp_dbg(NSP_DEBUG_SYNC, "sync_reg=0x%x, ack_width=0x%x", sync->SyncRegister, sync->AckWidth); return TRUE;}/* * start ninja hardware timer */static void nsp_start_timer(Scsi_Cmnd *SCpnt, int time){ unsigned int base = SCpnt->device->host->io_port; nsp_hw_data *data = (nsp_hw_data *)SCpnt->device->host->hostdata; //nsp_dbg(NSP_DEBUG_INTR, "in SCpnt=0x%p, time=%d", SCpnt, time); data->TimerCount = time; nsp_index_write(base, TIMERCOUNT, time);}/* * wait for bus phase change */static int nsp_negate_signal(Scsi_Cmnd *SCpnt, unsigned char mask, char *str){ unsigned int base = SCpnt->device->host->io_port; unsigned char reg; int time_out; //nsp_dbg(NSP_DEBUG_INTR, "in"); time_out = 100; do { reg = nsp_index_read(base, SCSIBUSMON); if (reg == 0xff) { break; } } while ((time_out-- != 0) && (reg & mask) != 0); if (time_out == 0) { nsp_msg(KERN_DEBUG, " %s signal off timeut", str); } return 0;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -