root/drivers/scsi/aha1542.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 *  Driver for Adaptec AHA-1542 SCSI host adapters
 *
 *  Copyright (C) 1992  Tommy Thorn
 *  Copyright (C) 1993, 1994, 1995 Eric Youngdale
 *  Copyright (C) 2015 Ondrej Zary
 */

#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/isa.h>
#include <linux/pnp.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <asm/dma.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include "aha1542.h"

#define MAXBOARDS 4

static bool isapnp = 1;
module_param(isapnp, bool, 0);
MODULE_PARM_DESC(isapnp, "enable PnP support (default=1)");

static int io[MAXBOARDS] = { 0x330, 0x334, 0, 0 };
module_param_hw_array(io, int, ioport, NULL, 0);
MODULE_PARM_DESC(io, "base IO address of controller (0x130,0x134,0x230,0x234,0x330,0x334, default=0x330,0x334)");

/* time AHA spends on the AT-bus during data transfer */
static int bus_on[MAXBOARDS] = { -1, -1, -1, -1 }; /* power-on default: 11us */
module_param_array(bus_on, int, NULL, 0);
MODULE_PARM_DESC(bus_on, "bus on time [us] (2-15, default=-1 [HW default: 11])");

/* time AHA spends off the bus (not to monopolize it) during data transfer  */
static int bus_off[MAXBOARDS] = { -1, -1, -1, -1 }; /* power-on default: 4us */
module_param_array(bus_off, int, NULL, 0);
MODULE_PARM_DESC(bus_off, "bus off time [us] (1-64, default=-1 [HW default: 4])");

/* default is jumper selected (J1 on 1542A), factory default = 5 MB/s */
static int dma_speed[MAXBOARDS] = { -1, -1, -1, -1 };
module_param_array(dma_speed, int, NULL, 0);
MODULE_PARM_DESC(dma_speed, "DMA speed [MB/s] (5,6,7,8,10, default=-1 [by jumper])");

#define BIOS_TRANSLATION_6432 1 /* Default case these days */
#define BIOS_TRANSLATION_25563 2        /* Big disk case */

struct aha1542_hostdata {
        /* This will effectively start both of them at the first mailbox */
        int bios_translation;   /* Mapping bios uses - for compatibility */
        int aha1542_last_mbi_used;
        int aha1542_last_mbo_used;
        struct scsi_cmnd *int_cmds[AHA1542_MAILBOXES];
        struct mailbox *mb;
        dma_addr_t mb_handle;
        struct ccb *ccb;
        dma_addr_t ccb_handle;
};

#define AHA1542_MAX_SECTORS       16

struct aha1542_cmd {
        /* bounce buffer */
        void *data_buffer;
        dma_addr_t data_buffer_handle;
};

static inline void aha1542_intr_reset(u16 base)
{
        outb(IRST, CONTROL(base));
}

static inline bool wait_mask(u16 port, u8 mask, u8 allof, u8 noneof, int timeout)
{
        bool delayed = true;

        if (timeout == 0) {
                timeout = 3000000;
                delayed = false;
        }

        while (1) {
                u8 bits = inb(port) & mask;
                if ((bits & allof) == allof && ((bits & noneof) == 0))
                        break;
                if (delayed)
                        mdelay(1);
                if (--timeout == 0)
                        return false;
        }

        return true;
}

static int aha1542_outb(unsigned int base, u8 val)
{
        if (!wait_mask(STATUS(base), CDF, 0, CDF, 0))
                return 1;
        outb(val, DATA(base));

        return 0;
}

static int aha1542_out(unsigned int base, u8 *buf, int len)
{
        while (len--) {
                if (!wait_mask(STATUS(base), CDF, 0, CDF, 0))
                        return 1;
                outb(*buf++, DATA(base));
        }
        if (!wait_mask(INTRFLAGS(base), INTRMASK, HACC, 0, 0))
                return 1;

        return 0;
}

/*
 * Only used at boot time, so we do not need to worry about latency as much
 * here
 */

static int aha1542_in(unsigned int base, u8 *buf, int len, int timeout)
{
        while (len--) {
                if (!wait_mask(STATUS(base), DF, DF, 0, timeout))
                        return 1;
                *buf++ = inb(DATA(base));
        }
        return 0;
}

static int makecode(unsigned hosterr, unsigned scsierr)
{
        switch (hosterr) {
        case 0x0:
        case 0xa:               /* Linked command complete without error and linked normally */
        case 0xb:               /* Linked command complete without error, interrupt generated */
                hosterr = 0;
                break;

        case 0x11:              /* Selection time out-The initiator selection or target
                                 * reselection was not complete within the SCSI Time out period
                                 */
                hosterr = DID_TIME_OUT;
                break;

        case 0x12:              /* Data overrun/underrun-The target attempted to transfer more data
                                 * than was allocated by the Data Length field or the sum of the
                                 * Scatter / Gather Data Length fields.
                                 */

        case 0x13:              /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */

        case 0x15:              /* MBO command was not 00, 01 or 02-The first byte of the CB was
                                 * invalid. This usually indicates a software failure.
                                 */

        case 0x16:              /* Invalid CCB Operation Code-The first byte of the CCB was invalid.
                                 * This usually indicates a software failure.
                                 */

        case 0x17:              /* Linked CCB does not have the same LUN-A subsequent CCB of a set
                                 * of linked CCB's does not specify the same logical unit number as
                                 * the first.
                                 */
        case 0x18:              /* Invalid Target Direction received from Host-The direction of a
                                 * Target Mode CCB was invalid.
                                 */

        case 0x19:              /* Duplicate CCB Received in Target Mode-More than once CCB was
                                 * received to service data transfer between the same target LUN
                                 * and initiator SCSI ID in the same direction.
                                 */

        case 0x1a:              /* Invalid CCB or Segment List Parameter-A segment list with a zero
                                 * length segment or invalid segment list boundaries was received.
                                 * A CCB parameter was invalid.
                                 */
#ifdef DEBUG
                printk("Aha1542: %x %x\n", hosterr, scsierr);
#endif
                hosterr = DID_ERROR;    /* Couldn't find any better */
                break;

        case 0x14:              /* Target bus phase sequence failure-An invalid bus phase or bus
                                 * phase sequence was requested by the target. The host adapter
                                 * will generate a SCSI Reset Condition, notifying the host with
                                 * a SCRD interrupt
                                 */
                hosterr = DID_RESET;
                break;
        default:
                printk(KERN_ERR "aha1542: makecode: unknown hoststatus %x\n", hosterr);
                break;
        }
        return scsierr | (hosterr << 16);
}

static int aha1542_test_port(struct Scsi_Host *sh)
{
        int i;

        /* Quick and dirty test for presence of the card. */
        if (inb(STATUS(sh->io_port)) == 0xff)
                return 0;

        /* Reset the adapter. I ought to make a hard reset, but it's not really necessary */

        /* In case some other card was probing here, reset interrupts */
        aha1542_intr_reset(sh->io_port);        /* reset interrupts, so they don't block */

        outb(SRST | IRST /*|SCRST */ , CONTROL(sh->io_port));

        mdelay(20);             /* Wait a little bit for things to settle down. */

        /* Expect INIT and IDLE, any of the others are bad */
        if (!wait_mask(STATUS(sh->io_port), STATMASK, INIT | IDLE, STST | DIAGF | INVDCMD | DF | CDF, 0))
                return 0;

        /* Shouldn't have generated any interrupts during reset */
        if (inb(INTRFLAGS(sh->io_port)) & INTRMASK)
                return 0;

        /*
         * Perform a host adapter inquiry instead so we do not need to set
         * up the mailboxes ahead of time
         */

        aha1542_outb(sh->io_port, CMD_INQUIRY);

        for (i = 0; i < 4; i++) {
                if (!wait_mask(STATUS(sh->io_port), DF, DF, 0, 0))
                        return 0;
                (void)inb(DATA(sh->io_port));
        }

        /* Reading port should reset DF */
        if (inb(STATUS(sh->io_port)) & DF)
                return 0;

        /* When HACC, command is completed, and we're though testing */
        if (!wait_mask(INTRFLAGS(sh->io_port), HACC, HACC, 0, 0))
                return 0;

        /* Clear interrupts */
        outb(IRST, CONTROL(sh->io_port));

        return 1;
}

static void aha1542_free_cmd(struct scsi_cmnd *cmd)
{
        struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);

        if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
                struct request *rq = scsi_cmd_to_rq(cmd);
                void *buf = acmd->data_buffer;
                struct req_iterator iter;
                struct bio_vec bv;

                rq_for_each_segment(bv, rq, iter) {
                        memcpy_to_bvec(&bv, buf);
                        buf += bv.bv_len;
                }
        }

        scsi_dma_unmap(cmd);
}

static irqreturn_t aha1542_interrupt(int irq, void *dev_id)
{
        struct Scsi_Host *sh = dev_id;
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        int errstatus, mbi, mbo, mbistatus;
        int number_serviced;
        unsigned long flags;
        struct scsi_cmnd *tmp_cmd;
        int flag;
        struct mailbox *mb = aha1542->mb;
        struct ccb *ccb = aha1542->ccb;

#ifdef DEBUG
        {
                flag = inb(INTRFLAGS(sh->io_port));
                shost_printk(KERN_DEBUG, sh, "aha1542_intr_handle: ");
                if (!(flag & ANYINTR))
                        printk("no interrupt?");
                if (flag & MBIF)
                        printk("MBIF ");
                if (flag & MBOA)
                        printk("MBOF ");
                if (flag & HACC)
                        printk("HACC ");
                if (flag & SCRD)
                        printk("SCRD ");
                printk("status %02x\n", inb(STATUS(sh->io_port)));
        }
#endif
        number_serviced = 0;

        spin_lock_irqsave(sh->host_lock, flags);
        while (1) {
                flag = inb(INTRFLAGS(sh->io_port));

                /*
                 * Check for unusual interrupts.  If any of these happen, we should
                 * probably do something special, but for now just printing a message
                 * is sufficient.  A SCSI reset detected is something that we really
                 * need to deal with in some way.
                 */
                if (flag & ~MBIF) {
                        if (flag & MBOA)
                                printk("MBOF ");
                        if (flag & HACC)
                                printk("HACC ");
                        if (flag & SCRD)
                                printk("SCRD ");
                }
                aha1542_intr_reset(sh->io_port);

                mbi = aha1542->aha1542_last_mbi_used + 1;
                if (mbi >= 2 * AHA1542_MAILBOXES)
                        mbi = AHA1542_MAILBOXES;

                do {
                        if (mb[mbi].status != 0)
                                break;
                        mbi++;
                        if (mbi >= 2 * AHA1542_MAILBOXES)
                                mbi = AHA1542_MAILBOXES;
                } while (mbi != aha1542->aha1542_last_mbi_used);

                if (mb[mbi].status == 0) {
                        spin_unlock_irqrestore(sh->host_lock, flags);
                        /* Hmm, no mail.  Must have read it the last time around */
                        if (!number_serviced)
                                shost_printk(KERN_WARNING, sh, "interrupt received, but no mail.\n");
                        return IRQ_HANDLED;
                }

                mbo = (scsi2int(mb[mbi].ccbptr) - (unsigned long)aha1542->ccb_handle) / sizeof(struct ccb);
                mbistatus = mb[mbi].status;
                mb[mbi].status = 0;
                aha1542->aha1542_last_mbi_used = mbi;

#ifdef DEBUG
                if (ccb[mbo].tarstat | ccb[mbo].hastat)
                        shost_printk(KERN_DEBUG, sh, "aha1542_command: returning %x (status %d)\n",
                               ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
#endif

                if (mbistatus == 3)
                        continue;       /* Aborted command not found */

#ifdef DEBUG
                shost_printk(KERN_DEBUG, sh, "...done %d %d\n", mbo, mbi);
#endif

                tmp_cmd = aha1542->int_cmds[mbo];

                if (!tmp_cmd) {
                        spin_unlock_irqrestore(sh->host_lock, flags);
                        shost_printk(KERN_WARNING, sh, "Unexpected interrupt\n");
                        shost_printk(KERN_WARNING, sh, "tarstat=%x, hastat=%x idlun=%x ccb#=%d\n", ccb[mbo].tarstat,
                               ccb[mbo].hastat, ccb[mbo].idlun, mbo);
                        return IRQ_HANDLED;
                }
                aha1542_free_cmd(tmp_cmd);
                /*
                 * Fetch the sense data, and tuck it away, in the required slot.  The
                 * Adaptec automatically fetches it, and there is no guarantee that
                 * we will still have it in the cdb when we come back
                 */
                if (ccb[mbo].tarstat == 2)
                        memcpy(tmp_cmd->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
                               SCSI_SENSE_BUFFERSIZE);


                /* is there mail :-) */

                /* more error checking left out here */
                if (mbistatus != 1)
                        /* This is surely wrong, but I don't know what's right */
                        errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
                else
                        errstatus = 0;

#ifdef DEBUG
                if (errstatus)
                        shost_printk(KERN_DEBUG, sh, "(aha1542 error:%x %x %x) ", errstatus,
                               ccb[mbo].hastat, ccb[mbo].tarstat);
                if (ccb[mbo].tarstat == 2)
                        print_hex_dump_bytes("sense: ", DUMP_PREFIX_NONE, &ccb[mbo].cdb[ccb[mbo].cdblen], 12);
                if (errstatus)
                        printk("aha1542_intr_handle: returning %6x\n", errstatus);
#endif
                tmp_cmd->result = errstatus;
                aha1542->int_cmds[mbo] = NULL;  /* This effectively frees up the mailbox slot, as
                                                 * far as queuecommand is concerned
                                                 */
                scsi_done(tmp_cmd);
                number_serviced++;
        }
}

static enum scsi_qc_status aha1542_queuecommand(struct Scsi_Host *sh,
                                                struct scsi_cmnd *cmd)
{
        struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        u8 direction;
        u8 target = cmd->device->id;
        u8 lun = cmd->device->lun;
        unsigned long flags;
        int bufflen = scsi_bufflen(cmd);
        int mbo;
        struct mailbox *mb = aha1542->mb;
        struct ccb *ccb = aha1542->ccb;

        if (*cmd->cmnd == REQUEST_SENSE) {
                /* Don't do the command - we have the sense data already */
                cmd->result = 0;
                scsi_done(cmd);
                return 0;
        }
#ifdef DEBUG
        {
                int i = -1;
                if (*cmd->cmnd == READ_10 || *cmd->cmnd == WRITE_10)
                        i = xscsi2int(cmd->cmnd + 2);
                else if (*cmd->cmnd == READ_6 || *cmd->cmnd == WRITE_6)
                        i = scsi2int(cmd->cmnd + 2);
                shost_printk(KERN_DEBUG, sh, "aha1542_queuecommand: dev %d cmd %02x pos %d len %d",
                                                target, *cmd->cmnd, i, bufflen);
                print_hex_dump_bytes("command: ", DUMP_PREFIX_NONE, cmd->cmnd, cmd->cmd_len);
        }
#endif

        if (cmd->sc_data_direction == DMA_TO_DEVICE) {
                struct request *rq = scsi_cmd_to_rq(cmd);
                void *buf = acmd->data_buffer;
                struct req_iterator iter;
                struct bio_vec bv;

                rq_for_each_segment(bv, rq, iter) {
                        memcpy_from_bvec(buf, &bv);
                        buf += bv.bv_len;
                }
        }

        /*
         * Use the outgoing mailboxes in a round-robin fashion, because this
         * is how the host adapter will scan for them
         */

        spin_lock_irqsave(sh->host_lock, flags);
        mbo = aha1542->aha1542_last_mbo_used + 1;
        if (mbo >= AHA1542_MAILBOXES)
                mbo = 0;

        do {
                if (mb[mbo].status == 0 && aha1542->int_cmds[mbo] == NULL)
                        break;
                mbo++;
                if (mbo >= AHA1542_MAILBOXES)
                        mbo = 0;
        } while (mbo != aha1542->aha1542_last_mbo_used);

        if (mb[mbo].status || aha1542->int_cmds[mbo])
                panic("Unable to find empty mailbox for aha1542.\n");

        aha1542->int_cmds[mbo] = cmd;   /* This will effectively prevent someone else from
                                         * screwing with this cdb.
                                         */

        aha1542->aha1542_last_mbo_used = mbo;

#ifdef DEBUG
        shost_printk(KERN_DEBUG, sh, "Sending command (%d)...", mbo);
#endif

        /* This gets trashed for some reason */
        any2scsi(mb[mbo].ccbptr, aha1542->ccb_handle + mbo * sizeof(*ccb));

        memset(&ccb[mbo], 0, sizeof(struct ccb));

        ccb[mbo].cdblen = cmd->cmd_len;

        direction = 0;
        if (*cmd->cmnd == READ_10 || *cmd->cmnd == READ_6)
                direction = 8;
        else if (*cmd->cmnd == WRITE_10 || *cmd->cmnd == WRITE_6)
                direction = 16;

        memcpy(ccb[mbo].cdb, cmd->cmnd, ccb[mbo].cdblen);
        ccb[mbo].op = 0;        /* SCSI Initiator Command */
        any2scsi(ccb[mbo].datalen, bufflen);
        if (bufflen)
                any2scsi(ccb[mbo].dataptr, acmd->data_buffer_handle);
        else
                any2scsi(ccb[mbo].dataptr, 0);
        ccb[mbo].idlun = (target & 7) << 5 | direction | (lun & 7);     /*SCSI Target Id */
        ccb[mbo].rsalen = 16;
        ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
        ccb[mbo].commlinkid = 0;

#ifdef DEBUG
        print_hex_dump_bytes("sending: ", DUMP_PREFIX_NONE, &ccb[mbo], sizeof(ccb[mbo]) - 10);
        printk("aha1542_queuecommand: now waiting for interrupt ");
#endif
        mb[mbo].status = 1;
        aha1542_outb(cmd->device->host->io_port, CMD_START_SCSI);
        spin_unlock_irqrestore(sh->host_lock, flags);

        return 0;
}

/* Initialize mailboxes */
static void setup_mailboxes(struct Scsi_Host *sh)
{
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        u8 mb_cmd[5] = { CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
        int i;

        for (i = 0; i < AHA1542_MAILBOXES; i++) {
                aha1542->mb[i].status = 0;
                any2scsi(aha1542->mb[i].ccbptr,
                         aha1542->ccb_handle + i * sizeof(struct ccb));
                aha1542->mb[AHA1542_MAILBOXES + i].status = 0;
        }
        aha1542_intr_reset(sh->io_port);        /* reset interrupts, so they don't block */
        any2scsi(mb_cmd + 2, aha1542->mb_handle);
        if (aha1542_out(sh->io_port, mb_cmd, 5))
                shost_printk(KERN_ERR, sh, "failed setting up mailboxes\n");
        aha1542_intr_reset(sh->io_port);
}

static int aha1542_getconfig(struct Scsi_Host *sh)
{
        u8 inquiry_result[3];
        int i;
        i = inb(STATUS(sh->io_port));
        if (i & DF) {
                i = inb(DATA(sh->io_port));
        }
        aha1542_outb(sh->io_port, CMD_RETCONF);
        aha1542_in(sh->io_port, inquiry_result, 3, 0);
        if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 0))
                shost_printk(KERN_ERR, sh, "error querying board settings\n");
        aha1542_intr_reset(sh->io_port);
        switch (inquiry_result[0]) {
        case 0x80:
                sh->dma_channel = 7;
                break;
        case 0x40:
                sh->dma_channel = 6;
                break;
        case 0x20:
                sh->dma_channel = 5;
                break;
        case 0x01:
                sh->dma_channel = 0;
                break;
        case 0:
                /*
                 * This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
                 * Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this.
                 */
                sh->dma_channel = 0xFF;
                break;
        default:
                shost_printk(KERN_ERR, sh, "Unable to determine DMA channel.\n");
                return -1;
        }
        switch (inquiry_result[1]) {
        case 0x40:
                sh->irq = 15;
                break;
        case 0x20:
                sh->irq = 14;
                break;
        case 0x8:
                sh->irq = 12;
                break;
        case 0x4:
                sh->irq = 11;
                break;
        case 0x2:
                sh->irq = 10;
                break;
        case 0x1:
                sh->irq = 9;
                break;
        default:
                shost_printk(KERN_ERR, sh, "Unable to determine IRQ level.\n");
                return -1;
        }
        sh->this_id = inquiry_result[2] & 7;
        return 0;
}

/*
 * This function should only be called for 1542C boards - we can detect
 * the special firmware settings and unlock the board
 */

static int aha1542_mbenable(struct Scsi_Host *sh)
{
        static u8 mbenable_cmd[3];
        static u8 mbenable_result[2];
        int retval;

        retval = BIOS_TRANSLATION_6432;

        aha1542_outb(sh->io_port, CMD_EXTBIOS);
        if (aha1542_in(sh->io_port, mbenable_result, 2, 100))
                return retval;
        if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 100))
                goto fail;
        aha1542_intr_reset(sh->io_port);

        if ((mbenable_result[0] & 0x08) || mbenable_result[1]) {
                mbenable_cmd[0] = CMD_MBENABLE;
                mbenable_cmd[1] = 0;
                mbenable_cmd[2] = mbenable_result[1];

                if ((mbenable_result[0] & 0x08) && (mbenable_result[1] & 0x03))
                        retval = BIOS_TRANSLATION_25563;

                if (aha1542_out(sh->io_port, mbenable_cmd, 3))
                        goto fail;
        }
        while (0) {
fail:
                shost_printk(KERN_ERR, sh, "Mailbox init failed\n");
        }
        aha1542_intr_reset(sh->io_port);
        return retval;
}

/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
static int aha1542_query(struct Scsi_Host *sh)
{
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        u8 inquiry_result[4];
        int i;
        i = inb(STATUS(sh->io_port));
        if (i & DF) {
                i = inb(DATA(sh->io_port));
        }
        aha1542_outb(sh->io_port, CMD_INQUIRY);
        aha1542_in(sh->io_port, inquiry_result, 4, 0);
        if (!wait_mask(INTRFLAGS(sh->io_port), INTRMASK, HACC, 0, 0))
                shost_printk(KERN_ERR, sh, "error querying card type\n");
        aha1542_intr_reset(sh->io_port);

        aha1542->bios_translation = BIOS_TRANSLATION_6432;      /* Default case */

        /*
         * For an AHA1740 series board, we ignore the board since there is a
         * hardware bug which can lead to wrong blocks being returned if the board
         * is operating in the 1542 emulation mode.  Since there is an extended mode
         * driver, we simply ignore the board and let the 1740 driver pick it up.
         */

        if (inquiry_result[0] == 0x43) {
                shost_printk(KERN_INFO, sh, "Emulation mode not supported for AHA-1740 hardware, use aha1740 driver instead.\n");
                return 1;
        }

        /*
         * Always call this - boards that do not support extended bios translation
         * will ignore the command, and we will set the proper default
         */

        aha1542->bios_translation = aha1542_mbenable(sh);

        return 0;
}

static u8 dma_speed_hw(int dma_speed)
{
        switch (dma_speed) {
        case 5:
                return 0x00;
        case 6:
                return 0x04;
        case 7:
                return 0x01;
        case 8:
                return 0x02;
        case 10:
                return 0x03;
        }

        return 0xff;    /* invalid */
}

/* Set the Bus on/off-times as not to ruin floppy performance */
static void aha1542_set_bus_times(struct Scsi_Host *sh, int bus_on, int bus_off, int dma_speed)
{
        if (bus_on > 0) {
                u8 oncmd[] = { CMD_BUSON_TIME, clamp(bus_on, 2, 15) };

                aha1542_intr_reset(sh->io_port);
                if (aha1542_out(sh->io_port, oncmd, 2))
                        goto fail;
        }

        if (bus_off > 0) {
                u8 offcmd[] = { CMD_BUSOFF_TIME, clamp(bus_off, 1, 64) };

                aha1542_intr_reset(sh->io_port);
                if (aha1542_out(sh->io_port, offcmd, 2))
                        goto fail;
        }

        if (dma_speed_hw(dma_speed) != 0xff) {
                u8 dmacmd[] = { CMD_DMASPEED, dma_speed_hw(dma_speed) };

                aha1542_intr_reset(sh->io_port);
                if (aha1542_out(sh->io_port, dmacmd, 2))
                        goto fail;
        }
        aha1542_intr_reset(sh->io_port);
        return;
fail:
        shost_printk(KERN_ERR, sh, "setting bus on/off-time failed\n");
        aha1542_intr_reset(sh->io_port);
}

/* return non-zero on detection */
static struct Scsi_Host *aha1542_hw_init(const struct scsi_host_template *tpnt,
                                         struct device *pdev, int indx)
{
        unsigned int base_io = io[indx];
        struct Scsi_Host *sh;
        struct aha1542_hostdata *aha1542;
        char dma_info[] = "no DMA";

        if (base_io == 0)
                return NULL;

        if (!request_region(base_io, AHA1542_REGION_SIZE, "aha1542"))
                return NULL;

        sh = scsi_host_alloc(tpnt, sizeof(struct aha1542_hostdata));
        if (!sh)
                goto release;
        aha1542 = shost_priv(sh);

        sh->unique_id = base_io;
        sh->io_port = base_io;
        sh->n_io_port = AHA1542_REGION_SIZE;
        aha1542->aha1542_last_mbi_used = 2 * AHA1542_MAILBOXES - 1;
        aha1542->aha1542_last_mbo_used = AHA1542_MAILBOXES - 1;

        if (!aha1542_test_port(sh))
                goto unregister;

        aha1542_set_bus_times(sh, bus_on[indx], bus_off[indx], dma_speed[indx]);
        if (aha1542_query(sh))
                goto unregister;
        if (aha1542_getconfig(sh) == -1)
                goto unregister;

        if (sh->dma_channel != 0xFF)
                snprintf(dma_info, sizeof(dma_info), "DMA %d", sh->dma_channel);
        shost_printk(KERN_INFO, sh, "Adaptec AHA-1542 (SCSI-ID %d) at IO 0x%x, IRQ %d, %s\n",
                                sh->this_id, base_io, sh->irq, dma_info);
        if (aha1542->bios_translation == BIOS_TRANSLATION_25563)
                shost_printk(KERN_INFO, sh, "Using extended bios translation\n");

        if (dma_set_mask_and_coherent(pdev, DMA_BIT_MASK(24)) < 0)
                goto unregister;

        aha1542->mb = dma_alloc_coherent(pdev,
                        AHA1542_MAILBOXES * 2 * sizeof(struct mailbox),
                        &aha1542->mb_handle, GFP_KERNEL);
        if (!aha1542->mb)
                goto unregister;

        aha1542->ccb = dma_alloc_coherent(pdev,
                        AHA1542_MAILBOXES * sizeof(struct ccb),
                        &aha1542->ccb_handle, GFP_KERNEL);
        if (!aha1542->ccb)
                goto free_mb;

        setup_mailboxes(sh);

        if (request_irq(sh->irq, aha1542_interrupt, 0, "aha1542", sh)) {
                shost_printk(KERN_ERR, sh, "Unable to allocate IRQ.\n");
                goto free_ccb;
        }
        if (sh->dma_channel != 0xFF) {
                if (request_dma(sh->dma_channel, "aha1542")) {
                        shost_printk(KERN_ERR, sh, "Unable to allocate DMA channel.\n");
                        goto free_irq;
                }
                if (sh->dma_channel == 0 || sh->dma_channel >= 5) {
                        set_dma_mode(sh->dma_channel, DMA_MODE_CASCADE);
                        enable_dma(sh->dma_channel);
                }
        }

        if (scsi_add_host(sh, pdev))
                goto free_dma;

        scsi_scan_host(sh);

        return sh;

free_dma:
        if (sh->dma_channel != 0xff)
                free_dma(sh->dma_channel);
free_irq:
        free_irq(sh->irq, sh);
free_ccb:
        dma_free_coherent(pdev, AHA1542_MAILBOXES * sizeof(struct ccb),
                          aha1542->ccb, aha1542->ccb_handle);
free_mb:
        dma_free_coherent(pdev, AHA1542_MAILBOXES * 2 * sizeof(struct mailbox),
                          aha1542->mb, aha1542->mb_handle);
unregister:
        scsi_host_put(sh);
release:
        release_region(base_io, AHA1542_REGION_SIZE);

        return NULL;
}

static int aha1542_release(struct Scsi_Host *sh)
{
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        struct device *dev = sh->dma_dev;

        scsi_remove_host(sh);
        if (sh->dma_channel != 0xff)
                free_dma(sh->dma_channel);
        dma_free_coherent(dev, AHA1542_MAILBOXES * sizeof(struct ccb),
                          aha1542->ccb, aha1542->ccb_handle);
        dma_free_coherent(dev, AHA1542_MAILBOXES * 2 * sizeof(struct mailbox),
                          aha1542->mb, aha1542->mb_handle);
        if (sh->irq)
                free_irq(sh->irq, sh);
        if (sh->io_port && sh->n_io_port)
                release_region(sh->io_port, sh->n_io_port);
        scsi_host_put(sh);
        return 0;
}


/*
 * This is a device reset.  This is handled by sending a special command
 * to the device.
 */
static int aha1542_dev_reset(struct scsi_cmnd *cmd)
{
        struct Scsi_Host *sh = cmd->device->host;
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        unsigned long flags;
        struct mailbox *mb = aha1542->mb;
        u8 target = cmd->device->id;
        u8 lun = cmd->device->lun;
        int mbo;
        struct ccb *ccb = aha1542->ccb;

        spin_lock_irqsave(sh->host_lock, flags);
        mbo = aha1542->aha1542_last_mbo_used + 1;
        if (mbo >= AHA1542_MAILBOXES)
                mbo = 0;

        do {
                if (mb[mbo].status == 0 && aha1542->int_cmds[mbo] == NULL)
                        break;
                mbo++;
                if (mbo >= AHA1542_MAILBOXES)
                        mbo = 0;
        } while (mbo != aha1542->aha1542_last_mbo_used);

        if (mb[mbo].status || aha1542->int_cmds[mbo])
                panic("Unable to find empty mailbox for aha1542.\n");

        aha1542->int_cmds[mbo] = cmd;   /* This will effectively
                                         * prevent someone else from
                                         * screwing with this cdb.
                                         */

        aha1542->aha1542_last_mbo_used = mbo;

        /* This gets trashed for some reason */
        any2scsi(mb[mbo].ccbptr, aha1542->ccb_handle + mbo * sizeof(*ccb));

        memset(&ccb[mbo], 0, sizeof(struct ccb));

        ccb[mbo].op = 0x81;     /* BUS DEVICE RESET */

        ccb[mbo].idlun = (target & 7) << 5 | (lun & 7);         /*SCSI Target Id */

        ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
        ccb[mbo].commlinkid = 0;

        /*
         * Now tell the 1542 to flush all pending commands for this
         * target
         */
        aha1542_outb(sh->io_port, CMD_START_SCSI);
        spin_unlock_irqrestore(sh->host_lock, flags);

        scmd_printk(KERN_WARNING, cmd,
                "Trying device reset for target\n");

        return SUCCESS;
}

static int aha1542_reset(struct scsi_cmnd *cmd, u8 reset_cmd)
{
        struct Scsi_Host *sh = cmd->device->host;
        struct aha1542_hostdata *aha1542 = shost_priv(sh);
        unsigned long flags;
        int i;

        spin_lock_irqsave(sh->host_lock, flags);
        /*
         * This does a scsi reset for all devices on the bus.
         * In principle, we could also reset the 1542 - should
         * we do this?  Try this first, and we can add that later
         * if it turns out to be useful.
         */
        outb(reset_cmd, CONTROL(cmd->device->host->io_port));

        if (!wait_mask(STATUS(cmd->device->host->io_port),
             STATMASK, IDLE, STST | DIAGF | INVDCMD | DF | CDF, 0)) {
                spin_unlock_irqrestore(sh->host_lock, flags);
                return FAILED;
        }

        /*
         * We need to do this too before the 1542 can interact with
         * us again after host reset.
         */
        if (reset_cmd & HRST)
                setup_mailboxes(cmd->device->host);

        /*
         * Now try to pick up the pieces.  For all pending commands,
         * free any internal data structures, and basically clear things
         * out.  We do not try and restart any commands or anything -
         * the strategy handler takes care of that crap.
         */
        shost_printk(KERN_WARNING, cmd->device->host, "Sent BUS RESET to scsi host %d\n", cmd->device->host->host_no);

        for (i = 0; i < AHA1542_MAILBOXES; i++) {
                if (aha1542->int_cmds[i] != NULL) {
                        struct scsi_cmnd *tmp_cmd;
                        tmp_cmd = aha1542->int_cmds[i];

                        if (tmp_cmd->device->soft_reset) {
                                /*
                                 * If this device implements the soft reset option,
                                 * then it is still holding onto the command, and
                                 * may yet complete it.  In this case, we don't
                                 * flush the data.
                                 */
                                continue;
                        }
                        aha1542_free_cmd(tmp_cmd);
                        aha1542->int_cmds[i] = NULL;
                        aha1542->mb[i].status = 0;
                }
        }

        spin_unlock_irqrestore(sh->host_lock, flags);
        return SUCCESS;
}

static int aha1542_bus_reset(struct scsi_cmnd *cmd)
{
        return aha1542_reset(cmd, SCRST);
}

static int aha1542_host_reset(struct scsi_cmnd *cmd)
{
        return aha1542_reset(cmd, HRST | SCRST);
}

static int aha1542_biosparam(struct scsi_device *sdev,
                struct gendisk *unused, sector_t capacity, int geom[])
{
        struct aha1542_hostdata *aha1542 = shost_priv(sdev->host);

        if (capacity >= 0x200000 &&
                        aha1542->bios_translation == BIOS_TRANSLATION_25563) {
                /* Please verify that this is the same as what DOS returns */
                geom[0] = 255;  /* heads */
                geom[1] = 63;   /* sectors */
        } else {
                geom[0] = 64;   /* heads */
                geom[1] = 32;   /* sectors */
        }
        geom[2] = sector_div(capacity, geom[0] * geom[1]);      /* cylinders */

        return 0;
}

MODULE_DESCRIPTION("Adaptec AHA-1542 SCSI host adapter driver");
MODULE_LICENSE("GPL");

static int aha1542_init_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
{
        struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);

        acmd->data_buffer = dma_alloc_coherent(shost->dma_dev,
                        SECTOR_SIZE * AHA1542_MAX_SECTORS,
                        &acmd->data_buffer_handle, GFP_KERNEL);
        if (!acmd->data_buffer)
                return -ENOMEM;
        return 0;
}

static int aha1542_exit_cmd_priv(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
{
        struct aha1542_cmd *acmd = scsi_cmd_priv(cmd);

        dma_free_coherent(shost->dma_dev, SECTOR_SIZE * AHA1542_MAX_SECTORS,
                        acmd->data_buffer, acmd->data_buffer_handle);
        return 0;
}

static const struct scsi_host_template driver_template = {
        .module                 = THIS_MODULE,
        .proc_name              = "aha1542",
        .name                   = "Adaptec 1542",
        .cmd_size               = sizeof(struct aha1542_cmd),
        .queuecommand           = aha1542_queuecommand,
        .eh_device_reset_handler= aha1542_dev_reset,
        .eh_bus_reset_handler   = aha1542_bus_reset,
        .eh_host_reset_handler  = aha1542_host_reset,
        .bios_param             = aha1542_biosparam,
        .init_cmd_priv          = aha1542_init_cmd_priv,
        .exit_cmd_priv          = aha1542_exit_cmd_priv,
        .can_queue              = AHA1542_MAILBOXES,
        .this_id                = 7,
        .max_sectors            = AHA1542_MAX_SECTORS,
        .sg_tablesize           = SG_ALL,
};

static int aha1542_isa_match(struct device *pdev, unsigned int ndev)
{
        struct Scsi_Host *sh = aha1542_hw_init(&driver_template, pdev, ndev);

        if (!sh)
                return 0;

        dev_set_drvdata(pdev, sh);
        return 1;
}

static void aha1542_isa_remove(struct device *pdev,
                                    unsigned int ndev)
{
        aha1542_release(dev_get_drvdata(pdev));
        dev_set_drvdata(pdev, NULL);
}

static struct isa_driver aha1542_isa_driver = {
        .match          = aha1542_isa_match,
        .remove         = aha1542_isa_remove,
        .driver         = {
                .name   = "aha1542"
        },
};
static int isa_registered;

#ifdef CONFIG_PNP
static const struct pnp_device_id aha1542_pnp_ids[] = {
        { .id = "ADP1542" },
        { .id = "" }
};
MODULE_DEVICE_TABLE(pnp, aha1542_pnp_ids);

static int aha1542_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
{
        int indx;
        struct Scsi_Host *sh;

        for (indx = 0; indx < ARRAY_SIZE(io); indx++) {
                if (io[indx])
                        continue;

                if (pnp_activate_dev(pdev) < 0)
                        continue;

                io[indx] = pnp_port_start(pdev, 0);

                /*
                 * The card can be queried for its DMA, we have
                 * the DMA set up that is enough
                 */

                dev_info(&pdev->dev, "ISAPnP found an AHA1535 at I/O 0x%03X", io[indx]);
        }

        sh = aha1542_hw_init(&driver_template, &pdev->dev, indx);
        if (!sh)
                return -ENODEV;

        pnp_set_drvdata(pdev, sh);
        return 0;
}

static void aha1542_pnp_remove(struct pnp_dev *pdev)
{
        aha1542_release(pnp_get_drvdata(pdev));
        pnp_set_drvdata(pdev, NULL);
}

static struct pnp_driver aha1542_pnp_driver = {
        .name           = "aha1542",
        .id_table       = aha1542_pnp_ids,
        .probe          = aha1542_pnp_probe,
        .remove         = aha1542_pnp_remove,
};
static int pnp_registered;
#endif /* CONFIG_PNP */

static int __init aha1542_init(void)
{
        int ret = 0;

#ifdef CONFIG_PNP
        if (isapnp) {
                ret = pnp_register_driver(&aha1542_pnp_driver);
                if (!ret)
                        pnp_registered = 1;
        }
#endif
        ret = isa_register_driver(&aha1542_isa_driver, MAXBOARDS);
        if (!ret)
                isa_registered = 1;

#ifdef CONFIG_PNP
        if (pnp_registered)
                ret = 0;
#endif
        if (isa_registered)
                ret = 0;

        return ret;
}

static void __exit aha1542_exit(void)
{
#ifdef CONFIG_PNP
        if (pnp_registered)
                pnp_unregister_driver(&aha1542_pnp_driver);
#endif
        if (isa_registered)
                isa_unregister_driver(&aha1542_isa_driver);
}

module_init(aha1542_init);
module_exit(aha1542_exit);