atp870u.c

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

C
2,946
字号
/*  *  Copyright (C) 1997	Wu Ching Chen *  2.1.x update (C) 1998  Krzysztof G. Baranowski *  2.5.x update (C) 2002  Red Hat <alan@redhat.com> *  2.6.x update (C) 2004  Red Hat <alan@redhat.com> * * Marcelo Tosatti <marcelo@conectiva.com.br> : SMP fixes * * Wu Ching Chen : NULL pointer fixes  2000/06/02 *		   support atp876 chip *		   enable 32 bit fifo transfer *		   support cdrom & remove device run ultra speed *		   fix disconnect bug  2000/12/21 *		   support atp880 chip lvd u160 2001/05/15 *		   fix prd table bug 2001/09/12 (7.1) * * atp885 support add by ACARD Hao Ping Lian 2005/01/05 */#include <linux/module.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <linux/blkdev.h>#include <linux/dma-mapping.h>#include <asm/system.h>#include <asm/io.h>#include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include "atp870u.h"static struct scsi_host_template atp870u_template;static void send_s870(struct atp_unit *dev,unsigned char c);static void is885(struct atp_unit *dev, unsigned int wkport,unsigned char c);static void tscam_885(void);static irqreturn_t atp870u_intr_handle(int irq, void *dev_id){	unsigned long flags;	unsigned short int tmpcip, id;	unsigned char i, j, c, target_id, lun,cmdp;	unsigned char *prd;	struct scsi_cmnd *workreq;	unsigned int workport, tmport, tmport1;	unsigned long adrcnt, k;#ifdef ED_DBGP	unsigned long l;#endif	int errstus;	struct Scsi_Host *host = dev_id;	struct atp_unit *dev = (struct atp_unit *)&host->hostdata;	for (c = 0; c < 2; c++) {		tmport = dev->ioport[c] + 0x1f;		j = inb(tmport);		if ((j & 0x80) != 0)		{				   		goto ch_sel;		}		dev->in_int[c] = 0;	}	return IRQ_NONE;ch_sel:#ifdef ED_DBGP		printk("atp870u_intr_handle enter\n");#endif		dev->in_int[c] = 1;	cmdp = inb(dev->ioport[c] + 0x10);	workport = dev->ioport[c];	if (dev->working[c] != 0) {		if (dev->dev_id == ATP885_DEVID) {			tmport1 = workport + 0x16;			if ((inb(tmport1) & 0x80) == 0)				outb((inb(tmport1) | 0x80), tmport1);		}				tmpcip = dev->pciport[c];		if ((inb(tmpcip) & 0x08) != 0)		{			tmpcip += 0x2;			for (k=0; k < 1000; k++) {				if ((inb(tmpcip) & 0x08) == 0) {					goto stop_dma;				}				if ((inb(tmpcip) & 0x01) == 0) {					goto stop_dma;				}			}		}stop_dma:		tmpcip = dev->pciport[c];		outb(0x00, tmpcip);		tmport -= 0x08;				i = inb(tmport);				if (dev->dev_id == ATP885_DEVID) {			tmpcip += 2;			outb(0x06, tmpcip);			tmpcip -= 2;		}		tmport -= 0x02;		target_id = inb(tmport);		tmport += 0x02;		/*		 *	Remap wide devices onto id numbers		 */		if ((target_id & 0x40) != 0) {			target_id = (target_id & 0x07) | 0x08;		} else {			target_id &= 0x07;		}		if ((j & 0x40) != 0) {		     if (dev->last_cmd[c] == 0xff) {			dev->last_cmd[c] = target_id;		     }		     dev->last_cmd[c] |= 0x40;		}		if (dev->dev_id == ATP885_DEVID) 			dev->r1f[c][target_id] |= j;#ifdef ED_DBGP		printk("atp870u_intr_handle status = %x\n",i);#endif			if (i == 0x85) {			if ((dev->last_cmd[c] & 0xf0) != 0x40) {			   dev->last_cmd[c] = 0xff;			}			if (dev->dev_id == ATP885_DEVID) {				tmport -= 0x05;				adrcnt = 0;				((unsigned char *) &adrcnt)[2] = inb(tmport++);				((unsigned char *) &adrcnt)[1] = inb(tmport++);				((unsigned char *) &adrcnt)[0] = inb(tmport);				if (dev->id[c][target_id].last_len != adrcnt)				{			   		k = dev->id[c][target_id].last_len;			   		k -= adrcnt;			   		dev->id[c][target_id].tran_len = k;			   			   	dev->id[c][target_id].last_len = adrcnt;			   				}#ifdef ED_DBGP				printk("tmport = %x dev->id[c][target_id].last_len = %d dev->id[c][target_id].tran_len = %d\n",tmport,dev->id[c][target_id].last_len,dev->id[c][target_id].tran_len);#endif					}			/*			 *      Flip wide			 */						if (dev->wide_id[c] != 0) {				tmport = workport + 0x1b;				outb(0x01, tmport);				while ((inb(tmport) & 0x01) != 0x01) {					outb(0x01, tmport);				}			}					/*			 *	Issue more commands			 */			spin_lock_irqsave(dev->host->host_lock, flags);			 			 			if (((dev->quhd[c] != dev->quend[c]) || (dev->last_cmd[c] != 0xff)) &&			    (dev->in_snd[c] == 0)) {#ifdef ED_DBGP				printk("Call sent_s870\n");#endif								send_s870(dev,c);			}			spin_unlock_irqrestore(dev->host->host_lock, flags);			/*			 *	Done			 */			dev->in_int[c] = 0;#ifdef ED_DBGP				printk("Status 0x85 return\n");#endif							goto handled;		}		if (i == 0x40) {		     dev->last_cmd[c] |= 0x40;		     dev->in_int[c] = 0;		     goto handled;		}		if (i == 0x21) {			if ((dev->last_cmd[c] & 0xf0) != 0x40) {			   dev->last_cmd[c] = 0xff;			}			tmport -= 0x05;			adrcnt = 0;			((unsigned char *) &adrcnt)[2] = inb(tmport++);			((unsigned char *) &adrcnt)[1] = inb(tmport++);			((unsigned char *) &adrcnt)[0] = inb(tmport);			k = dev->id[c][target_id].last_len;			k -= adrcnt;			dev->id[c][target_id].tran_len = k;			dev->id[c][target_id].last_len = adrcnt;			tmport -= 0x04;			outb(0x41, tmport);			tmport += 0x08;			outb(0x08, tmport);			dev->in_int[c] = 0;			goto handled;		}		if (dev->dev_id == ATP885_DEVID) {			if ((i == 0x4c) || (i == 0x4d) || (i == 0x8c) || (i == 0x8d)) {		   		if ((i == 0x4c) || (i == 0x8c)) 		      			i=0x48;		   		else 		      			i=0x49;		   	}						}		if ((i == 0x80) || (i == 0x8f)) {#ifdef ED_DBGP			printk(KERN_DEBUG "Device reselect\n");#endif						lun = 0;			tmport -= 0x07;			if (cmdp == 0x44 || i==0x80) {				tmport += 0x0d;				lun = inb(tmport) & 0x07;			} else {				if ((dev->last_cmd[c] & 0xf0) != 0x40) {				   dev->last_cmd[c] = 0xff;				}				if (cmdp == 0x41) {#ifdef ED_DBGP					printk("cmdp = 0x41\n");#endif											tmport += 0x02;					adrcnt = 0;					((unsigned char *) &adrcnt)[2] = inb(tmport++);					((unsigned char *) &adrcnt)[1] = inb(tmport++);					((unsigned char *) &adrcnt)[0] = inb(tmport);					k = dev->id[c][target_id].last_len;					k -= adrcnt;					dev->id[c][target_id].tran_len = k;					dev->id[c][target_id].last_len = adrcnt;					tmport += 0x04;					outb(0x08, tmport);					dev->in_int[c] = 0;					goto handled;				} else {#ifdef ED_DBGP					printk("cmdp != 0x41\n");#endif											outb(0x46, tmport);					dev->id[c][target_id].dirct = 0x00;					tmport += 0x02;					outb(0x00, tmport++);					outb(0x00, tmport++);					outb(0x00, tmport++);					tmport += 0x03;					outb(0x08, tmport);					dev->in_int[c] = 0;					goto handled;				}			}			if (dev->last_cmd[c] != 0xff) {			   dev->last_cmd[c] |= 0x40;			}			if (dev->dev_id == ATP885_DEVID) {				j = inb(dev->baseport + 0x29) & 0xfe;				outb(j, dev->baseport + 0x29);				tmport = workport + 0x16;			} else {				tmport = workport + 0x10;				outb(0x45, tmport);				tmport += 0x06;							}						target_id = inb(tmport);			/*			 *	Remap wide identifiers			 */			if ((target_id & 0x10) != 0) {				target_id = (target_id & 0x07) | 0x08;			} else {				target_id &= 0x07;			}			if (dev->dev_id == ATP885_DEVID) {				tmport = workport + 0x10;				outb(0x45, tmport);			}			workreq = dev->id[c][target_id].curr_req;#ifdef ED_DBGP						scmd_printk(KERN_DEBUG, workreq, "CDB");			for (l = 0; l < workreq->cmd_len; l++)				printk(KERN_DEBUG " %x",workreq->cmnd[l]);			printk("\n");#endif							tmport = workport + 0x0f;			outb(lun, tmport);			tmport += 0x02;			outb(dev->id[c][target_id].devsp, tmport++);			adrcnt = dev->id[c][target_id].tran_len;			k = dev->id[c][target_id].last_len;			outb(((unsigned char *) &k)[2], tmport++);			outb(((unsigned char *) &k)[1], tmport++);			outb(((unsigned char *) &k)[0], tmport++);#ifdef ED_DBGP						printk("k %x, k[0] 0x%x k[1] 0x%x k[2] 0x%x\n", k, inb(tmport-1), inb(tmport-2), inb(tmport-3));#endif						/* Remap wide */			j = target_id;			if (target_id > 7) {				j = (j & 0x07) | 0x40;			}			/* Add direction */			j |= dev->id[c][target_id].dirct;			outb(j, tmport++);			outb(0x80,tmport);						/* enable 32 bit fifo transfer */				if (dev->dev_id == ATP885_DEVID) {				tmpcip = dev->pciport[c] + 1;				i=inb(tmpcip) & 0xf3;				//j=workreq->cmnd[0];	    		    					if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {				   i |= 0x0c;				}				outb(i,tmpcip);		    		    					} else if ((dev->dev_id == ATP880_DEVID1) ||	    		    	   (dev->dev_id == ATP880_DEVID2) ) {				tmport = workport - 0x05;				if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {					outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);				} else {					outb((unsigned char) (inb(tmport) & 0x3f), tmport);				}			} else {								tmport = workport + 0x3a;				if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {					outb((unsigned char) ((inb(tmport) & 0xf3) | 0x08), tmport);				} else {					outb((unsigned char) (inb(tmport) & 0xf3), tmport);				}																	}				tmport = workport + 0x1b;			j = 0;			id = 1;			id = id << target_id;			/*			 *	Is this a wide device			 */			if ((id & dev->wide_id[c]) != 0) {				j |= 0x01;			}			outb(j, tmport);			while ((inb(tmport) & 0x01) != j) {				outb(j,tmport);			}			if (dev->id[c][target_id].last_len == 0) {				tmport = workport + 0x18;				outb(0x08, tmport);				dev->in_int[c] = 0;#ifdef ED_DBGP				printk("dev->id[c][target_id].last_len = 0\n");#endif									goto handled;			}#ifdef ED_DBGP			printk("target_id = %d adrcnt = %d\n",target_id,adrcnt);#endif						prd = dev->id[c][target_id].prd_pos;			while (adrcnt != 0) {				id = ((unsigned short int *)prd)[2];				if (id == 0) {					k = 0x10000;				} else {					k = id;				}				if (k > adrcnt) {					((unsigned short int *)prd)[2] = (unsigned short int)					    (k - adrcnt);					((unsigned long *)prd)[0] += adrcnt;					adrcnt = 0;					dev->id[c][target_id].prd_pos = prd;				} else {					adrcnt -= k;					dev->id[c][target_id].prdaddr += 0x08;					prd += 0x08;					if (adrcnt == 0) {						dev->id[c][target_id].prd_pos = prd;					}				}							}			tmpcip = dev->pciport[c] + 0x04;			outl(dev->id[c][target_id].prdaddr, tmpcip);#ifdef ED_DBGP			printk("dev->id[%d][%d].prdaddr 0x%8x\n", c, target_id, dev->id[c][target_id].prdaddr);#endif			if (dev->dev_id == ATP885_DEVID) {				tmpcip -= 0x04;			} else {				tmpcip -= 0x02;				outb(0x06, tmpcip);				outb(0x00, tmpcip);				tmpcip -= 0x02;			}			tmport = workport + 0x18;			/*			 *	Check transfer direction			 */			if (dev->id[c][target_id].dirct != 0) {				outb(0x08, tmport);				outb(0x01, tmpcip);				dev->in_int[c] = 0;#ifdef ED_DBGP				printk("status 0x80 return dirct != 0\n");#endif								goto handled;			}			outb(0x08, tmport);			outb(0x09, tmpcip);			dev->in_int[c] = 0;#ifdef ED_DBGP			printk("status 0x80 return dirct = 0\n");#endif						goto handled;		}		/*		 *	Current scsi request on this target		 */		workreq = dev->id[c][target_id].curr_req;		if (i == 0x42) {			if ((dev->last_cmd[c] & 0xf0) != 0x40)			{			   dev->last_cmd[c] = 0xff;			}			errstus = 0x02;			workreq->result = errstus;			goto go_42;		}		if (i == 0x16) {			if ((dev->last_cmd[c] & 0xf0) != 0x40) {			   dev->last_cmd[c] = 0xff;			}			errstus = 0;			tmport -= 0x08;			errstus = inb(tmport);			if (((dev->r1f[c][target_id] & 0x10) != 0)&&(dev->dev_id==ATP885_DEVID)) {			   printk(KERN_WARNING "AEC67162 CRC ERROR !\n");			   errstus = 0x02;			}			workreq->result = errstus;go_42:			if (dev->dev_id == ATP885_DEVID) {						j = inb(dev->baseport + 0x29) | 0x01;				outb(j, dev->baseport + 0x29);			}			/*			 *	Complete the command			 */			if (workreq->use_sg) {				pci_unmap_sg(dev->pdev,					(struct scatterlist *)workreq->request_buffer,					workreq->use_sg,					workreq->sc_data_direction);			} else if (workreq->request_bufflen &&					workreq->sc_data_direction != DMA_NONE) {				pci_unmap_single(dev->pdev,					workreq->SCp.dma_handle,					workreq->request_bufflen,					workreq->sc_data_direction);			}						spin_lock_irqsave(dev->host->host_lock, flags);			(*workreq->scsi_done) (workreq);#ifdef ED_DBGP			   printk("workreq->scsi_done\n");#endif				/*			 *	Clear it off the queue			 */			dev->id[c][target_id].curr_req = NULL;			dev->working[c]--;			spin_unlock_irqrestore(dev->host->host_lock, flags);			/*			 *      Take it back wide			 */			if (dev->wide_id[c] != 0) {				tmport = workport + 0x1b;				outb(0x01, tmport);				while ((inb(tmport) & 0x01) != 0x01) {					outb(0x01, tmport);				}       			} 			/*			 *	If there is stuff to send and nothing going then send it			 */			spin_lock_irqsave(dev->host->host_lock, flags);			if (((dev->last_cmd[c] != 0xff) || (dev->quhd[c] != dev->quend[c])) &&			    (dev->in_snd[c] == 0)) {#ifdef ED_DBGP			   printk("Call sent_s870(scsi_done)\n");#endif				   			   send_s870(dev,c);			}			spin_unlock_irqrestore(dev->host->host_lock, flags);			dev->in_int[c] = 0;			goto handled;		}		if ((dev->last_cmd[c] & 0xf0) != 0x40) {		   dev->last_cmd[c] = 0xff;		}		if (i == 0x4f) {			i = 0x89;		}		i &= 0x0f;		if (i == 0x09) {			tmpcip += 4;			outl(dev->id[c][target_id].prdaddr, tmpcip);			tmpcip = tmpcip - 2;			outb(0x06, tmpcip);			outb(0x00, tmpcip);			tmpcip = tmpcip - 2;			tmport = workport + 0x10;			outb(0x41, tmport);			if (dev->dev_id == ATP885_DEVID) {				tmport += 2;				k = dev->id[c][target_id].last_len;				outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);				outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);				outb((unsigned char) (((unsigned char *) (&k))[0]), tmport);				dev->id[c][target_id].dirct = 0x00;				tmport += 0x04;			} else {				dev->id[c][target_id].dirct = 0x00;				tmport += 0x08;							}			outb(0x08, tmport);			outb(0x09, tmpcip);			dev->in_int[c] = 0;			goto handled;		}		if (i == 0x08) {			tmpcip += 4;			outl(dev->id[c][target_id].prdaddr, tmpcip);			tmpcip = tmpcip - 2;			outb(0x06, tmpcip);			outb(0x00, tmpcip);			tmpcip = tmpcip - 2;			tmport = workport + 0x10;			outb(0x41, tmport);			if (dev->dev_id == ATP885_DEVID) {						tmport += 2;				k = dev->id[c][target_id].last_len;				outb((unsigned char) (((unsigned char *) (&k))[2]), tmport++);				outb((unsigned char) (((unsigned char *) (&k))[1]), tmport++);				outb((unsigned char) (((unsigned char *) (&k))[0]), tmport++);			} else {				tmport += 5;			}			outb((unsigned char) (inb(tmport) | 0x20), tmport);			dev->id[c][target_id].dirct = 0x20;			tmport += 0x03;			outb(0x08, tmport);			outb(0x01, tmpcip);			dev->in_int[c] = 0;			goto handled;		}		tmport -= 0x07;		if (i == 0x0a) {			outb(0x30, tmport);		} else {			outb(0x46, tmport);		}		dev->id[c][target_id].dirct = 0x00;		tmport += 0x02;		outb(0x00, tmport++);		outb(0x00, tmport++);

⌨️ 快捷键说明

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