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