summaryrefslogtreecommitdiffstats
path: root/drivers/scsi/atp870u.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/atp870u.c
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/scsi/atp870u.c')
-rw-r--r--drivers/scsi/atp870u.c3970
1 files changed, 3970 insertions, 0 deletions
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c
new file mode 100644
index 000000000000..45b75ddacaab
--- /dev/null
+++ b/drivers/scsi/atp870u.c
@@ -0,0 +1,3970 @@
+/*
+ * 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 <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, struct pt_regs *regs)
+{
+ 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
+ printk(KERN_DEBUG "Channel = %d ID = %d LUN = %d CDB",c,workreq->device->id,workreq->device->lun);
+ for(l=0;l<workreq->cmd_len;l++)
+ {
+ printk(KERN_DEBUG " %x",workreq->cmnd[l]);
+ }
+#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->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++);
+ outb(0x00, tmport++);
+ tmport += 0x03;
+ outb(0x08, tmport);
+ dev->in_int[c] = 0;
+ goto handled;
+ } else {
+// tmport = workport + 0x17;
+// inb(tmport);
+// dev->working[c] = 0;
+ dev->in_int[c] = 0;
+ goto handled;
+ }
+
+handled:
+#ifdef ED_DBGP
+ printk("atp870u_intr_handle exit\n");
+#endif
+ 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 char c;
+ unsigned int tmport,m;
+ struct atp_unit *dev;
+ struct Scsi_Host *host;
+
+ c = req_p->device->channel;
+ req_p->sense_buffer[0]=0;
+ req_p->resid = 0;
+ if (req_p->device->channel > 1) {
+ req_p->result = 0x00040000;
+ done(req_p);
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : req_p->device->channel > 1\n");
+#endif
+ 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_id[c]) == 0) {
+ req_p->result = 0x00040000;
+ done(req_p);
+ return 0;
+ }
+
+ if (done) {
+ req_p->scsi_done = done;
+ } else {
+#ifdef ED_DBGP
+ printk( "atp870u_queuecommand: done can't be NULL\n");
+#endif
+ req_p->result = 0;
+ done(req_p);
+ return 0;
+ }
+
+ /*
+ * Count new command
+ */
+ dev->quend[c]++;
+ if (dev->quend[c] >= qcnt) {
+ dev->quend[c] = 0;
+ }
+
+ /*
+ * Check queue state
+ */
+ if (dev->quhd[c] == dev->quend[c]) {
+ if (dev->quend[c] == 0) {
+ dev->quend[c] = qcnt;
+ }
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : dev->quhd[c] == dev->quend[c]\n");
+#endif
+ dev->quend[c]--;
+ req_p->result = 0x00020000;
+ done(req_p);
+ return 0;
+ }
+ dev->quereq[c][dev->quend[c]] = req_p;
+ tmport = dev->ioport[c] + 0x1c;
+#ifdef ED_DBGP
+ printk("dev->ioport[c] = %x inb(tmport) = %x dev->in_int[%d] = %d dev->in_snd[%d] = %d\n",dev->ioport[c],inb(tmport),c,dev->in_int[c],c,dev->in_snd[c]);
+#endif
+ if ((inb(tmport) == 0) && (dev->in_int[c] == 0) && (dev->in_snd[c] == 0)) {
+#ifdef ED_DBGP
+ printk("Call sent_s870(atp870u_queuecommand)\n");
+#endif
+ send_s870(dev,c);
+ }
+#ifdef ED_DBGP
+ printk("atp870u_queuecommand : exit\n");
+#endif
+ 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 atp_unit *dev,unsigned char c)
+{
+ unsigned int tmport;
+ struct scsi_cmnd *workreq;
+ unsigned int i;//,k;
+ unsigned char j, target_id;
+ unsigned char *prd;
+ unsigned short int tmpcip, w;
+ unsigned long l, bttl = 0;
+ unsigned int workport;
+ struct scatterlist *sgpnt;
+ unsigned long sg_count;
+
+ if (dev->in_snd[c] != 0) {
+#ifdef ED_DBGP
+ printk("cmnd in_snd\n");
+#endif
+ return;
+ }
+#ifdef ED_DBGP
+ printk("Sent_s870 enter\n");
+#endif
+ dev->in_snd[c] = 1;
+ if ((dev->last_cmd[c] != 0xff) && ((dev->last_cmd[c] & 0x40) != 0)) {
+ dev->last_cmd[c] &= 0x0f;
+ workreq = dev->id[c][dev->last_cmd[c]].curr_req;
+ if (workreq != NULL) { /* check NULL pointer */
+ goto cmd_subp;
+ }
+ dev->last_cmd[c] = 0xff;
+ if (dev->quhd[c] == dev->quend[c]) {
+ dev->in_snd[c] = 0;
+ return ;
+ }
+ }
+ if ((dev->last_cmd[c] != 0xff) && (dev->working[c] != 0)) {
+ dev->in_snd[c] = 0;
+ return ;
+ }
+ dev->working[c]++;
+ j = dev->quhd[c];
+ dev->quhd[c]++;
+ if (dev->quhd[c] >= qcnt) {
+ dev->quhd[c] = 0;
+ }
+ workreq = dev->quereq[c][dev->quhd[c]];
+ if (dev->id[c][workreq->device->id].curr_req == 0) {
+ dev->id[c][workreq->device->id].curr_req = workreq;
+ dev->last_cmd[c] = workreq->device->id;
+ goto cmd_subp;
+ }
+ dev->quhd[c] = j;
+ dev->working[c]--;
+ dev->in_snd[c] = 0;
+ return;
+cmd_subp:
+ workport = dev->ioport[c];
+ tmport = workport + 0x1f;
+ if ((inb(tmport) & 0xb0) != 0) {
+ goto abortsnd;
+ }
+ tmport = workport + 0x1c;
+ if (inb(tmport) == 0) {
+ goto oktosend;
+ }
+abortsnd:
+#ifdef ED_DBGP
+ printk("Abort to Send\n");
+#endif
+ dev->last_cmd[c] |= 0x40;
+ dev->in_snd[c] = 0;
+ return;
+oktosend:
+#ifdef ED_DBGP
+ printk("OK to Send\n");
+ printk("CDB");
+ for(i=0;i<workreq->cmd_len;i++) {
+ printk(" %x",workreq->cmnd[i]);
+ }
+ printk("\nChannel = %d ID = %d LUN = %d\n",c,workreq->device->id,workreq->device->lun);
+#endif
+ if (dev->dev_id == ATP885_DEVID) {
+ j = inb(dev->baseport + 0x29) & 0xfe;
+ outb(j, dev->baseport + 0x29);
+ dev->r1f[c][workreq->device->id] = 0;
+ }
+
+ if (workreq->cmnd[0] == READ_CAPACITY) {
+ if (workreq->request_bufflen > 8) {
+ workreq->request_bufflen = 0x08;
+ }
+ }
+ if (workreq->cmnd[0] == 0x00) {
+ workreq->request_bufflen = 0;
+ }
+
+ tmport = workport + 0x1b;
+ j = 0;
+ target_id = workreq->device->id;
+
+ /*
+ * Wide ?
+ */
+ w = 1;
+ w = w << target_id;
+ if ((w & dev->wide_id[c]) != 0) {
+ j |= 0x01;
+ }
+ outb(j, tmport);
+ while ((inb(tmport) & 0x01) != j) {
+ outb(j,tmport);
+#ifdef ED_DBGP
+ printk("send_s870 while loop 1\n");
+#endif
+ }
+ /*
+ * Write the command
+ */
+
+ tmport = workport;
+ outb(workreq->cmd_len, tmport++);
+ outb(0x2c, tmport++);
+ if (dev->dev_id == ATP885_DEVID) {
+ outb(0x7f, tmport++);
+ } else {
+ outb(0xcf, tmport++);
+ }
+ for (i = 0; i < workreq->cmd_len; i++) {
+ outb(workreq->cmnd[i], tmport++);
+ }
+ tmport = workport + 0x0f;
+ outb(workreq->device->lun, tmport);
+ tmport += 0x02;
+ /*
+ * Write the target
+ */
+ outb(dev->id[c][target_id].devsp, tmport++);
+#ifdef ED_DBGP
+ printk("dev->id[%d][%d].devsp = %2x\n",c,target_id,dev->id[c][target_id].devsp);
+#endif
+ /*
+ * Figure out the transfer size
+ */
+ if (workreq->use_sg) {
+#ifdef ED_DBGP
+ printk("Using SGL\n");
+#endif
+ l = 0;
+
+ sgpnt = (struct scatterlist *) workreq->request_buffer;
+ sg_count = pci_map_sg(dev->pdev, sgpnt, workreq->use_sg,
+ workreq->sc_data_direction);
+
+ for (i = 0; i < workreq->use_sg; i++) {
+ if (sgpnt[i].length == 0 || workreq->use_sg > ATP870U_SCATTER) {
+ panic("Foooooooood fight!");
+ }
+ l += sgpnt[i].length;
+ }
+#ifdef ED_DBGP
+ printk( "send_s870: workreq->use_sg %d, sg_count %d l %8ld\n", workreq->use_sg, sg_count, l);
+#endif
+ } else if(workreq->request_bufflen && workreq->sc_data_direction != PCI_DMA_NONE) {
+#ifdef ED_DBGP
+ printk("Not using SGL\n");
+#endif
+ workreq->SCp.dma_handle = pci_map_single(dev->pdev, workreq->request_buffer,
+ workreq->request_bufflen,
+ workreq->sc_data_direction);
+ l = workreq->request_bufflen;
+#ifdef ED_DBGP
+ printk( "send_s870: workreq->use_sg %d, l %8ld\n", workreq->use_sg, l);
+#endif
+ } 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[c][j].last_len = l;
+ dev->id[c][j].tran_len = 0;
+#ifdef ED_DBGP
+ printk("dev->id[%2d][%2d].last_len = %d\n",c,j,dev->id[c][j].last_len);
+#endif
+ /*
+ * Flip the wide bits
+ */
+ if ((j & 0x08) != 0) {
+ j = (j & 0x07) | 0x40;
+ }
+ /*
+ * Check transfer direction
+ */
+ if (workreq->sc_data_direction == DMA_TO_DEVICE) {
+ outb((unsigned char) (j | 0x20), tmport++);
+ } else {
+ outb(j, tmport++);
+ }
+ outb((unsigned char) (inb(tmport) | 0x80), tmport);
+ outb(0x80, tmport);
+ tmport = workport + 0x1c;
+ dev->id[c][target_id].dirct = 0;
+ if (l == 0) {
+ if (inb(tmport) == 0) {
+ tmport = workport + 0x18;
+#ifdef ED_DBGP
+ printk("change SCSI_CMD_REG 0x08\n");
+#endif
+ outb(0x08, tmport);
+ } else {
+ dev->last_cmd[c] |= 0x40;
+ }
+ dev->in_snd[c] = 0;
+ return;
+ }
+ tmpcip = dev->pciport[c];
+ prd = dev->id[c][target_id].prd_table;
+ dev->id[c][target_id].prd_pos = prd;
+
+ /*
+ * Now write the request list. Either as scatter/gather or as
+ * a linear chain.
+ */
+
+ if (workreq->use_sg) {
+ sgpnt = (struct scatterlist *) workreq->request_buffer;
+ i = 0;
+ for (j = 0; j < workreq->use_sg; j++) {
+ bttl = sg_dma_address(&sgpnt[j]);
+ l=sg_dma_len(&sgpnt[j]);
+#ifdef ED_DBGP
+ printk("1. bttl %x, l %x\n",bttl, l);
+#endif
+ while (l > 0x10000) {
+ (((u16 *) (prd))[i + 3]) = 0x0000;
+ (((u16 *) (prd))[i + 2]) = 0x0000;
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+ l -= 0x10000;
+ bttl += 0x10000;
+ i += 0x04;
+ }
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+ (((u16 *) (prd))[i + 2]) = cpu_to_le16(l);
+ (((u16 *) (prd))[i + 3]) = 0;
+ i += 0x04;
+ }
+ (((u16 *) (prd))[i - 1]) = cpu_to_le16(0x8000);
+#ifdef ED_DBGP
+ printk("prd %4x %4x %4x %4x\n",(((unsigned short int *)prd)[0]),(((unsigned short int *)prd)[1]),(((unsigned short int *)prd)[2]),(((unsigned short int *)prd)[3]));
+ printk("2. bttl %x, l %x\n",bttl, l);
+#endif
+ } else {
+ /*
+ * For a linear request write a chain of blocks
+ */
+ bttl = workreq->SCp.dma_handle;
+ l = workreq->request_bufflen;
+ i = 0;
+#ifdef ED_DBGP
+ printk("3. bttl %x, l %x\n",bttl, l);
+#endif
+ while (l > 0x10000) {
+ (((u16 *) (prd))[i + 3]) = 0x0000;
+ (((u16 *) (prd))[i + 2]) = 0x0000;
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+ l -= 0x10000;
+ bttl += 0x10000;
+ i += 0x04;
+ }
+ (((u16 *) (prd))[i + 3]) = cpu_to_le16(0x8000);
+ (((u16 *) (prd))[i + 2]) = cpu_to_le16(l);
+ (((u32 *) (prd))[i >> 1]) = cpu_to_le32(bttl);
+#ifdef ED_DBGP
+ printk("prd %4x %4x %4x %4x\n",(((unsigned short int *)prd)[0]),(((unsigned short int *)prd)[1]),(((unsigned short int *)prd)[2]),(((unsigned short int *)prd)[3]));
+ printk("4. bttl %x, l %x\n",bttl, l);
+#endif
+
+ }
+ tmpcip += 4;
+#ifdef ED_DBGP
+ printk("send_s870: prdaddr_2 0x%8x tmpcip %x target_id %d\n", dev->id[c][target_id].prdaddr,tmpcip,target_id);
+#endif
+ outl(dev->id[c][target_id].prdaddr, tmpcip);
+ tmpcip = tmpcip - 2;
+ outb(0x06, tmpcip);
+ outb(0x00, tmpcip);
+ if (dev->dev_id == ATP885_DEVID) {
+ tmpcip--;
+ j=inb(tmpcip) & 0xf3;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) ||
+ (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ j |= 0x0c;
+ }
+ outb(j,tmpcip);
+ tmpcip--;
+ } else if ((dev->dev_id == ATP880_DEVID1) ||
+ (dev->dev_id == ATP880_DEVID2)) {
+ tmpcip =tmpcip -2;
+ 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 {
+ tmpcip =tmpcip -2;
+ tmport = workport + 0x3a;
+ if ((workreq->cmnd[0] == 0x08) || (workreq->cmnd[0] == 0x28) || (workreq->cmnd[0] == 0x0a) || (workreq->cmnd[0] == 0x2a)) {
+ outb((inb(tmport) & 0xf3) | 0x08, tmport);
+ } else {
+ outb(inb(tmport) & 0xf3, tmport);
+ }
+ }
+ tmport = workport + 0x1c;
+
+ if(workreq->sc_data_direction == DMA_TO_DEVICE) {
+ dev->id[c][target_id].dirct = 0x20;
+ if (inb(tmport) == 0) {
+ tmport = workport + 0x18;
+ outb(0x08, tmport);
+ outb(0x01, tmpcip);
+#ifdef ED_DBGP
+ printk( "start DMA(to target)\n");
+#endif
+ } else {
+ dev->last_cmd[c] |= 0x40;
+ }
+ dev->in_snd[c] = 0;
+ return;
+ }
+ if (inb(tmport) == 0) {
+ tmport = workport + 0x18;
+ outb(0x08, tmport);
+ outb(0x09, tmpcip);
+#ifdef ED_DBGP
+ printk( "start DMA(to host)\n");
+#endif
+ } else {
+ dev->last_cmd[c] |= 0x40;
+ }
+ dev->in_snd[c] = 0;
+ return;
+
+}
+
+static unsigned char fun_scam(struct atp_unit *dev, unsigned short int *val)
+{
+ unsigned int tmport;
+ unsigned short int i, k;
+ unsigned char j;
+
+ tmport = dev->ioport[0] + 0x1c;
+ outw(*val, tmport);
+FUN_D7:
+ for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
+ k = inw(tmport);
+ j = (unsigned char) (k >> 8);
+ if ((k & 0x8000) != 0) { /* DB7 all release? */
+ goto FUN_D7;
+ }
+ }
+ *val |= 0x4000; /* assert DB6 */
+ outw(*val, tmport);
+ *val &= 0xdfff; /* assert DB5 */
+ outw(*val, tmport);
+FUN_D5:
+ for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
+ if ((inw(tmport) & 0x2000) != 0) { /* DB5 all release? */
+ goto FUN_D5;
+ }
+ }
+ *val |= 0x8000; /* no DB4-0, assert DB7 */
+ *val &= 0xe0ff;
+ outw(*val, tmport);
+ *val &= 0xbfff; /* release DB6 */
+ outw(*val, tmport);
+FUN_D6:
+ for (i = 0; i < 10; i++) { /* stable >= bus settle delay(400 ns) */
+ if ((inw(tmport) & 0x4000) != 0) { /* DB6 all release? */
+ goto FUN_D6;
+ }
+ }
+
+ return j;
+}
+
+static void tscam(struct Scsi_Host *host)
+{
+
+ unsigned int tmport;
+ unsigned char i, j, k;
+ unsigned long n;
+ unsigned short int m, assignid_map, val;
+ unsigned char mbuf[33], quintet[2];
+ struct atp_unit *dev = (struct atp_unit *)&host->hostdata;
+ static unsigned char g2q_tab[8] = {
+ 0x38, 0x31, 0x32, 0x2b, 0x34, 0x2d, 0x2e, 0x27
+ };
+
+/* I can't believe we need this before we've even done anything. Remove it
+ * and see if anyone bitches.
+ for (i = 0; i < 0x10; i++) {
+ udelay(0xffff);
+ }
+ */
+
+ tmport = dev->ioport[0] + 1;
+ outb(0x08, tmport++);
+ outb(0x7f, tmport);
+ tmport = dev->ioport[0] + 0x11;
+ outb(0x20, tmport);
+
+ if ((dev->scam_on & 0x40) == 0) {
+ return;
+ }
+ m = 1;
+ m <<= dev->host_id[0];
+ j = 16;
+ if (dev->chip_ver < 4) {
+ m |= 0xff00;
+ j = 8;
+ }
+ assignid_map = m;
+ tmport = dev->ioport[0] + 0x02;
+ outb(0x02, tmport++); /* 2*2=4ms,3EH 2/32*3E=3.9ms */
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+
+ for (i = 0; i < j; i++) {
+ m = 1;
+ m = m << i;
+ if ((m & assignid_map) != 0) {
+ continue;
+ }
+ tmport = dev->ioport[0] + 0x0f;
+ outb(0, tmport++);
+ tmport += 0x02;
+ outb(0, tmport++);
+ outb(0, tmport++);
+ outb(0, tmport++);
+ if (i > 7) {
+ k = (i & 0x07) | 0x40;
+ } else {
+ k = i;
+ }
+ outb(k, tmport++);
+ tmport = dev->ioport[0] + 0x1b;
+ if (dev->chip_ver == 4) {
+ outb(0x01, tmport);
+ } else {
+ outb(0x00, tmport);
+ }
+wait_rdyok:
+ tmport = dev->ioport[0] + 0x18;
+ outb(0x09, tmport);
+ tmport += 0x07;
+
+ while ((inb(tmport) & 0x80) == 0x00)
+ cpu_relax();
+ tmport -= 0x08;
+ k = inb(tmport);
+ if (k != 0x16) {
+ if ((k == 0x85) || (k == 0x42)) {
+ continue;
+ }
+ tmport = dev->ioport[0] + 0x10;
+ outb(0x41, tmport);
+ goto wait_rdyok;
+ }
+ assignid_map |= m;
+
+ }
+ tmport = dev->ioport[0]