linux/drivers/scsi/qlogicfas408.c
<<
>>
Prefs
   1/*----------------------------------------------------------------*/
   2/*
   3   Qlogic linux driver - work in progress. No Warranty express or implied.
   4   Use at your own risk.  Support Tort Reform so you won't have to read all
   5   these silly disclaimers.
   6
   7   Copyright 1994, Tom Zerucha.   
   8   tz@execpc.com
   9   
  10   Additional Code, and much appreciated help by
  11   Michael A. Griffith
  12   grif@cs.ucr.edu
  13
  14   Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
  15   help respectively, and for suffering through my foolishness during the
  16   debugging process.
  17
  18   Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
  19   (you can reference it, but it is incomplete and inaccurate in places)
  20
  21   Version 0.46 1/30/97 - kernel 1.2.0+
  22
  23   Functions as standalone, loadable, and PCMCIA driver, the latter from
  24   Dave Hinds' PCMCIA package.
  25   
  26   Cleaned up 26/10/2002 by Alan Cox <alan@lxorguk.ukuu.org.uk> as part of the 2.5
  27   SCSI driver cleanup and audit. This driver still needs work on the
  28   following
  29        -       Non terminating hardware waits
  30        -       Some layering violations with its pcmcia stub
  31
  32   Redistributable under terms of the GNU General Public License
  33
  34   For the avoidance of doubt the "preferred form" of this code is one which
  35   is in an open non patent encumbered format. Where cryptographic key signing
  36   forms part of the process of creating an executable the information
  37   including keys needed to generate an equivalently functional executable
  38   are deemed to be part of the source code.
  39
  40*/
  41
  42#include <linux/module.h>
  43#include <linux/blkdev.h>               /* to get disk capacity */
  44#include <linux/kernel.h>
  45#include <linux/string.h>
  46#include <linux/init.h>
  47#include <linux/interrupt.h>
  48#include <linux/ioport.h>
  49#include <linux/proc_fs.h>
  50#include <linux/unistd.h>
  51#include <linux/spinlock.h>
  52#include <linux/stat.h>
  53
  54#include <asm/io.h>
  55#include <asm/irq.h>
  56#include <asm/dma.h>
  57
  58#include "scsi.h"
  59#include <scsi/scsi_host.h>
  60#include "qlogicfas408.h"
  61
  62/*----------------------------------------------------------------*/
  63static int qlcfg5 = (XTALFREQ << 5);    /* 15625/512 */
  64static int qlcfg6 = SYNCXFRPD;
  65static int qlcfg7 = SYNCOFFST;
  66static int qlcfg8 = (SLOWCABLE << 7) | (QL_ENABLE_PARITY << 4);
  67static int qlcfg9 = ((XTALFREQ + 4) / 5);
  68static int qlcfgc = (FASTCLK << 3) | (FASTSCSI << 4);
  69
  70/*----------------------------------------------------------------*/
  71
  72/*----------------------------------------------------------------*/
  73/* local functions */
  74/*----------------------------------------------------------------*/
  75
  76/* error recovery - reset everything */
  77
  78static void ql_zap(struct qlogicfas408_priv *priv)
  79{
  80        int x;
  81        int qbase = priv->qbase;
  82        int int_type = priv->int_type;
  83
  84        x = inb(qbase + 0xd);
  85        REG0;
  86        outb(3, qbase + 3);     /* reset SCSI */
  87        outb(2, qbase + 3);     /* reset chip */
  88        if (x & 0x80)
  89                REG1;
  90}
  91
  92/*
  93 *      Do a pseudo-dma tranfer
  94 */
  95 
  96static int ql_pdma(struct qlogicfas408_priv *priv, int phase, char *request, int reqlen)
  97{
  98        int j;
  99        int qbase = priv->qbase;
 100        j = 0;
 101        if (phase & 1) {        /* in */
 102#if QL_TURBO_PDMA
 103                rtrc(4)
 104                /* empty fifo in large chunks */
 105                if (reqlen >= 128 && (inb(qbase + 8) & 2)) {    /* full */
 106                        insl(qbase + 4, request, 32);
 107                        reqlen -= 128;
 108                        request += 128;
 109                }
 110                while (reqlen >= 84 && !(j & 0xc0))     /* 2/3 */
 111                        if ((j = inb(qbase + 8)) & 4) 
 112                        {
 113                                insl(qbase + 4, request, 21);
 114                                reqlen -= 84;
 115                                request += 84;
 116                        }
 117                if (reqlen >= 44 && (inb(qbase + 8) & 8)) {     /* 1/3 */
 118                        insl(qbase + 4, request, 11);
 119                        reqlen -= 44;
 120                        request += 44;
 121                }
 122#endif
 123                /* until both empty and int (or until reclen is 0) */
 124                rtrc(7)
 125                j = 0;
 126                while (reqlen && !((j & 0x10) && (j & 0xc0))) 
 127                {
 128                        /* while bytes to receive and not empty */
 129                        j &= 0xc0;
 130                        while (reqlen && !((j = inb(qbase + 8)) & 0x10)) 
 131                        {
 132                                *request++ = inb(qbase + 4);
 133                                reqlen--;
 134                        }
 135                        if (j & 0x10)
 136                                j = inb(qbase + 8);
 137
 138                }
 139        } else {                /* out */
 140#if QL_TURBO_PDMA
 141                rtrc(4)
 142                    if (reqlen >= 128 && inb(qbase + 8) & 0x10) {       /* empty */
 143                        outsl(qbase + 4, request, 32);
 144                        reqlen -= 128;
 145                        request += 128;
 146                }
 147                while (reqlen >= 84 && !(j & 0xc0))     /* 1/3 */
 148                        if (!((j = inb(qbase + 8)) & 8)) {
 149                                outsl(qbase + 4, request, 21);
 150                                reqlen -= 84;
 151                                request += 84;
 152                        }
 153                if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {    /* 2/3 */
 154                        outsl(qbase + 4, request, 10);
 155                        reqlen -= 40;
 156                        request += 40;
 157                }
 158#endif
 159                /* until full and int (or until reclen is 0) */
 160                rtrc(7)
 161                    j = 0;
 162                while (reqlen && !((j & 2) && (j & 0xc0))) {
 163                        /* while bytes to send and not full */
 164                        while (reqlen && !((j = inb(qbase + 8)) & 2)) 
 165                        {
 166                                outb(*request++, qbase + 4);
 167                                reqlen--;
 168                        }
 169                        if (j & 2)
 170                                j = inb(qbase + 8);
 171                }
 172        }
 173        /* maybe return reqlen */
 174        return inb(qbase + 8) & 0xc0;
 175}
 176
 177/*
 178 *      Wait for interrupt flag (polled - not real hardware interrupt) 
 179 */
 180
 181static int ql_wai(struct qlogicfas408_priv *priv)
 182{
 183        int k;
 184        int qbase = priv->qbase;
 185        unsigned long i;
 186
 187        k = 0;
 188        i = jiffies + WATCHDOG;
 189        while (time_before(jiffies, i) && !priv->qabort &&
 190                                        !((k = inb(qbase + 4)) & 0xe0)) {
 191                barrier();
 192                cpu_relax();
 193        }
 194        if (time_after_eq(jiffies, i))
 195                return (DID_TIME_OUT);
 196        if (priv->qabort)
 197                return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
 198        if (k & 0x60)
 199                ql_zap(priv);
 200        if (k & 0x20)
 201                return (DID_PARITY);
 202        if (k & 0x40)
 203                return (DID_ERROR);
 204        return 0;
 205}
 206
 207/*
 208 *      Initiate scsi command - queueing handler 
 209 *      caller must hold host lock
 210 */
 211
 212static void ql_icmd(struct scsi_cmnd *cmd)
 213{
 214        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 215        int     qbase = priv->qbase;
 216        int     int_type = priv->int_type;
 217        unsigned int i;
 218
 219        priv->qabort = 0;
 220
 221        REG0;
 222        /* clearing of interrupts and the fifo is needed */
 223
 224        inb(qbase + 5);         /* clear interrupts */
 225        if (inb(qbase + 5))     /* if still interrupting */
 226                outb(2, qbase + 3);     /* reset chip */
 227        else if (inb(qbase + 7) & 0x1f)
 228                outb(1, qbase + 3);     /* clear fifo */
 229        while (inb(qbase + 5)); /* clear ints */
 230        REG1;
 231        outb(1, qbase + 8);     /* set for PIO pseudo DMA */
 232        outb(0, qbase + 0xb);   /* disable ints */
 233        inb(qbase + 8);         /* clear int bits */
 234        REG0;
 235        outb(0x40, qbase + 0xb);        /* enable features */
 236
 237        /* configurables */
 238        outb(qlcfgc, qbase + 0xc);
 239        /* config: no reset interrupt, (initiator) bus id */
 240        outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
 241        outb(qlcfg7, qbase + 7);
 242        outb(qlcfg6, qbase + 6);
 243         /**/ outb(qlcfg5, qbase + 5);  /* select timer */
 244        outb(qlcfg9 & 7, qbase + 9);    /* prescaler */
 245/*      outb(0x99, qbase + 5);  */
 246        outb(scmd_id(cmd), qbase + 4);
 247
 248        for (i = 0; i < cmd->cmd_len; i++)
 249                outb(cmd->cmnd[i], qbase + 2);
 250
 251        priv->qlcmd = cmd;
 252        outb(0x41, qbase + 3);  /* select and send command */
 253}
 254
 255/*
 256 *      Process scsi command - usually after interrupt 
 257 */
 258
 259static unsigned int ql_pcmd(struct scsi_cmnd *cmd)
 260{
 261        unsigned int i, j;
 262        unsigned long k;
 263        unsigned int result;    /* ultimate return result */
 264        unsigned int status;    /* scsi returned status */
 265        unsigned int message;   /* scsi returned message */
 266        unsigned int phase;     /* recorded scsi phase */
 267        unsigned int reqlen;    /* total length of transfer */
 268        char *buf;
 269        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 270        int qbase = priv->qbase;
 271        int int_type = priv->int_type;
 272
 273        rtrc(1)
 274        j = inb(qbase + 6);
 275        i = inb(qbase + 5);
 276        if (i == 0x20) {
 277                return (DID_NO_CONNECT << 16);
 278        }
 279        i |= inb(qbase + 5);    /* the 0x10 bit can be set after the 0x08 */
 280        if (i != 0x18) {
 281                printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
 282                ql_zap(priv);
 283                return (DID_BAD_INTR << 16);
 284        }
 285        j &= 7;                 /* j = inb( qbase + 7 ) >> 5; */
 286
 287        /* correct status is supposed to be step 4 */
 288        /* it sometimes returns step 3 but with 0 bytes left to send */
 289        /* We can try stuffing the FIFO with the max each time, but we will get a
 290           sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
 291
 292        if (j != 3 && j != 4) {
 293                printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
 294                     j, i, inb(qbase + 7) & 0x1f);
 295                ql_zap(priv);
 296                return (DID_ERROR << 16);
 297        }
 298        result = DID_OK;
 299        if (inb(qbase + 7) & 0x1f)      /* if some bytes in fifo */
 300                outb(1, qbase + 3);     /* clear fifo */
 301        /* note that request_bufflen is the total xfer size when sg is used */
 302        reqlen = scsi_bufflen(cmd);
 303        /* note that it won't work if transfers > 16M are requested */
 304        if (reqlen && !((phase = inb(qbase + 4)) & 6)) {        /* data phase */
 305                struct scatterlist *sg;
 306                rtrc(2)
 307                outb(reqlen, qbase);    /* low-mid xfer cnt */
 308                outb(reqlen >> 8, qbase + 1);   /* low-mid xfer cnt */
 309                outb(reqlen >> 16, qbase + 0xe);        /* high xfer cnt */
 310                outb(0x90, qbase + 3);  /* command do xfer */
 311                /* PIO pseudo DMA to buffer or sglist */
 312                REG1;
 313
 314                scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
 315                        if (priv->qabort) {
 316                                REG0;
 317                                return ((priv->qabort == 1 ?
 318                                         DID_ABORT : DID_RESET) << 16);
 319                        }
 320                        buf = sg_virt(sg);
 321                        if (ql_pdma(priv, phase, buf, sg->length))
 322                                break;
 323                }
 324                REG0;
 325                rtrc(2)
 326                /*
 327                 *      Wait for irq (split into second state of irq handler
 328                 *      if this can take time) 
 329                 */
 330                if ((k = ql_wai(priv)))
 331                        return (k << 16);
 332                k = inb(qbase + 5);     /* should be 0x10, bus service */
 333        }
 334
 335        /*
 336         *      Enter Status (and Message In) Phase 
 337         */
 338         
 339        k = jiffies + WATCHDOG;
 340
 341        while (time_before(jiffies, k) && !priv->qabort &&
 342                                                !(inb(qbase + 4) & 6))
 343                cpu_relax();    /* wait for status phase */
 344
 345        if (time_after_eq(jiffies, k)) {
 346                ql_zap(priv);
 347                return (DID_TIME_OUT << 16);
 348        }
 349
 350        /* FIXME: timeout ?? */
 351        while (inb(qbase + 5))
 352                cpu_relax();    /* clear pending ints */
 353
 354        if (priv->qabort)
 355                return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
 356
 357        outb(0x11, qbase + 3);  /* get status and message */
 358        if ((k = ql_wai(priv)))
 359                return (k << 16);
 360        i = inb(qbase + 5);     /* get chip irq stat */
 361        j = inb(qbase + 7) & 0x1f;      /* and bytes rec'd */
 362        status = inb(qbase + 2);
 363        message = inb(qbase + 2);
 364
 365        /*
 366         *      Should get function complete int if Status and message, else 
 367         *      bus serv if only status 
 368         */
 369        if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
 370                printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
 371                result = DID_ERROR;
 372        }
 373        outb(0x12, qbase + 3);  /* done, disconnect */
 374        rtrc(1)
 375        if ((k = ql_wai(priv)))
 376                return (k << 16);
 377
 378        /*
 379         *      Should get bus service interrupt and disconnect interrupt 
 380         */
 381         
 382        i = inb(qbase + 5);     /* should be bus service */
 383        while (!priv->qabort && ((i & 0x20) != 0x20)) {
 384                barrier();
 385                cpu_relax();
 386                i |= inb(qbase + 5);
 387        }
 388        rtrc(0)
 389
 390        if (priv->qabort)
 391                return ((priv->qabort == 1 ? DID_ABORT : DID_RESET) << 16);
 392                
 393        return (result << 16) | (message << 8) | (status & STATUS_MASK);
 394}
 395
 396/*
 397 *      Interrupt handler 
 398 */
 399
 400static void ql_ihandl(void *dev_id)
 401{
 402        struct scsi_cmnd *icmd;
 403        struct Scsi_Host *host = dev_id;
 404        struct qlogicfas408_priv *priv = get_priv_by_host(host);
 405        int qbase = priv->qbase;
 406        REG0;
 407
 408        if (!(inb(qbase + 4) & 0x80))   /* false alarm? */
 409                return;
 410
 411        if (priv->qlcmd == NULL) {      /* no command to process? */
 412                int i;
 413                i = 16;
 414                while (i-- && inb(qbase + 5));  /* maybe also ql_zap() */
 415                return;
 416        }
 417        icmd = priv->qlcmd;
 418        icmd->result = ql_pcmd(icmd);
 419        priv->qlcmd = NULL;
 420        /*
 421         *      If result is CHECK CONDITION done calls qcommand to request 
 422         *      sense 
 423         */
 424        (icmd->scsi_done) (icmd);
 425}
 426
 427irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
 428{
 429        unsigned long flags;
 430        struct Scsi_Host *host = dev_id;
 431
 432        spin_lock_irqsave(host->host_lock, flags);
 433        ql_ihandl(dev_id);
 434        spin_unlock_irqrestore(host->host_lock, flags);
 435        return IRQ_HANDLED;
 436}
 437
 438/*
 439 *      Queued command
 440 */
 441
 442static int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd,
 443                              void (*done) (struct scsi_cmnd *))
 444{
 445        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 446        if (scmd_id(cmd) == priv->qinitid) {
 447                cmd->result = DID_BAD_TARGET << 16;
 448                done(cmd);
 449                return 0;
 450        }
 451
 452        cmd->scsi_done = done;
 453        /* wait for the last command's interrupt to finish */
 454        while (priv->qlcmd != NULL) {
 455                barrier();
 456                cpu_relax();
 457        }
 458        ql_icmd(cmd);
 459        return 0;
 460}
 461
 462DEF_SCSI_QCMD(qlogicfas408_queuecommand)
 463
 464/* 
 465 *      Return bios parameters 
 466 */
 467
 468int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
 469                           sector_t capacity, int ip[])
 470{
 471/* This should mimic the DOS Qlogic driver's behavior exactly */
 472        ip[0] = 0x40;
 473        ip[1] = 0x20;
 474        ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
 475        if (ip[2] > 1024) {
 476                ip[0] = 0xff;
 477                ip[1] = 0x3f;
 478                ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
 479#if 0
 480                if (ip[2] > 1023)
 481                        ip[2] = 1023;
 482#endif
 483        }
 484        return 0;
 485}
 486
 487/*
 488 *      Abort a command in progress
 489 */
 490 
 491int qlogicfas408_abort(struct scsi_cmnd *cmd)
 492{
 493        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 494        priv->qabort = 1;
 495        ql_zap(priv);
 496        return SUCCESS;
 497}
 498
 499/* 
 500 *      Reset SCSI bus
 501 *      FIXME: This function is invoked with cmd = NULL directly by
 502 *      the PCMCIA qlogic_stub code. This wants fixing
 503 */
 504
 505int qlogicfas408_bus_reset(struct scsi_cmnd *cmd)
 506{
 507        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 508        unsigned long flags;
 509
 510        priv->qabort = 2;
 511
 512        spin_lock_irqsave(cmd->device->host->host_lock, flags);
 513        ql_zap(priv);
 514        spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
 515
 516        return SUCCESS;
 517}
 518
 519/*
 520 *      Return info string
 521 */
 522
 523const char *qlogicfas408_info(struct Scsi_Host *host)
 524{
 525        struct qlogicfas408_priv *priv = get_priv_by_host(host);
 526        return priv->qinfo;
 527}
 528
 529/*
 530 *      Get type of chip
 531 */
 532
 533int qlogicfas408_get_chip_type(int qbase, int int_type)
 534{
 535        REG1;
 536        return inb(qbase + 0xe) & 0xf8;
 537}
 538
 539/*
 540 *      Perform initialization tasks
 541 */
 542
 543void qlogicfas408_setup(int qbase, int id, int int_type)
 544{
 545        outb(1, qbase + 8);     /* set for PIO pseudo DMA */
 546        REG0;
 547        outb(0x40 | qlcfg8 | id, qbase + 8);    /* (ini) bus id, disable scsi rst */
 548        outb(qlcfg5, qbase + 5);        /* select timer */
 549        outb(qlcfg9, qbase + 9);        /* prescaler */
 550
 551#if QL_RESET_AT_START
 552        outb(3, qbase + 3);
 553
 554        REG1;
 555        /* FIXME: timeout */
 556        while (inb(qbase + 0xf) & 4)
 557                cpu_relax();
 558
 559        REG0;
 560#endif
 561}
 562
 563/*
 564 *      Checks if this is a QLogic FAS 408
 565 */
 566
 567int qlogicfas408_detect(int qbase, int int_type)
 568{
 569        REG1;
 570        return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
 571               ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));           
 572}
 573
 574/*
 575 *      Disable interrupts
 576 */
 577
 578void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
 579{
 580        int qbase = priv->qbase;
 581        int int_type = priv->int_type;
 582
 583        REG1;
 584        outb(0, qbase + 0xb);   /* disable ints */
 585}
 586
 587/*
 588 *      Init and exit functions
 589 */
 590
 591static int __init qlogicfas408_init(void)
 592{
 593        return 0;
 594}
 595
 596static void __exit qlogicfas408_exit(void)
 597{
 598
 599}
 600
 601MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
 602MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
 603MODULE_LICENSE("GPL");
 604module_init(qlogicfas408_init);
 605module_exit(qlogicfas408_exit);
 606
 607EXPORT_SYMBOL(qlogicfas408_info);
 608EXPORT_SYMBOL(qlogicfas408_queuecommand);
 609EXPORT_SYMBOL(qlogicfas408_abort);
 610EXPORT_SYMBOL(qlogicfas408_bus_reset);
 611EXPORT_SYMBOL(qlogicfas408_biosparam);
 612EXPORT_SYMBOL(qlogicfas408_ihandl);
 613EXPORT_SYMBOL(qlogicfas408_get_chip_type);
 614EXPORT_SYMBOL(qlogicfas408_setup);
 615EXPORT_SYMBOL(qlogicfas408_detect);
 616EXPORT_SYMBOL(qlogicfas408_disable_ints);
 617
 618
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.