URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [scsi/] [esp.c] - Rev 1777
Go to most recent revision | Compare with Previous | Blame | View Log
/* esp.c: EnhancedScsiProcessor Sun SCSI driver code. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) */ #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 "scsi.h" #include "hosts.h" #include "esp.h" #include <asm/sbus.h> #include <asm/dma.h> #include <asm/system.h> #include <asm/idprom.h> #include <asm/machines.h> #include <asm/ptrace.h> #include <asm/pgtable.h> #include <asm/oplib.h> #include <asm/vaddrs.h> #include <asm/io.h> #define DEBUG_ESP /* #define DEBUG_ESP_SG */ #if defined(DEBUG_ESP) #define ESPLOG(foo) printk foo #else #define ESPLOG(foo) #endif /* (DEBUG_ESP) */ #define INTERNAL_ESP_ERROR \ (panic ("Internal ESP driver error in file %s, line %d\n", \ __FILE__, __LINE__)) #define INTERNAL_ESP_ERROR_NOPANIC \ (printk ("Internal ESP driver error in file %s, line %d\n", \ __FILE__, __LINE__)) /* This enum will be expanded when we have sync code written. */ enum { not_issued = 0x01, /* Still in the issue_SC queue. */ in_selection = 0x02, /* ESP is arbitrating, awaiting IRQ */ in_datain = 0x04, /* Data is transferring over the bus */ in_dataout = 0x08, /* Data is transferring over the bus */ in_status = 0x10, /* Awaiting status/msg bytes from target */ in_finale = 0x11, /* Sent Msg ack, awaiting disconnect */ }; struct proc_dir_entry proc_scsi_esp = { PROC_SCSI_ESP, 3, "esp", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; struct Sparc_ESP *espchain; static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs); static void esp_done(struct Sparc_ESP *esp, int error); /* Debugging routines */ struct esp_cmdstrings { unchar cmdchar; char *text; } esp_cmd_strings[] = { /* Miscellaneous */ { ESP_CMD_NULL, "ESP_NOP", }, { ESP_CMD_FLUSH, "FIFO_FLUSH", }, { ESP_CMD_RC, "RSTESP", }, { ESP_CMD_RS, "RSTSCSI", }, /* Disconnected State Group */ { ESP_CMD_RSEL, "RESLCTSEQ", }, { ESP_CMD_SEL, "SLCTNATN", }, { ESP_CMD_SELA, "SLCTATN", }, { ESP_CMD_SELAS, "SLCTATNSTOP", }, { ESP_CMD_ESEL, "ENSLCTRESEL", }, { ESP_CMD_DSEL, "DISSELRESEL", }, { ESP_CMD_SA3, "SLCTATN3", }, { ESP_CMD_RSEL3, "RESLCTSEQ", }, /* Target State Group */ { ESP_CMD_SMSG, "SNDMSG", }, { ESP_CMD_SSTAT, "SNDSTATUS", }, { ESP_CMD_SDATA, "SNDDATA", }, { ESP_CMD_DSEQ, "DISCSEQ", }, { ESP_CMD_TSEQ, "TERMSEQ", }, { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", }, { ESP_CMD_DCNCT, "DISC", }, { ESP_CMD_RMSG, "RCVMSG", }, { ESP_CMD_RCMD, "RCVCMD", }, { ESP_CMD_RDATA, "RCVDATA", }, { ESP_CMD_RCSEQ, "RCVCMDSEQ", }, /* Initiator State Group */ { ESP_CMD_TI, "TRANSINFO", }, { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", }, { ESP_CMD_MOK, "MSGACCEPTED", }, { ESP_CMD_TPAD, "TPAD", }, { ESP_CMD_SATN, "SATN", }, { ESP_CMD_RATN, "RATN", }, }; #define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings))) /* Print textual representation of an ESP command */ static inline void esp_print_cmd(unchar espcmd) { unchar dma_bit = espcmd & ESP_CMD_DMA; int i; espcmd &= ~dma_bit; for(i=0; i<NUM_ESP_COMMANDS; i++) if(esp_cmd_strings[i].cmdchar == espcmd) break; if(i==NUM_ESP_COMMANDS) printk("ESP_Unknown"); else printk("%s%s", esp_cmd_strings[i].text, ((dma_bit) ? "+DMA" : "")); } /* Print the status register's value */ static inline void esp_print_statreg(unchar statreg) { unchar phase; printk("STATUS<"); phase = statreg & ESP_STAT_PMASK; printk("%s,", (phase == ESP_DOP ? "DATA-OUT" : (phase == ESP_DIP ? "DATA-IN" : (phase == ESP_CMDP ? "COMMAND" : (phase == ESP_STATP ? "STATUS" : (phase == ESP_MOP ? "MSG-OUT" : (phase == ESP_MIP ? "MSG_IN" : "unknown"))))))); if(statreg & ESP_STAT_TDONE) printk("TRANS_DONE,"); if(statreg & ESP_STAT_TCNT) printk("TCOUNT_ZERO,"); if(statreg & ESP_STAT_PERR) printk("P_ERROR,"); if(statreg & ESP_STAT_SPAM) printk("SPAM,"); if(statreg & ESP_STAT_INTR) printk("IRQ,"); printk(">"); } /* Print the interrupt register's value */ static inline void esp_print_ireg(unchar intreg) { printk("INTREG< "); if(intreg & ESP_INTR_S) printk("SLCT_NATN "); if(intreg & ESP_INTR_SATN) printk("SLCT_ATN "); if(intreg & ESP_INTR_RSEL) printk("RSLCT "); if(intreg & ESP_INTR_FDONE) printk("FDONE "); if(intreg & ESP_INTR_BSERV) printk("BSERV "); if(intreg & ESP_INTR_DC) printk("DISCNCT "); if(intreg & ESP_INTR_IC) printk("ILL_CMD "); if(intreg & ESP_INTR_SR) printk("SCSI_BUS_RESET "); printk(">"); } /* Print the sequence step registers contents */ static inline void esp_print_seqreg(unchar stepreg) { stepreg &= ESP_STEP_VBITS; printk("STEP<%s>", (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" : (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" : (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" : (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" : (stepreg == ESP_STEP_FINI ? "CMD_SENT_OK" : "UNKNOWN")))))); } /* Manipulation of the ESP command queues. Thanks to the aha152x driver * and its author, Juergen E. Fischer, for the methods used here. * Note that these are per-ESP queues, not global queues like * the aha152x driver uses. */ static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC) { Scsi_Cmnd *end; unsigned long flags; save_flags(flags); cli(); new_SC->host_scribble = (unsigned char *) NULL; if(!*SC) *SC = new_SC; else { for(end=*SC;end->host_scribble;end=(Scsi_Cmnd *)end->host_scribble) ; end->host_scribble = (unsigned char *) new_SC; } restore_flags(flags); } static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC) { Scsi_Cmnd *ptr; unsigned long flags; save_flags(flags); cli(); ptr = *SC; if(ptr) *SC = (Scsi_Cmnd *) (*SC)->host_scribble; restore_flags(flags); return ptr; } static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun) { Scsi_Cmnd *ptr, *prev; unsigned long flags; save_flags(flags); cli(); for(ptr = *SC, prev = NULL; ptr && ((ptr->target != target) || (ptr->lun != lun)); prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble) ; if(ptr) { if(prev) prev->host_scribble=ptr->host_scribble; else *SC=(Scsi_Cmnd *)ptr->host_scribble; } restore_flags(flags); return ptr; } static inline void do_pause(unsigned amount) { unsigned long the_time = jiffies + amount; while(jiffies < the_time) barrier(); /* Not really needed, but... */ } /* This places the ESP into a known state at boot time. */ static inline void esp_bootup_reset(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) { struct sparc_dma_registers *dregs = esp->dregs; volatile unchar trash; /* Punt the DVMA into a known state. */ dregs->cond_reg |= DMA_RST_SCSI; do_pause(100); dregs->cond_reg &= ~(DMA_RST_SCSI); if(esp->dma->revision == dvmarev2) if(esp->erev != esp100) dregs->cond_reg |= DMA_3CLKS; else if(esp->dma->revision == dvmarev3) if(esp->erev == fas236 || esp->erev == fas100a) { dregs->cond_reg &= ~(DMA_3CLKS); dregs->cond_reg |= DMA_2CLKS; } else if(esp->dma->revision == dvmaesc1) dregs->cond_reg |= DMA_ADD_ENABLE; DMA_INTSON(dregs); /* Now reset the ESP chip */ eregs->esp_cmd = ESP_CMD_RC; eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA); eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA); /* borken hardware... */ /* Reload the configuration registers */ eregs->esp_cfg1 = esp->config1; eregs->esp_cfact = esp->cfact; eregs->esp_stp = 0; eregs->esp_soff = 0; eregs->esp_timeo = esp->sync_defp; if(esp->erev == esp100a || esp->erev == esp236) eregs->esp_cfg2 = esp->config2; if(esp->erev == esp236) eregs->esp_cfg3 = esp->config3[0]; /* Eat any bitrot in the chip */ trash = eregs->esp_intrpt; /* Reset the SCSI bus, but tell ESP not to generate an irq */ eregs->esp_cfg1 |= ESP_CONFIG1_SRRDISAB; eregs->esp_cmd = ESP_CMD_RS; do_pause(200); eregs->esp_cfg1 = esp->config1; /* Eat any bitrot in the chip and we are done... */ trash = eregs->esp_intrpt; } /* Detecting ESP chips on the machine. This is the simple and easy * version. */ int esp_detect(Scsi_Host_Template *tpnt) { struct Sparc_ESP *esp, *elink; struct Scsi_Host *esp_host; struct linux_sbus *sbus; struct linux_sbus_device *esp_dev, *sbdev_iter; struct Sparc_ESP_regs *eregs; struct sparc_dma_registers *dregs; struct Linux_SBus_DMA *dma, *dlink; unsigned int fmhz; unchar ccf, bsizes, bsizes_more; int nesps = 0; int esp_node; espchain = 0; if(!SBus_chain) panic("No SBUS in esp_detect()"); for_each_sbus(sbus) { for_each_sbusdev(sbdev_iter, sbus) { /* Is it an esp sbus device? */ esp_dev = sbdev_iter; if(strcmp(esp_dev->prom_name, "esp") && strcmp(esp_dev->prom_name, "SUNW,esp")) { if(!esp_dev->child || strcmp(esp_dev->prom_name, "espdma")) continue; /* nope... */ esp_dev = esp_dev->child; if(strcmp(esp_dev->prom_name, "esp") && strcmp(esp_dev->prom_name, "SUNW,esp")) continue; /* how can this happen? */ } esp_host = scsi_register(tpnt, sizeof(struct Sparc_ESP)); if(!esp_host) panic("Cannot register ESP SCSI host"); esp = (struct Sparc_ESP *) esp_host->hostdata; if(!esp) panic("No esp in hostdata"); esp->ehost = esp_host; esp->edev = esp_dev; /* Put into the chain of esp chips detected */ if(espchain) { elink = espchain; while(elink->next) elink = elink->next; elink->next = esp; } else { espchain = esp; } esp->next = 0; /* Get misc. prom information */ #define ESP_IS_MY_DVMA(esp, dma) \ ((esp->edev->my_bus == dma->SBus_dev->my_bus) && \ (esp->edev->slot == dma->SBus_dev->slot) && \ (!strcmp(dma->SBus_dev->prom_name, "dma") || \ !strcmp(dma->SBus_dev->prom_name, "espdma"))) esp_node = esp_dev->prom_node; prom_getstring(esp_node, "name", esp->prom_name, sizeof(esp->prom_name)); esp->prom_node = esp_node; for_each_dvma(dlink) { if(ESP_IS_MY_DVMA(esp, dlink) && !dlink->allocated) break; } #undef ESP_IS_MY_DVMA /* If we don't know how to handle the dvma, do not use this device */ if(!dlink){ printk ("Cannot find dvma for ESP SCSI\n"); scsi_unregister (esp_host); continue; } if (dlink->allocated){ printk ("esp: can't use my espdma\n"); scsi_unregister (esp_host); continue; } dlink->allocated = 1; dma = dlink; esp->dma = dma; esp->dregs = dregs = dma->regs; /* Map in the ESP registers from I/O space */ prom_apply_sbus_ranges(esp->edev->reg_addrs, 1); esp->eregs = eregs = (struct Sparc_ESP_regs *) sparc_alloc_io(esp->edev->reg_addrs[0].phys_addr, 0, PAGE_SIZE, "ESP Registers", esp->edev->reg_addrs[0].which_io, 0x0); if(!eregs) panic("ESP registers unmappable"); esp->esp_command = sparc_dvma_malloc(16, "ESP DVMA Cmd Block"); if(!esp->esp_command) panic("ESP DVMA transport area unmappable"); /* Set up the irq's etc. */ esp->ehost->base = (unsigned char *) esp->eregs; esp->ehost->io_port = (unsigned int) esp->eregs; esp->ehost->n_io_port = (unsigned char) esp->edev->reg_addrs[0].reg_size; /* XXX The following may be different on sun4ms XXX */ esp->ehost->irq = esp->irq = esp->edev->irqs[0].pri; /* Allocate the irq only if necessary */ for_each_esp(elink) { if((elink != esp) && (esp->irq == elink->irq)) { goto esp_irq_acquired; /* BASIC rulez */ } } /* XXX We have shared interrupts per level now, maybe * XXX use them, maybe not... */ if(request_irq(esp->ehost->irq, esp_intr, SA_INTERRUPT, "Sparc ESP SCSI", NULL)) panic("Cannot acquire ESP irq line"); esp_irq_acquired: printk("esp%d: IRQ %d ", nesps, esp->ehost->irq); /* Figure out our scsi ID on the bus */ esp->scsi_id = prom_getintdefault(esp->prom_node, "initiator-id", -1); if(esp->scsi_id == -1) esp->scsi_id = prom_getintdefault(esp->prom_node, "scsi-initiator-id", -1); if(esp->scsi_id == -1) esp->scsi_id = prom_getintdefault(esp->edev->my_bus->prom_node, "scsi-initiator-id", 7); esp->ehost->this_id = esp->scsi_id; esp->scsi_id_mask = (1 << esp->scsi_id); /* Check for differential bus */ esp->diff = prom_getintdefault(esp->prom_node, "differential", -1); esp->diff = (esp->diff == -1) ? 0 : 1; /* Check out the clock properties of the chip */ fmhz = prom_getintdefault(esp->prom_node, "clock-frequency", -1); if(fmhz==-1) fmhz = prom_getintdefault(esp->edev->my_bus->prom_node, "clock-frequency", -1); if(fmhz <= (5000)) ccf = 0; else ccf = (((5000 - 1) + (fmhz))/(5000)); if(!ccf || ccf > 8) { ccf = ESP_CCF_F4; fmhz = (5000 * 4); } if(ccf==(ESP_CCF_F7+1)) esp->cfact = ESP_CCF_F0; else if(ccf == ESP_CCF_NEVER) esp->cfact = ESP_CCF_F2; else esp->cfact = ccf; esp->cfreq = fmhz; esp->ccycle = ((1000000000) / ((fmhz)/1000)); esp->ctick = ((7682 * esp->cfact * esp->ccycle)/1000); esp->sync_defp = ((7682 + esp->ctick - 1) / esp->ctick); /* XXX HACK HACK HACK XXX */ if (esp->sync_defp < 153) esp->sync_defp = 153; printk("SCSI ID %d Clock %d MHz Period %2x ", esp->scsi_id, (fmhz / 1000), esp->sync_defp); /* Find the burst sizes this dma supports. */ bsizes = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff); bsizes_more = prom_getintdefault(esp->edev->my_bus->prom_node, "burst-sizes", 0xff); if(bsizes_more != 0xff) bsizes &= bsizes_more; if(bsizes == 0xff || (bsizes & DMA_BURST16)==0 || (bsizes & DMA_BURST32)==0) bsizes = (DMA_BURST32 - 1); esp->bursts = bsizes; /* Probe the revision of this esp */ esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); esp->config3[0] = ESP_CONFIG3_TENB; eregs->esp_cfg2 = esp->config2; if((eregs->esp_cfg2 & ~(ESP_CONFIG2_MAGIC)) != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { printk("NCR53C90(esp100) detected\n"); esp->erev = esp100; } else { eregs->esp_cfg2 = esp->config2 = 0; eregs->esp_cfg3 = 0; eregs->esp_cfg3 = esp->config3[0] = 5; if(eregs->esp_cfg3 != 5) { printk("NCR53C90A(esp100a) detected\n"); esp->erev = esp100a; } else { int target; for(target=0; target<8; target++) esp->config3[target] = 0; eregs->esp_cfg3 = 0; if(esp->cfact > ESP_CCF_F5) { printk("NCR53C9XF(espfast) detected\n"); esp->erev = fast; esp->config2 |= ESP_CONFIG2_FENAB; eregs->esp_cfg2 = esp->config2; } else { printk("NCR53C9x(esp236) detected\n"); esp->erev = esp236; eregs->esp_cfg2 = esp->config2 = 0; } } } /* Initialize the command queues */ esp->current_SC = 0; esp->disconnected_SC = 0; esp->issue_SC = 0; /* Reset the thing before we try anything... */ esp_bootup_reset(esp, eregs); nesps++; #ifdef THREADED_ESP_DRIVER kernel_thread(esp_kernel_thread, esp, 0); #endif } /* for each sbusdev */ } /* for each sbus */ return nesps; } /* * The info function will return whatever useful * information the developer sees fit. If not provided, then * the name field will be used instead. */ const char *esp_info(struct Scsi_Host *host) { struct Sparc_ESP *esp; esp = (struct Sparc_ESP *) host->hostdata; switch(esp->erev) { case esp100: return "Sparc ESP100 (NCR53C90)"; case esp100a: return "Sparc ESP100A (NCR53C90A)"; case esp236: return "Sparc ESP236"; case fast: return "Sparc ESP-FAST (236 or 100A)"; case fas236: return "Sparc ESP236-FAST"; case fas100a: return "Sparc ESP100A-FAST"; default: panic("Bogon ESP revision"); }; } /* Execute a SCSI command when the bus is free. All callers * turn off all interrupts, so we don't need to explicitly do * it here. */ static inline void esp_exec_cmd(struct Sparc_ESP *esp) { struct sparc_dma_registers *dregs; struct Sparc_ESP_regs *eregs; Scsi_Cmnd *SCptr; int i; eregs = esp->eregs; dregs = esp->dregs; /* Grab first member of the issue queue. */ SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); if(!SCptr) goto bad; SCptr->SCp.phase = in_selection; /* NCR docs say: * 1) Load select/reselect Bus ID register with target ID * 2) Load select/reselect Timeout Reg with desired value * 3) Load Synchronous offset register with zero (for * asynchronous transfers). * 4) Load Synchronous Transfer Period register (if * synchronous) * 5) Load FIFO with 6, 10, or 12 byte SCSI command * 6) Issue SELECTION_WITHOUT_ATTENTION command * * They also mention that a DMA NOP command must be issued * to the SCSI chip under many circumstances, plus it's * also a good idea to flush out the fifo just in case. */ /* Load zeros into COUNTER via 2 DMA NOP chip commands * due to flaky implementations of the 53C9x which don't * get the idea the first time around. */ dregs->cond_reg = (DMA_INT_ENAB | DMA_FIFO_INV); eregs->esp_tclow = 0; eregs->esp_tcmed = 0; eregs->esp_cmd = (ESP_CMD_NULL | ESP_CMD_DMA); /* Flush the fifo of excess garbage. */ eregs->esp_cmd = ESP_CMD_FLUSH; /* Load bus-id and timeout values. */ eregs->esp_busid = (SCptr->target & 7); eregs->esp_timeo = esp->sync_defp; eregs->esp_soff = 0; /* This means async transfer... */ eregs->esp_stp = 0; /* Load FIFO with the actual SCSI command. */ for(i=0; i < SCptr->cmd_len; i++) eregs->esp_fdata = SCptr->cmnd[i]; /* Make sure the dvma forwards the ESP interrupt. */ dregs->cond_reg = DMA_INT_ENAB; /* Tell ESP to SELECT without asserting ATN. */ eregs->esp_cmd = ESP_CMD_SEL; return; bad: panic("esp: daaarrrkk starrr crashesss...."); } /* Queue a SCSI command delivered from the mid-level Linux SCSI code. */ int esp_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) { struct Sparc_ESP *esp; unsigned long flags; save_flags(flags); cli(); /* Set up func ptr and initial driver cmd-phase. */ SCpnt->scsi_done = done; SCpnt->SCp.phase = not_issued; esp = (struct Sparc_ESP *) SCpnt->host->hostdata; /* We use the scratch area. */ if(!SCpnt->use_sg) { SCpnt->SCp.this_residual = SCpnt->request_bufflen; SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->request_buffer; SCpnt->SCp.buffers_residual = 0; SCpnt->SCp.Status = CHECK_CONDITION; SCpnt->SCp.Message = 0; SCpnt->SCp.have_data_in = 0; SCpnt->SCp.sent_command = 0; SCpnt->SCp.ptr = mmu_get_scsi_one((char *)SCpnt->SCp.buffer, SCpnt->SCp.this_residual, esp->edev->my_bus); } else { #ifdef DEBUG_ESP_SG printk("esp: sglist at %p with %d buffers\n", SCpnt->buffer, SCpnt->use_sg); #endif SCpnt->SCp.buffer = (struct scatterlist *) SCpnt->buffer; SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1; SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; mmu_get_scsi_sgl((struct mmu_sglist *) SCpnt->SCp.buffer, SCpnt->SCp.buffers_residual, esp->edev->my_bus); SCpnt->SCp.ptr = (char *) SCpnt->SCp.buffer->alt_address; } /* Place into our queue. */ append_SC(&esp->issue_SC, SCpnt); /* Run it now if we can */ if(!esp->current_SC) esp_exec_cmd(esp); restore_flags(flags); return 0; } /* Only queuing supported in this ESP driver. */ int esp_command(Scsi_Cmnd *SCpnt) { ESPLOG(("esp: esp_command() called...\n")); return -1; } /* Abort a command. Those that are on the bus force a SCSI bus * reset. */ int esp_abort(Scsi_Cmnd *SCpnt) { ESPLOG(("esp_abort: Not implemented yet\n")); return SCSI_ABORT_ERROR; } /* Reset ESP chip, reset hanging bus, then kill active and * disconnected commands for targets without soft reset. */ int esp_reset(Scsi_Cmnd *SCptr, unsigned int how) { ESPLOG(("esp_reset: Not implemented yet\n")); return SCSI_RESET_ERROR; } /* Internal ESP done function. */ static inline void esp_done(struct Sparc_ESP *esp, int error) { unsigned long flags; Scsi_Cmnd *done_SC; if(esp->current_SC) { /* Critical section... */ save_flags(flags); cli(); done_SC = esp->current_SC; esp->current_SC = NULL; /* Free dvma entry. */ if(!done_SC->use_sg) { mmu_release_scsi_one(done_SC->SCp.ptr, done_SC->SCp.this_residual, esp->edev->my_bus); } else { struct scatterlist *scl = (struct scatterlist *)done_SC->buffer; #ifdef DEBUG_ESP_SG printk("esp: unmapping sg "); #endif mmu_release_scsi_sgl((struct mmu_sglist *) scl, done_SC->use_sg - 1, esp->edev->my_bus); #ifdef DEBUG_ESP_SG printk("done.\n"); #endif } done_SC->result = error; if(done_SC->scsi_done) done_SC->scsi_done(done_SC); else panic("esp: esp->current_SC->scsi_done() == NULL"); /* Bus is free, issue any commands in the queue. */ if(esp->issue_SC) esp_exec_cmd(esp); restore_flags(flags); /* End of critical section... */ } else panic("esp: done() called with NULL esp->current_SC"); } #ifdef THREADED_ESP_DRIVER /* planning stage... */ /* With multiple lots of commands being processed I frequently * see a situation where we see galloping esp herds. esp_done() * wakes the entire world up and each interrupt causes a reschedule. * This kernel thread fixes some of these unwanted effects during * IO intensive activity.... I hope... */ static void esp_kernel_thread(void *opaque) { struct Sparc_ESP *esp = opaque; for(;;) { unsigned long flags; while(esp->eatme_SC) { struct Scsi_Cmnd *SCpnt; SCpnt = remove_first_SC(esp->eatme_SC); esp_done(esp, error, SCpnt); } sleep(); } } #endif /* Read the interrupt status registers on this ESP board */ static inline void esp_updatesoft(struct Sparc_ESP *esp, struct Sparc_ESP_regs *eregs) { /* Update our software copies of the three ESP status * registers for this ESP. Be careful, reading the * ESP interrupt register clears the status and sequence * step registers (unlatches them, you get the idea). * So read the interrupt register last. */ esp->seqreg = eregs->esp_sstep; esp->sreg = eregs->esp_status; /* Supposedly, the ESP100A and above assert the highest * bit in the status register if an interrupt is pending. * I've never seen this work properly, so let's clear it * manually while we are here. If I see any esp chips * for which this bit is reliable I will conditionalize * this. However, I don't see what this extra bit can * buy me with all the tests I'll have to place all over * the code to actually use it when I 'can'. Plus the * 'pending interrupt' condition can more than reliably * be obtained from the DVMA control register. * * "Broken hardware" -Linus */ esp->sreg &= (~ESP_STAT_INTR); esp->ireg = eregs->esp_intrpt; /* Must be last or we lose */ } /* #define ESP_IRQ_TRACE */ #ifdef ESP_IRQ_TRACE #define ETRACE(foo) printk foo #else #define ETRACE(foo) #endif static char last_fflags, last_status, last_msg; /* Main interrupt handler for an esp adapter. */ static inline void esp_handle(struct Sparc_ESP *esp) { struct sparc_dma_registers *dregs; struct Sparc_ESP_regs *eregs; Scsi_Cmnd *SCptr; eregs = esp->eregs; dregs = esp->dregs; SCptr = esp->current_SC; DMA_IRQ_ENTRY(esp->dma, dregs); esp_updatesoft(esp, eregs); ETRACE(("ESPIRQ: <%2x,%2x,%2x> --> ", esp->ireg, esp->sreg, esp->seqreg)); /* Check for errors. */ if(!SCptr) panic("esp_handle: current_SC == penguin within interrupt!"); /* At this point in time, this esp driver should not see * scsibus resets, parity errors, or gross errors unless * something truly terrible happens which we are not ready * to properly recover from yet. */ if((esp->ireg & (ESP_INTR_SR | ESP_INTR_IC)) || (esp->sreg & (ESP_STAT_PERR | ESP_STAT_SPAM))) { printk("esp: really bad error detected\n"); printk("esp: intr<%2x> stat<%2x> seq<%2x>", esp->ireg, esp->sreg, esp->seqreg); printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase); panic("esp: cannot continue\n"); } if(dregs->cond_reg & DMA_HNDL_ERROR) { printk("esp: DMA shows an error cond_reg<%08lx> addr<%p>\n", dregs->cond_reg, dregs->st_addr); printk("esp: intr<%2x> stat<%2x> seq<%2x>", esp->ireg, esp->sreg, esp->seqreg); printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase); panic("esp: cannot continue\n"); } if(esp->sreg & ESP_STAT_PERR) { printk("esp: SCSI bus parity error\n"); printk("esp: intr<%2x> stat<%2x> seq<%2x>", esp->ireg, esp->sreg, esp->seqreg); printk("esp: SCptr->SCp.phase = %d\n", SCptr->SCp.phase); panic("esp: cannot continue\n"); } /* Service interrupt. */ switch(SCptr->SCp.phase) { case not_issued: panic("Unexpected ESP interrupt, current_SC not issued."); break; case in_selection: if(esp->ireg & ESP_INTR_RSEL) { /* XXX Some day XXX */ panic("ESP penguin reselected in async mode."); } else if(esp->ireg & ESP_INTR_DC) { /* Either we are scanning the bus and no-one * lives at this target or it didn't respond. */ ETRACE(("DISCONNECT\n")); #ifdef THREADED_ESP_DRIVER append_SC(esp->eatme_SC, esp->current_SC); esp->current_SC = 0; wake_up(esp_kernel_thread); #else esp_done(esp, (DID_NO_CONNECT << 16)); #endif goto esp_handle_done; } else if((esp->ireg & (ESP_INTR_FDONE | ESP_INTR_BSERV)) == (ESP_INTR_FDONE | ESP_INTR_BSERV)) { /* Selection successful, check the sequence step. */ /* XXX I know, I know... add error recovery. XXX */ switch(esp->seqreg & ESP_STEP_VBITS) { case ESP_STEP_NCMD: panic("esp: penguin didn't enter cmd phase."); break; case ESP_STEP_PPC: panic("esp: penguin prematurely changed from cmd phase."); break; case ESP_STEP_FINI: /* At the completion of every command * or message-out phase, we _must_ * unlatch the fifo-flags register * with an ESP nop command. */ eregs->esp_cmd = ESP_CMD_NULL; /* Selection/Command sequence completed. We * (at least for this driver) will be in * either one of the data phases or status * phase, check the status register to find * out. */ switch(esp->sreg & ESP_STAT_PMASK) { default: printk("esp: Not datain/dataout/status.\n"); panic("esp: penguin phase transition after selection."); break; case ESP_DOP: /* Data out phase. */ dregs->cond_reg |= DMA_FIFO_INV; while(dregs->cond_reg & DMA_FIFO_ISDRAIN) barrier(); SCptr->SCp.phase = in_dataout; #ifdef DEBUG_ESP_SG if(SCptr->use_sg) printk("esp: sg-start <%p,%d>", SCptr->SCp.ptr, SCptr->SCp.this_residual); #endif eregs->esp_tclow = SCptr->SCp.this_residual; eregs->esp_tcmed = (SCptr->SCp.this_residual>>8); eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL); /* This is either the one buffer dvma ptr, * or the first one in the scatter gather * list. Check out esp_queue to see how * this is set up. */ dregs->st_addr = SCptr->SCp.ptr; dregs->cond_reg &= ~(DMA_ST_WRITE); dregs->cond_reg |= (DMA_ENABLE | DMA_INT_ENAB); eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI); ETRACE(("DATA_OUT\n")); goto esp_handle_done; case ESP_DIP: /* Data in phase. */ dregs->cond_reg |= DMA_FIFO_INV; while(dregs->cond_reg & DMA_FIFO_ISDRAIN) barrier(); SCptr->SCp.phase = in_datain; #ifdef DEBUG_ESP_SG if(SCptr->use_sg) printk("esp: sg-start <%p,%d>", SCptr->SCp.ptr, SCptr->SCp.this_residual); #endif eregs->esp_tclow = SCptr->SCp.this_residual; eregs->esp_tcmed = (SCptr->SCp.this_residual>>8); eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL); /* This is either the one buffer dvma ptr, * or the first one in the scatter gather * list. Check out esp_queue to see how * this is set up. */ dregs->st_addr = SCptr->SCp.ptr; dregs->cond_reg |= (DMA_ENABLE | DMA_ST_WRITE | DMA_INT_ENAB); eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI); ETRACE(("DATA_IN\n")); goto esp_handle_done; case ESP_STATP: /* Status phase. */ SCptr->SCp.phase = in_status; eregs->esp_cmd = ESP_CMD_ICCSEQ; ETRACE(("STATUS\n")); goto esp_handle_done; /* Wait for message. */ }; }; } else if(esp->ireg & ESP_INTR_FDONE) { /* I'd like to investigate why this happens... */ ESPLOG(("esp: This is weird, halfway through ")); ESPLOG(("selection, trying to continue anyways.\n")); goto esp_handle_done; } else { panic("esp: Did not get bus service during selection."); goto esp_handle_done; } panic("esp: Mr. Potatoe Head is on the loose!"); case in_datain: /* Drain the fifo for writes to memory. */ switch(esp->dma->revision) { case dvmarev0: case dvmarev1: case dvmarevplus: case dvmarev2: case dvmarev3: /* Force a drain. */ dregs->cond_reg |= DMA_FIFO_STDRAIN; /* fall through */ case dvmaesc1: /* Wait for the fifo to drain completely. */ while(dregs->cond_reg & DMA_FIFO_ISDRAIN) barrier(); break; }; case in_dataout: dregs->cond_reg &= ~DMA_ENABLE; /* We may be pipelining an sg-list. */ if(SCptr->use_sg) { if(SCptr->SCp.buffers_residual) { /* If we do not see a BUS SERVICE interrupt * at this point, or we see that we have left * the current data phase, then we lose. */ if(!(esp->ireg & ESP_INTR_BSERV) || ((esp->sreg & ESP_STAT_PMASK) > 1)) panic("esp: Aiee penguin on the SCSI-bus."); ++SCptr->SCp.buffer; --SCptr->SCp.buffers_residual; SCptr->SCp.this_residual = SCptr->SCp.buffer->length; SCptr->SCp.ptr = SCptr->SCp.buffer->alt_address; #ifdef DEBUG_ESP_SG printk("<%p,%d> ", SCptr->SCp.ptr, SCptr->SCp.this_residual); #endif /* Latch in new esp counters... */ eregs->esp_tclow = SCptr->SCp.this_residual; eregs->esp_tcmed = (SCptr->SCp.this_residual>>8); eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_NULL); /* Reload DVMA gate array with new vaddr and enab. */ dregs->st_addr = SCptr->SCp.ptr; dregs->cond_reg |= DMA_ENABLE; /* Tell the esp to start transferring. */ eregs->esp_cmd = (ESP_CMD_DMA | ESP_CMD_TI); goto esp_handle_done; } #ifdef DEBUG_ESP_SG printk("done.\n"); #endif } /* Take a look at what happened. */ if(esp->ireg & ESP_INTR_DC) { panic("esp: target disconnects during data transfer."); goto esp_handle_done; } else if(esp->ireg & ESP_INTR_BSERV) { if((esp->sreg & ESP_STAT_PMASK) != ESP_STATP) { panic("esp: Not status phase after data phase."); goto esp_handle_done; } SCptr->SCp.phase = in_status; eregs->esp_cmd = ESP_CMD_ICCSEQ; ETRACE(("STATUS\n")); goto esp_handle_done; /* Wait for message. */ } else { printk("esp: did not get bus service after data transfer."); printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n", esp->ireg, esp->sreg, esp->seqreg); panic("esp: penguin data transfer."); goto esp_handle_done; } case in_status: if(esp->ireg & ESP_INTR_DC) { panic("esp: penguin disconnects in status phase."); goto esp_handle_done; } else if (esp->ireg & ESP_INTR_FDONE) { /* Status and Message now sit in the fifo for us. */ last_fflags = eregs->esp_fflags; SCptr->SCp.phase = in_finale; last_status = SCptr->SCp.Status = eregs->esp_fdata; last_msg = SCptr->SCp.Message = eregs->esp_fdata; eregs->esp_cmd = ESP_CMD_MOK; ETRACE(("FINALE\n")); goto esp_handle_done; } else { panic("esp: penguin status phase."); } case in_finale: if(esp->ireg & ESP_INTR_BSERV) { panic("esp: penguin doesn't disconnect after status msg-ack."); goto esp_handle_done; } else if(esp->ireg & ESP_INTR_DC) { /* Nexus is complete. */ #ifdef THREADED_ESP_DRIVER append_SC(esp->eatme_SC, esp->current_SC); esp->current_SC = 0; wake_up(esp_kernel_thread); #else esp_done(esp, ((SCptr->SCp.Status & 0xff) | ((SCptr->SCp.Message & 0xff) << 8) | (DID_OK << 16))); #endif ETRACE(("NEXUS_COMPLETE\n")); goto esp_handle_done; } else { printk("esp: wacky state while in in_finale phase.\n"); printk("esp_status: intr<%2x> stat<%2x> seq<%2x>\n", esp->ireg, esp->sreg, esp->seqreg); panic("esp: penguin esp state."); goto esp_handle_done; } default: panic("esp: detected penguin phase."); goto esp_handle_done; } panic("esp: Heading to the promised land."); esp_handle_done: DMA_IRQ_EXIT(esp->dma, dregs); return; } static void esp_intr(int irq, void *dev_id, struct pt_regs *pregs) { struct Sparc_ESP *esp; /* Handle all ESP interrupts showing */ for_each_esp(esp) { if(DMA_IRQ_P(esp->dregs)) { esp_handle(esp); } } }
Go to most recent revision | Compare with Previous | Blame | View Log