162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* qlogicpti.c: Performance Technologies QlogicISP sbus card driver.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 1996, 2006, 2008 David S. Miller (davem@davemloft.net)
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * A lot of this driver was directly stolen from Erik H. Moe's PCI
762306a36Sopenharmony_ci * Qlogic ISP driver.  Mucho kudos to him for this code.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * An even bigger kudos to John Grana at Performance Technologies
1062306a36Sopenharmony_ci * for providing me with the hardware to write this driver, you rule
1162306a36Sopenharmony_ci * John you really do.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * May, 2, 1997: Added support for QLGC,isp --jj
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/delay.h>
1862306a36Sopenharmony_ci#include <linux/types.h>
1962306a36Sopenharmony_ci#include <linux/string.h>
2062306a36Sopenharmony_ci#include <linux/gfp.h>
2162306a36Sopenharmony_ci#include <linux/blkdev.h>
2262306a36Sopenharmony_ci#include <linux/proc_fs.h>
2362306a36Sopenharmony_ci#include <linux/stat.h>
2462306a36Sopenharmony_ci#include <linux/init.h>
2562306a36Sopenharmony_ci#include <linux/spinlock.h>
2662306a36Sopenharmony_ci#include <linux/interrupt.h>
2762306a36Sopenharmony_ci#include <linux/module.h>
2862306a36Sopenharmony_ci#include <linux/jiffies.h>
2962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
3062306a36Sopenharmony_ci#include <linux/of.h>
3162306a36Sopenharmony_ci#include <linux/platform_device.h>
3262306a36Sopenharmony_ci#include <linux/firmware.h>
3362306a36Sopenharmony_ci#include <linux/pgtable.h>
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <asm/byteorder.h>
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#include "qlogicpti.h"
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci#include <asm/dma.h>
4062306a36Sopenharmony_ci#include <asm/ptrace.h>
4162306a36Sopenharmony_ci#include <asm/oplib.h>
4262306a36Sopenharmony_ci#include <asm/io.h>
4362306a36Sopenharmony_ci#include <asm/irq.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <scsi/scsi.h>
4662306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
4762306a36Sopenharmony_ci#include <scsi/scsi_device.h>
4862306a36Sopenharmony_ci#include <scsi/scsi_eh.h>
4962306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
5062306a36Sopenharmony_ci#include <scsi/scsi_host.h>
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define MAX_TARGETS	16
5362306a36Sopenharmony_ci#define MAX_LUNS	8	/* 32 for 1.31 F/W */
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define DEFAULT_LOOP_COUNT	10000
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic struct qlogicpti *qptichain = NULL;
5862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(qptichain_lock);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define PACKB(a, b)			(((a)<<4)|(b))
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const u_char mbox_param[] = {
6362306a36Sopenharmony_ci	PACKB(1, 1),	/* MBOX_NO_OP */
6462306a36Sopenharmony_ci	PACKB(5, 5),	/* MBOX_LOAD_RAM */
6562306a36Sopenharmony_ci	PACKB(2, 0),	/* MBOX_EXEC_FIRMWARE */
6662306a36Sopenharmony_ci	PACKB(5, 5),	/* MBOX_DUMP_RAM */
6762306a36Sopenharmony_ci	PACKB(3, 3),	/* MBOX_WRITE_RAM_WORD */
6862306a36Sopenharmony_ci	PACKB(2, 3),	/* MBOX_READ_RAM_WORD */
6962306a36Sopenharmony_ci	PACKB(6, 6),	/* MBOX_MAILBOX_REG_TEST */
7062306a36Sopenharmony_ci	PACKB(2, 3),	/* MBOX_VERIFY_CHECKSUM	*/
7162306a36Sopenharmony_ci	PACKB(1, 3),	/* MBOX_ABOUT_FIRMWARE */
7262306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x0009 */
7362306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x000a */
7462306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x000b */
7562306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x000c */
7662306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x000d */
7762306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_CHECK_FIRMWARE */
7862306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x000f */
7962306a36Sopenharmony_ci	PACKB(5, 5),	/* MBOX_INIT_REQ_QUEUE */
8062306a36Sopenharmony_ci	PACKB(6, 6),	/* MBOX_INIT_RES_QUEUE */
8162306a36Sopenharmony_ci	PACKB(4, 4),	/* MBOX_EXECUTE_IOCB */
8262306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_WAKE_UP	*/
8362306a36Sopenharmony_ci	PACKB(1, 6),	/* MBOX_STOP_FIRMWARE */
8462306a36Sopenharmony_ci	PACKB(4, 4),	/* MBOX_ABORT */
8562306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_ABORT_DEVICE */
8662306a36Sopenharmony_ci	PACKB(3, 3),	/* MBOX_ABORT_TARGET */
8762306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_BUS_RESET */
8862306a36Sopenharmony_ci	PACKB(2, 3),	/* MBOX_STOP_QUEUE */
8962306a36Sopenharmony_ci	PACKB(2, 3),	/* MBOX_START_QUEUE */
9062306a36Sopenharmony_ci	PACKB(2, 3),	/* MBOX_SINGLE_STEP_QUEUE */
9162306a36Sopenharmony_ci	PACKB(2, 3),	/* MBOX_ABORT_QUEUE */
9262306a36Sopenharmony_ci	PACKB(2, 4),	/* MBOX_GET_DEV_QUEUE_STATUS */
9362306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x001e */
9462306a36Sopenharmony_ci	PACKB(1, 3),	/* MBOX_GET_FIRMWARE_STATUS */
9562306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_INIT_SCSI_ID */
9662306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_SELECT_TIMEOUT */
9762306a36Sopenharmony_ci	PACKB(1, 3),	/* MBOX_GET_RETRY_COUNT	*/
9862306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_TAG_AGE_LIMIT */
9962306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_CLOCK_RATE */
10062306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_ACT_NEG_STATE */
10162306a36Sopenharmony_ci	PACKB(1, 2),	/* MBOX_GET_ASYNC_DATA_SETUP_TIME */
10262306a36Sopenharmony_ci	PACKB(1, 3),	/* MBOX_GET_SBUS_PARAMS */
10362306a36Sopenharmony_ci	PACKB(2, 4),	/* MBOX_GET_TARGET_PARAMS */
10462306a36Sopenharmony_ci	PACKB(2, 4),	/* MBOX_GET_DEV_QUEUE_PARAMS */
10562306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x002a */
10662306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x002b */
10762306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x002c */
10862306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x002d */
10962306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x002e */
11062306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x002f */
11162306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_INIT_SCSI_ID */
11262306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_SELECT_TIMEOUT */
11362306a36Sopenharmony_ci	PACKB(3, 3),	/* MBOX_SET_RETRY_COUNT	*/
11462306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_TAG_AGE_LIMIT */
11562306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_CLOCK_RATE */
11662306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_ACTIVE_NEG_STATE */
11762306a36Sopenharmony_ci	PACKB(2, 2),	/* MBOX_SET_ASYNC_DATA_SETUP_TIME */
11862306a36Sopenharmony_ci	PACKB(3, 3),	/* MBOX_SET_SBUS_CONTROL_PARAMS */
11962306a36Sopenharmony_ci	PACKB(4, 4),	/* MBOX_SET_TARGET_PARAMS */
12062306a36Sopenharmony_ci	PACKB(4, 4),	/* MBOX_SET_DEV_QUEUE_PARAMS */
12162306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x003a */
12262306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x003b */
12362306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x003c */
12462306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x003d */
12562306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x003e */
12662306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x003f */
12762306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x0040 */
12862306a36Sopenharmony_ci	PACKB(0, 0),	/* 0x0041 */
12962306a36Sopenharmony_ci	PACKB(0, 0)	/* 0x0042 */
13062306a36Sopenharmony_ci};
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define MAX_MBOX_COMMAND	ARRAY_SIZE(mbox_param)
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci/* queue length's _must_ be power of two: */
13562306a36Sopenharmony_ci#define QUEUE_DEPTH(in, out, ql)	((in - out) & (ql))
13662306a36Sopenharmony_ci#define REQ_QUEUE_DEPTH(in, out)	QUEUE_DEPTH(in, out, 		     \
13762306a36Sopenharmony_ci						    QLOGICPTI_REQ_QUEUE_LEN)
13862306a36Sopenharmony_ci#define RES_QUEUE_DEPTH(in, out)	QUEUE_DEPTH(in, out, RES_QUEUE_LEN)
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic inline void qlogicpti_enable_irqs(struct qlogicpti *qpti)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	sbus_writew(SBUS_CTRL_ERIRQ | SBUS_CTRL_GENAB,
14362306a36Sopenharmony_ci		    qpti->qregs + SBUS_CTRL);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic inline void qlogicpti_disable_irqs(struct qlogicpti *qpti)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_CTRL);
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic inline void set_sbus_cfg1(struct qlogicpti *qpti)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	u16 val;
15462306a36Sopenharmony_ci	u8 bursts = qpti->bursts;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci#if 0	/* It appears that at least PTI cards do not support
15762306a36Sopenharmony_ci	 * 64-byte bursts and that setting the B64 bit actually
15862306a36Sopenharmony_ci	 * is a nop and the chip ends up using the smallest burst
15962306a36Sopenharmony_ci	 * size. -DaveM
16062306a36Sopenharmony_ci	 */
16162306a36Sopenharmony_ci	if (sbus_can_burst64() && (bursts & DMA_BURST64)) {
16262306a36Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B64);
16362306a36Sopenharmony_ci	} else
16462306a36Sopenharmony_ci#endif
16562306a36Sopenharmony_ci	if (bursts & DMA_BURST32) {
16662306a36Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B32);
16762306a36Sopenharmony_ci	} else if (bursts & DMA_BURST16) {
16862306a36Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B16);
16962306a36Sopenharmony_ci	} else if (bursts & DMA_BURST8) {
17062306a36Sopenharmony_ci		val = (SBUS_CFG1_BENAB | SBUS_CFG1_B8);
17162306a36Sopenharmony_ci	} else {
17262306a36Sopenharmony_ci		val = 0; /* No sbus bursts for you... */
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci	sbus_writew(val, qpti->qregs + SBUS_CFG1);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int qlogicpti_mbox_command(struct qlogicpti *qpti, u_short param[], int force)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int loop_count;
18062306a36Sopenharmony_ci	u16 tmp;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (mbox_param[param[0]] == 0)
18362306a36Sopenharmony_ci		return 1;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	/* Set SBUS semaphore. */
18662306a36Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE);
18762306a36Sopenharmony_ci	tmp |= SBUS_SEMAPHORE_LCK;
18862306a36Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	/* Wait for host IRQ bit to clear. */
19162306a36Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
19262306a36Sopenharmony_ci	while (--loop_count && (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_HIRQ)) {
19362306a36Sopenharmony_ci		barrier();
19462306a36Sopenharmony_ci		cpu_relax();
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	if (!loop_count)
19762306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command loop timeout #1\n",
19862306a36Sopenharmony_ci		       qpti->qpti_id);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Write mailbox command registers. */
20162306a36Sopenharmony_ci	switch (mbox_param[param[0]] >> 4) {
20262306a36Sopenharmony_ci	case 6: sbus_writew(param[5], qpti->qregs + MBOX5);
20362306a36Sopenharmony_ci		fallthrough;
20462306a36Sopenharmony_ci	case 5: sbus_writew(param[4], qpti->qregs + MBOX4);
20562306a36Sopenharmony_ci		fallthrough;
20662306a36Sopenharmony_ci	case 4: sbus_writew(param[3], qpti->qregs + MBOX3);
20762306a36Sopenharmony_ci		fallthrough;
20862306a36Sopenharmony_ci	case 3: sbus_writew(param[2], qpti->qregs + MBOX2);
20962306a36Sopenharmony_ci		fallthrough;
21062306a36Sopenharmony_ci	case 2: sbus_writew(param[1], qpti->qregs + MBOX1);
21162306a36Sopenharmony_ci		fallthrough;
21262306a36Sopenharmony_ci	case 1: sbus_writew(param[0], qpti->qregs + MBOX0);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/* Clear RISC interrupt. */
21662306a36Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + HCCTRL);
21762306a36Sopenharmony_ci	tmp |= HCCTRL_CRIRQ;
21862306a36Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + HCCTRL);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Clear SBUS semaphore. */
22162306a36Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Set HOST interrupt. */
22462306a36Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + HCCTRL);
22562306a36Sopenharmony_ci	tmp |= HCCTRL_SHIRQ;
22662306a36Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + HCCTRL);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	/* Wait for HOST interrupt clears. */
22962306a36Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
23062306a36Sopenharmony_ci	while (--loop_count &&
23162306a36Sopenharmony_ci	       (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_CRIRQ))
23262306a36Sopenharmony_ci		udelay(20);
23362306a36Sopenharmony_ci	if (!loop_count)
23462306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command[%04x] loop timeout #2\n",
23562306a36Sopenharmony_ci		       qpti->qpti_id, param[0]);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* Wait for SBUS semaphore to get set. */
23862306a36Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
23962306a36Sopenharmony_ci	while (--loop_count &&
24062306a36Sopenharmony_ci	       !(sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK)) {
24162306a36Sopenharmony_ci		udelay(20);
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		/* Workaround for some buggy chips. */
24462306a36Sopenharmony_ci		if (sbus_readw(qpti->qregs + MBOX0) & 0x4000)
24562306a36Sopenharmony_ci			break;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	if (!loop_count)
24862306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command[%04x] loop timeout #3\n",
24962306a36Sopenharmony_ci		       qpti->qpti_id, param[0]);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	/* Wait for MBOX busy condition to go away. */
25262306a36Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
25362306a36Sopenharmony_ci	while (--loop_count && (sbus_readw(qpti->qregs + MBOX0) == 0x04))
25462306a36Sopenharmony_ci		udelay(20);
25562306a36Sopenharmony_ci	if (!loop_count)
25662306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: mbox_command[%04x] loop timeout #4\n",
25762306a36Sopenharmony_ci		       qpti->qpti_id, param[0]);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	/* Read back output parameters. */
26062306a36Sopenharmony_ci	switch (mbox_param[param[0]] & 0xf) {
26162306a36Sopenharmony_ci	case 6: param[5] = sbus_readw(qpti->qregs + MBOX5);
26262306a36Sopenharmony_ci		fallthrough;
26362306a36Sopenharmony_ci	case 5: param[4] = sbus_readw(qpti->qregs + MBOX4);
26462306a36Sopenharmony_ci		fallthrough;
26562306a36Sopenharmony_ci	case 4: param[3] = sbus_readw(qpti->qregs + MBOX3);
26662306a36Sopenharmony_ci		fallthrough;
26762306a36Sopenharmony_ci	case 3: param[2] = sbus_readw(qpti->qregs + MBOX2);
26862306a36Sopenharmony_ci		fallthrough;
26962306a36Sopenharmony_ci	case 2: param[1] = sbus_readw(qpti->qregs + MBOX1);
27062306a36Sopenharmony_ci		fallthrough;
27162306a36Sopenharmony_ci	case 1: param[0] = sbus_readw(qpti->qregs + MBOX0);
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	/* Clear RISC interrupt. */
27562306a36Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + HCCTRL);
27662306a36Sopenharmony_ci	tmp |= HCCTRL_CRIRQ;
27762306a36Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + HCCTRL);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Release SBUS semaphore. */
28062306a36Sopenharmony_ci	tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE);
28162306a36Sopenharmony_ci	tmp &= ~(SBUS_SEMAPHORE_LCK);
28262306a36Sopenharmony_ci	sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* We're done. */
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	int i;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	qpti->host_param.initiator_scsi_id = qpti->scsi_id;
29362306a36Sopenharmony_ci	qpti->host_param.bus_reset_delay = 3;
29462306a36Sopenharmony_ci	qpti->host_param.retry_count = 0;
29562306a36Sopenharmony_ci	qpti->host_param.retry_delay = 5;
29662306a36Sopenharmony_ci	qpti->host_param.async_data_setup_time = 3;
29762306a36Sopenharmony_ci	qpti->host_param.req_ack_active_negation = 1;
29862306a36Sopenharmony_ci	qpti->host_param.data_line_active_negation = 1;
29962306a36Sopenharmony_ci	qpti->host_param.data_dma_burst_enable = 1;
30062306a36Sopenharmony_ci	qpti->host_param.command_dma_burst_enable = 1;
30162306a36Sopenharmony_ci	qpti->host_param.tag_aging = 8;
30262306a36Sopenharmony_ci	qpti->host_param.selection_timeout = 250;
30362306a36Sopenharmony_ci	qpti->host_param.max_queue_depth = 256;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	for(i = 0; i < MAX_TARGETS; i++) {
30662306a36Sopenharmony_ci		/*
30762306a36Sopenharmony_ci		 * disconnect, parity, arq, reneg on reset, and, oddly enough
30862306a36Sopenharmony_ci		 * tags...the midlayer's notion of tagged support has to match
30962306a36Sopenharmony_ci		 * our device settings, and since we base whether we enable a
31062306a36Sopenharmony_ci		 * tag on a  per-cmnd basis upon what the midlayer sez, we
31162306a36Sopenharmony_ci		 * actually enable the capability here.
31262306a36Sopenharmony_ci		 */
31362306a36Sopenharmony_ci		qpti->dev_param[i].device_flags = 0xcd;
31462306a36Sopenharmony_ci		qpti->dev_param[i].execution_throttle = 16;
31562306a36Sopenharmony_ci		if (qpti->ultra) {
31662306a36Sopenharmony_ci			qpti->dev_param[i].synchronous_period = 12;
31762306a36Sopenharmony_ci			qpti->dev_param[i].synchronous_offset = 8;
31862306a36Sopenharmony_ci		} else {
31962306a36Sopenharmony_ci			qpti->dev_param[i].synchronous_period = 25;
32062306a36Sopenharmony_ci			qpti->dev_param[i].synchronous_offset = 12;
32162306a36Sopenharmony_ci		}
32262306a36Sopenharmony_ci		qpti->dev_param[i].device_enable = 1;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int qlogicpti_reset_hardware(struct Scsi_Host *host)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
32962306a36Sopenharmony_ci	u_short param[6];
33062306a36Sopenharmony_ci	unsigned short risc_code_addr;
33162306a36Sopenharmony_ci	int loop_count, i;
33262306a36Sopenharmony_ci	unsigned long flags;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	risc_code_addr = 0x1000;	/* all load addresses are at 0x1000 */
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	spin_lock_irqsave(host->host_lock, flags);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Only reset the scsi bus if it is not free. */
34162306a36Sopenharmony_ci	if (sbus_readw(qpti->qregs + CPU_PCTRL) & CPU_PCTRL_BSY) {
34262306a36Sopenharmony_ci		sbus_writew(CPU_ORIDE_RMOD, qpti->qregs + CPU_ORIDE);
34362306a36Sopenharmony_ci		sbus_writew(CPU_CMD_BRESET, qpti->qregs + CPU_CMD);
34462306a36Sopenharmony_ci		udelay(400);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL);
34862306a36Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL);
34962306a36Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	loop_count = DEFAULT_LOOP_COUNT;
35262306a36Sopenharmony_ci	while (--loop_count && ((sbus_readw(qpti->qregs + MBOX0) & 0xff) == 0x04))
35362306a36Sopenharmony_ci		udelay(20);
35462306a36Sopenharmony_ci	if (!loop_count)
35562306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: reset_hardware loop timeout\n",
35662306a36Sopenharmony_ci		       qpti->qpti_id);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
35962306a36Sopenharmony_ci	set_sbus_cfg1(qpti);
36062306a36Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) {
36362306a36Sopenharmony_ci		qpti->ultra = 1;
36462306a36Sopenharmony_ci		sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA),
36562306a36Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
36662306a36Sopenharmony_ci	} else {
36762306a36Sopenharmony_ci		qpti->ultra = 0;
36862306a36Sopenharmony_ci		sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT),
36962306a36Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
37062306a36Sopenharmony_ci	}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	/* reset adapter and per-device default values. */
37362306a36Sopenharmony_ci	/* do it after finding out whether we're ultra mode capable */
37462306a36Sopenharmony_ci	qlogicpti_set_hostdev_defaults(qpti);
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	/* Release the RISC processor. */
37762306a36Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/* Get RISC to start executing the firmware code. */
38062306a36Sopenharmony_ci	param[0] = MBOX_EXEC_FIRMWARE;
38162306a36Sopenharmony_ci	param[1] = risc_code_addr;
38262306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
38362306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n",
38462306a36Sopenharmony_ci		       qpti->qpti_id);
38562306a36Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
38662306a36Sopenharmony_ci		return 1;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* Set initiator scsi ID. */
39062306a36Sopenharmony_ci	param[0] = MBOX_SET_INIT_SCSI_ID;
39162306a36Sopenharmony_ci	param[1] = qpti->host_param.initiator_scsi_id;
39262306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
39362306a36Sopenharmony_ci	   (param[0] != MBOX_COMMAND_COMPLETE)) {
39462306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n",
39562306a36Sopenharmony_ci		       qpti->qpti_id);
39662306a36Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
39762306a36Sopenharmony_ci		return 1;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* Initialize state of the queues, both hw and sw. */
40162306a36Sopenharmony_ci	qpti->req_in_ptr = qpti->res_out_ptr = 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	param[0] = MBOX_INIT_RES_QUEUE;
40462306a36Sopenharmony_ci	param[1] = RES_QUEUE_LEN + 1;
40562306a36Sopenharmony_ci	param[2] = (u_short) (qpti->res_dvma >> 16);
40662306a36Sopenharmony_ci	param[3] = (u_short) (qpti->res_dvma & 0xffff);
40762306a36Sopenharmony_ci	param[4] = param[5] = 0;
40862306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
40962306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n",
41062306a36Sopenharmony_ci		       qpti->qpti_id);
41162306a36Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
41262306a36Sopenharmony_ci		return 1;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	param[0] = MBOX_INIT_REQ_QUEUE;
41662306a36Sopenharmony_ci	param[1] = QLOGICPTI_REQ_QUEUE_LEN + 1;
41762306a36Sopenharmony_ci	param[2] = (u_short) (qpti->req_dvma >> 16);
41862306a36Sopenharmony_ci	param[3] = (u_short) (qpti->req_dvma & 0xffff);
41962306a36Sopenharmony_ci	param[4] = param[5] = 0;
42062306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
42162306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n",
42262306a36Sopenharmony_ci		       qpti->qpti_id);
42362306a36Sopenharmony_ci		spin_unlock_irqrestore(host->host_lock, flags);
42462306a36Sopenharmony_ci		return 1;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	param[0] = MBOX_SET_RETRY_COUNT;
42862306a36Sopenharmony_ci	param[1] = qpti->host_param.retry_count;
42962306a36Sopenharmony_ci	param[2] = qpti->host_param.retry_delay;
43062306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	param[0] = MBOX_SET_TAG_AGE_LIMIT;
43362306a36Sopenharmony_ci	param[1] = qpti->host_param.tag_aging;
43462306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	for (i = 0; i < MAX_TARGETS; i++) {
43762306a36Sopenharmony_ci		param[0] = MBOX_GET_DEV_QUEUE_PARAMS;
43862306a36Sopenharmony_ci		param[1] = (i << 8);
43962306a36Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 0);
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	param[0] = MBOX_GET_FIRMWARE_STATUS;
44362306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	param[0] = MBOX_SET_SELECT_TIMEOUT;
44662306a36Sopenharmony_ci	param[1] = qpti->host_param.selection_timeout;
44762306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	for (i = 0; i < MAX_TARGETS; i++) {
45062306a36Sopenharmony_ci		param[0] = MBOX_SET_TARGET_PARAMS;
45162306a36Sopenharmony_ci		param[1] = (i << 8);
45262306a36Sopenharmony_ci		param[2] = (qpti->dev_param[i].device_flags << 8);
45362306a36Sopenharmony_ci		/*
45462306a36Sopenharmony_ci		 * Since we're now loading 1.31 f/w, force narrow/async.
45562306a36Sopenharmony_ci		 */
45662306a36Sopenharmony_ci		param[2] |= 0xc0;
45762306a36Sopenharmony_ci		param[3] = 0;	/* no offset, we do not have sync mode yet */
45862306a36Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 0);
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	/*
46262306a36Sopenharmony_ci	 * Always (sigh) do an initial bus reset (kicks f/w).
46362306a36Sopenharmony_ci	 */
46462306a36Sopenharmony_ci	param[0] = MBOX_BUS_RESET;
46562306a36Sopenharmony_ci	param[1] = qpti->host_param.bus_reset_delay;
46662306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
46762306a36Sopenharmony_ci	qpti->send_marker = 1;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	spin_unlock_irqrestore(host->host_lock, flags);
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci#define PTI_RESET_LIMIT 400
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic int qlogicpti_load_firmware(struct qlogicpti *qpti)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	const struct firmware *fw;
47862306a36Sopenharmony_ci	const char fwname[] = "qlogic/isp1000.bin";
47962306a36Sopenharmony_ci	const __le16 *fw_data;
48062306a36Sopenharmony_ci	struct Scsi_Host *host = qpti->qhost;
48162306a36Sopenharmony_ci	unsigned short csum = 0;
48262306a36Sopenharmony_ci	unsigned short param[6];
48362306a36Sopenharmony_ci	unsigned short risc_code_addr, risc_code_length;
48462306a36Sopenharmony_ci	int err;
48562306a36Sopenharmony_ci	unsigned long flags;
48662306a36Sopenharmony_ci	int i, timeout;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	err = request_firmware(&fw, fwname, &qpti->op->dev);
48962306a36Sopenharmony_ci	if (err) {
49062306a36Sopenharmony_ci		printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
49162306a36Sopenharmony_ci		       fwname, err);
49262306a36Sopenharmony_ci		return err;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci	if (fw->size % 2) {
49562306a36Sopenharmony_ci		printk(KERN_ERR "Bogus length %zu in image \"%s\"\n",
49662306a36Sopenharmony_ci		       fw->size, fwname);
49762306a36Sopenharmony_ci		err = -EINVAL;
49862306a36Sopenharmony_ci		goto outfirm;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	fw_data = (const __le16 *)&fw->data[0];
50162306a36Sopenharmony_ci	risc_code_addr = 0x1000;	/* all f/w modules load at 0x1000 */
50262306a36Sopenharmony_ci	risc_code_length = fw->size / 2;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	spin_lock_irqsave(host->host_lock, flags);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* Verify the checksum twice, one before loading it, and once
50762306a36Sopenharmony_ci	 * afterwards via the mailbox commands.
50862306a36Sopenharmony_ci	 */
50962306a36Sopenharmony_ci	for (i = 0; i < risc_code_length; i++)
51062306a36Sopenharmony_ci		csum += __le16_to_cpu(fw_data[i]);
51162306a36Sopenharmony_ci	if (csum) {
51262306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!",
51362306a36Sopenharmony_ci		       qpti->qpti_id);
51462306a36Sopenharmony_ci		err = 1;
51562306a36Sopenharmony_ci		goto out;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci	sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL);
51862306a36Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL);
51962306a36Sopenharmony_ci	sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL);
52062306a36Sopenharmony_ci	timeout = PTI_RESET_LIMIT;
52162306a36Sopenharmony_ci	while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET))
52262306a36Sopenharmony_ci		udelay(20);
52362306a36Sopenharmony_ci	if (!timeout) {
52462306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id);
52562306a36Sopenharmony_ci		err = 1;
52662306a36Sopenharmony_ci		goto out;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL);
53062306a36Sopenharmony_ci	mdelay(1);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	sbus_writew((SBUS_CTRL_GENAB | SBUS_CTRL_ERIRQ), qpti->qregs + SBUS_CTRL);
53362306a36Sopenharmony_ci	set_sbus_cfg1(qpti);
53462306a36Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) {
53762306a36Sopenharmony_ci		qpti->ultra = 1;
53862306a36Sopenharmony_ci		sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA),
53962306a36Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
54062306a36Sopenharmony_ci	} else {
54162306a36Sopenharmony_ci		qpti->ultra = 0;
54262306a36Sopenharmony_ci		sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT),
54362306a36Sopenharmony_ci			    qpti->qregs + RISC_MTREG);
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Pin lines are only stable while RISC is paused. */
54962306a36Sopenharmony_ci	sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL);
55062306a36Sopenharmony_ci	if (sbus_readw(qpti->qregs + CPU_PDIFF) & CPU_PDIFF_MODE)
55162306a36Sopenharmony_ci		qpti->differential = 1;
55262306a36Sopenharmony_ci	else
55362306a36Sopenharmony_ci		qpti->differential = 0;
55462306a36Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	/* This shouldn't be necessary- we've reset things so we should be
55762306a36Sopenharmony_ci	   running from the ROM now.. */
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	param[0] = MBOX_STOP_FIRMWARE;
56062306a36Sopenharmony_ci	param[1] = param[2] = param[3] = param[4] = param[5] = 0;
56162306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1)) {
56262306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: Cannot stop firmware for reload.\n",
56362306a36Sopenharmony_ci		       qpti->qpti_id);
56462306a36Sopenharmony_ci		err = 1;
56562306a36Sopenharmony_ci		goto out;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	/* Load it up.. */
56962306a36Sopenharmony_ci	for (i = 0; i < risc_code_length; i++) {
57062306a36Sopenharmony_ci		param[0] = MBOX_WRITE_RAM_WORD;
57162306a36Sopenharmony_ci		param[1] = risc_code_addr + i;
57262306a36Sopenharmony_ci		param[2] = __le16_to_cpu(fw_data[i]);
57362306a36Sopenharmony_ci		if (qlogicpti_mbox_command(qpti, param, 1) ||
57462306a36Sopenharmony_ci		    param[0] != MBOX_COMMAND_COMPLETE) {
57562306a36Sopenharmony_ci			printk("qlogicpti%d: Firmware dload failed, I'm bolixed!\n",
57662306a36Sopenharmony_ci			       qpti->qpti_id);
57762306a36Sopenharmony_ci			err = 1;
57862306a36Sopenharmony_ci			goto out;
57962306a36Sopenharmony_ci		}
58062306a36Sopenharmony_ci	}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	/* Reset the ISP again. */
58362306a36Sopenharmony_ci	sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL);
58462306a36Sopenharmony_ci	mdelay(1);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
58762306a36Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
58862306a36Sopenharmony_ci	sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	/* Ask ISP to verify the checksum of the new code. */
59162306a36Sopenharmony_ci	param[0] = MBOX_VERIFY_CHECKSUM;
59262306a36Sopenharmony_ci	param[1] = risc_code_addr;
59362306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
59462306a36Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
59562306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: New firmware csum failure!\n",
59662306a36Sopenharmony_ci		       qpti->qpti_id);
59762306a36Sopenharmony_ci		err = 1;
59862306a36Sopenharmony_ci		goto out;
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci	/* Start using newly downloaded firmware. */
60262306a36Sopenharmony_ci	param[0] = MBOX_EXEC_FIRMWARE;
60362306a36Sopenharmony_ci	param[1] = risc_code_addr;
60462306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 1);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	param[0] = MBOX_ABOUT_FIRMWARE;
60762306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
60862306a36Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
60962306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: AboutFirmware cmd fails.\n",
61062306a36Sopenharmony_ci		       qpti->qpti_id);
61162306a36Sopenharmony_ci		err = 1;
61262306a36Sopenharmony_ci		goto out;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	/* Snag the major and minor revisions from the result. */
61662306a36Sopenharmony_ci	qpti->fware_majrev = param[1];
61762306a36Sopenharmony_ci	qpti->fware_minrev = param[2];
61862306a36Sopenharmony_ci	qpti->fware_micrev = param[3];
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Set the clock rate */
62162306a36Sopenharmony_ci	param[0] = MBOX_SET_CLOCK_RATE;
62262306a36Sopenharmony_ci	param[1] = qpti->clock;
62362306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 1) ||
62462306a36Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
62562306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: could not set clock rate.\n",
62662306a36Sopenharmony_ci		       qpti->qpti_id);
62762306a36Sopenharmony_ci		err = 1;
62862306a36Sopenharmony_ci		goto out;
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (qpti->is_pti != 0) {
63262306a36Sopenharmony_ci		/* Load scsi initiator ID and interrupt level into sbus static ram. */
63362306a36Sopenharmony_ci		param[0] = MBOX_WRITE_RAM_WORD;
63462306a36Sopenharmony_ci		param[1] = 0xff80;
63562306a36Sopenharmony_ci		param[2] = (unsigned short) qpti->scsi_id;
63662306a36Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 1);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci		param[0] = MBOX_WRITE_RAM_WORD;
63962306a36Sopenharmony_ci		param[1] = 0xff00;
64062306a36Sopenharmony_ci		param[2] = (unsigned short) 3;
64162306a36Sopenharmony_ci		qlogicpti_mbox_command(qpti, param, 1);
64262306a36Sopenharmony_ci	}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_ciout:
64562306a36Sopenharmony_ci	spin_unlock_irqrestore(host->host_lock, flags);
64662306a36Sopenharmony_cioutfirm:
64762306a36Sopenharmony_ci	release_firmware(fw);
64862306a36Sopenharmony_ci	return err;
64962306a36Sopenharmony_ci}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_cistatic int qlogicpti_verify_tmon(struct qlogicpti *qpti)
65262306a36Sopenharmony_ci{
65362306a36Sopenharmony_ci	int curstat = sbus_readb(qpti->sreg);
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	curstat &= 0xf0;
65662306a36Sopenharmony_ci	if (!(curstat & SREG_FUSE) && (qpti->swsreg & SREG_FUSE))
65762306a36Sopenharmony_ci		printk("qlogicpti%d: Fuse returned to normal state.\n", qpti->qpti_id);
65862306a36Sopenharmony_ci	if (!(curstat & SREG_TPOWER) && (qpti->swsreg & SREG_TPOWER))
65962306a36Sopenharmony_ci		printk("qlogicpti%d: termpwr back to normal state.\n", qpti->qpti_id);
66062306a36Sopenharmony_ci	if (curstat != qpti->swsreg) {
66162306a36Sopenharmony_ci		int error = 0;
66262306a36Sopenharmony_ci		if (curstat & SREG_FUSE) {
66362306a36Sopenharmony_ci			error++;
66462306a36Sopenharmony_ci			printk("qlogicpti%d: Fuse is open!\n", qpti->qpti_id);
66562306a36Sopenharmony_ci		}
66662306a36Sopenharmony_ci		if (curstat & SREG_TPOWER) {
66762306a36Sopenharmony_ci			error++;
66862306a36Sopenharmony_ci			printk("qlogicpti%d: termpwr failure\n", qpti->qpti_id);
66962306a36Sopenharmony_ci		}
67062306a36Sopenharmony_ci		if (qpti->differential &&
67162306a36Sopenharmony_ci		    (curstat & SREG_DSENSE) != SREG_DSENSE) {
67262306a36Sopenharmony_ci			error++;
67362306a36Sopenharmony_ci			printk("qlogicpti%d: You have a single ended device on a "
67462306a36Sopenharmony_ci			       "differential bus!  Please fix!\n", qpti->qpti_id);
67562306a36Sopenharmony_ci		}
67662306a36Sopenharmony_ci		qpti->swsreg = curstat;
67762306a36Sopenharmony_ci		return error;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci	return 0;
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic irqreturn_t qpti_intr(int irq, void *dev_id);
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic void qpti_chain_add(struct qlogicpti *qpti)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci	spin_lock_irq(&qptichain_lock);
68762306a36Sopenharmony_ci	if (qptichain != NULL) {
68862306a36Sopenharmony_ci		struct qlogicpti *qlink = qptichain;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci		while(qlink->next)
69162306a36Sopenharmony_ci			qlink = qlink->next;
69262306a36Sopenharmony_ci		qlink->next = qpti;
69362306a36Sopenharmony_ci	} else {
69462306a36Sopenharmony_ci		qptichain = qpti;
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci	qpti->next = NULL;
69762306a36Sopenharmony_ci	spin_unlock_irq(&qptichain_lock);
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_cistatic void qpti_chain_del(struct qlogicpti *qpti)
70162306a36Sopenharmony_ci{
70262306a36Sopenharmony_ci	spin_lock_irq(&qptichain_lock);
70362306a36Sopenharmony_ci	if (qptichain == qpti) {
70462306a36Sopenharmony_ci		qptichain = qpti->next;
70562306a36Sopenharmony_ci	} else {
70662306a36Sopenharmony_ci		struct qlogicpti *qlink = qptichain;
70762306a36Sopenharmony_ci		while(qlink->next != qpti)
70862306a36Sopenharmony_ci			qlink = qlink->next;
70962306a36Sopenharmony_ci		qlink->next = qpti->next;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	qpti->next = NULL;
71262306a36Sopenharmony_ci	spin_unlock_irq(&qptichain_lock);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int qpti_map_regs(struct qlogicpti *qpti)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct platform_device *op = qpti->op;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	qpti->qregs = of_ioremap(&op->resource[0], 0,
72062306a36Sopenharmony_ci				 resource_size(&op->resource[0]),
72162306a36Sopenharmony_ci				 "PTI Qlogic/ISP");
72262306a36Sopenharmony_ci	if (!qpti->qregs) {
72362306a36Sopenharmony_ci		printk("PTI: Qlogic/ISP registers are unmappable\n");
72462306a36Sopenharmony_ci		return -ENODEV;
72562306a36Sopenharmony_ci	}
72662306a36Sopenharmony_ci	if (qpti->is_pti) {
72762306a36Sopenharmony_ci		qpti->sreg = of_ioremap(&op->resource[0], (16 * 4096),
72862306a36Sopenharmony_ci					sizeof(unsigned char),
72962306a36Sopenharmony_ci					"PTI Qlogic/ISP statreg");
73062306a36Sopenharmony_ci		if (!qpti->sreg) {
73162306a36Sopenharmony_ci			printk("PTI: Qlogic/ISP status register is unmappable\n");
73262306a36Sopenharmony_ci			return -ENODEV;
73362306a36Sopenharmony_ci		}
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci	return 0;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int qpti_register_irq(struct qlogicpti *qpti)
73962306a36Sopenharmony_ci{
74062306a36Sopenharmony_ci	struct platform_device *op = qpti->op;
74162306a36Sopenharmony_ci
74262306a36Sopenharmony_ci	qpti->qhost->irq = qpti->irq = op->archdata.irqs[0];
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	/* We used to try various overly-clever things to
74562306a36Sopenharmony_ci	 * reduce the interrupt processing overhead on
74662306a36Sopenharmony_ci	 * sun4c/sun4m when multiple PTI's shared the
74762306a36Sopenharmony_ci	 * same IRQ.  It was too complex and messy to
74862306a36Sopenharmony_ci	 * sanely maintain.
74962306a36Sopenharmony_ci	 */
75062306a36Sopenharmony_ci	if (request_irq(qpti->irq, qpti_intr,
75162306a36Sopenharmony_ci			IRQF_SHARED, "QlogicPTI", qpti))
75262306a36Sopenharmony_ci		goto fail;
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	printk("qlogicpti%d: IRQ %d ", qpti->qpti_id, qpti->irq);
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	return 0;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_cifail:
75962306a36Sopenharmony_ci	printk("qlogicpti%d: Cannot acquire irq line\n", qpti->qpti_id);
76062306a36Sopenharmony_ci	return -1;
76162306a36Sopenharmony_ci}
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_cistatic void qpti_get_scsi_id(struct qlogicpti *qpti)
76462306a36Sopenharmony_ci{
76562306a36Sopenharmony_ci	struct platform_device *op = qpti->op;
76662306a36Sopenharmony_ci	struct device_node *dp;
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	dp = op->dev.of_node;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	qpti->scsi_id = of_getintprop_default(dp, "initiator-id", -1);
77162306a36Sopenharmony_ci	if (qpti->scsi_id == -1)
77262306a36Sopenharmony_ci		qpti->scsi_id = of_getintprop_default(dp, "scsi-initiator-id",
77362306a36Sopenharmony_ci						      -1);
77462306a36Sopenharmony_ci	if (qpti->scsi_id == -1)
77562306a36Sopenharmony_ci		qpti->scsi_id =
77662306a36Sopenharmony_ci			of_getintprop_default(dp->parent,
77762306a36Sopenharmony_ci					      "scsi-initiator-id", 7);
77862306a36Sopenharmony_ci	qpti->qhost->this_id = qpti->scsi_id;
77962306a36Sopenharmony_ci	qpti->qhost->max_sectors = 64;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	printk("SCSI ID %d ", qpti->scsi_id);
78262306a36Sopenharmony_ci}
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_cistatic void qpti_get_bursts(struct qlogicpti *qpti)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct platform_device *op = qpti->op;
78762306a36Sopenharmony_ci	u8 bursts, bmask;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	bursts = of_getintprop_default(op->dev.of_node, "burst-sizes", 0xff);
79062306a36Sopenharmony_ci	bmask = of_getintprop_default(op->dev.of_node->parent, "burst-sizes", 0xff);
79162306a36Sopenharmony_ci	if (bmask != 0xff)
79262306a36Sopenharmony_ci		bursts &= bmask;
79362306a36Sopenharmony_ci	if (bursts == 0xff ||
79462306a36Sopenharmony_ci	    (bursts & DMA_BURST16) == 0 ||
79562306a36Sopenharmony_ci	    (bursts & DMA_BURST32) == 0)
79662306a36Sopenharmony_ci		bursts = (DMA_BURST32 - 1);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci	qpti->bursts = bursts;
79962306a36Sopenharmony_ci}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_cistatic void qpti_get_clock(struct qlogicpti *qpti)
80262306a36Sopenharmony_ci{
80362306a36Sopenharmony_ci	unsigned int cfreq;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	/* Check for what the clock input to this card is.
80662306a36Sopenharmony_ci	 * Default to 40Mhz.
80762306a36Sopenharmony_ci	 */
80862306a36Sopenharmony_ci	cfreq = prom_getintdefault(qpti->prom_node,"clock-frequency",40000000);
80962306a36Sopenharmony_ci	qpti->clock = (cfreq + 500000)/1000000;
81062306a36Sopenharmony_ci	if (qpti->clock == 0) /* bullshit */
81162306a36Sopenharmony_ci		qpti->clock = 40;
81262306a36Sopenharmony_ci}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci/* The request and response queues must each be aligned
81562306a36Sopenharmony_ci * on a page boundary.
81662306a36Sopenharmony_ci */
81762306a36Sopenharmony_cistatic int qpti_map_queues(struct qlogicpti *qpti)
81862306a36Sopenharmony_ci{
81962306a36Sopenharmony_ci	struct platform_device *op = qpti->op;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci#define QSIZE(entries)	(((entries) + 1) * QUEUE_ENTRY_LEN)
82262306a36Sopenharmony_ci	qpti->res_cpu = dma_alloc_coherent(&op->dev,
82362306a36Sopenharmony_ci					   QSIZE(RES_QUEUE_LEN),
82462306a36Sopenharmony_ci					   &qpti->res_dvma, GFP_ATOMIC);
82562306a36Sopenharmony_ci	if (qpti->res_cpu == NULL ||
82662306a36Sopenharmony_ci	    qpti->res_dvma == 0) {
82762306a36Sopenharmony_ci		printk("QPTI: Cannot map response queue.\n");
82862306a36Sopenharmony_ci		return -1;
82962306a36Sopenharmony_ci	}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	qpti->req_cpu = dma_alloc_coherent(&op->dev,
83262306a36Sopenharmony_ci					   QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
83362306a36Sopenharmony_ci					   &qpti->req_dvma, GFP_ATOMIC);
83462306a36Sopenharmony_ci	if (qpti->req_cpu == NULL ||
83562306a36Sopenharmony_ci	    qpti->req_dvma == 0) {
83662306a36Sopenharmony_ci		dma_free_coherent(&op->dev, QSIZE(RES_QUEUE_LEN),
83762306a36Sopenharmony_ci				  qpti->res_cpu, qpti->res_dvma);
83862306a36Sopenharmony_ci		printk("QPTI: Cannot map request queue.\n");
83962306a36Sopenharmony_ci		return -1;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci	memset(qpti->res_cpu, 0, QSIZE(RES_QUEUE_LEN));
84262306a36Sopenharmony_ci	memset(qpti->req_cpu, 0, QSIZE(QLOGICPTI_REQ_QUEUE_LEN));
84362306a36Sopenharmony_ci	return 0;
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic const char *qlogicpti_info(struct Scsi_Host *host)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	static char buf[80];
84962306a36Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	sprintf(buf, "PTI Qlogic,ISP SBUS SCSI irq %d regs at %p",
85262306a36Sopenharmony_ci		qpti->qhost->irq, qpti->qregs);
85362306a36Sopenharmony_ci	return buf;
85462306a36Sopenharmony_ci}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci/* I am a certified frobtronicist. */
85762306a36Sopenharmony_cistatic inline void marker_frob(struct Command_Entry *cmd)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct Marker_Entry *marker = (struct Marker_Entry *) cmd;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	memset(marker, 0, sizeof(struct Marker_Entry));
86262306a36Sopenharmony_ci	marker->hdr.entry_cnt = 1;
86362306a36Sopenharmony_ci	marker->hdr.entry_type = ENTRY_MARKER;
86462306a36Sopenharmony_ci	marker->modifier = SYNC_ALL;
86562306a36Sopenharmony_ci	marker->rsvd = 0;
86662306a36Sopenharmony_ci}
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cistatic inline void cmd_frob(struct Command_Entry *cmd, struct scsi_cmnd *Cmnd,
86962306a36Sopenharmony_ci			    struct qlogicpti *qpti)
87062306a36Sopenharmony_ci{
87162306a36Sopenharmony_ci	memset(cmd, 0, sizeof(struct Command_Entry));
87262306a36Sopenharmony_ci	cmd->hdr.entry_cnt = 1;
87362306a36Sopenharmony_ci	cmd->hdr.entry_type = ENTRY_COMMAND;
87462306a36Sopenharmony_ci	cmd->target_id = Cmnd->device->id;
87562306a36Sopenharmony_ci	cmd->target_lun = Cmnd->device->lun;
87662306a36Sopenharmony_ci	cmd->cdb_length = Cmnd->cmd_len;
87762306a36Sopenharmony_ci	cmd->control_flags = 0;
87862306a36Sopenharmony_ci	if (Cmnd->device->tagged_supported) {
87962306a36Sopenharmony_ci		if (qpti->cmd_count[Cmnd->device->id] == 0)
88062306a36Sopenharmony_ci			qpti->tag_ages[Cmnd->device->id] = jiffies;
88162306a36Sopenharmony_ci		if (time_after(jiffies, qpti->tag_ages[Cmnd->device->id] + (5*HZ))) {
88262306a36Sopenharmony_ci			cmd->control_flags = CFLAG_ORDERED_TAG;
88362306a36Sopenharmony_ci			qpti->tag_ages[Cmnd->device->id] = jiffies;
88462306a36Sopenharmony_ci		} else
88562306a36Sopenharmony_ci			cmd->control_flags = CFLAG_SIMPLE_TAG;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci	if ((Cmnd->cmnd[0] == WRITE_6) ||
88862306a36Sopenharmony_ci	    (Cmnd->cmnd[0] == WRITE_10) ||
88962306a36Sopenharmony_ci	    (Cmnd->cmnd[0] == WRITE_12))
89062306a36Sopenharmony_ci		cmd->control_flags |= CFLAG_WRITE;
89162306a36Sopenharmony_ci	else
89262306a36Sopenharmony_ci		cmd->control_flags |= CFLAG_READ;
89362306a36Sopenharmony_ci	cmd->time_out = scsi_cmd_to_rq(Cmnd)->timeout / HZ;
89462306a36Sopenharmony_ci	memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len);
89562306a36Sopenharmony_ci}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci/* Do it to it baby. */
89862306a36Sopenharmony_cistatic inline int load_cmd(struct scsi_cmnd *Cmnd, struct Command_Entry *cmd,
89962306a36Sopenharmony_ci			   struct qlogicpti *qpti, u_int in_ptr, u_int out_ptr)
90062306a36Sopenharmony_ci{
90162306a36Sopenharmony_ci	struct dataseg *ds;
90262306a36Sopenharmony_ci	struct scatterlist *sg, *s;
90362306a36Sopenharmony_ci	int i, n;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (scsi_bufflen(Cmnd)) {
90662306a36Sopenharmony_ci		int sg_count;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci		sg = scsi_sglist(Cmnd);
90962306a36Sopenharmony_ci		sg_count = dma_map_sg(&qpti->op->dev, sg,
91062306a36Sopenharmony_ci				      scsi_sg_count(Cmnd),
91162306a36Sopenharmony_ci				      Cmnd->sc_data_direction);
91262306a36Sopenharmony_ci		if (!sg_count)
91362306a36Sopenharmony_ci			return -1;
91462306a36Sopenharmony_ci		ds = cmd->dataseg;
91562306a36Sopenharmony_ci		cmd->segment_cnt = sg_count;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci		/* Fill in first four sg entries: */
91862306a36Sopenharmony_ci		n = sg_count;
91962306a36Sopenharmony_ci		if (n > 4)
92062306a36Sopenharmony_ci			n = 4;
92162306a36Sopenharmony_ci		for_each_sg(sg, s, n, i) {
92262306a36Sopenharmony_ci			ds[i].d_base = sg_dma_address(s);
92362306a36Sopenharmony_ci			ds[i].d_count = sg_dma_len(s);
92462306a36Sopenharmony_ci		}
92562306a36Sopenharmony_ci		sg_count -= 4;
92662306a36Sopenharmony_ci		sg = s;
92762306a36Sopenharmony_ci		while (sg_count > 0) {
92862306a36Sopenharmony_ci			struct Continuation_Entry *cont;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci			++cmd->hdr.entry_cnt;
93162306a36Sopenharmony_ci			cont = (struct Continuation_Entry *) &qpti->req_cpu[in_ptr];
93262306a36Sopenharmony_ci			in_ptr = NEXT_REQ_PTR(in_ptr);
93362306a36Sopenharmony_ci			if (in_ptr == out_ptr)
93462306a36Sopenharmony_ci				return -1;
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ci			cont->hdr.entry_type = ENTRY_CONTINUATION;
93762306a36Sopenharmony_ci			cont->hdr.entry_cnt = 0;
93862306a36Sopenharmony_ci			cont->hdr.sys_def_1 = 0;
93962306a36Sopenharmony_ci			cont->hdr.flags = 0;
94062306a36Sopenharmony_ci			cont->reserved = 0;
94162306a36Sopenharmony_ci			ds = cont->dataseg;
94262306a36Sopenharmony_ci			n = sg_count;
94362306a36Sopenharmony_ci			if (n > 7)
94462306a36Sopenharmony_ci				n = 7;
94562306a36Sopenharmony_ci			for_each_sg(sg, s, n, i) {
94662306a36Sopenharmony_ci				ds[i].d_base = sg_dma_address(s);
94762306a36Sopenharmony_ci				ds[i].d_count = sg_dma_len(s);
94862306a36Sopenharmony_ci			}
94962306a36Sopenharmony_ci			sg_count -= n;
95062306a36Sopenharmony_ci			sg = s;
95162306a36Sopenharmony_ci		}
95262306a36Sopenharmony_ci	} else {
95362306a36Sopenharmony_ci		cmd->dataseg[0].d_base = 0;
95462306a36Sopenharmony_ci		cmd->dataseg[0].d_count = 0;
95562306a36Sopenharmony_ci		cmd->segment_cnt = 1; /* Shouldn't this be 0? */
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	/* Committed, record Scsi_Cmd so we can find it later. */
95962306a36Sopenharmony_ci	cmd->handle = in_ptr;
96062306a36Sopenharmony_ci	qpti->cmd_slots[in_ptr] = Cmnd;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	qpti->cmd_count[Cmnd->device->id]++;
96362306a36Sopenharmony_ci	sbus_writew(in_ptr, qpti->qregs + MBOX4);
96462306a36Sopenharmony_ci	qpti->req_in_ptr = in_ptr;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	return in_ptr;
96762306a36Sopenharmony_ci}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_cistatic inline void update_can_queue(struct Scsi_Host *host, u_int in_ptr, u_int out_ptr)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	/* Temporary workaround until bug is found and fixed (one bug has been found
97262306a36Sopenharmony_ci	   already, but fixing it makes things even worse) -jj */
97362306a36Sopenharmony_ci	int num_free = QLOGICPTI_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr) - 64;
97462306a36Sopenharmony_ci	host->can_queue = scsi_host_busy(host) + num_free;
97562306a36Sopenharmony_ci	host->sg_tablesize = QLOGICPTI_MAX_SG(num_free);
97662306a36Sopenharmony_ci}
97762306a36Sopenharmony_ci
97862306a36Sopenharmony_cistatic int qlogicpti_slave_configure(struct scsi_device *sdev)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	struct qlogicpti *qpti = shost_priv(sdev->host);
98162306a36Sopenharmony_ci	int tgt = sdev->id;
98262306a36Sopenharmony_ci	u_short param[6];
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* tags handled in midlayer */
98562306a36Sopenharmony_ci	/* enable sync mode? */
98662306a36Sopenharmony_ci	if (sdev->sdtr) {
98762306a36Sopenharmony_ci		qpti->dev_param[tgt].device_flags |= 0x10;
98862306a36Sopenharmony_ci	} else {
98962306a36Sopenharmony_ci		qpti->dev_param[tgt].synchronous_offset = 0;
99062306a36Sopenharmony_ci		qpti->dev_param[tgt].synchronous_period = 0;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci	/* are we wide capable? */
99362306a36Sopenharmony_ci	if (sdev->wdtr)
99462306a36Sopenharmony_ci		qpti->dev_param[tgt].device_flags |= 0x20;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	param[0] = MBOX_SET_TARGET_PARAMS;
99762306a36Sopenharmony_ci	param[1] = (tgt << 8);
99862306a36Sopenharmony_ci	param[2] = (qpti->dev_param[tgt].device_flags << 8);
99962306a36Sopenharmony_ci	if (qpti->dev_param[tgt].device_flags & 0x10) {
100062306a36Sopenharmony_ci		param[3] = (qpti->dev_param[tgt].synchronous_offset << 8) |
100162306a36Sopenharmony_ci			qpti->dev_param[tgt].synchronous_period;
100262306a36Sopenharmony_ci	} else {
100362306a36Sopenharmony_ci		param[3] = 0;
100462306a36Sopenharmony_ci	}
100562306a36Sopenharmony_ci	qlogicpti_mbox_command(qpti, param, 0);
100662306a36Sopenharmony_ci	return 0;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_ci/*
101062306a36Sopenharmony_ci * The middle SCSI layer ensures that queuecommand never gets invoked
101162306a36Sopenharmony_ci * concurrently with itself or the interrupt handler (though the
101262306a36Sopenharmony_ci * interrupt handler may call this routine as part of
101362306a36Sopenharmony_ci * request-completion handling).
101462306a36Sopenharmony_ci *
101562306a36Sopenharmony_ci * "This code must fly." -davem
101662306a36Sopenharmony_ci */
101762306a36Sopenharmony_cistatic int qlogicpti_queuecommand_lck(struct scsi_cmnd *Cmnd)
101862306a36Sopenharmony_ci{
101962306a36Sopenharmony_ci	void (*done)(struct scsi_cmnd *) = scsi_done;
102062306a36Sopenharmony_ci	struct Scsi_Host *host = Cmnd->device->host;
102162306a36Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
102262306a36Sopenharmony_ci	struct Command_Entry *cmd;
102362306a36Sopenharmony_ci	u_int out_ptr;
102462306a36Sopenharmony_ci	int in_ptr;
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	in_ptr = qpti->req_in_ptr;
102762306a36Sopenharmony_ci	cmd = (struct Command_Entry *) &qpti->req_cpu[in_ptr];
102862306a36Sopenharmony_ci	out_ptr = sbus_readw(qpti->qregs + MBOX4);
102962306a36Sopenharmony_ci	in_ptr = NEXT_REQ_PTR(in_ptr);
103062306a36Sopenharmony_ci	if (in_ptr == out_ptr)
103162306a36Sopenharmony_ci		goto toss_command;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	if (qpti->send_marker) {
103462306a36Sopenharmony_ci		marker_frob(cmd);
103562306a36Sopenharmony_ci		qpti->send_marker = 0;
103662306a36Sopenharmony_ci		if (NEXT_REQ_PTR(in_ptr) == out_ptr) {
103762306a36Sopenharmony_ci			sbus_writew(in_ptr, qpti->qregs + MBOX4);
103862306a36Sopenharmony_ci			qpti->req_in_ptr = in_ptr;
103962306a36Sopenharmony_ci			goto toss_command;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci		cmd = (struct Command_Entry *) &qpti->req_cpu[in_ptr];
104262306a36Sopenharmony_ci		in_ptr = NEXT_REQ_PTR(in_ptr);
104362306a36Sopenharmony_ci	}
104462306a36Sopenharmony_ci	cmd_frob(cmd, Cmnd, qpti);
104562306a36Sopenharmony_ci	if ((in_ptr = load_cmd(Cmnd, cmd, qpti, in_ptr, out_ptr)) == -1)
104662306a36Sopenharmony_ci		goto toss_command;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	update_can_queue(host, in_ptr, out_ptr);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	return 0;
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_citoss_command:
105362306a36Sopenharmony_ci	printk(KERN_EMERG "qlogicpti%d: request queue overflow\n",
105462306a36Sopenharmony_ci	       qpti->qpti_id);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	/* Unfortunately, unless you use the new EH code, which
105762306a36Sopenharmony_ci	 * we don't, the midlayer will ignore the return value,
105862306a36Sopenharmony_ci	 * which is insane.  We pick up the pieces like this.
105962306a36Sopenharmony_ci	 */
106062306a36Sopenharmony_ci	Cmnd->result = DID_BUS_BUSY;
106162306a36Sopenharmony_ci	done(Cmnd);
106262306a36Sopenharmony_ci	return 1;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic DEF_SCSI_QCMD(qlogicpti_queuecommand)
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_cistatic int qlogicpti_return_status(struct Status_Entry *sts, int id)
106862306a36Sopenharmony_ci{
106962306a36Sopenharmony_ci	int host_status = DID_ERROR;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	switch (sts->completion_status) {
107262306a36Sopenharmony_ci	      case CS_COMPLETE:
107362306a36Sopenharmony_ci		host_status = DID_OK;
107462306a36Sopenharmony_ci		break;
107562306a36Sopenharmony_ci	      case CS_INCOMPLETE:
107662306a36Sopenharmony_ci		if (!(sts->state_flags & SF_GOT_BUS))
107762306a36Sopenharmony_ci			host_status = DID_NO_CONNECT;
107862306a36Sopenharmony_ci		else if (!(sts->state_flags & SF_GOT_TARGET))
107962306a36Sopenharmony_ci			host_status = DID_BAD_TARGET;
108062306a36Sopenharmony_ci		else if (!(sts->state_flags & SF_SENT_CDB))
108162306a36Sopenharmony_ci			host_status = DID_ERROR;
108262306a36Sopenharmony_ci		else if (!(sts->state_flags & SF_TRANSFERRED_DATA))
108362306a36Sopenharmony_ci			host_status = DID_ERROR;
108462306a36Sopenharmony_ci		else if (!(sts->state_flags & SF_GOT_STATUS))
108562306a36Sopenharmony_ci			host_status = DID_ERROR;
108662306a36Sopenharmony_ci		else if (!(sts->state_flags & SF_GOT_SENSE))
108762306a36Sopenharmony_ci			host_status = DID_ERROR;
108862306a36Sopenharmony_ci		break;
108962306a36Sopenharmony_ci	      case CS_DMA_ERROR:
109062306a36Sopenharmony_ci	      case CS_TRANSPORT_ERROR:
109162306a36Sopenharmony_ci		host_status = DID_ERROR;
109262306a36Sopenharmony_ci		break;
109362306a36Sopenharmony_ci	      case CS_RESET_OCCURRED:
109462306a36Sopenharmony_ci	      case CS_BUS_RESET:
109562306a36Sopenharmony_ci		host_status = DID_RESET;
109662306a36Sopenharmony_ci		break;
109762306a36Sopenharmony_ci	      case CS_ABORTED:
109862306a36Sopenharmony_ci		host_status = DID_ABORT;
109962306a36Sopenharmony_ci		break;
110062306a36Sopenharmony_ci	      case CS_TIMEOUT:
110162306a36Sopenharmony_ci		host_status = DID_TIME_OUT;
110262306a36Sopenharmony_ci		break;
110362306a36Sopenharmony_ci	      case CS_DATA_OVERRUN:
110462306a36Sopenharmony_ci	      case CS_COMMAND_OVERRUN:
110562306a36Sopenharmony_ci	      case CS_STATUS_OVERRUN:
110662306a36Sopenharmony_ci	      case CS_BAD_MESSAGE:
110762306a36Sopenharmony_ci	      case CS_NO_MESSAGE_OUT:
110862306a36Sopenharmony_ci	      case CS_EXT_ID_FAILED:
110962306a36Sopenharmony_ci	      case CS_IDE_MSG_FAILED:
111062306a36Sopenharmony_ci	      case CS_ABORT_MSG_FAILED:
111162306a36Sopenharmony_ci	      case CS_NOP_MSG_FAILED:
111262306a36Sopenharmony_ci	      case CS_PARITY_ERROR_MSG_FAILED:
111362306a36Sopenharmony_ci	      case CS_DEVICE_RESET_MSG_FAILED:
111462306a36Sopenharmony_ci	      case CS_ID_MSG_FAILED:
111562306a36Sopenharmony_ci	      case CS_UNEXP_BUS_FREE:
111662306a36Sopenharmony_ci		host_status = DID_ERROR;
111762306a36Sopenharmony_ci		break;
111862306a36Sopenharmony_ci	      case CS_DATA_UNDERRUN:
111962306a36Sopenharmony_ci		host_status = DID_OK;
112062306a36Sopenharmony_ci		break;
112162306a36Sopenharmony_ci	      default:
112262306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: unknown completion status 0x%04x\n",
112362306a36Sopenharmony_ci		       id, sts->completion_status);
112462306a36Sopenharmony_ci		host_status = DID_ERROR;
112562306a36Sopenharmony_ci		break;
112662306a36Sopenharmony_ci	}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	return (sts->scsi_status & STATUS_MASK) | (host_status << 16);
112962306a36Sopenharmony_ci}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_cistatic struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti)
113262306a36Sopenharmony_ci{
113362306a36Sopenharmony_ci	struct scsi_cmnd *Cmnd, *done_queue = NULL;
113462306a36Sopenharmony_ci	struct Status_Entry *sts;
113562306a36Sopenharmony_ci	u_int in_ptr, out_ptr;
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	if (!(sbus_readw(qpti->qregs + SBUS_STAT) & SBUS_STAT_RINT))
113862306a36Sopenharmony_ci		return NULL;
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	in_ptr = sbus_readw(qpti->qregs + MBOX5);
114162306a36Sopenharmony_ci	sbus_writew(HCCTRL_CRIRQ, qpti->qregs + HCCTRL);
114262306a36Sopenharmony_ci	if (sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK) {
114362306a36Sopenharmony_ci		switch (sbus_readw(qpti->qregs + MBOX0)) {
114462306a36Sopenharmony_ci		case ASYNC_SCSI_BUS_RESET:
114562306a36Sopenharmony_ci		case EXECUTION_TIMEOUT_RESET:
114662306a36Sopenharmony_ci			qpti->send_marker = 1;
114762306a36Sopenharmony_ci			break;
114862306a36Sopenharmony_ci		case INVALID_COMMAND:
114962306a36Sopenharmony_ci		case HOST_INTERFACE_ERROR:
115062306a36Sopenharmony_ci		case COMMAND_ERROR:
115162306a36Sopenharmony_ci		case COMMAND_PARAM_ERROR:
115262306a36Sopenharmony_ci			break;
115362306a36Sopenharmony_ci		};
115462306a36Sopenharmony_ci		sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE);
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	/* This looks like a network driver! */
115862306a36Sopenharmony_ci	out_ptr = qpti->res_out_ptr;
115962306a36Sopenharmony_ci	while (out_ptr != in_ptr) {
116062306a36Sopenharmony_ci		u_int cmd_slot;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		sts = (struct Status_Entry *) &qpti->res_cpu[out_ptr];
116362306a36Sopenharmony_ci		out_ptr = NEXT_RES_PTR(out_ptr);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci		/* We store an index in the handle, not the pointer in
116662306a36Sopenharmony_ci		 * some form.  This avoids problems due to the fact
116762306a36Sopenharmony_ci		 * that the handle provided is only 32-bits. -DaveM
116862306a36Sopenharmony_ci		 */
116962306a36Sopenharmony_ci		cmd_slot = sts->handle;
117062306a36Sopenharmony_ci		Cmnd = qpti->cmd_slots[cmd_slot];
117162306a36Sopenharmony_ci		qpti->cmd_slots[cmd_slot] = NULL;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		if (sts->completion_status == CS_RESET_OCCURRED ||
117462306a36Sopenharmony_ci		    sts->completion_status == CS_ABORTED ||
117562306a36Sopenharmony_ci		    (sts->status_flags & STF_BUS_RESET))
117662306a36Sopenharmony_ci			qpti->send_marker = 1;
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci		if (sts->state_flags & SF_GOT_SENSE)
117962306a36Sopenharmony_ci			memcpy(Cmnd->sense_buffer, sts->req_sense_data,
118062306a36Sopenharmony_ci			       SCSI_SENSE_BUFFERSIZE);
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		if (sts->hdr.entry_type == ENTRY_STATUS)
118362306a36Sopenharmony_ci			Cmnd->result =
118462306a36Sopenharmony_ci			    qlogicpti_return_status(sts, qpti->qpti_id);
118562306a36Sopenharmony_ci		else
118662306a36Sopenharmony_ci			Cmnd->result = DID_ERROR << 16;
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		if (scsi_bufflen(Cmnd))
118962306a36Sopenharmony_ci			dma_unmap_sg(&qpti->op->dev,
119062306a36Sopenharmony_ci				     scsi_sglist(Cmnd), scsi_sg_count(Cmnd),
119162306a36Sopenharmony_ci				     Cmnd->sc_data_direction);
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci		qpti->cmd_count[Cmnd->device->id]--;
119462306a36Sopenharmony_ci		sbus_writew(out_ptr, qpti->qregs + MBOX5);
119562306a36Sopenharmony_ci		Cmnd->host_scribble = (unsigned char *) done_queue;
119662306a36Sopenharmony_ci		done_queue = Cmnd;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci	qpti->res_out_ptr = out_ptr;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	return done_queue;
120162306a36Sopenharmony_ci}
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_cistatic irqreturn_t qpti_intr(int irq, void *dev_id)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	struct qlogicpti *qpti = dev_id;
120662306a36Sopenharmony_ci	unsigned long flags;
120762306a36Sopenharmony_ci	struct scsi_cmnd *dq;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	spin_lock_irqsave(qpti->qhost->host_lock, flags);
121062306a36Sopenharmony_ci	dq = qlogicpti_intr_handler(qpti);
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	if (dq != NULL) {
121362306a36Sopenharmony_ci		do {
121462306a36Sopenharmony_ci			struct scsi_cmnd *next;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci			next = (struct scsi_cmnd *) dq->host_scribble;
121762306a36Sopenharmony_ci			scsi_done(dq);
121862306a36Sopenharmony_ci			dq = next;
121962306a36Sopenharmony_ci		} while (dq != NULL);
122062306a36Sopenharmony_ci	}
122162306a36Sopenharmony_ci	spin_unlock_irqrestore(qpti->qhost->host_lock, flags);
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	return IRQ_HANDLED;
122462306a36Sopenharmony_ci}
122562306a36Sopenharmony_ci
122662306a36Sopenharmony_cistatic int qlogicpti_abort(struct scsi_cmnd *Cmnd)
122762306a36Sopenharmony_ci{
122862306a36Sopenharmony_ci	u_short param[6];
122962306a36Sopenharmony_ci	struct Scsi_Host *host = Cmnd->device->host;
123062306a36Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
123162306a36Sopenharmony_ci	int return_status = SUCCESS;
123262306a36Sopenharmony_ci	u32 cmd_cookie;
123362306a36Sopenharmony_ci	int i;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	printk(KERN_WARNING "qlogicpti%d: Aborting cmd for tgt[%d] lun[%d]\n",
123662306a36Sopenharmony_ci	       qpti->qpti_id, (int)Cmnd->device->id, (int)Cmnd->device->lun);
123762306a36Sopenharmony_ci
123862306a36Sopenharmony_ci	qlogicpti_disable_irqs(qpti);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci	/* Find the 32-bit cookie we gave to the firmware for
124162306a36Sopenharmony_ci	 * this command.
124262306a36Sopenharmony_ci	 */
124362306a36Sopenharmony_ci	for (i = 0; i < QLOGICPTI_REQ_QUEUE_LEN + 1; i++)
124462306a36Sopenharmony_ci		if (qpti->cmd_slots[i] == Cmnd)
124562306a36Sopenharmony_ci			break;
124662306a36Sopenharmony_ci	cmd_cookie = i;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	param[0] = MBOX_ABORT;
124962306a36Sopenharmony_ci	param[1] = (((u_short) Cmnd->device->id) << 8) | Cmnd->device->lun;
125062306a36Sopenharmony_ci	param[2] = cmd_cookie >> 16;
125162306a36Sopenharmony_ci	param[3] = cmd_cookie & 0xffff;
125262306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 0) ||
125362306a36Sopenharmony_ci	    (param[0] != MBOX_COMMAND_COMPLETE)) {
125462306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicpti%d: scsi abort failure: %x\n",
125562306a36Sopenharmony_ci		       qpti->qpti_id, param[0]);
125662306a36Sopenharmony_ci		return_status = FAILED;
125762306a36Sopenharmony_ci	}
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	return return_status;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic int qlogicpti_reset(struct scsi_cmnd *Cmnd)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	u_short param[6];
126762306a36Sopenharmony_ci	struct Scsi_Host *host = Cmnd->device->host;
126862306a36Sopenharmony_ci	struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata;
126962306a36Sopenharmony_ci	int return_status = SUCCESS;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	printk(KERN_WARNING "qlogicpti%d: Resetting SCSI bus!\n",
127262306a36Sopenharmony_ci	       qpti->qpti_id);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	qlogicpti_disable_irqs(qpti);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	param[0] = MBOX_BUS_RESET;
127762306a36Sopenharmony_ci	param[1] = qpti->host_param.bus_reset_delay;
127862306a36Sopenharmony_ci	if (qlogicpti_mbox_command(qpti, param, 0) ||
127962306a36Sopenharmony_ci	   (param[0] != MBOX_COMMAND_COMPLETE)) {
128062306a36Sopenharmony_ci		printk(KERN_EMERG "qlogicisp%d: scsi bus reset failure: %x\n",
128162306a36Sopenharmony_ci		       qpti->qpti_id, param[0]);
128262306a36Sopenharmony_ci		return_status = FAILED;
128362306a36Sopenharmony_ci	}
128462306a36Sopenharmony_ci
128562306a36Sopenharmony_ci	qlogicpti_enable_irqs(qpti);
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	return return_status;
128862306a36Sopenharmony_ci}
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic const struct scsi_host_template qpti_template = {
129162306a36Sopenharmony_ci	.module			= THIS_MODULE,
129262306a36Sopenharmony_ci	.name			= "qlogicpti",
129362306a36Sopenharmony_ci	.info			= qlogicpti_info,
129462306a36Sopenharmony_ci	.queuecommand		= qlogicpti_queuecommand,
129562306a36Sopenharmony_ci	.slave_configure	= qlogicpti_slave_configure,
129662306a36Sopenharmony_ci	.eh_abort_handler	= qlogicpti_abort,
129762306a36Sopenharmony_ci	.eh_host_reset_handler	= qlogicpti_reset,
129862306a36Sopenharmony_ci	.can_queue		= QLOGICPTI_REQ_QUEUE_LEN,
129962306a36Sopenharmony_ci	.this_id		= 7,
130062306a36Sopenharmony_ci	.sg_tablesize		= QLOGICPTI_MAX_SG(QLOGICPTI_REQ_QUEUE_LEN),
130162306a36Sopenharmony_ci};
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_cistatic const struct of_device_id qpti_match[];
130462306a36Sopenharmony_cistatic int qpti_sbus_probe(struct platform_device *op)
130562306a36Sopenharmony_ci{
130662306a36Sopenharmony_ci	struct device_node *dp = op->dev.of_node;
130762306a36Sopenharmony_ci	struct Scsi_Host *host;
130862306a36Sopenharmony_ci	struct qlogicpti *qpti;
130962306a36Sopenharmony_ci	static int nqptis;
131062306a36Sopenharmony_ci	const char *fcode;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	/* Sometimes Antares cards come up not completely
131362306a36Sopenharmony_ci	 * setup, and we get a report of a zero IRQ.
131462306a36Sopenharmony_ci	 */
131562306a36Sopenharmony_ci	if (op->archdata.irqs[0] == 0)
131662306a36Sopenharmony_ci		return -ENODEV;
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	host = scsi_host_alloc(&qpti_template, sizeof(struct qlogicpti));
131962306a36Sopenharmony_ci	if (!host)
132062306a36Sopenharmony_ci		return -ENOMEM;
132162306a36Sopenharmony_ci
132262306a36Sopenharmony_ci	qpti = shost_priv(host);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	host->max_id = MAX_TARGETS;
132562306a36Sopenharmony_ci	qpti->qhost = host;
132662306a36Sopenharmony_ci	qpti->op = op;
132762306a36Sopenharmony_ci	qpti->qpti_id = nqptis;
132862306a36Sopenharmony_ci	qpti->is_pti = !of_node_name_eq(op->dev.of_node, "QLGC,isp");
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	if (qpti_map_regs(qpti) < 0)
133162306a36Sopenharmony_ci		goto fail_unlink;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (qpti_register_irq(qpti) < 0)
133462306a36Sopenharmony_ci		goto fail_unmap_regs;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	qpti_get_scsi_id(qpti);
133762306a36Sopenharmony_ci	qpti_get_bursts(qpti);
133862306a36Sopenharmony_ci	qpti_get_clock(qpti);
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	/* Clear out scsi_cmnd array. */
134162306a36Sopenharmony_ci	memset(qpti->cmd_slots, 0, sizeof(qpti->cmd_slots));
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (qpti_map_queues(qpti) < 0)
134462306a36Sopenharmony_ci		goto fail_free_irq;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	/* Load the firmware. */
134762306a36Sopenharmony_ci	if (qlogicpti_load_firmware(qpti))
134862306a36Sopenharmony_ci		goto fail_unmap_queues;
134962306a36Sopenharmony_ci	if (qpti->is_pti) {
135062306a36Sopenharmony_ci		/* Check the PTI status reg. */
135162306a36Sopenharmony_ci		if (qlogicpti_verify_tmon(qpti))
135262306a36Sopenharmony_ci			goto fail_unmap_queues;
135362306a36Sopenharmony_ci	}
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/* Reset the ISP and init res/req queues. */
135662306a36Sopenharmony_ci	if (qlogicpti_reset_hardware(host))
135762306a36Sopenharmony_ci		goto fail_unmap_queues;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	printk("(Firmware v%d.%d.%d)", qpti->fware_majrev,
136062306a36Sopenharmony_ci	       qpti->fware_minrev, qpti->fware_micrev);
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	fcode = of_get_property(dp, "isp-fcode", NULL);
136362306a36Sopenharmony_ci	if (fcode && fcode[0])
136462306a36Sopenharmony_ci		printk("(FCode %s)", fcode);
136562306a36Sopenharmony_ci	qpti->differential = of_property_read_bool(dp, "differential");
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	printk("\nqlogicpti%d: [%s Wide, using %s interface]\n",
136862306a36Sopenharmony_ci		qpti->qpti_id,
136962306a36Sopenharmony_ci		(qpti->ultra ? "Ultra" : "Fast"),
137062306a36Sopenharmony_ci		(qpti->differential ? "differential" : "single ended"));
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	if (scsi_add_host(host, &op->dev)) {
137362306a36Sopenharmony_ci		printk("qlogicpti%d: Failed scsi_add_host\n", qpti->qpti_id);
137462306a36Sopenharmony_ci		goto fail_unmap_queues;
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	dev_set_drvdata(&op->dev, qpti);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci	qpti_chain_add(qpti);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	scsi_scan_host(host);
138262306a36Sopenharmony_ci	nqptis++;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	return 0;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cifail_unmap_queues:
138762306a36Sopenharmony_ci#define QSIZE(entries)	(((entries) + 1) * QUEUE_ENTRY_LEN)
138862306a36Sopenharmony_ci	dma_free_coherent(&op->dev,
138962306a36Sopenharmony_ci			  QSIZE(RES_QUEUE_LEN),
139062306a36Sopenharmony_ci			  qpti->res_cpu, qpti->res_dvma);
139162306a36Sopenharmony_ci	dma_free_coherent(&op->dev,
139262306a36Sopenharmony_ci			  QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
139362306a36Sopenharmony_ci			  qpti->req_cpu, qpti->req_dvma);
139462306a36Sopenharmony_ci#undef QSIZE
139562306a36Sopenharmony_ci
139662306a36Sopenharmony_cifail_free_irq:
139762306a36Sopenharmony_ci	free_irq(qpti->irq, qpti);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_cifail_unmap_regs:
140062306a36Sopenharmony_ci	of_iounmap(&op->resource[0], qpti->qregs,
140162306a36Sopenharmony_ci		   resource_size(&op->resource[0]));
140262306a36Sopenharmony_ci	if (qpti->is_pti)
140362306a36Sopenharmony_ci		of_iounmap(&op->resource[0], qpti->sreg,
140462306a36Sopenharmony_ci			   sizeof(unsigned char));
140562306a36Sopenharmony_ci
140662306a36Sopenharmony_cifail_unlink:
140762306a36Sopenharmony_ci	scsi_host_put(host);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	return -ENODEV;
141062306a36Sopenharmony_ci}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_cistatic int qpti_sbus_remove(struct platform_device *op)
141362306a36Sopenharmony_ci{
141462306a36Sopenharmony_ci	struct qlogicpti *qpti = dev_get_drvdata(&op->dev);
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	qpti_chain_del(qpti);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	scsi_remove_host(qpti->qhost);
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci	/* Shut up the card. */
142162306a36Sopenharmony_ci	sbus_writew(0, qpti->qregs + SBUS_CTRL);
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	/* Free IRQ handler and unmap Qlogic,ISP and PTI status regs. */
142462306a36Sopenharmony_ci	free_irq(qpti->irq, qpti);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci#define QSIZE(entries)	(((entries) + 1) * QUEUE_ENTRY_LEN)
142762306a36Sopenharmony_ci	dma_free_coherent(&op->dev,
142862306a36Sopenharmony_ci			  QSIZE(RES_QUEUE_LEN),
142962306a36Sopenharmony_ci			  qpti->res_cpu, qpti->res_dvma);
143062306a36Sopenharmony_ci	dma_free_coherent(&op->dev,
143162306a36Sopenharmony_ci			  QSIZE(QLOGICPTI_REQ_QUEUE_LEN),
143262306a36Sopenharmony_ci			  qpti->req_cpu, qpti->req_dvma);
143362306a36Sopenharmony_ci#undef QSIZE
143462306a36Sopenharmony_ci
143562306a36Sopenharmony_ci	of_iounmap(&op->resource[0], qpti->qregs,
143662306a36Sopenharmony_ci		   resource_size(&op->resource[0]));
143762306a36Sopenharmony_ci	if (qpti->is_pti)
143862306a36Sopenharmony_ci		of_iounmap(&op->resource[0], qpti->sreg, sizeof(unsigned char));
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	scsi_host_put(qpti->qhost);
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_ci	return 0;
144362306a36Sopenharmony_ci}
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_cistatic const struct of_device_id qpti_match[] = {
144662306a36Sopenharmony_ci	{
144762306a36Sopenharmony_ci		.name = "ptisp",
144862306a36Sopenharmony_ci	},
144962306a36Sopenharmony_ci	{
145062306a36Sopenharmony_ci		.name = "PTI,ptisp",
145162306a36Sopenharmony_ci	},
145262306a36Sopenharmony_ci	{
145362306a36Sopenharmony_ci		.name = "QLGC,isp",
145462306a36Sopenharmony_ci	},
145562306a36Sopenharmony_ci	{
145662306a36Sopenharmony_ci		.name = "SUNW,isp",
145762306a36Sopenharmony_ci	},
145862306a36Sopenharmony_ci	{},
145962306a36Sopenharmony_ci};
146062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, qpti_match);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_cistatic struct platform_driver qpti_sbus_driver = {
146362306a36Sopenharmony_ci	.driver = {
146462306a36Sopenharmony_ci		.name = "qpti",
146562306a36Sopenharmony_ci		.of_match_table = qpti_match,
146662306a36Sopenharmony_ci	},
146762306a36Sopenharmony_ci	.probe		= qpti_sbus_probe,
146862306a36Sopenharmony_ci	.remove		= qpti_sbus_remove,
146962306a36Sopenharmony_ci};
147062306a36Sopenharmony_cimodule_platform_driver(qpti_sbus_driver);
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ciMODULE_DESCRIPTION("QlogicISP SBUS driver");
147362306a36Sopenharmony_ciMODULE_AUTHOR("David S. Miller (davem@davemloft.net)");
147462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
147562306a36Sopenharmony_ciMODULE_VERSION("2.1");
147662306a36Sopenharmony_ciMODULE_FIRMWARE("qlogic/isp1000.bin");
1477