diff options
Diffstat (limited to 'drivers/scsi/lpfc/lpfc_sli.c')
-rw-r--r-- | drivers/scsi/lpfc/lpfc_sli.c | 311 |
1 files changed, 204 insertions, 107 deletions
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 1f876328b44..d6ffe26ae12 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2004-2005 Emulex. All rights reserved. * + * Copyright (C) 2004-2006 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * @@ -513,7 +513,9 @@ lpfc_sli_chk_mbx_command(uint8_t mbxCommand) case MBX_SET_MASK: case MBX_SET_SLIM: case MBX_UNREG_D_ID: + case MBX_KILL_BOARD: case MBX_CONFIG_FARP: + case MBX_BEACON: case MBX_LOAD_AREA: case MBX_RUN_BIU_DIAG64: case MBX_CONFIG_PORT: @@ -1512,98 +1514,162 @@ lpfc_sli_abort_iocb_ring(struct lpfc_hba *phba, struct lpfc_sli_ring *pring) return errcnt; } -/****************************************************************************** -* lpfc_sli_send_reset -* -* Note: After returning from this function, the HBA cannot be accessed for -* 1 ms. Since we do not wish to delay in interrupt context, it is the -* responsibility of the caller to perform the mdelay(1) and flush via readl(). -******************************************************************************/ -static int -lpfc_sli_send_reset(struct lpfc_hba * phba, uint16_t skip_post) +int +lpfc_sli_brdready(struct lpfc_hba * phba, uint32_t mask) { - MAILBOX_t *swpmb; - volatile uint32_t word0; - void __iomem *to_slim; - unsigned long flags = 0; + uint32_t status; + int i = 0; + int retval = 0; - spin_lock_irqsave(phba->host->host_lock, flags); + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); - /* A board reset must use REAL SLIM. */ - phba->sli.sli_flag &= ~LPFC_SLI2_ACTIVE; + /* + * Check status register every 100ms for 5 retries, then every + * 500ms for 5, then every 2.5 sec for 5, then reset board and + * every 2.5 sec for 4. + * Break our of the loop if errors occurred during init. + */ + while (((status & mask) != mask) && + !(status & HS_FFERM) && + i++ < 20) { - word0 = 0; - swpmb = (MAILBOX_t *) & word0; - swpmb->mbxCommand = MBX_RESTART; - swpmb->mbxHc = 1; + if (i <= 5) + msleep(10); + else if (i <= 10) + msleep(500); + else + msleep(2500); - to_slim = phba->MBslimaddr; - writel(*(uint32_t *) swpmb, to_slim); - readl(to_slim); /* flush */ + if (i == 15) { + phba->hba_state = LPFC_STATE_UNKNOWN; /* Do post */ + lpfc_sli_brdrestart(phba); + } + /* Read the HBA Host Status Register */ + status = readl(phba->HSregaddr); + } - /* Only skip post after fc_ffinit is completed */ - if (skip_post) { - word0 = 1; /* This is really setting up word1 */ - } else { - word0 = 0; /* This is really setting up word1 */ + /* Check to see if any errors occurred during init */ + if ((status & HS_FFERM) || (i >= 20)) { + phba->hba_state = LPFC_HBA_ERROR; + retval = 1; } - to_slim = phba->MBslimaddr + sizeof (uint32_t); - writel(*(uint32_t *) swpmb, to_slim); - readl(to_slim); /* flush */ - /* Turn off parity checking and serr during the physical reset */ - pci_read_config_word(phba->pcidev, PCI_COMMAND, &phba->pci_cfg_value); - pci_write_config_word(phba->pcidev, PCI_COMMAND, - (phba->pci_cfg_value & - ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR))); + return retval; +} - writel(HC_INITFF, phba->HCregaddr); +int +lpfc_sli_brdkill(struct lpfc_hba * phba) +{ + struct lpfc_sli *psli; + LPFC_MBOXQ_t *pmb; + uint32_t status; + uint32_t ha_copy; + int retval; + int i = 0; - phba->hba_state = LPFC_INIT_START; - spin_unlock_irqrestore(phba->host->host_lock, flags); + psli = &phba->sli; - return 0; + /* Kill HBA */ + lpfc_printf_log(phba, + KERN_INFO, + LOG_SLI, + "%d:0329 Kill HBA Data: x%x x%x\n", + phba->brd_no, + phba->hba_state, + psli->sli_flag); + + if ((pmb = (LPFC_MBOXQ_t *) mempool_alloc(phba->mbox_mem_pool, + GFP_ATOMIC)) == 0) { + return 1; + } + + /* Disable the error attention */ + spin_lock_irq(phba->host->host_lock); + status = readl(phba->HCregaddr); + status &= ~HC_ERINT_ENA; + writel(status, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + spin_unlock_irq(phba->host->host_lock); + + lpfc_kill_board(phba, pmb); + pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; + retval = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT); + + if (retval != MBX_SUCCESS) { + if (retval != MBX_BUSY) + mempool_free(pmb, phba->mbox_mem_pool); + return 1; + } + + mempool_free(pmb, phba->mbox_mem_pool); + + /* There is no completion for a KILL_BOARD mbox cmd. Check for an error + * attention every 100ms for 3 seconds. If we don't get ERATT after + * 3 seconds we still set HBA_ERROR state because the status of the + * board is now undefined. + */ + ha_copy = readl(phba->HAregaddr); + + while ((i++ < 30) && !(ha_copy & HA_ERATT)) { + mdelay(100); + ha_copy = readl(phba->HAregaddr); + } + + del_timer_sync(&psli->mbox_tmo); + + spin_lock_irq(phba->host->host_lock); + psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; + spin_unlock_irq(phba->host->host_lock); + + psli->mbox_active = NULL; + lpfc_hba_down_post(phba); + phba->hba_state = LPFC_HBA_ERROR; + + return (ha_copy & HA_ERATT ? 0 : 1); } -static int -lpfc_sli_brdreset(struct lpfc_hba * phba, uint16_t skip_post) +int +lpfc_sli_brdreset(struct lpfc_hba * phba) { + struct lpfc_sli *psli; struct lpfc_sli_ring *pring; + uint16_t cfg_value; int i; - struct lpfc_dmabuf *mp, *next_mp; - unsigned long flags = 0; - - lpfc_sli_send_reset(phba, skip_post); - mdelay(1); - spin_lock_irqsave(phba->host->host_lock, flags); - /* Risk the write on flush case ie no delay after the readl */ - readl(phba->HCregaddr); /* flush */ - /* Now toggle INITFF bit set by lpfc_sli_send_reset */ - writel(0, phba->HCregaddr); - readl(phba->HCregaddr); /* flush */ + psli = &phba->sli; - /* Restore PCI cmd register */ - pci_write_config_word(phba->pcidev, PCI_COMMAND, phba->pci_cfg_value); + /* Reset HBA */ + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "%d:0325 Reset HBA Data: x%x x%x\n", phba->brd_no, + phba->hba_state, psli->sli_flag); /* perform board reset */ phba->fc_eventTag = 0; phba->fc_myDID = 0; - phba->fc_prevDID = Mask_DID; + phba->fc_prevDID = 0; - /* Reset HBA */ - lpfc_printf_log(phba, - KERN_INFO, - LOG_SLI, - "%d:0325 Reset HBA Data: x%x x%x x%x\n", - phba->brd_no, - phba->hba_state, - phba->sli.sli_flag, - skip_post); + psli->sli_flag = 0; + + /* Turn off parity checking and serr during the physical reset */ + pci_read_config_word(phba->pcidev, PCI_COMMAND, &cfg_value); + pci_write_config_word(phba->pcidev, PCI_COMMAND, + (cfg_value & + ~(PCI_COMMAND_PARITY | PCI_COMMAND_SERR))); + + /* Now toggle INITFF bit in the Host Control Register */ + writel(HC_INITFF, phba->HCregaddr); + mdelay(1); + readl(phba->HCregaddr); /* flush */ + writel(0, phba->HCregaddr); + readl(phba->HCregaddr); /* flush */ + + /* Restore PCI cmd register */ + pci_write_config_word(phba->pcidev, PCI_COMMAND, cfg_value); /* Initialize relevant SLI info */ - for (i = 0; i < phba->sli.num_rings; i++) { - pring = &phba->sli.ring[i]; + for (i = 0; i < psli->num_rings; i++) { + pring = &psli->ring[i]; pring->flag = 0; pring->rspidx = 0; pring->next_cmdidx = 0; @@ -1611,27 +1677,62 @@ lpfc_sli_brdreset(struct lpfc_hba * phba, uint16_t skip_post) pring->cmdidx = 0; pring->missbufcnt = 0; } - spin_unlock_irqrestore(phba->host->host_lock, flags); - if (skip_post) { - mdelay(100); + phba->hba_state = LPFC_WARM_START; + return 0; +} + +int +lpfc_sli_brdrestart(struct lpfc_hba * phba) +{ + MAILBOX_t *mb; + struct lpfc_sli *psli; + uint16_t skip_post; + volatile uint32_t word0; + void __iomem *to_slim; + + spin_lock_irq(phba->host->host_lock); + + psli = &phba->sli; + + /* Restart HBA */ + lpfc_printf_log(phba, KERN_INFO, LOG_SLI, + "%d:0328 Restart HBA Data: x%x x%x\n", phba->brd_no, + phba->hba_state, psli->sli_flag); + + word0 = 0; + mb = (MAILBOX_t *) &word0; + mb->mbxCommand = MBX_RESTART; + mb->mbxHc = 1; + + to_slim = phba->MBslimaddr; + writel(*(uint32_t *) mb, to_slim); + readl(to_slim); /* flush */ + + /* Only skip post after fc_ffinit is completed */ + if (phba->hba_state) { + skip_post = 1; + word0 = 1; /* This is really setting up word1 */ } else { - mdelay(2000); + skip_post = 0; + word0 = 0; /* This is really setting up word1 */ } + to_slim = (uint8_t *) phba->MBslimaddr + sizeof (uint32_t); + writel(*(uint32_t *) mb, to_slim); + readl(to_slim); /* flush */ - spin_lock_irqsave(phba->host->host_lock, flags); - /* Cleanup preposted buffers on the ELS ring */ - pring = &phba->sli.ring[LPFC_ELS_RING]; - list_for_each_entry_safe(mp, next_mp, &pring->postbufq, list) { - list_del(&mp->list); - pring->postbufq_cnt--; - lpfc_mbuf_free(phba, mp->virt, mp->phys); - kfree(mp); - } - spin_unlock_irqrestore(phba->host->host_lock, flags); + lpfc_sli_brdreset(phba); - for (i = 0; i < phba->sli.num_rings; i++) - lpfc_sli_abort_iocb_ring(phba, &phba->sli.ring[i]); + phba->hba_state = LPFC_INIT_START; + + spin_unlock_irq(phba->host->host_lock); + + if (skip_post) + mdelay(100); + else + mdelay(2000); + + lpfc_hba_down_post(phba); return 0; } @@ -1691,7 +1792,8 @@ lpfc_sli_chipset_init(struct lpfc_hba *phba) } if (i == 15) { - lpfc_sli_brdreset(phba, 0); + phba->hba_state = LPFC_STATE_UNKNOWN; /* Do post */ + lpfc_sli_brdrestart(phba); } /* Read the HBA Host Status Register */ status = readl(phba->HSregaddr); @@ -1735,8 +1837,8 @@ lpfc_sli_hba_setup(struct lpfc_hba * phba) } while (resetcount < 2 && !done) { - phba->hba_state = 0; - lpfc_sli_brdreset(phba, 0); + phba->hba_state = LPFC_STATE_UNKNOWN; + lpfc_sli_brdrestart(phba); msleep(2500); rc = lpfc_sli_chipset_init(phba); if (rc) @@ -1920,6 +2022,14 @@ lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) mb = &pmbox->mb; status = MBX_SUCCESS; + if (phba->hba_state == LPFC_HBA_ERROR) { + spin_unlock_irqrestore(phba->host->host_lock, drvr_flag); + + /* Mbox command <mbxCommand> cannot issue */ + LOG_MBOX_CANNOT_ISSUE_DATA( phba, mb, psli, flag) + return (MBX_NOT_FINISHED); + } + if (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE) { /* Polling for a mbox command when another one is already active * is not allowed in SLI. Also, the driver must have established @@ -2002,7 +2112,8 @@ lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) /* If we are not polling, we MUST be in SLI2 mode */ if (flag != MBX_POLL) { - if (!(psli->sli_flag & LPFC_SLI2_ACTIVE)) { + if (!(psli->sli_flag & LPFC_SLI2_ACTIVE) && + (mb->mbxCommand != MBX_KILL_BOARD)) { psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; spin_unlock_irqrestore(phba->host->host_lock, drvr_flag); @@ -2035,7 +2146,8 @@ lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) /* First copy command data to host SLIM area */ lpfc_sli_pcimem_bcopy(mb, &phba->slim2p->mbx, MAILBOX_CMD_SIZE); } else { - if (mb->mbxCommand == MBX_CONFIG_PORT) { + if (mb->mbxCommand == MBX_CONFIG_PORT || + mb->mbxCommand == MBX_KILL_BOARD) { /* copy command data into host mbox for cmpl */ lpfc_sli_pcimem_bcopy(mb, &phba->slim2p->mbx, MAILBOX_CMD_SIZE); @@ -2086,8 +2198,9 @@ lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) ha_copy = readl(phba->HAregaddr); /* Wait for command to complete */ - while (((word0 & OWN_CHIP) == OWN_CHIP) - || !(ha_copy & HA_MBATT)) { + while (((word0 & OWN_CHIP) == OWN_CHIP) || + (!(ha_copy & HA_MBATT) && + (phba->hba_state > LPFC_WARM_START))) { if (i++ >= 100) { psli->sli_flag &= ~LPFC_SLI_MBOX_ACTIVE; spin_unlock_irqrestore(phba->host->host_lock, @@ -2455,15 +2568,6 @@ lpfc_sli_hba_down(struct lpfc_hba * phba) spin_unlock_irqrestore(phba->host->host_lock, flags); - /* - * Provided the hba is not in an error state, reset it. It is not - * capable of IO anymore. - */ - if (phba->hba_state != LPFC_HBA_ERROR) { - phba->hba_state = LPFC_INIT_START; - lpfc_sli_brdreset(phba, 1); - } - return 1; } @@ -2976,13 +3080,6 @@ lpfc_intr_handler(int irq, void *dev_id, struct pt_regs * regs) /* Clear Chip error bit */ writel(HA_ERATT, phba->HAregaddr); readl(phba->HAregaddr); /* flush */ - - /* - * Reseting the HBA is the only reliable way - * to shutdown interrupt when there is a - * ERROR. - */ - lpfc_sli_send_reset(phba, 1); } spin_lock(phba->host->host_lock); |