📄 qlogicpti.c
字号:
/* qlogicpti.c: Performance Technologies QlogicISP sbus card driver. * * Copyright (C) 1996 David S. Miller (davem@caipfs.rutgers.edu) * * A lot of this driver was directly stolen from Erik H. Moe's PCI * Qlogic ISP driver. Mucho kudos to him for this code. * * An even bigger kudos to John Grana at Performance Technologies * for providing me with the hardware to write this driver, you rule * John you really do. * * May, 2, 1997: Added support for QLGC,isp --jj */#include <linux/kernel.h>#include <linux/delay.h>#include <linux/types.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/blk.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/init.h>#include <linux/spinlock.h>#include <asm/byteorder.h>#include "scsi.h"#include "hosts.h"#include "qlogicpti.h"#include <asm/sbus.h>#include <asm/dma.h>#include <asm/system.h>#include <asm/ptrace.h>#include <asm/pgtable.h>#include <asm/oplib.h>#include <asm/vaddrs.h>#include <asm/io.h>#include <asm/irq.h>#include <linux/module.h>#define MAX_TARGETS 16#define MAX_LUNS 8 /* 32 for 1.31 F/W */#define DEFAULT_LOOP_COUNT 10000#include "qlogicpti_asm.c"static struct qlogicpti *qptichain = NULL;static spinlock_t qptichain_lock = SPIN_LOCK_UNLOCKED;static int qptis_running = 0;#define PACKB(a, b) (((a)<<4)|(b))const u_char mbox_param[] = { PACKB(1, 1), /* MBOX_NO_OP */ PACKB(5, 5), /* MBOX_LOAD_RAM */ PACKB(2, 0), /* MBOX_EXEC_FIRMWARE */ PACKB(5, 5), /* MBOX_DUMP_RAM */ PACKB(3, 3), /* MBOX_WRITE_RAM_WORD */ PACKB(2, 3), /* MBOX_READ_RAM_WORD */ PACKB(6, 6), /* MBOX_MAILBOX_REG_TEST */ PACKB(2, 3), /* MBOX_VERIFY_CHECKSUM */ PACKB(1, 3), /* MBOX_ABOUT_FIRMWARE */ PACKB(0, 0), /* 0x0009 */ PACKB(0, 0), /* 0x000a */ PACKB(0, 0), /* 0x000b */ PACKB(0, 0), /* 0x000c */ PACKB(0, 0), /* 0x000d */ PACKB(1, 2), /* MBOX_CHECK_FIRMWARE */ PACKB(0, 0), /* 0x000f */ PACKB(5, 5), /* MBOX_INIT_REQ_QUEUE */ PACKB(6, 6), /* MBOX_INIT_RES_QUEUE */ PACKB(4, 4), /* MBOX_EXECUTE_IOCB */ PACKB(2, 2), /* MBOX_WAKE_UP */ PACKB(1, 6), /* MBOX_STOP_FIRMWARE */ PACKB(4, 4), /* MBOX_ABORT */ PACKB(2, 2), /* MBOX_ABORT_DEVICE */ PACKB(3, 3), /* MBOX_ABORT_TARGET */ PACKB(2, 2), /* MBOX_BUS_RESET */ PACKB(2, 3), /* MBOX_STOP_QUEUE */ PACKB(2, 3), /* MBOX_START_QUEUE */ PACKB(2, 3), /* MBOX_SINGLE_STEP_QUEUE */ PACKB(2, 3), /* MBOX_ABORT_QUEUE */ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_STATUS */ PACKB(0, 0), /* 0x001e */ PACKB(1, 3), /* MBOX_GET_FIRMWARE_STATUS */ PACKB(1, 2), /* MBOX_GET_INIT_SCSI_ID */ PACKB(1, 2), /* MBOX_GET_SELECT_TIMEOUT */ PACKB(1, 3), /* MBOX_GET_RETRY_COUNT */ PACKB(1, 2), /* MBOX_GET_TAG_AGE_LIMIT */ PACKB(1, 2), /* MBOX_GET_CLOCK_RATE */ PACKB(1, 2), /* MBOX_GET_ACT_NEG_STATE */ PACKB(1, 2), /* MBOX_GET_ASYNC_DATA_SETUP_TIME */ PACKB(1, 3), /* MBOX_GET_SBUS_PARAMS */ PACKB(2, 4), /* MBOX_GET_TARGET_PARAMS */ PACKB(2, 4), /* MBOX_GET_DEV_QUEUE_PARAMS */ PACKB(0, 0), /* 0x002a */ PACKB(0, 0), /* 0x002b */ PACKB(0, 0), /* 0x002c */ PACKB(0, 0), /* 0x002d */ PACKB(0, 0), /* 0x002e */ PACKB(0, 0), /* 0x002f */ PACKB(2, 2), /* MBOX_SET_INIT_SCSI_ID */ PACKB(2, 2), /* MBOX_SET_SELECT_TIMEOUT */ PACKB(3, 3), /* MBOX_SET_RETRY_COUNT */ PACKB(2, 2), /* MBOX_SET_TAG_AGE_LIMIT */ PACKB(2, 2), /* MBOX_SET_CLOCK_RATE */ PACKB(2, 2), /* MBOX_SET_ACTIVE_NEG_STATE */ PACKB(2, 2), /* MBOX_SET_ASYNC_DATA_SETUP_TIME */ PACKB(3, 3), /* MBOX_SET_SBUS_CONTROL_PARAMS */ PACKB(4, 4), /* MBOX_SET_TARGET_PARAMS */ PACKB(4, 4), /* MBOX_SET_DEV_QUEUE_PARAMS */ PACKB(0, 0), /* 0x003a */ PACKB(0, 0), /* 0x003b */ PACKB(0, 0), /* 0x003c */ PACKB(0, 0), /* 0x003d */ PACKB(0, 0), /* 0x003e */ PACKB(0, 0), /* 0x003f */ PACKB(0, 0), /* 0x0040 */ PACKB(0, 0), /* 0x0041 */ PACKB(0, 0) /* 0x0042 */};#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short))/* queue length's _must_ be power of two: */#define QUEUE_DEPTH(in, out, ql) ((in - out) & (ql))#define REQ_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, \ QLOGICPTI_REQ_QUEUE_LEN)#define RES_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, RES_QUEUE_LEN)static inline void qlogicpti_enable_irqs(struct qlogicpti *qpti){ sbus_writew(SBUS_CTRL_ERIRQ | SBUS_CTRL_GENAB, qpti->qregs + SBUS_CTRL);}static inline void qlogicpti_disable_irqs(struct qlogicpti *qpti){ sbus_writew(0, qpti->qregs + SBUS_CTRL);}static inline void set_sbus_cfg1(struct qlogicpti *qpti){ u16 val; u8 bursts = qpti->bursts;#if 0 /* It appears that at least PTI cards do not support * 64-byte bursts and that setting the B64 bit actually * is a nop and the chip ends up using the smallest burst * size. -DaveM */ if (sbus_can_burst64(qpti->sdev) && (bursts & DMA_BURST64)) { val = (SBUS_CFG1_BENAB | SBUS_CFG1_B64); } else#endif if (bursts & DMA_BURST32) { val = (SBUS_CFG1_BENAB | SBUS_CFG1_B32); } else if (bursts & DMA_BURST16) { val = (SBUS_CFG1_BENAB | SBUS_CFG1_B16); } else if (bursts & DMA_BURST8) { val = (SBUS_CFG1_BENAB | SBUS_CFG1_B8); } else { val = 0; /* No sbus bursts for you... */ } sbus_writew(val, qpti->qregs + SBUS_CFG1);}static int qlogicpti_mbox_command(struct qlogicpti *qpti, u_short param[], int force){ int loop_count; u16 tmp; if (mbox_param[param[0]] == 0) return 1; /* Set SBUS semaphore. */ tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE); tmp |= SBUS_SEMAPHORE_LCK; sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE); /* Wait for host IRQ bit to clear. */ loop_count = DEFAULT_LOOP_COUNT; while (--loop_count && (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_HIRQ)) barrier(); if (!loop_count) printk(KERN_EMERG "qlogicpti: mbox_command loop timeout #1\n"); /* Write mailbox command registers. */ switch (mbox_param[param[0]] >> 4) { case 6: sbus_writew(param[5], qpti->qregs + MBOX5); case 5: sbus_writew(param[4], qpti->qregs + MBOX4); case 4: sbus_writew(param[3], qpti->qregs + MBOX3); case 3: sbus_writew(param[2], qpti->qregs + MBOX2); case 2: sbus_writew(param[1], qpti->qregs + MBOX1); case 1: sbus_writew(param[0], qpti->qregs + MBOX0); } /* Clear RISC interrupt. */ tmp = sbus_readw(qpti->qregs + HCCTRL); tmp |= HCCTRL_CRIRQ; sbus_writew(tmp, qpti->qregs + HCCTRL); /* Clear SBUS semaphore. */ sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE); /* Set HOST interrupt. */ tmp = sbus_readw(qpti->qregs + HCCTRL); tmp |= HCCTRL_SHIRQ; sbus_writew(tmp, qpti->qregs + HCCTRL); /* Wait for HOST interrupt clears. */ loop_count = DEFAULT_LOOP_COUNT; while (--loop_count && (sbus_readw(qpti->qregs + HCCTRL) & HCCTRL_CRIRQ)) udelay(20); if (!loop_count) printk(KERN_EMERG "qlogicpti: mbox_command[%04x] loop timeout #2\n", param[0]); /* Wait for SBUS semaphore to get set. */ loop_count = DEFAULT_LOOP_COUNT; while (--loop_count && !(sbus_readw(qpti->qregs + SBUS_SEMAPHORE) & SBUS_SEMAPHORE_LCK)) { udelay(20); /* Workaround for some buggy chips. */ if (sbus_readw(qpti->qregs + MBOX0) & 0x4000) break; } if (!loop_count) printk(KERN_EMERG "qlogicpti: mbox_command[%04x] loop timeout #3\n", param[0]); /* Wait for MBOX busy condition to go away. */ loop_count = DEFAULT_LOOP_COUNT; while (--loop_count && (sbus_readw(qpti->qregs + MBOX0) == 0x04)) udelay(20); if (!loop_count) printk(KERN_EMERG "qlogicpti: mbox_command[%04x] loop timeout #4\n", param[0]); /* Read back output parameters. */ switch (mbox_param[param[0]] & 0xf) { case 6: param[5] = sbus_readw(qpti->qregs + MBOX5); case 5: param[4] = sbus_readw(qpti->qregs + MBOX4); case 4: param[3] = sbus_readw(qpti->qregs + MBOX3); case 3: param[2] = sbus_readw(qpti->qregs + MBOX2); case 2: param[1] = sbus_readw(qpti->qregs + MBOX1); case 1: param[0] = sbus_readw(qpti->qregs + MBOX0); } /* Clear RISC interrupt. */ tmp = sbus_readw(qpti->qregs + HCCTRL); tmp |= HCCTRL_CRIRQ; sbus_writew(tmp, qpti->qregs + HCCTRL); /* Release SBUS semaphore. */ tmp = sbus_readw(qpti->qregs + SBUS_SEMAPHORE); tmp &= ~(SBUS_SEMAPHORE_LCK); sbus_writew(tmp, qpti->qregs + SBUS_SEMAPHORE); /* We're done. */ return 0;}static inline void qlogicpti_set_hostdev_defaults(struct qlogicpti *qpti){ int i; qpti->host_param.initiator_scsi_id = qpti->scsi_id; qpti->host_param.bus_reset_delay = 3; qpti->host_param.retry_count = 0; qpti->host_param.retry_delay = 5; qpti->host_param.async_data_setup_time = 3; qpti->host_param.req_ack_active_negation = 1; qpti->host_param.data_line_active_negation = 1; qpti->host_param.data_dma_burst_enable = 1; qpti->host_param.command_dma_burst_enable = 1; qpti->host_param.tag_aging = 8; qpti->host_param.selection_timeout = 250; qpti->host_param.max_queue_depth = 256; for(i = 0; i < MAX_TARGETS; i++) { /* * disconnect, parity, arq, reneg on reset, and, oddly enough * tags...the midlayer's notion of tagged support has to match * our device settings, and since we base whether we enable a * tag on a per-cmnd basis upon what the midlayer sez, we * actually enable the capability here. */ qpti->dev_param[i].device_flags = 0xcd; qpti->dev_param[i].execution_throttle = 16; if (qpti->ultra) { qpti->dev_param[i].synchronous_period = 12; qpti->dev_param[i].synchronous_offset = 8; } else { qpti->dev_param[i].synchronous_period = 25; qpti->dev_param[i].synchronous_offset = 12; } qpti->dev_param[i].device_enable = 1; } /* this is very important to set! */ qpti->sbits = 1 << qpti->scsi_id;}static int qlogicpti_reset_hardware(struct Scsi_Host *host){ struct qlogicpti *qpti = (struct qlogicpti *) host->hostdata; u_short param[6]; unsigned short risc_code_addr; int loop_count, i; unsigned long flags; risc_code_addr = 0x1000; /* all load addresses are at 0x1000 */ save_flags(flags); cli(); sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL); /* Only reset the scsi bus if it is not free. */ if (sbus_readw(qpti->qregs + CPU_PCTRL) & CPU_PCTRL_BSY) { sbus_writew(CPU_ORIDE_RMOD, qpti->qregs + CPU_ORIDE); sbus_writew(CPU_CMD_BRESET, qpti->qregs + CPU_CMD); udelay(400); } sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL); sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL); sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL); loop_count = DEFAULT_LOOP_COUNT; while (--loop_count && ((sbus_readw(qpti->qregs + MBOX0) & 0xff) == 0x04)) udelay(20); if (!loop_count) printk(KERN_EMERG "qlogicpti: reset_hardware loop timeout\n"); sbus_writew(HCCTRL_PAUSE, qpti->qregs + HCCTRL); set_sbus_cfg1(qpti); qlogicpti_enable_irqs(qpti); if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) { qpti->ultra = 1; sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA), qpti->qregs + RISC_MTREG); } else { qpti->ultra = 0; sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT), qpti->qregs + RISC_MTREG); } /* reset adapter and per-device default values. */ /* do it after finding out whether we're ultra mode capable */ qlogicpti_set_hostdev_defaults(qpti); /* Release the RISC processor. */ sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL); /* Get RISC to start executing the firmware code. */ param[0] = MBOX_EXEC_FIRMWARE; param[1] = risc_code_addr; if (qlogicpti_mbox_command(qpti, param, 1)) { printk(KERN_EMERG "qlogicpti%d: Cannot execute ISP firmware.\n", qpti->qpti_id); restore_flags(flags); return 1; } /* Set initiator scsi ID. */ param[0] = MBOX_SET_INIT_SCSI_ID; param[1] = qpti->host_param.initiator_scsi_id; if (qlogicpti_mbox_command(qpti, param, 1) || (param[0] != MBOX_COMMAND_COMPLETE)) { printk(KERN_EMERG "qlogicpti%d: Cannot set initiator SCSI ID.\n", qpti->qpti_id); restore_flags(flags); return 1; } /* Initialize state of the queues, both hw and sw. */ qpti->req_in_ptr = qpti->res_out_ptr = 0; param[0] = MBOX_INIT_RES_QUEUE; param[1] = RES_QUEUE_LEN + 1; param[2] = (u_short) (qpti->res_dvma >> 16); param[3] = (u_short) (qpti->res_dvma & 0xffff); param[4] = param[5] = 0; if (qlogicpti_mbox_command(qpti, param, 1)) { printk(KERN_EMERG "qlogicpti%d: Cannot init response queue.\n", qpti->qpti_id); restore_flags(flags); return 1; } param[0] = MBOX_INIT_REQ_QUEUE; param[1] = QLOGICPTI_REQ_QUEUE_LEN + 1; param[2] = (u_short) (qpti->req_dvma >> 16); param[3] = (u_short) (qpti->req_dvma & 0xffff); param[4] = param[5] = 0; if (qlogicpti_mbox_command(qpti, param, 1)) { printk(KERN_EMERG "qlogicpti%d: Cannot init request queue.\n", qpti->qpti_id); restore_flags(flags); return 1; } param[0] = MBOX_SET_RETRY_COUNT; param[1] = qpti->host_param.retry_count; param[2] = qpti->host_param.retry_delay; qlogicpti_mbox_command(qpti, param, 0); param[0] = MBOX_SET_TAG_AGE_LIMIT; param[1] = qpti->host_param.tag_aging; qlogicpti_mbox_command(qpti, param, 0); for (i = 0; i < MAX_TARGETS; i++) { param[0] = MBOX_GET_DEV_QUEUE_PARAMS; param[1] = (i << 8); qlogicpti_mbox_command(qpti, param, 0); } param[0] = MBOX_GET_FIRMWARE_STATUS; qlogicpti_mbox_command(qpti, param, 0); param[0] = MBOX_SET_SELECT_TIMEOUT; param[1] = qpti->host_param.selection_timeout; qlogicpti_mbox_command(qpti, param, 0); for (i = 0; i < MAX_TARGETS; i++) { param[0] = MBOX_SET_TARGET_PARAMS; param[1] = (i << 8); param[2] = (qpti->dev_param[i].device_flags << 8); /* * Since we're now loading 1.31 f/w, force narrow/async. */ param[2] |= 0xc0; param[3] = 0; /* no offset, we do not have sync mode yet */ qlogicpti_mbox_command(qpti, param, 0); } /* * Always (sigh) do an initial bus reset (kicks f/w). */ param[0] = MBOX_BUS_RESET; param[1] = qpti->host_param.bus_reset_delay; qlogicpti_mbox_command(qpti, param, 0); qpti->send_marker = 1; restore_flags(flags); return 0;}#define PTI_RESET_LIMIT 400static int __init qlogicpti_load_firmware(struct qlogicpti *qpti){ unsigned short csum = 0; unsigned short param[6]; unsigned short *risc_code, risc_code_addr, risc_code_length; unsigned long flags; int i, timeout; risc_code = &sbus_risc_code01[0]; risc_code_addr = 0x1000; /* all f/w modules load at 0x1000 */ risc_code_length = sbus_risc_code_length01; save_flags(flags); cli(); /* Verify the checksum twice, one before loading it, and once * afterwards via the mailbox commands. */ for (i = 0; i < risc_code_length; i++) csum += risc_code[i]; if (csum) { restore_flags(flags); printk(KERN_EMERG "qlogicpti%d: Aieee, firmware checksum failed!", qpti->qpti_id); return 1; } sbus_writew(SBUS_CTRL_RESET, qpti->qregs + SBUS_CTRL); sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + CMD_DMA_CTRL); sbus_writew((DMA_CTRL_CCLEAR | DMA_CTRL_CIRQ), qpti->qregs + DATA_DMA_CTRL); timeout = PTI_RESET_LIMIT; while (--timeout && (sbus_readw(qpti->qregs + SBUS_CTRL) & SBUS_CTRL_RESET)) udelay(20); if (!timeout) { restore_flags(flags); printk(KERN_EMERG "qlogicpti%d: Cannot reset the ISP.", qpti->qpti_id); return 1; } sbus_writew(HCCTRL_RESET, qpti->qregs + HCCTRL); mdelay(1); sbus_writew((SBUS_CTRL_GENAB | SBUS_CTRL_ERIRQ), qpti->qregs + SBUS_CTRL); set_sbus_cfg1(qpti); sbus_writew(0, qpti->qregs + SBUS_SEMAPHORE); if (sbus_readw(qpti->qregs + RISC_PSR) & RISC_PSR_ULTRA) { qpti->ultra = 1; sbus_writew((RISC_MTREG_P0ULTRA | RISC_MTREG_P1ULTRA), qpti->qregs + RISC_MTREG); } else { qpti->ultra = 0; sbus_writew((RISC_MTREG_P0DFLT | RISC_MTREG_P1DFLT), qpti->qregs + RISC_MTREG); } sbus_writew(HCCTRL_REL, qpti->qregs + HCCTRL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -