/* * 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. Load more
/* * 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 MFCNT static 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_FS static 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_FS static 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); #endif static 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 void pci_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 void pci_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 u8 mpt_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 int mpt_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 void mpt_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 void mpt_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(); }