18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* qlogicpti.c: Performance Technologies QlogicISP sbus card driver.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 1996, 2006, 2008 David S. Miller (davem@davemloft.net)
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * A lot of this driver was directly stolen from Erik H. Moe's PCI
78c2ecf20Sopenharmony_ci * Qlogic ISP driver.  Mucho kudos to him for this code.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * An even bigger kudos to John Grana at Performance Technologies
108c2ecf20Sopenharmony_ci * for providing me with the hardware to write this driver, you rule
118c2ecf20Sopenharmony_ci * John you really do.
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * May, 2, 1997: Added support for QLGC,isp --jj
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/kernel.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/types.h>
198c2ecf20Sopenharmony_ci#include <linux/string.h>
208c2ecf20Sopenharmony_ci#include <linux/gfp.h>
218c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
228c2ecf20Sopenharmony_ci#include <linux/proc_fs.h>
238c2ecf20Sopenharmony_ci#include <linux/stat.h>
248c2ecf20Sopenharmony_ci#include <linux/init.h>
258c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
268c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
278c2ecf20Sopenharmony_ci#include <linux/module.h>
288c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
298c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
308c2ecf20Sopenharmony_ci#include <linux/of.h>
318c2ecf20Sopenharmony_ci#include <linux/of_device.h>
328c2ecf20Sopenharmony_ci#include <linux/firmware.h>
338c2ecf20Sopenharmony_ci#include <linux/pgtable.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "qlogicpti.h"
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <asm/dma.h>
408c2ecf20Sopenharmony_ci#include <asm/ptrace.h>
418c2ecf20Sopenharmony_ci#include <asm/oplib.h>
428c2ecf20Sopenharmony_ci#include <asm/io.h>
438c2ecf20Sopenharmony_ci#include <asm/irq.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_eh.h>
498c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h>
508c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define MAX_TARGETS	16
538c2ecf20Sopenharmony_ci#define MAX_LUNS	8	/* 32 for 1.31 F/W */
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define DEFAULT_LOOP_COUNT	10000
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic struct qlogicpti *qptichain = NULL;
588c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(qptichain_lock);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci#define PACKB(a, b)			(((a)<<4)|(b))
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic const u_char mbox_param[] = {
638c2ecf20Sopenharmony_ci	PACKB(1, 1),	/* MBOX_NO_OP */
648c2ecf20Sopenharmony_ci	PACKB(5, 5),	/* MBOX_LOAD_RAM */
658c2ecf20Sopenharmony_ci	PACKB(2, 0),	/* MBOX_EXEC_FIRMWARE */
668c2ecf20Sopenharmony_ci	PACKB(5, 5),	/* MBOX_DUMP_RAM */
678c2ecf20Sopenharmony_ci	PACKB(3, 3),	/* MBOX_WRITE_RAM_WORD */
688c2ecf20Sopenharmony_ci	PACKB(2, 3),	/* MBOX_READ_RAM_WORD */
698c2ecf20Sopenharmony_ci	PACKB(6, 6),	/* MBOX_MAILBOX_REG_TEST */
708c2ecf20Sopenharmony_ci	PACKB(2, 3),	/* MBOX_VERIFY_CHECKSUM	*/
718c2ecf20Sopenharmony_ci	PACKB(1, 3),	/* MBOX_ABOUT_FIRMWARE */
728c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x0009 */
738c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x000a */
748c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x000b */
758c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x000c */
768c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x000d */
778c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_CHECK_FIRMWARE */
788c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x000f */
798c2ecf20Sopenharmony_ci	PACKB(5, 5),	/* MBOX_INIT_REQ_QUEUE */
808c2ecf20Sopenharmony_ci	PACKB(6, 6),	/* MBOX_INIT_RES_QUEUE */
818c2ecf20Sopenharmony_ci	PACKB(4, 4),	/* MBOX_EXECUTE_IOCB */
828c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_WAKE_UP	*/
838c2ecf20Sopenharmony_ci	PACKB(1, 6),	/* MBOX_STOP_FIRMWARE */
848c2ecf20Sopenharmony_ci	PACKB(4, 4),	/* MBOX_ABORT */
858c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_ABORT_DEVICE */
868c2ecf20Sopenharmony_ci	PACKB(3, 3),	/* MBOX_ABORT_TARGET */
878c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_BUS_RESET */
888c2ecf20Sopenharmony_ci	PACKB(2, 3),	/* MBOX_STOP_QUEUE */
898c2ecf20Sopenharmony_ci	PACKB(2, 3),	/* MBOX_START_QUEUE */
908c2ecf20Sopenharmony_ci	PACKB(2, 3),	/* MBOX_SINGLE_STEP_QUEUE */
918c2ecf20Sopenharmony_ci	PACKB(2, 3),	/* MBOX_ABORT_QUEUE */
928c2ecf20Sopenharmony_ci	PACKB(2, 4),	/* MBOX_GET_DEV_QUEUE_STATUS */
938c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x001e */
948c2ecf20Sopenharmony_ci	PACKB(1, 3),	/* MBOX_GET_FIRMWARE_STATUS */
958c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_INIT_SCSI_ID */
968c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_SELECT_TIMEOUT */
978c2ecf20Sopenharmony_ci	PACKB(1, 3),	/* MBOX_GET_RETRY_COUNT	*/
988c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_TAG_AGE_LIMIT */
998c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_CLOCK_RATE */
1008c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_ACT_NEG_STATE */
1018c2ecf20Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_ASYNC_DATA_SETUP_TIME */
1028c2ecf20Sopenharmony_ci	PACKB(1, 3),	/* MBOX_GET_SBUS_PARAMS */
1038c2ecf20Sopenharmony_ci	PACKB(2, 4),	/* MBOX_GET_TARGET_PARAMS */
1048c2ecf20Sopenharmony_ci	PACKB(2, 4),	/* MBOX_GET_DEV_QUEUE_PARAMS */
1058c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x002a */
1068c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x002b */
1078c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x002c */
1088c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x002d */
1098c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x002e */
1108c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x002f */
1118c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_INIT_SCSI_ID */
1128c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_SELECT_TIMEOUT */
1138c2ecf20Sopenharmony_ci	PACKB(3, 3),	/* MBOX_SET_RETRY_COUNT	*/
1148c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_TAG_AGE_LIMIT */
1158c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_CLOCK_RATE */
1168c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_ACTIVE_NEG_STATE */
1178c2ecf20Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_ASYNC_DATA_SETUP_TIME */
1188c2ecf20Sopenharmony_ci	PACKB(3, 3),	/* MBOX_SET_SBUS_CONTROL_PARAMS */
1198c2ecf20Sopenharmony_ci	PACKB(4, 4),	/* MBOX_SET_TARGET_PARAMS */
1208c2ecf20Sopenharmony_ci	PACKB(4, 4),	/* MBOX_SET_DEV_QUEUE_PARAMS */
1218c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x003a */
1228c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x003b */
1238c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x003c */
1248c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x003d */
1258c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x003e */
1268c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x003f */
1278c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x0040 */
1288c2ecf20Sopenharmony_ci	PACKB(0, 0),	/* 0x0041 */
1298c2ecf20Sopenharmony_ci	PACKB(0, 0)	/* 0x0042 */
1308c2ecf20Sopenharmony_ci};
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#define MAX_MBOX_COMMAND	ARRAY_SIZE(mbox_param)
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci/* queue length's _must_ be power of two: */
1358c2ecf20Sopenharmony_ci#define QUEUE_DEPTH(in, out, ql)	((in - out) & (ql))
1368c2ecf20Sopenharmony_ci#define REQ_QUEUE_DEPTH(in, out)	QUEUE_DEPTH(in, out, 		     \
1378c2ecf20Sopenharmony_ci						    QLOGICPTI_REQ_QUEUE_LEN)
1388c2ecf20Sopenharmony_ci#define RES_QUEUE_DEPTH(in, out)	QUEUE_DEPTH(in, out, RES_QUEUE_LEN)
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic inline void qlogicpti_enable_irqs(struct qlogicpti *qpti)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	sbus_writew(SBUS_CTRL_ERIRQ | SBUS_CTRL_GENAB,
1438c2ecf20Sopenharmony_ci		    qpti->qregs + SBUS_CTRL);
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic inline void qlogicpti_disable_irqs(struct qlogicpti *qpti)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_CTRL);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic inline void set_sbus_cfg1(struct qlogicpti *qpti)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	u16 val;
1548c2ecf20Sopenharmony_ci	u8 bursts = qpti->bursts;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci#if 0	/* It appears that at least PTI cards do not support
1578c2ecf20Sopenharmony_ci	 * 64-byte bursts and that setting the B64 bit actually
1588c2ecf20Sopenharmony_ci	 * is a nop and the chip ends up using the smallest burst
1598c2ecf20Sopenharmony_ci	 * size. -DaveM
1608c2ecf20Sopenharmony_ci	 */
1618c2ecf20Sopenharmony_ci	if (sbus_can_burst64() && (bursts & DMA_BURST64)) {
1628c2ecf20Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B64);
1638c2ecf20Sopenharmony_ci	} else
1648c2ecf20Sopenharmony_ci#endif
1658c2ecf20Sopenharmony_ci	if (bursts & DMA_BURST32) {
1668c2ecf20Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B32);
1678c2ecf20Sopenharmony_ci	} else if (bursts & DMA_BURST16) {
1688c2ecf20Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B16);
1698c2ecf20Sopenharmony_ci	} else if (bursts & DMA_BURST8) {
1708c2ecf20Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B8);
1718c2ecf20Sopenharmony_ci	} else {
1728c2ecf20Sopenharmony_ci		val = 0; /* No sbus bursts for you... */
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci	sbus_writew(val, qpti->qregs + SBUS_CFG1);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_cistatic int qlogicpti_mbox_command(struct qlogicpti *qpti, u_short param[], int force)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	int loop_count;
1808c2ecf20Sopenharmony_ci	u16 tmp;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (mbox_param[param[0]] == 0)
1838c2ecf20Sopenharmony_ci		return 1;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* Set SBUS semaphore. */
1868c2ecf20Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE);
1878c2ecf20Sopenharmony_ci	tmp |= SBUS_SEMAPHORE_LCK;
1888c2ecf20Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* Wait for host IRQ bit to clear. */
1918c2ecf20Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
1928c2ecf20Sopenharmony_ci	while (--loop_count && (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_HIRQ)) {
1938c2ecf20Sopenharmony_ci		barrier();
1948c2ecf20Sopenharmony_ci		cpu_relax();
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	if (!loop_count)
1978c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command loop timeout #1\n",
1988c2ecf20Sopenharmony_ci		       qpti->qpti_id);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* Write mailbox command registers. */
2018c2ecf20Sopenharmony_ci	switch (mbox_param[param[0]] >> 4) {
2028c2ecf20Sopenharmony_ci	case 6: sbus_writew(param[5], qpti->qregs + MBOX5);
2038c2ecf20Sopenharmony_ci		fallthrough;
2048c2ecf20Sopenharmony_ci	case 5: sbus_writew(param[4], qpti->qregs + MBOX4);
2058c2ecf20Sopenharmony_ci		fallthrough;
2068c2ecf20Sopenharmony_ci	case 4: sbus_writew(param[3], qpti->qregs + MBOX3);
2078c2ecf20Sopenharmony_ci		fallthrough;
2088c2ecf20Sopenharmony_ci	case 3: sbus_writew(param[2], qpti->qregs + MBOX2);
2098c2ecf20Sopenharmony_ci		fallthrough;
2108c2ecf20Sopenharmony_ci	case 2: sbus_writew(param[1], qpti->qregs + MBOX1);
2118c2ecf20Sopenharmony_ci		fallthrough;
2128c2ecf20Sopenharmony_ci	case 1: sbus_writew(param[0], qpti->qregs + MBOX0);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	/* Clear RISC interrupt. */
2168c2ecf20Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + HCCTRL);
2178c2ecf20Sopenharmony_ci	tmp |= HCCTRL_CRIRQ;
2188c2ecf20Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + HCCTRL);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	/* Clear SBUS semaphore. */
2218c2ecf20Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* Set HOST interrupt. */
2248c2ecf20Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + HCCTRL);
2258c2ecf20Sopenharmony_ci	tmp |= HCCTRL_SHIRQ;
2268c2ecf20Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + HCCTRL);
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	/* Wait for HOST interrupt clears. */
2298c2ecf20Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
2308c2ecf20Sopenharmony_ci	while (--loop_count &&
2318c2ecf20Sopenharmony_ci	       (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_CRIRQ))
2328c2ecf20Sopenharmony_ci		udelay(20);
2338c2ecf20Sopenharmony_ci	if (!loop_count)
2348c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command[%04x] loop timeout #2\n",
2358c2ecf20Sopenharmony_ci		       qpti->qpti_id, param[0]);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Wait for SBUS semaphore to get set. */
2388c2ecf20Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
2398c2ecf20Sopenharmony_ci	while (--loop_count &&
2408c2ecf20Sopenharmony_ci	       !(sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK)) {
2418c2ecf20Sopenharmony_ci		udelay(20);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci		/* Workaround for some buggy chips. */
2448c2ecf20Sopenharmony_ci		if (sbus_readw(qpti->qregs + MBOX0) & 0x4000)
2458c2ecf20Sopenharmony_ci			break;
2468c2ecf20Sopenharmony_ci	}
2478c2ecf20Sopenharmony_ci	if (!loop_count)
2488c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command[%04x] loop timeout #3\n",
2498c2ecf20Sopenharmony_ci		       qpti->qpti_id, param[0]);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	/* Wait for MBOX busy condition to go away. */
2528c2ecf20Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
2538c2ecf20Sopenharmony_ci	while (--loop_count && (sbus_readw(qpti->qregs + MBOX0) == 0x04))
2548c2ecf20Sopenharmony_ci		udelay(20);
2558c2ecf20Sopenharmony_ci	if (!loop_count)
2568c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command[%04x] loop timeout #4\n",
2578c2ecf20Sopenharmony_ci		       qpti->qpti_id, param[0]);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* Read back output parameters. */
2608c2ecf20Sopenharmony_ci	switch (mbox_param[param[0]] & 0xf) {
2618c2ecf20Sopenharmony_ci	case 6: param[5] = sbus_readw(qpti->qregs + MBOX5);
2628c2ecf20Sopenharmony_ci		fallthrough;
2638c2ecf20Sopenharmony_ci	case 5: param[4] = sbus_readw(qpti->qregs + MBOX4);
2648c2ecf20Sopenharmony_ci		fallthrough;
2658c2ecf20Sopenharmony_ci	case 4: param[3] = sbus_readw(qpti->qregs + MBOX3);
2668c2ecf20Sopenharmony_ci		fallthrough;
2678c2ecf20Sopenharmony_ci	case 3: param[2] = sbus_readw(qpti->qregs + MBOX2);
2688c2ecf20Sopenharmony_ci		fallthrough;
2698c2ecf20Sopenharmony_ci	case 2: param[1] = sbus_readw(qpti->qregs + MBOX1);
2708c2ecf20Sopenharmony_ci		fallthrough;
2718c2ecf20Sopenharmony_ci	case 1: param[0] = sbus_readw(qpti->qregs + MBOX0);
2728c2ecf20Sopenharmony_ci	}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	/* Clear RISC interrupt. */
2758c2ecf20Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + HCCTRL);
2768c2ecf20Sopenharmony_ci	tmp |= HCCTRL_CRIRQ;
2778c2ecf20Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + HCCTRL);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	/* Release SBUS semaphore. */
2808c2ecf20Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE);
2818c2ecf20Sopenharmony_ci	tmp &= ~(SBUS_SEMAPHORE_LCK);
2828c2ecf20Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	/* We're done. */
2858c2ecf20Sopenharmony_ci	return 0;
2868c2ecf20Sopenharmony_ci}
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cistatic inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti)
2898c2ecf20Sopenharmony_ci{
2908c2ecf20Sopenharmony_ci	int i;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	qpti->host_param.initiator_scsi_id = qpti->scsi_id;
2938c2ecf20Sopenharmony_ci	qpti->host_param.bus_reset_delay = 3;
2948c2ecf20Sopenharmony_ci	qpti->host_param.retry_count = 0;
2958c2ecf20Sopenharmony_ci	qpti->host_param.retry_delay = 5;
2968c2ecf20Sopenharmony_ci	qpti->host_param.async_data_setup_time = 3;
2978c2ecf20Sopenharmony_ci	qpti->host_param.req_ack_active_negation = 1;
2988c2ecf20Sopenharmony_ci	qpti->host_param.data_line_active_negation = 1;
2998c2ecf20Sopenharmony_ci	qpti->host_param.data_dma_burst_enable = 1;
3008c2ecf20Sopenharmony_ci	qpti->host_param.command_dma_burst_enable = 1;
3018c2ecf20Sopenharmony_ci	qpti->host_param.tag_aging = 8;
3028c2ecf20Sopenharmony_ci	qpti->host_param.selection_timeout = 250;
3038c2ecf20Sopenharmony_ci	qpti->host_param.max_queue_depth = 256;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	for(i = 0; i < MAX_TARGETS; i++) {
3068c2ecf20Sopenharmony_ci		/*
3078c2ecf20Sopenharmony_ci		 * disconnect, parity, arq, reneg on reset, and, oddly enough
3088c2ecf20Sopenharmony_ci		 * tags...the midlayer's notion of tagged support has to match
3098c2ecf20Sopenharmony_ci		 * our device settings, and since we base whether we enable a
3108c2ecf20Sopenharmony_ci		 * tag on a  per-cmnd basis upon what the midlayer sez, we
3118c2ecf20Sopenharmony_ci		 * actually enable the capability here.
3128c2ecf20Sopenharmony_ci		 */
3138c2ecf20Sopenharmony_ci		qpti->dev_param[i].device_flags = 0xcd;
3148c2ecf20Sopenharmony_ci		qpti->dev_param[i].execution_throttle = 16;
3158c2ecf20Sopenharmony_ci		if (qpti->ultra) {
3168c2ecf20Sopenharmony_ci			qpti->dev_param[i].synchronous_period = 12;
3178c2ecf20Sopenharmony_ci			qpti->dev_param[i].synchronous_offset = 8;
3188c2ecf20Sopenharmony_ci		} else {
3198c2ecf20Sopenharmony_ci			qpti->dev_param[i].synchronous_period = 25;
3208c2ecf20Sopenharmony_ci			qpti->dev_param[i].synchronous_offset = 12;
3218c2ecf20Sopenharmony_ci		}
3228c2ecf20Sopenharmony_ci		qpti->dev_param[i].device_enable = 1;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic int qlogicpti_reset_hardware(struct Scsi_Host *host)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
3298c2ecf20Sopenharmony_ci	u_short param[6];
3308c2ecf20Sopenharmony_ci	unsigned short risc_code_addr;
3318c2ecf20Sopenharmony_ci	int loop_count, i;
3328c2ecf20Sopenharmony_ci	unsigned long flags;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	risc_code_addr = 0x1000;	/* all load addresses are at 0x1000 */
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	spin_lock_irqsave(host->host_lock, flags);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Only reset the scsi bus if it is not free. */
3418c2ecf20Sopenharmony_ci	if (sbus_readw(qpti->qregs + CPU_PCTRL) & CPU_PCTRL_BSY) {
3428c2ecf20Sopenharmony_ci		sbus_writew(CPU_ORIDE_RMOD, qpti->qregs + CPU_ORIDE);
3438c2ecf20Sopenharmony_ci		sbus_writew(CPU_CMD_BRESET, qpti->qregs + CPU_CMD);
3448c2ecf20Sopenharmony_ci		udelay(400);
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL);
3488c2ecf20Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL);
3498c2ecf20Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
3528c2ecf20Sopenharmony_ci	while (--loop_count && ((sbus_readw(qpti->qregs + MBOX0) & 0xff) == 0x04))
3538c2ecf20Sopenharmony_ci		udelay(20);
3548c2ecf20Sopenharmony_ci	if (!loop_count)
3558c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: reset_hardware loop timeout\n",
3568c2ecf20Sopenharmony_ci		       qpti->qpti_id);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
3598c2ecf20Sopenharmony_ci	set_sbus_cfg1(qpti);
3608c2ecf20Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) {
3638c2ecf20Sopenharmony_ci		qpti->ultra = 1;
3648c2ecf20Sopenharmony_ci		sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA),
3658c2ecf20Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
3668c2ecf20Sopenharmony_ci	} else {
3678c2ecf20Sopenharmony_ci		qpti->ultra = 0;
3688c2ecf20Sopenharmony_ci		sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT),
3698c2ecf20Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	/* reset adapter and per-device default values. */
3738c2ecf20Sopenharmony_ci	/* do it after finding out whether we're ultra mode capable */
3748c2ecf20Sopenharmony_ci	qlogicpti_set_hostdev_defaults(qpti);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* Release the RISC processor. */
3778c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* Get RISC to start executing the firmware code. */
3808c2ecf20Sopenharmony_ci	param[0] = MBOX_EXEC_FIRMWARE;
3818c2ecf20Sopenharmony_ci	param[1] = risc_code_addr;
3828c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
3838c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n",
3848c2ecf20Sopenharmony_ci		       qpti->qpti_id);
3858c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
3868c2ecf20Sopenharmony_ci		return 1;
3878c2ecf20Sopenharmony_ci	}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* Set initiator scsi ID. */
3908c2ecf20Sopenharmony_ci	param[0] = MBOX_SET_INIT_SCSI_ID;
3918c2ecf20Sopenharmony_ci	param[1] = qpti->host_param.initiator_scsi_id;
3928c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
3938c2ecf20Sopenharmony_ci	   (param[0] != MBOX_COMMAND_COMPLETE)) {
3948c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n",
3958c2ecf20Sopenharmony_ci		       qpti->qpti_id);
3968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
3978c2ecf20Sopenharmony_ci		return 1;
3988c2ecf20Sopenharmony_ci	}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	/* Initialize state of the queues, both hw and sw. */
4018c2ecf20Sopenharmony_ci	qpti->req_in_ptr = qpti->res_out_ptr = 0;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	param[0] = MBOX_INIT_RES_QUEUE;
4048c2ecf20Sopenharmony_ci	param[1] = RES_QUEUE_LEN + 1;
4058c2ecf20Sopenharmony_ci	param[2] = (u_short) (qpti->res_dvma >> 16);
4068c2ecf20Sopenharmony_ci	param[3] = (u_short) (qpti->res_dvma & 0xffff);
4078c2ecf20Sopenharmony_ci	param[4] = param[5] = 0;
4088c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
4098c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n",
4108c2ecf20Sopenharmony_ci		       qpti->qpti_id);
4118c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
4128c2ecf20Sopenharmony_ci		return 1;
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	param[0] = MBOX_INIT_REQ_QUEUE;
4168c2ecf20Sopenharmony_ci	param[1] = QLOGICPTI_REQ_QUEUE_LEN + 1;
4178c2ecf20Sopenharmony_ci	param[2] = (u_short) (qpti->req_dvma >> 16);
4188c2ecf20Sopenharmony_ci	param[3] = (u_short) (qpti->req_dvma & 0xffff);
4198c2ecf20Sopenharmony_ci	param[4] = param[5] = 0;
4208c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
4218c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n",
4228c2ecf20Sopenharmony_ci		       qpti->qpti_id);
4238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
4248c2ecf20Sopenharmony_ci		return 1;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	param[0] = MBOX_SET_RETRY_COUNT;
4288c2ecf20Sopenharmony_ci	param[1] = qpti->host_param.retry_count;
4298c2ecf20Sopenharmony_ci	param[2] = qpti->host_param.retry_delay;
4308c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	param[0] = MBOX_SET_TAG_AGE_LIMIT;
4338c2ecf20Sopenharmony_ci	param[1] = qpti->host_param.tag_aging;
4348c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_TARGETS; i++) {
4378c2ecf20Sopenharmony_ci		param[0] = MBOX_GET_DEV_QUEUE_PARAMS;
4388c2ecf20Sopenharmony_ci		param[1] = (i << 8);
4398c2ecf20Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 0);
4408c2ecf20Sopenharmony_ci	}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	param[0] = MBOX_GET_FIRMWARE_STATUS;
4438c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	param[0] = MBOX_SET_SELECT_TIMEOUT;
4468c2ecf20Sopenharmony_ci	param[1] = qpti->host_param.selection_timeout;
4478c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_TARGETS; i++) {
4508c2ecf20Sopenharmony_ci		param[0] = MBOX_SET_TARGET_PARAMS;
4518c2ecf20Sopenharmony_ci		param[1] = (i << 8);
4528c2ecf20Sopenharmony_ci		param[2] = (qpti->dev_param[i].device_flags << 8);
4538c2ecf20Sopenharmony_ci		/*
4548c2ecf20Sopenharmony_ci		 * Since we're now loading 1.31 f/w, force narrow/async.
4558c2ecf20Sopenharmony_ci		 */
4568c2ecf20Sopenharmony_ci		param[2] |= 0xc0;
4578c2ecf20Sopenharmony_ci		param[3] = 0;	/* no offset, we do not have sync mode yet */
4588c2ecf20Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 0);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/*
4628c2ecf20Sopenharmony_ci	 * Always (sigh) do an initial bus reset (kicks f/w).
4638c2ecf20Sopenharmony_ci	 */
4648c2ecf20Sopenharmony_ci	param[0] = MBOX_BUS_RESET;
4658c2ecf20Sopenharmony_ci	param[1] = qpti->host_param.bus_reset_delay;
4668c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
4678c2ecf20Sopenharmony_ci	qpti->send_marker = 1;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(host->host_lock, flags);
4708c2ecf20Sopenharmony_ci	return 0;
4718c2ecf20Sopenharmony_ci}
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci#define PTI_RESET_LIMIT 400
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic int qlogicpti_load_firmware(struct qlogicpti *qpti)
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	const struct firmware *fw;
4788c2ecf20Sopenharmony_ci	const char fwname[] = "qlogic/isp1000.bin";
4798c2ecf20Sopenharmony_ci	const __le16 *fw_data;
4808c2ecf20Sopenharmony_ci	struct Scsi_Host *host = qpti->qhost;
4818c2ecf20Sopenharmony_ci	unsigned short csum = 0;
4828c2ecf20Sopenharmony_ci	unsigned short param[6];
4838c2ecf20Sopenharmony_ci	unsigned short risc_code_addr, risc_code_length;
4848c2ecf20Sopenharmony_ci	int err;
4858c2ecf20Sopenharmony_ci	unsigned long flags;
4868c2ecf20Sopenharmony_ci	int i, timeout;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	err = request_firmware(&fw, fwname, &qpti->op->dev);
4898c2ecf20Sopenharmony_ci	if (err) {
4908c2ecf20Sopenharmony_ci		printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
4918c2ecf20Sopenharmony_ci		       fwname, err);
4928c2ecf20Sopenharmony_ci		return err;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	if (fw->size % 2) {
4958c2ecf20Sopenharmony_ci		printk(KERN_ERR "Bogus length %zu in image \"%s\"\n",
4968c2ecf20Sopenharmony_ci		       fw->size, fwname);
4978c2ecf20Sopenharmony_ci		err = -EINVAL;
4988c2ecf20Sopenharmony_ci		goto outfirm;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci	fw_data = (const __le16 *)&fw->data[0];
5018c2ecf20Sopenharmony_ci	risc_code_addr = 0x1000;	/* all f/w modules load at 0x1000 */
5028c2ecf20Sopenharmony_ci	risc_code_length = fw->size / 2;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	spin_lock_irqsave(host->host_lock, flags);
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	/* Verify the checksum twice, one before loading it, and once
5078c2ecf20Sopenharmony_ci	 * afterwards via the mailbox commands.
5088c2ecf20Sopenharmony_ci	 */
5098c2ecf20Sopenharmony_ci	for (i = 0; i < risc_code_length; i++)
5108c2ecf20Sopenharmony_ci		csum += __le16_to_cpu(fw_data[i]);
5118c2ecf20Sopenharmony_ci	if (csum) {
5128c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
5138c2ecf20Sopenharmony_ci		       qpti->qpti_id);
5148c2ecf20Sopenharmony_ci		err = 1;
5158c2ecf20Sopenharmony_ci		goto out;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL);
5188c2ecf20Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL);
5198c2ecf20Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL);
5208c2ecf20Sopenharmony_ci	timeout = PTI_RESET_LIMIT;
5218c2ecf20Sopenharmony_ci	while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET))
5228c2ecf20Sopenharmony_ci		udelay(20);
5238c2ecf20Sopenharmony_ci	if (!timeout) {
5248c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id);
5258c2ecf20Sopenharmony_ci		err = 1;
5268c2ecf20Sopenharmony_ci		goto out;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL);
5308c2ecf20Sopenharmony_ci	mdelay(1);
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	sbus_writew((SBUS_CTRL_GENAB | SBUS_CTRL_ERIRQ), qpti->qregs + SBUS_CTRL);
5338c2ecf20Sopenharmony_ci	set_sbus_cfg1(qpti);
5348c2ecf20Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) {
5378c2ecf20Sopenharmony_ci		qpti->ultra = 1;
5388c2ecf20Sopenharmony_ci		sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA),
5398c2ecf20Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
5408c2ecf20Sopenharmony_ci	} else {
5418c2ecf20Sopenharmony_ci		qpti->ultra = 0;
5428c2ecf20Sopenharmony_ci		sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT),
5438c2ecf20Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
5448c2ecf20Sopenharmony_ci	}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* Pin lines are only stable while RISC is paused. */
5498c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
5508c2ecf20Sopenharmony_ci	if (sbus_readw(qpti->qregs + CPU_PDIFF) & CPU_PDIFF_MODE)
5518c2ecf20Sopenharmony_ci		qpti->differential = 1;
5528c2ecf20Sopenharmony_ci	else
5538c2ecf20Sopenharmony_ci		qpti->differential = 0;
5548c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/* This shouldn't be necessary- we've reset things so we should be
5578c2ecf20Sopenharmony_ci	   running from the ROM now.. */
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	param[0] = MBOX_STOP_FIRMWARE;
5608c2ecf20Sopenharmony_ci	param[1] = param[2] = param[3] = param[4] = param[5] = 0;
5618c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
5628c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot stop firmware for reload.\n",
5638c2ecf20Sopenharmony_ci		       qpti->qpti_id);
5648c2ecf20Sopenharmony_ci		err = 1;
5658c2ecf20Sopenharmony_ci		goto out;
5668c2ecf20Sopenharmony_ci	}
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	/* Load it up.. */
5698c2ecf20Sopenharmony_ci	for (i = 0; i < risc_code_length; i++) {
5708c2ecf20Sopenharmony_ci		param[0] = MBOX_WRITE_RAM_WORD;
5718c2ecf20Sopenharmony_ci		param[1] = risc_code_addr + i;
5728c2ecf20Sopenharmony_ci		param[2] = __le16_to_cpu(fw_data[i]);
5738c2ecf20Sopenharmony_ci		if (qlogicpti_mbox_command(qpti, param, 1) ||
5748c2ecf20Sopenharmony_ci		    param[0] != MBOX_COMMAND_COMPLETE) {
5758c2ecf20Sopenharmony_ci			printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
5768c2ecf20Sopenharmony_ci			       qpti->qpti_id);
5778c2ecf20Sopenharmony_ci			err = 1;
5788c2ecf20Sopenharmony_ci			goto out;
5798c2ecf20Sopenharmony_ci		}
5808c2ecf20Sopenharmony_ci	}
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	/* Reset the ISP again. */
5838c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL);
5848c2ecf20Sopenharmony_ci	mdelay(1);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
5878c2ecf20Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
5888c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* Ask ISP to verify the checksum of the new code. */
5918c2ecf20Sopenharmony_ci	param[0] = MBOX_VERIFY_CHECKSUM;
5928c2ecf20Sopenharmony_ci	param[1] = risc_code_addr;
5938c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
5948c2ecf20Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
5958c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n",
5968c2ecf20Sopenharmony_ci		       qpti->qpti_id);
5978c2ecf20Sopenharmony_ci		err = 1;
5988c2ecf20Sopenharmony_ci		goto out;
5998c2ecf20Sopenharmony_ci	}
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	/* Start using newly downloaded firmware. */
6028c2ecf20Sopenharmony_ci	param[0] = MBOX_EXEC_FIRMWARE;
6038c2ecf20Sopenharmony_ci	param[1] = risc_code_addr;
6048c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 1);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	param[0] = MBOX_ABOUT_FIRMWARE;
6078c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
6088c2ecf20Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
6098c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: AboutFirmware cmd fails.\n",
6108c2ecf20Sopenharmony_ci		       qpti->qpti_id);
6118c2ecf20Sopenharmony_ci		err = 1;
6128c2ecf20Sopenharmony_ci		goto out;
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	/* Snag the major and minor revisions from the result. */
6168c2ecf20Sopenharmony_ci	qpti->fware_majrev = param[1];
6178c2ecf20Sopenharmony_ci	qpti->fware_minrev = param[2];
6188c2ecf20Sopenharmony_ci	qpti->fware_micrev = param[3];
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* Set the clock rate */
6218c2ecf20Sopenharmony_ci	param[0] = MBOX_SET_CLOCK_RATE;
6228c2ecf20Sopenharmony_ci	param[1] = qpti->clock;
6238c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
6248c2ecf20Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
6258c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n",
6268c2ecf20Sopenharmony_ci		       qpti->qpti_id);
6278c2ecf20Sopenharmony_ci		err = 1;
6288c2ecf20Sopenharmony_ci		goto out;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (qpti->is_pti != 0) {
6328c2ecf20Sopenharmony_ci		/* Load scsi initiator ID and interrupt level into sbus static ram. */
6338c2ecf20Sopenharmony_ci		param[0] = MBOX_WRITE_RAM_WORD;
6348c2ecf20Sopenharmony_ci		param[1] = 0xff80;
6358c2ecf20Sopenharmony_ci		param[2] = (unsigned short) qpti->scsi_id;
6368c2ecf20Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 1);
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		param[0] = MBOX_WRITE_RAM_WORD;
6398c2ecf20Sopenharmony_ci		param[1] = 0xff00;
6408c2ecf20Sopenharmony_ci		param[2] = (unsigned short) 3;
6418c2ecf20Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 1);
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ciout:
6458c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(host->host_lock, flags);
6468c2ecf20Sopenharmony_cioutfirm:
6478c2ecf20Sopenharmony_ci	release_firmware(fw);
6488c2ecf20Sopenharmony_ci	return err;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic int qlogicpti_verify_tmon(struct qlogicpti *qpti)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	int curstat = sbus_readb(qpti->sreg);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	curstat &= 0xf0;
6568c2ecf20Sopenharmony_ci	if (!(curstat & SREG_FUSE) && (qpti->swsreg & SREG_FUSE))
6578c2ecf20Sopenharmony_ci		printk("qlogicpti%d: Fuse returned to normal state.\n", qpti->qpti_id);
6588c2ecf20Sopenharmony_ci	if (!(curstat & SREG_TPOWER) && (qpti->swsreg & SREG_TPOWER))
6598c2ecf20Sopenharmony_ci		printk("qlogicpti%d: termpwr back to normal state.\n", qpti->qpti_id);
6608c2ecf20Sopenharmony_ci	if (curstat != qpti->swsreg) {
6618c2ecf20Sopenharmony_ci		int error = 0;
6628c2ecf20Sopenharmony_ci		if (curstat & SREG_FUSE) {
6638c2ecf20Sopenharmony_ci			error++;
6648c2ecf20Sopenharmony_ci			printk("qlogicpti%d: Fuse is open!\n", qpti->qpti_id);
6658c2ecf20Sopenharmony_ci		}
6668c2ecf20Sopenharmony_ci		if (curstat & SREG_TPOWER) {
6678c2ecf20Sopenharmony_ci			error++;
6688c2ecf20Sopenharmony_ci			printk("qlogicpti%d: termpwr failure\n", qpti->qpti_id);
6698c2ecf20Sopenharmony_ci		}
6708c2ecf20Sopenharmony_ci		if (qpti->differential &&
6718c2ecf20Sopenharmony_ci		    (curstat & SREG_DSENSE) != SREG_DSENSE) {
6728c2ecf20Sopenharmony_ci			error++;
6738c2ecf20Sopenharmony_ci			printk("qlogicpti%d: You have a single ended device on a "
6748c2ecf20Sopenharmony_ci			       "differential bus!  Please fix!\n", qpti->qpti_id);
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci		qpti->swsreg = curstat;
6778c2ecf20Sopenharmony_ci		return error;
6788c2ecf20Sopenharmony_ci	}
6798c2ecf20Sopenharmony_ci	return 0;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cistatic irqreturn_t qpti_intr(int irq, void *dev_id);
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_cistatic void qpti_chain_add(struct qlogicpti *qpti)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	spin_lock_irq(&qptichain_lock);
6878c2ecf20Sopenharmony_ci	if (qptichain != NULL) {
6888c2ecf20Sopenharmony_ci		struct qlogicpti *qlink = qptichain;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		while(qlink->next)
6918c2ecf20Sopenharmony_ci			qlink = qlink->next;
6928c2ecf20Sopenharmony_ci		qlink->next = qpti;
6938c2ecf20Sopenharmony_ci	} else {
6948c2ecf20Sopenharmony_ci		qptichain = qpti;
6958c2ecf20Sopenharmony_ci	}
6968c2ecf20Sopenharmony_ci	qpti->next = NULL;
6978c2ecf20Sopenharmony_ci	spin_unlock_irq(&qptichain_lock);
6988c2ecf20Sopenharmony_ci}
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_cistatic void qpti_chain_del(struct qlogicpti *qpti)
7018c2ecf20Sopenharmony_ci{
7028c2ecf20Sopenharmony_ci	spin_lock_irq(&qptichain_lock);
7038c2ecf20Sopenharmony_ci	if (qptichain == qpti) {
7048c2ecf20Sopenharmony_ci		qptichain = qpti->next;
7058c2ecf20Sopenharmony_ci	} else {
7068c2ecf20Sopenharmony_ci		struct qlogicpti *qlink = qptichain;
7078c2ecf20Sopenharmony_ci		while(qlink->next != qpti)
7088c2ecf20Sopenharmony_ci			qlink = qlink->next;
7098c2ecf20Sopenharmony_ci		qlink->next = qpti->next;
7108c2ecf20Sopenharmony_ci	}
7118c2ecf20Sopenharmony_ci	qpti->next = NULL;
7128c2ecf20Sopenharmony_ci	spin_unlock_irq(&qptichain_lock);
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic int qpti_map_regs(struct qlogicpti *qpti)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct platform_device *op = qpti->op;
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	qpti->qregs = of_ioremap(&op->resource[0], 0,
7208c2ecf20Sopenharmony_ci				 resource_size(&op->resource[0]),
7218c2ecf20Sopenharmony_ci				 "PTI Qlogic/ISP");
7228c2ecf20Sopenharmony_ci	if (!qpti->qregs) {
7238c2ecf20Sopenharmony_ci		printk("PTI: Qlogic/ISP registers are unmappable\n");
7248c2ecf20Sopenharmony_ci		return -ENODEV;
7258c2ecf20Sopenharmony_ci	}
7268c2ecf20Sopenharmony_ci	if (qpti->is_pti) {
7278c2ecf20Sopenharmony_ci		qpti->sreg = of_ioremap(&op->resource[0], (16 * 4096),
7288c2ecf20Sopenharmony_ci					sizeof(unsigned char),
7298c2ecf20Sopenharmony_ci					"PTI Qlogic/ISP statreg");
7308c2ecf20Sopenharmony_ci		if (!qpti->sreg) {
7318c2ecf20Sopenharmony_ci			printk("PTI: Qlogic/ISP status register is unmappable\n");
7328c2ecf20Sopenharmony_ci			return -ENODEV;
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci	return 0;
7368c2ecf20Sopenharmony_ci}
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_cistatic int qpti_register_irq(struct qlogicpti *qpti)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	struct platform_device *op = qpti->op;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	qpti->qhost->irq = qpti->irq = op->archdata.irqs[0];
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	/* We used to try various overly-clever things to
7458c2ecf20Sopenharmony_ci	 * reduce the interrupt processing overhead on
7468c2ecf20Sopenharmony_ci	 * sun4c/sun4m when multiple PTI's shared the
7478c2ecf20Sopenharmony_ci	 * same IRQ.  It was too complex and messy to
7488c2ecf20Sopenharmony_ci	 * sanely maintain.
7498c2ecf20Sopenharmony_ci	 */
7508c2ecf20Sopenharmony_ci	if (request_irq(qpti->irq, qpti_intr,
7518c2ecf20Sopenharmony_ci			IRQF_SHARED, "QlogicPTI", qpti))
7528c2ecf20Sopenharmony_ci		goto fail;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	printk("qlogicpti%d: IRQ %d ", qpti->qpti_id, qpti->irq);
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	return 0;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_cifail:
7598c2ecf20Sopenharmony_ci	printk("qlogicpti%d: Cannot acquire irq line\n", qpti->qpti_id);
7608c2ecf20Sopenharmony_ci	return -1;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_cistatic void qpti_get_scsi_id(struct qlogicpti *qpti)
7648c2ecf20Sopenharmony_ci{
7658c2ecf20Sopenharmony_ci	struct platform_device *op = qpti->op;
7668c2ecf20Sopenharmony_ci	struct device_node *dp;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	dp = op->dev.of_node;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	qpti->scsi_id = of_getintprop_default(dp, "initiator-id", -1);
7718c2ecf20Sopenharmony_ci	if (qpti->scsi_id == -1)
7728c2ecf20Sopenharmony_ci		qpti->scsi_id = of_getintprop_default(dp, "scsi-initiator-id",
7738c2ecf20Sopenharmony_ci						      -1);
7748c2ecf20Sopenharmony_ci	if (qpti->scsi_id == -1)
7758c2ecf20Sopenharmony_ci		qpti->scsi_id =
7768c2ecf20Sopenharmony_ci			of_getintprop_default(dp->parent,
7778c2ecf20Sopenharmony_ci					      "scsi-initiator-id", 7);
7788c2ecf20Sopenharmony_ci	qpti->qhost->this_id = qpti->scsi_id;
7798c2ecf20Sopenharmony_ci	qpti->qhost->max_sectors = 64;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	printk("SCSI ID %d ", qpti->scsi_id);
7828c2ecf20Sopenharmony_ci}
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_cistatic void qpti_get_bursts(struct qlogicpti *qpti)
7858c2ecf20Sopenharmony_ci{
7868c2ecf20Sopenharmony_ci	struct platform_device *op = qpti->op;
7878c2ecf20Sopenharmony_ci	u8 bursts, bmask;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	bursts = of_getintprop_default(op->dev.of_node, "burst-sizes", 0xff);
7908c2ecf20Sopenharmony_ci	bmask = of_getintprop_default(op->dev.of_node->parent, "burst-sizes", 0xff);
7918c2ecf20Sopenharmony_ci	if (bmask != 0xff)
7928c2ecf20Sopenharmony_ci		bursts &= bmask;
7938c2ecf20Sopenharmony_ci	if (bursts == 0xff ||
7948c2ecf20Sopenharmony_ci	    (bursts & DMA_BURST16) == 0 ||
7958c2ecf20Sopenharmony_ci	    (bursts & DMA_BURST32) == 0)
7968c2ecf20Sopenharmony_ci		bursts = (DMA_BURST32 - 1);
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	qpti->bursts = bursts;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_cistatic void qpti_get_clock(struct qlogicpti *qpti)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	unsigned int cfreq;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	/* Check for what the clock input to this card is.
8068c2ecf20Sopenharmony_ci	 * Default to 40Mhz.
8078c2ecf20Sopenharmony_ci	 */
8088c2ecf20Sopenharmony_ci	cfreq = prom_getintdefault(qpti->prom_node,"clock-frequency",40000000);
8098c2ecf20Sopenharmony_ci	qpti->clock = (cfreq + 500000)/1000000;
8108c2ecf20Sopenharmony_ci	if (qpti->clock == 0) /* bullshit */
8118c2ecf20Sopenharmony_ci		qpti->clock = 40;
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci/* The request and response queues must each be aligned
8158c2ecf20Sopenharmony_ci * on a page boundary.
8168c2ecf20Sopenharmony_ci */
8178c2ecf20Sopenharmony_cistatic int qpti_map_queues(struct qlogicpti *qpti)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	struct platform_device *op = qpti->op;
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci#define QSIZE(entries)	(((entries) + 1) * QUEUE_ENTRY_LEN)
8228c2ecf20Sopenharmony_ci	qpti->res_cpu = dma_alloc_coherent(&op->dev,
8238c2ecf20Sopenharmony_ci					   QSIZE(RES_QUEUE_LEN),
8248c2ecf20Sopenharmony_ci					   &qpti->res_dvma, GFP_ATOMIC);
8258c2ecf20Sopenharmony_ci	if (qpti->res_cpu == NULL ||
8268c2ecf20Sopenharmony_ci	    qpti->res_dvma == 0) {
8278c2ecf20Sopenharmony_ci		printk("QPTI: Cannot map response queue.\n");
8288c2ecf20Sopenharmony_ci		return -1;
8298c2ecf20Sopenharmony_ci	}
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	qpti->req_cpu = dma_alloc_coherent(&op->dev,
8328c2ecf20Sopenharmony_ci					   QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
8338c2ecf20Sopenharmony_ci					   &qpti->req_dvma, GFP_ATOMIC);
8348c2ecf20Sopenharmony_ci	if (qpti->req_cpu == NULL ||
8358c2ecf20Sopenharmony_ci	    qpti->req_dvma == 0) {
8368c2ecf20Sopenharmony_ci		dma_free_coherent(&op->dev, QSIZE(RES_QUEUE_LEN),
8378c2ecf20Sopenharmony_ci				  qpti->res_cpu, qpti->res_dvma);
8388c2ecf20Sopenharmony_ci		printk("QPTI: Cannot map request queue.\n");
8398c2ecf20Sopenharmony_ci		return -1;
8408c2ecf20Sopenharmony_ci	}
8418c2ecf20Sopenharmony_ci	memset(qpti->res_cpu, 0, QSIZE(RES_QUEUE_LEN));
8428c2ecf20Sopenharmony_ci	memset(qpti->req_cpu, 0, QSIZE(QLOGICPTI_REQ_QUEUE_LEN));
8438c2ecf20Sopenharmony_ci	return 0;
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ciconst char *qlogicpti_info(struct Scsi_Host *host)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	static char buf[80];
8498c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	sprintf(buf, "PTI Qlogic,ISP SBUS SCSI irq %d regs at %p",
8528c2ecf20Sopenharmony_ci		qpti->qhost->irq, qpti->qregs);
8538c2ecf20Sopenharmony_ci	return buf;
8548c2ecf20Sopenharmony_ci}
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci/* I am a certified frobtronicist. */
8578c2ecf20Sopenharmony_cistatic inline void marker_frob(struct Command_Entry *cmd)
8588c2ecf20Sopenharmony_ci{
8598c2ecf20Sopenharmony_ci	struct Marker_Entry *marker = (struct Marker_Entry *) cmd;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	memset(marker, 0, sizeof(struct Marker_Entry));
8628c2ecf20Sopenharmony_ci	marker->hdr.entry_cnt = 1;
8638c2ecf20Sopenharmony_ci	marker->hdr.entry_type = ENTRY_MARKER;
8648c2ecf20Sopenharmony_ci	marker->modifier = SYNC_ALL;
8658c2ecf20Sopenharmony_ci	marker->rsvd = 0;
8668c2ecf20Sopenharmony_ci}
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_cistatic inline void cmd_frob(struct Command_Entry *cmd, struct scsi_cmnd *Cmnd,
8698c2ecf20Sopenharmony_ci			    struct qlogicpti *qpti)
8708c2ecf20Sopenharmony_ci{
8718c2ecf20Sopenharmony_ci	memset(cmd, 0, sizeof(struct Command_Entry));
8728c2ecf20Sopenharmony_ci	cmd->hdr.entry_cnt = 1;
8738c2ecf20Sopenharmony_ci	cmd->hdr.entry_type = ENTRY_COMMAND;
8748c2ecf20Sopenharmony_ci	cmd->target_id = Cmnd->device->id;
8758c2ecf20Sopenharmony_ci	cmd->target_lun = Cmnd->device->lun;
8768c2ecf20Sopenharmony_ci	cmd->cdb_length = Cmnd->cmd_len;
8778c2ecf20Sopenharmony_ci	cmd->control_flags = 0;
8788c2ecf20Sopenharmony_ci	if (Cmnd->device->tagged_supported) {
8798c2ecf20Sopenharmony_ci		if (qpti->cmd_count[Cmnd->device->id] == 0)
8808c2ecf20Sopenharmony_ci			qpti->tag_ages[Cmnd->device->id] = jiffies;
8818c2ecf20Sopenharmony_ci		if (time_after(jiffies, qpti->tag_ages[Cmnd->device->id] + (5*HZ))) {
8828c2ecf20Sopenharmony_ci			cmd->control_flags = CFLAG_ORDERED_TAG;
8838c2ecf20Sopenharmony_ci			qpti->tag_ages[Cmnd->device->id] = jiffies;
8848c2ecf20Sopenharmony_ci		} else
8858c2ecf20Sopenharmony_ci			cmd->control_flags = CFLAG_SIMPLE_TAG;
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci	if ((Cmnd->cmnd[0] == WRITE_6) ||
8888c2ecf20Sopenharmony_ci	    (Cmnd->cmnd[0] == WRITE_10) ||
8898c2ecf20Sopenharmony_ci	    (Cmnd->cmnd[0] == WRITE_12))
8908c2ecf20Sopenharmony_ci		cmd->control_flags |= CFLAG_WRITE;
8918c2ecf20Sopenharmony_ci	else
8928c2ecf20Sopenharmony_ci		cmd->control_flags |= CFLAG_READ;
8938c2ecf20Sopenharmony_ci	cmd->time_out = Cmnd->request->timeout/HZ;
8948c2ecf20Sopenharmony_ci	memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len);
8958c2ecf20Sopenharmony_ci}
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci/* Do it to it baby. */
8988c2ecf20Sopenharmony_cistatic inline int load_cmd(struct scsi_cmnd *Cmnd, struct Command_Entry *cmd,
8998c2ecf20Sopenharmony_ci			   struct qlogicpti *qpti, u_int in_ptr, u_int out_ptr)
9008c2ecf20Sopenharmony_ci{
9018c2ecf20Sopenharmony_ci	struct dataseg *ds;
9028c2ecf20Sopenharmony_ci	struct scatterlist *sg, *s;
9038c2ecf20Sopenharmony_ci	int i, n;
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	if (scsi_bufflen(Cmnd)) {
9068c2ecf20Sopenharmony_ci		int sg_count;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci		sg = scsi_sglist(Cmnd);
9098c2ecf20Sopenharmony_ci		sg_count = dma_map_sg(&qpti->op->dev, sg,
9108c2ecf20Sopenharmony_ci				      scsi_sg_count(Cmnd),
9118c2ecf20Sopenharmony_ci				      Cmnd->sc_data_direction);
9128c2ecf20Sopenharmony_ci
9138c2ecf20Sopenharmony_ci		ds = cmd->dataseg;
9148c2ecf20Sopenharmony_ci		cmd->segment_cnt = sg_count;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		/* Fill in first four sg entries: */
9178c2ecf20Sopenharmony_ci		n = sg_count;
9188c2ecf20Sopenharmony_ci		if (n > 4)
9198c2ecf20Sopenharmony_ci			n = 4;
9208c2ecf20Sopenharmony_ci		for_each_sg(sg, s, n, i) {
9218c2ecf20Sopenharmony_ci			ds[i].d_base = sg_dma_address(s);
9228c2ecf20Sopenharmony_ci			ds[i].d_count = sg_dma_len(s);
9238c2ecf20Sopenharmony_ci		}
9248c2ecf20Sopenharmony_ci		sg_count -= 4;
9258c2ecf20Sopenharmony_ci		sg = s;
9268c2ecf20Sopenharmony_ci		while (sg_count > 0) {
9278c2ecf20Sopenharmony_ci			struct Continuation_Entry *cont;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci			++cmd->hdr.entry_cnt;
9308c2ecf20Sopenharmony_ci			cont = (struct Continuation_Entry *) &qpti->req_cpu[in_ptr];
9318c2ecf20Sopenharmony_ci			in_ptr = NEXT_REQ_PTR(in_ptr);
9328c2ecf20Sopenharmony_ci			if (in_ptr == out_ptr)
9338c2ecf20Sopenharmony_ci				return -1;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci			cont->hdr.entry_type = ENTRY_CONTINUATION;
9368c2ecf20Sopenharmony_ci			cont->hdr.entry_cnt = 0;
9378c2ecf20Sopenharmony_ci			cont->hdr.sys_def_1 = 0;
9388c2ecf20Sopenharmony_ci			cont->hdr.flags = 0;
9398c2ecf20Sopenharmony_ci			cont->reserved = 0;
9408c2ecf20Sopenharmony_ci			ds = cont->dataseg;
9418c2ecf20Sopenharmony_ci			n = sg_count;
9428c2ecf20Sopenharmony_ci			if (n > 7)
9438c2ecf20Sopenharmony_ci				n = 7;
9448c2ecf20Sopenharmony_ci			for_each_sg(sg, s, n, i) {
9458c2ecf20Sopenharmony_ci				ds[i].d_base = sg_dma_address(s);
9468c2ecf20Sopenharmony_ci				ds[i].d_count = sg_dma_len(s);
9478c2ecf20Sopenharmony_ci			}
9488c2ecf20Sopenharmony_ci			sg_count -= n;
9498c2ecf20Sopenharmony_ci			sg = s;
9508c2ecf20Sopenharmony_ci		}
9518c2ecf20Sopenharmony_ci	} else {
9528c2ecf20Sopenharmony_ci		cmd->dataseg[0].d_base = 0;
9538c2ecf20Sopenharmony_ci		cmd->dataseg[0].d_count = 0;
9548c2ecf20Sopenharmony_ci		cmd->segment_cnt = 1; /* Shouldn't this be 0? */
9558c2ecf20Sopenharmony_ci	}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	/* Committed, record Scsi_Cmd so we can find it later. */
9588c2ecf20Sopenharmony_ci	cmd->handle = in_ptr;
9598c2ecf20Sopenharmony_ci	qpti->cmd_slots[in_ptr] = Cmnd;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	qpti->cmd_count[Cmnd->device->id]++;
9628c2ecf20Sopenharmony_ci	sbus_writew(in_ptr, qpti->qregs + MBOX4);
9638c2ecf20Sopenharmony_ci	qpti->req_in_ptr = in_ptr;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci	return in_ptr;
9668c2ecf20Sopenharmony_ci}
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_cistatic inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int out_ptr)
9698c2ecf20Sopenharmony_ci{
9708c2ecf20Sopenharmony_ci	/* Temporary workaround until bug is found and fixed (one bug has been found
9718c2ecf20Sopenharmony_ci	   already, but fixing it makes things even worse) -jj */
9728c2ecf20Sopenharmony_ci	int num_free = QLOGICPTI_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64;
9738c2ecf20Sopenharmony_ci	host->can_queue = scsi_host_busy(host) + num_free;
9748c2ecf20Sopenharmony_ci	host->sg_tablesize = QLOGICPTI_MAX_SG(num_free);
9758c2ecf20Sopenharmony_ci}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_cistatic int qlogicpti_slave_configure(struct scsi_device *sdev)
9788c2ecf20Sopenharmony_ci{
9798c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = shost_priv(sdev->host);
9808c2ecf20Sopenharmony_ci	int tgt = sdev->id;
9818c2ecf20Sopenharmony_ci	u_short param[6];
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	/* tags handled in midlayer */
9848c2ecf20Sopenharmony_ci	/* enable sync mode? */
9858c2ecf20Sopenharmony_ci	if (sdev->sdtr) {
9868c2ecf20Sopenharmony_ci		qpti->dev_param[tgt].device_flags |= 0x10;
9878c2ecf20Sopenharmony_ci	} else {
9888c2ecf20Sopenharmony_ci		qpti->dev_param[tgt].synchronous_offset = 0;
9898c2ecf20Sopenharmony_ci		qpti->dev_param[tgt].synchronous_period = 0;
9908c2ecf20Sopenharmony_ci	}
9918c2ecf20Sopenharmony_ci	/* are we wide capable? */
9928c2ecf20Sopenharmony_ci	if (sdev->wdtr)
9938c2ecf20Sopenharmony_ci		qpti->dev_param[tgt].device_flags |= 0x20;
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci	param[0] = MBOX_SET_TARGET_PARAMS;
9968c2ecf20Sopenharmony_ci	param[1] = (tgt << 8);
9978c2ecf20Sopenharmony_ci	param[2] = (qpti->dev_param[tgt].device_flags << 8);
9988c2ecf20Sopenharmony_ci	if (qpti->dev_param[tgt].device_flags & 0x10) {
9998c2ecf20Sopenharmony_ci		param[3] = (qpti->dev_param[tgt].synchronous_offset << 8) |
10008c2ecf20Sopenharmony_ci			qpti->dev_param[tgt].synchronous_period;
10018c2ecf20Sopenharmony_ci	} else {
10028c2ecf20Sopenharmony_ci		param[3] = 0;
10038c2ecf20Sopenharmony_ci	}
10048c2ecf20Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
10058c2ecf20Sopenharmony_ci	return 0;
10068c2ecf20Sopenharmony_ci}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci/*
10098c2ecf20Sopenharmony_ci * The middle SCSI layer ensures that queuecommand never gets invoked
10108c2ecf20Sopenharmony_ci * concurrently with itself or the interrupt handler (though the
10118c2ecf20Sopenharmony_ci * interrupt handler may call this routine as part of
10128c2ecf20Sopenharmony_ci * request-completion handling).
10138c2ecf20Sopenharmony_ci *
10148c2ecf20Sopenharmony_ci * "This code must fly." -davem
10158c2ecf20Sopenharmony_ci */
10168c2ecf20Sopenharmony_cistatic int qlogicpti_queuecommand_lck(struct scsi_cmnd *Cmnd, void (*done)(struct scsi_cmnd *))
10178c2ecf20Sopenharmony_ci{
10188c2ecf20Sopenharmony_ci	struct Scsi_Host *host = Cmnd->device->host;
10198c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
10208c2ecf20Sopenharmony_ci	struct Command_Entry *cmd;
10218c2ecf20Sopenharmony_ci	u_int out_ptr;
10228c2ecf20Sopenharmony_ci	int in_ptr;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	Cmnd->scsi_done = done;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	in_ptr = qpti->req_in_ptr;
10278c2ecf20Sopenharmony_ci	cmd = (struct Command_Entry *) &qpti->req_cpu[in_ptr];
10288c2ecf20Sopenharmony_ci	out_ptr = sbus_readw(qpti->qregs + MBOX4);
10298c2ecf20Sopenharmony_ci	in_ptr = NEXT_REQ_PTR(in_ptr);
10308c2ecf20Sopenharmony_ci	if (in_ptr == out_ptr)
10318c2ecf20Sopenharmony_ci		goto toss_command;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci	if (qpti->send_marker) {
10348c2ecf20Sopenharmony_ci		marker_frob(cmd);
10358c2ecf20Sopenharmony_ci		qpti->send_marker = 0;
10368c2ecf20Sopenharmony_ci		if (NEXT_REQ_PTR(in_ptr) == out_ptr) {
10378c2ecf20Sopenharmony_ci			sbus_writew(in_ptr, qpti->qregs + MBOX4);
10388c2ecf20Sopenharmony_ci			qpti->req_in_ptr = in_ptr;
10398c2ecf20Sopenharmony_ci			goto toss_command;
10408c2ecf20Sopenharmony_ci		}
10418c2ecf20Sopenharmony_ci		cmd = (struct Command_Entry *) &qpti->req_cpu[in_ptr];
10428c2ecf20Sopenharmony_ci		in_ptr = NEXT_REQ_PTR(in_ptr);
10438c2ecf20Sopenharmony_ci	}
10448c2ecf20Sopenharmony_ci	cmd_frob(cmd, Cmnd, qpti);
10458c2ecf20Sopenharmony_ci	if ((in_ptr = load_cmd(Cmnd, cmd, qpti, in_ptr, out_ptr)) == -1)
10468c2ecf20Sopenharmony_ci		goto toss_command;
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	update_can_queue(host, in_ptr, out_ptr);
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	return 0;
10518c2ecf20Sopenharmony_ci
10528c2ecf20Sopenharmony_citoss_command:
10538c2ecf20Sopenharmony_ci	printk(KERN_EMERG "qlogicpti%d: request queue overflow\n",
10548c2ecf20Sopenharmony_ci	       qpti->qpti_id);
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci	/* Unfortunately, unless you use the new EH code, which
10578c2ecf20Sopenharmony_ci	 * we don't, the midlayer will ignore the return value,
10588c2ecf20Sopenharmony_ci	 * which is insane.  We pick up the pieces like this.
10598c2ecf20Sopenharmony_ci	 */
10608c2ecf20Sopenharmony_ci	Cmnd->result = DID_BUS_BUSY;
10618c2ecf20Sopenharmony_ci	done(Cmnd);
10628c2ecf20Sopenharmony_ci	return 1;
10638c2ecf20Sopenharmony_ci}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_cistatic DEF_SCSI_QCMD(qlogicpti_queuecommand)
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_cistatic int qlogicpti_return_status(struct Status_Entry *sts, int id)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	int host_status = DID_ERROR;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	switch (sts->completion_status) {
10728c2ecf20Sopenharmony_ci	      case CS_COMPLETE:
10738c2ecf20Sopenharmony_ci		host_status = DID_OK;
10748c2ecf20Sopenharmony_ci		break;
10758c2ecf20Sopenharmony_ci	      case CS_INCOMPLETE:
10768c2ecf20Sopenharmony_ci		if (!(sts->state_flags & SF_GOT_BUS))
10778c2ecf20Sopenharmony_ci			host_status = DID_NO_CONNECT;
10788c2ecf20Sopenharmony_ci		else if (!(sts->state_flags & SF_GOT_TARGET))
10798c2ecf20Sopenharmony_ci			host_status = DID_BAD_TARGET;
10808c2ecf20Sopenharmony_ci		else if (!(sts->state_flags & SF_SENT_CDB))
10818c2ecf20Sopenharmony_ci			host_status = DID_ERROR;
10828c2ecf20Sopenharmony_ci		else if (!(sts->state_flags & SF_TRANSFERRED_DATA))
10838c2ecf20Sopenharmony_ci			host_status = DID_ERROR;
10848c2ecf20Sopenharmony_ci		else if (!(sts->state_flags & SF_GOT_STATUS))
10858c2ecf20Sopenharmony_ci			host_status = DID_ERROR;
10868c2ecf20Sopenharmony_ci		else if (!(sts->state_flags & SF_GOT_SENSE))
10878c2ecf20Sopenharmony_ci			host_status = DID_ERROR;
10888c2ecf20Sopenharmony_ci		break;
10898c2ecf20Sopenharmony_ci	      case CS_DMA_ERROR:
10908c2ecf20Sopenharmony_ci	      case CS_TRANSPORT_ERROR:
10918c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
10928c2ecf20Sopenharmony_ci		break;
10938c2ecf20Sopenharmony_ci	      case CS_RESET_OCCURRED:
10948c2ecf20Sopenharmony_ci	      case CS_BUS_RESET:
10958c2ecf20Sopenharmony_ci		host_status = DID_RESET;
10968c2ecf20Sopenharmony_ci		break;
10978c2ecf20Sopenharmony_ci	      case CS_ABORTED:
10988c2ecf20Sopenharmony_ci		host_status = DID_ABORT;
10998c2ecf20Sopenharmony_ci		break;
11008c2ecf20Sopenharmony_ci	      case CS_TIMEOUT:
11018c2ecf20Sopenharmony_ci		host_status = DID_TIME_OUT;
11028c2ecf20Sopenharmony_ci		break;
11038c2ecf20Sopenharmony_ci	      case CS_DATA_OVERRUN:
11048c2ecf20Sopenharmony_ci	      case CS_COMMAND_OVERRUN:
11058c2ecf20Sopenharmony_ci	      case CS_STATUS_OVERRUN:
11068c2ecf20Sopenharmony_ci	      case CS_BAD_MESSAGE:
11078c2ecf20Sopenharmony_ci	      case CS_NO_MESSAGE_OUT:
11088c2ecf20Sopenharmony_ci	      case CS_EXT_ID_FAILED:
11098c2ecf20Sopenharmony_ci	      case CS_IDE_MSG_FAILED:
11108c2ecf20Sopenharmony_ci	      case CS_ABORT_MSG_FAILED:
11118c2ecf20Sopenharmony_ci	      case CS_NOP_MSG_FAILED:
11128c2ecf20Sopenharmony_ci	      case CS_PARITY_ERROR_MSG_FAILED:
11138c2ecf20Sopenharmony_ci	      case CS_DEVICE_RESET_MSG_FAILED:
11148c2ecf20Sopenharmony_ci	      case CS_ID_MSG_FAILED:
11158c2ecf20Sopenharmony_ci	      case CS_UNEXP_BUS_FREE:
11168c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
11178c2ecf20Sopenharmony_ci		break;
11188c2ecf20Sopenharmony_ci	      case CS_DATA_UNDERRUN:
11198c2ecf20Sopenharmony_ci		host_status = DID_OK;
11208c2ecf20Sopenharmony_ci		break;
11218c2ecf20Sopenharmony_ci	      default:
11228c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: unknown completion status 0x%04x\n",
11238c2ecf20Sopenharmony_ci		       id, sts->completion_status);
11248c2ecf20Sopenharmony_ci		host_status = DID_ERROR;
11258c2ecf20Sopenharmony_ci		break;
11268c2ecf20Sopenharmony_ci	}
11278c2ecf20Sopenharmony_ci
11288c2ecf20Sopenharmony_ci	return (sts->scsi_status & STATUS_MASK) | (host_status << 16);
11298c2ecf20Sopenharmony_ci}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_cistatic struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti)
11328c2ecf20Sopenharmony_ci{
11338c2ecf20Sopenharmony_ci	struct scsi_cmnd *Cmnd, *done_queue = NULL;
11348c2ecf20Sopenharmony_ci	struct Status_Entry *sts;
11358c2ecf20Sopenharmony_ci	u_int in_ptr, out_ptr;
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci	if (!(sbus_readw(qpti->qregs + SBUS_STAT) & SBUS_STAT_RINT))
11388c2ecf20Sopenharmony_ci		return NULL;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	in_ptr = sbus_readw(qpti->qregs + MBOX5);
11418c2ecf20Sopenharmony_ci	sbus_writew(HCCTRL_CRIRQ, qpti->qregs + HCCTRL);
11428c2ecf20Sopenharmony_ci	if (sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK) {
11438c2ecf20Sopenharmony_ci		switch (sbus_readw(qpti->qregs + MBOX0)) {
11448c2ecf20Sopenharmony_ci		case ASYNC_SCSI_BUS_RESET:
11458c2ecf20Sopenharmony_ci		case EXECUTION_TIMEOUT_RESET:
11468c2ecf20Sopenharmony_ci			qpti->send_marker = 1;
11478c2ecf20Sopenharmony_ci			break;
11488c2ecf20Sopenharmony_ci		case INVALID_COMMAND:
11498c2ecf20Sopenharmony_ci		case HOST_INTERFACE_ERROR:
11508c2ecf20Sopenharmony_ci		case COMMAND_ERROR:
11518c2ecf20Sopenharmony_ci		case COMMAND_PARAM_ERROR:
11528c2ecf20Sopenharmony_ci			break;
11538c2ecf20Sopenharmony_ci		};
11548c2ecf20Sopenharmony_ci		sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
11558c2ecf20Sopenharmony_ci	}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	/* This looks like a network driver! */
11588c2ecf20Sopenharmony_ci	out_ptr = qpti->res_out_ptr;
11598c2ecf20Sopenharmony_ci	while (out_ptr != in_ptr) {
11608c2ecf20Sopenharmony_ci		u_int cmd_slot;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci		sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr];
11638c2ecf20Sopenharmony_ci		out_ptr = NEXT_RES_PTR(out_ptr);
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci		/* We store an index in the handle, not the pointer in
11668c2ecf20Sopenharmony_ci		 * some form.  This avoids problems due to the fact
11678c2ecf20Sopenharmony_ci		 * that the handle provided is only 32-bits. -DaveM
11688c2ecf20Sopenharmony_ci		 */
11698c2ecf20Sopenharmony_ci		cmd_slot = sts->handle;
11708c2ecf20Sopenharmony_ci		Cmnd = qpti->cmd_slots[cmd_slot];
11718c2ecf20Sopenharmony_ci		qpti->cmd_slots[cmd_slot] = NULL;
11728c2ecf20Sopenharmony_ci
11738c2ecf20Sopenharmony_ci		if (sts->completion_status == CS_RESET_OCCURRED ||
11748c2ecf20Sopenharmony_ci		    sts->completion_status == CS_ABORTED ||
11758c2ecf20Sopenharmony_ci		    (sts->status_flags & STF_BUS_RESET))
11768c2ecf20Sopenharmony_ci			qpti->send_marker = 1;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci		if (sts->state_flags & SF_GOT_SENSE)
11798c2ecf20Sopenharmony_ci			memcpy(Cmnd->sense_buffer, sts->req_sense_data,
11808c2ecf20Sopenharmony_ci			       SCSI_SENSE_BUFFERSIZE);
11818c2ecf20Sopenharmony_ci
11828c2ecf20Sopenharmony_ci		if (sts->hdr.entry_type == ENTRY_STATUS)
11838c2ecf20Sopenharmony_ci			Cmnd->result =
11848c2ecf20Sopenharmony_ci			    qlogicpti_return_status(sts, qpti->qpti_id);
11858c2ecf20Sopenharmony_ci		else
11868c2ecf20Sopenharmony_ci			Cmnd->result = DID_ERROR << 16;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci		if (scsi_bufflen(Cmnd))
11898c2ecf20Sopenharmony_ci			dma_unmap_sg(&qpti->op->dev,
11908c2ecf20Sopenharmony_ci				     scsi_sglist(Cmnd), scsi_sg_count(Cmnd),
11918c2ecf20Sopenharmony_ci				     Cmnd->sc_data_direction);
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci		qpti->cmd_count[Cmnd->device->id]--;
11948c2ecf20Sopenharmony_ci		sbus_writew(out_ptr, qpti->qregs + MBOX5);
11958c2ecf20Sopenharmony_ci		Cmnd->host_scribble = (unsigned char *) done_queue;
11968c2ecf20Sopenharmony_ci		done_queue = Cmnd;
11978c2ecf20Sopenharmony_ci	}
11988c2ecf20Sopenharmony_ci	qpti->res_out_ptr = out_ptr;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	return done_queue;
12018c2ecf20Sopenharmony_ci}
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_cistatic irqreturn_t qpti_intr(int irq, void *dev_id)
12048c2ecf20Sopenharmony_ci{
12058c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = dev_id;
12068c2ecf20Sopenharmony_ci	unsigned long flags;
12078c2ecf20Sopenharmony_ci	struct scsi_cmnd *dq;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	spin_lock_irqsave(qpti->qhost->host_lock, flags);
12108c2ecf20Sopenharmony_ci	dq = qlogicpti_intr_handler(qpti);
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	if (dq != NULL) {
12138c2ecf20Sopenharmony_ci		do {
12148c2ecf20Sopenharmony_ci			struct scsi_cmnd *next;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci			next = (struct scsi_cmnd *) dq->host_scribble;
12178c2ecf20Sopenharmony_ci			dq->scsi_done(dq);
12188c2ecf20Sopenharmony_ci			dq = next;
12198c2ecf20Sopenharmony_ci		} while (dq != NULL);
12208c2ecf20Sopenharmony_ci	}
12218c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(qpti->qhost->host_lock, flags);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
12248c2ecf20Sopenharmony_ci}
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_cistatic int qlogicpti_abort(struct scsi_cmnd *Cmnd)
12278c2ecf20Sopenharmony_ci{
12288c2ecf20Sopenharmony_ci	u_short param[6];
12298c2ecf20Sopenharmony_ci	struct Scsi_Host *host = Cmnd->device->host;
12308c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
12318c2ecf20Sopenharmony_ci	int return_status = SUCCESS;
12328c2ecf20Sopenharmony_ci	u32 cmd_cookie;
12338c2ecf20Sopenharmony_ci	int i;
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci	printk(KERN_WARNING "qlogicpti%d: Aborting cmd for tgt[%d] lun[%d]\n",
12368c2ecf20Sopenharmony_ci	       qpti->qpti_id, (int)Cmnd->device->id, (int)Cmnd->device->lun);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	qlogicpti_disable_irqs(qpti);
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	/* Find the 32-bit cookie we gave to the firmware for
12418c2ecf20Sopenharmony_ci	 * this command.
12428c2ecf20Sopenharmony_ci	 */
12438c2ecf20Sopenharmony_ci	for (i = 0; i < QLOGICPTI_REQ_QUEUE_LEN + 1; i++)
12448c2ecf20Sopenharmony_ci		if (qpti->cmd_slots[i] == Cmnd)
12458c2ecf20Sopenharmony_ci			break;
12468c2ecf20Sopenharmony_ci	cmd_cookie = i;
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci	param[0] = MBOX_ABORT;
12498c2ecf20Sopenharmony_ci	param[1] = (((u_short) Cmnd->device->id) << 8) | Cmnd->device->lun;
12508c2ecf20Sopenharmony_ci	param[2] = cmd_cookie >> 16;
12518c2ecf20Sopenharmony_ci	param[3] = cmd_cookie & 0xffff;
12528c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 0) ||
12538c2ecf20Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
12548c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: scsi abort failure: %x\n",
12558c2ecf20Sopenharmony_ci		       qpti->qpti_id, param[0]);
12568c2ecf20Sopenharmony_ci		return_status = FAILED;
12578c2ecf20Sopenharmony_ci	}
12588c2ecf20Sopenharmony_ci
12598c2ecf20Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	return return_status;
12628c2ecf20Sopenharmony_ci}
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_cistatic int qlogicpti_reset(struct scsi_cmnd *Cmnd)
12658c2ecf20Sopenharmony_ci{
12668c2ecf20Sopenharmony_ci	u_short param[6];
12678c2ecf20Sopenharmony_ci	struct Scsi_Host *host = Cmnd->device->host;
12688c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
12698c2ecf20Sopenharmony_ci	int return_status = SUCCESS;
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	printk(KERN_WARNING "qlogicpti%d: Resetting SCSI bus!\n",
12728c2ecf20Sopenharmony_ci	       qpti->qpti_id);
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	qlogicpti_disable_irqs(qpti);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_ci	param[0] = MBOX_BUS_RESET;
12778c2ecf20Sopenharmony_ci	param[1] = qpti->host_param.bus_reset_delay;
12788c2ecf20Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 0) ||
12798c2ecf20Sopenharmony_ci	   (param[0] != MBOX_COMMAND_COMPLETE)) {
12808c2ecf20Sopenharmony_ci		printk(KERN_EMERG "qlogicisp%d: scsi bus reset failure: %x\n",
12818c2ecf20Sopenharmony_ci		       qpti->qpti_id, param[0]);
12828c2ecf20Sopenharmony_ci		return_status = FAILED;
12838c2ecf20Sopenharmony_ci	}
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	return return_status;
12888c2ecf20Sopenharmony_ci}
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_cistatic struct scsi_host_template qpti_template = {
12918c2ecf20Sopenharmony_ci	.module			= THIS_MODULE,
12928c2ecf20Sopenharmony_ci	.name			= "qlogicpti",
12938c2ecf20Sopenharmony_ci	.info			= qlogicpti_info,
12948c2ecf20Sopenharmony_ci	.queuecommand		= qlogicpti_queuecommand,
12958c2ecf20Sopenharmony_ci	.slave_configure	= qlogicpti_slave_configure,
12968c2ecf20Sopenharmony_ci	.eh_abort_handler	= qlogicpti_abort,
12978c2ecf20Sopenharmony_ci	.eh_host_reset_handler	= qlogicpti_reset,
12988c2ecf20Sopenharmony_ci	.can_queue		= QLOGICPTI_REQ_QUEUE_LEN,
12998c2ecf20Sopenharmony_ci	.this_id		= 7,
13008c2ecf20Sopenharmony_ci	.sg_tablesize		= QLOGICPTI_MAX_SG(QLOGICPTI_REQ_QUEUE_LEN),
13018c2ecf20Sopenharmony_ci};
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic const struct of_device_id qpti_match[];
13048c2ecf20Sopenharmony_cistatic int qpti_sbus_probe(struct platform_device *op)
13058c2ecf20Sopenharmony_ci{
13068c2ecf20Sopenharmony_ci	struct device_node *dp = op->dev.of_node;
13078c2ecf20Sopenharmony_ci	struct Scsi_Host *host;
13088c2ecf20Sopenharmony_ci	struct qlogicpti *qpti;
13098c2ecf20Sopenharmony_ci	static int nqptis;
13108c2ecf20Sopenharmony_ci	const char *fcode;
13118c2ecf20Sopenharmony_ci
13128c2ecf20Sopenharmony_ci	/* Sometimes Antares cards come up not completely
13138c2ecf20Sopenharmony_ci	 * setup, and we get a report of a zero IRQ.
13148c2ecf20Sopenharmony_ci	 */
13158c2ecf20Sopenharmony_ci	if (op->archdata.irqs[0] == 0)
13168c2ecf20Sopenharmony_ci		return -ENODEV;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	host = scsi_host_alloc(&qpti_template, sizeof(struct qlogicpti));
13198c2ecf20Sopenharmony_ci	if (!host)
13208c2ecf20Sopenharmony_ci		return -ENOMEM;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	qpti = shost_priv(host);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci	host->max_id = MAX_TARGETS;
13258c2ecf20Sopenharmony_ci	qpti->qhost = host;
13268c2ecf20Sopenharmony_ci	qpti->op = op;
13278c2ecf20Sopenharmony_ci	qpti->qpti_id = nqptis;
13288c2ecf20Sopenharmony_ci	qpti->is_pti = !of_node_name_eq(op->dev.of_node, "QLGC,isp");
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci	if (qpti_map_regs(qpti) < 0)
13318c2ecf20Sopenharmony_ci		goto fail_unlink;
13328c2ecf20Sopenharmony_ci
13338c2ecf20Sopenharmony_ci	if (qpti_register_irq(qpti) < 0)
13348c2ecf20Sopenharmony_ci		goto fail_unmap_regs;
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	qpti_get_scsi_id(qpti);
13378c2ecf20Sopenharmony_ci	qpti_get_bursts(qpti);
13388c2ecf20Sopenharmony_ci	qpti_get_clock(qpti);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	/* Clear out scsi_cmnd array. */
13418c2ecf20Sopenharmony_ci	memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci	if (qpti_map_queues(qpti) < 0)
13448c2ecf20Sopenharmony_ci		goto fail_free_irq;
13458c2ecf20Sopenharmony_ci
13468c2ecf20Sopenharmony_ci	/* Load the firmware. */
13478c2ecf20Sopenharmony_ci	if (qlogicpti_load_firmware(qpti))
13488c2ecf20Sopenharmony_ci		goto fail_unmap_queues;
13498c2ecf20Sopenharmony_ci	if (qpti->is_pti) {
13508c2ecf20Sopenharmony_ci		/* Check the PTI status reg. */
13518c2ecf20Sopenharmony_ci		if (qlogicpti_verify_tmon(qpti))
13528c2ecf20Sopenharmony_ci			goto fail_unmap_queues;
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	/* Reset the ISP and init res/req queues. */
13568c2ecf20Sopenharmony_ci	if (qlogicpti_reset_hardware(host))
13578c2ecf20Sopenharmony_ci		goto fail_unmap_queues;
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	printk("(Firmware v%d.%d.%d)", qpti->fware_majrev,
13608c2ecf20Sopenharmony_ci	       qpti->fware_minrev, qpti->fware_micrev);
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci	fcode = of_get_property(dp, "isp-fcode", NULL);
13638c2ecf20Sopenharmony_ci	if (fcode && fcode[0])
13648c2ecf20Sopenharmony_ci		printk("(FCode %s)", fcode);
13658c2ecf20Sopenharmony_ci	if (of_find_property(dp, "differential", NULL) != NULL)
13668c2ecf20Sopenharmony_ci		qpti->differential = 1;
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci	printk("\nqlogicpti%d: [%s Wide, using %s interface]\n",
13698c2ecf20Sopenharmony_ci		qpti->qpti_id,
13708c2ecf20Sopenharmony_ci		(qpti->ultra ? "Ultra" : "Fast"),
13718c2ecf20Sopenharmony_ci		(qpti->differential ? "differential" : "single ended"));
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	if (scsi_add_host(host, &op->dev)) {
13748c2ecf20Sopenharmony_ci		printk("qlogicpti%d: Failed scsi_add_host\n", qpti->qpti_id);
13758c2ecf20Sopenharmony_ci		goto fail_unmap_queues;
13768c2ecf20Sopenharmony_ci	}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci	dev_set_drvdata(&op->dev, qpti);
13798c2ecf20Sopenharmony_ci
13808c2ecf20Sopenharmony_ci	qpti_chain_add(qpti);
13818c2ecf20Sopenharmony_ci
13828c2ecf20Sopenharmony_ci	scsi_scan_host(host);
13838c2ecf20Sopenharmony_ci	nqptis++;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	return 0;
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_cifail_unmap_queues:
13888c2ecf20Sopenharmony_ci#define QSIZE(entries)	(((entries) + 1) * QUEUE_ENTRY_LEN)
13898c2ecf20Sopenharmony_ci	dma_free_coherent(&op->dev,
13908c2ecf20Sopenharmony_ci			  QSIZE(RES_QUEUE_LEN),
13918c2ecf20Sopenharmony_ci			  qpti->res_cpu, qpti->res_dvma);
13928c2ecf20Sopenharmony_ci	dma_free_coherent(&op->dev,
13938c2ecf20Sopenharmony_ci			  QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
13948c2ecf20Sopenharmony_ci			  qpti->req_cpu, qpti->req_dvma);
13958c2ecf20Sopenharmony_ci#undef QSIZE
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_cifail_free_irq:
13988c2ecf20Sopenharmony_ci	free_irq(qpti->irq, qpti);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_cifail_unmap_regs:
14018c2ecf20Sopenharmony_ci	of_iounmap(&op->resource[0], qpti->qregs,
14028c2ecf20Sopenharmony_ci		   resource_size(&op->resource[0]));
14038c2ecf20Sopenharmony_ci	if (qpti->is_pti)
14048c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[0], qpti->sreg,
14058c2ecf20Sopenharmony_ci			   sizeof(unsigned char));
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_cifail_unlink:
14088c2ecf20Sopenharmony_ci	scsi_host_put(host);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	return -ENODEV;
14118c2ecf20Sopenharmony_ci}
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_cistatic int qpti_sbus_remove(struct platform_device *op)
14148c2ecf20Sopenharmony_ci{
14158c2ecf20Sopenharmony_ci	struct qlogicpti *qpti = dev_get_drvdata(&op->dev);
14168c2ecf20Sopenharmony_ci
14178c2ecf20Sopenharmony_ci	qpti_chain_del(qpti);
14188c2ecf20Sopenharmony_ci
14198c2ecf20Sopenharmony_ci	scsi_remove_host(qpti->qhost);
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci	/* Shut up the card. */
14228c2ecf20Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_CTRL);
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci	/* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */
14258c2ecf20Sopenharmony_ci	free_irq(qpti->irq, qpti);
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci#define QSIZE(entries)	(((entries) + 1) * QUEUE_ENTRY_LEN)
14288c2ecf20Sopenharmony_ci	dma_free_coherent(&op->dev,
14298c2ecf20Sopenharmony_ci			  QSIZE(RES_QUEUE_LEN),
14308c2ecf20Sopenharmony_ci			  qpti->res_cpu, qpti->res_dvma);
14318c2ecf20Sopenharmony_ci	dma_free_coherent(&op->dev,
14328c2ecf20Sopenharmony_ci			  QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
14338c2ecf20Sopenharmony_ci			  qpti->req_cpu, qpti->req_dvma);
14348c2ecf20Sopenharmony_ci#undef QSIZE
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	of_iounmap(&op->resource[0], qpti->qregs,
14378c2ecf20Sopenharmony_ci		   resource_size(&op->resource[0]));
14388c2ecf20Sopenharmony_ci	if (qpti->is_pti)
14398c2ecf20Sopenharmony_ci		of_iounmap(&op->resource[0], qpti->sreg, sizeof(unsigned char));
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	scsi_host_put(qpti->qhost);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	return 0;
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_cistatic const struct of_device_id qpti_match[] = {
14478c2ecf20Sopenharmony_ci	{
14488c2ecf20Sopenharmony_ci		.name = "ptisp",
14498c2ecf20Sopenharmony_ci	},
14508c2ecf20Sopenharmony_ci	{
14518c2ecf20Sopenharmony_ci		.name = "PTI,ptisp",
14528c2ecf20Sopenharmony_ci	},
14538c2ecf20Sopenharmony_ci	{
14548c2ecf20Sopenharmony_ci		.name = "QLGC,isp",
14558c2ecf20Sopenharmony_ci	},
14568c2ecf20Sopenharmony_ci	{
14578c2ecf20Sopenharmony_ci		.name = "SUNW,isp",
14588c2ecf20Sopenharmony_ci	},
14598c2ecf20Sopenharmony_ci	{},
14608c2ecf20Sopenharmony_ci};
14618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, qpti_match);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_cistatic struct platform_driver qpti_sbus_driver = {
14648c2ecf20Sopenharmony_ci	.driver = {
14658c2ecf20Sopenharmony_ci		.name = "qpti",
14668c2ecf20Sopenharmony_ci		.of_match_table = qpti_match,
14678c2ecf20Sopenharmony_ci	},
14688c2ecf20Sopenharmony_ci	.probe		= qpti_sbus_probe,
14698c2ecf20Sopenharmony_ci	.remove		= qpti_sbus_remove,
14708c2ecf20Sopenharmony_ci};
14718c2ecf20Sopenharmony_cimodule_platform_driver(qpti_sbus_driver);
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QlogicISP SBUS driver");
14748c2ecf20Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
14758c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
14768c2ecf20Sopenharmony_ciMODULE_VERSION("2.1");
14778c2ecf20Sopenharmony_ciMODULE_FIRMWARE("qlogic/isp1000.bin");
1478