mpt2sas v00.100.11.15 : mpt2sas_base sources
PATCH 1/12 mpt2sas v00.100.11.15 : mpt2sas_base sources
mpt2sas_base
Common API layer interface to MPT (Message Passing Technology) firmware.
Signed-off-by Eric Moore <[email protected]>
---
drivers/scsi/mpt2sas/mpt2sas_base.c +3435 -0
@@ -0,0 +1,3435 @@ +/* +* This is the Fusion MPT base driver providing common API layer interface +* for access to MPT (Message Passing Technology) firmware. +* +* This code is based on drivers/scsi/mpt2sas/mpt2_base.c +* Copyright (C) 2007-2008 LSI Corporation +* (mailto:[email protected]) +* +* 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; either version 2 +* of the License, or (at your option) any later version. +* +* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +* USA. +*/ + +#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> +#include <linux/dma-mapping.h> +#include <linux/sort.h> +#include <linux/io.h> + +#include "mpt2sas_base.h" + +static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS ]; + +#define FAULT_POLLING_INTERVAL 1000/* in milliseconds */ +#define MPT2SAS_MAX_REQUEST_QUEUE 500/* maximum controller queue depth */ + +static int max_queue_depth =-1 ; +module_param (max_queue_depth, int,0 ); +MODULE_PARM_DESC (max_queue_depth," max controller queue depth " ); + +static int max_sgl_entries =-1 ; +module_param (max_sgl_entries, int,0 ); +MODULE_PARM_DESC (max_sgl_entries," max sg entries " ); + +static int msix_disable =-1 ; +module_param (msix_disable, int,0 ); +MODULE_PARM_DESC (msix_disable," disable msix routed interrupts (default=0)" ); + +/** +* _base_fault_reset_work - workq handling ioc fault conditions +* @work: input argument, used to derive ioc +* Context: sleep. +* +* Return nothing. +*/ +static void +_base_fault_reset_work (struct work_struct *work) +{ +struct MPT2SAS_ADAPTER *ioc = + container_of(work, struct MPT2SAS_ADAPTER, fault_reset_work.work); +unsigned long flags; +u32 doorbell; +int rc; + +spin_lock_irqsave (&ioc->ioc_reset_in_progress_lock, flags); +if (ioc->ioc_reset_in_progress) + goto rearm_timer; +spin_unlock_irqrestore (&ioc->ioc_reset_in_progress_lock, flags); + + doorbell =mpt2sas_base_get_iocstate (ioc,0 ); +if ((doorbell &MPI2_IOC_STATE_MASK ) ==MPI2_IOC_STATE_FAULT ) { + rc =mpt2sas_base_hard_reset_handler (ioc,CAN_SLEEP , +FORCE_BIG_HAMMER ); +printk (MPT2SAS_WARN_FMT "%s: hard reset: %s\n" , ioc->name, + __func__, (rc ==0 ) ?"success" :"failed" ); + doorbell =mpt2sas_base_get_iocstate (ioc,0 ); +if ((doorbell &MPI2_IOC_STATE_MASK ) ==MPI2_IOC_STATE_FAULT ) +mpt2sas_base_fault_info (ioc, doorbell & +MPI2_DOORBELL_DATA_MASK ); + } + +spin_lock_irqsave (&ioc->ioc_reset_in_progress_lock, flags); + rearm_timer: +if (ioc->fault_reset_work_q) +queue_delayed_work (ioc->fault_reset_work_q, + &ioc->fault_reset_work, +msecs_to_jiffies (FAULT_POLLING_INTERVAL )); +spin_unlock_irqrestore (&ioc->ioc_reset_in_progress_lock, flags); +} + +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +/** +* _base_sas_ioc_info - verbose translation of the ioc status +* @ioc: pointer to scsi command object +* @mpi_reply: reply mf payload returned from firmware +* @request_hdr: request mf +* +* Return nothing. +*/ +static void +_base_sas_ioc_info (struct MPT2SAS_ADAPTER *ioc,MPI2DefaultReply_t *mpi_reply, +MPI2RequestHeader_t *request_hdr) +{ +u16 ioc_status =le16_to_cpu (mpi_reply->IOCStatus) & +MPI2_IOCSTATUS_MASK ; +char *desc =NULL ; +u16 frame_sz; +char *func_str =NULL ; + +/* SCSI_IO, RAID_PASS are handled from _scsih_scsi_ioc_info */ +if (request_hdr->Function ==MPI2_FUNCTION_SCSI_IO_REQUEST || + request_hdr->Function ==MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH || + request_hdr->Function ==MPI2_FUNCTION_EVENT_NOTIFICATION ) +return ; + +switch (ioc_status) { + +/**************************************************************************** +* Common IOCStatus values for all replies +****************************************************************************/ + +case MPI2_IOCSTATUS_INVALID_FUNCTION : + desc ="invalid function" ; +break ; +case MPI2_IOCSTATUS_BUSY : + desc ="busy" ; +break ; +case MPI2_IOCSTATUS_INVALID_SGL : + desc ="invalid sgl" ; +break ; +case MPI2_IOCSTATUS_INTERNAL_ERROR : + desc ="internal error" ; +break ; +case MPI2_IOCSTATUS_INVALID_VPID : + desc ="invalid vpid" ; +break ; +case MPI2_IOCSTATUS_INSUFFICIENT_RESOURCES : + desc ="insufficient resources" ; +break ; +case MPI2_IOCSTATUS_INVALID_FIELD : + desc ="invalid field" ; +break ; +case MPI2_IOCSTATUS_INVALID_STATE : + desc ="invalid state" ; +break ; +case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED : + desc ="op state not supported" ; +break ; + +/**************************************************************************** +* Config IOCStatus values +****************************************************************************/ + +case MPI2_IOCSTATUS_CONFIG_INVALID_ACTION : + desc ="config invalid action" ; +break ; +case MPI2_IOCSTATUS_CONFIG_INVALID_TYPE : + desc ="config invalid type" ; +break ; +case MPI2_IOCSTATUS_CONFIG_INVALID_PAGE : + desc ="config invalid page" ; +break ; +case MPI2_IOCSTATUS_CONFIG_INVALID_DATA : + desc ="config invalid data" ; +break ; +case MPI2_IOCSTATUS_CONFIG_NO_DEFAULTS : + desc ="config no defaults" ; +break ; +case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT : + desc ="config cant commit" ; +break ; + +/**************************************************************************** +* SCSI IO Reply +****************************************************************************/ + +case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR : +case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE : +case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE : +case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN : +case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN : +case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR : +case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR : +case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED : +case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH : +case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED : +case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED : +case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED : +break ; + +/**************************************************************************** +* For use by SCSI Initiator and SCSI Target end-to-end data protection +****************************************************************************/ + +case MPI2_IOCSTATUS_EEDP_GUARD_ERROR : + desc ="eedp guard error" ; +break ; +case MPI2_IOCSTATUS_EEDP_REF_TAG_ERROR : + desc ="eedp ref tag error" ; +break ; +case MPI2_IOCSTATUS_EEDP_APP_TAG_ERROR : + desc ="eedp app tag error" ; +break ; + +/**************************************************************************** +* SCSI Target values +****************************************************************************/ + +case MPI2_IOCSTATUS_TARGET_INVALID_IO_INDEX : + desc ="target invalid io index" ; +break ; +case MPI2_IOCSTATUS_TARGET_ABORTED : + desc ="target aborted" ; +break ; +case MPI2_IOCSTATUS_TARGET_NO_CONN_RETRYABLE : + desc ="target no conn retryable" ; +break ; +case MPI2_IOCSTATUS_TARGET_NO_CONNECTION : + desc ="target no connection" ; +break ; +case MPI2_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH : + desc ="target xfer count mismatch" ; +break ; +case MPI2_IOCSTATUS_TARGET_DATA_OFFSET_ERROR : + desc ="target data offset error" ; +break ; +case MPI2_IOCSTATUS_TARGET_TOO_MUCH_WRITE_DATA : + desc ="target too much write data" ; +break ; +case MPI2_IOCSTATUS_TARGET_IU_TOO_SHORT : + desc ="target iu too short" ; +break ; +case MPI2_IOCSTATUS_TARGET_ACK_NAK_TIMEOUT : + desc ="target ack nak timeout" ; +break ; +case MPI2_IOCSTATUS_TARGET_NAK_RECEIVED : + desc ="target nak received" ; +break ; + +/**************************************************************************** +* Serial Attached SCSI values +****************************************************************************/ + +case MPI2_IOCSTATUS_SAS_SMP_REQUEST_FAILED : + desc ="smp request failed" ; +break ; +case MPI2_IOCSTATUS_SAS_SMP_DATA_OVERRUN : + desc ="smp data overrun" ; +break ; + +/**************************************************************************** +* Diagnostic Buffer Post / Diagnostic Release values +****************************************************************************/ + +case MPI2_IOCSTATUS_DIAGNOSTIC_RELEASED : + desc ="diagnostic released" ; +break ; +default : +break ; + } + +if (!desc) +return ; + +switch (request_hdr->Function) { +case MPI2_FUNCTION_CONFIG : + frame_sz =sizeof (Mpi2ConfigRequest_t) + ioc->sge_size; + func_str ="config_page" ; +break ; +case MPI2_FUNCTION_SCSI_TASK_MGMT : + frame_sz =sizeof (Mpi2SCSITaskManagementRequest_t); + func_str ="task_mgmt" ; +break ; +case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL : + frame_sz =sizeof (Mpi2SasIoUnitControlRequest_t); + func_str ="sas_iounit_ctl" ; +break ; +case MPI2_FUNCTION_SCSI_ENCLOSURE_PROCESSOR : + frame_sz =sizeof (Mpi2SepRequest_t); + func_str ="enclosure" ; +break ; +case MPI2_FUNCTION_IOC_INIT : + frame_sz =sizeof (Mpi2IOCInitRequest_t); + func_str ="ioc_init" ; +break ; +case MPI2_FUNCTION_PORT_ENABLE : + frame_sz =sizeof (Mpi2PortEnableRequest_t); + func_str ="port_enable" ; +break ; +case MPI2_FUNCTION_SMP_PASSTHROUGH : + frame_sz =sizeof (Mpi2SmpPassthroughRequest_t) + ioc->sge_size; + func_str ="smp_passthru" ; +break ; +default : + frame_sz =32 ; + func_str ="unknown" ; +break ; + } + +printk (MPT2SAS_WARN_FMT "ioc_status: %s(0x%04x), request(0x%p)," +" (%s)\n" , ioc->name, desc, ioc_status, request_hdr, func_str); + +_debug_dump_mf (request_hdr, frame_sz/4 ); +} + +/** +* _base_display_event_data - verbose translation of firmware asyn events +* @ioc: pointer to scsi command object +* @mpi_reply: reply mf payload returned from firmware +* +* Return nothing. +*/ +static void +_base_display_event_data (struct MPT2SAS_ADAPTER *ioc, +Mpi2EventNotificationReply_t *mpi_reply) +{ +char *desc =NULL ; +u16 event; + +if (!(ioc->logging_level &MPT_DEBUG_EVENTS )) +return ; + + event =le16_to_cpu (mpi_reply->Event); + +switch (event) { +case MPI2_EVENT_LOG_DATA : + desc ="Log Data" ; +break ; +case MPI2_EVENT_STATE_CHANGE : + desc ="Status Change" ; +break ; +case MPI2_EVENT_HARD_RESET_RECEIVED : + desc ="Hard Reset Received" ; +break ; +case MPI2_EVENT_EVENT_CHANGE : + desc ="Event Change" ; +break ; +case MPI2_EVENT_TASK_SET_FULL : + desc ="Task Set Full" ; +break ; +case MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE : + desc ="Device Status Change" ; +break ; +case MPI2_EVENT_IR_OPERATION_STATUS : + desc ="IR Operation Status" ; +break ; +case MPI2_EVENT_SAS_DISCOVERY : + desc ="Discovery" ; +break ; +case MPI2_EVENT_SAS_BROADCAST_PRIMITIVE : + desc ="SAS Broadcast Primitive" ; +break ; +case MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE : + desc ="SAS Init Device Status Change" ; +break ; +case MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW : + desc ="SAS Init Table Overflow" ; +break ; +case MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST : + desc ="SAS Topology Change List" ; +break ; +case MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE : + desc ="SAS Enclosure Device Status Change" ; +break ; +case MPI2_EVENT_IR_VOLUME : + desc ="IR Volume" ; +break ; +case MPI2_EVENT_IR_PHYSICAL_DISK : + desc ="IR Physical Disk" ; +break ; +case MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST : + desc ="IR Configuration Change List" ; +break ; +case MPI2_EVENT_LOG_ENTRY_ADDED : + desc ="Log Entry Added" ; +break ; + } + +if (!desc) +return ; + +printk (MPT2SAS_INFO_FMT "%s\n" , ioc->name, desc); +} +#endif + +/** +* _base_sas_log_info - verbose translation of firmware log info +* @ioc: pointer to scsi command object +* @log_info: log info +* +* Return nothing. +*/ +static void +_base_sas_log_info (struct MPT2SAS_ADAPTER *ioc ,u32 log_info) +{ +union loginfo_type { +u32 loginfo; +struct { +u32 subcode:16 ; +u32 code:8 ; +u32 originator:4 ; +u32 bus_type:4 ; + } dw; + }; +union loginfo_type sas_loginfo; +char *originator_str =NULL ; + + sas_loginfo.loginfo = log_info; +if (sas_loginfo.dw.bus_type !=3 /*SAS*/ ) +return ; + +/* eat the loginfos associated with task aborts */ +if (ioc->ignore_loginfos && (log_info ==30050000 || log_info == +0x31140000 || log_info ==0x31130000 )) +return ; + +switch (sas_loginfo.dw.originator) { +case 0 : + originator_str ="IOP" ; +break ; +case 1 : + originator_str ="PL" ; +break ; +case 2 : + originator_str ="IR" ; +break ; + } + +printk (MPT2SAS_WARN_FMT "log_info(0x%08x): originator(%s), " +"code(0x%02x), sub_code(0x%04x)\n" , ioc->name, log_info, + originator_str, sas_loginfo.dw.code, + sas_loginfo.dw.subcode); +} + +/** +* mpt2sas_base_fault_info - verbose translation of firmware FAULT code +* @ioc: pointer to scsi command object +* @fault_code: fault code +* +* Return nothing. +*/ +void +mpt2sas_base_fault_info (struct MPT2SAS_ADAPTER *ioc ,u16 fault_code) +{ +printk (MPT2SAS_ERR_FMT "fault_state(0x%04x)!\n" , + ioc->name, fault_code); +} + +/** +* _base_display_reply_info - +* @ioc: pointer to scsi command object +* @smid: system request message index +* @VF_ID: virtual function id +* @reply: reply message frame(lower 32bit addr) +* +* Return nothing. +*/ +static void +_base_display_reply_info (struct MPT2SAS_ADAPTER *ioc,u16 smid,u8 VF_ID , +u32 reply) +{ +MPI2DefaultReply_t *mpi_reply; +u16 ioc_status; + + mpi_reply =mpt2sas_base_get_reply_virt_addr (ioc, reply); + ioc_status =le16_to_cpu (mpi_reply->IOCStatus); +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +if ((ioc_status &MPI2_IOCSTATUS_MASK ) && + (ioc->logging_level &MPT_DEBUG_REPLY )) { +_base_sas_ioc_info (ioc , mpi_reply, +mpt2sas_base_get_msg_frame (ioc, smid)); + } +#endif +if (ioc_status &MPI2_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE ) +_base_sas_log_info (ioc,le32_to_cpu (mpi_reply->IOCLogInfo)); +} + +/** +* mpt2sas_base_done - base internal command completion routine +* @ioc: pointer to scsi command object +* @smid: system request message index +* @VF_ID: virtual function id +* @reply: reply message frame(lower 32bit addr) +* +* Return nothing. +*/ +void +mpt2sas_base_done (struct MPT2SAS_ADAPTER *ioc,u16 smid,u8 VF_ID ,u32 reply) +{ +MPI2DefaultReply_t *mpi_reply; + + mpi_reply =mpt2sas_base_get_reply_virt_addr (ioc, reply); +if (mpi_reply && mpi_reply->Function ==MPI2_FUNCTION_EVENT_ACK ) +return ; + +if (ioc->base_cmds.status ==MPT2_CMD_NOT_USED ) +return ; + + ioc->base_cmds.status |=MPT2_CMD_COMPLETE ; +if (mpi_reply) { + ioc->base_cmds.status |=MPT2_CMD_REPLY_VALID ; +memcpy (ioc->base_cmds.reply, mpi_reply, mpi_reply->MsgLength*4 ); + } + ioc->base_cmds.status &= ~MPT2_CMD_PENDING ; +complete (&ioc->base_cmds.done); +} + +/** +* _base_async_event - main callback handler for firmware asyn events +* @ioc: pointer to scsi command object +* @VF_ID: virtual function id +* @reply: reply message frame(lower 32bit addr) +* +* Return nothing. +*/ +static void +_base_async_event (struct MPT2SAS_ADAPTER *ioc,u8 VF_ID ,u32 reply) +{ +Mpi2EventNotificationReply_t *mpi_reply; +Mpi2EventAckRequest_t *ack_request; +u16 smid; + + mpi_reply =mpt2sas_base_get_reply_virt_addr (ioc, reply); +if (!mpi_reply) +return ; +if (mpi_reply->Function !=MPI2_FUNCTION_EVENT_NOTIFICATION ) +return ; +#ifdef CONFIG_SCSI_MPT2SAS_LOGGING +_base_display_event_data (ioc, mpi_reply); +#endif +if (!(mpi_reply->AckRequired &MPI2_EVENT_NOTIFICATION_ACK_REQUIRED )) + goto out; + smid =mpt2sas_base_get_smid (ioc, ioc->base_cb_idx); +if (!smid) { +printk (MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n" , + ioc->name, __func__); + goto out; + } + + ack_request =mpt2sas_base_get_msg_frame (ioc, smid); +memset (ack_request,0 ,sizeof (Mpi2EventAckRequest_t)); + ack_request->Function =MPI2_FUNCTION_EVENT_ACK ; + ack_request->Event = mpi_reply->Event; + ack_request->EventContext = mpi_reply->EventContext; + ack_request->VF_ID =VF_ID ; +mpt2sas_base_put_smid_default (ioc, smid,VF_ID ); + + out: + +/* scsih callback handler */ +mpt2sas_scsih_event_callback (ioc,VF_ID , reply); + +/* ctl callback handler */ +mpt2sas_ctl_event_callback (ioc,VF_ID , reply); +} + +/** +* _base_mask_interrupts - disable interrupts +* @ioc: pointer to scsi command object +* +* Disabling ResetIRQ, Reply and Doorbell Interrupts +* +* Return nothing. +*/ +static void +_base_mask_interrupts (struct MPT2SAS_ADAPTER *ioc) +{ +u32 him_register; + + ioc->mask_interrupts =1 ; + him_register =readl (&ioc->chip->HostInterruptMask); + him_register |=MPI2_HIM_DIM +MPI2_HIM_RIM +MPI2_HIM_RESET_IRQ_MASK ; +writel (him_register, &ioc->chip->HostInterruptMask); +readl (&ioc->chip->HostInterruptMask); +} + +/** +* _base_unmask_interrupts - enable interrupts +* @ioc: pointer to scsi command object +* +* Enabling only Reply Interrupts +* +* Return nothing. +*/ +static void +_base_unmask_interrupts (struct MPT2SAS_ADAPTER *ioc) +{ +u32 him_register; + +writel (0 , &ioc->chip->HostInterruptStatus); + him_register =readl (&ioc->chip->HostInterruptMask); + him_register &= ~MPI2_HIM_RIM ; +writel (him_register, &ioc->chip->HostInterruptMask); + ioc->mask_interrupts =0 ; +} + +/** +* _base_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) +* +* Return IRQ_HANDLE if processed, else IRQ_NONE. +*/ +static irqreturn_t +_base_interrupt (int irq,void *bus_id) +{ +u32 post_index, post_index_next, completed_cmds; +u8 request_desript_type; +u16 smid; +u8 cb_idx; +u32 reply; +u8 VF_ID ; +int i; +struct MPT2SAS_ADAPTER *ioc = bus_id; + +if (ioc->mask_interrupts) +return IRQ_NONE ; + + post_index = ioc->reply_post_host_index; + request_desript_type = ioc->reply_post_free[post_index]. + Default.ReplyFlags &MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK ; +if (request_desript_type ==MPI2_RPY_DESCRIPT_FLAGS_UNUSED ) +return IRQ_NONE ; + + completed_cmds =0 ; +do { +if (ioc->reply_post_free[post_index].Words == ~0ULL ) + goto out; + reply =0 ; + cb_idx =0xFF ; + smid =le16_to_cpu (ioc->reply_post_free[post_index]. + Default.DescriptorTypeDependent1); +VF_ID = ioc->reply_post_free[post_index]. + Default.VF_ID; +if (request_desript_type == +MPI2_RPY_DESCRIPT_FLAGS_ADDRESS_REPLY ) { + reply =le32_to_cpu (ioc->reply_post_free[post_index]. + AddressReply.ReplyFrameAddress); + }else if (request_desript_type == +MPI2_RPY_DESCRIPT_FLAGS_TARGET_COMMAND_BUFFER ) + goto next; +else if (request_desript_type == +MPI2_RPY_DESCRIPT_FLAGS_TARGETASSIST_SUCCESS ) + goto next; +if (smid) + cb_idx = ioc->scsi_lookup[smid -1 ].cb_idx; +if (smid && cb_idx !=0xFF ) { + mpt_callbacks[cb_idx](ioc, smid,VF_ID , reply); +if (reply) +_base_display_reply_info (ioc, smid,VF_ID , + reply); +mpt2sas_base_free_smid (ioc, smid); + } +if (!smid) +_base_async_event (ioc,VF_ID , reply); + +/* reply free queue handling */ +if (reply) { + ioc->reply_free_host_index = + (ioc->reply_free_host_index == + (ioc->reply_free_queue_depth -1 )) ? +0 : ioc->reply_free_host_index +1 ; + ioc->reply_free[ioc->reply_free_host_index] = +cpu_to_le32 (reply); +writel (ioc->reply_free_host_index, + &ioc->chip->ReplyFreeHostIndex); +wmb (); + } + + next: + post_index_next = (post_index == (ioc->reply_post_queue_depth - +1 )) ?0 : post_index +1 ; + request_desript_type = + ioc->reply_post_free[post_index_next].Default.ReplyFlags + &MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK ; + completed_cmds++; +if (request_desript_type ==MPI2_RPY_DESCRIPT_FLAGS_UNUSED ) + goto out; + post_index = post_index_next; + }while (1 ); + + out: + +if (!completed_cmds) +return IRQ_NONE ; + +/* reply post descriptor handling */ + post_index_next = ioc->reply_post_host_index; +for (i =0 ; i < completed_cmds; i++) { + post_index = post_index_next; +/* poison the reply post descriptor */ + ioc->reply_post_free[post_index_next].Words = ~0ULL ; + post_index_next = (post_index == + (ioc->reply_post_queue_depth -1 )) + ?0 : post_index +1 ; + } + ioc->reply_post_host_index = post_index_next; +writel (post_index_next, &ioc->chip->ReplyPostHostIndex); +wmb (); +return IRQ_HANDLED ; +} + +/** +* mpt2sas_base_release_callback_handler - clear interupt callback handler +* @cb_idx: callback index +* +* Return nothing. +*/ +void +mpt2sas_base_release_callback_handler (u8 cb_idx) +{ + mpt_callbacks[cb_idx] =NULL ; +} + +/** +* mpt2sas_base_register_callback_handler - obtain index for the interrupt callback handler +* @cb_func: callback function +* +* Returns cb_func. +*/ +u8 +mpt2sas_base_register_callback_handler (MPT_CALLBACK cb_func) +{ +u8 cb_idx; + +for (cb_idx =MPT_MAX_CALLBACKS -1 ; cb_idx; cb_idx--) +if (mpt_callbacks[cb_idx] ==NULL ) +break ; + + mpt_callbacks[cb_idx] = cb_func; +return cb_idx; +} + +/** +* mpt2sas_base_initialize_callback_handler - initialize the interrupt callback handler +* +* Return nothing. +*/ +void +mpt2sas_base_initialize_callback_handler (void ) +{ +u8 cb_idx; + +for (cb_idx =0 ; cb_idx <MPT_MAX_CALLBACKS ; cb_idx++) +mpt2sas_base_release_callback_handler (cb_idx); +} + +/** +* mpt2sas_base_build_zero_len_sge - build zero length sg entry +* @ioc: per adapter object +* @paddr: virtual address for SGE +* +* Create a zero length scatter gather entry to insure the IOCs hardware has +* something to use if the target device goes brain dead and tries +* to send data even when none is asked for. +* +* Return nothing. +*/ +void +mpt2sas_base_build_zero_len_sge (struct MPT2SAS_ADAPTER *ioc,void *paddr) +{ +u32 flags_length = (u32)((MPI2_SGE_FLAGS_LAST_ELEMENT | +MPI2_SGE_FLAGS_END_OF_BUFFER |MPI2_SGE_FLAGS_END_OF_LIST | +MPI2_SGE_FLAGS_SIMPLE_ELEMENT ) << +MPI2_SGE_FLAGS_SHIFT ); + ioc->base_add_sg_single (paddr, flags_length,-1 ); +} + +/** +* _base_add_sg_single_32 - Place a simple 32 bit SGE at address pAddr. +* @paddr: virtual address for SGE +* @flags_length: SGE flags and data transfer length +* @dma_addr: Physical address +* +* Return nothing. +*/ +static void +_base_add_sg_single_32 (void *paddr,u32 flags_length,dma_addr_t dma_addr) +{ +Mpi2SGESimple32_t *sgel = paddr; + + flags_length |= (MPI2_SGE_FLAGS_32_BIT_ADDRESSING | +MPI2_SGE_FLAGS_SYSTEM_ADDRESS ) <<MPI2_SGE_FLAGS_SHIFT ; + sgel->FlagsLength =cpu_to_le32 (flags_length); + sgel->Address =cpu_to_le32 (dma_addr); +} + + +/** +* _base_add_sg_single_64 - Place a simple 64 bit SGE at address pAddr. +* @paddr: virtual address for SGE +* @flags_length: SGE flags and data transfer length +* @dma_addr: Physical address +* +* Return nothing. +*/ +static void +_base_add_sg_single_64 (void *paddr,u32 flags_length,dma_addr_t dma_addr) +{ +Mpi2SGESimple64_t *sgel = paddr; + + flags_length |= (MPI2_SGE_FLAGS_64_BIT_ADDRESSING | +MPI2_SGE_FLAGS_SYSTEM_ADDRESS ) <<MPI2_SGE_FLAGS_SHIFT ; + sgel->FlagsLength =cpu_to_le32 (flags_length); + sgel->Address =cpu_to_le64 (dma_addr); +} + +#define convert_to_kb (x) ((x) << (PAGE_SHIFT - 10)) + +/** +* _base_config_dma_addressing - set dma addressing +* @ioc: per adapter object +* @pdev: PCI device struct +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_config_dma_addressing (struct MPT2SAS_ADAPTER *ioc,struct pci_dev *pdev) +{ +struct sysinfo s; +char *desc =NULL ; + +if (sizeof (dma_addr_t) >4 ) { +const uint64_t required_mask = +dma_get_required_mask (&pdev->dev); +if ((required_mask >DMA_32BIT_MASK ) && !pci_set_dma_mask (pdev, +DMA_64BIT_MASK ) && !pci_set_consistent_dma_mask (pdev, +DMA_64BIT_MASK )) { + ioc->base_add_sg_single = &_base_add_sg_single_64; + ioc->sge_size =sizeof (Mpi2SGESimple64_t); + desc ="64" ; + goto out; + } + } + +if (!pci_set_dma_mask (pdev,DMA_32BIT_MASK ) + && !pci_set_consistent_dma_mask (pdev,DMA_32BIT_MASK )) { + ioc->base_add_sg_single = &_base_add_sg_single_32; + ioc->sge_size =sizeof (Mpi2SGESimple32_t); + desc ="32" ; + }else +return -ENODEV ; + + out: +si_meminfo (&s); +printk (MPT2SAS_INFO_FMT "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, " +"total mem (%ld kB)\n" , ioc->name, desc,convert_to_kb (s.totalram)); + +return 0 ; +} + +/** +* _base_save_msix_table - backup msix vector table +* @ioc: per adapter object +* +* This address an errata where diag reset clears out the table +*/ +static void +_base_save_msix_table (struct MPT2SAS_ADAPTER *ioc) +{ +int i; + +if (!ioc->msix_enable || ioc->msix_table_backup ==NULL ) +return ; + +for (i =0 ; i < ioc->msix_vector_count; i++) + ioc->msix_table_backup[i] = ioc->msix_table[i]; +} + +/** +* _base_restore_msix_table - this restores the msix vector table +* @ioc: per adapter object +* +*/ +static void +_base_restore_msix_table (struct MPT2SAS_ADAPTER *ioc) +{ +int i; + +if (!ioc->msix_enable || ioc->msix_table_backup ==NULL ) +return ; + +for (i =0 ; i < ioc->msix_vector_count; i++) + ioc->msix_table[i] = ioc->msix_table_backup[i]; +} + +/** +* _base_check_enable_msix - checks MSIX capabable. +* @ioc: per adapter object +* +* Check to see if card is capable of MSIX, and set number +* of avaliable msix vectors +*/ +static int +_base_check_enable_msix (struct MPT2SAS_ADAPTER *ioc) +{ +int base; +u16 message_control; +u32 msix_table_offset; + + base =pci_find_capability (ioc->pdev,PCI_CAP_ID_MSIX ); +if (!base) { +dfailprintk (ioc,printk (MPT2SAS_INFO_FMT "msix not " +"supported\n" , ioc->name)); +return -EINVAL ; + } + +/* get msix vector count */ +pci_read_config_word (ioc->pdev, base +2 , &message_control); + ioc->msix_vector_count = (message_control &0x3FF ) +1 ; + +/* get msix table */ +pci_read_config_dword (ioc->pdev, base +4 , &msix_table_offset); + msix_table_offset &=0xFFFFFFF8 ; + ioc->msix_table = (u32 *)((void *)ioc->chip + msix_table_offset); + +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "msix is supported, " +"vector_count(%d), table_offset(0x%08x), table(%p)\n" , ioc->name, + ioc->msix_vector_count, msix_table_offset, ioc->msix_table)); +return 0 ; +} + +/** +* _base_disable_msix - disables msix +* @ioc: per adapter object +* +*/ +static void +_base_disable_msix (struct MPT2SAS_ADAPTER *ioc) +{ +if (ioc->msix_enable) { +pci_disable_msix (ioc->pdev); +kfree (ioc->msix_table_backup); + ioc->msix_table_backup =NULL ; + ioc->msix_enable =0 ; + } +} + +/** +* _base_enable_msix - enables msix, failback to io_apic +* @ioc: per adapter object +* +*/ +static int +_base_enable_msix (struct MPT2SAS_ADAPTER *ioc) +{ +struct msix_entry entries; +int r; +u8 try_msix =0 ; + +if (msix_disable ==-1 || msix_disable ==0 ) + try_msix =1 ; + +if (!try_msix) + goto try_ioapic; + +if (_base_check_enable_msix (ioc) !=0 ) + goto try_ioapic; + + ioc->msix_table_backup =kcalloc (ioc->msix_vector_count, +sizeof (u32),GFP_KERNEL ); +if (!ioc->msix_table_backup) { +dfailprintk (ioc,printk (MPT2SAS_INFO_FMT "allocation for " +"msix_table_backup failed!!!\n" , ioc->name)); + goto try_ioapic; + } + +memset (&entries,0 ,sizeof (struct msix_entry )); + r =pci_enable_msix (ioc->pdev, &entries,1 ); +if (r) { +dfailprintk (ioc,printk (MPT2SAS_INFO_FMT "pci_enable_msix " +"failed (r=%d) !!!\n" , ioc->name, r)); + goto try_ioapic; + } + + r =request_irq (entries.vector, _base_interrupt,IRQF_SHARED , + ioc->name, ioc); +if (r) { +dfailprintk (ioc,printk (MPT2SAS_INFO_FMT "unable to allocate " +"interrupt %d !!!\n" , ioc->name, entries.vector)); +pci_disable_msix (ioc->pdev); + goto try_ioapic; + } + + ioc->pci_irq = entries.vector; + ioc->msix_enable =1 ; +return 0 ; + +/* failback to io_apic interrupt routing */ + try_ioapic: + + r =request_irq (ioc->pdev->irq, _base_interrupt,IRQF_SHARED , + ioc->name, ioc); +if (r) { +printk (MPT2SAS_ERR_FMT "unable to allocate interrupt %d!\n" , + ioc->name, ioc->pdev->irq); + r = -EBUSY ; + goto out_fail; + } + + ioc->pci_irq = ioc->pdev->irq; +return 0 ; + + out_fail: +return r; +} + +/** +* mpt2sas_base_map_resources - map in controller resources (io/irq/memap) +* @ioc: per adapter object +* +* Returns 0 for success, non-zero for failure. +*/ +int +mpt2sas_base_map_resources (struct MPT2SAS_ADAPTER *ioc) +{ +struct pci_dev *pdev = ioc->pdev; +u32 memap_sz; +u32 pio_sz; +int i, r =0 ; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , + ioc->name, __func__)); + + ioc->bars =pci_select_bars (pdev,IORESOURCE_MEM ); +if (pci_enable_device_mem (pdev)) { +printk (MPT2SAS_WARN_FMT "pci_enable_device_mem: " +"failed\n" , ioc->name); +return -ENODEV ; + } + + +if (pci_request_selected_regions (pdev, ioc->bars, +MPT2SAS_DRIVER_NAME )) { +printk (MPT2SAS_WARN_FMT "pci_request_selected_regions: " +"failed\n" , ioc->name); + r = -ENODEV ; + goto out_fail; + } + +pci_set_master (pdev); + +if (_base_config_dma_addressing (ioc, pdev) !=0 ) { +printk (MPT2SAS_WARN_FMT "no suitable DMA mask for %s\n" , + ioc->name,pci_name (pdev)); + r = -ENODEV ; + goto out_fail; + } + +for (i =0 , memap_sz =0 , pio_sz =0 ; i <DEVICE_COUNT_RESOURCE ; i++) { +if (pci_resource_flags (pdev, i) &PCI_BASE_ADDRESS_SPACE_IO ) { +if (pio_sz) +continue ; + ioc->pio_chip =pci_resource_start (pdev, i); + pio_sz =pci_resource_len (pdev, i); + }else { +if (memap_sz) +continue ; + ioc->chip_phys =pci_resource_start (pdev, i); + memap_sz =pci_resource_len (pdev, i); + ioc->chip =ioremap (ioc->chip_phys, memap_sz); +if (ioc->chip ==NULL ) { +printk (MPT2SAS_ERR_FMT "unable to map adapter " +"memory!\n" , ioc->name); + r = -EINVAL ; + goto out_fail; + } + } + } + +pci_set_drvdata (pdev, ioc->shost); +_base_mask_interrupts (ioc); + r =_base_enable_msix (ioc); +if (r) + goto out_fail; + +printk (MPT2SAS_INFO_FMT "%s: IRQ %d\n" , + ioc->name, ((ioc->msix_enable) ?"PCI-MSI-X enabled" : +"IO-APIC enabled" ), ioc->pci_irq); +printk (MPT2SAS_INFO_FMT "iomem(0x%lx), mapped(0x%p), size(%d)\n" , + ioc->name, ioc->chip_phys, ioc->chip, memap_sz); +printk (MPT2SAS_INFO_FMT "ioport(0x%lx), size(%d)\n" , + ioc->name, ioc->pio_chip, pio_sz); + +return 0 ; + + out_fail: +if (ioc->chip_phys) +iounmap (ioc->chip); + ioc->chip_phys =0 ; + ioc->pci_irq =-1 ; +pci_release_selected_regions (ioc->pdev, ioc->bars); +pci_disable_device (pdev); +pci_set_drvdata (pdev,NULL ); +return r; +} + +/** +* mpt2sas_base_get_msg_frame_dma - obtain request mf pointer phys addr +* @ioc: per adapter object +* @smid: system request message index(smid zero is invalid) +* +* Returns phys pointer to message frame. +*/ +dma_addr_t +mpt2sas_base_get_msg_frame_dma (struct MPT2SAS_ADAPTER *ioc,u16 smid) +{ +return ioc->request_dma + (smid * ioc->request_sz); +} + +/** +* mpt2sas_base_get_msg_frame - obtain request mf pointer +* @ioc: per adapter object +* @smid: system request message index(smid zero is invalid) +* +* Returns virt pointer to message frame. +*/ +void * +mpt2sas_base_get_msg_frame (struct MPT2SAS_ADAPTER *ioc,u16 smid) +{ +return (void *)(ioc->request + (smid * ioc->request_sz)); +} + +/** +* mpt2sas_base_get_sense_buffer - obtain a sense buffer assigned to a mf request +* @ioc: per adapter object +* @smid: system request message index +* +* Returns virt pointer to sense buffer. +*/ +void * +mpt2sas_base_get_sense_buffer (struct MPT2SAS_ADAPTER *ioc,u16 smid) +{ +return (void *)(ioc->sense + ((smid -1 ) *SCSI_SENSE_BUFFERSIZE )); +} + +/** +* mpt2sas_base_get_sense_buffer_dma - obtain a sense buffer assigned to a mf request +* @ioc: per adapter object +* @smid: system request message index +* +* Returns phys pointer to sense buffer. +*/ +dma_addr_t +mpt2sas_base_get_sense_buffer_dma (struct MPT2SAS_ADAPTER *ioc,u16 smid) +{ +return ioc->sense_dma + ((smid -1 ) *SCSI_SENSE_BUFFERSIZE ); +} + +/** +* mpt2sas_base_get_reply_virt_addr - obtain reply frames virt address +* @ioc: per adapter object +* @phys_addr: lower 32 physical addr of the reply +* +* Converts 32bit lower physical addr into a virt address. +*/ +void * +mpt2sas_base_get_reply_virt_addr (struct MPT2SAS_ADAPTER *ioc,u32 phys_addr) +{ +if (!phys_addr) +return NULL ; +return ioc->reply + (phys_addr - (u32 )ioc->reply_dma); +} + +/** +* mpt2sas_base_get_smid - obtain a free smid +* @ioc: per adapter object +* @cb_idx: callback index +* +* Returns smid (zero is invalid) +*/ +u16 +mpt2sas_base_get_smid (struct MPT2SAS_ADAPTER *ioc,u8 cb_idx) +{ +unsigned long flags; +struct request_tracker *request; +u16 smid; + +spin_lock_irqsave (&ioc->scsi_lookup_lock, flags); +if (list_empty (&ioc->free_list)) { +spin_unlock_irqrestore (&ioc->scsi_lookup_lock, flags); +printk (MPT2SAS_ERR_FMT "%s: smid not available\n" , + ioc->name, __func__); +return 0 ; + } + + request =list_entry (ioc->free_list.next, + struct request_tracker, tracker_list); + request->cb_idx = cb_idx; + smid = request->smid; +list_del (&request->tracker_list); +spin_unlock_irqrestore (&ioc->scsi_lookup_lock, flags); +return smid; +} + + +/** +* mpt2sas_base_free_smid - put smid back on free_list +* @ioc: per adapter object +* @smid: system request message index +* +* Return nothing. +*/ +void +mpt2sas_base_free_smid (struct MPT2SAS_ADAPTER *ioc,u16 smid) +{ +unsigned long flags; + +spin_lock_irqsave (&ioc->scsi_lookup_lock, flags); + ioc->scsi_lookup[smid -1 ].cb_idx =0xFF ; +list_add_tail (&ioc->scsi_lookup[smid -1 ].tracker_list, + &ioc->free_list); +spin_unlock_irqrestore (&ioc->scsi_lookup_lock, flags); + +/* +* See _wait_for_commands_to_complete() call with regards to this code. +*/ +if (ioc->shost_recovery && ioc->pending_io_count) { +if (ioc->pending_io_count ==1 ) +wake_up (&ioc->reset_wq); + ioc->pending_io_count--; + } +} + +/** +* _base_writeq - 64 bit write to MMIO +* @ioc: per adapter object +* @b: data payload +* @addr: address in MMIO space +* @writeq_lock: spin lock +* +* Glue for handling an atomic 64 bit word to MMIO. This special handling takes +* care of 32 bit environment where its not quarenteed to send the entire word +* in one transfer. +*/ +#ifndef writeq +static inline void _base_writeq (__u64 b,volatile void __iomem *addr, +spinlock_t *writeq_lock) +{ +unsigned long flags; +__u64 data_out =cpu_to_le64 (b); + +spin_lock_irqsave (writeq_lock, flags); +writel ((u32)(data_out), addr); +writel ((u32)(data_out >>32 ), (addr +4 )); +spin_unlock_irqrestore (writeq_lock, flags); +} +#else +static inline void _base_writeq (__u64 b,volatile void __iomem *addr, +spinlock_t *writeq_lock) +{ +writeq (cpu_to_le64 (b), addr); +} +#endif + +/** +* mpt2sas_base_put_smid_scsi_io - send SCSI_IO request to firmware +* @ioc: per adapter object +* @smid: system request message index +* @vf_id: virtual function id +* @handle: device handle +* +* Return nothing. +*/ +void +mpt2sas_base_put_smid_scsi_io (struct MPT2SAS_ADAPTER *ioc,u16 smid,u8 vf_id, +u16 handle) +{ +Mpi2RequestDescriptorUnion_t descriptor; +u64 *request = (u64 *)&descriptor; + + + descriptor.SCSIIO.RequestFlags =MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO ; + descriptor.SCSIIO.VF_ID = vf_id; + descriptor.SCSIIO.SMID =cpu_to_le16 (smid); + descriptor.SCSIIO.DevHandle =cpu_to_le16 (handle); + descriptor.SCSIIO.LMID =0 ; +_base_writeq (*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + + +/** +* mpt2sas_base_put_smid_hi_priority - send Task Managment request to firmware +* @ioc: per adapter object +* @smid: system request message index +* @vf_id: virtual function id +* +* Return nothing. +*/ +void +mpt2sas_base_put_smid_hi_priority (struct MPT2SAS_ADAPTER *ioc,u16 smid, +u8 vf_id) +{ +Mpi2RequestDescriptorUnion_t descriptor; +u64 *request = (u64 *)&descriptor; + + descriptor.HighPriority.RequestFlags = +MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY ; + descriptor.HighPriority.VF_ID = vf_id; + descriptor.HighPriority.SMID =cpu_to_le16 (smid); + descriptor.HighPriority.LMID =0 ; + descriptor.HighPriority.Reserved1 =0 ; +_base_writeq (*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + +/** +* mpt2sas_base_put_smid_default - Default, primarily used for config pages +* @ioc: per adapter object +* @smid: system request message index +* @vf_id: virtual function id +* +* Return nothing. +*/ +void +mpt2sas_base_put_smid_default (struct MPT2SAS_ADAPTER *ioc,u16 smid,u8 vf_id) +{ +Mpi2RequestDescriptorUnion_t descriptor; +u64 *request = (u64 *)&descriptor; + + descriptor.Default.RequestFlags =MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE ; + descriptor.Default.VF_ID = vf_id; + descriptor.Default.SMID =cpu_to_le16 (smid); + descriptor.Default.LMID =0 ; + descriptor.Default.DescriptorTypeDependent =0 ; +_base_writeq (*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + +/** +* mpt2sas_base_put_smid_target_assist - send Target Assist/Status to firmware +* @ioc: per adapter object +* @smid: system request message index +* @vf_id: virtual function id +* @io_index: value used to track the IO +* +* Return nothing. +*/ +void +mpt2sas_base_put_smid_target_assist (struct MPT2SAS_ADAPTER *ioc,u16 smid, +u8 vf_id,u16 io_index) +{ +Mpi2RequestDescriptorUnion_t descriptor; +u64 *request = (u64 *)&descriptor; + + descriptor.SCSITarget.RequestFlags = +MPI2_REQ_DESCRIPT_FLAGS_SCSI_TARGET ; + descriptor.SCSITarget.VF_ID = vf_id; + descriptor.SCSITarget.SMID =cpu_to_le16 (smid); + descriptor.SCSITarget.LMID =0 ; + descriptor.SCSITarget.IoIndex =cpu_to_le16 (io_index); +_base_writeq (*request, &ioc->chip->RequestDescriptorPostLow, + &ioc->scsi_lookup_lock); +} + +/** +* _base_display_ioc_capabilities - Disply IOC's capabilities. +* @ioc: per adapter object +* +* Return nothing. +*/ +static void +_base_display_ioc_capabilities (struct MPT2SAS_ADAPTER *ioc) +{ +int i =0 ; +char desc[16 ]; +u8 revision; +u32 iounit_pg1_flags; + +pci_read_config_byte (ioc->pdev,PCI_CLASS_REVISION , &revision); +strncpy (desc, ioc->manu_pg0.ChipName,16 ); +printk (MPT2SAS_INFO_FMT "%s: FWVersion(%02d.%02d.%02d.%02d), " +"ChipRevision(0x%02x), BiosVersion(%02d.%02d.%02d.%02d)\n" , + ioc->name, desc, + (ioc->facts.FWVersion.Word &0xFF000000 ) >>24 , + (ioc->facts.FWVersion.Word &0x00FF0000 ) >>16 , + (ioc->facts.FWVersion.Word &0x0000FF00 ) >>8 , + ioc->facts.FWVersion.Word &0x000000FF , + revision, + (ioc->bios_pg3.BiosVersion &0xFF000000 ) >>24 , + (ioc->bios_pg3.BiosVersion &0x00FF0000 ) >>16 , + (ioc->bios_pg3.BiosVersion &0x0000FF00 ) >>8 , + ioc->bios_pg3.BiosVersion &0x000000FF ); + +printk (MPT2SAS_INFO_FMT "Protocol=(" , ioc->name); + +if (ioc->facts.ProtocolFlags &MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR ) { +printk ("Initiator" ); + i++; + } + +if (ioc->facts.ProtocolFlags &MPI2_IOCFACTS_PROTOCOL_SCSI_TARGET ) { +printk ("%sTarget" , i ?"," :"" ); + i++; + } + + i =0 ; +printk ("), " ); +printk ("Capabilities=(" ); + +if (ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID ) { +printk ("Raid" ); + i++; + } + +if (ioc->facts.IOCCapabilities &MPI2_IOCFACTS_CAPABILITY_TLR ) { +printk ("%sTLR" , i ?"," :"" ); + i++; + } + +if (ioc->facts.IOCCapabilities &MPI2_IOCFACTS_CAPABILITY_MULTICAST ) { +printk ("%sMulticast" , i ?"," :"" ); + i++; + } + +if (ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_BIDIRECTIONAL_TARGET ) { +printk ("%sBIDI Target" , i ?"," :"" ); + i++; + } + +if (ioc->facts.IOCCapabilities &MPI2_IOCFACTS_CAPABILITY_EEDP ) { +printk ("%sEEDP" , i ?"," :"" ); + i++; + } + +if (ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_SNAPSHOT_BUFFER ) { +printk ("%sSnapshot Buffer" , i ?"," :"" ); + i++; + } + +if (ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_DIAG_TRACE_BUFFER ) { +printk ("%sDiag Trace Buffer" , i ?"," :"" ); + i++; + } + +if (ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING ) { +printk ("%sTask Set Full" , i ?"," :"" ); + i++; + } + + iounit_pg1_flags =le32_to_cpu (ioc->iounit_pg1.Flags); +if (!(iounit_pg1_flags &MPI2_IOUNITPAGE1_NATIVE_COMMAND_Q_DISABLE )) { +printk ("%sNCQ" , i ?"," :"" ); + i++; + } + +printk (")\n" ); +} + +/** +* _base_static_config_pages - static start of day config pages +* @ioc: per adapter object +* +* Return nothing. +*/ +static void +_base_static_config_pages (struct MPT2SAS_ADAPTER *ioc) +{ +Mpi2ConfigReply_t mpi_reply; +u32 iounit_pg1_flags; + +mpt2sas_config_get_manufacturing_pg0 (ioc, &mpi_reply, &ioc->manu_pg0); +mpt2sas_config_get_bios_pg2 (ioc, &mpi_reply, &ioc->bios_pg2); +mpt2sas_config_get_bios_pg3 (ioc, &mpi_reply, &ioc->bios_pg3); +mpt2sas_config_get_ioc_pg8 (ioc, &mpi_reply, &ioc->ioc_pg8); +mpt2sas_config_get_iounit_pg0 (ioc, &mpi_reply, &ioc->iounit_pg0); +mpt2sas_config_get_iounit_pg1 (ioc, &mpi_reply, &ioc->iounit_pg1); +_base_display_ioc_capabilities (ioc); + +/* +* Enable task_set_full handling in iounit_pg1 when the +* facts capabilities indicate that its supported. +*/ + iounit_pg1_flags =le32_to_cpu (ioc->iounit_pg1.Flags); +if ((ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_TASK_SET_FULL_HANDLING )) + iounit_pg1_flags &= + ~MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING ; +else + iounit_pg1_flags |= +MPI2_IOUNITPAGE1_DISABLE_TASK_SET_FULL_HANDLING ; + ioc->iounit_pg1.Flags =cpu_to_le32 (iounit_pg1_flags); +mpt2sas_config_set_iounit_pg1 (ioc, &mpi_reply, ioc->iounit_pg1); +} + +/** +* _base_release_memory_pools - release memory +* @ioc: per adapter object +* +* Free memory allocated from _base_allocate_memory_pools. +* +* Return nothing. +*/ +static void +_base_release_memory_pools (struct MPT2SAS_ADAPTER *ioc) +{ +dexitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + +if (ioc->request) { +pci_free_consistent (ioc->pdev, ioc->request_dma_sz, + ioc->request, ioc->request_dma); +dexitprintk (ioc,printk (MPT2SAS_INFO_FMT "request_pool(0x%p)" +": free\n" , ioc->name, ioc->request)); + ioc->request =NULL ; + } + +if (ioc->sense) { +pci_pool_free (ioc->sense_dma_pool, ioc->sense, ioc->sense_dma); +if (ioc->sense_dma_pool) +pci_pool_destroy (ioc->sense_dma_pool); +dexitprintk (ioc,printk (MPT2SAS_INFO_FMT "sense_pool(0x%p)" +": free\n" , ioc->name, ioc->sense)); + ioc->sense =NULL ; + } + +if (ioc->reply) { +pci_pool_free (ioc->reply_dma_pool, ioc->reply, ioc->reply_dma); +if (ioc->reply_dma_pool) +pci_pool_destroy (ioc->reply_dma_pool); +dexitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply_pool(0x%p)" +": free\n" , ioc->name, ioc->reply)); + ioc->reply =NULL ; + } + +if (ioc->reply_free) { +pci_pool_free (ioc->reply_free_dma_pool, ioc->reply_free, + ioc->reply_free_dma); +if (ioc->reply_free_dma_pool) +pci_pool_destroy (ioc->reply_free_dma_pool); +dexitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply_free_pool" +"(0x%p): free\n" , ioc->name, ioc->reply_free)); + ioc->reply_free =NULL ; + } + +if (ioc->reply_post_free) { +pci_pool_free (ioc->reply_post_free_dma_pool, + ioc->reply_post_free, ioc->reply_post_free_dma); +if (ioc->reply_post_free_dma_pool) +pci_pool_destroy (ioc->reply_post_free_dma_pool); +dexitprintk (ioc,printk (MPT2SAS_INFO_FMT +"reply_post_free_pool(0x%p): free\n" , ioc->name, + ioc->reply_post_free)); + ioc->reply_post_free =NULL ; + } + +if (ioc->config_page) { +dexitprintk (ioc,printk (MPT2SAS_INFO_FMT +"config_page(0x%p): free\n" , ioc->name, + ioc->config_page)); +pci_free_consistent (ioc->pdev, ioc->config_page_sz, + ioc->config_page, ioc->config_page_dma); + } + +kfree (ioc->scsi_lookup); +} + + +/** +* _base_allocate_memory_pools - allocate start of day memory pools +* @ioc: per adapter object +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 success, anything else error +*/ +static int +_base_allocate_memory_pools (struct MPT2SAS_ADAPTER *ioc,int sleep_flag) +{ +Mpi2IOCFactsReply_t *facts; +u32 queue_size, queue_diff; +u16 max_sge_elements; +u16 num_of_reply_frames; +u16 chains_needed_per_io; +u32 sz, total_sz; +u16 i; +u32 retry_sz; +u16 max_request_credit; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + + retry_sz =0 ; + facts = &ioc->facts; + +/* command line tunables for max sgl entries */ +if (max_sgl_entries !=-1 ) { + ioc->shost->sg_tablesize = (max_sgl_entries < +MPT2SAS_SG_DEPTH ) ? max_sgl_entries : +MPT2SAS_SG_DEPTH ; + }else { + ioc->shost->sg_tablesize =MPT2SAS_SG_DEPTH ; + } + +/* command line tunables for max controller queue depth */ +if (max_queue_depth !=-1 ) { + max_request_credit = (max_queue_depth < facts->RequestCredit) + ? max_queue_depth : facts->RequestCredit; + }else { + max_request_credit = (facts->RequestCredit > +MPT2SAS_MAX_REQUEST_QUEUE ) ?MPT2SAS_MAX_REQUEST_QUEUE : + facts->RequestCredit; + } + ioc->request_depth = max_request_credit; + +/* request frame size */ + ioc->request_sz = facts->IOCRequestFrameSize *4 ; + +/* reply frame size */ + ioc->reply_sz = facts->ReplyFrameSize *4 ; + + retry_allocation: + total_sz =0 ; +/* calculate number of sg elements left over in the 1st frame */ + max_sge_elements = ioc->request_sz - ((sizeof (Mpi2SCSIIORequest_t) - +sizeof (Mpi2SGEIOUnion_t)) + ioc->sge_size); + ioc->max_sges_in_main_message = max_sge_elements/ioc->sge_size; + +/* now do the same for a chain buffer */ + max_sge_elements = ioc->request_sz - ioc->sge_size; + ioc->max_sges_in_chain_message = max_sge_elements/ioc->sge_size; + + ioc->chain_offset_value_for_main_message = + ((sizeof (Mpi2SCSIIORequest_t) -sizeof (Mpi2SGEIOUnion_t)) + + (ioc->max_sges_in_chain_message * ioc->sge_size)) /4 ; + +/* +* MPT2SAS_SG_DEPTH = CONFIG_FUSION_MAX_SGE +*/ + chains_needed_per_io = ((ioc->shost->sg_tablesize - + ioc->max_sges_in_main_message)/ioc->max_sges_in_chain_message) + +1 ; +if (chains_needed_per_io > facts->MaxChainDepth) { + chains_needed_per_io = facts->MaxChainDepth; + ioc->shost->sg_tablesize =min_t (u16, + ioc->max_sges_in_main_message + (ioc->max_sges_in_chain_message + * chains_needed_per_io), ioc->shost->sg_tablesize); + } + ioc->chains_needed_per_io = chains_needed_per_io; + +/* reply free queue sizing - taking into account for events */ + num_of_reply_frames = ioc->request_depth +32 ; + +/* number of replies frames can't be a multiple of 16 */ +/* decrease number of reply frames by 1 */ +if (!(num_of_reply_frames %16 )) + num_of_reply_frames--; + +/* calculate number of reply free queue entries +* (must be multiple of 16) +*/ + +/* (we know reply_free_queue_depth is not a multiple of 16) */ + queue_size = num_of_reply_frames; + queue_size +=16 - (queue_size %16 ); + ioc->reply_free_queue_depth = queue_size; + +/* reply descriptor post queue sizing */ +/* this size should be the number of request frames + number of reply +* frames +*/ + + queue_size = ioc->request_depth + num_of_reply_frames +1 ; +/* round up to 16 byte boundary */ +if (queue_size %16 ) + queue_size +=16 - (queue_size %16 ); + +/* check against IOC maximum reply post queue depth */ +if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) { + queue_diff = queue_size - + facts->MaxReplyDescriptorPostQueueDepth; + +/* round queue_diff up to multiple of 16 */ +if (queue_diff %16 ) + queue_diff +=16 - (queue_diff %16 ); + +/* adjust request_depth, reply_free_queue_depth, +* and queue_size +*/ + ioc->request_depth -= queue_diff; + ioc->reply_free_queue_depth -= queue_diff; + queue_size -= queue_diff; + } + ioc->reply_post_queue_depth = queue_size; + +/* max scsi host queue depth */ + ioc->shost->can_queue = ioc->request_depth -INTERNAL_CMDS_COUNT ; +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "scsi host queue: depth" +"(%d)\n" , ioc->name, ioc->shost->can_queue)); + +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "scatter gather: " +"sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), " +"chains_per_io(%d)\n" , ioc->name, ioc->max_sges_in_main_message, + ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize, + ioc->chains_needed_per_io)); + +/* contiguous pool for request and chains, 16 byte align, one extra " +* "frame for smid=0 +*/ + ioc->chain_depth = ioc->chains_needed_per_io * ioc->request_depth; + sz = ((ioc->request_depth +1 + ioc->chain_depth) * ioc->request_sz); + + ioc->request_dma_sz = sz; + ioc->request =pci_alloc_consistent (ioc->pdev, sz, &ioc->request_dma); +if (!ioc->request) { +printk (MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " +"failed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " +"total(%d kB)\n" , ioc->name, ioc->request_depth, + ioc->chains_needed_per_io, ioc->request_sz, sz/1024 ); +if (ioc->request_depth <MPT2SAS_SAS_QUEUE_DEPTH ) + goto out; + retry_sz +=64 ; + ioc->request_depth = max_request_credit - retry_sz; + goto retry_allocation; + } + +if (retry_sz) +printk (MPT2SAS_ERR_FMT "request pool: pci_alloc_consistent " +"succeed: req_depth(%d), chains_per_io(%d), frame_sz(%d), " +"total(%d kb)\n" , ioc->name, ioc->request_depth, + ioc->chains_needed_per_io, ioc->request_sz, sz/1024 ); + + ioc->chain = ioc->request + ((ioc->request_depth +1 ) * + ioc->request_sz); + ioc->chain_dma = ioc->request_dma + ((ioc->request_depth +1 ) * + ioc->request_sz); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "request pool(0x%p): " +"depth(%d), frame_size(%d), pool_size(%d kB)\n" , ioc->name, + ioc->request, ioc->request_depth, ioc->request_sz, + ((ioc->request_depth +1 ) * ioc->request_sz)/1024 )); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "chain pool(0x%p): depth" +"(%d), frame_size(%d), pool_size(%d kB)\n" , ioc->name, ioc->chain, + ioc->chain_depth, ioc->request_sz, ((ioc->chain_depth * + ioc->request_sz))/1024 )); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "request pool: dma(0x%llx)\n" , + ioc->name, (unsigned long long ) ioc->request_dma)); + total_sz += sz; + + ioc->scsi_lookup =kcalloc (ioc->request_depth, +sizeof (struct request_tracker ),GFP_KERNEL ); +if (!ioc->scsi_lookup) { +printk (MPT2SAS_ERR_FMT "scsi_lookup: kcalloc failed\n" , + ioc->name); + goto out; + } + +/* initialize some bits */ +for (i =0 ; i < ioc->request_depth; i++) + ioc->scsi_lookup[i].smid = i +1 ; + +/* sense buffers, 4 byte align */ + sz = ioc->request_depth *SCSI_SENSE_BUFFERSIZE ; + ioc->sense_dma_pool =pci_pool_create ("sense pool" , ioc->pdev, sz,4 , +0 ); +if (!ioc->sense_dma_pool) { +printk (MPT2SAS_ERR_FMT "sense pool: pci_pool_create failed\n" , + ioc->name); + goto out; + } + ioc->sense =pci_pool_alloc (ioc->sense_dma_pool ,GFP_KERNEL , + &ioc->sense_dma); +if (!ioc->sense) { +printk (MPT2SAS_ERR_FMT "sense pool: pci_pool_alloc failed\n" , + ioc->name); + goto out; + } +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT +"sense pool(0x%p): depth(%d), element_size(%d), pool_size" +"(%d kB)\n" , ioc->name, ioc->sense, ioc->request_depth, +SCSI_SENSE_BUFFERSIZE , sz/1024 )); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "sense_dma(0x%llx)\n" , + ioc->name, (unsigned long long )ioc->sense_dma)); + total_sz += sz; + +/* reply pool, 4 byte align */ + sz = ioc->reply_free_queue_depth * ioc->reply_sz; + ioc->reply_dma_pool =pci_pool_create ("reply pool" , ioc->pdev, sz,4 , +0 ); +if (!ioc->reply_dma_pool) { +printk (MPT2SAS_ERR_FMT "reply pool: pci_pool_create failed\n" , + ioc->name); + goto out; + } + ioc->reply =pci_pool_alloc (ioc->reply_dma_pool ,GFP_KERNEL , + &ioc->reply_dma); +if (!ioc->reply) { +printk (MPT2SAS_ERR_FMT "reply pool: pci_pool_alloc failed\n" , + ioc->name); + goto out; + } +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply pool(0x%p): depth" +"(%d), frame_size(%d), pool_size(%d kB)\n" , ioc->name, ioc->reply, + ioc->reply_free_queue_depth, ioc->reply_sz, sz/1024 )); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply_dma(0x%llx)\n" , + ioc->name, (unsigned long long )ioc->reply_dma)); + total_sz += sz; + +/* reply free queue, 16 byte align */ + sz = ioc->reply_free_queue_depth *4 ; + ioc->reply_free_dma_pool =pci_pool_create ("reply_free pool" , + ioc->pdev, sz,16 ,0 ); +if (!ioc->reply_free_dma_pool) { +printk (MPT2SAS_ERR_FMT "reply_free pool: pci_pool_create " +"failed\n" , ioc->name); + goto out; + } + ioc->reply_free =pci_pool_alloc (ioc->reply_free_dma_pool ,GFP_KERNEL , + &ioc->reply_free_dma); +if (!ioc->reply_free) { +printk (MPT2SAS_ERR_FMT "reply_free pool: pci_pool_alloc " +"failed\n" , ioc->name); + goto out; + } +memset (ioc->reply_free,0 , sz); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply_free pool(0x%p): " +"depth(%d), element_size(%d), pool_size(%d kB)\n" , ioc->name, + ioc->reply_free, ioc->reply_free_queue_depth,4 , sz/1024 )); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply_free_dma" +"(0x%llx)\n" , ioc->name, (unsigned long long )ioc->reply_free_dma)); + total_sz += sz; + +/* reply post queue, 16 byte align */ + sz = ioc->reply_post_queue_depth *sizeof (Mpi2DefaultReplyDescriptor_t); + ioc->reply_post_free_dma_pool =pci_pool_create ("reply_post_free pool" , + ioc->pdev, sz,16 ,0 ); +if (!ioc->reply_post_free_dma_pool) { +printk (MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_create " +"failed\n" , ioc->name); + goto out; + } + ioc->reply_post_free =pci_pool_alloc (ioc->reply_post_free_dma_pool , +GFP_KERNEL , &ioc->reply_post_free_dma); +if (!ioc->reply_post_free) { +printk (MPT2SAS_ERR_FMT "reply_post_free pool: pci_pool_alloc " +"failed\n" , ioc->name); + goto out; + } +memset (ioc->reply_post_free,0 , sz); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply post free pool" +"(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n" , + ioc->name, ioc->reply_post_free, ioc->reply_post_queue_depth,8 , + sz/1024 )); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "reply_post_free_dma = " +"(0x%llx)\n" , ioc->name, (unsigned long long ) + ioc->reply_post_free_dma)); + total_sz += sz; + + ioc->config_page_sz =512 ; + ioc->config_page =pci_alloc_consistent (ioc->pdev, + ioc->config_page_sz, &ioc->config_page_dma); +if (!ioc->config_page) { +printk (MPT2SAS_ERR_FMT "config page: pci_pool_alloc " +"failed\n" , ioc->name); + goto out; + } +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "config page(0x%p): size" +"(%d)\n" , ioc->name, ioc->config_page, ioc->config_page_sz)); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "config_page_dma" +"(0x%llx)\n" , ioc->name, (unsigned long long )ioc->config_page_dma)); + total_sz += ioc->config_page_sz; + +printk (MPT2SAS_INFO_FMT "Allocated physical memory: size(%d kB)\n" , + ioc->name, total_sz/1024 ); +printk (MPT2SAS_INFO_FMT "Current Controller Queue Depth(%d), " +"Max Controller Queue Depth(%d)\n" , + ioc->name, ioc->shost->can_queue, facts->RequestCredit); +printk (MPT2SAS_INFO_FMT "Scatter Gather Elements per IO(%d)\n" , + ioc->name, ioc->shost->sg_tablesize); +return 0 ; + + out: +_base_release_memory_pools (ioc); +return -ENOMEM ; +} + + +/** +* mpt2sas_base_get_iocstate - Get the current state of a MPT adapter. +* @ioc: Pointer to MPT_ADAPTER structure +* @cooked: Request raw or cooked IOC state +* +* Returns all IOC Doorbell register bits if cooked==0, else just the +* Doorbell bits in MPI_IOC_STATE_MASK. +*/ +u32 +mpt2sas_base_get_iocstate (struct MPT2SAS_ADAPTER *ioc,int cooked) +{ +u32 s, sc; + + s =readl (&ioc->chip->Doorbell); + sc = s &MPI2_IOC_STATE_MASK ; +return cooked ? sc : s; +} + +/** +* _base_wait_on_iocstate - waiting on a particular ioc state +* @ioc_state: controller state { READY, OPERATIONAL, or RESET } +* @timeout: timeout in second +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_wait_on_iocstate (struct MPT2SAS_ADAPTER *ioc,u32 ioc_state,int timeout, +int sleep_flag) +{ +u32 count, cntdn; +u32 current_state; + + count =0 ; + cntdn = (sleep_flag ==CAN_SLEEP ) ?1000 *timeout :2000 *timeout; +do { + current_state =mpt2sas_base_get_iocstate (ioc,1 ); +if (current_state == ioc_state) +return 0 ; +if (count && current_state ==MPI2_IOC_STATE_FAULT ) +break ; +if (sleep_flag ==CAN_SLEEP ) +msleep (1 ); +else +udelay (500 ); + count++; + }while (--cntdn); + +return current_state; +} + +/** +* _base_wait_for_doorbell_int - waiting for controller interrupt(generated by +* a write to the doorbell) +* @ioc: per adapter object +* @timeout: timeout in second +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +* +* Notes: MPI2_HIS_IOC2SYS_DB_STATUS - set to one when IOC writes to doorbell. +*/ +static int +_base_wait_for_doorbell_int (struct MPT2SAS_ADAPTER *ioc,int timeout, +int sleep_flag) +{ +u32 cntdn, count; +u32 int_status; + + count =0 ; + cntdn = (sleep_flag ==CAN_SLEEP ) ?1000 *timeout :2000 *timeout; +do { + int_status =readl (&ioc->chip->HostInterruptStatus); +if (int_status &MPI2_HIS_IOC2SYS_DB_STATUS ) { +dhsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s: " +"successfull count(%d), timeout(%d)\n" , ioc->name, + __func__, count, timeout)); +return 0 ; + } +if (sleep_flag ==CAN_SLEEP ) +msleep (1 ); +else +udelay (500 ); + count++; + }while (--cntdn); + +printk (MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " +"int_status(%x)!\n" , ioc->name, __func__, count, int_status); +return -EFAULT ; +} + +/** +* _base_wait_for_doorbell_ack - waiting for controller to read the doorbell. +* @ioc: per adapter object +* @timeout: timeout in second +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +* +* Notes: MPI2_HIS_SYS2IOC_DB_STATUS - set to one when host writes to +* doorbell. +*/ +static int +_base_wait_for_doorbell_ack (struct MPT2SAS_ADAPTER *ioc,int timeout, +int sleep_flag) +{ +u32 cntdn, count; +u32 int_status; +u32 doorbell; + + count =0 ; + cntdn = (sleep_flag ==CAN_SLEEP ) ?1000 *timeout :2000 *timeout; +do { + int_status =readl (&ioc->chip->HostInterruptStatus); +if (!(int_status &MPI2_HIS_SYS2IOC_DB_STATUS )) { +dhsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s: " +"successfull count(%d), timeout(%d)\n" , ioc->name, + __func__, count, timeout)); +return 0 ; + }else if (int_status &MPI2_HIS_IOC2SYS_DB_STATUS ) { + doorbell =readl (&ioc->chip->Doorbell); +if ((doorbell &MPI2_IOC_STATE_MASK ) == +MPI2_IOC_STATE_FAULT ) { +mpt2sas_base_fault_info (ioc , doorbell); +return -EFAULT ; + } + }else if (int_status ==0xFFFFFFFF ) + goto out; + +if (sleep_flag ==CAN_SLEEP ) +msleep (1 ); +else +udelay (500 ); + count++; + }while (--cntdn); + + out: +printk (MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " +"int_status(%x)!\n" , ioc->name, __func__, count, int_status); +return -EFAULT ; +} + +/** +* _base_wait_for_doorbell_not_used - waiting for doorbell to not be in use +* @ioc: per adapter object +* @timeout: timeout in second +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +* +*/ +static int +_base_wait_for_doorbell_not_used (struct MPT2SAS_ADAPTER *ioc,int timeout, +int sleep_flag) +{ +u32 cntdn, count; +u32 doorbell_reg; + + count =0 ; + cntdn = (sleep_flag ==CAN_SLEEP ) ?1000 *timeout :2000 *timeout; +do { + doorbell_reg =readl (&ioc->chip->Doorbell); +if (!(doorbell_reg &MPI2_DOORBELL_USED )) { +dhsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s: " +"successfull count(%d), timeout(%d)\n" , ioc->name, + __func__, count, timeout)); +return 0 ; + } +if (sleep_flag ==CAN_SLEEP ) +msleep (1 ); +else +udelay (500 ); + count++; + }while (--cntdn); + +printk (MPT2SAS_ERR_FMT "%s: failed due to timeout count(%d), " +"doorbell_reg(%x)!\n" , ioc->name, __func__, count, doorbell_reg); +return -EFAULT ; +} + +/** +* _base_send_ioc_reset - send doorbell reset +* @ioc: per adapter object +* @reset_type: currently only supports: MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET +* @timeout: timeout in second +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_send_ioc_reset (struct MPT2SAS_ADAPTER *ioc,u8 reset_type,int timeout, +int sleep_flag) +{ +u32 ioc_state; +int r =0 ; + +if (reset_type !=MPI2_FUNCTION_IOC_MESSAGE_UNIT_RESET ) { +printk (MPT2SAS_ERR_FMT "%s: unknown reset_type\n" , + ioc->name, __func__); +return -EFAULT ; + } + +if (!(ioc->facts.IOCCapabilities & +MPI2_IOCFACTS_CAPABILITY_EVENT_REPLAY )) +return -EFAULT ; + +printk (MPT2SAS_INFO_FMT "sending message unit reset !!\n" , ioc->name); + +writel (reset_type <<MPI2_DOORBELL_FUNCTION_SHIFT , + &ioc->chip->Doorbell); +if ((_base_wait_for_doorbell_ack (ioc,15 , sleep_flag))) { + r = -EFAULT ; + goto out; + } + ioc_state =_base_wait_on_iocstate (ioc,MPI2_IOC_STATE_READY , + timeout, sleep_flag); +if (ioc_state) { +printk (MPT2SAS_ERR_FMT "%s: failed going to ready state " +" (ioc_state=0x%x)\n" , ioc->name, __func__, ioc_state); + r = -EFAULT ; + goto out; + } + out: +printk (MPT2SAS_INFO_FMT "message unit reset: %s\n" , + ioc->name, ((r ==0 ) ?"SUCCESS" :"FAILED" )); +return r; +} + +/** +* _base_handshake_req_reply_wait - send request thru doorbell interface +* @ioc: per adapter object +* @request_bytes: request length +* @request: pointer having request payload +* @reply_bytes: reply length +* @reply: pointer to reply payload +* @timeout: timeout in second +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_handshake_req_reply_wait (struct MPT2SAS_ADAPTER *ioc,int request_bytes, +u32 *request,int reply_bytes,u16 *reply,int timeout,int sleep_flag) +{ +MPI2DefaultReply_t *default_reply = (MPI2DefaultReply_t *)reply; +int i; +u8 failed; +u16 dummy; +u32 *mfp; + +/* make sure doorbell is not in use */ +if ((readl (&ioc->chip->Doorbell) &MPI2_DOORBELL_USED )) { +printk (MPT2SAS_ERR_FMT "doorbell is in use " +" (line=%d)\n" , ioc->name, __LINE__); +return -EFAULT ; + } + +/* clear pending doorbell interrupts from previous state changes */ +if (readl (&ioc->chip->HostInterruptStatus) & +MPI2_HIS_IOC2SYS_DB_STATUS ) +writel (0 , &ioc->chip->HostInterruptStatus); + +/* send message to ioc */ +writel (((MPI2_FUNCTION_HANDSHAKE <<MPI2_DOORBELL_FUNCTION_SHIFT ) | + ((request_bytes/4 )<<MPI2_DOORBELL_ADD_DWORDS_SHIFT )), + &ioc->chip->Doorbell); + +if ((_base_wait_for_doorbell_int (ioc,5 , sleep_flag))) { +printk (MPT2SAS_ERR_FMT "doorbell handshake " +"int failed (line=%d)\n" , ioc->name, __LINE__); +return -EFAULT ; + } +writel (0 , &ioc->chip->HostInterruptStatus); + +if ((_base_wait_for_doorbell_ack (ioc,5 , sleep_flag))) { +printk (MPT2SAS_ERR_FMT "doorbell handshake " +"ack failed (line=%d)\n" , ioc->name, __LINE__); +return -EFAULT ; + } + +/* send message 32-bits at a time */ +for (i =0 , failed =0 ; i < request_bytes/4 && !failed; i++) { +writel (cpu_to_le32 (request[i]), &ioc->chip->Doorbell); +if ((_base_wait_for_doorbell_ack (ioc,5 , sleep_flag))) + failed =1 ; + } + +if (failed) { +printk (MPT2SAS_ERR_FMT "doorbell handshake " +"sending request failed (line=%d)\n" , ioc->name, __LINE__); +return -EFAULT ; + } + +/* now wait for the reply */ +if ((_base_wait_for_doorbell_int (ioc, timeout, sleep_flag))) { +printk (MPT2SAS_ERR_FMT "doorbell handshake " +"int failed (line=%d)\n" , ioc->name, __LINE__); +return -EFAULT ; + } + +/* read the first two 16-bits, it gives the total length of the reply */ + reply[0 ] =le16_to_cpu (readl (&ioc->chip->Doorbell) + &MPI2_DOORBELL_DATA_MASK ); +writel (0 , &ioc->chip->HostInterruptStatus); +if ((_base_wait_for_doorbell_int (ioc,5 , sleep_flag))) { +printk (MPT2SAS_ERR_FMT "doorbell handshake " +"int failed (line=%d)\n" , ioc->name, __LINE__); +return -EFAULT ; + } + reply[1 ] =le16_to_cpu (readl (&ioc->chip->Doorbell) + &MPI2_DOORBELL_DATA_MASK ); +writel (0 , &ioc->chip->HostInterruptStatus); + +for (i =2 ; i < default_reply->MsgLength *2 ; i++) { +if ((_base_wait_for_doorbell_int (ioc,5 , sleep_flag))) { +printk (MPT2SAS_ERR_FMT "doorbell " +"handshake int failed (line=%d)\n" , ioc->name, + __LINE__); +return -EFAULT ; + } +if (i >= reply_bytes/2 )/* overflow case */ + dummy =readl (&ioc->chip->Doorbell); +else + reply[i] =le16_to_cpu (readl (&ioc->chip->Doorbell) + &MPI2_DOORBELL_DATA_MASK ); +writel (0 , &ioc->chip->HostInterruptStatus); + } + +_base_wait_for_doorbell_int (ioc,5 , sleep_flag); +if (_base_wait_for_doorbell_not_used (ioc,5 , sleep_flag) !=0 ) { +dhsprintk (ioc,printk (MPT2SAS_INFO_FMT "doorbell is in use " +" (line=%d)\n" , ioc->name, __LINE__)); + } +writel (0 , &ioc->chip->HostInterruptStatus); + +if (ioc->logging_level &MPT_DEBUG_INIT ) { + mfp = (u32 *)reply; +printk (KERN_DEBUG "\toffset:data\n" ); +for (i =0 ; i < reply_bytes/4 ; i++) +printk (KERN_DEBUG "\t[0x%02x]:%08x\n" , i*4 , +le32_to_cpu (mfp[i])); + } +return 0 ; +} + +/** +* mpt2sas_base_sas_iounit_control - send sas iounit control to FW +* @ioc: per adapter object +* @mpi_reply: the reply payload from FW +* @mpi_request: the request payload sent to FW +* +* The SAS IO Unit Control Request message allows the host to perform low-level +* operations, such as resets on the PHYs of the IO Unit, also allows the host +* to obtain the IOC assigned device handles for a device if it has other +* identifying information about the device, in addition allows the host to +* remove IOC resources associated with the device. +* +* Returns 0 for success, non-zero for failure. +*/ +int +mpt2sas_base_sas_iounit_control (struct MPT2SAS_ADAPTER *ioc, +Mpi2SasIoUnitControlReply_t *mpi_reply, +Mpi2SasIoUnitControlRequest_t *mpi_request) +{ +u16 smid; +u32 ioc_state; +unsigned long timeleft; +u8 issue_reset; +int rc; +void *request; +u16 wait_state_count; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + +mutex_lock (&ioc->base_cmds.mutex); + +if (ioc->base_cmds.status !=MPT2_CMD_NOT_USED ) { +printk (MPT2SAS_ERR_FMT "%s: base_cmd in use\n" , + ioc->name, __func__); + rc = -EAGAIN ; + goto out; + } + + wait_state_count =0 ; + ioc_state =mpt2sas_base_get_iocstate (ioc,1 ); +while (ioc_state !=MPI2_IOC_STATE_OPERATIONAL ) { +if (wait_state_count++ ==10 ) { +printk (MPT2SAS_ERR_FMT +"%s: failed due to ioc not operational\n" , + ioc->name, __func__); + rc = -EFAULT ; + goto out; + } +ssleep (1 ); + ioc_state =mpt2sas_base_get_iocstate (ioc,1 ); +printk (MPT2SAS_INFO_FMT "%s: waiting for " +"operational state(count=%d)\n" , ioc->name, + __func__, wait_state_count); + } + + smid =mpt2sas_base_get_smid (ioc, ioc->base_cb_idx); +if (!smid) { +printk (MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n" , + ioc->name, __func__); + rc = -EAGAIN ; + goto out; + } + + rc =0 ; + ioc->base_cmds.status =MPT2_CMD_PENDING ; + request =mpt2sas_base_get_msg_frame (ioc, smid); + ioc->base_cmds.smid = smid; +memcpy (request, mpi_request,sizeof (Mpi2SasIoUnitControlRequest_t)); +if (mpi_request->Operation ==MPI2_SAS_OP_PHY_HARD_RESET || + mpi_request->Operation ==MPI2_SAS_OP_PHY_LINK_RESET ) + ioc->ioc_link_reset_in_progress =1 ; +mpt2sas_base_put_smid_default (ioc, smid, mpi_request->VF_ID); + timeleft =wait_for_completion_timeout (&ioc->base_cmds.done, +msecs_to_jiffies (10000 )); +if ((mpi_request->Operation ==MPI2_SAS_OP_PHY_HARD_RESET || + mpi_request->Operation ==MPI2_SAS_OP_PHY_LINK_RESET ) && + ioc->ioc_link_reset_in_progress) + ioc->ioc_link_reset_in_progress =0 ; +if (!(ioc->base_cmds.status &MPT2_CMD_COMPLETE )) { +printk (MPT2SAS_ERR_FMT "%s: timeout\n" , + ioc->name, __func__); +_debug_dump_mf (mpi_request, +sizeof (Mpi2SasIoUnitControlRequest_t)/4 ); +if (!(ioc->base_cmds.status &MPT2_CMD_RESET )) + issue_reset =1 ; + goto issue_host_reset; + } +if (ioc->base_cmds.status &MPT2_CMD_REPLY_VALID ) +memcpy (mpi_reply, ioc->base_cmds.reply, +sizeof (Mpi2SasIoUnitControlReply_t)); +else +memset (mpi_reply,0 ,sizeof (Mpi2SasIoUnitControlReply_t)); + ioc->base_cmds.status =MPT2_CMD_NOT_USED ; + goto out; + + issue_host_reset: +if (issue_reset) +mpt2sas_base_hard_reset_handler (ioc,CAN_SLEEP , +FORCE_BIG_HAMMER ); + ioc->base_cmds.status =MPT2_CMD_NOT_USED ; + rc = -EFAULT ; + out: +mutex_unlock (&ioc->base_cmds.mutex); +return rc; +} + + +/** +* mpt2sas_base_scsi_enclosure_processor - sending request to sep device +* @ioc: per adapter object +* @mpi_reply: the reply payload from FW +* @mpi_request: the request payload sent to FW +* +* The SCSI Enclosure Processor request message causes the IOC to +* communicate with SES devices to control LED status signals. +* +* Returns 0 for success, non-zero for failure. +*/ +int +mpt2sas_base_scsi_enclosure_processor (struct MPT2SAS_ADAPTER *ioc, +Mpi2SepReply_t *mpi_reply,Mpi2SepRequest_t *mpi_request) +{ +u16 smid; +u32 ioc_state; +unsigned long timeleft; +u8 issue_reset; +int rc; +void *request; +u16 wait_state_count; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + +mutex_lock (&ioc->base_cmds.mutex); + +if (ioc->base_cmds.status !=MPT2_CMD_NOT_USED ) { +printk (MPT2SAS_ERR_FMT "%s: base_cmd in use\n" , + ioc->name, __func__); + rc = -EAGAIN ; + goto out; + } + + wait_state_count =0 ; + ioc_state =mpt2sas_base_get_iocstate (ioc,1 ); +while (ioc_state !=MPI2_IOC_STATE_OPERATIONAL ) { +if (wait_state_count++ ==10 ) { +printk (MPT2SAS_ERR_FMT +"%s: failed due to ioc not operational\n" , + ioc->name, __func__); + rc = -EFAULT ; + goto out; + } +ssleep (1 ); + ioc_state =mpt2sas_base_get_iocstate (ioc,1 ); +printk (MPT2SAS_INFO_FMT "%s: waiting for " +"operational state(count=%d)\n" , ioc->name, + __func__, wait_state_count); + } + + smid =mpt2sas_base_get_smid (ioc, ioc->base_cb_idx); +if (!smid) { +printk (MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n" , + ioc->name, __func__); + rc = -EAGAIN ; + goto out; + } + + rc =0 ; + ioc->base_cmds.status =MPT2_CMD_PENDING ; + request =mpt2sas_base_get_msg_frame (ioc, smid); + ioc->base_cmds.smid = smid; +memcpy (request, mpi_request,sizeof (Mpi2SepReply_t)); +mpt2sas_base_put_smid_default (ioc, smid, mpi_request->VF_ID); + timeleft =wait_for_completion_timeout (&ioc->base_cmds.done, +msecs_to_jiffies (10000 )); +if (!(ioc->base_cmds.status &MPT2_CMD_COMPLETE )) { +printk (MPT2SAS_ERR_FMT "%s: timeout\n" , + ioc->name, __func__); +_debug_dump_mf (mpi_request, +sizeof (Mpi2SepRequest_t)/4 ); +if (!(ioc->base_cmds.status &MPT2_CMD_RESET )) + issue_reset =1 ; + goto issue_host_reset; + } +if (ioc->base_cmds.status &MPT2_CMD_REPLY_VALID ) +memcpy (mpi_reply, ioc->base_cmds.reply, +sizeof (Mpi2SepReply_t)); +else +memset (mpi_reply,0 ,sizeof (Mpi2SepReply_t)); + ioc->base_cmds.status =MPT2_CMD_NOT_USED ; + goto out; + + issue_host_reset: +if (issue_reset) +mpt2sas_base_hard_reset_handler (ioc,CAN_SLEEP , +FORCE_BIG_HAMMER ); + ioc->base_cmds.status =MPT2_CMD_NOT_USED ; + rc = -EFAULT ; + out: +mutex_unlock (&ioc->base_cmds.mutex); +return rc; +} + +/** +* _base_get_port_facts - obtain port facts reply and save in ioc +* @ioc: per adapter object +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_get_port_facts (struct MPT2SAS_ADAPTER *ioc,int port,int sleep_flag) +{ +Mpi2PortFactsRequest_t mpi_request; +Mpi2PortFactsReply_t mpi_reply, *pfacts; +int mpi_reply_sz, mpi_request_sz, r; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + + mpi_reply_sz =sizeof (Mpi2PortFactsReply_t); + mpi_request_sz =sizeof (Mpi2PortFactsRequest_t); +memset (&mpi_request,0 , mpi_request_sz); + mpi_request.Function =MPI2_FUNCTION_PORT_FACTS ; + mpi_request.PortNumber = port; + r =_base_handshake_req_reply_wait (ioc, mpi_request_sz, + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply,5 ,CAN_SLEEP ); + +if (r !=0 ) { +printk (MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n" , + ioc->name, __func__, r); +return r; + } + + pfacts = &ioc->pfacts[port]; +memset (pfacts,0 ,sizeof (Mpi2PortFactsReply_t)); + pfacts->PortNumber = mpi_reply.PortNumber; + pfacts->VP_ID = mpi_reply.VP_ID; + pfacts->VF_ID = mpi_reply.VF_ID; + pfacts->MaxPostedCmdBuffers = +le16_to_cpu (mpi_reply.MaxPostedCmdBuffers); + +return 0 ; +} + +/** +* _base_get_ioc_facts - obtain ioc facts reply and save in ioc +* @ioc: per adapter object +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_get_ioc_facts (struct MPT2SAS_ADAPTER *ioc,int sleep_flag) +{ +Mpi2IOCFactsRequest_t mpi_request; +Mpi2IOCFactsReply_t mpi_reply, *facts; +int mpi_reply_sz, mpi_request_sz, r; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + + mpi_reply_sz =sizeof (Mpi2IOCFactsReply_t); + mpi_request_sz =sizeof (Mpi2IOCFactsRequest_t); +memset (&mpi_request,0 , mpi_request_sz); + mpi_request.Function =MPI2_FUNCTION_IOC_FACTS ; + r =_base_handshake_req_reply_wait (ioc, mpi_request_sz, + (u32 *)&mpi_request, mpi_reply_sz, (u16 *)&mpi_reply,5 ,CAN_SLEEP ); + +if (r !=0 ) { +printk (MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n" , + ioc->name, __func__, r); +return r; + } + + facts = &ioc->facts; +memset (facts,0 ,sizeof (Mpi2IOCFactsReply_t)); + facts->MsgVersion =le16_to_cpu (mpi_reply.MsgVersion); + facts->HeaderVersion =le16_to_cpu (mpi_reply.HeaderVersion); + facts->VP_ID = mpi_reply.VP_ID; + facts->VF_ID = mpi_reply.VF_ID; + facts->IOCExceptions =le16_to_cpu (mpi_reply.IOCExceptions); + facts->MaxChainDepth = mpi_reply.MaxChainDepth; + facts->WhoInit = mpi_reply.WhoInit; + facts->NumberOfPorts = mpi_reply.NumberOfPorts; + facts->RequestCredit =le16_to_cpu (mpi_reply.RequestCredit); + facts->MaxReplyDescriptorPostQueueDepth = +le16_to_cpu (mpi_reply.MaxReplyDescriptorPostQueueDepth); + facts->ProductID =le16_to_cpu (mpi_reply.ProductID); + facts->IOCCapabilities =le32_to_cpu (mpi_reply.IOCCapabilities); +if ((facts->IOCCapabilities &MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID )) + ioc->ir_firmware =1 ; + facts->FWVersion.Word =le32_to_cpu (mpi_reply.FWVersion.Word); + facts->IOCRequestFrameSize = +le16_to_cpu (mpi_reply.IOCRequestFrameSize); + facts->MaxInitiators =le16_to_cpu (mpi_reply.MaxInitiators); + facts->MaxTargets =le16_to_cpu (mpi_reply.MaxTargets); + ioc->shost->max_id =-1 ; + facts->MaxSasExpanders =le16_to_cpu (mpi_reply.MaxSasExpanders); + facts->MaxEnclosures =le16_to_cpu (mpi_reply.MaxEnclosures); + facts->ProtocolFlags =le16_to_cpu (mpi_reply.ProtocolFlags); + facts->HighPriorityCredit = +le16_to_cpu (mpi_reply.HighPriorityCredit); + facts->ReplyFrameSize = mpi_reply.ReplyFrameSize; + facts->MaxDevHandle =le16_to_cpu (mpi_reply.MaxDevHandle); + +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "hba queue depth(%d), " +"max chains per io(%d)\n" , ioc->name, facts->RequestCredit, + facts->MaxChainDepth)); +dinitprintk (ioc,printk (MPT2SAS_INFO_FMT "request frame size(%d), " +"reply frame size(%d)\n" , ioc->name, + facts->IOCRequestFrameSize *4 , facts->ReplyFrameSize *4 )); +return 0 ; +} + +/** +* _base_send_ioc_init - send ioc_init to firmware +* @ioc: per adapter object +* @VF_ID: virtual function id +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_send_ioc_init (struct MPT2SAS_ADAPTER *ioc,u8 VF_ID ,int sleep_flag) +{ +Mpi2IOCInitRequest_t mpi_request; +Mpi2IOCInitReply_t mpi_reply; +int r; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + +memset (&mpi_request,0 ,sizeof (Mpi2IOCInitRequest_t)); + mpi_request.Function =MPI2_FUNCTION_IOC_INIT ; + mpi_request.WhoInit =MPI2_WHOINIT_HOST_DRIVER ; + mpi_request.VF_ID =VF_ID ; + mpi_request.MsgVersion =cpu_to_le16 (MPI2_VERSION ); + mpi_request.HeaderVersion =cpu_to_le16 (MPI2_HEADER_VERSION ); + +/* In MPI Revision I (0xA), the SystemReplyFrameSize(offset 0x18) was +* removed and made reserved. For those with older firmware will need +* this fix. It was decided that the Reply and Request frame sizes are +* the same. +*/ +if ((ioc->facts.HeaderVersion >>8 ) <0xA ) { + mpi_request.Reserved7 =cpu_to_le16 (ioc->reply_sz); +/* mpi_request.SystemReplyFrameSize = +* cpu_to_le16(ioc->reply_sz); +*/ + } + + mpi_request.SystemRequestFrameSize =cpu_to_le16 (ioc->request_sz/4 ); + mpi_request.ReplyDescriptorPostQueueDepth = +cpu_to_le16 (ioc->reply_post_queue_depth); + mpi_request.ReplyFreeQueueDepth = +cpu_to_le16 (ioc->reply_free_queue_depth); + +#if BITS_PER_LONG >32 + mpi_request.SenseBufferAddressHigh = +cpu_to_le32 (ioc->sense_dma >>32 ); + mpi_request.SystemReplyAddressHigh = +cpu_to_le32 (ioc->reply_dma >>32 ); + mpi_request.SystemRequestFrameBaseAddress = +cpu_to_le64 (ioc->request_dma); + mpi_request.ReplyFreeQueueAddress = +cpu_to_le64 (ioc->reply_free_dma); + mpi_request.ReplyDescriptorPostQueueAddress = +cpu_to_le64 (ioc->reply_post_free_dma); +#else + mpi_request.SystemRequestFrameBaseAddress = +cpu_to_le32 (ioc->request_dma); + mpi_request.ReplyFreeQueueAddress = +cpu_to_le32 (ioc->reply_free_dma); + mpi_request.ReplyDescriptorPostQueueAddress = +cpu_to_le32 (ioc->reply_post_free_dma); +#endif + +if (ioc->logging_level &MPT_DEBUG_INIT ) { +u32 *mfp; +int i; + + mfp = (u32 *)&mpi_request; +printk (KERN_DEBUG "\toffset:data\n" ); +for (i =0 ; i <sizeof (Mpi2IOCInitRequest_t)/4 ; i++) +printk (KERN_DEBUG "\t[0x%02x]:%08x\n" , i*4 , +le32_to_cpu (mfp[i])); + } + + r =_base_handshake_req_reply_wait (ioc, +sizeof (Mpi2IOCInitRequest_t), (u32 *)&mpi_request, +sizeof (Mpi2IOCInitReply_t), (u16 *)&mpi_reply,10 , + sleep_flag); + +if (r !=0 ) { +printk (MPT2SAS_ERR_FMT "%s: handshake failed (r=%d)\n" , + ioc->name, __func__, r); +return r; + } + +if (mpi_reply.IOCStatus !=MPI2_IOCSTATUS_SUCCESS || + mpi_reply.IOCLogInfo) { +printk (MPT2SAS_ERR_FMT "%s: failed\n" , ioc->name, __func__); + r = -EIO ; + } + +return 0 ; +} + +/** +* _base_send_port_enable - send port_enable(discovery stuff) to firmware +* @ioc: per adapter object +* @VF_ID: virtual function id +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_send_port_enable (struct MPT2SAS_ADAPTER *ioc,u8 VF_ID ,int sleep_flag) +{ +Mpi2PortEnableRequest_t *mpi_request; +u32 ioc_state; +unsigned long timeleft; +int r =0 ; +u16 smid; + +printk (MPT2SAS_INFO_FMT "sending port enable !!\n" , ioc->name); + +if (ioc->base_cmds.status &MPT2_CMD_PENDING ) { +printk (MPT2SAS_ERR_FMT "%s: internal command already in use\n" , + ioc->name, __func__); +return -EAGAIN ; + } + + smid =mpt2sas_base_get_smid (ioc, ioc->base_cb_idx); +if (!smid) { +printk (MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n" , + ioc->name, __func__); +return -EAGAIN ; + } + + ioc->base_cmds.status =MPT2_CMD_PENDING ; + mpi_request =mpt2sas_base_get_msg_frame (ioc, smid); + ioc->base_cmds.smid = smid; +memset (mpi_request,0 ,sizeof (Mpi2PortEnableRequest_t)); + mpi_request->Function =MPI2_FUNCTION_PORT_ENABLE ; + mpi_request->VF_ID =VF_ID ; + +mpt2sas_base_put_smid_default (ioc, smid,VF_ID ); + timeleft =wait_for_completion_timeout (&ioc->base_cmds.done, +300 *HZ ); +if (!(ioc->base_cmds.status &MPT2_CMD_COMPLETE )) { +printk (MPT2SAS_ERR_FMT "%s: timeout\n" , + ioc->name, __func__); +_debug_dump_mf (mpi_request, +sizeof (Mpi2PortEnableRequest_t)/4 ); +if (ioc->base_cmds.status &MPT2_CMD_RESET ) + r = -EFAULT ; +else + r = -ETIME ; + goto out; + }else +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s: complete\n" , + ioc->name, __func__)); + + ioc_state =_base_wait_on_iocstate (ioc,MPI2_IOC_STATE_OPERATIONAL , +60 , sleep_flag); +if (ioc_state) { +printk (MPT2SAS_ERR_FMT "%s: failed going to operational state " +" (ioc_state=0x%x)\n" , ioc->name, __func__, ioc_state); + r = -EFAULT ; + } + out: + ioc->base_cmds.status =MPT2_CMD_NOT_USED ; +printk (MPT2SAS_INFO_FMT "port enable: %s\n" , + ioc->name, ((r ==0 ) ?"SUCCESS" :"FAILED" )); +return r; +} + +/** +* _base_unmask_events - turn on notification for this event +* @ioc: per adapter object +* @event: firmware event +* +* The mask is stored in ioc->event_masks. +*/ +static void +_base_unmask_events (struct MPT2SAS_ADAPTER *ioc,u16 event) +{ +u32 desired_event; + +if (event >=128 ) +return ; + + desired_event = (1 << (event %32 )); + +if (event <32 ) + ioc->event_masks[0 ] &= ~desired_event; +else if (event <64 ) + ioc->event_masks[1 ] &= ~desired_event; +else if (event <96 ) + ioc->event_masks[2 ] &= ~desired_event; +else if (event <128 ) + ioc->event_masks[3 ] &= ~desired_event; +} + +/** +* _base_event_notification - send event notification +* @ioc: per adapter object +* @VF_ID: virtual function id +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_event_notification (struct MPT2SAS_ADAPTER *ioc,u8 VF_ID ,int sleep_flag) +{ +Mpi2EventNotificationRequest_t *mpi_request; +unsigned long timeleft; +u16 smid; +int r =0 ; +int i; + +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s\n" , ioc->name, + __func__)); + +if (ioc->base_cmds.status &MPT2_CMD_PENDING ) { +printk (MPT2SAS_ERR_FMT "%s: internal command already in use\n" , + ioc->name, __func__); +return -EAGAIN ; + } + + smid =mpt2sas_base_get_smid (ioc, ioc->base_cb_idx); +if (!smid) { +printk (MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n" , + ioc->name, __func__); +return -EAGAIN ; + } + ioc->base_cmds.status =MPT2_CMD_PENDING ; + mpi_request =mpt2sas_base_get_msg_frame (ioc, smid); + ioc->base_cmds.smid = smid; +memset (mpi_request,0 ,sizeof (Mpi2EventNotificationRequest_t)); + mpi_request->Function =MPI2_FUNCTION_EVENT_NOTIFICATION ; + mpi_request->VF_ID =VF_ID ; +for (i =0 ; i <MPI2_EVENT_NOTIFY_EVENTMASK_WORDS ; i++) + mpi_request->EventMasks[i] = +le32_to_cpu (ioc->event_masks[i]); +mpt2sas_base_put_smid_default (ioc, smid,VF_ID ); + timeleft =wait_for_completion_timeout (&ioc->base_cmds.done,30 *HZ ); +if (!(ioc->base_cmds.status &MPT2_CMD_COMPLETE )) { +printk (MPT2SAS_ERR_FMT "%s: timeout\n" , + ioc->name, __func__); +_debug_dump_mf (mpi_request, +sizeof (Mpi2EventNotificationRequest_t)/4 ); +if (ioc->base_cmds.status &MPT2_CMD_RESET ) + r = -EFAULT ; +else + r = -ETIME ; + }else +dinitprintk (ioc,printk (MPT2SAS_DEBUG_FMT "%s: complete\n" , + ioc->name, __func__)); + ioc->base_cmds.status =MPT2_CMD_NOT_USED ; +return r; +} + +/** +* mpt2sas_base_validate_event_type - validating event types +* @ioc: per adapter object +* @event: firmware event +* +* This will turn on firmware event notification when application +* ask for that event. We don't mask events that are already enabled. +*/ +void +mpt2sas_base_validate_event_type (struct MPT2SAS_ADAPTER *ioc,u32 *event_type) +{ +int i, j; +u32 event_mask, desired_event; +u8 send_update_to_fw; + +for (i =0 , send_update_to_fw =0 ; i < +MPI2_EVENT_NOTIFY_EVENTMASK_WORDS ; i++) { + event_mask = ~event_type[i]; + desired_event =1 ; +for (j =0 ; j <32 ; j++) { +if (!(event_mask & desired_event) && + (ioc->event_masks[i] & desired_event)) { + ioc->event_masks[i] &= ~desired_event; + send_update_to_fw =1 ; + } + desired_event = (desired_event <<1 ); + } + } + +if (!send_update_to_fw) +return ; + +mutex_lock (&ioc->base_cmds.mutex); +_base_event_notification (ioc,0 ,CAN_SLEEP ); +mutex_unlock (&ioc->base_cmds.mutex); +} + +/** +* _base_diag_reset - the "big hammer" start of day reset +* @ioc: per adapter object +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_diag_reset (struct MPT2SAS_ADAPTER *ioc,int sleep_flag) +{ +u32 host_diagnostic; +u32 ioc_state; +u32 count; +u32 hcb_size; + +printk (MPT2SAS_INFO_FMT "sending diag reset !!\n" , ioc->name); + +_base_save_msix_table (ioc); + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "clear interrupts\n" , + ioc->name)); +writel (0 , &ioc->chip->HostInterruptStatus); + + count =0 ; +do { +/* Write magic sequence to WriteSequence register +* Loop until in diagnostic mode +*/ +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "write magic " +"sequence\n" , ioc->name)); +writel (MPI2_WRSEQ_FLUSH_KEY_VALUE , &ioc->chip->WriteSequence); +writel (MPI2_WRSEQ_1ST_KEY_VALUE , &ioc->chip->WriteSequence); +writel (MPI2_WRSEQ_2ND_KEY_VALUE , &ioc->chip->WriteSequence); +writel (MPI2_WRSEQ_3RD_KEY_VALUE , &ioc->chip->WriteSequence); +writel (MPI2_WRSEQ_4TH_KEY_VALUE , &ioc->chip->WriteSequence); +writel (MPI2_WRSEQ_5TH_KEY_VALUE , &ioc->chip->WriteSequence); +writel (MPI2_WRSEQ_6TH_KEY_VALUE , &ioc->chip->WriteSequence); + +/* wait 100 msec */ +if (sleep_flag ==CAN_SLEEP ) +msleep (100 ); +else +mdelay (100 ); + +if (count++ >20 ) + goto out; + + host_diagnostic =readl (&ioc->chip->HostDiagnostic); +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "wrote magic " +"sequence: count(%d), host_diagnostic(0x%08x)\n" , + ioc->name, count, host_diagnostic)); + + }while ((host_diagnostic &MPI2_DIAG_DIAG_WRITE_ENABLE ) ==0 ); + + hcb_size =readl (&ioc->chip->HCBSize); + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "diag reset: issued\n" , + ioc->name)); +writel (host_diagnostic |MPI2_DIAG_RESET_ADAPTER , + &ioc->chip->HostDiagnostic); + +/* don't access any registers for 50 milliseconds */ +msleep (50 ); + +/* 300 second max wait */ +for (count =0 ; count <3000000 ; count++) { + + host_diagnostic =readl (&ioc->chip->HostDiagnostic); + +if (host_diagnostic ==0xFFFFFFFF ) + goto out; +if (!(host_diagnostic &MPI2_DIAG_RESET_ADAPTER )) +break ; + +/* wait 100 msec */ +if (sleep_flag ==CAN_SLEEP ) +msleep (1 ); +else +mdelay (1 ); + } + +if (host_diagnostic &MPI2_DIAG_HCB_MODE ) { + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "restart the adapter " +"assuming the HCB Address points to good F/W\n" , + ioc->name)); + host_diagnostic &= ~MPI2_DIAG_BOOT_DEVICE_SELECT_MASK ; + host_diagnostic |=MPI2_DIAG_BOOT_DEVICE_SELECT_HCDW ; +writel (host_diagnostic, &ioc->chip->HostDiagnostic); + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT +"re-enable the HCDW\n" , ioc->name)); +writel (hcb_size |MPI2_HCB_SIZE_HCB_ENABLE , + &ioc->chip->HCBSize); + } + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "restart the adapter\n" , + ioc->name)); +writel (host_diagnostic & ~MPI2_DIAG_HOLD_IOC_RESET , + &ioc->chip->HostDiagnostic); + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "disable writes to the " +"diagnostic register\n" , ioc->name)); +writel (MPI2_WRSEQ_FLUSH_KEY_VALUE , &ioc->chip->WriteSequence); + +drsprintk (ioc,printk (MPT2SAS_DEBUG_FMT "Wait for FW to go to the " +"READY state\n" , ioc->name)); + ioc_state =_base_wait_on_iocstate (ioc,MPI2_IOC_STATE_READY ,20 , + sleep_flag); +if (ioc_state) { +printk (MPT2SAS_ERR_FMT "%s: failed going to ready state " +" (ioc_state=0x%x)\n" , ioc->name, __func__, ioc_state); + goto out; + } + +_base_restore_msix_table (ioc); +printk (MPT2SAS_INFO_FMT "diag reset: SUCCESS\n" , ioc->name); +return 0 ; + + out: +printk (MPT2SAS_ERR_FMT "diag reset: FAILED\n" , ioc->name); +return -EFAULT ; +} + +/** +* _base_make_ioc_ready - put controller in READY state +* @ioc: per adapter object +* @sleep_flag: CAN_SLEEP or NO_SLEEP +* @type: FORCE_BIG_HAMMER or SOFT_RESET +* +* Returns 0 for success, non-zero for failure. +*/ +static int +_base_make_ioc_ready (struct MPT2SAS_ADAPTER *ioc,int sleep_flag, +enum reset_type type) +{ +u32 ioc_state; + + dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name,
Lines of this file are not shown. View the raw message.
drivers/scsi/mpt2sas/mpt2sas_base.h +779 -0
779 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Part of the diff is not shown here. View the raw message.