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