atp870u.c

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

C
2,735
字号
/* *  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) */#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 <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 Scsi_Host *host);static irqreturn_t atp870u_intr_handle(int irq, void *dev_id,					struct pt_regs *regs){	unsigned long flags;	unsigned short int tmpcip, id;	unsigned char i, j, target_id, lun;	unsigned char *prd;	struct scsi_cmnd *workrequ;	unsigned int workportu, tmport;	unsigned long adrcntu, k;	int errstus;	struct Scsi_Host *host = dev_id;	struct atp_unit *dev = (struct atp_unit *)&host->hostdata;	dev->in_int = 1;	workportu = dev->ioport;	tmport = workportu;	if (dev->working != 0) {		tmport += 0x1f;		j = inb(tmport);		if ((j & 0x80) == 0) {			dev->in_int = 0;			return IRQ_NONE;		}		tmpcip = dev->pciport;		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;		outb(0x00, tmpcip);		tmport -= 0x08;		i = inb(tmport);		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 == 0xff) {				dev->last_cmd = target_id;			}			dev->last_cmd |= 0x40;		}		if (i == 0x85) {			if ((dev->last_cmd & 0xf0) != 0x40) {				dev->last_cmd = 0xff;			}			/*			 *      Flip wide			 */			if (dev->wide_idu != 0) {				tmport = workportu + 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->quhdu != dev->quendu) || (dev->last_cmd != 0xff)) && (dev->in_snd == 0)) {				send_s870(host);			}			spin_unlock_irqrestore(dev->host->host_lock, flags);			/*			 *      Done			 */			dev->in_int = 0;			goto out;		}		if (i == 0x40) {			dev->last_cmd |= 0x40;			dev->in_int = 0;			goto out;		}		if (i == 0x21) {			if ((dev->last_cmd & 0xf0) != 0x40) {				dev->last_cmd = 0xff;			}			tmport -= 0x05;			adrcntu = 0;			((unsigned char *) &adrcntu)[2] = inb(tmport++);			((unsigned char *) &adrcntu)[1] = inb(tmport++);			((unsigned char *) &adrcntu)[0] = inb(tmport);			k = dev->id[target_id].last_lenu;			k -= adrcntu;			dev->id[target_id].tran_lenu = k;			dev->id[target_id].last_lenu = adrcntu;			tmport -= 0x04;			outb(0x41, tmport);			tmport += 0x08;			outb(0x08, tmport);			dev->in_int = 0;			goto out;		}		if ((i == 0x80) || (i == 0x8f)) {			lun = 0;			tmport -= 0x07;			j = inb(tmport);			if (j == 0x44 || i == 0x80) {				tmport += 0x0d;				lun = inb(tmport) & 0x07;			} else {				if ((dev->last_cmd & 0xf0) != 0x40) {					dev->last_cmd = 0xff;				}				if (j == 0x41) {					tmport += 0x02;					adrcntu = 0;					((unsigned char *) &adrcntu)[2] = inb(tmport++);					((unsigned char *) &adrcntu)[1] = inb(tmport++);					((unsigned char *) &adrcntu)[0] = inb(tmport);					k = dev->id[target_id].last_lenu;					k -= adrcntu;					dev->id[target_id].tran_lenu = k;					dev->id[target_id].last_lenu = adrcntu;					tmport += 0x04;					outb(0x08, tmport);					dev->in_int = 0;					goto out;				} else {					outb(0x46, tmport);					dev->id[target_id].dirctu = 0x00;					tmport += 0x02;					outb(0x00, tmport++);					outb(0x00, tmport++);					outb(0x00, tmport++);					tmport += 0x03;					outb(0x08, tmport);					dev->in_int = 0;					goto out;				}			}			if (dev->last_cmd != 0xff) {				dev->last_cmd |= 0x40;			}			tmport = workportu + 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;			}			workrequ = dev->id[target_id].curr_req;			tmport = workportu + 0x0f;			outb(lun, tmport);			tmport += 0x02;			outb(dev->id[target_id].devspu, tmport++);			adrcntu = dev->id[target_id].tran_lenu;			k = dev->id[target_id].last_lenu;			outb(((unsigned char *) &k)[2], tmport++);			outb(((unsigned char *) &k)[1], tmport++);			outb(((unsigned char *) &k)[0], tmport++);			/* Remap wide */			j = target_id;			if (target_id > 7) {				j = (j & 0x07) | 0x40;			}			/* Add direction */			j |= dev->id[target_id].dirctu;			outb(j, tmport++);			outb(0x80, tmport);			/* enable 32 bit fifo transfer */			if (dev->deviceid != 0x8081) {				tmport = workportu + 0x3a;				if ((dev->ata_cdbu[0] == 0x08) || (dev->ata_cdbu[0] == 0x28) || (dev->ata_cdbu[0] == 0x0a) || (dev->ata_cdbu[0] == 0x2a)) {					outb((unsigned char) ((inb(tmport) & 0xf3) | 0x08), tmport);				} else {					outb((unsigned char) (inb(tmport) & 0xf3), tmport);				}			} else {				tmport = workportu - 0x05;				if ((dev->ata_cdbu[0] == 0x08) || (dev->ata_cdbu[0] == 0x28) || (dev->ata_cdbu[0] == 0x0a) || (dev->ata_cdbu[0] == 0x2a)) {					outb((unsigned char) ((inb(tmport) & 0x3f) | 0xc0), tmport);				} else {					outb((unsigned char) (inb(tmport) & 0x3f), tmport);				}			}			tmport = workportu + 0x1b;			j = 0;			id = 1;			id = id << target_id;			/*			 *      Is this a wide device			 */			if ((id & dev->wide_idu) != 0) {				j |= 0x01;			}			outb(j, tmport);			while ((inb(tmport) & 0x01) != j) {				outb(j, tmport);			}			if (dev->id[target_id].last_lenu == 0) {				tmport = workportu + 0x18;				outb(0x08, tmport);				dev->in_int = 0;				goto out;			}			prd = dev->id[target_id].prd_posu;			while (adrcntu != 0) {				id = ((unsigned short int *) (prd))[2];				if (id == 0) {					k = 0x10000;				} else {					k = id;				}				if (k > adrcntu) {					((unsigned short int *) (prd))[2] = (unsigned short int)					    (k - adrcntu);					((unsigned long *) (prd))[0] += adrcntu;					adrcntu = 0;					dev->id[target_id].prd_posu = prd;				} else {					adrcntu -= k;					dev->id[target_id].prdaddru += 0x08;					prd += 0x08;					if (adrcntu == 0) {						dev->id[target_id].prd_posu = prd;					}				}			}			tmpcip = dev->pciport + 0x04;			outl(dev->id[target_id].prdaddru, tmpcip);			tmpcip -= 0x02;			outb(0x06, tmpcip);			outb(0x00, tmpcip);			tmpcip -= 0x02;			tmport = workportu + 0x18;			/*			 *      Check transfer direction			 */			if (dev->id[target_id].dirctu != 0) {				outb(0x08, tmport);				outb(0x01, tmpcip);				dev->in_int = 0;				goto out;			}			outb(0x08, tmport);			outb(0x09, tmpcip);			dev->in_int = 0;			goto out;		}		/*		 *      Current scsi request on this target		 */		workrequ = dev->id[target_id].curr_req;		if (i == 0x42) {			if ((dev->last_cmd & 0xf0) != 0x40) {				dev->last_cmd = 0xff;			}			errstus = 0x02;			workrequ->result = errstus;			goto go_42;		}		if (i == 0x16) {			if ((dev->last_cmd & 0xf0) != 0x40) {				dev->last_cmd = 0xff;			}			errstus = 0;			tmport -= 0x08;			errstus = inb(tmport);			workrequ->result = errstus;go_42:			/*			 *      Complete the command			 */			 			if (workrequ->use_sg) {				pci_unmap_sg(dev->pdev,					(struct scatterlist *)workrequ->buffer,					workrequ->use_sg,					workrequ->sc_data_direction);			} else if (workrequ->request_bufflen &&					workrequ->sc_data_direction != DMA_NONE) {				pci_unmap_single(dev->pdev,					workrequ->SCp.dma_handle,					workrequ->request_bufflen,					workrequ->sc_data_direction);			}			spin_lock_irqsave(dev->host->host_lock, flags);			(*workrequ->scsi_done) (workrequ);			/*			 *      Clear it off the queue			 */			dev->id[target_id].curr_req = NULL;			dev->working--;			spin_unlock_irqrestore(dev->host->host_lock, flags);			/*			 *      Take it back wide			 */			if (dev->wide_idu != 0) {				tmport = workportu + 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 != 0xff) || (dev->quhdu != dev->quendu)) && (dev->in_snd == 0)) {				send_s870(host);			}			spin_unlock_irqrestore(dev->host->host_lock, flags);			dev->in_int = 0;			goto out;		}		if ((dev->last_cmd & 0xf0) != 0x40) {			dev->last_cmd = 0xff;		}		if (i == 0x4f) {			i = 0x89;		}		i &= 0x0f;		if (i == 0x09) {			tmpcip = tmpcip + 4;			outl(dev->id[target_id].prdaddru, tmpcip);			tmpcip = tmpcip - 2;			outb(0x06, tmpcip);			outb(0x00, tmpcip);			tmpcip = tmpcip - 2;			tmport = workportu + 0x10;			outb(0x41, tmport);			dev->id[target_id].dirctu = 0x00;			tmport += 0x08;			outb(0x08, tmport);			outb(0x09, tmpcip);			dev->in_int = 0;			goto out;		}		if (i == 0x08) {			tmpcip = tmpcip + 4;			outl(dev->id[target_id].prdaddru, tmpcip);			tmpcip = tmpcip - 2;			outb(0x06, tmpcip);			outb(0x00, tmpcip);			tmpcip = tmpcip - 2;			tmport = workportu + 0x10;			outb(0x41, tmport);			tmport += 0x05;			outb((unsigned char) (inb(tmport) | 0x20), tmport);			dev->id[target_id].dirctu = 0x20;			tmport += 0x03;			outb(0x08, tmport);			outb(0x01, tmpcip);			dev->in_int = 0;			goto out;		}		tmport -= 0x07;		if (i == 0x0a) {			outb(0x30, tmport);		} else {			outb(0x46, tmport);		}		dev->id[target_id].dirctu = 0x00;		tmport += 0x02;		outb(0x00, tmport++);		outb(0x00, tmport++);		outb(0x00, tmport++);		tmport += 0x03;		outb(0x08, tmport);		dev->in_int = 0;		goto out;	} else {//              tmport = workportu + 0x17;//              inb(tmport);//              dev->working = 0;		dev->in_int = 0;	}out:	return IRQ_HANDLED;}/** *	atp870u_queuecommand	-	Queue SCSI command *	@req_p: request block *	@done: completion function * *	Queue a command to the ATP queue. Called with the host lock held. */ static int atp870u_queuecommand(struct scsi_cmnd *req_p,		void (*done) (struct scsi_cmnd *)){	unsigned short int m;	unsigned int tmport;	struct Scsi_Host *host;	struct atp_unit *dev;	if (req_p->device->channel != 0) {		req_p->result = 0x00040000;		done(req_p);		return 0;	};	host = req_p->device->host;	dev = (struct atp_unit *)&host->hostdata;		m = 1;	m = m << req_p->device->id;	/*	 *      Fake a timeout for missing targets	 */	if ((m & dev->active_idu) == 0) {		req_p->result = 0x00040000;		done(req_p);		return 0;	}	if (done) {		req_p->scsi_done = done;	} else {		printk(KERN_WARNING "atp870u_queuecommand: done can't be NULL\n");		req_p->result = 0;		done(req_p);		return 0;	}	/*	 *      Count new command	 */	dev->quendu++;	if (dev->quendu >= qcnt) {		dev->quendu = 0;	}	/*	 *      Check queue state	 */	if (dev->quhdu == dev->quendu) {		if (dev->quendu == 0) {			dev->quendu = qcnt;		}		dev->quendu--;		req_p->result = 0x00020000;		done(req_p);		return 0;	}	dev->querequ[dev->quendu] = req_p;	tmport = dev->ioport + 0x1c;	if ((inb(tmport) == 0) && (dev->in_int == 0) && (dev->in_snd == 0)) {		send_s870(host);	}	return 0;}/** *	send_s870	-	send a command to the controller *	@host: host * *	On entry there is work queued to be done. We move some of that work to the *	controller itself.  * *	Caller holds the host lock. */ static void send_s870(struct Scsi_Host *host){	unsigned int tmport;	struct scsi_cmnd *workrequ;	unsigned int i;	unsigned char j, target_id;	unsigned char *prd;	unsigned short int tmpcip, w;	unsigned long l;	dma_addr_t bttl;	unsigned int workportu;	struct scatterlist *sgpnt;	struct atp_unit *dev = (struct atp_unit *)&host->hostdata;	int sg_count;	if (dev->in_snd != 0) {		return;	}	dev->in_snd = 1;	if ((dev->last_cmd != 0xff) && ((dev->last_cmd & 0x40) != 0)) {		dev->last_cmd &= 0x0f;		workrequ = dev->id[dev->last_cmd].curr_req;		if (workrequ != NULL) {	/* check NULL pointer */			goto cmd_subp;		}		dev->last_cmd = 0xff;		if (dev->quhdu == dev->quendu) {			dev->in_snd = 0;			return;		}	}	if ((dev->last_cmd != 0xff) && (dev->working != 0)) {		dev->in_snd = 0;		return;	}	dev->working++;	j = dev->quhdu;	dev->quhdu++;	if (dev->quhdu >= qcnt) {		dev->quhdu = 0;	}	workrequ = dev->querequ[dev->quhdu];	if (dev->id[workrequ->device->id].curr_req == 0) {		dev->id[workrequ->device->id].curr_req = workrequ;		dev->last_cmd = workrequ->device->id;		goto cmd_subp;	}	dev->quhdu = j;	dev->working--;	dev->in_snd = 0;	return;cmd_subp:	workportu = dev->ioport;	tmport = workportu + 0x1f;	if ((inb(tmport) & 0xb0) != 0) {		goto abortsnd;	}	tmport = workportu + 0x1c;	if (inb(tmport) == 0) {		goto oktosend;	}abortsnd:	dev->last_cmd |= 0x40;	dev->in_snd = 0;	return;oktosend:	memcpy(&dev->ata_cdbu[0], &workrequ->cmnd[0], workrequ->cmd_len);	if (dev->ata_cdbu[0] == READ_CAPACITY) {		if (workrequ->request_bufflen > 8) {			workrequ->request_bufflen = 0x08;		}	}	if (dev->ata_cdbu[0] == 0x00) {		workrequ->request_bufflen = 0;	}	tmport = workportu + 0x1b;	j = 0;	target_id = workrequ->device->id;	/*	 *      Wide ?	 */	w = 1;	w = w << target_id;	if ((w & dev->wide_idu) != 0) {		j |= 0x01;	}	outb(j, tmport);	while ((inb(tmport) & 0x01) != j) {		outb(j, tmport);	}	/*	 *      Write the command	 */	tmport = workportu;	outb(workrequ->cmd_len, tmport++);	outb(0x2c, tmport++);	outb(0xcf, tmport++);	for (i = 0; i < workrequ->cmd_len; i++) {		outb(dev->ata_cdbu[i], tmport++);	}	tmport = workportu + 0x0f;	outb(workrequ->device->lun, tmport);	tmport += 0x02;	/*	 *      Write the target	 */	outb(dev->id[target_id].devspu, tmport++);	/*	 *      Figure out the transfer size	 */	if (workrequ->use_sg) {		l = 0;		sgpnt = (struct scatterlist *) workrequ->request_buffer;		sg_count = pci_map_sg(dev->pdev, sgpnt, workrequ->use_sg,				workrequ->sc_data_direction);		for (i = 0; i < workrequ->use_sg; i++) {			if (sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER) {				panic("Foooooooood fight!");			}			l += sgpnt[i].length;		}	} else if(workrequ->request_bufflen && workrequ->sc_data_direction != PCI_DMA_NONE) {		workrequ->SCp.dma_handle = pci_map_single(dev->pdev,				workrequ->request_buffer,				workrequ->request_bufflen,				workrequ->sc_data_direction);		l = workrequ->request_bufflen;	}	else l = 0;	/*	 *      Write transfer size	 */	outb((unsigned char) (((unsigned char *) (&l))[2]), tmport++);	outb((unsigned char) (((unsigned char *) (&l))[1]), tmport++);	outb((unsigned char) (((unsigned char *) (&l))[0]), tmport++);	j = target_id;	dev->id[j].last_lenu = l;	dev->id[j].tran_lenu = 0;	/*	 *      Flip the wide bits	 */

⌨️ 快捷键说明

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