📄 atp870u.c
字号:
/* $Id: atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $ * linux/kernel/atp870u.c * * Copyright (C) 1997 Wu Ching Chen * 2.1.x update (C) 1998 Krzysztof G. Baranowski * * 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 (7.1) */#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/sched.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <asm/system.h>#include <asm/io.h>#include <linux/pci.h>#include <linux/blk.h>#include "scsi.h"#include "hosts.h"#include "atp870u.h"#include<linux/stat.h>void mydlyu(unsigned int);/* * static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/atp870u.c,v 1.0 1997/05/07 15:22:00 root Exp root $"; */static unsigned char admaxu = 1;static unsigned short int sync_idu;static unsigned int irqnumu[2] = {0, 0};struct atp_unit{ unsigned long ioport; unsigned long irq; unsigned long pciport; unsigned char last_cmd; unsigned char in_snd; unsigned char in_int; unsigned char quhdu; unsigned char quendu; unsigned char scam_on; unsigned char global_map; unsigned char chip_veru; unsigned char host_idu; int working; unsigned short wide_idu; unsigned short active_idu; unsigned short ultra_map; unsigned short async; unsigned short deviceid; unsigned char ata_cdbu[16]; unsigned char sp[16]; Scsi_Cmnd *querequ[qcnt]; struct atp_id { unsigned char dirctu; unsigned char devspu; unsigned char devtypeu; unsigned long prdaddru; unsigned long tran_lenu; unsigned long last_lenu; unsigned char *prd_posu; unsigned char *prd_tableu; Scsi_Cmnd *curr_req; } id[16];};static struct Scsi_Host *atp_host[2] = {NULL, NULL};static struct atp_unit atp_unit[2];static void atp870u_intr_handle(int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; unsigned short int tmpcip, id; unsigned char i, j, h, target_id, lun; unsigned char *prd; Scsi_Cmnd *workrequ; unsigned int workportu, tmport; unsigned long adrcntu, k; int errstus; struct atp_unit *dev = dev_id; for (h = 0; h < 2; h++) { if (irq == irqnumu[h]) { goto irq_numok; } } return;irq_numok: 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; } 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 */ if (((dev->quhdu != dev->quendu) || (dev->last_cmd != 0xff)) && (dev->in_snd == 0)) { send_s870(h); } /* * Done */ dev->in_int = 0; return; } if (i == 0x40) { dev->last_cmd |= 0x40; dev->in_int = 0; return; } 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; return; } 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; return; } 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; return; } } 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; return; } 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; return; } outb(0x08, tmport); outb(0x09, tmpcip); dev->in_int = 0; return; } /* * 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 */ spin_lock_irqsave(&io_request_lock, flags); (*workrequ->scsi_done) (workrequ); /* * Clear it off the queue */ dev->id[target_id].curr_req = 0; dev->working--; spin_unlock_irqrestore(&io_request_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 */ if (((dev->last_cmd != 0xff) || (dev->quhdu != dev->quendu)) && (dev->in_snd == 0)) { send_s870(h); } dev->in_int = 0; return; } 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; return; } 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; return; } 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; return; } else {// tmport = workportu + 0x17;// inb(tmport);// dev->working = 0; dev->in_int = 0; return; }}int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done) (Scsi_Cmnd *)){ unsigned char h; unsigned long flags; unsigned short int m; unsigned int tmport; struct atp_unit *dev; for (h = 0; h <= admaxu; h++) { if (req_p->host == atp_host[h]) { goto host_ok; } } return 0;host_ok: if (req_p->channel != 0) { req_p->result = 0x00040000; done(req_p); return 0; } dev = &atp_unit[h]; m = 1; m = m << req_p->target; /* * 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 */ save_flags(flags); cli(); 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); restore_flags(flags); return 0; } dev->querequ[dev->quendu] = req_p; tmport = dev->ioport + 0x1c; restore_flags(flags); if ((inb(tmport) == 0) && (dev->in_int == 0) && (dev->in_snd == 0)) { send_s870(h); } return 0;}void mydlyu(unsigned int dlycnt){ unsigned int i; for (i = 0; i < dlycnt; i++) { inb(0x80); }}void send_s870(unsigned char h){ unsigned int tmport; Scsi_Cmnd *workrequ; unsigned long flags; unsigned int i; unsigned char j, target_id; unsigned char *prd; unsigned short int tmpcip, w; unsigned long l, bttl; unsigned int workportu; struct scatterlist *sgpnt; struct atp_unit *dev = &atp_unit[h]; save_flags(flags); cli(); if (dev->in_snd != 0) { restore_flags(flags); 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; restore_flags(flags); return ; } } if ((dev->last_cmd != 0xff) && (dev->working != 0)) { dev->in_snd = 0; restore_flags(flags); return ; } dev->working++; j = dev->quhdu; dev->quhdu++; if (dev->quhdu >= qcnt) { dev->quhdu = 0; } workrequ = dev->querequ[dev->quhdu]; if (dev->id[workrequ->target].curr_req == 0) { dev->id[workrequ->target].curr_req = workrequ; dev->last_cmd = workrequ->target; goto cmd_subp; } dev->quhdu = j; dev->working--; dev->in_snd = 0; restore_flags(flags); 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; restore_flags(flags); 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->target; /* * 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -