/* $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 : SMP fixes * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scsi.h" #include "hosts.h" #include "atp870u.h" #include struct proc_dir_entry proc_scsi_atp870u = { PROC_SCSI_ATP870U, 7, "atp870u", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; 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,host_idu[2],chip_veru[2],scam_on[2],global_map[2]; static unsigned short int active_idu[2],wide_idu[2],sync_idu,ultra_map[2]; static int workingu[2]={0,0}; static Scsi_Cmnd *querequ[2][qcnt],*curr_req[2][16]; static unsigned char devspu[2][16] = {{0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}, {0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20}}; static unsigned char dirctu[2][16],last_cmd[2],in_snd[2],in_int[2]; static unsigned char ata_cdbu[2][16]; static unsigned int ioportu[2]={0,0}; static unsigned int irqnumu[2]={0,0}; static unsigned short int pciportu[2]; static unsigned long prdaddru[2][16],tran_lenu[2][16],last_lenu[2][16]; static unsigned char prd_tableu[2][16][1024]; static unsigned char *prd_posu[2][16]; static unsigned char quhdu[2],quendu[2]; static unsigned char devtypeu[2][16] = {{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}; static struct Scsi_Host * atp_host[2]={NULL,NULL}; 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,tarid,lun; unsigned char *prd; Scsi_Cmnd *workrequ; unsigned int workportu,tmport; unsigned long adrcntu,k; int errstus; for ( h=0; h < 2; h++ ) { if ( ( irq & 0x0f ) == irqnumu[h] ) { goto irq_numok; } } return; irq_numok: in_int[h]=1; workportu=ioportu[h]; tmport=workportu; if ( workingu[h] != 0 ) { tmport += 0x1f; j=inb(tmport); tmpcip=pciportu[h]; if ((inb(tmpcip) & 0x08) != 0) { tmpcip += 0x2; while((inb(tmpcip) & 0x08) != 0); } tmpcip=pciportu[h]; outb(0x00,tmpcip); tmport -=0x08; i=inb(tmport); if ((j & 0x40) == 0) { if ((last_cmd[h] & 0x40) == 0) { last_cmd[h]=0xff; } } else { last_cmd[h] |= 0x40; } tmport -= 0x02; tarid=inb(tmport); tmport += 0x02; if ((tarid & 0x40) != 0) { tarid=(tarid & 0x07) | 0x08; } else { tarid &= 0x07; } if ( i == 0x85 ) { if (wide_idu[h] != 0) { tmport=workportu+0x1b; j=inb(tmport) & 0x0e; j |= 0x01; outb(j,tmport); } if (((quhdu[h] != quendu[h]) || (last_cmd[h] != 0xff)) && (in_snd[h] == 0)) { send_s870(h); } in_int[h]=0; return; } if ( i == 0x21 ) { tmport -= 0x05; adrcntu=0; ((unsigned char *)&adrcntu)[2]=inb(tmport++); ((unsigned char *)&adrcntu)[1]=inb(tmport++); ((unsigned char *)&adrcntu)[0]=inb(tmport); k=last_lenu[h][tarid]; k -= adrcntu; tran_lenu[h][tarid]= k; last_lenu[h][tarid]=adrcntu; tmport -= 0x04; outb(0x41,tmport); tmport += 0x08; outb(0x08,tmport); in_int[h]=0; return ; } if ((i == 0x80) || (i == 0x8f)) { lun=0; tmport -= 0x07; j=inb(tmport); if ( j == 0x44 ) { tmport += 0x0d; lun=inb(tmport) & 0x07; } else { 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=last_lenu[h][tarid]; k -= adrcntu; tran_lenu[h][tarid]= k; last_lenu[h][tarid]=adrcntu; tmport += 0x04; outb(0x08,tmport); in_int[h]=0; return ; } else { outb(0x46,tmport); dirctu[h][tarid]=0x00; tmport += 0x02; outb(0x00,tmport++); outb(0x00,tmport++); outb(0x00,tmport++); tmport+=0x03; outb(0x08,tmport); in_int[h]=0; return; } } tmport=workportu + 0x10; outb(0x45,tmport); tmport += 0x06; tarid=inb(tmport); if ((tarid & 0x10) != 0) { tarid=(tarid & 0x07) | 0x08; } else { tarid &= 0x07; } workrequ=curr_req[h][tarid]; tmport=workportu + 0x0f; outb(lun,tmport); tmport += 0x02; outb(devspu[h][tarid],tmport++); adrcntu=tran_lenu[h][tarid]; k=last_lenu[h][tarid]; outb(((unsigned char *)&k)[2],tmport++); outb(((unsigned char *)&k)[1],tmport++); outb(((unsigned char *)&k)[0],tmport++); j=tarid; if ( tarid > 7 ) { j = (j & 0x07) | 0x40; } j |= dirctu[h][tarid]; outb(j,tmport++); outb(0x80,tmport); tmport=workportu + 0x1b; j=inb(tmport) & 0x0e; id=1; id=id << tarid; if ((id & wide_idu[h]) != 0) { j |= 0x01; } outb(j,tmport); if ( last_lenu[h][tarid] == 0 ) { tmport=workportu + 0x18; outb(0x08,tmport); in_int[h]=0; return ; } prd=prd_posu[h][tarid]; 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; prd_posu[h][tarid]=prd; } else { adrcntu -= k; prdaddru[h][tarid] += 0x08; prd += 0x08; if ( adrcntu == 0 ) { prd_posu[h][tarid]=prd; } } } tmpcip=pciportu[h] + 0x04; outl(prdaddru[h][tarid],tmpcip); tmpcip -= 0x02; outb(0x06,tmpcip); outb(0x00,tmpcip); tmpcip -= 0x02; tmport=workportu + 0x18; if ( dirctu[h][tarid] != 0 ) { outb(0x08,tmport); outb(0x01,tmpcip); in_int[h]=0; return; } outb(0x08,tmport); outb(0x09,tmpcip); in_int[h]=0; return; } workrequ=curr_req[h][tarid]; if ( i == 0x42 ) { errstus=0x02; workrequ->result=errstus; goto go_42; } if ( i == 0x16 ) { errstus=0; tmport -= 0x08; errstus=inb(tmport); workrequ->result=errstus; /* if ( errstus == 0x02 ) { tmport +=0x10; if ((inb(tmport) & 0x80) != 0) { printk(" autosense "); } tmport -=0x09; outb(0,tmport); tmport=workportu+0x3a; outb((unsigned char)(inb(tmport) | 0x10),tmport); tmport -= 0x39; outb(0x08,tmport++); outb(0x7f,tmport++); outb(0x03,tmport++); outb(0x00,tmport++); outb(0x00,tmport++); outb(0x00,tmport++); outb(0x0e,tmport++); outb(0x00,tmport); tmport+=0x07; outb(0x00,tmport++); tmport++; outb(devspu[h][workrequ->target],tmport++); outb(0x00,tmport++); outb(0x00,tmport++); outb(0x0e,tmport++); tmport+=0x03; outb(0x09,tmport); tmport+=0x07; i=0; adrcntu=(unsigned long)(&workrequ->sense_buffer[0]); get_sens: j=inb(tmport); if ((j & 0x01) != 0) { tmport-=0x06; (unsigned char)(((caddr_t) adrcntu)[i++])=inb(tmport); tmport+=0x06; goto get_sens; } if ((j & 0x80) == 0) { goto get_sens; } if ((j & 0x40) == 0) { tmport-=0x08; i=inb(tmport); } tmport=workportu+0x3a; outb((unsigned char)(inb(tmport) & 0xef),tmport); tmport=workportu+0x01; outb(0x2c,tmport); tmport += 0x15; outb(0x80,tmport); } */ go_42: spin_lock_irqsave(&io_request_lock, flags); (*workrequ->scsi_done)(workrequ); spin_unlock_irqrestore(&io_request_lock, flags); curr_req[h][tarid]=0; workingu[h]--; if (wide_idu[h] != 0) { tmport=workportu+0x1b; j=inb(tmport) & 0x0e; j |= 0x01; outb(j,tmport); } if (((last_cmd[h] != 0xff) || (quhdu[h] != quendu[h])) && (in_snd[h] == 0)) { send_s870(h); } in_int[h]=0; return; } if ( i == 0x4f ) { i=0x89; } i &= 0x0f; if ( i == 0x09 ) { tmpcip=tmpcip+4; outl(prdaddru[h][tarid],tmpcip); tmpcip=tmpcip-2; outb(0x06,tmpcip); outb(0x00,tmpcip); tmpcip=tmpcip-2; tmport=workportu+0x10; outb(0x41,tmport); dirctu[h][tarid]=0x00; tmport += 0x08; outb(0x08,tmport); outb(0x09,tmpcip); in_int[h]=0; return; } if ( i == 0x08 ) { tmpcip=tmpcip+4; outl(prdaddru[h][tarid],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); dirctu[h][tarid]=0x20; tmport += 0x03; outb(0x08,tmport); outb(0x01,tmpcip); in_int[h]=0; return; } tmport -= 0x07; if ( i == 0x0a ) { outb(0x30,tmport); } else { outb(0x46,tmport); } dirctu[h][tarid]=0x00; tmport += 0x02; outb(0x00,tmport++); outb(0x00,tmport++); outb(0x00,tmport++); tmport+=0x03; outb(0x08,tmport); in_int[h]=0; return; } else { tmport=workportu+0x17; inb(tmport); workingu[h]=0; in_int[h]=0; return; } } int atp870u_queuecommand(Scsi_Cmnd * req_p, void (*done)(Scsi_Cmnd *)) { unsigned char i,h; unsigned long flags; unsigned short int m; unsigned int tmport; 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; } m=1; m= m << req_p->target; if ( ( m & active_idu[h] ) == 0 ) { req_p->result = 0x00040000; done(req_p); return 0; } if (done) { req_p->scsi_done = done; } else { printk("atp870u_queuecommand: done can't be NULL\n"); req_p->result = 0; done(req_p); return 0; } quendu[h]++; if ( quendu[h] >= qcnt ) { quendu[h]=0; } wait_que_empty: if ( quhdu[h] == quendu[h] ) { goto wait_que_empty; } save_flags(flags); cli(); querequ[h][quendu[h]]=req_p; if ( quendu[h] == 0 ) { i=qcnt-1; } else { i=quendu[h]-1; } tmport = ioportu[h]+0x1c; restore_flags(flags); if ((inb(tmport) == 0) && (in_int[h] == 0) && (in_snd[h] == 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,tarid; unsigned char *prd; unsigned short int tmpcip,w; unsigned long l,bttl; unsigned int workportu; struct scatterlist * sgpnt; save_flags(flags); cli(); if ( in_snd[h] != 0 ) { restore_flags(flags); return; } in_snd[h]=1; if ((last_cmd[h] != 0xff) && ((last_cmd[h] & 0x40) != 0)) { last_cmd[h] &= 0x0f; workrequ=curr_req[h][last_cmd[h]]; goto cmd_subp; } workingu[h]++; j=quhdu[h]; quhdu[h]++; if ( quhdu[h] >= qcnt ) { quhdu[h]=0; } workrequ=querequ[h][quhdu[h]]; if ( curr_req[h][workrequ->target] == 0 ) { curr_req[h][workrequ->target]=workrequ; last_cmd[h]=workrequ->target; goto cmd_subp; } quhdu[h]=j; workingu[h]--; in_snd[h]=0; restore_flags(flags); return ; cmd_subp: workportu=ioportu[h]; tmport=workportu+0x1f; if ((inb(tmport) & 0xb0) != 0) { goto abortsnd; } tmport=workportu+0x1c; if ( inb(tmport) == 0 ) { goto oktosend; } abortsnd: last_cmd[h] |= 0x40; in_snd[h]=0; restore_flags(flags); return; oktosend: memcpy(&ata_cdbu[h][0], &workrequ->cmnd[0], workrequ->cmd_len); if ( ata_cdbu[h][0] == 0x25 ) { if ( workrequ->request_bufflen > 8 ) { workrequ->request_bufflen=0x08; } } if ( ata_cdbu[h][0] == 0x12 ) { if ( workrequ->request_bufflen > 0x24 ) { workrequ->request_bufflen = 0x24; ata_cdbu[h][4]=0x24; } } tmport=workportu+0x1b; j=inb(tmport) & 0x0e; tarid=workrequ->target; w=1; w = w << tarid; if ((w & wide_idu[h]) != 0) { j |= 0x01; } outb(j,tmport); tmport=workportu; outb(workrequ->cmd_len,tmport++); outb(0x2c,tmport++); outb(0xcf,tmport++); for ( i=0 ; i < workrequ->cmd_len ; i++ ) { outb(ata_cdbu[h][i],tmport++); } tmport=workportu+0x0f; outb(0x00,tmport); tmport+=0x02; outb(devspu[h][tarid],tmport++); if (workrequ->use_sg) { l=0; sgpnt = (struct scatterlist *) workrequ->request_buffer; for(i=0; iuse_sg; i++) { if(sgpnt[i].length == 0 || workrequ->use_sg > ATP870U_SCATTER) { panic("Foooooooood fight!"); } l += sgpnt[i].length; } } else { l=workrequ->request_bufflen; } 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=tarid; last_lenu[h][j]=l; tran_lenu[h][j]=0; if ((j & 0x08) != 0) { j=(j & 0x07) | 0x40; } if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) { outb((unsigned char)(j | 0x20),tmport++); } else { outb(j,tmport++); } outb(0x80,tmport); tmport=workportu + 0x1c; dirctu[h][tarid]=0; if ( l == 0 ) { if ( inb(tmport) == 0 ) { tmport=workportu+0x18; outb(0x08,tmport); } else { last_cmd[h] |= 0x40; } in_snd[h]=0; restore_flags(flags); return; } tmpcip=pciportu[h]; prd=&prd_tableu[h][tarid][0]; prd_posu[h][tarid]=prd; if (workrequ->use_sg) { sgpnt = (struct scatterlist *) workrequ->request_buffer; i=0; for(j=0; juse_sg; j++) { (unsigned long)(((unsigned long *)(prd))[i >> 1])=(unsigned long)sgpnt[j].address; (unsigned short int)(((unsigned short int *)(prd))[i+2])=sgpnt[j].length; (unsigned short int)(((unsigned short int *)(prd))[i+3])=0; i +=0x04; } (unsigned short int)(((unsigned short int *)(prd))[i-1])=0x8000; } else { bttl=(unsigned long)workrequ->request_buffer; l=workrequ->request_bufflen; i=0; while ( l > 0x10000 ) { (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x0000; (unsigned short int)(((unsigned short int *)(prd))[i+2])=0x0000; (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl; l -= 0x10000; bttl += 0x10000; i += 0x04; } (unsigned short int)(((unsigned short int *)(prd))[i+3])=0x8000; (unsigned short int)(((unsigned short int *)(prd))[i+2])=l; (unsigned long)(((unsigned long *)(prd))[i >> 1])=bttl; } tmpcip=tmpcip+4; prdaddru[h][tarid]=(unsigned long)&prd_tableu[h][tarid][0]; outl(prdaddru[h][tarid],tmpcip); tmpcip=tmpcip-2; outb(0x06,tmpcip); outb(0x00,tmpcip); tmpcip=tmpcip-2; if ((ata_cdbu[h][0] == 0x0a) || (ata_cdbu[h][0] == 0x2a) || (ata_cdbu[h][0] == 0xaa) || (ata_cdbu[h][0] == 0x15)) { dirctu[h][tarid]=0x20; if ( inb(tmport) == 0 ) { tmport=workportu+0x18; outb(0x08,tmport); outb(0x01,tmpcip); } else { last_cmd[h] |= 0x40; } in_snd[h]=0; restore_flags(flags); return; } if ( inb(tmport) == 0 ) { tmport=workportu+0x18; outb(0x08,tmport); outb(0x09,tmpcip); } else { last_cmd[h] |= 0x40; } in_snd[h]=0; restore_flags(flags); return; } static void internal_done(Scsi_Cmnd * SCpnt) { SCpnt->SCp.Status++; } int atp870u_command(Scsi_Cmnd * SCpnt) { atp870u_queuecommand(SCpnt, internal_done); SCpnt->SCp.Status = 0; while (!SCpnt->SCp.Status) barrier(); return SCpnt->result; } unsigned char fun_scam ( unsigned char host,unsigned short int * val ) { unsigned int tmport ; unsigned short int i,k; unsigned char j; tmport = ioportu[host]+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; } void tscam( unsigned char 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]; static unsigned char g2q_tab[8]={ 0x38,0x31,0x32,0x2b,0x34,0x2d,0x2e,0x27 }; for ( i=0; i < 0x10; i++ ) { mydlyu(0xffff); } tmport = ioportu[host]+1; outb(0x08,tmport++); outb(0x7f,tmport); tmport = ioportu[host]+0x11; outb(0x20,tmport); if ((scam_on[host] & 0x40) == 0) { return; } m=1; m <<= host_idu[host]; j=16; if ( chip_veru[host] < 4 ) { m |= 0xff00; j=8; } assignid_map=m; tmport = ioportu[host]+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< 7 ) { k=(i & 0x07) | 0x40; } else { k=i; } outb(k,tmport++); tmport = ioportu[host]+0x1b; if ( chip_veru[host] == 4 ) { outb((unsigned char)((inb(tmport) & 0x0e) | 0x01),tmport); } else { outb((unsigned char)(inb(tmport) & 0x0e),tmport); } wait_rdyok: tmport = ioportu[host]+0x18; outb(0x09,tmport); tmport += 0x07; while ((inb(tmport) & 0x80) == 0x00); tmport -= 0x08; k=inb(tmport); if ( k != 0x16 ) { if ((k == 0x85) || (k == 0x42)) { continue; } tmport = ioportu[host]+0x10; outb(0x41,tmport); goto wait_rdyok; } assignid_map |= m; } tmport = ioportu[host]+0x02; outb(0x7f,tmport); tmport = ioportu[host]+0x1b; outb(0x02,tmport); outb(0,0x80); val=0x0080; /* bsy */ tmport = ioportu[host]+0x1c; outw(val,tmport); val |=0x0040; /* sel */ outw(val,tmport); val |=0x0004; /* msg */ outw(val,tmport); inb(0x80); /* 2 deskew delay(45ns*2=90ns) */ val &=0x007f; /* no bsy */ outw(val,tmport); mydlyu(0xffff); /* recommanded SCAM selection response time */ mydlyu(0xffff); val &=0x00fb; /* after 1ms no msg */ outw(val,tmport); wait_nomsg: if ((inb(tmport) & 0x04) != 0) { goto wait_nomsg; } outb(1,0x80); mydlyu(100); for ( n=0; n < 0x30000; n++ ) { if ((inb(tmport) & 0x80) != 0) /* bsy ? */ { goto wait_io; } } goto TCM_SYNC; wait_io: for ( n=0; n < 0x30000; n++ ) { if ((inb(tmport) & 0x81) == 0x0081) { goto wait_io1; } } goto TCM_SYNC; wait_io1: inb(0x80); val |=0x8003; /* io,cd,db7 */ outw(val,tmport); inb(0x80); val &=0x00bf; /* no sel */ outw(val,tmport); outb(2,0x80); TCM_SYNC: mydlyu(0x800); if ((inb(tmport) & 0x80) == 0x00) /* bsy ? */ { outw(0,tmport--); outb(0,tmport); tmport=ioportu[host] + 0x15; outb(0,tmport); tmport += 0x03; outb(0x09,tmport); tmport += 0x07; while ((inb(tmport) & 0x80) == 0); tmport -= 0x08; inb(tmport); return; } val &= 0x00ff; /* synchronization */ val |= 0x3f00; fun_scam(host,&val); outb(3,0x80); val &= 0x00ff; /* isolation */ val |= 0x2000; fun_scam(host,&val); outb(4,0x80); i=8; j=0; TCM_ID: if ((inw(tmport) & 0x2000) == 0) { goto TCM_ID; } outb(5,0x80); val &= 0x00ff; /* get ID_STRING */ val |= 0x2000; k=fun_scam(host,&val); if ((k & 0x03) == 0) { goto TCM_5; } mbuf[j] <<= 0x01; mbuf[j] &= 0xfe; if ((k & 0x02) != 0) { mbuf[j] |= 0x01; } i--; if ( i > 0 ) { goto TCM_ID; } j++; i=8; goto TCM_ID; TCM_5: /* isolation complete.. */ /* mbuf[32]=0; printk(" \n%x %x %x %s\n ",assignid_map,mbuf[0],mbuf[1],&mbuf[2]); */ i=15; j=mbuf[0]; if ((j & 0x20) != 0) /* bit5=1:ID upto 7 */ { i=7; } if ((j & 0x06) == 0) /* IDvalid? */ { goto G2Q5; } k=mbuf[1]; small_id: m=1; m <<= k; if ((m & assignid_map) == 0) { goto G2Q_QUIN; } if ( k > 0 ) { k--; goto small_id; } G2Q5: /* srch from max acceptable ID# */ k=i; /* max acceptable ID# */ G2Q_LP: m=1; m <<= k; if ((m & assignid_map) == 0) { goto G2Q_QUIN; } if ( k > 0 ) { k--; goto G2Q_LP; } G2Q_QUIN: /* k=binID#, */ assignid_map |= m; if ( k < 8 ) { quintet[0]=0x38; /* 1st dft ID<8 */ } else { quintet[0]=0x31; /* 1st ID>=8 */ } k &= 0x07; quintet[1]=g2q_tab[k]; val &= 0x00ff; /* AssignID 1stQuintet,AH=001xxxxx */ m=quintet[0] << 8; val |= m; fun_scam(host,&val); val &= 0x00ff; /* AssignID 2ndQuintet,AH=001xxxxx */ m=quintet[1] << 8; val |= m; fun_scam(host,&val); goto TCM_SYNC; } void is870(unsigned long host,unsigned int wkport ) { unsigned int tmport ; unsigned char i,j,k,rmb; unsigned short int m; static unsigned char mbuf[512]; static unsigned char satn[9] = { 0,0,0,0,0,0,0,6,6 }; static unsigned char inqd[9] = { 0x12,0,0,0,0x24,0,0,0x24,6 }; static unsigned char synn[6] = { 0x80,1,3,1,0x19,0x0e }; static unsigned char synu[6] = { 0x80,1,3,1,0x0c,0x0e }; static unsigned char synw[6] = { 0x80,1,3,1,0x0c,0x07 }; static unsigned char wide[6] = { 0x80,1,2,3,1,0 }; sync_idu=0; tmport=wkport+0x3a; outb((unsigned char)(inb(tmport) | 0x10),tmport); for ( i = 0 ; i < 16 ; i ++ ) { if ((chip_veru[host] != 4) && (i > 7)) { break; } m=1; m=m< 0x64 ) { continue; } if ( mbuf[4] > 0x0c ) { mbuf[4]=0x0c; } devspu[host][i] = mbuf[4]; if ((mbuf[3] < 0x0d) && (rmb == 0)) { j=0xa0; goto set_syn_ok; } if ( mbuf[3] < 0x1a ) { j=0x20; goto set_syn_ok; } if ( mbuf[3] < 0x33 ) { j=0x40; goto set_syn_ok; } if ( mbuf[3] < 0x4c ) { j=0x50; goto set_syn_ok; } j=0x60; set_syn_ok: devspu[host][i] = (devspu[host][i] & 0x0f) | j; } tmport=wkport+0x3a; outb((unsigned char)(inb(tmport) & 0xef),tmport); } /* return non-zero on detection */ int atp870u_detect(Scsi_Host_Template * tpnt) { unsigned char irq,h,k; unsigned long flags; unsigned int base_io,error,tmport; unsigned short index = 0; unsigned char pci_bus[3], pci_device_fn[3], chip_ver[3],host_id; struct Scsi_Host * shpnt = NULL; int count = 0; static unsigned short devid[7]={0x8002,0x8010,0x8020,0x8030,0x8040,0x8050,0}; static struct pci_dev *pdev = NULL, *acard_pdev[3]; printk("aec671x_detect: \n"); if (!pci_present()) { printk(" NO BIOS32 SUPPORT.\n"); return count; } tpnt->proc_dir = &proc_scsi_atp870u; for ( h = 0 ; h < 2 ; h++ ) { active_idu[h]=0; wide_idu[h]=0; host_idu[h]=0x07; quhdu[h]=0; quendu[h]=0; pci_bus[h]=0; pci_device_fn[h]=0xff; chip_ver[h]=0; last_cmd[h]=0xff; in_snd[h]=0; in_int[h]=0; for ( k = 0 ; k < qcnt ; k++ ) { querequ[h][k]=0; } for ( k = 0 ; k < 16 ; k++ ) { curr_req[h][k]=0; } } h=0; while ( devid[h] != 0 ) { pdev = pci_find_device(0x1191,devid[h],pdev); if (pdev == NULL) { h++; index=0; continue; } chip_ver[2]=0; /* To avoid messing with the things below... */ acard_pdev[2] = pdev; pci_device_fn[2] = pdev->devfn; pci_bus[2] = pdev->bus->number; if ( devid[h] == 0x8002 ) { error = pci_read_config_byte(pdev,0x08,&chip_ver[2]); if ( chip_ver[2] < 2 ) { goto nxt_devfn; } } if ( devid[h] == 0x8010 ) { chip_ver[2]=0x04; } if ( pci_device_fn[2] < pci_device_fn[0] ) { acard_pdev[1]=acard_pdev[0]; pci_bus[1]=pci_bus[0]; pci_device_fn[1]=pci_device_fn[0]; chip_ver[1]=chip_ver[0]; acard_pdev[0]=acard_pdev[2]; pci_bus[0]=pci_bus[2]; pci_device_fn[0]=pci_device_fn[2]; chip_ver[0]=chip_ver[2]; } else if ( pci_device_fn[2] < pci_device_fn[1] ) { acard_pdev[1]=acard_pdev[2]; pci_bus[1]=pci_bus[2]; pci_device_fn[1]=pci_device_fn[2]; chip_ver[1]=chip_ver[2]; } nxt_devfn: index++; if ( index > 3 ) { index=0; h++; } } for ( h=0; h < 2; h++ ) { if ( pci_device_fn[h] == 0xff ) { return count; } pdev = acard_pdev[h]; pdev->devfn = pci_device_fn[h]; pdev->bus->number = pci_bus[h]; /* Found an atp870u/w. */ error = pci_read_config_dword(pdev,0x10,&base_io); error += pci_read_config_byte(pdev,0x3c,&irq); error += pci_read_config_byte(pdev,0x49,&host_id); base_io &= 0xfffffff8; printk(" ACARD AEC-671X PCI Ultra/W SCSI-3 Host Adapter: %d IO:%x, IRQ:%d.\n" ,h,base_io,irq); ioportu[h]=base_io; pciportu[h]=base_io + 0x20; irqnumu[h]=irq; host_id &= 0x07; host_idu[h]=host_id; chip_veru[h]=chip_ver[h]; tmport=base_io+0x22; scam_on[h]=inb(tmport); tmport += 0x0b; global_map[h]=inb(tmport++); ultra_map[h]=inw(tmport); if ( ultra_map[h] == 0 ) { scam_on[h]=0x00; global_map[h]=0x20; ultra_map[h]=0xffff; } shpnt = scsi_register(tpnt,4); save_flags(flags); cli(); if (request_irq(irq,atp870u_intr_handle, 0, "atp870u", NULL)) { printk("Unable to allocate IRQ for Acard controller.\n"); goto unregister; } tmport=base_io+0x3a; k=(inb(tmport) & 0xf3) | 0x10; outb(k,tmport); outb((k & 0xdf),tmport); mydlyu(0x8000); outb(k,tmport); mydlyu(0x8000); tmport=base_io; outb((host_id | 0x08),tmport); tmport += 0x18; outb(0,tmport); tmport += 0x07; while ((inb(tmport) & 0x80) == 0); tmport -= 0x08; inb(tmport); tmport = base_io +1; outb(8,tmport++); outb(0x7f,tmport); tmport = base_io + 0x11; outb(0x20,tmport); tscam(h); is870(h,base_io); tmport=base_io+0x3a; outb((inb(tmport) & 0xef),tmport); atp_host[h] = shpnt; if ( chip_ver[h] == 4 ) { shpnt->max_id = 16; } shpnt->this_id = host_id; shpnt->unique_id = base_io; shpnt->io_port = base_io; shpnt->n_io_port = 0x40; /* Number of bytes of I/O space used */ shpnt->irq = irq; restore_flags(flags); request_region(base_io, 0x40,"atp870u"); /* Register the IO ports that we use */ count++; index++; continue; unregister: scsi_unregister(shpnt); restore_flags(flags); index++; continue; } return count; } /* The abort command does not leave the device in a clean state where it is available to be used again. Until this gets worked out, we will leave it commented out. */ int atp870u_abort(Scsi_Cmnd * SCpnt) { unsigned char h,j; unsigned int tmport; /* printk(" atp870u_abort: \n"); */ for ( h=0; h <= admaxu; h++ ) { if ( SCpnt->host == atp_host[h] ) { goto find_adp; } } panic("Abort host not found !"); find_adp: printk(" workingu=%x last_cmd=%x ",workingu[h],last_cmd[h]); printk(" quhdu=%x quendu=%x ",quhdu[h],quendu[h]); tmport=ioportu[h]; for ( j=0; j < 0x17; j++) { printk(" r%2x=%2x",j,inb(tmport++)); } tmport += 0x05; printk(" r1c=%2x",inb(tmport)); tmport += 0x03; printk(" r1f=%2x in_snd=%2x ",inb(tmport),in_snd[h]); tmport++; printk(" r20=%2x",inb(tmport)); tmport += 0x02; printk(" r22=%2x \n",inb(tmport)); return (SCSI_ABORT_SNOOZE); } int atp870u_reset(Scsi_Cmnd * SCpnt, unsigned int reset_flags) { unsigned char h; /* * See if a bus reset was suggested. */ /* printk("atp870u_reset: \n"); */ for( h=0; h <= admaxu; h++ ) { if ( SCpnt->host == atp_host[h] ) { goto find_host; } } panic("Reset bus host not found !"); find_host: /* SCpnt->result = 0x00080000; SCpnt->scsi_done(SCpnt); workingu[h]=0; quhdu[h]=0; quendu[h]=0; return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET); */ return (SCSI_RESET_SNOOZE); } const char * atp870u_info(struct Scsi_Host *notused) { static char buffer[128]; strcpy(buffer, "ACARD AEC-6710/6712 PCI Ultra/W SCSI-3 Adapter Driver V1.0 "); return buffer; } int atp870u_set_info(char *buffer, int length, struct Scsi_Host *HBAptr) { return (-ENOSYS); /* Currently this is a no-op */ } #define BLS buffer + len + size int atp870u_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout) { struct Scsi_Host *HBAptr; static u8 buff[512]; int i; int size = 0; int len = 0; off_t begin = 0; off_t pos = 0; HBAptr = NULL; for (i = 0; i < 2; i++) { if ((HBAptr = atp_host[i]) != NULL) { if (HBAptr->host_no == hostno) { break; } HBAptr = NULL; } } if (HBAptr == NULL) { size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno); len += size; pos = begin + len; size = 0; goto stop_output; } if (inout == TRUE) /* Has data been written to the file? */ { return (atp870u_set_info(buffer, length, HBAptr)); } if (offset == 0) { memset(buff, 0, sizeof(buff)); } size += sprintf(BLS, "ACARD AEC-671X Driver Version: 1.0\n"); len += size; pos = begin + len; size = 0; size += sprintf(BLS, "\n"); size += sprintf(BLS, "Adapter Configuration:\n"); size += sprintf(BLS, " Base IO: %#.4lx\n", HBAptr->io_port); size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq); len += size; pos = begin + len; size = 0; stop_output: *start = buffer + (offset - begin); /* Start of wanted data */ len -= (offset - begin); /* Start slop */ if (len > length) { len = length; /* Ending slop */ } return (len); } #include "sd.h" int atp870u_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip) { int heads, sectors, cylinders; heads = 64; sectors = 32; cylinders = disk->capacity / (heads * sectors); if ( cylinders > 1024 ) { heads = 255; sectors = 63; cylinders = disk->capacity / (heads * sectors); } ip[0] = heads; ip[1] = sectors; ip[2] = cylinders; return 0; } #ifdef MODULE Scsi_Host_Template driver_template = ATP870U; #include "scsi_module.c" #endif