⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nsp_cs.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/*======================================================================    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 + -