root/drivers/scsi/mesh.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware)
 * bus adaptor found on Power Macintosh computers.
 * We assume the MESH is connected to a DBDMA (descriptor-based DMA)
 * controller.
 *
 * Paul Mackerras, August 1996.
 * Copyright (C) 1996 Paul Mackerras.
 *
 * Apr. 21 2002  - BenH         Rework bus reset code for new error handler
 *                              Add delay after initial bus reset
 *                              Add module parameters
 *
 * Sep. 27 2003  - BenH         Move to new driver model, fix some write posting
 *                              issues
 * To do:
 * - handle aborts correctly
 * - retry arbitration if lost (unless higher levels do this for us)
 * - power down the chip when no device is detected
 */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/blkdev.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
#include <linux/interrupt.h>
#include <linux/reboot.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <linux/pgtable.h>
#include <asm/dbdma.h>
#include <asm/io.h>
#include <asm/prom.h>
#include <asm/irq.h>
#include <asm/hydra.h>
#include <asm/processor.h>
#include <asm/setup.h>
#include <asm/pmac_feature.h>
#include <asm/macio.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>

#include "mesh.h"

#if 1
#undef KERN_DEBUG
#define KERN_DEBUG KERN_WARNING
#endif

MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
MODULE_DESCRIPTION("PowerMac MESH SCSI driver");
MODULE_LICENSE("GPL");

static int sync_rate = CONFIG_SCSI_MESH_SYNC_RATE;
static int sync_targets = 0xff;
static int resel_targets = 0xff;
static int debug_targets = 0;   /* print debug for these targets */
static int init_reset_delay = CONFIG_SCSI_MESH_RESET_DELAY_MS;

module_param(sync_rate, int, 0);
MODULE_PARM_DESC(sync_rate, "Synchronous rate (0..10, 0=async)");
module_param(sync_targets, int, 0);
MODULE_PARM_DESC(sync_targets, "Bitmask of targets allowed to set synchronous");
module_param(resel_targets, int, 0);
MODULE_PARM_DESC(resel_targets, "Bitmask of targets allowed to set disconnect");
module_param(debug_targets, int, 0644);
MODULE_PARM_DESC(debug_targets, "Bitmask of debugged targets");
module_param(init_reset_delay, int, 0);
MODULE_PARM_DESC(init_reset_delay, "Initial bus reset delay (0=no reset)");

static int mesh_sync_period = 100;
static int mesh_sync_offset = 0;
static unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */

#define ALLOW_SYNC(tgt)         ((sync_targets >> (tgt)) & 1)
#define ALLOW_RESEL(tgt)        ((resel_targets >> (tgt)) & 1)
#define ALLOW_DEBUG(tgt)        ((debug_targets >> (tgt)) & 1)
#define DEBUG_TARGET(cmd)       ((cmd) && ALLOW_DEBUG((cmd)->device->id))

#undef MESH_DBG
#define N_DBG_LOG       50
#define N_DBG_SLOG      20
#define NUM_DBG_EVENTS  13
#undef  DBG_USE_TB              /* bombs on 601 */

struct dbglog {
        char    *fmt;
        u32     tb;
        u8      phase;
        u8      bs0;
        u8      bs1;
        u8      tgt;
        int     d;
};

enum mesh_phase {
        idle,
        arbitrating,
        selecting,
        commanding,
        dataing,
        statusing,
        busfreeing,
        disconnecting,
        reselecting,
        sleeping
};

enum msg_phase {
        msg_none,
        msg_out,
        msg_out_xxx,
        msg_out_last,
        msg_in,
        msg_in_bad,
};

enum sdtr_phase {
        do_sdtr,
        sdtr_sent,
        sdtr_done
};

struct mesh_target {
        enum sdtr_phase sdtr_state;
        int     sync_params;
        int     data_goes_out;          /* guess as to data direction */
        struct scsi_cmnd *current_req;
        u32     saved_ptr;
#ifdef MESH_DBG
        int     log_ix;
        int     n_log;
        struct dbglog log[N_DBG_LOG];
#endif
};

struct mesh_state {
        volatile struct mesh_regs __iomem *mesh;
        int     meshintr;
        volatile struct dbdma_regs __iomem *dma;
        int     dmaintr;
        struct  Scsi_Host *host;
        struct  mesh_state *next;
        struct scsi_cmnd *request_q;
        struct scsi_cmnd *request_qtail;
        enum mesh_phase phase;          /* what we're currently trying to do */
        enum msg_phase msgphase;
        int     conn_tgt;               /* target we're connected to */
        struct scsi_cmnd *current_req;          /* req we're currently working on */
        int     data_ptr;
        int     dma_started;
        int     dma_count;
        int     stat;
        int     aborting;
        int     expect_reply;
        int     n_msgin;
        u8      msgin[16];
        int     n_msgout;
        int     last_n_msgout;
        u8      msgout[16];
        struct dbdma_cmd *dma_cmds;     /* space for dbdma commands, aligned */
        dma_addr_t dma_cmd_bus;
        void    *dma_cmd_space;
        int     dma_cmd_size;
        int     clk_freq;
        struct mesh_target tgts[8];
        struct macio_dev *mdev;
        struct pci_dev* pdev;
#ifdef MESH_DBG
        int     log_ix;
        int     n_log;
        struct dbglog log[N_DBG_SLOG];
#endif
};

/*
 * Driver is too messy, we need a few prototypes...
 */
static void mesh_done(struct mesh_state *ms, int start_next);
static void mesh_interrupt(struct mesh_state *ms);
static void cmd_complete(struct mesh_state *ms);
static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd);
static void halt_dma(struct mesh_state *ms);
static void phase_mismatch(struct mesh_state *ms);


/*
 * Some debugging & logging routines
 */

#ifdef MESH_DBG

static inline u32 readtb(void)
{
        u32 tb;

#ifdef DBG_USE_TB
        /* Beware: if you enable this, it will crash on 601s. */
        asm ("mftb %0" : "=r" (tb) : );
#else
        tb = 0;
#endif
        return tb;
}

static void dlog(struct mesh_state *ms, char *fmt, int a)
{
        struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
        struct dbglog *tlp, *slp;

        tlp = &tp->log[tp->log_ix];
        slp = &ms->log[ms->log_ix];
        tlp->fmt = fmt;
        tlp->tb = readtb();
        tlp->phase = (ms->msgphase << 4) + ms->phase;
        tlp->bs0 = ms->mesh->bus_status0;
        tlp->bs1 = ms->mesh->bus_status1;
        tlp->tgt = ms->conn_tgt;
        tlp->d = a;
        *slp = *tlp;
        if (++tp->log_ix >= N_DBG_LOG)
                tp->log_ix = 0;
        if (tp->n_log < N_DBG_LOG)
                ++tp->n_log;
        if (++ms->log_ix >= N_DBG_SLOG)
                ms->log_ix = 0;
        if (ms->n_log < N_DBG_SLOG)
                ++ms->n_log;
}

static void dumplog(struct mesh_state *ms, int t)
{
        struct mesh_target *tp = &ms->tgts[t];
        struct dbglog *lp;
        int i;

        if (tp->n_log == 0)
                return;
        i = tp->log_ix - tp->n_log;
        if (i < 0)
                i += N_DBG_LOG;
        tp->n_log = 0;
        do {
                lp = &tp->log[i];
                printk(KERN_DEBUG "mesh log %d: bs=%.2x%.2x ph=%.2x ",
                       t, lp->bs1, lp->bs0, lp->phase);
#ifdef DBG_USE_TB
                printk("tb=%10u ", lp->tb);
#endif
                printk(lp->fmt, lp->d);
                printk("\n");
                if (++i >= N_DBG_LOG)
                        i = 0;
        } while (i != tp->log_ix);
}

static void dumpslog(struct mesh_state *ms)
{
        struct dbglog *lp;
        int i;

        if (ms->n_log == 0)
                return;
        i = ms->log_ix - ms->n_log;
        if (i < 0)
                i += N_DBG_SLOG;
        ms->n_log = 0;
        do {
                lp = &ms->log[i];
                printk(KERN_DEBUG "mesh log: bs=%.2x%.2x ph=%.2x t%d ",
                       lp->bs1, lp->bs0, lp->phase, lp->tgt);
#ifdef DBG_USE_TB
                printk("tb=%10u ", lp->tb);
#endif
                printk(lp->fmt, lp->d);
                printk("\n");
                if (++i >= N_DBG_SLOG)
                        i = 0;
        } while (i != ms->log_ix);
}

#else

static inline void dlog(struct mesh_state *ms, char *fmt, int a)
{}
static inline void dumplog(struct mesh_state *ms, int tgt)
{}
static inline void dumpslog(struct mesh_state *ms)
{}

#endif /* MESH_DBG */

#define MKWORD(a, b, c, d)      (((a) << 24) + ((b) << 16) + ((c) << 8) + (d))

static void
mesh_dump_regs(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        volatile struct dbdma_regs __iomem *md = ms->dma;
        int t;
        struct mesh_target *tp;

        printk(KERN_DEBUG "mesh: state at %p, regs at %p, dma at %p\n",
               ms, mr, md);
        printk(KERN_DEBUG "    ct=%4x seq=%2x bs=%4x fc=%2x "
               "exc=%2x err=%2x im=%2x int=%2x sp=%2x\n",
               (mr->count_hi << 8) + mr->count_lo, mr->sequence,
               (mr->bus_status1 << 8) + mr->bus_status0, mr->fifo_count,
               mr->exception, mr->error, mr->intr_mask, mr->interrupt,
               mr->sync_params);
        while(in_8(&mr->fifo_count))
                printk(KERN_DEBUG " fifo data=%.2x\n",in_8(&mr->fifo));
        printk(KERN_DEBUG "    dma stat=%x cmdptr=%x\n",
               in_le32(&md->status), in_le32(&md->cmdptr));
        printk(KERN_DEBUG "    phase=%d msgphase=%d conn_tgt=%d data_ptr=%d\n",
               ms->phase, ms->msgphase, ms->conn_tgt, ms->data_ptr);
        printk(KERN_DEBUG "    dma_st=%d dma_ct=%d n_msgout=%d\n",
               ms->dma_started, ms->dma_count, ms->n_msgout);
        for (t = 0; t < 8; ++t) {
                tp = &ms->tgts[t];
                if (tp->current_req == NULL)
                        continue;
                printk(KERN_DEBUG "    target %d: req=%p goes_out=%d saved_ptr=%d\n",
                       t, tp->current_req, tp->data_goes_out, tp->saved_ptr);
        }
}


/*
 * Flush write buffers on the bus path to the mesh
 */
static inline void mesh_flush_io(volatile struct mesh_regs __iomem *mr)
{
        (void)in_8(&mr->mesh_id);
}


/* Called with  meshinterrupt disabled, initialize the chipset
 * and eventually do the initial bus reset. The lock must not be
 * held since we can schedule.
 */
static void mesh_init(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        volatile struct dbdma_regs __iomem *md = ms->dma;

        mesh_flush_io(mr);
        udelay(100);

        /* Reset controller */
        out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);   /* stop dma */
        out_8(&mr->exception, 0xff);    /* clear all exception bits */
        out_8(&mr->error, 0xff);        /* clear all error bits */
        out_8(&mr->sequence, SEQ_RESETMESH);
        mesh_flush_io(mr);
        udelay(10);
        out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        out_8(&mr->source_id, ms->host->this_id);
        out_8(&mr->sel_timeout, 25);    /* 250ms */
        out_8(&mr->sync_params, ASYNC_PARAMS);

        if (init_reset_delay) {
                printk(KERN_INFO "mesh: performing initial bus reset...\n");
                
                /* Reset bus */
                out_8(&mr->bus_status1, BS1_RST);       /* assert RST */
                mesh_flush_io(mr);
                udelay(30);                     /* leave it on for >= 25us */
                out_8(&mr->bus_status1, 0);     /* negate RST */
                mesh_flush_io(mr);

                /* Wait for bus to come back */
                msleep(init_reset_delay);
        }
        
        /* Reconfigure controller */
        out_8(&mr->interrupt, 0xff);    /* clear all interrupt bits */
        out_8(&mr->sequence, SEQ_FLUSHFIFO);
        mesh_flush_io(mr);
        udelay(1);
        out_8(&mr->sync_params, ASYNC_PARAMS);
        out_8(&mr->sequence, SEQ_ENBRESEL);

        ms->phase = idle;
        ms->msgphase = msg_none;
}


static void mesh_start_cmd(struct mesh_state *ms, struct scsi_cmnd *cmd)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        int t, id;

        id = cmd->device->id;
        ms->current_req = cmd;
        ms->tgts[id].data_goes_out = cmd->sc_data_direction == DMA_TO_DEVICE;
        ms->tgts[id].current_req = cmd;

#if 1
        if (DEBUG_TARGET(cmd)) {
                int i;
                printk(KERN_DEBUG "mesh_start: %p tgt=%d cmd=", cmd, id);
                for (i = 0; i < cmd->cmd_len; ++i)
                        printk(" %x", cmd->cmnd[i]);
                printk(" use_sg=%d buffer=%p bufflen=%u\n",
                       scsi_sg_count(cmd), scsi_sglist(cmd), scsi_bufflen(cmd));
        }
#endif
        if (ms->dma_started)
                panic("mesh: double DMA start !\n");

        ms->phase = arbitrating;
        ms->msgphase = msg_none;
        ms->data_ptr = 0;
        ms->dma_started = 0;
        ms->n_msgout = 0;
        ms->last_n_msgout = 0;
        ms->expect_reply = 0;
        ms->conn_tgt = id;
        ms->tgts[id].saved_ptr = 0;
        ms->stat = DID_OK;
        ms->aborting = 0;
#ifdef MESH_DBG
        ms->tgts[id].n_log = 0;
        dlog(ms, "start cmd=%x", (int) cmd);
#endif

        /* Off we go */
        dlog(ms, "about to arb, intr/exc/err/fc=%.8x",
             MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
        out_8(&mr->interrupt, INT_CMDDONE);
        out_8(&mr->sequence, SEQ_ENBRESEL);
        mesh_flush_io(mr);
        udelay(1);

        if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
                /*
                 * Some other device has the bus or is arbitrating for it -
                 * probably a target which is about to reselect us.
                 */
                dlog(ms, "busy b4 arb, intr/exc/err/fc=%.8x",
                     MKWORD(mr->interrupt, mr->exception,
                            mr->error, mr->fifo_count));
                for (t = 100; t > 0; --t) {
                        if ((in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) == 0)
                                break;
                        if (in_8(&mr->interrupt) != 0) {
                                dlog(ms, "intr b4 arb, intr/exc/err/fc=%.8x",
                                     MKWORD(mr->interrupt, mr->exception,
                                            mr->error, mr->fifo_count));
                                mesh_interrupt(ms);
                                if (ms->phase != arbitrating)
                                        return;
                        }
                        udelay(1);
                }
                if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
                        /* XXX should try again in a little while */
                        ms->stat = DID_BUS_BUSY;
                        ms->phase = idle;
                        mesh_done(ms, 0);
                        return;
                }
        }

        /*
         * Apparently the mesh has a bug where it will assert both its
         * own bit and the target's bit on the bus during arbitration.
         */
        out_8(&mr->dest_id, mr->source_id);

        /*
         * There appears to be a race with reselection sometimes,
         * where a target reselects us just as we issue the
         * arbitrate command.  It seems that then the arbitrate
         * command just hangs waiting for the bus to be free
         * without giving us a reselection exception.
         * The only way I have found to get it to respond correctly
         * is this: disable reselection before issuing the arbitrate
         * command, then after issuing it, if it looks like a target
         * is trying to reselect us, reset the mesh and then enable
         * reselection.
         */
        out_8(&mr->sequence, SEQ_DISRESEL);
        if (in_8(&mr->interrupt) != 0) {
                dlog(ms, "intr after disresel, intr/exc/err/fc=%.8x",
                     MKWORD(mr->interrupt, mr->exception,
                            mr->error, mr->fifo_count));
                mesh_interrupt(ms);
                if (ms->phase != arbitrating)
                        return;
                dlog(ms, "after intr after disresel, intr/exc/err/fc=%.8x",
                     MKWORD(mr->interrupt, mr->exception,
                            mr->error, mr->fifo_count));
        }

        out_8(&mr->sequence, SEQ_ARBITRATE);

        for (t = 230; t > 0; --t) {
                if (in_8(&mr->interrupt) != 0)
                        break;
                udelay(1);
        }
        dlog(ms, "after arb, intr/exc/err/fc=%.8x",
             MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
        if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
            && (in_8(&mr->bus_status0) & BS0_IO)) {
                /* looks like a reselection - try resetting the mesh */
                dlog(ms, "resel? after arb, intr/exc/err/fc=%.8x",
                     MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
                out_8(&mr->sequence, SEQ_RESETMESH);
                mesh_flush_io(mr);
                udelay(10);
                out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
                out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
                out_8(&mr->sequence, SEQ_ENBRESEL);
                mesh_flush_io(mr);
                for (t = 10; t > 0 && in_8(&mr->interrupt) == 0; --t)
                        udelay(1);
                dlog(ms, "tried reset after arb, intr/exc/err/fc=%.8x",
                     MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
#ifndef MESH_MULTIPLE_HOSTS
                if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
                    && (in_8(&mr->bus_status0) & BS0_IO)) {
                        printk(KERN_ERR "mesh: controller not responding"
                               " to reselection!\n");
                        /*
                         * If this is a target reselecting us, and the
                         * mesh isn't responding, the higher levels of
                         * the scsi code will eventually time out and
                         * reset the bus.
                         */
                }
#endif
        }
}

/*
 * Start the next command for a MESH.
 * Should be called with interrupts disabled.
 */
static void mesh_start(struct mesh_state *ms)
{
        struct scsi_cmnd *cmd, *prev, *next;

        if (ms->phase != idle || ms->current_req != NULL) {
                printk(KERN_ERR "inappropriate mesh_start (phase=%d, ms=%p)",
                       ms->phase, ms);
                return;
        }

        while (ms->phase == idle) {
                prev = NULL;
                for (cmd = ms->request_q; ; cmd = (struct scsi_cmnd *) cmd->host_scribble) {
                        if (cmd == NULL)
                                return;
                        if (ms->tgts[cmd->device->id].current_req == NULL)
                                break;
                        prev = cmd;
                }
                next = (struct scsi_cmnd *) cmd->host_scribble;
                if (prev == NULL)
                        ms->request_q = next;
                else
                        prev->host_scribble = (void *) next;
                if (next == NULL)
                        ms->request_qtail = prev;

                mesh_start_cmd(ms, cmd);
        }
}

static void mesh_done(struct mesh_state *ms, int start_next)
{
        struct scsi_cmnd *cmd;
        struct mesh_target *tp = &ms->tgts[ms->conn_tgt];

        cmd = ms->current_req;
        ms->current_req = NULL;
        tp->current_req = NULL;
        if (cmd) {
                struct mesh_cmd_priv *mcmd = mesh_priv(cmd);

                set_host_byte(cmd, ms->stat);
                set_status_byte(cmd, mcmd->status);
                if (ms->stat == DID_OK)
                        scsi_msg_to_host_byte(cmd, mcmd->message);
                if (DEBUG_TARGET(cmd)) {
                        printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
                               cmd->result, ms->data_ptr, scsi_bufflen(cmd));
#if 0
                        /* needs to use sg? */
                        if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 3)
                            && cmd->request_buffer != 0) {
                                unsigned char *b = cmd->request_buffer;
                                printk(KERN_DEBUG "buffer = %x %x %x %x %x %x %x %x\n",
                                       b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
                        }
#endif
                }
                mcmd->this_residual -= ms->data_ptr;
                scsi_done(cmd);
        }
        if (start_next) {
                out_8(&ms->mesh->sequence, SEQ_ENBRESEL);
                mesh_flush_io(ms->mesh);
                udelay(1);
                ms->phase = idle;
                mesh_start(ms);
        }
}

static inline void add_sdtr_msg(struct mesh_state *ms)
{
        int i = ms->n_msgout;

        ms->msgout[i] = EXTENDED_MESSAGE;
        ms->msgout[i+1] = 3;
        ms->msgout[i+2] = EXTENDED_SDTR;
        ms->msgout[i+3] = mesh_sync_period/4;
        ms->msgout[i+4] = (ALLOW_SYNC(ms->conn_tgt)? mesh_sync_offset: 0);
        ms->n_msgout = i + 5;
}

static void set_sdtr(struct mesh_state *ms, int period, int offset)
{
        struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        int v, tr;

        tp->sdtr_state = sdtr_done;
        if (offset == 0) {
                /* asynchronous */
                if (SYNC_OFF(tp->sync_params))
                        printk(KERN_INFO "mesh: target %d now asynchronous\n",
                               ms->conn_tgt);
                tp->sync_params = ASYNC_PARAMS;
                out_8(&mr->sync_params, ASYNC_PARAMS);
                return;
        }
        /*
         * We need to compute ceil(clk_freq * period / 500e6) - 2
         * without incurring overflow.
         */
        v = (ms->clk_freq / 5000) * period;
        if (v <= 250000) {
                /* special case: sync_period == 5 * clk_period */
                v = 0;
                /* units of tr are 100kB/s */
                tr = (ms->clk_freq + 250000) / 500000;
        } else {
                /* sync_period == (v + 2) * 2 * clk_period */
                v = (v + 99999) / 100000 - 2;
                if (v > 15)
                        v = 15; /* oops */
                tr = ((ms->clk_freq / (v + 2)) + 199999) / 200000;
        }
        if (offset > 15)
                offset = 15;    /* can't happen */
        tp->sync_params = SYNC_PARAMS(offset, v);
        out_8(&mr->sync_params, tp->sync_params);
        printk(KERN_INFO "mesh: target %d synchronous at %d.%d MB/s\n",
               ms->conn_tgt, tr/10, tr%10);
}

static void start_phase(struct mesh_state *ms)
{
        int i, seq, nb;
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        volatile struct dbdma_regs __iomem *md = ms->dma;
        struct scsi_cmnd *cmd = ms->current_req;
        struct mesh_target *tp = &ms->tgts[ms->conn_tgt];

        dlog(ms, "start_phase nmo/exc/fc/seq = %.8x",
             MKWORD(ms->n_msgout, mr->exception, mr->fifo_count, mr->sequence));
        out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
        switch (ms->msgphase) {
        case msg_none:
                break;

        case msg_in:
                out_8(&mr->count_hi, 0);
                out_8(&mr->count_lo, 1);
                out_8(&mr->sequence, SEQ_MSGIN + seq);
                ms->n_msgin = 0;
                return;

        case msg_out:
                /*
                 * To make sure ATN drops before we assert ACK for
                 * the last byte of the message, we have to do the
                 * last byte specially.
                 */
                if (ms->n_msgout <= 0) {
                        printk(KERN_ERR "mesh: msg_out but n_msgout=%d\n",
                               ms->n_msgout);
                        mesh_dump_regs(ms);
                        ms->msgphase = msg_none;
                        break;
                }
                if (ALLOW_DEBUG(ms->conn_tgt)) {
                        printk(KERN_DEBUG "mesh: sending %d msg bytes:",
                               ms->n_msgout);
                        for (i = 0; i < ms->n_msgout; ++i)
                                printk(" %x", ms->msgout[i]);
                        printk("\n");
                }
                dlog(ms, "msgout msg=%.8x", MKWORD(ms->n_msgout, ms->msgout[0],
                                                ms->msgout[1], ms->msgout[2]));
                out_8(&mr->count_hi, 0);
                out_8(&mr->sequence, SEQ_FLUSHFIFO);
                mesh_flush_io(mr);
                udelay(1);
                /*
                 * If ATN is not already asserted, we assert it, then
                 * issue a SEQ_MSGOUT to get the mesh to drop ACK.
                 */
                if ((in_8(&mr->bus_status0) & BS0_ATN) == 0) {
                        dlog(ms, "bus0 was %.2x explicitly asserting ATN", mr->bus_status0);
                        out_8(&mr->bus_status0, BS0_ATN); /* explicit ATN */
                        mesh_flush_io(mr);
                        udelay(1);
                        out_8(&mr->count_lo, 1);
                        out_8(&mr->sequence, SEQ_MSGOUT + seq);
                        out_8(&mr->bus_status0, 0); /* release explicit ATN */
                        dlog(ms,"hace: after explicit ATN bus0=%.2x",mr->bus_status0);
                }
                if (ms->n_msgout == 1) {
                        /*
                         * We can't issue the SEQ_MSGOUT without ATN
                         * until the target has asserted REQ.  The logic
                         * in cmd_complete handles both situations:
                         * REQ already asserted or not.
                         */
                        cmd_complete(ms);
                } else {
                        out_8(&mr->count_lo, ms->n_msgout - 1);
                        out_8(&mr->sequence, SEQ_MSGOUT + seq);
                        for (i = 0; i < ms->n_msgout - 1; ++i)
                                out_8(&mr->fifo, ms->msgout[i]);
                }
                return;

        default:
                printk(KERN_ERR "mesh bug: start_phase msgphase=%d\n",
                       ms->msgphase);
        }

        switch (ms->phase) {
        case selecting:
                out_8(&mr->dest_id, ms->conn_tgt);
                out_8(&mr->sequence, SEQ_SELECT + SEQ_ATN);
                break;
        case commanding:
                out_8(&mr->sync_params, tp->sync_params);
                out_8(&mr->count_hi, 0);
                if (cmd) {
                        out_8(&mr->count_lo, cmd->cmd_len);
                        out_8(&mr->sequence, SEQ_COMMAND + seq);
                        for (i = 0; i < cmd->cmd_len; ++i)
                                out_8(&mr->fifo, cmd->cmnd[i]);
                } else {
                        out_8(&mr->count_lo, 6);
                        out_8(&mr->sequence, SEQ_COMMAND + seq);
                        for (i = 0; i < 6; ++i)
                                out_8(&mr->fifo, 0);
                }
                break;
        case dataing:
                /* transfer data, if any */
                if (!ms->dma_started) {
                        set_dma_cmds(ms, cmd);
                        out_le32(&md->cmdptr, virt_to_phys(ms->dma_cmds));
                        out_le32(&md->control, (RUN << 16) | RUN);
                        ms->dma_started = 1;
                }
                nb = ms->dma_count;
                if (nb > 0xfff0)
                        nb = 0xfff0;
                ms->dma_count -= nb;
                ms->data_ptr += nb;
                out_8(&mr->count_lo, nb);
                out_8(&mr->count_hi, nb >> 8);
                out_8(&mr->sequence, (tp->data_goes_out?
                                SEQ_DATAOUT: SEQ_DATAIN) + SEQ_DMA_MODE + seq);
                break;
        case statusing:
                out_8(&mr->count_hi, 0);
                out_8(&mr->count_lo, 1);
                out_8(&mr->sequence, SEQ_STATUS + seq);
                break;
        case busfreeing:
        case disconnecting:
                out_8(&mr->sequence, SEQ_ENBRESEL);
                mesh_flush_io(mr);
                udelay(1);
                dlog(ms, "enbresel intr/exc/err/fc=%.8x",
                     MKWORD(mr->interrupt, mr->exception, mr->error,
                            mr->fifo_count));
                out_8(&mr->sequence, SEQ_BUSFREE);
                break;
        default:
                printk(KERN_ERR "mesh: start_phase called with phase=%d\n",
                       ms->phase);
                dumpslog(ms);
        }

}

static inline void get_msgin(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        int i, n;

        n = mr->fifo_count;
        if (n != 0) {
                i = ms->n_msgin;
                ms->n_msgin = i + n;
                for (; n > 0; --n)
                        ms->msgin[i++] = in_8(&mr->fifo);
        }
}

static inline int msgin_length(struct mesh_state *ms)
{
        int b, n;

        n = 1;
        if (ms->n_msgin > 0) {
                b = ms->msgin[0];
                if (b == 1) {
                        /* extended message */
                        n = ms->n_msgin < 2? 2: ms->msgin[1] + 2;
                } else if (0x20 <= b && b <= 0x2f) {
                        /* 2-byte message */
                        n = 2;
                }
        }
        return n;
}

static void reselected(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        struct scsi_cmnd *cmd;
        struct mesh_target *tp;
        int b, t, prev;

        switch (ms->phase) {
        case idle:
                break;
        case arbitrating:
                if ((cmd = ms->current_req) != NULL) {
                        /* put the command back on the queue */
                        cmd->host_scribble = (void *) ms->request_q;
                        if (ms->request_q == NULL)
                                ms->request_qtail = cmd;
                        ms->request_q = cmd;
                        tp = &ms->tgts[cmd->device->id];
                        tp->current_req = NULL;
                }
                break;
        case busfreeing:
                ms->phase = reselecting;
                mesh_done(ms, 0);
                break;
        case disconnecting:
                break;
        default:
                printk(KERN_ERR "mesh: reselected in phase %d/%d tgt %d\n",
                       ms->msgphase, ms->phase, ms->conn_tgt);
                dumplog(ms, ms->conn_tgt);
                dumpslog(ms);
        }

        if (ms->dma_started) {
                printk(KERN_ERR "mesh: reselected with DMA started !\n");
                halt_dma(ms);
        }
        ms->current_req = NULL;
        ms->phase = dataing;
        ms->msgphase = msg_in;
        ms->n_msgout = 0;
        ms->last_n_msgout = 0;
        prev = ms->conn_tgt;

        /*
         * We seem to get abortive reselections sometimes.
         */
        while ((in_8(&mr->bus_status1) & BS1_BSY) == 0) {
                static int mesh_aborted_resels;
                mesh_aborted_resels++;
                out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
                mesh_flush_io(mr);
                udelay(1);
                out_8(&mr->sequence, SEQ_ENBRESEL);
                mesh_flush_io(mr);
                udelay(5);
                dlog(ms, "extra resel err/exc/fc = %.6x",
                     MKWORD(0, mr->error, mr->exception, mr->fifo_count));
        }
        out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        mesh_flush_io(mr);
        udelay(1);
        out_8(&mr->sequence, SEQ_ENBRESEL);
        mesh_flush_io(mr);
        udelay(1);
        out_8(&mr->sync_params, ASYNC_PARAMS);

        /*
         * Find out who reselected us.
         */
        if (in_8(&mr->fifo_count) == 0) {
                printk(KERN_ERR "mesh: reselection but nothing in fifo?\n");
                ms->conn_tgt = ms->host->this_id;
                goto bogus;
        }
        /* get the last byte in the fifo */
        do {
                b = in_8(&mr->fifo);
                dlog(ms, "reseldata %x", b);
        } while (in_8(&mr->fifo_count));
        for (t = 0; t < 8; ++t)
                if ((b & (1 << t)) != 0 && t != ms->host->this_id)
                        break;
        if (b != (1 << t) + (1 << ms->host->this_id)) {
                printk(KERN_ERR "mesh: bad reselection data %x\n", b);
                ms->conn_tgt = ms->host->this_id;
                goto bogus;
        }


        /*
         * Set up to continue with that target's transfer.
         */
        ms->conn_tgt = t;
        tp = &ms->tgts[t];
        out_8(&mr->sync_params, tp->sync_params);
        if (ALLOW_DEBUG(t)) {
                printk(KERN_DEBUG "mesh: reselected by target %d\n", t);
                printk(KERN_DEBUG "mesh: saved_ptr=%x goes_out=%d cmd=%p\n",
                       tp->saved_ptr, tp->data_goes_out, tp->current_req);
        }
        ms->current_req = tp->current_req;
        if (tp->current_req == NULL) {
                printk(KERN_ERR "mesh: reselected by tgt %d but no cmd!\n", t);
                goto bogus;
        }
        ms->data_ptr = tp->saved_ptr;
        dlog(ms, "resel prev tgt=%d", prev);
        dlog(ms, "resel err/exc=%.4x", MKWORD(0, 0, mr->error, mr->exception));
        start_phase(ms);
        return;

bogus:
        dumplog(ms, ms->conn_tgt);
        dumpslog(ms);
        ms->data_ptr = 0;
        ms->aborting = 1;
        start_phase(ms);
}

static void do_abort(struct mesh_state *ms)
{
        ms->msgout[0] = ABORT;
        ms->n_msgout = 1;
        ms->aborting = 1;
        ms->stat = DID_ABORT;
        dlog(ms, "abort", 0);
}

static void handle_reset(struct mesh_state *ms)
{
        int tgt;
        struct mesh_target *tp;
        struct scsi_cmnd *cmd;
        volatile struct mesh_regs __iomem *mr = ms->mesh;

        for (tgt = 0; tgt < 8; ++tgt) {
                tp = &ms->tgts[tgt];
                if ((cmd = tp->current_req) != NULL) {
                        set_host_byte(cmd, DID_RESET);
                        tp->current_req = NULL;
                        scsi_done(cmd);
                }
                ms->tgts[tgt].sdtr_state = do_sdtr;
                ms->tgts[tgt].sync_params = ASYNC_PARAMS;
        }
        ms->current_req = NULL;
        while ((cmd = ms->request_q) != NULL) {
                ms->request_q = (struct scsi_cmnd *) cmd->host_scribble;
                set_host_byte(cmd, DID_RESET);
                scsi_done(cmd);
        }
        ms->phase = idle;
        ms->msgphase = msg_none;
        out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        out_8(&mr->sequence, SEQ_FLUSHFIFO);
        mesh_flush_io(mr);
        udelay(1);
        out_8(&mr->sync_params, ASYNC_PARAMS);
        out_8(&mr->sequence, SEQ_ENBRESEL);
}

static irqreturn_t do_mesh_interrupt(int irq, void *dev_id)
{
        unsigned long flags;
        struct mesh_state *ms = dev_id;
        struct Scsi_Host *dev = ms->host;
        
        spin_lock_irqsave(dev->host_lock, flags);
        mesh_interrupt(ms);
        spin_unlock_irqrestore(dev->host_lock, flags);
        return IRQ_HANDLED;
}

static void handle_error(struct mesh_state *ms)
{
        int err, exc, count;
        volatile struct mesh_regs __iomem *mr = ms->mesh;

        err = in_8(&mr->error);
        exc = in_8(&mr->exception);
        out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        dlog(ms, "error err/exc/fc/cl=%.8x",
             MKWORD(err, exc, mr->fifo_count, mr->count_lo));
        if (err & ERR_SCSIRESET) {
                /* SCSI bus was reset */
                printk(KERN_INFO "mesh: SCSI bus reset detected: "
                       "waiting for end...");
                while ((in_8(&mr->bus_status1) & BS1_RST) != 0)
                        udelay(1);
                printk("done\n");
                if (ms->dma_started)
                        halt_dma(ms);
                handle_reset(ms);
                /* request_q is empty, no point in mesh_start() */
                return;
        }
        if (err & ERR_UNEXPDISC) {
                /* Unexpected disconnect */
                if (exc & EXC_RESELECTED) {
                        reselected(ms);
                        return;
                }
                if (!ms->aborting) {
                        printk(KERN_WARNING "mesh: target %d aborted\n",
                               ms->conn_tgt);
                        dumplog(ms, ms->conn_tgt);
                        dumpslog(ms);
                }
                out_8(&mr->interrupt, INT_CMDDONE);
                ms->stat = DID_ABORT;
                mesh_done(ms, 1);
                return;
        }
        if (err & ERR_PARITY) {
                if (ms->msgphase == msg_in) {
                        printk(KERN_ERR "mesh: msg parity error, target %d\n",
                               ms->conn_tgt);
                        ms->msgout[0] = MSG_PARITY_ERROR;
                        ms->n_msgout = 1;
                        ms->msgphase = msg_in_bad;
                        cmd_complete(ms);
                        return;
                }
                if (ms->stat == DID_OK) {
                        printk(KERN_ERR "mesh: parity error, target %d\n",
                               ms->conn_tgt);
                        ms->stat = DID_PARITY;
                }
                count = (mr->count_hi << 8) + mr->count_lo;
                if (count == 0) {
                        cmd_complete(ms);
                } else {
                        /* reissue the data transfer command */
                        out_8(&mr->sequence, mr->sequence);
                }
                return;
        }
        if (err & ERR_SEQERR) {
                if (exc & EXC_RESELECTED) {
                        /* This can happen if we issue a command to
                           get the bus just after the target reselects us. */
                        static int mesh_resel_seqerr;
                        mesh_resel_seqerr++;
                        reselected(ms);
                        return;
                }
                if (exc == EXC_PHASEMM) {
                        static int mesh_phasemm_seqerr;
                        mesh_phasemm_seqerr++;
                        phase_mismatch(ms);
                        return;
                }
                printk(KERN_ERR "mesh: sequence error (err=%x exc=%x)\n",
                       err, exc);
        } else {
                printk(KERN_ERR "mesh: unknown error %x (exc=%x)\n", err, exc);
        }
        mesh_dump_regs(ms);
        dumplog(ms, ms->conn_tgt);
        if (ms->phase > selecting && (in_8(&mr->bus_status1) & BS1_BSY)) {
                /* try to do what the target wants */
                do_abort(ms);
                phase_mismatch(ms);
                return;
        }
        ms->stat = DID_ERROR;
        mesh_done(ms, 1);
}

static void handle_exception(struct mesh_state *ms)
{
        int exc;
        volatile struct mesh_regs __iomem *mr = ms->mesh;

        exc = in_8(&mr->exception);
        out_8(&mr->interrupt, INT_EXCEPTION | INT_CMDDONE);
        if (exc & EXC_RESELECTED) {
                static int mesh_resel_exc;
                mesh_resel_exc++;
                reselected(ms);
        } else if (exc == EXC_ARBLOST) {
                printk(KERN_DEBUG "mesh: lost arbitration\n");
                ms->stat = DID_BUS_BUSY;
                mesh_done(ms, 1);
        } else if (exc == EXC_SELTO) {
                /* selection timed out */
                ms->stat = DID_BAD_TARGET;
                mesh_done(ms, 1);
        } else if (exc == EXC_PHASEMM) {
                /* target wants to do something different:
                   find out what it wants and do it. */
                phase_mismatch(ms);
        } else {
                printk(KERN_ERR "mesh: can't cope with exception %x\n", exc);
                mesh_dump_regs(ms);
                dumplog(ms, ms->conn_tgt);
                do_abort(ms);
                phase_mismatch(ms);
        }
}

static void handle_msgin(struct mesh_state *ms)
{
        int i, code;
        struct scsi_cmnd *cmd = ms->current_req;
        struct mesh_target *tp = &ms->tgts[ms->conn_tgt];

        if (ms->n_msgin == 0)
                return;
        code = ms->msgin[0];
        if (ALLOW_DEBUG(ms->conn_tgt)) {
                printk(KERN_DEBUG "got %d message bytes:", ms->n_msgin);
                for (i = 0; i < ms->n_msgin; ++i)
                        printk(" %x", ms->msgin[i]);
                printk("\n");
        }
        dlog(ms, "msgin msg=%.8x",
             MKWORD(ms->n_msgin, code, ms->msgin[1], ms->msgin[2]));

        ms->expect_reply = 0;
        ms->n_msgout = 0;
        if (ms->n_msgin < msgin_length(ms))
                goto reject;
        if (cmd)
                mesh_priv(cmd)->message = code;
        switch (code) {
        case COMMAND_COMPLETE:
                break;
        case EXTENDED_MESSAGE:
                switch (ms->msgin[2]) {
                case EXTENDED_MODIFY_DATA_POINTER:
                        ms->data_ptr += (ms->msgin[3] << 24) + ms->msgin[6]
                                + (ms->msgin[4] << 16) + (ms->msgin[5] << 8);
                        break;
                case EXTENDED_SDTR:
                        if (tp->sdtr_state != sdtr_sent) {
                                /* reply with an SDTR */
                                add_sdtr_msg(ms);
                                /* limit period to at least his value,
                                   offset to no more than his */
                                if (ms->msgout[3] < ms->msgin[3])
                                        ms->msgout[3] = ms->msgin[3];
                                if (ms->msgout[4] > ms->msgin[4])
                                        ms->msgout[4] = ms->msgin[4];
                                set_sdtr(ms, ms->msgout[3], ms->msgout[4]);
                                ms->msgphase = msg_out;
                        } else {
                                set_sdtr(ms, ms->msgin[3], ms->msgin[4]);
                        }
                        break;
                default:
                        goto reject;
                }
                break;
        case SAVE_POINTERS:
                tp->saved_ptr = ms->data_ptr;
                break;
        case RESTORE_POINTERS:
                ms->data_ptr = tp->saved_ptr;
                break;
        case DISCONNECT:
                ms->phase = disconnecting;
                break;
        case ABORT:
                break;
        case MESSAGE_REJECT:
                if (tp->sdtr_state == sdtr_sent)
                        set_sdtr(ms, 0, 0);
                break;
        case NOP:
                break;
        default:
                if (IDENTIFY_BASE <= code && code <= IDENTIFY_BASE + 7) {
                        if (cmd == NULL) {
                                do_abort(ms);
                                ms->msgphase = msg_out;
                        } else if (code != cmd->device->lun + IDENTIFY_BASE) {
                                printk(KERN_WARNING "mesh: lun mismatch "
                                       "(%d != %llu) on reselection from "
                                       "target %d\n", code - IDENTIFY_BASE,
                                       cmd->device->lun, ms->conn_tgt);
                        }
                        break;
                }
                goto reject;
        }
        return;

 reject:
        printk(KERN_WARNING "mesh: rejecting message from target %d:",
               ms->conn_tgt);
        for (i = 0; i < ms->n_msgin; ++i)
                printk(" %x", ms->msgin[i]);
        printk("\n");
        ms->msgout[0] = MESSAGE_REJECT;
        ms->n_msgout = 1;
        ms->msgphase = msg_out;
}

/*
 * Set up DMA commands for transferring data.
 */
static void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd)
{
        int i, dma_cmd, total, off, dtot;
        struct scatterlist *scl;
        struct dbdma_cmd *dcmds;

        dma_cmd = ms->tgts[ms->conn_tgt].data_goes_out?
                OUTPUT_MORE: INPUT_MORE;
        dcmds = ms->dma_cmds;
        dtot = 0;
        if (cmd) {
                int nseg;

                mesh_priv(cmd)->this_residual = scsi_bufflen(cmd);

                nseg = scsi_dma_map(cmd);
                BUG_ON(nseg < 0);

                if (nseg) {
                        total = 0;
                        off = ms->data_ptr;

                        scsi_for_each_sg(cmd, scl, nseg, i) {
                                u32 dma_addr = sg_dma_address(scl);
                                u32 dma_len = sg_dma_len(scl);
                                
                                total += scl->length;
                                if (off >= dma_len) {
                                        off -= dma_len;
                                        continue;
                                }
                                if (dma_len > 0xffff)
                                        panic("mesh: scatterlist element >= 64k");
                                dcmds->req_count = cpu_to_le16(dma_len - off);
                                dcmds->command = cpu_to_le16(dma_cmd);
                                dcmds->phy_addr = cpu_to_le32(dma_addr + off);
                                dcmds->xfer_status = 0;
                                ++dcmds;
                                dtot += dma_len - off;
                                off = 0;
                        }
                }
        }
        if (dtot == 0) {
                /* Either the target has overrun our buffer,
                   or the caller didn't provide a buffer. */
                static char mesh_extra_buf[64];

                dtot = sizeof(mesh_extra_buf);
                dcmds->req_count = cpu_to_le16(dtot);
                dcmds->phy_addr = cpu_to_le32(virt_to_phys(mesh_extra_buf));
                dcmds->xfer_status = 0;
                ++dcmds;
        }
        dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
        dcmds[-1].command = cpu_to_le16(dma_cmd);
        memset(dcmds, 0, sizeof(*dcmds));
        dcmds->command = cpu_to_le16(DBDMA_STOP);
        ms->dma_count = dtot;
}

static void halt_dma(struct mesh_state *ms)
{
        volatile struct dbdma_regs __iomem *md = ms->dma;
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        struct scsi_cmnd *cmd = ms->current_req;
        int t, nb;

        if (!ms->tgts[ms->conn_tgt].data_goes_out) {
                /* wait a little while until the fifo drains */
                t = 50;
                while (t > 0 && in_8(&mr->fifo_count) != 0
                       && (in_le32(&md->status) & ACTIVE) != 0) {
                        --t;
                        udelay(1);
                }
        }
        out_le32(&md->control, RUN << 16);      /* turn off RUN bit */
        nb = (mr->count_hi << 8) + mr->count_lo;
        dlog(ms, "halt_dma fc/count=%.6x",
             MKWORD(0, mr->fifo_count, 0, nb));
        if (ms->tgts[ms->conn_tgt].data_goes_out)
                nb += mr->fifo_count;
        /* nb is the number of bytes not yet transferred
           to/from the target. */
        ms->data_ptr -= nb;
        dlog(ms, "data_ptr %x", ms->data_ptr);
        if (ms->data_ptr < 0) {
                printk(KERN_ERR "mesh: halt_dma: data_ptr=%d (nb=%d, ms=%p)\n",
                       ms->data_ptr, nb, ms);
                ms->data_ptr = 0;
#ifdef MESH_DBG
                dumplog(ms, ms->conn_tgt);
                dumpslog(ms);
#endif /* MESH_DBG */
        } else if (cmd && scsi_bufflen(cmd) &&
                   ms->data_ptr > scsi_bufflen(cmd)) {
                printk(KERN_DEBUG "mesh: target %d overrun, "
                       "data_ptr=%x total=%x goes_out=%d\n",
                       ms->conn_tgt, ms->data_ptr, scsi_bufflen(cmd),
                       ms->tgts[ms->conn_tgt].data_goes_out);
        }
        if (cmd)
                scsi_dma_unmap(cmd);
        ms->dma_started = 0;
}

static void phase_mismatch(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        int phase;

        dlog(ms, "phasemm ch/cl/seq/fc=%.8x",
             MKWORD(mr->count_hi, mr->count_lo, mr->sequence, mr->fifo_count));
        phase = in_8(&mr->bus_status0) & BS0_PHASE;
        if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {
                /* output the last byte of the message, without ATN */
                out_8(&mr->count_lo, 1);
                out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
                mesh_flush_io(mr);
                udelay(1);
                out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
                ms->msgphase = msg_out_last;
                return;
        }

        if (ms->msgphase == msg_in) {
                get_msgin(ms);
                if (ms->n_msgin)
                        handle_msgin(ms);
        }

        if (ms->dma_started)
                halt_dma(ms);
        if (mr->fifo_count) {
                out_8(&mr->sequence, SEQ_FLUSHFIFO);
                mesh_flush_io(mr);
                udelay(1);
        }

        ms->msgphase = msg_none;
        switch (phase) {
        case BP_DATAIN:
                ms->tgts[ms->conn_tgt].data_goes_out = 0;
                ms->phase = dataing;
                break;
        case BP_DATAOUT:
                ms->tgts[ms->conn_tgt].data_goes_out = 1;
                ms->phase = dataing;
                break;
        case BP_COMMAND:
                ms->phase = commanding;
                break;
        case BP_STATUS:
                ms->phase = statusing;
                break;
        case BP_MSGIN:
                ms->msgphase = msg_in;
                ms->n_msgin = 0;
                break;
        case BP_MSGOUT:
                ms->msgphase = msg_out;
                if (ms->n_msgout == 0) {
                        if (ms->aborting) {
                                do_abort(ms);
                        } else {
                                if (ms->last_n_msgout == 0) {
                                        printk(KERN_DEBUG
                                               "mesh: no msg to repeat\n");
                                        ms->msgout[0] = NOP;
                                        ms->last_n_msgout = 1;
                                }
                                ms->n_msgout = ms->last_n_msgout;
                        }
                }
                break;
        default:
                printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);
                ms->stat = DID_ERROR;
                mesh_done(ms, 1);
                return;
        }

        start_phase(ms);
}

static void cmd_complete(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        struct scsi_cmnd *cmd = ms->current_req;
        struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
        int seq, n, t;

        dlog(ms, "cmd_complete fc=%x", mr->fifo_count);
        seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
        switch (ms->msgphase) {
        case msg_out_xxx:
                /* huh?  we expected a phase mismatch */
                ms->n_msgin = 0;
                ms->msgphase = msg_in;
                fallthrough;

        case msg_in:
                /* should have some message bytes in fifo */
                get_msgin(ms);
                n = msgin_length(ms);
                if (ms->n_msgin < n) {
                        out_8(&mr->count_lo, n - ms->n_msgin);
                        out_8(&mr->sequence, SEQ_MSGIN + seq);
                } else {
                        ms->msgphase = msg_none;
                        handle_msgin(ms);
                        start_phase(ms);
                }
                break;

        case msg_in_bad:
                out_8(&mr->sequence, SEQ_FLUSHFIFO);
                mesh_flush_io(mr);
                udelay(1);
                out_8(&mr->count_lo, 1);
                out_8(&mr->sequence, SEQ_MSGIN + SEQ_ATN + use_active_neg);
                break;

        case msg_out:
                /*
                 * To get the right timing on ATN wrt ACK, we have
                 * to get the MESH to drop ACK, wait until REQ gets
                 * asserted, then drop ATN.  To do this we first
                 * issue a SEQ_MSGOUT with ATN and wait for REQ,
                 * then change the command to a SEQ_MSGOUT w/o ATN.
                 * If we don't see REQ in a reasonable time, we
                 * change the command to SEQ_MSGIN with ATN,
                 * wait for the phase mismatch interrupt, then
                 * issue the SEQ_MSGOUT without ATN.
                 */
                out_8(&mr->count_lo, 1);
                out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);
                t = 30;         /* wait up to 30us */
                while ((in_8(&mr->bus_status0) & BS0_REQ) == 0 && --t >= 0)
                        udelay(1);
                dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",
                     MKWORD(mr->error, mr->exception,
                            mr->fifo_count, mr->count_lo));
                if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) {
                        /* whoops, target didn't do what we expected */
                        ms->last_n_msgout = ms->n_msgout;
                        ms->n_msgout = 0;
                        if (in_8(&mr->interrupt) & INT_ERROR) {
                                printk(KERN_ERR "mesh: error %x in msg_out\n",
                                       in_8(&mr->error));
                                handle_error(ms);
                                return;
                        }
                        if (in_8(&mr->exception) != EXC_PHASEMM)
                                printk(KERN_ERR "mesh: exc %x in msg_out\n",
                                       in_8(&mr->exception));
                        else
                                printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",
                                       in_8(&mr->bus_status0));
                        handle_exception(ms);
                        return;
                }
                if (in_8(&mr->bus_status0) & BS0_REQ) {
                        out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
                        mesh_flush_io(mr);
                        udelay(1);
                        out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
                        ms->msgphase = msg_out_last;
                } else {
                        out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);
                        ms->msgphase = msg_out_xxx;
                }
                break;

        case msg_out_last:
                ms->last_n_msgout = ms->n_msgout;
                ms->n_msgout = 0;
                ms->msgphase = ms->expect_reply? msg_in: msg_none;
                start_phase(ms);
                break;

        case msg_none:
                switch (ms->phase) {
                case idle:
                        printk(KERN_ERR "mesh: interrupt in idle phase?\n");
                        dumpslog(ms);
                        return;
                case selecting:
                        dlog(ms, "Selecting phase at command completion",0);
                        ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),
                                                 (cmd? cmd->device->lun: 0));
                        ms->n_msgout = 1;
                        ms->expect_reply = 0;
                        if (ms->aborting) {
                                ms->msgout[0] = ABORT;
                                ms->n_msgout++;
                        } else if (tp->sdtr_state == do_sdtr) {
                                /* add SDTR message */
                                add_sdtr_msg(ms);
                                ms->expect_reply = 1;
                                tp->sdtr_state = sdtr_sent;
                        }
                        ms->msgphase = msg_out;
                        /*
                         * We need to wait for REQ before dropping ATN.
                         * We wait for at most 30us, then fall back to
                         * a scheme where we issue a SEQ_COMMAND with ATN,
                         * which will give us a phase mismatch interrupt
                         * when REQ does come, and then we send the message.
                         */
                        t = 230;                /* wait up to 230us */
                        while ((in_8(&mr->bus_status0) & BS0_REQ) == 0) {
                                if (--t < 0) {
                                        dlog(ms, "impatient for req", ms->n_msgout);
                                        ms->msgphase = msg_none;
                                        break;
                                }
                                udelay(1);
                        }
                        break;
                case dataing:
                        if (ms->dma_count != 0) {
                                start_phase(ms);
                                return;
                        }
                        /*
                         * We can get a phase mismatch here if the target
                         * changes to the status phase, even though we have
                         * had a command complete interrupt.  Then, if we
                         * issue the SEQ_STATUS command, we'll get a sequence
                         * error interrupt.  Which isn't so bad except that
                         * occasionally the mesh actually executes the
                         * SEQ_STATUS *as well as* giving us the sequence
                         * error and phase mismatch exception.
                         */
                        out_8(&mr->sequence, 0);
                        out_8(&mr->interrupt,
                              INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
                        halt_dma(ms);
                        break;
                case statusing:
                        if (cmd) {
                                struct mesh_cmd_priv *mcmd = mesh_priv(cmd);

                                mcmd->status = mr->fifo;
                                if (DEBUG_TARGET(cmd))
                                        printk(KERN_DEBUG "mesh: status is %x\n",
                                               mcmd->status);
                        }
                        ms->msgphase = msg_in;
                        break;
                case busfreeing:
                        mesh_done(ms, 1);
                        return;
                case disconnecting:
                        ms->current_req = NULL;
                        ms->phase = idle;
                        mesh_start(ms);
                        return;
                default:
                        break;
                }
                ++ms->phase;
                start_phase(ms);
                break;
        }
}


/*
 * Called by midlayer with host locked to queue a new
 * request
 */
static enum scsi_qc_status mesh_queue_lck(struct scsi_cmnd *cmd)
{
        struct mesh_state *ms;

        cmd->host_scribble = NULL;

        ms = (struct mesh_state *) cmd->device->host->hostdata;

        if (ms->request_q == NULL)
                ms->request_q = cmd;
        else
                ms->request_qtail->host_scribble = (void *) cmd;
        ms->request_qtail = cmd;

        if (ms->phase == idle)
                mesh_start(ms);

        return 0;
}

static DEF_SCSI_QCMD(mesh_queue)

/*
 * Called to handle interrupts, either call by the interrupt
 * handler (do_mesh_interrupt) or by other functions in
 * exceptional circumstances
 */
static void mesh_interrupt(struct mesh_state *ms)
{
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        int intr;

#if 0
        if (ALLOW_DEBUG(ms->conn_tgt))
                printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x "
                       "phase=%d msgphase=%d\n", mr->bus_status0,
                       mr->interrupt, mr->exception, mr->error,
                       ms->phase, ms->msgphase);
#endif
        while ((intr = in_8(&mr->interrupt)) != 0) {
                dlog(ms, "interrupt intr/err/exc/seq=%.8x", 
                     MKWORD(intr, mr->error, mr->exception, mr->sequence));
                if (intr & INT_ERROR) {
                        handle_error(ms);
                } else if (intr & INT_EXCEPTION) {
                        handle_exception(ms);
                } else if (intr & INT_CMDDONE) {
                        out_8(&mr->interrupt, INT_CMDDONE);
                        cmd_complete(ms);
                }
        }
}

/* Todo: here we can at least try to remove the command from the
 * queue if it isn't connected yet, and for pending command, assert
 * ATN until the bus gets freed.
 */
static int mesh_abort(struct scsi_cmnd *cmd)
{
        struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;

        printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
        mesh_dump_regs(ms);
        dumplog(ms, cmd->device->id);
        dumpslog(ms);
        return FAILED;
}

/*
 * Called by the midlayer with the lock held to reset the
 * SCSI host and bus.
 * The midlayer will wait for devices to come back, we don't need
 * to do that ourselves
 */
static int mesh_host_reset(struct scsi_cmnd *cmd)
{
        struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
        volatile struct mesh_regs __iomem *mr = ms->mesh;
        volatile struct dbdma_regs __iomem *md = ms->dma;
        unsigned long flags;

        printk(KERN_DEBUG "mesh_host_reset\n");

        spin_lock_irqsave(ms->host->host_lock, flags);

        if (ms->dma_started)
                halt_dma(ms);

        /* Reset the controller & dbdma channel */
        out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);   /* stop dma */
        out_8(&mr->exception, 0xff);    /* clear all exception bits */
        out_8(&mr->error, 0xff);        /* clear all error bits */
        out_8(&mr->sequence, SEQ_RESETMESH);
        mesh_flush_io(mr);
        udelay(1);
        out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        out_8(&mr->source_id, ms->host->this_id);
        out_8(&mr->sel_timeout, 25);    /* 250ms */
        out_8(&mr->sync_params, ASYNC_PARAMS);

        /* Reset the bus */
        out_8(&mr->bus_status1, BS1_RST);       /* assert RST */
        mesh_flush_io(mr);
        udelay(30);                     /* leave it on for >= 25us */
        out_8(&mr->bus_status1, 0);     /* negate RST */

        /* Complete pending commands */
        handle_reset(ms);
        
        spin_unlock_irqrestore(ms->host->host_lock, flags);
        return SUCCESS;
}

static void set_mesh_power(struct mesh_state *ms, int state)
{
        if (!machine_is(powermac))
                return;
        if (state) {
                pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1);
                msleep(200);
        } else {
                pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
                msleep(10);
        }
}


#ifdef CONFIG_PM
static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
{
        struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
        unsigned long flags;

        switch (mesg.event) {
        case PM_EVENT_SUSPEND:
        case PM_EVENT_HIBERNATE:
        case PM_EVENT_FREEZE:
        case PM_EVENT_POWEROFF:
                break;
        default:
                return 0;
        }
        if (ms->phase == sleeping)
                return 0;

        scsi_block_requests(ms->host);
        spin_lock_irqsave(ms->host->host_lock, flags);
        while(ms->phase != idle) {
                spin_unlock_irqrestore(ms->host->host_lock, flags);
                msleep(10);
                spin_lock_irqsave(ms->host->host_lock, flags);
        }
        ms->phase = sleeping;
        spin_unlock_irqrestore(ms->host->host_lock, flags);
        disable_irq(ms->meshintr);
        set_mesh_power(ms, 0);

        return 0;
}

static int mesh_resume(struct macio_dev *mdev)
{
        struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
        unsigned long flags;

        if (ms->phase != sleeping)
                return 0;

        set_mesh_power(ms, 1);
        mesh_init(ms);
        spin_lock_irqsave(ms->host->host_lock, flags);
        mesh_start(ms);
        spin_unlock_irqrestore(ms->host->host_lock, flags);
        enable_irq(ms->meshintr);
        scsi_unblock_requests(ms->host);

        return 0;
}

#endif /* CONFIG_PM */

/*
 * If we leave drives set for synchronous transfers (especially
 * CDROMs), and reboot to MacOS, it gets confused, poor thing.
 * So, on reboot we reset the SCSI bus.
 */
static int mesh_shutdown(struct macio_dev *mdev)
{
        struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
        volatile struct mesh_regs __iomem *mr;
        unsigned long flags;

        printk(KERN_INFO "resetting MESH scsi bus(es)\n");
        spin_lock_irqsave(ms->host->host_lock, flags);
        mr = ms->mesh;
        out_8(&mr->intr_mask, 0);
        out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
        out_8(&mr->bus_status1, BS1_RST);
        mesh_flush_io(mr);
        udelay(30);
        out_8(&mr->bus_status1, 0);
        spin_unlock_irqrestore(ms->host->host_lock, flags);

        return 0;
}

static const struct scsi_host_template mesh_template = {
        .proc_name                      = "mesh",
        .name                           = "MESH",
        .queuecommand                   = mesh_queue,
        .eh_abort_handler               = mesh_abort,
        .eh_host_reset_handler          = mesh_host_reset,
        .can_queue                      = 20,
        .this_id                        = 7,
        .sg_tablesize                   = SG_ALL,
        .cmd_per_lun                    = 2,
        .max_segment_size               = 65535,
        .cmd_size                       = sizeof(struct mesh_cmd_priv),
};

static int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
{
        struct device_node *mesh = macio_get_of_node(mdev);
        struct pci_dev* pdev = macio_get_pci_dev(mdev);
        int tgt, minper;
        const int *cfp;
        struct mesh_state *ms;
        struct Scsi_Host *mesh_host;
        void *dma_cmd_space;
        dma_addr_t dma_cmd_bus;

        switch (mdev->bus->chip->type) {
        case macio_heathrow:
        case macio_gatwick:
        case macio_paddington:
                use_active_neg = 0;
                break;
        default:
                use_active_neg = SEQ_ACTIVE_NEG;
        }

        if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) {
                printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
                       " (got %d,%d)\n", macio_resource_count(mdev),
                       macio_irq_count(mdev));
                return -ENODEV;
        }

        if (macio_request_resources(mdev, "mesh") != 0) {
                printk(KERN_ERR "mesh: unable to request memory resources");
                return -EBUSY;
        }
        mesh_host = scsi_host_alloc(&mesh_template, sizeof(struct mesh_state));
        if (mesh_host == NULL) {
                printk(KERN_ERR "mesh: couldn't register host");
                goto out_release;
        }
        
        mesh_host->base = macio_resource_start(mdev, 0);
        mesh_host->irq = macio_irq(mdev, 0);
        ms = (struct mesh_state *) mesh_host->hostdata;
        macio_set_drvdata(mdev, ms);
        ms->host = mesh_host;
        ms->mdev = mdev;
        ms->pdev = pdev;
        
        ms->mesh = ioremap(macio_resource_start(mdev, 0), 0x1000);
        if (ms->mesh == NULL) {
                printk(KERN_ERR "mesh: can't map registers\n");
                goto out_free;
        }               
        ms->dma = ioremap(macio_resource_start(mdev, 1), 0x1000);
        if (ms->dma == NULL) {
                printk(KERN_ERR "mesh: can't map registers\n");
                iounmap(ms->mesh);
                goto out_free;
        }

        ms->meshintr = macio_irq(mdev, 0);
        ms->dmaintr = macio_irq(mdev, 1);

        /* Space for dma command list: +1 for stop command,
         * +1 to allow for aligning.
         */
        ms->dma_cmd_size = (mesh_host->sg_tablesize + 2) * sizeof(struct dbdma_cmd);

        /* We use the PCI APIs for now until the generic one gets fixed
         * enough or until we get some macio-specific versions
         */
        dma_cmd_space = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev,
                                           ms->dma_cmd_size, &dma_cmd_bus,
                                           GFP_KERNEL);
        if (dma_cmd_space == NULL) {
                printk(KERN_ERR "mesh: can't allocate DMA table\n");
                goto out_unmap;
        }

        ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
        ms->dma_cmd_space = dma_cmd_space;
        ms->dma_cmd_bus = dma_cmd_bus + ((unsigned long)ms->dma_cmds)
                - (unsigned long)dma_cmd_space;
        ms->current_req = NULL;
        for (tgt = 0; tgt < 8; ++tgt) {
                ms->tgts[tgt].sdtr_state = do_sdtr;
                ms->tgts[tgt].sync_params = ASYNC_PARAMS;
                ms->tgts[tgt].current_req = NULL;
        }

        if ((cfp = of_get_property(mesh, "clock-frequency", NULL)))
                ms->clk_freq = *cfp;
        else {
                printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n");
                ms->clk_freq = 50000000;
        }

        /* The maximum sync rate is clock / 5; increase
         * mesh_sync_period if necessary.
         */
        minper = 1000000000 / (ms->clk_freq / 5); /* ns */
        if (mesh_sync_period < minper)
                mesh_sync_period = minper;

        /* Power up the chip */
        set_mesh_power(ms, 1);

        /* Set it up */
        mesh_init(ms);

        /* Request interrupt */
        if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) {
                printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);
                goto out_shutdown;
        }

        /* Add scsi host & scan */
        if (scsi_add_host(mesh_host, &mdev->ofdev.dev))
                goto out_release_irq;
        scsi_scan_host(mesh_host);

        return 0;

 out_release_irq:
        free_irq(ms->meshintr, ms);
 out_shutdown:
        /* shutdown & reset bus in case of error or macos can be confused
         * at reboot if the bus was set to synchronous mode already
         */
        mesh_shutdown(mdev);
        set_mesh_power(ms, 0);
        dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
                            ms->dma_cmd_space, ms->dma_cmd_bus);
 out_unmap:
        iounmap(ms->dma);
        iounmap(ms->mesh);
 out_free:
        scsi_host_put(mesh_host);
 out_release:
        macio_release_resources(mdev);

        return -ENODEV;
}

static void mesh_remove(struct macio_dev *mdev)
{
        struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
        struct Scsi_Host *mesh_host = ms->host;

        scsi_remove_host(mesh_host);

        free_irq(ms->meshintr, ms);

        /* Reset scsi bus */
        mesh_shutdown(mdev);

        /* Shut down chip & termination */
        set_mesh_power(ms, 0);

        /* Unmap registers & dma controller */
        iounmap(ms->mesh);
        iounmap(ms->dma);

        /* Free DMA commands memory */
        dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
                            ms->dma_cmd_space, ms->dma_cmd_bus);

        /* Release memory resources */
        macio_release_resources(mdev);

        scsi_host_put(mesh_host);
}

static struct of_device_id mesh_match[] = 
{
        {
        .name           = "mesh",
        },
        {
        .type           = "scsi",
        .compatible     = "chrp,mesh0"
        },
        {},
};
MODULE_DEVICE_TABLE (of, mesh_match);

static struct macio_driver mesh_driver = 
{
        .driver = {
                .name           = "mesh",
                .owner          = THIS_MODULE,
                .of_match_table = mesh_match,
        },
        .probe          = mesh_probe,
        .remove         = mesh_remove,
        .shutdown       = mesh_shutdown,
#ifdef CONFIG_PM
        .suspend        = mesh_suspend,
        .resume         = mesh_resume,
#endif
};


static int __init init_mesh(void)
{

        /* Calculate sync rate from module parameters */
        if (sync_rate > 10)
                sync_rate = 10;
        if (sync_rate > 0) {
                printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
                mesh_sync_period = 1000 / sync_rate;    /* ns */
                mesh_sync_offset = 15;
        } else
                printk(KERN_INFO "mesh: configured for asynchronous\n");

        return macio_register_driver(&mesh_driver);
}

static void __exit exit_mesh(void)
{
        return macio_unregister_driver(&mesh_driver);
}

module_init(init_mesh);
module_exit(exit_mesh);