18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SCSI low-level driver for the MESH (Macintosh Enhanced SCSI Hardware)
48c2ecf20Sopenharmony_ci * bus adaptor found on Power Macintosh computers.
58c2ecf20Sopenharmony_ci * We assume the MESH is connected to a DBDMA (descriptor-based DMA)
68c2ecf20Sopenharmony_ci * controller.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Paul Mackerras, August 1996.
98c2ecf20Sopenharmony_ci * Copyright (C) 1996 Paul Mackerras.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Apr. 21 2002  - BenH		Rework bus reset code for new error handler
128c2ecf20Sopenharmony_ci *                              Add delay after initial bus reset
138c2ecf20Sopenharmony_ci *                              Add module parameters
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * Sep. 27 2003  - BenH		Move to new driver model, fix some write posting
168c2ecf20Sopenharmony_ci *				issues
178c2ecf20Sopenharmony_ci * To do:
188c2ecf20Sopenharmony_ci * - handle aborts correctly
198c2ecf20Sopenharmony_ci * - retry arbitration if lost (unless higher levels do this for us)
208c2ecf20Sopenharmony_ci * - power down the chip when no device is detected
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci#include <linux/module.h>
238c2ecf20Sopenharmony_ci#include <linux/kernel.h>
248c2ecf20Sopenharmony_ci#include <linux/delay.h>
258c2ecf20Sopenharmony_ci#include <linux/types.h>
268c2ecf20Sopenharmony_ci#include <linux/string.h>
278c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
288c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
298c2ecf20Sopenharmony_ci#include <linux/stat.h>
308c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
318c2ecf20Sopenharmony_ci#include <linux/reboot.h>
328c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
338c2ecf20Sopenharmony_ci#include <linux/pci.h>
348c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
358c2ecf20Sopenharmony_ci#include <asm/dbdma.h>
368c2ecf20Sopenharmony_ci#include <asm/io.h>
378c2ecf20Sopenharmony_ci#include <asm/prom.h>
388c2ecf20Sopenharmony_ci#include <asm/irq.h>
398c2ecf20Sopenharmony_ci#include <asm/hydra.h>
408c2ecf20Sopenharmony_ci#include <asm/processor.h>
418c2ecf20Sopenharmony_ci#include <asm/machdep.h>
428c2ecf20Sopenharmony_ci#include <asm/pmac_feature.h>
438c2ecf20Sopenharmony_ci#include <asm/macio.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
468c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
478c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
488c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#include "mesh.h"
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#if 1
538c2ecf20Sopenharmony_ci#undef KERN_DEBUG
548c2ecf20Sopenharmony_ci#define KERN_DEBUG KERN_WARNING
558c2ecf20Sopenharmony_ci#endif
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Paul Mackerras (paulus@samba.org)");
588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PowerMac MESH SCSI driver");
598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic int sync_rate = CONFIG_SCSI_MESH_SYNC_RATE;
628c2ecf20Sopenharmony_cistatic int sync_targets = 0xff;
638c2ecf20Sopenharmony_cistatic int resel_targets = 0xff;
648c2ecf20Sopenharmony_cistatic int debug_targets = 0;	/* print debug for these targets */
658c2ecf20Sopenharmony_cistatic int init_reset_delay = CONFIG_SCSI_MESH_RESET_DELAY_MS;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cimodule_param(sync_rate, int, 0);
688c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sync_rate, "Synchronous rate (0..10, 0=async)");
698c2ecf20Sopenharmony_cimodule_param(sync_targets, int, 0);
708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sync_targets, "Bitmask of targets allowed to set synchronous");
718c2ecf20Sopenharmony_cimodule_param(resel_targets, int, 0);
728c2ecf20Sopenharmony_ciMODULE_PARM_DESC(resel_targets, "Bitmask of targets allowed to set disconnect");
738c2ecf20Sopenharmony_cimodule_param(debug_targets, int, 0644);
748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_targets, "Bitmask of debugged targets");
758c2ecf20Sopenharmony_cimodule_param(init_reset_delay, int, 0);
768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(init_reset_delay, "Initial bus reset delay (0=no reset)");
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int mesh_sync_period = 100;
798c2ecf20Sopenharmony_cistatic int mesh_sync_offset = 0;
808c2ecf20Sopenharmony_cistatic unsigned char use_active_neg = 0;  /* bit mask for SEQ_ACTIVE_NEG if used */
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define ALLOW_SYNC(tgt)		((sync_targets >> (tgt)) & 1)
838c2ecf20Sopenharmony_ci#define ALLOW_RESEL(tgt)	((resel_targets >> (tgt)) & 1)
848c2ecf20Sopenharmony_ci#define ALLOW_DEBUG(tgt)	((debug_targets >> (tgt)) & 1)
858c2ecf20Sopenharmony_ci#define DEBUG_TARGET(cmd)	((cmd) && ALLOW_DEBUG((cmd)->device->id))
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#undef MESH_DBG
888c2ecf20Sopenharmony_ci#define N_DBG_LOG	50
898c2ecf20Sopenharmony_ci#define N_DBG_SLOG	20
908c2ecf20Sopenharmony_ci#define NUM_DBG_EVENTS	13
918c2ecf20Sopenharmony_ci#undef	DBG_USE_TB		/* bombs on 601 */
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistruct dbglog {
948c2ecf20Sopenharmony_ci	char	*fmt;
958c2ecf20Sopenharmony_ci	u32	tb;
968c2ecf20Sopenharmony_ci	u8	phase;
978c2ecf20Sopenharmony_ci	u8	bs0;
988c2ecf20Sopenharmony_ci	u8	bs1;
998c2ecf20Sopenharmony_ci	u8	tgt;
1008c2ecf20Sopenharmony_ci	int	d;
1018c2ecf20Sopenharmony_ci};
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cienum mesh_phase {
1048c2ecf20Sopenharmony_ci	idle,
1058c2ecf20Sopenharmony_ci	arbitrating,
1068c2ecf20Sopenharmony_ci	selecting,
1078c2ecf20Sopenharmony_ci	commanding,
1088c2ecf20Sopenharmony_ci	dataing,
1098c2ecf20Sopenharmony_ci	statusing,
1108c2ecf20Sopenharmony_ci	busfreeing,
1118c2ecf20Sopenharmony_ci	disconnecting,
1128c2ecf20Sopenharmony_ci	reselecting,
1138c2ecf20Sopenharmony_ci	sleeping
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cienum msg_phase {
1178c2ecf20Sopenharmony_ci	msg_none,
1188c2ecf20Sopenharmony_ci	msg_out,
1198c2ecf20Sopenharmony_ci	msg_out_xxx,
1208c2ecf20Sopenharmony_ci	msg_out_last,
1218c2ecf20Sopenharmony_ci	msg_in,
1228c2ecf20Sopenharmony_ci	msg_in_bad,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cienum sdtr_phase {
1268c2ecf20Sopenharmony_ci	do_sdtr,
1278c2ecf20Sopenharmony_ci	sdtr_sent,
1288c2ecf20Sopenharmony_ci	sdtr_done
1298c2ecf20Sopenharmony_ci};
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistruct mesh_target {
1328c2ecf20Sopenharmony_ci	enum sdtr_phase sdtr_state;
1338c2ecf20Sopenharmony_ci	int	sync_params;
1348c2ecf20Sopenharmony_ci	int	data_goes_out;		/* guess as to data direction */
1358c2ecf20Sopenharmony_ci	struct scsi_cmnd *current_req;
1368c2ecf20Sopenharmony_ci	u32	saved_ptr;
1378c2ecf20Sopenharmony_ci#ifdef MESH_DBG
1388c2ecf20Sopenharmony_ci	int	log_ix;
1398c2ecf20Sopenharmony_ci	int	n_log;
1408c2ecf20Sopenharmony_ci	struct dbglog log[N_DBG_LOG];
1418c2ecf20Sopenharmony_ci#endif
1428c2ecf20Sopenharmony_ci};
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistruct mesh_state {
1458c2ecf20Sopenharmony_ci	volatile struct	mesh_regs __iomem *mesh;
1468c2ecf20Sopenharmony_ci	int	meshintr;
1478c2ecf20Sopenharmony_ci	volatile struct	dbdma_regs __iomem *dma;
1488c2ecf20Sopenharmony_ci	int	dmaintr;
1498c2ecf20Sopenharmony_ci	struct	Scsi_Host *host;
1508c2ecf20Sopenharmony_ci	struct	mesh_state *next;
1518c2ecf20Sopenharmony_ci	struct scsi_cmnd *request_q;
1528c2ecf20Sopenharmony_ci	struct scsi_cmnd *request_qtail;
1538c2ecf20Sopenharmony_ci	enum mesh_phase phase;		/* what we're currently trying to do */
1548c2ecf20Sopenharmony_ci	enum msg_phase msgphase;
1558c2ecf20Sopenharmony_ci	int	conn_tgt;		/* target we're connected to */
1568c2ecf20Sopenharmony_ci	struct scsi_cmnd *current_req;		/* req we're currently working on */
1578c2ecf20Sopenharmony_ci	int	data_ptr;
1588c2ecf20Sopenharmony_ci	int	dma_started;
1598c2ecf20Sopenharmony_ci	int	dma_count;
1608c2ecf20Sopenharmony_ci	int	stat;
1618c2ecf20Sopenharmony_ci	int	aborting;
1628c2ecf20Sopenharmony_ci	int	expect_reply;
1638c2ecf20Sopenharmony_ci	int	n_msgin;
1648c2ecf20Sopenharmony_ci	u8	msgin[16];
1658c2ecf20Sopenharmony_ci	int	n_msgout;
1668c2ecf20Sopenharmony_ci	int	last_n_msgout;
1678c2ecf20Sopenharmony_ci	u8	msgout[16];
1688c2ecf20Sopenharmony_ci	struct dbdma_cmd *dma_cmds;	/* space for dbdma commands, aligned */
1698c2ecf20Sopenharmony_ci	dma_addr_t dma_cmd_bus;
1708c2ecf20Sopenharmony_ci	void	*dma_cmd_space;
1718c2ecf20Sopenharmony_ci	int	dma_cmd_size;
1728c2ecf20Sopenharmony_ci	int	clk_freq;
1738c2ecf20Sopenharmony_ci	struct mesh_target tgts[8];
1748c2ecf20Sopenharmony_ci	struct macio_dev *mdev;
1758c2ecf20Sopenharmony_ci	struct pci_dev* pdev;
1768c2ecf20Sopenharmony_ci#ifdef MESH_DBG
1778c2ecf20Sopenharmony_ci	int	log_ix;
1788c2ecf20Sopenharmony_ci	int	n_log;
1798c2ecf20Sopenharmony_ci	struct dbglog log[N_DBG_SLOG];
1808c2ecf20Sopenharmony_ci#endif
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci/*
1848c2ecf20Sopenharmony_ci * Driver is too messy, we need a few prototypes...
1858c2ecf20Sopenharmony_ci */
1868c2ecf20Sopenharmony_cistatic void mesh_done(struct mesh_state *ms, int start_next);
1878c2ecf20Sopenharmony_cistatic void mesh_interrupt(struct mesh_state *ms);
1888c2ecf20Sopenharmony_cistatic void cmd_complete(struct mesh_state *ms);
1898c2ecf20Sopenharmony_cistatic void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd);
1908c2ecf20Sopenharmony_cistatic void halt_dma(struct mesh_state *ms);
1918c2ecf20Sopenharmony_cistatic void phase_mismatch(struct mesh_state *ms);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci/*
1958c2ecf20Sopenharmony_ci * Some debugging & logging routines
1968c2ecf20Sopenharmony_ci */
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci#ifdef MESH_DBG
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic inline u32 readtb(void)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	u32 tb;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci#ifdef DBG_USE_TB
2058c2ecf20Sopenharmony_ci	/* Beware: if you enable this, it will crash on 601s. */
2068c2ecf20Sopenharmony_ci	asm ("mftb %0" : "=r" (tb) : );
2078c2ecf20Sopenharmony_ci#else
2088c2ecf20Sopenharmony_ci	tb = 0;
2098c2ecf20Sopenharmony_ci#endif
2108c2ecf20Sopenharmony_ci	return tb;
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic void dlog(struct mesh_state *ms, char *fmt, int a)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
2168c2ecf20Sopenharmony_ci	struct dbglog *tlp, *slp;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	tlp = &tp->log[tp->log_ix];
2198c2ecf20Sopenharmony_ci	slp = &ms->log[ms->log_ix];
2208c2ecf20Sopenharmony_ci	tlp->fmt = fmt;
2218c2ecf20Sopenharmony_ci	tlp->tb = readtb();
2228c2ecf20Sopenharmony_ci	tlp->phase = (ms->msgphase << 4) + ms->phase;
2238c2ecf20Sopenharmony_ci	tlp->bs0 = ms->mesh->bus_status0;
2248c2ecf20Sopenharmony_ci	tlp->bs1 = ms->mesh->bus_status1;
2258c2ecf20Sopenharmony_ci	tlp->tgt = ms->conn_tgt;
2268c2ecf20Sopenharmony_ci	tlp->d = a;
2278c2ecf20Sopenharmony_ci	*slp = *tlp;
2288c2ecf20Sopenharmony_ci	if (++tp->log_ix >= N_DBG_LOG)
2298c2ecf20Sopenharmony_ci		tp->log_ix = 0;
2308c2ecf20Sopenharmony_ci	if (tp->n_log < N_DBG_LOG)
2318c2ecf20Sopenharmony_ci		++tp->n_log;
2328c2ecf20Sopenharmony_ci	if (++ms->log_ix >= N_DBG_SLOG)
2338c2ecf20Sopenharmony_ci		ms->log_ix = 0;
2348c2ecf20Sopenharmony_ci	if (ms->n_log < N_DBG_SLOG)
2358c2ecf20Sopenharmony_ci		++ms->n_log;
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_cistatic void dumplog(struct mesh_state *ms, int t)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[t];
2418c2ecf20Sopenharmony_ci	struct dbglog *lp;
2428c2ecf20Sopenharmony_ci	int i;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (tp->n_log == 0)
2458c2ecf20Sopenharmony_ci		return;
2468c2ecf20Sopenharmony_ci	i = tp->log_ix - tp->n_log;
2478c2ecf20Sopenharmony_ci	if (i < 0)
2488c2ecf20Sopenharmony_ci		i += N_DBG_LOG;
2498c2ecf20Sopenharmony_ci	tp->n_log = 0;
2508c2ecf20Sopenharmony_ci	do {
2518c2ecf20Sopenharmony_ci		lp = &tp->log[i];
2528c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh log %d: bs=%.2x%.2x ph=%.2x ",
2538c2ecf20Sopenharmony_ci		       t, lp->bs1, lp->bs0, lp->phase);
2548c2ecf20Sopenharmony_ci#ifdef DBG_USE_TB
2558c2ecf20Sopenharmony_ci		printk("tb=%10u ", lp->tb);
2568c2ecf20Sopenharmony_ci#endif
2578c2ecf20Sopenharmony_ci		printk(lp->fmt, lp->d);
2588c2ecf20Sopenharmony_ci		printk("\n");
2598c2ecf20Sopenharmony_ci		if (++i >= N_DBG_LOG)
2608c2ecf20Sopenharmony_ci			i = 0;
2618c2ecf20Sopenharmony_ci	} while (i != tp->log_ix);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void dumpslog(struct mesh_state *ms)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct dbglog *lp;
2678c2ecf20Sopenharmony_ci	int i;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	if (ms->n_log == 0)
2708c2ecf20Sopenharmony_ci		return;
2718c2ecf20Sopenharmony_ci	i = ms->log_ix - ms->n_log;
2728c2ecf20Sopenharmony_ci	if (i < 0)
2738c2ecf20Sopenharmony_ci		i += N_DBG_SLOG;
2748c2ecf20Sopenharmony_ci	ms->n_log = 0;
2758c2ecf20Sopenharmony_ci	do {
2768c2ecf20Sopenharmony_ci		lp = &ms->log[i];
2778c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh log: bs=%.2x%.2x ph=%.2x t%d ",
2788c2ecf20Sopenharmony_ci		       lp->bs1, lp->bs0, lp->phase, lp->tgt);
2798c2ecf20Sopenharmony_ci#ifdef DBG_USE_TB
2808c2ecf20Sopenharmony_ci		printk("tb=%10u ", lp->tb);
2818c2ecf20Sopenharmony_ci#endif
2828c2ecf20Sopenharmony_ci		printk(lp->fmt, lp->d);
2838c2ecf20Sopenharmony_ci		printk("\n");
2848c2ecf20Sopenharmony_ci		if (++i >= N_DBG_SLOG)
2858c2ecf20Sopenharmony_ci			i = 0;
2868c2ecf20Sopenharmony_ci	} while (i != ms->log_ix);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci#else
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic inline void dlog(struct mesh_state *ms, char *fmt, int a)
2928c2ecf20Sopenharmony_ci{}
2938c2ecf20Sopenharmony_cistatic inline void dumplog(struct mesh_state *ms, int tgt)
2948c2ecf20Sopenharmony_ci{}
2958c2ecf20Sopenharmony_cistatic inline void dumpslog(struct mesh_state *ms)
2968c2ecf20Sopenharmony_ci{}
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci#endif /* MESH_DBG */
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci#define MKWORD(a, b, c, d)	(((a) << 24) + ((b) << 16) + ((c) << 8) + (d))
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_cistatic void
3038c2ecf20Sopenharmony_cimesh_dump_regs(struct mesh_state *ms)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
3068c2ecf20Sopenharmony_ci	volatile struct dbdma_regs __iomem *md = ms->dma;
3078c2ecf20Sopenharmony_ci	int t;
3088c2ecf20Sopenharmony_ci	struct mesh_target *tp;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "mesh: state at %p, regs at %p, dma at %p\n",
3118c2ecf20Sopenharmony_ci	       ms, mr, md);
3128c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "    ct=%4x seq=%2x bs=%4x fc=%2x "
3138c2ecf20Sopenharmony_ci	       "exc=%2x err=%2x im=%2x int=%2x sp=%2x\n",
3148c2ecf20Sopenharmony_ci	       (mr->count_hi << 8) + mr->count_lo, mr->sequence,
3158c2ecf20Sopenharmony_ci	       (mr->bus_status1 << 8) + mr->bus_status0, mr->fifo_count,
3168c2ecf20Sopenharmony_ci	       mr->exception, mr->error, mr->intr_mask, mr->interrupt,
3178c2ecf20Sopenharmony_ci	       mr->sync_params);
3188c2ecf20Sopenharmony_ci	while(in_8(&mr->fifo_count))
3198c2ecf20Sopenharmony_ci		printk(KERN_DEBUG " fifo data=%.2x\n",in_8(&mr->fifo));
3208c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "    dma stat=%x cmdptr=%x\n",
3218c2ecf20Sopenharmony_ci	       in_le32(&md->status), in_le32(&md->cmdptr));
3228c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "    phase=%d msgphase=%d conn_tgt=%d data_ptr=%d\n",
3238c2ecf20Sopenharmony_ci	       ms->phase, ms->msgphase, ms->conn_tgt, ms->data_ptr);
3248c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "    dma_st=%d dma_ct=%d n_msgout=%d\n",
3258c2ecf20Sopenharmony_ci	       ms->dma_started, ms->dma_count, ms->n_msgout);
3268c2ecf20Sopenharmony_ci	for (t = 0; t < 8; ++t) {
3278c2ecf20Sopenharmony_ci		tp = &ms->tgts[t];
3288c2ecf20Sopenharmony_ci		if (tp->current_req == NULL)
3298c2ecf20Sopenharmony_ci			continue;
3308c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "    target %d: req=%p goes_out=%d saved_ptr=%d\n",
3318c2ecf20Sopenharmony_ci		       t, tp->current_req, tp->data_goes_out, tp->saved_ptr);
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/*
3378c2ecf20Sopenharmony_ci * Flush write buffers on the bus path to the mesh
3388c2ecf20Sopenharmony_ci */
3398c2ecf20Sopenharmony_cistatic inline void mesh_flush_io(volatile struct mesh_regs __iomem *mr)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	(void)in_8(&mr->mesh_id);
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci/*
3468c2ecf20Sopenharmony_ci * Complete a SCSI command
3478c2ecf20Sopenharmony_ci */
3488c2ecf20Sopenharmony_cistatic void mesh_completed(struct mesh_state *ms, struct scsi_cmnd *cmd)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	(*cmd->scsi_done)(cmd);
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci/* Called with  meshinterrupt disabled, initialize the chipset
3558c2ecf20Sopenharmony_ci * and eventually do the initial bus reset. The lock must not be
3568c2ecf20Sopenharmony_ci * held since we can schedule.
3578c2ecf20Sopenharmony_ci */
3588c2ecf20Sopenharmony_cistatic void mesh_init(struct mesh_state *ms)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
3618c2ecf20Sopenharmony_ci	volatile struct dbdma_regs __iomem *md = ms->dma;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	mesh_flush_io(mr);
3648c2ecf20Sopenharmony_ci	udelay(100);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* Reset controller */
3678c2ecf20Sopenharmony_ci	out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* stop dma */
3688c2ecf20Sopenharmony_ci	out_8(&mr->exception, 0xff);	/* clear all exception bits */
3698c2ecf20Sopenharmony_ci	out_8(&mr->error, 0xff);	/* clear all error bits */
3708c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_RESETMESH);
3718c2ecf20Sopenharmony_ci	mesh_flush_io(mr);
3728c2ecf20Sopenharmony_ci	udelay(10);
3738c2ecf20Sopenharmony_ci	out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
3748c2ecf20Sopenharmony_ci	out_8(&mr->source_id, ms->host->this_id);
3758c2ecf20Sopenharmony_ci	out_8(&mr->sel_timeout, 25);	/* 250ms */
3768c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, ASYNC_PARAMS);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (init_reset_delay) {
3798c2ecf20Sopenharmony_ci		printk(KERN_INFO "mesh: performing initial bus reset...\n");
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		/* Reset bus */
3828c2ecf20Sopenharmony_ci		out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
3838c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
3848c2ecf20Sopenharmony_ci		udelay(30);			/* leave it on for >= 25us */
3858c2ecf20Sopenharmony_ci		out_8(&mr->bus_status1, 0);	/* negate RST */
3868c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		/* Wait for bus to come back */
3898c2ecf20Sopenharmony_ci		msleep(init_reset_delay);
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	/* Reconfigure controller */
3938c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, 0xff);	/* clear all interrupt bits */
3948c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_FLUSHFIFO);
3958c2ecf20Sopenharmony_ci	mesh_flush_io(mr);
3968c2ecf20Sopenharmony_ci	udelay(1);
3978c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, ASYNC_PARAMS);
3988c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_ENBRESEL);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	ms->phase = idle;
4018c2ecf20Sopenharmony_ci	ms->msgphase = msg_none;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic void mesh_start_cmd(struct mesh_state *ms, struct scsi_cmnd *cmd)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
4088c2ecf20Sopenharmony_ci	int t, id;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	id = cmd->device->id;
4118c2ecf20Sopenharmony_ci	ms->current_req = cmd;
4128c2ecf20Sopenharmony_ci	ms->tgts[id].data_goes_out = cmd->sc_data_direction == DMA_TO_DEVICE;
4138c2ecf20Sopenharmony_ci	ms->tgts[id].current_req = cmd;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci#if 1
4168c2ecf20Sopenharmony_ci	if (DEBUG_TARGET(cmd)) {
4178c2ecf20Sopenharmony_ci		int i;
4188c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh_start: %p tgt=%d cmd=", cmd, id);
4198c2ecf20Sopenharmony_ci		for (i = 0; i < cmd->cmd_len; ++i)
4208c2ecf20Sopenharmony_ci			printk(" %x", cmd->cmnd[i]);
4218c2ecf20Sopenharmony_ci		printk(" use_sg=%d buffer=%p bufflen=%u\n",
4228c2ecf20Sopenharmony_ci		       scsi_sg_count(cmd), scsi_sglist(cmd), scsi_bufflen(cmd));
4238c2ecf20Sopenharmony_ci	}
4248c2ecf20Sopenharmony_ci#endif
4258c2ecf20Sopenharmony_ci	if (ms->dma_started)
4268c2ecf20Sopenharmony_ci		panic("mesh: double DMA start !\n");
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	ms->phase = arbitrating;
4298c2ecf20Sopenharmony_ci	ms->msgphase = msg_none;
4308c2ecf20Sopenharmony_ci	ms->data_ptr = 0;
4318c2ecf20Sopenharmony_ci	ms->dma_started = 0;
4328c2ecf20Sopenharmony_ci	ms->n_msgout = 0;
4338c2ecf20Sopenharmony_ci	ms->last_n_msgout = 0;
4348c2ecf20Sopenharmony_ci	ms->expect_reply = 0;
4358c2ecf20Sopenharmony_ci	ms->conn_tgt = id;
4368c2ecf20Sopenharmony_ci	ms->tgts[id].saved_ptr = 0;
4378c2ecf20Sopenharmony_ci	ms->stat = DID_OK;
4388c2ecf20Sopenharmony_ci	ms->aborting = 0;
4398c2ecf20Sopenharmony_ci#ifdef MESH_DBG
4408c2ecf20Sopenharmony_ci	ms->tgts[id].n_log = 0;
4418c2ecf20Sopenharmony_ci	dlog(ms, "start cmd=%x", (int) cmd);
4428c2ecf20Sopenharmony_ci#endif
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	/* Off we go */
4458c2ecf20Sopenharmony_ci	dlog(ms, "about to arb, intr/exc/err/fc=%.8x",
4468c2ecf20Sopenharmony_ci	     MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
4478c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_CMDDONE);
4488c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_ENBRESEL);
4498c2ecf20Sopenharmony_ci	mesh_flush_io(mr);
4508c2ecf20Sopenharmony_ci	udelay(1);
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
4538c2ecf20Sopenharmony_ci		/*
4548c2ecf20Sopenharmony_ci		 * Some other device has the bus or is arbitrating for it -
4558c2ecf20Sopenharmony_ci		 * probably a target which is about to reselect us.
4568c2ecf20Sopenharmony_ci		 */
4578c2ecf20Sopenharmony_ci		dlog(ms, "busy b4 arb, intr/exc/err/fc=%.8x",
4588c2ecf20Sopenharmony_ci		     MKWORD(mr->interrupt, mr->exception,
4598c2ecf20Sopenharmony_ci			    mr->error, mr->fifo_count));
4608c2ecf20Sopenharmony_ci		for (t = 100; t > 0; --t) {
4618c2ecf20Sopenharmony_ci			if ((in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) == 0)
4628c2ecf20Sopenharmony_ci				break;
4638c2ecf20Sopenharmony_ci			if (in_8(&mr->interrupt) != 0) {
4648c2ecf20Sopenharmony_ci				dlog(ms, "intr b4 arb, intr/exc/err/fc=%.8x",
4658c2ecf20Sopenharmony_ci				     MKWORD(mr->interrupt, mr->exception,
4668c2ecf20Sopenharmony_ci					    mr->error, mr->fifo_count));
4678c2ecf20Sopenharmony_ci				mesh_interrupt(ms);
4688c2ecf20Sopenharmony_ci				if (ms->phase != arbitrating)
4698c2ecf20Sopenharmony_ci					return;
4708c2ecf20Sopenharmony_ci			}
4718c2ecf20Sopenharmony_ci			udelay(1);
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci		if (in_8(&mr->bus_status1) & (BS1_BSY | BS1_SEL)) {
4748c2ecf20Sopenharmony_ci			/* XXX should try again in a little while */
4758c2ecf20Sopenharmony_ci			ms->stat = DID_BUS_BUSY;
4768c2ecf20Sopenharmony_ci			ms->phase = idle;
4778c2ecf20Sopenharmony_ci			mesh_done(ms, 0);
4788c2ecf20Sopenharmony_ci			return;
4798c2ecf20Sopenharmony_ci		}
4808c2ecf20Sopenharmony_ci	}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	/*
4838c2ecf20Sopenharmony_ci	 * Apparently the mesh has a bug where it will assert both its
4848c2ecf20Sopenharmony_ci	 * own bit and the target's bit on the bus during arbitration.
4858c2ecf20Sopenharmony_ci	 */
4868c2ecf20Sopenharmony_ci	out_8(&mr->dest_id, mr->source_id);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	/*
4898c2ecf20Sopenharmony_ci	 * There appears to be a race with reselection sometimes,
4908c2ecf20Sopenharmony_ci	 * where a target reselects us just as we issue the
4918c2ecf20Sopenharmony_ci	 * arbitrate command.  It seems that then the arbitrate
4928c2ecf20Sopenharmony_ci	 * command just hangs waiting for the bus to be free
4938c2ecf20Sopenharmony_ci	 * without giving us a reselection exception.
4948c2ecf20Sopenharmony_ci	 * The only way I have found to get it to respond correctly
4958c2ecf20Sopenharmony_ci	 * is this: disable reselection before issuing the arbitrate
4968c2ecf20Sopenharmony_ci	 * command, then after issuing it, if it looks like a target
4978c2ecf20Sopenharmony_ci	 * is trying to reselect us, reset the mesh and then enable
4988c2ecf20Sopenharmony_ci	 * reselection.
4998c2ecf20Sopenharmony_ci	 */
5008c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_DISRESEL);
5018c2ecf20Sopenharmony_ci	if (in_8(&mr->interrupt) != 0) {
5028c2ecf20Sopenharmony_ci		dlog(ms, "intr after disresel, intr/exc/err/fc=%.8x",
5038c2ecf20Sopenharmony_ci		     MKWORD(mr->interrupt, mr->exception,
5048c2ecf20Sopenharmony_ci			    mr->error, mr->fifo_count));
5058c2ecf20Sopenharmony_ci		mesh_interrupt(ms);
5068c2ecf20Sopenharmony_ci		if (ms->phase != arbitrating)
5078c2ecf20Sopenharmony_ci			return;
5088c2ecf20Sopenharmony_ci		dlog(ms, "after intr after disresel, intr/exc/err/fc=%.8x",
5098c2ecf20Sopenharmony_ci		     MKWORD(mr->interrupt, mr->exception,
5108c2ecf20Sopenharmony_ci			    mr->error, mr->fifo_count));
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_ARBITRATE);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	for (t = 230; t > 0; --t) {
5168c2ecf20Sopenharmony_ci		if (in_8(&mr->interrupt) != 0)
5178c2ecf20Sopenharmony_ci			break;
5188c2ecf20Sopenharmony_ci		udelay(1);
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci	dlog(ms, "after arb, intr/exc/err/fc=%.8x",
5218c2ecf20Sopenharmony_ci	     MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
5228c2ecf20Sopenharmony_ci	if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
5238c2ecf20Sopenharmony_ci	    && (in_8(&mr->bus_status0) & BS0_IO)) {
5248c2ecf20Sopenharmony_ci		/* looks like a reselection - try resetting the mesh */
5258c2ecf20Sopenharmony_ci		dlog(ms, "resel? after arb, intr/exc/err/fc=%.8x",
5268c2ecf20Sopenharmony_ci		     MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
5278c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_RESETMESH);
5288c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
5298c2ecf20Sopenharmony_ci		udelay(10);
5308c2ecf20Sopenharmony_ci		out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
5318c2ecf20Sopenharmony_ci		out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
5328c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_ENBRESEL);
5338c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
5348c2ecf20Sopenharmony_ci		for (t = 10; t > 0 && in_8(&mr->interrupt) == 0; --t)
5358c2ecf20Sopenharmony_ci			udelay(1);
5368c2ecf20Sopenharmony_ci		dlog(ms, "tried reset after arb, intr/exc/err/fc=%.8x",
5378c2ecf20Sopenharmony_ci		     MKWORD(mr->interrupt, mr->exception, mr->error, mr->fifo_count));
5388c2ecf20Sopenharmony_ci#ifndef MESH_MULTIPLE_HOSTS
5398c2ecf20Sopenharmony_ci		if (in_8(&mr->interrupt) == 0 && (in_8(&mr->bus_status1) & BS1_SEL)
5408c2ecf20Sopenharmony_ci		    && (in_8(&mr->bus_status0) & BS0_IO)) {
5418c2ecf20Sopenharmony_ci			printk(KERN_ERR "mesh: controller not responding"
5428c2ecf20Sopenharmony_ci			       " to reselection!\n");
5438c2ecf20Sopenharmony_ci			/*
5448c2ecf20Sopenharmony_ci			 * If this is a target reselecting us, and the
5458c2ecf20Sopenharmony_ci			 * mesh isn't responding, the higher levels of
5468c2ecf20Sopenharmony_ci			 * the scsi code will eventually time out and
5478c2ecf20Sopenharmony_ci			 * reset the bus.
5488c2ecf20Sopenharmony_ci			 */
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci#endif
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci/*
5558c2ecf20Sopenharmony_ci * Start the next command for a MESH.
5568c2ecf20Sopenharmony_ci * Should be called with interrupts disabled.
5578c2ecf20Sopenharmony_ci */
5588c2ecf20Sopenharmony_cistatic void mesh_start(struct mesh_state *ms)
5598c2ecf20Sopenharmony_ci{
5608c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd, *prev, *next;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	if (ms->phase != idle || ms->current_req != NULL) {
5638c2ecf20Sopenharmony_ci		printk(KERN_ERR "inappropriate mesh_start (phase=%d, ms=%p)",
5648c2ecf20Sopenharmony_ci		       ms->phase, ms);
5658c2ecf20Sopenharmony_ci		return;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	while (ms->phase == idle) {
5698c2ecf20Sopenharmony_ci		prev = NULL;
5708c2ecf20Sopenharmony_ci		for (cmd = ms->request_q; ; cmd = (struct scsi_cmnd *) cmd->host_scribble) {
5718c2ecf20Sopenharmony_ci			if (cmd == NULL)
5728c2ecf20Sopenharmony_ci				return;
5738c2ecf20Sopenharmony_ci			if (ms->tgts[cmd->device->id].current_req == NULL)
5748c2ecf20Sopenharmony_ci				break;
5758c2ecf20Sopenharmony_ci			prev = cmd;
5768c2ecf20Sopenharmony_ci		}
5778c2ecf20Sopenharmony_ci		next = (struct scsi_cmnd *) cmd->host_scribble;
5788c2ecf20Sopenharmony_ci		if (prev == NULL)
5798c2ecf20Sopenharmony_ci			ms->request_q = next;
5808c2ecf20Sopenharmony_ci		else
5818c2ecf20Sopenharmony_ci			prev->host_scribble = (void *) next;
5828c2ecf20Sopenharmony_ci		if (next == NULL)
5838c2ecf20Sopenharmony_ci			ms->request_qtail = prev;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		mesh_start_cmd(ms, cmd);
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_cistatic void mesh_done(struct mesh_state *ms, int start_next)
5908c2ecf20Sopenharmony_ci{
5918c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd;
5928c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	cmd = ms->current_req;
5958c2ecf20Sopenharmony_ci	ms->current_req = NULL;
5968c2ecf20Sopenharmony_ci	tp->current_req = NULL;
5978c2ecf20Sopenharmony_ci	if (cmd) {
5988c2ecf20Sopenharmony_ci		cmd->result = (ms->stat << 16) | cmd->SCp.Status;
5998c2ecf20Sopenharmony_ci		if (ms->stat == DID_OK)
6008c2ecf20Sopenharmony_ci			cmd->result |= cmd->SCp.Message << 8;
6018c2ecf20Sopenharmony_ci		if (DEBUG_TARGET(cmd)) {
6028c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "mesh_done: result = %x, data_ptr=%d, buflen=%d\n",
6038c2ecf20Sopenharmony_ci			       cmd->result, ms->data_ptr, scsi_bufflen(cmd));
6048c2ecf20Sopenharmony_ci#if 0
6058c2ecf20Sopenharmony_ci			/* needs to use sg? */
6068c2ecf20Sopenharmony_ci			if ((cmd->cmnd[0] == 0 || cmd->cmnd[0] == 0x12 || cmd->cmnd[0] == 3)
6078c2ecf20Sopenharmony_ci			    && cmd->request_buffer != 0) {
6088c2ecf20Sopenharmony_ci				unsigned char *b = cmd->request_buffer;
6098c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "buffer = %x %x %x %x %x %x %x %x\n",
6108c2ecf20Sopenharmony_ci				       b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]);
6118c2ecf20Sopenharmony_ci			}
6128c2ecf20Sopenharmony_ci#endif
6138c2ecf20Sopenharmony_ci		}
6148c2ecf20Sopenharmony_ci		cmd->SCp.this_residual -= ms->data_ptr;
6158c2ecf20Sopenharmony_ci		mesh_completed(ms, cmd);
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci	if (start_next) {
6188c2ecf20Sopenharmony_ci		out_8(&ms->mesh->sequence, SEQ_ENBRESEL);
6198c2ecf20Sopenharmony_ci		mesh_flush_io(ms->mesh);
6208c2ecf20Sopenharmony_ci		udelay(1);
6218c2ecf20Sopenharmony_ci		ms->phase = idle;
6228c2ecf20Sopenharmony_ci		mesh_start(ms);
6238c2ecf20Sopenharmony_ci	}
6248c2ecf20Sopenharmony_ci}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic inline void add_sdtr_msg(struct mesh_state *ms)
6278c2ecf20Sopenharmony_ci{
6288c2ecf20Sopenharmony_ci	int i = ms->n_msgout;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	ms->msgout[i] = EXTENDED_MESSAGE;
6318c2ecf20Sopenharmony_ci	ms->msgout[i+1] = 3;
6328c2ecf20Sopenharmony_ci	ms->msgout[i+2] = EXTENDED_SDTR;
6338c2ecf20Sopenharmony_ci	ms->msgout[i+3] = mesh_sync_period/4;
6348c2ecf20Sopenharmony_ci	ms->msgout[i+4] = (ALLOW_SYNC(ms->conn_tgt)? mesh_sync_offset: 0);
6358c2ecf20Sopenharmony_ci	ms->n_msgout = i + 5;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_cistatic void set_sdtr(struct mesh_state *ms, int period, int offset)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
6418c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
6428c2ecf20Sopenharmony_ci	int v, tr;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	tp->sdtr_state = sdtr_done;
6458c2ecf20Sopenharmony_ci	if (offset == 0) {
6468c2ecf20Sopenharmony_ci		/* asynchronous */
6478c2ecf20Sopenharmony_ci		if (SYNC_OFF(tp->sync_params))
6488c2ecf20Sopenharmony_ci			printk(KERN_INFO "mesh: target %d now asynchronous\n",
6498c2ecf20Sopenharmony_ci			       ms->conn_tgt);
6508c2ecf20Sopenharmony_ci		tp->sync_params = ASYNC_PARAMS;
6518c2ecf20Sopenharmony_ci		out_8(&mr->sync_params, ASYNC_PARAMS);
6528c2ecf20Sopenharmony_ci		return;
6538c2ecf20Sopenharmony_ci	}
6548c2ecf20Sopenharmony_ci	/*
6558c2ecf20Sopenharmony_ci	 * We need to compute ceil(clk_freq * period / 500e6) - 2
6568c2ecf20Sopenharmony_ci	 * without incurring overflow.
6578c2ecf20Sopenharmony_ci	 */
6588c2ecf20Sopenharmony_ci	v = (ms->clk_freq / 5000) * period;
6598c2ecf20Sopenharmony_ci	if (v <= 250000) {
6608c2ecf20Sopenharmony_ci		/* special case: sync_period == 5 * clk_period */
6618c2ecf20Sopenharmony_ci		v = 0;
6628c2ecf20Sopenharmony_ci		/* units of tr are 100kB/s */
6638c2ecf20Sopenharmony_ci		tr = (ms->clk_freq + 250000) / 500000;
6648c2ecf20Sopenharmony_ci	} else {
6658c2ecf20Sopenharmony_ci		/* sync_period == (v + 2) * 2 * clk_period */
6668c2ecf20Sopenharmony_ci		v = (v + 99999) / 100000 - 2;
6678c2ecf20Sopenharmony_ci		if (v > 15)
6688c2ecf20Sopenharmony_ci			v = 15;	/* oops */
6698c2ecf20Sopenharmony_ci		tr = ((ms->clk_freq / (v + 2)) + 199999) / 200000;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci	if (offset > 15)
6728c2ecf20Sopenharmony_ci		offset = 15;	/* can't happen */
6738c2ecf20Sopenharmony_ci	tp->sync_params = SYNC_PARAMS(offset, v);
6748c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, tp->sync_params);
6758c2ecf20Sopenharmony_ci	printk(KERN_INFO "mesh: target %d synchronous at %d.%d MB/s\n",
6768c2ecf20Sopenharmony_ci	       ms->conn_tgt, tr/10, tr%10);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic void start_phase(struct mesh_state *ms)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	int i, seq, nb;
6828c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
6838c2ecf20Sopenharmony_ci	volatile struct dbdma_regs __iomem *md = ms->dma;
6848c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = ms->current_req;
6858c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	dlog(ms, "start_phase nmo/exc/fc/seq = %.8x",
6888c2ecf20Sopenharmony_ci	     MKWORD(ms->n_msgout, mr->exception, mr->fifo_count, mr->sequence));
6898c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
6908c2ecf20Sopenharmony_ci	seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
6918c2ecf20Sopenharmony_ci	switch (ms->msgphase) {
6928c2ecf20Sopenharmony_ci	case msg_none:
6938c2ecf20Sopenharmony_ci		break;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	case msg_in:
6968c2ecf20Sopenharmony_ci		out_8(&mr->count_hi, 0);
6978c2ecf20Sopenharmony_ci		out_8(&mr->count_lo, 1);
6988c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_MSGIN + seq);
6998c2ecf20Sopenharmony_ci		ms->n_msgin = 0;
7008c2ecf20Sopenharmony_ci		return;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	case msg_out:
7038c2ecf20Sopenharmony_ci		/*
7048c2ecf20Sopenharmony_ci		 * To make sure ATN drops before we assert ACK for
7058c2ecf20Sopenharmony_ci		 * the last byte of the message, we have to do the
7068c2ecf20Sopenharmony_ci		 * last byte specially.
7078c2ecf20Sopenharmony_ci		 */
7088c2ecf20Sopenharmony_ci		if (ms->n_msgout <= 0) {
7098c2ecf20Sopenharmony_ci			printk(KERN_ERR "mesh: msg_out but n_msgout=%d\n",
7108c2ecf20Sopenharmony_ci			       ms->n_msgout);
7118c2ecf20Sopenharmony_ci			mesh_dump_regs(ms);
7128c2ecf20Sopenharmony_ci			ms->msgphase = msg_none;
7138c2ecf20Sopenharmony_ci			break;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci		if (ALLOW_DEBUG(ms->conn_tgt)) {
7168c2ecf20Sopenharmony_ci			printk(KERN_DEBUG "mesh: sending %d msg bytes:",
7178c2ecf20Sopenharmony_ci			       ms->n_msgout);
7188c2ecf20Sopenharmony_ci			for (i = 0; i < ms->n_msgout; ++i)
7198c2ecf20Sopenharmony_ci				printk(" %x", ms->msgout[i]);
7208c2ecf20Sopenharmony_ci			printk("\n");
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci		dlog(ms, "msgout msg=%.8x", MKWORD(ms->n_msgout, ms->msgout[0],
7238c2ecf20Sopenharmony_ci						ms->msgout[1], ms->msgout[2]));
7248c2ecf20Sopenharmony_ci		out_8(&mr->count_hi, 0);
7258c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_FLUSHFIFO);
7268c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
7278c2ecf20Sopenharmony_ci		udelay(1);
7288c2ecf20Sopenharmony_ci		/*
7298c2ecf20Sopenharmony_ci		 * If ATN is not already asserted, we assert it, then
7308c2ecf20Sopenharmony_ci		 * issue a SEQ_MSGOUT to get the mesh to drop ACK.
7318c2ecf20Sopenharmony_ci		 */
7328c2ecf20Sopenharmony_ci		if ((in_8(&mr->bus_status0) & BS0_ATN) == 0) {
7338c2ecf20Sopenharmony_ci			dlog(ms, "bus0 was %.2x explicitly asserting ATN", mr->bus_status0);
7348c2ecf20Sopenharmony_ci			out_8(&mr->bus_status0, BS0_ATN); /* explicit ATN */
7358c2ecf20Sopenharmony_ci			mesh_flush_io(mr);
7368c2ecf20Sopenharmony_ci			udelay(1);
7378c2ecf20Sopenharmony_ci			out_8(&mr->count_lo, 1);
7388c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_MSGOUT + seq);
7398c2ecf20Sopenharmony_ci			out_8(&mr->bus_status0, 0); /* release explicit ATN */
7408c2ecf20Sopenharmony_ci			dlog(ms,"hace: after explicit ATN bus0=%.2x",mr->bus_status0);
7418c2ecf20Sopenharmony_ci		}
7428c2ecf20Sopenharmony_ci		if (ms->n_msgout == 1) {
7438c2ecf20Sopenharmony_ci			/*
7448c2ecf20Sopenharmony_ci			 * We can't issue the SEQ_MSGOUT without ATN
7458c2ecf20Sopenharmony_ci			 * until the target has asserted REQ.  The logic
7468c2ecf20Sopenharmony_ci			 * in cmd_complete handles both situations:
7478c2ecf20Sopenharmony_ci			 * REQ already asserted or not.
7488c2ecf20Sopenharmony_ci			 */
7498c2ecf20Sopenharmony_ci			cmd_complete(ms);
7508c2ecf20Sopenharmony_ci		} else {
7518c2ecf20Sopenharmony_ci			out_8(&mr->count_lo, ms->n_msgout - 1);
7528c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_MSGOUT + seq);
7538c2ecf20Sopenharmony_ci			for (i = 0; i < ms->n_msgout - 1; ++i)
7548c2ecf20Sopenharmony_ci				out_8(&mr->fifo, ms->msgout[i]);
7558c2ecf20Sopenharmony_ci		}
7568c2ecf20Sopenharmony_ci		return;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	default:
7598c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh bug: start_phase msgphase=%d\n",
7608c2ecf20Sopenharmony_ci		       ms->msgphase);
7618c2ecf20Sopenharmony_ci	}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	switch (ms->phase) {
7648c2ecf20Sopenharmony_ci	case selecting:
7658c2ecf20Sopenharmony_ci		out_8(&mr->dest_id, ms->conn_tgt);
7668c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_SELECT + SEQ_ATN);
7678c2ecf20Sopenharmony_ci		break;
7688c2ecf20Sopenharmony_ci	case commanding:
7698c2ecf20Sopenharmony_ci		out_8(&mr->sync_params, tp->sync_params);
7708c2ecf20Sopenharmony_ci		out_8(&mr->count_hi, 0);
7718c2ecf20Sopenharmony_ci		if (cmd) {
7728c2ecf20Sopenharmony_ci			out_8(&mr->count_lo, cmd->cmd_len);
7738c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_COMMAND + seq);
7748c2ecf20Sopenharmony_ci			for (i = 0; i < cmd->cmd_len; ++i)
7758c2ecf20Sopenharmony_ci				out_8(&mr->fifo, cmd->cmnd[i]);
7768c2ecf20Sopenharmony_ci		} else {
7778c2ecf20Sopenharmony_ci			out_8(&mr->count_lo, 6);
7788c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_COMMAND + seq);
7798c2ecf20Sopenharmony_ci			for (i = 0; i < 6; ++i)
7808c2ecf20Sopenharmony_ci				out_8(&mr->fifo, 0);
7818c2ecf20Sopenharmony_ci		}
7828c2ecf20Sopenharmony_ci		break;
7838c2ecf20Sopenharmony_ci	case dataing:
7848c2ecf20Sopenharmony_ci		/* transfer data, if any */
7858c2ecf20Sopenharmony_ci		if (!ms->dma_started) {
7868c2ecf20Sopenharmony_ci			set_dma_cmds(ms, cmd);
7878c2ecf20Sopenharmony_ci			out_le32(&md->cmdptr, virt_to_phys(ms->dma_cmds));
7888c2ecf20Sopenharmony_ci			out_le32(&md->control, (RUN << 16) | RUN);
7898c2ecf20Sopenharmony_ci			ms->dma_started = 1;
7908c2ecf20Sopenharmony_ci		}
7918c2ecf20Sopenharmony_ci		nb = ms->dma_count;
7928c2ecf20Sopenharmony_ci		if (nb > 0xfff0)
7938c2ecf20Sopenharmony_ci			nb = 0xfff0;
7948c2ecf20Sopenharmony_ci		ms->dma_count -= nb;
7958c2ecf20Sopenharmony_ci		ms->data_ptr += nb;
7968c2ecf20Sopenharmony_ci		out_8(&mr->count_lo, nb);
7978c2ecf20Sopenharmony_ci		out_8(&mr->count_hi, nb >> 8);
7988c2ecf20Sopenharmony_ci		out_8(&mr->sequence, (tp->data_goes_out?
7998c2ecf20Sopenharmony_ci				SEQ_DATAOUT: SEQ_DATAIN) + SEQ_DMA_MODE + seq);
8008c2ecf20Sopenharmony_ci		break;
8018c2ecf20Sopenharmony_ci	case statusing:
8028c2ecf20Sopenharmony_ci		out_8(&mr->count_hi, 0);
8038c2ecf20Sopenharmony_ci		out_8(&mr->count_lo, 1);
8048c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_STATUS + seq);
8058c2ecf20Sopenharmony_ci		break;
8068c2ecf20Sopenharmony_ci	case busfreeing:
8078c2ecf20Sopenharmony_ci	case disconnecting:
8088c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_ENBRESEL);
8098c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
8108c2ecf20Sopenharmony_ci		udelay(1);
8118c2ecf20Sopenharmony_ci		dlog(ms, "enbresel intr/exc/err/fc=%.8x",
8128c2ecf20Sopenharmony_ci		     MKWORD(mr->interrupt, mr->exception, mr->error,
8138c2ecf20Sopenharmony_ci			    mr->fifo_count));
8148c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_BUSFREE);
8158c2ecf20Sopenharmony_ci		break;
8168c2ecf20Sopenharmony_ci	default:
8178c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: start_phase called with phase=%d\n",
8188c2ecf20Sopenharmony_ci		       ms->phase);
8198c2ecf20Sopenharmony_ci		dumpslog(ms);
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_cistatic inline void get_msgin(struct mesh_state *ms)
8258c2ecf20Sopenharmony_ci{
8268c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
8278c2ecf20Sopenharmony_ci	int i, n;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	n = mr->fifo_count;
8308c2ecf20Sopenharmony_ci	if (n != 0) {
8318c2ecf20Sopenharmony_ci		i = ms->n_msgin;
8328c2ecf20Sopenharmony_ci		ms->n_msgin = i + n;
8338c2ecf20Sopenharmony_ci		for (; n > 0; --n)
8348c2ecf20Sopenharmony_ci			ms->msgin[i++] = in_8(&mr->fifo);
8358c2ecf20Sopenharmony_ci	}
8368c2ecf20Sopenharmony_ci}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_cistatic inline int msgin_length(struct mesh_state *ms)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	int b, n;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	n = 1;
8438c2ecf20Sopenharmony_ci	if (ms->n_msgin > 0) {
8448c2ecf20Sopenharmony_ci		b = ms->msgin[0];
8458c2ecf20Sopenharmony_ci		if (b == 1) {
8468c2ecf20Sopenharmony_ci			/* extended message */
8478c2ecf20Sopenharmony_ci			n = ms->n_msgin < 2? 2: ms->msgin[1] + 2;
8488c2ecf20Sopenharmony_ci		} else if (0x20 <= b && b <= 0x2f) {
8498c2ecf20Sopenharmony_ci			/* 2-byte message */
8508c2ecf20Sopenharmony_ci			n = 2;
8518c2ecf20Sopenharmony_ci		}
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci	return n;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_cistatic void reselected(struct mesh_state *ms)
8578c2ecf20Sopenharmony_ci{
8588c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
8598c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd;
8608c2ecf20Sopenharmony_ci	struct mesh_target *tp;
8618c2ecf20Sopenharmony_ci	int b, t, prev;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	switch (ms->phase) {
8648c2ecf20Sopenharmony_ci	case idle:
8658c2ecf20Sopenharmony_ci		break;
8668c2ecf20Sopenharmony_ci	case arbitrating:
8678c2ecf20Sopenharmony_ci		if ((cmd = ms->current_req) != NULL) {
8688c2ecf20Sopenharmony_ci			/* put the command back on the queue */
8698c2ecf20Sopenharmony_ci			cmd->host_scribble = (void *) ms->request_q;
8708c2ecf20Sopenharmony_ci			if (ms->request_q == NULL)
8718c2ecf20Sopenharmony_ci				ms->request_qtail = cmd;
8728c2ecf20Sopenharmony_ci			ms->request_q = cmd;
8738c2ecf20Sopenharmony_ci			tp = &ms->tgts[cmd->device->id];
8748c2ecf20Sopenharmony_ci			tp->current_req = NULL;
8758c2ecf20Sopenharmony_ci		}
8768c2ecf20Sopenharmony_ci		break;
8778c2ecf20Sopenharmony_ci	case busfreeing:
8788c2ecf20Sopenharmony_ci		ms->phase = reselecting;
8798c2ecf20Sopenharmony_ci		mesh_done(ms, 0);
8808c2ecf20Sopenharmony_ci		break;
8818c2ecf20Sopenharmony_ci	case disconnecting:
8828c2ecf20Sopenharmony_ci		break;
8838c2ecf20Sopenharmony_ci	default:
8848c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: reselected in phase %d/%d tgt %d\n",
8858c2ecf20Sopenharmony_ci		       ms->msgphase, ms->phase, ms->conn_tgt);
8868c2ecf20Sopenharmony_ci		dumplog(ms, ms->conn_tgt);
8878c2ecf20Sopenharmony_ci		dumpslog(ms);
8888c2ecf20Sopenharmony_ci	}
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (ms->dma_started) {
8918c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: reselected with DMA started !\n");
8928c2ecf20Sopenharmony_ci		halt_dma(ms);
8938c2ecf20Sopenharmony_ci	}
8948c2ecf20Sopenharmony_ci	ms->current_req = NULL;
8958c2ecf20Sopenharmony_ci	ms->phase = dataing;
8968c2ecf20Sopenharmony_ci	ms->msgphase = msg_in;
8978c2ecf20Sopenharmony_ci	ms->n_msgout = 0;
8988c2ecf20Sopenharmony_ci	ms->last_n_msgout = 0;
8998c2ecf20Sopenharmony_ci	prev = ms->conn_tgt;
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	/*
9028c2ecf20Sopenharmony_ci	 * We seem to get abortive reselections sometimes.
9038c2ecf20Sopenharmony_ci	 */
9048c2ecf20Sopenharmony_ci	while ((in_8(&mr->bus_status1) & BS1_BSY) == 0) {
9058c2ecf20Sopenharmony_ci		static int mesh_aborted_resels;
9068c2ecf20Sopenharmony_ci		mesh_aborted_resels++;
9078c2ecf20Sopenharmony_ci		out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
9088c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
9098c2ecf20Sopenharmony_ci		udelay(1);
9108c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_ENBRESEL);
9118c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
9128c2ecf20Sopenharmony_ci		udelay(5);
9138c2ecf20Sopenharmony_ci		dlog(ms, "extra resel err/exc/fc = %.6x",
9148c2ecf20Sopenharmony_ci		     MKWORD(0, mr->error, mr->exception, mr->fifo_count));
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
9178c2ecf20Sopenharmony_ci       	mesh_flush_io(mr);
9188c2ecf20Sopenharmony_ci	udelay(1);
9198c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_ENBRESEL);
9208c2ecf20Sopenharmony_ci       	mesh_flush_io(mr);
9218c2ecf20Sopenharmony_ci	udelay(1);
9228c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, ASYNC_PARAMS);
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci	/*
9258c2ecf20Sopenharmony_ci	 * Find out who reselected us.
9268c2ecf20Sopenharmony_ci	 */
9278c2ecf20Sopenharmony_ci	if (in_8(&mr->fifo_count) == 0) {
9288c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: reselection but nothing in fifo?\n");
9298c2ecf20Sopenharmony_ci		ms->conn_tgt = ms->host->this_id;
9308c2ecf20Sopenharmony_ci		goto bogus;
9318c2ecf20Sopenharmony_ci	}
9328c2ecf20Sopenharmony_ci	/* get the last byte in the fifo */
9338c2ecf20Sopenharmony_ci	do {
9348c2ecf20Sopenharmony_ci		b = in_8(&mr->fifo);
9358c2ecf20Sopenharmony_ci		dlog(ms, "reseldata %x", b);
9368c2ecf20Sopenharmony_ci	} while (in_8(&mr->fifo_count));
9378c2ecf20Sopenharmony_ci	for (t = 0; t < 8; ++t)
9388c2ecf20Sopenharmony_ci		if ((b & (1 << t)) != 0 && t != ms->host->this_id)
9398c2ecf20Sopenharmony_ci			break;
9408c2ecf20Sopenharmony_ci	if (b != (1 << t) + (1 << ms->host->this_id)) {
9418c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: bad reselection data %x\n", b);
9428c2ecf20Sopenharmony_ci		ms->conn_tgt = ms->host->this_id;
9438c2ecf20Sopenharmony_ci		goto bogus;
9448c2ecf20Sopenharmony_ci	}
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	/*
9488c2ecf20Sopenharmony_ci	 * Set up to continue with that target's transfer.
9498c2ecf20Sopenharmony_ci	 */
9508c2ecf20Sopenharmony_ci	ms->conn_tgt = t;
9518c2ecf20Sopenharmony_ci	tp = &ms->tgts[t];
9528c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, tp->sync_params);
9538c2ecf20Sopenharmony_ci	if (ALLOW_DEBUG(t)) {
9548c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh: reselected by target %d\n", t);
9558c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh: saved_ptr=%x goes_out=%d cmd=%p\n",
9568c2ecf20Sopenharmony_ci		       tp->saved_ptr, tp->data_goes_out, tp->current_req);
9578c2ecf20Sopenharmony_ci	}
9588c2ecf20Sopenharmony_ci	ms->current_req = tp->current_req;
9598c2ecf20Sopenharmony_ci	if (tp->current_req == NULL) {
9608c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: reselected by tgt %d but no cmd!\n", t);
9618c2ecf20Sopenharmony_ci		goto bogus;
9628c2ecf20Sopenharmony_ci	}
9638c2ecf20Sopenharmony_ci	ms->data_ptr = tp->saved_ptr;
9648c2ecf20Sopenharmony_ci	dlog(ms, "resel prev tgt=%d", prev);
9658c2ecf20Sopenharmony_ci	dlog(ms, "resel err/exc=%.4x", MKWORD(0, 0, mr->error, mr->exception));
9668c2ecf20Sopenharmony_ci	start_phase(ms);
9678c2ecf20Sopenharmony_ci	return;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_cibogus:
9708c2ecf20Sopenharmony_ci	dumplog(ms, ms->conn_tgt);
9718c2ecf20Sopenharmony_ci	dumpslog(ms);
9728c2ecf20Sopenharmony_ci	ms->data_ptr = 0;
9738c2ecf20Sopenharmony_ci	ms->aborting = 1;
9748c2ecf20Sopenharmony_ci	start_phase(ms);
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic void do_abort(struct mesh_state *ms)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	ms->msgout[0] = ABORT;
9808c2ecf20Sopenharmony_ci	ms->n_msgout = 1;
9818c2ecf20Sopenharmony_ci	ms->aborting = 1;
9828c2ecf20Sopenharmony_ci	ms->stat = DID_ABORT;
9838c2ecf20Sopenharmony_ci	dlog(ms, "abort", 0);
9848c2ecf20Sopenharmony_ci}
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_cistatic void handle_reset(struct mesh_state *ms)
9878c2ecf20Sopenharmony_ci{
9888c2ecf20Sopenharmony_ci	int tgt;
9898c2ecf20Sopenharmony_ci	struct mesh_target *tp;
9908c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd;
9918c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	for (tgt = 0; tgt < 8; ++tgt) {
9948c2ecf20Sopenharmony_ci		tp = &ms->tgts[tgt];
9958c2ecf20Sopenharmony_ci		if ((cmd = tp->current_req) != NULL) {
9968c2ecf20Sopenharmony_ci			cmd->result = DID_RESET << 16;
9978c2ecf20Sopenharmony_ci			tp->current_req = NULL;
9988c2ecf20Sopenharmony_ci			mesh_completed(ms, cmd);
9998c2ecf20Sopenharmony_ci		}
10008c2ecf20Sopenharmony_ci		ms->tgts[tgt].sdtr_state = do_sdtr;
10018c2ecf20Sopenharmony_ci		ms->tgts[tgt].sync_params = ASYNC_PARAMS;
10028c2ecf20Sopenharmony_ci	}
10038c2ecf20Sopenharmony_ci	ms->current_req = NULL;
10048c2ecf20Sopenharmony_ci	while ((cmd = ms->request_q) != NULL) {
10058c2ecf20Sopenharmony_ci		ms->request_q = (struct scsi_cmnd *) cmd->host_scribble;
10068c2ecf20Sopenharmony_ci		cmd->result = DID_RESET << 16;
10078c2ecf20Sopenharmony_ci		mesh_completed(ms, cmd);
10088c2ecf20Sopenharmony_ci	}
10098c2ecf20Sopenharmony_ci	ms->phase = idle;
10108c2ecf20Sopenharmony_ci	ms->msgphase = msg_none;
10118c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
10128c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_FLUSHFIFO);
10138c2ecf20Sopenharmony_ci       	mesh_flush_io(mr);
10148c2ecf20Sopenharmony_ci	udelay(1);
10158c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, ASYNC_PARAMS);
10168c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_ENBRESEL);
10178c2ecf20Sopenharmony_ci}
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_cistatic irqreturn_t do_mesh_interrupt(int irq, void *dev_id)
10208c2ecf20Sopenharmony_ci{
10218c2ecf20Sopenharmony_ci	unsigned long flags;
10228c2ecf20Sopenharmony_ci	struct mesh_state *ms = dev_id;
10238c2ecf20Sopenharmony_ci	struct Scsi_Host *dev = ms->host;
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci	spin_lock_irqsave(dev->host_lock, flags);
10268c2ecf20Sopenharmony_ci	mesh_interrupt(ms);
10278c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(dev->host_lock, flags);
10288c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
10298c2ecf20Sopenharmony_ci}
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_cistatic void handle_error(struct mesh_state *ms)
10328c2ecf20Sopenharmony_ci{
10338c2ecf20Sopenharmony_ci	int err, exc, count;
10348c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	err = in_8(&mr->error);
10378c2ecf20Sopenharmony_ci	exc = in_8(&mr->exception);
10388c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
10398c2ecf20Sopenharmony_ci	dlog(ms, "error err/exc/fc/cl=%.8x",
10408c2ecf20Sopenharmony_ci	     MKWORD(err, exc, mr->fifo_count, mr->count_lo));
10418c2ecf20Sopenharmony_ci	if (err & ERR_SCSIRESET) {
10428c2ecf20Sopenharmony_ci		/* SCSI bus was reset */
10438c2ecf20Sopenharmony_ci		printk(KERN_INFO "mesh: SCSI bus reset detected: "
10448c2ecf20Sopenharmony_ci		       "waiting for end...");
10458c2ecf20Sopenharmony_ci		while ((in_8(&mr->bus_status1) & BS1_RST) != 0)
10468c2ecf20Sopenharmony_ci			udelay(1);
10478c2ecf20Sopenharmony_ci		printk("done\n");
10488c2ecf20Sopenharmony_ci		if (ms->dma_started)
10498c2ecf20Sopenharmony_ci			halt_dma(ms);
10508c2ecf20Sopenharmony_ci		handle_reset(ms);
10518c2ecf20Sopenharmony_ci		/* request_q is empty, no point in mesh_start() */
10528c2ecf20Sopenharmony_ci		return;
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci	if (err & ERR_UNEXPDISC) {
10558c2ecf20Sopenharmony_ci		/* Unexpected disconnect */
10568c2ecf20Sopenharmony_ci		if (exc & EXC_RESELECTED) {
10578c2ecf20Sopenharmony_ci			reselected(ms);
10588c2ecf20Sopenharmony_ci			return;
10598c2ecf20Sopenharmony_ci		}
10608c2ecf20Sopenharmony_ci		if (!ms->aborting) {
10618c2ecf20Sopenharmony_ci			printk(KERN_WARNING "mesh: target %d aborted\n",
10628c2ecf20Sopenharmony_ci			       ms->conn_tgt);
10638c2ecf20Sopenharmony_ci			dumplog(ms, ms->conn_tgt);
10648c2ecf20Sopenharmony_ci			dumpslog(ms);
10658c2ecf20Sopenharmony_ci		}
10668c2ecf20Sopenharmony_ci		out_8(&mr->interrupt, INT_CMDDONE);
10678c2ecf20Sopenharmony_ci		ms->stat = DID_ABORT;
10688c2ecf20Sopenharmony_ci		mesh_done(ms, 1);
10698c2ecf20Sopenharmony_ci		return;
10708c2ecf20Sopenharmony_ci	}
10718c2ecf20Sopenharmony_ci	if (err & ERR_PARITY) {
10728c2ecf20Sopenharmony_ci		if (ms->msgphase == msg_in) {
10738c2ecf20Sopenharmony_ci			printk(KERN_ERR "mesh: msg parity error, target %d\n",
10748c2ecf20Sopenharmony_ci			       ms->conn_tgt);
10758c2ecf20Sopenharmony_ci			ms->msgout[0] = MSG_PARITY_ERROR;
10768c2ecf20Sopenharmony_ci			ms->n_msgout = 1;
10778c2ecf20Sopenharmony_ci			ms->msgphase = msg_in_bad;
10788c2ecf20Sopenharmony_ci			cmd_complete(ms);
10798c2ecf20Sopenharmony_ci			return;
10808c2ecf20Sopenharmony_ci		}
10818c2ecf20Sopenharmony_ci		if (ms->stat == DID_OK) {
10828c2ecf20Sopenharmony_ci			printk(KERN_ERR "mesh: parity error, target %d\n",
10838c2ecf20Sopenharmony_ci			       ms->conn_tgt);
10848c2ecf20Sopenharmony_ci			ms->stat = DID_PARITY;
10858c2ecf20Sopenharmony_ci		}
10868c2ecf20Sopenharmony_ci		count = (mr->count_hi << 8) + mr->count_lo;
10878c2ecf20Sopenharmony_ci		if (count == 0) {
10888c2ecf20Sopenharmony_ci			cmd_complete(ms);
10898c2ecf20Sopenharmony_ci		} else {
10908c2ecf20Sopenharmony_ci			/* reissue the data transfer command */
10918c2ecf20Sopenharmony_ci			out_8(&mr->sequence, mr->sequence);
10928c2ecf20Sopenharmony_ci		}
10938c2ecf20Sopenharmony_ci		return;
10948c2ecf20Sopenharmony_ci	}
10958c2ecf20Sopenharmony_ci	if (err & ERR_SEQERR) {
10968c2ecf20Sopenharmony_ci		if (exc & EXC_RESELECTED) {
10978c2ecf20Sopenharmony_ci			/* This can happen if we issue a command to
10988c2ecf20Sopenharmony_ci			   get the bus just after the target reselects us. */
10998c2ecf20Sopenharmony_ci			static int mesh_resel_seqerr;
11008c2ecf20Sopenharmony_ci			mesh_resel_seqerr++;
11018c2ecf20Sopenharmony_ci			reselected(ms);
11028c2ecf20Sopenharmony_ci			return;
11038c2ecf20Sopenharmony_ci		}
11048c2ecf20Sopenharmony_ci		if (exc == EXC_PHASEMM) {
11058c2ecf20Sopenharmony_ci			static int mesh_phasemm_seqerr;
11068c2ecf20Sopenharmony_ci			mesh_phasemm_seqerr++;
11078c2ecf20Sopenharmony_ci			phase_mismatch(ms);
11088c2ecf20Sopenharmony_ci			return;
11098c2ecf20Sopenharmony_ci		}
11108c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: sequence error (err=%x exc=%x)\n",
11118c2ecf20Sopenharmony_ci		       err, exc);
11128c2ecf20Sopenharmony_ci	} else {
11138c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: unknown error %x (exc=%x)\n", err, exc);
11148c2ecf20Sopenharmony_ci	}
11158c2ecf20Sopenharmony_ci	mesh_dump_regs(ms);
11168c2ecf20Sopenharmony_ci	dumplog(ms, ms->conn_tgt);
11178c2ecf20Sopenharmony_ci	if (ms->phase > selecting && (in_8(&mr->bus_status1) & BS1_BSY)) {
11188c2ecf20Sopenharmony_ci		/* try to do what the target wants */
11198c2ecf20Sopenharmony_ci		do_abort(ms);
11208c2ecf20Sopenharmony_ci		phase_mismatch(ms);
11218c2ecf20Sopenharmony_ci		return;
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci	ms->stat = DID_ERROR;
11248c2ecf20Sopenharmony_ci	mesh_done(ms, 1);
11258c2ecf20Sopenharmony_ci}
11268c2ecf20Sopenharmony_ci
11278c2ecf20Sopenharmony_cistatic void handle_exception(struct mesh_state *ms)
11288c2ecf20Sopenharmony_ci{
11298c2ecf20Sopenharmony_ci	int exc;
11308c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci	exc = in_8(&mr->exception);
11338c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_EXCEPTION | INT_CMDDONE);
11348c2ecf20Sopenharmony_ci	if (exc & EXC_RESELECTED) {
11358c2ecf20Sopenharmony_ci		static int mesh_resel_exc;
11368c2ecf20Sopenharmony_ci		mesh_resel_exc++;
11378c2ecf20Sopenharmony_ci		reselected(ms);
11388c2ecf20Sopenharmony_ci	} else if (exc == EXC_ARBLOST) {
11398c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh: lost arbitration\n");
11408c2ecf20Sopenharmony_ci		ms->stat = DID_BUS_BUSY;
11418c2ecf20Sopenharmony_ci		mesh_done(ms, 1);
11428c2ecf20Sopenharmony_ci	} else if (exc == EXC_SELTO) {
11438c2ecf20Sopenharmony_ci		/* selection timed out */
11448c2ecf20Sopenharmony_ci		ms->stat = DID_BAD_TARGET;
11458c2ecf20Sopenharmony_ci		mesh_done(ms, 1);
11468c2ecf20Sopenharmony_ci	} else if (exc == EXC_PHASEMM) {
11478c2ecf20Sopenharmony_ci		/* target wants to do something different:
11488c2ecf20Sopenharmony_ci		   find out what it wants and do it. */
11498c2ecf20Sopenharmony_ci		phase_mismatch(ms);
11508c2ecf20Sopenharmony_ci	} else {
11518c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: can't cope with exception %x\n", exc);
11528c2ecf20Sopenharmony_ci		mesh_dump_regs(ms);
11538c2ecf20Sopenharmony_ci		dumplog(ms, ms->conn_tgt);
11548c2ecf20Sopenharmony_ci		do_abort(ms);
11558c2ecf20Sopenharmony_ci		phase_mismatch(ms);
11568c2ecf20Sopenharmony_ci	}
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_cistatic void handle_msgin(struct mesh_state *ms)
11608c2ecf20Sopenharmony_ci{
11618c2ecf20Sopenharmony_ci	int i, code;
11628c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = ms->current_req;
11638c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci	if (ms->n_msgin == 0)
11668c2ecf20Sopenharmony_ci		return;
11678c2ecf20Sopenharmony_ci	code = ms->msgin[0];
11688c2ecf20Sopenharmony_ci	if (ALLOW_DEBUG(ms->conn_tgt)) {
11698c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "got %d message bytes:", ms->n_msgin);
11708c2ecf20Sopenharmony_ci		for (i = 0; i < ms->n_msgin; ++i)
11718c2ecf20Sopenharmony_ci			printk(" %x", ms->msgin[i]);
11728c2ecf20Sopenharmony_ci		printk("\n");
11738c2ecf20Sopenharmony_ci	}
11748c2ecf20Sopenharmony_ci	dlog(ms, "msgin msg=%.8x",
11758c2ecf20Sopenharmony_ci	     MKWORD(ms->n_msgin, code, ms->msgin[1], ms->msgin[2]));
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci	ms->expect_reply = 0;
11788c2ecf20Sopenharmony_ci	ms->n_msgout = 0;
11798c2ecf20Sopenharmony_ci	if (ms->n_msgin < msgin_length(ms))
11808c2ecf20Sopenharmony_ci		goto reject;
11818c2ecf20Sopenharmony_ci	if (cmd)
11828c2ecf20Sopenharmony_ci		cmd->SCp.Message = code;
11838c2ecf20Sopenharmony_ci	switch (code) {
11848c2ecf20Sopenharmony_ci	case COMMAND_COMPLETE:
11858c2ecf20Sopenharmony_ci		break;
11868c2ecf20Sopenharmony_ci	case EXTENDED_MESSAGE:
11878c2ecf20Sopenharmony_ci		switch (ms->msgin[2]) {
11888c2ecf20Sopenharmony_ci		case EXTENDED_MODIFY_DATA_POINTER:
11898c2ecf20Sopenharmony_ci			ms->data_ptr += (ms->msgin[3] << 24) + ms->msgin[6]
11908c2ecf20Sopenharmony_ci				+ (ms->msgin[4] << 16) + (ms->msgin[5] << 8);
11918c2ecf20Sopenharmony_ci			break;
11928c2ecf20Sopenharmony_ci		case EXTENDED_SDTR:
11938c2ecf20Sopenharmony_ci			if (tp->sdtr_state != sdtr_sent) {
11948c2ecf20Sopenharmony_ci				/* reply with an SDTR */
11958c2ecf20Sopenharmony_ci				add_sdtr_msg(ms);
11968c2ecf20Sopenharmony_ci				/* limit period to at least his value,
11978c2ecf20Sopenharmony_ci				   offset to no more than his */
11988c2ecf20Sopenharmony_ci				if (ms->msgout[3] < ms->msgin[3])
11998c2ecf20Sopenharmony_ci					ms->msgout[3] = ms->msgin[3];
12008c2ecf20Sopenharmony_ci				if (ms->msgout[4] > ms->msgin[4])
12018c2ecf20Sopenharmony_ci					ms->msgout[4] = ms->msgin[4];
12028c2ecf20Sopenharmony_ci				set_sdtr(ms, ms->msgout[3], ms->msgout[4]);
12038c2ecf20Sopenharmony_ci				ms->msgphase = msg_out;
12048c2ecf20Sopenharmony_ci			} else {
12058c2ecf20Sopenharmony_ci				set_sdtr(ms, ms->msgin[3], ms->msgin[4]);
12068c2ecf20Sopenharmony_ci			}
12078c2ecf20Sopenharmony_ci			break;
12088c2ecf20Sopenharmony_ci		default:
12098c2ecf20Sopenharmony_ci			goto reject;
12108c2ecf20Sopenharmony_ci		}
12118c2ecf20Sopenharmony_ci		break;
12128c2ecf20Sopenharmony_ci	case SAVE_POINTERS:
12138c2ecf20Sopenharmony_ci		tp->saved_ptr = ms->data_ptr;
12148c2ecf20Sopenharmony_ci		break;
12158c2ecf20Sopenharmony_ci	case RESTORE_POINTERS:
12168c2ecf20Sopenharmony_ci		ms->data_ptr = tp->saved_ptr;
12178c2ecf20Sopenharmony_ci		break;
12188c2ecf20Sopenharmony_ci	case DISCONNECT:
12198c2ecf20Sopenharmony_ci		ms->phase = disconnecting;
12208c2ecf20Sopenharmony_ci		break;
12218c2ecf20Sopenharmony_ci	case ABORT:
12228c2ecf20Sopenharmony_ci		break;
12238c2ecf20Sopenharmony_ci	case MESSAGE_REJECT:
12248c2ecf20Sopenharmony_ci		if (tp->sdtr_state == sdtr_sent)
12258c2ecf20Sopenharmony_ci			set_sdtr(ms, 0, 0);
12268c2ecf20Sopenharmony_ci		break;
12278c2ecf20Sopenharmony_ci	case NOP:
12288c2ecf20Sopenharmony_ci		break;
12298c2ecf20Sopenharmony_ci	default:
12308c2ecf20Sopenharmony_ci		if (IDENTIFY_BASE <= code && code <= IDENTIFY_BASE + 7) {
12318c2ecf20Sopenharmony_ci			if (cmd == NULL) {
12328c2ecf20Sopenharmony_ci				do_abort(ms);
12338c2ecf20Sopenharmony_ci				ms->msgphase = msg_out;
12348c2ecf20Sopenharmony_ci			} else if (code != cmd->device->lun + IDENTIFY_BASE) {
12358c2ecf20Sopenharmony_ci				printk(KERN_WARNING "mesh: lun mismatch "
12368c2ecf20Sopenharmony_ci				       "(%d != %llu) on reselection from "
12378c2ecf20Sopenharmony_ci				       "target %d\n", code - IDENTIFY_BASE,
12388c2ecf20Sopenharmony_ci				       cmd->device->lun, ms->conn_tgt);
12398c2ecf20Sopenharmony_ci			}
12408c2ecf20Sopenharmony_ci			break;
12418c2ecf20Sopenharmony_ci		}
12428c2ecf20Sopenharmony_ci		goto reject;
12438c2ecf20Sopenharmony_ci	}
12448c2ecf20Sopenharmony_ci	return;
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci reject:
12478c2ecf20Sopenharmony_ci	printk(KERN_WARNING "mesh: rejecting message from target %d:",
12488c2ecf20Sopenharmony_ci	       ms->conn_tgt);
12498c2ecf20Sopenharmony_ci	for (i = 0; i < ms->n_msgin; ++i)
12508c2ecf20Sopenharmony_ci		printk(" %x", ms->msgin[i]);
12518c2ecf20Sopenharmony_ci	printk("\n");
12528c2ecf20Sopenharmony_ci	ms->msgout[0] = MESSAGE_REJECT;
12538c2ecf20Sopenharmony_ci	ms->n_msgout = 1;
12548c2ecf20Sopenharmony_ci	ms->msgphase = msg_out;
12558c2ecf20Sopenharmony_ci}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci/*
12588c2ecf20Sopenharmony_ci * Set up DMA commands for transferring data.
12598c2ecf20Sopenharmony_ci */
12608c2ecf20Sopenharmony_cistatic void set_dma_cmds(struct mesh_state *ms, struct scsi_cmnd *cmd)
12618c2ecf20Sopenharmony_ci{
12628c2ecf20Sopenharmony_ci	int i, dma_cmd, total, off, dtot;
12638c2ecf20Sopenharmony_ci	struct scatterlist *scl;
12648c2ecf20Sopenharmony_ci	struct dbdma_cmd *dcmds;
12658c2ecf20Sopenharmony_ci
12668c2ecf20Sopenharmony_ci	dma_cmd = ms->tgts[ms->conn_tgt].data_goes_out?
12678c2ecf20Sopenharmony_ci		OUTPUT_MORE: INPUT_MORE;
12688c2ecf20Sopenharmony_ci	dcmds = ms->dma_cmds;
12698c2ecf20Sopenharmony_ci	dtot = 0;
12708c2ecf20Sopenharmony_ci	if (cmd) {
12718c2ecf20Sopenharmony_ci		int nseg;
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci		cmd->SCp.this_residual = scsi_bufflen(cmd);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci		nseg = scsi_dma_map(cmd);
12768c2ecf20Sopenharmony_ci		BUG_ON(nseg < 0);
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci		if (nseg) {
12798c2ecf20Sopenharmony_ci			total = 0;
12808c2ecf20Sopenharmony_ci			off = ms->data_ptr;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci			scsi_for_each_sg(cmd, scl, nseg, i) {
12838c2ecf20Sopenharmony_ci				u32 dma_addr = sg_dma_address(scl);
12848c2ecf20Sopenharmony_ci				u32 dma_len = sg_dma_len(scl);
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci				total += scl->length;
12878c2ecf20Sopenharmony_ci				if (off >= dma_len) {
12888c2ecf20Sopenharmony_ci					off -= dma_len;
12898c2ecf20Sopenharmony_ci					continue;
12908c2ecf20Sopenharmony_ci				}
12918c2ecf20Sopenharmony_ci				if (dma_len > 0xffff)
12928c2ecf20Sopenharmony_ci					panic("mesh: scatterlist element >= 64k");
12938c2ecf20Sopenharmony_ci				dcmds->req_count = cpu_to_le16(dma_len - off);
12948c2ecf20Sopenharmony_ci				dcmds->command = cpu_to_le16(dma_cmd);
12958c2ecf20Sopenharmony_ci				dcmds->phy_addr = cpu_to_le32(dma_addr + off);
12968c2ecf20Sopenharmony_ci				dcmds->xfer_status = 0;
12978c2ecf20Sopenharmony_ci				++dcmds;
12988c2ecf20Sopenharmony_ci				dtot += dma_len - off;
12998c2ecf20Sopenharmony_ci				off = 0;
13008c2ecf20Sopenharmony_ci			}
13018c2ecf20Sopenharmony_ci		}
13028c2ecf20Sopenharmony_ci	}
13038c2ecf20Sopenharmony_ci	if (dtot == 0) {
13048c2ecf20Sopenharmony_ci		/* Either the target has overrun our buffer,
13058c2ecf20Sopenharmony_ci		   or the caller didn't provide a buffer. */
13068c2ecf20Sopenharmony_ci		static char mesh_extra_buf[64];
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci		dtot = sizeof(mesh_extra_buf);
13098c2ecf20Sopenharmony_ci		dcmds->req_count = cpu_to_le16(dtot);
13108c2ecf20Sopenharmony_ci		dcmds->phy_addr = cpu_to_le32(virt_to_phys(mesh_extra_buf));
13118c2ecf20Sopenharmony_ci		dcmds->xfer_status = 0;
13128c2ecf20Sopenharmony_ci		++dcmds;
13138c2ecf20Sopenharmony_ci	}
13148c2ecf20Sopenharmony_ci	dma_cmd += OUTPUT_LAST - OUTPUT_MORE;
13158c2ecf20Sopenharmony_ci	dcmds[-1].command = cpu_to_le16(dma_cmd);
13168c2ecf20Sopenharmony_ci	memset(dcmds, 0, sizeof(*dcmds));
13178c2ecf20Sopenharmony_ci	dcmds->command = cpu_to_le16(DBDMA_STOP);
13188c2ecf20Sopenharmony_ci	ms->dma_count = dtot;
13198c2ecf20Sopenharmony_ci}
13208c2ecf20Sopenharmony_ci
13218c2ecf20Sopenharmony_cistatic void halt_dma(struct mesh_state *ms)
13228c2ecf20Sopenharmony_ci{
13238c2ecf20Sopenharmony_ci	volatile struct dbdma_regs __iomem *md = ms->dma;
13248c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
13258c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = ms->current_req;
13268c2ecf20Sopenharmony_ci	int t, nb;
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	if (!ms->tgts[ms->conn_tgt].data_goes_out) {
13298c2ecf20Sopenharmony_ci		/* wait a little while until the fifo drains */
13308c2ecf20Sopenharmony_ci		t = 50;
13318c2ecf20Sopenharmony_ci		while (t > 0 && in_8(&mr->fifo_count) != 0
13328c2ecf20Sopenharmony_ci		       && (in_le32(&md->status) & ACTIVE) != 0) {
13338c2ecf20Sopenharmony_ci			--t;
13348c2ecf20Sopenharmony_ci			udelay(1);
13358c2ecf20Sopenharmony_ci		}
13368c2ecf20Sopenharmony_ci	}
13378c2ecf20Sopenharmony_ci	out_le32(&md->control, RUN << 16);	/* turn off RUN bit */
13388c2ecf20Sopenharmony_ci	nb = (mr->count_hi << 8) + mr->count_lo;
13398c2ecf20Sopenharmony_ci	dlog(ms, "halt_dma fc/count=%.6x",
13408c2ecf20Sopenharmony_ci	     MKWORD(0, mr->fifo_count, 0, nb));
13418c2ecf20Sopenharmony_ci	if (ms->tgts[ms->conn_tgt].data_goes_out)
13428c2ecf20Sopenharmony_ci		nb += mr->fifo_count;
13438c2ecf20Sopenharmony_ci	/* nb is the number of bytes not yet transferred
13448c2ecf20Sopenharmony_ci	   to/from the target. */
13458c2ecf20Sopenharmony_ci	ms->data_ptr -= nb;
13468c2ecf20Sopenharmony_ci	dlog(ms, "data_ptr %x", ms->data_ptr);
13478c2ecf20Sopenharmony_ci	if (ms->data_ptr < 0) {
13488c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: halt_dma: data_ptr=%d (nb=%d, ms=%p)\n",
13498c2ecf20Sopenharmony_ci		       ms->data_ptr, nb, ms);
13508c2ecf20Sopenharmony_ci		ms->data_ptr = 0;
13518c2ecf20Sopenharmony_ci#ifdef MESH_DBG
13528c2ecf20Sopenharmony_ci		dumplog(ms, ms->conn_tgt);
13538c2ecf20Sopenharmony_ci		dumpslog(ms);
13548c2ecf20Sopenharmony_ci#endif /* MESH_DBG */
13558c2ecf20Sopenharmony_ci	} else if (cmd && scsi_bufflen(cmd) &&
13568c2ecf20Sopenharmony_ci		   ms->data_ptr > scsi_bufflen(cmd)) {
13578c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh: target %d overrun, "
13588c2ecf20Sopenharmony_ci		       "data_ptr=%x total=%x goes_out=%d\n",
13598c2ecf20Sopenharmony_ci		       ms->conn_tgt, ms->data_ptr, scsi_bufflen(cmd),
13608c2ecf20Sopenharmony_ci		       ms->tgts[ms->conn_tgt].data_goes_out);
13618c2ecf20Sopenharmony_ci	}
13628c2ecf20Sopenharmony_ci	if (cmd)
13638c2ecf20Sopenharmony_ci		scsi_dma_unmap(cmd);
13648c2ecf20Sopenharmony_ci	ms->dma_started = 0;
13658c2ecf20Sopenharmony_ci}
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_cistatic void phase_mismatch(struct mesh_state *ms)
13688c2ecf20Sopenharmony_ci{
13698c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
13708c2ecf20Sopenharmony_ci	int phase;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci	dlog(ms, "phasemm ch/cl/seq/fc=%.8x",
13738c2ecf20Sopenharmony_ci	     MKWORD(mr->count_hi, mr->count_lo, mr->sequence, mr->fifo_count));
13748c2ecf20Sopenharmony_ci	phase = in_8(&mr->bus_status0) & BS0_PHASE;
13758c2ecf20Sopenharmony_ci	if (ms->msgphase == msg_out_xxx && phase == BP_MSGOUT) {
13768c2ecf20Sopenharmony_ci		/* output the last byte of the message, without ATN */
13778c2ecf20Sopenharmony_ci		out_8(&mr->count_lo, 1);
13788c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
13798c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
13808c2ecf20Sopenharmony_ci		udelay(1);
13818c2ecf20Sopenharmony_ci		out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
13828c2ecf20Sopenharmony_ci		ms->msgphase = msg_out_last;
13838c2ecf20Sopenharmony_ci		return;
13848c2ecf20Sopenharmony_ci	}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci	if (ms->msgphase == msg_in) {
13878c2ecf20Sopenharmony_ci		get_msgin(ms);
13888c2ecf20Sopenharmony_ci		if (ms->n_msgin)
13898c2ecf20Sopenharmony_ci			handle_msgin(ms);
13908c2ecf20Sopenharmony_ci	}
13918c2ecf20Sopenharmony_ci
13928c2ecf20Sopenharmony_ci	if (ms->dma_started)
13938c2ecf20Sopenharmony_ci		halt_dma(ms);
13948c2ecf20Sopenharmony_ci	if (mr->fifo_count) {
13958c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_FLUSHFIFO);
13968c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
13978c2ecf20Sopenharmony_ci		udelay(1);
13988c2ecf20Sopenharmony_ci	}
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci	ms->msgphase = msg_none;
14018c2ecf20Sopenharmony_ci	switch (phase) {
14028c2ecf20Sopenharmony_ci	case BP_DATAIN:
14038c2ecf20Sopenharmony_ci		ms->tgts[ms->conn_tgt].data_goes_out = 0;
14048c2ecf20Sopenharmony_ci		ms->phase = dataing;
14058c2ecf20Sopenharmony_ci		break;
14068c2ecf20Sopenharmony_ci	case BP_DATAOUT:
14078c2ecf20Sopenharmony_ci		ms->tgts[ms->conn_tgt].data_goes_out = 1;
14088c2ecf20Sopenharmony_ci		ms->phase = dataing;
14098c2ecf20Sopenharmony_ci		break;
14108c2ecf20Sopenharmony_ci	case BP_COMMAND:
14118c2ecf20Sopenharmony_ci		ms->phase = commanding;
14128c2ecf20Sopenharmony_ci		break;
14138c2ecf20Sopenharmony_ci	case BP_STATUS:
14148c2ecf20Sopenharmony_ci		ms->phase = statusing;
14158c2ecf20Sopenharmony_ci		break;
14168c2ecf20Sopenharmony_ci	case BP_MSGIN:
14178c2ecf20Sopenharmony_ci		ms->msgphase = msg_in;
14188c2ecf20Sopenharmony_ci		ms->n_msgin = 0;
14198c2ecf20Sopenharmony_ci		break;
14208c2ecf20Sopenharmony_ci	case BP_MSGOUT:
14218c2ecf20Sopenharmony_ci		ms->msgphase = msg_out;
14228c2ecf20Sopenharmony_ci		if (ms->n_msgout == 0) {
14238c2ecf20Sopenharmony_ci			if (ms->aborting) {
14248c2ecf20Sopenharmony_ci				do_abort(ms);
14258c2ecf20Sopenharmony_ci			} else {
14268c2ecf20Sopenharmony_ci				if (ms->last_n_msgout == 0) {
14278c2ecf20Sopenharmony_ci					printk(KERN_DEBUG
14288c2ecf20Sopenharmony_ci					       "mesh: no msg to repeat\n");
14298c2ecf20Sopenharmony_ci					ms->msgout[0] = NOP;
14308c2ecf20Sopenharmony_ci					ms->last_n_msgout = 1;
14318c2ecf20Sopenharmony_ci				}
14328c2ecf20Sopenharmony_ci				ms->n_msgout = ms->last_n_msgout;
14338c2ecf20Sopenharmony_ci			}
14348c2ecf20Sopenharmony_ci		}
14358c2ecf20Sopenharmony_ci		break;
14368c2ecf20Sopenharmony_ci	default:
14378c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh: unknown scsi phase %x\n", phase);
14388c2ecf20Sopenharmony_ci		ms->stat = DID_ERROR;
14398c2ecf20Sopenharmony_ci		mesh_done(ms, 1);
14408c2ecf20Sopenharmony_ci		return;
14418c2ecf20Sopenharmony_ci	}
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	start_phase(ms);
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic void cmd_complete(struct mesh_state *ms)
14478c2ecf20Sopenharmony_ci{
14488c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
14498c2ecf20Sopenharmony_ci	struct scsi_cmnd *cmd = ms->current_req;
14508c2ecf20Sopenharmony_ci	struct mesh_target *tp = &ms->tgts[ms->conn_tgt];
14518c2ecf20Sopenharmony_ci	int seq, n, t;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	dlog(ms, "cmd_complete fc=%x", mr->fifo_count);
14548c2ecf20Sopenharmony_ci	seq = use_active_neg + (ms->n_msgout? SEQ_ATN: 0);
14558c2ecf20Sopenharmony_ci	switch (ms->msgphase) {
14568c2ecf20Sopenharmony_ci	case msg_out_xxx:
14578c2ecf20Sopenharmony_ci		/* huh?  we expected a phase mismatch */
14588c2ecf20Sopenharmony_ci		ms->n_msgin = 0;
14598c2ecf20Sopenharmony_ci		ms->msgphase = msg_in;
14608c2ecf20Sopenharmony_ci		fallthrough;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	case msg_in:
14638c2ecf20Sopenharmony_ci		/* should have some message bytes in fifo */
14648c2ecf20Sopenharmony_ci		get_msgin(ms);
14658c2ecf20Sopenharmony_ci		n = msgin_length(ms);
14668c2ecf20Sopenharmony_ci		if (ms->n_msgin < n) {
14678c2ecf20Sopenharmony_ci			out_8(&mr->count_lo, n - ms->n_msgin);
14688c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_MSGIN + seq);
14698c2ecf20Sopenharmony_ci		} else {
14708c2ecf20Sopenharmony_ci			ms->msgphase = msg_none;
14718c2ecf20Sopenharmony_ci			handle_msgin(ms);
14728c2ecf20Sopenharmony_ci			start_phase(ms);
14738c2ecf20Sopenharmony_ci		}
14748c2ecf20Sopenharmony_ci		break;
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_ci	case msg_in_bad:
14778c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_FLUSHFIFO);
14788c2ecf20Sopenharmony_ci		mesh_flush_io(mr);
14798c2ecf20Sopenharmony_ci		udelay(1);
14808c2ecf20Sopenharmony_ci		out_8(&mr->count_lo, 1);
14818c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_MSGIN + SEQ_ATN + use_active_neg);
14828c2ecf20Sopenharmony_ci		break;
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	case msg_out:
14858c2ecf20Sopenharmony_ci		/*
14868c2ecf20Sopenharmony_ci		 * To get the right timing on ATN wrt ACK, we have
14878c2ecf20Sopenharmony_ci		 * to get the MESH to drop ACK, wait until REQ gets
14888c2ecf20Sopenharmony_ci		 * asserted, then drop ATN.  To do this we first
14898c2ecf20Sopenharmony_ci		 * issue a SEQ_MSGOUT with ATN and wait for REQ,
14908c2ecf20Sopenharmony_ci		 * then change the command to a SEQ_MSGOUT w/o ATN.
14918c2ecf20Sopenharmony_ci		 * If we don't see REQ in a reasonable time, we
14928c2ecf20Sopenharmony_ci		 * change the command to SEQ_MSGIN with ATN,
14938c2ecf20Sopenharmony_ci		 * wait for the phase mismatch interrupt, then
14948c2ecf20Sopenharmony_ci		 * issue the SEQ_MSGOUT without ATN.
14958c2ecf20Sopenharmony_ci		 */
14968c2ecf20Sopenharmony_ci		out_8(&mr->count_lo, 1);
14978c2ecf20Sopenharmony_ci		out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg + SEQ_ATN);
14988c2ecf20Sopenharmony_ci		t = 30;		/* wait up to 30us */
14998c2ecf20Sopenharmony_ci		while ((in_8(&mr->bus_status0) & BS0_REQ) == 0 && --t >= 0)
15008c2ecf20Sopenharmony_ci			udelay(1);
15018c2ecf20Sopenharmony_ci		dlog(ms, "last_mbyte err/exc/fc/cl=%.8x",
15028c2ecf20Sopenharmony_ci		     MKWORD(mr->error, mr->exception,
15038c2ecf20Sopenharmony_ci			    mr->fifo_count, mr->count_lo));
15048c2ecf20Sopenharmony_ci		if (in_8(&mr->interrupt) & (INT_ERROR | INT_EXCEPTION)) {
15058c2ecf20Sopenharmony_ci			/* whoops, target didn't do what we expected */
15068c2ecf20Sopenharmony_ci			ms->last_n_msgout = ms->n_msgout;
15078c2ecf20Sopenharmony_ci			ms->n_msgout = 0;
15088c2ecf20Sopenharmony_ci			if (in_8(&mr->interrupt) & INT_ERROR) {
15098c2ecf20Sopenharmony_ci				printk(KERN_ERR "mesh: error %x in msg_out\n",
15108c2ecf20Sopenharmony_ci				       in_8(&mr->error));
15118c2ecf20Sopenharmony_ci				handle_error(ms);
15128c2ecf20Sopenharmony_ci				return;
15138c2ecf20Sopenharmony_ci			}
15148c2ecf20Sopenharmony_ci			if (in_8(&mr->exception) != EXC_PHASEMM)
15158c2ecf20Sopenharmony_ci				printk(KERN_ERR "mesh: exc %x in msg_out\n",
15168c2ecf20Sopenharmony_ci				       in_8(&mr->exception));
15178c2ecf20Sopenharmony_ci			else
15188c2ecf20Sopenharmony_ci				printk(KERN_DEBUG "mesh: bs0=%x in msg_out\n",
15198c2ecf20Sopenharmony_ci				       in_8(&mr->bus_status0));
15208c2ecf20Sopenharmony_ci			handle_exception(ms);
15218c2ecf20Sopenharmony_ci			return;
15228c2ecf20Sopenharmony_ci		}
15238c2ecf20Sopenharmony_ci		if (in_8(&mr->bus_status0) & BS0_REQ) {
15248c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_MSGOUT + use_active_neg);
15258c2ecf20Sopenharmony_ci			mesh_flush_io(mr);
15268c2ecf20Sopenharmony_ci			udelay(1);
15278c2ecf20Sopenharmony_ci			out_8(&mr->fifo, ms->msgout[ms->n_msgout-1]);
15288c2ecf20Sopenharmony_ci			ms->msgphase = msg_out_last;
15298c2ecf20Sopenharmony_ci		} else {
15308c2ecf20Sopenharmony_ci			out_8(&mr->sequence, SEQ_MSGIN + use_active_neg + SEQ_ATN);
15318c2ecf20Sopenharmony_ci			ms->msgphase = msg_out_xxx;
15328c2ecf20Sopenharmony_ci		}
15338c2ecf20Sopenharmony_ci		break;
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	case msg_out_last:
15368c2ecf20Sopenharmony_ci		ms->last_n_msgout = ms->n_msgout;
15378c2ecf20Sopenharmony_ci		ms->n_msgout = 0;
15388c2ecf20Sopenharmony_ci		ms->msgphase = ms->expect_reply? msg_in: msg_none;
15398c2ecf20Sopenharmony_ci		start_phase(ms);
15408c2ecf20Sopenharmony_ci		break;
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	case msg_none:
15438c2ecf20Sopenharmony_ci		switch (ms->phase) {
15448c2ecf20Sopenharmony_ci		case idle:
15458c2ecf20Sopenharmony_ci			printk(KERN_ERR "mesh: interrupt in idle phase?\n");
15468c2ecf20Sopenharmony_ci			dumpslog(ms);
15478c2ecf20Sopenharmony_ci			return;
15488c2ecf20Sopenharmony_ci		case selecting:
15498c2ecf20Sopenharmony_ci			dlog(ms, "Selecting phase at command completion",0);
15508c2ecf20Sopenharmony_ci			ms->msgout[0] = IDENTIFY(ALLOW_RESEL(ms->conn_tgt),
15518c2ecf20Sopenharmony_ci						 (cmd? cmd->device->lun: 0));
15528c2ecf20Sopenharmony_ci			ms->n_msgout = 1;
15538c2ecf20Sopenharmony_ci			ms->expect_reply = 0;
15548c2ecf20Sopenharmony_ci			if (ms->aborting) {
15558c2ecf20Sopenharmony_ci				ms->msgout[0] = ABORT;
15568c2ecf20Sopenharmony_ci				ms->n_msgout++;
15578c2ecf20Sopenharmony_ci			} else if (tp->sdtr_state == do_sdtr) {
15588c2ecf20Sopenharmony_ci				/* add SDTR message */
15598c2ecf20Sopenharmony_ci				add_sdtr_msg(ms);
15608c2ecf20Sopenharmony_ci				ms->expect_reply = 1;
15618c2ecf20Sopenharmony_ci				tp->sdtr_state = sdtr_sent;
15628c2ecf20Sopenharmony_ci			}
15638c2ecf20Sopenharmony_ci			ms->msgphase = msg_out;
15648c2ecf20Sopenharmony_ci			/*
15658c2ecf20Sopenharmony_ci			 * We need to wait for REQ before dropping ATN.
15668c2ecf20Sopenharmony_ci			 * We wait for at most 30us, then fall back to
15678c2ecf20Sopenharmony_ci			 * a scheme where we issue a SEQ_COMMAND with ATN,
15688c2ecf20Sopenharmony_ci			 * which will give us a phase mismatch interrupt
15698c2ecf20Sopenharmony_ci			 * when REQ does come, and then we send the message.
15708c2ecf20Sopenharmony_ci			 */
15718c2ecf20Sopenharmony_ci			t = 230;		/* wait up to 230us */
15728c2ecf20Sopenharmony_ci			while ((in_8(&mr->bus_status0) & BS0_REQ) == 0) {
15738c2ecf20Sopenharmony_ci				if (--t < 0) {
15748c2ecf20Sopenharmony_ci					dlog(ms, "impatient for req", ms->n_msgout);
15758c2ecf20Sopenharmony_ci					ms->msgphase = msg_none;
15768c2ecf20Sopenharmony_ci					break;
15778c2ecf20Sopenharmony_ci				}
15788c2ecf20Sopenharmony_ci				udelay(1);
15798c2ecf20Sopenharmony_ci			}
15808c2ecf20Sopenharmony_ci			break;
15818c2ecf20Sopenharmony_ci		case dataing:
15828c2ecf20Sopenharmony_ci			if (ms->dma_count != 0) {
15838c2ecf20Sopenharmony_ci				start_phase(ms);
15848c2ecf20Sopenharmony_ci				return;
15858c2ecf20Sopenharmony_ci			}
15868c2ecf20Sopenharmony_ci			/*
15878c2ecf20Sopenharmony_ci			 * We can get a phase mismatch here if the target
15888c2ecf20Sopenharmony_ci			 * changes to the status phase, even though we have
15898c2ecf20Sopenharmony_ci			 * had a command complete interrupt.  Then, if we
15908c2ecf20Sopenharmony_ci			 * issue the SEQ_STATUS command, we'll get a sequence
15918c2ecf20Sopenharmony_ci			 * error interrupt.  Which isn't so bad except that
15928c2ecf20Sopenharmony_ci			 * occasionally the mesh actually executes the
15938c2ecf20Sopenharmony_ci			 * SEQ_STATUS *as well as* giving us the sequence
15948c2ecf20Sopenharmony_ci			 * error and phase mismatch exception.
15958c2ecf20Sopenharmony_ci			 */
15968c2ecf20Sopenharmony_ci			out_8(&mr->sequence, 0);
15978c2ecf20Sopenharmony_ci			out_8(&mr->interrupt,
15988c2ecf20Sopenharmony_ci			      INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
15998c2ecf20Sopenharmony_ci			halt_dma(ms);
16008c2ecf20Sopenharmony_ci			break;
16018c2ecf20Sopenharmony_ci		case statusing:
16028c2ecf20Sopenharmony_ci			if (cmd) {
16038c2ecf20Sopenharmony_ci				cmd->SCp.Status = mr->fifo;
16048c2ecf20Sopenharmony_ci				if (DEBUG_TARGET(cmd))
16058c2ecf20Sopenharmony_ci					printk(KERN_DEBUG "mesh: status is %x\n",
16068c2ecf20Sopenharmony_ci					       cmd->SCp.Status);
16078c2ecf20Sopenharmony_ci			}
16088c2ecf20Sopenharmony_ci			ms->msgphase = msg_in;
16098c2ecf20Sopenharmony_ci			break;
16108c2ecf20Sopenharmony_ci		case busfreeing:
16118c2ecf20Sopenharmony_ci			mesh_done(ms, 1);
16128c2ecf20Sopenharmony_ci			return;
16138c2ecf20Sopenharmony_ci		case disconnecting:
16148c2ecf20Sopenharmony_ci			ms->current_req = NULL;
16158c2ecf20Sopenharmony_ci			ms->phase = idle;
16168c2ecf20Sopenharmony_ci			mesh_start(ms);
16178c2ecf20Sopenharmony_ci			return;
16188c2ecf20Sopenharmony_ci		default:
16198c2ecf20Sopenharmony_ci			break;
16208c2ecf20Sopenharmony_ci		}
16218c2ecf20Sopenharmony_ci		++ms->phase;
16228c2ecf20Sopenharmony_ci		start_phase(ms);
16238c2ecf20Sopenharmony_ci		break;
16248c2ecf20Sopenharmony_ci	}
16258c2ecf20Sopenharmony_ci}
16268c2ecf20Sopenharmony_ci
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci/*
16298c2ecf20Sopenharmony_ci * Called by midlayer with host locked to queue a new
16308c2ecf20Sopenharmony_ci * request
16318c2ecf20Sopenharmony_ci */
16328c2ecf20Sopenharmony_cistatic int mesh_queue_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
16338c2ecf20Sopenharmony_ci{
16348c2ecf20Sopenharmony_ci	struct mesh_state *ms;
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci	cmd->scsi_done = done;
16378c2ecf20Sopenharmony_ci	cmd->host_scribble = NULL;
16388c2ecf20Sopenharmony_ci
16398c2ecf20Sopenharmony_ci	ms = (struct mesh_state *) cmd->device->host->hostdata;
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_ci	if (ms->request_q == NULL)
16428c2ecf20Sopenharmony_ci		ms->request_q = cmd;
16438c2ecf20Sopenharmony_ci	else
16448c2ecf20Sopenharmony_ci		ms->request_qtail->host_scribble = (void *) cmd;
16458c2ecf20Sopenharmony_ci	ms->request_qtail = cmd;
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_ci	if (ms->phase == idle)
16488c2ecf20Sopenharmony_ci		mesh_start(ms);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	return 0;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(mesh_queue)
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_ci/*
16568c2ecf20Sopenharmony_ci * Called to handle interrupts, either call by the interrupt
16578c2ecf20Sopenharmony_ci * handler (do_mesh_interrupt) or by other functions in
16588c2ecf20Sopenharmony_ci * exceptional circumstances
16598c2ecf20Sopenharmony_ci */
16608c2ecf20Sopenharmony_cistatic void mesh_interrupt(struct mesh_state *ms)
16618c2ecf20Sopenharmony_ci{
16628c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
16638c2ecf20Sopenharmony_ci	int intr;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci#if 0
16668c2ecf20Sopenharmony_ci	if (ALLOW_DEBUG(ms->conn_tgt))
16678c2ecf20Sopenharmony_ci		printk(KERN_DEBUG "mesh_intr, bs0=%x int=%x exc=%x err=%x "
16688c2ecf20Sopenharmony_ci		       "phase=%d msgphase=%d\n", mr->bus_status0,
16698c2ecf20Sopenharmony_ci		       mr->interrupt, mr->exception, mr->error,
16708c2ecf20Sopenharmony_ci		       ms->phase, ms->msgphase);
16718c2ecf20Sopenharmony_ci#endif
16728c2ecf20Sopenharmony_ci	while ((intr = in_8(&mr->interrupt)) != 0) {
16738c2ecf20Sopenharmony_ci		dlog(ms, "interrupt intr/err/exc/seq=%.8x",
16748c2ecf20Sopenharmony_ci		     MKWORD(intr, mr->error, mr->exception, mr->sequence));
16758c2ecf20Sopenharmony_ci		if (intr & INT_ERROR) {
16768c2ecf20Sopenharmony_ci			handle_error(ms);
16778c2ecf20Sopenharmony_ci		} else if (intr & INT_EXCEPTION) {
16788c2ecf20Sopenharmony_ci			handle_exception(ms);
16798c2ecf20Sopenharmony_ci		} else if (intr & INT_CMDDONE) {
16808c2ecf20Sopenharmony_ci			out_8(&mr->interrupt, INT_CMDDONE);
16818c2ecf20Sopenharmony_ci			cmd_complete(ms);
16828c2ecf20Sopenharmony_ci		}
16838c2ecf20Sopenharmony_ci	}
16848c2ecf20Sopenharmony_ci}
16858c2ecf20Sopenharmony_ci
16868c2ecf20Sopenharmony_ci/* Todo: here we can at least try to remove the command from the
16878c2ecf20Sopenharmony_ci * queue if it isn't connected yet, and for pending command, assert
16888c2ecf20Sopenharmony_ci * ATN until the bus gets freed.
16898c2ecf20Sopenharmony_ci */
16908c2ecf20Sopenharmony_cistatic int mesh_abort(struct scsi_cmnd *cmd)
16918c2ecf20Sopenharmony_ci{
16928c2ecf20Sopenharmony_ci	struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
16938c2ecf20Sopenharmony_ci
16948c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "mesh_abort(%p)\n", cmd);
16958c2ecf20Sopenharmony_ci	mesh_dump_regs(ms);
16968c2ecf20Sopenharmony_ci	dumplog(ms, cmd->device->id);
16978c2ecf20Sopenharmony_ci	dumpslog(ms);
16988c2ecf20Sopenharmony_ci	return FAILED;
16998c2ecf20Sopenharmony_ci}
17008c2ecf20Sopenharmony_ci
17018c2ecf20Sopenharmony_ci/*
17028c2ecf20Sopenharmony_ci * Called by the midlayer with the lock held to reset the
17038c2ecf20Sopenharmony_ci * SCSI host and bus.
17048c2ecf20Sopenharmony_ci * The midlayer will wait for devices to come back, we don't need
17058c2ecf20Sopenharmony_ci * to do that ourselves
17068c2ecf20Sopenharmony_ci */
17078c2ecf20Sopenharmony_cistatic int mesh_host_reset(struct scsi_cmnd *cmd)
17088c2ecf20Sopenharmony_ci{
17098c2ecf20Sopenharmony_ci	struct mesh_state *ms = (struct mesh_state *) cmd->device->host->hostdata;
17108c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr = ms->mesh;
17118c2ecf20Sopenharmony_ci	volatile struct dbdma_regs __iomem *md = ms->dma;
17128c2ecf20Sopenharmony_ci	unsigned long flags;
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "mesh_host_reset\n");
17158c2ecf20Sopenharmony_ci
17168c2ecf20Sopenharmony_ci	spin_lock_irqsave(ms->host->host_lock, flags);
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_ci	if (ms->dma_started)
17198c2ecf20Sopenharmony_ci		halt_dma(ms);
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	/* Reset the controller & dbdma channel */
17228c2ecf20Sopenharmony_ci	out_le32(&md->control, (RUN|PAUSE|FLUSH|WAKE) << 16);	/* stop dma */
17238c2ecf20Sopenharmony_ci	out_8(&mr->exception, 0xff);	/* clear all exception bits */
17248c2ecf20Sopenharmony_ci	out_8(&mr->error, 0xff);	/* clear all error bits */
17258c2ecf20Sopenharmony_ci	out_8(&mr->sequence, SEQ_RESETMESH);
17268c2ecf20Sopenharmony_ci       	mesh_flush_io(mr);
17278c2ecf20Sopenharmony_ci	udelay(1);
17288c2ecf20Sopenharmony_ci	out_8(&mr->intr_mask, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
17298c2ecf20Sopenharmony_ci	out_8(&mr->source_id, ms->host->this_id);
17308c2ecf20Sopenharmony_ci	out_8(&mr->sel_timeout, 25);	/* 250ms */
17318c2ecf20Sopenharmony_ci	out_8(&mr->sync_params, ASYNC_PARAMS);
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci	/* Reset the bus */
17348c2ecf20Sopenharmony_ci	out_8(&mr->bus_status1, BS1_RST);	/* assert RST */
17358c2ecf20Sopenharmony_ci       	mesh_flush_io(mr);
17368c2ecf20Sopenharmony_ci	udelay(30);			/* leave it on for >= 25us */
17378c2ecf20Sopenharmony_ci	out_8(&mr->bus_status1, 0);	/* negate RST */
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci	/* Complete pending commands */
17408c2ecf20Sopenharmony_ci	handle_reset(ms);
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ms->host->host_lock, flags);
17438c2ecf20Sopenharmony_ci	return SUCCESS;
17448c2ecf20Sopenharmony_ci}
17458c2ecf20Sopenharmony_ci
17468c2ecf20Sopenharmony_cistatic void set_mesh_power(struct mesh_state *ms, int state)
17478c2ecf20Sopenharmony_ci{
17488c2ecf20Sopenharmony_ci	if (!machine_is(powermac))
17498c2ecf20Sopenharmony_ci		return;
17508c2ecf20Sopenharmony_ci	if (state) {
17518c2ecf20Sopenharmony_ci		pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1);
17528c2ecf20Sopenharmony_ci		msleep(200);
17538c2ecf20Sopenharmony_ci	} else {
17548c2ecf20Sopenharmony_ci		pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
17558c2ecf20Sopenharmony_ci		msleep(10);
17568c2ecf20Sopenharmony_ci	}
17578c2ecf20Sopenharmony_ci}
17588c2ecf20Sopenharmony_ci
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
17618c2ecf20Sopenharmony_cistatic int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
17628c2ecf20Sopenharmony_ci{
17638c2ecf20Sopenharmony_ci	struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
17648c2ecf20Sopenharmony_ci	unsigned long flags;
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_ci	switch (mesg.event) {
17678c2ecf20Sopenharmony_ci	case PM_EVENT_SUSPEND:
17688c2ecf20Sopenharmony_ci	case PM_EVENT_HIBERNATE:
17698c2ecf20Sopenharmony_ci	case PM_EVENT_FREEZE:
17708c2ecf20Sopenharmony_ci		break;
17718c2ecf20Sopenharmony_ci	default:
17728c2ecf20Sopenharmony_ci		return 0;
17738c2ecf20Sopenharmony_ci	}
17748c2ecf20Sopenharmony_ci	if (ms->phase == sleeping)
17758c2ecf20Sopenharmony_ci		return 0;
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	scsi_block_requests(ms->host);
17788c2ecf20Sopenharmony_ci	spin_lock_irqsave(ms->host->host_lock, flags);
17798c2ecf20Sopenharmony_ci	while(ms->phase != idle) {
17808c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(ms->host->host_lock, flags);
17818c2ecf20Sopenharmony_ci		msleep(10);
17828c2ecf20Sopenharmony_ci		spin_lock_irqsave(ms->host->host_lock, flags);
17838c2ecf20Sopenharmony_ci	}
17848c2ecf20Sopenharmony_ci	ms->phase = sleeping;
17858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ms->host->host_lock, flags);
17868c2ecf20Sopenharmony_ci	disable_irq(ms->meshintr);
17878c2ecf20Sopenharmony_ci	set_mesh_power(ms, 0);
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci	return 0;
17908c2ecf20Sopenharmony_ci}
17918c2ecf20Sopenharmony_ci
17928c2ecf20Sopenharmony_cistatic int mesh_resume(struct macio_dev *mdev)
17938c2ecf20Sopenharmony_ci{
17948c2ecf20Sopenharmony_ci	struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
17958c2ecf20Sopenharmony_ci	unsigned long flags;
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci	if (ms->phase != sleeping)
17988c2ecf20Sopenharmony_ci		return 0;
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci	set_mesh_power(ms, 1);
18018c2ecf20Sopenharmony_ci	mesh_init(ms);
18028c2ecf20Sopenharmony_ci	spin_lock_irqsave(ms->host->host_lock, flags);
18038c2ecf20Sopenharmony_ci	mesh_start(ms);
18048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ms->host->host_lock, flags);
18058c2ecf20Sopenharmony_ci	enable_irq(ms->meshintr);
18068c2ecf20Sopenharmony_ci	scsi_unblock_requests(ms->host);
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_ci	return 0;
18098c2ecf20Sopenharmony_ci}
18108c2ecf20Sopenharmony_ci
18118c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci/*
18148c2ecf20Sopenharmony_ci * If we leave drives set for synchronous transfers (especially
18158c2ecf20Sopenharmony_ci * CDROMs), and reboot to MacOS, it gets confused, poor thing.
18168c2ecf20Sopenharmony_ci * So, on reboot we reset the SCSI bus.
18178c2ecf20Sopenharmony_ci */
18188c2ecf20Sopenharmony_cistatic int mesh_shutdown(struct macio_dev *mdev)
18198c2ecf20Sopenharmony_ci{
18208c2ecf20Sopenharmony_ci	struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
18218c2ecf20Sopenharmony_ci	volatile struct mesh_regs __iomem *mr;
18228c2ecf20Sopenharmony_ci	unsigned long flags;
18238c2ecf20Sopenharmony_ci
18248c2ecf20Sopenharmony_ci       	printk(KERN_INFO "resetting MESH scsi bus(es)\n");
18258c2ecf20Sopenharmony_ci	spin_lock_irqsave(ms->host->host_lock, flags);
18268c2ecf20Sopenharmony_ci       	mr = ms->mesh;
18278c2ecf20Sopenharmony_ci	out_8(&mr->intr_mask, 0);
18288c2ecf20Sopenharmony_ci	out_8(&mr->interrupt, INT_ERROR | INT_EXCEPTION | INT_CMDDONE);
18298c2ecf20Sopenharmony_ci	out_8(&mr->bus_status1, BS1_RST);
18308c2ecf20Sopenharmony_ci	mesh_flush_io(mr);
18318c2ecf20Sopenharmony_ci	udelay(30);
18328c2ecf20Sopenharmony_ci	out_8(&mr->bus_status1, 0);
18338c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(ms->host->host_lock, flags);
18348c2ecf20Sopenharmony_ci
18358c2ecf20Sopenharmony_ci	return 0;
18368c2ecf20Sopenharmony_ci}
18378c2ecf20Sopenharmony_ci
18388c2ecf20Sopenharmony_cistatic struct scsi_host_template mesh_template = {
18398c2ecf20Sopenharmony_ci	.proc_name			= "mesh",
18408c2ecf20Sopenharmony_ci	.name				= "MESH",
18418c2ecf20Sopenharmony_ci	.queuecommand			= mesh_queue,
18428c2ecf20Sopenharmony_ci	.eh_abort_handler		= mesh_abort,
18438c2ecf20Sopenharmony_ci	.eh_host_reset_handler		= mesh_host_reset,
18448c2ecf20Sopenharmony_ci	.can_queue			= 20,
18458c2ecf20Sopenharmony_ci	.this_id			= 7,
18468c2ecf20Sopenharmony_ci	.sg_tablesize			= SG_ALL,
18478c2ecf20Sopenharmony_ci	.cmd_per_lun			= 2,
18488c2ecf20Sopenharmony_ci	.max_segment_size		= 65535,
18498c2ecf20Sopenharmony_ci};
18508c2ecf20Sopenharmony_ci
18518c2ecf20Sopenharmony_cistatic int mesh_probe(struct macio_dev *mdev, const struct of_device_id *match)
18528c2ecf20Sopenharmony_ci{
18538c2ecf20Sopenharmony_ci	struct device_node *mesh = macio_get_of_node(mdev);
18548c2ecf20Sopenharmony_ci	struct pci_dev* pdev = macio_get_pci_dev(mdev);
18558c2ecf20Sopenharmony_ci	int tgt, minper;
18568c2ecf20Sopenharmony_ci	const int *cfp;
18578c2ecf20Sopenharmony_ci	struct mesh_state *ms;
18588c2ecf20Sopenharmony_ci	struct Scsi_Host *mesh_host;
18598c2ecf20Sopenharmony_ci	void *dma_cmd_space;
18608c2ecf20Sopenharmony_ci	dma_addr_t dma_cmd_bus;
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci	switch (mdev->bus->chip->type) {
18638c2ecf20Sopenharmony_ci	case macio_heathrow:
18648c2ecf20Sopenharmony_ci	case macio_gatwick:
18658c2ecf20Sopenharmony_ci	case macio_paddington:
18668c2ecf20Sopenharmony_ci		use_active_neg = 0;
18678c2ecf20Sopenharmony_ci		break;
18688c2ecf20Sopenharmony_ci	default:
18698c2ecf20Sopenharmony_ci		use_active_neg = SEQ_ACTIVE_NEG;
18708c2ecf20Sopenharmony_ci	}
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci	if (macio_resource_count(mdev) != 2 || macio_irq_count(mdev) != 2) {
18738c2ecf20Sopenharmony_ci       		printk(KERN_ERR "mesh: expected 2 addrs and 2 intrs"
18748c2ecf20Sopenharmony_ci	       	       " (got %d,%d)\n", macio_resource_count(mdev),
18758c2ecf20Sopenharmony_ci		       macio_irq_count(mdev));
18768c2ecf20Sopenharmony_ci		return -ENODEV;
18778c2ecf20Sopenharmony_ci	}
18788c2ecf20Sopenharmony_ci
18798c2ecf20Sopenharmony_ci	if (macio_request_resources(mdev, "mesh") != 0) {
18808c2ecf20Sopenharmony_ci       		printk(KERN_ERR "mesh: unable to request memory resources");
18818c2ecf20Sopenharmony_ci		return -EBUSY;
18828c2ecf20Sopenharmony_ci	}
18838c2ecf20Sopenharmony_ci       	mesh_host = scsi_host_alloc(&mesh_template, sizeof(struct mesh_state));
18848c2ecf20Sopenharmony_ci	if (mesh_host == NULL) {
18858c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: couldn't register host");
18868c2ecf20Sopenharmony_ci		goto out_release;
18878c2ecf20Sopenharmony_ci	}
18888c2ecf20Sopenharmony_ci
18898c2ecf20Sopenharmony_ci	/* Old junk for root discovery, that will die ultimately */
18908c2ecf20Sopenharmony_ci#if !defined(MODULE)
18918c2ecf20Sopenharmony_ci       	note_scsi_host(mesh, mesh_host);
18928c2ecf20Sopenharmony_ci#endif
18938c2ecf20Sopenharmony_ci
18948c2ecf20Sopenharmony_ci	mesh_host->base = macio_resource_start(mdev, 0);
18958c2ecf20Sopenharmony_ci	mesh_host->irq = macio_irq(mdev, 0);
18968c2ecf20Sopenharmony_ci       	ms = (struct mesh_state *) mesh_host->hostdata;
18978c2ecf20Sopenharmony_ci	macio_set_drvdata(mdev, ms);
18988c2ecf20Sopenharmony_ci	ms->host = mesh_host;
18998c2ecf20Sopenharmony_ci	ms->mdev = mdev;
19008c2ecf20Sopenharmony_ci	ms->pdev = pdev;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	ms->mesh = ioremap(macio_resource_start(mdev, 0), 0x1000);
19038c2ecf20Sopenharmony_ci	if (ms->mesh == NULL) {
19048c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: can't map registers\n");
19058c2ecf20Sopenharmony_ci		goto out_free;
19068c2ecf20Sopenharmony_ci	}
19078c2ecf20Sopenharmony_ci	ms->dma = ioremap(macio_resource_start(mdev, 1), 0x1000);
19088c2ecf20Sopenharmony_ci	if (ms->dma == NULL) {
19098c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: can't map registers\n");
19108c2ecf20Sopenharmony_ci		iounmap(ms->mesh);
19118c2ecf20Sopenharmony_ci		goto out_free;
19128c2ecf20Sopenharmony_ci	}
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci       	ms->meshintr = macio_irq(mdev, 0);
19158c2ecf20Sopenharmony_ci       	ms->dmaintr = macio_irq(mdev, 1);
19168c2ecf20Sopenharmony_ci
19178c2ecf20Sopenharmony_ci       	/* Space for dma command list: +1 for stop command,
19188c2ecf20Sopenharmony_ci       	 * +1 to allow for aligning.
19198c2ecf20Sopenharmony_ci	 */
19208c2ecf20Sopenharmony_ci	ms->dma_cmd_size = (mesh_host->sg_tablesize + 2) * sizeof(struct dbdma_cmd);
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci	/* We use the PCI APIs for now until the generic one gets fixed
19238c2ecf20Sopenharmony_ci	 * enough or until we get some macio-specific versions
19248c2ecf20Sopenharmony_ci	 */
19258c2ecf20Sopenharmony_ci	dma_cmd_space = dma_alloc_coherent(&macio_get_pci_dev(mdev)->dev,
19268c2ecf20Sopenharmony_ci					   ms->dma_cmd_size, &dma_cmd_bus,
19278c2ecf20Sopenharmony_ci					   GFP_KERNEL);
19288c2ecf20Sopenharmony_ci	if (dma_cmd_space == NULL) {
19298c2ecf20Sopenharmony_ci		printk(KERN_ERR "mesh: can't allocate DMA table\n");
19308c2ecf20Sopenharmony_ci		goto out_unmap;
19318c2ecf20Sopenharmony_ci	}
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	ms->dma_cmds = (struct dbdma_cmd *) DBDMA_ALIGN(dma_cmd_space);
19348c2ecf20Sopenharmony_ci       	ms->dma_cmd_space = dma_cmd_space;
19358c2ecf20Sopenharmony_ci	ms->dma_cmd_bus = dma_cmd_bus + ((unsigned long)ms->dma_cmds)
19368c2ecf20Sopenharmony_ci		- (unsigned long)dma_cmd_space;
19378c2ecf20Sopenharmony_ci	ms->current_req = NULL;
19388c2ecf20Sopenharmony_ci       	for (tgt = 0; tgt < 8; ++tgt) {
19398c2ecf20Sopenharmony_ci	       	ms->tgts[tgt].sdtr_state = do_sdtr;
19408c2ecf20Sopenharmony_ci	       	ms->tgts[tgt].sync_params = ASYNC_PARAMS;
19418c2ecf20Sopenharmony_ci	       	ms->tgts[tgt].current_req = NULL;
19428c2ecf20Sopenharmony_ci       	}
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci	if ((cfp = of_get_property(mesh, "clock-frequency", NULL)))
19458c2ecf20Sopenharmony_ci       		ms->clk_freq = *cfp;
19468c2ecf20Sopenharmony_ci	else {
19478c2ecf20Sopenharmony_ci       		printk(KERN_INFO "mesh: assuming 50MHz clock frequency\n");
19488c2ecf20Sopenharmony_ci	       	ms->clk_freq = 50000000;
19498c2ecf20Sopenharmony_ci       	}
19508c2ecf20Sopenharmony_ci
19518c2ecf20Sopenharmony_ci       	/* The maximum sync rate is clock / 5; increase
19528c2ecf20Sopenharmony_ci       	 * mesh_sync_period if necessary.
19538c2ecf20Sopenharmony_ci	 */
19548c2ecf20Sopenharmony_ci	minper = 1000000000 / (ms->clk_freq / 5); /* ns */
19558c2ecf20Sopenharmony_ci	if (mesh_sync_period < minper)
19568c2ecf20Sopenharmony_ci		mesh_sync_period = minper;
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci	/* Power up the chip */
19598c2ecf20Sopenharmony_ci	set_mesh_power(ms, 1);
19608c2ecf20Sopenharmony_ci
19618c2ecf20Sopenharmony_ci	/* Set it up */
19628c2ecf20Sopenharmony_ci       	mesh_init(ms);
19638c2ecf20Sopenharmony_ci
19648c2ecf20Sopenharmony_ci	/* Request interrupt */
19658c2ecf20Sopenharmony_ci       	if (request_irq(ms->meshintr, do_mesh_interrupt, 0, "MESH", ms)) {
19668c2ecf20Sopenharmony_ci	       	printk(KERN_ERR "MESH: can't get irq %d\n", ms->meshintr);
19678c2ecf20Sopenharmony_ci		goto out_shutdown;
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	/* Add scsi host & scan */
19718c2ecf20Sopenharmony_ci	if (scsi_add_host(mesh_host, &mdev->ofdev.dev))
19728c2ecf20Sopenharmony_ci		goto out_release_irq;
19738c2ecf20Sopenharmony_ci	scsi_scan_host(mesh_host);
19748c2ecf20Sopenharmony_ci
19758c2ecf20Sopenharmony_ci	return 0;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci out_release_irq:
19788c2ecf20Sopenharmony_ci	free_irq(ms->meshintr, ms);
19798c2ecf20Sopenharmony_ci out_shutdown:
19808c2ecf20Sopenharmony_ci	/* shutdown & reset bus in case of error or macos can be confused
19818c2ecf20Sopenharmony_ci	 * at reboot if the bus was set to synchronous mode already
19828c2ecf20Sopenharmony_ci	 */
19838c2ecf20Sopenharmony_ci	mesh_shutdown(mdev);
19848c2ecf20Sopenharmony_ci	set_mesh_power(ms, 0);
19858c2ecf20Sopenharmony_ci	dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
19868c2ecf20Sopenharmony_ci			    ms->dma_cmd_space, ms->dma_cmd_bus);
19878c2ecf20Sopenharmony_ci out_unmap:
19888c2ecf20Sopenharmony_ci	iounmap(ms->dma);
19898c2ecf20Sopenharmony_ci	iounmap(ms->mesh);
19908c2ecf20Sopenharmony_ci out_free:
19918c2ecf20Sopenharmony_ci	scsi_host_put(mesh_host);
19928c2ecf20Sopenharmony_ci out_release:
19938c2ecf20Sopenharmony_ci	macio_release_resources(mdev);
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	return -ENODEV;
19968c2ecf20Sopenharmony_ci}
19978c2ecf20Sopenharmony_ci
19988c2ecf20Sopenharmony_cistatic int mesh_remove(struct macio_dev *mdev)
19998c2ecf20Sopenharmony_ci{
20008c2ecf20Sopenharmony_ci	struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
20018c2ecf20Sopenharmony_ci	struct Scsi_Host *mesh_host = ms->host;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	scsi_remove_host(mesh_host);
20048c2ecf20Sopenharmony_ci
20058c2ecf20Sopenharmony_ci	free_irq(ms->meshintr, ms);
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	/* Reset scsi bus */
20088c2ecf20Sopenharmony_ci	mesh_shutdown(mdev);
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	/* Shut down chip & termination */
20118c2ecf20Sopenharmony_ci	set_mesh_power(ms, 0);
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci	/* Unmap registers & dma controller */
20148c2ecf20Sopenharmony_ci	iounmap(ms->mesh);
20158c2ecf20Sopenharmony_ci       	iounmap(ms->dma);
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	/* Free DMA commands memory */
20188c2ecf20Sopenharmony_ci	dma_free_coherent(&macio_get_pci_dev(mdev)->dev, ms->dma_cmd_size,
20198c2ecf20Sopenharmony_ci			    ms->dma_cmd_space, ms->dma_cmd_bus);
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_ci	/* Release memory resources */
20228c2ecf20Sopenharmony_ci	macio_release_resources(mdev);
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	scsi_host_put(mesh_host);
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_ci	return 0;
20278c2ecf20Sopenharmony_ci}
20288c2ecf20Sopenharmony_ci
20298c2ecf20Sopenharmony_ci
20308c2ecf20Sopenharmony_cistatic struct of_device_id mesh_match[] =
20318c2ecf20Sopenharmony_ci{
20328c2ecf20Sopenharmony_ci	{
20338c2ecf20Sopenharmony_ci	.name 		= "mesh",
20348c2ecf20Sopenharmony_ci	},
20358c2ecf20Sopenharmony_ci	{
20368c2ecf20Sopenharmony_ci	.type		= "scsi",
20378c2ecf20Sopenharmony_ci	.compatible	= "chrp,mesh0"
20388c2ecf20Sopenharmony_ci	},
20398c2ecf20Sopenharmony_ci	{},
20408c2ecf20Sopenharmony_ci};
20418c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE (of, mesh_match);
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_cistatic struct macio_driver mesh_driver =
20448c2ecf20Sopenharmony_ci{
20458c2ecf20Sopenharmony_ci	.driver = {
20468c2ecf20Sopenharmony_ci		.name 		= "mesh",
20478c2ecf20Sopenharmony_ci		.owner		= THIS_MODULE,
20488c2ecf20Sopenharmony_ci		.of_match_table	= mesh_match,
20498c2ecf20Sopenharmony_ci	},
20508c2ecf20Sopenharmony_ci	.probe		= mesh_probe,
20518c2ecf20Sopenharmony_ci	.remove		= mesh_remove,
20528c2ecf20Sopenharmony_ci	.shutdown	= mesh_shutdown,
20538c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
20548c2ecf20Sopenharmony_ci	.suspend	= mesh_suspend,
20558c2ecf20Sopenharmony_ci	.resume		= mesh_resume,
20568c2ecf20Sopenharmony_ci#endif
20578c2ecf20Sopenharmony_ci};
20588c2ecf20Sopenharmony_ci
20598c2ecf20Sopenharmony_ci
20608c2ecf20Sopenharmony_cistatic int __init init_mesh(void)
20618c2ecf20Sopenharmony_ci{
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	/* Calculate sync rate from module parameters */
20648c2ecf20Sopenharmony_ci	if (sync_rate > 10)
20658c2ecf20Sopenharmony_ci		sync_rate = 10;
20668c2ecf20Sopenharmony_ci	if (sync_rate > 0) {
20678c2ecf20Sopenharmony_ci		printk(KERN_INFO "mesh: configured for synchronous %d MB/s\n", sync_rate);
20688c2ecf20Sopenharmony_ci		mesh_sync_period = 1000 / sync_rate;	/* ns */
20698c2ecf20Sopenharmony_ci		mesh_sync_offset = 15;
20708c2ecf20Sopenharmony_ci	} else
20718c2ecf20Sopenharmony_ci		printk(KERN_INFO "mesh: configured for asynchronous\n");
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci	return macio_register_driver(&mesh_driver);
20748c2ecf20Sopenharmony_ci}
20758c2ecf20Sopenharmony_ci
20768c2ecf20Sopenharmony_cistatic void __exit exit_mesh(void)
20778c2ecf20Sopenharmony_ci{
20788c2ecf20Sopenharmony_ci	return macio_unregister_driver(&mesh_driver);
20798c2ecf20Sopenharmony_ci}
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_cimodule_init(init_mesh);
20828c2ecf20Sopenharmony_cimodule_exit(exit_mesh);
2083