diff options
Diffstat (limited to 'drivers/message/fusion/mptbase.c')
-rw-r--r-- | drivers/message/fusion/mptbase.c | 5946 |
1 files changed, 5946 insertions, 0 deletions
diff --git a/drivers/message/fusion/mptbase.c b/drivers/message/fusion/mptbase.c new file mode 100644 index 00000000000..942cc1c2a18 --- /dev/null +++ b/drivers/message/fusion/mptbase.c @@ -0,0 +1,5946 @@ +/* + * linux/drivers/message/fusion/mptbase.c + * High performance SCSI + LAN / Fibre Channel device drivers. + * This is the Fusion MPT base driver which supports multiple + * (SCSI + LAN) specialized protocol drivers. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * There are lots of people not mentioned below that deserve credit + * and thanks but won't get it here - sorry in advance that you + * got overlooked. + * + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A special thanks to Noah Romer (LSI Logic) for tons of work + * and tough debugging on the LAN driver, especially early on;-) + * And to Roger Hickerson (LSI Logic) for tirelessly supporting + * this driver project. + * + * A special thanks to Pamela Delaney (LSI Logic) for tons of work + * and countless enhancements while adding support for the 1030 + * chip family. Pam has been instrumental in the development of + * of the 2.xx.xx series fusion drivers, and her contributions are + * far too numerous to hope to list in one place. + * + * All manner of help from Stephen Shirron (LSI Logic): + * low-level FC analysis, debug + various fixes in FCxx firmware, + * initial port to alpha platform, various driver code optimizations, + * being a faithful sounding board on all sorts of issues & ideas, + * etc. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * Special thanks goes to the I2O LAN driver people at the + * University of Helsinki, who, unbeknownst to them, provided + * the inspiration and initial structure for this driver. + * + * A really huge debt of gratitude is owed to Eddie C. Dost + * for gobs of hard work fixing and optimizing LAN code. + * THANK YOU! + * + * Copyright (c) 1999-2004 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:sjralston1@netscape.net) + * (mailto:mpt_linux_developer@lsil.com) + * + * $Id: mptbase.c,v 1.126 2002/12/16 15:28:45 pdelaney Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + 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/config.h> +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/init.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> /* needed for in_interrupt() proto */ +#include <asm/io.h> +#ifdef CONFIG_MTRR +#include <asm/mtrr.h> +#endif +#ifdef __sparc__ +#include <asm/irq.h> /* needed for __irq_itoa() proto */ +#endif + +#include "mptbase.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"); + +/* + * cmd line parameters + */ +#ifdef MFCNT +static int mfcounter = 0; +#define PRINT_MF_COUNT 20000 +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Public data... + */ +int mpt_lan_index = -1; +int mpt_stm_index = -1; + +struct proc_dir_entry *mpt_proc_root_dir; + +#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]; + +static int mpt_base_index = -1; +static int last_drv_idx = -1; + +static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Forward protos... + */ +static irqreturn_t mpt_interrupt(int irq, void *bus_id, struct pt_regs *r); +static int mpt_base_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 u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked); +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, 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 GetFcPortPage0(MPT_ADAPTER *ioc, int portnum); +static int GetIoUnitPage2(MPT_ADAPTER *ioc); +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_timer_expired(unsigned long data); +static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch); +static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp); + +#ifdef CONFIG_PROC_FS +static int procmpt_summary_read(char *buf, char **start, off_t offset, + int request, int *eof, void *data); +static int procmpt_version_read(char *buf, char **start, off_t offset, + int request, int *eof, void *data); +static int procmpt_iocinfo_read(char *buf, char **start, off_t offset, + int request, int *eof, void *data); +#endif +static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc); + +//int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag); +static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers); +static void mpt_sp_ioc_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_sp_log_info(MPT_ADAPTER *ioc, u32 log_info); + +/* module entry point */ +static int __devinit mptbase_probe (struct pci_dev *, const struct pci_device_id *); +static void __devexit mptbase_remove(struct pci_dev *); +static void mptbase_shutdown(struct device * ); +static int __init fusion_init (void); +static void __exit fusion_exit (void); + +/**************************************************************************** + * Supported hardware + */ + +static struct pci_device_id mptbase_pci_table[] = { + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC909, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC929X, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FC919X, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_53C1030, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_1030_53C1035, + PCI_ANY_ID, PCI_ANY_ID }, + {0} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(pci, mptbase_pci_table); + +#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) + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_interrupt - MPT adapter (IOC) specific interrupt handler. + * @irq: irq number (not used) + * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure + * @r: pt_regs pointer (not used) + * + * This routine is registered via the request_irq() kernel API call, + * and handles all interrupts generated from a specific MPT adapter + * (also referred to as a IO Controller or IOC). + * This routine must clear the interrupt from the adapter and does + * so by reading the reply FIFO. Multiple replies may be processed + * per single call to this routine; up to MPT_MAX_REPLIES_PER_ISR + * which is currently set to 32 in mptbase.h. + * + * This routine handles register-level access of the adapter but + * dispatches (calls) a protocol-specific callback routine to handle + * the protocol-specific details of the MPT request completion. + */ +static irqreturn_t +mpt_interrupt(int irq, void *bus_id, struct pt_regs *r) +{ + MPT_ADAPTER *ioc; + MPT_FRAME_HDR *mf; + MPT_FRAME_HDR *mr; + u32 pa; + int req_idx; + int cb_idx; + int type; + int freeme; + + ioc = (MPT_ADAPTER *)bus_id; + + /* + * Drain the reply FIFO! + * + * NOTES: I've seen up to 10 replies processed in this loop, so far... + * Update: I've seen up to 9182 replies processed in this loop! ?? + * Update: Limit ourselves to processing max of N replies + * (bottom of loop). + */ + while (1) { + + if ((pa = CHIPREG_READ32_dmasync(&ioc->chip->ReplyFifo)) == 0xFFFFFFFF) + return IRQ_HANDLED; + + cb_idx = 0; + freeme = 0; + + /* + * Check for non-TURBO reply! + */ + if (pa & MPI_ADDRESS_REPLY_A_BIT) { + u32 reply_dma_low; + u16 ioc_stat; + + /* non-TURBO reply! Hmmm, something may be up... + * Newest turbo reply mechanism; get address + * via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)! + */ + + /* Map DMA address of reply header to cpu address. + * pa is 32 bits - but the dma address may be 32 or 64 bits + * get offset based only only the low addresses + */ + reply_dma_low = (pa = (pa << 1)); + mr = (MPT_FRAME_HDR *)((u8 *)ioc->reply_frames + + (reply_dma_low - ioc->reply_frames_low_dma)); + + req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx); + cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + + dmfprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p req_idx=%x\n", + ioc->name, mr, req_idx)); + DBG_DUMP_REPLY_FRAME(mr) + + /* NEW! 20010301 -sralston + * Check/log IOC log info + */ + ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus); + if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { + u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo); + if (ioc->bus_type == FC) + mpt_fc_log_info(ioc, log_info); + else if (ioc->bus_type == SCSI) + mpt_sp_log_info(ioc, log_info); + } + if (ioc_stat & MPI_IOCSTATUS_MASK) { + if (ioc->bus_type == SCSI) + mpt_sp_ioc_info(ioc, (u32)ioc_stat, mf); + } + } else { + /* + * Process turbo (context) reply... + */ + dmfprintk((MYIOC_s_INFO_FMT "Got TURBO reply req_idx=%08x\n", ioc->name, pa)); + type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT); + if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) { + cb_idx = mpt_stm_index; + mf = NULL; + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + } else if (type == MPI_CONTEXT_REPLY_TYPE_LAN) { + cb_idx = mpt_lan_index; + /* + * BUG FIX! 20001218 -sralston + * 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); + freeme = 1; + /* + * IMPORTANT! Invalidate the callback! + */ + cb_idx = 0; + } else { + mf = NULL; + } + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + } else { + req_idx = pa & 0x0000FFFF; + cb_idx = (pa & 0x00FF0000) >> 16; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + mr = NULL; + } + pa = 0; /* No reply flush! */ + } + +#ifdef MPT_DEBUG_IRQ + if (ioc->bus_type == SCSI) { + /* Verify mf, mr are reasonable. + */ + if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth)) + || (mf < ioc->req_frames)) ) { + printk(MYIOC_s_WARN_FMT + "mpt_interrupt: Invalid mf (%p) req_idx (%d)!\n", ioc->name, (void *)mf, req_idx); + cb_idx = 0; + pa = 0; + freeme = 0; + } + if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth)) + || (mr < ioc->reply_frames)) ) { + printk(MYIOC_s_WARN_FMT + "mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr); + cb_idx = 0; + pa = 0; + freeme = 0; + } + if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) { + printk(MYIOC_s_WARN_FMT + "mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx); + cb_idx = 0; + pa = 0; + freeme = 0; + } + } +#endif + + /* Check for (valid) IO callback! */ + if (cb_idx) { + /* Do the callback! */ + freeme = (*(MptCallbacks[cb_idx]))(ioc, mf, mr); + } + + if (pa) { + /* Flush (non-TURBO) reply with a WRITE! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, pa); + } + + if (freeme) { + unsigned long flags; + + /* Put Request back on FreeQ! */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ); +#ifdef MFCNT + ioc->mfcnt--; +#endif + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + } + + mb(); + } /* drain reply FIFO */ + + return IRQ_HANDLED; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_base_reply - MPT base driver's callback routine; all base driver + * "internal" request/reply processing is routed here. + * Currently used for EventNotification and EventAck handling. + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @reply: Pointer to MPT reply frame (NULL if TurboReply) + * + * Returns 1 indicating original alloc'd request frame ptr + * should be freed, or 0 if it shouldn't. + */ +static int +mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply) +{ + int freereq = 1; + u8 func; + + dprintk((MYIOC_s_INFO_FMT "mpt_base_reply() called\n", ioc->name)); + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(MYIOC_s_ERR_FMT "NULL or BAD request frame ptr! (=%p)\n", + ioc->name, (void *)mf); + return 1; + } + + if (reply == NULL) { + dprintk((MYIOC_s_ERR_FMT "Unexpected NULL Event (turbo?) reply!\n", + ioc->name)); + return 1; + } + + if (!(reply->u.hdr.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) { + dmfprintk((KERN_INFO MYNAM ": Original request frame (@%p) header\n", mf)); + DBG_DUMP_REQUEST_FRAME_HDR(mf) + } + + func = reply->u.hdr.Function; + dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, Function=%02Xh\n", + ioc->name, func)); + + if (func == MPI_FUNCTION_EVENT_NOTIFICATION) { + EventNotificationReply_t *pEvReply = (EventNotificationReply_t *) reply; + int evHandlers = 0; + int results; + + results = ProcessEventNotification(ioc, pEvReply, &evHandlers); + if (results != evHandlers) { + /* CHECKME! Any special handling needed here? */ + devtprintk((MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n", + ioc->name, evHandlers, results)); + } + + /* + * Hmmm... It seems that EventNotificationReply is an exception + * to the rule of one reply per request. + */ + if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY) + freereq = 0; + +#ifdef CONFIG_PROC_FS +// LogEvent(ioc, pEvReply); +#endif + + } else if (func == MPI_FUNCTION_EVENT_ACK) { + dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, EventAck reply received\n", + ioc->name)); + } else if (func == MPI_FUNCTION_CONFIG || + func == MPI_FUNCTION_TOOLBOX) { + CONFIGPARMS *pCfg; + unsigned long flags; + + dcprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n", + ioc->name, mf, reply)); + + pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *))); + + if (pCfg) { + /* disable timer and remove from linked list */ + del_timer(&pCfg->timer); + + spin_lock_irqsave(&ioc->FreeQlock, flags); + list_del(&pCfg->linkage); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + /* + * If IOC Status is SUCCESS, save the header + * and set the status code to GOOD. + */ + pCfg->status = MPT_CONFIG_ERROR; + if (reply) { + ConfigReply_t *pReply = (ConfigReply_t *)reply; + u16 status; + + status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK; + dcprintk((KERN_NOTICE " IOCStatus=%04xh, IOCLogInfo=%08xh\n", + status, le32_to_cpu(pReply->IOCLogInfo))); + + pCfg->status = status; + if (status == MPI_IOCSTATUS_SUCCESS) { + pCfg->hdr->PageVersion = pReply->Header.PageVersion; + pCfg->hdr->PageLength = pReply->Header.PageLength; + pCfg->hdr->PageNumber = pReply->Header.PageNumber; + pCfg->hdr->PageType = pReply->Header.PageType; + } + } + + /* + * Wake up the original calling thread + */ + pCfg->wait_done = 1; + wake_up(&mpt_waitq); + } + } else { + printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n", + ioc->name, func); + } + + /* + * Conditionally tell caller to free the original + * EventNotification/EventAck/unexpected request frame! + */ + return freereq; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_register - Register protocol-specific main callback handler. + * @cbfunc: callback function pointer + * @dclass: Protocol driver's class (%MPT_DRIVER_CLASS enum value) + * + * This routine is called by a protocol-specific driver (SCSI host, + * LAN, SCSI target) to register it's reply callback routine. Each + * protocol-specific driver must do this before it will be able to + * use any IOC resources, such as obtaining request frames. + * + * NOTES: The SCSI protocol driver currently calls this routine thrice + * in order to register separate callbacks; one for "normal" SCSI IO; + * one for MptScsiTaskMgmt requests; one for Scan/DV requests. + * + * Returns a positive integer valued "handle" in the + * range (and S.O.D. order) {N,...,7,6,5,...,1} if successful. + * Any non-positive return value (including zero!) should be considered + * an error by the caller. + */ +int +mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass) +{ + int i; + + last_drv_idx = -1; + + /* + * Search for empty callback slot in this order: {N,...,7,6,5,...,1} + * (slot/handle 0 is reserved!) + */ + for (i = MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) { + if (MptCallbacks[i] == NULL) { + MptCallbacks[i] = cbfunc; + MptDriverClass[i] = dclass; + MptEvHandlers[i] = NULL; + last_drv_idx = i; + break; + } + } + + return last_drv_idx; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_deregister - Deregister a protocol drivers resources. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine when it's + * module is unloaded. + */ +void +mpt_deregister(int cb_idx) +{ + if ((cb_idx >= 0) && (cb_idx < MPT_MAX_PROTOCOL_DRIVERS)) { + MptCallbacks[cb_idx] = NULL; + MptDriverClass[cb_idx] = MPTUNKNOWN_DRIVER; + MptEvHandlers[cb_idx] = NULL; + + last_drv_idx++; + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_event_register - Register protocol-specific event callback + * handler. + * @cb_idx: previously registered (via mpt_register) callback handle + * @ev_cbfunc: callback function + * + * This routine can be called by one or more protocol-specific drivers + * if/when they choose to be notified of MPT events. + * + * Returns 0 for success. + */ +int +mpt_event_register(int cb_idx, MPT_EVHANDLER ev_cbfunc) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return -1; + + MptEvHandlers[cb_idx] = ev_cbfunc; + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_event_deregister - Deregister protocol-specific event callback + * handler. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine + * when it does not (or can no longer) handle events, + * or when it's module is unloaded. + */ +void +mpt_event_deregister(int cb_idx) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return; + + MptEvHandlers[cb_idx] = NULL; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_reset_register - Register protocol-specific IOC reset handler. + * @cb_idx: previously registered (via mpt_register) callback handle + * @reset_func: reset function + * + * This routine can be called by one or more protocol-specific drivers + * if/when they choose to be notified of IOC resets. + * + * Returns 0 for success. + */ +int +mpt_reset_register(int cb_idx, MPT_RESETHANDLER reset_func) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return -1; + + MptResetHandlers[cb_idx] = reset_func; + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_reset_deregister - Deregister protocol-specific IOC reset handler. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine + * when it does not (or can no longer) handle IOC reset handling, + * or when it's module is unloaded. + */ +void +mpt_reset_deregister(int cb_idx) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return; + + MptResetHandlers[cb_idx] = NULL; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_device_driver_register - Register device driver hooks + */ +int +mpt_device_driver_register(struct mpt_pci_driver * dd_cbfunc, int cb_idx) +{ + MPT_ADAPTER *ioc; + int error=0; + + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) { + error= -EINVAL; + return error; + } + + MptDeviceDriverHandlers[cb_idx] = dd_cbfunc; + + /* call per pci device probe entry point */ + list_for_each_entry(ioc, &ioc_list, list) { + if(dd_cbfunc->probe) { + error = dd_cbfunc->probe(ioc->pcidev, + ioc->pcidev->driver->id_table); + if(error != 0) + return error; + } + } + + return error; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_device_driver_deregister - DeRegister device driver hooks + */ +void +mpt_device_driver_deregister(int cb_idx) +{ + struct mpt_pci_driver *dd_cbfunc; + MPT_ADAPTER *ioc; + + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return; + + dd_cbfunc = MptDeviceDriverHandlers[cb_idx]; + + list_for_each_entry(ioc, &ioc_list, list) { + if (dd_cbfunc->remove) + dd_cbfunc->remove(ioc->pcidev); + } + + MptDeviceDriverHandlers[cb_idx] = NULL; +} + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_get_msg_frame - Obtain a MPT request frame from the pool (of 1024) + * allocated per MPT adapter. + * @handle: Handle of registered MPT protocol driver + * @ioc: Pointer to MPT adapter structure + * + * Returns pointer to a MPT request frame or %NULL if none are available + * or IOC is not active. + */ +MPT_FRAME_HDR* +mpt_get_msg_frame(int handle, MPT_ADAPTER *ioc) +{ + MPT_FRAME_HDR *mf; + unsigned long flags; + u16 req_idx; /* Request index */ + + /* validate handle and ioc identifier */ + +#ifdef MFCNT + if (!ioc->active) + printk(KERN_WARNING "IOC Not Active! mpt_get_msg_frame returning NULL!\n"); +#endif + + /* If interrupts are not attached, do not return a request frame */ + if (!ioc->active) + return NULL; + + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (!list_empty(&ioc->FreeQ)) { + int req_offset; + + mf = list_entry(ioc->FreeQ.next, MPT_FRAME_HDR, + u.frame.linkage.list); + list_del(&mf->u.frame.linkage.list); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ + req_offset = (u8 *)mf - (u8 *)ioc->req_frames; + /* u16! */ + req_idx = cpu_to_le16(req_offset / ioc->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.req_idx = req_idx; + mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; + ioc->RequestNB[req_idx] = ioc->NB_for_64_byte_frame; /* Default, will be changed if necessary in SG generation */ +#ifdef MFCNT + ioc->mfcnt++; +#endif + } + else + mf = NULL; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + +#ifdef MFCNT + if (mf == NULL) + printk(KERN_WARNING "IOC Active. No free Msg Frames! Count 0x%x Max 0x%x\n", ioc->mfcnt, ioc->req_depth); + mfcounter++; + if (mfcounter == PRINT_MF_COUNT) + printk(KERN_INFO "MF Count 0x%x Max 0x%x \n", ioc->mfcnt, ioc->req_depth); +#endif + + dmfprintk((KERN_INFO MYNAM ": %s: mpt_get_msg_frame(%d,%d), got mf=%p\n", + ioc->name, handle, ioc->id, mf)); + return mf; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_put_msg_frame - Send a protocol specific MPT request frame + * to a IOC. + * @handle: Handle of registered MPT protocol driver + * @ioc: Pointer to MPT adapter structure + * @mf: Pointer to MPT request frame + * + * This routine posts a MPT request frame to the request post FIFO of a + * specific MPT adapter. + */ +void +mpt_put_msg_frame(int handle, MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) +{ + u32 mf_dma_addr; + int req_offset; + u16 req_idx; /* Request index */ + + /* ensure values are reset properly! */ + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ + req_offset = (u8 *)mf - (u8 *)ioc->req_frames; + /* u16! */ + req_idx = cpu_to_le16(req_offset / ioc->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.req_idx = req_idx; + mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; + +#ifdef MPT_DEBUG_MSG_FRAME + { + u32 *m = mf->u.frame.hwhdr.__hdr; + int ii, n; + + printk(KERN_INFO MYNAM ": %s: About to Put msg frame @ %p:\n" KERN_INFO " ", + ioc->name, m); + n = ioc->req_sz/4 - 1; + while (m[n] == 0) + n--; + for (ii=0; ii<=n; ii++) { + if (ii && ((ii%8)==0)) + printk("\n" KERN_INFO " "); + printk(" %08x", le32_to_cpu(m[ii])); + } + printk("\n"); + } +#endif + + mf_dma_addr = (ioc->req_frames_low_dma + req_offset) | ioc->RequestNB[req_idx]; + dsgprintk((MYIOC_s_INFO_FMT "mf_dma_addr=%x req_idx=%d RequestNB=%x\n", ioc->name, mf_dma_addr, req_idx, ioc->RequestNB[req_idx])); + CHIPREG_WRITE32(&ioc->chip->RequestFifo, mf_dma_addr); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_free_msg_frame - Place MPT request frame back on FreeQ. + * @handle: Handle of registered MPT protocol driver + * @ioc: Pointer to MPT adapter structure + * @mf: Pointer to MPT request frame + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +void +mpt_free_msg_frame(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf) +{ + unsigned long flags; + + /* Put Request back on FreeQ! */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + list_add_tail(&mf->u.frame.linkage.list, &ioc->FreeQ); +#ifdef MFCNT + ioc->mfcnt--; +#endif + spin_unlock_irqrestore(&ioc->FreeQlock, flags); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_add_sge - Place a simple SGE at address pAddr. + * @pAddr: virtual address for SGE + * @flagslength: SGE flags and data transfer length + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +void +mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGESimple64_t *pSge = (SGESimple64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pSge->Address.High = cpu_to_le32(tmp); + + } else { + SGESimple32_t *pSge = (SGESimple32_t *) pAddr; + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address = cpu_to_le32(dma_addr); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_send_handshake_request - Send MPT request via doorbell + * handshake method. + * @handle: Handle of registered MPT protocol driver + * @ioc: Pointer to MPT adapter structure + * @reqBytes: Size of the request in bytes + * @req: Pointer to MPT request frame + * @sleepFlag: Use schedule if CAN_SLEEP else use udelay. + * + * This routine is used exclusively to send MptScsiTaskMgmt + * requests since they are required to be sent via doorbell handshake. + * + * NOTE: It is the callers responsibility to byte-swap fields in the + * request which are greater than 1 byte in size. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt_send_handshake_request(int handle, MPT_ADAPTER *ioc, int reqBytes, u32 *req, int sleepFlag) +{ + int r = 0; + u8 *req_as_bytes; + int ii; + + /* State is known to be good upon entering + * this function so issue the bus reset + * request. + */ + + /* + * Emulate what mpt_put_msg_frame() does /wrt to sanity + * setting cb_idx/req_idx. But ONLY if this request + * is in proper (pre-alloc'd) request buffer range... + */ + ii = MFPTR_2_MPT_INDEX(ioc,(MPT_FRAME_HDR*)req); + if (reqBytes >= 12 && ii >= 0 && ii < ioc->req_depth) { + MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req; + mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(ii); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; + } + + /* Make sure there are no doorbells */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + CHIPREG_WRITE32(&ioc->chip->Doorbell, + ((MPI_FUNCTION_HANDSHAKE<<MPI_DOORBELL_FUNCTION_SHIFT) | + ((reqBytes/4)<<MPI_DOORBELL_ADD_DWORDS_SHIFT))); + + /* Wait for IOC doorbell int */ + if ((ii = WaitForDoorbellInt(ioc, 5, sleepFlag)) < 0) { + return ii; + } + + /* Read doorbell and check for active bit */ + if (!(CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_DOORBELL_ACTIVE)) + return -5; + + dhsprintk((KERN_INFO MYNAM ": %s: mpt_send_handshake_request start, WaitCnt=%d\n", + ioc->name, ii)); + + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) { + return -2; + } + + /* Send request via doorbell handshake */ + req_as_bytes = (u8 *) req; + for (ii = 0; ii < reqBytes/4; ii++) { + u32 word; + + word = ((req_as_bytes[(ii*4) + 0] << 0) | + (req_as_bytes[(ii*4) + 1] << 8) | + (req_as_bytes[(ii*4) + 2] << 16) | + (req_as_bytes[(ii*4) + 3] << 24)); + CHIPREG_WRITE32(&ioc->chip->Doorbell, word); + if ((r = WaitForDoorbellAck(ioc, 5, sleepFlag)) < 0) { + r = -3; + break; + } + } + + if (r >= 0 && WaitForDoorbellInt(ioc, 10, sleepFlag) >= 0) + r = 0; + else + r = -4; + + /* Make sure there are no doorbells */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_verify_adapter - Given a unique IOC identifier, set pointer to + * the associated MPT adapter structure. + * @iocid: IOC unique identifier (integer) + * @iocpp: Pointer to pointer to IOC adapter + * + * Returns iocid and sets iocpp. + */ +int +mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp) +{ + MPT_ADAPTER *ioc; + + list_for_each_entry(ioc,&ioc_list,list) { + if (ioc->id == iocid) { + *iocpp =ioc; + return iocid; + } + } + + *iocpp = NULL; + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptbase_probe - Install a PCI intelligent MPT adapter. + * @pdev: Pointer to pci_dev structure + * + * This routine performs all the steps necessary to bring the IOC of + * a MPT adapter to a OPERATIONAL state. This includes registering + * memory regions, registering the interrupt, and allocating request + * and reply memory pools. + * + * This routine also pre-fetches the LAN MAC address of a Fibre Channel + |