📄 lpfc_init.c
字号:
/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2004-2005 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * * Public License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID. See the GNU General Public License for * * more details, a copy of which can be found in the file COPYING * * included with this package. * *******************************************************************/#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/dma-mapping.h>#include <linux/idr.h>#include <linux/interrupt.h>#include <linux/kthread.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport_fc.h>#include "lpfc_hw.h"#include "lpfc_sli.h"#include "lpfc_disc.h"#include "lpfc_scsi.h"#include "lpfc.h"#include "lpfc_logmsg.h"#include "lpfc_crtn.h"#include "lpfc_version.h"static int lpfc_parse_vpd(struct lpfc_hba *, uint8_t *);static void lpfc_get_hba_model_desc(struct lpfc_hba *, uint8_t *, uint8_t *);static int lpfc_post_rcv_buf(struct lpfc_hba *);static struct scsi_transport_template *lpfc_transport_template = NULL;static DEFINE_IDR(lpfc_hba_index);/************************************************************************//* *//* lpfc_config_port_prep *//* This routine will do LPFC initialization prior to the *//* CONFIG_PORT mailbox command. This will be initialized *//* as a SLI layer callback routine. *//* This routine returns 0 on success or -ERESTART if it wants *//* the SLI layer to reset the HBA and try again. Any *//* other return value indicates an error. *//* *//************************************************************************/intlpfc_config_port_prep(struct lpfc_hba * phba){ lpfc_vpd_t *vp = &phba->vpd; int i = 0, rc; LPFC_MBOXQ_t *pmb; MAILBOX_t *mb; char *lpfc_vpd_data = NULL; uint16_t offset = 0; static char licensed[56] = "key unlock for use with gnu public licensed code only\0"; pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmb) { phba->hba_state = LPFC_HBA_ERROR; return -ENOMEM; } mb = &pmb->mb; phba->hba_state = LPFC_INIT_MBX_CMDS; if (lpfc_is_LC_HBA(phba->pcidev->device)) { uint32_t *ptext = (uint32_t *) licensed; for (i = 0; i < 56; i += sizeof (uint32_t), ptext++) *ptext = cpu_to_be32(*ptext); lpfc_read_nv(phba, pmb); memset((char*)mb->un.varRDnvp.rsvd3, 0, sizeof (mb->un.varRDnvp.rsvd3)); memcpy((char*)mb->un.varRDnvp.rsvd3, licensed, sizeof (licensed)); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_MBOX, "%d:0324 Config Port initialization " "error, mbxCmd x%x READ_NVPARM, " "mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); mempool_free(pmb, phba->mbox_mem_pool); return -ERESTART; } memcpy(phba->wwnn, (char *)mb->un.varRDnvp.nodename, sizeof (mb->un.varRDnvp.nodename)); } /* Setup and issue mailbox READ REV command */ lpfc_read_rev(phba, pmb); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0439 Adapter failed to init, mbxCmd x%x " "READ_REV, mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); mempool_free( pmb, phba->mbox_mem_pool); return -ERESTART; } /* The HBA's current state is provided by the ProgType and rr fields. * Read and check the value of these fields before continuing to config * this port. */ if (mb->un.varRdRev.rr == 0 || mb->un.varRdRev.un.b.ProgType != 2) { /* Old firmware */ vp->rev.rBit = 0; lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0440 Adapter failed to init, mbxCmd x%x " "READ_REV detected outdated firmware" "Data: x%x\n", phba->brd_no, mb->mbxCommand, 0); mempool_free(pmb, phba->mbox_mem_pool); return -ERESTART; } else { vp->rev.rBit = 1; vp->rev.sli1FwRev = mb->un.varRdRev.sli1FwRev; memcpy(vp->rev.sli1FwName, (char*)mb->un.varRdRev.sli1FwName, 16); vp->rev.sli2FwRev = mb->un.varRdRev.sli2FwRev; memcpy(vp->rev.sli2FwName, (char *)mb->un.varRdRev.sli2FwName, 16); } /* Save information as VPD data */ vp->rev.biuRev = mb->un.varRdRev.biuRev; vp->rev.smRev = mb->un.varRdRev.smRev; vp->rev.smFwRev = mb->un.varRdRev.un.smFwRev; vp->rev.endecRev = mb->un.varRdRev.endecRev; vp->rev.fcphHigh = mb->un.varRdRev.fcphHigh; vp->rev.fcphLow = mb->un.varRdRev.fcphLow; vp->rev.feaLevelHigh = mb->un.varRdRev.feaLevelHigh; vp->rev.feaLevelLow = mb->un.varRdRev.feaLevelLow; vp->rev.postKernRev = mb->un.varRdRev.postKernRev; vp->rev.opFwRev = mb->un.varRdRev.opFwRev; if (lpfc_is_LC_HBA(phba->pcidev->device)) memcpy(phba->RandomData, (char *)&mb->un.varWords[24], sizeof (phba->RandomData)); /* Get the default values for Model Name and Description */ lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc); /* Get adapter VPD information */ pmb->context2 = kmalloc(DMP_RSP_SIZE, GFP_KERNEL); if (!pmb->context2) goto out_free_mbox; lpfc_vpd_data = kmalloc(DMP_VPD_SIZE, GFP_KERNEL); if (!lpfc_vpd_data) goto out_free_context2; do { lpfc_dump_mem(phba, pmb, offset); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_INFO, LOG_INIT, "%d:0441 VPD not present on adapter, " "mbxCmd x%x DUMP VPD, mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); kfree(lpfc_vpd_data); lpfc_vpd_data = NULL; break; } lpfc_sli_pcimem_bcopy(pmb->context2, lpfc_vpd_data + offset, mb->un.varDmp.word_cnt); offset += mb->un.varDmp.word_cnt; } while (mb->un.varDmp.word_cnt); lpfc_parse_vpd(phba, lpfc_vpd_data); kfree(lpfc_vpd_data);out_free_context2: kfree(pmb->context2);out_free_mbox: mempool_free(pmb, phba->mbox_mem_pool); return 0;}/************************************************************************//* *//* lpfc_config_port_post *//* This routine will do LPFC initialization after the *//* CONFIG_PORT mailbox command. This will be initialized *//* as a SLI layer callback routine. *//* This routine returns 0 on success. Any other return value *//* indicates an error. *//* *//************************************************************************/intlpfc_config_port_post(struct lpfc_hba * phba){ LPFC_MBOXQ_t *pmb; MAILBOX_t *mb; struct lpfc_dmabuf *mp; struct lpfc_sli *psli = &phba->sli; uint32_t status, timeout; int i, j, rc; pmb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL); if (!pmb) { phba->hba_state = LPFC_HBA_ERROR; return -ENOMEM; } mb = &pmb->mb; lpfc_config_link(phba, pmb); rc = lpfc_sli_issue_mbox(phba, pmb, MBX_POLL); if (rc != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0447 Adapter failed init, mbxCmd x%x " "CONFIG_LINK mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); phba->hba_state = LPFC_HBA_ERROR; mempool_free( pmb, phba->mbox_mem_pool); return -EIO; } /* Get login parameters for NID. */ lpfc_read_sparam(phba, pmb); if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0448 Adapter failed init, mbxCmd x%x " "READ_SPARM mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); phba->hba_state = LPFC_HBA_ERROR; mp = (struct lpfc_dmabuf *) pmb->context1; mempool_free( pmb, phba->mbox_mem_pool); lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); return -EIO; } mp = (struct lpfc_dmabuf *) pmb->context1; memcpy(&phba->fc_sparam, mp->virt, sizeof (struct serv_parm)); lpfc_mbuf_free(phba, mp->virt, mp->phys); kfree(mp); pmb->context1 = NULL; memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName, sizeof (struct lpfc_name)); memcpy(&phba->fc_portname, &phba->fc_sparam.portName, sizeof (struct lpfc_name)); /* If no serial number in VPD data, use low 6 bytes of WWNN */ /* This should be consolidated into parse_vpd ? - mr */ if (phba->SerialNumber[0] == 0) { uint8_t *outptr; outptr = &phba->fc_nodename.u.s.IEEE[0]; for (i = 0; i < 12; i++) { status = *outptr++; j = ((status & 0xf0) >> 4); if (j <= 9) phba->SerialNumber[i] = (char)((uint8_t) 0x30 + (uint8_t) j); else phba->SerialNumber[i] = (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); i++; j = (status & 0xf); if (j <= 9) phba->SerialNumber[i] = (char)((uint8_t) 0x30 + (uint8_t) j); else phba->SerialNumber[i] = (char)((uint8_t) 0x61 + (uint8_t) (j - 10)); } } /* This should turn on DELAYED ABTS for ELS timeouts */ lpfc_set_slim(phba, pmb, 0x052198, 0x1); if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { phba->hba_state = LPFC_HBA_ERROR; mempool_free( pmb, phba->mbox_mem_pool); return -EIO; } lpfc_read_config(phba, pmb); if (lpfc_sli_issue_mbox(phba, pmb, MBX_POLL) != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0453 Adapter failed to init, mbxCmd x%x " "READ_CONFIG, mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); phba->hba_state = LPFC_HBA_ERROR; mempool_free( pmb, phba->mbox_mem_pool); return -EIO; } /* Reset the DFT_HBA_Q_DEPTH to the max xri */ if (phba->cfg_hba_queue_depth > (mb->un.varRdConfig.max_xri+1)) phba->cfg_hba_queue_depth = mb->un.varRdConfig.max_xri + 1; phba->lmt = mb->un.varRdConfig.lmt; /* HBA is not 4GB capable, or HBA is not 2GB capable, don't let link speed ask for it */ if ((((phba->lmt & LMT_4250_10bit) != LMT_4250_10bit) && (phba->cfg_link_speed > LINK_SPEED_2G)) || (((phba->lmt & LMT_2125_10bit) != LMT_2125_10bit) && (phba->cfg_link_speed > LINK_SPEED_1G))) { /* Reset link speed to auto. 1G/2GB HBA cfg'd for 4G */ lpfc_printf_log(phba, KERN_WARNING, LOG_LINK_EVENT, "%d:1302 Invalid speed for this board: " "Reset link speed to auto: x%x\n", phba->brd_no, phba->cfg_link_speed); phba->cfg_link_speed = LINK_SPEED_AUTO; } phba->hba_state = LPFC_LINK_DOWN; /* Only process IOCBs on ring 0 till hba_state is READY */ if (psli->ring[psli->ip_ring].cmdringaddr) psli->ring[psli->ip_ring].flag |= LPFC_STOP_IOCB_EVENT; if (psli->ring[psli->fcp_ring].cmdringaddr) psli->ring[psli->fcp_ring].flag |= LPFC_STOP_IOCB_EVENT; if (psli->ring[psli->next_ring].cmdringaddr) psli->ring[psli->next_ring].flag |= LPFC_STOP_IOCB_EVENT; /* Post receive buffers for desired rings */ lpfc_post_rcv_buf(phba); /* Enable appropriate host interrupts */ spin_lock_irq(phba->host->host_lock); status = readl(phba->HCregaddr); status |= HC_MBINT_ENA | HC_ERINT_ENA | HC_LAINT_ENA; if (psli->num_rings > 0) status |= HC_R0INT_ENA; if (psli->num_rings > 1) status |= HC_R1INT_ENA; if (psli->num_rings > 2) status |= HC_R2INT_ENA; if (psli->num_rings > 3) status |= HC_R3INT_ENA; writel(status, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ spin_unlock_irq(phba->host->host_lock); /* * Setup the ring 0 (els) timeout handler */ timeout = phba->fc_ratov << 1; phba->els_tmofunc.expires = jiffies + HZ * timeout; add_timer(&phba->els_tmofunc); lpfc_init_link(phba, pmb, phba->cfg_topology, phba->cfg_link_speed); pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl; if (lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT) != MBX_SUCCESS) { lpfc_printf_log(phba, KERN_ERR, LOG_INIT, "%d:0454 Adapter failed to init, mbxCmd x%x " "INIT_LINK, mbxStatus x%x\n", phba->brd_no, mb->mbxCommand, mb->mbxStatus); /* Clear all interrupt enable conditions */ writel(0, phba->HCregaddr); readl(phba->HCregaddr); /* flush */ /* Clear all pending interrupts */ writel(0xffffffff, phba->HAregaddr); readl(phba->HAregaddr); /* flush */ phba->hba_state = LPFC_HBA_ERROR; mempool_free(pmb, phba->mbox_mem_pool); return -EIO; } /* MBOX buffer will be freed in mbox compl */ i = 0; while ((phba->hba_state != LPFC_HBA_READY) || (phba->num_disc_nodes) || (phba->fc_prli_sent) || ((phba->fc_map_cnt == 0) && (i<2)) || (psli->sli_flag & LPFC_SLI_MBOX_ACTIVE)) { /* Check every second for 30 retries. */ i++; if (i > 30) { break; } if ((i >= 15) && (phba->hba_state <= LPFC_LINK_DOWN)) { /* The link is down. Set linkdown timeout */ break; } /* Delay for 1 second to give discovery time to complete. */ msleep(1000); } /* Since num_disc_nodes keys off of PLOGI, delay a bit to let
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -