📄 qlogicisp.c
字号:
#define MAX_MBOX_COMMAND (sizeof(mbox_param)/sizeof(u_short))struct host_param { u_short fifo_threshold; u_short host_adapter_enable; u_short initiator_scsi_id; u_short bus_reset_delay; u_short retry_count; u_short retry_delay; u_short async_data_setup_time; u_short req_ack_active_negation; u_short data_line_active_negation; u_short data_dma_burst_enable; u_short command_dma_burst_enable; u_short tag_aging; u_short selection_timeout; u_short max_queue_depth;};/* * Device Flags: * * Bit Name * --------- * 7 Disconnect Privilege * 6 Parity Checking * 5 Wide Data Transfers * 4 Synchronous Data Transfers * 3 Tagged Queuing * 2 Automatic Request Sense * 1 Stop Queue on Check Condition * 0 Renegotiate on Error */struct dev_param { u_short device_flags; u_short execution_throttle; u_short synchronous_period; u_short synchronous_offset; u_short device_enable; u_short reserved; /* pad */};/* * The result queue can be quite a bit smaller since continuation entries * do not show up there: */#define RES_QUEUE_LEN ((QLOGICISP_REQ_QUEUE_LEN + 1) / 8 - 1)#define QUEUE_ENTRY_LEN 64struct isp1020_hostdata { u_char revision; struct host_param host_param; struct dev_param dev_param[MAX_TARGETS]; struct pci_dev *pci_dev; /* result and request queues (shared with isp1020): */ u_int req_in_ptr; /* index of next request slot */ u_int res_out_ptr; /* index of next result slot */ /* this is here so the queues are nicely aligned */ long send_marker; /* do we need to send a marker? */ char res[RES_QUEUE_LEN+1][QUEUE_ENTRY_LEN]; char req[QLOGICISP_REQ_QUEUE_LEN+1][QUEUE_ENTRY_LEN];};/* 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, \ QLOGICISP_REQ_QUEUE_LEN)#define RES_QUEUE_DEPTH(in, out) QUEUE_DEPTH(in, out, RES_QUEUE_LEN)static void isp1020_enable_irqs(struct Scsi_Host *);static void isp1020_disable_irqs(struct Scsi_Host *);static int isp1020_init(struct Scsi_Host *);static int isp1020_reset_hardware(struct Scsi_Host *);static int isp1020_set_defaults(struct Scsi_Host *);static int isp1020_load_parameters(struct Scsi_Host *);static int isp1020_mbox_command(struct Scsi_Host *, u_short []); static int isp1020_return_status(struct Status_Entry *);static void isp1020_intr_handler(int, void *, struct pt_regs *);static void do_isp1020_intr_handler(int, void *, struct pt_regs *);#if USE_NVRAM_DEFAULTSstatic int isp1020_get_defaults(struct Scsi_Host *);static int isp1020_verify_nvram(struct Scsi_Host *);static u_short isp1020_read_nvram_word(struct Scsi_Host *, u_short);#endif#if DEBUG_ISP1020static void isp1020_print_scsi_cmd(Scsi_Cmnd *);#endif#if DEBUG_ISP1020_INTRstatic void isp1020_print_status_entry(struct Status_Entry *);#endifstatic struct proc_dir_entry proc_scsi_isp1020 = { PROC_SCSI_QLOGICISP, 7, "isp1020", S_IFDIR | S_IRUGO | S_IXUGO, 2};static inline void isp1020_enable_irqs(struct Scsi_Host *host){ outw(ISP_EN_INT|ISP_EN_RISC, host->io_port + PCI_INTF_CTL);}static inline void isp1020_disable_irqs(struct Scsi_Host *host){ outw(0x0, host->io_port + PCI_INTF_CTL);}int isp1020_detect(Scsi_Host_Template *tmpt){ int hosts = 0; struct Scsi_Host *host; struct isp1020_hostdata *hostdata; struct pci_dev *pdev = NULL; ENTER("isp1020_detect"); tmpt->proc_dir = &proc_scsi_isp1020; if (pci_present() == 0) { printk("qlogicisp : PCI not present\n"); return 0; } while ((pdev = pci_find_device(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP1020, pdev))) { host = scsi_register(tmpt, sizeof(struct isp1020_hostdata)); hostdata = (struct isp1020_hostdata *) host->hostdata; memset(hostdata, 0, sizeof(struct isp1020_hostdata)); hostdata->pci_dev = pdev; if (isp1020_init(host) || isp1020_reset_hardware(host)#if USE_NVRAM_DEFAULTS || isp1020_get_defaults(host)#else || isp1020_set_defaults(host)#endif /* USE_NVRAM_DEFAULTS */ || isp1020_load_parameters(host)) { scsi_unregister(host); continue; } host->this_id = hostdata->host_param.initiator_scsi_id; if (request_irq(host->irq, do_isp1020_intr_handler, SA_INTERRUPT | SA_SHIRQ, "qlogicisp", host)) { printk("qlogicisp : interrupt %d already in use\n", host->irq); scsi_unregister(host); continue; } if (check_region(host->io_port, 0xff)) { printk("qlogicisp : i/o region 0x%lx-0x%lx already " "in use\n", host->io_port, host->io_port + 0xff); free_irq(host->irq, host); scsi_unregister(host); continue; } request_region(host->io_port, 0xff, "qlogicisp"); outw(0x0, host->io_port + PCI_SEMAPHORE); outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR); isp1020_enable_irqs(host); hosts++; } LEAVE("isp1020_detect"); return hosts;}int isp1020_release(struct Scsi_Host *host){ struct isp1020_hostdata *hostdata; ENTER("isp1020_release"); hostdata = (struct isp1020_hostdata *) host->hostdata; outw(0x0, host->io_port + PCI_INTF_CTL); free_irq(host->irq, host); release_region(host->io_port, 0xff); LEAVE("isp1020_release"); return 0;}const char *isp1020_info(struct Scsi_Host *host){ static char buf[80]; struct isp1020_hostdata *hostdata; ENTER("isp1020_info"); hostdata = (struct isp1020_hostdata *) host->hostdata; sprintf(buf, "QLogic ISP1020 SCSI on PCI bus %02x device %02x irq %d base 0x%lx", hostdata->pci_dev->bus->number, hostdata->pci_dev->devfn, host->irq, host->io_port); LEAVE("isp1020_info"); return buf;}/* * The middle SCSI layer ensures that queuecommand never gets invoked * concurrently with itself or the interrupt handler (though the * interrupt handler may call this routine as part of * request-completion handling). */int isp1020_queuecommand(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *)){ int i, sg_count, n, num_free; u_int in_ptr, out_ptr; struct dataseg * ds; struct scatterlist *sg; struct Command_Entry *cmd; struct Continuation_Entry *cont; struct Scsi_Host *host; struct isp1020_hostdata *hostdata; ENTER("isp1020_queuecommand"); host = Cmnd->host; hostdata = (struct isp1020_hostdata *) host->hostdata; Cmnd->scsi_done = done; DEBUG(isp1020_print_scsi_cmd(Cmnd)); out_ptr = inw(host->io_port + MBOX4); in_ptr = hostdata->req_in_ptr; DEBUG(printk("qlogicisp : request queue depth %d\n", REQ_QUEUE_DEPTH(in_ptr, out_ptr))); cmd = (struct Command_Entry *) &hostdata->req[in_ptr][0]; in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN; if (in_ptr == out_ptr) { printk("qlogicisp : request queue overflow\n"); return 1; } if (hostdata->send_marker) { struct Marker_Entry *marker; TRACE("queue marker", in_ptr, 0); DEBUG(printk("qlogicisp : adding marker entry\n")); marker = (struct Marker_Entry *) cmd; memset(marker, 0, sizeof(struct Marker_Entry)); marker->hdr.entry_type = ENTRY_MARKER; marker->hdr.entry_cnt = 1; marker->modifier = SYNC_ALL; hostdata->send_marker = 0; if (((in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN) == out_ptr) { outw(in_ptr, host->io_port + MBOX4); hostdata->req_in_ptr = in_ptr; printk("qlogicisp : request queue overflow\n"); return 1; } cmd = (struct Command_Entry *) &hostdata->req[in_ptr][0]; in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN; } TRACE("queue command", in_ptr, Cmnd); memset(cmd, 0, sizeof(struct Command_Entry)); cmd->hdr.entry_type = ENTRY_COMMAND; cmd->hdr.entry_cnt = 1; cmd->handle = cpu_to_le32((u_int) virt_to_bus(Cmnd)); cmd->target_lun = Cmnd->lun; cmd->target_id = Cmnd->target; cmd->cdb_length = cpu_to_le16(Cmnd->cmd_len); cmd->control_flags = cpu_to_le16(CFLAG_READ | CFLAG_WRITE); cmd->time_out = cpu_to_le16(30); memcpy(cmd->cdb, Cmnd->cmnd, Cmnd->cmd_len); if (Cmnd->use_sg) { cmd->segment_cnt = cpu_to_le16(sg_count = Cmnd->use_sg); sg = (struct scatterlist *) Cmnd->request_buffer; ds = cmd->dataseg; /* fill in first four sg entries: */ n = sg_count; if (n > 4) n = 4; for (i = 0; i < n; i++) { ds[i].d_base = cpu_to_le32((u_int) virt_to_bus(sg->address)); ds[i].d_count = cpu_to_le32(sg->length); ++sg; } sg_count -= 4; while (sg_count > 0) { ++cmd->hdr.entry_cnt; cont = (struct Continuation_Entry *) &hostdata->req[in_ptr][0]; in_ptr = (in_ptr + 1) & QLOGICISP_REQ_QUEUE_LEN; if (in_ptr == out_ptr) { printk("isp1020: unexpected request queue " "overflow\n"); return 1; } TRACE("queue continuation", in_ptr, 0); cont->hdr.entry_type = ENTRY_CONTINUATION; cont->hdr.entry_cnt = 0; cont->hdr.sys_def_1 = 0; cont->hdr.flags = 0; cont->reserved = 0; ds = cont->dataseg; n = sg_count; if (n > 7) n = 7; for (i = 0; i < n; ++i) { ds[i].d_base = cpu_to_le32((u_int)virt_to_bus(sg->address)); ds[i].d_count = cpu_to_le32(sg->length); ++sg; } sg_count -= n; } } else { cmd->dataseg[0].d_base = cpu_to_le32((u_int) virt_to_bus(Cmnd->request_buffer)); cmd->dataseg[0].d_count = cpu_to_le32((u_int) Cmnd->request_bufflen); cmd->segment_cnt = cpu_to_le16(1); } outw(in_ptr, host->io_port + MBOX4); hostdata->req_in_ptr = in_ptr; num_free = QLOGICISP_REQ_QUEUE_LEN - REQ_QUEUE_DEPTH(in_ptr, out_ptr); host->can_queue = host->host_busy + num_free; host->sg_tablesize = QLOGICISP_MAX_SG(num_free); LEAVE("isp1020_queuecommand"); return 0;}#define ASYNC_EVENT_INTERRUPT 0x01void do_isp1020_intr_handler(int irq, void *dev_id, struct pt_regs *regs){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); isp1020_intr_handler(irq, dev_id, regs); spin_unlock_irqrestore(&io_request_lock, flags);}void isp1020_intr_handler(int irq, void *dev_id, struct pt_regs *regs){ Scsi_Cmnd *Cmnd; struct Status_Entry *sts; struct Scsi_Host *host = dev_id; struct isp1020_hostdata *hostdata; u_int in_ptr, out_ptr; u_short status; ENTER_INTR("isp1020_intr_handler"); hostdata = (struct isp1020_hostdata *) host->hostdata; DEBUG_INTR(printk("qlogicisp : interrupt on line %d\n", irq)); if (!(inw(host->io_port + PCI_INTF_STS) & 0x04)) { /* spurious interrupts can happen legally */ DEBUG_INTR(printk("qlogicisp: got spurious interrupt\n")); return; } in_ptr = inw(host->io_port + MBOX5); outw(HCCR_CLEAR_RISC_INTR, host->io_port + HOST_HCCR); if ((inw(host->io_port + PCI_SEMAPHORE) & ASYNC_EVENT_INTERRUPT)) { status = inw(host->io_port + MBOX0); DEBUG_INTR(printk("qlogicisp : mbox completion status: %x\n", status)); switch (status) { case ASYNC_SCSI_BUS_RESET: case EXECUTION_TIMEOUT_RESET: hostdata->send_marker = 1; break; case INVALID_COMMAND: case HOST_INTERFACE_ERROR: case COMMAND_ERROR: case COMMAND_PARAM_ERROR: printk("qlogicisp : bad mailbox return status\n"); break; } outw(0x0, host->io_port + PCI_SEMAPHORE); } out_ptr = hostdata->res_out_ptr; DEBUG_INTR(printk("qlogicisp : response queue update\n")); DEBUG_INTR(printk("qlogicisp : response queue depth %d\n", QUEUE_DEPTH(in_ptr, out_ptr, RES_QUEUE_LEN))); while (out_ptr != in_ptr) { sts = (struct Status_Entry *) &hostdata->res[out_ptr][0]; out_ptr = (out_ptr + 1) & RES_QUEUE_LEN; Cmnd = (Scsi_Cmnd *) bus_to_virt(le32_to_cpu(sts->handle)); TRACE("done", out_ptr, Cmnd); if (le16_to_cpu(sts->completion_status) == CS_RESET_OCCURRED || le16_to_cpu(sts->completion_status) == CS_ABORTED || (le16_to_cpu(sts->status_flags) & STF_BUS_RESET)) hostdata->send_marker = 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -