root/kernel/blk_drv/scsi/7000fasst.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. wd7000fasst_stat
  2. wd7000fasst_out
  3. wd7000fasst_make_error_code
  4. wd7000fasst_init
  5. wd7000fasst_info
  6. wd7000fasst_intr_handle
  7. wd7000fasst_queuecommand
  8. internal_done
  9. wd7000fasst_command
  10. wd7000fasst_call_buh
  11. wd7000fasst_detect
  12. wd7000fasst_abort
  13. wd7000fasst_reset

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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, 
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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()
     /* [previous][next][first][last][top][bottom][index][help] */
 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 */
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 423 {
 424     printk("wd7000fasst_abort\n");
 425     return 0;
 426 }
 427 
 428 int wd7000fasst_reset(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 ");

/* [previous][next][first][last][top][bottom][index][help] */