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 + -
显示快捷键?