nsp32.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,409 行 · 第 1/5 页

C
2,409
字号
	unsigned char	target  = SCpnt->device->id;	nsp32_autoparam *param  = data->autoparam;	unsigned char	phase;	int		i, ret;	unsigned int	msgout;	u16_le	        s;	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");	/*	 * check bus free	 */	phase = nsp32_read1(base, SCSI_BUS_MONITOR);	if (phase != BUSMON_BUS_FREE) {		nsp32_msg(KERN_WARNING, "bus busy");		show_busphase(phase & BUSMON_PHASE_MASK);		SCpnt->result = DID_BUS_BUSY << 16;		return FALSE;	}	/*	 * message out	 *	 * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.	 *       over 3 messages needs another routine.	 */	if (data->msgout_len == 0) {		nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");		SCpnt->result = DID_ERROR << 16;		return FALSE;	} else if (data->msgout_len > 0 && data->msgout_len <= 3) {		msgout = 0;		for (i = 0; i < data->msgout_len; i++) {			/*			 * the sending order of the message is:			 *  MCNT 3: MSG#0 -> MSG#1 -> MSG#2			 *  MCNT 2:          MSG#1 -> MSG#2			 *  MCNT 1:                   MSG#2    			 */			msgout >>= 8;			msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);		}		msgout |= MV_VALID;	/* MV valid */		msgout |= (unsigned int)data->msgout_len; /* len */	} else {		/* data->msgout_len > 3 */		msgout = 0;	}	// nsp_dbg(NSP32_DEBUG_AUTOSCSI, "sel time out=0x%x\n", nsp32_read2(base, SEL_TIME_OUT));	// nsp32_write2(base, SEL_TIME_OUT,   SEL_TIMEOUT_TIME);	/*	 * setup asic parameter	 */	memset(param, 0, sizeof(nsp32_autoparam));	/* cdb */	for (i = 0; i < SCpnt->cmd_len; i++) {		param->cdb[4 * i] = SCpnt->cmnd[i];	}	/* outgoing messages */	param->msgout = cpu_to_le32(msgout);	/* syncreg, ackwidth, target id, SREQ sampling rate */	param->syncreg    = data->cur_target->syncreg;	param->ackwidth   = data->cur_target->ackwidth;	param->target_id  = BIT(host_id) | BIT(target);	param->sample_reg = data->cur_target->sample_reg;	// nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "sample rate=0x%x\n", data->cur_target->sample_reg);	/* command control */	param->command_control = cpu_to_le16(CLEAR_CDB_FIFO_POINTER |					     AUTOSCSI_START         |					     AUTO_MSGIN_00_OR_04    |					     AUTO_MSGIN_02          |					     AUTO_ATN               );	/* transfer control */	s = 0;	switch (data->trans_method) {	case NSP32_TRANSFER_BUSMASTER:		s |= BM_START;		break;	case NSP32_TRANSFER_MMIO:		s |= CB_MMIO_MODE;		break;	case NSP32_TRANSFER_PIO:		s |= CB_IO_MODE;		break;	default:		nsp32_msg(KERN_ERR, "unknown trans_method");		break;	}	/*	 * OR-ed BLIEND_MODE, FIFO intr is decreased, instead of PCI bus waits.	 * For bus master transfer, it's taken off.	 */	s |= (TRANSFER_GO | ALL_COUNTER_CLR);	param->transfer_control = cpu_to_le16(s);	/* sg table addr */	param->sgt_pointer = cpu_to_le32(data->cur_lunt->sglun_paddr);	/*	 * transfer parameter to ASIC	 */	nsp32_write4(base, SGT_ADR,         data->auto_paddr);	nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |		                            AUTO_PARAMETER         );	/*	 * Check arbitration	 */	ret = nsp32_arbitration(SCpnt, base);	return ret;}/* * Selection with AUTO SCSI (without AUTO PARAMETER) */static int nsp32_selection_autoscsi(Scsi_Cmnd *SCpnt){	nsp32_hw_data  *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	unsigned int	base    = SCpnt->device->host->io_port;	unsigned int	host_id = SCpnt->device->host->this_id;	unsigned char	target  = SCpnt->device->id;	unsigned char	phase;	int		status;	unsigned short	command	= 0;	unsigned int	msgout  = 0;	unsigned short	execph;	int		i;	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "in");	/*	 * IRQ disable	 */	nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);	/*	 * check bus line	 */	phase = nsp32_read1(base, SCSI_BUS_MONITOR);	if(((phase & BUSMON_BSY) == 1) || (phase & BUSMON_SEL) == 1) {		nsp32_msg(KERN_WARNING, "bus busy");		SCpnt->result = DID_BUS_BUSY << 16;		status = 1;		goto out;        }	/*	 * clear execph	 */	execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);	/*	 * clear FIFO counter to set CDBs	 */	nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER);	/*	 * set CDB0 - CDB15	 */	for (i = 0; i < SCpnt->cmd_len; i++) {		nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);        }	nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[0]);	/*	 * set SCSIOUT LATCH(initiator)/TARGET(target) (OR-ed) ID	 */	nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID, BIT(host_id) | BIT(target));	/*	 * set SCSI MSGOUT REG	 *	 * Note: If the range of msgout_len is 1 - 3, fill scsi_msgout.	 *       over 3 messages needs another routine.	 */	if (data->msgout_len == 0) {		nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");		SCpnt->result = DID_ERROR << 16;		status = 1;		goto out;	} else if (data->msgout_len > 0 && data->msgout_len <= 3) {		msgout = 0;		for (i = 0; i < data->msgout_len; i++) {			/*			 * the sending order of the message is:			 *  MCNT 3: MSG#0 -> MSG#1 -> MSG#2			 *  MCNT 2:          MSG#1 -> MSG#2			 *  MCNT 1:                   MSG#2    			 */			msgout >>= 8;			msgout |= ((unsigned int)(data->msgoutbuf[i]) << 24);		}		msgout |= MV_VALID;	/* MV valid */		msgout |= (unsigned int)data->msgout_len; /* len */		nsp32_write4(base, SCSI_MSG_OUT, msgout);	} else {		/* data->msgout_len > 3 */		nsp32_write4(base, SCSI_MSG_OUT, 0);	}	/*	 * set selection timeout(= 250ms)	 */	nsp32_write2(base, SEL_TIME_OUT,   SEL_TIMEOUT_TIME);	/*	 * set SREQ hazard killer sampling rate	 * 	 * TODO: sample_rate (BASE+0F) is 0 when internal clock = 40MHz.	 *      check other internal clock!	 */	nsp32_write1(base, SREQ_SMPL_RATE, data->cur_target->sample_reg);	/*	 * clear Arbit	 */	nsp32_write1(base, SET_ARBIT,      ARBIT_CLEAR);	/*	 * set SYNCREG	 * Don't set BM_START_ADR before setting this register.	 */	nsp32_write1(base, SYNC_REG,  data->cur_target->syncreg);	/*	 * set ACKWIDTH	 */	nsp32_write1(base, ACK_WIDTH, data->cur_target->ackwidth);	nsp32_dbg(NSP32_DEBUG_AUTOSCSI,		  "syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",		  nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),		  nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgout_len=%d, msgout=0x%x",		  data->msgout_len, msgout);	/*	 * set SGT ADDR (physical address)	 */	nsp32_write4(base, SGT_ADR, data->cur_lunt->sglun_paddr);	/*	 * set TRANSFER CONTROL REG	 */	command = 0;	command |= (TRANSFER_GO | ALL_COUNTER_CLR);	if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {		if (SCpnt->request_bufflen > 0) {			command |= BM_START;		}	} else if (data->trans_method & NSP32_TRANSFER_MMIO) {		command |= CB_MMIO_MODE;	} else if (data->trans_method & NSP32_TRANSFER_PIO) {		command |= CB_IO_MODE;	}	nsp32_write2(base, TRANSFER_CONTROL, command);	/*	 * start AUTO SCSI, kick off arbitration	 */	command = (CLEAR_CDB_FIFO_POINTER |		   AUTOSCSI_START         |		   AUTO_MSGIN_00_OR_04    |		   AUTO_MSGIN_02          |		   AUTO_ATN                );	nsp32_write2(base, COMMAND_CONTROL, command);	/*	 * Check arbitration	 */	status = nsp32_arbitration(SCpnt, base); out:	/*	 * IRQ enable	 */	nsp32_write2(base, IRQ_CONTROL, 0);	return status;}/* * Arbitration Status Check *	 * Note: Arbitration counter is waited during ARBIT_GO is not lifting. *	 Using udelay(1) consumes CPU time and system time, but  *	 arbitration delay time is defined minimal 2.4us in SCSI *	 specification, thus udelay works as coarse grained wait timer. */static int nsp32_arbitration(Scsi_Cmnd *SCpnt, unsigned int base){	unsigned char arbit;	int	      status = TRUE;	int	      time   = 0;	do {		arbit = nsp32_read1(base, ARBIT_STATUS);		time++;	} while ((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&		 (time <= ARBIT_TIMEOUT_TIME));	nsp32_dbg(NSP32_DEBUG_AUTOSCSI,		  "arbit: 0x%x, delay time: %d", arbit, time);	if (arbit & ARBIT_WIN) {		/* Arbitration succeeded */		SCpnt->result = DID_OK << 16;		nsp32_index_write1(base, EXT_PORT, LED_ON); /* PCI LED on */	} else if (arbit & ARBIT_FAIL) {		/* Arbitration failed */		SCpnt->result = DID_BUS_BUSY << 16;		status = FALSE;	} else {		/*		 * unknown error or ARBIT_GO timeout,		 * something lock up! guess no connection.		 */		nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit timeout");		SCpnt->result = DID_NO_CONNECT << 16;		status = FALSE;        }	/*	 * clear Arbit	 */	nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);	return status;}/* * reselection * * Note: This reselection routine is called from msgin_occur, *	 reselection target id&lun must be already set. *	 SCSI-2 says IDENTIFY implies RESTORE_POINTER operation. */static int nsp32_reselection(Scsi_Cmnd *SCpnt, unsigned char newlun){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	unsigned int   host_id = SCpnt->device->host->this_id;	unsigned int   base    = SCpnt->device->host->io_port;	unsigned char  tmpid, newid;	nsp32_dbg(NSP32_DEBUG_RESELECTION, "enter");	/*	 * calculate reselected SCSI ID	 */	tmpid = nsp32_read1(base, RESELECT_ID);	tmpid &= (~BIT(host_id));	newid = 0;	while (tmpid) {		if (tmpid & 1) {			break;		}		tmpid >>= 1;		newid++;	}	/*	 * If reselected New ID:LUN is not existed	 * or current nexus is not existed, unexpected	 * reselection is occurred. Send reject message.	 */	if (newid >= ARRAY_SIZE(data->lunt) || newlun >= ARRAY_SIZE(data->lunt[0])) {		nsp32_msg(KERN_WARNING, "unknown id/lun");		return FALSE;	} else if(data->lunt[newid][newlun].SCpnt == NULL) {		nsp32_msg(KERN_WARNING, "no SCSI command is processing");		return FALSE;	}	data->cur_id    = newid;	data->cur_lun   = newlun;	data->cur_target = &(data->target[newid]);	data->cur_lunt   = &(data->lunt[newid][newlun]);	/* reset SACK/SavedACK counter (or ALL clear?) */	nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);	return TRUE;}/* * nsp32_setup_sg_table - build scatter gather list for transfer data *			    with bus master. * * Note: NinjaSCSI-32Bi/UDE bus master can not transfer over 64KB at a time. */static int nsp32_setup_sg_table(Scsi_Cmnd *SCpnt){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	struct scatterlist   *sgl;	nsp32_sgtable *sgt = data->cur_lunt->sglun->sgt;	int num, i;	u32_le l;	if (SCpnt->request_bufflen == 0) {		return TRUE;	}	if (sgt == NULL) {		nsp32_dbg(NSP32_DEBUG_SGLIST, "SGT == null");		return FALSE;	}	if (SCpnt->use_sg) {		sgl = (struct scatterlist *)SCpnt->request_buffer;		num = pci_map_sg(data->Pci, sgl, SCpnt->use_sg,				 scsi_to_pci_dma_dir(SCpnt->sc_data_direction));		for (i = 0; i < num; i++) {			/*			 * Build nsp32_sglist, substitute sg dma addresses.			 */			sgt[i].addr = cpu_to_le32(sg_dma_address(sgl));			sgt[i].len  = cpu_to_le32(sg_dma_len(sgl));			sgl++;			if (le32_to_cpu(sgt[i].len) > 0x10000) {				nsp32_msg(KERN_ERR,					"can't transfer over 64KB at a time, size=0x%lx", le32_to_cpu(sgt[i].len));				return FALSE;			}			nsp32_dbg(NSP32_DEBUG_SGLIST,				  "num 0x%x : addr 0x%lx len 0x%lx",				  i,				  le32_to_cpu(sgt[i].addr),				  le32_to_cpu(sgt[i].len ));		}		/* set end mark */		l = le32_to_cpu(sgt[num-1].len);		sgt[num-1].len = cpu_to_le32(l | SGTEND);	} else {		SCpnt->SCp.have_data_in	= pci_map_single(data->Pci,			SCpnt->request_buffer, SCpnt->request_bufflen,			scsi_to_pci_dma_dir(SCpnt->sc_data_direction));		sgt[0].addr = cpu_to_le32(SCpnt->SCp.have_data_in);		sgt[0].len  = cpu_to_le32(SCpnt->request_bufflen | SGTEND); /* set end mark */		if (SCpnt->request_bufflen > 0x10000) {			nsp32_msg(KERN_ERR,				  "can't transfer over 64KB at a time, size=0x%lx", SCpnt->request_bufflen);			return FALSE;		}		nsp32_dbg(NSP32_DEBUG_SGLIST, "single : addr 0x%lx len=0x%lx",			  le32_to_cpu(sgt[0].addr),			  le32_to_cpu(sgt[0].len ));	}	return TRUE;}static int nsp32_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->device->host->hostdata;	nsp32_target *target;	nsp32_lunt   *cur_lunt;	int ret;	nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,		  "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "		  "use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",		  SCpnt->device->id, SCpnt->device->lun, SCpnt->cmnd[0], SCpnt->cmd_len,		  SCpnt->use_sg, SCpnt->request_buffer, SCpnt->request_bufflen);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?