|
|
1.1 ! root 1: /* $Id: 7000fasst.c,v 1.1 1992/07/24 06:27:38 root Exp root $ ! 2: * linux/kernel/7000fasst.c ! 3: * ! 4: * Copyright (C) 1992 Thomas Wuensche ! 5: * closely related to the aha1542 driver from Tommy Thorn ! 6: * ( as close as different hardware allows on a lowlevel-driver :-) ) ! 7: */ ! 8: ! 9: #include <linux/config.h> ! 10: #include <linux/kernel.h> ! 11: #include <linux/head.h> ! 12: #include <linux/types.h> ! 13: #include <linux/string.h> ! 14: #include <asm/system.h> ! 15: #include <asm/io.h> ! 16: #include "scsi.h" ! 17: #include "hosts.h" ! 18: ! 19: struct mailbox{ ! 20: unchar status; ! 21: unchar scbptr[3]; ! 22: }; ! 23: ! 24: /* #define DEBUG */ ! 25: ! 26: #include "7000fasst.h" ! 27: #ifdef DEBUG ! 28: #define DEB(x) x ! 29: #else ! 30: #define DEB(x) ! 31: #endif ! 32: ! 33: /*static const char RCSid[] = "$Header: /usr/src/linux/kernel/blk_drv/scsi/RCS/7000fasst.c,v 1.1 1992/07/24 06:27:38 root Exp root $";*/ ! 34: ! 35: static struct scb scbs[OGMB_CNT]; ! 36: ! 37: long wd7000fasst_WAITnexttimeout = 3000000; ! 38: ! 39: void (*wd7000fasst_do_done)() = NULL; ! 40: extern void wd7000fasst_interrupt(); ! 41: void wd7000fasst_call_buh(); ! 42: ! 43: static unchar controlstat = 0; ! 44: static unchar wd7000fasst_hostno; ! 45: ! 46: #define wd7000fasst_intr_reset() outb(0,INTR_ACK) ! 47: #define PC_IMR 0x21 ! 48: #define AT_IMR 0xa1 ! 49: ! 50: #define wd7000fasst_enable_intr(){\ ! 51: controlstat |= INT_EN;\ ! 52: outb(controlstat,CONTROL);\ ! 53: outb((inb((intr_chan<=7)?PC_IMR:AT_IMR))& ~0xff,(intr_chan<=7)?PC_IMR:AT_IMR);} ! 54: ! 55: #define wd7000fasst_disable_intr() outb(controlstat |= INT_EN, CONTROL) ! 56: #define wd7000fasst_enable_dma() {\ ! 57: controlstat |= DMA_EN;\ ! 58: outb(controlstat,CONTROL);\ ! 59: outb((DMA_CH|CASCADE),DMA_MODE_REG);\ ! 60: outb(DMA_CH,DMA_MASK_REG);} ! 61: ! 62: #define wd7000fasst_disable_dma() {\ ! 63: outb(DMA_CH|S_DMA_MASK,DMA_MASK_REG);\ ! 64: controlstat &= ~DMA_EN;\ ! 65: outb(controlstat,CONTROL);} ! 66: ! 67: #define WAIT(port, mask, allof, noneof) \ ! 68: { register WAITbits; \ ! 69: register WAITtimeout = wd7000fasst_WAITnexttimeout; \ ! 70: while (1) { \ ! 71: WAITbits = inb(port) & (mask); \ ! 72: if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \ ! 73: break; \ ! 74: if (--WAITtimeout == 0) goto fail; \ ! 75: } \ ! 76: } ! 77: ! 78: static void wd7000fasst_stat(void) ! 79: { ! 80: /* int s = inb(ASC_STAT), i = inb(INTR_STAT);*/ ! 81: /* printk("status = %x, intrflags = %x served %d last %x\n", s, i, intr_flag, intr_last); ! 82: printk("status=%x intrflags=%x\n", s, i); ! 83: */} ! 84: ! 85: static int wd7000fasst_out(unchar *cmdp, int len) ! 86: { ! 87: while (len--) ! 88: { ! 89: WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); ! 90: outb(*cmdp++, COMMAND); ! 91: } ! 92: return 0; ! 93: fail: ! 94: printk("wd7000fasst_out failed(%d): ", len+1); wd7000fasst_stat(); ! 95: return 1; ! 96: } ! 97: ! 98: int wd7000fasst_make_error_code(unsigned hosterr, unsigned scsierr) ! 99: { ! 100: #ifdef DEBUG ! 101: int in_error=hosterr; ! 102: #endif ! 103: switch ((hosterr&0xff00)>>8){ ! 104: case 0: /* It is reserved, should never happen */ ! 105: hosterr=DID_ERROR; ! 106: break; ! 107: case 1: hosterr=DID_OK; ! 108: break; ! 109: case 2: /* Command complete with logged error */ ! 110: /* My actual copies of the manual pages are unreadable ! 111: * For now we simply tell there is an error */ ! 112: DEB(printk("Hosterror: VUE = %x\n",hosterr&0xff);) ! 113: switch (hosterr&0xff) { ! 114: default: DEB(printk("wd7000fasst_make_error_code: unknown hoststatus %x\n", hosterr);) ! 115: hosterr=DID_ERROR; ! 116: break; ! 117: } ! 118: break; ! 119: case 4: hosterr=DID_BAD_TARGET; /* Command failed to complete without SCSI status */ ! 120: break; ! 121: case 5: hosterr=DID_RESET; /* Cmd terminated; Bus reset by external device */ ! 122: break; ! 123: case 6: hosterr=DID_ERROR;/* Hardware Failure, requires host reset */ ! 124: break; ! 125: case 7: hosterr=DID_RESET; ! 126: break; ! 127: case 8: hosterr=DID_OK; ! 128: printk("wd7000fasst: Linked command not implemented\n"); ! 129: break; ! 130: } ! 131: #ifdef DEBUG ! 132: if (scsierr||hosterr) printk("SCSI-Command error: SCSI %x HOST %x RETURN %x\n",scsierr,in_error,hosterr); ! 133: #endif ! 134: return scsierr|(hosterr << 16); ! 135: } ! 136: ! 137: /* The following is space for the Mailboxes */ ! 138: struct{ struct mailbox ombox[OGMB_CNT]; ! 139: struct mailbox imbox[ICMB_CNT]; } mbstruct; ! 140: ! 141: int wd7000fasst_init(void) ! 142: { int i; ! 143: volatile int debug = 0; ! 144: /* Page 47 */ ! 145: unchar init_block[]={ 1, 7, 0x18, 0x18, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT }; ! 146: /* Reset the adapter. I ought to make a hard reset, but it's not really nessesary */ ! 147: ! 148: DEB(printk("wd7000fasst_init called \n")); ! 149: ! 150: outb(SCSI_RES|ASC_RES, CONTROL); ! 151: /* Wait at least 25 us */ ! 152: for (i=0; i< 1000; i++) inb(ASC_STAT); ! 153: /* Now reset the reset */ ! 154: outb(0,CONTROL); ! 155: debug = 1; ! 156: /* Expect Command Port Ready */ ! 157: WAIT(ASC_STAT, STATMASK, CMD_RDY, 0); ! 158: DEB(printk("wd7000fasst_init: Power on Diagnostics finished\n")); ! 159: if ((i=inb(INTR_STAT))!=1) ! 160: printk("Power on Diagnostics error %x\n",i); ! 161: ! 162: debug = 2; ! 163: /* Clear mbstruct */ ! 164: memset(&mbstruct,0,sizeof (mbstruct)); ! 165: /* Set up init block */ ! 166: any2scsi(init_block+5,&mbstruct); ! 167: /* Execute init command */ ! 168: wd7000fasst_out(init_block,sizeof(init_block)); ! 169: DEB(printk("Init-Block :"); ! 170: for (i=0;i<sizeof(init_block);i++) printk(" %x",init_block[i]); ! 171: printk("\n");) ! 172: /* Wait until init finished */ ! 173: WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); ! 174: outb(2,COMMAND); ! 175: WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0); ! 176: /* Enable Interrupt and DMA */ ! 177: wd7000fasst_enable_dma(); ! 178: wd7000fasst_call_buh(); ! 179: DEB(printk("wd7000fasst_detect: enable interrupt channel %d\n", intr_chan)); ! 180: wd7000fasst_enable_intr(); ! 181: printk("wd7000fasst_init: Controller initialized\n"); ! 182: return 1; ! 183: fail: ! 184: return 0; /* 0 = not ok */ ! 185: } ! 186: ! 187: /* What's this little function for? */ ! 188: char *wd7000fasst_info(void) ! 189: { ! 190: static char buffer[] = "Western Digital 7000-FASST"; ! 191: return buffer; ! 192: } ! 193: ! 194: /* A "high" level interrupt handler */ ! 195: void wd7000fasst_intr_handle(void) ! 196: { struct scb * scbptr; ! 197: DEB(int len=sizeof (struct scb);) ! 198: DEB(int k;) ! 199: unsigned host_error,scsi_error; ! 200: int flag = inb(INTR_STAT); ! 201: void (*my_done)() = wd7000fasst_do_done; ! 202: int errstatus; ! 203: DEB(printk("WD Interrupt aufgetreten\n")); ! 204: if (!(inb(ASC_STAT)&0x80)){ ! 205: printk("Interrupt without Interrupt\n"); ! 206: wd7000fasst_intr_reset(); ! 207: return; ! 208: } ! 209: wd7000fasst_do_done = NULL; ! 210: if (!my_done) { ! 211: printk("wd7000fasst_intr_handle: Unexpected interrupt\n"); ! 212: wd7000fasst_intr_reset(); ! 213: return; ! 214: } ! 215: ! 216: /* is there mail for me :-) */ ! 217: if ((flag&0xc0)==0xc0){ ! 218: /* Ok, the interrupt is for an incoming mailbox */ ! 219: /* We make the content available for the starter of the command */ ! 220: DEB(if ((flag&0xc0)==0xc0) printk("INTR_STAT: %x mbstat: %x\n",flag,mbstruct.imbox[flag&0x3f].status)); ! 221: if (mbstruct.imbox[flag&0x3f].status==0){ ! 222: /* Something strange happened */ ! 223: wd7000fasst_intr_reset(); ! 224: return; ! 225: ; ! 226: } ! 227: scbptr=(struct scb *)scsi2int(mbstruct.imbox[flag&0x3f].scbptr); ! 228: DEB(printk("Datenbereiche aus %x ein %x \n",scbptr,&(scbs[flag&0x3f])); ! 229: printk("SCB after return:\n"); ! 230: k=0; ! 231: while (len-- >0){ ! 232: printk("%x ",*((unchar *)scbptr)); ! 233: ((unchar *)scbptr)++; ! 234: if (++k==16){ printk("\n"); k=0; } ! 235: }); ! 236: } ! 237: else { printk("Error in interrupt\n"); return; } ! 238: /* more error checking left out here */ ! 239: ! 240: scbptr=(struct scb *)scsi2int(mbstruct.imbox[flag&0x3f].scbptr); ! 241: host_error=scbptr->vue | mbstruct.imbox[flag&0x3f].status<<8; ! 242: scsi_error=scbptr->sretstat; ! 243: errstatus=wd7000fasst_make_error_code(host_error,scsi_error); ! 244: DEB(if (errstatus) printk("Target was %x\n",scbptr->idlun>>5);) ! 245: DEB(if (errstatus) printk("wd7000fasst_intr_handle: returning %6x\n", errstatus)); ! 246: DEB(printk("wd7000fasst_intr_handle: Status of the finished command: %x\n",mbstruct.imbox[flag&0x3f].status)); ! 247: /* I make a SCSI reset */ ! 248: /* Left out */ ! 249: my_done(wd7000fasst_hostno,errstatus); ! 250: wd7000fasst_intr_reset(); ! 251: return; ! 252: } ! 253: ! 254: volatile static int internal_done_flag = 0; ! 255: volatile static int internal_done_errcode = 0; ! 256: ! 257: /* The following code queues a SCSI command */ ! 258: int wd7000fasst_queuecommand(unchar target, const void *cmnd, void *buff, int bufflen, ! 259: void (*done)(int, int)) ! 260: { ! 261: int i; ! 262: #ifdef DEBUG ! 263: int j; ! 264: #endif ! 265: unchar *cmd = (unchar *) cmnd; ! 266: /* We first look for a free outgoing mailbox */ ! 267: for (i=0;i<OGMB_CNT;i++){ ! 268: if (mbstruct.ombox[i].status==0){ ! 269: /* We found one, now set up the scb */ ! 270: DEB(printk("Found outgoing mbox %x\n",i)); ! 271: memset(&scbs[i], 0, sizeof(struct scb)); ! 272: /* scbs[i].cdblen = (*cmd<=0x1f)?6:10; */ /* SCSI Command Descriptor Block Length */ ! 273: memcpy(scbs[i].scbdata, cmd, (*cmd<=0x1f)?6:10); ! 274: scbs[i].op = 0; /* SCSI Initiator Command */ ! 275: scbs[i].idlun = (target<<5)&0xe0; /* SCSI Target Id Bit 7-5 Target Id*/ ! 276: any2scsi(scbs[i].dataptr,buff); ! 277: any2scsi(scbs[i].maxdata,bufflen); ! 278: scbs[i].direc=0x40; /* Disable direction check */ ! 279: DEB(printk("Kommando fuer target %x ist: ",target); ! 280: for (j=0;j<12;j++) printk(" %x",scbs[i].scbdata[j]); ! 281: printk("\n")); ! 282: /* Now we set up the pointer to scb, then the status of the mbox */ ! 283: any2scsi((mbstruct.ombox[i].scbptr),&(scbs[i])); ! 284: mbstruct.ombox[i].status=1; ! 285: /* Everything set up, start the command */ ! 286: break; ! 287: } ! 288: } ! 289: if (i==OGMB_CNT){ ! 290: /* No free mbox, send command "Interrupt on free OGMB" */ ! 291: DEB(printk("No free Mailbox\n")); ! 292: return 0; ! 293: } ! 294: { int len,k; ! 295: struct scb * scbptr; ! 296: DEB(printk("Found outgoing mbox %x\n",i)); ! 297: scbptr=&(scbs[i]); ! 298: len=sizeof(struct scb); ! 299: k=0; ! 300: DEB(printk("SCB before execute:\n"); ! 301: while (len-- >0){ ! 302: printk("%x ",*((unchar *)scbptr)); ! 303: ((unchar *)scbptr)++; ! 304: if (++k==16){ printk("\n"); k=0; } ! 305: };) ! 306: } ! 307: /* Set up the "done" response function */ ! 308: if (done) { ! 309: DEB(printk("wd7000fasst_queuecommand: now waiting for interrupt "); ! 310: wd7000fasst_stat()); ! 311: if (wd7000fasst_do_done) ! 312: printk("wd7000fasst_queuecommand: Two concurrent queuecommand?\n"); ! 313: else ! 314: wd7000fasst_do_done = done; ! 315: DEB(wd7000fasst_stat()); ! 316: wd7000fasst_enable_intr(); ! 317: } ! 318: else{ ! 319: printk("wd7000fasst_queuecommand: done can't be NULL\n"); ! 320: return 0; ! 321: } ! 322: /* Now we initialize execution */ ! 323: retry: WAIT(ASC_STAT,STATMASK,CMD_RDY,0); ! 324: outb(0x80+i,COMMAND); ! 325: WAIT(ASC_STAT,STATMASK,CMD_RDY,0); ! 326: if (inb(ASC_STAT)&CMD_REJ) goto retry; ! 327: return 1; ! 328: /* Wait until done */ ! 329: ! 330: fail: ! 331: return 0; ! 332: } ! 333: ! 334: /* We use this function for queueing a command from wd7000fasst_command */ ! 335: static void internal_done(int host, int errcode) ! 336: { ! 337: internal_done_errcode = errcode; ! 338: ++internal_done_flag; ! 339: } ! 340: ! 341: int wd7000fasst_command(unchar target, const void *cmnd, void *buff, int bufflen) ! 342: { ! 343: #ifdef DEBUG ! 344: int k; ! 345: #endif ! 346: wd7000fasst_queuecommand(target, cmnd, buff, bufflen, internal_done); ! 347: ! 348: while (!internal_done_flag); ! 349: internal_done_flag = 0; ! 350: DEB(printk("wd7000fasst_command finished: ..leaving with errcode %x\n", ! 351: internal_done_errcode)); ! 352: DEB(for (k=0;k<5000000;k++) inb(INTR_STAT)); ! 353: return internal_done_errcode; ! 354: } ! 355: ! 356: /* a hack to avoid a strange compilation error */ ! 357: ! 358: void wd7000fasst_call_buh() ! 359: { ! 360: set_intr_gate((intr_chan<=7)?intr_chan+8:intr_chan+0x20,&wd7000fasst_interrupt); ! 361: } ! 362: ! 363: /* return non-zero on detection */ ! 364: static const char *wd_bases[] = {(char *)0xce000}; ! 365: typedef struct {char * signature; ! 366: unsigned offset; ! 367: unsigned length; ! 368: }Signature; ! 369: ! 370: static const Signature signatures[] = ! 371: {{"SSTBIOS",0xd,0x7}}; ! 372: ! 373: #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature)) ! 374: ! 375: int wd7000fasst_detect(int hostnum) /* hostnum ignored for now */ ! 376: { ! 377: int i,j; ! 378: char const * base_address = 0; ! 379: /* Store our host number */ ! 380: wd7000fasst_hostno=hostnum; ! 381: DEB(printk("wd7000fasst_detect: \n")); ! 382: ! 383: for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){ ! 384: for(j=0;j<NUM_SIGNATURES;j++){ ! 385: if(!memcmp((void *)(wd_bases[i] + signatures[j].offset), ! 386: (void *) signatures[j].signature,signatures[j].length)){ ! 387: base_address=wd_bases[i]; ! 388: printk("WD 7000-FASST detected\n"); ! 389: } ! 390: } ! 391: } ! 392: if (!base_address) return 0; ! 393: wd7000fasst_init(); ! 394: ! 395: /* Set the Bus on/off-times as not to ruin floppy performens */ ! 396: ! 397: wd7000fasst_stat(); ! 398: ! 399: printk(" *** READ CAPACITY ***\n"); ! 400: ! 401: { unchar rstat; ! 402: unchar buf[8]; ! 403: static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0}; ! 404: int i; ! 405: ! 406: for (i = 0; i < sizeof(buf); ++i) buf[i] = 0x87; ! 407: for (i = 0; i < 3; ++i){ ! 408: rstat=0; ! 409: while (rstat<2){ ! 410: if (wd7000fasst_command(i, cmd, buf, sizeof(buf))) rstat++; ! 411: else break; ! 412: } ! 413: if (rstat<2) ! 414: printk("wd7000fasst_detect: LU %d sector_size 0x%x device_size 0x%x capacity %d\n", ! 415: i, xscsi2int(buf+4), xscsi2int(buf), xscsi2int(buf+4)*xscsi2int(buf)); ! 416: } ! 417: } ! 418: ! 419: return 1; ! 420: } ! 421: ! 422: int wd7000fasst_abort(int i) ! 423: { ! 424: printk("wd7000fasst_abort\n"); ! 425: return 0; ! 426: } ! 427: ! 428: int wd7000fasst_reset(void) ! 429: { ! 430: printk("wd7000fasst_reset called\n"); ! 431: return 0; ! 432: } ! 433: ! 434: __asm__(" ! 435: _wd7000fasst_interrupt: ! 436: cld ! 437: pushl %eax ! 438: pushl %ecx ! 439: pushl %edx ! 440: push %ds ! 441: push %es ! 442: push %fs ! 443: movl $0x10,%eax ! 444: mov %ax,%ds ! 445: mov %ax,%es ! 446: movl $0x17,%eax ! 447: mov %ax,%fs ! 448: # Please, someone, change this to use the timer ! 449: # andl $0xfffeffff,_timer_active ! 450: movl $_wd7000fasst_intr_handle,%edx ! 451: call *%edx # ``interesting'' way of handling intr. ! 452: # Free the interrupt only after resetting the host interrupt ! 453: movb $0x20,%al ! 454: outb %al,$0xA0 # EOI to interrupt controller #1 ! 455: jmp 1f # give port chance to breathe ! 456: 1: jmp 1f ! 457: 1: outb %al,$0x20 ! 458: pop %fs ! 459: pop %es ! 460: pop %ds ! 461: popl %edx ! 462: popl %ecx ! 463: popl %eax ! 464: iret ! 465: ");
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.