wd7000.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,669 行 · 第 1/4 页
C
1,669 行
return (NULL); } } /* Take the lock, then check we didnt get beaten, if so try again */ spin_lock_irqsave(&scbpool_lock, flags); if (freescbs < needed) { spin_unlock_irqrestore(&scbpool_lock, flags); goto retry; } scb = scbfree; freescbs -= needed; for (i = 0; i < needed; i++) { p = scbfree; scbfree = p->next; } p->next = NULL; spin_unlock_irqrestore(&scbpool_lock, flags); spin_lock_irq(host->host_lock); return (scb);}static inline void free_scb(Scb * scb){ unsigned long flags; spin_lock_irqsave(&scbpool_lock, flags); memset(scb, 0, sizeof(Scb)); scb->next = scbfree; scbfree = scb; freescbs++; spin_unlock_irqrestore(&scbpool_lock, flags);}static inline void init_scbs(void){ int i; spin_lock_init(&scbpool_lock); /* This is only ever called before the SCB pool is active */ scbfree = &(scbs[0]); memset(scbs, 0, sizeof(scbs)); for (i = 0; i < MAX_SCBS - 1; i++) { scbs[i].next = &(scbs[i + 1]); scbs[i].SCpnt = NULL; } scbs[MAX_SCBS - 1].next = NULL; scbs[MAX_SCBS - 1].SCpnt = NULL;}static int mail_out(Adapter * host, Scb * scbptr)/* * Note: this can also be used for ICBs; just cast to the parm type. */{ int i, ogmb; unsigned long flags; unchar start_ogmb; Mailbox *ogmbs = host->mb.ogmb; int *next_ogmb = &(host->next_ogmb); dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr); /* We first look for a free outgoing mailbox */ spin_lock_irqsave(host->sh->host_lock, flags); ogmb = *next_ogmb; for (i = 0; i < OGMB_CNT; i++) { if (ogmbs[ogmb].status == 0) { dprintk(" using OGMB 0x%x", ogmb); ogmbs[ogmb].status = 1; any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr); *next_ogmb = (ogmb + 1) % OGMB_CNT; break; } else ogmb = (ogmb + 1) % OGMB_CNT; } spin_unlock_irqrestore(host->sh->host_lock, flags); dprintk(", scb is 0x%06lx", (long) scbptr); if (i >= OGMB_CNT) { /* * Alternatively, we might issue the "interrupt on free OGMB", * and sleep, but it must be ensured that it isn't the init * task running. Instead, this version assumes that the caller * will be persistent, and try again. Since it's the adapter * that marks OGMB's free, waiting even with interrupts off * should work, since they are freed very quickly in most cases. */ dprintk(", no free OGMBs.\n"); return (0); } wd7000_enable_intr(host); start_ogmb = START_OGMB | ogmb; command_out(host, &start_ogmb, 1); dprintk(", awaiting interrupt.\n"); return (1);}static int make_code(unsigned hosterr, unsigned scsierr){#ifdef WD7000_DEBUG int in_error = hosterr;#endif switch ((hosterr >> 8) & 0xff) { case 0: /* Reserved */ hosterr = DID_ERROR; break; case 1: /* Command Complete, no errors */ hosterr = DID_OK; break; case 2: /* Command complete, error logged in scb status (scsierr) */ hosterr = DID_OK; break; case 4: /* Command failed to complete - timeout */ hosterr = DID_TIME_OUT; break; case 5: /* Command terminated; Bus reset by external device */ hosterr = DID_RESET; break; case 6: /* Unexpected Command Received w/ host as target */ hosterr = DID_BAD_TARGET; break; case 80: /* Unexpected Reselection */ case 81: /* Unexpected Selection */ hosterr = DID_BAD_INTR; break; case 82: /* Abort Command Message */ hosterr = DID_ABORT; break; case 83: /* SCSI Bus Software Reset */ case 84: /* SCSI Bus Hardware Reset */ hosterr = DID_RESET; break; default: /* Reserved */ hosterr = DID_ERROR; }#ifdef WD7000_DEBUG if (scsierr || hosterr) dprintk("\nSCSI command error: SCSI 0x%02x host 0x%04x return %d\n", scsierr, in_error, hosterr);#endif return (scsierr | (hosterr << 16));}#define wd7000_intr_ack(host) outb (0, host->iobase + ASC_INTR_ACK)static irqreturn_t wd7000_intr(int irq, void *dev_id, struct pt_regs *regs){ Adapter *host = (Adapter *) dev_id; int flag, icmb, errstatus, icmb_status; int host_error, scsi_error; Scb *scb; /* for SCSI commands */ IcbAny *icb; /* for host commands */ struct scsi_cmnd *SCpnt; Mailbox *icmbs = host->mb.icmb; unsigned long flags; spin_lock_irqsave(host->sh->host_lock, flags); host->int_counter++; dprintk("wd7000_intr: irq = %d, host = 0x%06lx\n", irq, (long) host); flag = inb(host->iobase + ASC_INTR_STAT); dprintk("wd7000_intr: intr stat = 0x%02x\n", flag); if (!(inb(host->iobase + ASC_STAT) & INT_IM)) { /* NB: these are _very_ possible if IRQ 15 is being used, since * it's the "garbage collector" on the 2nd 8259 PIC. Specifically, * any interrupt signal into the 8259 which can't be identified * comes out as 7 from the 8259, which is 15 to the host. Thus, it * is a good thing the WD7000 has an interrupt status port, so we * can sort these out. Otherwise, electrical noise and other such * problems would be indistinguishable from valid interrupts... */ dprintk("wd7000_intr: phantom interrupt...\n"); goto ack; } if (!(flag & MB_INTR)) goto ack; /* The interrupt is for a mailbox */ if (!(flag & IMB_INTR)) { dprintk("wd7000_intr: free outgoing mailbox\n"); /* * If sleep_on() and the "interrupt on free OGMB" command are * used in mail_out(), wake_up() should correspondingly be called * here. For now, we don't need to do anything special. */ goto ack; } /* The interrupt is for an incoming mailbox */ icmb = flag & MB_MASK; icmb_status = icmbs[icmb].status; if (icmb_status & 0x80) { /* unsolicited - result in ICMB */ dprintk("wd7000_intr: unsolicited interrupt 0x%02x\n", icmb_status); goto ack; } /* Aaaargh! (Zaga) */ scb = isa_bus_to_virt(scsi2int((unchar *) icmbs[icmb].scbptr)); icmbs[icmb].status = 0; if (scb->op & ICB_OP_MASK) { /* an SCB is done */ icb = (IcbAny *) scb; icb->status = icmb_status; icb->phase = 0; goto ack; } SCpnt = scb->SCpnt; if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */ host_error = scb->vue | (icmb_status << 8); scsi_error = scb->status; errstatus = make_code(host_error, scsi_error); SCpnt->result = errstatus; free_scb(scb); SCpnt->scsi_done(SCpnt); } ack: dprintk("wd7000_intr: return from interrupt handler\n"); wd7000_intr_ack(host); spin_unlock_irqrestore(host->sh->host_lock, flags); return IRQ_HANDLED;}static int wd7000_queuecommand(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)){ Scb *scb; Sgb *sgb; unchar *cdb = (unchar *) SCpnt->cmnd; unchar idlun; short cdblen; Adapter *host = (Adapter *) SCpnt->device->host->hostdata; cdblen = SCpnt->cmd_len; idlun = ((SCpnt->device->id << 5) & 0xe0) | (SCpnt->device->lun & 7); SCpnt->scsi_done = done; SCpnt->SCp.phase = 1; scb = alloc_scbs(SCpnt->device->host, 1); scb->idlun = idlun; memcpy(scb->cdb, cdb, cdblen); scb->direc = 0x40; /* Disable direction check */ scb->SCpnt = SCpnt; /* so we can find stuff later */ SCpnt->host_scribble = (unchar *) scb; scb->host = host; if (SCpnt->use_sg) { struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer; unsigned i; if (SCpnt->device->host->sg_tablesize == SG_NONE) { panic("wd7000_queuecommand: scatter/gather not supported.\n"); } dprintk("Using scatter/gather with %d elements.\n", SCpnt->use_sg); sgb = scb->sgb; scb->op = 1; any2scsi(scb->dataptr, (int) sgb); any2scsi(scb->maxlen, SCpnt->use_sg * sizeof(Sgb)); for (i = 0; i < SCpnt->use_sg; i++) { any2scsi(sgb[i].ptr, isa_page_to_bus(sg[i].page) + sg[i].offset); any2scsi(sgb[i].len, sg[i].length); } } else { scb->op = 0; any2scsi(scb->dataptr, isa_virt_to_bus(SCpnt->request_buffer)); any2scsi(scb->maxlen, SCpnt->request_bufflen); } /* FIXME: drop lock and yield here ? */ while (!mail_out(host, scb)) cpu_relax(); /* keep trying */ return 0;}static int wd7000_diagnostics(Adapter * host, int code){ static IcbDiag icb = { ICB_OP_DIAGNOSTICS }; static unchar buf[256]; unsigned long timeout; icb.type = code; any2scsi(icb.len, sizeof(buf)); any2scsi(icb.ptr, (int) &buf); icb.phase = 1; /* * This routine is only called at init, so there should be OGMBs * available. I'm assuming so here. If this is going to * fail, I can just let the timeout catch the failure. */ mail_out(host, (struct scb *) &icb); timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */ while (icb.phase && time_before(jiffies, timeout)) { cpu_relax(); /* wait for completion */ barrier(); } if (icb.phase) { printk("wd7000_diagnostics: timed out.\n"); return (0); } if (make_code(icb.vue | (icb.status << 8), 0)) { printk("wd7000_diagnostics: failed (0x%02x,0x%02x)\n", icb.vue, icb.status); return (0); } return (1);}static int wd7000_adapter_reset(Adapter * host){ InitCmd init_cmd = { INITIALIZATION, 7, host->bus_on, host->bus_off, 0, {0, 0, 0}, OGMB_CNT, ICMB_CNT }; int diag; /* * Reset the adapter - only. The SCSI bus was initialized at power-up, * and we need to do this just so we control the mailboxes, etc. */ outb(ASC_RES, host->iobase + ASC_CONTROL); udelay(40); /* reset pulse: this is 40us, only need 25us */ outb(0, host->iobase + ASC_CONTROL); host->control = 0; /* this must always shadow ASC_CONTROL */ if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) { printk(KERN_ERR "wd7000_init: WAIT timed out.\n"); return -1; /* -1 = not ok */ } if ((diag = inb(host->iobase + ASC_INTR_STAT)) != 1) { printk("wd7000_init: "); switch (diag) { case 2: printk(KERN_ERR "RAM failure.\n"); break; case 3: printk(KERN_ERR "FIFO R/W failed\n"); break; case 4: printk(KERN_ERR "SBIC register R/W failed\n"); break; case 5: printk(KERN_ERR "Initialization D-FF failed.\n"); break; case 6: printk(KERN_ERR "Host IRQ D-FF failed.\n"); break; case 7: printk(KERN_ERR "ROM checksum error.\n"); break; default: printk(KERN_ERR "diagnostic code 0x%02Xh received.\n", diag); } return -1; } /* Clear mailboxes */ memset(&(host->mb), 0, sizeof(host->mb)); /* Execute init command */ any2scsi((unchar *) & (init_cmd.mailboxes), (int) &(host->mb)); if (!command_out(host, (unchar *) & init_cmd, sizeof(init_cmd))) { printk(KERN_ERR "wd7000_adapter_reset: adapter initialization failed.\n"); return -1; } if (WAIT(host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) { printk("wd7000_adapter_reset: WAIT timed out.\n"); return -1; } return 0;}static int wd7000_init(Adapter * host){ if (wd7000_adapter_reset(host) == -1) return 0; if (request_irq(host->irq, wd7000_intr, SA_INTERRUPT, "wd7000", host)) { printk("wd7000_init: can't get IRQ %d.\n", host->irq);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?