wd7000.c

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

C
1,669
字号
			return (NULL);		}	}	/* Take the lock, then check we didnt get beaten, if so try again */	spin_lock_irqsave(&scbpool_lock, flags);	if (freescbs < needed) {		spin_unlock_irqrestore(&scbpool_lock, flags);		goto retry;	}	scb = scbfree;	freescbs -= needed;	for (i = 0; i < needed; i++) {		p = scbfree;		scbfree = p->next;	}	p->next = NULL;	spin_unlock_irqrestore(&scbpool_lock, flags);	spin_lock_irq(host->host_lock);	return (scb);}static inline void free_scb(Scb * scb){	unsigned long flags;	spin_lock_irqsave(&scbpool_lock, flags);	memset(scb, 0, sizeof(Scb));	scb->next = scbfree;	scbfree = scb;	freescbs++;	spin_unlock_irqrestore(&scbpool_lock, flags);}static inline void init_scbs(void){	int i;	spin_lock_init(&scbpool_lock);	/* This is only ever called before the SCB pool is active */	scbfree = &(scbs[0]);	memset(scbs, 0, sizeof(scbs));	for (i = 0; i < MAX_SCBS - 1; i++) {		scbs[i].next = &(scbs[i + 1]);		scbs[i].SCpnt = NULL;	}	scbs[MAX_SCBS - 1].next = NULL;	scbs[MAX_SCBS - 1].SCpnt = NULL;}static int mail_out(Adapter * host, Scb * scbptr)/* *  Note: this can also be used for ICBs; just cast to the parm type. */{	int i, ogmb;	unsigned long flags;	unchar start_ogmb;	Mailbox *ogmbs = host->mb.ogmb;	int *next_ogmb = &(host->next_ogmb);	dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr);	/* We first look for a free outgoing mailbox */	spin_lock_irqsave(host->sh->host_lock, flags);	ogmb = *next_ogmb;	for (i = 0; i < OGMB_CNT; i++) {		if (ogmbs[ogmb].status == 0) {			dprintk(" using OGMB 0x%x", ogmb);			ogmbs[ogmb].status = 1;			any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);			*next_ogmb = (ogmb + 1) % OGMB_CNT;			break;		} else			ogmb = (ogmb + 1) % OGMB_CNT;	}	spin_unlock_irqrestore(host->sh->host_lock, flags);	dprintk(", scb is 0x%06lx", (long) scbptr);	if (i >= OGMB_CNT) {		/*		 *  Alternatively, we might issue the "interrupt on free OGMB",		 *  and sleep, but it must be ensured that it isn't the init		 *  task running.  Instead, this version assumes that the caller		 *  will be persistent, and try again.  Since it's the adapter		 *  that marks OGMB's free, waiting even with interrupts off		 *  should work, since they are freed very quickly in most cases.		 */		dprintk(", no free OGMBs.\n");		return (0);	}	wd7000_enable_intr(host);	start_ogmb = START_OGMB | ogmb;	command_out(host, &start_ogmb, 1);	dprintk(", awaiting interrupt.\n");	return (1);}static int make_code(unsigned hosterr, unsigned scsierr){#ifdef WD7000_DEBUG	int in_error = hosterr;#endif	switch ((hosterr >> 8) & 0xff) {	case 0:		/* Reserved */		hosterr = DID_ERROR;		break;	case 1:		/* Command Complete, no errors */		hosterr = DID_OK;		break;	case 2:		/* Command complete, error logged in scb status (scsierr) */		hosterr = DID_OK;		break;	case 4:		/* Command failed to complete - timeout */		hosterr = DID_TIME_OUT;		break;	case 5:		/* Command terminated; Bus reset by external device */		hosterr = DID_RESET;		break;	case 6:		/* Unexpected Command Received w/ host as target */		hosterr = DID_BAD_TARGET;		break;	case 80:		/* Unexpected Reselection */	case 81:		/* Unexpected Selection */		hosterr = DID_BAD_INTR;		break;	case 82:		/* Abort Command Message  */		hosterr = DID_ABORT;		break;	case 83:		/* SCSI Bus Software Reset */	case 84:		/* SCSI Bus Hardware Reset */		hosterr = DID_RESET;		break;	default:		/* Reserved */		hosterr = DID_ERROR;	}#ifdef WD7000_DEBUG	if (scsierr || hosterr)		dprintk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr);#endif	return (scsierr | (hosterr << 16));}#define wd7000_intr_ack(host)   outb (0, host->iobase + ASC_INTR_ACK)static irqreturn_t wd7000_intr(int irq, void *dev_id, struct pt_regs *regs){	Adapter *host = (Adapter *) dev_id;	int flag, icmb, errstatus, icmb_status;	int host_error, scsi_error;	Scb *scb;	/* for SCSI commands */	IcbAny *icb;	/* for host commands */	struct scsi_cmnd *SCpnt;	Mailbox *icmbs = host->mb.icmb;	unsigned long flags;	spin_lock_irqsave(host->sh->host_lock, flags);	host->int_counter++;	dprintk("wd7000_intr: irq = %d, host = 0x%06lx\n", irq, (long) host);	flag = inb(host->iobase + ASC_INTR_STAT);	dprintk("wd7000_intr: intr stat = 0x%02x\n", flag);	if (!(inb(host->iobase + ASC_STAT) & INT_IM)) {		/* NB: these are _very_ possible if IRQ 15 is being used, since		 * it's the "garbage collector" on the 2nd 8259 PIC.  Specifically,		 * any interrupt signal into the 8259 which can't be identified		 * comes out as 7 from the 8259, which is 15 to the host.  Thus, it		 * is a good thing the WD7000 has an interrupt status port, so we		 * can sort these out.  Otherwise, electrical noise and other such		 * problems would be indistinguishable from valid interrupts...		 */		dprintk("wd7000_intr: phantom interrupt...\n");		goto ack;	}	if (!(flag & MB_INTR))		goto ack;	/* The interrupt is for a mailbox */	if (!(flag & IMB_INTR)) {		dprintk("wd7000_intr: free outgoing mailbox\n");		/*		 * If sleep_on() and the "interrupt on free OGMB" command are		 * used in mail_out(), wake_up() should correspondingly be called		 * here.  For now, we don't need to do anything special.		 */		goto ack;	}	/* The interrupt is for an incoming mailbox */	icmb = flag & MB_MASK;	icmb_status = icmbs[icmb].status;	if (icmb_status & 0x80) {	/* unsolicited - result in ICMB */		dprintk("wd7000_intr: unsolicited interrupt 0x%02x\n", icmb_status);		goto ack;	}	/* Aaaargh! (Zaga) */	scb = isa_bus_to_virt(scsi2int((unchar *) icmbs[icmb].scbptr));	icmbs[icmb].status = 0;	if (scb->op & ICB_OP_MASK) {	/* an SCB is done */		icb = (IcbAny *) scb;		icb->status = icmb_status;		icb->phase = 0;		goto ack;	}	SCpnt = scb->SCpnt;	if (--(SCpnt->SCp.phase) <= 0) {	/* all scbs are done */		host_error = scb->vue | (icmb_status << 8);		scsi_error = scb->status;		errstatus = make_code(host_error, scsi_error);		SCpnt->result = errstatus;		free_scb(scb);		SCpnt->scsi_done(SCpnt);	} ack:	dprintk("wd7000_intr: return from interrupt handler\n");	wd7000_intr_ack(host);	spin_unlock_irqrestore(host->sh->host_lock, flags);	return IRQ_HANDLED;}static int wd7000_queuecommand(struct scsi_cmnd *SCpnt,		void (*done)(struct scsi_cmnd *)){	Scb *scb;	Sgb *sgb;	unchar *cdb = (unchar *) SCpnt->cmnd;	unchar idlun;	short cdblen;	Adapter *host = (Adapter *) SCpnt->device->host->hostdata;	cdblen = SCpnt->cmd_len;	idlun = ((SCpnt->device->id << 5) & 0xe0) | (SCpnt->device->lun & 7);	SCpnt->scsi_done = done;	SCpnt->SCp.phase = 1;	scb = alloc_scbs(SCpnt->device->host, 1);	scb->idlun = idlun;	memcpy(scb->cdb, cdb, cdblen);	scb->direc = 0x40;	/* Disable direction check */	scb->SCpnt = SCpnt;	/* so we can find stuff later */	SCpnt->host_scribble = (unchar *) scb;	scb->host = host;	if (SCpnt->use_sg) {		struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;		unsigned i;		if (SCpnt->device->host->sg_tablesize == SG_NONE) {			panic("wd7000_queuecommand: scatter/gather not supported.\n");		}		dprintk("Using scatter/gather with %d elements.\n", SCpnt->use_sg);		sgb = scb->sgb;		scb->op = 1;		any2scsi(scb->dataptr, (int) sgb);		any2scsi(scb->maxlen, SCpnt->use_sg * sizeof(Sgb));		for (i = 0; i < SCpnt->use_sg; i++) {			any2scsi(sgb[i].ptr, isa_page_to_bus(sg[i].page) + sg[i].offset);			any2scsi(sgb[i].len, sg[i].length);		}	} else {		scb->op = 0;		any2scsi(scb->dataptr, isa_virt_to_bus(SCpnt->request_buffer));		any2scsi(scb->maxlen, SCpnt->request_bufflen);	}	/* FIXME: drop lock and yield here ? */	while (!mail_out(host, scb))		cpu_relax();	/* keep trying */	return 0;}static int wd7000_diagnostics(Adapter * host, int code){	static IcbDiag icb = { ICB_OP_DIAGNOSTICS };	static unchar buf[256];	unsigned long timeout;	icb.type = code;	any2scsi(icb.len, sizeof(buf));	any2scsi(icb.ptr, (int) &buf);	icb.phase = 1;	/*	 * This routine is only called at init, so there should be OGMBs	 * available.  I'm assuming so here.  If this is going to	 * fail, I can just let the timeout catch the failure.	 */	mail_out(host, (struct scb *) &icb);	timeout = jiffies + WAITnexttimeout;	/* wait up to 2 seconds */	while (icb.phase && time_before(jiffies, timeout)) {		cpu_relax();	/* wait for completion */		barrier();	}	if (icb.phase) {		printk("wd7000_diagnostics: timed out.\n");		return (0);	}	if (make_code(icb.vue | (icb.status << 8), 0)) {		printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status);		return (0);	}	return (1);}static int wd7000_adapter_reset(Adapter * host){	InitCmd init_cmd = {		INITIALIZATION,		7,		host->bus_on,		host->bus_off,		0,		{0, 0, 0},		OGMB_CNT,		ICMB_CNT	};	int diag;	/*	 *  Reset the adapter - only.  The SCSI bus was initialized at power-up,	 *  and we need to do this just so we control the mailboxes, etc.	 */	outb(ASC_RES, host->iobase + ASC_CONTROL);	udelay(40);		/* reset pulse: this is 40us, only need 25us */	outb(0, host->iobase + ASC_CONTROL);	host->control = 0;	/* this must always shadow ASC_CONTROL */	if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {		printk(KERN_ERR "wd7000_init: WAIT timed out.\n");		return -1;	/* -1 = not ok */	}	if ((diag = inb(host->iobase + ASC_INTR_STAT)) != 1) {		printk("wd7000_init: ");		switch (diag) {		case 2:			printk(KERN_ERR "RAM failure.\n");			break;		case 3:			printk(KERN_ERR "FIFO R/W failed\n");			break;		case 4:			printk(KERN_ERR "SBIC register R/W failed\n");			break;		case 5:			printk(KERN_ERR "Initialization D-FF failed.\n");			break;		case 6:			printk(KERN_ERR "Host IRQ D-FF failed.\n");			break;		case 7:			printk(KERN_ERR "ROM checksum error.\n");			break;		default:			printk(KERN_ERR "diagnostic code 0x%02Xh received.\n", diag);		}		return -1;	}	/* Clear mailboxes */	memset(&(host->mb), 0, sizeof(host->mb));	/* Execute init command */	any2scsi((unchar *) & (init_cmd.mailboxes), (int) &(host->mb));	if (!command_out(host, (unchar *) & init_cmd, sizeof(init_cmd))) {		printk(KERN_ERR "wd7000_adapter_reset: adapter initialization failed.\n");		return -1;	}	if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {		printk("wd7000_adapter_reset: WAIT timed out.\n");		return -1;	}	return 0;}static int wd7000_init(Adapter * host){	if (wd7000_adapter_reset(host) == -1)		return 0;	if (request_irq(host->irq, wd7000_intr, SA_INTERRUPT, "wd7000", host)) {		printk("wd7000_init: can't get IRQ %d.\n", host->irq);

⌨️ 快捷键说明

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