root/kernel/blk_drv/scsi/wd7000.c

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

DEFINITIONS

This source file includes following definitions.
  1. wd7000_enable_intr
  2. wd7000_enable_dma
  3. delay
  4. command_out
  5. alloc_scb
  6. free_scb
  7. init_scbs
  8. mail_out
  9. make_code
  10. wd7000_scsi_done
  11. wd7000_intr_handle
  12. wd7000_queuecommand
  13. wd7000_command
  14. wd7000_init
  15. wd7000_detect
  16. wd7000_append_info
  17. wd7000_info
  18. wd7000_abort
  19. wd7000_reset
  20. wd7000_biosparam

   1 /* $Id: $
   2  *  linux/kernel/wd7000.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  *  Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to
   9  *  accomodate Eric Youngdale's modifications to scsi.c.  Nov 1992.
  10  */
  11 
  12 #include <stdarg.h>
  13 #include <linux/kernel.h>
  14 #include <linux/head.h>
  15 #include <linux/types.h>
  16 #include <linux/string.h>
  17 #include <linux/sched.h>
  18 #include <asm/system.h>
  19 #include <asm/dma.h>
  20 #include <asm/io.h>
  21 #include "../blk.h"
  22 #include "scsi.h"
  23 #include "hosts.h"
  24 
  25 /* #define DEBUG  */
  26 
  27 #include "wd7000.h"
  28 
  29 
  30 #ifdef DEBUG
  31 #define DEB(x) x
  32 #else
  33 #define DEB(x)
  34 #endif
  35 
  36 
  37 /*
  38    Driver data structures:
  39    - mb and scbs are required for interfacing with the host adapter.
  40      An SCB has extra fields not visible to the adapter; mb's
  41      _cannot_ do this, since the adapter assumes they are contiguous in
  42      memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
  43      to access them.
  44    - An icb is for host-only (non-SCSI) commands.  ICBs are 16 bytes each;
  45      the additional bytes are used only by the driver.
  46    - For now, a pool of SCBs are kept in global storage by this driver,
  47      and are allocated and freed as needed.
  48 
  49   For two reasons, I decided not to use a single SCB per OGMB:
  50    - the 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
  51      not when it has finished.  Since the SCB must be around for completion,
  52      problems arise when SCBs correspond to OGMBs, which may be reallocated
  53      earlier (or delayed unnecessarily until a command completes).
  54    - Scatter/gather can be implemented by this driver using an SCB per
  55      scatter/gather buffer, but this is difficult when SCBs and Scsi_Cmnds
  56      are matched 1-1 via this correspondence.
  57 
  58   Instead, mailboxes are used as transient data structures, simply for
  59   carrying SCB addresses to/from the 7000-FASST2.  SCBs are allocated as
  60   "host_scribble" in an Scsi_Cmnd, and are maintained relative to that
  61   Scsi_Cmnd.
  62 
  63   Note also since SCBs are not "permanently" associated with mailboxes,
  64   there is no need to keep a global list of Scsi_Cmnd pointers indexed
  65   by OGMB.   Again, SCBs reference their Scsi_Cmnds directly, so mailbox
  66   indices need not be involved.
  67 */
  68 
  69 static struct {
  70        struct wd_mailbox ogmb[OGMB_CNT]; 
  71        struct wd_mailbox icmb[ICMB_CNT];
  72 } mb;
  73 static int next_ogmb = 0;   /* to reduce contention at mailboxes */
  74 
  75 static Scb scbs[MAX_SCBS];
  76 static Scb *scbfree = NULL;
  77 
  78 static int wd7000_host = 0;
  79 static unchar controlstat = 0;
  80 
  81 #define wd7000_intr_ack()  outb(0,INTR_ACK)
  82 
  83 static long WAITnexttimeout = 3000000;
  84 
  85 
  86 static inline void wd7000_enable_intr()
     /* [previous][next][first][last][top][bottom][index][help] */
  87 {
  88     controlstat |= INT_EN;
  89     outb(controlstat,CONTROL);
  90 }
  91 
  92 
  93 static inline void wd7000_enable_dma()
     /* [previous][next][first][last][top][bottom][index][help] */
  94 {
  95     controlstat |= DMA_EN;
  96     outb(controlstat,CONTROL);
  97     set_dma_mode(DMA_CH, DMA_MODE_CASCADE);
  98     enable_dma(DMA_CH);
  99 }
 100 
 101 
 102 #define WAIT(port, mask, allof, noneof)                                 \
 103  { register WAITbits;                                                   \
 104    register WAITtimeout = WAITnexttimeout;                              \
 105    while (1) {                                                          \
 106      WAITbits = inb(port) & (mask);                                     \
 107      if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
 108        break;                                                           \
 109      if (--WAITtimeout == 0) goto fail;                                 \
 110    }                                                                    \
 111  }
 112 
 113 
 114 static inline void delay( unsigned how_long )
     /* [previous][next][first][last][top][bottom][index][help] */
 115 {
 116      unsigned long time = jiffies + how_long;
 117 
 118      while (jiffies < time);
 119 }
 120 
 121 
 122 static inline int command_out(unchar *cmdp, int len)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     while (len--)  {
 125         WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
 126         outb(*cmdp++, COMMAND);
 127     }
 128     return 1;
 129 
 130 fail:
 131     printk("wd7000_out WAIT failed(%d): ", len+1);
 132     return 0;
 133 }
 134 
 135 static inline Scb *alloc_scb(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137     Scb *scb;
 138     unsigned long flags;
 139 
 140     save_flags(flags);
 141     cli();
 142 
 143     if (scbfree == NULL)  {
 144         panic("wd7000: can't allocate free SCB.\n");
 145         restore_flags(flags);
 146         return NULL;
 147     }
 148     scb = scbfree;  scbfree = scb->next;
 149     memset(scb, 0, sizeof(Scb));  scb->next = NULL;
 150 
 151     restore_flags(flags);
 152 
 153     return scb;
 154 }
 155 
 156 
 157 static inline void free_scb( Scb *scb )
     /* [previous][next][first][last][top][bottom][index][help] */
 158 {
 159     unsigned long flags;
 160 
 161     save_flags(flags);
 162     cli();
 163 
 164     memset(scb, 0, sizeof(Scb));
 165     scb->next = scbfree;  scbfree = scb;
 166 
 167     restore_flags(flags);
 168 }
 169 
 170 
 171 static inline void init_scbs(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 172 {
 173     int i;
 174     unsigned long flags;
 175 
 176     save_flags(flags);
 177     cli();
 178 
 179     scbfree = &(scbs[0]);
 180     for (i = 0;  i < MAX_SCBS-1;  i++)  scbs[i].next = &(scbs[i+1]);
 181     scbs[MAX_SCBS-1].next = NULL;
 182 
 183     restore_flags(flags);
 184 }    
 185     
 186 
 187 static int mail_out( Scb *scbptr )
     /* [previous][next][first][last][top][bottom][index][help] */
 188 /*
 189  *  Note: this can also be used for ICBs; just cast to the parm type.
 190  */
 191 {
 192     int i, ogmb;
 193     unsigned long flags;
 194 
 195     DEB(printk("wd7000_scb_out: %06x");)
 196 
 197     /* We first look for a free outgoing mailbox */
 198     save_flags(flags);
 199     cli();
 200     ogmb = next_ogmb;
 201     for (i = 0; i < OGMB_CNT; i++) {
 202         if (mb.ogmb[ogmb].status == 0)  {
 203             DEB(printk(" using OGMB %x",ogmb));
 204             mb.ogmb[ogmb].status = 1;
 205             any2scsi(mb.ogmb[ogmb].scbptr, scbptr);
 206 
 207             next_ogmb = (ogmb+1) % OGMB_CNT;
 208             break;
 209         }  else
 210             ogmb = (++ogmb) % OGMB_CNT;
 211     }
 212     restore_flags(flags);
 213 
 214     if (i >= OGMB_CNT) {
 215         DEB(printk(", no free OGMBs.\n");)
 216         /* Alternatively, issue "interrupt on free OGMB", and sleep... */
 217         return 0;
 218     }
 219 
 220     wd7000_enable_intr(); 
 221     do  {
 222         WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
 223         outb(START_OGMB|ogmb, COMMAND);
 224         WAIT(ASC_STAT,STATMASK,CMD_RDY,0);
 225     }  while (inb(ASC_STAT) & CMD_REJ);
 226 
 227     DEB(printk(", awaiting interrupt.\n");)
 228     return 1;
 229 
 230 fail:
 231     DEB(printk(", WAIT timed out.\n");)
 232     return 0;
 233 }
 234 
 235 
 236 int make_code(unsigned hosterr, unsigned scsierr)
     /* [previous][next][first][last][top][bottom][index][help] */
 237 {   
 238 #ifdef DEBUG
 239     int in_error = hosterr;
 240 #endif
 241 
 242     switch ((hosterr>>8)&0xff){
 243         case 0: /* Reserved */
 244                 hosterr = DID_ERROR;
 245                 break;
 246         case 1: /* Command Complete, no errors */
 247                 hosterr = DID_OK;
 248                 break;
 249         case 2: /* Command complete, error logged in scb status (scsierr) */ 
 250                 hosterr = DID_OK;
 251                 break;
 252         case 4: /* Command failed to complete - timeout */
 253                 hosterr = DID_TIME_OUT;
 254                 break;
 255         case 5: /* Command terminated; Bus reset by external device */
 256                 hosterr = DID_RESET;
 257                 break;
 258         case 6: /* Unexpected Command Received w/ host as target */
 259                 hosterr = DID_BAD_TARGET;
 260                 break;
 261         case 80: /* Unexpected Reselection */
 262         case 81: /* Unexpected Selection */
 263                 hosterr = DID_BAD_INTR;
 264                 break;
 265         case 82: /* Abort Command Message  */
 266                 hosterr = DID_ABORT;
 267                 break;
 268         case 83: /* SCSI Bus Software Reset */
 269         case 84: /* SCSI Bus Hardware Reset */
 270                 hosterr = DID_RESET;
 271                 break;
 272         default: /* Reserved */
 273                 hosterr = DID_ERROR;
 274                 break;
 275         }
 276 #ifdef DEBUG
 277     if (scsierr||hosterr)
 278         printk("\nSCSI command error: SCSI %02x host %04x return %d",
 279                scsierr,in_error,hosterr);
 280 #endif
 281     return scsierr | (hosterr << 16);
 282 }
 283 
 284 
 285 static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287     DEB(printk("wd7000_scsi_done: %06x\n",SCpnt);)
 288     SCpnt->SCp.phase = 0;
 289 }
 290 
 291 
 292 void wd7000_intr_handle(int irq)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294     int flag, icmb, errstatus, icmb_status;
 295     int host_error, scsi_error;
 296     Scb *scb, *scbn;      /* for SCSI commands */
 297     unchar *icb;          /* for host commands */
 298     Scsi_Cmnd *SCpnt;
 299 
 300     flag = inb(INTR_STAT);
 301     DEB(printk("wd7000_intr_handle: intr stat = %02x",flag);)
 302 
 303     if (!(inb(ASC_STAT)&0x80)){ 
 304         DEB(printk("\nwd7000_intr_handle: phantom interrupt...\n");)
 305         wd7000_intr_ack();
 306         return; 
 307     }
 308 
 309     /* check for an incoming mailbox */
 310     if ((flag & 0x40) == 0) {
 311         /*  for a free OGMB - need code for this case... */
 312         DEB(printk("wd7000_intr_handle: free outgoing mailbox\n");)
 313         wd7000_intr_ack();
 314         return;
 315     }
 316     /* The interrupt is for an incoming mailbox */
 317     icmb = flag & 0x3f;
 318     scb = (struct scb *) scsi2int(mb.icmb[icmb].scbptr);
 319     icmb_status = mb.icmb[icmb].status;
 320     mb.icmb[icmb].status = 0;
 321 
 322 #ifdef DEBUG
 323     printk(" ICMB %d posted for SCB/ICB %06x, status %02x",
 324            icmb, scb, icmb_status );
 325 #endif
 326 
 327     if (scb->op == 0)  {   /* an SCB is done */
 328         SCpnt = scb->SCpnt;
 329         if (--(SCpnt->SCp.phase) <= 0)  {  /* all scbs for SCpnt are done */
 330             host_error = scb->vue | (icmb_status << 8);
 331             scsi_error = scb->status;
 332             errstatus = make_code(host_error,scsi_error);    
 333             SCpnt->result = errstatus;
 334 
 335             scb = (Scb *) SCpnt->host_scribble;  scbn = scb;
 336             while (scb != NULL)  {
 337                 scbn = scb->next;
 338                 free_scb(scb);
 339                 scb = scbn;
 340             }
 341 
 342             SCpnt->scsi_done(SCpnt);
 343         }
 344     }  else  {    /* an ICB is done */
 345         icb = (unchar *) scb;
 346         icb[ICB_STATUS] = icmb_status;
 347         icb[ICB_PHASE] = 0;
 348     }
 349 
 350     wd7000_intr_ack();
 351     DEB(printk(".\n");)
 352     return;
 353 }
 354 
 355 
 356 int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358     Scb *scb;
 359     unchar *cdb;
 360     unchar idlun;
 361     short cdblen;
 362 
 363     cdb = (unchar *) SCpnt->cmnd;
 364     cdblen = (*cdb <= 0x1f ? 6 : 10);
 365     idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
 366     SCpnt->scsi_done = done;
 367 
 368     if (SCpnt->use_sg)  {   /* set up linked SCBs to do scatter/gather */
 369 #ifdef 0
 370         struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
 371         short i;
 372         Scb *scbn;
 373 
 374         /*
 375             Allocate the scbs first, and set the next pointers, since we
 376             need these later.
 377             Save the list via host_scribble.
 378         */
 379         scbn = NULL;
 380         for (i = 0;  i < SCpnt->use_sg;  i++)  {
 381             scb = alloc_scb();  scb->next = scbn;  scbn = scb;
 382         }
 383         SCpnt->host_scribble = (unchar *) scb;
 384         /*
 385             Note that the following is set up do to scatter/gather via
 386             linked commands, with an interrupt only after _all_ are
 387             finished.  This is mostly to simplify error handling, which
 388             hasn't been added to the interrupt handler for the other case
 389             (interrupt per SCB).   However, this should work with intr/SCB
 390             by setting phase = use_sg and setting the flag bit in each
 391             of the CDBs.
 392         */
 393         SCpnt->SCp.phase = 1;    /* set this to the # of interrupts expected */
 394 
 395         for (i = 0;  i < SCpnt->use_sg;  i++)  {
 396             scb->op = 0;
 397             scb->idlun = idlun;
 398             memcpy(scb->cdb, cdb, cdblen);
 399             /*
 400               Here, set CDB fields for block address & block count.
 401               I don't know block size; that's why this whole thing is
 402               commented out.  Would be nice if scatterlist had both
 403               byte and block counts per element (block address could be
 404               computed).
 405               Also need to know for sure if any SCSI commands other than
 406               READ/WRITE can use scatter/gather - I would hope not.
 407             */
 408             any2scsi(scb->dataptr, sg[i].address);
 409             any2scsi(scb->maxlen, sg[i].length);
 410             if (i < SCpnt->use_sg-1)  {        /* if this isn't the last */
 411                 any2scsi(scb->linkptr, scb->next);        /* set link */
 412                 scb->cdb[cdblen-1] |= 0x01;               /* set link bit */
 413             }
 414             scb->direc = 0x40;        /* Disable direction check */
 415             scb->SCpnt = SCpnt;       /* so we can find stuff later */
 416         }
 417 #else
 418         panic("wd7000_queuecommand: scatter/gather not implemented.\n");
 419 #endif
 420     }  else  {  /* just one command - use scb[0] */
 421         SCpnt->SCp.phase = 1;
 422         scb = alloc_scb();
 423         SCpnt->host_scribble = (unchar *) scb;
 424         scb->op = 0;
 425         scb->idlun = idlun;
 426         memcpy(scb->cdb, cdb, cdblen);
 427         any2scsi(scb->dataptr, SCpnt->request_buffer);
 428         any2scsi(scb->maxlen, SCpnt->request_bufflen);
 429         scb->direc = 0x40;              /* Disable direction check */
 430         scb->SCpnt = SCpnt;             /* so we can find stuff later */
 431     }
 432 
 433     return mail_out(scb);
 434 }
 435 
 436 
 437 int wd7000_command(Scsi_Cmnd *SCpnt)
     /* [previous][next][first][last][top][bottom][index][help] */
 438 {
 439     wd7000_queuecommand(SCpnt, wd7000_scsi_done);
 440 
 441     while (SCpnt->SCp.phase > 0);  /* phase counts scbs down to 0 */
 442 
 443     return SCpnt->result;
 444 }
 445 
 446 
 447 int wd7000_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 448 {   int i;
 449     unchar init_block[] = {
 450         INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, 0, 0, 0, OGMB_CNT, ICMB_CNT
 451     };
 452 
 453     /* Reset the adapter. */
 454     outb(SCSI_RES|ASC_RES, CONTROL);
 455     delay(1);  /* reset pulse: this is 10ms, only need 25us */
 456     outb(0,CONTROL);  controlstat = 0;
 457     /*
 458        Wait 2 seconds, then expect Command Port Ready.
 459 
 460        I suspect something else needs to be done here, but I don't know
 461        what.  The OEM doc says power-up diagnostics take 2 seconds, and
 462        indeed, SCSI commands submitted before then will time out, but
 463        none of what follows seems deterred by _not_ waiting 2 secs.
 464     */
 465     delay(200);
 466 
 467     WAIT(ASC_STAT, STATMASK, CMD_RDY, 0);
 468     DEB(printk("wd7000_init: Power-on Diagnostics finished\n");)
 469     if ((i=inb(INTR_STAT)) != 1) {
 470         panic("wd7000_init: Power-on Diagnostics error\n"); 
 471         return 0;
 472     }
 473     
 474     /* Clear mailboxes */
 475     memset(&mb,0,sizeof (mb));
 476     /* Set up SCB free list */
 477     init_scbs();
 478 
 479     /* Set up init block */
 480     any2scsi(init_block+5,&mb);
 481     /* Execute init command */
 482     if (!command_out(init_block,sizeof(init_block)))  {
 483         panic("WD-7000 Initialization failed.\n"); 
 484         return 0;
 485     }
 486     
 487     /* Wait until init finished */
 488     WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
 489     outb(DISABLE_UNS_INTR, COMMAND); 
 490     WAIT(ASC_STAT, STATMASK, CMD_RDY | ASC_INI, 0);
 491 
 492     /* Enable Interrupt and DMA */
 493     if (request_irq(IRQ_LVL, wd7000_intr_handle)) {
 494       panic("Unable to allocate IRQ for WD-7000.\n");
 495       return 0;
 496     };
 497     if(request_dma(DMA_CH)) {
 498       panic("Unable to allocate DMA channel for WD-7000.\n");
 499       free_irq(IRQ_LVL);
 500       return 0;
 501     };
 502     wd7000_enable_dma();
 503     wd7000_enable_intr();
 504 
 505     printk("WD-7000 initialized.\n");
 506     return 1;
 507   fail:
 508     return 0;                                   /* 0 = not ok */
 509 }
 510 
 511 
 512 static const char *wd_bases[] = {(char *)0xce000};
 513 typedef struct {
 514     char * signature;
 515     unsigned offset;
 516     unsigned length;
 517 } Signature;
 518 
 519 static const Signature signatures[] = {{"SSTBIOS",0xd,0x7}};
 520 
 521 #define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
 522 
 523 
 524 int wd7000_detect(int hostnum) /* hostnum ignored for now */
     /* [previous][next][first][last][top][bottom][index][help] */
 525 /* 
 526  *  return non-zero on detection
 527  */
 528 {
 529     int i,j;
 530     char const * base_address = 0;
 531     /* Store our host number */
 532     wd7000_host = hostnum;
 533 
 534     for(i=0;i<(sizeof(wd_bases)/sizeof(char *));i++){
 535         for(j=0;j<NUM_SIGNATURES;j++){
 536             if(!memcmp((void *)(wd_bases[i] + signatures[j].offset),
 537                 (void *) signatures[j].signature,signatures[j].length)){
 538                     base_address=wd_bases[i];
 539                     printk("WD-7000 detected.\n");
 540             }   
 541         }
 542     }
 543     if (!base_address) return 0;
 544     wd7000_init();    
 545 
 546     return 1;
 547 }
 548 
 549 
 550 
 551 static void wd7000_append_info( char *info, const char *fmt, ... )
     /* [previous][next][first][last][top][bottom][index][help] */
 552 /*
 553  *  This is just so I can use vsprintf...
 554  */
 555 {
 556     va_list args;
 557     extern int vsprintf(char *buf, const char *fmt, va_list args);
 558 
 559     va_start(args, fmt);
 560     vsprintf(info, fmt, args);
 561     va_end(args);
 562 
 563     return;
 564 }
 565 
 566 
 567 const char *wd7000_info(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 568 {
 569     static char info[80] = "Western Digital WD-7000, Firmware Revision ";
 570     volatile unchar icb[ICB_LEN] = {0x8c};  /* read firmware revision level */
 571     unchar rl1, rl2;
 572 
 573     icb[ICB_PHASE] = 1;
 574     mail_out( (struct scb *) icb );
 575     while (icb[ICB_PHASE]) /* wait for completion */;
 576     rl1 = icb[1];
 577     rl2 = icb[2];
 578 
 579     /* now format the above, and append to info */
 580     wd7000_append_info( info+strlen(info), "%d.%d.\n", rl1, rl2 );
 581 
 582     return info;
 583 }
 584 
 585 
 586 int wd7000_abort(Scsi_Cmnd * SCpnt, int i)
     /* [previous][next][first][last][top][bottom][index][help] */
 587 {
 588 #ifdef DEBUG
 589     printk("wd7000_abort: Scsi_Cmnd = 0x%08x, code = %d ", SCpnt, i);
 590     printk("id %d lun %d cdb", SCpnt->target, SCpnt->lun);
 591     {  int j;  unchar *cdbj = (unchar *) SCpnt->cmnd;
 592        for (j=0; j < (*cdbj <= 0x1f?6:10);  j++)  printk(" %02x", *(cdbj++));
 593        printk(" result %08x\n", SCpnt->result);
 594     }
 595 #endif
 596     return 0;
 597 }
 598 
 599 
 600 int wd7000_reset(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 601 {
 602     printk("wd7000_reset\n");
 603     return 0;
 604 }
 605 
 606 
 607 int wd7000_biosparam(int size, int dev, int* info)
     /* [previous][next][first][last][top][bottom][index][help] */
 608 /*
 609  *  This is borrowed directly from aha1542.c, but my disks are organized
 610  *   this way, so I think it will work OK.
 611  */
 612 {
 613   info[0] = 32;
 614   info[1] = 64;
 615   info[2] = (size + 2047) >> 11;
 616   if (info[2] >= 1024) info[2] = 1024;
 617   return 0;
 618 }
 619 

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