8526 lines · 235.9 KB · C
RawDownload
/* *  linux/drivers/message/fusion/mptbase.c *      This is the Fusion MPT base driver which supports multiple *      (SCSI + LAN) specialized protocol drivers. *      For use with LSI PCI chip/adapter(s) *      running LSI Fusion MPT (Message Passing Technology) firmware. * *  Copyright (c) 1999-2008 LSI Corporation *  (mailto:DL-MPTFusionLinux@lsi.com) * *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//*    This program is free software; you can redistribute it and/or modify    it under the terms of the GNU General Public License as published by    the Free Software Foundation; version 2 of the License.     This program is distributed in the hope that it will be useful,    but WITHOUT ANY WARRANTY; without even the implied warranty of    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    GNU General Public License for more details.     NO WARRANTY    THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR    CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT    LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,    MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is    solely responsible for determining the appropriateness of using and    distributing the Program and assumes all risks associated with its    exercise of rights under this Agreement, including but not limited to    the risks and costs of program errors, damage to or loss of data,    programs or equipment, and unavailability or interruption of operations.     DISCLAIMER OF LIABILITY    NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL    DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR    TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE    USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED    HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES     You should have received a copy of the GNU General Public License    along with this program; if not, write to the Free Software    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA*//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #include <linux/kernel.h>#include <linux/module.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/seq_file.h>#include <linux/slab.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/kdev_t.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/dma-mapping.h>#include <linux/kthread.h>#include <scsi/scsi_host.h> #include "mptbase.h"#include "lsi/mpi_log_fc.h" /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/#define my_NAME		"Fusion MPT base driver"#define my_VERSION	MPT_LINUX_VERSION_COMMON#define MYNAM		"mptbase" MODULE_AUTHOR(MODULEAUTHOR);MODULE_DESCRIPTION(my_NAME);MODULE_LICENSE("GPL");MODULE_VERSION(my_VERSION); /* *  cmd line parameters */ static int mpt_msi_enable_spi;module_param(mpt_msi_enable_spi, int, 0);MODULE_PARM_DESC(mpt_msi_enable_spi,		 " Enable MSI Support for SPI controllers (default=0)"); static int mpt_msi_enable_fc;module_param(mpt_msi_enable_fc, int, 0);MODULE_PARM_DESC(mpt_msi_enable_fc,		 " Enable MSI Support for FC controllers (default=0)"); static int mpt_msi_enable_sas;module_param(mpt_msi_enable_sas, int, 0);MODULE_PARM_DESC(mpt_msi_enable_sas,		 " Enable MSI Support for SAS controllers (default=0)"); static int mpt_channel_mapping;module_param(mpt_channel_mapping, int, 0);MODULE_PARM_DESC(mpt_channel_mapping, " Mapping id's to channels (default=0)"); static int mpt_debug_level;static int mpt_set_debug_level(const char *val, const struct kernel_param *kp);module_param_call(mpt_debug_level, mpt_set_debug_level, param_get_int,		  &mpt_debug_level, 0600);MODULE_PARM_DESC(mpt_debug_level,		 " debug level - refer to mptdebug.h - (default=0)"); int mpt_fwfault_debug;EXPORT_SYMBOL(mpt_fwfault_debug);module_param(mpt_fwfault_debug, int, 0600);MODULE_PARM_DESC(mpt_fwfault_debug,		 "Enable detection of Firmware fault and halt Firmware on fault - (default=0)"); static char	MptCallbacksName[MPT_MAX_PROTOCOL_DRIVERS]				[MPT_MAX_CALLBACKNAME_LEN+1]; #ifdef MFCNTstatic int mfcounter = 0;#define PRINT_MF_COUNT 20000#endif /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* *  Public data... */ #define WHOINIT_UNKNOWN		0xAA /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* *  Private data... */					/* Adapter link list */LIST_HEAD(ioc_list);					/* Callback lookup table */static MPT_CALLBACK		 MptCallbacks[MPT_MAX_PROTOCOL_DRIVERS];					/* Protocol driver class lookup table */static int			 MptDriverClass[MPT_MAX_PROTOCOL_DRIVERS];					/* Event handler lookup table */static MPT_EVHANDLER		 MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS];					/* Reset handler lookup table */static MPT_RESETHANDLER		 MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS];static struct mpt_pci_driver 	*MptDeviceDriverHandlers[MPT_MAX_PROTOCOL_DRIVERS]; #ifdef CONFIG_PROC_FSstatic struct proc_dir_entry 	*mpt_proc_root_dir;#endif /* *  Driver Callback Index's */static u8 mpt_base_index = MPT_MAX_PROTOCOL_DRIVERS;static u8 last_drv_idx; /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* *  Forward protos... */static irqreturn_t mpt_interrupt(int irq, void *bus_id);static int	mptbase_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,		MPT_FRAME_HDR *reply);static int	mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes,			u32 *req, int replyBytes, u16 *u16reply, int maxwait,			int sleepFlag);static int	mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag);static void	mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev);static void	mpt_adapter_disable(MPT_ADAPTER *ioc);static void	mpt_adapter_dispose(MPT_ADAPTER *ioc); static void	MptDisplayIocCapabilities(MPT_ADAPTER *ioc);static int	MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag);static int	GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason);static int	GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag);static int	SendIocInit(MPT_ADAPTER *ioc, int sleepFlag);static int	SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag);static int	mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag);static int	mpt_downloadboot(MPT_ADAPTER *ioc, MpiFwHeader_t *pFwHeader, int sleepFlag);static int	mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag);static int	KickStart(MPT_ADAPTER *ioc, int ignore, int sleepFlag);static int	SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag);static int	PrimeIocFifos(MPT_ADAPTER *ioc);static int	WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag);static int	WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag);static int	WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag);static int	GetLanConfigPages(MPT_ADAPTER *ioc);static int	GetIoUnitPage2(MPT_ADAPTER *ioc);int		mptbase_sas_persist_operation(MPT_ADAPTER *ioc, u8 persist_opcode);static int	mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum);static int	mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum);static void 	mpt_read_ioc_pg_1(MPT_ADAPTER *ioc);static void 	mpt_read_ioc_pg_4(MPT_ADAPTER *ioc);static void	mpt_get_manufacturing_pg_0(MPT_ADAPTER *ioc);static int	SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch,	int sleepFlag);static int	SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp);static int	mpt_host_page_access_control(MPT_ADAPTER *ioc, u8 access_control_value, int sleepFlag);static int	mpt_host_page_alloc(MPT_ADAPTER *ioc, pIOCInit_t ioc_init); #ifdef CONFIG_PROC_FSstatic int mpt_summary_proc_show(struct seq_file *m, void *v);static int mpt_version_proc_show(struct seq_file *m, void *v);static int mpt_iocinfo_proc_show(struct seq_file *m, void *v);#endifstatic void	mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc); static int	ProcessEventNotification(MPT_ADAPTER *ioc,		EventNotificationReply_t *evReply, int *evHandlers);static void	mpt_iocstatus_info(MPT_ADAPTER *ioc, u32 ioc_status, MPT_FRAME_HDR *mf);static void	mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info);static void	mpt_spi_log_info(MPT_ADAPTER *ioc, u32 log_info);static void	mpt_sas_log_info(MPT_ADAPTER *ioc, u32 log_info , u8 cb_idx);static int	mpt_read_ioc_pg_3(MPT_ADAPTER *ioc);static void	mpt_inactive_raid_list_free(MPT_ADAPTER *ioc); /* module entry point */static int  __init    fusion_init  (void);static void __exit    fusion_exit  (void); #define CHIPREG_READ32(addr) 		readl_relaxed(addr)#define CHIPREG_READ32_dmasync(addr)	readl(addr)#define CHIPREG_WRITE32(addr,val) 	writel(val, addr)#define CHIPREG_PIO_WRITE32(addr,val)	outl(val, (unsigned long)addr)#define CHIPREG_PIO_READ32(addr) 	inl((unsigned long)addr) static voidpci_disable_io_access(struct pci_dev *pdev){	u16 command_reg; 	pci_read_config_word(pdev, PCI_COMMAND, &command_reg);	command_reg &= ~1;	pci_write_config_word(pdev, PCI_COMMAND, command_reg);} static voidpci_enable_io_access(struct pci_dev *pdev){	u16 command_reg; 	pci_read_config_word(pdev, PCI_COMMAND, &command_reg);	command_reg |= 1;	pci_write_config_word(pdev, PCI_COMMAND, command_reg);} static int mpt_set_debug_level(const char *val, const struct kernel_param *kp){	int ret = param_set_int(val, kp);	MPT_ADAPTER *ioc; 	if (ret)		return ret; 	list_for_each_entry(ioc, &ioc_list, list)		ioc->debug_level = mpt_debug_level;	return 0;} /** *	mpt_get_cb_idx - obtain cb_idx for registered driver *	@dclass: class driver enum * *	Returns cb_idx, or zero means it wasn't found **/static u8mpt_get_cb_idx(MPT_DRIVER_CLASS dclass){	u8 cb_idx; 	for (cb_idx = MPT_MAX_PROTOCOL_DRIVERS-1; cb_idx; cb_idx--)		if (MptDriverClass[cb_idx] == dclass)			return cb_idx;	return 0;} /** * mpt_is_discovery_complete - determine if discovery has completed * @ioc: per adatper instance * * Returns 1 when discovery completed, else zero. */static intmpt_is_discovery_complete(MPT_ADAPTER *ioc){	ConfigExtendedPageHeader_t hdr;	CONFIGPARMS cfg;	SasIOUnitPage0_t *buffer;	dma_addr_t dma_handle;	int rc = 0; 	memset(&hdr, 0, sizeof(ConfigExtendedPageHeader_t));	memset(&cfg, 0, sizeof(CONFIGPARMS));	hdr.PageVersion = MPI_SASIOUNITPAGE0_PAGEVERSION;	hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;	hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_IO_UNIT;	cfg.cfghdr.ehdr = &hdr;	cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; 	if ((mpt_config(ioc, &cfg)))		goto out;	if (!hdr.ExtPageLength)		goto out; 	buffer = dma_alloc_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4,				    &dma_handle, GFP_KERNEL);	if (!buffer)		goto out; 	cfg.physAddr = dma_handle;	cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; 	if ((mpt_config(ioc, &cfg)))		goto out_free_consistent; 	if (!(buffer->PhyData[0].PortFlags &	    MPI_SAS_IOUNIT0_PORT_FLAGS_DISCOVERY_IN_PROGRESS))		rc = 1;  out_free_consistent:	dma_free_coherent(&ioc->pcidev->dev, hdr.ExtPageLength * 4, buffer,			  dma_handle); out:	return rc;}  /** *  mpt_remove_dead_ioc_func - kthread context to remove dead ioc * @arg: input argument, used to derive ioc * * Return 0 if controller is removed from pci subsystem. * Return -1 for other case. */static int mpt_remove_dead_ioc_func(void *arg){	MPT_ADAPTER *ioc = (MPT_ADAPTER *)arg;	struct pci_dev *pdev; 	if (!ioc)		return -1; 	pdev = ioc->pcidev;	if (!pdev)		return -1; 	pci_stop_and_remove_bus_device_locked(pdev);	return 0;}   /** *	mpt_fault_reset_work - work performed on workq after ioc fault *	@work: input argument, used to derive ioc ***/static voidmpt_fault_reset_work(struct work_struct *work){	MPT_ADAPTER	*ioc =	    container_of(work, MPT_ADAPTER, fault_reset_work.work);	u32		 ioc_raw_state;	int		 rc;	unsigned long	 flags;	MPT_SCSI_HOST	*hd;	struct task_struct *p; 	if (ioc->ioc_reset_in_progress || !ioc->active)		goto out;  	ioc_raw_state = mpt_GetIocState(ioc, 0);	if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_MASK) {		printk(MYIOC_s_INFO_FMT "%s: IOC is non-operational !!!!\n",		    ioc->name, __func__); 		/*		 * Call mptscsih_flush_pending_cmds callback so that we		 * flush all pending commands back to OS.		 * This call is required to aovid deadlock at block layer.		 * Dead IOC will fail to do diag reset,and this call is safe		 * since dead ioc will never return any command back from HW.		 */		hd = shost_priv(ioc->sh);		ioc->schedule_dead_ioc_flush_running_cmds(hd); 		/*Remove the Dead Host */		p = kthread_run(mpt_remove_dead_ioc_func, ioc,				"mpt_dead_ioc_%d", ioc->id);		if (IS_ERR(p))	{			printk(MYIOC_s_ERR_FMT				"%s: Running mpt_dead_ioc thread failed !\n",				ioc->name, __func__);		} else {			printk(MYIOC_s_WARN_FMT				"%s: Running mpt_dead_ioc thread success !\n",				ioc->name, __func__);		}		return; /* don't rearm timer */	} 	if ((ioc_raw_state & MPI_IOC_STATE_MASK)			== MPI_IOC_STATE_FAULT) {		printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",		       ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);		printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",		       ioc->name, __func__);		rc = mpt_HardResetHandler(ioc, CAN_SLEEP);		printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,		       __func__, (rc == 0) ? "success" : "failed");		ioc_raw_state = mpt_GetIocState(ioc, 0);		if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)			printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "			    "reset (%04xh)\n", ioc->name, ioc_raw_state &			    MPI_DOORBELL_DATA_MASK);	} else if (ioc->bus_type == SAS && ioc->sas_discovery_quiesce_io) {		if ((mpt_is_discovery_complete(ioc))) {			devtprintk(ioc, printk(MYIOC_s_DEBUG_FMT "clearing "			    "discovery_quiesce_io flag\n", ioc->name));			ioc->sas_discovery_quiesce_io = 0;		}	}  out:	/*	 * Take turns polling alternate controller	 */	if (ioc->alt_ioc)		ioc = ioc->alt_ioc; 	/* rearm the timer */	spin_lock_irqsave(&ioc->taskmgmt_lock, flags);	if (ioc->reset_work_q)		queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,			msecs_to_jiffies(MPT_POLLING_INTERVAL));	spin_unlock_irqrestore(&ioc->taskmgmt_lock, flags);}  /* *  Process turbo (context) reply... */static voidmpt_turbo_reply(MPT_ADAPTER *ioc, u32 pa){	MPT_FRAME_HDR *mf = NULL;	MPT_FRAME_HDR *mr = NULL;	u16 req_idx = 0;	u8 cb_idx; 	dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Got TURBO reply req_idx=%08x\n",				ioc->name, pa)); 	switch (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT) {	case MPI_CONTEXT_REPLY_TYPE_SCSI_INIT:		req_idx = pa & 0x0000FFFF;		cb_idx = (pa & 0x00FF0000) >> 16;		mf = MPT_INDEX_2_MFPTR(ioc, req_idx);		break;	case MPI_CONTEXT_REPLY_TYPE_LAN:		cb_idx = mpt_get_cb_idx(MPTLAN_DRIVER);		/*		 *  Blind set of mf to NULL here was fatal		 *  after lan_reply says "freeme"		 *  Fix sort of combined with an optimization here;		 *  added explicit check for case where lan_reply		 *  was just returning 1 and doing nothing else.		 *  For this case skip the callback, but set up		 *  proper mf value first here:-)		 */		if ((pa & 0x58000000) == 0x58000000) {			req_idx = pa & 0x0000FFFF;			mf = MPT_INDEX_2_MFPTR(ioc, req_idx);			mpt_free_msg_frame(ioc, mf);			mb();			return;		}		mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa);		break;	case MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET:		cb_idx = mpt_get_cb_idx(MPTSTM_DRIVER);		mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa);		break;	default:		cb_idx = 0;		BUG();	} 	/*  Check for (valid) IO callback!  */	if (!cb_idx || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS ||		MptCallbacks[cb_idx] == NULL) {		printk(MYIOC_s_WARN_FMT "%s: Invalid cb_idx (%d)!\n",				__func__, ioc->name, cb_idx);		goto out;	} 	if (MptCallbacks[cb_idx](ioc, mf, mr))		mpt_free_msg_frame(ioc, mf); out:	mb();}

Lines 1–500 of 8526.