diff options
Diffstat (limited to 'drivers/scsi/aic7xxx.c')
-rw-r--r-- | drivers/scsi/aic7xxx.c | 272 |
1 files changed, 159 insertions, 113 deletions
diff --git a/drivers/scsi/aic7xxx.c b/drivers/scsi/aic7xxx.c index 812870cda..760dcc75b 100644 --- a/drivers/scsi/aic7xxx.c +++ b/drivers/scsi/aic7xxx.c @@ -269,7 +269,7 @@ struct proc_dir_entry proc_scsi_aic7xxx = { 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; -#define AIC7XXX_C_VERSION "5.1.19" +#define AIC7XXX_C_VERSION "5.1.20" #define NUMBER(arr) (sizeof(arr) / sizeof(arr[0])) #define MIN(a,b) (((a) < (b)) ? (a) : (b)) @@ -878,13 +878,14 @@ typedef enum { AHC_SG_PRELOAD = 0x0080, AHC_SPIOCAP = 0x0100, AHC_ULTRA3 = 0x0200, + AHC_NEW_AUTOTERM = 0x0400, AHC_AIC7770_FE = AHC_FENONE, AHC_AIC7850_FE = AHC_SPIOCAP, AHC_AIC7860_FE = AHC_ULTRA|AHC_SPIOCAP, AHC_AIC7870_FE = AHC_FENONE, AHC_AIC7880_FE = AHC_ULTRA, AHC_AIC7890_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA2| - AHC_QUEUE_REGS|AHC_SG_PRELOAD, + AHC_QUEUE_REGS|AHC_SG_PRELOAD|AHC_NEW_AUTOTERM, AHC_AIC7895_FE = AHC_MORE_SRAM|AHC_CMD_CHAN|AHC_ULTRA, AHC_AIC7896_FE = AHC_AIC7890_FE, AHC_AIC7892_FE = AHC_AIC7890_FE|AHC_ULTRA3, @@ -1351,7 +1352,14 @@ static int aic7xxx_dump_sequencer = 0; * would result in never finding any devices :) */ static int aic7xxx_no_probe = 0; - +/* + * On some machines, enabling the external SCB RAM isn't reliable yet. I + * haven't had time to make test patches for things like changing the + * timing mode on that external RAM either. Some of those changes may + * fix the problem. Until then though, we default to external SCB RAM + * off and give a command line option to enable it. + */ +static int aic7xxx_scbram = 0; /* * So that insmod can find the variable and make it point to something */ @@ -1450,13 +1458,12 @@ aic_inb(struct aic7xxx_host *p, long port) unsigned char x; if(p->maddr) { - x = p->maddr[port]; + x = readb(p->maddr + port); } else { x = inb(p->base + port); } - mb(); return(x); #else return(inb(p->base + port)); @@ -1469,7 +1476,7 @@ aic_outb(struct aic7xxx_host *p, unsigned char val, long port) #ifdef MMAPIO if(p->maddr) { - p->maddr[port] = val; + writeb(val, p->maddr + port); } else { @@ -1513,6 +1520,7 @@ aic7xxx_setup(char *s, int *dummy) { "pci_parity", &aic7xxx_pci_parity }, { "dump_card", &aic7xxx_dump_card }, { "dump_sequencer", &aic7xxx_dump_sequencer }, + { "scbram", &aic7xxx_scbram }, { "tag_info", NULL } }; @@ -6193,7 +6201,7 @@ aic7xxx_handle_scsiint(struct aic7xxx_host *p, unsigned char intstat) cmd->result = 0; scb = NULL; } - if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) + else if (scb->cmd == p->dev_dtr_cmnd[TARGET_INDEX(scb->cmd)]) { /* * Turn off the needsdtr, needwdtr, and needppr bits since this device @@ -6541,6 +6549,105 @@ aic7xxx_check_scbs(struct aic7xxx_host *p, char *buffer) } #endif + +/*+F************************************************************************* + * Function: + * aic7xxx_handle_command_completion_intr + * + * Description: + * SCSI command completion interrupt handler. + *-F*************************************************************************/ +static void +aic7xxx_handle_command_completion_intr(struct aic7xxx_host *p) +{ + struct aic7xxx_scb *scb = NULL; + Scsi_Cmnd *cmd; + unsigned char scb_index; + +#ifdef AIC7XXX_VERBOSE_DEBUGGING + if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) + printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1); +#endif + + /* + * Read the INTSTAT location after clearing the CMDINT bit. This forces + * any posted PCI writes to flush to memory. Gerard Roudier suggested + * this fix to the possible race of clearing the CMDINT bit but not + * having all command bytes flushed onto the qoutfifo. + */ + aic_outb(p, CLRCMDINT, CLRINT); + aic_inb(p, INTSTAT); + /* + * The sequencer will continue running when it + * issues this interrupt. There may be >1 commands + * finished, so loop until we've processed them all. + */ + + while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL) + { + scb_index = p->qoutfifo[p->qoutfifonext]; + p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL; + if ( scb_index >= p->scb_data->numscbs ) + scb = NULL; + else + scb = p->scb_data->scb_array[scb_index]; + if (scb == NULL) + { + printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no, + -1, -1, -1, scb_index); + continue; + } + else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) + { + printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags " + "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags, + (unsigned long) scb->cmd); + continue; + } + else if (scb->flags & SCB_QUEUED_ABORT) + { + pause_sequencer(p); + if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) && + (aic_inb(p, SCB_TAG) == scb->hscb->tag) ) + { + unpause_sequencer(p, FALSE); + continue; + } + aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel, + scb->cmd->lun, scb->hscb->tag); + scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT | + SCB_QUEUED_ABORT); + unpause_sequencer(p, FALSE); + } + else if (scb->flags & SCB_ABORT) + { + /* + * We started to abort this, but it completed on us, let it + * through as successful + */ + scb->flags &= ~(SCB_ABORT|SCB_RESET); + } + switch (status_byte(scb->hscb->target_status)) + { + case QUEUE_FULL: + case BUSY: + scb->hscb->target_status = 0; + scb->cmd->result = 0; + aic7xxx_error(scb->cmd) = DID_OK; + break; + default: + cmd = scb->cmd; + if (scb->hscb->residual_SG_segment_count != 0) + { + aic7xxx_calculate_residual(p, scb); + } + cmd->result |= (aic7xxx_error(cmd) << 16); + aic7xxx_done(p, scb); + break; + } + } +} + /*+F************************************************************************* * Function: * aic7xxx_isr @@ -6600,95 +6707,7 @@ aic7xxx_isr(int irq, void *dev_id, struct pt_regs *regs) */ if (intstat & CMDCMPLT) { - struct aic7xxx_scb *scb = NULL; - Scsi_Cmnd *cmd; - unsigned char scb_index; - -#ifdef AIC7XXX_VERBOSE_DEBUGGING - if( (p->isr_count < 16) && (aic7xxx_verbose > 0xffff) ) - printk(INFO_LEAD "Command Complete Int.\n", p->host_no, -1, -1, -1); -#endif - - /* - * Clear interrupt status before running the completion loop. - * This eliminates a race condition whereby a command could - * complete between the last check of qoutfifo and the - * CLRCMDINT statement. This would result in us thinking the - * qoutfifo was empty when it wasn't, and in actuality be a lost - * completion interrupt. With multiple devices or tagged queueing - * this could be very bad if we caught all but the last completion - * and no more are imediately sent. - */ - aic_outb(p, CLRCMDINT, CLRINT); - /* - * The sequencer will continue running when it - * issues this interrupt. There may be >1 commands - * finished, so loop until we've processed them all. - */ - - while (p->qoutfifo[p->qoutfifonext] != SCB_LIST_NULL) - { - scb_index = p->qoutfifo[p->qoutfifonext]; - p->qoutfifo[p->qoutfifonext++] = SCB_LIST_NULL; - if ( scb_index >= p->scb_data->numscbs ) - scb = NULL; - else - scb = p->scb_data->scb_array[scb_index]; - if (scb == NULL) - { - printk(WARN_LEAD "CMDCMPLT with invalid SCB index %d\n", p->host_no, - -1, -1, -1, scb_index); - continue; - } - else if (!(scb->flags & SCB_ACTIVE) || (scb->cmd == NULL)) - { - printk(WARN_LEAD "CMDCMPLT without command for SCB %d, SCB flags " - "0x%x, cmd 0x%lx\n", p->host_no, -1, -1, -1, scb_index, scb->flags, - (unsigned long) scb->cmd); - continue; - } - else if (scb->flags & SCB_QUEUED_ABORT) - { - pause_sequencer(p); - if ( ((aic_inb(p, LASTPHASE) & PHASE_MASK) != P_BUSFREE) && - (aic_inb(p, SCB_TAG) == scb->hscb->tag) ) - { - unpause_sequencer(p, FALSE); - continue; - } - aic7xxx_reset_device(p, scb->cmd->target, scb->cmd->channel, - scb->cmd->lun, scb->hscb->tag); - scb->flags &= ~(SCB_QUEUED_FOR_DONE | SCB_RESET | SCB_ABORT | - SCB_QUEUED_ABORT); - unpause_sequencer(p, FALSE); - } - else if (scb->flags & SCB_ABORT) - { - /* - * We started to abort this, but it completed on us, let it - * through as successful - */ - scb->flags &= ~(SCB_ABORT|SCB_RESET); - } - switch (status_byte(scb->hscb->target_status)) - { - case QUEUE_FULL: - case BUSY: - scb->hscb->target_status = 0; - scb->cmd->result = 0; - aic7xxx_error(scb->cmd) = DID_OK; - break; - default: - cmd = scb->cmd; - if (scb->hscb->residual_SG_segment_count != 0) - { - aic7xxx_calculate_residual(p, scb); - } - cmd->result |= (aic7xxx_error(cmd) << 16); - aic7xxx_done(p, scb); - break; - } - } + aic7xxx_handle_command_completion_intr(p); } if (intstat & BRKADRINT) @@ -7619,9 +7638,10 @@ configure_termination(struct aic7xxx_host *p) aic_outb(p, SEEMS | SEECS, SEECTL); sxfrctl1 &= ~STPWEN; if ( (p->adapter_control & CFAUTOTERM) || - (p->features & AHC_ULTRA2) ) + (p->features & AHC_NEW_AUTOTERM) ) { - if ( (p->adapter_control & CFAUTOTERM) && !(p->features & AHC_ULTRA2) ) + if ( (p->adapter_control & CFAUTOTERM) && + !(p->features & AHC_NEW_AUTOTERM) ) { printk(KERN_INFO "(scsi%d) Warning - detected auto-termination\n", p->host_no); @@ -7635,7 +7655,7 @@ configure_termination(struct aic7xxx_host *p) } /* Configure auto termination. */ - if (p->features & AHC_ULTRA2) + if (p->features & AHC_NEW_AUTOTERM) { if (aic7xxx_override_term == -1) aic7xxx_ultra2_term_detect(p, &enableSE_low, &enableSE_high, @@ -7668,7 +7688,7 @@ configure_termination(struct aic7xxx_host *p) if (max_target <= 8) internal68_present = 0; - if ( !(p->features & AHC_ULTRA2) ) + if ( !(p->features & AHC_NEW_AUTOTERM) ) { if (max_target > 8) { @@ -7698,7 +7718,7 @@ configure_termination(struct aic7xxx_host *p) * SE Low Term Enable = BRDDAT5 (7890) * LVD High Term Enable = BRDDAT4 (7890) */ - if ( !(p->features & AHC_ULTRA2) && + if ( !(p->features & AHC_NEW_AUTOTERM) && (internal50_present && internal68_present && external_present) ) { printk(KERN_INFO "(scsi%d) Illegal cable configuration!! Only two\n", @@ -7731,7 +7751,7 @@ configure_termination(struct aic7xxx_host *p) (external_present ? 1 : 0)) <= 1) || (enableSE_low != 0) ) { - if (p->features & AHC_ULTRA2) + if (p->features & AHC_NEW_AUTOTERM) brddat |= BRDDAT5; else sxfrctl1 |= STPWEN; @@ -7762,7 +7782,7 @@ configure_termination(struct aic7xxx_host *p) { if (p->adapter_control & CFSTERM) { - if (p->features & AHC_ULTRA2) + if (p->features & AHC_NEW_AUTOTERM) brddat |= BRDDAT5; else sxfrctl1 |= STPWEN; @@ -9409,7 +9429,7 @@ aic7xxx_detect(Scsi_Host_Template *template) AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7887, AHC_AIC7880, - AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, + AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE | AHC_NEW_AUTOTERM, 18, 32, C46 }, {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7888, AHC_AIC7880, AHC_PAGESCBS | AHC_BIOS_ENABLED, AHC_AIC7880_FE, 18, @@ -9539,7 +9559,7 @@ aic7xxx_detect(Scsi_Host_Template *template) temp_p->base = pdev->resource[0].start; temp_p->mbase = pdev->resource[1].start; current_p = list_p; - while(current_p) + while(current_p && temp_p) { if ( ((current_p->pci_bus == temp_p->pci_bus) && (current_p->pci_device_fn == temp_p->pci_device_fn)) || @@ -9958,7 +9978,8 @@ aic7xxx_detect(Scsi_Host_Template *template) #endif if (temp_p->features & AHC_ULTRA2) { - if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) + if ( (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) && + (aic7xxx_scbram) ) { aic_outb(temp_p, aic_inb(temp_p, DSCOMMAND0) & ~SCBRAMSEL_ULTRA2, @@ -9966,12 +9987,33 @@ aic7xxx_detect(Scsi_Host_Template *template) temp_p->flags |= AHC_EXTERNAL_SRAM; devconfig |= EXTSCBPEN; } + else if (aic_inb(temp_p, DSCOMMAND0) & RAMPSM_ULTRA2) + { + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: external SCB RAM detected, " + "but not enabled\n"); + } } - else if (devconfig & RAMPSM) + else { - devconfig &= ~SCBRAMSEL; - devconfig |= EXTSCBPEN; - temp_p->flags |= AHC_EXTERNAL_SRAM; + if ((devconfig & RAMPSM) && (aic7xxx_scbram)) + { + devconfig &= ~SCBRAMSEL; + devconfig |= EXTSCBPEN; + temp_p->flags |= AHC_EXTERNAL_SRAM; + } + else if (devconfig & RAMPSM) + { + printk(KERN_INFO "aic7xxx: <%s> at PCI %d/%d\n", + board_names[aic_pdevs[i].board_name_index], + PCI_SLOT(temp_p->pci_device_fn), + PCI_FUNC(temp_p->pci_device_fn)); + printk("aic7xxx: external SCB RAM detected, " + "but not enabled\n"); + } } #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,92) pci_write_config_dword(pdev, DEVCONFIG, devconfig); @@ -11588,13 +11630,17 @@ aic7xxx_reset(Scsi_Cmnd *cmd, unsigned int flags) if ( aic7xxx_scb_on_qoutfifo(p, scb) ) { if(aic7xxx_verbose & VERBOSE_RESET_RETURN) - printk(INFO_LEAD "SCB on qoutfifo, returning.\n", p->host_no, + printk(INFO_LEAD "SCB on qoutfifo, completing.\n", p->host_no, CTL_OF_SCB(scb)); - aic7xxx_run_done_queue(p, TRUE); + if ((aic_inb(p,INTSTAT) & CMDCMPLT) == 0) + printk(INFO_LEAD "missed CMDCMPLT interrupt!\n", p->host_no, + CTL_OF_SCB(scb)); + aic7xxx_handle_command_completion_intr(p); + aic7xxx_done_cmds_complete(p); aic7xxx_run_waiting_queues(p); unpause_sequencer(p, FALSE); DRIVER_UNLOCK - return(SCSI_RESET_NOT_RUNNING); + return(SCSI_RESET_SUCCESS); } if ( flags & SCSI_RESET_SUGGEST_HOST_RESET ) { |