File:  [MW Coherent from dump] / coherent / b / STREAMS / io.386 / haiAha.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Wed May 29 04:56:36 2019 UTC (7 years, 2 months ago) by root
Branches: MarkWilliams, MAIN
CVS tags: relic, HEAD
coherent

/***********************************************************************
 *  Module: aha154x.c
 *
 *  Host Adapter Interface routines for the Adaptec AHA154XX series
 *  of host adapters.
 *
 *  Much of the information used to create this driver was obtained
 *  from the AHA1540B/1542B Technical Reference Manual available from
 *  Adaptec by phone or snail mail at:
 *
 *      Adaptec - Literature Department
 *      691 South Milpitas Blvd.
 *      Milpitas, CA 95035
 *      (408) 945-8600
 *
 *  Copyright (c), 1993 Christopher Sean Hilton, All Rights Reserved.
 *
 *  Last Modified: Fri Jun  4 20:03:17 1993 by [chris]
 */

#define _COH_HA_                /* Host Adaptor Module */

#include <stddef.h>
#include <sys/coherent.h>
#include <sys/con.h>
#include <sys/types.h>

#include <sys/haiscsi.h>

/***********************************************************************
 *  Constants - Patch for your own circumstances.
 */

unsigned short  AHABASE =       0x330,      /* Port Base */
                AHAINTR =       11,         /* Host Adapter Interrupt vector */
                AHADMACHAN =    0x05;       /* DMA Channel */

#define CTRLREG     (AHABASE + 0)   /* Control Register (Write) */
#define     HRST    bit(7)          /* Hard Reset */
#define     SRST    bit(6)          /* Soft Reset */
#define     IRST    bit(5)          /* Interrupt Reset */
#define     SCRST   bit(4)          /* SCSI Bus Reset */

#define STSREG      (AHABASE + 0)   /* Status Register (Read) */
#define     STST    bit(7)          /* Self Test in progress */
#define     DIAGF   bit(6)          /* Internal Diagnostic Failure */
#define     INIT    bit(5)          /* Mailbox Initialization Required */
#define     IDLE    bit(4)          /* SCSI Host Adapter Idle */
#define     CDF     bit(3)          /* Command/Data Out Port Full */
#define     DF      bit(2)          /* Data In Port Full */
#define     INVDCMD bit(0)          /* Invalid HA Command */

#define CMDDATAOUT  (AHABASE + 1)   /* Command/Data Out (Write) */
#define     NOP         0x00        /* No Operation (really?) */
#define     MBINIT      0x01        /* Mail Box Initialization */
#define     STARTSCSI   0x02        /* Start a SCSI Command */
#define     STARTBIOS   0x03        /* Start a BIOS Command */
#define     HAINQUIRY   0x04        /* HA Inquiry */
#define     ENBLMBOA    0x05        /* Enable Mailbox Out Available Interrupt */
#define     STSELTO     0x06        /* Set Selection Timeout */
#define     SETBUSON    0x07        /* Set Bus on time */
#define     SETBUSOFF   0x08        /* Set Bus off time */
#define     SETXFER     0x09        /* Set transfer speed */
#define     RETINSTDEV  0x0a        /* Return Installed Devices */
#define     RETCFGDATA  0x0b        /* Return Configuration Data */
#define     ENBLTRGTMD  0x0c        /* Enable Target Mode */
#define     RETSUDATA   0x0d        /* Return Setup Data */

#define DATAIN      (AHABASE + 1)

#define INTRFLGS    (AHABASE + 2)
#define     ANYINTR bit(7)          /* Any Interrupt */
#define     SCRD    bit(3)          /* SCSI Reset Detected */
#define     HACC    bit(2)          /* HA Command Complete */
#define     MBOA    bit(1)          /* MBO Empty */
#define     MBIF    bit(0)          /* MBI Full */

#define MBOFREE     0x00        /* Mailbox out is free */
#define MBOSTART    0x01        /* Start CCB in this Mailbox */
#define MBOABORT    0x02        /* Abort CCB in this Mailbox */

#define MBIFREE     0x00        /* Mailbox in is free */
#define MBISUCCESS  0x01        /* Mailbox's CCB Completed Successfully */
#define MBIABORTED  0x02        /* Mailbox's CCB Aborted */
#define MBIABRTFLD  0x03        /* CCB to Abort not found */
#define MBIERROR    0x04        /* CCB Completed with error */

#define TIMEOUT     -1          /* Timeout from pollxxx() functions. */

#define ST_HAINIT   0x0001      /* Host Adapter being initialized */
#define ST_HAIDLE   0x0002      /* Host Adapter is idle */

/***********************************************************************
 *  Types
 */

#pragma align 1

typedef union addr3_u {         /* addr3_u for big/little endian conversions */
    unsigned long   value;
    unsigned char   byteval[sizeof(unsigned long)];
} addr3_t;

typedef union mbo_u *mbo_p;     /* Out Box to host adapter */

typedef union mbo_u {
    unsigned char   cmd;
    paddr_t         ccbaddr;
} mbo_t;

typedef union mbi_u *mbi_p;     /* In Box from host adapter */

typedef union mbi_u {
    unsigned char   sts;
    paddr_t         ccbaddr;
} mbi_t;

typedef struct mb_s {           /* Host adapter mailbox type */
    mbo_t       o[MAXDEVS];     /* One out box for each device */
    mbi_t       i[MAXDEVS];     /* One in box for each possible reply */
} mb_t;

typedef struct haccb_s *haccb_p;    /* Host Adapter Command/Control Block */

typedef struct haccb_s {
    unsigned char   opcode;
    unsigned char   addrctrl;
    unsigned char   cdblen;
    unsigned char   senselen;
    unsigned char   datalen[3];
    unsigned char   bufaddr[3];
    unsigned char   linkaddr[3];
    unsigned char   linkid;
    unsigned char   hoststs;
    unsigned char   trgtsts;
    unsigned char   pad[2];
    cdb_t           cdb;
} haccb_t;

typedef struct dsentry_s *dsentry_p;

typedef struct dsentry_s {
    unsigned char   size[3];
    unsigned char   addr[3];
} dsentry_t;

typedef struct dslist_s *dslist_p;

typedef struct dslist_s {
    dsentry_t       entries[16];
} dslist_t;

#pragma align

/***********************************************************************
 *  Variables
 */

static int      hastate;                /* Host Adapter State */
static mb_t     mb;
static haccb_t  ccb[MAXDEVS];
static paddr_t  ccbbase;
static srb_p    actv[MAXDEVS];
static dslist_t ds[MAXDEVS];

void abortscsi();

/***********************************************************************
 *  Support Functions
 */

#define min(a, b)   (((a) <= (b)) ? (a) : (b))

/***********************************************************************
 *  pollclr()
 *
 *  Wait for bit(s) to clear in a port.
 */

static int pollclr(port, bits)
unsigned port;
unsigned bits;

{
    long countdown = 1000000L;
    unsigned pval;

    while (countdown-- > 0L) {
        pval = inb(port) & 0xff;
        if ((pval & bits) == 0)
            return (pval & ~bits);
    }
    printf("pollclr() port 0x%x stuck at 0x%x waiting for 0x%x\n", port, pval, bits);
    return TIMEOUT;
}   /* pollclr() */

/***********************************************************************
 *  pollset()
 *
 *  Wait for bits in port to be set.
 */

static int pollset(port, bits)
unsigned port;
unsigned bits;
{
    long countdown = 1000000L;
    unsigned pval;

    while (countdown-- > 0L) {
        pval = inb(port) & 0xff;
        if ((pval & bits) == bits)
            return pval;
    }
    printf("pollset() port 0x%x stuck at 0x%x waiting for 0x%x\n", port, pval, bits);
    return TIMEOUT;
}   /* pollset() */

/***********************************************************************
 *  hacc()
 *
 *  Host Adapter Command Completed - Returns 1 if last host adapter
 *  command completed without error.
 */

static int hacc()

{
    unsigned stsreg;

    if (pollset(INTRFLGS, HACC) == TIMEOUT) {
        printf("Timeout waiting for Host Adapter Command Complete\n");
        return 0;
    }
    stsreg = inb(STSREG) & (IDLE | INIT | CDF | INVDCMD);
    if (stsreg != IDLE) {
        printf("Host Adapter Stuck - STSREG: 0x%x\n", stsreg);
        return 0;
    }
    return 1;
}   /* hacc() */

/***********************************************************************
 *  haidle()
 *
 *  Returns 1 if the Idle Bit is on in the adapter status register
 */

#define haidle()    (pollset(STSREG, IDLE) != TIMEOUT)

/***********************************************************************
 *  gethabyte()
 *
 *  Get a byte from the host adapter Data In register.
 */

static int gethabyte()

{
    return (pollset(STSREG, DF) == TIMEOUT) ? TIMEOUT : (inb(DATAIN) & 0xff);
}   /* gethabyte() */

/***********************************************************************
 *  puthabyte()
 *
 *  Write a byte to the host adapter Command/Data Out Register.
 */

static int puthabyte(b)
unsigned char b;

{
    if (pollclr(STSREG, CDF) == TIMEOUT)
        return 0;
    else {
        outb(CMDDATAOUT, b);
        return 1;
    }
}   /* puthabyte() */

/***********************************************************************
 *  isaha154x()
 *
 *  Returns a string indicating the type and revision of the AHA154x
 *  host adapter on the bus if it is out there.  Caveat: if something
 *  else is out there that can respond to this message I don't know
 *  what will happen.
 */

static char *isaha154x(haport)
unsigned haport;

{
    /*                      0123456789012345678901234 */
    static char brdid[] =  "Adaptec AHA-154x# Rev: ##";
    char buf[4], *p, *idstr = NULL;
    int ch, oldhaport, s;

    oldhaport = AHABASE;
    s = sphi();
    AHABASE = haport;
    if (haidle() && puthabyte(HAINQUIRY)) {
        for (p = buf; p < buf + sizeof(buf); ++p) {
            ch = gethabyte();
            if (ch == TIMEOUT)
                break;
            else
                *p = ch;
        }
        if (p >= buf + sizeof(buf)) {
            outb(CTRLREG, IRST);
            idstr = (inb(STSREG) & IDLE != IDLE) ? NULL : brdid;
            brdid[23] = buf[2];
            brdid[24] = buf[3];
            switch (buf[0]) {   /* Board Type Byte */
                case 'B':   /* AHA-1640 with 64 Head BIOS */
                    brdid[13] = '6';
                case '\0':  /* AHA-1540 with 16 head BIOS */
                case '0':   /* AHA-1540 with 64 head BIOS */
                    brdid[16] = ' ';
                    break;
                case 'A':   /* AHA-1540B with 64 head BIOS */
                    brdid[16] = 'B';
                    break;
                default:
                    idstr = "AHA-154x Conformant Host Adapter";
                    break;
            }
        }
    }
    AHABASE = oldhaport;
    spl(s);
    return idstr;
}   /* isaha154x() */

/***********************************************************************
 *  hareset()
 *
 *  Reset the host adapter and leave it ready for operation.
 */

static char *hareset()

{
    addr3_t mbaddr;
    int mbinitok, stsreg, i, s;
    char *p;

    if (p = isaha154x(AHABASE)) {
        s = sphi();
        outb(CTRLREG, SRST);
        if ((stsreg = pollclr(STSREG, STST)) == TIMEOUT)
            panic("SCSI: Timeout waiting for Host Adapter Initialization\n");

        if ((stsreg & DIAGF) || (stsreg & (INIT | IDLE)) != (INIT | IDLE))
            panic("SCSI: Host Adapter Initialization Failed.\n");

        memset(&mb, 0, sizeof(mb));
        mbaddr. value = vtop(&mb);
        mbinitok = 1;
        mbinitok &= puthabyte(MBINIT);
        mbinitok &= puthabyte(sizeof(mb) / (sizeof(mbo_t) + sizeof(mbi_t)));
        mbinitok &= puthabyte(mbaddr. byteval[2]);
        mbinitok &= puthabyte(mbaddr. byteval[1]);
        mbinitok &= puthabyte(mbaddr. byteval[0]);
        if (mbinitok && hacc())
            for (i = 0; i < MAXDEVS; ++i) {
                actv[i] = NULL;
                mb. o[i]. ccbaddr = vtop(ccb + i);
                flip(mb. o[i]. ccbaddr);
                mb. o[i]. cmd = MBOFREE;
            }
        else
            panic("SCSI: AHA-154XB Mailbox Initialization Failed.\n");

        ccbbase = vtop(ccb);
        spl(s);
    }
    return p;
}   /* hareset() */

/***********************************************************************
 *  dmacascade()
 *
 *  Set the selected (AHADMACHAN) dma channel to cascade mode.
 */

static void dmacascade()

{
    int dmaporta, dmaportb, s;

    s = sphi();
    if (AHADMACHAN == 0) {
        dmaporta = 0x0b;
        dmaportb = 0x0a;
    } else {
        dmaporta = 0xd6;
        dmaportb = 0xd4;
    }
    outb(dmaporta, 0xc0 | (AHADMACHAN & 3));
    outb(dmaportb, (AHADMACHAN & 3));
    spl(s);
}   /* dmacascade() */

/***********************************************************************
 *  checkmail()
 *
 *  Check the incoming mailboxes for messages.  Do this on any Mail
 *  Box In Full interrupt and before you fail a command for timeout.
 *  The code to determine which target device finished by tid is a
 *  bit tricky and relies on the following assumptions:
 *
 *      1)  The host adapter is installed in a Intel (big-endian)
 *          machine. Believe it or not there is (are) non-Intel CPU
 *          ISA bus machines. and the one that I know of is a M68000
 *          machine where this would not work.
 *
 *      2)  The kernel's data space is physically contiguous and is
 *          never swapped out.
 */

static int checkmail()

{
    static int startid = 0;
    int msgs = 0;
    int sts;
    int s;
    register int id;
    register srb_p r;
    register int i = startid;

    do {
        if (mb. i[i]. sts != MBIFREE) {
            s = sphi();
            sts = mb. i[i]. sts;
            flip(mb. i[i]. ccbaddr);
            id = (unsigned) ((mb. i[i]. ccbaddr & 0x00ffffffL) - ccbbase) / sizeof(haccb_t);
            if (r = actv[id]) {
                switch (sts) {
                case MBISUCCESS:
                case MBIERROR:
                case MBIABRTFLD:
                    r->status = ccb[id]. trgtsts;
                    break;
                case MBIABORTED:
                    r->status = ST_DRVABRT;
                    break;
                default:
                    panic("Host Adapter Mailbox In value corrupted\n");
                }
                actv[id] = NULL;
                if (r->cleanup)
                    (*(r->cleanup))(r);
                msgs |= bit(id);
            }
            mb. i[i]. ccbaddr = 0;
            spl(s);
        }
        else if (msgs)
            break;
        i = (i + 1) & 7;
    } while (i != startid);
    startid = i;

    return msgs;
}   /* checkmail() */

/***********************************************************************
 *  Driver Interface Functions
 */

/***********************************************************************
 *  hatimer()
 *
 *  Host adapter Timeout handler.
 */

void hatimer()

{
    register int    id;
    register srb_p  r;
    register int    active;
    int             s;

    s = sphi();
    checkmail();        /* Cleanup any missed interrupts, etc. */
    active = 0;
    for (id = 0; id < MAXDEVS; ++id) {
        if ((r = actv[id]) != NULL && r->timeout != 0) {
            if (--r->timeout == 0) {
                abortscsi(r);
                r->status = ST_DRVABRT;
            }
            else
                active = 1;
        }
    }
    drvl[SCSIMAJOR]. d_time = active;
    spl(s);
}   /* hatimer() */

/***********************************************************************
 *  haintr()
 *
 *  SCSI interrupt handler for host adapter.
 */

void haintr()

{
    int intrflgs;

    intrflgs = inb(INTRFLGS);
    if (intrflgs & ANYINTR)
        outb(CTRLREG, IRST);

    if (hastate == ST_HAINIT)
        return;
    if (intrflgs & MBIF) {
        checkmail();
        return;
    }
}   /* haintr() */

/***********************************************************************
 *  hainit()
 *
 *  Initialize the host adapter for operation.
 */

int hainit()

{
    char *p;

    hastate = ST_HAINIT;
    printf("Host Adapter Module: AHA-154x v1.0.0 beta\n");
    setivec(AHAINTR, haintr);
    p = hareset();
    if (!p)
        panic("AHA154x not found or init failed at port: 0x%x\n", AHABASE);
    else
        printf("Initializing %s at port 0x%x, IRQ 0x%x, DMA 0x%x\n", p, AHABASE, AHAINTR, AHADMACHAN);

    dmacascade();
    hastate = ST_HAIDLE;
    return 1;
}   /* hainit() */

/***********************************************************************
 *  mkdslist()
 *
 *  Make an Adaptec Data Segment list for a Scatter/Gather Request.
 *  Input:      d - the memory area for the Scatter/Gather List.
 *              b - the bufaddr structure for the memory block.
 */

static int mkdslist(d, b)
register dsentry_p  d;
bufaddr_p           b;
{
    paddr_t                 p_start;
    size_t                  p_size;
    paddr_t                 p_next;
    int                     segments = 1;
    register unsigned long  start;
    register unsigned long  end;
    dsentry_p               l = d;

    switch (b->space) {
    case KRNL_ADDR:     /* Kernal Address */
    case USER_ADDR:     /* User Address (Anything goes) */
        start = b->addr. vaddr;
        p_start = vtop(start);
        break;
    case SYSGLBL_ADDR:  /* System Global address (yeah) */
        start = b->addr. paddr;
        p_start = P2P(start);
        break;
    case PHYS_ADDR:     /* Physical Address - (who knows) */
    default:
        return 0;
    }
    end = start + b->size;
    p_size  = min(NBPC - (p_start & (NBPC - 1)), end - start);

    for ( ; ; ) {
        d->size[2] = ((unsigned char *) &p_size)[0];
        d->size[1] = ((unsigned char *) &p_size)[1];
        d->size[0] = ((unsigned char *) &p_size)[2];
        d->addr[2] = ((unsigned char *) &p_start)[0];
        d->addr[1] = ((unsigned char *) &p_start)[1];
        d->addr[0] = ((unsigned char *) &p_start)[2];
        if (start + p_size >= end)
            return segments;

        p_next = (b->space == SYSGLBL_ADDR) ? P2P(start + p_size) :
                                              vtop(start + p_size);
        if (p_next == p_start + p_size)
            /* Continue Last Segment */
            p_size += min(NBPC, end - start - p_size);
        else {
            /* Start New Segment */
            p_start = p_next;
            start += p_size;
            p_size = min(NBPC, end - start);
            ++d;
            if (++segments > (sizeof(dslist_t) / sizeof(dsentry_t)))
                return 0;
        }
    }
}   /* mkdslist() */

/***********************************************************************
 *  startscsi()
 *
 *  Send a SCSI CDB to a target device on the bus.
 */

int startscsi(r)
register srb_p   r;
{
    register haccb_p    c;
    paddr_t             bufaddr;
    size_t              datalen;
    register int        s;

    if (r->target >= MAXDEVS || r->lun >= MAXUNITS) {
        printf("Illegal device ID: %d LUN: %d", r->target, r->lun);
        return 0;
    }

    ++r->tries;
    if (actv[r->target]) {
        devmsg(r->dev, "Device busy: opcode (0x%x)", r->cdb. g0. opcode);
        return 0;
    }

    s = sphi();
    r->status = ST_PENDING;
    c = ccb + r->target;
    memset(c, 0, sizeof(haccb_t));
    c->opcode = 0;              /* Start SCSI CDB */
    c->addrctrl = (r->target << 5) | (r->lun & 7);
    if (r->xferdir & DMAREAD)  c->addrctrl |= 0x08;
    if (r->xferdir & DMAWRITE) c->addrctrl |= 0x10;
    c->cdblen = cpycdb(&(c->cdb), &(r->cdb));
    c->senselen = 1;

/***********************************************************************
 *  Set up the CCB's Address here. This turned out to be a bit more
 *  complicated than I thought it would be.
 */

    if (r->buf. space == PHYS_ADDR) {
        c->datalen[0] = ((unsigned char *) &(r->buf. size))[2];
        c->datalen[1] = ((unsigned char *) &(r->buf. size))[1];
        c->datalen[2] = ((unsigned char *) &(r->buf. size))[0];
        c->bufaddr[0] = ((unsigned char *) &(r->buf. addr. paddr))[2];
        c->bufaddr[1] = ((unsigned char *) &(r->buf. addr. paddr))[1];
        c->bufaddr[2] = ((unsigned char *) &(r->buf. addr. paddr))[0];
    }
    else {
        datalen = mkdslist(ds[r->target]. entries, &(r->buf));
        if (datalen == 0) {
            printf("SCSI ID %d - Bad Scatter/Gather list\n", r->target);
            spl(s);
            return 0;
        }
        else if (datalen == 1)
            memcpy(c->datalen, ds[r->target]. entries, 6);
        else {
            c->opcode = 2;
            bufaddr = vtop(ds[r->target]. entries);
            datalen *= 6;
            c->datalen[0] = ((unsigned char *) &datalen)[2];
            c->datalen[1] = ((unsigned char *) &datalen)[1];
            c->datalen[2] = ((unsigned char *) &datalen)[0];
            c->bufaddr[0] = ((unsigned char *) &bufaddr)[2];
            c->bufaddr[1] = ((unsigned char *) &bufaddr)[1];
            c->bufaddr[2] = ((unsigned char *) &bufaddr)[0];
        }
    }

    mb. o[r->target]. cmd = MBOSTART;
    if (puthabyte(STARTSCSI) && (inb(STSREG) & INVDCMD) == 0) {
        actv[r->target] = r;
        if (r->timeout)
            drvl[SCSIMAJOR]. d_time = 1;
    }
    else
        panic("startscsi(): AHA-154x Command Start Failure\n");

    spl(s);
    return 1;
}   /* startscsi() */

/***********************************************************************
 *  abortscsi()
 *
 *  Abort the SCSI Command at a target device on the bus.
 */

void abortscsi(r)
register srb_p   r;
{
    int s,
        spin;

    printf("abortscsi(id: %d opcode: (0x%x)\n", r->target, r->cdb. g0. opcode);
    s = sphi();
    r->timeout = 0;
    mb. o[r->target]. cmd = MBOABORT;
    if (!puthabyte(STARTSCSI) || (inb(STSREG) & INVDCMD) != 0)
        panic("abortscsi(): AHA-154x Command Start Failure\n");

    spin = 0x200001;
    while (r->status == ST_PENDING && --spin > 0L)
        if ((spin & 0xFFF) == 0)
            checkmail();

    if (r->status == ST_PENDING) {
        if (r->xferdir == DMAREAD &&
            r->buf. space != KRNL_ADDR &&
            r->buf. size > 0)
            panic("abortscsi failed at id: %d\n", r->target);
        else {
            actv[r->target] = NULL;
            r->status = ST_DRVABRT;
            if (r->cleanup)
                (*r->cleanup)(r);
        }
    }
    spl(s);
}   /* abortscsi() */

/***********************************************************************
 *  resetdevice()
 *
 *  Reset a SCSI target.  This is not used yet so it isn't coded but if
 *  you need to use it all you need to do is set up a CCB with opcode =
 *  0x81. Then send it to the Host Adapter.  It will do the rest.
 */

int resetdevice(id)
int id;
{
    panic("resetdevice() Called for ID: %d\n", id);
}   /* resetdevice() */

/* End of file */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.