diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/atp870u.c |
Linux-2.6.12-rc2v2.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.c | 3970 |
1 files changed, 3970 insertions, 0 deletions
diff --git a/drivers/scsi/atp870u.c b/drivers/scsi/atp870u.c new file mode 100644 index 00000000000..45b75ddacaa --- /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; |