lpfc_init.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,888 行 · 第 1/4 页

C
1,888
字号
/******************************************************************* * This file is part of the Emulex Linux Device Driver for         * * Fibre Channel Host Bus Adapters.                                * * 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              * *                                                                 * * 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 *, int);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 value of rr must be 1 since the driver set the cv field to 1.	 * This setting requires the FW to set all revision fields.	 */	if (mb->un.varRdRev.rr == 0) {		vp->rev.rBit = 0;		lpfc_printf_log(phba, KERN_ERR, LOG_INIT,				"%d:0440 Adapter failed to init, READ_REV has "				"missing revision information.\n",				phba->brd_no);		mempool_free(pmb, phba->mbox_mem_pool);		return -ERESTART;	}	/* Save information as VPD data */	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);	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 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);			mb->un.varDmp.word_cnt = 0;		}		if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)			mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;		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 && offset < DMP_VPD_SIZE);	lpfc_parse_vpd(phba, lpfc_vpd_data, offset);	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));		}	}	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;	/* Get the default values for Model Name and Description */	lpfc_get_hba_model_desc(phba, phba->ModelName, phba->ModelDesc);	if ((phba->cfg_link_speed > LINK_SPEED_10G)	    || ((phba->cfg_link_speed == LINK_SPEED_1G)		&& !(phba->lmt & LMT_1Gb))	    || ((phba->cfg_link_speed == LINK_SPEED_2G)		&& !(phba->lmt & LMT_2Gb))	    || ((phba->cfg_link_speed == LINK_SPEED_4G)		&& !(phba->lmt & LMT_4Gb))	    || ((phba->cfg_link_speed == LINK_SPEED_8G)		&& !(phba->lmt & LMT_8Gb))	    || ((phba->cfg_link_speed == LINK_SPEED_10G)		&& !(phba->lmt & LMT_10Gb))) {		/* Reset link speed to auto */		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;	if ((phba->cfg_poll & ENABLE_FCP_RING_POLLING) &&	    (phba->cfg_poll & DISABLE_FCP_RING_INT))		status &= ~(HC_R0INT_ENA << LPFC_FCP_RING);	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	 * any potential PRLIs to flush thru the SLI sub-system.	 */	msleep(50);	return (0);}/************************************************************************//*                                                                      *//*    lpfc_hba_down_prep                                                *//*    This routine will do LPFC uninitialization before the             *//*    HBA is reset when bringing down the SLI Layer. This will be       *//*    initialized as a SLI layer callback routine.                      *//*    This routine returns 0 on success. Any other return value         *//*    indicates an error.                                               *//*                                                                      *//************************************************************************/intlpfc_hba_down_prep(struct lpfc_hba * phba){	/* Disable interrupts */	writel(0, phba->HCregaddr);	readl(phba->HCregaddr); /* flush */	/* Cleanup potential discovery resources */	lpfc_els_flush_rscn(phba);	lpfc_els_flush_cmd(phba);	lpfc_disc_flush_list(phba);	return (0);}/************************************************************************//*                                                                      *//*    lpfc_hba_down_post                                                *//*    This routine will do uninitialization after the HBA is reset      *//*    when bringing down the SLI Layer.                                 *//*    This routine returns 0 on success. Any other return value         *//*    indicates an error.                                               *//*                                                                      *//************************************************************************/intlpfc_hba_down_post(struct lpfc_hba * phba){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?