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,
  97                   int reqlen)
  98{
  99        int j;
 100        int qbase = priv->qbase;
 101        j = 0;
 102        if (phase & 1) {        /* in */
 103#if QL_TURBO_PDMA
 104                rtrc(4)
 105                /* empty fifo in large chunks */
 106                if (reqlen >= 128 && (inb(qbase + 8) & 2)) {    /* full */
 107                        insl(qbase + 4, request, 32);
 108                        reqlen -= 128;
 109                        request += 128;
 110                }
 111                while (reqlen >= 84 && !(j & 0xc0))     /* 2/3 */
 112                        if ((j = inb(qbase + 8)) & 4)
 113                        {
 114                                insl(qbase + 4, request, 21);
 115                                reqlen -= 84;
 116                                request += 84;
 117                        }
 118                if (reqlen >= 44 && (inb(qbase + 8) & 8)) {     /* 1/3 */
 119                        insl(qbase + 4, request, 11);
 120                        reqlen -= 44;
 121                        request += 44;
 122                }
 123#endif
 124                /* until both empty and int (or until reclen is 0) */
 125                rtrc(7)
 126                j = 0;
 127                while (reqlen && !((j & 0x10) && (j & 0xc0)))
 128                {
 129                        /* while bytes to receive and not empty */
 130                        j &= 0xc0;
 131                        while (reqlen && !((j = inb(qbase + 8)) & 0x10))
 132                        {
 133                                *request++ = inb(qbase + 4);
 134                                reqlen--;
 135                        }
 136                        if (j & 0x10)
 137                                j = inb(qbase + 8);
 138
 139                }
 140        } else {                /* out */
 141#if QL_TURBO_PDMA
 142                rtrc(4)
 143                if (reqlen >= 128 && inb(qbase + 8) & 0x10) {   /* empty */
 144                        outsl(qbase + 4, request, 32);
 145                        reqlen -= 128;
 146                        request += 128;
 147                }
 148                while (reqlen >= 84 && !(j & 0xc0))     /* 1/3 */
 149                        if (!((j = inb(qbase + 8)) & 8)) {
 150                                outsl(qbase + 4, request, 21);
 151                                reqlen -= 84;
 152                                request += 84;
 153                        }
 154                if (reqlen >= 40 && !(inb(qbase + 8) & 4)) {    /* 2/3 */
 155                        outsl(qbase + 4, request, 10);
 156                        reqlen -= 40;
 157                        request += 40;
 158                }
 159#endif
 160                /* until full and int (or until reclen is 0) */
 161                rtrc(7)
 162                    j = 0;
 163                while (reqlen && !((j & 2) && (j & 0xc0))) {
 164                        /* while bytes to send and not full */
 165                        while (reqlen && !((j = inb(qbase + 8)) & 2))
 166                        {
 167                                outb(*request++, qbase + 4);
 168                                reqlen--;
 169                        }
 170                        if (j & 2)
 171                                j = inb(qbase + 8);
 172                }
 173        }
 174        /* maybe return reqlen */
 175        return inb(qbase + 8) & 0xc0;
 176}
 177
 178/*
 179 *      Wait for interrupt flag (polled - not real hardware interrupt)
 180 */
 181
 182static int ql_wai(struct qlogicfas408_priv *priv)
 183{
 184        int k;
 185        int qbase = priv->qbase;
 186        unsigned long i;
 187
 188        k = 0;
 189        i = jiffies + WATCHDOG;
 190        while (time_before(jiffies, i) && !priv->qabort &&
 191                                        !((k = inb(qbase + 4)) & 0xe0)) {
 192                barrier();
 193                cpu_relax();
 194        }
 195        if (time_after_eq(jiffies, i))
 196                return (DID_TIME_OUT);
 197        if (priv->qabort)
 198                return (priv->qabort == 1 ? DID_ABORT : DID_RESET);
 199        if (k & 0x60)
 200                ql_zap(priv);
 201        if (k & 0x20)
 202                return (DID_PARITY);
 203        if (k & 0x40)
 204                return (DID_ERROR);
 205        return 0;
 206}
 207
 208/*
 209 *      Initiate scsi command - queueing handler
 210 *      caller must hold host lock
 211 */
 212
 213static void ql_icmd(struct scsi_cmnd *cmd)
 214{
 215        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 216        int     qbase = priv->qbase;
 217        int     int_type = priv->int_type;
 218        unsigned int i;
 219
 220        priv->qabort = 0;
 221
 222        REG0;
 223        /* clearing of interrupts and the fifo is needed */
 224
 225        inb(qbase + 5);         /* clear interrupts */
 226        if (inb(qbase + 5))     /* if still interrupting */
 227                outb(2, qbase + 3);     /* reset chip */
 228        else if (inb(qbase + 7) & 0x1f)
 229                outb(1, qbase + 3);     /* clear fifo */
 230        while (inb(qbase + 5)); /* clear ints */
 231        REG1;
 232        outb(1, qbase + 8);     /* set for PIO pseudo DMA */
 233        outb(0, qbase + 0xb);   /* disable ints */
 234        inb(qbase + 8);         /* clear int bits */
 235        REG0;
 236        outb(0x40, qbase + 0xb);        /* enable features */
 237
 238        /* configurables */
 239        outb(qlcfgc, qbase + 0xc);
 240        /* config: no reset interrupt, (initiator) bus id */
 241        outb(0x40 | qlcfg8 | priv->qinitid, qbase + 8);
 242        outb(qlcfg7, qbase + 7);
 243        outb(qlcfg6, qbase + 6);
 244        outb(qlcfg5, qbase + 5);        /* select timer */
 245        outb(qlcfg9 & 7, qbase + 9);    /* prescaler */
 246/*      outb(0x99, qbase + 5);  */
 247        outb(scmd_id(cmd), qbase + 4);
 248
 249        for (i = 0; i < cmd->cmd_len; i++)
 250                outb(cmd->cmnd[i], qbase + 2);
 251
 252        priv->qlcmd = cmd;
 253        outb(0x41, qbase + 3);  /* select and send command */
 254}
 255
 256/*
 257 *      Process scsi command - usually after interrupt
 258 */
 259
 260static void ql_pcmd(struct scsi_cmnd *cmd)
 261{
 262        unsigned int i, j;
 263        unsigned long k;
 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                set_host_byte(cmd, DID_NO_CONNECT);
 278                return;
 279        }
 280        i |= inb(qbase + 5);    /* the 0x10 bit can be set after the 0x08 */
 281        if (i != 0x18) {
 282                printk(KERN_ERR "Ql:Bad Interrupt status:%02x\n", i);
 283                ql_zap(priv);
 284                set_host_byte(cmd, DID_BAD_INTR);
 285                return;
 286        }
 287        j &= 7;                 /* j = inb( qbase + 7 ) >> 5; */
 288
 289        /* correct status is supposed to be step 4 */
 290        /* it sometimes returns step 3 but with 0 bytes left to send */
 291        /* We can try stuffing the FIFO with the max each time, but we will get a
 292           sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
 293
 294        if (j != 3 && j != 4) {
 295                printk(KERN_ERR "Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n",
 296                     j, i, inb(qbase + 7) & 0x1f);
 297                ql_zap(priv);
 298                set_host_byte(cmd, DID_ERROR);
 299                return;
 300        }
 301
 302        if (inb(qbase + 7) & 0x1f)      /* if some bytes in fifo */
 303                outb(1, qbase + 3);     /* clear fifo */
 304        /* note that request_bufflen is the total xfer size when sg is used */
 305        reqlen = scsi_bufflen(cmd);
 306        /* note that it won't work if transfers > 16M are requested */
 307        if (reqlen && !((phase = inb(qbase + 4)) & 6)) {        /* data phase */
 308                struct scatterlist *sg;
 309                rtrc(2)
 310                outb(reqlen, qbase);    /* low-mid xfer cnt */
 311                outb(reqlen >> 8, qbase + 1);   /* low-mid xfer cnt */
 312                outb(reqlen >> 16, qbase + 0xe);        /* high xfer cnt */
 313                outb(0x90, qbase + 3);  /* command do xfer */
 314                /* PIO pseudo DMA to buffer or sglist */
 315                REG1;
 316
 317                scsi_for_each_sg(cmd, sg, scsi_sg_count(cmd), i) {
 318                        if (priv->qabort) {
 319                                REG0;
 320                                set_host_byte(cmd,
 321                                              priv->qabort == 1 ?
 322                                              DID_ABORT : DID_RESET);
 323                        }
 324                        buf = sg_virt(sg);
 325                        if (ql_pdma(priv, phase, buf, sg->length))
 326                                break;
 327                }
 328                REG0;
 329                rtrc(2);
 330                /*
 331                 *      Wait for irq (split into second state of irq handler
 332                 *      if this can take time)
 333                 */
 334                if ((k = ql_wai(priv))) {
 335                        set_host_byte(cmd, k);
 336                        return;
 337                }
 338                k = inb(qbase + 5);     /* should be 0x10, bus service */
 339        }
 340
 341        /*
 342         *      Enter Status (and Message In) Phase
 343         */
 344
 345        k = jiffies + WATCHDOG;
 346
 347        while (time_before(jiffies, k) && !priv->qabort &&
 348                                                !(inb(qbase + 4) & 6))
 349                cpu_relax();    /* wait for status phase */
 350
 351        if (time_after_eq(jiffies, k)) {
 352                ql_zap(priv);
 353                set_host_byte(cmd, DID_TIME_OUT);
 354                return;
 355        }
 356
 357        /* FIXME: timeout ?? */
 358        while (inb(qbase + 5))
 359                cpu_relax();    /* clear pending ints */
 360
 361        if (priv->qabort) {
 362                set_host_byte(cmd,
 363                              priv->qabort == 1 ? DID_ABORT : DID_RESET);
 364                return;
 365        }
 366
 367        outb(0x11, qbase + 3);  /* get status and message */
 368        if ((k = ql_wai(priv))) {
 369                set_host_byte(cmd, k);
 370                return;
 371        }
 372        i = inb(qbase + 5);     /* get chip irq stat */
 373        j = inb(qbase + 7) & 0x1f;      /* and bytes rec'd */
 374        status = inb(qbase + 2);
 375        message = inb(qbase + 2);
 376
 377        /*
 378         *      Should get function complete int if Status and message, else
 379         *      bus serv if only status
 380         */
 381        if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
 382                printk(KERN_ERR "Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
 383                set_host_byte(cmd, DID_ERROR);
 384        }
 385        outb(0x12, qbase + 3);  /* done, disconnect */
 386        rtrc(1);
 387        if ((k = ql_wai(priv))) {
 388                set_host_byte(cmd, k);
 389                return;
 390        }
 391
 392        /*
 393         *      Should get bus service interrupt and disconnect interrupt
 394         */
 395
 396        i = inb(qbase + 5);     /* should be bus service */
 397        while (!priv->qabort && ((i & 0x20) != 0x20)) {
 398                barrier();
 399                cpu_relax();
 400                i |= inb(qbase + 5);
 401        }
 402        rtrc(0);
 403
 404        if (priv->qabort) {
 405                set_host_byte(cmd,
 406                              priv->qabort == 1 ? DID_ABORT : DID_RESET);
 407                return;
 408        }
 409
 410        set_host_byte(cmd, DID_OK);
 411        if (message != COMMAND_COMPLETE)
 412                scsi_msg_to_host_byte(cmd, message);
 413        set_status_byte(cmd, status);
 414        return;
 415}
 416
 417/*
 418 *      Interrupt handler
 419 */
 420
 421static void ql_ihandl(void *dev_id)
 422{
 423        struct scsi_cmnd *icmd;
 424        struct Scsi_Host *host = dev_id;
 425        struct qlogicfas408_priv *priv = get_priv_by_host(host);
 426        int qbase = priv->qbase;
 427        REG0;
 428
 429        if (!(inb(qbase + 4) & 0x80))   /* false alarm? */
 430                return;
 431
 432        if (priv->qlcmd == NULL) {      /* no command to process? */
 433                int i;
 434                i = 16;
 435                while (i-- && inb(qbase + 5));  /* maybe also ql_zap() */
 436                return;
 437        }
 438        icmd = priv->qlcmd;
 439        ql_pcmd(icmd);
 440        priv->qlcmd = NULL;
 441        /*
 442         *      If result is CHECK CONDITION done calls qcommand to request
 443         *      sense
 444         */
 445        (icmd->scsi_done) (icmd);
 446}
 447
 448irqreturn_t qlogicfas408_ihandl(int irq, void *dev_id)
 449{
 450        unsigned long flags;
 451        struct Scsi_Host *host = dev_id;
 452
 453        spin_lock_irqsave(host->host_lock, flags);
 454        ql_ihandl(dev_id);
 455        spin_unlock_irqrestore(host->host_lock, flags);
 456        return IRQ_HANDLED;
 457}
 458
 459/*
 460 *      Queued command
 461 */
 462
 463static int qlogicfas408_queuecommand_lck(struct scsi_cmnd *cmd,
 464                              void (*done) (struct scsi_cmnd *))
 465{
 466        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 467
 468        set_host_byte(cmd, DID_OK);
 469        set_status_byte(cmd, SAM_STAT_GOOD);
 470        if (scmd_id(cmd) == priv->qinitid) {
 471                set_host_byte(cmd, DID_BAD_TARGET);
 472                done(cmd);
 473                return 0;
 474        }
 475
 476        cmd->scsi_done = done;
 477        /* wait for the last command's interrupt to finish */
 478        while (priv->qlcmd != NULL) {
 479                barrier();
 480                cpu_relax();
 481        }
 482        ql_icmd(cmd);
 483        return 0;
 484}
 485
 486DEF_SCSI_QCMD(qlogicfas408_queuecommand)
 487
 488/*
 489 *      Return bios parameters
 490 */
 491
 492int qlogicfas408_biosparam(struct scsi_device *disk, struct block_device *dev,
 493                           sector_t capacity, int ip[])
 494{
 495/* This should mimic the DOS Qlogic driver's behavior exactly */
 496        ip[0] = 0x40;
 497        ip[1] = 0x20;
 498        ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
 499        if (ip[2] > 1024) {
 500                ip[0] = 0xff;
 501                ip[1] = 0x3f;
 502                ip[2] = (unsigned long) capacity / (ip[0] * ip[1]);
 503#if 0
 504                if (ip[2] > 1023)
 505                        ip[2] = 1023;
 506#endif
 507        }
 508        return 0;
 509}
 510
 511/*
 512 *      Abort a command in progress
 513 */
 514
 515int qlogicfas408_abort(struct scsi_cmnd *cmd)
 516{
 517        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 518        priv->qabort = 1;
 519        ql_zap(priv);
 520        return SUCCESS;
 521}
 522
 523/*
 524 *      Reset SCSI bus
 525 *      FIXME: This function is invoked with cmd = NULL directly by
 526 *      the PCMCIA qlogic_stub code. This wants fixing
 527 */
 528
 529int qlogicfas408_host_reset(struct scsi_cmnd *cmd)
 530{
 531        struct qlogicfas408_priv *priv = get_priv_by_cmd(cmd);
 532        unsigned long flags;
 533
 534        priv->qabort = 2;
 535
 536        spin_lock_irqsave(cmd->device->host->host_lock, flags);
 537        ql_zap(priv);
 538        spin_unlock_irqrestore(cmd->device->host->host_lock, flags);
 539
 540        return SUCCESS;
 541}
 542
 543/*
 544 *      Return info string
 545 */
 546
 547const char *qlogicfas408_info(struct Scsi_Host *host)
 548{
 549        struct qlogicfas408_priv *priv = get_priv_by_host(host);
 550        return priv->qinfo;
 551}
 552
 553/*
 554 *      Get type of chip
 555 */
 556
 557int qlogicfas408_get_chip_type(int qbase, int int_type)
 558{
 559        REG1;
 560        return inb(qbase + 0xe) & 0xf8;
 561}
 562
 563/*
 564 *      Perform initialization tasks
 565 */
 566
 567void qlogicfas408_setup(int qbase, int id, int int_type)
 568{
 569        outb(1, qbase + 8);     /* set for PIO pseudo DMA */
 570        REG0;
 571        outb(0x40 | qlcfg8 | id, qbase + 8);    /* (ini) bus id, disable scsi rst */
 572        outb(qlcfg5, qbase + 5);        /* select timer */
 573        outb(qlcfg9, qbase + 9);        /* prescaler */
 574
 575#if QL_RESET_AT_START
 576        outb(3, qbase + 3);
 577
 578        REG1;
 579        /* FIXME: timeout */
 580        while (inb(qbase + 0xf) & 4)
 581                cpu_relax();
 582
 583        REG0;
 584#endif
 585}
 586
 587/*
 588 *      Checks if this is a QLogic FAS 408
 589 */
 590
 591int qlogicfas408_detect(int qbase, int int_type)
 592{
 593        REG1;
 594        return (((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7) &&
 595                ((inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7));
 596}
 597
 598/*
 599 *      Disable interrupts
 600 */
 601
 602void qlogicfas408_disable_ints(struct qlogicfas408_priv *priv)
 603{
 604        int qbase = priv->qbase;
 605        int int_type = priv->int_type;
 606
 607        REG1;
 608        outb(0, qbase + 0xb);   /* disable ints */
 609}
 610
 611/*
 612 *      Init and exit functions
 613 */
 614
 615static int __init qlogicfas408_init(void)
 616{
 617        return 0;
 618}
 619
 620static void __exit qlogicfas408_exit(void)
 621{
 622
 623}
 624
 625MODULE_AUTHOR("Tom Zerucha, Michael Griffith");
 626MODULE_DESCRIPTION("Driver for the Qlogic FAS SCSI controllers");
 627MODULE_LICENSE("GPL");
 628module_init(qlogicfas408_init);
 629module_exit(qlogicfas408_exit);
 630
 631EXPORT_SYMBOL(qlogicfas408_info);
 632EXPORT_SYMBOL(qlogicfas408_queuecommand);
 633EXPORT_SYMBOL(qlogicfas408_abort);
 634EXPORT_SYMBOL(qlogicfas408_host_reset);
 635EXPORT_SYMBOL(qlogicfas408_biosparam);
 636EXPORT_SYMBOL(qlogicfas408_ihandl);
 637EXPORT_SYMBOL(qlogicfas408_get_chip_type);
 638EXPORT_SYMBOL(qlogicfas408_setup);
 639EXPORT_SYMBOL(qlogicfas408_detect);
 640EXPORT_SYMBOL(qlogicfas408_disable_ints);
 641
 642