wd33c93.c

来自「linux 内核源代码」· C语言 代码 · 共 2,145 行 · 第 1/5 页

C
2,145
字号
/* Make sure that reselection is enabled at this point - it may * have been turned off for the command that just completed. */		write_wd33c93(regs, WD_SOURCE_ID, SRCID_ER);		DB(DB_INTR, printk("DISC-%ld", cmd->serial_number))		    if (cmd == NULL) {			printk(" - Already disconnected! ");			hostdata->state = S_UNCONNECTED;		}		switch (hostdata->state) {		case S_PRE_CMP_DISC:			hostdata->connected = NULL;			hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);			hostdata->state = S_UNCONNECTED;			DB(DB_INTR, printk(":%d", cmd->SCp.Status))			    if (cmd->cmnd[0] == REQUEST_SENSE				&& cmd->SCp.Status != GOOD)				cmd->result =				    (cmd->				     result & 0x00ffff) | (DID_ERROR << 16);			else				cmd->result =				    cmd->SCp.Status | (cmd->SCp.Message << 8);			cmd->scsi_done(cmd);			break;		case S_PRE_TMP_DISC:		case S_RUNNING_LEVEL2:			cmd->host_scribble = (uchar *) hostdata->disconnected_Q;			hostdata->disconnected_Q = cmd;			hostdata->connected = NULL;			hostdata->state = S_UNCONNECTED;#ifdef PROC_STATISTICS			hostdata->disc_done_cnt[cmd->device->id]++;#endif			break;		default:			printk("*** Unexpected DISCONNECT interrupt! ***");			hostdata->state = S_UNCONNECTED;		}/* We are no longer connected to a target - check to see if * there are commands waiting to be executed. */		spin_unlock_irqrestore(&hostdata->lock, flags);		wd33c93_execute(instance);		break;	case CSR_RESEL_AM:	case CSR_RESEL:		DB(DB_INTR, printk("RESEL%s", sr == CSR_RESEL_AM ? "_AM" : ""))		    /* Old chips (pre -A ???) don't have advanced features and will		     * generate CSR_RESEL.  In that case we have to extract the LUN the		     * hard way (see below).		     * First we have to make sure this reselection didn't		     * happen during Arbitration/Selection of some other device.		     * If yes, put losing command back on top of input_Q.		     */		    if (hostdata->level2 <= L2_NONE) {			if (hostdata->selecting) {				cmd = (struct scsi_cmnd *) hostdata->selecting;				hostdata->selecting = NULL;				hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);				cmd->host_scribble =				    (uchar *) hostdata->input_Q;				hostdata->input_Q = cmd;			}		}		else {			if (cmd) {				if (phs == 0x00) {					hostdata->busy[cmd->device->id] &=					    ~(1 << cmd->device->lun);					cmd->host_scribble =					    (uchar *) hostdata->input_Q;					hostdata->input_Q = cmd;				} else {					printk					    ("---%02x:%02x:%02x-TROUBLE: Intrusive ReSelect!---",					     asr, sr, phs);					while (1)						printk("\r");				}			}		}		/* OK - find out which device reselected us. */		id = read_wd33c93(regs, WD_SOURCE_ID);		id &= SRCID_MASK;		/* and extract the lun from the ID message. (Note that we don't		 * bother to check for a valid message here - I guess this is		 * not the right way to go, but...)		 */		if (sr == CSR_RESEL_AM) {			lun = read_wd33c93(regs, WD_DATA);			if (hostdata->level2 < L2_RESELECT)				write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK);			lun &= 7;		} else {			/* Old chip; wait for msgin phase to pick up the LUN. */			for (lun = 255; lun; lun--) {				if ((asr = read_aux_stat(regs)) & ASR_INT)					break;				udelay(10);			}			if (!(asr & ASR_INT)) {				printk				    ("wd33c93: Reselected without IDENTIFY\n");				lun = 0;			} else {				/* Verify this is a change to MSG_IN and read the message */				sr = read_wd33c93(regs, WD_SCSI_STATUS);				udelay(7);				if (sr == (CSR_ABORT | PHS_MESS_IN) ||				    sr == (CSR_UNEXP | PHS_MESS_IN) ||				    sr == (CSR_SRV_REQ | PHS_MESS_IN)) {					/* Got MSG_IN, grab target LUN */					lun = read_1_byte(regs);					/* Now we expect a 'paused with ACK asserted' int.. */					asr = read_aux_stat(regs);					if (!(asr & ASR_INT)) {						udelay(10);						asr = read_aux_stat(regs);						if (!(asr & ASR_INT))							printk							    ("wd33c93: No int after LUN on RESEL (%02x)\n",							     asr);					}					sr = read_wd33c93(regs, WD_SCSI_STATUS);					udelay(7);					if (sr != CSR_MSGIN)						printk						    ("wd33c93: Not paused with ACK on RESEL (%02x)\n",						     sr);					lun &= 7;					write_wd33c93_cmd(regs,							  WD_CMD_NEGATE_ACK);				} else {					printk					    ("wd33c93: Not MSG_IN on reselect (%02x)\n",					     sr);					lun = 0;				}			}		}		/* Now we look for the command that's reconnecting. */		cmd = (struct scsi_cmnd *) hostdata->disconnected_Q;		patch = NULL;		while (cmd) {			if (id == cmd->device->id && lun == cmd->device->lun)				break;			patch = cmd;			cmd = (struct scsi_cmnd *) cmd->host_scribble;		}		/* Hmm. Couldn't find a valid command.... What to do? */		if (!cmd) {			printk			    ("---TROUBLE: target %d.%d not in disconnect queue---",			     id, lun);			spin_unlock_irqrestore(&hostdata->lock, flags);			return;		}		/* Ok, found the command - now start it up again. */		if (patch)			patch->host_scribble = cmd->host_scribble;		else			hostdata->disconnected_Q =			    (struct scsi_cmnd *) cmd->host_scribble;		hostdata->connected = cmd;		/* We don't need to worry about 'initialize_SCp()' or 'hostdata->busy[]'		 * because these things are preserved over a disconnect.		 * But we DO need to fix the DPD bit so it's correct for this command.		 */		if (cmd->sc_data_direction == DMA_TO_DEVICE)			write_wd33c93(regs, WD_DESTINATION_ID, cmd->device->id);		else			write_wd33c93(regs, WD_DESTINATION_ID,				      cmd->device->id | DSTID_DPD);		if (hostdata->level2 >= L2_RESELECT) {			write_wd33c93_count(regs, 0);	/* we want a DATA_PHASE interrupt */			write_wd33c93(regs, WD_COMMAND_PHASE, 0x45);			write_wd33c93_cmd(regs, WD_CMD_SEL_ATN_XFER);			hostdata->state = S_RUNNING_LEVEL2;		} else			hostdata->state = S_CONNECTED;		DB(DB_INTR, printk("-%ld", cmd->serial_number))		    spin_unlock_irqrestore(&hostdata->lock, flags);		break;	default:		printk("--UNKNOWN INTERRUPT:%02x:%02x:%02x--", asr, sr, phs);		spin_unlock_irqrestore(&hostdata->lock, flags);	}	DB(DB_INTR, printk("} "))}static voidreset_wd33c93(struct Scsi_Host *instance){	struct WD33C93_hostdata *hostdata =	    (struct WD33C93_hostdata *) instance->hostdata;	const wd33c93_regs regs = hostdata->regs;	uchar sr;#ifdef CONFIG_SGI_IP22	{		int busycount = 0;		extern void sgiwd93_reset(unsigned long);		/* wait 'til the chip gets some time for us */		while ((read_aux_stat(regs) & ASR_BSY) && busycount++ < 100)			udelay (10);	/* 	 * there are scsi devices out there, which manage to lock up	 * the wd33c93 in a busy condition. In this state it won't	 * accept the reset command. The only way to solve this is to 	 * give the chip a hardware reset (if possible). The code below	 * does this for the SGI Indy, where this is possible	 */	/* still busy ? */	if (read_aux_stat(regs) & ASR_BSY)		sgiwd93_reset(instance->base); /* yeah, give it the hard one */	}#endif	write_wd33c93(regs, WD_OWN_ID, OWNID_EAF | OWNID_RAF |		      instance->this_id | hostdata->clock_freq);	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);	write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER,		      calc_sync_xfer(hostdata->default_sx_per / 4,				     DEFAULT_SX_OFF, 0, hostdata->sx_table));	write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET);#ifdef CONFIG_MVME147_SCSI	udelay(25);		/* The old wd33c93 on MVME147 needs this, at least */#endif	while (!(read_aux_stat(regs) & ASR_INT))		;	sr = read_wd33c93(regs, WD_SCSI_STATUS);	hostdata->microcode = read_wd33c93(regs, WD_CDB_1);	if (sr == 0x00)		hostdata->chip = C_WD33C93;	else if (sr == 0x01) {		write_wd33c93(regs, WD_QUEUE_TAG, 0xa5);	/* any random number */		sr = read_wd33c93(regs, WD_QUEUE_TAG);		if (sr == 0xa5) {			hostdata->chip = C_WD33C93B;			write_wd33c93(regs, WD_QUEUE_TAG, 0);		} else			hostdata->chip = C_WD33C93A;	} else		hostdata->chip = C_UNKNOWN_CHIP;	if (hostdata->chip != C_WD33C93B)	/* Fast SCSI unavailable */		hostdata->fast = 0;	write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE);	write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED);}intwd33c93_host_reset(struct scsi_cmnd * SCpnt){	struct Scsi_Host *instance;	struct WD33C93_hostdata *hostdata;	int i;	instance = SCpnt->device->host;	hostdata = (struct WD33C93_hostdata *) instance->hostdata;	printk("scsi%d: reset. ", instance->host_no);	disable_irq(instance->irq);	hostdata->dma_stop(instance, NULL, 0);	for (i = 0; i < 8; i++) {		hostdata->busy[i] = 0;		hostdata->sync_xfer[i] =			calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF,					0, hostdata->sx_table);		hostdata->sync_stat[i] = SS_UNSET;	/* using default sync values */	}	hostdata->input_Q = NULL;	hostdata->selecting = NULL;	hostdata->connected = NULL;	hostdata->disconnected_Q = NULL;	hostdata->state = S_UNCONNECTED;	hostdata->dma = D_DMA_OFF;	hostdata->incoming_ptr = 0;	hostdata->outgoing_len = 0;	reset_wd33c93(instance);	SCpnt->result = DID_RESET << 16;	enable_irq(instance->irq);	return SUCCESS;}intwd33c93_abort(struct scsi_cmnd * cmd){	struct Scsi_Host *instance;	struct WD33C93_hostdata *hostdata;	wd33c93_regs regs;	struct scsi_cmnd *tmp, *prev;	disable_irq(cmd->device->host->irq);	instance = cmd->device->host;	hostdata = (struct WD33C93_hostdata *) instance->hostdata;	regs = hostdata->regs;/* * Case 1 : If the command hasn't been issued yet, we simply remove it *     from the input_Q. */	tmp = (struct scsi_cmnd *) hostdata->input_Q;	prev = NULL;	while (tmp) {		if (tmp == cmd) {			if (prev)				prev->host_scribble = cmd->host_scribble;			else				hostdata->input_Q =				    (struct scsi_cmnd *) cmd->host_scribble;			cmd->host_scribble = NULL;			cmd->result = DID_ABORT << 16;			printk			    ("scsi%d: Abort - removing command %ld from input_Q. ",			     instance->host_no, cmd->serial_number);			enable_irq(cmd->device->host->irq);			cmd->scsi_done(cmd);			return SUCCESS;		}		prev = tmp;		tmp = (struct scsi_cmnd *) tmp->host_scribble;	}/* * Case 2 : If the command is connected, we're going to fail the abort *     and let the high level SCSI driver retry at a later time or *     issue a reset. * *     Timeouts, and therefore aborted commands, will be highly unlikely *     and handling them cleanly in this situation would make the common *     case of noresets less efficient, and would pollute our code.  So, *     we fail. */	if (hostdata->connected == cmd) {		uchar sr, asr;		unsigned long timeout;		printk("scsi%d: Aborting connected command %ld - ",		       instance->host_no, cmd->serial_number);		printk("stopping DMA - ");		if (hostdata->dma == D_DMA_RUNNING) {			hostdata->dma_stop(instance, cmd, 0);			hostdata->dma = D_DMA_OFF;		}		printk("sending wd33c93 ABORT command - ");		write_wd33c93(regs, WD_CONTROL,			      CTRL_IDI | CTRL_EDI | CTRL_POLLED);		write_wd33c93_cmd(regs, WD_CMD_ABORT);/* Now we have to attempt to flush out the FIFO... */		printk("flushing fifo - ");		timeout = 1000000;		do {			asr = read_aux_stat(regs);			if (asr & ASR_DBR)				read_wd33c93(regs, WD_DATA);		} while (!(asr & ASR_INT) && timeout-- > 0);		sr = read_wd33c93(regs, WD_SCSI_STATUS);		printk		    ("asr=%02x, sr=%02x, %ld bytes un-transferred (timeout=%ld) - ",		     asr, sr, read_wd33c93_count(regs), timeout);		/*		 * Abort command processed.		 * Still connected.		 * We must disconnect.		 */		printk("sending wd33c93 DISCONNECT command - ");		write_wd33c93_cmd(regs, WD_CMD_DISCONNECT);		timeout = 1000000;		asr = read_aux_stat(regs);		while ((asr & ASR_CIP) && timeout-- > 0)			asr = read_aux_stat(regs);		sr = read_wd33c93(regs, WD_SCSI_STATUS);		printk("asr=%02x, sr=%02x.", asr, sr);		hostdata->busy[cmd->device->id] &= ~(1 << cmd->device->lun);		hostdata->connected = NULL;		hostdata->state = S_UNCONNECTED;		cmd->result = DID_ABORT << 16;/*      sti();*/		wd33c93_execute(instance);		enable_irq(cmd->device->host->irq);		cmd->scsi_done(cmd);

⌨️ 快捷键说明

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