📄 nsp_cs.c
字号:
/*====================================================================== NinjaSCSI-3 / NinjaSCSI-32Bi PCMCIA SCSI hostadapter card driver By: YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp> 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.42 2001/09/10 10:30:58 elca Exp $ */#ifdef NSP_KERNEL_2_2#include <pcmcia/config.h>#include <pcmcia/k_compat.h>#endif#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/tqueue.h>#include <linux/interrupt.h>#include <linux/module.h>#include <linux/major.h>#include <linux/blk.h>#include <linux/stat.h>#include <asm/io.h>#include <asm/irq.h>#include <../drivers/scsi/scsi.h>#include <../drivers/scsi/hosts.h>#include <../drivers/scsi/sd.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/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");MODULE_SUPPORTED_DEVICE("sd,sr,sg,st");MODULE_LICENSE("GPL");#ifdef PCMCIA_DEBUGstatic int pc_debug = PCMCIA_DEBUG;MODULE_PARM(pc_debug, "i");MODULE_PARM_DESC(pc_debug, "set debug level");static char *version = "$Id: nsp_cs.c,v 1.42 2001/09/10 10:30:58 elca Exp $";#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args)#else#define DEBUG(n, args...) /* */#endif#include "nsp_io.h"/*====================================================================*/typedef struct scsi_info_t { dev_link_t link; struct Scsi_Host *host; int ndev; dev_node_t node[8]; int stop; struct bus_operations *bus;} scsi_info_t;/*----------------------------------------------------------------*/#if (KERNEL_VERSION(2,4,0) > LINUX_VERSION_CODE)#define PROC_SCSI_NSP PROC_SCSI_IBMMCA /* bad hack... */static struct proc_dir_entry proc_scsi_nsp = { PROC_SCSI_NSP, 6, "nsp_cs", S_IFDIR | S_IRUGO | S_IXUGO, 2};#endif/*====================================================================*//* 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");static int irq_list[4] = { -1 };MODULE_PARM(irq_list, "1-4i");MODULE_PARM_DESC(irq_list, "IRQ number list");/*----------------------------------------------------------------*//* driver state info, local to driver */static char nspinfo[100]; /* description *//* /usr/src/linux/drivers/scsi/hosts.h */static Scsi_Host_Template driver_template = {/* next: NULL,*/#if (KERNEL_VERSION(2,3,99) > LINUX_VERSION_CODE) proc_dir: &proc_scsi_nsp, /* kernel 2.2 */#else proc_name: "nsp_cs", /* kernel 2.4 */#endif/* proc_info: NULL,*/ name: "WorkBit NinjaSCSI-3/32Bi", detect: nsp_detect, release: nsp_release, info: nsp_info,/* command: NULL,*/ 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, abort: nsp_abort, reset: nsp_reset,/* slave_attach: NULL,*//* bios_param: NULL,*/ can_queue: 1, this_id: SCSI_INITIATOR_ID, sg_tablesize: SG_ALL, cmd_per_lun: 1,/* present: 0,*//* unchecked_isa_dma: 0,*/ use_clustering: DISABLE_CLUSTERING, use_new_eh_code: 0,/* emulated: 0,*/};static dev_link_t *dev_list = NULL;static dev_info_t dev_info = {"nsp_cs"};static nsp_hw_data nsp_data;/***********************************************************/static int nsp_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){#ifdef PCMCIA_DEBUG //unsigned int host_id = SCpnt->host->this_id; //unsigned int base = SCpnt->host->io_port; unsigned char target = SCpnt->target;#endif nsp_hw_data *data = &nsp_data; DEBUG(0, __FUNCTION__ "() SCpnt=0x%p target=%d lun=%d buff=0x%p bufflen=%d use_sg=%d\n", SCpnt, target, SCpnt->lun, SCpnt->request_buffer, SCpnt->request_bufflen, SCpnt->use_sg); //DEBUG(0, " before CurrentSC=0x%p\n", data->CurrentSC); if(data->CurrentSC != NULL) { printk(KERN_DEBUG " " __FUNCTION__ "() CurrentSC!=NULL this can't be happen\n"); data->CurrentSC = NULL; SCpnt->result = DID_BAD_TARGET << 16; done(SCpnt); return -1; } show_command(SCpnt); SCpnt->scsi_done = done; data->CurrentSC = SCpnt; RESID = SCpnt->request_bufflen; SCpnt->SCp.Status = -1; SCpnt->SCp.Message = -1; SCpnt->SCp.have_data_in = IO_UNKNOWN; SCpnt->SCp.sent_command = 0; SCpnt->SCp.phase = PH_UNDETERMINED; /* 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 = SCpnt->SCp.buffer->address; 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, data) == FALSE) { DEBUG(0, " selection fail\n"); data->CurrentSC = NULL; SCpnt->result = DID_NO_CONNECT << 16; done(SCpnt); return -1; } //DEBUG(0, __FUNCTION__ "() out\n"); 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; //DEBUG(0, __FUNCTION__ "() enabled=%d\n", 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);}/* * Initialize Ninja hardware */static int nsphw_init(nsp_hw_data *data){ unsigned int base = data->BaseAddress; int i, j; sync_data tmp_sync = { SyncNegotiation: SYNC_NOT_YET, SyncPeriod: 0, SyncOffset: 0 }; DEBUG(0, __FUNCTION__ "() in base=0x%x\n", base); data->ScsiClockDiv = CLOCK_40M; data->CurrentSC = NULL; data->FifoCount = 0; data->TransferMode = MODE_IO8; /* setup sync data */ for ( i = 0; i < N_TARGET; i++ ) { for ( j = 0; j < N_LUN; j++ ) { data->Sync[i][j] = tmp_sync; } } /* 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) { printk(KERN_INFO "nsp_cs: terminator power on\n"); 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 unsigned int nsphw_start_selection(Scsi_Cmnd *SCpnt, nsp_hw_data *data){ unsigned int host_id = SCpnt->host->this_id; unsigned int base = SCpnt->host->io_port; unsigned char target = SCpnt->target; int wait_count; unsigned char phase, arbit; //DEBUG(0, __FUNCTION__ "()in\n"); phase = nsp_index_read(base, SCSIBUSMON); if(phase != BUSMON_BUS_FREE) { //DEBUG(0, " bus busy\n"); return FALSE; } /* start arbitration */ //DEBUG(0, " start arbit\n"); SCpnt->SCp.phase = PH_ARBSTART; nsp_index_write(base, SETARBIT, ARBIT_GO); wait_count = jiffies + 10 * HZ; do { /* XXX: what a stupid chip! */ arbit = nsp_index_read(base, ARBITSTATUS); //DEBUG(0, " arbit=%d, wait_count=%d\n", arbit, wait_count); udelay(1); /* hold 1.2us */ } while((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 && time_before(jiffies, wait_count)); if((arbit & ARBIT_WIN) == 0) { //DEBUG(0, " arbit fail\n"); nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR); return FALSE; } /* assert select line */ //DEBUG(0, " assert SEL line\n"); SCpnt->SCp.phase = PH_SELSTART; udelay(3); nsp_index_write(base, SCSIDATALATCH, (1 << host_id) | (1 << target)); nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_ATN); udelay(3); nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_BSY | SCSI_DATAOUT_ENB | SCSI_ATN); nsp_index_write(base, SETARBIT, ARBIT_FLAG_CLEAR); udelay(3); nsp_index_write(base, SCSIBUSCTRL, SCSI_SEL | SCSI_DATAOUT_ENB | SCSI_ATN); /* check selection timeout */ nsp_start_timer(SCpnt, data, 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*/ {0x0, 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*/ {0x0, 0, 0, 0}};/* * setup synchronous data transfer mode */static int nsp_msg(Scsi_Cmnd *SCpnt, nsp_hw_data *data){ unsigned char target = SCpnt->target; unsigned char lun = SCpnt->lun; sync_data *sync = &(data->Sync[target][lun]); struct nsp_sync_table *sync_table; unsigned int period, offset; int i; DEBUG(0, __FUNCTION__ "()\n");/**!**/ period = sync->SyncPeriod; offset = sync->SyncOffset; DEBUG(0, " period=0x%x, offset=0x%x\n", period, offset); if (data->ScsiClockDiv == CLOCK_20M) { sync_table = &nsp_sync_table_20M[0]; } else { sync_table = &nsp_sync_table_40M[0]; } 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 */ DEBUG(0, " no proper period/offset\n"); sync->SyncPeriod = 0; sync->SyncOffset = 0; sync->SyncRegister = 0; sync->AckWidth = 0; sync->SyncNegotiation = SYNC_OK; return FALSE; } sync->SyncRegister = (sync_table->chip_period << SYNCREG_PERIOD_SHIFT) | (offset & SYNCREG_OFFSET_MASK); sync->AckWidth = sync_table->ack_width; sync->SyncNegotiation = SYNC_OK; DEBUG(0, " sync_reg=0x%x, ack_width=0x%x\n", sync->SyncRegister, sync->AckWidth); return TRUE;}/* * start ninja hardware timer */static void nsp_start_timer(Scsi_Cmnd *SCpnt, nsp_hw_data *data, int time){ unsigned int base = SCpnt->host->io_port; //DEBUG(0, __FUNCTION__ "() in SCpnt=0x%p, time=%d\n", 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->host->io_port; unsigned char reg; int count, i = TRUE; //DEBUG(0, __FUNCTION__ "()\n"); count = jiffies + HZ; do { reg = nsp_index_read(base, SCSIBUSMON); if (reg == 0xff) { break; } } while ((i = time_before(jiffies, count)) && (reg & mask) != 0); if (!i) { printk(KERN_DEBUG __FUNCTION__ " %s signal off timeut\n", str); } return 0;}/* * expect Ninja Irq */static int nsp_expect_signal(Scsi_Cmnd *SCpnt, unsigned char current_phase, unsigned char mask){ unsigned int base = SCpnt->host->io_port; int wait_count; unsigned char phase, i_src; //DEBUG(0, __FUNCTION__ "() current_phase=0x%x, mask=0x%x\n", current_phase, mask); wait_count = jiffies + HZ; do { phase = nsp_index_read(base, SCSIBUSMON); if (phase == 0xff) { //DEBUG(0, " ret -1\n"); return -1; } i_src = nsp_read(base, IRQSTATUS); if (i_src & IRQSTATUS_SCSI) { //DEBUG(0, " ret 0 found scsi signal\n"); return 0; } if ((phase & mask) != 0 && (phase & BUSMON_PHASE_MASK) == current_phase) { //DEBUG(0, " ret 1 phase=0x%x\n", phase); return 1; } } while(time_before(jiffies, wait_count)); //DEBUG(0, __FUNCTION__ " : " __FUNCTION__ " timeout\n"); return -1;}/* * transfer SCSI message */static int nsp_xfer(Scsi_Cmnd *SCpnt, nsp_hw_data *data, int phase){ unsigned int base = SCpnt->host->io_port; char *buf = data->MsgBuffer; int len = MIN(MSGBUF_SIZE, data->MsgLen); int ptr; int ret; //DEBUG(0, __FUNCTION__ "()\n"); for (ptr = 0; len > 0; len --, ptr ++) { ret = nsp_expect_signal(SCpnt, phase, BUSMON_REQ); if (ret <= 0) { DEBUG(0, " xfer quit\n"); return 0; } /* if last byte, negate ATN */ if (len == 1 && SCpnt->SCp.phase == PH_MSG_OUT) { nsp_index_write(base, SCSIBUSCTRL, AUTODIRECTION | ACKENB); } /* read & write message */ if (phase & BUSMON_IO) { DEBUG(0, " read msg\n"); buf[ptr] = nsp_index_read(base, SCSIDATAWITHACK); } else { DEBUG(0, " write msg\n"); nsp_index_write(base, SCSIDATAWITHACK, buf[ptr]); } nsp_negate_signal(SCpnt, BUSMON_ACK, "xfer<ack>"); } return len;}/* * get extra SCSI data from fifo */static int nsp_dataphase_bypass(Scsi_Cmnd *SCpnt, nsp_hw_data *data){ unsigned int count; //DEBUG(0, __FUNCTION__ "()\n"); if (SCpnt->SCp.have_data_in != IO_IN) { return 0; } count = nsp_fifo_count(SCpnt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -