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

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