162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Linux MegaRAID driver for SAS based RAID controllers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2003-2013 LSI Corporation 662306a36Sopenharmony_ci * Copyright (c) 2013-2016 Avago Technologies 762306a36Sopenharmony_ci * Copyright (c) 2016-2018 Broadcom Inc. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Authors: Broadcom Inc. 1062306a36Sopenharmony_ci * Sreenivas Bagalkote 1162306a36Sopenharmony_ci * Sumant Patro 1262306a36Sopenharmony_ci * Bo Yang 1362306a36Sopenharmony_ci * Adam Radford 1462306a36Sopenharmony_ci * Kashyap Desai <kashyap.desai@broadcom.com> 1562306a36Sopenharmony_ci * Sumit Saxena <sumit.saxena@broadcom.com> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * Send feedback to: megaraidlinux.pdl@broadcom.com 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/list.h> 2462306a36Sopenharmony_ci#include <linux/moduleparam.h> 2562306a36Sopenharmony_ci#include <linux/module.h> 2662306a36Sopenharmony_ci#include <linux/spinlock.h> 2762306a36Sopenharmony_ci#include <linux/interrupt.h> 2862306a36Sopenharmony_ci#include <linux/delay.h> 2962306a36Sopenharmony_ci#include <linux/uio.h> 3062306a36Sopenharmony_ci#include <linux/slab.h> 3162306a36Sopenharmony_ci#include <linux/uaccess.h> 3262306a36Sopenharmony_ci#include <asm/unaligned.h> 3362306a36Sopenharmony_ci#include <linux/fs.h> 3462306a36Sopenharmony_ci#include <linux/compat.h> 3562306a36Sopenharmony_ci#include <linux/blkdev.h> 3662306a36Sopenharmony_ci#include <linux/mutex.h> 3762306a36Sopenharmony_ci#include <linux/poll.h> 3862306a36Sopenharmony_ci#include <linux/vmalloc.h> 3962306a36Sopenharmony_ci#include <linux/irq_poll.h> 4062306a36Sopenharmony_ci#include <linux/blk-mq-pci.h> 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#include <scsi/scsi.h> 4362306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 4462306a36Sopenharmony_ci#include <scsi/scsi_device.h> 4562306a36Sopenharmony_ci#include <scsi/scsi_host.h> 4662306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 4762306a36Sopenharmony_ci#include <scsi/scsi_dbg.h> 4862306a36Sopenharmony_ci#include "megaraid_sas_fusion.h" 4962306a36Sopenharmony_ci#include "megaraid_sas.h" 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Number of sectors per IO command 5362306a36Sopenharmony_ci * Will be set in megasas_init_mfi if user does not provide 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_cistatic unsigned int max_sectors; 5662306a36Sopenharmony_cimodule_param_named(max_sectors, max_sectors, int, 0444); 5762306a36Sopenharmony_ciMODULE_PARM_DESC(max_sectors, 5862306a36Sopenharmony_ci "Maximum number of sectors per IO command"); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int msix_disable; 6162306a36Sopenharmony_cimodule_param(msix_disable, int, 0444); 6262306a36Sopenharmony_ciMODULE_PARM_DESC(msix_disable, "Disable MSI-X interrupt handling. Default: 0"); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic unsigned int msix_vectors; 6562306a36Sopenharmony_cimodule_param(msix_vectors, int, 0444); 6662306a36Sopenharmony_ciMODULE_PARM_DESC(msix_vectors, "MSI-X max vector count. Default: Set by FW"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic int allow_vf_ioctls; 6962306a36Sopenharmony_cimodule_param(allow_vf_ioctls, int, 0444); 7062306a36Sopenharmony_ciMODULE_PARM_DESC(allow_vf_ioctls, "Allow ioctls in SR-IOV VF mode. Default: 0"); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic unsigned int throttlequeuedepth = MEGASAS_THROTTLE_QUEUE_DEPTH; 7362306a36Sopenharmony_cimodule_param(throttlequeuedepth, int, 0444); 7462306a36Sopenharmony_ciMODULE_PARM_DESC(throttlequeuedepth, 7562306a36Sopenharmony_ci "Adapter queue depth when throttled due to I/O timeout. Default: 16"); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciunsigned int resetwaittime = MEGASAS_RESET_WAIT_TIME; 7862306a36Sopenharmony_cimodule_param(resetwaittime, int, 0444); 7962306a36Sopenharmony_ciMODULE_PARM_DESC(resetwaittime, "Wait time in (1-180s) after I/O timeout before resetting adapter. Default: 180s"); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic int smp_affinity_enable = 1; 8262306a36Sopenharmony_cimodule_param(smp_affinity_enable, int, 0444); 8362306a36Sopenharmony_ciMODULE_PARM_DESC(smp_affinity_enable, "SMP affinity feature enable/disable Default: enable(1)"); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int rdpq_enable = 1; 8662306a36Sopenharmony_cimodule_param(rdpq_enable, int, 0444); 8762306a36Sopenharmony_ciMODULE_PARM_DESC(rdpq_enable, "Allocate reply queue in chunks for large queue depth enable/disable Default: enable(1)"); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciunsigned int dual_qdepth_disable; 9062306a36Sopenharmony_cimodule_param(dual_qdepth_disable, int, 0444); 9162306a36Sopenharmony_ciMODULE_PARM_DESC(dual_qdepth_disable, "Disable dual queue depth feature. Default: 0"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic unsigned int scmd_timeout = MEGASAS_DEFAULT_CMD_TIMEOUT; 9462306a36Sopenharmony_cimodule_param(scmd_timeout, int, 0444); 9562306a36Sopenharmony_ciMODULE_PARM_DESC(scmd_timeout, "scsi command timeout (10-90s), default 90s. See megasas_reset_timer."); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciint perf_mode = -1; 9862306a36Sopenharmony_cimodule_param(perf_mode, int, 0444); 9962306a36Sopenharmony_ciMODULE_PARM_DESC(perf_mode, "Performance mode (only for Aero adapters), options:\n\t\t" 10062306a36Sopenharmony_ci "0 - balanced: High iops and low latency queues are allocated &\n\t\t" 10162306a36Sopenharmony_ci "interrupt coalescing is enabled only on high iops queues\n\t\t" 10262306a36Sopenharmony_ci "1 - iops: High iops queues are not allocated &\n\t\t" 10362306a36Sopenharmony_ci "interrupt coalescing is enabled on all queues\n\t\t" 10462306a36Sopenharmony_ci "2 - latency: High iops queues are not allocated &\n\t\t" 10562306a36Sopenharmony_ci "interrupt coalescing is disabled on all queues\n\t\t" 10662306a36Sopenharmony_ci "default mode is 'balanced'" 10762306a36Sopenharmony_ci ); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ciint event_log_level = MFI_EVT_CLASS_CRITICAL; 11062306a36Sopenharmony_cimodule_param(event_log_level, int, 0644); 11162306a36Sopenharmony_ciMODULE_PARM_DESC(event_log_level, "Asynchronous event logging level- range is: -2(CLASS_DEBUG) to 4(CLASS_DEAD), Default: 2(CLASS_CRITICAL)"); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ciunsigned int enable_sdev_max_qd; 11462306a36Sopenharmony_cimodule_param(enable_sdev_max_qd, int, 0444); 11562306a36Sopenharmony_ciMODULE_PARM_DESC(enable_sdev_max_qd, "Enable sdev max qd as can_queue. Default: 0"); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ciint poll_queues; 11862306a36Sopenharmony_cimodule_param(poll_queues, int, 0444); 11962306a36Sopenharmony_ciMODULE_PARM_DESC(poll_queues, "Number of queues to be use for io_uring poll mode.\n\t\t" 12062306a36Sopenharmony_ci "This parameter is effective only if host_tagset_enable=1 &\n\t\t" 12162306a36Sopenharmony_ci "It is not applicable for MFI_SERIES. &\n\t\t" 12262306a36Sopenharmony_ci "Driver will work in latency mode. &\n\t\t" 12362306a36Sopenharmony_ci "High iops queues are not allocated &\n\t\t" 12462306a36Sopenharmony_ci ); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciint host_tagset_enable = 1; 12762306a36Sopenharmony_cimodule_param(host_tagset_enable, int, 0444); 12862306a36Sopenharmony_ciMODULE_PARM_DESC(host_tagset_enable, "Shared host tagset enable/disable Default: enable(1)"); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 13162306a36Sopenharmony_ciMODULE_VERSION(MEGASAS_VERSION); 13262306a36Sopenharmony_ciMODULE_AUTHOR("megaraidlinux.pdl@broadcom.com"); 13362306a36Sopenharmony_ciMODULE_DESCRIPTION("Broadcom MegaRAID SAS Driver"); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciint megasas_transition_to_ready(struct megasas_instance *instance, int ocr); 13662306a36Sopenharmony_cistatic int megasas_get_pd_list(struct megasas_instance *instance); 13762306a36Sopenharmony_cistatic int megasas_ld_list_query(struct megasas_instance *instance, 13862306a36Sopenharmony_ci u8 query_type); 13962306a36Sopenharmony_cistatic int megasas_issue_init_mfi(struct megasas_instance *instance); 14062306a36Sopenharmony_cistatic int megasas_register_aen(struct megasas_instance *instance, 14162306a36Sopenharmony_ci u32 seq_num, u32 class_locale_word); 14262306a36Sopenharmony_cistatic void megasas_get_pd_info(struct megasas_instance *instance, 14362306a36Sopenharmony_ci struct scsi_device *sdev); 14462306a36Sopenharmony_cistatic void 14562306a36Sopenharmony_cimegasas_set_ld_removed_by_fw(struct megasas_instance *instance); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* 14862306a36Sopenharmony_ci * PCI ID table for all supported controllers 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic struct pci_device_id megasas_pci_table[] = { 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)}, 15362306a36Sopenharmony_ci /* xscale IOP */ 15462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)}, 15562306a36Sopenharmony_ci /* ppc IOP */ 15662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078DE)}, 15762306a36Sopenharmony_ci /* ppc IOP */ 15862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078GEN2)}, 15962306a36Sopenharmony_ci /* gen2*/ 16062306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0079GEN2)}, 16162306a36Sopenharmony_ci /* gen2*/ 16262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0073SKINNY)}, 16362306a36Sopenharmony_ci /* skinny*/ 16462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0071SKINNY)}, 16562306a36Sopenharmony_ci /* skinny*/ 16662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)}, 16762306a36Sopenharmony_ci /* xscale IOP, vega */ 16862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)}, 16962306a36Sopenharmony_ci /* xscale IOP */ 17062306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FUSION)}, 17162306a36Sopenharmony_ci /* Fusion */ 17262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_PLASMA)}, 17362306a36Sopenharmony_ci /* Plasma */ 17462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INVADER)}, 17562306a36Sopenharmony_ci /* Invader */ 17662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FURY)}, 17762306a36Sopenharmony_ci /* Fury */ 17862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INTRUDER)}, 17962306a36Sopenharmony_ci /* Intruder */ 18062306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_INTRUDER_24)}, 18162306a36Sopenharmony_ci /* Intruder 24 port*/ 18262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CUTLASS_52)}, 18362306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CUTLASS_53)}, 18462306a36Sopenharmony_ci /* VENTURA */ 18562306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VENTURA)}, 18662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CRUSADER)}, 18762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_HARPOON)}, 18862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_TOMCAT)}, 18962306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VENTURA_4PORT)}, 19062306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_CRUSADER_4PORT)}, 19162306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E1)}, 19262306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E2)}, 19362306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E5)}, 19462306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E6)}, 19562306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E0)}, 19662306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E3)}, 19762306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E4)}, 19862306a36Sopenharmony_ci {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_AERO_10E7)}, 19962306a36Sopenharmony_ci {} 20062306a36Sopenharmony_ci}; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, megasas_pci_table); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int megasas_mgmt_majorno; 20562306a36Sopenharmony_cistruct megasas_mgmt_info megasas_mgmt_info; 20662306a36Sopenharmony_cistatic struct fasync_struct *megasas_async_queue; 20762306a36Sopenharmony_cistatic DEFINE_MUTEX(megasas_async_queue_mutex); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int megasas_poll_wait_aen; 21062306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait); 21162306a36Sopenharmony_cistatic u32 support_poll_for_event; 21262306a36Sopenharmony_ciu32 megasas_dbg_lvl; 21362306a36Sopenharmony_cistatic u32 support_device_change; 21462306a36Sopenharmony_cistatic bool support_nvme_encapsulation; 21562306a36Sopenharmony_cistatic bool support_pci_lane_margining; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci/* define lock for aen poll */ 21862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(poll_aen_lock); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ciextern struct dentry *megasas_debugfs_root; 22162306a36Sopenharmony_ciextern int megasas_blk_mq_poll(struct Scsi_Host *shost, unsigned int queue_num); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_civoid 22462306a36Sopenharmony_cimegasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, 22562306a36Sopenharmony_ci u8 alt_status); 22662306a36Sopenharmony_cistatic u32 22762306a36Sopenharmony_cimegasas_read_fw_status_reg_gen2(struct megasas_instance *instance); 22862306a36Sopenharmony_cistatic int 22962306a36Sopenharmony_cimegasas_adp_reset_gen2(struct megasas_instance *instance, 23062306a36Sopenharmony_ci struct megasas_register_set __iomem *reg_set); 23162306a36Sopenharmony_cistatic irqreturn_t megasas_isr(int irq, void *devp); 23262306a36Sopenharmony_cistatic u32 23362306a36Sopenharmony_cimegasas_init_adapter_mfi(struct megasas_instance *instance); 23462306a36Sopenharmony_ciu32 23562306a36Sopenharmony_cimegasas_build_and_issue_cmd(struct megasas_instance *instance, 23662306a36Sopenharmony_ci struct scsi_cmnd *scmd); 23762306a36Sopenharmony_cistatic void megasas_complete_cmd_dpc(unsigned long instance_addr); 23862306a36Sopenharmony_ciint 23962306a36Sopenharmony_ciwait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd, 24062306a36Sopenharmony_ci int seconds); 24162306a36Sopenharmony_civoid megasas_fusion_ocr_wq(struct work_struct *work); 24262306a36Sopenharmony_cistatic int megasas_get_ld_vf_affiliation(struct megasas_instance *instance, 24362306a36Sopenharmony_ci int initial); 24462306a36Sopenharmony_cistatic int 24562306a36Sopenharmony_cimegasas_set_dma_mask(struct megasas_instance *instance); 24662306a36Sopenharmony_cistatic int 24762306a36Sopenharmony_cimegasas_alloc_ctrl_mem(struct megasas_instance *instance); 24862306a36Sopenharmony_cistatic inline void 24962306a36Sopenharmony_cimegasas_free_ctrl_mem(struct megasas_instance *instance); 25062306a36Sopenharmony_cistatic inline int 25162306a36Sopenharmony_cimegasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance); 25262306a36Sopenharmony_cistatic inline void 25362306a36Sopenharmony_cimegasas_free_ctrl_dma_buffers(struct megasas_instance *instance); 25462306a36Sopenharmony_cistatic inline void 25562306a36Sopenharmony_cimegasas_init_ctrl_params(struct megasas_instance *instance); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciu32 megasas_readl(struct megasas_instance *instance, 25862306a36Sopenharmony_ci const volatile void __iomem *addr) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci u32 i = 0, ret_val; 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * Due to a HW errata in Aero controllers, reads to certain 26362306a36Sopenharmony_ci * Fusion registers could intermittently return all zeroes. 26462306a36Sopenharmony_ci * This behavior is transient in nature and subsequent reads will 26562306a36Sopenharmony_ci * return valid value. As a workaround in driver, retry readl for 26662306a36Sopenharmony_ci * up to thirty times until a non-zero value is read. 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (instance->adapter_type == AERO_SERIES) { 26962306a36Sopenharmony_ci do { 27062306a36Sopenharmony_ci ret_val = readl(addr); 27162306a36Sopenharmony_ci i++; 27262306a36Sopenharmony_ci } while (ret_val == 0 && i < 30); 27362306a36Sopenharmony_ci return ret_val; 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci return readl(addr); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/** 28062306a36Sopenharmony_ci * megasas_set_dma_settings - Populate DMA address, length and flags for DCMDs 28162306a36Sopenharmony_ci * @instance: Adapter soft state 28262306a36Sopenharmony_ci * @dcmd: DCMD frame inside MFI command 28362306a36Sopenharmony_ci * @dma_addr: DMA address of buffer to be passed to FW 28462306a36Sopenharmony_ci * @dma_len: Length of DMA buffer to be passed to FW 28562306a36Sopenharmony_ci * @return: void 28662306a36Sopenharmony_ci */ 28762306a36Sopenharmony_civoid megasas_set_dma_settings(struct megasas_instance *instance, 28862306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd, 28962306a36Sopenharmony_ci dma_addr_t dma_addr, u32 dma_len) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci if (instance->consistent_mask_64bit) { 29262306a36Sopenharmony_ci dcmd->sgl.sge64[0].phys_addr = cpu_to_le64(dma_addr); 29362306a36Sopenharmony_ci dcmd->sgl.sge64[0].length = cpu_to_le32(dma_len); 29462306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(dcmd->flags | MFI_FRAME_SGL64); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci } else { 29762306a36Sopenharmony_ci dcmd->sgl.sge32[0].phys_addr = 29862306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(dma_addr)); 29962306a36Sopenharmony_ci dcmd->sgl.sge32[0].length = cpu_to_le32(dma_len); 30062306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(dcmd->flags); 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void 30562306a36Sopenharmony_cimegasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci instance->instancet->fire_cmd(instance, 30862306a36Sopenharmony_ci cmd->frame_phys_addr, 0, instance->reg_set); 30962306a36Sopenharmony_ci return; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci/** 31362306a36Sopenharmony_ci * megasas_get_cmd - Get a command from the free pool 31462306a36Sopenharmony_ci * @instance: Adapter soft state 31562306a36Sopenharmony_ci * 31662306a36Sopenharmony_ci * Returns a free command from the pool 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_cistruct megasas_cmd *megasas_get_cmd(struct megasas_instance 31962306a36Sopenharmony_ci *instance) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci unsigned long flags; 32262306a36Sopenharmony_ci struct megasas_cmd *cmd = NULL; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci spin_lock_irqsave(&instance->mfi_pool_lock, flags); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!list_empty(&instance->cmd_pool)) { 32762306a36Sopenharmony_ci cmd = list_entry((&instance->cmd_pool)->next, 32862306a36Sopenharmony_ci struct megasas_cmd, list); 32962306a36Sopenharmony_ci list_del_init(&cmd->list); 33062306a36Sopenharmony_ci } else { 33162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Command pool empty!\n"); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); 33562306a36Sopenharmony_ci return cmd; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci/** 33962306a36Sopenharmony_ci * megasas_return_cmd - Return a cmd to free command pool 34062306a36Sopenharmony_ci * @instance: Adapter soft state 34162306a36Sopenharmony_ci * @cmd: Command packet to be returned to free command pool 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_civoid 34462306a36Sopenharmony_cimegasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci unsigned long flags; 34762306a36Sopenharmony_ci u32 blk_tags; 34862306a36Sopenharmony_ci struct megasas_cmd_fusion *cmd_fusion; 34962306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* This flag is used only for fusion adapter. 35262306a36Sopenharmony_ci * Wait for Interrupt for Polled mode DCMD 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci if (cmd->flags & DRV_DCMD_POLLED_MODE) 35562306a36Sopenharmony_ci return; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci spin_lock_irqsave(&instance->mfi_pool_lock, flags); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (fusion) { 36062306a36Sopenharmony_ci blk_tags = instance->max_scsi_cmds + cmd->index; 36162306a36Sopenharmony_ci cmd_fusion = fusion->cmd_list[blk_tags]; 36262306a36Sopenharmony_ci megasas_return_cmd_fusion(instance, cmd_fusion); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci cmd->scmd = NULL; 36562306a36Sopenharmony_ci cmd->frame_count = 0; 36662306a36Sopenharmony_ci cmd->flags = 0; 36762306a36Sopenharmony_ci memset(cmd->frame, 0, instance->mfi_frame_size); 36862306a36Sopenharmony_ci cmd->frame->io.context = cpu_to_le32(cmd->index); 36962306a36Sopenharmony_ci if (!fusion && reset_devices) 37062306a36Sopenharmony_ci cmd->frame->hdr.cmd = MFI_CMD_INVALID; 37162306a36Sopenharmony_ci list_add(&cmd->list, (&instance->cmd_pool)->next); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic const char * 37862306a36Sopenharmony_ciformat_timestamp(uint32_t timestamp) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci static char buffer[32]; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if ((timestamp & 0xff000000) == 0xff000000) 38362306a36Sopenharmony_ci snprintf(buffer, sizeof(buffer), "boot + %us", timestamp & 38462306a36Sopenharmony_ci 0x00ffffff); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci snprintf(buffer, sizeof(buffer), "%us", timestamp); 38762306a36Sopenharmony_ci return buffer; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_cistatic const char * 39162306a36Sopenharmony_ciformat_class(int8_t class) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci static char buffer[6]; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci switch (class) { 39662306a36Sopenharmony_ci case MFI_EVT_CLASS_DEBUG: 39762306a36Sopenharmony_ci return "debug"; 39862306a36Sopenharmony_ci case MFI_EVT_CLASS_PROGRESS: 39962306a36Sopenharmony_ci return "progress"; 40062306a36Sopenharmony_ci case MFI_EVT_CLASS_INFO: 40162306a36Sopenharmony_ci return "info"; 40262306a36Sopenharmony_ci case MFI_EVT_CLASS_WARNING: 40362306a36Sopenharmony_ci return "WARN"; 40462306a36Sopenharmony_ci case MFI_EVT_CLASS_CRITICAL: 40562306a36Sopenharmony_ci return "CRIT"; 40662306a36Sopenharmony_ci case MFI_EVT_CLASS_FATAL: 40762306a36Sopenharmony_ci return "FATAL"; 40862306a36Sopenharmony_ci case MFI_EVT_CLASS_DEAD: 40962306a36Sopenharmony_ci return "DEAD"; 41062306a36Sopenharmony_ci default: 41162306a36Sopenharmony_ci snprintf(buffer, sizeof(buffer), "%d", class); 41262306a36Sopenharmony_ci return buffer; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci} 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci/** 41762306a36Sopenharmony_ci * megasas_decode_evt: Decode FW AEN event and print critical event 41862306a36Sopenharmony_ci * for information. 41962306a36Sopenharmony_ci * @instance: Adapter soft state 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_cistatic void 42262306a36Sopenharmony_cimegasas_decode_evt(struct megasas_instance *instance) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci struct megasas_evt_detail *evt_detail = instance->evt_detail; 42562306a36Sopenharmony_ci union megasas_evt_class_locale class_locale; 42662306a36Sopenharmony_ci class_locale.word = le32_to_cpu(evt_detail->cl.word); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci if ((event_log_level < MFI_EVT_CLASS_DEBUG) || 42962306a36Sopenharmony_ci (event_log_level > MFI_EVT_CLASS_DEAD)) { 43062306a36Sopenharmony_ci printk(KERN_WARNING "megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n"); 43162306a36Sopenharmony_ci event_log_level = MFI_EVT_CLASS_CRITICAL; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (class_locale.members.class >= event_log_level) 43562306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%d (%s/0x%04x/%s) - %s\n", 43662306a36Sopenharmony_ci le32_to_cpu(evt_detail->seq_num), 43762306a36Sopenharmony_ci format_timestamp(le32_to_cpu(evt_detail->time_stamp)), 43862306a36Sopenharmony_ci (class_locale.members.locale), 43962306a36Sopenharmony_ci format_class(class_locale.members.class), 44062306a36Sopenharmony_ci evt_detail->description); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 44362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 44462306a36Sopenharmony_ci "evt_detail.args.ld.target_id/index %d/%d\n", 44562306a36Sopenharmony_ci evt_detail->args.ld.target_id, evt_detail->args.ld.ld_index); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * The following functions are defined for xscale 45162306a36Sopenharmony_ci * (deviceid : 1064R, PERC5) controllers 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/** 45562306a36Sopenharmony_ci * megasas_enable_intr_xscale - Enables interrupts 45662306a36Sopenharmony_ci * @instance: Adapter soft state 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic inline void 45962306a36Sopenharmony_cimegasas_enable_intr_xscale(struct megasas_instance *instance) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci regs = instance->reg_set; 46462306a36Sopenharmony_ci writel(0, &(regs)->outbound_intr_mask); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 46762306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci/** 47162306a36Sopenharmony_ci * megasas_disable_intr_xscale -Disables interrupt 47262306a36Sopenharmony_ci * @instance: Adapter soft state 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_cistatic inline void 47562306a36Sopenharmony_cimegasas_disable_intr_xscale(struct megasas_instance *instance) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 47862306a36Sopenharmony_ci u32 mask = 0x1f; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci regs = instance->reg_set; 48162306a36Sopenharmony_ci writel(mask, ®s->outbound_intr_mask); 48262306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 48362306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/** 48762306a36Sopenharmony_ci * megasas_read_fw_status_reg_xscale - returns the current FW status value 48862306a36Sopenharmony_ci * @instance: Adapter soft state 48962306a36Sopenharmony_ci */ 49062306a36Sopenharmony_cistatic u32 49162306a36Sopenharmony_cimegasas_read_fw_status_reg_xscale(struct megasas_instance *instance) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci return readl(&instance->reg_set->outbound_msg_0); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci/** 49662306a36Sopenharmony_ci * megasas_clear_intr_xscale - Check & clear interrupt 49762306a36Sopenharmony_ci * @instance: Adapter soft state 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic int 50062306a36Sopenharmony_cimegasas_clear_intr_xscale(struct megasas_instance *instance) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci u32 status; 50362306a36Sopenharmony_ci u32 mfiStatus = 0; 50462306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 50562306a36Sopenharmony_ci regs = instance->reg_set; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * Check if it is our interrupt 50962306a36Sopenharmony_ci */ 51062306a36Sopenharmony_ci status = readl(®s->outbound_intr_status); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (status & MFI_OB_INTR_STATUS_MASK) 51362306a36Sopenharmony_ci mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; 51462306a36Sopenharmony_ci if (status & MFI_XSCALE_OMR0_CHANGE_INTERRUPT) 51562306a36Sopenharmony_ci mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci /* 51862306a36Sopenharmony_ci * Clear the interrupt by writing back the same value 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_ci if (mfiStatus) 52162306a36Sopenharmony_ci writel(status, ®s->outbound_intr_status); 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 52462306a36Sopenharmony_ci readl(®s->outbound_intr_status); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return mfiStatus; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/** 53062306a36Sopenharmony_ci * megasas_fire_cmd_xscale - Sends command to the FW 53162306a36Sopenharmony_ci * @instance: Adapter soft state 53262306a36Sopenharmony_ci * @frame_phys_addr : Physical address of cmd 53362306a36Sopenharmony_ci * @frame_count : Number of frames for the command 53462306a36Sopenharmony_ci * @regs : MFI register set 53562306a36Sopenharmony_ci */ 53662306a36Sopenharmony_cistatic inline void 53762306a36Sopenharmony_cimegasas_fire_cmd_xscale(struct megasas_instance *instance, 53862306a36Sopenharmony_ci dma_addr_t frame_phys_addr, 53962306a36Sopenharmony_ci u32 frame_count, 54062306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci unsigned long flags; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 54562306a36Sopenharmony_ci writel((frame_phys_addr >> 3)|(frame_count), 54662306a36Sopenharmony_ci &(regs)->inbound_queue_port); 54762306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/** 55162306a36Sopenharmony_ci * megasas_adp_reset_xscale - For controller reset 55262306a36Sopenharmony_ci * @instance: Adapter soft state 55362306a36Sopenharmony_ci * @regs: MFI register set 55462306a36Sopenharmony_ci */ 55562306a36Sopenharmony_cistatic int 55662306a36Sopenharmony_cimegasas_adp_reset_xscale(struct megasas_instance *instance, 55762306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci u32 i; 56062306a36Sopenharmony_ci u32 pcidata; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci writel(MFI_ADP_RESET, ®s->inbound_doorbell); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci for (i = 0; i < 3; i++) 56562306a36Sopenharmony_ci msleep(1000); /* sleep for 3 secs */ 56662306a36Sopenharmony_ci pcidata = 0; 56762306a36Sopenharmony_ci pci_read_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, &pcidata); 56862306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "pcidata = %x\n", pcidata); 56962306a36Sopenharmony_ci if (pcidata & 0x2) { 57062306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "mfi 1068 offset read=%x\n", pcidata); 57162306a36Sopenharmony_ci pcidata &= ~0x2; 57262306a36Sopenharmony_ci pci_write_config_dword(instance->pdev, 57362306a36Sopenharmony_ci MFI_1068_PCSR_OFFSET, pcidata); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci for (i = 0; i < 2; i++) 57662306a36Sopenharmony_ci msleep(1000); /* need to wait 2 secs again */ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci pcidata = 0; 57962306a36Sopenharmony_ci pci_read_config_dword(instance->pdev, 58062306a36Sopenharmony_ci MFI_1068_FW_HANDSHAKE_OFFSET, &pcidata); 58162306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "1068 offset handshake read=%x\n", pcidata); 58262306a36Sopenharmony_ci if ((pcidata & 0xffff0000) == MFI_1068_FW_READY) { 58362306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "1068 offset pcidt=%x\n", pcidata); 58462306a36Sopenharmony_ci pcidata = 0; 58562306a36Sopenharmony_ci pci_write_config_dword(instance->pdev, 58662306a36Sopenharmony_ci MFI_1068_FW_HANDSHAKE_OFFSET, pcidata); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci return 0; 59062306a36Sopenharmony_ci} 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci/** 59362306a36Sopenharmony_ci * megasas_check_reset_xscale - For controller reset check 59462306a36Sopenharmony_ci * @instance: Adapter soft state 59562306a36Sopenharmony_ci * @regs: MFI register set 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_cistatic int 59862306a36Sopenharmony_cimegasas_check_reset_xscale(struct megasas_instance *instance, 59962306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci if ((atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) && 60262306a36Sopenharmony_ci (le32_to_cpu(*instance->consumer) == 60362306a36Sopenharmony_ci MEGASAS_ADPRESET_INPROG_SIGN)) 60462306a36Sopenharmony_ci return 1; 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci} 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_cistatic struct megasas_instance_template megasas_instance_template_xscale = { 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci .fire_cmd = megasas_fire_cmd_xscale, 61162306a36Sopenharmony_ci .enable_intr = megasas_enable_intr_xscale, 61262306a36Sopenharmony_ci .disable_intr = megasas_disable_intr_xscale, 61362306a36Sopenharmony_ci .clear_intr = megasas_clear_intr_xscale, 61462306a36Sopenharmony_ci .read_fw_status_reg = megasas_read_fw_status_reg_xscale, 61562306a36Sopenharmony_ci .adp_reset = megasas_adp_reset_xscale, 61662306a36Sopenharmony_ci .check_reset = megasas_check_reset_xscale, 61762306a36Sopenharmony_ci .service_isr = megasas_isr, 61862306a36Sopenharmony_ci .tasklet = megasas_complete_cmd_dpc, 61962306a36Sopenharmony_ci .init_adapter = megasas_init_adapter_mfi, 62062306a36Sopenharmony_ci .build_and_issue_cmd = megasas_build_and_issue_cmd, 62162306a36Sopenharmony_ci .issue_dcmd = megasas_issue_dcmd, 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci/* 62562306a36Sopenharmony_ci * This is the end of set of functions & definitions specific 62662306a36Sopenharmony_ci * to xscale (deviceid : 1064R, PERC5) controllers 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci/* 63062306a36Sopenharmony_ci * The following functions are defined for ppc (deviceid : 0x60) 63162306a36Sopenharmony_ci * controllers 63262306a36Sopenharmony_ci */ 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci/** 63562306a36Sopenharmony_ci * megasas_enable_intr_ppc - Enables interrupts 63662306a36Sopenharmony_ci * @instance: Adapter soft state 63762306a36Sopenharmony_ci */ 63862306a36Sopenharmony_cistatic inline void 63962306a36Sopenharmony_cimegasas_enable_intr_ppc(struct megasas_instance *instance) 64062306a36Sopenharmony_ci{ 64162306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci regs = instance->reg_set; 64462306a36Sopenharmony_ci writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci writel(~0x80000000, &(regs)->outbound_intr_mask); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 64962306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci/** 65362306a36Sopenharmony_ci * megasas_disable_intr_ppc - Disable interrupt 65462306a36Sopenharmony_ci * @instance: Adapter soft state 65562306a36Sopenharmony_ci */ 65662306a36Sopenharmony_cistatic inline void 65762306a36Sopenharmony_cimegasas_disable_intr_ppc(struct megasas_instance *instance) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 66062306a36Sopenharmony_ci u32 mask = 0xFFFFFFFF; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci regs = instance->reg_set; 66362306a36Sopenharmony_ci writel(mask, ®s->outbound_intr_mask); 66462306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 66562306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/** 66962306a36Sopenharmony_ci * megasas_read_fw_status_reg_ppc - returns the current FW status value 67062306a36Sopenharmony_ci * @instance: Adapter soft state 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_cistatic u32 67362306a36Sopenharmony_cimegasas_read_fw_status_reg_ppc(struct megasas_instance *instance) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci return readl(&instance->reg_set->outbound_scratch_pad_0); 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci/** 67962306a36Sopenharmony_ci * megasas_clear_intr_ppc - Check & clear interrupt 68062306a36Sopenharmony_ci * @instance: Adapter soft state 68162306a36Sopenharmony_ci */ 68262306a36Sopenharmony_cistatic int 68362306a36Sopenharmony_cimegasas_clear_intr_ppc(struct megasas_instance *instance) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci u32 status, mfiStatus = 0; 68662306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 68762306a36Sopenharmony_ci regs = instance->reg_set; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci /* 69062306a36Sopenharmony_ci * Check if it is our interrupt 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci status = readl(®s->outbound_intr_status); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (status & MFI_REPLY_1078_MESSAGE_INTERRUPT) 69562306a36Sopenharmony_ci mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (status & MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT) 69862306a36Sopenharmony_ci mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * Clear the interrupt by writing back the same value 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci writel(status, ®s->outbound_doorbell_clear); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 70662306a36Sopenharmony_ci readl(®s->outbound_doorbell_clear); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return mfiStatus; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci/** 71262306a36Sopenharmony_ci * megasas_fire_cmd_ppc - Sends command to the FW 71362306a36Sopenharmony_ci * @instance: Adapter soft state 71462306a36Sopenharmony_ci * @frame_phys_addr: Physical address of cmd 71562306a36Sopenharmony_ci * @frame_count: Number of frames for the command 71662306a36Sopenharmony_ci * @regs: MFI register set 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_cistatic inline void 71962306a36Sopenharmony_cimegasas_fire_cmd_ppc(struct megasas_instance *instance, 72062306a36Sopenharmony_ci dma_addr_t frame_phys_addr, 72162306a36Sopenharmony_ci u32 frame_count, 72262306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 72362306a36Sopenharmony_ci{ 72462306a36Sopenharmony_ci unsigned long flags; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 72762306a36Sopenharmony_ci writel((frame_phys_addr | (frame_count<<1))|1, 72862306a36Sopenharmony_ci &(regs)->inbound_queue_port); 72962306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci/** 73362306a36Sopenharmony_ci * megasas_check_reset_ppc - For controller reset check 73462306a36Sopenharmony_ci * @instance: Adapter soft state 73562306a36Sopenharmony_ci * @regs: MFI register set 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_cistatic int 73862306a36Sopenharmony_cimegasas_check_reset_ppc(struct megasas_instance *instance, 73962306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 74062306a36Sopenharmony_ci{ 74162306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) 74262306a36Sopenharmony_ci return 1; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci return 0; 74562306a36Sopenharmony_ci} 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_cistatic struct megasas_instance_template megasas_instance_template_ppc = { 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci .fire_cmd = megasas_fire_cmd_ppc, 75062306a36Sopenharmony_ci .enable_intr = megasas_enable_intr_ppc, 75162306a36Sopenharmony_ci .disable_intr = megasas_disable_intr_ppc, 75262306a36Sopenharmony_ci .clear_intr = megasas_clear_intr_ppc, 75362306a36Sopenharmony_ci .read_fw_status_reg = megasas_read_fw_status_reg_ppc, 75462306a36Sopenharmony_ci .adp_reset = megasas_adp_reset_xscale, 75562306a36Sopenharmony_ci .check_reset = megasas_check_reset_ppc, 75662306a36Sopenharmony_ci .service_isr = megasas_isr, 75762306a36Sopenharmony_ci .tasklet = megasas_complete_cmd_dpc, 75862306a36Sopenharmony_ci .init_adapter = megasas_init_adapter_mfi, 75962306a36Sopenharmony_ci .build_and_issue_cmd = megasas_build_and_issue_cmd, 76062306a36Sopenharmony_ci .issue_dcmd = megasas_issue_dcmd, 76162306a36Sopenharmony_ci}; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/** 76462306a36Sopenharmony_ci * megasas_enable_intr_skinny - Enables interrupts 76562306a36Sopenharmony_ci * @instance: Adapter soft state 76662306a36Sopenharmony_ci */ 76762306a36Sopenharmony_cistatic inline void 76862306a36Sopenharmony_cimegasas_enable_intr_skinny(struct megasas_instance *instance) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci regs = instance->reg_set; 77362306a36Sopenharmony_ci writel(0xFFFFFFFF, &(regs)->outbound_intr_mask); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 77862306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci/** 78262306a36Sopenharmony_ci * megasas_disable_intr_skinny - Disables interrupt 78362306a36Sopenharmony_ci * @instance: Adapter soft state 78462306a36Sopenharmony_ci */ 78562306a36Sopenharmony_cistatic inline void 78662306a36Sopenharmony_cimegasas_disable_intr_skinny(struct megasas_instance *instance) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 78962306a36Sopenharmony_ci u32 mask = 0xFFFFFFFF; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci regs = instance->reg_set; 79262306a36Sopenharmony_ci writel(mask, ®s->outbound_intr_mask); 79362306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 79462306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci/** 79862306a36Sopenharmony_ci * megasas_read_fw_status_reg_skinny - returns the current FW status value 79962306a36Sopenharmony_ci * @instance: Adapter soft state 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_cistatic u32 80262306a36Sopenharmony_cimegasas_read_fw_status_reg_skinny(struct megasas_instance *instance) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci return readl(&instance->reg_set->outbound_scratch_pad_0); 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/** 80862306a36Sopenharmony_ci * megasas_clear_intr_skinny - Check & clear interrupt 80962306a36Sopenharmony_ci * @instance: Adapter soft state 81062306a36Sopenharmony_ci */ 81162306a36Sopenharmony_cistatic int 81262306a36Sopenharmony_cimegasas_clear_intr_skinny(struct megasas_instance *instance) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci u32 status; 81562306a36Sopenharmony_ci u32 mfiStatus = 0; 81662306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 81762306a36Sopenharmony_ci regs = instance->reg_set; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* 82062306a36Sopenharmony_ci * Check if it is our interrupt 82162306a36Sopenharmony_ci */ 82262306a36Sopenharmony_ci status = readl(®s->outbound_intr_status); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) { 82562306a36Sopenharmony_ci return 0; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* 82962306a36Sopenharmony_ci * Check if it is our interrupt 83062306a36Sopenharmony_ci */ 83162306a36Sopenharmony_ci if ((megasas_read_fw_status_reg_skinny(instance) & MFI_STATE_MASK) == 83262306a36Sopenharmony_ci MFI_STATE_FAULT) { 83362306a36Sopenharmony_ci mfiStatus = MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; 83462306a36Sopenharmony_ci } else 83562306a36Sopenharmony_ci mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* 83862306a36Sopenharmony_ci * Clear the interrupt by writing back the same value 83962306a36Sopenharmony_ci */ 84062306a36Sopenharmony_ci writel(status, ®s->outbound_intr_status); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci /* 84362306a36Sopenharmony_ci * dummy read to flush PCI 84462306a36Sopenharmony_ci */ 84562306a36Sopenharmony_ci readl(®s->outbound_intr_status); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return mfiStatus; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci/** 85162306a36Sopenharmony_ci * megasas_fire_cmd_skinny - Sends command to the FW 85262306a36Sopenharmony_ci * @instance: Adapter soft state 85362306a36Sopenharmony_ci * @frame_phys_addr: Physical address of cmd 85462306a36Sopenharmony_ci * @frame_count: Number of frames for the command 85562306a36Sopenharmony_ci * @regs: MFI register set 85662306a36Sopenharmony_ci */ 85762306a36Sopenharmony_cistatic inline void 85862306a36Sopenharmony_cimegasas_fire_cmd_skinny(struct megasas_instance *instance, 85962306a36Sopenharmony_ci dma_addr_t frame_phys_addr, 86062306a36Sopenharmony_ci u32 frame_count, 86162306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 86262306a36Sopenharmony_ci{ 86362306a36Sopenharmony_ci unsigned long flags; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 86662306a36Sopenharmony_ci writel(upper_32_bits(frame_phys_addr), 86762306a36Sopenharmony_ci &(regs)->inbound_high_queue_port); 86862306a36Sopenharmony_ci writel((lower_32_bits(frame_phys_addr) | (frame_count<<1))|1, 86962306a36Sopenharmony_ci &(regs)->inbound_low_queue_port); 87062306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/** 87462306a36Sopenharmony_ci * megasas_check_reset_skinny - For controller reset check 87562306a36Sopenharmony_ci * @instance: Adapter soft state 87662306a36Sopenharmony_ci * @regs: MFI register set 87762306a36Sopenharmony_ci */ 87862306a36Sopenharmony_cistatic int 87962306a36Sopenharmony_cimegasas_check_reset_skinny(struct megasas_instance *instance, 88062306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) 88362306a36Sopenharmony_ci return 1; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci return 0; 88662306a36Sopenharmony_ci} 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cistatic struct megasas_instance_template megasas_instance_template_skinny = { 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci .fire_cmd = megasas_fire_cmd_skinny, 89162306a36Sopenharmony_ci .enable_intr = megasas_enable_intr_skinny, 89262306a36Sopenharmony_ci .disable_intr = megasas_disable_intr_skinny, 89362306a36Sopenharmony_ci .clear_intr = megasas_clear_intr_skinny, 89462306a36Sopenharmony_ci .read_fw_status_reg = megasas_read_fw_status_reg_skinny, 89562306a36Sopenharmony_ci .adp_reset = megasas_adp_reset_gen2, 89662306a36Sopenharmony_ci .check_reset = megasas_check_reset_skinny, 89762306a36Sopenharmony_ci .service_isr = megasas_isr, 89862306a36Sopenharmony_ci .tasklet = megasas_complete_cmd_dpc, 89962306a36Sopenharmony_ci .init_adapter = megasas_init_adapter_mfi, 90062306a36Sopenharmony_ci .build_and_issue_cmd = megasas_build_and_issue_cmd, 90162306a36Sopenharmony_ci .issue_dcmd = megasas_issue_dcmd, 90262306a36Sopenharmony_ci}; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/* 90662306a36Sopenharmony_ci * The following functions are defined for gen2 (deviceid : 0x78 0x79) 90762306a36Sopenharmony_ci * controllers 90862306a36Sopenharmony_ci */ 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci/** 91162306a36Sopenharmony_ci * megasas_enable_intr_gen2 - Enables interrupts 91262306a36Sopenharmony_ci * @instance: Adapter soft state 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_cistatic inline void 91562306a36Sopenharmony_cimegasas_enable_intr_gen2(struct megasas_instance *instance) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci regs = instance->reg_set; 92062306a36Sopenharmony_ci writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* write ~0x00000005 (4 & 1) to the intr mask*/ 92362306a36Sopenharmony_ci writel(~MFI_GEN2_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 92662306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 92762306a36Sopenharmony_ci} 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci/** 93062306a36Sopenharmony_ci * megasas_disable_intr_gen2 - Disables interrupt 93162306a36Sopenharmony_ci * @instance: Adapter soft state 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_cistatic inline void 93462306a36Sopenharmony_cimegasas_disable_intr_gen2(struct megasas_instance *instance) 93562306a36Sopenharmony_ci{ 93662306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 93762306a36Sopenharmony_ci u32 mask = 0xFFFFFFFF; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci regs = instance->reg_set; 94062306a36Sopenharmony_ci writel(mask, ®s->outbound_intr_mask); 94162306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 94262306a36Sopenharmony_ci readl(®s->outbound_intr_mask); 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci/** 94662306a36Sopenharmony_ci * megasas_read_fw_status_reg_gen2 - returns the current FW status value 94762306a36Sopenharmony_ci * @instance: Adapter soft state 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_cistatic u32 95062306a36Sopenharmony_cimegasas_read_fw_status_reg_gen2(struct megasas_instance *instance) 95162306a36Sopenharmony_ci{ 95262306a36Sopenharmony_ci return readl(&instance->reg_set->outbound_scratch_pad_0); 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci/** 95662306a36Sopenharmony_ci * megasas_clear_intr_gen2 - Check & clear interrupt 95762306a36Sopenharmony_ci * @instance: Adapter soft state 95862306a36Sopenharmony_ci */ 95962306a36Sopenharmony_cistatic int 96062306a36Sopenharmony_cimegasas_clear_intr_gen2(struct megasas_instance *instance) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci u32 status; 96362306a36Sopenharmony_ci u32 mfiStatus = 0; 96462306a36Sopenharmony_ci struct megasas_register_set __iomem *regs; 96562306a36Sopenharmony_ci regs = instance->reg_set; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci /* 96862306a36Sopenharmony_ci * Check if it is our interrupt 96962306a36Sopenharmony_ci */ 97062306a36Sopenharmony_ci status = readl(®s->outbound_intr_status); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci if (status & MFI_INTR_FLAG_REPLY_MESSAGE) { 97362306a36Sopenharmony_ci mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci if (status & MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT) { 97662306a36Sopenharmony_ci mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci /* 98062306a36Sopenharmony_ci * Clear the interrupt by writing back the same value 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci if (mfiStatus) 98362306a36Sopenharmony_ci writel(status, ®s->outbound_doorbell_clear); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* Dummy readl to force pci flush */ 98662306a36Sopenharmony_ci readl(®s->outbound_intr_status); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return mfiStatus; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/** 99262306a36Sopenharmony_ci * megasas_fire_cmd_gen2 - Sends command to the FW 99362306a36Sopenharmony_ci * @instance: Adapter soft state 99462306a36Sopenharmony_ci * @frame_phys_addr: Physical address of cmd 99562306a36Sopenharmony_ci * @frame_count: Number of frames for the command 99662306a36Sopenharmony_ci * @regs: MFI register set 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_cistatic inline void 99962306a36Sopenharmony_cimegasas_fire_cmd_gen2(struct megasas_instance *instance, 100062306a36Sopenharmony_ci dma_addr_t frame_phys_addr, 100162306a36Sopenharmony_ci u32 frame_count, 100262306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci unsigned long flags; 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 100762306a36Sopenharmony_ci writel((frame_phys_addr | (frame_count<<1))|1, 100862306a36Sopenharmony_ci &(regs)->inbound_queue_port); 100962306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 101062306a36Sopenharmony_ci} 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci/** 101362306a36Sopenharmony_ci * megasas_adp_reset_gen2 - For controller reset 101462306a36Sopenharmony_ci * @instance: Adapter soft state 101562306a36Sopenharmony_ci * @reg_set: MFI register set 101662306a36Sopenharmony_ci */ 101762306a36Sopenharmony_cistatic int 101862306a36Sopenharmony_cimegasas_adp_reset_gen2(struct megasas_instance *instance, 101962306a36Sopenharmony_ci struct megasas_register_set __iomem *reg_set) 102062306a36Sopenharmony_ci{ 102162306a36Sopenharmony_ci u32 retry = 0 ; 102262306a36Sopenharmony_ci u32 HostDiag; 102362306a36Sopenharmony_ci u32 __iomem *seq_offset = ®_set->seq_offset; 102462306a36Sopenharmony_ci u32 __iomem *hostdiag_offset = ®_set->host_diag; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci if (instance->instancet == &megasas_instance_template_skinny) { 102762306a36Sopenharmony_ci seq_offset = ®_set->fusion_seq_offset; 102862306a36Sopenharmony_ci hostdiag_offset = ®_set->fusion_host_diag; 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci writel(0, seq_offset); 103262306a36Sopenharmony_ci writel(4, seq_offset); 103362306a36Sopenharmony_ci writel(0xb, seq_offset); 103462306a36Sopenharmony_ci writel(2, seq_offset); 103562306a36Sopenharmony_ci writel(7, seq_offset); 103662306a36Sopenharmony_ci writel(0xd, seq_offset); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci msleep(1000); 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci HostDiag = (u32)readl(hostdiag_offset); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci while (!(HostDiag & DIAG_WRITE_ENABLE)) { 104362306a36Sopenharmony_ci msleep(100); 104462306a36Sopenharmony_ci HostDiag = (u32)readl(hostdiag_offset); 104562306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "RESETGEN2: retry=%x, hostdiag=%x\n", 104662306a36Sopenharmony_ci retry, HostDiag); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci if (retry++ >= 100) 104962306a36Sopenharmony_ci return 1; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "ADP_RESET_GEN2: HostDiag=%x\n", HostDiag); 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci writel((HostDiag | DIAG_RESET_ADAPTER), hostdiag_offset); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci ssleep(10); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci HostDiag = (u32)readl(hostdiag_offset); 106062306a36Sopenharmony_ci while (HostDiag & DIAG_RESET_ADAPTER) { 106162306a36Sopenharmony_ci msleep(100); 106262306a36Sopenharmony_ci HostDiag = (u32)readl(hostdiag_offset); 106362306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "RESET_GEN2: retry=%x, hostdiag=%x\n", 106462306a36Sopenharmony_ci retry, HostDiag); 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci if (retry++ >= 1000) 106762306a36Sopenharmony_ci return 1; 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci/** 107462306a36Sopenharmony_ci * megasas_check_reset_gen2 - For controller reset check 107562306a36Sopenharmony_ci * @instance: Adapter soft state 107662306a36Sopenharmony_ci * @regs: MFI register set 107762306a36Sopenharmony_ci */ 107862306a36Sopenharmony_cistatic int 107962306a36Sopenharmony_cimegasas_check_reset_gen2(struct megasas_instance *instance, 108062306a36Sopenharmony_ci struct megasas_register_set __iomem *regs) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) 108362306a36Sopenharmony_ci return 1; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci return 0; 108662306a36Sopenharmony_ci} 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_cistatic struct megasas_instance_template megasas_instance_template_gen2 = { 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci .fire_cmd = megasas_fire_cmd_gen2, 109162306a36Sopenharmony_ci .enable_intr = megasas_enable_intr_gen2, 109262306a36Sopenharmony_ci .disable_intr = megasas_disable_intr_gen2, 109362306a36Sopenharmony_ci .clear_intr = megasas_clear_intr_gen2, 109462306a36Sopenharmony_ci .read_fw_status_reg = megasas_read_fw_status_reg_gen2, 109562306a36Sopenharmony_ci .adp_reset = megasas_adp_reset_gen2, 109662306a36Sopenharmony_ci .check_reset = megasas_check_reset_gen2, 109762306a36Sopenharmony_ci .service_isr = megasas_isr, 109862306a36Sopenharmony_ci .tasklet = megasas_complete_cmd_dpc, 109962306a36Sopenharmony_ci .init_adapter = megasas_init_adapter_mfi, 110062306a36Sopenharmony_ci .build_and_issue_cmd = megasas_build_and_issue_cmd, 110162306a36Sopenharmony_ci .issue_dcmd = megasas_issue_dcmd, 110262306a36Sopenharmony_ci}; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci/* 110562306a36Sopenharmony_ci * This is the end of set of functions & definitions 110662306a36Sopenharmony_ci * specific to gen2 (deviceid : 0x78, 0x79) controllers 110762306a36Sopenharmony_ci */ 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci/* 111062306a36Sopenharmony_ci * Template added for TB (Fusion) 111162306a36Sopenharmony_ci */ 111262306a36Sopenharmony_ciextern struct megasas_instance_template megasas_instance_template_fusion; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci/** 111562306a36Sopenharmony_ci * megasas_issue_polled - Issues a polling command 111662306a36Sopenharmony_ci * @instance: Adapter soft state 111762306a36Sopenharmony_ci * @cmd: Command packet to be issued 111862306a36Sopenharmony_ci * 111962306a36Sopenharmony_ci * For polling, MFI requires the cmd_status to be set to MFI_STAT_INVALID_STATUS before posting. 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ciint 112262306a36Sopenharmony_cimegasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) 112362306a36Sopenharmony_ci{ 112462306a36Sopenharmony_ci struct megasas_header *frame_hdr = &cmd->frame->hdr; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci frame_hdr->cmd_status = MFI_STAT_INVALID_STATUS; 112762306a36Sopenharmony_ci frame_hdr->flags |= cpu_to_le16(MFI_FRAME_DONT_POST_IN_REPLY_QUEUE); 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 113062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed from %s %d\n", 113162306a36Sopenharmony_ci __func__, __LINE__); 113262306a36Sopenharmony_ci return DCMD_INIT; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci instance->instancet->issue_dcmd(instance, cmd); 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci return wait_and_poll(instance, cmd, instance->requestorId ? 113862306a36Sopenharmony_ci MEGASAS_ROUTINE_WAIT_TIME_VF : MFI_IO_TIMEOUT_SECS); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci/** 114262306a36Sopenharmony_ci * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds 114362306a36Sopenharmony_ci * @instance: Adapter soft state 114462306a36Sopenharmony_ci * @cmd: Command to be issued 114562306a36Sopenharmony_ci * @timeout: Timeout in seconds 114662306a36Sopenharmony_ci * 114762306a36Sopenharmony_ci * This function waits on an event for the command to be returned from ISR. 114862306a36Sopenharmony_ci * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs 114962306a36Sopenharmony_ci * Used to issue ioctl commands. 115062306a36Sopenharmony_ci */ 115162306a36Sopenharmony_ciint 115262306a36Sopenharmony_cimegasas_issue_blocked_cmd(struct megasas_instance *instance, 115362306a36Sopenharmony_ci struct megasas_cmd *cmd, int timeout) 115462306a36Sopenharmony_ci{ 115562306a36Sopenharmony_ci int ret = 0; 115662306a36Sopenharmony_ci cmd->cmd_status_drv = DCMD_INIT; 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 115962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed from %s %d\n", 116062306a36Sopenharmony_ci __func__, __LINE__); 116162306a36Sopenharmony_ci return DCMD_INIT; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci instance->instancet->issue_dcmd(instance, cmd); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (timeout) { 116762306a36Sopenharmony_ci ret = wait_event_timeout(instance->int_cmd_wait_q, 116862306a36Sopenharmony_ci cmd->cmd_status_drv != DCMD_INIT, timeout * HZ); 116962306a36Sopenharmony_ci if (!ret) { 117062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 117162306a36Sopenharmony_ci "DCMD(opcode: 0x%x) is timed out, func:%s\n", 117262306a36Sopenharmony_ci cmd->frame->dcmd.opcode, __func__); 117362306a36Sopenharmony_ci return DCMD_TIMEOUT; 117462306a36Sopenharmony_ci } 117562306a36Sopenharmony_ci } else 117662306a36Sopenharmony_ci wait_event(instance->int_cmd_wait_q, 117762306a36Sopenharmony_ci cmd->cmd_status_drv != DCMD_INIT); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci return cmd->cmd_status_drv; 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci/** 118362306a36Sopenharmony_ci * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd 118462306a36Sopenharmony_ci * @instance: Adapter soft state 118562306a36Sopenharmony_ci * @cmd_to_abort: Previously issued cmd to be aborted 118662306a36Sopenharmony_ci * @timeout: Timeout in seconds 118762306a36Sopenharmony_ci * 118862306a36Sopenharmony_ci * MFI firmware can abort previously issued AEN comamnd (automatic event 118962306a36Sopenharmony_ci * notification). The megasas_issue_blocked_abort_cmd() issues such abort 119062306a36Sopenharmony_ci * cmd and waits for return status. 119162306a36Sopenharmony_ci * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs 119262306a36Sopenharmony_ci */ 119362306a36Sopenharmony_cistatic int 119462306a36Sopenharmony_cimegasas_issue_blocked_abort_cmd(struct megasas_instance *instance, 119562306a36Sopenharmony_ci struct megasas_cmd *cmd_to_abort, int timeout) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct megasas_cmd *cmd; 119862306a36Sopenharmony_ci struct megasas_abort_frame *abort_fr; 119962306a36Sopenharmony_ci int ret = 0; 120062306a36Sopenharmony_ci u32 opcode; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (!cmd) 120562306a36Sopenharmony_ci return -1; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci abort_fr = &cmd->frame->abort; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* 121062306a36Sopenharmony_ci * Prepare and issue the abort frame 121162306a36Sopenharmony_ci */ 121262306a36Sopenharmony_ci abort_fr->cmd = MFI_CMD_ABORT; 121362306a36Sopenharmony_ci abort_fr->cmd_status = MFI_STAT_INVALID_STATUS; 121462306a36Sopenharmony_ci abort_fr->flags = cpu_to_le16(0); 121562306a36Sopenharmony_ci abort_fr->abort_context = cpu_to_le32(cmd_to_abort->index); 121662306a36Sopenharmony_ci abort_fr->abort_mfi_phys_addr_lo = 121762306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(cmd_to_abort->frame_phys_addr)); 121862306a36Sopenharmony_ci abort_fr->abort_mfi_phys_addr_hi = 121962306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(cmd_to_abort->frame_phys_addr)); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci cmd->sync_cmd = 1; 122262306a36Sopenharmony_ci cmd->cmd_status_drv = DCMD_INIT; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 122562306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed from %s %d\n", 122662306a36Sopenharmony_ci __func__, __LINE__); 122762306a36Sopenharmony_ci return DCMD_INIT; 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci instance->instancet->issue_dcmd(instance, cmd); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci if (timeout) { 123362306a36Sopenharmony_ci ret = wait_event_timeout(instance->abort_cmd_wait_q, 123462306a36Sopenharmony_ci cmd->cmd_status_drv != DCMD_INIT, timeout * HZ); 123562306a36Sopenharmony_ci if (!ret) { 123662306a36Sopenharmony_ci opcode = cmd_to_abort->frame->dcmd.opcode; 123762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 123862306a36Sopenharmony_ci "Abort(to be aborted DCMD opcode: 0x%x) is timed out func:%s\n", 123962306a36Sopenharmony_ci opcode, __func__); 124062306a36Sopenharmony_ci return DCMD_TIMEOUT; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci } else 124362306a36Sopenharmony_ci wait_event(instance->abort_cmd_wait_q, 124462306a36Sopenharmony_ci cmd->cmd_status_drv != DCMD_INIT); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci cmd->sync_cmd = 0; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 124962306a36Sopenharmony_ci return cmd->cmd_status_drv; 125062306a36Sopenharmony_ci} 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci/** 125362306a36Sopenharmony_ci * megasas_make_sgl32 - Prepares 32-bit SGL 125462306a36Sopenharmony_ci * @instance: Adapter soft state 125562306a36Sopenharmony_ci * @scp: SCSI command from the mid-layer 125662306a36Sopenharmony_ci * @mfi_sgl: SGL to be filled in 125762306a36Sopenharmony_ci * 125862306a36Sopenharmony_ci * If successful, this function returns the number of SG elements. Otherwise, 125962306a36Sopenharmony_ci * it returnes -1. 126062306a36Sopenharmony_ci */ 126162306a36Sopenharmony_cistatic int 126262306a36Sopenharmony_cimegasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp, 126362306a36Sopenharmony_ci union megasas_sgl *mfi_sgl) 126462306a36Sopenharmony_ci{ 126562306a36Sopenharmony_ci int i; 126662306a36Sopenharmony_ci int sge_count; 126762306a36Sopenharmony_ci struct scatterlist *os_sgl; 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci sge_count = scsi_dma_map(scp); 127062306a36Sopenharmony_ci BUG_ON(sge_count < 0); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci if (sge_count) { 127362306a36Sopenharmony_ci scsi_for_each_sg(scp, os_sgl, sge_count, i) { 127462306a36Sopenharmony_ci mfi_sgl->sge32[i].length = cpu_to_le32(sg_dma_len(os_sgl)); 127562306a36Sopenharmony_ci mfi_sgl->sge32[i].phys_addr = cpu_to_le32(sg_dma_address(os_sgl)); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci return sge_count; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci/** 128262306a36Sopenharmony_ci * megasas_make_sgl64 - Prepares 64-bit SGL 128362306a36Sopenharmony_ci * @instance: Adapter soft state 128462306a36Sopenharmony_ci * @scp: SCSI command from the mid-layer 128562306a36Sopenharmony_ci * @mfi_sgl: SGL to be filled in 128662306a36Sopenharmony_ci * 128762306a36Sopenharmony_ci * If successful, this function returns the number of SG elements. Otherwise, 128862306a36Sopenharmony_ci * it returnes -1. 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_cistatic int 129162306a36Sopenharmony_cimegasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, 129262306a36Sopenharmony_ci union megasas_sgl *mfi_sgl) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci int i; 129562306a36Sopenharmony_ci int sge_count; 129662306a36Sopenharmony_ci struct scatterlist *os_sgl; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci sge_count = scsi_dma_map(scp); 129962306a36Sopenharmony_ci BUG_ON(sge_count < 0); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (sge_count) { 130262306a36Sopenharmony_ci scsi_for_each_sg(scp, os_sgl, sge_count, i) { 130362306a36Sopenharmony_ci mfi_sgl->sge64[i].length = cpu_to_le32(sg_dma_len(os_sgl)); 130462306a36Sopenharmony_ci mfi_sgl->sge64[i].phys_addr = cpu_to_le64(sg_dma_address(os_sgl)); 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci return sge_count; 130862306a36Sopenharmony_ci} 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci/** 131162306a36Sopenharmony_ci * megasas_make_sgl_skinny - Prepares IEEE SGL 131262306a36Sopenharmony_ci * @instance: Adapter soft state 131362306a36Sopenharmony_ci * @scp: SCSI command from the mid-layer 131462306a36Sopenharmony_ci * @mfi_sgl: SGL to be filled in 131562306a36Sopenharmony_ci * 131662306a36Sopenharmony_ci * If successful, this function returns the number of SG elements. Otherwise, 131762306a36Sopenharmony_ci * it returnes -1. 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_cistatic int 132062306a36Sopenharmony_cimegasas_make_sgl_skinny(struct megasas_instance *instance, 132162306a36Sopenharmony_ci struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci int i; 132462306a36Sopenharmony_ci int sge_count; 132562306a36Sopenharmony_ci struct scatterlist *os_sgl; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci sge_count = scsi_dma_map(scp); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci if (sge_count) { 133062306a36Sopenharmony_ci scsi_for_each_sg(scp, os_sgl, sge_count, i) { 133162306a36Sopenharmony_ci mfi_sgl->sge_skinny[i].length = 133262306a36Sopenharmony_ci cpu_to_le32(sg_dma_len(os_sgl)); 133362306a36Sopenharmony_ci mfi_sgl->sge_skinny[i].phys_addr = 133462306a36Sopenharmony_ci cpu_to_le64(sg_dma_address(os_sgl)); 133562306a36Sopenharmony_ci mfi_sgl->sge_skinny[i].flag = cpu_to_le32(0); 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci return sge_count; 133962306a36Sopenharmony_ci} 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci /** 134262306a36Sopenharmony_ci * megasas_get_frame_count - Computes the number of frames 134362306a36Sopenharmony_ci * @frame_type : type of frame- io or pthru frame 134462306a36Sopenharmony_ci * @sge_count : number of sg elements 134562306a36Sopenharmony_ci * 134662306a36Sopenharmony_ci * Returns the number of frames required for numnber of sge's (sge_count) 134762306a36Sopenharmony_ci */ 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_cistatic u32 megasas_get_frame_count(struct megasas_instance *instance, 135062306a36Sopenharmony_ci u8 sge_count, u8 frame_type) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci int num_cnt; 135362306a36Sopenharmony_ci int sge_bytes; 135462306a36Sopenharmony_ci u32 sge_sz; 135562306a36Sopenharmony_ci u32 frame_count = 0; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : 135862306a36Sopenharmony_ci sizeof(struct megasas_sge32); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci if (instance->flag_ieee) { 136162306a36Sopenharmony_ci sge_sz = sizeof(struct megasas_sge_skinny); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci /* 136562306a36Sopenharmony_ci * Main frame can contain 2 SGEs for 64-bit SGLs and 136662306a36Sopenharmony_ci * 3 SGEs for 32-bit SGLs for ldio & 136762306a36Sopenharmony_ci * 1 SGEs for 64-bit SGLs and 136862306a36Sopenharmony_ci * 2 SGEs for 32-bit SGLs for pthru frame 136962306a36Sopenharmony_ci */ 137062306a36Sopenharmony_ci if (unlikely(frame_type == PTHRU_FRAME)) { 137162306a36Sopenharmony_ci if (instance->flag_ieee == 1) { 137262306a36Sopenharmony_ci num_cnt = sge_count - 1; 137362306a36Sopenharmony_ci } else if (IS_DMA64) 137462306a36Sopenharmony_ci num_cnt = sge_count - 1; 137562306a36Sopenharmony_ci else 137662306a36Sopenharmony_ci num_cnt = sge_count - 2; 137762306a36Sopenharmony_ci } else { 137862306a36Sopenharmony_ci if (instance->flag_ieee == 1) { 137962306a36Sopenharmony_ci num_cnt = sge_count - 1; 138062306a36Sopenharmony_ci } else if (IS_DMA64) 138162306a36Sopenharmony_ci num_cnt = sge_count - 2; 138262306a36Sopenharmony_ci else 138362306a36Sopenharmony_ci num_cnt = sge_count - 3; 138462306a36Sopenharmony_ci } 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if (num_cnt > 0) { 138762306a36Sopenharmony_ci sge_bytes = sge_sz * num_cnt; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + 139062306a36Sopenharmony_ci ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) ; 139162306a36Sopenharmony_ci } 139262306a36Sopenharmony_ci /* Main frame */ 139362306a36Sopenharmony_ci frame_count += 1; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (frame_count > 7) 139662306a36Sopenharmony_ci frame_count = 8; 139762306a36Sopenharmony_ci return frame_count; 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci/** 140162306a36Sopenharmony_ci * megasas_build_dcdb - Prepares a direct cdb (DCDB) command 140262306a36Sopenharmony_ci * @instance: Adapter soft state 140362306a36Sopenharmony_ci * @scp: SCSI command 140462306a36Sopenharmony_ci * @cmd: Command to be prepared in 140562306a36Sopenharmony_ci * 140662306a36Sopenharmony_ci * This function prepares CDB commands. These are typcially pass-through 140762306a36Sopenharmony_ci * commands to the devices. 140862306a36Sopenharmony_ci */ 140962306a36Sopenharmony_cistatic int 141062306a36Sopenharmony_cimegasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, 141162306a36Sopenharmony_ci struct megasas_cmd *cmd) 141262306a36Sopenharmony_ci{ 141362306a36Sopenharmony_ci u32 is_logical; 141462306a36Sopenharmony_ci u32 device_id; 141562306a36Sopenharmony_ci u16 flags = 0; 141662306a36Sopenharmony_ci struct megasas_pthru_frame *pthru; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci is_logical = MEGASAS_IS_LOGICAL(scp->device); 141962306a36Sopenharmony_ci device_id = MEGASAS_DEV_INDEX(scp); 142062306a36Sopenharmony_ci pthru = (struct megasas_pthru_frame *)cmd->frame; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci if (scp->sc_data_direction == DMA_TO_DEVICE) 142362306a36Sopenharmony_ci flags = MFI_FRAME_DIR_WRITE; 142462306a36Sopenharmony_ci else if (scp->sc_data_direction == DMA_FROM_DEVICE) 142562306a36Sopenharmony_ci flags = MFI_FRAME_DIR_READ; 142662306a36Sopenharmony_ci else if (scp->sc_data_direction == DMA_NONE) 142762306a36Sopenharmony_ci flags = MFI_FRAME_DIR_NONE; 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_ci if (instance->flag_ieee == 1) { 143062306a36Sopenharmony_ci flags |= MFI_FRAME_IEEE; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci /* 143462306a36Sopenharmony_ci * Prepare the DCDB frame 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_ci pthru->cmd = (is_logical) ? MFI_CMD_LD_SCSI_IO : MFI_CMD_PD_SCSI_IO; 143762306a36Sopenharmony_ci pthru->cmd_status = 0x0; 143862306a36Sopenharmony_ci pthru->scsi_status = 0x0; 143962306a36Sopenharmony_ci pthru->target_id = device_id; 144062306a36Sopenharmony_ci pthru->lun = scp->device->lun; 144162306a36Sopenharmony_ci pthru->cdb_len = scp->cmd_len; 144262306a36Sopenharmony_ci pthru->timeout = 0; 144362306a36Sopenharmony_ci pthru->pad_0 = 0; 144462306a36Sopenharmony_ci pthru->flags = cpu_to_le16(flags); 144562306a36Sopenharmony_ci pthru->data_xfer_len = cpu_to_le32(scsi_bufflen(scp)); 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci /* 145062306a36Sopenharmony_ci * If the command is for the tape device, set the 145162306a36Sopenharmony_ci * pthru timeout to the os layer timeout value. 145262306a36Sopenharmony_ci */ 145362306a36Sopenharmony_ci if (scp->device->type == TYPE_TAPE) { 145462306a36Sopenharmony_ci if (scsi_cmd_to_rq(scp)->timeout / HZ > 0xFFFF) 145562306a36Sopenharmony_ci pthru->timeout = cpu_to_le16(0xFFFF); 145662306a36Sopenharmony_ci else 145762306a36Sopenharmony_ci pthru->timeout = cpu_to_le16(scsi_cmd_to_rq(scp)->timeout / HZ); 145862306a36Sopenharmony_ci } 145962306a36Sopenharmony_ci 146062306a36Sopenharmony_ci /* 146162306a36Sopenharmony_ci * Construct SGL 146262306a36Sopenharmony_ci */ 146362306a36Sopenharmony_ci if (instance->flag_ieee == 1) { 146462306a36Sopenharmony_ci pthru->flags |= cpu_to_le16(MFI_FRAME_SGL64); 146562306a36Sopenharmony_ci pthru->sge_count = megasas_make_sgl_skinny(instance, scp, 146662306a36Sopenharmony_ci &pthru->sgl); 146762306a36Sopenharmony_ci } else if (IS_DMA64) { 146862306a36Sopenharmony_ci pthru->flags |= cpu_to_le16(MFI_FRAME_SGL64); 146962306a36Sopenharmony_ci pthru->sge_count = megasas_make_sgl64(instance, scp, 147062306a36Sopenharmony_ci &pthru->sgl); 147162306a36Sopenharmony_ci } else 147262306a36Sopenharmony_ci pthru->sge_count = megasas_make_sgl32(instance, scp, 147362306a36Sopenharmony_ci &pthru->sgl); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci if (pthru->sge_count > instance->max_num_sge) { 147662306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "DCDB too many SGE NUM=%x\n", 147762306a36Sopenharmony_ci pthru->sge_count); 147862306a36Sopenharmony_ci return 0; 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci /* 148262306a36Sopenharmony_ci * Sense info specific 148362306a36Sopenharmony_ci */ 148462306a36Sopenharmony_ci pthru->sense_len = SCSI_SENSE_BUFFERSIZE; 148562306a36Sopenharmony_ci pthru->sense_buf_phys_addr_hi = 148662306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(cmd->sense_phys_addr)); 148762306a36Sopenharmony_ci pthru->sense_buf_phys_addr_lo = 148862306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(cmd->sense_phys_addr)); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* 149162306a36Sopenharmony_ci * Compute the total number of frames this command consumes. FW uses 149262306a36Sopenharmony_ci * this number to pull sufficient number of frames from host memory. 149362306a36Sopenharmony_ci */ 149462306a36Sopenharmony_ci cmd->frame_count = megasas_get_frame_count(instance, pthru->sge_count, 149562306a36Sopenharmony_ci PTHRU_FRAME); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return cmd->frame_count; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci/** 150162306a36Sopenharmony_ci * megasas_build_ldio - Prepares IOs to logical devices 150262306a36Sopenharmony_ci * @instance: Adapter soft state 150362306a36Sopenharmony_ci * @scp: SCSI command 150462306a36Sopenharmony_ci * @cmd: Command to be prepared 150562306a36Sopenharmony_ci * 150662306a36Sopenharmony_ci * Frames (and accompanying SGLs) for regular SCSI IOs use this function. 150762306a36Sopenharmony_ci */ 150862306a36Sopenharmony_cistatic int 150962306a36Sopenharmony_cimegasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, 151062306a36Sopenharmony_ci struct megasas_cmd *cmd) 151162306a36Sopenharmony_ci{ 151262306a36Sopenharmony_ci u32 device_id; 151362306a36Sopenharmony_ci u8 sc = scp->cmnd[0]; 151462306a36Sopenharmony_ci u16 flags = 0; 151562306a36Sopenharmony_ci struct megasas_io_frame *ldio; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci device_id = MEGASAS_DEV_INDEX(scp); 151862306a36Sopenharmony_ci ldio = (struct megasas_io_frame *)cmd->frame; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci if (scp->sc_data_direction == DMA_TO_DEVICE) 152162306a36Sopenharmony_ci flags = MFI_FRAME_DIR_WRITE; 152262306a36Sopenharmony_ci else if (scp->sc_data_direction == DMA_FROM_DEVICE) 152362306a36Sopenharmony_ci flags = MFI_FRAME_DIR_READ; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (instance->flag_ieee == 1) { 152662306a36Sopenharmony_ci flags |= MFI_FRAME_IEEE; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci /* 153062306a36Sopenharmony_ci * Prepare the Logical IO frame: 2nd bit is zero for all read cmds 153162306a36Sopenharmony_ci */ 153262306a36Sopenharmony_ci ldio->cmd = (sc & 0x02) ? MFI_CMD_LD_WRITE : MFI_CMD_LD_READ; 153362306a36Sopenharmony_ci ldio->cmd_status = 0x0; 153462306a36Sopenharmony_ci ldio->scsi_status = 0x0; 153562306a36Sopenharmony_ci ldio->target_id = device_id; 153662306a36Sopenharmony_ci ldio->timeout = 0; 153762306a36Sopenharmony_ci ldio->reserved_0 = 0; 153862306a36Sopenharmony_ci ldio->pad_0 = 0; 153962306a36Sopenharmony_ci ldio->flags = cpu_to_le16(flags); 154062306a36Sopenharmony_ci ldio->start_lba_hi = 0; 154162306a36Sopenharmony_ci ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci /* 154462306a36Sopenharmony_ci * 6-byte READ(0x08) or WRITE(0x0A) cdb 154562306a36Sopenharmony_ci */ 154662306a36Sopenharmony_ci if (scp->cmd_len == 6) { 154762306a36Sopenharmony_ci ldio->lba_count = cpu_to_le32((u32) scp->cmnd[4]); 154862306a36Sopenharmony_ci ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[1] << 16) | 154962306a36Sopenharmony_ci ((u32) scp->cmnd[2] << 8) | 155062306a36Sopenharmony_ci (u32) scp->cmnd[3]); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci ldio->start_lba_lo &= cpu_to_le32(0x1FFFFF); 155362306a36Sopenharmony_ci } 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* 155662306a36Sopenharmony_ci * 10-byte READ(0x28) or WRITE(0x2A) cdb 155762306a36Sopenharmony_ci */ 155862306a36Sopenharmony_ci else if (scp->cmd_len == 10) { 155962306a36Sopenharmony_ci ldio->lba_count = cpu_to_le32((u32) scp->cmnd[8] | 156062306a36Sopenharmony_ci ((u32) scp->cmnd[7] << 8)); 156162306a36Sopenharmony_ci ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[2] << 24) | 156262306a36Sopenharmony_ci ((u32) scp->cmnd[3] << 16) | 156362306a36Sopenharmony_ci ((u32) scp->cmnd[4] << 8) | 156462306a36Sopenharmony_ci (u32) scp->cmnd[5]); 156562306a36Sopenharmony_ci } 156662306a36Sopenharmony_ci 156762306a36Sopenharmony_ci /* 156862306a36Sopenharmony_ci * 12-byte READ(0xA8) or WRITE(0xAA) cdb 156962306a36Sopenharmony_ci */ 157062306a36Sopenharmony_ci else if (scp->cmd_len == 12) { 157162306a36Sopenharmony_ci ldio->lba_count = cpu_to_le32(((u32) scp->cmnd[6] << 24) | 157262306a36Sopenharmony_ci ((u32) scp->cmnd[7] << 16) | 157362306a36Sopenharmony_ci ((u32) scp->cmnd[8] << 8) | 157462306a36Sopenharmony_ci (u32) scp->cmnd[9]); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[2] << 24) | 157762306a36Sopenharmony_ci ((u32) scp->cmnd[3] << 16) | 157862306a36Sopenharmony_ci ((u32) scp->cmnd[4] << 8) | 157962306a36Sopenharmony_ci (u32) scp->cmnd[5]); 158062306a36Sopenharmony_ci } 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci /* 158362306a36Sopenharmony_ci * 16-byte READ(0x88) or WRITE(0x8A) cdb 158462306a36Sopenharmony_ci */ 158562306a36Sopenharmony_ci else if (scp->cmd_len == 16) { 158662306a36Sopenharmony_ci ldio->lba_count = cpu_to_le32(((u32) scp->cmnd[10] << 24) | 158762306a36Sopenharmony_ci ((u32) scp->cmnd[11] << 16) | 158862306a36Sopenharmony_ci ((u32) scp->cmnd[12] << 8) | 158962306a36Sopenharmony_ci (u32) scp->cmnd[13]); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci ldio->start_lba_lo = cpu_to_le32(((u32) scp->cmnd[6] << 24) | 159262306a36Sopenharmony_ci ((u32) scp->cmnd[7] << 16) | 159362306a36Sopenharmony_ci ((u32) scp->cmnd[8] << 8) | 159462306a36Sopenharmony_ci (u32) scp->cmnd[9]); 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci ldio->start_lba_hi = cpu_to_le32(((u32) scp->cmnd[2] << 24) | 159762306a36Sopenharmony_ci ((u32) scp->cmnd[3] << 16) | 159862306a36Sopenharmony_ci ((u32) scp->cmnd[4] << 8) | 159962306a36Sopenharmony_ci (u32) scp->cmnd[5]); 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci } 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci /* 160462306a36Sopenharmony_ci * Construct SGL 160562306a36Sopenharmony_ci */ 160662306a36Sopenharmony_ci if (instance->flag_ieee) { 160762306a36Sopenharmony_ci ldio->flags |= cpu_to_le16(MFI_FRAME_SGL64); 160862306a36Sopenharmony_ci ldio->sge_count = megasas_make_sgl_skinny(instance, scp, 160962306a36Sopenharmony_ci &ldio->sgl); 161062306a36Sopenharmony_ci } else if (IS_DMA64) { 161162306a36Sopenharmony_ci ldio->flags |= cpu_to_le16(MFI_FRAME_SGL64); 161262306a36Sopenharmony_ci ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl); 161362306a36Sopenharmony_ci } else 161462306a36Sopenharmony_ci ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl); 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci if (ldio->sge_count > instance->max_num_sge) { 161762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "build_ld_io: sge_count = %x\n", 161862306a36Sopenharmony_ci ldio->sge_count); 161962306a36Sopenharmony_ci return 0; 162062306a36Sopenharmony_ci } 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci /* 162362306a36Sopenharmony_ci * Sense info specific 162462306a36Sopenharmony_ci */ 162562306a36Sopenharmony_ci ldio->sense_len = SCSI_SENSE_BUFFERSIZE; 162662306a36Sopenharmony_ci ldio->sense_buf_phys_addr_hi = 0; 162762306a36Sopenharmony_ci ldio->sense_buf_phys_addr_lo = cpu_to_le32(cmd->sense_phys_addr); 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci /* 163062306a36Sopenharmony_ci * Compute the total number of frames this command consumes. FW uses 163162306a36Sopenharmony_ci * this number to pull sufficient number of frames from host memory. 163262306a36Sopenharmony_ci */ 163362306a36Sopenharmony_ci cmd->frame_count = megasas_get_frame_count(instance, 163462306a36Sopenharmony_ci ldio->sge_count, IO_FRAME); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci return cmd->frame_count; 163762306a36Sopenharmony_ci} 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_ci/** 164062306a36Sopenharmony_ci * megasas_cmd_type - Checks if the cmd is for logical drive/sysPD 164162306a36Sopenharmony_ci * and whether it's RW or non RW 164262306a36Sopenharmony_ci * @cmd: SCSI command 164362306a36Sopenharmony_ci * 164462306a36Sopenharmony_ci */ 164562306a36Sopenharmony_ciinline int megasas_cmd_type(struct scsi_cmnd *cmd) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci int ret; 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ci switch (cmd->cmnd[0]) { 165062306a36Sopenharmony_ci case READ_10: 165162306a36Sopenharmony_ci case WRITE_10: 165262306a36Sopenharmony_ci case READ_12: 165362306a36Sopenharmony_ci case WRITE_12: 165462306a36Sopenharmony_ci case READ_6: 165562306a36Sopenharmony_ci case WRITE_6: 165662306a36Sopenharmony_ci case READ_16: 165762306a36Sopenharmony_ci case WRITE_16: 165862306a36Sopenharmony_ci ret = (MEGASAS_IS_LOGICAL(cmd->device)) ? 165962306a36Sopenharmony_ci READ_WRITE_LDIO : READ_WRITE_SYSPDIO; 166062306a36Sopenharmony_ci break; 166162306a36Sopenharmony_ci default: 166262306a36Sopenharmony_ci ret = (MEGASAS_IS_LOGICAL(cmd->device)) ? 166362306a36Sopenharmony_ci NON_READ_WRITE_LDIO : NON_READ_WRITE_SYSPDIO; 166462306a36Sopenharmony_ci } 166562306a36Sopenharmony_ci return ret; 166662306a36Sopenharmony_ci} 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci /** 166962306a36Sopenharmony_ci * megasas_dump_pending_frames - Dumps the frame address of all pending cmds 167062306a36Sopenharmony_ci * in FW 167162306a36Sopenharmony_ci * @instance: Adapter soft state 167262306a36Sopenharmony_ci */ 167362306a36Sopenharmony_cistatic inline void 167462306a36Sopenharmony_cimegasas_dump_pending_frames(struct megasas_instance *instance) 167562306a36Sopenharmony_ci{ 167662306a36Sopenharmony_ci struct megasas_cmd *cmd; 167762306a36Sopenharmony_ci int i,n; 167862306a36Sopenharmony_ci union megasas_sgl *mfi_sgl; 167962306a36Sopenharmony_ci struct megasas_io_frame *ldio; 168062306a36Sopenharmony_ci struct megasas_pthru_frame *pthru; 168162306a36Sopenharmony_ci u32 sgcount; 168262306a36Sopenharmony_ci u16 max_cmd = instance->max_fw_cmds; 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: Dumping Frame Phys Address of all pending cmds in FW\n",instance->host->host_no); 168562306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: Total OS Pending cmds : %d\n",instance->host->host_no,atomic_read(&instance->fw_outstanding)); 168662306a36Sopenharmony_ci if (IS_DMA64) 168762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: 64 bit SGLs were sent to FW\n",instance->host->host_no); 168862306a36Sopenharmony_ci else 168962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: 32 bit SGLs were sent to FW\n",instance->host->host_no); 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: Pending OS cmds in FW : \n",instance->host->host_no); 169262306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 169362306a36Sopenharmony_ci cmd = instance->cmd_list[i]; 169462306a36Sopenharmony_ci if (!cmd->scmd) 169562306a36Sopenharmony_ci continue; 169662306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: Frame addr :0x%08lx : ",instance->host->host_no,(unsigned long)cmd->frame_phys_addr); 169762306a36Sopenharmony_ci if (megasas_cmd_type(cmd->scmd) == READ_WRITE_LDIO) { 169862306a36Sopenharmony_ci ldio = (struct megasas_io_frame *)cmd->frame; 169962306a36Sopenharmony_ci mfi_sgl = &ldio->sgl; 170062306a36Sopenharmony_ci sgcount = ldio->sge_count; 170162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x," 170262306a36Sopenharmony_ci " lba lo : 0x%x, lba_hi : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n", 170362306a36Sopenharmony_ci instance->host->host_no, cmd->frame_count, ldio->cmd, ldio->target_id, 170462306a36Sopenharmony_ci le32_to_cpu(ldio->start_lba_lo), le32_to_cpu(ldio->start_lba_hi), 170562306a36Sopenharmony_ci le32_to_cpu(ldio->sense_buf_phys_addr_lo), sgcount); 170662306a36Sopenharmony_ci } else { 170762306a36Sopenharmony_ci pthru = (struct megasas_pthru_frame *) cmd->frame; 170862306a36Sopenharmony_ci mfi_sgl = &pthru->sgl; 170962306a36Sopenharmony_ci sgcount = pthru->sge_count; 171062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: frame count : 0x%x, Cmd : 0x%x, Tgt id : 0x%x, " 171162306a36Sopenharmony_ci "lun : 0x%x, cdb_len : 0x%x, data xfer len : 0x%x, sense_buf addr : 0x%x,sge count : 0x%x\n", 171262306a36Sopenharmony_ci instance->host->host_no, cmd->frame_count, pthru->cmd, pthru->target_id, 171362306a36Sopenharmony_ci pthru->lun, pthru->cdb_len, le32_to_cpu(pthru->data_xfer_len), 171462306a36Sopenharmony_ci le32_to_cpu(pthru->sense_buf_phys_addr_lo), sgcount); 171562306a36Sopenharmony_ci } 171662306a36Sopenharmony_ci if (megasas_dbg_lvl & MEGASAS_DBG_LVL) { 171762306a36Sopenharmony_ci for (n = 0; n < sgcount; n++) { 171862306a36Sopenharmony_ci if (IS_DMA64) 171962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%llx\n", 172062306a36Sopenharmony_ci le32_to_cpu(mfi_sgl->sge64[n].length), 172162306a36Sopenharmony_ci le64_to_cpu(mfi_sgl->sge64[n].phys_addr)); 172262306a36Sopenharmony_ci else 172362306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "sgl len : 0x%x, sgl addr : 0x%x\n", 172462306a36Sopenharmony_ci le32_to_cpu(mfi_sgl->sge32[n].length), 172562306a36Sopenharmony_ci le32_to_cpu(mfi_sgl->sge32[n].phys_addr)); 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci } 172862306a36Sopenharmony_ci } /*for max_cmd*/ 172962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: Pending Internal cmds in FW : \n",instance->host->host_no); 173062306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci cmd = instance->cmd_list[i]; 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci if (cmd->sync_cmd == 1) 173562306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "0x%08lx : ", (unsigned long)cmd->frame_phys_addr); 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "[%d]: Dumping Done\n\n",instance->host->host_no); 173862306a36Sopenharmony_ci} 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ciu32 174162306a36Sopenharmony_cimegasas_build_and_issue_cmd(struct megasas_instance *instance, 174262306a36Sopenharmony_ci struct scsi_cmnd *scmd) 174362306a36Sopenharmony_ci{ 174462306a36Sopenharmony_ci struct megasas_cmd *cmd; 174562306a36Sopenharmony_ci u32 frame_count; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 174862306a36Sopenharmony_ci if (!cmd) 174962306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci /* 175262306a36Sopenharmony_ci * Logical drive command 175362306a36Sopenharmony_ci */ 175462306a36Sopenharmony_ci if (megasas_cmd_type(scmd) == READ_WRITE_LDIO) 175562306a36Sopenharmony_ci frame_count = megasas_build_ldio(instance, scmd, cmd); 175662306a36Sopenharmony_ci else 175762306a36Sopenharmony_ci frame_count = megasas_build_dcdb(instance, scmd, cmd); 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci if (!frame_count) 176062306a36Sopenharmony_ci goto out_return_cmd; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci cmd->scmd = scmd; 176362306a36Sopenharmony_ci megasas_priv(scmd)->cmd_priv = cmd; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci /* 176662306a36Sopenharmony_ci * Issue the command to the FW 176762306a36Sopenharmony_ci */ 176862306a36Sopenharmony_ci atomic_inc(&instance->fw_outstanding); 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, 177162306a36Sopenharmony_ci cmd->frame_count-1, instance->reg_set); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci return 0; 177462306a36Sopenharmony_ciout_return_cmd: 177562306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 177662306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci/** 178162306a36Sopenharmony_ci * megasas_queue_command - Queue entry point 178262306a36Sopenharmony_ci * @shost: adapter SCSI host 178362306a36Sopenharmony_ci * @scmd: SCSI command to be queued 178462306a36Sopenharmony_ci */ 178562306a36Sopenharmony_cistatic int 178662306a36Sopenharmony_cimegasas_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scmd) 178762306a36Sopenharmony_ci{ 178862306a36Sopenharmony_ci struct megasas_instance *instance; 178962306a36Sopenharmony_ci struct MR_PRIV_DEVICE *mr_device_priv_data; 179062306a36Sopenharmony_ci u32 ld_tgt_id; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci instance = (struct megasas_instance *) 179362306a36Sopenharmony_ci scmd->device->host->hostdata; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (instance->unload == 1) { 179662306a36Sopenharmony_ci scmd->result = DID_NO_CONNECT << 16; 179762306a36Sopenharmony_ci scsi_done(scmd); 179862306a36Sopenharmony_ci return 0; 179962306a36Sopenharmony_ci } 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (instance->issuepend_done == 0) 180262306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci /* Check for an mpio path and adjust behavior */ 180662306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_ADPRESET_SM_INFAULT) { 180762306a36Sopenharmony_ci if (megasas_check_mpio_paths(instance, scmd) == 180862306a36Sopenharmony_ci (DID_REQUEUE << 16)) { 180962306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 181062306a36Sopenharmony_ci } else { 181162306a36Sopenharmony_ci scmd->result = DID_NO_CONNECT << 16; 181262306a36Sopenharmony_ci scsi_done(scmd); 181362306a36Sopenharmony_ci return 0; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci } 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci mr_device_priv_data = scmd->device->hostdata; 181862306a36Sopenharmony_ci if (!mr_device_priv_data || 181962306a36Sopenharmony_ci (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR)) { 182062306a36Sopenharmony_ci scmd->result = DID_NO_CONNECT << 16; 182162306a36Sopenharmony_ci scsi_done(scmd); 182262306a36Sopenharmony_ci return 0; 182362306a36Sopenharmony_ci } 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (MEGASAS_IS_LOGICAL(scmd->device)) { 182662306a36Sopenharmony_ci ld_tgt_id = MEGASAS_TARGET_ID(scmd->device); 182762306a36Sopenharmony_ci if (instance->ld_tgtid_status[ld_tgt_id] == LD_TARGET_ID_DELETED) { 182862306a36Sopenharmony_ci scmd->result = DID_NO_CONNECT << 16; 182962306a36Sopenharmony_ci scsi_done(scmd); 183062306a36Sopenharmony_ci return 0; 183162306a36Sopenharmony_ci } 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) 183562306a36Sopenharmony_ci return SCSI_MLQUEUE_HOST_BUSY; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci if (mr_device_priv_data->tm_busy) 183862306a36Sopenharmony_ci return SCSI_MLQUEUE_DEVICE_BUSY; 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci scmd->result = 0; 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci if (MEGASAS_IS_LOGICAL(scmd->device) && 184462306a36Sopenharmony_ci (scmd->device->id >= instance->fw_supported_vd_count || 184562306a36Sopenharmony_ci scmd->device->lun)) { 184662306a36Sopenharmony_ci scmd->result = DID_BAD_TARGET << 16; 184762306a36Sopenharmony_ci goto out_done; 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci if ((scmd->cmnd[0] == SYNCHRONIZE_CACHE) && 185162306a36Sopenharmony_ci MEGASAS_IS_LOGICAL(scmd->device) && 185262306a36Sopenharmony_ci (!instance->fw_sync_cache_support)) { 185362306a36Sopenharmony_ci scmd->result = DID_OK << 16; 185462306a36Sopenharmony_ci goto out_done; 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci return instance->instancet->build_and_issue_cmd(instance, scmd); 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci out_done: 186062306a36Sopenharmony_ci scsi_done(scmd); 186162306a36Sopenharmony_ci return 0; 186262306a36Sopenharmony_ci} 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_cistatic struct megasas_instance *megasas_lookup_instance(u16 host_no) 186562306a36Sopenharmony_ci{ 186662306a36Sopenharmony_ci int i; 186762306a36Sopenharmony_ci 186862306a36Sopenharmony_ci for (i = 0; i < megasas_mgmt_info.max_index; i++) { 186962306a36Sopenharmony_ci 187062306a36Sopenharmony_ci if ((megasas_mgmt_info.instance[i]) && 187162306a36Sopenharmony_ci (megasas_mgmt_info.instance[i]->host->host_no == host_no)) 187262306a36Sopenharmony_ci return megasas_mgmt_info.instance[i]; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci return NULL; 187662306a36Sopenharmony_ci} 187762306a36Sopenharmony_ci 187862306a36Sopenharmony_ci/* 187962306a36Sopenharmony_ci* megasas_set_dynamic_target_properties - 188062306a36Sopenharmony_ci* Device property set by driver may not be static and it is required to be 188162306a36Sopenharmony_ci* updated after OCR 188262306a36Sopenharmony_ci* 188362306a36Sopenharmony_ci* set tm_capable. 188462306a36Sopenharmony_ci* set dma alignment (only for eedp protection enable vd). 188562306a36Sopenharmony_ci* 188662306a36Sopenharmony_ci* @sdev: OS provided scsi device 188762306a36Sopenharmony_ci* 188862306a36Sopenharmony_ci* Returns void 188962306a36Sopenharmony_ci*/ 189062306a36Sopenharmony_civoid megasas_set_dynamic_target_properties(struct scsi_device *sdev, 189162306a36Sopenharmony_ci bool is_target_prop) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci u16 pd_index = 0, ld; 189462306a36Sopenharmony_ci u32 device_id; 189562306a36Sopenharmony_ci struct megasas_instance *instance; 189662306a36Sopenharmony_ci struct fusion_context *fusion; 189762306a36Sopenharmony_ci struct MR_PRIV_DEVICE *mr_device_priv_data; 189862306a36Sopenharmony_ci struct MR_PD_CFG_SEQ_NUM_SYNC *pd_sync; 189962306a36Sopenharmony_ci struct MR_LD_RAID *raid; 190062306a36Sopenharmony_ci struct MR_DRV_RAID_MAP_ALL *local_map_ptr; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci instance = megasas_lookup_instance(sdev->host->host_no); 190362306a36Sopenharmony_ci fusion = instance->ctrl_context; 190462306a36Sopenharmony_ci mr_device_priv_data = sdev->hostdata; 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_ci if (!fusion || !mr_device_priv_data) 190762306a36Sopenharmony_ci return; 190862306a36Sopenharmony_ci 190962306a36Sopenharmony_ci if (MEGASAS_IS_LOGICAL(sdev)) { 191062306a36Sopenharmony_ci device_id = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) 191162306a36Sopenharmony_ci + sdev->id; 191262306a36Sopenharmony_ci local_map_ptr = fusion->ld_drv_map[(instance->map_id & 1)]; 191362306a36Sopenharmony_ci ld = MR_TargetIdToLdGet(device_id, local_map_ptr); 191462306a36Sopenharmony_ci if (ld >= instance->fw_supported_vd_count) 191562306a36Sopenharmony_ci return; 191662306a36Sopenharmony_ci raid = MR_LdRaidGet(ld, local_map_ptr); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) 191962306a36Sopenharmony_ci blk_queue_update_dma_alignment(sdev->request_queue, 0x7); 192062306a36Sopenharmony_ci 192162306a36Sopenharmony_ci mr_device_priv_data->is_tm_capable = 192262306a36Sopenharmony_ci raid->capability.tmCapable; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci if (!raid->flags.isEPD) 192562306a36Sopenharmony_ci sdev->no_write_same = 1; 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci } else if (instance->use_seqnum_jbod_fp) { 192862306a36Sopenharmony_ci pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + 192962306a36Sopenharmony_ci sdev->id; 193062306a36Sopenharmony_ci pd_sync = (void *)fusion->pd_seq_sync 193162306a36Sopenharmony_ci [(instance->pd_seq_map_id - 1) & 1]; 193262306a36Sopenharmony_ci mr_device_priv_data->is_tm_capable = 193362306a36Sopenharmony_ci pd_sync->seq[pd_index].capability.tmCapable; 193462306a36Sopenharmony_ci } 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci if (is_target_prop && instance->tgt_prop->reset_tmo) { 193762306a36Sopenharmony_ci /* 193862306a36Sopenharmony_ci * If FW provides a target reset timeout value, driver will use 193962306a36Sopenharmony_ci * it. If not set, fallback to default values. 194062306a36Sopenharmony_ci */ 194162306a36Sopenharmony_ci mr_device_priv_data->target_reset_tmo = 194262306a36Sopenharmony_ci min_t(u8, instance->max_reset_tmo, 194362306a36Sopenharmony_ci instance->tgt_prop->reset_tmo); 194462306a36Sopenharmony_ci mr_device_priv_data->task_abort_tmo = instance->task_abort_tmo; 194562306a36Sopenharmony_ci } else { 194662306a36Sopenharmony_ci mr_device_priv_data->target_reset_tmo = 194762306a36Sopenharmony_ci MEGASAS_DEFAULT_TM_TIMEOUT; 194862306a36Sopenharmony_ci mr_device_priv_data->task_abort_tmo = 194962306a36Sopenharmony_ci MEGASAS_DEFAULT_TM_TIMEOUT; 195062306a36Sopenharmony_ci } 195162306a36Sopenharmony_ci} 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci/* 195462306a36Sopenharmony_ci * megasas_set_nvme_device_properties - 195562306a36Sopenharmony_ci * set nomerges=2 195662306a36Sopenharmony_ci * set virtual page boundary = 4K (current mr_nvme_pg_size is 4K). 195762306a36Sopenharmony_ci * set maximum io transfer = MDTS of NVME device provided by MR firmware. 195862306a36Sopenharmony_ci * 195962306a36Sopenharmony_ci * MR firmware provides value in KB. Caller of this function converts 196062306a36Sopenharmony_ci * kb into bytes. 196162306a36Sopenharmony_ci * 196262306a36Sopenharmony_ci * e.a MDTS=5 means 2^5 * nvme page size. (In case of 4K page size, 196362306a36Sopenharmony_ci * MR firmware provides value 128 as (32 * 4K) = 128K. 196462306a36Sopenharmony_ci * 196562306a36Sopenharmony_ci * @sdev: scsi device 196662306a36Sopenharmony_ci * @max_io_size: maximum io transfer size 196762306a36Sopenharmony_ci * 196862306a36Sopenharmony_ci */ 196962306a36Sopenharmony_cistatic inline void 197062306a36Sopenharmony_cimegasas_set_nvme_device_properties(struct scsi_device *sdev, u32 max_io_size) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci struct megasas_instance *instance; 197362306a36Sopenharmony_ci u32 mr_nvme_pg_size; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci instance = (struct megasas_instance *)sdev->host->hostdata; 197662306a36Sopenharmony_ci mr_nvme_pg_size = max_t(u32, instance->nvme_page_size, 197762306a36Sopenharmony_ci MR_DEFAULT_NVME_PAGE_SIZE); 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci blk_queue_max_hw_sectors(sdev->request_queue, (max_io_size / 512)); 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NOMERGES, sdev->request_queue); 198262306a36Sopenharmony_ci blk_queue_virt_boundary(sdev->request_queue, mr_nvme_pg_size - 1); 198362306a36Sopenharmony_ci} 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci/* 198662306a36Sopenharmony_ci * megasas_set_fw_assisted_qd - 198762306a36Sopenharmony_ci * set device queue depth to can_queue 198862306a36Sopenharmony_ci * set device queue depth to fw assisted qd 198962306a36Sopenharmony_ci * 199062306a36Sopenharmony_ci * @sdev: scsi device 199162306a36Sopenharmony_ci * @is_target_prop true, if fw provided target properties. 199262306a36Sopenharmony_ci */ 199362306a36Sopenharmony_cistatic void megasas_set_fw_assisted_qd(struct scsi_device *sdev, 199462306a36Sopenharmony_ci bool is_target_prop) 199562306a36Sopenharmony_ci{ 199662306a36Sopenharmony_ci u8 interface_type; 199762306a36Sopenharmony_ci u32 device_qd = MEGASAS_DEFAULT_CMD_PER_LUN; 199862306a36Sopenharmony_ci u32 tgt_device_qd; 199962306a36Sopenharmony_ci struct megasas_instance *instance; 200062306a36Sopenharmony_ci struct MR_PRIV_DEVICE *mr_device_priv_data; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci instance = megasas_lookup_instance(sdev->host->host_no); 200362306a36Sopenharmony_ci mr_device_priv_data = sdev->hostdata; 200462306a36Sopenharmony_ci interface_type = mr_device_priv_data->interface_type; 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci switch (interface_type) { 200762306a36Sopenharmony_ci case SAS_PD: 200862306a36Sopenharmony_ci device_qd = MEGASAS_SAS_QD; 200962306a36Sopenharmony_ci break; 201062306a36Sopenharmony_ci case SATA_PD: 201162306a36Sopenharmony_ci device_qd = MEGASAS_SATA_QD; 201262306a36Sopenharmony_ci break; 201362306a36Sopenharmony_ci case NVME_PD: 201462306a36Sopenharmony_ci device_qd = MEGASAS_NVME_QD; 201562306a36Sopenharmony_ci break; 201662306a36Sopenharmony_ci } 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci if (is_target_prop) { 201962306a36Sopenharmony_ci tgt_device_qd = le32_to_cpu(instance->tgt_prop->device_qdepth); 202062306a36Sopenharmony_ci if (tgt_device_qd) 202162306a36Sopenharmony_ci device_qd = min(instance->host->can_queue, 202262306a36Sopenharmony_ci (int)tgt_device_qd); 202362306a36Sopenharmony_ci } 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci if (instance->enable_sdev_max_qd && interface_type != UNKNOWN_DRIVE) 202662306a36Sopenharmony_ci device_qd = instance->host->can_queue; 202762306a36Sopenharmony_ci 202862306a36Sopenharmony_ci scsi_change_queue_depth(sdev, device_qd); 202962306a36Sopenharmony_ci} 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci/* 203262306a36Sopenharmony_ci * megasas_set_static_target_properties - 203362306a36Sopenharmony_ci * Device property set by driver are static and it is not required to be 203462306a36Sopenharmony_ci * updated after OCR. 203562306a36Sopenharmony_ci * 203662306a36Sopenharmony_ci * set io timeout 203762306a36Sopenharmony_ci * set device queue depth 203862306a36Sopenharmony_ci * set nvme device properties. see - megasas_set_nvme_device_properties 203962306a36Sopenharmony_ci * 204062306a36Sopenharmony_ci * @sdev: scsi device 204162306a36Sopenharmony_ci * @is_target_prop true, if fw provided target properties. 204262306a36Sopenharmony_ci */ 204362306a36Sopenharmony_cistatic void megasas_set_static_target_properties(struct scsi_device *sdev, 204462306a36Sopenharmony_ci bool is_target_prop) 204562306a36Sopenharmony_ci{ 204662306a36Sopenharmony_ci u32 max_io_size_kb = MR_DEFAULT_NVME_MDTS_KB; 204762306a36Sopenharmony_ci struct megasas_instance *instance; 204862306a36Sopenharmony_ci 204962306a36Sopenharmony_ci instance = megasas_lookup_instance(sdev->host->host_no); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci /* 205262306a36Sopenharmony_ci * The RAID firmware may require extended timeouts. 205362306a36Sopenharmony_ci */ 205462306a36Sopenharmony_ci blk_queue_rq_timeout(sdev->request_queue, scmd_timeout * HZ); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci /* max_io_size_kb will be set to non zero for 205762306a36Sopenharmony_ci * nvme based vd and syspd. 205862306a36Sopenharmony_ci */ 205962306a36Sopenharmony_ci if (is_target_prop) 206062306a36Sopenharmony_ci max_io_size_kb = le32_to_cpu(instance->tgt_prop->max_io_size_kb); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci if (instance->nvme_page_size && max_io_size_kb) 206362306a36Sopenharmony_ci megasas_set_nvme_device_properties(sdev, (max_io_size_kb << 10)); 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_ci megasas_set_fw_assisted_qd(sdev, is_target_prop); 206662306a36Sopenharmony_ci} 206762306a36Sopenharmony_ci 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic int megasas_slave_configure(struct scsi_device *sdev) 207062306a36Sopenharmony_ci{ 207162306a36Sopenharmony_ci u16 pd_index = 0; 207262306a36Sopenharmony_ci struct megasas_instance *instance; 207362306a36Sopenharmony_ci int ret_target_prop = DCMD_FAILED; 207462306a36Sopenharmony_ci bool is_target_prop = false; 207562306a36Sopenharmony_ci 207662306a36Sopenharmony_ci instance = megasas_lookup_instance(sdev->host->host_no); 207762306a36Sopenharmony_ci if (instance->pd_list_not_supported) { 207862306a36Sopenharmony_ci if (!MEGASAS_IS_LOGICAL(sdev) && sdev->type == TYPE_DISK) { 207962306a36Sopenharmony_ci pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + 208062306a36Sopenharmony_ci sdev->id; 208162306a36Sopenharmony_ci if (instance->pd_list[pd_index].driveState != 208262306a36Sopenharmony_ci MR_PD_STATE_SYSTEM) 208362306a36Sopenharmony_ci return -ENXIO; 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 208862306a36Sopenharmony_ci /* Send DCMD to Firmware and cache the information */ 208962306a36Sopenharmony_ci if ((instance->pd_info) && !MEGASAS_IS_LOGICAL(sdev)) 209062306a36Sopenharmony_ci megasas_get_pd_info(instance, sdev); 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci /* Some ventura firmware may not have instance->nvme_page_size set. 209362306a36Sopenharmony_ci * Do not send MR_DCMD_DRV_GET_TARGET_PROP 209462306a36Sopenharmony_ci */ 209562306a36Sopenharmony_ci if ((instance->tgt_prop) && (instance->nvme_page_size)) 209662306a36Sopenharmony_ci ret_target_prop = megasas_get_target_prop(instance, sdev); 209762306a36Sopenharmony_ci 209862306a36Sopenharmony_ci is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false; 209962306a36Sopenharmony_ci megasas_set_static_target_properties(sdev, is_target_prop); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci /* This sdev property may change post OCR */ 210262306a36Sopenharmony_ci megasas_set_dynamic_target_properties(sdev, is_target_prop); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci return 0; 210762306a36Sopenharmony_ci} 210862306a36Sopenharmony_ci 210962306a36Sopenharmony_cistatic int megasas_slave_alloc(struct scsi_device *sdev) 211062306a36Sopenharmony_ci{ 211162306a36Sopenharmony_ci u16 pd_index = 0, ld_tgt_id; 211262306a36Sopenharmony_ci struct megasas_instance *instance ; 211362306a36Sopenharmony_ci struct MR_PRIV_DEVICE *mr_device_priv_data; 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci instance = megasas_lookup_instance(sdev->host->host_no); 211662306a36Sopenharmony_ci if (!MEGASAS_IS_LOGICAL(sdev)) { 211762306a36Sopenharmony_ci /* 211862306a36Sopenharmony_ci * Open the OS scan to the SYSTEM PD 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_ci pd_index = 212162306a36Sopenharmony_ci (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + 212262306a36Sopenharmony_ci sdev->id; 212362306a36Sopenharmony_ci if ((instance->pd_list_not_supported || 212462306a36Sopenharmony_ci instance->pd_list[pd_index].driveState == 212562306a36Sopenharmony_ci MR_PD_STATE_SYSTEM)) { 212662306a36Sopenharmony_ci goto scan_target; 212762306a36Sopenharmony_ci } 212862306a36Sopenharmony_ci return -ENXIO; 212962306a36Sopenharmony_ci } else if (!MEGASAS_IS_LUN_VALID(sdev)) { 213062306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__); 213162306a36Sopenharmony_ci return -ENXIO; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ciscan_target: 213562306a36Sopenharmony_ci mr_device_priv_data = kzalloc(sizeof(*mr_device_priv_data), 213662306a36Sopenharmony_ci GFP_KERNEL); 213762306a36Sopenharmony_ci if (!mr_device_priv_data) 213862306a36Sopenharmony_ci return -ENOMEM; 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci if (MEGASAS_IS_LOGICAL(sdev)) { 214162306a36Sopenharmony_ci ld_tgt_id = MEGASAS_TARGET_ID(sdev); 214262306a36Sopenharmony_ci instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_ACTIVE; 214362306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 214462306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "LD target ID %d created.\n", ld_tgt_id); 214562306a36Sopenharmony_ci } 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci sdev->hostdata = mr_device_priv_data; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci atomic_set(&mr_device_priv_data->r1_ldio_hint, 215062306a36Sopenharmony_ci instance->r1_ldio_hint_default); 215162306a36Sopenharmony_ci return 0; 215262306a36Sopenharmony_ci} 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_cistatic void megasas_slave_destroy(struct scsi_device *sdev) 215562306a36Sopenharmony_ci{ 215662306a36Sopenharmony_ci u16 ld_tgt_id; 215762306a36Sopenharmony_ci struct megasas_instance *instance; 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_ci instance = megasas_lookup_instance(sdev->host->host_no); 216062306a36Sopenharmony_ci 216162306a36Sopenharmony_ci if (MEGASAS_IS_LOGICAL(sdev)) { 216262306a36Sopenharmony_ci if (!MEGASAS_IS_LUN_VALID(sdev)) { 216362306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "%s: invalid LUN\n", __func__); 216462306a36Sopenharmony_ci return; 216562306a36Sopenharmony_ci } 216662306a36Sopenharmony_ci ld_tgt_id = MEGASAS_TARGET_ID(sdev); 216762306a36Sopenharmony_ci instance->ld_tgtid_status[ld_tgt_id] = LD_TARGET_ID_DELETED; 216862306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 216962306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, 217062306a36Sopenharmony_ci "LD target ID %d removed from OS stack\n", ld_tgt_id); 217162306a36Sopenharmony_ci } 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci kfree(sdev->hostdata); 217462306a36Sopenharmony_ci sdev->hostdata = NULL; 217562306a36Sopenharmony_ci} 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci/* 217862306a36Sopenharmony_ci* megasas_complete_outstanding_ioctls - Complete outstanding ioctls after a 217962306a36Sopenharmony_ci* kill adapter 218062306a36Sopenharmony_ci* @instance: Adapter soft state 218162306a36Sopenharmony_ci* 218262306a36Sopenharmony_ci*/ 218362306a36Sopenharmony_cistatic void megasas_complete_outstanding_ioctls(struct megasas_instance *instance) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci int i; 218662306a36Sopenharmony_ci struct megasas_cmd *cmd_mfi; 218762306a36Sopenharmony_ci struct megasas_cmd_fusion *cmd_fusion; 218862306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci /* Find all outstanding ioctls */ 219162306a36Sopenharmony_ci if (fusion) { 219262306a36Sopenharmony_ci for (i = 0; i < instance->max_fw_cmds; i++) { 219362306a36Sopenharmony_ci cmd_fusion = fusion->cmd_list[i]; 219462306a36Sopenharmony_ci if (cmd_fusion->sync_cmd_idx != (u32)ULONG_MAX) { 219562306a36Sopenharmony_ci cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; 219662306a36Sopenharmony_ci if (cmd_mfi->sync_cmd && 219762306a36Sopenharmony_ci (cmd_mfi->frame->hdr.cmd != MFI_CMD_ABORT)) { 219862306a36Sopenharmony_ci cmd_mfi->frame->hdr.cmd_status = 219962306a36Sopenharmony_ci MFI_STAT_WRONG_STATE; 220062306a36Sopenharmony_ci megasas_complete_cmd(instance, 220162306a36Sopenharmony_ci cmd_mfi, DID_OK); 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci } 220462306a36Sopenharmony_ci } 220562306a36Sopenharmony_ci } else { 220662306a36Sopenharmony_ci for (i = 0; i < instance->max_fw_cmds; i++) { 220762306a36Sopenharmony_ci cmd_mfi = instance->cmd_list[i]; 220862306a36Sopenharmony_ci if (cmd_mfi->sync_cmd && cmd_mfi->frame->hdr.cmd != 220962306a36Sopenharmony_ci MFI_CMD_ABORT) 221062306a36Sopenharmony_ci megasas_complete_cmd(instance, cmd_mfi, DID_OK); 221162306a36Sopenharmony_ci } 221262306a36Sopenharmony_ci } 221362306a36Sopenharmony_ci} 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_civoid megaraid_sas_kill_hba(struct megasas_instance *instance) 221762306a36Sopenharmony_ci{ 221862306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 221962306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, 222062306a36Sopenharmony_ci "Adapter already dead, skipping kill HBA\n"); 222162306a36Sopenharmony_ci return; 222262306a36Sopenharmony_ci } 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci /* Set critical error to block I/O & ioctls in case caller didn't */ 222562306a36Sopenharmony_ci atomic_set(&instance->adprecovery, MEGASAS_HW_CRITICAL_ERROR); 222662306a36Sopenharmony_ci /* Wait 1 second to ensure IO or ioctls in build have posted */ 222762306a36Sopenharmony_ci msleep(1000); 222862306a36Sopenharmony_ci if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || 222962306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || 223062306a36Sopenharmony_ci (instance->adapter_type != MFI_SERIES)) { 223162306a36Sopenharmony_ci if (!instance->requestorId) { 223262306a36Sopenharmony_ci writel(MFI_STOP_ADP, &instance->reg_set->doorbell); 223362306a36Sopenharmony_ci /* Flush */ 223462306a36Sopenharmony_ci readl(&instance->reg_set->doorbell); 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci if (instance->requestorId && instance->peerIsPresent) 223762306a36Sopenharmony_ci memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); 223862306a36Sopenharmony_ci } else { 223962306a36Sopenharmony_ci writel(MFI_STOP_ADP, 224062306a36Sopenharmony_ci &instance->reg_set->inbound_doorbell); 224162306a36Sopenharmony_ci } 224262306a36Sopenharmony_ci /* Complete outstanding ioctls when adapter is killed */ 224362306a36Sopenharmony_ci megasas_complete_outstanding_ioctls(instance); 224462306a36Sopenharmony_ci} 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci /** 224762306a36Sopenharmony_ci * megasas_check_and_restore_queue_depth - Check if queue depth needs to be 224862306a36Sopenharmony_ci * restored to max value 224962306a36Sopenharmony_ci * @instance: Adapter soft state 225062306a36Sopenharmony_ci * 225162306a36Sopenharmony_ci */ 225262306a36Sopenharmony_civoid 225362306a36Sopenharmony_cimegasas_check_and_restore_queue_depth(struct megasas_instance *instance) 225462306a36Sopenharmony_ci{ 225562306a36Sopenharmony_ci unsigned long flags; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci if (instance->flag & MEGASAS_FW_BUSY 225862306a36Sopenharmony_ci && time_after(jiffies, instance->last_time + 5 * HZ) 225962306a36Sopenharmony_ci && atomic_read(&instance->fw_outstanding) < 226062306a36Sopenharmony_ci instance->throttlequeuedepth + 1) { 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci spin_lock_irqsave(instance->host->host_lock, flags); 226362306a36Sopenharmony_ci instance->flag &= ~MEGASAS_FW_BUSY; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci instance->host->can_queue = instance->cur_can_queue; 226662306a36Sopenharmony_ci spin_unlock_irqrestore(instance->host->host_lock, flags); 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci} 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci/** 227162306a36Sopenharmony_ci * megasas_complete_cmd_dpc - Returns FW's controller structure 227262306a36Sopenharmony_ci * @instance_addr: Address of adapter soft state 227362306a36Sopenharmony_ci * 227462306a36Sopenharmony_ci * Tasklet to complete cmds 227562306a36Sopenharmony_ci */ 227662306a36Sopenharmony_cistatic void megasas_complete_cmd_dpc(unsigned long instance_addr) 227762306a36Sopenharmony_ci{ 227862306a36Sopenharmony_ci u32 producer; 227962306a36Sopenharmony_ci u32 consumer; 228062306a36Sopenharmony_ci u32 context; 228162306a36Sopenharmony_ci struct megasas_cmd *cmd; 228262306a36Sopenharmony_ci struct megasas_instance *instance = 228362306a36Sopenharmony_ci (struct megasas_instance *)instance_addr; 228462306a36Sopenharmony_ci unsigned long flags; 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci /* If we have already declared adapter dead, donot complete cmds */ 228762306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) 228862306a36Sopenharmony_ci return; 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci spin_lock_irqsave(&instance->completion_lock, flags); 229162306a36Sopenharmony_ci 229262306a36Sopenharmony_ci producer = le32_to_cpu(*instance->producer); 229362306a36Sopenharmony_ci consumer = le32_to_cpu(*instance->consumer); 229462306a36Sopenharmony_ci 229562306a36Sopenharmony_ci while (consumer != producer) { 229662306a36Sopenharmony_ci context = le32_to_cpu(instance->reply_queue[consumer]); 229762306a36Sopenharmony_ci if (context >= instance->max_fw_cmds) { 229862306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Unexpected context value %x\n", 229962306a36Sopenharmony_ci context); 230062306a36Sopenharmony_ci BUG(); 230162306a36Sopenharmony_ci } 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci cmd = instance->cmd_list[context]; 230462306a36Sopenharmony_ci 230562306a36Sopenharmony_ci megasas_complete_cmd(instance, cmd, DID_OK); 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci consumer++; 230862306a36Sopenharmony_ci if (consumer == (instance->max_fw_cmds + 1)) { 230962306a36Sopenharmony_ci consumer = 0; 231062306a36Sopenharmony_ci } 231162306a36Sopenharmony_ci } 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_ci *instance->consumer = cpu_to_le32(producer); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->completion_lock, flags); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci /* 231862306a36Sopenharmony_ci * Check if we can restore can_queue 231962306a36Sopenharmony_ci */ 232062306a36Sopenharmony_ci megasas_check_and_restore_queue_depth(instance); 232162306a36Sopenharmony_ci} 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_cistatic void megasas_sriov_heartbeat_handler(struct timer_list *t); 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci/** 232662306a36Sopenharmony_ci * megasas_start_timer - Initializes sriov heartbeat timer object 232762306a36Sopenharmony_ci * @instance: Adapter soft state 232862306a36Sopenharmony_ci * 232962306a36Sopenharmony_ci */ 233062306a36Sopenharmony_civoid megasas_start_timer(struct megasas_instance *instance) 233162306a36Sopenharmony_ci{ 233262306a36Sopenharmony_ci struct timer_list *timer = &instance->sriov_heartbeat_timer; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci timer_setup(timer, megasas_sriov_heartbeat_handler, 0); 233562306a36Sopenharmony_ci timer->expires = jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF; 233662306a36Sopenharmony_ci add_timer(timer); 233762306a36Sopenharmony_ci} 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_cistatic void 234062306a36Sopenharmony_cimegasas_internal_reset_defer_cmds(struct megasas_instance *instance); 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_cistatic void 234362306a36Sopenharmony_ciprocess_fw_state_change_wq(struct work_struct *work); 234462306a36Sopenharmony_ci 234562306a36Sopenharmony_cistatic void megasas_do_ocr(struct megasas_instance *instance) 234662306a36Sopenharmony_ci{ 234762306a36Sopenharmony_ci if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || 234862306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) || 234962306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR)) { 235062306a36Sopenharmony_ci *instance->consumer = cpu_to_le32(MEGASAS_ADPRESET_INPROG_SIGN); 235162306a36Sopenharmony_ci } 235262306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 235362306a36Sopenharmony_ci atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT); 235462306a36Sopenharmony_ci instance->issuepend_done = 0; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci atomic_set(&instance->fw_outstanding, 0); 235762306a36Sopenharmony_ci megasas_internal_reset_defer_cmds(instance); 235862306a36Sopenharmony_ci process_fw_state_change_wq(&instance->work_init); 235962306a36Sopenharmony_ci} 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_cistatic int megasas_get_ld_vf_affiliation_111(struct megasas_instance *instance, 236262306a36Sopenharmony_ci int initial) 236362306a36Sopenharmony_ci{ 236462306a36Sopenharmony_ci struct megasas_cmd *cmd; 236562306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 236662306a36Sopenharmony_ci struct MR_LD_VF_AFFILIATION_111 *new_affiliation_111 = NULL; 236762306a36Sopenharmony_ci dma_addr_t new_affiliation_111_h; 236862306a36Sopenharmony_ci int ld, retval = 0; 236962306a36Sopenharmony_ci u8 thisVf; 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci if (!cmd) { 237462306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation_111:" 237562306a36Sopenharmony_ci "Failed to get cmd for scsi%d\n", 237662306a36Sopenharmony_ci instance->host->host_no); 237762306a36Sopenharmony_ci return -ENOMEM; 237862306a36Sopenharmony_ci } 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 238162306a36Sopenharmony_ci 238262306a36Sopenharmony_ci if (!instance->vf_affiliation_111) { 238362306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF " 238462306a36Sopenharmony_ci "affiliation for scsi%d\n", instance->host->host_no); 238562306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 238662306a36Sopenharmony_ci return -ENOMEM; 238762306a36Sopenharmony_ci } 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci if (initial) 239062306a36Sopenharmony_ci memset(instance->vf_affiliation_111, 0, 239162306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111)); 239262306a36Sopenharmony_ci else { 239362306a36Sopenharmony_ci new_affiliation_111 = 239462306a36Sopenharmony_ci dma_alloc_coherent(&instance->pdev->dev, 239562306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111), 239662306a36Sopenharmony_ci &new_affiliation_111_h, GFP_KERNEL); 239762306a36Sopenharmony_ci if (!new_affiliation_111) { 239862306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " 239962306a36Sopenharmony_ci "memory for new affiliation for scsi%d\n", 240062306a36Sopenharmony_ci instance->host->host_no); 240162306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 240262306a36Sopenharmony_ci return -ENOMEM; 240362306a36Sopenharmony_ci } 240462306a36Sopenharmony_ci } 240562306a36Sopenharmony_ci 240662306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 240962306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 241062306a36Sopenharmony_ci dcmd->sge_count = 1; 241162306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH); 241262306a36Sopenharmony_ci dcmd->timeout = 0; 241362306a36Sopenharmony_ci dcmd->pad_0 = 0; 241462306a36Sopenharmony_ci dcmd->data_xfer_len = 241562306a36Sopenharmony_ci cpu_to_le32(sizeof(struct MR_LD_VF_AFFILIATION_111)); 241662306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS_111); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci if (initial) 241962306a36Sopenharmony_ci dcmd->sgl.sge32[0].phys_addr = 242062306a36Sopenharmony_ci cpu_to_le32(instance->vf_affiliation_111_h); 242162306a36Sopenharmony_ci else 242262306a36Sopenharmony_ci dcmd->sgl.sge32[0].phys_addr = 242362306a36Sopenharmony_ci cpu_to_le32(new_affiliation_111_h); 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci dcmd->sgl.sge32[0].length = cpu_to_le32( 242662306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111)); 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for " 242962306a36Sopenharmony_ci "scsi%d\n", instance->host->host_no); 243062306a36Sopenharmony_ci 243162306a36Sopenharmony_ci if (megasas_issue_blocked_cmd(instance, cmd, 0) != DCMD_SUCCESS) { 243262306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD" 243362306a36Sopenharmony_ci " failed with status 0x%x for scsi%d\n", 243462306a36Sopenharmony_ci dcmd->cmd_status, instance->host->host_no); 243562306a36Sopenharmony_ci retval = 1; /* Do a scan if we couldn't get affiliation */ 243662306a36Sopenharmony_ci goto out; 243762306a36Sopenharmony_ci } 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci if (!initial) { 244062306a36Sopenharmony_ci thisVf = new_affiliation_111->thisVf; 244162306a36Sopenharmony_ci for (ld = 0 ; ld < new_affiliation_111->vdCount; ld++) 244262306a36Sopenharmony_ci if (instance->vf_affiliation_111->map[ld].policy[thisVf] != 244362306a36Sopenharmony_ci new_affiliation_111->map[ld].policy[thisVf]) { 244462306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: " 244562306a36Sopenharmony_ci "Got new LD/VF affiliation for scsi%d\n", 244662306a36Sopenharmony_ci instance->host->host_no); 244762306a36Sopenharmony_ci memcpy(instance->vf_affiliation_111, 244862306a36Sopenharmony_ci new_affiliation_111, 244962306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111)); 245062306a36Sopenharmony_ci retval = 1; 245162306a36Sopenharmony_ci goto out; 245262306a36Sopenharmony_ci } 245362306a36Sopenharmony_ci } 245462306a36Sopenharmony_ciout: 245562306a36Sopenharmony_ci if (new_affiliation_111) { 245662306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 245762306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111), 245862306a36Sopenharmony_ci new_affiliation_111, 245962306a36Sopenharmony_ci new_affiliation_111_h); 246062306a36Sopenharmony_ci } 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci return retval; 246562306a36Sopenharmony_ci} 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_cistatic int megasas_get_ld_vf_affiliation_12(struct megasas_instance *instance, 246862306a36Sopenharmony_ci int initial) 246962306a36Sopenharmony_ci{ 247062306a36Sopenharmony_ci struct megasas_cmd *cmd; 247162306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 247262306a36Sopenharmony_ci struct MR_LD_VF_AFFILIATION *new_affiliation = NULL; 247362306a36Sopenharmony_ci struct MR_LD_VF_MAP *newmap = NULL, *savedmap = NULL; 247462306a36Sopenharmony_ci dma_addr_t new_affiliation_h; 247562306a36Sopenharmony_ci int i, j, retval = 0, found = 0, doscan = 0; 247662306a36Sopenharmony_ci u8 thisVf; 247762306a36Sopenharmony_ci 247862306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_ci if (!cmd) { 248162306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_vf_affiliation12: " 248262306a36Sopenharmony_ci "Failed to get cmd for scsi%d\n", 248362306a36Sopenharmony_ci instance->host->host_no); 248462306a36Sopenharmony_ci return -ENOMEM; 248562306a36Sopenharmony_ci } 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 248862306a36Sopenharmony_ci 248962306a36Sopenharmony_ci if (!instance->vf_affiliation) { 249062306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Couldn't get LD/VF " 249162306a36Sopenharmony_ci "affiliation for scsi%d\n", instance->host->host_no); 249262306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 249362306a36Sopenharmony_ci return -ENOMEM; 249462306a36Sopenharmony_ci } 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci if (initial) 249762306a36Sopenharmony_ci memset(instance->vf_affiliation, 0, (MAX_LOGICAL_DRIVES + 1) * 249862306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION)); 249962306a36Sopenharmony_ci else { 250062306a36Sopenharmony_ci new_affiliation = 250162306a36Sopenharmony_ci dma_alloc_coherent(&instance->pdev->dev, 250262306a36Sopenharmony_ci (MAX_LOGICAL_DRIVES + 1) * sizeof(struct MR_LD_VF_AFFILIATION), 250362306a36Sopenharmony_ci &new_affiliation_h, GFP_KERNEL); 250462306a36Sopenharmony_ci if (!new_affiliation) { 250562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate " 250662306a36Sopenharmony_ci "memory for new affiliation for scsi%d\n", 250762306a36Sopenharmony_ci instance->host->host_no); 250862306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 250962306a36Sopenharmony_ci return -ENOMEM; 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci } 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 251662306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 251762306a36Sopenharmony_ci dcmd->sge_count = 1; 251862306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH); 251962306a36Sopenharmony_ci dcmd->timeout = 0; 252062306a36Sopenharmony_ci dcmd->pad_0 = 0; 252162306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) * 252262306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION)); 252362306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_LD_VF_MAP_GET_ALL_LDS); 252462306a36Sopenharmony_ci 252562306a36Sopenharmony_ci if (initial) 252662306a36Sopenharmony_ci dcmd->sgl.sge32[0].phys_addr = 252762306a36Sopenharmony_ci cpu_to_le32(instance->vf_affiliation_h); 252862306a36Sopenharmony_ci else 252962306a36Sopenharmony_ci dcmd->sgl.sge32[0].phys_addr = 253062306a36Sopenharmony_ci cpu_to_le32(new_affiliation_h); 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_ci dcmd->sgl.sge32[0].length = cpu_to_le32((MAX_LOGICAL_DRIVES + 1) * 253362306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION)); 253462306a36Sopenharmony_ci 253562306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Getting LD/VF affiliation for " 253662306a36Sopenharmony_ci "scsi%d\n", instance->host->host_no); 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci if (megasas_issue_blocked_cmd(instance, cmd, 0) != DCMD_SUCCESS) { 254062306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: LD/VF affiliation DCMD" 254162306a36Sopenharmony_ci " failed with status 0x%x for scsi%d\n", 254262306a36Sopenharmony_ci dcmd->cmd_status, instance->host->host_no); 254362306a36Sopenharmony_ci retval = 1; /* Do a scan if we couldn't get affiliation */ 254462306a36Sopenharmony_ci goto out; 254562306a36Sopenharmony_ci } 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci if (!initial) { 254862306a36Sopenharmony_ci if (!new_affiliation->ldCount) { 254962306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF " 255062306a36Sopenharmony_ci "affiliation for passive path for scsi%d\n", 255162306a36Sopenharmony_ci instance->host->host_no); 255262306a36Sopenharmony_ci retval = 1; 255362306a36Sopenharmony_ci goto out; 255462306a36Sopenharmony_ci } 255562306a36Sopenharmony_ci newmap = new_affiliation->map; 255662306a36Sopenharmony_ci savedmap = instance->vf_affiliation->map; 255762306a36Sopenharmony_ci thisVf = new_affiliation->thisVf; 255862306a36Sopenharmony_ci for (i = 0 ; i < new_affiliation->ldCount; i++) { 255962306a36Sopenharmony_ci found = 0; 256062306a36Sopenharmony_ci for (j = 0; j < instance->vf_affiliation->ldCount; 256162306a36Sopenharmony_ci j++) { 256262306a36Sopenharmony_ci if (newmap->ref.targetId == 256362306a36Sopenharmony_ci savedmap->ref.targetId) { 256462306a36Sopenharmony_ci found = 1; 256562306a36Sopenharmony_ci if (newmap->policy[thisVf] != 256662306a36Sopenharmony_ci savedmap->policy[thisVf]) { 256762306a36Sopenharmony_ci doscan = 1; 256862306a36Sopenharmony_ci goto out; 256962306a36Sopenharmony_ci } 257062306a36Sopenharmony_ci } 257162306a36Sopenharmony_ci savedmap = (struct MR_LD_VF_MAP *) 257262306a36Sopenharmony_ci ((unsigned char *)savedmap + 257362306a36Sopenharmony_ci savedmap->size); 257462306a36Sopenharmony_ci } 257562306a36Sopenharmony_ci if (!found && newmap->policy[thisVf] != 257662306a36Sopenharmony_ci MR_LD_ACCESS_HIDDEN) { 257762306a36Sopenharmony_ci doscan = 1; 257862306a36Sopenharmony_ci goto out; 257962306a36Sopenharmony_ci } 258062306a36Sopenharmony_ci newmap = (struct MR_LD_VF_MAP *) 258162306a36Sopenharmony_ci ((unsigned char *)newmap + newmap->size); 258262306a36Sopenharmony_ci } 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci newmap = new_affiliation->map; 258562306a36Sopenharmony_ci savedmap = instance->vf_affiliation->map; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci for (i = 0 ; i < instance->vf_affiliation->ldCount; i++) { 258862306a36Sopenharmony_ci found = 0; 258962306a36Sopenharmony_ci for (j = 0 ; j < new_affiliation->ldCount; j++) { 259062306a36Sopenharmony_ci if (savedmap->ref.targetId == 259162306a36Sopenharmony_ci newmap->ref.targetId) { 259262306a36Sopenharmony_ci found = 1; 259362306a36Sopenharmony_ci if (savedmap->policy[thisVf] != 259462306a36Sopenharmony_ci newmap->policy[thisVf]) { 259562306a36Sopenharmony_ci doscan = 1; 259662306a36Sopenharmony_ci goto out; 259762306a36Sopenharmony_ci } 259862306a36Sopenharmony_ci } 259962306a36Sopenharmony_ci newmap = (struct MR_LD_VF_MAP *) 260062306a36Sopenharmony_ci ((unsigned char *)newmap + 260162306a36Sopenharmony_ci newmap->size); 260262306a36Sopenharmony_ci } 260362306a36Sopenharmony_ci if (!found && savedmap->policy[thisVf] != 260462306a36Sopenharmony_ci MR_LD_ACCESS_HIDDEN) { 260562306a36Sopenharmony_ci doscan = 1; 260662306a36Sopenharmony_ci goto out; 260762306a36Sopenharmony_ci } 260862306a36Sopenharmony_ci savedmap = (struct MR_LD_VF_MAP *) 260962306a36Sopenharmony_ci ((unsigned char *)savedmap + 261062306a36Sopenharmony_ci savedmap->size); 261162306a36Sopenharmony_ci } 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ciout: 261462306a36Sopenharmony_ci if (doscan) { 261562306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Got new LD/VF " 261662306a36Sopenharmony_ci "affiliation for scsi%d\n", instance->host->host_no); 261762306a36Sopenharmony_ci memcpy(instance->vf_affiliation, new_affiliation, 261862306a36Sopenharmony_ci new_affiliation->size); 261962306a36Sopenharmony_ci retval = 1; 262062306a36Sopenharmony_ci } 262162306a36Sopenharmony_ci 262262306a36Sopenharmony_ci if (new_affiliation) 262362306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 262462306a36Sopenharmony_ci (MAX_LOGICAL_DRIVES + 1) * 262562306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION), 262662306a36Sopenharmony_ci new_affiliation, new_affiliation_h); 262762306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci return retval; 263062306a36Sopenharmony_ci} 263162306a36Sopenharmony_ci 263262306a36Sopenharmony_ci/* This function will get the current SR-IOV LD/VF affiliation */ 263362306a36Sopenharmony_cistatic int megasas_get_ld_vf_affiliation(struct megasas_instance *instance, 263462306a36Sopenharmony_ci int initial) 263562306a36Sopenharmony_ci{ 263662306a36Sopenharmony_ci int retval; 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_ci if (instance->PlasmaFW111) 263962306a36Sopenharmony_ci retval = megasas_get_ld_vf_affiliation_111(instance, initial); 264062306a36Sopenharmony_ci else 264162306a36Sopenharmony_ci retval = megasas_get_ld_vf_affiliation_12(instance, initial); 264262306a36Sopenharmony_ci return retval; 264362306a36Sopenharmony_ci} 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci/* This function will tell FW to start the SR-IOV heartbeat */ 264662306a36Sopenharmony_ciint megasas_sriov_start_heartbeat(struct megasas_instance *instance, 264762306a36Sopenharmony_ci int initial) 264862306a36Sopenharmony_ci{ 264962306a36Sopenharmony_ci struct megasas_cmd *cmd; 265062306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 265162306a36Sopenharmony_ci int retval = 0; 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci if (!cmd) { 265662306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_sriov_start_heartbeat: " 265762306a36Sopenharmony_ci "Failed to get cmd for scsi%d\n", 265862306a36Sopenharmony_ci instance->host->host_no); 265962306a36Sopenharmony_ci return -ENOMEM; 266062306a36Sopenharmony_ci } 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci if (initial) { 266562306a36Sopenharmony_ci instance->hb_host_mem = 266662306a36Sopenharmony_ci dma_alloc_coherent(&instance->pdev->dev, 266762306a36Sopenharmony_ci sizeof(struct MR_CTRL_HB_HOST_MEM), 266862306a36Sopenharmony_ci &instance->hb_host_mem_h, 266962306a36Sopenharmony_ci GFP_KERNEL); 267062306a36Sopenharmony_ci if (!instance->hb_host_mem) { 267162306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "SR-IOV: Couldn't allocate" 267262306a36Sopenharmony_ci " memory for heartbeat host memory for scsi%d\n", 267362306a36Sopenharmony_ci instance->host->host_no); 267462306a36Sopenharmony_ci retval = -ENOMEM; 267562306a36Sopenharmony_ci goto out; 267662306a36Sopenharmony_ci } 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_ci dcmd->mbox.s[0] = cpu_to_le16(sizeof(struct MR_CTRL_HB_HOST_MEM)); 268262306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 268362306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 268462306a36Sopenharmony_ci dcmd->sge_count = 1; 268562306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_BOTH); 268662306a36Sopenharmony_ci dcmd->timeout = 0; 268762306a36Sopenharmony_ci dcmd->pad_0 = 0; 268862306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_CTRL_HB_HOST_MEM)); 268962306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SHARED_HOST_MEM_ALLOC); 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, instance->hb_host_mem_h, 269262306a36Sopenharmony_ci sizeof(struct MR_CTRL_HB_HOST_MEM)); 269362306a36Sopenharmony_ci 269462306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Starting heartbeat for scsi%d\n", 269562306a36Sopenharmony_ci instance->host->host_no); 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 269862306a36Sopenharmony_ci !instance->mask_interrupts) 269962306a36Sopenharmony_ci retval = megasas_issue_blocked_cmd(instance, cmd, 270062306a36Sopenharmony_ci MEGASAS_ROUTINE_WAIT_TIME_VF); 270162306a36Sopenharmony_ci else 270262306a36Sopenharmony_ci retval = megasas_issue_polled(instance, cmd); 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci if (retval) { 270562306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: MR_DCMD_CTRL_SHARED_HOST" 270662306a36Sopenharmony_ci "_MEM_ALLOC DCMD %s for scsi%d\n", 270762306a36Sopenharmony_ci (dcmd->cmd_status == MFI_STAT_INVALID_STATUS) ? 270862306a36Sopenharmony_ci "timed out" : "failed", instance->host->host_no); 270962306a36Sopenharmony_ci retval = 1; 271062306a36Sopenharmony_ci } 271162306a36Sopenharmony_ci 271262306a36Sopenharmony_ciout: 271362306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_ci return retval; 271662306a36Sopenharmony_ci} 271762306a36Sopenharmony_ci 271862306a36Sopenharmony_ci/* Handler for SR-IOV heartbeat */ 271962306a36Sopenharmony_cistatic void megasas_sriov_heartbeat_handler(struct timer_list *t) 272062306a36Sopenharmony_ci{ 272162306a36Sopenharmony_ci struct megasas_instance *instance = 272262306a36Sopenharmony_ci from_timer(instance, t, sriov_heartbeat_timer); 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci if (instance->hb_host_mem->HB.fwCounter != 272562306a36Sopenharmony_ci instance->hb_host_mem->HB.driverCounter) { 272662306a36Sopenharmony_ci instance->hb_host_mem->HB.driverCounter = 272762306a36Sopenharmony_ci instance->hb_host_mem->HB.fwCounter; 272862306a36Sopenharmony_ci mod_timer(&instance->sriov_heartbeat_timer, 272962306a36Sopenharmony_ci jiffies + MEGASAS_SRIOV_HEARTBEAT_INTERVAL_VF); 273062306a36Sopenharmony_ci } else { 273162306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "SR-IOV: Heartbeat never " 273262306a36Sopenharmony_ci "completed for scsi%d\n", instance->host->host_no); 273362306a36Sopenharmony_ci schedule_work(&instance->work_init); 273462306a36Sopenharmony_ci } 273562306a36Sopenharmony_ci} 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci/** 273862306a36Sopenharmony_ci * megasas_wait_for_outstanding - Wait for all outstanding cmds 273962306a36Sopenharmony_ci * @instance: Adapter soft state 274062306a36Sopenharmony_ci * 274162306a36Sopenharmony_ci * This function waits for up to MEGASAS_RESET_WAIT_TIME seconds for FW to 274262306a36Sopenharmony_ci * complete all its outstanding commands. Returns error if one or more IOs 274362306a36Sopenharmony_ci * are pending after this time period. It also marks the controller dead. 274462306a36Sopenharmony_ci */ 274562306a36Sopenharmony_cistatic int megasas_wait_for_outstanding(struct megasas_instance *instance) 274662306a36Sopenharmony_ci{ 274762306a36Sopenharmony_ci int i, sl, outstanding; 274862306a36Sopenharmony_ci u32 reset_index; 274962306a36Sopenharmony_ci u32 wait_time = MEGASAS_RESET_WAIT_TIME; 275062306a36Sopenharmony_ci unsigned long flags; 275162306a36Sopenharmony_ci struct list_head clist_local; 275262306a36Sopenharmony_ci struct megasas_cmd *reset_cmd; 275362306a36Sopenharmony_ci u32 fw_state; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 275662306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s:%d HBA is killed.\n", 275762306a36Sopenharmony_ci __func__, __LINE__); 275862306a36Sopenharmony_ci return FAILED; 275962306a36Sopenharmony_ci } 276062306a36Sopenharmony_ci 276162306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) { 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci INIT_LIST_HEAD(&clist_local); 276462306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 276562306a36Sopenharmony_ci list_splice_init(&instance->internal_reset_pending_q, 276662306a36Sopenharmony_ci &clist_local); 276762306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "HBA reset wait ...\n"); 277062306a36Sopenharmony_ci for (i = 0; i < wait_time; i++) { 277162306a36Sopenharmony_ci msleep(1000); 277262306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HBA_OPERATIONAL) 277362306a36Sopenharmony_ci break; 277462306a36Sopenharmony_ci } 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_HBA_OPERATIONAL) { 277762306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "reset: Stopping HBA.\n"); 277862306a36Sopenharmony_ci atomic_set(&instance->adprecovery, MEGASAS_HW_CRITICAL_ERROR); 277962306a36Sopenharmony_ci return FAILED; 278062306a36Sopenharmony_ci } 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci reset_index = 0; 278362306a36Sopenharmony_ci while (!list_empty(&clist_local)) { 278462306a36Sopenharmony_ci reset_cmd = list_entry((&clist_local)->next, 278562306a36Sopenharmony_ci struct megasas_cmd, list); 278662306a36Sopenharmony_ci list_del_init(&reset_cmd->list); 278762306a36Sopenharmony_ci if (reset_cmd->scmd) { 278862306a36Sopenharmony_ci reset_cmd->scmd->result = DID_REQUEUE << 16; 278962306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "%d:%p reset [%02x]\n", 279062306a36Sopenharmony_ci reset_index, reset_cmd, 279162306a36Sopenharmony_ci reset_cmd->scmd->cmnd[0]); 279262306a36Sopenharmony_ci 279362306a36Sopenharmony_ci scsi_done(reset_cmd->scmd); 279462306a36Sopenharmony_ci megasas_return_cmd(instance, reset_cmd); 279562306a36Sopenharmony_ci } else if (reset_cmd->sync_cmd) { 279662306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "%p synch cmds" 279762306a36Sopenharmony_ci "reset queue\n", 279862306a36Sopenharmony_ci reset_cmd); 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci reset_cmd->cmd_status_drv = DCMD_INIT; 280162306a36Sopenharmony_ci instance->instancet->fire_cmd(instance, 280262306a36Sopenharmony_ci reset_cmd->frame_phys_addr, 280362306a36Sopenharmony_ci 0, instance->reg_set); 280462306a36Sopenharmony_ci } else { 280562306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "%p unexpected" 280662306a36Sopenharmony_ci "cmds lst\n", 280762306a36Sopenharmony_ci reset_cmd); 280862306a36Sopenharmony_ci } 280962306a36Sopenharmony_ci reset_index++; 281062306a36Sopenharmony_ci } 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci return SUCCESS; 281362306a36Sopenharmony_ci } 281462306a36Sopenharmony_ci 281562306a36Sopenharmony_ci for (i = 0; i < resetwaittime; i++) { 281662306a36Sopenharmony_ci outstanding = atomic_read(&instance->fw_outstanding); 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci if (!outstanding) 281962306a36Sopenharmony_ci break; 282062306a36Sopenharmony_ci 282162306a36Sopenharmony_ci if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { 282262306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "[%2d]waiting for %d " 282362306a36Sopenharmony_ci "commands to complete\n",i,outstanding); 282462306a36Sopenharmony_ci /* 282562306a36Sopenharmony_ci * Call cmd completion routine. Cmd to be 282662306a36Sopenharmony_ci * be completed directly without depending on isr. 282762306a36Sopenharmony_ci */ 282862306a36Sopenharmony_ci megasas_complete_cmd_dpc((unsigned long)instance); 282962306a36Sopenharmony_ci } 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci msleep(1000); 283262306a36Sopenharmony_ci } 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci i = 0; 283562306a36Sopenharmony_ci outstanding = atomic_read(&instance->fw_outstanding); 283662306a36Sopenharmony_ci fw_state = instance->instancet->read_fw_status_reg(instance) & MFI_STATE_MASK; 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci if ((!outstanding && (fw_state == MFI_STATE_OPERATIONAL))) 283962306a36Sopenharmony_ci goto no_outstanding; 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci if (instance->disableOnlineCtrlReset) 284262306a36Sopenharmony_ci goto kill_hba_and_failed; 284362306a36Sopenharmony_ci do { 284462306a36Sopenharmony_ci if ((fw_state == MFI_STATE_FAULT) || atomic_read(&instance->fw_outstanding)) { 284562306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 284662306a36Sopenharmony_ci "%s:%d waiting_for_outstanding: before issue OCR. FW state = 0x%x, outstanding 0x%x\n", 284762306a36Sopenharmony_ci __func__, __LINE__, fw_state, atomic_read(&instance->fw_outstanding)); 284862306a36Sopenharmony_ci if (i == 3) 284962306a36Sopenharmony_ci goto kill_hba_and_failed; 285062306a36Sopenharmony_ci megasas_do_ocr(instance); 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 285362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s:%d OCR failed and HBA is killed.\n", 285462306a36Sopenharmony_ci __func__, __LINE__); 285562306a36Sopenharmony_ci return FAILED; 285662306a36Sopenharmony_ci } 285762306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s:%d waiting_for_outstanding: after issue OCR.\n", 285862306a36Sopenharmony_ci __func__, __LINE__); 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci for (sl = 0; sl < 10; sl++) 286162306a36Sopenharmony_ci msleep(500); 286262306a36Sopenharmony_ci 286362306a36Sopenharmony_ci outstanding = atomic_read(&instance->fw_outstanding); 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci fw_state = instance->instancet->read_fw_status_reg(instance) & MFI_STATE_MASK; 286662306a36Sopenharmony_ci if ((!outstanding && (fw_state == MFI_STATE_OPERATIONAL))) 286762306a36Sopenharmony_ci goto no_outstanding; 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci i++; 287062306a36Sopenharmony_ci } while (i <= 3); 287162306a36Sopenharmony_ci 287262306a36Sopenharmony_cino_outstanding: 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s:%d no more pending commands remain after reset handling.\n", 287562306a36Sopenharmony_ci __func__, __LINE__); 287662306a36Sopenharmony_ci return SUCCESS; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_cikill_hba_and_failed: 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci /* Reset not supported, kill adapter */ 288162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s:%d killing adapter scsi%d" 288262306a36Sopenharmony_ci " disableOnlineCtrlReset %d fw_outstanding %d \n", 288362306a36Sopenharmony_ci __func__, __LINE__, instance->host->host_no, instance->disableOnlineCtrlReset, 288462306a36Sopenharmony_ci atomic_read(&instance->fw_outstanding)); 288562306a36Sopenharmony_ci megasas_dump_pending_frames(instance); 288662306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci return FAILED; 288962306a36Sopenharmony_ci} 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci/** 289262306a36Sopenharmony_ci * megasas_generic_reset - Generic reset routine 289362306a36Sopenharmony_ci * @scmd: Mid-layer SCSI command 289462306a36Sopenharmony_ci * 289562306a36Sopenharmony_ci * This routine implements a generic reset handler for device, bus and host 289662306a36Sopenharmony_ci * reset requests. Device, bus and host specific reset handlers can use this 289762306a36Sopenharmony_ci * function after they do their specific tasks. 289862306a36Sopenharmony_ci */ 289962306a36Sopenharmony_cistatic int megasas_generic_reset(struct scsi_cmnd *scmd) 290062306a36Sopenharmony_ci{ 290162306a36Sopenharmony_ci int ret_val; 290262306a36Sopenharmony_ci struct megasas_instance *instance; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci instance = (struct megasas_instance *)scmd->device->host->hostdata; 290562306a36Sopenharmony_ci 290662306a36Sopenharmony_ci scmd_printk(KERN_NOTICE, scmd, "megasas: RESET cmd=%x retries=%x\n", 290762306a36Sopenharmony_ci scmd->cmnd[0], scmd->retries); 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 291062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "cannot recover from previous reset failures\n"); 291162306a36Sopenharmony_ci return FAILED; 291262306a36Sopenharmony_ci } 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci ret_val = megasas_wait_for_outstanding(instance); 291562306a36Sopenharmony_ci if (ret_val == SUCCESS) 291662306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "reset successful\n"); 291762306a36Sopenharmony_ci else 291862306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "failed to do reset\n"); 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci return ret_val; 292162306a36Sopenharmony_ci} 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci/** 292462306a36Sopenharmony_ci * megasas_reset_timer - quiesce the adapter if required 292562306a36Sopenharmony_ci * @scmd: scsi cmnd 292662306a36Sopenharmony_ci * 292762306a36Sopenharmony_ci * Sets the FW busy flag and reduces the host->can_queue if the 292862306a36Sopenharmony_ci * cmd has not been completed within the timeout period. 292962306a36Sopenharmony_ci */ 293062306a36Sopenharmony_cistatic enum scsi_timeout_action megasas_reset_timer(struct scsi_cmnd *scmd) 293162306a36Sopenharmony_ci{ 293262306a36Sopenharmony_ci struct megasas_instance *instance; 293362306a36Sopenharmony_ci unsigned long flags; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci if (time_after(jiffies, scmd->jiffies_at_alloc + 293662306a36Sopenharmony_ci (scmd_timeout * 2) * HZ)) { 293762306a36Sopenharmony_ci return SCSI_EH_NOT_HANDLED; 293862306a36Sopenharmony_ci } 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci instance = (struct megasas_instance *)scmd->device->host->hostdata; 294162306a36Sopenharmony_ci if (!(instance->flag & MEGASAS_FW_BUSY)) { 294262306a36Sopenharmony_ci /* FW is busy, throttle IO */ 294362306a36Sopenharmony_ci spin_lock_irqsave(instance->host->host_lock, flags); 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci instance->host->can_queue = instance->throttlequeuedepth; 294662306a36Sopenharmony_ci instance->last_time = jiffies; 294762306a36Sopenharmony_ci instance->flag |= MEGASAS_FW_BUSY; 294862306a36Sopenharmony_ci 294962306a36Sopenharmony_ci spin_unlock_irqrestore(instance->host->host_lock, flags); 295062306a36Sopenharmony_ci } 295162306a36Sopenharmony_ci return SCSI_EH_RESET_TIMER; 295262306a36Sopenharmony_ci} 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci/** 295562306a36Sopenharmony_ci * megasas_dump - This function will print hexdump of provided buffer. 295662306a36Sopenharmony_ci * @buf: Buffer to be dumped 295762306a36Sopenharmony_ci * @sz: Size in bytes 295862306a36Sopenharmony_ci * @format: Different formats of dumping e.g. format=n will 295962306a36Sopenharmony_ci * cause only 'n' 32 bit words to be dumped in a single 296062306a36Sopenharmony_ci * line. 296162306a36Sopenharmony_ci */ 296262306a36Sopenharmony_ciinline void 296362306a36Sopenharmony_cimegasas_dump(void *buf, int sz, int format) 296462306a36Sopenharmony_ci{ 296562306a36Sopenharmony_ci int i; 296662306a36Sopenharmony_ci __le32 *buf_loc = (__le32 *)buf; 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci for (i = 0; i < (sz / sizeof(__le32)); i++) { 296962306a36Sopenharmony_ci if ((i % format) == 0) { 297062306a36Sopenharmony_ci if (i != 0) 297162306a36Sopenharmony_ci printk(KERN_CONT "\n"); 297262306a36Sopenharmony_ci printk(KERN_CONT "%08x: ", (i * 4)); 297362306a36Sopenharmony_ci } 297462306a36Sopenharmony_ci printk(KERN_CONT "%08x ", le32_to_cpu(buf_loc[i])); 297562306a36Sopenharmony_ci } 297662306a36Sopenharmony_ci printk(KERN_CONT "\n"); 297762306a36Sopenharmony_ci} 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci/** 298062306a36Sopenharmony_ci * megasas_dump_reg_set - This function will print hexdump of register set 298162306a36Sopenharmony_ci * @reg_set: Register set to be dumped 298262306a36Sopenharmony_ci */ 298362306a36Sopenharmony_ciinline void 298462306a36Sopenharmony_cimegasas_dump_reg_set(void __iomem *reg_set) 298562306a36Sopenharmony_ci{ 298662306a36Sopenharmony_ci unsigned int i, sz = 256; 298762306a36Sopenharmony_ci u32 __iomem *reg = (u32 __iomem *)reg_set; 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci for (i = 0; i < (sz / sizeof(u32)); i++) 299062306a36Sopenharmony_ci printk("%08x: %08x\n", (i * 4), readl(®[i])); 299162306a36Sopenharmony_ci} 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci/** 299462306a36Sopenharmony_ci * megasas_dump_fusion_io - This function will print key details 299562306a36Sopenharmony_ci * of SCSI IO 299662306a36Sopenharmony_ci * @scmd: SCSI command pointer of SCSI IO 299762306a36Sopenharmony_ci */ 299862306a36Sopenharmony_civoid 299962306a36Sopenharmony_cimegasas_dump_fusion_io(struct scsi_cmnd *scmd) 300062306a36Sopenharmony_ci{ 300162306a36Sopenharmony_ci struct megasas_cmd_fusion *cmd = megasas_priv(scmd)->cmd_priv; 300262306a36Sopenharmony_ci union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; 300362306a36Sopenharmony_ci struct megasas_instance *instance; 300462306a36Sopenharmony_ci 300562306a36Sopenharmony_ci instance = (struct megasas_instance *)scmd->device->host->hostdata; 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 300862306a36Sopenharmony_ci "scmd: (0x%p) retries: 0x%x allowed: 0x%x\n", 300962306a36Sopenharmony_ci scmd, scmd->retries, scmd->allowed); 301062306a36Sopenharmony_ci scsi_print_command(scmd); 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci if (cmd) { 301362306a36Sopenharmony_ci req_desc = (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)cmd->request_desc; 301462306a36Sopenharmony_ci scmd_printk(KERN_INFO, scmd, "Request descriptor details:\n"); 301562306a36Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 301662306a36Sopenharmony_ci "RequestFlags:0x%x MSIxIndex:0x%x SMID:0x%x LMID:0x%x DevHandle:0x%x\n", 301762306a36Sopenharmony_ci req_desc->SCSIIO.RequestFlags, 301862306a36Sopenharmony_ci req_desc->SCSIIO.MSIxIndex, req_desc->SCSIIO.SMID, 301962306a36Sopenharmony_ci req_desc->SCSIIO.LMID, req_desc->SCSIIO.DevHandle); 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ci printk(KERN_INFO "IO request frame:\n"); 302262306a36Sopenharmony_ci megasas_dump(cmd->io_request, 302362306a36Sopenharmony_ci MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE, 8); 302462306a36Sopenharmony_ci printk(KERN_INFO "Chain frame:\n"); 302562306a36Sopenharmony_ci megasas_dump(cmd->sg_frame, 302662306a36Sopenharmony_ci instance->max_chain_frame_sz, 8); 302762306a36Sopenharmony_ci } 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci} 303062306a36Sopenharmony_ci 303162306a36Sopenharmony_ci/* 303262306a36Sopenharmony_ci * megasas_dump_sys_regs - This function will dump system registers through 303362306a36Sopenharmony_ci * sysfs. 303462306a36Sopenharmony_ci * @reg_set: Pointer to System register set. 303562306a36Sopenharmony_ci * @buf: Buffer to which output is to be written. 303662306a36Sopenharmony_ci * @return: Number of bytes written to buffer. 303762306a36Sopenharmony_ci */ 303862306a36Sopenharmony_cistatic inline ssize_t 303962306a36Sopenharmony_cimegasas_dump_sys_regs(void __iomem *reg_set, char *buf) 304062306a36Sopenharmony_ci{ 304162306a36Sopenharmony_ci unsigned int i, sz = 256; 304262306a36Sopenharmony_ci int bytes_wrote = 0; 304362306a36Sopenharmony_ci char *loc = (char *)buf; 304462306a36Sopenharmony_ci u32 __iomem *reg = (u32 __iomem *)reg_set; 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci for (i = 0; i < sz / sizeof(u32); i++) { 304762306a36Sopenharmony_ci bytes_wrote += scnprintf(loc + bytes_wrote, 304862306a36Sopenharmony_ci PAGE_SIZE - bytes_wrote, 304962306a36Sopenharmony_ci "%08x: %08x\n", (i * 4), 305062306a36Sopenharmony_ci readl(®[i])); 305162306a36Sopenharmony_ci } 305262306a36Sopenharmony_ci return bytes_wrote; 305362306a36Sopenharmony_ci} 305462306a36Sopenharmony_ci 305562306a36Sopenharmony_ci/** 305662306a36Sopenharmony_ci * megasas_reset_bus_host - Bus & host reset handler entry point 305762306a36Sopenharmony_ci * @scmd: Mid-layer SCSI command 305862306a36Sopenharmony_ci */ 305962306a36Sopenharmony_cistatic int megasas_reset_bus_host(struct scsi_cmnd *scmd) 306062306a36Sopenharmony_ci{ 306162306a36Sopenharmony_ci int ret; 306262306a36Sopenharmony_ci struct megasas_instance *instance; 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci instance = (struct megasas_instance *)scmd->device->host->hostdata; 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 306762306a36Sopenharmony_ci "OCR is requested due to IO timeout!!\n"); 306862306a36Sopenharmony_ci 306962306a36Sopenharmony_ci scmd_printk(KERN_INFO, scmd, 307062306a36Sopenharmony_ci "SCSI host state: %d SCSI host busy: %d FW outstanding: %d\n", 307162306a36Sopenharmony_ci scmd->device->host->shost_state, 307262306a36Sopenharmony_ci scsi_host_busy(scmd->device->host), 307362306a36Sopenharmony_ci atomic_read(&instance->fw_outstanding)); 307462306a36Sopenharmony_ci /* 307562306a36Sopenharmony_ci * First wait for all commands to complete 307662306a36Sopenharmony_ci */ 307762306a36Sopenharmony_ci if (instance->adapter_type == MFI_SERIES) { 307862306a36Sopenharmony_ci ret = megasas_generic_reset(scmd); 307962306a36Sopenharmony_ci } else { 308062306a36Sopenharmony_ci megasas_dump_fusion_io(scmd); 308162306a36Sopenharmony_ci ret = megasas_reset_fusion(scmd->device->host, 308262306a36Sopenharmony_ci SCSIIO_TIMEOUT_OCR); 308362306a36Sopenharmony_ci } 308462306a36Sopenharmony_ci 308562306a36Sopenharmony_ci return ret; 308662306a36Sopenharmony_ci} 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci/** 308962306a36Sopenharmony_ci * megasas_task_abort - Issues task abort request to firmware 309062306a36Sopenharmony_ci * (supported only for fusion adapters) 309162306a36Sopenharmony_ci * @scmd: SCSI command pointer 309262306a36Sopenharmony_ci */ 309362306a36Sopenharmony_cistatic int megasas_task_abort(struct scsi_cmnd *scmd) 309462306a36Sopenharmony_ci{ 309562306a36Sopenharmony_ci int ret; 309662306a36Sopenharmony_ci struct megasas_instance *instance; 309762306a36Sopenharmony_ci 309862306a36Sopenharmony_ci instance = (struct megasas_instance *)scmd->device->host->hostdata; 309962306a36Sopenharmony_ci 310062306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 310162306a36Sopenharmony_ci ret = megasas_task_abort_fusion(scmd); 310262306a36Sopenharmony_ci else { 310362306a36Sopenharmony_ci sdev_printk(KERN_NOTICE, scmd->device, "TASK ABORT not supported\n"); 310462306a36Sopenharmony_ci ret = FAILED; 310562306a36Sopenharmony_ci } 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci return ret; 310862306a36Sopenharmony_ci} 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci/** 311162306a36Sopenharmony_ci * megasas_reset_target: Issues target reset request to firmware 311262306a36Sopenharmony_ci * (supported only for fusion adapters) 311362306a36Sopenharmony_ci * @scmd: SCSI command pointer 311462306a36Sopenharmony_ci */ 311562306a36Sopenharmony_cistatic int megasas_reset_target(struct scsi_cmnd *scmd) 311662306a36Sopenharmony_ci{ 311762306a36Sopenharmony_ci int ret; 311862306a36Sopenharmony_ci struct megasas_instance *instance; 311962306a36Sopenharmony_ci 312062306a36Sopenharmony_ci instance = (struct megasas_instance *)scmd->device->host->hostdata; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 312362306a36Sopenharmony_ci ret = megasas_reset_target_fusion(scmd); 312462306a36Sopenharmony_ci else { 312562306a36Sopenharmony_ci sdev_printk(KERN_NOTICE, scmd->device, "TARGET RESET not supported\n"); 312662306a36Sopenharmony_ci ret = FAILED; 312762306a36Sopenharmony_ci } 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci return ret; 313062306a36Sopenharmony_ci} 313162306a36Sopenharmony_ci 313262306a36Sopenharmony_ci/** 313362306a36Sopenharmony_ci * megasas_bios_param - Returns disk geometry for a disk 313462306a36Sopenharmony_ci * @sdev: device handle 313562306a36Sopenharmony_ci * @bdev: block device 313662306a36Sopenharmony_ci * @capacity: drive capacity 313762306a36Sopenharmony_ci * @geom: geometry parameters 313862306a36Sopenharmony_ci */ 313962306a36Sopenharmony_cistatic int 314062306a36Sopenharmony_cimegasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, 314162306a36Sopenharmony_ci sector_t capacity, int geom[]) 314262306a36Sopenharmony_ci{ 314362306a36Sopenharmony_ci int heads; 314462306a36Sopenharmony_ci int sectors; 314562306a36Sopenharmony_ci sector_t cylinders; 314662306a36Sopenharmony_ci unsigned long tmp; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci /* Default heads (64) & sectors (32) */ 314962306a36Sopenharmony_ci heads = 64; 315062306a36Sopenharmony_ci sectors = 32; 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci tmp = heads * sectors; 315362306a36Sopenharmony_ci cylinders = capacity; 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci sector_div(cylinders, tmp); 315662306a36Sopenharmony_ci 315762306a36Sopenharmony_ci /* 315862306a36Sopenharmony_ci * Handle extended translation size for logical drives > 1Gb 315962306a36Sopenharmony_ci */ 316062306a36Sopenharmony_ci 316162306a36Sopenharmony_ci if (capacity >= 0x200000) { 316262306a36Sopenharmony_ci heads = 255; 316362306a36Sopenharmony_ci sectors = 63; 316462306a36Sopenharmony_ci tmp = heads*sectors; 316562306a36Sopenharmony_ci cylinders = capacity; 316662306a36Sopenharmony_ci sector_div(cylinders, tmp); 316762306a36Sopenharmony_ci } 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci geom[0] = heads; 317062306a36Sopenharmony_ci geom[1] = sectors; 317162306a36Sopenharmony_ci geom[2] = cylinders; 317262306a36Sopenharmony_ci 317362306a36Sopenharmony_ci return 0; 317462306a36Sopenharmony_ci} 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_cistatic void megasas_map_queues(struct Scsi_Host *shost) 317762306a36Sopenharmony_ci{ 317862306a36Sopenharmony_ci struct megasas_instance *instance; 317962306a36Sopenharmony_ci int qoff = 0, offset; 318062306a36Sopenharmony_ci struct blk_mq_queue_map *map; 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci instance = (struct megasas_instance *)shost->hostdata; 318362306a36Sopenharmony_ci 318462306a36Sopenharmony_ci if (shost->nr_hw_queues == 1) 318562306a36Sopenharmony_ci return; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci offset = instance->low_latency_index_start; 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci /* Setup Default hctx */ 319062306a36Sopenharmony_ci map = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; 319162306a36Sopenharmony_ci map->nr_queues = instance->msix_vectors - offset; 319262306a36Sopenharmony_ci map->queue_offset = 0; 319362306a36Sopenharmony_ci blk_mq_pci_map_queues(map, instance->pdev, offset); 319462306a36Sopenharmony_ci qoff += map->nr_queues; 319562306a36Sopenharmony_ci offset += map->nr_queues; 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci /* we never use READ queue, so can't cheat blk-mq */ 319862306a36Sopenharmony_ci shost->tag_set.map[HCTX_TYPE_READ].nr_queues = 0; 319962306a36Sopenharmony_ci 320062306a36Sopenharmony_ci /* Setup Poll hctx */ 320162306a36Sopenharmony_ci map = &shost->tag_set.map[HCTX_TYPE_POLL]; 320262306a36Sopenharmony_ci map->nr_queues = instance->iopoll_q_count; 320362306a36Sopenharmony_ci if (map->nr_queues) { 320462306a36Sopenharmony_ci /* 320562306a36Sopenharmony_ci * The poll queue(s) doesn't have an IRQ (and hence IRQ 320662306a36Sopenharmony_ci * affinity), so use the regular blk-mq cpu mapping 320762306a36Sopenharmony_ci */ 320862306a36Sopenharmony_ci map->queue_offset = qoff; 320962306a36Sopenharmony_ci blk_mq_map_queues(map); 321062306a36Sopenharmony_ci } 321162306a36Sopenharmony_ci} 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_cistatic void megasas_aen_polling(struct work_struct *work); 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci/** 321662306a36Sopenharmony_ci * megasas_service_aen - Processes an event notification 321762306a36Sopenharmony_ci * @instance: Adapter soft state 321862306a36Sopenharmony_ci * @cmd: AEN command completed by the ISR 321962306a36Sopenharmony_ci * 322062306a36Sopenharmony_ci * For AEN, driver sends a command down to FW that is held by the FW till an 322162306a36Sopenharmony_ci * event occurs. When an event of interest occurs, FW completes the command 322262306a36Sopenharmony_ci * that it was previously holding. 322362306a36Sopenharmony_ci * 322462306a36Sopenharmony_ci * This routines sends SIGIO signal to processes that have registered with the 322562306a36Sopenharmony_ci * driver for AEN. 322662306a36Sopenharmony_ci */ 322762306a36Sopenharmony_cistatic void 322862306a36Sopenharmony_cimegasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) 322962306a36Sopenharmony_ci{ 323062306a36Sopenharmony_ci unsigned long flags; 323162306a36Sopenharmony_ci 323262306a36Sopenharmony_ci /* 323362306a36Sopenharmony_ci * Don't signal app if it is just an aborted previously registered aen 323462306a36Sopenharmony_ci */ 323562306a36Sopenharmony_ci if ((!cmd->abort_aen) && (instance->unload == 0)) { 323662306a36Sopenharmony_ci spin_lock_irqsave(&poll_aen_lock, flags); 323762306a36Sopenharmony_ci megasas_poll_wait_aen = 1; 323862306a36Sopenharmony_ci spin_unlock_irqrestore(&poll_aen_lock, flags); 323962306a36Sopenharmony_ci wake_up(&megasas_poll_wait); 324062306a36Sopenharmony_ci kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci else 324362306a36Sopenharmony_ci cmd->abort_aen = 0; 324462306a36Sopenharmony_ci 324562306a36Sopenharmony_ci instance->aen_cmd = NULL; 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci if ((instance->unload == 0) && 325062306a36Sopenharmony_ci ((instance->issuepend_done == 1))) { 325162306a36Sopenharmony_ci struct megasas_aen_event *ev; 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci ev = kzalloc(sizeof(*ev), GFP_ATOMIC); 325462306a36Sopenharmony_ci if (!ev) { 325562306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "megasas_service_aen: out of memory\n"); 325662306a36Sopenharmony_ci } else { 325762306a36Sopenharmony_ci ev->instance = instance; 325862306a36Sopenharmony_ci instance->ev = ev; 325962306a36Sopenharmony_ci INIT_DELAYED_WORK(&ev->hotplug_work, 326062306a36Sopenharmony_ci megasas_aen_polling); 326162306a36Sopenharmony_ci schedule_delayed_work(&ev->hotplug_work, 0); 326262306a36Sopenharmony_ci } 326362306a36Sopenharmony_ci } 326462306a36Sopenharmony_ci} 326562306a36Sopenharmony_ci 326662306a36Sopenharmony_cistatic ssize_t 326762306a36Sopenharmony_cifw_crash_buffer_store(struct device *cdev, 326862306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 326962306a36Sopenharmony_ci{ 327062306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 327162306a36Sopenharmony_ci struct megasas_instance *instance = 327262306a36Sopenharmony_ci (struct megasas_instance *) shost->hostdata; 327362306a36Sopenharmony_ci int val = 0; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci if (kstrtoint(buf, 0, &val) != 0) 327662306a36Sopenharmony_ci return -EINVAL; 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_ci mutex_lock(&instance->crashdump_lock); 327962306a36Sopenharmony_ci instance->fw_crash_buffer_offset = val; 328062306a36Sopenharmony_ci mutex_unlock(&instance->crashdump_lock); 328162306a36Sopenharmony_ci return strlen(buf); 328262306a36Sopenharmony_ci} 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_cistatic ssize_t 328562306a36Sopenharmony_cifw_crash_buffer_show(struct device *cdev, 328662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 328762306a36Sopenharmony_ci{ 328862306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 328962306a36Sopenharmony_ci struct megasas_instance *instance = 329062306a36Sopenharmony_ci (struct megasas_instance *) shost->hostdata; 329162306a36Sopenharmony_ci u32 size; 329262306a36Sopenharmony_ci unsigned long dmachunk = CRASH_DMA_BUF_SIZE; 329362306a36Sopenharmony_ci unsigned long chunk_left_bytes; 329462306a36Sopenharmony_ci unsigned long src_addr; 329562306a36Sopenharmony_ci u32 buff_offset; 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci mutex_lock(&instance->crashdump_lock); 329862306a36Sopenharmony_ci buff_offset = instance->fw_crash_buffer_offset; 329962306a36Sopenharmony_ci if (!instance->crash_dump_buf || 330062306a36Sopenharmony_ci !((instance->fw_crash_state == AVAILABLE) || 330162306a36Sopenharmony_ci (instance->fw_crash_state == COPYING))) { 330262306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 330362306a36Sopenharmony_ci "Firmware crash dump is not available\n"); 330462306a36Sopenharmony_ci mutex_unlock(&instance->crashdump_lock); 330562306a36Sopenharmony_ci return -EINVAL; 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) { 330962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 331062306a36Sopenharmony_ci "Firmware crash dump offset is out of range\n"); 331162306a36Sopenharmony_ci mutex_unlock(&instance->crashdump_lock); 331262306a36Sopenharmony_ci return 0; 331362306a36Sopenharmony_ci } 331462306a36Sopenharmony_ci 331562306a36Sopenharmony_ci size = (instance->fw_crash_buffer_size * dmachunk) - buff_offset; 331662306a36Sopenharmony_ci chunk_left_bytes = dmachunk - (buff_offset % dmachunk); 331762306a36Sopenharmony_ci size = (size > chunk_left_bytes) ? chunk_left_bytes : size; 331862306a36Sopenharmony_ci size = (size >= PAGE_SIZE) ? (PAGE_SIZE - 1) : size; 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] + 332162306a36Sopenharmony_ci (buff_offset % dmachunk); 332262306a36Sopenharmony_ci memcpy(buf, (void *)src_addr, size); 332362306a36Sopenharmony_ci mutex_unlock(&instance->crashdump_lock); 332462306a36Sopenharmony_ci 332562306a36Sopenharmony_ci return size; 332662306a36Sopenharmony_ci} 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_cistatic ssize_t 332962306a36Sopenharmony_cifw_crash_buffer_size_show(struct device *cdev, 333062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 333162306a36Sopenharmony_ci{ 333262306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 333362306a36Sopenharmony_ci struct megasas_instance *instance = 333462306a36Sopenharmony_ci (struct megasas_instance *) shost->hostdata; 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long) 333762306a36Sopenharmony_ci ((instance->fw_crash_buffer_size) * 1024 * 1024)/PAGE_SIZE); 333862306a36Sopenharmony_ci} 333962306a36Sopenharmony_ci 334062306a36Sopenharmony_cistatic ssize_t 334162306a36Sopenharmony_cifw_crash_state_store(struct device *cdev, 334262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 334362306a36Sopenharmony_ci{ 334462306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 334562306a36Sopenharmony_ci struct megasas_instance *instance = 334662306a36Sopenharmony_ci (struct megasas_instance *) shost->hostdata; 334762306a36Sopenharmony_ci int val = 0; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci if (kstrtoint(buf, 0, &val) != 0) 335062306a36Sopenharmony_ci return -EINVAL; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci if ((val <= AVAILABLE || val > COPY_ERROR)) { 335362306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "application updates invalid " 335462306a36Sopenharmony_ci "firmware crash state\n"); 335562306a36Sopenharmony_ci return -EINVAL; 335662306a36Sopenharmony_ci } 335762306a36Sopenharmony_ci 335862306a36Sopenharmony_ci instance->fw_crash_state = val; 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci if ((val == COPIED) || (val == COPY_ERROR)) { 336162306a36Sopenharmony_ci mutex_lock(&instance->crashdump_lock); 336262306a36Sopenharmony_ci megasas_free_host_crash_buffer(instance); 336362306a36Sopenharmony_ci mutex_unlock(&instance->crashdump_lock); 336462306a36Sopenharmony_ci if (val == COPY_ERROR) 336562306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "application failed to " 336662306a36Sopenharmony_ci "copy Firmware crash dump\n"); 336762306a36Sopenharmony_ci else 336862306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Firmware crash dump " 336962306a36Sopenharmony_ci "copied successfully\n"); 337062306a36Sopenharmony_ci } 337162306a36Sopenharmony_ci return strlen(buf); 337262306a36Sopenharmony_ci} 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_cistatic ssize_t 337562306a36Sopenharmony_cifw_crash_state_show(struct device *cdev, 337662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 337762306a36Sopenharmony_ci{ 337862306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 337962306a36Sopenharmony_ci struct megasas_instance *instance = 338062306a36Sopenharmony_ci (struct megasas_instance *) shost->hostdata; 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", instance->fw_crash_state); 338362306a36Sopenharmony_ci} 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_cistatic ssize_t 338662306a36Sopenharmony_cipage_size_show(struct device *cdev, 338762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 338862306a36Sopenharmony_ci{ 338962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%ld\n", (unsigned long)PAGE_SIZE - 1); 339062306a36Sopenharmony_ci} 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_cistatic ssize_t 339362306a36Sopenharmony_cildio_outstanding_show(struct device *cdev, struct device_attribute *attr, 339462306a36Sopenharmony_ci char *buf) 339562306a36Sopenharmony_ci{ 339662306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 339762306a36Sopenharmony_ci struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&instance->ldio_outstanding)); 340062306a36Sopenharmony_ci} 340162306a36Sopenharmony_ci 340262306a36Sopenharmony_cistatic ssize_t 340362306a36Sopenharmony_cifw_cmds_outstanding_show(struct device *cdev, 340462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 340562306a36Sopenharmony_ci{ 340662306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 340762306a36Sopenharmony_ci struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; 340862306a36Sopenharmony_ci 340962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&instance->fw_outstanding)); 341062306a36Sopenharmony_ci} 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_cistatic ssize_t 341362306a36Sopenharmony_cienable_sdev_max_qd_show(struct device *cdev, 341462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 341562306a36Sopenharmony_ci{ 341662306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 341762306a36Sopenharmony_ci struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%d\n", instance->enable_sdev_max_qd); 342062306a36Sopenharmony_ci} 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_cistatic ssize_t 342362306a36Sopenharmony_cienable_sdev_max_qd_store(struct device *cdev, 342462306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t count) 342562306a36Sopenharmony_ci{ 342662306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 342762306a36Sopenharmony_ci struct megasas_instance *instance = (struct megasas_instance *)shost->hostdata; 342862306a36Sopenharmony_ci u32 val = 0; 342962306a36Sopenharmony_ci bool is_target_prop; 343062306a36Sopenharmony_ci int ret_target_prop = DCMD_FAILED; 343162306a36Sopenharmony_ci struct scsi_device *sdev; 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_ci if (kstrtou32(buf, 0, &val) != 0) { 343462306a36Sopenharmony_ci pr_err("megasas: could not set enable_sdev_max_qd\n"); 343562306a36Sopenharmony_ci return -EINVAL; 343662306a36Sopenharmony_ci } 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 343962306a36Sopenharmony_ci if (val) 344062306a36Sopenharmony_ci instance->enable_sdev_max_qd = true; 344162306a36Sopenharmony_ci else 344262306a36Sopenharmony_ci instance->enable_sdev_max_qd = false; 344362306a36Sopenharmony_ci 344462306a36Sopenharmony_ci shost_for_each_device(sdev, shost) { 344562306a36Sopenharmony_ci ret_target_prop = megasas_get_target_prop(instance, sdev); 344662306a36Sopenharmony_ci is_target_prop = (ret_target_prop == DCMD_SUCCESS) ? true : false; 344762306a36Sopenharmony_ci megasas_set_fw_assisted_qd(sdev, is_target_prop); 344862306a36Sopenharmony_ci } 344962306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 345062306a36Sopenharmony_ci 345162306a36Sopenharmony_ci return strlen(buf); 345262306a36Sopenharmony_ci} 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_cistatic ssize_t 345562306a36Sopenharmony_cidump_system_regs_show(struct device *cdev, 345662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 345762306a36Sopenharmony_ci{ 345862306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 345962306a36Sopenharmony_ci struct megasas_instance *instance = 346062306a36Sopenharmony_ci (struct megasas_instance *)shost->hostdata; 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci return megasas_dump_sys_regs(instance->reg_set, buf); 346362306a36Sopenharmony_ci} 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_cistatic ssize_t 346662306a36Sopenharmony_ciraid_map_id_show(struct device *cdev, struct device_attribute *attr, 346762306a36Sopenharmony_ci char *buf) 346862306a36Sopenharmony_ci{ 346962306a36Sopenharmony_ci struct Scsi_Host *shost = class_to_shost(cdev); 347062306a36Sopenharmony_ci struct megasas_instance *instance = 347162306a36Sopenharmony_ci (struct megasas_instance *)shost->hostdata; 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%ld\n", 347462306a36Sopenharmony_ci (unsigned long)instance->map_id); 347562306a36Sopenharmony_ci} 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fw_crash_buffer); 347862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fw_crash_buffer_size); 347962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fw_crash_state); 348062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(page_size); 348162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ldio_outstanding); 348262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(fw_cmds_outstanding); 348362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(enable_sdev_max_qd); 348462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(dump_system_regs); 348562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(raid_map_id); 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_cistatic struct attribute *megaraid_host_attrs[] = { 348862306a36Sopenharmony_ci &dev_attr_fw_crash_buffer_size.attr, 348962306a36Sopenharmony_ci &dev_attr_fw_crash_buffer.attr, 349062306a36Sopenharmony_ci &dev_attr_fw_crash_state.attr, 349162306a36Sopenharmony_ci &dev_attr_page_size.attr, 349262306a36Sopenharmony_ci &dev_attr_ldio_outstanding.attr, 349362306a36Sopenharmony_ci &dev_attr_fw_cmds_outstanding.attr, 349462306a36Sopenharmony_ci &dev_attr_enable_sdev_max_qd.attr, 349562306a36Sopenharmony_ci &dev_attr_dump_system_regs.attr, 349662306a36Sopenharmony_ci &dev_attr_raid_map_id.attr, 349762306a36Sopenharmony_ci NULL, 349862306a36Sopenharmony_ci}; 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ciATTRIBUTE_GROUPS(megaraid_host); 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_ci/* 350362306a36Sopenharmony_ci * Scsi host template for megaraid_sas driver 350462306a36Sopenharmony_ci */ 350562306a36Sopenharmony_cistatic const struct scsi_host_template megasas_template = { 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci .module = THIS_MODULE, 350862306a36Sopenharmony_ci .name = "Avago SAS based MegaRAID driver", 350962306a36Sopenharmony_ci .proc_name = "megaraid_sas", 351062306a36Sopenharmony_ci .slave_configure = megasas_slave_configure, 351162306a36Sopenharmony_ci .slave_alloc = megasas_slave_alloc, 351262306a36Sopenharmony_ci .slave_destroy = megasas_slave_destroy, 351362306a36Sopenharmony_ci .queuecommand = megasas_queue_command, 351462306a36Sopenharmony_ci .eh_target_reset_handler = megasas_reset_target, 351562306a36Sopenharmony_ci .eh_abort_handler = megasas_task_abort, 351662306a36Sopenharmony_ci .eh_host_reset_handler = megasas_reset_bus_host, 351762306a36Sopenharmony_ci .eh_timed_out = megasas_reset_timer, 351862306a36Sopenharmony_ci .shost_groups = megaraid_host_groups, 351962306a36Sopenharmony_ci .bios_param = megasas_bios_param, 352062306a36Sopenharmony_ci .map_queues = megasas_map_queues, 352162306a36Sopenharmony_ci .mq_poll = megasas_blk_mq_poll, 352262306a36Sopenharmony_ci .change_queue_depth = scsi_change_queue_depth, 352362306a36Sopenharmony_ci .max_segment_size = 0xffffffff, 352462306a36Sopenharmony_ci .cmd_size = sizeof(struct megasas_cmd_priv), 352562306a36Sopenharmony_ci}; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci/** 352862306a36Sopenharmony_ci * megasas_complete_int_cmd - Completes an internal command 352962306a36Sopenharmony_ci * @instance: Adapter soft state 353062306a36Sopenharmony_ci * @cmd: Command to be completed 353162306a36Sopenharmony_ci * 353262306a36Sopenharmony_ci * The megasas_issue_blocked_cmd() function waits for a command to complete 353362306a36Sopenharmony_ci * after it issues a command. This function wakes up that waiting routine by 353462306a36Sopenharmony_ci * calling wake_up() on the wait queue. 353562306a36Sopenharmony_ci */ 353662306a36Sopenharmony_cistatic void 353762306a36Sopenharmony_cimegasas_complete_int_cmd(struct megasas_instance *instance, 353862306a36Sopenharmony_ci struct megasas_cmd *cmd) 353962306a36Sopenharmony_ci{ 354062306a36Sopenharmony_ci if (cmd->cmd_status_drv == DCMD_INIT) 354162306a36Sopenharmony_ci cmd->cmd_status_drv = 354262306a36Sopenharmony_ci (cmd->frame->io.cmd_status == MFI_STAT_OK) ? 354362306a36Sopenharmony_ci DCMD_SUCCESS : DCMD_FAILED; 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci wake_up(&instance->int_cmd_wait_q); 354662306a36Sopenharmony_ci} 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci/** 354962306a36Sopenharmony_ci * megasas_complete_abort - Completes aborting a command 355062306a36Sopenharmony_ci * @instance: Adapter soft state 355162306a36Sopenharmony_ci * @cmd: Cmd that was issued to abort another cmd 355262306a36Sopenharmony_ci * 355362306a36Sopenharmony_ci * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q 355462306a36Sopenharmony_ci * after it issues an abort on a previously issued command. This function 355562306a36Sopenharmony_ci * wakes up all functions waiting on the same wait queue. 355662306a36Sopenharmony_ci */ 355762306a36Sopenharmony_cistatic void 355862306a36Sopenharmony_cimegasas_complete_abort(struct megasas_instance *instance, 355962306a36Sopenharmony_ci struct megasas_cmd *cmd) 356062306a36Sopenharmony_ci{ 356162306a36Sopenharmony_ci if (cmd->sync_cmd) { 356262306a36Sopenharmony_ci cmd->sync_cmd = 0; 356362306a36Sopenharmony_ci cmd->cmd_status_drv = DCMD_SUCCESS; 356462306a36Sopenharmony_ci wake_up(&instance->abort_cmd_wait_q); 356562306a36Sopenharmony_ci } 356662306a36Sopenharmony_ci} 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_cistatic void 356962306a36Sopenharmony_cimegasas_set_ld_removed_by_fw(struct megasas_instance *instance) 357062306a36Sopenharmony_ci{ 357162306a36Sopenharmony_ci uint i; 357262306a36Sopenharmony_ci 357362306a36Sopenharmony_ci for (i = 0; (i < MEGASAS_MAX_LD_IDS); i++) { 357462306a36Sopenharmony_ci if (instance->ld_ids_prev[i] != 0xff && 357562306a36Sopenharmony_ci instance->ld_ids_from_raidmap[i] == 0xff) { 357662306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 357762306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 357862306a36Sopenharmony_ci "LD target ID %d removed from RAID map\n", i); 357962306a36Sopenharmony_ci instance->ld_tgtid_status[i] = LD_TARGET_ID_DELETED; 358062306a36Sopenharmony_ci } 358162306a36Sopenharmony_ci } 358262306a36Sopenharmony_ci} 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci/** 358562306a36Sopenharmony_ci * megasas_complete_cmd - Completes a command 358662306a36Sopenharmony_ci * @instance: Adapter soft state 358762306a36Sopenharmony_ci * @cmd: Command to be completed 358862306a36Sopenharmony_ci * @alt_status: If non-zero, use this value as status to 358962306a36Sopenharmony_ci * SCSI mid-layer instead of the value returned 359062306a36Sopenharmony_ci * by the FW. This should be used if caller wants 359162306a36Sopenharmony_ci * an alternate status (as in the case of aborted 359262306a36Sopenharmony_ci * commands) 359362306a36Sopenharmony_ci */ 359462306a36Sopenharmony_civoid 359562306a36Sopenharmony_cimegasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, 359662306a36Sopenharmony_ci u8 alt_status) 359762306a36Sopenharmony_ci{ 359862306a36Sopenharmony_ci int exception = 0; 359962306a36Sopenharmony_ci struct megasas_header *hdr = &cmd->frame->hdr; 360062306a36Sopenharmony_ci unsigned long flags; 360162306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 360262306a36Sopenharmony_ci u32 opcode, status; 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_ci /* flag for the retry reset */ 360562306a36Sopenharmony_ci cmd->retry_for_fw_reset = 0; 360662306a36Sopenharmony_ci 360762306a36Sopenharmony_ci if (cmd->scmd) 360862306a36Sopenharmony_ci megasas_priv(cmd->scmd)->cmd_priv = NULL; 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci switch (hdr->cmd) { 361162306a36Sopenharmony_ci case MFI_CMD_INVALID: 361262306a36Sopenharmony_ci /* Some older 1068 controller FW may keep a pended 361362306a36Sopenharmony_ci MR_DCMD_CTRL_EVENT_GET_INFO left over from the main kernel 361462306a36Sopenharmony_ci when booting the kdump kernel. Ignore this command to 361562306a36Sopenharmony_ci prevent a kernel panic on shutdown of the kdump kernel. */ 361662306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "MFI_CMD_INVALID command " 361762306a36Sopenharmony_ci "completed\n"); 361862306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "If you have a controller " 361962306a36Sopenharmony_ci "other than PERC5, please upgrade your firmware\n"); 362062306a36Sopenharmony_ci break; 362162306a36Sopenharmony_ci case MFI_CMD_PD_SCSI_IO: 362262306a36Sopenharmony_ci case MFI_CMD_LD_SCSI_IO: 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci /* 362562306a36Sopenharmony_ci * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been 362662306a36Sopenharmony_ci * issued either through an IO path or an IOCTL path. If it 362762306a36Sopenharmony_ci * was via IOCTL, we will send it to internal completion. 362862306a36Sopenharmony_ci */ 362962306a36Sopenharmony_ci if (cmd->sync_cmd) { 363062306a36Sopenharmony_ci cmd->sync_cmd = 0; 363162306a36Sopenharmony_ci megasas_complete_int_cmd(instance, cmd); 363262306a36Sopenharmony_ci break; 363362306a36Sopenharmony_ci } 363462306a36Sopenharmony_ci fallthrough; 363562306a36Sopenharmony_ci 363662306a36Sopenharmony_ci case MFI_CMD_LD_READ: 363762306a36Sopenharmony_ci case MFI_CMD_LD_WRITE: 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci if (alt_status) { 364062306a36Sopenharmony_ci cmd->scmd->result = alt_status << 16; 364162306a36Sopenharmony_ci exception = 1; 364262306a36Sopenharmony_ci } 364362306a36Sopenharmony_ci 364462306a36Sopenharmony_ci if (exception) { 364562306a36Sopenharmony_ci 364662306a36Sopenharmony_ci atomic_dec(&instance->fw_outstanding); 364762306a36Sopenharmony_ci 364862306a36Sopenharmony_ci scsi_dma_unmap(cmd->scmd); 364962306a36Sopenharmony_ci scsi_done(cmd->scmd); 365062306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_ci break; 365362306a36Sopenharmony_ci } 365462306a36Sopenharmony_ci 365562306a36Sopenharmony_ci switch (hdr->cmd_status) { 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_ci case MFI_STAT_OK: 365862306a36Sopenharmony_ci cmd->scmd->result = DID_OK << 16; 365962306a36Sopenharmony_ci break; 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci case MFI_STAT_SCSI_IO_FAILED: 366262306a36Sopenharmony_ci case MFI_STAT_LD_INIT_IN_PROGRESS: 366362306a36Sopenharmony_ci cmd->scmd->result = 366462306a36Sopenharmony_ci (DID_ERROR << 16) | hdr->scsi_status; 366562306a36Sopenharmony_ci break; 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci case MFI_STAT_SCSI_DONE_WITH_ERROR: 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci cmd->scmd->result = (DID_OK << 16) | hdr->scsi_status; 367062306a36Sopenharmony_ci 367162306a36Sopenharmony_ci if (hdr->scsi_status == SAM_STAT_CHECK_CONDITION) { 367262306a36Sopenharmony_ci memset(cmd->scmd->sense_buffer, 0, 367362306a36Sopenharmony_ci SCSI_SENSE_BUFFERSIZE); 367462306a36Sopenharmony_ci memcpy(cmd->scmd->sense_buffer, cmd->sense, 367562306a36Sopenharmony_ci hdr->sense_len); 367662306a36Sopenharmony_ci } 367762306a36Sopenharmony_ci 367862306a36Sopenharmony_ci break; 367962306a36Sopenharmony_ci 368062306a36Sopenharmony_ci case MFI_STAT_LD_OFFLINE: 368162306a36Sopenharmony_ci case MFI_STAT_DEVICE_NOT_FOUND: 368262306a36Sopenharmony_ci cmd->scmd->result = DID_BAD_TARGET << 16; 368362306a36Sopenharmony_ci break; 368462306a36Sopenharmony_ci 368562306a36Sopenharmony_ci default: 368662306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "MFI FW status %#x\n", 368762306a36Sopenharmony_ci hdr->cmd_status); 368862306a36Sopenharmony_ci cmd->scmd->result = DID_ERROR << 16; 368962306a36Sopenharmony_ci break; 369062306a36Sopenharmony_ci } 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_ci atomic_dec(&instance->fw_outstanding); 369362306a36Sopenharmony_ci 369462306a36Sopenharmony_ci scsi_dma_unmap(cmd->scmd); 369562306a36Sopenharmony_ci scsi_done(cmd->scmd); 369662306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 369762306a36Sopenharmony_ci 369862306a36Sopenharmony_ci break; 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci case MFI_CMD_SMP: 370162306a36Sopenharmony_ci case MFI_CMD_STP: 370262306a36Sopenharmony_ci case MFI_CMD_NVME: 370362306a36Sopenharmony_ci case MFI_CMD_TOOLBOX: 370462306a36Sopenharmony_ci megasas_complete_int_cmd(instance, cmd); 370562306a36Sopenharmony_ci break; 370662306a36Sopenharmony_ci 370762306a36Sopenharmony_ci case MFI_CMD_DCMD: 370862306a36Sopenharmony_ci opcode = le32_to_cpu(cmd->frame->dcmd.opcode); 370962306a36Sopenharmony_ci /* Check for LD map update */ 371062306a36Sopenharmony_ci if ((opcode == MR_DCMD_LD_MAP_GET_INFO) 371162306a36Sopenharmony_ci && (cmd->frame->dcmd.mbox.b[1] == 1)) { 371262306a36Sopenharmony_ci fusion->fast_path_io = 0; 371362306a36Sopenharmony_ci spin_lock_irqsave(instance->host->host_lock, flags); 371462306a36Sopenharmony_ci status = cmd->frame->hdr.cmd_status; 371562306a36Sopenharmony_ci instance->map_update_cmd = NULL; 371662306a36Sopenharmony_ci if (status != MFI_STAT_OK) { 371762306a36Sopenharmony_ci if (status != MFI_STAT_NOT_FOUND) 371862306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, "map syncfailed, status = 0x%x\n", 371962306a36Sopenharmony_ci cmd->frame->hdr.cmd_status); 372062306a36Sopenharmony_ci else { 372162306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 372262306a36Sopenharmony_ci spin_unlock_irqrestore( 372362306a36Sopenharmony_ci instance->host->host_lock, 372462306a36Sopenharmony_ci flags); 372562306a36Sopenharmony_ci break; 372662306a36Sopenharmony_ci } 372762306a36Sopenharmony_ci } 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_ci /* 373262306a36Sopenharmony_ci * Set fast path IO to ZERO. 373362306a36Sopenharmony_ci * Validate Map will set proper value. 373462306a36Sopenharmony_ci * Meanwhile all IOs will go as LD IO. 373562306a36Sopenharmony_ci */ 373662306a36Sopenharmony_ci if (status == MFI_STAT_OK && 373762306a36Sopenharmony_ci (MR_ValidateMapInfo(instance, (instance->map_id + 1)))) { 373862306a36Sopenharmony_ci instance->map_id++; 373962306a36Sopenharmony_ci fusion->fast_path_io = 1; 374062306a36Sopenharmony_ci } else { 374162306a36Sopenharmony_ci fusion->fast_path_io = 0; 374262306a36Sopenharmony_ci } 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_ci if (instance->adapter_type >= INVADER_SERIES) 374562306a36Sopenharmony_ci megasas_set_ld_removed_by_fw(instance); 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ci megasas_sync_map_info(instance); 374862306a36Sopenharmony_ci spin_unlock_irqrestore(instance->host->host_lock, 374962306a36Sopenharmony_ci flags); 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci break; 375262306a36Sopenharmony_ci } 375362306a36Sopenharmony_ci if (opcode == MR_DCMD_CTRL_EVENT_GET_INFO || 375462306a36Sopenharmony_ci opcode == MR_DCMD_CTRL_EVENT_GET) { 375562306a36Sopenharmony_ci spin_lock_irqsave(&poll_aen_lock, flags); 375662306a36Sopenharmony_ci megasas_poll_wait_aen = 0; 375762306a36Sopenharmony_ci spin_unlock_irqrestore(&poll_aen_lock, flags); 375862306a36Sopenharmony_ci } 375962306a36Sopenharmony_ci 376062306a36Sopenharmony_ci /* FW has an updated PD sequence */ 376162306a36Sopenharmony_ci if ((opcode == MR_DCMD_SYSTEM_PD_MAP_GET_INFO) && 376262306a36Sopenharmony_ci (cmd->frame->dcmd.mbox.b[0] == 1)) { 376362306a36Sopenharmony_ci 376462306a36Sopenharmony_ci spin_lock_irqsave(instance->host->host_lock, flags); 376562306a36Sopenharmony_ci status = cmd->frame->hdr.cmd_status; 376662306a36Sopenharmony_ci instance->jbod_seq_cmd = NULL; 376762306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci if (status == MFI_STAT_OK) { 377062306a36Sopenharmony_ci instance->pd_seq_map_id++; 377162306a36Sopenharmony_ci /* Re-register a pd sync seq num cmd */ 377262306a36Sopenharmony_ci if (megasas_sync_pd_seq_num(instance, true)) 377362306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = false; 377462306a36Sopenharmony_ci } else 377562306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = false; 377662306a36Sopenharmony_ci 377762306a36Sopenharmony_ci spin_unlock_irqrestore(instance->host->host_lock, flags); 377862306a36Sopenharmony_ci break; 377962306a36Sopenharmony_ci } 378062306a36Sopenharmony_ci 378162306a36Sopenharmony_ci /* 378262306a36Sopenharmony_ci * See if got an event notification 378362306a36Sopenharmony_ci */ 378462306a36Sopenharmony_ci if (opcode == MR_DCMD_CTRL_EVENT_WAIT) 378562306a36Sopenharmony_ci megasas_service_aen(instance, cmd); 378662306a36Sopenharmony_ci else 378762306a36Sopenharmony_ci megasas_complete_int_cmd(instance, cmd); 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ci break; 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_ci case MFI_CMD_ABORT: 379262306a36Sopenharmony_ci /* 379362306a36Sopenharmony_ci * Cmd issued to abort another cmd returned 379462306a36Sopenharmony_ci */ 379562306a36Sopenharmony_ci megasas_complete_abort(instance, cmd); 379662306a36Sopenharmony_ci break; 379762306a36Sopenharmony_ci 379862306a36Sopenharmony_ci default: 379962306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Unknown command completed! [0x%X]\n", 380062306a36Sopenharmony_ci hdr->cmd); 380162306a36Sopenharmony_ci megasas_complete_int_cmd(instance, cmd); 380262306a36Sopenharmony_ci break; 380362306a36Sopenharmony_ci } 380462306a36Sopenharmony_ci} 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci/** 380762306a36Sopenharmony_ci * megasas_issue_pending_cmds_again - issue all pending cmds 380862306a36Sopenharmony_ci * in FW again because of the fw reset 380962306a36Sopenharmony_ci * @instance: Adapter soft state 381062306a36Sopenharmony_ci */ 381162306a36Sopenharmony_cistatic inline void 381262306a36Sopenharmony_cimegasas_issue_pending_cmds_again(struct megasas_instance *instance) 381362306a36Sopenharmony_ci{ 381462306a36Sopenharmony_ci struct megasas_cmd *cmd; 381562306a36Sopenharmony_ci struct list_head clist_local; 381662306a36Sopenharmony_ci union megasas_evt_class_locale class_locale; 381762306a36Sopenharmony_ci unsigned long flags; 381862306a36Sopenharmony_ci u32 seq_num; 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci INIT_LIST_HEAD(&clist_local); 382162306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 382262306a36Sopenharmony_ci list_splice_init(&instance->internal_reset_pending_q, &clist_local); 382362306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_ci while (!list_empty(&clist_local)) { 382662306a36Sopenharmony_ci cmd = list_entry((&clist_local)->next, 382762306a36Sopenharmony_ci struct megasas_cmd, list); 382862306a36Sopenharmony_ci list_del_init(&cmd->list); 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci if (cmd->sync_cmd || cmd->scmd) { 383162306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "command %p, %p:%d" 383262306a36Sopenharmony_ci "detected to be pending while HBA reset\n", 383362306a36Sopenharmony_ci cmd, cmd->scmd, cmd->sync_cmd); 383462306a36Sopenharmony_ci 383562306a36Sopenharmony_ci cmd->retry_for_fw_reset++; 383662306a36Sopenharmony_ci 383762306a36Sopenharmony_ci if (cmd->retry_for_fw_reset == 3) { 383862306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "cmd %p, %p:%d" 383962306a36Sopenharmony_ci "was tried multiple times during reset." 384062306a36Sopenharmony_ci "Shutting down the HBA\n", 384162306a36Sopenharmony_ci cmd, cmd->scmd, cmd->sync_cmd); 384262306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 384362306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 1); 384462306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 384562306a36Sopenharmony_ci return; 384662306a36Sopenharmony_ci } 384762306a36Sopenharmony_ci } 384862306a36Sopenharmony_ci 384962306a36Sopenharmony_ci if (cmd->sync_cmd == 1) { 385062306a36Sopenharmony_ci if (cmd->scmd) { 385162306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "unexpected" 385262306a36Sopenharmony_ci "cmd attached to internal command!\n"); 385362306a36Sopenharmony_ci } 385462306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "%p synchronous cmd" 385562306a36Sopenharmony_ci "on the internal reset queue," 385662306a36Sopenharmony_ci "issue it again.\n", cmd); 385762306a36Sopenharmony_ci cmd->cmd_status_drv = DCMD_INIT; 385862306a36Sopenharmony_ci instance->instancet->fire_cmd(instance, 385962306a36Sopenharmony_ci cmd->frame_phys_addr, 386062306a36Sopenharmony_ci 0, instance->reg_set); 386162306a36Sopenharmony_ci } else if (cmd->scmd) { 386262306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "%p scsi cmd [%02x]" 386362306a36Sopenharmony_ci "detected on the internal queue, issue again.\n", 386462306a36Sopenharmony_ci cmd, cmd->scmd->cmnd[0]); 386562306a36Sopenharmony_ci 386662306a36Sopenharmony_ci atomic_inc(&instance->fw_outstanding); 386762306a36Sopenharmony_ci instance->instancet->fire_cmd(instance, 386862306a36Sopenharmony_ci cmd->frame_phys_addr, 386962306a36Sopenharmony_ci cmd->frame_count-1, instance->reg_set); 387062306a36Sopenharmony_ci } else { 387162306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "%p unexpected cmd on the" 387262306a36Sopenharmony_ci "internal reset defer list while re-issue!!\n", 387362306a36Sopenharmony_ci cmd); 387462306a36Sopenharmony_ci } 387562306a36Sopenharmony_ci } 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci if (instance->aen_cmd) { 387862306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "aen_cmd in def process\n"); 387962306a36Sopenharmony_ci megasas_return_cmd(instance, instance->aen_cmd); 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_ci instance->aen_cmd = NULL; 388262306a36Sopenharmony_ci } 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci /* 388562306a36Sopenharmony_ci * Initiate AEN (Asynchronous Event Notification) 388662306a36Sopenharmony_ci */ 388762306a36Sopenharmony_ci seq_num = instance->last_seq_num; 388862306a36Sopenharmony_ci class_locale.members.reserved = 0; 388962306a36Sopenharmony_ci class_locale.members.locale = MR_EVT_LOCALE_ALL; 389062306a36Sopenharmony_ci class_locale.members.class = MR_EVT_CLASS_DEBUG; 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci megasas_register_aen(instance, seq_num, class_locale.word); 389362306a36Sopenharmony_ci} 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci/* 389662306a36Sopenharmony_ci * Move the internal reset pending commands to a deferred queue. 389762306a36Sopenharmony_ci * 389862306a36Sopenharmony_ci * We move the commands pending at internal reset time to a 389962306a36Sopenharmony_ci * pending queue. This queue would be flushed after successful 390062306a36Sopenharmony_ci * completion of the internal reset sequence. if the internal reset 390162306a36Sopenharmony_ci * did not complete in time, the kernel reset handler would flush 390262306a36Sopenharmony_ci * these commands. 390362306a36Sopenharmony_ci */ 390462306a36Sopenharmony_cistatic void 390562306a36Sopenharmony_cimegasas_internal_reset_defer_cmds(struct megasas_instance *instance) 390662306a36Sopenharmony_ci{ 390762306a36Sopenharmony_ci struct megasas_cmd *cmd; 390862306a36Sopenharmony_ci int i; 390962306a36Sopenharmony_ci u16 max_cmd = instance->max_fw_cmds; 391062306a36Sopenharmony_ci u32 defer_index; 391162306a36Sopenharmony_ci unsigned long flags; 391262306a36Sopenharmony_ci 391362306a36Sopenharmony_ci defer_index = 0; 391462306a36Sopenharmony_ci spin_lock_irqsave(&instance->mfi_pool_lock, flags); 391562306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 391662306a36Sopenharmony_ci cmd = instance->cmd_list[i]; 391762306a36Sopenharmony_ci if (cmd->sync_cmd == 1 || cmd->scmd) { 391862306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "moving cmd[%d]:%p:%d:%p" 391962306a36Sopenharmony_ci "on the defer queue as internal\n", 392062306a36Sopenharmony_ci defer_index, cmd, cmd->sync_cmd, cmd->scmd); 392162306a36Sopenharmony_ci 392262306a36Sopenharmony_ci if (!list_empty(&cmd->list)) { 392362306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "ERROR while" 392462306a36Sopenharmony_ci " moving this cmd:%p, %d %p, it was" 392562306a36Sopenharmony_ci "discovered on some list?\n", 392662306a36Sopenharmony_ci cmd, cmd->sync_cmd, cmd->scmd); 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci list_del_init(&cmd->list); 392962306a36Sopenharmony_ci } 393062306a36Sopenharmony_ci defer_index++; 393162306a36Sopenharmony_ci list_add_tail(&cmd->list, 393262306a36Sopenharmony_ci &instance->internal_reset_pending_q); 393362306a36Sopenharmony_ci } 393462306a36Sopenharmony_ci } 393562306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->mfi_pool_lock, flags); 393662306a36Sopenharmony_ci} 393762306a36Sopenharmony_ci 393862306a36Sopenharmony_ci 393962306a36Sopenharmony_cistatic void 394062306a36Sopenharmony_ciprocess_fw_state_change_wq(struct work_struct *work) 394162306a36Sopenharmony_ci{ 394262306a36Sopenharmony_ci struct megasas_instance *instance = 394362306a36Sopenharmony_ci container_of(work, struct megasas_instance, work_init); 394462306a36Sopenharmony_ci u32 wait; 394562306a36Sopenharmony_ci unsigned long flags; 394662306a36Sopenharmony_ci 394762306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) { 394862306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "error, recovery st %x\n", 394962306a36Sopenharmony_ci atomic_read(&instance->adprecovery)); 395062306a36Sopenharmony_ci return ; 395162306a36Sopenharmony_ci } 395262306a36Sopenharmony_ci 395362306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_ADPRESET_SM_INFAULT) { 395462306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "FW detected to be in fault" 395562306a36Sopenharmony_ci "state, restarting it...\n"); 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 395862306a36Sopenharmony_ci atomic_set(&instance->fw_outstanding, 0); 395962306a36Sopenharmony_ci 396062306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 1); 396162306a36Sopenharmony_ci instance->instancet->adp_reset(instance, instance->reg_set); 396262306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 0); 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "FW restarted successfully," 396562306a36Sopenharmony_ci "initiating next stage...\n"); 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "HBA recovery state machine," 396862306a36Sopenharmony_ci "state 2 starting...\n"); 396962306a36Sopenharmony_ci 397062306a36Sopenharmony_ci /* waiting for about 20 second before start the second init */ 397162306a36Sopenharmony_ci for (wait = 0; wait < 30; wait++) { 397262306a36Sopenharmony_ci msleep(1000); 397362306a36Sopenharmony_ci } 397462306a36Sopenharmony_ci 397562306a36Sopenharmony_ci if (megasas_transition_to_ready(instance, 1)) { 397662306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "adapter not ready\n"); 397762306a36Sopenharmony_ci 397862306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 1); 397962306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 398062306a36Sopenharmony_ci return ; 398162306a36Sopenharmony_ci } 398262306a36Sopenharmony_ci 398362306a36Sopenharmony_ci if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || 398462306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) || 398562306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR) 398662306a36Sopenharmony_ci ) { 398762306a36Sopenharmony_ci *instance->consumer = *instance->producer; 398862306a36Sopenharmony_ci } else { 398962306a36Sopenharmony_ci *instance->consumer = 0; 399062306a36Sopenharmony_ci *instance->producer = 0; 399162306a36Sopenharmony_ci } 399262306a36Sopenharmony_ci 399362306a36Sopenharmony_ci megasas_issue_init_mfi(instance); 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 399662306a36Sopenharmony_ci atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL); 399762306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 399862306a36Sopenharmony_ci instance->instancet->enable_intr(instance); 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci megasas_issue_pending_cmds_again(instance); 400162306a36Sopenharmony_ci instance->issuepend_done = 1; 400262306a36Sopenharmony_ci } 400362306a36Sopenharmony_ci} 400462306a36Sopenharmony_ci 400562306a36Sopenharmony_ci/** 400662306a36Sopenharmony_ci * megasas_deplete_reply_queue - Processes all completed commands 400762306a36Sopenharmony_ci * @instance: Adapter soft state 400862306a36Sopenharmony_ci * @alt_status: Alternate status to be returned to 400962306a36Sopenharmony_ci * SCSI mid-layer instead of the status 401062306a36Sopenharmony_ci * returned by the FW 401162306a36Sopenharmony_ci * Note: this must be called with hba lock held 401262306a36Sopenharmony_ci */ 401362306a36Sopenharmony_cistatic int 401462306a36Sopenharmony_cimegasas_deplete_reply_queue(struct megasas_instance *instance, 401562306a36Sopenharmony_ci u8 alt_status) 401662306a36Sopenharmony_ci{ 401762306a36Sopenharmony_ci u32 mfiStatus; 401862306a36Sopenharmony_ci u32 fw_state; 401962306a36Sopenharmony_ci 402062306a36Sopenharmony_ci if (instance->instancet->check_reset(instance, instance->reg_set) == 1) 402162306a36Sopenharmony_ci return IRQ_HANDLED; 402262306a36Sopenharmony_ci 402362306a36Sopenharmony_ci mfiStatus = instance->instancet->clear_intr(instance); 402462306a36Sopenharmony_ci if (mfiStatus == 0) { 402562306a36Sopenharmony_ci /* Hardware may not set outbound_intr_status in MSI-X mode */ 402662306a36Sopenharmony_ci if (!instance->msix_vectors) 402762306a36Sopenharmony_ci return IRQ_NONE; 402862306a36Sopenharmony_ci } 402962306a36Sopenharmony_ci 403062306a36Sopenharmony_ci instance->mfiStatus = mfiStatus; 403162306a36Sopenharmony_ci 403262306a36Sopenharmony_ci if ((mfiStatus & MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE)) { 403362306a36Sopenharmony_ci fw_state = instance->instancet->read_fw_status_reg( 403462306a36Sopenharmony_ci instance) & MFI_STATE_MASK; 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_ci if (fw_state != MFI_STATE_FAULT) { 403762306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "fw state:%x\n", 403862306a36Sopenharmony_ci fw_state); 403962306a36Sopenharmony_ci } 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_ci if ((fw_state == MFI_STATE_FAULT) && 404262306a36Sopenharmony_ci (instance->disableOnlineCtrlReset == 0)) { 404362306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "wait adp restart\n"); 404462306a36Sopenharmony_ci 404562306a36Sopenharmony_ci if ((instance->pdev->device == 404662306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS1064R) || 404762306a36Sopenharmony_ci (instance->pdev->device == 404862306a36Sopenharmony_ci PCI_DEVICE_ID_DELL_PERC5) || 404962306a36Sopenharmony_ci (instance->pdev->device == 405062306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_VERDE_ZCR)) { 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci *instance->consumer = 405362306a36Sopenharmony_ci cpu_to_le32(MEGASAS_ADPRESET_INPROG_SIGN); 405462306a36Sopenharmony_ci } 405562306a36Sopenharmony_ci 405662306a36Sopenharmony_ci 405762306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 405862306a36Sopenharmony_ci atomic_set(&instance->adprecovery, MEGASAS_ADPRESET_SM_INFAULT); 405962306a36Sopenharmony_ci instance->issuepend_done = 0; 406062306a36Sopenharmony_ci 406162306a36Sopenharmony_ci atomic_set(&instance->fw_outstanding, 0); 406262306a36Sopenharmony_ci megasas_internal_reset_defer_cmds(instance); 406362306a36Sopenharmony_ci 406462306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "fwState=%x, stage:%d\n", 406562306a36Sopenharmony_ci fw_state, atomic_read(&instance->adprecovery)); 406662306a36Sopenharmony_ci 406762306a36Sopenharmony_ci schedule_work(&instance->work_init); 406862306a36Sopenharmony_ci return IRQ_HANDLED; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci } else { 407162306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "fwstate:%x, dis_OCR=%x\n", 407262306a36Sopenharmony_ci fw_state, instance->disableOnlineCtrlReset); 407362306a36Sopenharmony_ci } 407462306a36Sopenharmony_ci } 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci tasklet_schedule(&instance->isr_tasklet); 407762306a36Sopenharmony_ci return IRQ_HANDLED; 407862306a36Sopenharmony_ci} 407962306a36Sopenharmony_ci 408062306a36Sopenharmony_ci/** 408162306a36Sopenharmony_ci * megasas_isr - isr entry point 408262306a36Sopenharmony_ci * @irq: IRQ number 408362306a36Sopenharmony_ci * @devp: IRQ context address 408462306a36Sopenharmony_ci */ 408562306a36Sopenharmony_cistatic irqreturn_t megasas_isr(int irq, void *devp) 408662306a36Sopenharmony_ci{ 408762306a36Sopenharmony_ci struct megasas_irq_context *irq_context = devp; 408862306a36Sopenharmony_ci struct megasas_instance *instance = irq_context->instance; 408962306a36Sopenharmony_ci unsigned long flags; 409062306a36Sopenharmony_ci irqreturn_t rc; 409162306a36Sopenharmony_ci 409262306a36Sopenharmony_ci if (atomic_read(&instance->fw_reset_no_pci_access)) 409362306a36Sopenharmony_ci return IRQ_HANDLED; 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_ci spin_lock_irqsave(&instance->hba_lock, flags); 409662306a36Sopenharmony_ci rc = megasas_deplete_reply_queue(instance, DID_OK); 409762306a36Sopenharmony_ci spin_unlock_irqrestore(&instance->hba_lock, flags); 409862306a36Sopenharmony_ci 409962306a36Sopenharmony_ci return rc; 410062306a36Sopenharmony_ci} 410162306a36Sopenharmony_ci 410262306a36Sopenharmony_ci/** 410362306a36Sopenharmony_ci * megasas_transition_to_ready - Move the FW to READY state 410462306a36Sopenharmony_ci * @instance: Adapter soft state 410562306a36Sopenharmony_ci * @ocr: Adapter reset state 410662306a36Sopenharmony_ci * 410762306a36Sopenharmony_ci * During the initialization, FW passes can potentially be in any one of 410862306a36Sopenharmony_ci * several possible states. If the FW in operational, waiting-for-handshake 410962306a36Sopenharmony_ci * states, driver must take steps to bring it to ready state. Otherwise, it 411062306a36Sopenharmony_ci * has to wait for the ready state. 411162306a36Sopenharmony_ci */ 411262306a36Sopenharmony_ciint 411362306a36Sopenharmony_cimegasas_transition_to_ready(struct megasas_instance *instance, int ocr) 411462306a36Sopenharmony_ci{ 411562306a36Sopenharmony_ci int i; 411662306a36Sopenharmony_ci u8 max_wait; 411762306a36Sopenharmony_ci u32 fw_state; 411862306a36Sopenharmony_ci u32 abs_state, curr_abs_state; 411962306a36Sopenharmony_ci 412062306a36Sopenharmony_ci abs_state = instance->instancet->read_fw_status_reg(instance); 412162306a36Sopenharmony_ci fw_state = abs_state & MFI_STATE_MASK; 412262306a36Sopenharmony_ci 412362306a36Sopenharmony_ci if (fw_state != MFI_STATE_READY) 412462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Waiting for FW to come to ready" 412562306a36Sopenharmony_ci " state\n"); 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_ci while (fw_state != MFI_STATE_READY) { 412862306a36Sopenharmony_ci 412962306a36Sopenharmony_ci switch (fw_state) { 413062306a36Sopenharmony_ci 413162306a36Sopenharmony_ci case MFI_STATE_FAULT: 413262306a36Sopenharmony_ci dev_printk(KERN_ERR, &instance->pdev->dev, 413362306a36Sopenharmony_ci "FW in FAULT state, Fault code:0x%x subcode:0x%x func:%s\n", 413462306a36Sopenharmony_ci abs_state & MFI_STATE_FAULT_CODE, 413562306a36Sopenharmony_ci abs_state & MFI_STATE_FAULT_SUBCODE, __func__); 413662306a36Sopenharmony_ci if (ocr) { 413762306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 413862306a36Sopenharmony_ci break; 413962306a36Sopenharmony_ci } else { 414062306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n"); 414162306a36Sopenharmony_ci megasas_dump_reg_set(instance->reg_set); 414262306a36Sopenharmony_ci return -ENODEV; 414362306a36Sopenharmony_ci } 414462306a36Sopenharmony_ci 414562306a36Sopenharmony_ci case MFI_STATE_WAIT_HANDSHAKE: 414662306a36Sopenharmony_ci /* 414762306a36Sopenharmony_ci * Set the CLR bit in inbound doorbell 414862306a36Sopenharmony_ci */ 414962306a36Sopenharmony_ci if ((instance->pdev->device == 415062306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0073SKINNY) || 415162306a36Sopenharmony_ci (instance->pdev->device == 415262306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0071SKINNY) || 415362306a36Sopenharmony_ci (instance->adapter_type != MFI_SERIES)) 415462306a36Sopenharmony_ci writel( 415562306a36Sopenharmony_ci MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, 415662306a36Sopenharmony_ci &instance->reg_set->doorbell); 415762306a36Sopenharmony_ci else 415862306a36Sopenharmony_ci writel( 415962306a36Sopenharmony_ci MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, 416062306a36Sopenharmony_ci &instance->reg_set->inbound_doorbell); 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 416362306a36Sopenharmony_ci break; 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_ci case MFI_STATE_BOOT_MESSAGE_PENDING: 416662306a36Sopenharmony_ci if ((instance->pdev->device == 416762306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0073SKINNY) || 416862306a36Sopenharmony_ci (instance->pdev->device == 416962306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0071SKINNY) || 417062306a36Sopenharmony_ci (instance->adapter_type != MFI_SERIES)) 417162306a36Sopenharmony_ci writel(MFI_INIT_HOTPLUG, 417262306a36Sopenharmony_ci &instance->reg_set->doorbell); 417362306a36Sopenharmony_ci else 417462306a36Sopenharmony_ci writel(MFI_INIT_HOTPLUG, 417562306a36Sopenharmony_ci &instance->reg_set->inbound_doorbell); 417662306a36Sopenharmony_ci 417762306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 417862306a36Sopenharmony_ci break; 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_ci case MFI_STATE_OPERATIONAL: 418162306a36Sopenharmony_ci /* 418262306a36Sopenharmony_ci * Bring it to READY state; assuming max wait 10 secs 418362306a36Sopenharmony_ci */ 418462306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 418562306a36Sopenharmony_ci if ((instance->pdev->device == 418662306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0073SKINNY) || 418762306a36Sopenharmony_ci (instance->pdev->device == 418862306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0071SKINNY) || 418962306a36Sopenharmony_ci (instance->adapter_type != MFI_SERIES)) { 419062306a36Sopenharmony_ci writel(MFI_RESET_FLAGS, 419162306a36Sopenharmony_ci &instance->reg_set->doorbell); 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) { 419462306a36Sopenharmony_ci for (i = 0; i < (10 * 1000); i += 20) { 419562306a36Sopenharmony_ci if (megasas_readl( 419662306a36Sopenharmony_ci instance, 419762306a36Sopenharmony_ci &instance-> 419862306a36Sopenharmony_ci reg_set-> 419962306a36Sopenharmony_ci doorbell) & 1) 420062306a36Sopenharmony_ci msleep(20); 420162306a36Sopenharmony_ci else 420262306a36Sopenharmony_ci break; 420362306a36Sopenharmony_ci } 420462306a36Sopenharmony_ci } 420562306a36Sopenharmony_ci } else 420662306a36Sopenharmony_ci writel(MFI_RESET_FLAGS, 420762306a36Sopenharmony_ci &instance->reg_set->inbound_doorbell); 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 421062306a36Sopenharmony_ci break; 421162306a36Sopenharmony_ci 421262306a36Sopenharmony_ci case MFI_STATE_UNDEFINED: 421362306a36Sopenharmony_ci /* 421462306a36Sopenharmony_ci * This state should not last for more than 2 seconds 421562306a36Sopenharmony_ci */ 421662306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 421762306a36Sopenharmony_ci break; 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci case MFI_STATE_BB_INIT: 422062306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 422162306a36Sopenharmony_ci break; 422262306a36Sopenharmony_ci 422362306a36Sopenharmony_ci case MFI_STATE_FW_INIT: 422462306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 422562306a36Sopenharmony_ci break; 422662306a36Sopenharmony_ci 422762306a36Sopenharmony_ci case MFI_STATE_FW_INIT_2: 422862306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 422962306a36Sopenharmony_ci break; 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci case MFI_STATE_DEVICE_SCAN: 423262306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 423362306a36Sopenharmony_ci break; 423462306a36Sopenharmony_ci 423562306a36Sopenharmony_ci case MFI_STATE_FLUSH_CACHE: 423662306a36Sopenharmony_ci max_wait = MEGASAS_RESET_WAIT_TIME; 423762306a36Sopenharmony_ci break; 423862306a36Sopenharmony_ci 423962306a36Sopenharmony_ci default: 424062306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Unknown state 0x%x\n", 424162306a36Sopenharmony_ci fw_state); 424262306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n"); 424362306a36Sopenharmony_ci megasas_dump_reg_set(instance->reg_set); 424462306a36Sopenharmony_ci return -ENODEV; 424562306a36Sopenharmony_ci } 424662306a36Sopenharmony_ci 424762306a36Sopenharmony_ci /* 424862306a36Sopenharmony_ci * The cur_state should not last for more than max_wait secs 424962306a36Sopenharmony_ci */ 425062306a36Sopenharmony_ci for (i = 0; i < max_wait * 50; i++) { 425162306a36Sopenharmony_ci curr_abs_state = instance->instancet-> 425262306a36Sopenharmony_ci read_fw_status_reg(instance); 425362306a36Sopenharmony_ci 425462306a36Sopenharmony_ci if (abs_state == curr_abs_state) { 425562306a36Sopenharmony_ci msleep(20); 425662306a36Sopenharmony_ci } else 425762306a36Sopenharmony_ci break; 425862306a36Sopenharmony_ci } 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_ci /* 426162306a36Sopenharmony_ci * Return error if fw_state hasn't changed after max_wait 426262306a36Sopenharmony_ci */ 426362306a36Sopenharmony_ci if (curr_abs_state == abs_state) { 426462306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "FW state [%d] hasn't changed " 426562306a36Sopenharmony_ci "in %d secs\n", fw_state, max_wait); 426662306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "System Register set:\n"); 426762306a36Sopenharmony_ci megasas_dump_reg_set(instance->reg_set); 426862306a36Sopenharmony_ci return -ENODEV; 426962306a36Sopenharmony_ci } 427062306a36Sopenharmony_ci 427162306a36Sopenharmony_ci abs_state = curr_abs_state; 427262306a36Sopenharmony_ci fw_state = curr_abs_state & MFI_STATE_MASK; 427362306a36Sopenharmony_ci } 427462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "FW now in Ready state\n"); 427562306a36Sopenharmony_ci 427662306a36Sopenharmony_ci return 0; 427762306a36Sopenharmony_ci} 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci/** 428062306a36Sopenharmony_ci * megasas_teardown_frame_pool - Destroy the cmd frame DMA pool 428162306a36Sopenharmony_ci * @instance: Adapter soft state 428262306a36Sopenharmony_ci */ 428362306a36Sopenharmony_cistatic void megasas_teardown_frame_pool(struct megasas_instance *instance) 428462306a36Sopenharmony_ci{ 428562306a36Sopenharmony_ci int i; 428662306a36Sopenharmony_ci u16 max_cmd = instance->max_mfi_cmds; 428762306a36Sopenharmony_ci struct megasas_cmd *cmd; 428862306a36Sopenharmony_ci 428962306a36Sopenharmony_ci if (!instance->frame_dma_pool) 429062306a36Sopenharmony_ci return; 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci /* 429362306a36Sopenharmony_ci * Return all frames to pool 429462306a36Sopenharmony_ci */ 429562306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 429662306a36Sopenharmony_ci 429762306a36Sopenharmony_ci cmd = instance->cmd_list[i]; 429862306a36Sopenharmony_ci 429962306a36Sopenharmony_ci if (cmd->frame) 430062306a36Sopenharmony_ci dma_pool_free(instance->frame_dma_pool, cmd->frame, 430162306a36Sopenharmony_ci cmd->frame_phys_addr); 430262306a36Sopenharmony_ci 430362306a36Sopenharmony_ci if (cmd->sense) 430462306a36Sopenharmony_ci dma_pool_free(instance->sense_dma_pool, cmd->sense, 430562306a36Sopenharmony_ci cmd->sense_phys_addr); 430662306a36Sopenharmony_ci } 430762306a36Sopenharmony_ci 430862306a36Sopenharmony_ci /* 430962306a36Sopenharmony_ci * Now destroy the pool itself 431062306a36Sopenharmony_ci */ 431162306a36Sopenharmony_ci dma_pool_destroy(instance->frame_dma_pool); 431262306a36Sopenharmony_ci dma_pool_destroy(instance->sense_dma_pool); 431362306a36Sopenharmony_ci 431462306a36Sopenharmony_ci instance->frame_dma_pool = NULL; 431562306a36Sopenharmony_ci instance->sense_dma_pool = NULL; 431662306a36Sopenharmony_ci} 431762306a36Sopenharmony_ci 431862306a36Sopenharmony_ci/** 431962306a36Sopenharmony_ci * megasas_create_frame_pool - Creates DMA pool for cmd frames 432062306a36Sopenharmony_ci * @instance: Adapter soft state 432162306a36Sopenharmony_ci * 432262306a36Sopenharmony_ci * Each command packet has an embedded DMA memory buffer that is used for 432362306a36Sopenharmony_ci * filling MFI frame and the SG list that immediately follows the frame. This 432462306a36Sopenharmony_ci * function creates those DMA memory buffers for each command packet by using 432562306a36Sopenharmony_ci * PCI pool facility. 432662306a36Sopenharmony_ci */ 432762306a36Sopenharmony_cistatic int megasas_create_frame_pool(struct megasas_instance *instance) 432862306a36Sopenharmony_ci{ 432962306a36Sopenharmony_ci int i; 433062306a36Sopenharmony_ci u16 max_cmd; 433162306a36Sopenharmony_ci u32 frame_count; 433262306a36Sopenharmony_ci struct megasas_cmd *cmd; 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci max_cmd = instance->max_mfi_cmds; 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci /* 433762306a36Sopenharmony_ci * For MFI controllers. 433862306a36Sopenharmony_ci * max_num_sge = 60 433962306a36Sopenharmony_ci * max_sge_sz = 16 byte (sizeof megasas_sge_skinny) 434062306a36Sopenharmony_ci * Total 960 byte (15 MFI frame of 64 byte) 434162306a36Sopenharmony_ci * 434262306a36Sopenharmony_ci * Fusion adapter require only 3 extra frame. 434362306a36Sopenharmony_ci * max_num_sge = 16 (defined as MAX_IOCTL_SGE) 434462306a36Sopenharmony_ci * max_sge_sz = 12 byte (sizeof megasas_sge64) 434562306a36Sopenharmony_ci * Total 192 byte (3 MFI frame of 64 byte) 434662306a36Sopenharmony_ci */ 434762306a36Sopenharmony_ci frame_count = (instance->adapter_type == MFI_SERIES) ? 434862306a36Sopenharmony_ci (15 + 1) : (3 + 1); 434962306a36Sopenharmony_ci instance->mfi_frame_size = MEGAMFI_FRAME_SIZE * frame_count; 435062306a36Sopenharmony_ci /* 435162306a36Sopenharmony_ci * Use DMA pool facility provided by PCI layer 435262306a36Sopenharmony_ci */ 435362306a36Sopenharmony_ci instance->frame_dma_pool = dma_pool_create("megasas frame pool", 435462306a36Sopenharmony_ci &instance->pdev->dev, 435562306a36Sopenharmony_ci instance->mfi_frame_size, 256, 0); 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci if (!instance->frame_dma_pool) { 435862306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup frame pool\n"); 435962306a36Sopenharmony_ci return -ENOMEM; 436062306a36Sopenharmony_ci } 436162306a36Sopenharmony_ci 436262306a36Sopenharmony_ci instance->sense_dma_pool = dma_pool_create("megasas sense pool", 436362306a36Sopenharmony_ci &instance->pdev->dev, 128, 436462306a36Sopenharmony_ci 4, 0); 436562306a36Sopenharmony_ci 436662306a36Sopenharmony_ci if (!instance->sense_dma_pool) { 436762306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "failed to setup sense pool\n"); 436862306a36Sopenharmony_ci 436962306a36Sopenharmony_ci dma_pool_destroy(instance->frame_dma_pool); 437062306a36Sopenharmony_ci instance->frame_dma_pool = NULL; 437162306a36Sopenharmony_ci 437262306a36Sopenharmony_ci return -ENOMEM; 437362306a36Sopenharmony_ci } 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci /* 437662306a36Sopenharmony_ci * Allocate and attach a frame to each of the commands in cmd_list. 437762306a36Sopenharmony_ci * By making cmd->index as the context instead of the &cmd, we can 437862306a36Sopenharmony_ci * always use 32bit context regardless of the architecture 437962306a36Sopenharmony_ci */ 438062306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 438162306a36Sopenharmony_ci 438262306a36Sopenharmony_ci cmd = instance->cmd_list[i]; 438362306a36Sopenharmony_ci 438462306a36Sopenharmony_ci cmd->frame = dma_pool_zalloc(instance->frame_dma_pool, 438562306a36Sopenharmony_ci GFP_KERNEL, &cmd->frame_phys_addr); 438662306a36Sopenharmony_ci 438762306a36Sopenharmony_ci cmd->sense = dma_pool_alloc(instance->sense_dma_pool, 438862306a36Sopenharmony_ci GFP_KERNEL, &cmd->sense_phys_addr); 438962306a36Sopenharmony_ci 439062306a36Sopenharmony_ci /* 439162306a36Sopenharmony_ci * megasas_teardown_frame_pool() takes care of freeing 439262306a36Sopenharmony_ci * whatever has been allocated 439362306a36Sopenharmony_ci */ 439462306a36Sopenharmony_ci if (!cmd->frame || !cmd->sense) { 439562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "dma_pool_alloc failed\n"); 439662306a36Sopenharmony_ci megasas_teardown_frame_pool(instance); 439762306a36Sopenharmony_ci return -ENOMEM; 439862306a36Sopenharmony_ci } 439962306a36Sopenharmony_ci 440062306a36Sopenharmony_ci cmd->frame->io.context = cpu_to_le32(cmd->index); 440162306a36Sopenharmony_ci cmd->frame->io.pad_0 = 0; 440262306a36Sopenharmony_ci if ((instance->adapter_type == MFI_SERIES) && reset_devices) 440362306a36Sopenharmony_ci cmd->frame->hdr.cmd = MFI_CMD_INVALID; 440462306a36Sopenharmony_ci } 440562306a36Sopenharmony_ci 440662306a36Sopenharmony_ci return 0; 440762306a36Sopenharmony_ci} 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci/** 441062306a36Sopenharmony_ci * megasas_free_cmds - Free all the cmds in the free cmd pool 441162306a36Sopenharmony_ci * @instance: Adapter soft state 441262306a36Sopenharmony_ci */ 441362306a36Sopenharmony_civoid megasas_free_cmds(struct megasas_instance *instance) 441462306a36Sopenharmony_ci{ 441562306a36Sopenharmony_ci int i; 441662306a36Sopenharmony_ci 441762306a36Sopenharmony_ci /* First free the MFI frame pool */ 441862306a36Sopenharmony_ci megasas_teardown_frame_pool(instance); 441962306a36Sopenharmony_ci 442062306a36Sopenharmony_ci /* Free all the commands in the cmd_list */ 442162306a36Sopenharmony_ci for (i = 0; i < instance->max_mfi_cmds; i++) 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_ci kfree(instance->cmd_list[i]); 442462306a36Sopenharmony_ci 442562306a36Sopenharmony_ci /* Free the cmd_list buffer itself */ 442662306a36Sopenharmony_ci kfree(instance->cmd_list); 442762306a36Sopenharmony_ci instance->cmd_list = NULL; 442862306a36Sopenharmony_ci 442962306a36Sopenharmony_ci INIT_LIST_HEAD(&instance->cmd_pool); 443062306a36Sopenharmony_ci} 443162306a36Sopenharmony_ci 443262306a36Sopenharmony_ci/** 443362306a36Sopenharmony_ci * megasas_alloc_cmds - Allocates the command packets 443462306a36Sopenharmony_ci * @instance: Adapter soft state 443562306a36Sopenharmony_ci * 443662306a36Sopenharmony_ci * Each command that is issued to the FW, whether IO commands from the OS or 443762306a36Sopenharmony_ci * internal commands like IOCTLs, are wrapped in local data structure called 443862306a36Sopenharmony_ci * megasas_cmd. The frame embedded in this megasas_cmd is actually issued to 443962306a36Sopenharmony_ci * the FW. 444062306a36Sopenharmony_ci * 444162306a36Sopenharmony_ci * Each frame has a 32-bit field called context (tag). This context is used 444262306a36Sopenharmony_ci * to get back the megasas_cmd from the frame when a frame gets completed in 444362306a36Sopenharmony_ci * the ISR. Typically the address of the megasas_cmd itself would be used as 444462306a36Sopenharmony_ci * the context. But we wanted to keep the differences between 32 and 64 bit 444562306a36Sopenharmony_ci * systems to the mininum. We always use 32 bit integers for the context. In 444662306a36Sopenharmony_ci * this driver, the 32 bit values are the indices into an array cmd_list. 444762306a36Sopenharmony_ci * This array is used only to look up the megasas_cmd given the context. The 444862306a36Sopenharmony_ci * free commands themselves are maintained in a linked list called cmd_pool. 444962306a36Sopenharmony_ci */ 445062306a36Sopenharmony_ciint megasas_alloc_cmds(struct megasas_instance *instance) 445162306a36Sopenharmony_ci{ 445262306a36Sopenharmony_ci int i; 445362306a36Sopenharmony_ci int j; 445462306a36Sopenharmony_ci u16 max_cmd; 445562306a36Sopenharmony_ci struct megasas_cmd *cmd; 445662306a36Sopenharmony_ci 445762306a36Sopenharmony_ci max_cmd = instance->max_mfi_cmds; 445862306a36Sopenharmony_ci 445962306a36Sopenharmony_ci /* 446062306a36Sopenharmony_ci * instance->cmd_list is an array of struct megasas_cmd pointers. 446162306a36Sopenharmony_ci * Allocate the dynamic array first and then allocate individual 446262306a36Sopenharmony_ci * commands. 446362306a36Sopenharmony_ci */ 446462306a36Sopenharmony_ci instance->cmd_list = kcalloc(max_cmd, sizeof(struct megasas_cmd*), GFP_KERNEL); 446562306a36Sopenharmony_ci 446662306a36Sopenharmony_ci if (!instance->cmd_list) { 446762306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "out of memory\n"); 446862306a36Sopenharmony_ci return -ENOMEM; 446962306a36Sopenharmony_ci } 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 447262306a36Sopenharmony_ci instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd), 447362306a36Sopenharmony_ci GFP_KERNEL); 447462306a36Sopenharmony_ci 447562306a36Sopenharmony_ci if (!instance->cmd_list[i]) { 447662306a36Sopenharmony_ci 447762306a36Sopenharmony_ci for (j = 0; j < i; j++) 447862306a36Sopenharmony_ci kfree(instance->cmd_list[j]); 447962306a36Sopenharmony_ci 448062306a36Sopenharmony_ci kfree(instance->cmd_list); 448162306a36Sopenharmony_ci instance->cmd_list = NULL; 448262306a36Sopenharmony_ci 448362306a36Sopenharmony_ci return -ENOMEM; 448462306a36Sopenharmony_ci } 448562306a36Sopenharmony_ci } 448662306a36Sopenharmony_ci 448762306a36Sopenharmony_ci for (i = 0; i < max_cmd; i++) { 448862306a36Sopenharmony_ci cmd = instance->cmd_list[i]; 448962306a36Sopenharmony_ci memset(cmd, 0, sizeof(struct megasas_cmd)); 449062306a36Sopenharmony_ci cmd->index = i; 449162306a36Sopenharmony_ci cmd->scmd = NULL; 449262306a36Sopenharmony_ci cmd->instance = instance; 449362306a36Sopenharmony_ci 449462306a36Sopenharmony_ci list_add_tail(&cmd->list, &instance->cmd_pool); 449562306a36Sopenharmony_ci } 449662306a36Sopenharmony_ci 449762306a36Sopenharmony_ci /* 449862306a36Sopenharmony_ci * Create a frame pool and assign one frame to each cmd 449962306a36Sopenharmony_ci */ 450062306a36Sopenharmony_ci if (megasas_create_frame_pool(instance)) { 450162306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error creating frame DMA pool\n"); 450262306a36Sopenharmony_ci megasas_free_cmds(instance); 450362306a36Sopenharmony_ci return -ENOMEM; 450462306a36Sopenharmony_ci } 450562306a36Sopenharmony_ci 450662306a36Sopenharmony_ci return 0; 450762306a36Sopenharmony_ci} 450862306a36Sopenharmony_ci 450962306a36Sopenharmony_ci/* 451062306a36Sopenharmony_ci * dcmd_timeout_ocr_possible - Check if OCR is possible based on Driver/FW state. 451162306a36Sopenharmony_ci * @instance: Adapter soft state 451262306a36Sopenharmony_ci * 451362306a36Sopenharmony_ci * Return 0 for only Fusion adapter, if driver load/unload is not in progress 451462306a36Sopenharmony_ci * or FW is not under OCR. 451562306a36Sopenharmony_ci */ 451662306a36Sopenharmony_ciinline int 451762306a36Sopenharmony_cidcmd_timeout_ocr_possible(struct megasas_instance *instance) { 451862306a36Sopenharmony_ci 451962306a36Sopenharmony_ci if (instance->adapter_type == MFI_SERIES) 452062306a36Sopenharmony_ci return KILL_ADAPTER; 452162306a36Sopenharmony_ci else if (instance->unload || 452262306a36Sopenharmony_ci test_bit(MEGASAS_FUSION_OCR_NOT_POSSIBLE, 452362306a36Sopenharmony_ci &instance->reset_flags)) 452462306a36Sopenharmony_ci return IGNORE_TIMEOUT; 452562306a36Sopenharmony_ci else 452662306a36Sopenharmony_ci return INITIATE_OCR; 452762306a36Sopenharmony_ci} 452862306a36Sopenharmony_ci 452962306a36Sopenharmony_cistatic void 453062306a36Sopenharmony_cimegasas_get_pd_info(struct megasas_instance *instance, struct scsi_device *sdev) 453162306a36Sopenharmony_ci{ 453262306a36Sopenharmony_ci int ret; 453362306a36Sopenharmony_ci struct megasas_cmd *cmd; 453462306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 453562306a36Sopenharmony_ci 453662306a36Sopenharmony_ci struct MR_PRIV_DEVICE *mr_device_priv_data; 453762306a36Sopenharmony_ci u16 device_id = 0; 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_ci device_id = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; 454062306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 454162306a36Sopenharmony_ci 454262306a36Sopenharmony_ci if (!cmd) { 454362306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed to get cmd %s\n", __func__); 454462306a36Sopenharmony_ci return; 454562306a36Sopenharmony_ci } 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 454862306a36Sopenharmony_ci 454962306a36Sopenharmony_ci memset(instance->pd_info, 0, sizeof(*instance->pd_info)); 455062306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 455162306a36Sopenharmony_ci 455262306a36Sopenharmony_ci dcmd->mbox.s[0] = cpu_to_le16(device_id); 455362306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 455462306a36Sopenharmony_ci dcmd->cmd_status = 0xFF; 455562306a36Sopenharmony_ci dcmd->sge_count = 1; 455662306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 455762306a36Sopenharmony_ci dcmd->timeout = 0; 455862306a36Sopenharmony_ci dcmd->pad_0 = 0; 455962306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_PD_INFO)); 456062306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_PD_GET_INFO); 456162306a36Sopenharmony_ci 456262306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, instance->pd_info_h, 456362306a36Sopenharmony_ci sizeof(struct MR_PD_INFO)); 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 456662306a36Sopenharmony_ci !instance->mask_interrupts) 456762306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS); 456862306a36Sopenharmony_ci else 456962306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 457062306a36Sopenharmony_ci 457162306a36Sopenharmony_ci switch (ret) { 457262306a36Sopenharmony_ci case DCMD_SUCCESS: 457362306a36Sopenharmony_ci mr_device_priv_data = sdev->hostdata; 457462306a36Sopenharmony_ci le16_to_cpus((u16 *)&instance->pd_info->state.ddf.pdType); 457562306a36Sopenharmony_ci mr_device_priv_data->interface_type = 457662306a36Sopenharmony_ci instance->pd_info->state.ddf.pdType.intf; 457762306a36Sopenharmony_ci break; 457862306a36Sopenharmony_ci 457962306a36Sopenharmony_ci case DCMD_TIMEOUT: 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 458262306a36Sopenharmony_ci case INITIATE_OCR: 458362306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 458462306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 458562306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 458662306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 458762306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 458862306a36Sopenharmony_ci break; 458962306a36Sopenharmony_ci case KILL_ADAPTER: 459062306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 459162306a36Sopenharmony_ci break; 459262306a36Sopenharmony_ci case IGNORE_TIMEOUT: 459362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 459462306a36Sopenharmony_ci __func__, __LINE__); 459562306a36Sopenharmony_ci break; 459662306a36Sopenharmony_ci } 459762306a36Sopenharmony_ci 459862306a36Sopenharmony_ci break; 459962306a36Sopenharmony_ci } 460062306a36Sopenharmony_ci 460162306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 460262306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 460362306a36Sopenharmony_ci 460462306a36Sopenharmony_ci return; 460562306a36Sopenharmony_ci} 460662306a36Sopenharmony_ci/* 460762306a36Sopenharmony_ci * megasas_get_pd_list_info - Returns FW's pd_list structure 460862306a36Sopenharmony_ci * @instance: Adapter soft state 460962306a36Sopenharmony_ci * @pd_list: pd_list structure 461062306a36Sopenharmony_ci * 461162306a36Sopenharmony_ci * Issues an internal command (DCMD) to get the FW's controller PD 461262306a36Sopenharmony_ci * list structure. This information is mainly used to find out SYSTEM 461362306a36Sopenharmony_ci * supported by the FW. 461462306a36Sopenharmony_ci */ 461562306a36Sopenharmony_cistatic int 461662306a36Sopenharmony_cimegasas_get_pd_list(struct megasas_instance *instance) 461762306a36Sopenharmony_ci{ 461862306a36Sopenharmony_ci int ret = 0, pd_index = 0; 461962306a36Sopenharmony_ci struct megasas_cmd *cmd; 462062306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 462162306a36Sopenharmony_ci struct MR_PD_LIST *ci; 462262306a36Sopenharmony_ci struct MR_PD_ADDRESS *pd_addr; 462362306a36Sopenharmony_ci 462462306a36Sopenharmony_ci if (instance->pd_list_not_supported) { 462562306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "MR_DCMD_PD_LIST_QUERY " 462662306a36Sopenharmony_ci "not supported by firmware\n"); 462762306a36Sopenharmony_ci return ret; 462862306a36Sopenharmony_ci } 462962306a36Sopenharmony_ci 463062306a36Sopenharmony_ci ci = instance->pd_list_buf; 463162306a36Sopenharmony_ci 463262306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 463362306a36Sopenharmony_ci 463462306a36Sopenharmony_ci if (!cmd) { 463562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "(get_pd_list): Failed to get cmd\n"); 463662306a36Sopenharmony_ci return -ENOMEM; 463762306a36Sopenharmony_ci } 463862306a36Sopenharmony_ci 463962306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 464262306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_ci dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST; 464562306a36Sopenharmony_ci dcmd->mbox.b[1] = 0; 464662306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 464762306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 464862306a36Sopenharmony_ci dcmd->sge_count = 1; 464962306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 465062306a36Sopenharmony_ci dcmd->timeout = 0; 465162306a36Sopenharmony_ci dcmd->pad_0 = 0; 465262306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST)); 465362306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_PD_LIST_QUERY); 465462306a36Sopenharmony_ci 465562306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, instance->pd_list_buf_h, 465662306a36Sopenharmony_ci (MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST))); 465762306a36Sopenharmony_ci 465862306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 465962306a36Sopenharmony_ci !instance->mask_interrupts) 466062306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, 466162306a36Sopenharmony_ci MFI_IO_TIMEOUT_SECS); 466262306a36Sopenharmony_ci else 466362306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 466462306a36Sopenharmony_ci 466562306a36Sopenharmony_ci switch (ret) { 466662306a36Sopenharmony_ci case DCMD_FAILED: 466762306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "MR_DCMD_PD_LIST_QUERY " 466862306a36Sopenharmony_ci "failed/not supported by firmware\n"); 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 467162306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 467262306a36Sopenharmony_ci else 467362306a36Sopenharmony_ci instance->pd_list_not_supported = 1; 467462306a36Sopenharmony_ci break; 467562306a36Sopenharmony_ci case DCMD_TIMEOUT: 467662306a36Sopenharmony_ci 467762306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 467862306a36Sopenharmony_ci case INITIATE_OCR: 467962306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 468062306a36Sopenharmony_ci /* 468162306a36Sopenharmony_ci * DCMD failed from AEN path. 468262306a36Sopenharmony_ci * AEN path already hold reset_mutex to avoid PCI access 468362306a36Sopenharmony_ci * while OCR is in progress. 468462306a36Sopenharmony_ci */ 468562306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 468662306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 468762306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 468862306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 468962306a36Sopenharmony_ci break; 469062306a36Sopenharmony_ci case KILL_ADAPTER: 469162306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 469262306a36Sopenharmony_ci break; 469362306a36Sopenharmony_ci case IGNORE_TIMEOUT: 469462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d \n", 469562306a36Sopenharmony_ci __func__, __LINE__); 469662306a36Sopenharmony_ci break; 469762306a36Sopenharmony_ci } 469862306a36Sopenharmony_ci 469962306a36Sopenharmony_ci break; 470062306a36Sopenharmony_ci 470162306a36Sopenharmony_ci case DCMD_SUCCESS: 470262306a36Sopenharmony_ci pd_addr = ci->addr; 470362306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 470462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s, sysPD count: 0x%x\n", 470562306a36Sopenharmony_ci __func__, le32_to_cpu(ci->count)); 470662306a36Sopenharmony_ci 470762306a36Sopenharmony_ci if ((le32_to_cpu(ci->count) > 470862306a36Sopenharmony_ci (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) 470962306a36Sopenharmony_ci break; 471062306a36Sopenharmony_ci 471162306a36Sopenharmony_ci memset(instance->local_pd_list, 0, 471262306a36Sopenharmony_ci MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); 471362306a36Sopenharmony_ci 471462306a36Sopenharmony_ci for (pd_index = 0; pd_index < le32_to_cpu(ci->count); pd_index++) { 471562306a36Sopenharmony_ci instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].tid = 471662306a36Sopenharmony_ci le16_to_cpu(pd_addr->deviceId); 471762306a36Sopenharmony_ci instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].driveType = 471862306a36Sopenharmony_ci pd_addr->scsiDevType; 471962306a36Sopenharmony_ci instance->local_pd_list[le16_to_cpu(pd_addr->deviceId)].driveState = 472062306a36Sopenharmony_ci MR_PD_STATE_SYSTEM; 472162306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 472262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 472362306a36Sopenharmony_ci "PD%d: targetID: 0x%03x deviceType:0x%x\n", 472462306a36Sopenharmony_ci pd_index, le16_to_cpu(pd_addr->deviceId), 472562306a36Sopenharmony_ci pd_addr->scsiDevType); 472662306a36Sopenharmony_ci pd_addr++; 472762306a36Sopenharmony_ci } 472862306a36Sopenharmony_ci 472962306a36Sopenharmony_ci memcpy(instance->pd_list, instance->local_pd_list, 473062306a36Sopenharmony_ci sizeof(instance->pd_list)); 473162306a36Sopenharmony_ci break; 473262306a36Sopenharmony_ci 473362306a36Sopenharmony_ci } 473462306a36Sopenharmony_ci 473562306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 473662306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 473762306a36Sopenharmony_ci 473862306a36Sopenharmony_ci return ret; 473962306a36Sopenharmony_ci} 474062306a36Sopenharmony_ci 474162306a36Sopenharmony_ci/* 474262306a36Sopenharmony_ci * megasas_get_ld_list_info - Returns FW's ld_list structure 474362306a36Sopenharmony_ci * @instance: Adapter soft state 474462306a36Sopenharmony_ci * @ld_list: ld_list structure 474562306a36Sopenharmony_ci * 474662306a36Sopenharmony_ci * Issues an internal command (DCMD) to get the FW's controller PD 474762306a36Sopenharmony_ci * list structure. This information is mainly used to find out SYSTEM 474862306a36Sopenharmony_ci * supported by the FW. 474962306a36Sopenharmony_ci */ 475062306a36Sopenharmony_cistatic int 475162306a36Sopenharmony_cimegasas_get_ld_list(struct megasas_instance *instance) 475262306a36Sopenharmony_ci{ 475362306a36Sopenharmony_ci int ret = 0, ld_index = 0, ids = 0; 475462306a36Sopenharmony_ci struct megasas_cmd *cmd; 475562306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 475662306a36Sopenharmony_ci struct MR_LD_LIST *ci; 475762306a36Sopenharmony_ci dma_addr_t ci_h = 0; 475862306a36Sopenharmony_ci u32 ld_count; 475962306a36Sopenharmony_ci 476062306a36Sopenharmony_ci ci = instance->ld_list_buf; 476162306a36Sopenharmony_ci ci_h = instance->ld_list_buf_h; 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 476462306a36Sopenharmony_ci 476562306a36Sopenharmony_ci if (!cmd) { 476662306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "megasas_get_ld_list: Failed to get cmd\n"); 476762306a36Sopenharmony_ci return -ENOMEM; 476862306a36Sopenharmony_ci } 476962306a36Sopenharmony_ci 477062306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 477162306a36Sopenharmony_ci 477262306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 477362306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 477462306a36Sopenharmony_ci 477562306a36Sopenharmony_ci if (instance->supportmax256vd) 477662306a36Sopenharmony_ci dcmd->mbox.b[0] = 1; 477762306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 477862306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 477962306a36Sopenharmony_ci dcmd->sge_count = 1; 478062306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 478162306a36Sopenharmony_ci dcmd->timeout = 0; 478262306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_LD_LIST)); 478362306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_LD_GET_LIST); 478462306a36Sopenharmony_ci dcmd->pad_0 = 0; 478562306a36Sopenharmony_ci 478662306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, ci_h, 478762306a36Sopenharmony_ci sizeof(struct MR_LD_LIST)); 478862306a36Sopenharmony_ci 478962306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 479062306a36Sopenharmony_ci !instance->mask_interrupts) 479162306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, 479262306a36Sopenharmony_ci MFI_IO_TIMEOUT_SECS); 479362306a36Sopenharmony_ci else 479462306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 479562306a36Sopenharmony_ci 479662306a36Sopenharmony_ci ld_count = le32_to_cpu(ci->ldCount); 479762306a36Sopenharmony_ci 479862306a36Sopenharmony_ci switch (ret) { 479962306a36Sopenharmony_ci case DCMD_FAILED: 480062306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 480162306a36Sopenharmony_ci break; 480262306a36Sopenharmony_ci case DCMD_TIMEOUT: 480362306a36Sopenharmony_ci 480462306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 480562306a36Sopenharmony_ci case INITIATE_OCR: 480662306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 480762306a36Sopenharmony_ci /* 480862306a36Sopenharmony_ci * DCMD failed from AEN path. 480962306a36Sopenharmony_ci * AEN path already hold reset_mutex to avoid PCI access 481062306a36Sopenharmony_ci * while OCR is in progress. 481162306a36Sopenharmony_ci */ 481262306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 481362306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 481462306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 481562306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 481662306a36Sopenharmony_ci break; 481762306a36Sopenharmony_ci case KILL_ADAPTER: 481862306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 481962306a36Sopenharmony_ci break; 482062306a36Sopenharmony_ci case IGNORE_TIMEOUT: 482162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 482262306a36Sopenharmony_ci __func__, __LINE__); 482362306a36Sopenharmony_ci break; 482462306a36Sopenharmony_ci } 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_ci break; 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_ci case DCMD_SUCCESS: 482962306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 483062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n", 483162306a36Sopenharmony_ci __func__, ld_count); 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci if (ld_count > instance->fw_supported_vd_count) 483462306a36Sopenharmony_ci break; 483562306a36Sopenharmony_ci 483662306a36Sopenharmony_ci memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT); 483762306a36Sopenharmony_ci 483862306a36Sopenharmony_ci for (ld_index = 0; ld_index < ld_count; ld_index++) { 483962306a36Sopenharmony_ci if (ci->ldList[ld_index].state != 0) { 484062306a36Sopenharmony_ci ids = ci->ldList[ld_index].ref.targetId; 484162306a36Sopenharmony_ci instance->ld_ids[ids] = ci->ldList[ld_index].ref.targetId; 484262306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 484362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 484462306a36Sopenharmony_ci "LD%d: targetID: 0x%03x\n", 484562306a36Sopenharmony_ci ld_index, ids); 484662306a36Sopenharmony_ci } 484762306a36Sopenharmony_ci } 484862306a36Sopenharmony_ci 484962306a36Sopenharmony_ci break; 485062306a36Sopenharmony_ci } 485162306a36Sopenharmony_ci 485262306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 485362306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 485462306a36Sopenharmony_ci 485562306a36Sopenharmony_ci return ret; 485662306a36Sopenharmony_ci} 485762306a36Sopenharmony_ci 485862306a36Sopenharmony_ci/** 485962306a36Sopenharmony_ci * megasas_ld_list_query - Returns FW's ld_list structure 486062306a36Sopenharmony_ci * @instance: Adapter soft state 486162306a36Sopenharmony_ci * @query_type: ld_list structure type 486262306a36Sopenharmony_ci * 486362306a36Sopenharmony_ci * Issues an internal command (DCMD) to get the FW's controller PD 486462306a36Sopenharmony_ci * list structure. This information is mainly used to find out SYSTEM 486562306a36Sopenharmony_ci * supported by the FW. 486662306a36Sopenharmony_ci */ 486762306a36Sopenharmony_cistatic int 486862306a36Sopenharmony_cimegasas_ld_list_query(struct megasas_instance *instance, u8 query_type) 486962306a36Sopenharmony_ci{ 487062306a36Sopenharmony_ci int ret = 0, ld_index = 0, ids = 0; 487162306a36Sopenharmony_ci struct megasas_cmd *cmd; 487262306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 487362306a36Sopenharmony_ci struct MR_LD_TARGETID_LIST *ci; 487462306a36Sopenharmony_ci dma_addr_t ci_h = 0; 487562306a36Sopenharmony_ci u32 tgtid_count; 487662306a36Sopenharmony_ci 487762306a36Sopenharmony_ci ci = instance->ld_targetid_list_buf; 487862306a36Sopenharmony_ci ci_h = instance->ld_targetid_list_buf_h; 487962306a36Sopenharmony_ci 488062306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 488162306a36Sopenharmony_ci 488262306a36Sopenharmony_ci if (!cmd) { 488362306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, 488462306a36Sopenharmony_ci "megasas_ld_list_query: Failed to get cmd\n"); 488562306a36Sopenharmony_ci return -ENOMEM; 488662306a36Sopenharmony_ci } 488762306a36Sopenharmony_ci 488862306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 488962306a36Sopenharmony_ci 489062306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 489162306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 489262306a36Sopenharmony_ci 489362306a36Sopenharmony_ci dcmd->mbox.b[0] = query_type; 489462306a36Sopenharmony_ci if (instance->supportmax256vd) 489562306a36Sopenharmony_ci dcmd->mbox.b[2] = 1; 489662306a36Sopenharmony_ci 489762306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 489862306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 489962306a36Sopenharmony_ci dcmd->sge_count = 1; 490062306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 490162306a36Sopenharmony_ci dcmd->timeout = 0; 490262306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_LD_TARGETID_LIST)); 490362306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_LD_LIST_QUERY); 490462306a36Sopenharmony_ci dcmd->pad_0 = 0; 490562306a36Sopenharmony_ci 490662306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, ci_h, 490762306a36Sopenharmony_ci sizeof(struct MR_LD_TARGETID_LIST)); 490862306a36Sopenharmony_ci 490962306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 491062306a36Sopenharmony_ci !instance->mask_interrupts) 491162306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS); 491262306a36Sopenharmony_ci else 491362306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 491462306a36Sopenharmony_ci 491562306a36Sopenharmony_ci switch (ret) { 491662306a36Sopenharmony_ci case DCMD_FAILED: 491762306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 491862306a36Sopenharmony_ci "DCMD not supported by firmware - %s %d\n", 491962306a36Sopenharmony_ci __func__, __LINE__); 492062306a36Sopenharmony_ci ret = megasas_get_ld_list(instance); 492162306a36Sopenharmony_ci break; 492262306a36Sopenharmony_ci case DCMD_TIMEOUT: 492362306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 492462306a36Sopenharmony_ci case INITIATE_OCR: 492562306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 492662306a36Sopenharmony_ci /* 492762306a36Sopenharmony_ci * DCMD failed from AEN path. 492862306a36Sopenharmony_ci * AEN path already hold reset_mutex to avoid PCI access 492962306a36Sopenharmony_ci * while OCR is in progress. 493062306a36Sopenharmony_ci */ 493162306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 493262306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 493362306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 493462306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 493562306a36Sopenharmony_ci break; 493662306a36Sopenharmony_ci case KILL_ADAPTER: 493762306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 493862306a36Sopenharmony_ci break; 493962306a36Sopenharmony_ci case IGNORE_TIMEOUT: 494062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 494162306a36Sopenharmony_ci __func__, __LINE__); 494262306a36Sopenharmony_ci break; 494362306a36Sopenharmony_ci } 494462306a36Sopenharmony_ci 494562306a36Sopenharmony_ci break; 494662306a36Sopenharmony_ci case DCMD_SUCCESS: 494762306a36Sopenharmony_ci tgtid_count = le32_to_cpu(ci->count); 494862306a36Sopenharmony_ci 494962306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 495062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s, LD count: 0x%x\n", 495162306a36Sopenharmony_ci __func__, tgtid_count); 495262306a36Sopenharmony_ci 495362306a36Sopenharmony_ci if ((tgtid_count > (instance->fw_supported_vd_count))) 495462306a36Sopenharmony_ci break; 495562306a36Sopenharmony_ci 495662306a36Sopenharmony_ci memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); 495762306a36Sopenharmony_ci for (ld_index = 0; ld_index < tgtid_count; ld_index++) { 495862306a36Sopenharmony_ci ids = ci->targetId[ld_index]; 495962306a36Sopenharmony_ci instance->ld_ids[ids] = ci->targetId[ld_index]; 496062306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 496162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "LD%d: targetID: 0x%03x\n", 496262306a36Sopenharmony_ci ld_index, ci->targetId[ld_index]); 496362306a36Sopenharmony_ci } 496462306a36Sopenharmony_ci 496562306a36Sopenharmony_ci break; 496662306a36Sopenharmony_ci } 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 496962306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 497062306a36Sopenharmony_ci 497162306a36Sopenharmony_ci return ret; 497262306a36Sopenharmony_ci} 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci/** 497562306a36Sopenharmony_ci * megasas_host_device_list_query 497662306a36Sopenharmony_ci * dcmd.opcode - MR_DCMD_CTRL_DEVICE_LIST_GET 497762306a36Sopenharmony_ci * dcmd.mbox - reserved 497862306a36Sopenharmony_ci * dcmd.sge IN - ptr to return MR_HOST_DEVICE_LIST structure 497962306a36Sopenharmony_ci * Desc: This DCMD will return the combined device list 498062306a36Sopenharmony_ci * Status: MFI_STAT_OK - List returned successfully 498162306a36Sopenharmony_ci * MFI_STAT_INVALID_CMD - Firmware support for the feature has been 498262306a36Sopenharmony_ci * disabled 498362306a36Sopenharmony_ci * @instance: Adapter soft state 498462306a36Sopenharmony_ci * @is_probe: Driver probe check 498562306a36Sopenharmony_ci * Return: 0 if DCMD succeeded 498662306a36Sopenharmony_ci * non-zero if failed 498762306a36Sopenharmony_ci */ 498862306a36Sopenharmony_cistatic int 498962306a36Sopenharmony_cimegasas_host_device_list_query(struct megasas_instance *instance, 499062306a36Sopenharmony_ci bool is_probe) 499162306a36Sopenharmony_ci{ 499262306a36Sopenharmony_ci int ret, i, target_id; 499362306a36Sopenharmony_ci struct megasas_cmd *cmd; 499462306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 499562306a36Sopenharmony_ci struct MR_HOST_DEVICE_LIST *ci; 499662306a36Sopenharmony_ci u32 count; 499762306a36Sopenharmony_ci dma_addr_t ci_h; 499862306a36Sopenharmony_ci 499962306a36Sopenharmony_ci ci = instance->host_device_list_buf; 500062306a36Sopenharmony_ci ci_h = instance->host_device_list_buf_h; 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 500362306a36Sopenharmony_ci 500462306a36Sopenharmony_ci if (!cmd) { 500562306a36Sopenharmony_ci dev_warn(&instance->pdev->dev, 500662306a36Sopenharmony_ci "%s: failed to get cmd\n", 500762306a36Sopenharmony_ci __func__); 500862306a36Sopenharmony_ci return -ENOMEM; 500962306a36Sopenharmony_ci } 501062306a36Sopenharmony_ci 501162306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 501262306a36Sopenharmony_ci 501362306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 501462306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_ci dcmd->mbox.b[0] = is_probe ? 0 : 1; 501762306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 501862306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 501962306a36Sopenharmony_ci dcmd->sge_count = 1; 502062306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 502162306a36Sopenharmony_ci dcmd->timeout = 0; 502262306a36Sopenharmony_ci dcmd->pad_0 = 0; 502362306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(HOST_DEVICE_LIST_SZ); 502462306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_DEVICE_LIST_GET); 502562306a36Sopenharmony_ci 502662306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, ci_h, HOST_DEVICE_LIST_SZ); 502762306a36Sopenharmony_ci 502862306a36Sopenharmony_ci if (!instance->mask_interrupts) { 502962306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, 503062306a36Sopenharmony_ci MFI_IO_TIMEOUT_SECS); 503162306a36Sopenharmony_ci } else { 503262306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 503362306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 503462306a36Sopenharmony_ci } 503562306a36Sopenharmony_ci 503662306a36Sopenharmony_ci switch (ret) { 503762306a36Sopenharmony_ci case DCMD_SUCCESS: 503862306a36Sopenharmony_ci /* Fill the internal pd_list and ld_ids array based on 503962306a36Sopenharmony_ci * targetIds returned by FW 504062306a36Sopenharmony_ci */ 504162306a36Sopenharmony_ci count = le32_to_cpu(ci->count); 504262306a36Sopenharmony_ci 504362306a36Sopenharmony_ci if (count > (MEGASAS_MAX_PD + MAX_LOGICAL_DRIVES_EXT)) 504462306a36Sopenharmony_ci break; 504562306a36Sopenharmony_ci 504662306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 504762306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "%s, Device count: 0x%x\n", 504862306a36Sopenharmony_ci __func__, count); 504962306a36Sopenharmony_ci 505062306a36Sopenharmony_ci memset(instance->local_pd_list, 0, 505162306a36Sopenharmony_ci MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); 505262306a36Sopenharmony_ci memset(instance->ld_ids, 0xff, MAX_LOGICAL_DRIVES_EXT); 505362306a36Sopenharmony_ci for (i = 0; i < count; i++) { 505462306a36Sopenharmony_ci target_id = le16_to_cpu(ci->host_device_list[i].target_id); 505562306a36Sopenharmony_ci if (ci->host_device_list[i].flags.u.bits.is_sys_pd) { 505662306a36Sopenharmony_ci instance->local_pd_list[target_id].tid = target_id; 505762306a36Sopenharmony_ci instance->local_pd_list[target_id].driveType = 505862306a36Sopenharmony_ci ci->host_device_list[i].scsi_type; 505962306a36Sopenharmony_ci instance->local_pd_list[target_id].driveState = 506062306a36Sopenharmony_ci MR_PD_STATE_SYSTEM; 506162306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 506262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 506362306a36Sopenharmony_ci "Device %d: PD targetID: 0x%03x deviceType:0x%x\n", 506462306a36Sopenharmony_ci i, target_id, ci->host_device_list[i].scsi_type); 506562306a36Sopenharmony_ci } else { 506662306a36Sopenharmony_ci instance->ld_ids[target_id] = target_id; 506762306a36Sopenharmony_ci if (megasas_dbg_lvl & LD_PD_DEBUG) 506862306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 506962306a36Sopenharmony_ci "Device %d: LD targetID: 0x%03x\n", 507062306a36Sopenharmony_ci i, target_id); 507162306a36Sopenharmony_ci } 507262306a36Sopenharmony_ci } 507362306a36Sopenharmony_ci 507462306a36Sopenharmony_ci memcpy(instance->pd_list, instance->local_pd_list, 507562306a36Sopenharmony_ci sizeof(instance->pd_list)); 507662306a36Sopenharmony_ci break; 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ci case DCMD_TIMEOUT: 507962306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 508062306a36Sopenharmony_ci case INITIATE_OCR: 508162306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 508262306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 508362306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 508462306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 508562306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 508662306a36Sopenharmony_ci break; 508762306a36Sopenharmony_ci case KILL_ADAPTER: 508862306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 508962306a36Sopenharmony_ci break; 509062306a36Sopenharmony_ci case IGNORE_TIMEOUT: 509162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 509262306a36Sopenharmony_ci __func__, __LINE__); 509362306a36Sopenharmony_ci break; 509462306a36Sopenharmony_ci } 509562306a36Sopenharmony_ci break; 509662306a36Sopenharmony_ci case DCMD_FAILED: 509762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 509862306a36Sopenharmony_ci "%s: MR_DCMD_CTRL_DEVICE_LIST_GET failed\n", 509962306a36Sopenharmony_ci __func__); 510062306a36Sopenharmony_ci break; 510162306a36Sopenharmony_ci } 510262306a36Sopenharmony_ci 510362306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 510462306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 510562306a36Sopenharmony_ci 510662306a36Sopenharmony_ci return ret; 510762306a36Sopenharmony_ci} 510862306a36Sopenharmony_ci 510962306a36Sopenharmony_ci/* 511062306a36Sopenharmony_ci * megasas_update_ext_vd_details : Update details w.r.t Extended VD 511162306a36Sopenharmony_ci * instance : Controller's instance 511262306a36Sopenharmony_ci*/ 511362306a36Sopenharmony_cistatic void megasas_update_ext_vd_details(struct megasas_instance *instance) 511462306a36Sopenharmony_ci{ 511562306a36Sopenharmony_ci struct fusion_context *fusion; 511662306a36Sopenharmony_ci u32 ventura_map_sz = 0; 511762306a36Sopenharmony_ci 511862306a36Sopenharmony_ci fusion = instance->ctrl_context; 511962306a36Sopenharmony_ci /* For MFI based controllers return dummy success */ 512062306a36Sopenharmony_ci if (!fusion) 512162306a36Sopenharmony_ci return; 512262306a36Sopenharmony_ci 512362306a36Sopenharmony_ci instance->supportmax256vd = 512462306a36Sopenharmony_ci instance->ctrl_info_buf->adapterOperations3.supportMaxExtLDs; 512562306a36Sopenharmony_ci /* Below is additional check to address future FW enhancement */ 512662306a36Sopenharmony_ci if (instance->ctrl_info_buf->max_lds > 64) 512762306a36Sopenharmony_ci instance->supportmax256vd = 1; 512862306a36Sopenharmony_ci 512962306a36Sopenharmony_ci instance->drv_supported_vd_count = MEGASAS_MAX_LD_CHANNELS 513062306a36Sopenharmony_ci * MEGASAS_MAX_DEV_PER_CHANNEL; 513162306a36Sopenharmony_ci instance->drv_supported_pd_count = MEGASAS_MAX_PD_CHANNELS 513262306a36Sopenharmony_ci * MEGASAS_MAX_DEV_PER_CHANNEL; 513362306a36Sopenharmony_ci if (instance->supportmax256vd) { 513462306a36Sopenharmony_ci instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES_EXT; 513562306a36Sopenharmony_ci instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; 513662306a36Sopenharmony_ci } else { 513762306a36Sopenharmony_ci instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES; 513862306a36Sopenharmony_ci instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; 513962306a36Sopenharmony_ci } 514062306a36Sopenharmony_ci 514162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 514262306a36Sopenharmony_ci "FW provided supportMaxExtLDs: %d\tmax_lds: %d\n", 514362306a36Sopenharmony_ci instance->ctrl_info_buf->adapterOperations3.supportMaxExtLDs ? 1 : 0, 514462306a36Sopenharmony_ci instance->ctrl_info_buf->max_lds); 514562306a36Sopenharmony_ci 514662306a36Sopenharmony_ci if (instance->max_raid_mapsize) { 514762306a36Sopenharmony_ci ventura_map_sz = instance->max_raid_mapsize * 514862306a36Sopenharmony_ci MR_MIN_MAP_SIZE; /* 64k */ 514962306a36Sopenharmony_ci fusion->current_map_sz = ventura_map_sz; 515062306a36Sopenharmony_ci fusion->max_map_sz = ventura_map_sz; 515162306a36Sopenharmony_ci } else { 515262306a36Sopenharmony_ci fusion->old_map_sz = 515362306a36Sopenharmony_ci struct_size_t(struct MR_FW_RAID_MAP, ldSpanMap, 515462306a36Sopenharmony_ci instance->fw_supported_vd_count); 515562306a36Sopenharmony_ci fusion->new_map_sz = sizeof(struct MR_FW_RAID_MAP_EXT); 515662306a36Sopenharmony_ci 515762306a36Sopenharmony_ci fusion->max_map_sz = 515862306a36Sopenharmony_ci max(fusion->old_map_sz, fusion->new_map_sz); 515962306a36Sopenharmony_ci 516062306a36Sopenharmony_ci if (instance->supportmax256vd) 516162306a36Sopenharmony_ci fusion->current_map_sz = fusion->new_map_sz; 516262306a36Sopenharmony_ci else 516362306a36Sopenharmony_ci fusion->current_map_sz = fusion->old_map_sz; 516462306a36Sopenharmony_ci } 516562306a36Sopenharmony_ci /* irrespective of FW raid maps, driver raid map is constant */ 516662306a36Sopenharmony_ci fusion->drv_map_sz = sizeof(struct MR_DRV_RAID_MAP_ALL); 516762306a36Sopenharmony_ci} 516862306a36Sopenharmony_ci 516962306a36Sopenharmony_ci/* 517062306a36Sopenharmony_ci * dcmd.opcode - MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES 517162306a36Sopenharmony_ci * dcmd.hdr.length - number of bytes to read 517262306a36Sopenharmony_ci * dcmd.sge - Ptr to MR_SNAPDUMP_PROPERTIES 517362306a36Sopenharmony_ci * Desc: Fill in snapdump properties 517462306a36Sopenharmony_ci * Status: MFI_STAT_OK- Command successful 517562306a36Sopenharmony_ci */ 517662306a36Sopenharmony_civoid megasas_get_snapdump_properties(struct megasas_instance *instance) 517762306a36Sopenharmony_ci{ 517862306a36Sopenharmony_ci int ret = 0; 517962306a36Sopenharmony_ci struct megasas_cmd *cmd; 518062306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 518162306a36Sopenharmony_ci struct MR_SNAPDUMP_PROPERTIES *ci; 518262306a36Sopenharmony_ci dma_addr_t ci_h = 0; 518362306a36Sopenharmony_ci 518462306a36Sopenharmony_ci ci = instance->snapdump_prop; 518562306a36Sopenharmony_ci ci_h = instance->snapdump_prop_h; 518662306a36Sopenharmony_ci 518762306a36Sopenharmony_ci if (!ci) 518862306a36Sopenharmony_ci return; 518962306a36Sopenharmony_ci 519062306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 519162306a36Sopenharmony_ci 519262306a36Sopenharmony_ci if (!cmd) { 519362306a36Sopenharmony_ci dev_dbg(&instance->pdev->dev, "Failed to get a free cmd\n"); 519462306a36Sopenharmony_ci return; 519562306a36Sopenharmony_ci } 519662306a36Sopenharmony_ci 519762306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 519862306a36Sopenharmony_ci 519962306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 520062306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 520362306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 520462306a36Sopenharmony_ci dcmd->sge_count = 1; 520562306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 520662306a36Sopenharmony_ci dcmd->timeout = 0; 520762306a36Sopenharmony_ci dcmd->pad_0 = 0; 520862306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct MR_SNAPDUMP_PROPERTIES)); 520962306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SNAPDUMP_GET_PROPERTIES); 521062306a36Sopenharmony_ci 521162306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, ci_h, 521262306a36Sopenharmony_ci sizeof(struct MR_SNAPDUMP_PROPERTIES)); 521362306a36Sopenharmony_ci 521462306a36Sopenharmony_ci if (!instance->mask_interrupts) { 521562306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, 521662306a36Sopenharmony_ci MFI_IO_TIMEOUT_SECS); 521762306a36Sopenharmony_ci } else { 521862306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 521962306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 522062306a36Sopenharmony_ci } 522162306a36Sopenharmony_ci 522262306a36Sopenharmony_ci switch (ret) { 522362306a36Sopenharmony_ci case DCMD_SUCCESS: 522462306a36Sopenharmony_ci instance->snapdump_wait_time = 522562306a36Sopenharmony_ci min_t(u8, ci->trigger_min_num_sec_before_ocr, 522662306a36Sopenharmony_ci MEGASAS_MAX_SNAP_DUMP_WAIT_TIME); 522762306a36Sopenharmony_ci break; 522862306a36Sopenharmony_ci 522962306a36Sopenharmony_ci case DCMD_TIMEOUT: 523062306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 523162306a36Sopenharmony_ci case INITIATE_OCR: 523262306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 523362306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 523462306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 523562306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 523662306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 523762306a36Sopenharmony_ci break; 523862306a36Sopenharmony_ci case KILL_ADAPTER: 523962306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 524062306a36Sopenharmony_ci break; 524162306a36Sopenharmony_ci case IGNORE_TIMEOUT: 524262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 524362306a36Sopenharmony_ci __func__, __LINE__); 524462306a36Sopenharmony_ci break; 524562306a36Sopenharmony_ci } 524662306a36Sopenharmony_ci } 524762306a36Sopenharmony_ci 524862306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 524962306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 525062306a36Sopenharmony_ci} 525162306a36Sopenharmony_ci 525262306a36Sopenharmony_ci/** 525362306a36Sopenharmony_ci * megasas_get_ctrl_info - Returns FW's controller structure 525462306a36Sopenharmony_ci * @instance: Adapter soft state 525562306a36Sopenharmony_ci * 525662306a36Sopenharmony_ci * Issues an internal command (DCMD) to get the FW's controller structure. 525762306a36Sopenharmony_ci * This information is mainly used to find out the maximum IO transfer per 525862306a36Sopenharmony_ci * command supported by the FW. 525962306a36Sopenharmony_ci */ 526062306a36Sopenharmony_ciint 526162306a36Sopenharmony_cimegasas_get_ctrl_info(struct megasas_instance *instance) 526262306a36Sopenharmony_ci{ 526362306a36Sopenharmony_ci int ret = 0; 526462306a36Sopenharmony_ci struct megasas_cmd *cmd; 526562306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 526662306a36Sopenharmony_ci struct megasas_ctrl_info *ci; 526762306a36Sopenharmony_ci dma_addr_t ci_h = 0; 526862306a36Sopenharmony_ci 526962306a36Sopenharmony_ci ci = instance->ctrl_info_buf; 527062306a36Sopenharmony_ci ci_h = instance->ctrl_info_buf_h; 527162306a36Sopenharmony_ci 527262306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 527362306a36Sopenharmony_ci 527462306a36Sopenharmony_ci if (!cmd) { 527562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a free cmd\n"); 527662306a36Sopenharmony_ci return -ENOMEM; 527762306a36Sopenharmony_ci } 527862306a36Sopenharmony_ci 527962306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 528062306a36Sopenharmony_ci 528162306a36Sopenharmony_ci memset(ci, 0, sizeof(*ci)); 528262306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 528362306a36Sopenharmony_ci 528462306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 528562306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 528662306a36Sopenharmony_ci dcmd->sge_count = 1; 528762306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 528862306a36Sopenharmony_ci dcmd->timeout = 0; 528962306a36Sopenharmony_ci dcmd->pad_0 = 0; 529062306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct megasas_ctrl_info)); 529162306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_GET_INFO); 529262306a36Sopenharmony_ci dcmd->mbox.b[0] = 1; 529362306a36Sopenharmony_ci 529462306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, ci_h, 529562306a36Sopenharmony_ci sizeof(struct megasas_ctrl_info)); 529662306a36Sopenharmony_ci 529762306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 529862306a36Sopenharmony_ci !instance->mask_interrupts) { 529962306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS); 530062306a36Sopenharmony_ci } else { 530162306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 530262306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 530362306a36Sopenharmony_ci } 530462306a36Sopenharmony_ci 530562306a36Sopenharmony_ci switch (ret) { 530662306a36Sopenharmony_ci case DCMD_SUCCESS: 530762306a36Sopenharmony_ci /* Save required controller information in 530862306a36Sopenharmony_ci * CPU endianness format. 530962306a36Sopenharmony_ci */ 531062306a36Sopenharmony_ci le32_to_cpus((u32 *)&ci->properties.OnOffProperties); 531162306a36Sopenharmony_ci le16_to_cpus((u16 *)&ci->properties.on_off_properties2); 531262306a36Sopenharmony_ci le32_to_cpus((u32 *)&ci->adapterOperations2); 531362306a36Sopenharmony_ci le32_to_cpus((u32 *)&ci->adapterOperations3); 531462306a36Sopenharmony_ci le16_to_cpus((u16 *)&ci->adapter_operations4); 531562306a36Sopenharmony_ci le32_to_cpus((u32 *)&ci->adapter_operations5); 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci /* Update the latest Ext VD info. 531862306a36Sopenharmony_ci * From Init path, store current firmware details. 531962306a36Sopenharmony_ci * From OCR path, detect any firmware properties changes. 532062306a36Sopenharmony_ci * in case of Firmware upgrade without system reboot. 532162306a36Sopenharmony_ci */ 532262306a36Sopenharmony_ci megasas_update_ext_vd_details(instance); 532362306a36Sopenharmony_ci instance->support_seqnum_jbod_fp = 532462306a36Sopenharmony_ci ci->adapterOperations3.useSeqNumJbodFP; 532562306a36Sopenharmony_ci instance->support_morethan256jbod = 532662306a36Sopenharmony_ci ci->adapter_operations4.support_pd_map_target_id; 532762306a36Sopenharmony_ci instance->support_nvme_passthru = 532862306a36Sopenharmony_ci ci->adapter_operations4.support_nvme_passthru; 532962306a36Sopenharmony_ci instance->support_pci_lane_margining = 533062306a36Sopenharmony_ci ci->adapter_operations5.support_pci_lane_margining; 533162306a36Sopenharmony_ci instance->task_abort_tmo = ci->TaskAbortTO; 533262306a36Sopenharmony_ci instance->max_reset_tmo = ci->MaxResetTO; 533362306a36Sopenharmony_ci 533462306a36Sopenharmony_ci /*Check whether controller is iMR or MR */ 533562306a36Sopenharmony_ci instance->is_imr = (ci->memory_size ? 0 : 1); 533662306a36Sopenharmony_ci 533762306a36Sopenharmony_ci instance->snapdump_wait_time = 533862306a36Sopenharmony_ci (ci->properties.on_off_properties2.enable_snap_dump ? 533962306a36Sopenharmony_ci MEGASAS_DEFAULT_SNAP_DUMP_WAIT_TIME : 0); 534062306a36Sopenharmony_ci 534162306a36Sopenharmony_ci instance->enable_fw_dev_list = 534262306a36Sopenharmony_ci ci->properties.on_off_properties2.enable_fw_dev_list; 534362306a36Sopenharmony_ci 534462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 534562306a36Sopenharmony_ci "controller type\t: %s(%dMB)\n", 534662306a36Sopenharmony_ci instance->is_imr ? "iMR" : "MR", 534762306a36Sopenharmony_ci le16_to_cpu(ci->memory_size)); 534862306a36Sopenharmony_ci 534962306a36Sopenharmony_ci instance->disableOnlineCtrlReset = 535062306a36Sopenharmony_ci ci->properties.OnOffProperties.disableOnlineCtrlReset; 535162306a36Sopenharmony_ci instance->secure_jbod_support = 535262306a36Sopenharmony_ci ci->adapterOperations3.supportSecurityonJBOD; 535362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Online Controller Reset(OCR)\t: %s\n", 535462306a36Sopenharmony_ci instance->disableOnlineCtrlReset ? "Disabled" : "Enabled"); 535562306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Secure JBOD support\t: %s\n", 535662306a36Sopenharmony_ci instance->secure_jbod_support ? "Yes" : "No"); 535762306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "NVMe passthru support\t: %s\n", 535862306a36Sopenharmony_ci instance->support_nvme_passthru ? "Yes" : "No"); 535962306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 536062306a36Sopenharmony_ci "FW provided TM TaskAbort/Reset timeout\t: %d secs/%d secs\n", 536162306a36Sopenharmony_ci instance->task_abort_tmo, instance->max_reset_tmo); 536262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "JBOD sequence map support\t: %s\n", 536362306a36Sopenharmony_ci instance->support_seqnum_jbod_fp ? "Yes" : "No"); 536462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "PCI Lane Margining support\t: %s\n", 536562306a36Sopenharmony_ci instance->support_pci_lane_margining ? "Yes" : "No"); 536662306a36Sopenharmony_ci 536762306a36Sopenharmony_ci break; 536862306a36Sopenharmony_ci 536962306a36Sopenharmony_ci case DCMD_TIMEOUT: 537062306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 537162306a36Sopenharmony_ci case INITIATE_OCR: 537262306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 537362306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 537462306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 537562306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 537662306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 537762306a36Sopenharmony_ci break; 537862306a36Sopenharmony_ci case KILL_ADAPTER: 537962306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 538062306a36Sopenharmony_ci break; 538162306a36Sopenharmony_ci case IGNORE_TIMEOUT: 538262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 538362306a36Sopenharmony_ci __func__, __LINE__); 538462306a36Sopenharmony_ci break; 538562306a36Sopenharmony_ci } 538662306a36Sopenharmony_ci break; 538762306a36Sopenharmony_ci case DCMD_FAILED: 538862306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 538962306a36Sopenharmony_ci break; 539062306a36Sopenharmony_ci 539162306a36Sopenharmony_ci } 539262306a36Sopenharmony_ci 539362306a36Sopenharmony_ci if (ret != DCMD_TIMEOUT) 539462306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 539562306a36Sopenharmony_ci 539662306a36Sopenharmony_ci return ret; 539762306a36Sopenharmony_ci} 539862306a36Sopenharmony_ci 539962306a36Sopenharmony_ci/* 540062306a36Sopenharmony_ci * megasas_set_crash_dump_params - Sends address of crash dump DMA buffer 540162306a36Sopenharmony_ci * to firmware 540262306a36Sopenharmony_ci * 540362306a36Sopenharmony_ci * @instance: Adapter soft state 540462306a36Sopenharmony_ci * @crash_buf_state - tell FW to turn ON/OFF crash dump feature 540562306a36Sopenharmony_ci MR_CRASH_BUF_TURN_OFF = 0 540662306a36Sopenharmony_ci MR_CRASH_BUF_TURN_ON = 1 540762306a36Sopenharmony_ci * @return 0 on success non-zero on failure. 540862306a36Sopenharmony_ci * Issues an internal command (DCMD) to set parameters for crash dump feature. 540962306a36Sopenharmony_ci * Driver will send address of crash dump DMA buffer and set mbox to tell FW 541062306a36Sopenharmony_ci * that driver supports crash dump feature. This DCMD will be sent only if 541162306a36Sopenharmony_ci * crash dump feature is supported by the FW. 541262306a36Sopenharmony_ci * 541362306a36Sopenharmony_ci */ 541462306a36Sopenharmony_ciint megasas_set_crash_dump_params(struct megasas_instance *instance, 541562306a36Sopenharmony_ci u8 crash_buf_state) 541662306a36Sopenharmony_ci{ 541762306a36Sopenharmony_ci int ret = 0; 541862306a36Sopenharmony_ci struct megasas_cmd *cmd; 541962306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 542062306a36Sopenharmony_ci 542162306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 542262306a36Sopenharmony_ci 542362306a36Sopenharmony_ci if (!cmd) { 542462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed to get a free cmd\n"); 542562306a36Sopenharmony_ci return -ENOMEM; 542662306a36Sopenharmony_ci } 542762306a36Sopenharmony_ci 542862306a36Sopenharmony_ci 542962306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 543062306a36Sopenharmony_ci 543162306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 543262306a36Sopenharmony_ci dcmd->mbox.b[0] = crash_buf_state; 543362306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 543462306a36Sopenharmony_ci dcmd->cmd_status = MFI_STAT_INVALID_STATUS; 543562306a36Sopenharmony_ci dcmd->sge_count = 1; 543662306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_NONE; 543762306a36Sopenharmony_ci dcmd->timeout = 0; 543862306a36Sopenharmony_ci dcmd->pad_0 = 0; 543962306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(CRASH_DMA_BUF_SIZE); 544062306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_SET_CRASH_DUMP_PARAMS); 544162306a36Sopenharmony_ci 544262306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, instance->crash_dump_h, 544362306a36Sopenharmony_ci CRASH_DMA_BUF_SIZE); 544462306a36Sopenharmony_ci 544562306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 544662306a36Sopenharmony_ci !instance->mask_interrupts) 544762306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS); 544862306a36Sopenharmony_ci else 544962306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 545062306a36Sopenharmony_ci 545162306a36Sopenharmony_ci if (ret == DCMD_TIMEOUT) { 545262306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 545362306a36Sopenharmony_ci case INITIATE_OCR: 545462306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 545562306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 545662306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 545762306a36Sopenharmony_ci break; 545862306a36Sopenharmony_ci case KILL_ADAPTER: 545962306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 546062306a36Sopenharmony_ci break; 546162306a36Sopenharmony_ci case IGNORE_TIMEOUT: 546262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Ignore DCMD timeout: %s %d\n", 546362306a36Sopenharmony_ci __func__, __LINE__); 546462306a36Sopenharmony_ci break; 546562306a36Sopenharmony_ci } 546662306a36Sopenharmony_ci } else 546762306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 546862306a36Sopenharmony_ci 546962306a36Sopenharmony_ci return ret; 547062306a36Sopenharmony_ci} 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_ci/** 547362306a36Sopenharmony_ci * megasas_issue_init_mfi - Initializes the FW 547462306a36Sopenharmony_ci * @instance: Adapter soft state 547562306a36Sopenharmony_ci * 547662306a36Sopenharmony_ci * Issues the INIT MFI cmd 547762306a36Sopenharmony_ci */ 547862306a36Sopenharmony_cistatic int 547962306a36Sopenharmony_cimegasas_issue_init_mfi(struct megasas_instance *instance) 548062306a36Sopenharmony_ci{ 548162306a36Sopenharmony_ci __le32 context; 548262306a36Sopenharmony_ci struct megasas_cmd *cmd; 548362306a36Sopenharmony_ci struct megasas_init_frame *init_frame; 548462306a36Sopenharmony_ci struct megasas_init_queue_info *initq_info; 548562306a36Sopenharmony_ci dma_addr_t init_frame_h; 548662306a36Sopenharmony_ci dma_addr_t initq_info_h; 548762306a36Sopenharmony_ci 548862306a36Sopenharmony_ci /* 548962306a36Sopenharmony_ci * Prepare a init frame. Note the init frame points to queue info 549062306a36Sopenharmony_ci * structure. Each frame has SGL allocated after first 64 bytes. For 549162306a36Sopenharmony_ci * this frame - since we don't need any SGL - we use SGL's space as 549262306a36Sopenharmony_ci * queue info structure 549362306a36Sopenharmony_ci * 549462306a36Sopenharmony_ci * We will not get a NULL command below. We just created the pool. 549562306a36Sopenharmony_ci */ 549662306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 549762306a36Sopenharmony_ci 549862306a36Sopenharmony_ci init_frame = (struct megasas_init_frame *)cmd->frame; 549962306a36Sopenharmony_ci initq_info = (struct megasas_init_queue_info *) 550062306a36Sopenharmony_ci ((unsigned long)init_frame + 64); 550162306a36Sopenharmony_ci 550262306a36Sopenharmony_ci init_frame_h = cmd->frame_phys_addr; 550362306a36Sopenharmony_ci initq_info_h = init_frame_h + 64; 550462306a36Sopenharmony_ci 550562306a36Sopenharmony_ci context = init_frame->context; 550662306a36Sopenharmony_ci memset(init_frame, 0, MEGAMFI_FRAME_SIZE); 550762306a36Sopenharmony_ci memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); 550862306a36Sopenharmony_ci init_frame->context = context; 550962306a36Sopenharmony_ci 551062306a36Sopenharmony_ci initq_info->reply_queue_entries = cpu_to_le32(instance->max_fw_cmds + 1); 551162306a36Sopenharmony_ci initq_info->reply_queue_start_phys_addr_lo = cpu_to_le32(instance->reply_queue_h); 551262306a36Sopenharmony_ci 551362306a36Sopenharmony_ci initq_info->producer_index_phys_addr_lo = cpu_to_le32(instance->producer_h); 551462306a36Sopenharmony_ci initq_info->consumer_index_phys_addr_lo = cpu_to_le32(instance->consumer_h); 551562306a36Sopenharmony_ci 551662306a36Sopenharmony_ci init_frame->cmd = MFI_CMD_INIT; 551762306a36Sopenharmony_ci init_frame->cmd_status = MFI_STAT_INVALID_STATUS; 551862306a36Sopenharmony_ci init_frame->queue_info_new_phys_addr_lo = 551962306a36Sopenharmony_ci cpu_to_le32(lower_32_bits(initq_info_h)); 552062306a36Sopenharmony_ci init_frame->queue_info_new_phys_addr_hi = 552162306a36Sopenharmony_ci cpu_to_le32(upper_32_bits(initq_info_h)); 552262306a36Sopenharmony_ci 552362306a36Sopenharmony_ci init_frame->data_xfer_len = cpu_to_le32(sizeof(struct megasas_init_queue_info)); 552462306a36Sopenharmony_ci 552562306a36Sopenharmony_ci /* 552662306a36Sopenharmony_ci * disable the intr before firing the init frame to FW 552762306a36Sopenharmony_ci */ 552862306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 552962306a36Sopenharmony_ci 553062306a36Sopenharmony_ci /* 553162306a36Sopenharmony_ci * Issue the init frame in polled mode 553262306a36Sopenharmony_ci */ 553362306a36Sopenharmony_ci 553462306a36Sopenharmony_ci if (megasas_issue_polled(instance, cmd)) { 553562306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed to init firmware\n"); 553662306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 553762306a36Sopenharmony_ci goto fail_fw_init; 553862306a36Sopenharmony_ci } 553962306a36Sopenharmony_ci 554062306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 554162306a36Sopenharmony_ci 554262306a36Sopenharmony_ci return 0; 554362306a36Sopenharmony_ci 554462306a36Sopenharmony_cifail_fw_init: 554562306a36Sopenharmony_ci return -EINVAL; 554662306a36Sopenharmony_ci} 554762306a36Sopenharmony_ci 554862306a36Sopenharmony_cistatic u32 554962306a36Sopenharmony_cimegasas_init_adapter_mfi(struct megasas_instance *instance) 555062306a36Sopenharmony_ci{ 555162306a36Sopenharmony_ci u32 context_sz; 555262306a36Sopenharmony_ci u32 reply_q_sz; 555362306a36Sopenharmony_ci 555462306a36Sopenharmony_ci /* 555562306a36Sopenharmony_ci * Get various operational parameters from status register 555662306a36Sopenharmony_ci */ 555762306a36Sopenharmony_ci instance->max_fw_cmds = instance->instancet->read_fw_status_reg(instance) & 0x00FFFF; 555862306a36Sopenharmony_ci /* 555962306a36Sopenharmony_ci * Reduce the max supported cmds by 1. This is to ensure that the 556062306a36Sopenharmony_ci * reply_q_sz (1 more than the max cmd that driver may send) 556162306a36Sopenharmony_ci * does not exceed max cmds that the FW can support 556262306a36Sopenharmony_ci */ 556362306a36Sopenharmony_ci instance->max_fw_cmds = instance->max_fw_cmds-1; 556462306a36Sopenharmony_ci instance->max_mfi_cmds = instance->max_fw_cmds; 556562306a36Sopenharmony_ci instance->max_num_sge = (instance->instancet->read_fw_status_reg(instance) & 0xFF0000) >> 556662306a36Sopenharmony_ci 0x10; 556762306a36Sopenharmony_ci /* 556862306a36Sopenharmony_ci * For MFI skinny adapters, MEGASAS_SKINNY_INT_CMDS commands 556962306a36Sopenharmony_ci * are reserved for IOCTL + driver's internal DCMDs. 557062306a36Sopenharmony_ci */ 557162306a36Sopenharmony_ci if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || 557262306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { 557362306a36Sopenharmony_ci instance->max_scsi_cmds = (instance->max_fw_cmds - 557462306a36Sopenharmony_ci MEGASAS_SKINNY_INT_CMDS); 557562306a36Sopenharmony_ci sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); 557662306a36Sopenharmony_ci } else { 557762306a36Sopenharmony_ci instance->max_scsi_cmds = (instance->max_fw_cmds - 557862306a36Sopenharmony_ci MEGASAS_INT_CMDS); 557962306a36Sopenharmony_ci sema_init(&instance->ioctl_sem, (MEGASAS_MFI_IOCTL_CMDS)); 558062306a36Sopenharmony_ci } 558162306a36Sopenharmony_ci 558262306a36Sopenharmony_ci instance->cur_can_queue = instance->max_scsi_cmds; 558362306a36Sopenharmony_ci /* 558462306a36Sopenharmony_ci * Create a pool of commands 558562306a36Sopenharmony_ci */ 558662306a36Sopenharmony_ci if (megasas_alloc_cmds(instance)) 558762306a36Sopenharmony_ci goto fail_alloc_cmds; 558862306a36Sopenharmony_ci 558962306a36Sopenharmony_ci /* 559062306a36Sopenharmony_ci * Allocate memory for reply queue. Length of reply queue should 559162306a36Sopenharmony_ci * be _one_ more than the maximum commands handled by the firmware. 559262306a36Sopenharmony_ci * 559362306a36Sopenharmony_ci * Note: When FW completes commands, it places corresponding contex 559462306a36Sopenharmony_ci * values in this circular reply queue. This circular queue is a fairly 559562306a36Sopenharmony_ci * typical producer-consumer queue. FW is the producer (of completed 559662306a36Sopenharmony_ci * commands) and the driver is the consumer. 559762306a36Sopenharmony_ci */ 559862306a36Sopenharmony_ci context_sz = sizeof(u32); 559962306a36Sopenharmony_ci reply_q_sz = context_sz * (instance->max_fw_cmds + 1); 560062306a36Sopenharmony_ci 560162306a36Sopenharmony_ci instance->reply_queue = dma_alloc_coherent(&instance->pdev->dev, 560262306a36Sopenharmony_ci reply_q_sz, &instance->reply_queue_h, GFP_KERNEL); 560362306a36Sopenharmony_ci 560462306a36Sopenharmony_ci if (!instance->reply_queue) { 560562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Out of DMA mem for reply queue\n"); 560662306a36Sopenharmony_ci goto fail_reply_queue; 560762306a36Sopenharmony_ci } 560862306a36Sopenharmony_ci 560962306a36Sopenharmony_ci if (megasas_issue_init_mfi(instance)) 561062306a36Sopenharmony_ci goto fail_fw_init; 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_ci if (megasas_get_ctrl_info(instance)) { 561362306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "(%d): Could get controller info " 561462306a36Sopenharmony_ci "Fail from %s %d\n", instance->unique_id, 561562306a36Sopenharmony_ci __func__, __LINE__); 561662306a36Sopenharmony_ci goto fail_fw_init; 561762306a36Sopenharmony_ci } 561862306a36Sopenharmony_ci 561962306a36Sopenharmony_ci instance->fw_support_ieee = 0; 562062306a36Sopenharmony_ci instance->fw_support_ieee = 562162306a36Sopenharmony_ci (instance->instancet->read_fw_status_reg(instance) & 562262306a36Sopenharmony_ci 0x04000000); 562362306a36Sopenharmony_ci 562462306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "megasas_init_mfi: fw_support_ieee=%d", 562562306a36Sopenharmony_ci instance->fw_support_ieee); 562662306a36Sopenharmony_ci 562762306a36Sopenharmony_ci if (instance->fw_support_ieee) 562862306a36Sopenharmony_ci instance->flag_ieee = 1; 562962306a36Sopenharmony_ci 563062306a36Sopenharmony_ci return 0; 563162306a36Sopenharmony_ci 563262306a36Sopenharmony_cifail_fw_init: 563362306a36Sopenharmony_ci 563462306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, reply_q_sz, 563562306a36Sopenharmony_ci instance->reply_queue, instance->reply_queue_h); 563662306a36Sopenharmony_cifail_reply_queue: 563762306a36Sopenharmony_ci megasas_free_cmds(instance); 563862306a36Sopenharmony_ci 563962306a36Sopenharmony_cifail_alloc_cmds: 564062306a36Sopenharmony_ci return 1; 564162306a36Sopenharmony_ci} 564262306a36Sopenharmony_ci 564362306a36Sopenharmony_cistatic 564462306a36Sopenharmony_civoid megasas_setup_irq_poll(struct megasas_instance *instance) 564562306a36Sopenharmony_ci{ 564662306a36Sopenharmony_ci struct megasas_irq_context *irq_ctx; 564762306a36Sopenharmony_ci u32 count, i; 564862306a36Sopenharmony_ci 564962306a36Sopenharmony_ci count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; 565062306a36Sopenharmony_ci 565162306a36Sopenharmony_ci /* Initialize IRQ poll */ 565262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 565362306a36Sopenharmony_ci irq_ctx = &instance->irq_context[i]; 565462306a36Sopenharmony_ci irq_ctx->os_irq = pci_irq_vector(instance->pdev, i); 565562306a36Sopenharmony_ci irq_ctx->irq_poll_scheduled = false; 565662306a36Sopenharmony_ci irq_poll_init(&irq_ctx->irqpoll, 565762306a36Sopenharmony_ci instance->threshold_reply_count, 565862306a36Sopenharmony_ci megasas_irqpoll); 565962306a36Sopenharmony_ci } 566062306a36Sopenharmony_ci} 566162306a36Sopenharmony_ci 566262306a36Sopenharmony_ci/* 566362306a36Sopenharmony_ci * megasas_setup_irqs_ioapic - register legacy interrupts. 566462306a36Sopenharmony_ci * @instance: Adapter soft state 566562306a36Sopenharmony_ci * 566662306a36Sopenharmony_ci * Do not enable interrupt, only setup ISRs. 566762306a36Sopenharmony_ci * 566862306a36Sopenharmony_ci * Return 0 on success. 566962306a36Sopenharmony_ci */ 567062306a36Sopenharmony_cistatic int 567162306a36Sopenharmony_cimegasas_setup_irqs_ioapic(struct megasas_instance *instance) 567262306a36Sopenharmony_ci{ 567362306a36Sopenharmony_ci struct pci_dev *pdev; 567462306a36Sopenharmony_ci 567562306a36Sopenharmony_ci pdev = instance->pdev; 567662306a36Sopenharmony_ci instance->irq_context[0].instance = instance; 567762306a36Sopenharmony_ci instance->irq_context[0].MSIxIndex = 0; 567862306a36Sopenharmony_ci snprintf(instance->irq_context->name, MEGASAS_MSIX_NAME_LEN, "%s%u", 567962306a36Sopenharmony_ci "megasas", instance->host->host_no); 568062306a36Sopenharmony_ci if (request_irq(pci_irq_vector(pdev, 0), 568162306a36Sopenharmony_ci instance->instancet->service_isr, IRQF_SHARED, 568262306a36Sopenharmony_ci instance->irq_context->name, &instance->irq_context[0])) { 568362306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 568462306a36Sopenharmony_ci "Failed to register IRQ from %s %d\n", 568562306a36Sopenharmony_ci __func__, __LINE__); 568662306a36Sopenharmony_ci return -1; 568762306a36Sopenharmony_ci } 568862306a36Sopenharmony_ci instance->perf_mode = MR_LATENCY_PERF_MODE; 568962306a36Sopenharmony_ci instance->low_latency_index_start = 0; 569062306a36Sopenharmony_ci return 0; 569162306a36Sopenharmony_ci} 569262306a36Sopenharmony_ci 569362306a36Sopenharmony_ci/** 569462306a36Sopenharmony_ci * megasas_setup_irqs_msix - register MSI-x interrupts. 569562306a36Sopenharmony_ci * @instance: Adapter soft state 569662306a36Sopenharmony_ci * @is_probe: Driver probe check 569762306a36Sopenharmony_ci * 569862306a36Sopenharmony_ci * Do not enable interrupt, only setup ISRs. 569962306a36Sopenharmony_ci * 570062306a36Sopenharmony_ci * Return 0 on success. 570162306a36Sopenharmony_ci */ 570262306a36Sopenharmony_cistatic int 570362306a36Sopenharmony_cimegasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe) 570462306a36Sopenharmony_ci{ 570562306a36Sopenharmony_ci int i, j; 570662306a36Sopenharmony_ci struct pci_dev *pdev; 570762306a36Sopenharmony_ci 570862306a36Sopenharmony_ci pdev = instance->pdev; 570962306a36Sopenharmony_ci 571062306a36Sopenharmony_ci /* Try MSI-x */ 571162306a36Sopenharmony_ci for (i = 0; i < instance->msix_vectors; i++) { 571262306a36Sopenharmony_ci instance->irq_context[i].instance = instance; 571362306a36Sopenharmony_ci instance->irq_context[i].MSIxIndex = i; 571462306a36Sopenharmony_ci snprintf(instance->irq_context[i].name, MEGASAS_MSIX_NAME_LEN, "%s%u-msix%u", 571562306a36Sopenharmony_ci "megasas", instance->host->host_no, i); 571662306a36Sopenharmony_ci if (request_irq(pci_irq_vector(pdev, i), 571762306a36Sopenharmony_ci instance->instancet->service_isr, 0, instance->irq_context[i].name, 571862306a36Sopenharmony_ci &instance->irq_context[i])) { 571962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 572062306a36Sopenharmony_ci "Failed to register IRQ for vector %d.\n", i); 572162306a36Sopenharmony_ci for (j = 0; j < i; j++) { 572262306a36Sopenharmony_ci if (j < instance->low_latency_index_start) 572362306a36Sopenharmony_ci irq_update_affinity_hint( 572462306a36Sopenharmony_ci pci_irq_vector(pdev, j), NULL); 572562306a36Sopenharmony_ci free_irq(pci_irq_vector(pdev, j), 572662306a36Sopenharmony_ci &instance->irq_context[j]); 572762306a36Sopenharmony_ci } 572862306a36Sopenharmony_ci /* Retry irq register for IO_APIC*/ 572962306a36Sopenharmony_ci instance->msix_vectors = 0; 573062306a36Sopenharmony_ci instance->msix_load_balance = false; 573162306a36Sopenharmony_ci if (is_probe) { 573262306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 573362306a36Sopenharmony_ci return megasas_setup_irqs_ioapic(instance); 573462306a36Sopenharmony_ci } else { 573562306a36Sopenharmony_ci return -1; 573662306a36Sopenharmony_ci } 573762306a36Sopenharmony_ci } 573862306a36Sopenharmony_ci } 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_ci return 0; 574162306a36Sopenharmony_ci} 574262306a36Sopenharmony_ci 574362306a36Sopenharmony_ci/* 574462306a36Sopenharmony_ci * megasas_destroy_irqs- unregister interrupts. 574562306a36Sopenharmony_ci * @instance: Adapter soft state 574662306a36Sopenharmony_ci * return: void 574762306a36Sopenharmony_ci */ 574862306a36Sopenharmony_cistatic void 574962306a36Sopenharmony_cimegasas_destroy_irqs(struct megasas_instance *instance) { 575062306a36Sopenharmony_ci 575162306a36Sopenharmony_ci int i; 575262306a36Sopenharmony_ci int count; 575362306a36Sopenharmony_ci struct megasas_irq_context *irq_ctx; 575462306a36Sopenharmony_ci 575562306a36Sopenharmony_ci count = instance->msix_vectors > 0 ? instance->msix_vectors : 1; 575662306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) { 575762306a36Sopenharmony_ci for (i = 0; i < count; i++) { 575862306a36Sopenharmony_ci irq_ctx = &instance->irq_context[i]; 575962306a36Sopenharmony_ci irq_poll_disable(&irq_ctx->irqpoll); 576062306a36Sopenharmony_ci } 576162306a36Sopenharmony_ci } 576262306a36Sopenharmony_ci 576362306a36Sopenharmony_ci if (instance->msix_vectors) 576462306a36Sopenharmony_ci for (i = 0; i < instance->msix_vectors; i++) { 576562306a36Sopenharmony_ci if (i < instance->low_latency_index_start) 576662306a36Sopenharmony_ci irq_update_affinity_hint( 576762306a36Sopenharmony_ci pci_irq_vector(instance->pdev, i), NULL); 576862306a36Sopenharmony_ci free_irq(pci_irq_vector(instance->pdev, i), 576962306a36Sopenharmony_ci &instance->irq_context[i]); 577062306a36Sopenharmony_ci } 577162306a36Sopenharmony_ci else 577262306a36Sopenharmony_ci free_irq(pci_irq_vector(instance->pdev, 0), 577362306a36Sopenharmony_ci &instance->irq_context[0]); 577462306a36Sopenharmony_ci} 577562306a36Sopenharmony_ci 577662306a36Sopenharmony_ci/** 577762306a36Sopenharmony_ci * megasas_setup_jbod_map - setup jbod map for FP seq_number. 577862306a36Sopenharmony_ci * @instance: Adapter soft state 577962306a36Sopenharmony_ci * 578062306a36Sopenharmony_ci * Return 0 on success. 578162306a36Sopenharmony_ci */ 578262306a36Sopenharmony_civoid 578362306a36Sopenharmony_cimegasas_setup_jbod_map(struct megasas_instance *instance) 578462306a36Sopenharmony_ci{ 578562306a36Sopenharmony_ci int i; 578662306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 578762306a36Sopenharmony_ci size_t pd_seq_map_sz; 578862306a36Sopenharmony_ci 578962306a36Sopenharmony_ci pd_seq_map_sz = struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC, seq, 579062306a36Sopenharmony_ci MAX_PHYSICAL_DEVICES); 579162306a36Sopenharmony_ci 579262306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = 579362306a36Sopenharmony_ci instance->support_seqnum_jbod_fp; 579462306a36Sopenharmony_ci if (reset_devices || !fusion || 579562306a36Sopenharmony_ci !instance->support_seqnum_jbod_fp) { 579662306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 579762306a36Sopenharmony_ci "JBOD sequence map is disabled %s %d\n", 579862306a36Sopenharmony_ci __func__, __LINE__); 579962306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = false; 580062306a36Sopenharmony_ci return; 580162306a36Sopenharmony_ci } 580262306a36Sopenharmony_ci 580362306a36Sopenharmony_ci if (fusion->pd_seq_sync[0]) 580462306a36Sopenharmony_ci goto skip_alloc; 580562306a36Sopenharmony_ci 580662306a36Sopenharmony_ci for (i = 0; i < JBOD_MAPS_COUNT; i++) { 580762306a36Sopenharmony_ci fusion->pd_seq_sync[i] = dma_alloc_coherent 580862306a36Sopenharmony_ci (&instance->pdev->dev, pd_seq_map_sz, 580962306a36Sopenharmony_ci &fusion->pd_seq_phys[i], GFP_KERNEL); 581062306a36Sopenharmony_ci if (!fusion->pd_seq_sync[i]) { 581162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 581262306a36Sopenharmony_ci "Failed to allocate memory from %s %d\n", 581362306a36Sopenharmony_ci __func__, __LINE__); 581462306a36Sopenharmony_ci if (i == 1) { 581562306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 581662306a36Sopenharmony_ci pd_seq_map_sz, fusion->pd_seq_sync[0], 581762306a36Sopenharmony_ci fusion->pd_seq_phys[0]); 581862306a36Sopenharmony_ci fusion->pd_seq_sync[0] = NULL; 581962306a36Sopenharmony_ci } 582062306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = false; 582162306a36Sopenharmony_ci return; 582262306a36Sopenharmony_ci } 582362306a36Sopenharmony_ci } 582462306a36Sopenharmony_ci 582562306a36Sopenharmony_ciskip_alloc: 582662306a36Sopenharmony_ci if (!megasas_sync_pd_seq_num(instance, false) && 582762306a36Sopenharmony_ci !megasas_sync_pd_seq_num(instance, true)) 582862306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = true; 582962306a36Sopenharmony_ci else 583062306a36Sopenharmony_ci instance->use_seqnum_jbod_fp = false; 583162306a36Sopenharmony_ci} 583262306a36Sopenharmony_ci 583362306a36Sopenharmony_cistatic void megasas_setup_reply_map(struct megasas_instance *instance) 583462306a36Sopenharmony_ci{ 583562306a36Sopenharmony_ci const struct cpumask *mask; 583662306a36Sopenharmony_ci unsigned int queue, cpu, low_latency_index_start; 583762306a36Sopenharmony_ci 583862306a36Sopenharmony_ci low_latency_index_start = instance->low_latency_index_start; 583962306a36Sopenharmony_ci 584062306a36Sopenharmony_ci for (queue = low_latency_index_start; queue < instance->msix_vectors; queue++) { 584162306a36Sopenharmony_ci mask = pci_irq_get_affinity(instance->pdev, queue); 584262306a36Sopenharmony_ci if (!mask) 584362306a36Sopenharmony_ci goto fallback; 584462306a36Sopenharmony_ci 584562306a36Sopenharmony_ci for_each_cpu(cpu, mask) 584662306a36Sopenharmony_ci instance->reply_map[cpu] = queue; 584762306a36Sopenharmony_ci } 584862306a36Sopenharmony_ci return; 584962306a36Sopenharmony_ci 585062306a36Sopenharmony_cifallback: 585162306a36Sopenharmony_ci queue = low_latency_index_start; 585262306a36Sopenharmony_ci for_each_possible_cpu(cpu) { 585362306a36Sopenharmony_ci instance->reply_map[cpu] = queue; 585462306a36Sopenharmony_ci if (queue == (instance->msix_vectors - 1)) 585562306a36Sopenharmony_ci queue = low_latency_index_start; 585662306a36Sopenharmony_ci else 585762306a36Sopenharmony_ci queue++; 585862306a36Sopenharmony_ci } 585962306a36Sopenharmony_ci} 586062306a36Sopenharmony_ci 586162306a36Sopenharmony_ci/** 586262306a36Sopenharmony_ci * megasas_get_device_list - Get the PD and LD device list from FW. 586362306a36Sopenharmony_ci * @instance: Adapter soft state 586462306a36Sopenharmony_ci * @return: Success or failure 586562306a36Sopenharmony_ci * 586662306a36Sopenharmony_ci * Issue DCMDs to Firmware to get the PD and LD list. 586762306a36Sopenharmony_ci * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination 586862306a36Sopenharmony_ci * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. 586962306a36Sopenharmony_ci */ 587062306a36Sopenharmony_cistatic 587162306a36Sopenharmony_ciint megasas_get_device_list(struct megasas_instance *instance) 587262306a36Sopenharmony_ci{ 587362306a36Sopenharmony_ci if (instance->enable_fw_dev_list) { 587462306a36Sopenharmony_ci if (megasas_host_device_list_query(instance, true)) 587562306a36Sopenharmony_ci return FAILED; 587662306a36Sopenharmony_ci } else { 587762306a36Sopenharmony_ci if (megasas_get_pd_list(instance) < 0) { 587862306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "failed to get PD list\n"); 587962306a36Sopenharmony_ci return FAILED; 588062306a36Sopenharmony_ci } 588162306a36Sopenharmony_ci 588262306a36Sopenharmony_ci if (megasas_ld_list_query(instance, 588362306a36Sopenharmony_ci MR_LD_QUERY_TYPE_EXPOSED_TO_HOST)) { 588462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "failed to get LD list\n"); 588562306a36Sopenharmony_ci return FAILED; 588662306a36Sopenharmony_ci } 588762306a36Sopenharmony_ci } 588862306a36Sopenharmony_ci 588962306a36Sopenharmony_ci return SUCCESS; 589062306a36Sopenharmony_ci} 589162306a36Sopenharmony_ci 589262306a36Sopenharmony_ci/** 589362306a36Sopenharmony_ci * megasas_set_high_iops_queue_affinity_and_hint - Set affinity and hint 589462306a36Sopenharmony_ci * for high IOPS queues 589562306a36Sopenharmony_ci * @instance: Adapter soft state 589662306a36Sopenharmony_ci * return: void 589762306a36Sopenharmony_ci */ 589862306a36Sopenharmony_cistatic inline void 589962306a36Sopenharmony_cimegasas_set_high_iops_queue_affinity_and_hint(struct megasas_instance *instance) 590062306a36Sopenharmony_ci{ 590162306a36Sopenharmony_ci int i; 590262306a36Sopenharmony_ci unsigned int irq; 590362306a36Sopenharmony_ci const struct cpumask *mask; 590462306a36Sopenharmony_ci 590562306a36Sopenharmony_ci if (instance->perf_mode == MR_BALANCED_PERF_MODE) { 590662306a36Sopenharmony_ci mask = cpumask_of_node(dev_to_node(&instance->pdev->dev)); 590762306a36Sopenharmony_ci 590862306a36Sopenharmony_ci for (i = 0; i < instance->low_latency_index_start; i++) { 590962306a36Sopenharmony_ci irq = pci_irq_vector(instance->pdev, i); 591062306a36Sopenharmony_ci irq_set_affinity_and_hint(irq, mask); 591162306a36Sopenharmony_ci } 591262306a36Sopenharmony_ci } 591362306a36Sopenharmony_ci} 591462306a36Sopenharmony_ci 591562306a36Sopenharmony_cistatic int 591662306a36Sopenharmony_ci__megasas_alloc_irq_vectors(struct megasas_instance *instance) 591762306a36Sopenharmony_ci{ 591862306a36Sopenharmony_ci int i, irq_flags; 591962306a36Sopenharmony_ci struct irq_affinity desc = { .pre_vectors = instance->low_latency_index_start }; 592062306a36Sopenharmony_ci struct irq_affinity *descp = &desc; 592162306a36Sopenharmony_ci 592262306a36Sopenharmony_ci irq_flags = PCI_IRQ_MSIX; 592362306a36Sopenharmony_ci 592462306a36Sopenharmony_ci if (instance->smp_affinity_enable) 592562306a36Sopenharmony_ci irq_flags |= PCI_IRQ_AFFINITY | PCI_IRQ_ALL_TYPES; 592662306a36Sopenharmony_ci else 592762306a36Sopenharmony_ci descp = NULL; 592862306a36Sopenharmony_ci 592962306a36Sopenharmony_ci /* Do not allocate msix vectors for poll_queues. 593062306a36Sopenharmony_ci * msix_vectors is always within a range of FW supported reply queue. 593162306a36Sopenharmony_ci */ 593262306a36Sopenharmony_ci i = pci_alloc_irq_vectors_affinity(instance->pdev, 593362306a36Sopenharmony_ci instance->low_latency_index_start, 593462306a36Sopenharmony_ci instance->msix_vectors - instance->iopoll_q_count, irq_flags, descp); 593562306a36Sopenharmony_ci 593662306a36Sopenharmony_ci return i; 593762306a36Sopenharmony_ci} 593862306a36Sopenharmony_ci 593962306a36Sopenharmony_ci/** 594062306a36Sopenharmony_ci * megasas_alloc_irq_vectors - Allocate IRQ vectors/enable MSI-x vectors 594162306a36Sopenharmony_ci * @instance: Adapter soft state 594262306a36Sopenharmony_ci * return: void 594362306a36Sopenharmony_ci */ 594462306a36Sopenharmony_cistatic void 594562306a36Sopenharmony_cimegasas_alloc_irq_vectors(struct megasas_instance *instance) 594662306a36Sopenharmony_ci{ 594762306a36Sopenharmony_ci int i; 594862306a36Sopenharmony_ci unsigned int num_msix_req; 594962306a36Sopenharmony_ci 595062306a36Sopenharmony_ci instance->iopoll_q_count = 0; 595162306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 595262306a36Sopenharmony_ci poll_queues) { 595362306a36Sopenharmony_ci 595462306a36Sopenharmony_ci instance->perf_mode = MR_LATENCY_PERF_MODE; 595562306a36Sopenharmony_ci instance->low_latency_index_start = 1; 595662306a36Sopenharmony_ci 595762306a36Sopenharmony_ci /* reserve for default and non-mananged pre-vector. */ 595862306a36Sopenharmony_ci if (instance->msix_vectors > (poll_queues + 2)) 595962306a36Sopenharmony_ci instance->iopoll_q_count = poll_queues; 596062306a36Sopenharmony_ci else 596162306a36Sopenharmony_ci instance->iopoll_q_count = 0; 596262306a36Sopenharmony_ci 596362306a36Sopenharmony_ci num_msix_req = num_online_cpus() + instance->low_latency_index_start; 596462306a36Sopenharmony_ci instance->msix_vectors = min(num_msix_req, 596562306a36Sopenharmony_ci instance->msix_vectors); 596662306a36Sopenharmony_ci 596762306a36Sopenharmony_ci } 596862306a36Sopenharmony_ci 596962306a36Sopenharmony_ci i = __megasas_alloc_irq_vectors(instance); 597062306a36Sopenharmony_ci 597162306a36Sopenharmony_ci if (((instance->perf_mode == MR_BALANCED_PERF_MODE) 597262306a36Sopenharmony_ci || instance->iopoll_q_count) && 597362306a36Sopenharmony_ci (i != (instance->msix_vectors - instance->iopoll_q_count))) { 597462306a36Sopenharmony_ci if (instance->msix_vectors) 597562306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 597662306a36Sopenharmony_ci /* Disable Balanced IOPS mode and try realloc vectors */ 597762306a36Sopenharmony_ci instance->perf_mode = MR_LATENCY_PERF_MODE; 597862306a36Sopenharmony_ci instance->low_latency_index_start = 1; 597962306a36Sopenharmony_ci num_msix_req = num_online_cpus() + instance->low_latency_index_start; 598062306a36Sopenharmony_ci 598162306a36Sopenharmony_ci instance->msix_vectors = min(num_msix_req, 598262306a36Sopenharmony_ci instance->msix_vectors); 598362306a36Sopenharmony_ci 598462306a36Sopenharmony_ci instance->iopoll_q_count = 0; 598562306a36Sopenharmony_ci i = __megasas_alloc_irq_vectors(instance); 598662306a36Sopenharmony_ci 598762306a36Sopenharmony_ci } 598862306a36Sopenharmony_ci 598962306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 599062306a36Sopenharmony_ci "requested/available msix %d/%d poll_queue %d\n", 599162306a36Sopenharmony_ci instance->msix_vectors - instance->iopoll_q_count, 599262306a36Sopenharmony_ci i, instance->iopoll_q_count); 599362306a36Sopenharmony_ci 599462306a36Sopenharmony_ci if (i > 0) 599562306a36Sopenharmony_ci instance->msix_vectors = i; 599662306a36Sopenharmony_ci else 599762306a36Sopenharmony_ci instance->msix_vectors = 0; 599862306a36Sopenharmony_ci 599962306a36Sopenharmony_ci if (instance->smp_affinity_enable) 600062306a36Sopenharmony_ci megasas_set_high_iops_queue_affinity_and_hint(instance); 600162306a36Sopenharmony_ci} 600262306a36Sopenharmony_ci 600362306a36Sopenharmony_ci/** 600462306a36Sopenharmony_ci * megasas_init_fw - Initializes the FW 600562306a36Sopenharmony_ci * @instance: Adapter soft state 600662306a36Sopenharmony_ci * 600762306a36Sopenharmony_ci * This is the main function for initializing firmware 600862306a36Sopenharmony_ci */ 600962306a36Sopenharmony_ci 601062306a36Sopenharmony_cistatic int megasas_init_fw(struct megasas_instance *instance) 601162306a36Sopenharmony_ci{ 601262306a36Sopenharmony_ci u32 max_sectors_1; 601362306a36Sopenharmony_ci u32 max_sectors_2, tmp_sectors, msix_enable; 601462306a36Sopenharmony_ci u32 scratch_pad_1, scratch_pad_2, scratch_pad_3, status_reg; 601562306a36Sopenharmony_ci resource_size_t base_addr; 601662306a36Sopenharmony_ci void *base_addr_phys; 601762306a36Sopenharmony_ci struct megasas_ctrl_info *ctrl_info = NULL; 601862306a36Sopenharmony_ci unsigned long bar_list; 601962306a36Sopenharmony_ci int i, j, loop; 602062306a36Sopenharmony_ci struct IOV_111 *iovPtr; 602162306a36Sopenharmony_ci struct fusion_context *fusion; 602262306a36Sopenharmony_ci bool intr_coalescing; 602362306a36Sopenharmony_ci unsigned int num_msix_req; 602462306a36Sopenharmony_ci u16 lnksta, speed; 602562306a36Sopenharmony_ci 602662306a36Sopenharmony_ci fusion = instance->ctrl_context; 602762306a36Sopenharmony_ci 602862306a36Sopenharmony_ci /* Find first memory bar */ 602962306a36Sopenharmony_ci bar_list = pci_select_bars(instance->pdev, IORESOURCE_MEM); 603062306a36Sopenharmony_ci instance->bar = find_first_bit(&bar_list, BITS_PER_LONG); 603162306a36Sopenharmony_ci if (pci_request_selected_regions(instance->pdev, 1<<instance->bar, 603262306a36Sopenharmony_ci "megasas: LSI")) { 603362306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "IO memory region busy!\n"); 603462306a36Sopenharmony_ci return -EBUSY; 603562306a36Sopenharmony_ci } 603662306a36Sopenharmony_ci 603762306a36Sopenharmony_ci base_addr = pci_resource_start(instance->pdev, instance->bar); 603862306a36Sopenharmony_ci instance->reg_set = ioremap(base_addr, 8192); 603962306a36Sopenharmony_ci 604062306a36Sopenharmony_ci if (!instance->reg_set) { 604162306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to map IO mem\n"); 604262306a36Sopenharmony_ci goto fail_ioremap; 604362306a36Sopenharmony_ci } 604462306a36Sopenharmony_ci 604562306a36Sopenharmony_ci base_addr_phys = &base_addr; 604662306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, 604762306a36Sopenharmony_ci "BAR:0x%lx BAR's base_addr(phys):%pa mapped virt_addr:0x%p\n", 604862306a36Sopenharmony_ci instance->bar, base_addr_phys, instance->reg_set); 604962306a36Sopenharmony_ci 605062306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 605162306a36Sopenharmony_ci instance->instancet = &megasas_instance_template_fusion; 605262306a36Sopenharmony_ci else { 605362306a36Sopenharmony_ci switch (instance->pdev->device) { 605462306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS1078R: 605562306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS1078DE: 605662306a36Sopenharmony_ci instance->instancet = &megasas_instance_template_ppc; 605762306a36Sopenharmony_ci break; 605862306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS1078GEN2: 605962306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS0079GEN2: 606062306a36Sopenharmony_ci instance->instancet = &megasas_instance_template_gen2; 606162306a36Sopenharmony_ci break; 606262306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS0073SKINNY: 606362306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS0071SKINNY: 606462306a36Sopenharmony_ci instance->instancet = &megasas_instance_template_skinny; 606562306a36Sopenharmony_ci break; 606662306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_SAS1064R: 606762306a36Sopenharmony_ci case PCI_DEVICE_ID_DELL_PERC5: 606862306a36Sopenharmony_ci default: 606962306a36Sopenharmony_ci instance->instancet = &megasas_instance_template_xscale; 607062306a36Sopenharmony_ci instance->pd_list_not_supported = 1; 607162306a36Sopenharmony_ci break; 607262306a36Sopenharmony_ci } 607362306a36Sopenharmony_ci } 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_ci if (megasas_transition_to_ready(instance, 0)) { 607662306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 607762306a36Sopenharmony_ci "Failed to transition controller to ready from %s!\n", 607862306a36Sopenharmony_ci __func__); 607962306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) { 608062306a36Sopenharmony_ci status_reg = instance->instancet->read_fw_status_reg( 608162306a36Sopenharmony_ci instance); 608262306a36Sopenharmony_ci if (status_reg & MFI_RESET_ADAPTER) { 608362306a36Sopenharmony_ci if (megasas_adp_reset_wait_for_ready 608462306a36Sopenharmony_ci (instance, true, 0) == FAILED) 608562306a36Sopenharmony_ci goto fail_ready_state; 608662306a36Sopenharmony_ci } else { 608762306a36Sopenharmony_ci goto fail_ready_state; 608862306a36Sopenharmony_ci } 608962306a36Sopenharmony_ci } else { 609062306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 1); 609162306a36Sopenharmony_ci instance->instancet->adp_reset 609262306a36Sopenharmony_ci (instance, instance->reg_set); 609362306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 0); 609462306a36Sopenharmony_ci 609562306a36Sopenharmony_ci /*waiting for about 30 second before retry*/ 609662306a36Sopenharmony_ci ssleep(30); 609762306a36Sopenharmony_ci 609862306a36Sopenharmony_ci if (megasas_transition_to_ready(instance, 0)) 609962306a36Sopenharmony_ci goto fail_ready_state; 610062306a36Sopenharmony_ci } 610162306a36Sopenharmony_ci 610262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 610362306a36Sopenharmony_ci "FW restarted successfully from %s!\n", 610462306a36Sopenharmony_ci __func__); 610562306a36Sopenharmony_ci } 610662306a36Sopenharmony_ci 610762306a36Sopenharmony_ci megasas_init_ctrl_params(instance); 610862306a36Sopenharmony_ci 610962306a36Sopenharmony_ci if (megasas_set_dma_mask(instance)) 611062306a36Sopenharmony_ci goto fail_ready_state; 611162306a36Sopenharmony_ci 611262306a36Sopenharmony_ci if (megasas_alloc_ctrl_mem(instance)) 611362306a36Sopenharmony_ci goto fail_alloc_dma_buf; 611462306a36Sopenharmony_ci 611562306a36Sopenharmony_ci if (megasas_alloc_ctrl_dma_buffers(instance)) 611662306a36Sopenharmony_ci goto fail_alloc_dma_buf; 611762306a36Sopenharmony_ci 611862306a36Sopenharmony_ci fusion = instance->ctrl_context; 611962306a36Sopenharmony_ci 612062306a36Sopenharmony_ci if (instance->adapter_type >= VENTURA_SERIES) { 612162306a36Sopenharmony_ci scratch_pad_2 = 612262306a36Sopenharmony_ci megasas_readl(instance, 612362306a36Sopenharmony_ci &instance->reg_set->outbound_scratch_pad_2); 612462306a36Sopenharmony_ci instance->max_raid_mapsize = ((scratch_pad_2 >> 612562306a36Sopenharmony_ci MR_MAX_RAID_MAP_SIZE_OFFSET_SHIFT) & 612662306a36Sopenharmony_ci MR_MAX_RAID_MAP_SIZE_MASK); 612762306a36Sopenharmony_ci } 612862306a36Sopenharmony_ci 612962306a36Sopenharmony_ci instance->enable_sdev_max_qd = enable_sdev_max_qd; 613062306a36Sopenharmony_ci 613162306a36Sopenharmony_ci switch (instance->adapter_type) { 613262306a36Sopenharmony_ci case VENTURA_SERIES: 613362306a36Sopenharmony_ci fusion->pcie_bw_limitation = true; 613462306a36Sopenharmony_ci break; 613562306a36Sopenharmony_ci case AERO_SERIES: 613662306a36Sopenharmony_ci fusion->r56_div_offload = true; 613762306a36Sopenharmony_ci break; 613862306a36Sopenharmony_ci default: 613962306a36Sopenharmony_ci break; 614062306a36Sopenharmony_ci } 614162306a36Sopenharmony_ci 614262306a36Sopenharmony_ci /* Check if MSI-X is supported while in ready state */ 614362306a36Sopenharmony_ci msix_enable = (instance->instancet->read_fw_status_reg(instance) & 614462306a36Sopenharmony_ci 0x4000000) >> 0x1a; 614562306a36Sopenharmony_ci if (msix_enable && !msix_disable) { 614662306a36Sopenharmony_ci 614762306a36Sopenharmony_ci scratch_pad_1 = megasas_readl 614862306a36Sopenharmony_ci (instance, &instance->reg_set->outbound_scratch_pad_1); 614962306a36Sopenharmony_ci /* Check max MSI-X vectors */ 615062306a36Sopenharmony_ci if (fusion) { 615162306a36Sopenharmony_ci if (instance->adapter_type == THUNDERBOLT_SERIES) { 615262306a36Sopenharmony_ci /* Thunderbolt Series*/ 615362306a36Sopenharmony_ci instance->msix_vectors = (scratch_pad_1 615462306a36Sopenharmony_ci & MR_MAX_REPLY_QUEUES_OFFSET) + 1; 615562306a36Sopenharmony_ci } else { 615662306a36Sopenharmony_ci instance->msix_vectors = ((scratch_pad_1 615762306a36Sopenharmony_ci & MR_MAX_REPLY_QUEUES_EXT_OFFSET) 615862306a36Sopenharmony_ci >> MR_MAX_REPLY_QUEUES_EXT_OFFSET_SHIFT) + 1; 615962306a36Sopenharmony_ci 616062306a36Sopenharmony_ci /* 616162306a36Sopenharmony_ci * For Invader series, > 8 MSI-x vectors 616262306a36Sopenharmony_ci * supported by FW/HW implies combined 616362306a36Sopenharmony_ci * reply queue mode is enabled. 616462306a36Sopenharmony_ci * For Ventura series, > 16 MSI-x vectors 616562306a36Sopenharmony_ci * supported by FW/HW implies combined 616662306a36Sopenharmony_ci * reply queue mode is enabled. 616762306a36Sopenharmony_ci */ 616862306a36Sopenharmony_ci switch (instance->adapter_type) { 616962306a36Sopenharmony_ci case INVADER_SERIES: 617062306a36Sopenharmony_ci if (instance->msix_vectors > 8) 617162306a36Sopenharmony_ci instance->msix_combined = true; 617262306a36Sopenharmony_ci break; 617362306a36Sopenharmony_ci case AERO_SERIES: 617462306a36Sopenharmony_ci case VENTURA_SERIES: 617562306a36Sopenharmony_ci if (instance->msix_vectors > 16) 617662306a36Sopenharmony_ci instance->msix_combined = true; 617762306a36Sopenharmony_ci break; 617862306a36Sopenharmony_ci } 617962306a36Sopenharmony_ci 618062306a36Sopenharmony_ci if (rdpq_enable) 618162306a36Sopenharmony_ci instance->is_rdpq = (scratch_pad_1 & MR_RDPQ_MODE_OFFSET) ? 618262306a36Sopenharmony_ci 1 : 0; 618362306a36Sopenharmony_ci 618462306a36Sopenharmony_ci if (instance->adapter_type >= INVADER_SERIES && 618562306a36Sopenharmony_ci !instance->msix_combined) { 618662306a36Sopenharmony_ci instance->msix_load_balance = true; 618762306a36Sopenharmony_ci instance->smp_affinity_enable = false; 618862306a36Sopenharmony_ci } 618962306a36Sopenharmony_ci 619062306a36Sopenharmony_ci /* Save 1-15 reply post index address to local memory 619162306a36Sopenharmony_ci * Index 0 is already saved from reg offset 619262306a36Sopenharmony_ci * MPI2_REPLY_POST_HOST_INDEX_OFFSET 619362306a36Sopenharmony_ci */ 619462306a36Sopenharmony_ci for (loop = 1; loop < MR_MAX_MSIX_REG_ARRAY; loop++) { 619562306a36Sopenharmony_ci instance->reply_post_host_index_addr[loop] = 619662306a36Sopenharmony_ci (u32 __iomem *) 619762306a36Sopenharmony_ci ((u8 __iomem *)instance->reg_set + 619862306a36Sopenharmony_ci MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET 619962306a36Sopenharmony_ci + (loop * 0x10)); 620062306a36Sopenharmony_ci } 620162306a36Sopenharmony_ci } 620262306a36Sopenharmony_ci 620362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 620462306a36Sopenharmony_ci "firmware supports msix\t: (%d)", 620562306a36Sopenharmony_ci instance->msix_vectors); 620662306a36Sopenharmony_ci if (msix_vectors) 620762306a36Sopenharmony_ci instance->msix_vectors = min(msix_vectors, 620862306a36Sopenharmony_ci instance->msix_vectors); 620962306a36Sopenharmony_ci } else /* MFI adapters */ 621062306a36Sopenharmony_ci instance->msix_vectors = 1; 621162306a36Sopenharmony_ci 621262306a36Sopenharmony_ci 621362306a36Sopenharmony_ci /* 621462306a36Sopenharmony_ci * For Aero (if some conditions are met), driver will configure a 621562306a36Sopenharmony_ci * few additional reply queues with interrupt coalescing enabled. 621662306a36Sopenharmony_ci * These queues with interrupt coalescing enabled are called 621762306a36Sopenharmony_ci * High IOPS queues and rest of reply queues (based on number of 621862306a36Sopenharmony_ci * logical CPUs) are termed as Low latency queues. 621962306a36Sopenharmony_ci * 622062306a36Sopenharmony_ci * Total Number of reply queues = High IOPS queues + low latency queues 622162306a36Sopenharmony_ci * 622262306a36Sopenharmony_ci * For rest of fusion adapters, 1 additional reply queue will be 622362306a36Sopenharmony_ci * reserved for management commands, rest of reply queues 622462306a36Sopenharmony_ci * (based on number of logical CPUs) will be used for IOs and 622562306a36Sopenharmony_ci * referenced as IO queues. 622662306a36Sopenharmony_ci * Total Number of reply queues = 1 + IO queues 622762306a36Sopenharmony_ci * 622862306a36Sopenharmony_ci * MFI adapters supports single MSI-x so single reply queue 622962306a36Sopenharmony_ci * will be used for IO and management commands. 623062306a36Sopenharmony_ci */ 623162306a36Sopenharmony_ci 623262306a36Sopenharmony_ci intr_coalescing = (scratch_pad_1 & MR_INTR_COALESCING_SUPPORT_OFFSET) ? 623362306a36Sopenharmony_ci true : false; 623462306a36Sopenharmony_ci if (intr_coalescing && 623562306a36Sopenharmony_ci (num_online_cpus() >= MR_HIGH_IOPS_QUEUE_COUNT) && 623662306a36Sopenharmony_ci (instance->msix_vectors == MEGASAS_MAX_MSIX_QUEUES)) 623762306a36Sopenharmony_ci instance->perf_mode = MR_BALANCED_PERF_MODE; 623862306a36Sopenharmony_ci else 623962306a36Sopenharmony_ci instance->perf_mode = MR_LATENCY_PERF_MODE; 624062306a36Sopenharmony_ci 624162306a36Sopenharmony_ci 624262306a36Sopenharmony_ci if (instance->adapter_type == AERO_SERIES) { 624362306a36Sopenharmony_ci pcie_capability_read_word(instance->pdev, PCI_EXP_LNKSTA, &lnksta); 624462306a36Sopenharmony_ci speed = lnksta & PCI_EXP_LNKSTA_CLS; 624562306a36Sopenharmony_ci 624662306a36Sopenharmony_ci /* 624762306a36Sopenharmony_ci * For Aero, if PCIe link speed is <16 GT/s, then driver should operate 624862306a36Sopenharmony_ci * in latency perf mode and enable R1 PCI bandwidth algorithm 624962306a36Sopenharmony_ci */ 625062306a36Sopenharmony_ci if (speed < 0x4) { 625162306a36Sopenharmony_ci instance->perf_mode = MR_LATENCY_PERF_MODE; 625262306a36Sopenharmony_ci fusion->pcie_bw_limitation = true; 625362306a36Sopenharmony_ci } 625462306a36Sopenharmony_ci 625562306a36Sopenharmony_ci /* 625662306a36Sopenharmony_ci * Performance mode settings provided through module parameter-perf_mode will 625762306a36Sopenharmony_ci * take affect only for: 625862306a36Sopenharmony_ci * 1. Aero family of adapters. 625962306a36Sopenharmony_ci * 2. When user sets module parameter- perf_mode in range of 0-2. 626062306a36Sopenharmony_ci */ 626162306a36Sopenharmony_ci if ((perf_mode >= MR_BALANCED_PERF_MODE) && 626262306a36Sopenharmony_ci (perf_mode <= MR_LATENCY_PERF_MODE)) 626362306a36Sopenharmony_ci instance->perf_mode = perf_mode; 626462306a36Sopenharmony_ci /* 626562306a36Sopenharmony_ci * If intr coalescing is not supported by controller FW, then IOPS 626662306a36Sopenharmony_ci * and Balanced modes are not feasible. 626762306a36Sopenharmony_ci */ 626862306a36Sopenharmony_ci if (!intr_coalescing) 626962306a36Sopenharmony_ci instance->perf_mode = MR_LATENCY_PERF_MODE; 627062306a36Sopenharmony_ci 627162306a36Sopenharmony_ci } 627262306a36Sopenharmony_ci 627362306a36Sopenharmony_ci if (instance->perf_mode == MR_BALANCED_PERF_MODE) 627462306a36Sopenharmony_ci instance->low_latency_index_start = 627562306a36Sopenharmony_ci MR_HIGH_IOPS_QUEUE_COUNT; 627662306a36Sopenharmony_ci else 627762306a36Sopenharmony_ci instance->low_latency_index_start = 1; 627862306a36Sopenharmony_ci 627962306a36Sopenharmony_ci num_msix_req = num_online_cpus() + instance->low_latency_index_start; 628062306a36Sopenharmony_ci 628162306a36Sopenharmony_ci instance->msix_vectors = min(num_msix_req, 628262306a36Sopenharmony_ci instance->msix_vectors); 628362306a36Sopenharmony_ci 628462306a36Sopenharmony_ci megasas_alloc_irq_vectors(instance); 628562306a36Sopenharmony_ci if (!instance->msix_vectors) 628662306a36Sopenharmony_ci instance->msix_load_balance = false; 628762306a36Sopenharmony_ci } 628862306a36Sopenharmony_ci /* 628962306a36Sopenharmony_ci * MSI-X host index 0 is common for all adapter. 629062306a36Sopenharmony_ci * It is used for all MPT based Adapters. 629162306a36Sopenharmony_ci */ 629262306a36Sopenharmony_ci if (instance->msix_combined) { 629362306a36Sopenharmony_ci instance->reply_post_host_index_addr[0] = 629462306a36Sopenharmony_ci (u32 *)((u8 *)instance->reg_set + 629562306a36Sopenharmony_ci MPI2_SUP_REPLY_POST_HOST_INDEX_OFFSET); 629662306a36Sopenharmony_ci } else { 629762306a36Sopenharmony_ci instance->reply_post_host_index_addr[0] = 629862306a36Sopenharmony_ci (u32 *)((u8 *)instance->reg_set + 629962306a36Sopenharmony_ci MPI2_REPLY_POST_HOST_INDEX_OFFSET); 630062306a36Sopenharmony_ci } 630162306a36Sopenharmony_ci 630262306a36Sopenharmony_ci if (!instance->msix_vectors) { 630362306a36Sopenharmony_ci i = pci_alloc_irq_vectors(instance->pdev, 1, 1, PCI_IRQ_LEGACY); 630462306a36Sopenharmony_ci if (i < 0) 630562306a36Sopenharmony_ci goto fail_init_adapter; 630662306a36Sopenharmony_ci } 630762306a36Sopenharmony_ci 630862306a36Sopenharmony_ci megasas_setup_reply_map(instance); 630962306a36Sopenharmony_ci 631062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 631162306a36Sopenharmony_ci "current msix/online cpus\t: (%d/%d)\n", 631262306a36Sopenharmony_ci instance->msix_vectors, (unsigned int)num_online_cpus()); 631362306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 631462306a36Sopenharmony_ci "RDPQ mode\t: (%s)\n", instance->is_rdpq ? "enabled" : "disabled"); 631562306a36Sopenharmony_ci 631662306a36Sopenharmony_ci tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, 631762306a36Sopenharmony_ci (unsigned long)instance); 631862306a36Sopenharmony_ci 631962306a36Sopenharmony_ci /* 632062306a36Sopenharmony_ci * Below are default value for legacy Firmware. 632162306a36Sopenharmony_ci * non-fusion based controllers 632262306a36Sopenharmony_ci */ 632362306a36Sopenharmony_ci instance->fw_supported_vd_count = MAX_LOGICAL_DRIVES; 632462306a36Sopenharmony_ci instance->fw_supported_pd_count = MAX_PHYSICAL_DEVICES; 632562306a36Sopenharmony_ci /* Get operational params, sge flags, send init cmd to controller */ 632662306a36Sopenharmony_ci if (instance->instancet->init_adapter(instance)) 632762306a36Sopenharmony_ci goto fail_init_adapter; 632862306a36Sopenharmony_ci 632962306a36Sopenharmony_ci if (instance->adapter_type >= VENTURA_SERIES) { 633062306a36Sopenharmony_ci scratch_pad_3 = 633162306a36Sopenharmony_ci megasas_readl(instance, 633262306a36Sopenharmony_ci &instance->reg_set->outbound_scratch_pad_3); 633362306a36Sopenharmony_ci if ((scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK) >= 633462306a36Sopenharmony_ci MR_DEFAULT_NVME_PAGE_SHIFT) 633562306a36Sopenharmony_ci instance->nvme_page_size = 633662306a36Sopenharmony_ci (1 << (scratch_pad_3 & MR_NVME_PAGE_SIZE_MASK)); 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 633962306a36Sopenharmony_ci "NVME page size\t: (%d)\n", instance->nvme_page_size); 634062306a36Sopenharmony_ci } 634162306a36Sopenharmony_ci 634262306a36Sopenharmony_ci if (instance->msix_vectors ? 634362306a36Sopenharmony_ci megasas_setup_irqs_msix(instance, 1) : 634462306a36Sopenharmony_ci megasas_setup_irqs_ioapic(instance)) 634562306a36Sopenharmony_ci goto fail_init_adapter; 634662306a36Sopenharmony_ci 634762306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 634862306a36Sopenharmony_ci megasas_setup_irq_poll(instance); 634962306a36Sopenharmony_ci 635062306a36Sopenharmony_ci instance->instancet->enable_intr(instance); 635162306a36Sopenharmony_ci 635262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "INIT adapter done\n"); 635362306a36Sopenharmony_ci 635462306a36Sopenharmony_ci megasas_setup_jbod_map(instance); 635562306a36Sopenharmony_ci 635662306a36Sopenharmony_ci if (megasas_get_device_list(instance) != SUCCESS) { 635762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 635862306a36Sopenharmony_ci "%s: megasas_get_device_list failed\n", 635962306a36Sopenharmony_ci __func__); 636062306a36Sopenharmony_ci goto fail_get_ld_pd_list; 636162306a36Sopenharmony_ci } 636262306a36Sopenharmony_ci 636362306a36Sopenharmony_ci /* stream detection initialization */ 636462306a36Sopenharmony_ci if (instance->adapter_type >= VENTURA_SERIES) { 636562306a36Sopenharmony_ci fusion->stream_detect_by_ld = 636662306a36Sopenharmony_ci kcalloc(MAX_LOGICAL_DRIVES_EXT, 636762306a36Sopenharmony_ci sizeof(struct LD_STREAM_DETECT *), 636862306a36Sopenharmony_ci GFP_KERNEL); 636962306a36Sopenharmony_ci if (!fusion->stream_detect_by_ld) { 637062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 637162306a36Sopenharmony_ci "unable to allocate stream detection for pool of LDs\n"); 637262306a36Sopenharmony_ci goto fail_get_ld_pd_list; 637362306a36Sopenharmony_ci } 637462306a36Sopenharmony_ci for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i) { 637562306a36Sopenharmony_ci fusion->stream_detect_by_ld[i] = 637662306a36Sopenharmony_ci kzalloc(sizeof(struct LD_STREAM_DETECT), 637762306a36Sopenharmony_ci GFP_KERNEL); 637862306a36Sopenharmony_ci if (!fusion->stream_detect_by_ld[i]) { 637962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 638062306a36Sopenharmony_ci "unable to allocate stream detect by LD\n "); 638162306a36Sopenharmony_ci for (j = 0; j < i; ++j) 638262306a36Sopenharmony_ci kfree(fusion->stream_detect_by_ld[j]); 638362306a36Sopenharmony_ci kfree(fusion->stream_detect_by_ld); 638462306a36Sopenharmony_ci fusion->stream_detect_by_ld = NULL; 638562306a36Sopenharmony_ci goto fail_get_ld_pd_list; 638662306a36Sopenharmony_ci } 638762306a36Sopenharmony_ci fusion->stream_detect_by_ld[i]->mru_bit_map 638862306a36Sopenharmony_ci = MR_STREAM_BITMAP; 638962306a36Sopenharmony_ci } 639062306a36Sopenharmony_ci } 639162306a36Sopenharmony_ci 639262306a36Sopenharmony_ci /* 639362306a36Sopenharmony_ci * Compute the max allowed sectors per IO: The controller info has two 639462306a36Sopenharmony_ci * limits on max sectors. Driver should use the minimum of these two. 639562306a36Sopenharmony_ci * 639662306a36Sopenharmony_ci * 1 << stripe_sz_ops.min = max sectors per strip 639762306a36Sopenharmony_ci * 639862306a36Sopenharmony_ci * Note that older firmwares ( < FW ver 30) didn't report information 639962306a36Sopenharmony_ci * to calculate max_sectors_1. So the number ended up as zero always. 640062306a36Sopenharmony_ci */ 640162306a36Sopenharmony_ci tmp_sectors = 0; 640262306a36Sopenharmony_ci ctrl_info = instance->ctrl_info_buf; 640362306a36Sopenharmony_ci 640462306a36Sopenharmony_ci max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) * 640562306a36Sopenharmony_ci le16_to_cpu(ctrl_info->max_strips_per_io); 640662306a36Sopenharmony_ci max_sectors_2 = le32_to_cpu(ctrl_info->max_request_size); 640762306a36Sopenharmony_ci 640862306a36Sopenharmony_ci tmp_sectors = min_t(u32, max_sectors_1, max_sectors_2); 640962306a36Sopenharmony_ci 641062306a36Sopenharmony_ci instance->peerIsPresent = ctrl_info->cluster.peerIsPresent; 641162306a36Sopenharmony_ci instance->passive = ctrl_info->cluster.passive; 641262306a36Sopenharmony_ci memcpy(instance->clusterId, ctrl_info->clusterId, sizeof(instance->clusterId)); 641362306a36Sopenharmony_ci instance->UnevenSpanSupport = 641462306a36Sopenharmony_ci ctrl_info->adapterOperations2.supportUnevenSpans; 641562306a36Sopenharmony_ci if (instance->UnevenSpanSupport) { 641662306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 641762306a36Sopenharmony_ci if (MR_ValidateMapInfo(instance, instance->map_id)) 641862306a36Sopenharmony_ci fusion->fast_path_io = 1; 641962306a36Sopenharmony_ci else 642062306a36Sopenharmony_ci fusion->fast_path_io = 0; 642162306a36Sopenharmony_ci 642262306a36Sopenharmony_ci } 642362306a36Sopenharmony_ci if (ctrl_info->host_interface.SRIOV) { 642462306a36Sopenharmony_ci instance->requestorId = ctrl_info->iov.requestorId; 642562306a36Sopenharmony_ci if (instance->pdev->device == PCI_DEVICE_ID_LSI_PLASMA) { 642662306a36Sopenharmony_ci if (!ctrl_info->adapterOperations2.activePassive) 642762306a36Sopenharmony_ci instance->PlasmaFW111 = 1; 642862306a36Sopenharmony_ci 642962306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "SR-IOV: firmware type: %s\n", 643062306a36Sopenharmony_ci instance->PlasmaFW111 ? "1.11" : "new"); 643162306a36Sopenharmony_ci 643262306a36Sopenharmony_ci if (instance->PlasmaFW111) { 643362306a36Sopenharmony_ci iovPtr = (struct IOV_111 *) 643462306a36Sopenharmony_ci ((unsigned char *)ctrl_info + IOV_111_OFFSET); 643562306a36Sopenharmony_ci instance->requestorId = iovPtr->requestorId; 643662306a36Sopenharmony_ci } 643762306a36Sopenharmony_ci } 643862306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "SRIOV: VF requestorId %d\n", 643962306a36Sopenharmony_ci instance->requestorId); 644062306a36Sopenharmony_ci } 644162306a36Sopenharmony_ci 644262306a36Sopenharmony_ci instance->crash_dump_fw_support = 644362306a36Sopenharmony_ci ctrl_info->adapterOperations3.supportCrashDump; 644462306a36Sopenharmony_ci instance->crash_dump_drv_support = 644562306a36Sopenharmony_ci (instance->crash_dump_fw_support && 644662306a36Sopenharmony_ci instance->crash_dump_buf); 644762306a36Sopenharmony_ci if (instance->crash_dump_drv_support) 644862306a36Sopenharmony_ci megasas_set_crash_dump_params(instance, 644962306a36Sopenharmony_ci MR_CRASH_BUF_TURN_OFF); 645062306a36Sopenharmony_ci 645162306a36Sopenharmony_ci else { 645262306a36Sopenharmony_ci if (instance->crash_dump_buf) 645362306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 645462306a36Sopenharmony_ci CRASH_DMA_BUF_SIZE, 645562306a36Sopenharmony_ci instance->crash_dump_buf, 645662306a36Sopenharmony_ci instance->crash_dump_h); 645762306a36Sopenharmony_ci instance->crash_dump_buf = NULL; 645862306a36Sopenharmony_ci } 645962306a36Sopenharmony_ci 646062306a36Sopenharmony_ci if (instance->snapdump_wait_time) { 646162306a36Sopenharmony_ci megasas_get_snapdump_properties(instance); 646262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Snap dump wait time\t: %d\n", 646362306a36Sopenharmony_ci instance->snapdump_wait_time); 646462306a36Sopenharmony_ci } 646562306a36Sopenharmony_ci 646662306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 646762306a36Sopenharmony_ci "pci id\t\t: (0x%04x)/(0x%04x)/(0x%04x)/(0x%04x)\n", 646862306a36Sopenharmony_ci le16_to_cpu(ctrl_info->pci.vendor_id), 646962306a36Sopenharmony_ci le16_to_cpu(ctrl_info->pci.device_id), 647062306a36Sopenharmony_ci le16_to_cpu(ctrl_info->pci.sub_vendor_id), 647162306a36Sopenharmony_ci le16_to_cpu(ctrl_info->pci.sub_device_id)); 647262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "unevenspan support : %s\n", 647362306a36Sopenharmony_ci instance->UnevenSpanSupport ? "yes" : "no"); 647462306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "firmware crash dump : %s\n", 647562306a36Sopenharmony_ci instance->crash_dump_drv_support ? "yes" : "no"); 647662306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "JBOD sequence map : %s\n", 647762306a36Sopenharmony_ci instance->use_seqnum_jbod_fp ? "enabled" : "disabled"); 647862306a36Sopenharmony_ci 647962306a36Sopenharmony_ci instance->max_sectors_per_req = instance->max_num_sge * 648062306a36Sopenharmony_ci SGE_BUFFER_SIZE / 512; 648162306a36Sopenharmony_ci if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors)) 648262306a36Sopenharmony_ci instance->max_sectors_per_req = tmp_sectors; 648362306a36Sopenharmony_ci 648462306a36Sopenharmony_ci /* Check for valid throttlequeuedepth module parameter */ 648562306a36Sopenharmony_ci if (throttlequeuedepth && 648662306a36Sopenharmony_ci throttlequeuedepth <= instance->max_scsi_cmds) 648762306a36Sopenharmony_ci instance->throttlequeuedepth = throttlequeuedepth; 648862306a36Sopenharmony_ci else 648962306a36Sopenharmony_ci instance->throttlequeuedepth = 649062306a36Sopenharmony_ci MEGASAS_THROTTLE_QUEUE_DEPTH; 649162306a36Sopenharmony_ci 649262306a36Sopenharmony_ci if ((resetwaittime < 1) || 649362306a36Sopenharmony_ci (resetwaittime > MEGASAS_RESET_WAIT_TIME)) 649462306a36Sopenharmony_ci resetwaittime = MEGASAS_RESET_WAIT_TIME; 649562306a36Sopenharmony_ci 649662306a36Sopenharmony_ci if ((scmd_timeout < 10) || (scmd_timeout > MEGASAS_DEFAULT_CMD_TIMEOUT)) 649762306a36Sopenharmony_ci scmd_timeout = MEGASAS_DEFAULT_CMD_TIMEOUT; 649862306a36Sopenharmony_ci 649962306a36Sopenharmony_ci /* Launch SR-IOV heartbeat timer */ 650062306a36Sopenharmony_ci if (instance->requestorId) { 650162306a36Sopenharmony_ci if (!megasas_sriov_start_heartbeat(instance, 1)) { 650262306a36Sopenharmony_ci megasas_start_timer(instance); 650362306a36Sopenharmony_ci } else { 650462306a36Sopenharmony_ci instance->skip_heartbeat_timer_del = 1; 650562306a36Sopenharmony_ci goto fail_get_ld_pd_list; 650662306a36Sopenharmony_ci } 650762306a36Sopenharmony_ci } 650862306a36Sopenharmony_ci 650962306a36Sopenharmony_ci /* 651062306a36Sopenharmony_ci * Create and start watchdog thread which will monitor 651162306a36Sopenharmony_ci * controller state every 1 sec and trigger OCR when 651262306a36Sopenharmony_ci * it enters fault state 651362306a36Sopenharmony_ci */ 651462306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 651562306a36Sopenharmony_ci if (megasas_fusion_start_watchdog(instance) != SUCCESS) 651662306a36Sopenharmony_ci goto fail_start_watchdog; 651762306a36Sopenharmony_ci 651862306a36Sopenharmony_ci return 0; 651962306a36Sopenharmony_ci 652062306a36Sopenharmony_cifail_start_watchdog: 652162306a36Sopenharmony_ci if (instance->requestorId && !instance->skip_heartbeat_timer_del) 652262306a36Sopenharmony_ci del_timer_sync(&instance->sriov_heartbeat_timer); 652362306a36Sopenharmony_cifail_get_ld_pd_list: 652462306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 652562306a36Sopenharmony_ci megasas_destroy_irqs(instance); 652662306a36Sopenharmony_cifail_init_adapter: 652762306a36Sopenharmony_ci if (instance->msix_vectors) 652862306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 652962306a36Sopenharmony_ci instance->msix_vectors = 0; 653062306a36Sopenharmony_cifail_alloc_dma_buf: 653162306a36Sopenharmony_ci megasas_free_ctrl_dma_buffers(instance); 653262306a36Sopenharmony_ci megasas_free_ctrl_mem(instance); 653362306a36Sopenharmony_cifail_ready_state: 653462306a36Sopenharmony_ci iounmap(instance->reg_set); 653562306a36Sopenharmony_ci 653662306a36Sopenharmony_cifail_ioremap: 653762306a36Sopenharmony_ci pci_release_selected_regions(instance->pdev, 1<<instance->bar); 653862306a36Sopenharmony_ci 653962306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed from %s %d\n", 654062306a36Sopenharmony_ci __func__, __LINE__); 654162306a36Sopenharmony_ci return -EINVAL; 654262306a36Sopenharmony_ci} 654362306a36Sopenharmony_ci 654462306a36Sopenharmony_ci/** 654562306a36Sopenharmony_ci * megasas_release_mfi - Reverses the FW initialization 654662306a36Sopenharmony_ci * @instance: Adapter soft state 654762306a36Sopenharmony_ci */ 654862306a36Sopenharmony_cistatic void megasas_release_mfi(struct megasas_instance *instance) 654962306a36Sopenharmony_ci{ 655062306a36Sopenharmony_ci u32 reply_q_sz = sizeof(u32) *(instance->max_mfi_cmds + 1); 655162306a36Sopenharmony_ci 655262306a36Sopenharmony_ci if (instance->reply_queue) 655362306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, reply_q_sz, 655462306a36Sopenharmony_ci instance->reply_queue, instance->reply_queue_h); 655562306a36Sopenharmony_ci 655662306a36Sopenharmony_ci megasas_free_cmds(instance); 655762306a36Sopenharmony_ci 655862306a36Sopenharmony_ci iounmap(instance->reg_set); 655962306a36Sopenharmony_ci 656062306a36Sopenharmony_ci pci_release_selected_regions(instance->pdev, 1<<instance->bar); 656162306a36Sopenharmony_ci} 656262306a36Sopenharmony_ci 656362306a36Sopenharmony_ci/** 656462306a36Sopenharmony_ci * megasas_get_seq_num - Gets latest event sequence numbers 656562306a36Sopenharmony_ci * @instance: Adapter soft state 656662306a36Sopenharmony_ci * @eli: FW event log sequence numbers information 656762306a36Sopenharmony_ci * 656862306a36Sopenharmony_ci * FW maintains a log of all events in a non-volatile area. Upper layers would 656962306a36Sopenharmony_ci * usually find out the latest sequence number of the events, the seq number at 657062306a36Sopenharmony_ci * the boot etc. They would "read" all the events below the latest seq number 657162306a36Sopenharmony_ci * by issuing a direct fw cmd (DCMD). For the future events (beyond latest seq 657262306a36Sopenharmony_ci * number), they would subsribe to AEN (asynchronous event notification) and 657362306a36Sopenharmony_ci * wait for the events to happen. 657462306a36Sopenharmony_ci */ 657562306a36Sopenharmony_cistatic int 657662306a36Sopenharmony_cimegasas_get_seq_num(struct megasas_instance *instance, 657762306a36Sopenharmony_ci struct megasas_evt_log_info *eli) 657862306a36Sopenharmony_ci{ 657962306a36Sopenharmony_ci struct megasas_cmd *cmd; 658062306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 658162306a36Sopenharmony_ci struct megasas_evt_log_info *el_info; 658262306a36Sopenharmony_ci dma_addr_t el_info_h = 0; 658362306a36Sopenharmony_ci int ret; 658462306a36Sopenharmony_ci 658562306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 658662306a36Sopenharmony_ci 658762306a36Sopenharmony_ci if (!cmd) { 658862306a36Sopenharmony_ci return -ENOMEM; 658962306a36Sopenharmony_ci } 659062306a36Sopenharmony_ci 659162306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 659262306a36Sopenharmony_ci el_info = dma_alloc_coherent(&instance->pdev->dev, 659362306a36Sopenharmony_ci sizeof(struct megasas_evt_log_info), 659462306a36Sopenharmony_ci &el_info_h, GFP_KERNEL); 659562306a36Sopenharmony_ci if (!el_info) { 659662306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 659762306a36Sopenharmony_ci return -ENOMEM; 659862306a36Sopenharmony_ci } 659962306a36Sopenharmony_ci 660062306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 660162306a36Sopenharmony_ci 660262306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 660362306a36Sopenharmony_ci dcmd->cmd_status = 0x0; 660462306a36Sopenharmony_ci dcmd->sge_count = 1; 660562306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 660662306a36Sopenharmony_ci dcmd->timeout = 0; 660762306a36Sopenharmony_ci dcmd->pad_0 = 0; 660862306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct megasas_evt_log_info)); 660962306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_EVENT_GET_INFO); 661062306a36Sopenharmony_ci 661162306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, el_info_h, 661262306a36Sopenharmony_ci sizeof(struct megasas_evt_log_info)); 661362306a36Sopenharmony_ci 661462306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS); 661562306a36Sopenharmony_ci if (ret != DCMD_SUCCESS) { 661662306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed from %s %d\n", 661762306a36Sopenharmony_ci __func__, __LINE__); 661862306a36Sopenharmony_ci goto dcmd_failed; 661962306a36Sopenharmony_ci } 662062306a36Sopenharmony_ci 662162306a36Sopenharmony_ci /* 662262306a36Sopenharmony_ci * Copy the data back into callers buffer 662362306a36Sopenharmony_ci */ 662462306a36Sopenharmony_ci eli->newest_seq_num = el_info->newest_seq_num; 662562306a36Sopenharmony_ci eli->oldest_seq_num = el_info->oldest_seq_num; 662662306a36Sopenharmony_ci eli->clear_seq_num = el_info->clear_seq_num; 662762306a36Sopenharmony_ci eli->shutdown_seq_num = el_info->shutdown_seq_num; 662862306a36Sopenharmony_ci eli->boot_seq_num = el_info->boot_seq_num; 662962306a36Sopenharmony_ci 663062306a36Sopenharmony_cidcmd_failed: 663162306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 663262306a36Sopenharmony_ci sizeof(struct megasas_evt_log_info), 663362306a36Sopenharmony_ci el_info, el_info_h); 663462306a36Sopenharmony_ci 663562306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 663662306a36Sopenharmony_ci 663762306a36Sopenharmony_ci return ret; 663862306a36Sopenharmony_ci} 663962306a36Sopenharmony_ci 664062306a36Sopenharmony_ci/** 664162306a36Sopenharmony_ci * megasas_register_aen - Registers for asynchronous event notification 664262306a36Sopenharmony_ci * @instance: Adapter soft state 664362306a36Sopenharmony_ci * @seq_num: The starting sequence number 664462306a36Sopenharmony_ci * @class_locale_word: Class of the event 664562306a36Sopenharmony_ci * 664662306a36Sopenharmony_ci * This function subscribes for AEN for events beyond the @seq_num. It requests 664762306a36Sopenharmony_ci * to be notified if and only if the event is of type @class_locale 664862306a36Sopenharmony_ci */ 664962306a36Sopenharmony_cistatic int 665062306a36Sopenharmony_cimegasas_register_aen(struct megasas_instance *instance, u32 seq_num, 665162306a36Sopenharmony_ci u32 class_locale_word) 665262306a36Sopenharmony_ci{ 665362306a36Sopenharmony_ci int ret_val; 665462306a36Sopenharmony_ci struct megasas_cmd *cmd; 665562306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 665662306a36Sopenharmony_ci union megasas_evt_class_locale curr_aen; 665762306a36Sopenharmony_ci union megasas_evt_class_locale prev_aen; 665862306a36Sopenharmony_ci 665962306a36Sopenharmony_ci /* 666062306a36Sopenharmony_ci * If there an AEN pending already (aen_cmd), check if the 666162306a36Sopenharmony_ci * class_locale of that pending AEN is inclusive of the new 666262306a36Sopenharmony_ci * AEN request we currently have. If it is, then we don't have 666362306a36Sopenharmony_ci * to do anything. In other words, whichever events the current 666462306a36Sopenharmony_ci * AEN request is subscribing to, have already been subscribed 666562306a36Sopenharmony_ci * to. 666662306a36Sopenharmony_ci * 666762306a36Sopenharmony_ci * If the old_cmd is _not_ inclusive, then we have to abort 666862306a36Sopenharmony_ci * that command, form a class_locale that is superset of both 666962306a36Sopenharmony_ci * old and current and re-issue to the FW 667062306a36Sopenharmony_ci */ 667162306a36Sopenharmony_ci 667262306a36Sopenharmony_ci curr_aen.word = class_locale_word; 667362306a36Sopenharmony_ci 667462306a36Sopenharmony_ci if (instance->aen_cmd) { 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci prev_aen.word = 667762306a36Sopenharmony_ci le32_to_cpu(instance->aen_cmd->frame->dcmd.mbox.w[1]); 667862306a36Sopenharmony_ci 667962306a36Sopenharmony_ci if ((curr_aen.members.class < MFI_EVT_CLASS_DEBUG) || 668062306a36Sopenharmony_ci (curr_aen.members.class > MFI_EVT_CLASS_DEAD)) { 668162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 668262306a36Sopenharmony_ci "%s %d out of range class %d send by application\n", 668362306a36Sopenharmony_ci __func__, __LINE__, curr_aen.members.class); 668462306a36Sopenharmony_ci return 0; 668562306a36Sopenharmony_ci } 668662306a36Sopenharmony_ci 668762306a36Sopenharmony_ci /* 668862306a36Sopenharmony_ci * A class whose enum value is smaller is inclusive of all 668962306a36Sopenharmony_ci * higher values. If a PROGRESS (= -1) was previously 669062306a36Sopenharmony_ci * registered, then a new registration requests for higher 669162306a36Sopenharmony_ci * classes need not be sent to FW. They are automatically 669262306a36Sopenharmony_ci * included. 669362306a36Sopenharmony_ci * 669462306a36Sopenharmony_ci * Locale numbers don't have such hierarchy. They are bitmap 669562306a36Sopenharmony_ci * values 669662306a36Sopenharmony_ci */ 669762306a36Sopenharmony_ci if ((prev_aen.members.class <= curr_aen.members.class) && 669862306a36Sopenharmony_ci !((prev_aen.members.locale & curr_aen.members.locale) ^ 669962306a36Sopenharmony_ci curr_aen.members.locale)) { 670062306a36Sopenharmony_ci /* 670162306a36Sopenharmony_ci * Previously issued event registration includes 670262306a36Sopenharmony_ci * current request. Nothing to do. 670362306a36Sopenharmony_ci */ 670462306a36Sopenharmony_ci return 0; 670562306a36Sopenharmony_ci } else { 670662306a36Sopenharmony_ci curr_aen.members.locale |= prev_aen.members.locale; 670762306a36Sopenharmony_ci 670862306a36Sopenharmony_ci if (prev_aen.members.class < curr_aen.members.class) 670962306a36Sopenharmony_ci curr_aen.members.class = prev_aen.members.class; 671062306a36Sopenharmony_ci 671162306a36Sopenharmony_ci instance->aen_cmd->abort_aen = 1; 671262306a36Sopenharmony_ci ret_val = megasas_issue_blocked_abort_cmd(instance, 671362306a36Sopenharmony_ci instance-> 671462306a36Sopenharmony_ci aen_cmd, 30); 671562306a36Sopenharmony_ci 671662306a36Sopenharmony_ci if (ret_val) { 671762306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to abort " 671862306a36Sopenharmony_ci "previous AEN command\n"); 671962306a36Sopenharmony_ci return ret_val; 672062306a36Sopenharmony_ci } 672162306a36Sopenharmony_ci } 672262306a36Sopenharmony_ci } 672362306a36Sopenharmony_ci 672462306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 672562306a36Sopenharmony_ci 672662306a36Sopenharmony_ci if (!cmd) 672762306a36Sopenharmony_ci return -ENOMEM; 672862306a36Sopenharmony_ci 672962306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 673062306a36Sopenharmony_ci 673162306a36Sopenharmony_ci memset(instance->evt_detail, 0, sizeof(struct megasas_evt_detail)); 673262306a36Sopenharmony_ci 673362306a36Sopenharmony_ci /* 673462306a36Sopenharmony_ci * Prepare DCMD for aen registration 673562306a36Sopenharmony_ci */ 673662306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 673762306a36Sopenharmony_ci 673862306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 673962306a36Sopenharmony_ci dcmd->cmd_status = 0x0; 674062306a36Sopenharmony_ci dcmd->sge_count = 1; 674162306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 674262306a36Sopenharmony_ci dcmd->timeout = 0; 674362306a36Sopenharmony_ci dcmd->pad_0 = 0; 674462306a36Sopenharmony_ci dcmd->data_xfer_len = cpu_to_le32(sizeof(struct megasas_evt_detail)); 674562306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_EVENT_WAIT); 674662306a36Sopenharmony_ci dcmd->mbox.w[0] = cpu_to_le32(seq_num); 674762306a36Sopenharmony_ci instance->last_seq_num = seq_num; 674862306a36Sopenharmony_ci dcmd->mbox.w[1] = cpu_to_le32(curr_aen.word); 674962306a36Sopenharmony_ci 675062306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, instance->evt_detail_h, 675162306a36Sopenharmony_ci sizeof(struct megasas_evt_detail)); 675262306a36Sopenharmony_ci 675362306a36Sopenharmony_ci if (instance->aen_cmd != NULL) { 675462306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 675562306a36Sopenharmony_ci return 0; 675662306a36Sopenharmony_ci } 675762306a36Sopenharmony_ci 675862306a36Sopenharmony_ci /* 675962306a36Sopenharmony_ci * Store reference to the cmd used to register for AEN. When an 676062306a36Sopenharmony_ci * application wants us to register for AEN, we have to abort this 676162306a36Sopenharmony_ci * cmd and re-register with a new EVENT LOCALE supplied by that app 676262306a36Sopenharmony_ci */ 676362306a36Sopenharmony_ci instance->aen_cmd = cmd; 676462306a36Sopenharmony_ci 676562306a36Sopenharmony_ci /* 676662306a36Sopenharmony_ci * Issue the aen registration frame 676762306a36Sopenharmony_ci */ 676862306a36Sopenharmony_ci instance->instancet->issue_dcmd(instance, cmd); 676962306a36Sopenharmony_ci 677062306a36Sopenharmony_ci return 0; 677162306a36Sopenharmony_ci} 677262306a36Sopenharmony_ci 677362306a36Sopenharmony_ci/* megasas_get_target_prop - Send DCMD with below details to firmware. 677462306a36Sopenharmony_ci * 677562306a36Sopenharmony_ci * This DCMD will fetch few properties of LD/system PD defined 677662306a36Sopenharmony_ci * in MR_TARGET_DEV_PROPERTIES. eg. Queue Depth, MDTS value. 677762306a36Sopenharmony_ci * 677862306a36Sopenharmony_ci * DCMD send by drivers whenever new target is added to the OS. 677962306a36Sopenharmony_ci * 678062306a36Sopenharmony_ci * dcmd.opcode - MR_DCMD_DEV_GET_TARGET_PROP 678162306a36Sopenharmony_ci * dcmd.mbox.b[0] - DCMD is to be fired for LD or system PD. 678262306a36Sopenharmony_ci * 0 = system PD, 1 = LD. 678362306a36Sopenharmony_ci * dcmd.mbox.s[1] - TargetID for LD/system PD. 678462306a36Sopenharmony_ci * dcmd.sge IN - Pointer to return MR_TARGET_DEV_PROPERTIES. 678562306a36Sopenharmony_ci * 678662306a36Sopenharmony_ci * @instance: Adapter soft state 678762306a36Sopenharmony_ci * @sdev: OS provided scsi device 678862306a36Sopenharmony_ci * 678962306a36Sopenharmony_ci * Returns 0 on success non-zero on failure. 679062306a36Sopenharmony_ci */ 679162306a36Sopenharmony_ciint 679262306a36Sopenharmony_cimegasas_get_target_prop(struct megasas_instance *instance, 679362306a36Sopenharmony_ci struct scsi_device *sdev) 679462306a36Sopenharmony_ci{ 679562306a36Sopenharmony_ci int ret; 679662306a36Sopenharmony_ci struct megasas_cmd *cmd; 679762306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 679862306a36Sopenharmony_ci u16 targetId = ((sdev->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + 679962306a36Sopenharmony_ci sdev->id; 680062306a36Sopenharmony_ci 680162306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 680262306a36Sopenharmony_ci 680362306a36Sopenharmony_ci if (!cmd) { 680462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 680562306a36Sopenharmony_ci "Failed to get cmd %s\n", __func__); 680662306a36Sopenharmony_ci return -ENOMEM; 680762306a36Sopenharmony_ci } 680862306a36Sopenharmony_ci 680962306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 681062306a36Sopenharmony_ci 681162306a36Sopenharmony_ci memset(instance->tgt_prop, 0, sizeof(*instance->tgt_prop)); 681262306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 681362306a36Sopenharmony_ci dcmd->mbox.b[0] = MEGASAS_IS_LOGICAL(sdev); 681462306a36Sopenharmony_ci 681562306a36Sopenharmony_ci dcmd->mbox.s[1] = cpu_to_le16(targetId); 681662306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 681762306a36Sopenharmony_ci dcmd->cmd_status = 0xFF; 681862306a36Sopenharmony_ci dcmd->sge_count = 1; 681962306a36Sopenharmony_ci dcmd->flags = MFI_FRAME_DIR_READ; 682062306a36Sopenharmony_ci dcmd->timeout = 0; 682162306a36Sopenharmony_ci dcmd->pad_0 = 0; 682262306a36Sopenharmony_ci dcmd->data_xfer_len = 682362306a36Sopenharmony_ci cpu_to_le32(sizeof(struct MR_TARGET_PROPERTIES)); 682462306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_DRV_GET_TARGET_PROP); 682562306a36Sopenharmony_ci 682662306a36Sopenharmony_ci megasas_set_dma_settings(instance, dcmd, instance->tgt_prop_h, 682762306a36Sopenharmony_ci sizeof(struct MR_TARGET_PROPERTIES)); 682862306a36Sopenharmony_ci 682962306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 683062306a36Sopenharmony_ci !instance->mask_interrupts) 683162306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, 683262306a36Sopenharmony_ci cmd, MFI_IO_TIMEOUT_SECS); 683362306a36Sopenharmony_ci else 683462306a36Sopenharmony_ci ret = megasas_issue_polled(instance, cmd); 683562306a36Sopenharmony_ci 683662306a36Sopenharmony_ci switch (ret) { 683762306a36Sopenharmony_ci case DCMD_TIMEOUT: 683862306a36Sopenharmony_ci switch (dcmd_timeout_ocr_possible(instance)) { 683962306a36Sopenharmony_ci case INITIATE_OCR: 684062306a36Sopenharmony_ci cmd->flags |= DRV_DCMD_SKIP_REFIRE; 684162306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 684262306a36Sopenharmony_ci megasas_reset_fusion(instance->host, 684362306a36Sopenharmony_ci MFI_IO_TIMEOUT_OCR); 684462306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 684562306a36Sopenharmony_ci break; 684662306a36Sopenharmony_ci case KILL_ADAPTER: 684762306a36Sopenharmony_ci megaraid_sas_kill_hba(instance); 684862306a36Sopenharmony_ci break; 684962306a36Sopenharmony_ci case IGNORE_TIMEOUT: 685062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 685162306a36Sopenharmony_ci "Ignore DCMD timeout: %s %d\n", 685262306a36Sopenharmony_ci __func__, __LINE__); 685362306a36Sopenharmony_ci break; 685462306a36Sopenharmony_ci } 685562306a36Sopenharmony_ci break; 685662306a36Sopenharmony_ci 685762306a36Sopenharmony_ci default: 685862306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 685962306a36Sopenharmony_ci } 686062306a36Sopenharmony_ci if (ret != DCMD_SUCCESS) 686162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 686262306a36Sopenharmony_ci "return from %s %d return value %d\n", 686362306a36Sopenharmony_ci __func__, __LINE__, ret); 686462306a36Sopenharmony_ci 686562306a36Sopenharmony_ci return ret; 686662306a36Sopenharmony_ci} 686762306a36Sopenharmony_ci 686862306a36Sopenharmony_ci/** 686962306a36Sopenharmony_ci * megasas_start_aen - Subscribes to AEN during driver load time 687062306a36Sopenharmony_ci * @instance: Adapter soft state 687162306a36Sopenharmony_ci */ 687262306a36Sopenharmony_cistatic int megasas_start_aen(struct megasas_instance *instance) 687362306a36Sopenharmony_ci{ 687462306a36Sopenharmony_ci struct megasas_evt_log_info eli; 687562306a36Sopenharmony_ci union megasas_evt_class_locale class_locale; 687662306a36Sopenharmony_ci 687762306a36Sopenharmony_ci /* 687862306a36Sopenharmony_ci * Get the latest sequence number from FW 687962306a36Sopenharmony_ci */ 688062306a36Sopenharmony_ci memset(&eli, 0, sizeof(eli)); 688162306a36Sopenharmony_ci 688262306a36Sopenharmony_ci if (megasas_get_seq_num(instance, &eli)) 688362306a36Sopenharmony_ci return -1; 688462306a36Sopenharmony_ci 688562306a36Sopenharmony_ci /* 688662306a36Sopenharmony_ci * Register AEN with FW for latest sequence number plus 1 688762306a36Sopenharmony_ci */ 688862306a36Sopenharmony_ci class_locale.members.reserved = 0; 688962306a36Sopenharmony_ci class_locale.members.locale = MR_EVT_LOCALE_ALL; 689062306a36Sopenharmony_ci class_locale.members.class = MR_EVT_CLASS_DEBUG; 689162306a36Sopenharmony_ci 689262306a36Sopenharmony_ci return megasas_register_aen(instance, 689362306a36Sopenharmony_ci le32_to_cpu(eli.newest_seq_num) + 1, 689462306a36Sopenharmony_ci class_locale.word); 689562306a36Sopenharmony_ci} 689662306a36Sopenharmony_ci 689762306a36Sopenharmony_ci/** 689862306a36Sopenharmony_ci * megasas_io_attach - Attaches this driver to SCSI mid-layer 689962306a36Sopenharmony_ci * @instance: Adapter soft state 690062306a36Sopenharmony_ci */ 690162306a36Sopenharmony_cistatic int megasas_io_attach(struct megasas_instance *instance) 690262306a36Sopenharmony_ci{ 690362306a36Sopenharmony_ci struct Scsi_Host *host = instance->host; 690462306a36Sopenharmony_ci 690562306a36Sopenharmony_ci /* 690662306a36Sopenharmony_ci * Export parameters required by SCSI mid-layer 690762306a36Sopenharmony_ci */ 690862306a36Sopenharmony_ci host->unique_id = instance->unique_id; 690962306a36Sopenharmony_ci host->can_queue = instance->max_scsi_cmds; 691062306a36Sopenharmony_ci host->this_id = instance->init_id; 691162306a36Sopenharmony_ci host->sg_tablesize = instance->max_num_sge; 691262306a36Sopenharmony_ci 691362306a36Sopenharmony_ci if (instance->fw_support_ieee) 691462306a36Sopenharmony_ci instance->max_sectors_per_req = MEGASAS_MAX_SECTORS_IEEE; 691562306a36Sopenharmony_ci 691662306a36Sopenharmony_ci /* 691762306a36Sopenharmony_ci * Check if the module parameter value for max_sectors can be used 691862306a36Sopenharmony_ci */ 691962306a36Sopenharmony_ci if (max_sectors && max_sectors < instance->max_sectors_per_req) 692062306a36Sopenharmony_ci instance->max_sectors_per_req = max_sectors; 692162306a36Sopenharmony_ci else { 692262306a36Sopenharmony_ci if (max_sectors) { 692362306a36Sopenharmony_ci if (((instance->pdev->device == 692462306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS1078GEN2) || 692562306a36Sopenharmony_ci (instance->pdev->device == 692662306a36Sopenharmony_ci PCI_DEVICE_ID_LSI_SAS0079GEN2)) && 692762306a36Sopenharmony_ci (max_sectors <= MEGASAS_MAX_SECTORS)) { 692862306a36Sopenharmony_ci instance->max_sectors_per_req = max_sectors; 692962306a36Sopenharmony_ci } else { 693062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "max_sectors should be > 0" 693162306a36Sopenharmony_ci "and <= %d (or < 1MB for GEN2 controller)\n", 693262306a36Sopenharmony_ci instance->max_sectors_per_req); 693362306a36Sopenharmony_ci } 693462306a36Sopenharmony_ci } 693562306a36Sopenharmony_ci } 693662306a36Sopenharmony_ci 693762306a36Sopenharmony_ci host->max_sectors = instance->max_sectors_per_req; 693862306a36Sopenharmony_ci host->cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN; 693962306a36Sopenharmony_ci host->max_channel = MEGASAS_MAX_CHANNELS - 1; 694062306a36Sopenharmony_ci host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL; 694162306a36Sopenharmony_ci host->max_lun = MEGASAS_MAX_LUN; 694262306a36Sopenharmony_ci host->max_cmd_len = 16; 694362306a36Sopenharmony_ci 694462306a36Sopenharmony_ci /* Use shared host tagset only for fusion adaptors 694562306a36Sopenharmony_ci * if there are managed interrupts (smp affinity enabled case). 694662306a36Sopenharmony_ci * Single msix_vectors in kdump, so shared host tag is also disabled. 694762306a36Sopenharmony_ci */ 694862306a36Sopenharmony_ci 694962306a36Sopenharmony_ci host->host_tagset = 0; 695062306a36Sopenharmony_ci host->nr_hw_queues = 1; 695162306a36Sopenharmony_ci 695262306a36Sopenharmony_ci if ((instance->adapter_type != MFI_SERIES) && 695362306a36Sopenharmony_ci (instance->msix_vectors > instance->low_latency_index_start) && 695462306a36Sopenharmony_ci host_tagset_enable && 695562306a36Sopenharmony_ci instance->smp_affinity_enable) { 695662306a36Sopenharmony_ci host->host_tagset = 1; 695762306a36Sopenharmony_ci host->nr_hw_queues = instance->msix_vectors - 695862306a36Sopenharmony_ci instance->low_latency_index_start + instance->iopoll_q_count; 695962306a36Sopenharmony_ci if (instance->iopoll_q_count) 696062306a36Sopenharmony_ci host->nr_maps = 3; 696162306a36Sopenharmony_ci } else { 696262306a36Sopenharmony_ci instance->iopoll_q_count = 0; 696362306a36Sopenharmony_ci } 696462306a36Sopenharmony_ci 696562306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 696662306a36Sopenharmony_ci "Max firmware commands: %d shared with default " 696762306a36Sopenharmony_ci "hw_queues = %d poll_queues %d\n", instance->max_fw_cmds, 696862306a36Sopenharmony_ci host->nr_hw_queues - instance->iopoll_q_count, 696962306a36Sopenharmony_ci instance->iopoll_q_count); 697062306a36Sopenharmony_ci /* 697162306a36Sopenharmony_ci * Notify the mid-layer about the new controller 697262306a36Sopenharmony_ci */ 697362306a36Sopenharmony_ci if (scsi_add_host(host, &instance->pdev->dev)) { 697462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 697562306a36Sopenharmony_ci "Failed to add host from %s %d\n", 697662306a36Sopenharmony_ci __func__, __LINE__); 697762306a36Sopenharmony_ci return -ENODEV; 697862306a36Sopenharmony_ci } 697962306a36Sopenharmony_ci 698062306a36Sopenharmony_ci return 0; 698162306a36Sopenharmony_ci} 698262306a36Sopenharmony_ci 698362306a36Sopenharmony_ci/** 698462306a36Sopenharmony_ci * megasas_set_dma_mask - Set DMA mask for supported controllers 698562306a36Sopenharmony_ci * 698662306a36Sopenharmony_ci * @instance: Adapter soft state 698762306a36Sopenharmony_ci * Description: 698862306a36Sopenharmony_ci * 698962306a36Sopenharmony_ci * For Ventura, driver/FW will operate in 63bit DMA addresses. 699062306a36Sopenharmony_ci * 699162306a36Sopenharmony_ci * For invader- 699262306a36Sopenharmony_ci * By default, driver/FW will operate in 32bit DMA addresses 699362306a36Sopenharmony_ci * for consistent DMA mapping but if 32 bit consistent 699462306a36Sopenharmony_ci * DMA mask fails, driver will try with 63 bit consistent 699562306a36Sopenharmony_ci * mask provided FW is true 63bit DMA capable 699662306a36Sopenharmony_ci * 699762306a36Sopenharmony_ci * For older controllers(Thunderbolt and MFI based adapters)- 699862306a36Sopenharmony_ci * driver/FW will operate in 32 bit consistent DMA addresses. 699962306a36Sopenharmony_ci */ 700062306a36Sopenharmony_cistatic int 700162306a36Sopenharmony_cimegasas_set_dma_mask(struct megasas_instance *instance) 700262306a36Sopenharmony_ci{ 700362306a36Sopenharmony_ci u64 consistent_mask; 700462306a36Sopenharmony_ci struct pci_dev *pdev; 700562306a36Sopenharmony_ci u32 scratch_pad_1; 700662306a36Sopenharmony_ci 700762306a36Sopenharmony_ci pdev = instance->pdev; 700862306a36Sopenharmony_ci consistent_mask = (instance->adapter_type >= VENTURA_SERIES) ? 700962306a36Sopenharmony_ci DMA_BIT_MASK(63) : DMA_BIT_MASK(32); 701062306a36Sopenharmony_ci 701162306a36Sopenharmony_ci if (IS_DMA64) { 701262306a36Sopenharmony_ci if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(63)) && 701362306a36Sopenharmony_ci dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) 701462306a36Sopenharmony_ci goto fail_set_dma_mask; 701562306a36Sopenharmony_ci 701662306a36Sopenharmony_ci if ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) && 701762306a36Sopenharmony_ci (dma_set_coherent_mask(&pdev->dev, consistent_mask) && 701862306a36Sopenharmony_ci dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))) { 701962306a36Sopenharmony_ci /* 702062306a36Sopenharmony_ci * If 32 bit DMA mask fails, then try for 64 bit mask 702162306a36Sopenharmony_ci * for FW capable of handling 64 bit DMA. 702262306a36Sopenharmony_ci */ 702362306a36Sopenharmony_ci scratch_pad_1 = megasas_readl 702462306a36Sopenharmony_ci (instance, &instance->reg_set->outbound_scratch_pad_1); 702562306a36Sopenharmony_ci 702662306a36Sopenharmony_ci if (!(scratch_pad_1 & MR_CAN_HANDLE_64_BIT_DMA_OFFSET)) 702762306a36Sopenharmony_ci goto fail_set_dma_mask; 702862306a36Sopenharmony_ci else if (dma_set_mask_and_coherent(&pdev->dev, 702962306a36Sopenharmony_ci DMA_BIT_MASK(63))) 703062306a36Sopenharmony_ci goto fail_set_dma_mask; 703162306a36Sopenharmony_ci } 703262306a36Sopenharmony_ci } else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) 703362306a36Sopenharmony_ci goto fail_set_dma_mask; 703462306a36Sopenharmony_ci 703562306a36Sopenharmony_ci if (pdev->dev.coherent_dma_mask == DMA_BIT_MASK(32)) 703662306a36Sopenharmony_ci instance->consistent_mask_64bit = false; 703762306a36Sopenharmony_ci else 703862306a36Sopenharmony_ci instance->consistent_mask_64bit = true; 703962306a36Sopenharmony_ci 704062306a36Sopenharmony_ci dev_info(&pdev->dev, "%s bit DMA mask and %s bit consistent mask\n", 704162306a36Sopenharmony_ci ((*pdev->dev.dma_mask == DMA_BIT_MASK(63)) ? "63" : "32"), 704262306a36Sopenharmony_ci (instance->consistent_mask_64bit ? "63" : "32")); 704362306a36Sopenharmony_ci 704462306a36Sopenharmony_ci return 0; 704562306a36Sopenharmony_ci 704662306a36Sopenharmony_cifail_set_dma_mask: 704762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to set DMA mask\n"); 704862306a36Sopenharmony_ci return -1; 704962306a36Sopenharmony_ci 705062306a36Sopenharmony_ci} 705162306a36Sopenharmony_ci 705262306a36Sopenharmony_ci/* 705362306a36Sopenharmony_ci * megasas_set_adapter_type - Set adapter type. 705462306a36Sopenharmony_ci * Supported controllers can be divided in 705562306a36Sopenharmony_ci * different categories- 705662306a36Sopenharmony_ci * enum MR_ADAPTER_TYPE { 705762306a36Sopenharmony_ci * MFI_SERIES = 1, 705862306a36Sopenharmony_ci * THUNDERBOLT_SERIES = 2, 705962306a36Sopenharmony_ci * INVADER_SERIES = 3, 706062306a36Sopenharmony_ci * VENTURA_SERIES = 4, 706162306a36Sopenharmony_ci * AERO_SERIES = 5, 706262306a36Sopenharmony_ci * }; 706362306a36Sopenharmony_ci * @instance: Adapter soft state 706462306a36Sopenharmony_ci * return: void 706562306a36Sopenharmony_ci */ 706662306a36Sopenharmony_cistatic inline void megasas_set_adapter_type(struct megasas_instance *instance) 706762306a36Sopenharmony_ci{ 706862306a36Sopenharmony_ci if ((instance->pdev->vendor == PCI_VENDOR_ID_DELL) && 706962306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5)) { 707062306a36Sopenharmony_ci instance->adapter_type = MFI_SERIES; 707162306a36Sopenharmony_ci } else { 707262306a36Sopenharmony_ci switch (instance->pdev->device) { 707362306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E1: 707462306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E2: 707562306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E5: 707662306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E6: 707762306a36Sopenharmony_ci instance->adapter_type = AERO_SERIES; 707862306a36Sopenharmony_ci break; 707962306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_VENTURA: 708062306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_CRUSADER: 708162306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_HARPOON: 708262306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_TOMCAT: 708362306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_VENTURA_4PORT: 708462306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_CRUSADER_4PORT: 708562306a36Sopenharmony_ci instance->adapter_type = VENTURA_SERIES; 708662306a36Sopenharmony_ci break; 708762306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_FUSION: 708862306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_PLASMA: 708962306a36Sopenharmony_ci instance->adapter_type = THUNDERBOLT_SERIES; 709062306a36Sopenharmony_ci break; 709162306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_INVADER: 709262306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_INTRUDER: 709362306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_INTRUDER_24: 709462306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_CUTLASS_52: 709562306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_CUTLASS_53: 709662306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_FURY: 709762306a36Sopenharmony_ci instance->adapter_type = INVADER_SERIES; 709862306a36Sopenharmony_ci break; 709962306a36Sopenharmony_ci default: /* For all other supported controllers */ 710062306a36Sopenharmony_ci instance->adapter_type = MFI_SERIES; 710162306a36Sopenharmony_ci break; 710262306a36Sopenharmony_ci } 710362306a36Sopenharmony_ci } 710462306a36Sopenharmony_ci} 710562306a36Sopenharmony_ci 710662306a36Sopenharmony_cistatic inline int megasas_alloc_mfi_ctrl_mem(struct megasas_instance *instance) 710762306a36Sopenharmony_ci{ 710862306a36Sopenharmony_ci instance->producer = dma_alloc_coherent(&instance->pdev->dev, 710962306a36Sopenharmony_ci sizeof(u32), &instance->producer_h, GFP_KERNEL); 711062306a36Sopenharmony_ci instance->consumer = dma_alloc_coherent(&instance->pdev->dev, 711162306a36Sopenharmony_ci sizeof(u32), &instance->consumer_h, GFP_KERNEL); 711262306a36Sopenharmony_ci 711362306a36Sopenharmony_ci if (!instance->producer || !instance->consumer) { 711462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 711562306a36Sopenharmony_ci "Failed to allocate memory for producer, consumer\n"); 711662306a36Sopenharmony_ci return -1; 711762306a36Sopenharmony_ci } 711862306a36Sopenharmony_ci 711962306a36Sopenharmony_ci *instance->producer = 0; 712062306a36Sopenharmony_ci *instance->consumer = 0; 712162306a36Sopenharmony_ci return 0; 712262306a36Sopenharmony_ci} 712362306a36Sopenharmony_ci 712462306a36Sopenharmony_ci/** 712562306a36Sopenharmony_ci * megasas_alloc_ctrl_mem - Allocate per controller memory for core data 712662306a36Sopenharmony_ci * structures which are not common across MFI 712762306a36Sopenharmony_ci * adapters and fusion adapters. 712862306a36Sopenharmony_ci * For MFI based adapters, allocate producer and 712962306a36Sopenharmony_ci * consumer buffers. For fusion adapters, allocate 713062306a36Sopenharmony_ci * memory for fusion context. 713162306a36Sopenharmony_ci * @instance: Adapter soft state 713262306a36Sopenharmony_ci * return: 0 for SUCCESS 713362306a36Sopenharmony_ci */ 713462306a36Sopenharmony_cistatic int megasas_alloc_ctrl_mem(struct megasas_instance *instance) 713562306a36Sopenharmony_ci{ 713662306a36Sopenharmony_ci instance->reply_map = kcalloc(nr_cpu_ids, sizeof(unsigned int), 713762306a36Sopenharmony_ci GFP_KERNEL); 713862306a36Sopenharmony_ci if (!instance->reply_map) 713962306a36Sopenharmony_ci return -ENOMEM; 714062306a36Sopenharmony_ci 714162306a36Sopenharmony_ci switch (instance->adapter_type) { 714262306a36Sopenharmony_ci case MFI_SERIES: 714362306a36Sopenharmony_ci if (megasas_alloc_mfi_ctrl_mem(instance)) 714462306a36Sopenharmony_ci return -ENOMEM; 714562306a36Sopenharmony_ci break; 714662306a36Sopenharmony_ci case AERO_SERIES: 714762306a36Sopenharmony_ci case VENTURA_SERIES: 714862306a36Sopenharmony_ci case THUNDERBOLT_SERIES: 714962306a36Sopenharmony_ci case INVADER_SERIES: 715062306a36Sopenharmony_ci if (megasas_alloc_fusion_context(instance)) 715162306a36Sopenharmony_ci return -ENOMEM; 715262306a36Sopenharmony_ci break; 715362306a36Sopenharmony_ci } 715462306a36Sopenharmony_ci 715562306a36Sopenharmony_ci return 0; 715662306a36Sopenharmony_ci} 715762306a36Sopenharmony_ci 715862306a36Sopenharmony_ci/* 715962306a36Sopenharmony_ci * megasas_free_ctrl_mem - Free fusion context for fusion adapters and 716062306a36Sopenharmony_ci * producer, consumer buffers for MFI adapters 716162306a36Sopenharmony_ci * 716262306a36Sopenharmony_ci * @instance - Adapter soft instance 716362306a36Sopenharmony_ci * 716462306a36Sopenharmony_ci */ 716562306a36Sopenharmony_cistatic inline void megasas_free_ctrl_mem(struct megasas_instance *instance) 716662306a36Sopenharmony_ci{ 716762306a36Sopenharmony_ci kfree(instance->reply_map); 716862306a36Sopenharmony_ci if (instance->adapter_type == MFI_SERIES) { 716962306a36Sopenharmony_ci if (instance->producer) 717062306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, sizeof(u32), 717162306a36Sopenharmony_ci instance->producer, 717262306a36Sopenharmony_ci instance->producer_h); 717362306a36Sopenharmony_ci if (instance->consumer) 717462306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, sizeof(u32), 717562306a36Sopenharmony_ci instance->consumer, 717662306a36Sopenharmony_ci instance->consumer_h); 717762306a36Sopenharmony_ci } else { 717862306a36Sopenharmony_ci megasas_free_fusion_context(instance); 717962306a36Sopenharmony_ci } 718062306a36Sopenharmony_ci} 718162306a36Sopenharmony_ci 718262306a36Sopenharmony_ci/** 718362306a36Sopenharmony_ci * megasas_alloc_ctrl_dma_buffers - Allocate consistent DMA buffers during 718462306a36Sopenharmony_ci * driver load time 718562306a36Sopenharmony_ci * 718662306a36Sopenharmony_ci * @instance: Adapter soft instance 718762306a36Sopenharmony_ci * 718862306a36Sopenharmony_ci * @return: O for SUCCESS 718962306a36Sopenharmony_ci */ 719062306a36Sopenharmony_cistatic inline 719162306a36Sopenharmony_ciint megasas_alloc_ctrl_dma_buffers(struct megasas_instance *instance) 719262306a36Sopenharmony_ci{ 719362306a36Sopenharmony_ci struct pci_dev *pdev = instance->pdev; 719462306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 719562306a36Sopenharmony_ci 719662306a36Sopenharmony_ci instance->evt_detail = dma_alloc_coherent(&pdev->dev, 719762306a36Sopenharmony_ci sizeof(struct megasas_evt_detail), 719862306a36Sopenharmony_ci &instance->evt_detail_h, GFP_KERNEL); 719962306a36Sopenharmony_ci 720062306a36Sopenharmony_ci if (!instance->evt_detail) { 720162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 720262306a36Sopenharmony_ci "Failed to allocate event detail buffer\n"); 720362306a36Sopenharmony_ci return -ENOMEM; 720462306a36Sopenharmony_ci } 720562306a36Sopenharmony_ci 720662306a36Sopenharmony_ci if (fusion) { 720762306a36Sopenharmony_ci fusion->ioc_init_request = 720862306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 720962306a36Sopenharmony_ci sizeof(struct MPI2_IOC_INIT_REQUEST), 721062306a36Sopenharmony_ci &fusion->ioc_init_request_phys, 721162306a36Sopenharmony_ci GFP_KERNEL); 721262306a36Sopenharmony_ci 721362306a36Sopenharmony_ci if (!fusion->ioc_init_request) { 721462306a36Sopenharmony_ci dev_err(&pdev->dev, 721562306a36Sopenharmony_ci "Failed to allocate ioc init request\n"); 721662306a36Sopenharmony_ci return -ENOMEM; 721762306a36Sopenharmony_ci } 721862306a36Sopenharmony_ci 721962306a36Sopenharmony_ci instance->snapdump_prop = dma_alloc_coherent(&pdev->dev, 722062306a36Sopenharmony_ci sizeof(struct MR_SNAPDUMP_PROPERTIES), 722162306a36Sopenharmony_ci &instance->snapdump_prop_h, GFP_KERNEL); 722262306a36Sopenharmony_ci 722362306a36Sopenharmony_ci if (!instance->snapdump_prop) 722462306a36Sopenharmony_ci dev_err(&pdev->dev, 722562306a36Sopenharmony_ci "Failed to allocate snapdump properties buffer\n"); 722662306a36Sopenharmony_ci 722762306a36Sopenharmony_ci instance->host_device_list_buf = dma_alloc_coherent(&pdev->dev, 722862306a36Sopenharmony_ci HOST_DEVICE_LIST_SZ, 722962306a36Sopenharmony_ci &instance->host_device_list_buf_h, 723062306a36Sopenharmony_ci GFP_KERNEL); 723162306a36Sopenharmony_ci 723262306a36Sopenharmony_ci if (!instance->host_device_list_buf) { 723362306a36Sopenharmony_ci dev_err(&pdev->dev, 723462306a36Sopenharmony_ci "Failed to allocate targetid list buffer\n"); 723562306a36Sopenharmony_ci return -ENOMEM; 723662306a36Sopenharmony_ci } 723762306a36Sopenharmony_ci 723862306a36Sopenharmony_ci } 723962306a36Sopenharmony_ci 724062306a36Sopenharmony_ci instance->pd_list_buf = 724162306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 724262306a36Sopenharmony_ci MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), 724362306a36Sopenharmony_ci &instance->pd_list_buf_h, GFP_KERNEL); 724462306a36Sopenharmony_ci 724562306a36Sopenharmony_ci if (!instance->pd_list_buf) { 724662306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate PD list buffer\n"); 724762306a36Sopenharmony_ci return -ENOMEM; 724862306a36Sopenharmony_ci } 724962306a36Sopenharmony_ci 725062306a36Sopenharmony_ci instance->ctrl_info_buf = 725162306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 725262306a36Sopenharmony_ci sizeof(struct megasas_ctrl_info), 725362306a36Sopenharmony_ci &instance->ctrl_info_buf_h, GFP_KERNEL); 725462306a36Sopenharmony_ci 725562306a36Sopenharmony_ci if (!instance->ctrl_info_buf) { 725662306a36Sopenharmony_ci dev_err(&pdev->dev, 725762306a36Sopenharmony_ci "Failed to allocate controller info buffer\n"); 725862306a36Sopenharmony_ci return -ENOMEM; 725962306a36Sopenharmony_ci } 726062306a36Sopenharmony_ci 726162306a36Sopenharmony_ci instance->ld_list_buf = 726262306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 726362306a36Sopenharmony_ci sizeof(struct MR_LD_LIST), 726462306a36Sopenharmony_ci &instance->ld_list_buf_h, GFP_KERNEL); 726562306a36Sopenharmony_ci 726662306a36Sopenharmony_ci if (!instance->ld_list_buf) { 726762306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to allocate LD list buffer\n"); 726862306a36Sopenharmony_ci return -ENOMEM; 726962306a36Sopenharmony_ci } 727062306a36Sopenharmony_ci 727162306a36Sopenharmony_ci instance->ld_targetid_list_buf = 727262306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 727362306a36Sopenharmony_ci sizeof(struct MR_LD_TARGETID_LIST), 727462306a36Sopenharmony_ci &instance->ld_targetid_list_buf_h, GFP_KERNEL); 727562306a36Sopenharmony_ci 727662306a36Sopenharmony_ci if (!instance->ld_targetid_list_buf) { 727762306a36Sopenharmony_ci dev_err(&pdev->dev, 727862306a36Sopenharmony_ci "Failed to allocate LD targetid list buffer\n"); 727962306a36Sopenharmony_ci return -ENOMEM; 728062306a36Sopenharmony_ci } 728162306a36Sopenharmony_ci 728262306a36Sopenharmony_ci if (!reset_devices) { 728362306a36Sopenharmony_ci instance->system_info_buf = 728462306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 728562306a36Sopenharmony_ci sizeof(struct MR_DRV_SYSTEM_INFO), 728662306a36Sopenharmony_ci &instance->system_info_h, GFP_KERNEL); 728762306a36Sopenharmony_ci instance->pd_info = 728862306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 728962306a36Sopenharmony_ci sizeof(struct MR_PD_INFO), 729062306a36Sopenharmony_ci &instance->pd_info_h, GFP_KERNEL); 729162306a36Sopenharmony_ci instance->tgt_prop = 729262306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 729362306a36Sopenharmony_ci sizeof(struct MR_TARGET_PROPERTIES), 729462306a36Sopenharmony_ci &instance->tgt_prop_h, GFP_KERNEL); 729562306a36Sopenharmony_ci instance->crash_dump_buf = 729662306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, CRASH_DMA_BUF_SIZE, 729762306a36Sopenharmony_ci &instance->crash_dump_h, GFP_KERNEL); 729862306a36Sopenharmony_ci 729962306a36Sopenharmony_ci if (!instance->system_info_buf) 730062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 730162306a36Sopenharmony_ci "Failed to allocate system info buffer\n"); 730262306a36Sopenharmony_ci 730362306a36Sopenharmony_ci if (!instance->pd_info) 730462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 730562306a36Sopenharmony_ci "Failed to allocate pd_info buffer\n"); 730662306a36Sopenharmony_ci 730762306a36Sopenharmony_ci if (!instance->tgt_prop) 730862306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 730962306a36Sopenharmony_ci "Failed to allocate tgt_prop buffer\n"); 731062306a36Sopenharmony_ci 731162306a36Sopenharmony_ci if (!instance->crash_dump_buf) 731262306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 731362306a36Sopenharmony_ci "Failed to allocate crash dump buffer\n"); 731462306a36Sopenharmony_ci } 731562306a36Sopenharmony_ci 731662306a36Sopenharmony_ci return 0; 731762306a36Sopenharmony_ci} 731862306a36Sopenharmony_ci 731962306a36Sopenharmony_ci/* 732062306a36Sopenharmony_ci * megasas_free_ctrl_dma_buffers - Free consistent DMA buffers allocated 732162306a36Sopenharmony_ci * during driver load time 732262306a36Sopenharmony_ci * 732362306a36Sopenharmony_ci * @instance- Adapter soft instance 732462306a36Sopenharmony_ci * 732562306a36Sopenharmony_ci */ 732662306a36Sopenharmony_cistatic inline 732762306a36Sopenharmony_civoid megasas_free_ctrl_dma_buffers(struct megasas_instance *instance) 732862306a36Sopenharmony_ci{ 732962306a36Sopenharmony_ci struct pci_dev *pdev = instance->pdev; 733062306a36Sopenharmony_ci struct fusion_context *fusion = instance->ctrl_context; 733162306a36Sopenharmony_ci 733262306a36Sopenharmony_ci if (instance->evt_detail) 733362306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct megasas_evt_detail), 733462306a36Sopenharmony_ci instance->evt_detail, 733562306a36Sopenharmony_ci instance->evt_detail_h); 733662306a36Sopenharmony_ci 733762306a36Sopenharmony_ci if (fusion && fusion->ioc_init_request) 733862306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 733962306a36Sopenharmony_ci sizeof(struct MPI2_IOC_INIT_REQUEST), 734062306a36Sopenharmony_ci fusion->ioc_init_request, 734162306a36Sopenharmony_ci fusion->ioc_init_request_phys); 734262306a36Sopenharmony_ci 734362306a36Sopenharmony_ci if (instance->pd_list_buf) 734462306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 734562306a36Sopenharmony_ci MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), 734662306a36Sopenharmony_ci instance->pd_list_buf, 734762306a36Sopenharmony_ci instance->pd_list_buf_h); 734862306a36Sopenharmony_ci 734962306a36Sopenharmony_ci if (instance->ld_list_buf) 735062306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_LIST), 735162306a36Sopenharmony_ci instance->ld_list_buf, 735262306a36Sopenharmony_ci instance->ld_list_buf_h); 735362306a36Sopenharmony_ci 735462306a36Sopenharmony_ci if (instance->ld_targetid_list_buf) 735562306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct MR_LD_TARGETID_LIST), 735662306a36Sopenharmony_ci instance->ld_targetid_list_buf, 735762306a36Sopenharmony_ci instance->ld_targetid_list_buf_h); 735862306a36Sopenharmony_ci 735962306a36Sopenharmony_ci if (instance->ctrl_info_buf) 736062306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct megasas_ctrl_info), 736162306a36Sopenharmony_ci instance->ctrl_info_buf, 736262306a36Sopenharmony_ci instance->ctrl_info_buf_h); 736362306a36Sopenharmony_ci 736462306a36Sopenharmony_ci if (instance->system_info_buf) 736562306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct MR_DRV_SYSTEM_INFO), 736662306a36Sopenharmony_ci instance->system_info_buf, 736762306a36Sopenharmony_ci instance->system_info_h); 736862306a36Sopenharmony_ci 736962306a36Sopenharmony_ci if (instance->pd_info) 737062306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct MR_PD_INFO), 737162306a36Sopenharmony_ci instance->pd_info, instance->pd_info_h); 737262306a36Sopenharmony_ci 737362306a36Sopenharmony_ci if (instance->tgt_prop) 737462306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct MR_TARGET_PROPERTIES), 737562306a36Sopenharmony_ci instance->tgt_prop, instance->tgt_prop_h); 737662306a36Sopenharmony_ci 737762306a36Sopenharmony_ci if (instance->crash_dump_buf) 737862306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, CRASH_DMA_BUF_SIZE, 737962306a36Sopenharmony_ci instance->crash_dump_buf, 738062306a36Sopenharmony_ci instance->crash_dump_h); 738162306a36Sopenharmony_ci 738262306a36Sopenharmony_ci if (instance->snapdump_prop) 738362306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 738462306a36Sopenharmony_ci sizeof(struct MR_SNAPDUMP_PROPERTIES), 738562306a36Sopenharmony_ci instance->snapdump_prop, 738662306a36Sopenharmony_ci instance->snapdump_prop_h); 738762306a36Sopenharmony_ci 738862306a36Sopenharmony_ci if (instance->host_device_list_buf) 738962306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 739062306a36Sopenharmony_ci HOST_DEVICE_LIST_SZ, 739162306a36Sopenharmony_ci instance->host_device_list_buf, 739262306a36Sopenharmony_ci instance->host_device_list_buf_h); 739362306a36Sopenharmony_ci 739462306a36Sopenharmony_ci} 739562306a36Sopenharmony_ci 739662306a36Sopenharmony_ci/* 739762306a36Sopenharmony_ci * megasas_init_ctrl_params - Initialize controller's instance 739862306a36Sopenharmony_ci * parameters before FW init 739962306a36Sopenharmony_ci * @instance - Adapter soft instance 740062306a36Sopenharmony_ci * @return - void 740162306a36Sopenharmony_ci */ 740262306a36Sopenharmony_cistatic inline void megasas_init_ctrl_params(struct megasas_instance *instance) 740362306a36Sopenharmony_ci{ 740462306a36Sopenharmony_ci instance->fw_crash_state = UNAVAILABLE; 740562306a36Sopenharmony_ci 740662306a36Sopenharmony_ci megasas_poll_wait_aen = 0; 740762306a36Sopenharmony_ci instance->issuepend_done = 1; 740862306a36Sopenharmony_ci atomic_set(&instance->adprecovery, MEGASAS_HBA_OPERATIONAL); 740962306a36Sopenharmony_ci 741062306a36Sopenharmony_ci /* 741162306a36Sopenharmony_ci * Initialize locks and queues 741262306a36Sopenharmony_ci */ 741362306a36Sopenharmony_ci INIT_LIST_HEAD(&instance->cmd_pool); 741462306a36Sopenharmony_ci INIT_LIST_HEAD(&instance->internal_reset_pending_q); 741562306a36Sopenharmony_ci 741662306a36Sopenharmony_ci atomic_set(&instance->fw_outstanding, 0); 741762306a36Sopenharmony_ci atomic64_set(&instance->total_io_count, 0); 741862306a36Sopenharmony_ci 741962306a36Sopenharmony_ci init_waitqueue_head(&instance->int_cmd_wait_q); 742062306a36Sopenharmony_ci init_waitqueue_head(&instance->abort_cmd_wait_q); 742162306a36Sopenharmony_ci 742262306a36Sopenharmony_ci mutex_init(&instance->crashdump_lock); 742362306a36Sopenharmony_ci spin_lock_init(&instance->mfi_pool_lock); 742462306a36Sopenharmony_ci spin_lock_init(&instance->hba_lock); 742562306a36Sopenharmony_ci spin_lock_init(&instance->stream_lock); 742662306a36Sopenharmony_ci spin_lock_init(&instance->completion_lock); 742762306a36Sopenharmony_ci 742862306a36Sopenharmony_ci mutex_init(&instance->reset_mutex); 742962306a36Sopenharmony_ci 743062306a36Sopenharmony_ci if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || 743162306a36Sopenharmony_ci (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) 743262306a36Sopenharmony_ci instance->flag_ieee = 1; 743362306a36Sopenharmony_ci 743462306a36Sopenharmony_ci instance->flag = 0; 743562306a36Sopenharmony_ci instance->unload = 1; 743662306a36Sopenharmony_ci instance->last_time = 0; 743762306a36Sopenharmony_ci instance->disableOnlineCtrlReset = 1; 743862306a36Sopenharmony_ci instance->UnevenSpanSupport = 0; 743962306a36Sopenharmony_ci instance->smp_affinity_enable = smp_affinity_enable ? true : false; 744062306a36Sopenharmony_ci instance->msix_load_balance = false; 744162306a36Sopenharmony_ci 744262306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 744362306a36Sopenharmony_ci INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq); 744462306a36Sopenharmony_ci else 744562306a36Sopenharmony_ci INIT_WORK(&instance->work_init, process_fw_state_change_wq); 744662306a36Sopenharmony_ci} 744762306a36Sopenharmony_ci 744862306a36Sopenharmony_ci/** 744962306a36Sopenharmony_ci * megasas_probe_one - PCI hotplug entry point 745062306a36Sopenharmony_ci * @pdev: PCI device structure 745162306a36Sopenharmony_ci * @id: PCI ids of supported hotplugged adapter 745262306a36Sopenharmony_ci */ 745362306a36Sopenharmony_cistatic int megasas_probe_one(struct pci_dev *pdev, 745462306a36Sopenharmony_ci const struct pci_device_id *id) 745562306a36Sopenharmony_ci{ 745662306a36Sopenharmony_ci int rval, pos; 745762306a36Sopenharmony_ci struct Scsi_Host *host; 745862306a36Sopenharmony_ci struct megasas_instance *instance; 745962306a36Sopenharmony_ci u16 control = 0; 746062306a36Sopenharmony_ci 746162306a36Sopenharmony_ci switch (pdev->device) { 746262306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E0: 746362306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E3: 746462306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E4: 746562306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E7: 746662306a36Sopenharmony_ci dev_err(&pdev->dev, "Adapter is in non secure mode\n"); 746762306a36Sopenharmony_ci return 1; 746862306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E1: 746962306a36Sopenharmony_ci case PCI_DEVICE_ID_LSI_AERO_10E5: 747062306a36Sopenharmony_ci dev_info(&pdev->dev, "Adapter is in configurable secure mode\n"); 747162306a36Sopenharmony_ci break; 747262306a36Sopenharmony_ci } 747362306a36Sopenharmony_ci 747462306a36Sopenharmony_ci /* Reset MSI-X in the kdump kernel */ 747562306a36Sopenharmony_ci if (reset_devices) { 747662306a36Sopenharmony_ci pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); 747762306a36Sopenharmony_ci if (pos) { 747862306a36Sopenharmony_ci pci_read_config_word(pdev, pos + PCI_MSIX_FLAGS, 747962306a36Sopenharmony_ci &control); 748062306a36Sopenharmony_ci if (control & PCI_MSIX_FLAGS_ENABLE) { 748162306a36Sopenharmony_ci dev_info(&pdev->dev, "resetting MSI-X\n"); 748262306a36Sopenharmony_ci pci_write_config_word(pdev, 748362306a36Sopenharmony_ci pos + PCI_MSIX_FLAGS, 748462306a36Sopenharmony_ci control & 748562306a36Sopenharmony_ci ~PCI_MSIX_FLAGS_ENABLE); 748662306a36Sopenharmony_ci } 748762306a36Sopenharmony_ci } 748862306a36Sopenharmony_ci } 748962306a36Sopenharmony_ci 749062306a36Sopenharmony_ci /* 749162306a36Sopenharmony_ci * PCI prepping: enable device set bus mastering and dma mask 749262306a36Sopenharmony_ci */ 749362306a36Sopenharmony_ci rval = pci_enable_device_mem(pdev); 749462306a36Sopenharmony_ci 749562306a36Sopenharmony_ci if (rval) { 749662306a36Sopenharmony_ci return rval; 749762306a36Sopenharmony_ci } 749862306a36Sopenharmony_ci 749962306a36Sopenharmony_ci pci_set_master(pdev); 750062306a36Sopenharmony_ci 750162306a36Sopenharmony_ci host = scsi_host_alloc(&megasas_template, 750262306a36Sopenharmony_ci sizeof(struct megasas_instance)); 750362306a36Sopenharmony_ci 750462306a36Sopenharmony_ci if (!host) { 750562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &pdev->dev, "scsi_host_alloc failed\n"); 750662306a36Sopenharmony_ci goto fail_alloc_instance; 750762306a36Sopenharmony_ci } 750862306a36Sopenharmony_ci 750962306a36Sopenharmony_ci instance = (struct megasas_instance *)host->hostdata; 751062306a36Sopenharmony_ci memset(instance, 0, sizeof(*instance)); 751162306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 0); 751262306a36Sopenharmony_ci 751362306a36Sopenharmony_ci /* 751462306a36Sopenharmony_ci * Initialize PCI related and misc parameters 751562306a36Sopenharmony_ci */ 751662306a36Sopenharmony_ci instance->pdev = pdev; 751762306a36Sopenharmony_ci instance->host = host; 751862306a36Sopenharmony_ci instance->unique_id = pci_dev_id(pdev); 751962306a36Sopenharmony_ci instance->init_id = MEGASAS_DEFAULT_INIT_ID; 752062306a36Sopenharmony_ci 752162306a36Sopenharmony_ci megasas_set_adapter_type(instance); 752262306a36Sopenharmony_ci 752362306a36Sopenharmony_ci /* 752462306a36Sopenharmony_ci * Initialize MFI Firmware 752562306a36Sopenharmony_ci */ 752662306a36Sopenharmony_ci if (megasas_init_fw(instance)) 752762306a36Sopenharmony_ci goto fail_init_mfi; 752862306a36Sopenharmony_ci 752962306a36Sopenharmony_ci if (instance->requestorId) { 753062306a36Sopenharmony_ci if (instance->PlasmaFW111) { 753162306a36Sopenharmony_ci instance->vf_affiliation_111 = 753262306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 753362306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111), 753462306a36Sopenharmony_ci &instance->vf_affiliation_111_h, 753562306a36Sopenharmony_ci GFP_KERNEL); 753662306a36Sopenharmony_ci if (!instance->vf_affiliation_111) 753762306a36Sopenharmony_ci dev_warn(&pdev->dev, "Can't allocate " 753862306a36Sopenharmony_ci "memory for VF affiliation buffer\n"); 753962306a36Sopenharmony_ci } else { 754062306a36Sopenharmony_ci instance->vf_affiliation = 754162306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, 754262306a36Sopenharmony_ci (MAX_LOGICAL_DRIVES + 1) * 754362306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION), 754462306a36Sopenharmony_ci &instance->vf_affiliation_h, 754562306a36Sopenharmony_ci GFP_KERNEL); 754662306a36Sopenharmony_ci if (!instance->vf_affiliation) 754762306a36Sopenharmony_ci dev_warn(&pdev->dev, "Can't allocate " 754862306a36Sopenharmony_ci "memory for VF affiliation buffer\n"); 754962306a36Sopenharmony_ci } 755062306a36Sopenharmony_ci } 755162306a36Sopenharmony_ci 755262306a36Sopenharmony_ci /* 755362306a36Sopenharmony_ci * Store instance in PCI softstate 755462306a36Sopenharmony_ci */ 755562306a36Sopenharmony_ci pci_set_drvdata(pdev, instance); 755662306a36Sopenharmony_ci 755762306a36Sopenharmony_ci /* 755862306a36Sopenharmony_ci * Add this controller to megasas_mgmt_info structure so that it 755962306a36Sopenharmony_ci * can be exported to management applications 756062306a36Sopenharmony_ci */ 756162306a36Sopenharmony_ci megasas_mgmt_info.count++; 756262306a36Sopenharmony_ci megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance; 756362306a36Sopenharmony_ci megasas_mgmt_info.max_index++; 756462306a36Sopenharmony_ci 756562306a36Sopenharmony_ci /* 756662306a36Sopenharmony_ci * Register with SCSI mid-layer 756762306a36Sopenharmony_ci */ 756862306a36Sopenharmony_ci if (megasas_io_attach(instance)) 756962306a36Sopenharmony_ci goto fail_io_attach; 757062306a36Sopenharmony_ci 757162306a36Sopenharmony_ci instance->unload = 0; 757262306a36Sopenharmony_ci /* 757362306a36Sopenharmony_ci * Trigger SCSI to scan our drives 757462306a36Sopenharmony_ci */ 757562306a36Sopenharmony_ci if (!instance->enable_fw_dev_list || 757662306a36Sopenharmony_ci (instance->host_device_list_buf->count > 0)) 757762306a36Sopenharmony_ci scsi_scan_host(host); 757862306a36Sopenharmony_ci 757962306a36Sopenharmony_ci /* 758062306a36Sopenharmony_ci * Initiate AEN (Asynchronous Event Notification) 758162306a36Sopenharmony_ci */ 758262306a36Sopenharmony_ci if (megasas_start_aen(instance)) { 758362306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &pdev->dev, "start aen failed\n"); 758462306a36Sopenharmony_ci goto fail_start_aen; 758562306a36Sopenharmony_ci } 758662306a36Sopenharmony_ci 758762306a36Sopenharmony_ci megasas_setup_debugfs(instance); 758862306a36Sopenharmony_ci 758962306a36Sopenharmony_ci /* Get current SR-IOV LD/VF affiliation */ 759062306a36Sopenharmony_ci if (instance->requestorId) 759162306a36Sopenharmony_ci megasas_get_ld_vf_affiliation(instance, 1); 759262306a36Sopenharmony_ci 759362306a36Sopenharmony_ci return 0; 759462306a36Sopenharmony_ci 759562306a36Sopenharmony_cifail_start_aen: 759662306a36Sopenharmony_ci instance->unload = 1; 759762306a36Sopenharmony_ci scsi_remove_host(instance->host); 759862306a36Sopenharmony_cifail_io_attach: 759962306a36Sopenharmony_ci megasas_mgmt_info.count--; 760062306a36Sopenharmony_ci megasas_mgmt_info.max_index--; 760162306a36Sopenharmony_ci megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; 760262306a36Sopenharmony_ci 760362306a36Sopenharmony_ci if (instance->requestorId && !instance->skip_heartbeat_timer_del) 760462306a36Sopenharmony_ci del_timer_sync(&instance->sriov_heartbeat_timer); 760562306a36Sopenharmony_ci 760662306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 760762306a36Sopenharmony_ci megasas_destroy_irqs(instance); 760862306a36Sopenharmony_ci 760962306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 761062306a36Sopenharmony_ci megasas_release_fusion(instance); 761162306a36Sopenharmony_ci else 761262306a36Sopenharmony_ci megasas_release_mfi(instance); 761362306a36Sopenharmony_ci 761462306a36Sopenharmony_ci if (instance->msix_vectors) 761562306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 761662306a36Sopenharmony_ci instance->msix_vectors = 0; 761762306a36Sopenharmony_ci 761862306a36Sopenharmony_ci if (instance->fw_crash_state != UNAVAILABLE) 761962306a36Sopenharmony_ci megasas_free_host_crash_buffer(instance); 762062306a36Sopenharmony_ci 762162306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 762262306a36Sopenharmony_ci megasas_fusion_stop_watchdog(instance); 762362306a36Sopenharmony_cifail_init_mfi: 762462306a36Sopenharmony_ci scsi_host_put(host); 762562306a36Sopenharmony_cifail_alloc_instance: 762662306a36Sopenharmony_ci pci_disable_device(pdev); 762762306a36Sopenharmony_ci 762862306a36Sopenharmony_ci return -ENODEV; 762962306a36Sopenharmony_ci} 763062306a36Sopenharmony_ci 763162306a36Sopenharmony_ci/** 763262306a36Sopenharmony_ci * megasas_flush_cache - Requests FW to flush all its caches 763362306a36Sopenharmony_ci * @instance: Adapter soft state 763462306a36Sopenharmony_ci */ 763562306a36Sopenharmony_cistatic void megasas_flush_cache(struct megasas_instance *instance) 763662306a36Sopenharmony_ci{ 763762306a36Sopenharmony_ci struct megasas_cmd *cmd; 763862306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 763962306a36Sopenharmony_ci 764062306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) 764162306a36Sopenharmony_ci return; 764262306a36Sopenharmony_ci 764362306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 764462306a36Sopenharmony_ci 764562306a36Sopenharmony_ci if (!cmd) 764662306a36Sopenharmony_ci return; 764762306a36Sopenharmony_ci 764862306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 764962306a36Sopenharmony_ci 765062306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 765162306a36Sopenharmony_ci 765262306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 765362306a36Sopenharmony_ci dcmd->cmd_status = 0x0; 765462306a36Sopenharmony_ci dcmd->sge_count = 0; 765562306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE); 765662306a36Sopenharmony_ci dcmd->timeout = 0; 765762306a36Sopenharmony_ci dcmd->pad_0 = 0; 765862306a36Sopenharmony_ci dcmd->data_xfer_len = 0; 765962306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(MR_DCMD_CTRL_CACHE_FLUSH); 766062306a36Sopenharmony_ci dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; 766162306a36Sopenharmony_ci 766262306a36Sopenharmony_ci if (megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS) 766362306a36Sopenharmony_ci != DCMD_SUCCESS) { 766462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 766562306a36Sopenharmony_ci "return from %s %d\n", __func__, __LINE__); 766662306a36Sopenharmony_ci return; 766762306a36Sopenharmony_ci } 766862306a36Sopenharmony_ci 766962306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 767062306a36Sopenharmony_ci} 767162306a36Sopenharmony_ci 767262306a36Sopenharmony_ci/** 767362306a36Sopenharmony_ci * megasas_shutdown_controller - Instructs FW to shutdown the controller 767462306a36Sopenharmony_ci * @instance: Adapter soft state 767562306a36Sopenharmony_ci * @opcode: Shutdown/Hibernate 767662306a36Sopenharmony_ci */ 767762306a36Sopenharmony_cistatic void megasas_shutdown_controller(struct megasas_instance *instance, 767862306a36Sopenharmony_ci u32 opcode) 767962306a36Sopenharmony_ci{ 768062306a36Sopenharmony_ci struct megasas_cmd *cmd; 768162306a36Sopenharmony_ci struct megasas_dcmd_frame *dcmd; 768262306a36Sopenharmony_ci 768362306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) 768462306a36Sopenharmony_ci return; 768562306a36Sopenharmony_ci 768662306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 768762306a36Sopenharmony_ci 768862306a36Sopenharmony_ci if (!cmd) 768962306a36Sopenharmony_ci return; 769062306a36Sopenharmony_ci 769162306a36Sopenharmony_ci if (instance->aen_cmd) 769262306a36Sopenharmony_ci megasas_issue_blocked_abort_cmd(instance, 769362306a36Sopenharmony_ci instance->aen_cmd, MFI_IO_TIMEOUT_SECS); 769462306a36Sopenharmony_ci if (instance->map_update_cmd) 769562306a36Sopenharmony_ci megasas_issue_blocked_abort_cmd(instance, 769662306a36Sopenharmony_ci instance->map_update_cmd, MFI_IO_TIMEOUT_SECS); 769762306a36Sopenharmony_ci if (instance->jbod_seq_cmd) 769862306a36Sopenharmony_ci megasas_issue_blocked_abort_cmd(instance, 769962306a36Sopenharmony_ci instance->jbod_seq_cmd, MFI_IO_TIMEOUT_SECS); 770062306a36Sopenharmony_ci 770162306a36Sopenharmony_ci dcmd = &cmd->frame->dcmd; 770262306a36Sopenharmony_ci 770362306a36Sopenharmony_ci memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); 770462306a36Sopenharmony_ci 770562306a36Sopenharmony_ci dcmd->cmd = MFI_CMD_DCMD; 770662306a36Sopenharmony_ci dcmd->cmd_status = 0x0; 770762306a36Sopenharmony_ci dcmd->sge_count = 0; 770862306a36Sopenharmony_ci dcmd->flags = cpu_to_le16(MFI_FRAME_DIR_NONE); 770962306a36Sopenharmony_ci dcmd->timeout = 0; 771062306a36Sopenharmony_ci dcmd->pad_0 = 0; 771162306a36Sopenharmony_ci dcmd->data_xfer_len = 0; 771262306a36Sopenharmony_ci dcmd->opcode = cpu_to_le32(opcode); 771362306a36Sopenharmony_ci 771462306a36Sopenharmony_ci if (megasas_issue_blocked_cmd(instance, cmd, MFI_IO_TIMEOUT_SECS) 771562306a36Sopenharmony_ci != DCMD_SUCCESS) { 771662306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 771762306a36Sopenharmony_ci "return from %s %d\n", __func__, __LINE__); 771862306a36Sopenharmony_ci return; 771962306a36Sopenharmony_ci } 772062306a36Sopenharmony_ci 772162306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 772262306a36Sopenharmony_ci} 772362306a36Sopenharmony_ci 772462306a36Sopenharmony_ci/** 772562306a36Sopenharmony_ci * megasas_suspend - driver suspend entry point 772662306a36Sopenharmony_ci * @dev: Device structure 772762306a36Sopenharmony_ci */ 772862306a36Sopenharmony_cistatic int __maybe_unused 772962306a36Sopenharmony_cimegasas_suspend(struct device *dev) 773062306a36Sopenharmony_ci{ 773162306a36Sopenharmony_ci struct megasas_instance *instance; 773262306a36Sopenharmony_ci 773362306a36Sopenharmony_ci instance = dev_get_drvdata(dev); 773462306a36Sopenharmony_ci 773562306a36Sopenharmony_ci if (!instance) 773662306a36Sopenharmony_ci return 0; 773762306a36Sopenharmony_ci 773862306a36Sopenharmony_ci instance->unload = 1; 773962306a36Sopenharmony_ci 774062306a36Sopenharmony_ci dev_info(dev, "%s is called\n", __func__); 774162306a36Sopenharmony_ci 774262306a36Sopenharmony_ci /* Shutdown SR-IOV heartbeat timer */ 774362306a36Sopenharmony_ci if (instance->requestorId && !instance->skip_heartbeat_timer_del) 774462306a36Sopenharmony_ci del_timer_sync(&instance->sriov_heartbeat_timer); 774562306a36Sopenharmony_ci 774662306a36Sopenharmony_ci /* Stop the FW fault detection watchdog */ 774762306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 774862306a36Sopenharmony_ci megasas_fusion_stop_watchdog(instance); 774962306a36Sopenharmony_ci 775062306a36Sopenharmony_ci megasas_flush_cache(instance); 775162306a36Sopenharmony_ci megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); 775262306a36Sopenharmony_ci 775362306a36Sopenharmony_ci /* cancel the delayed work if this work still in queue */ 775462306a36Sopenharmony_ci if (instance->ev != NULL) { 775562306a36Sopenharmony_ci struct megasas_aen_event *ev = instance->ev; 775662306a36Sopenharmony_ci cancel_delayed_work_sync(&ev->hotplug_work); 775762306a36Sopenharmony_ci instance->ev = NULL; 775862306a36Sopenharmony_ci } 775962306a36Sopenharmony_ci 776062306a36Sopenharmony_ci tasklet_kill(&instance->isr_tasklet); 776162306a36Sopenharmony_ci 776262306a36Sopenharmony_ci pci_set_drvdata(instance->pdev, instance); 776362306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 776462306a36Sopenharmony_ci 776562306a36Sopenharmony_ci megasas_destroy_irqs(instance); 776662306a36Sopenharmony_ci 776762306a36Sopenharmony_ci if (instance->msix_vectors) 776862306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 776962306a36Sopenharmony_ci 777062306a36Sopenharmony_ci return 0; 777162306a36Sopenharmony_ci} 777262306a36Sopenharmony_ci 777362306a36Sopenharmony_ci/** 777462306a36Sopenharmony_ci * megasas_resume- driver resume entry point 777562306a36Sopenharmony_ci * @dev: Device structure 777662306a36Sopenharmony_ci */ 777762306a36Sopenharmony_cistatic int __maybe_unused 777862306a36Sopenharmony_cimegasas_resume(struct device *dev) 777962306a36Sopenharmony_ci{ 778062306a36Sopenharmony_ci int rval; 778162306a36Sopenharmony_ci struct Scsi_Host *host; 778262306a36Sopenharmony_ci struct megasas_instance *instance; 778362306a36Sopenharmony_ci u32 status_reg; 778462306a36Sopenharmony_ci 778562306a36Sopenharmony_ci instance = dev_get_drvdata(dev); 778662306a36Sopenharmony_ci 778762306a36Sopenharmony_ci if (!instance) 778862306a36Sopenharmony_ci return 0; 778962306a36Sopenharmony_ci 779062306a36Sopenharmony_ci host = instance->host; 779162306a36Sopenharmony_ci 779262306a36Sopenharmony_ci dev_info(dev, "%s is called\n", __func__); 779362306a36Sopenharmony_ci 779462306a36Sopenharmony_ci /* 779562306a36Sopenharmony_ci * We expect the FW state to be READY 779662306a36Sopenharmony_ci */ 779762306a36Sopenharmony_ci 779862306a36Sopenharmony_ci if (megasas_transition_to_ready(instance, 0)) { 779962306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 780062306a36Sopenharmony_ci "Failed to transition controller to ready from %s!\n", 780162306a36Sopenharmony_ci __func__); 780262306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) { 780362306a36Sopenharmony_ci status_reg = 780462306a36Sopenharmony_ci instance->instancet->read_fw_status_reg(instance); 780562306a36Sopenharmony_ci if (!(status_reg & MFI_RESET_ADAPTER) || 780662306a36Sopenharmony_ci ((megasas_adp_reset_wait_for_ready 780762306a36Sopenharmony_ci (instance, true, 0)) == FAILED)) 780862306a36Sopenharmony_ci goto fail_ready_state; 780962306a36Sopenharmony_ci } else { 781062306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 1); 781162306a36Sopenharmony_ci instance->instancet->adp_reset 781262306a36Sopenharmony_ci (instance, instance->reg_set); 781362306a36Sopenharmony_ci atomic_set(&instance->fw_reset_no_pci_access, 0); 781462306a36Sopenharmony_ci 781562306a36Sopenharmony_ci /* waiting for about 30 seconds before retry */ 781662306a36Sopenharmony_ci ssleep(30); 781762306a36Sopenharmony_ci 781862306a36Sopenharmony_ci if (megasas_transition_to_ready(instance, 0)) 781962306a36Sopenharmony_ci goto fail_ready_state; 782062306a36Sopenharmony_ci } 782162306a36Sopenharmony_ci 782262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 782362306a36Sopenharmony_ci "FW restarted successfully from %s!\n", 782462306a36Sopenharmony_ci __func__); 782562306a36Sopenharmony_ci } 782662306a36Sopenharmony_ci if (megasas_set_dma_mask(instance)) 782762306a36Sopenharmony_ci goto fail_set_dma_mask; 782862306a36Sopenharmony_ci 782962306a36Sopenharmony_ci /* 783062306a36Sopenharmony_ci * Initialize MFI Firmware 783162306a36Sopenharmony_ci */ 783262306a36Sopenharmony_ci 783362306a36Sopenharmony_ci atomic_set(&instance->fw_outstanding, 0); 783462306a36Sopenharmony_ci atomic_set(&instance->ldio_outstanding, 0); 783562306a36Sopenharmony_ci 783662306a36Sopenharmony_ci /* Now re-enable MSI-X */ 783762306a36Sopenharmony_ci if (instance->msix_vectors) 783862306a36Sopenharmony_ci megasas_alloc_irq_vectors(instance); 783962306a36Sopenharmony_ci 784062306a36Sopenharmony_ci if (!instance->msix_vectors) { 784162306a36Sopenharmony_ci rval = pci_alloc_irq_vectors(instance->pdev, 1, 1, 784262306a36Sopenharmony_ci PCI_IRQ_LEGACY); 784362306a36Sopenharmony_ci if (rval < 0) 784462306a36Sopenharmony_ci goto fail_reenable_msix; 784562306a36Sopenharmony_ci } 784662306a36Sopenharmony_ci 784762306a36Sopenharmony_ci megasas_setup_reply_map(instance); 784862306a36Sopenharmony_ci 784962306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) { 785062306a36Sopenharmony_ci megasas_reset_reply_desc(instance); 785162306a36Sopenharmony_ci if (megasas_ioc_init_fusion(instance)) { 785262306a36Sopenharmony_ci megasas_free_cmds(instance); 785362306a36Sopenharmony_ci megasas_free_cmds_fusion(instance); 785462306a36Sopenharmony_ci goto fail_init_mfi; 785562306a36Sopenharmony_ci } 785662306a36Sopenharmony_ci if (!megasas_get_map_info(instance)) 785762306a36Sopenharmony_ci megasas_sync_map_info(instance); 785862306a36Sopenharmony_ci } else { 785962306a36Sopenharmony_ci *instance->producer = 0; 786062306a36Sopenharmony_ci *instance->consumer = 0; 786162306a36Sopenharmony_ci if (megasas_issue_init_mfi(instance)) 786262306a36Sopenharmony_ci goto fail_init_mfi; 786362306a36Sopenharmony_ci } 786462306a36Sopenharmony_ci 786562306a36Sopenharmony_ci if (megasas_get_ctrl_info(instance) != DCMD_SUCCESS) 786662306a36Sopenharmony_ci goto fail_init_mfi; 786762306a36Sopenharmony_ci 786862306a36Sopenharmony_ci tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, 786962306a36Sopenharmony_ci (unsigned long)instance); 787062306a36Sopenharmony_ci 787162306a36Sopenharmony_ci if (instance->msix_vectors ? 787262306a36Sopenharmony_ci megasas_setup_irqs_msix(instance, 0) : 787362306a36Sopenharmony_ci megasas_setup_irqs_ioapic(instance)) 787462306a36Sopenharmony_ci goto fail_init_mfi; 787562306a36Sopenharmony_ci 787662306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 787762306a36Sopenharmony_ci megasas_setup_irq_poll(instance); 787862306a36Sopenharmony_ci 787962306a36Sopenharmony_ci /* Re-launch SR-IOV heartbeat timer */ 788062306a36Sopenharmony_ci if (instance->requestorId) { 788162306a36Sopenharmony_ci if (!megasas_sriov_start_heartbeat(instance, 0)) 788262306a36Sopenharmony_ci megasas_start_timer(instance); 788362306a36Sopenharmony_ci else { 788462306a36Sopenharmony_ci instance->skip_heartbeat_timer_del = 1; 788562306a36Sopenharmony_ci goto fail_init_mfi; 788662306a36Sopenharmony_ci } 788762306a36Sopenharmony_ci } 788862306a36Sopenharmony_ci 788962306a36Sopenharmony_ci instance->instancet->enable_intr(instance); 789062306a36Sopenharmony_ci megasas_setup_jbod_map(instance); 789162306a36Sopenharmony_ci instance->unload = 0; 789262306a36Sopenharmony_ci 789362306a36Sopenharmony_ci /* 789462306a36Sopenharmony_ci * Initiate AEN (Asynchronous Event Notification) 789562306a36Sopenharmony_ci */ 789662306a36Sopenharmony_ci if (megasas_start_aen(instance)) 789762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Start AEN failed\n"); 789862306a36Sopenharmony_ci 789962306a36Sopenharmony_ci /* Re-launch FW fault watchdog */ 790062306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 790162306a36Sopenharmony_ci if (megasas_fusion_start_watchdog(instance) != SUCCESS) 790262306a36Sopenharmony_ci goto fail_start_watchdog; 790362306a36Sopenharmony_ci 790462306a36Sopenharmony_ci return 0; 790562306a36Sopenharmony_ci 790662306a36Sopenharmony_cifail_start_watchdog: 790762306a36Sopenharmony_ci if (instance->requestorId && !instance->skip_heartbeat_timer_del) 790862306a36Sopenharmony_ci del_timer_sync(&instance->sriov_heartbeat_timer); 790962306a36Sopenharmony_cifail_init_mfi: 791062306a36Sopenharmony_ci megasas_free_ctrl_dma_buffers(instance); 791162306a36Sopenharmony_ci megasas_free_ctrl_mem(instance); 791262306a36Sopenharmony_ci scsi_host_put(host); 791362306a36Sopenharmony_ci 791462306a36Sopenharmony_cifail_reenable_msix: 791562306a36Sopenharmony_cifail_set_dma_mask: 791662306a36Sopenharmony_cifail_ready_state: 791762306a36Sopenharmony_ci 791862306a36Sopenharmony_ci return -ENODEV; 791962306a36Sopenharmony_ci} 792062306a36Sopenharmony_ci 792162306a36Sopenharmony_cistatic inline int 792262306a36Sopenharmony_cimegasas_wait_for_adapter_operational(struct megasas_instance *instance) 792362306a36Sopenharmony_ci{ 792462306a36Sopenharmony_ci int wait_time = MEGASAS_RESET_WAIT_TIME * 2; 792562306a36Sopenharmony_ci int i; 792662306a36Sopenharmony_ci u8 adp_state; 792762306a36Sopenharmony_ci 792862306a36Sopenharmony_ci for (i = 0; i < wait_time; i++) { 792962306a36Sopenharmony_ci adp_state = atomic_read(&instance->adprecovery); 793062306a36Sopenharmony_ci if ((adp_state == MEGASAS_HBA_OPERATIONAL) || 793162306a36Sopenharmony_ci (adp_state == MEGASAS_HW_CRITICAL_ERROR)) 793262306a36Sopenharmony_ci break; 793362306a36Sopenharmony_ci 793462306a36Sopenharmony_ci if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) 793562306a36Sopenharmony_ci dev_notice(&instance->pdev->dev, "waiting for controller reset to finish\n"); 793662306a36Sopenharmony_ci 793762306a36Sopenharmony_ci msleep(1000); 793862306a36Sopenharmony_ci } 793962306a36Sopenharmony_ci 794062306a36Sopenharmony_ci if (adp_state != MEGASAS_HBA_OPERATIONAL) { 794162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 794262306a36Sopenharmony_ci "%s HBA failed to become operational, adp_state %d\n", 794362306a36Sopenharmony_ci __func__, adp_state); 794462306a36Sopenharmony_ci return 1; 794562306a36Sopenharmony_ci } 794662306a36Sopenharmony_ci 794762306a36Sopenharmony_ci return 0; 794862306a36Sopenharmony_ci} 794962306a36Sopenharmony_ci 795062306a36Sopenharmony_ci/** 795162306a36Sopenharmony_ci * megasas_detach_one - PCI hot"un"plug entry point 795262306a36Sopenharmony_ci * @pdev: PCI device structure 795362306a36Sopenharmony_ci */ 795462306a36Sopenharmony_cistatic void megasas_detach_one(struct pci_dev *pdev) 795562306a36Sopenharmony_ci{ 795662306a36Sopenharmony_ci int i; 795762306a36Sopenharmony_ci struct Scsi_Host *host; 795862306a36Sopenharmony_ci struct megasas_instance *instance; 795962306a36Sopenharmony_ci struct fusion_context *fusion; 796062306a36Sopenharmony_ci size_t pd_seq_map_sz; 796162306a36Sopenharmony_ci 796262306a36Sopenharmony_ci instance = pci_get_drvdata(pdev); 796362306a36Sopenharmony_ci 796462306a36Sopenharmony_ci if (!instance) 796562306a36Sopenharmony_ci return; 796662306a36Sopenharmony_ci 796762306a36Sopenharmony_ci host = instance->host; 796862306a36Sopenharmony_ci fusion = instance->ctrl_context; 796962306a36Sopenharmony_ci 797062306a36Sopenharmony_ci /* Shutdown SR-IOV heartbeat timer */ 797162306a36Sopenharmony_ci if (instance->requestorId && !instance->skip_heartbeat_timer_del) 797262306a36Sopenharmony_ci del_timer_sync(&instance->sriov_heartbeat_timer); 797362306a36Sopenharmony_ci 797462306a36Sopenharmony_ci /* Stop the FW fault detection watchdog */ 797562306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) 797662306a36Sopenharmony_ci megasas_fusion_stop_watchdog(instance); 797762306a36Sopenharmony_ci 797862306a36Sopenharmony_ci if (instance->fw_crash_state != UNAVAILABLE) 797962306a36Sopenharmony_ci megasas_free_host_crash_buffer(instance); 798062306a36Sopenharmony_ci scsi_remove_host(instance->host); 798162306a36Sopenharmony_ci instance->unload = 1; 798262306a36Sopenharmony_ci 798362306a36Sopenharmony_ci if (megasas_wait_for_adapter_operational(instance)) 798462306a36Sopenharmony_ci goto skip_firing_dcmds; 798562306a36Sopenharmony_ci 798662306a36Sopenharmony_ci megasas_flush_cache(instance); 798762306a36Sopenharmony_ci megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); 798862306a36Sopenharmony_ci 798962306a36Sopenharmony_ciskip_firing_dcmds: 799062306a36Sopenharmony_ci /* cancel the delayed work if this work still in queue*/ 799162306a36Sopenharmony_ci if (instance->ev != NULL) { 799262306a36Sopenharmony_ci struct megasas_aen_event *ev = instance->ev; 799362306a36Sopenharmony_ci cancel_delayed_work_sync(&ev->hotplug_work); 799462306a36Sopenharmony_ci instance->ev = NULL; 799562306a36Sopenharmony_ci } 799662306a36Sopenharmony_ci 799762306a36Sopenharmony_ci /* cancel all wait events */ 799862306a36Sopenharmony_ci wake_up_all(&instance->int_cmd_wait_q); 799962306a36Sopenharmony_ci 800062306a36Sopenharmony_ci tasklet_kill(&instance->isr_tasklet); 800162306a36Sopenharmony_ci 800262306a36Sopenharmony_ci /* 800362306a36Sopenharmony_ci * Take the instance off the instance array. Note that we will not 800462306a36Sopenharmony_ci * decrement the max_index. We let this array be sparse array 800562306a36Sopenharmony_ci */ 800662306a36Sopenharmony_ci for (i = 0; i < megasas_mgmt_info.max_index; i++) { 800762306a36Sopenharmony_ci if (megasas_mgmt_info.instance[i] == instance) { 800862306a36Sopenharmony_ci megasas_mgmt_info.count--; 800962306a36Sopenharmony_ci megasas_mgmt_info.instance[i] = NULL; 801062306a36Sopenharmony_ci 801162306a36Sopenharmony_ci break; 801262306a36Sopenharmony_ci } 801362306a36Sopenharmony_ci } 801462306a36Sopenharmony_ci 801562306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 801662306a36Sopenharmony_ci 801762306a36Sopenharmony_ci megasas_destroy_irqs(instance); 801862306a36Sopenharmony_ci 801962306a36Sopenharmony_ci if (instance->msix_vectors) 802062306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 802162306a36Sopenharmony_ci 802262306a36Sopenharmony_ci if (instance->adapter_type >= VENTURA_SERIES) { 802362306a36Sopenharmony_ci for (i = 0; i < MAX_LOGICAL_DRIVES_EXT; ++i) 802462306a36Sopenharmony_ci kfree(fusion->stream_detect_by_ld[i]); 802562306a36Sopenharmony_ci kfree(fusion->stream_detect_by_ld); 802662306a36Sopenharmony_ci fusion->stream_detect_by_ld = NULL; 802762306a36Sopenharmony_ci } 802862306a36Sopenharmony_ci 802962306a36Sopenharmony_ci 803062306a36Sopenharmony_ci if (instance->adapter_type != MFI_SERIES) { 803162306a36Sopenharmony_ci megasas_release_fusion(instance); 803262306a36Sopenharmony_ci pd_seq_map_sz = 803362306a36Sopenharmony_ci struct_size_t(struct MR_PD_CFG_SEQ_NUM_SYNC, 803462306a36Sopenharmony_ci seq, MAX_PHYSICAL_DEVICES); 803562306a36Sopenharmony_ci for (i = 0; i < 2 ; i++) { 803662306a36Sopenharmony_ci if (fusion->ld_map[i]) 803762306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 803862306a36Sopenharmony_ci fusion->max_map_sz, 803962306a36Sopenharmony_ci fusion->ld_map[i], 804062306a36Sopenharmony_ci fusion->ld_map_phys[i]); 804162306a36Sopenharmony_ci if (fusion->ld_drv_map[i]) { 804262306a36Sopenharmony_ci if (is_vmalloc_addr(fusion->ld_drv_map[i])) 804362306a36Sopenharmony_ci vfree(fusion->ld_drv_map[i]); 804462306a36Sopenharmony_ci else 804562306a36Sopenharmony_ci free_pages((ulong)fusion->ld_drv_map[i], 804662306a36Sopenharmony_ci fusion->drv_map_pages); 804762306a36Sopenharmony_ci } 804862306a36Sopenharmony_ci 804962306a36Sopenharmony_ci if (fusion->pd_seq_sync[i]) 805062306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 805162306a36Sopenharmony_ci pd_seq_map_sz, 805262306a36Sopenharmony_ci fusion->pd_seq_sync[i], 805362306a36Sopenharmony_ci fusion->pd_seq_phys[i]); 805462306a36Sopenharmony_ci } 805562306a36Sopenharmony_ci } else { 805662306a36Sopenharmony_ci megasas_release_mfi(instance); 805762306a36Sopenharmony_ci } 805862306a36Sopenharmony_ci 805962306a36Sopenharmony_ci if (instance->vf_affiliation) 806062306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, (MAX_LOGICAL_DRIVES + 1) * 806162306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION), 806262306a36Sopenharmony_ci instance->vf_affiliation, 806362306a36Sopenharmony_ci instance->vf_affiliation_h); 806462306a36Sopenharmony_ci 806562306a36Sopenharmony_ci if (instance->vf_affiliation_111) 806662306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, 806762306a36Sopenharmony_ci sizeof(struct MR_LD_VF_AFFILIATION_111), 806862306a36Sopenharmony_ci instance->vf_affiliation_111, 806962306a36Sopenharmony_ci instance->vf_affiliation_111_h); 807062306a36Sopenharmony_ci 807162306a36Sopenharmony_ci if (instance->hb_host_mem) 807262306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct MR_CTRL_HB_HOST_MEM), 807362306a36Sopenharmony_ci instance->hb_host_mem, 807462306a36Sopenharmony_ci instance->hb_host_mem_h); 807562306a36Sopenharmony_ci 807662306a36Sopenharmony_ci megasas_free_ctrl_dma_buffers(instance); 807762306a36Sopenharmony_ci 807862306a36Sopenharmony_ci megasas_free_ctrl_mem(instance); 807962306a36Sopenharmony_ci 808062306a36Sopenharmony_ci megasas_destroy_debugfs(instance); 808162306a36Sopenharmony_ci 808262306a36Sopenharmony_ci scsi_host_put(host); 808362306a36Sopenharmony_ci 808462306a36Sopenharmony_ci pci_disable_device(pdev); 808562306a36Sopenharmony_ci} 808662306a36Sopenharmony_ci 808762306a36Sopenharmony_ci/** 808862306a36Sopenharmony_ci * megasas_shutdown - Shutdown entry point 808962306a36Sopenharmony_ci * @pdev: PCI device structure 809062306a36Sopenharmony_ci */ 809162306a36Sopenharmony_cistatic void megasas_shutdown(struct pci_dev *pdev) 809262306a36Sopenharmony_ci{ 809362306a36Sopenharmony_ci struct megasas_instance *instance = pci_get_drvdata(pdev); 809462306a36Sopenharmony_ci 809562306a36Sopenharmony_ci if (!instance) 809662306a36Sopenharmony_ci return; 809762306a36Sopenharmony_ci 809862306a36Sopenharmony_ci instance->unload = 1; 809962306a36Sopenharmony_ci 810062306a36Sopenharmony_ci if (megasas_wait_for_adapter_operational(instance)) 810162306a36Sopenharmony_ci goto skip_firing_dcmds; 810262306a36Sopenharmony_ci 810362306a36Sopenharmony_ci megasas_flush_cache(instance); 810462306a36Sopenharmony_ci megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); 810562306a36Sopenharmony_ci 810662306a36Sopenharmony_ciskip_firing_dcmds: 810762306a36Sopenharmony_ci instance->instancet->disable_intr(instance); 810862306a36Sopenharmony_ci megasas_destroy_irqs(instance); 810962306a36Sopenharmony_ci 811062306a36Sopenharmony_ci if (instance->msix_vectors) 811162306a36Sopenharmony_ci pci_free_irq_vectors(instance->pdev); 811262306a36Sopenharmony_ci} 811362306a36Sopenharmony_ci 811462306a36Sopenharmony_ci/* 811562306a36Sopenharmony_ci * megasas_mgmt_open - char node "open" entry point 811662306a36Sopenharmony_ci * @inode: char node inode 811762306a36Sopenharmony_ci * @filep: char node file 811862306a36Sopenharmony_ci */ 811962306a36Sopenharmony_cistatic int megasas_mgmt_open(struct inode *inode, struct file *filep) 812062306a36Sopenharmony_ci{ 812162306a36Sopenharmony_ci /* 812262306a36Sopenharmony_ci * Allow only those users with admin rights 812362306a36Sopenharmony_ci */ 812462306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 812562306a36Sopenharmony_ci return -EACCES; 812662306a36Sopenharmony_ci 812762306a36Sopenharmony_ci return 0; 812862306a36Sopenharmony_ci} 812962306a36Sopenharmony_ci 813062306a36Sopenharmony_ci/* 813162306a36Sopenharmony_ci * megasas_mgmt_fasync - Async notifier registration from applications 813262306a36Sopenharmony_ci * @fd: char node file descriptor number 813362306a36Sopenharmony_ci * @filep: char node file 813462306a36Sopenharmony_ci * @mode: notifier on/off 813562306a36Sopenharmony_ci * 813662306a36Sopenharmony_ci * This function adds the calling process to a driver global queue. When an 813762306a36Sopenharmony_ci * event occurs, SIGIO will be sent to all processes in this queue. 813862306a36Sopenharmony_ci */ 813962306a36Sopenharmony_cistatic int megasas_mgmt_fasync(int fd, struct file *filep, int mode) 814062306a36Sopenharmony_ci{ 814162306a36Sopenharmony_ci int rc; 814262306a36Sopenharmony_ci 814362306a36Sopenharmony_ci mutex_lock(&megasas_async_queue_mutex); 814462306a36Sopenharmony_ci 814562306a36Sopenharmony_ci rc = fasync_helper(fd, filep, mode, &megasas_async_queue); 814662306a36Sopenharmony_ci 814762306a36Sopenharmony_ci mutex_unlock(&megasas_async_queue_mutex); 814862306a36Sopenharmony_ci 814962306a36Sopenharmony_ci if (rc >= 0) { 815062306a36Sopenharmony_ci /* For sanity check when we get ioctl */ 815162306a36Sopenharmony_ci filep->private_data = filep; 815262306a36Sopenharmony_ci return 0; 815362306a36Sopenharmony_ci } 815462306a36Sopenharmony_ci 815562306a36Sopenharmony_ci printk(KERN_DEBUG "megasas: fasync_helper failed [%d]\n", rc); 815662306a36Sopenharmony_ci 815762306a36Sopenharmony_ci return rc; 815862306a36Sopenharmony_ci} 815962306a36Sopenharmony_ci 816062306a36Sopenharmony_ci/* 816162306a36Sopenharmony_ci * megasas_mgmt_poll - char node "poll" entry point 816262306a36Sopenharmony_ci * @filep: char node file 816362306a36Sopenharmony_ci * @wait: Events to poll for 816462306a36Sopenharmony_ci */ 816562306a36Sopenharmony_cistatic __poll_t megasas_mgmt_poll(struct file *file, poll_table *wait) 816662306a36Sopenharmony_ci{ 816762306a36Sopenharmony_ci __poll_t mask; 816862306a36Sopenharmony_ci unsigned long flags; 816962306a36Sopenharmony_ci 817062306a36Sopenharmony_ci poll_wait(file, &megasas_poll_wait, wait); 817162306a36Sopenharmony_ci spin_lock_irqsave(&poll_aen_lock, flags); 817262306a36Sopenharmony_ci if (megasas_poll_wait_aen) 817362306a36Sopenharmony_ci mask = (EPOLLIN | EPOLLRDNORM); 817462306a36Sopenharmony_ci else 817562306a36Sopenharmony_ci mask = 0; 817662306a36Sopenharmony_ci megasas_poll_wait_aen = 0; 817762306a36Sopenharmony_ci spin_unlock_irqrestore(&poll_aen_lock, flags); 817862306a36Sopenharmony_ci return mask; 817962306a36Sopenharmony_ci} 818062306a36Sopenharmony_ci 818162306a36Sopenharmony_ci/* 818262306a36Sopenharmony_ci * megasas_set_crash_dump_params_ioctl: 818362306a36Sopenharmony_ci * Send CRASH_DUMP_MODE DCMD to all controllers 818462306a36Sopenharmony_ci * @cmd: MFI command frame 818562306a36Sopenharmony_ci */ 818662306a36Sopenharmony_ci 818762306a36Sopenharmony_cistatic int megasas_set_crash_dump_params_ioctl(struct megasas_cmd *cmd) 818862306a36Sopenharmony_ci{ 818962306a36Sopenharmony_ci struct megasas_instance *local_instance; 819062306a36Sopenharmony_ci int i, error = 0; 819162306a36Sopenharmony_ci int crash_support; 819262306a36Sopenharmony_ci 819362306a36Sopenharmony_ci crash_support = cmd->frame->dcmd.mbox.w[0]; 819462306a36Sopenharmony_ci 819562306a36Sopenharmony_ci for (i = 0; i < megasas_mgmt_info.max_index; i++) { 819662306a36Sopenharmony_ci local_instance = megasas_mgmt_info.instance[i]; 819762306a36Sopenharmony_ci if (local_instance && local_instance->crash_dump_drv_support) { 819862306a36Sopenharmony_ci if ((atomic_read(&local_instance->adprecovery) == 819962306a36Sopenharmony_ci MEGASAS_HBA_OPERATIONAL) && 820062306a36Sopenharmony_ci !megasas_set_crash_dump_params(local_instance, 820162306a36Sopenharmony_ci crash_support)) { 820262306a36Sopenharmony_ci local_instance->crash_dump_app_support = 820362306a36Sopenharmony_ci crash_support; 820462306a36Sopenharmony_ci dev_info(&local_instance->pdev->dev, 820562306a36Sopenharmony_ci "Application firmware crash " 820662306a36Sopenharmony_ci "dump mode set success\n"); 820762306a36Sopenharmony_ci error = 0; 820862306a36Sopenharmony_ci } else { 820962306a36Sopenharmony_ci dev_info(&local_instance->pdev->dev, 821062306a36Sopenharmony_ci "Application firmware crash " 821162306a36Sopenharmony_ci "dump mode set failed\n"); 821262306a36Sopenharmony_ci error = -1; 821362306a36Sopenharmony_ci } 821462306a36Sopenharmony_ci } 821562306a36Sopenharmony_ci } 821662306a36Sopenharmony_ci return error; 821762306a36Sopenharmony_ci} 821862306a36Sopenharmony_ci 821962306a36Sopenharmony_ci/** 822062306a36Sopenharmony_ci * megasas_mgmt_fw_ioctl - Issues management ioctls to FW 822162306a36Sopenharmony_ci * @instance: Adapter soft state 822262306a36Sopenharmony_ci * @user_ioc: User's ioctl packet 822362306a36Sopenharmony_ci * @ioc: ioctl packet 822462306a36Sopenharmony_ci */ 822562306a36Sopenharmony_cistatic int 822662306a36Sopenharmony_cimegasas_mgmt_fw_ioctl(struct megasas_instance *instance, 822762306a36Sopenharmony_ci struct megasas_iocpacket __user * user_ioc, 822862306a36Sopenharmony_ci struct megasas_iocpacket *ioc) 822962306a36Sopenharmony_ci{ 823062306a36Sopenharmony_ci struct megasas_sge64 *kern_sge64 = NULL; 823162306a36Sopenharmony_ci struct megasas_sge32 *kern_sge32 = NULL; 823262306a36Sopenharmony_ci struct megasas_cmd *cmd; 823362306a36Sopenharmony_ci void *kbuff_arr[MAX_IOCTL_SGE]; 823462306a36Sopenharmony_ci dma_addr_t buf_handle = 0; 823562306a36Sopenharmony_ci int error = 0, i; 823662306a36Sopenharmony_ci void *sense = NULL; 823762306a36Sopenharmony_ci dma_addr_t sense_handle; 823862306a36Sopenharmony_ci void *sense_ptr; 823962306a36Sopenharmony_ci u32 opcode = 0; 824062306a36Sopenharmony_ci int ret = DCMD_SUCCESS; 824162306a36Sopenharmony_ci 824262306a36Sopenharmony_ci memset(kbuff_arr, 0, sizeof(kbuff_arr)); 824362306a36Sopenharmony_ci 824462306a36Sopenharmony_ci if (ioc->sge_count > MAX_IOCTL_SGE) { 824562306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "SGE count [%d] > max limit [%d]\n", 824662306a36Sopenharmony_ci ioc->sge_count, MAX_IOCTL_SGE); 824762306a36Sopenharmony_ci return -EINVAL; 824862306a36Sopenharmony_ci } 824962306a36Sopenharmony_ci 825062306a36Sopenharmony_ci if ((ioc->frame.hdr.cmd >= MFI_CMD_OP_COUNT) || 825162306a36Sopenharmony_ci ((ioc->frame.hdr.cmd == MFI_CMD_NVME) && 825262306a36Sopenharmony_ci !instance->support_nvme_passthru) || 825362306a36Sopenharmony_ci ((ioc->frame.hdr.cmd == MFI_CMD_TOOLBOX) && 825462306a36Sopenharmony_ci !instance->support_pci_lane_margining)) { 825562306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 825662306a36Sopenharmony_ci "Received invalid ioctl command 0x%x\n", 825762306a36Sopenharmony_ci ioc->frame.hdr.cmd); 825862306a36Sopenharmony_ci return -ENOTSUPP; 825962306a36Sopenharmony_ci } 826062306a36Sopenharmony_ci 826162306a36Sopenharmony_ci cmd = megasas_get_cmd(instance); 826262306a36Sopenharmony_ci if (!cmd) { 826362306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to get a cmd packet\n"); 826462306a36Sopenharmony_ci return -ENOMEM; 826562306a36Sopenharmony_ci } 826662306a36Sopenharmony_ci 826762306a36Sopenharmony_ci /* 826862306a36Sopenharmony_ci * User's IOCTL packet has 2 frames (maximum). Copy those two 826962306a36Sopenharmony_ci * frames into our cmd's frames. cmd->frame's context will get 827062306a36Sopenharmony_ci * overwritten when we copy from user's frames. So set that value 827162306a36Sopenharmony_ci * alone separately 827262306a36Sopenharmony_ci */ 827362306a36Sopenharmony_ci memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); 827462306a36Sopenharmony_ci cmd->frame->hdr.context = cpu_to_le32(cmd->index); 827562306a36Sopenharmony_ci cmd->frame->hdr.pad_0 = 0; 827662306a36Sopenharmony_ci 827762306a36Sopenharmony_ci cmd->frame->hdr.flags &= (~MFI_FRAME_IEEE); 827862306a36Sopenharmony_ci 827962306a36Sopenharmony_ci if (instance->consistent_mask_64bit) 828062306a36Sopenharmony_ci cmd->frame->hdr.flags |= cpu_to_le16((MFI_FRAME_SGL64 | 828162306a36Sopenharmony_ci MFI_FRAME_SENSE64)); 828262306a36Sopenharmony_ci else 828362306a36Sopenharmony_ci cmd->frame->hdr.flags &= cpu_to_le16(~(MFI_FRAME_SGL64 | 828462306a36Sopenharmony_ci MFI_FRAME_SENSE64)); 828562306a36Sopenharmony_ci 828662306a36Sopenharmony_ci if (cmd->frame->hdr.cmd == MFI_CMD_DCMD) 828762306a36Sopenharmony_ci opcode = le32_to_cpu(cmd->frame->dcmd.opcode); 828862306a36Sopenharmony_ci 828962306a36Sopenharmony_ci if (opcode == MR_DCMD_CTRL_SHUTDOWN) { 829062306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 829162306a36Sopenharmony_ci if (megasas_get_ctrl_info(instance) != DCMD_SUCCESS) { 829262306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 829362306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 829462306a36Sopenharmony_ci return -1; 829562306a36Sopenharmony_ci } 829662306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 829762306a36Sopenharmony_ci } 829862306a36Sopenharmony_ci 829962306a36Sopenharmony_ci if (opcode == MR_DRIVER_SET_APP_CRASHDUMP_MODE) { 830062306a36Sopenharmony_ci error = megasas_set_crash_dump_params_ioctl(cmd); 830162306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 830262306a36Sopenharmony_ci return error; 830362306a36Sopenharmony_ci } 830462306a36Sopenharmony_ci 830562306a36Sopenharmony_ci /* 830662306a36Sopenharmony_ci * The management interface between applications and the fw uses 830762306a36Sopenharmony_ci * MFI frames. E.g, RAID configuration changes, LD property changes 830862306a36Sopenharmony_ci * etc are accomplishes through different kinds of MFI frames. The 830962306a36Sopenharmony_ci * driver needs to care only about substituting user buffers with 831062306a36Sopenharmony_ci * kernel buffers in SGLs. The location of SGL is embedded in the 831162306a36Sopenharmony_ci * struct iocpacket itself. 831262306a36Sopenharmony_ci */ 831362306a36Sopenharmony_ci if (instance->consistent_mask_64bit) 831462306a36Sopenharmony_ci kern_sge64 = (struct megasas_sge64 *) 831562306a36Sopenharmony_ci ((unsigned long)cmd->frame + ioc->sgl_off); 831662306a36Sopenharmony_ci else 831762306a36Sopenharmony_ci kern_sge32 = (struct megasas_sge32 *) 831862306a36Sopenharmony_ci ((unsigned long)cmd->frame + ioc->sgl_off); 831962306a36Sopenharmony_ci 832062306a36Sopenharmony_ci /* 832162306a36Sopenharmony_ci * For each user buffer, create a mirror buffer and copy in 832262306a36Sopenharmony_ci */ 832362306a36Sopenharmony_ci for (i = 0; i < ioc->sge_count; i++) { 832462306a36Sopenharmony_ci if (!ioc->sgl[i].iov_len) 832562306a36Sopenharmony_ci continue; 832662306a36Sopenharmony_ci 832762306a36Sopenharmony_ci kbuff_arr[i] = dma_alloc_coherent(&instance->pdev->dev, 832862306a36Sopenharmony_ci ioc->sgl[i].iov_len, 832962306a36Sopenharmony_ci &buf_handle, GFP_KERNEL); 833062306a36Sopenharmony_ci if (!kbuff_arr[i]) { 833162306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Failed to alloc " 833262306a36Sopenharmony_ci "kernel SGL buffer for IOCTL\n"); 833362306a36Sopenharmony_ci error = -ENOMEM; 833462306a36Sopenharmony_ci goto out; 833562306a36Sopenharmony_ci } 833662306a36Sopenharmony_ci 833762306a36Sopenharmony_ci /* 833862306a36Sopenharmony_ci * We don't change the dma_coherent_mask, so 833962306a36Sopenharmony_ci * dma_alloc_coherent only returns 32bit addresses 834062306a36Sopenharmony_ci */ 834162306a36Sopenharmony_ci if (instance->consistent_mask_64bit) { 834262306a36Sopenharmony_ci kern_sge64[i].phys_addr = cpu_to_le64(buf_handle); 834362306a36Sopenharmony_ci kern_sge64[i].length = cpu_to_le32(ioc->sgl[i].iov_len); 834462306a36Sopenharmony_ci } else { 834562306a36Sopenharmony_ci kern_sge32[i].phys_addr = cpu_to_le32(buf_handle); 834662306a36Sopenharmony_ci kern_sge32[i].length = cpu_to_le32(ioc->sgl[i].iov_len); 834762306a36Sopenharmony_ci } 834862306a36Sopenharmony_ci 834962306a36Sopenharmony_ci /* 835062306a36Sopenharmony_ci * We created a kernel buffer corresponding to the 835162306a36Sopenharmony_ci * user buffer. Now copy in from the user buffer 835262306a36Sopenharmony_ci */ 835362306a36Sopenharmony_ci if (copy_from_user(kbuff_arr[i], ioc->sgl[i].iov_base, 835462306a36Sopenharmony_ci (u32) (ioc->sgl[i].iov_len))) { 835562306a36Sopenharmony_ci error = -EFAULT; 835662306a36Sopenharmony_ci goto out; 835762306a36Sopenharmony_ci } 835862306a36Sopenharmony_ci } 835962306a36Sopenharmony_ci 836062306a36Sopenharmony_ci if (ioc->sense_len) { 836162306a36Sopenharmony_ci /* make sure the pointer is part of the frame */ 836262306a36Sopenharmony_ci if (ioc->sense_off > 836362306a36Sopenharmony_ci (sizeof(union megasas_frame) - sizeof(__le64))) { 836462306a36Sopenharmony_ci error = -EINVAL; 836562306a36Sopenharmony_ci goto out; 836662306a36Sopenharmony_ci } 836762306a36Sopenharmony_ci 836862306a36Sopenharmony_ci sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len, 836962306a36Sopenharmony_ci &sense_handle, GFP_KERNEL); 837062306a36Sopenharmony_ci if (!sense) { 837162306a36Sopenharmony_ci error = -ENOMEM; 837262306a36Sopenharmony_ci goto out; 837362306a36Sopenharmony_ci } 837462306a36Sopenharmony_ci 837562306a36Sopenharmony_ci /* always store 64 bits regardless of addressing */ 837662306a36Sopenharmony_ci sense_ptr = (void *)cmd->frame + ioc->sense_off; 837762306a36Sopenharmony_ci put_unaligned_le64(sense_handle, sense_ptr); 837862306a36Sopenharmony_ci } 837962306a36Sopenharmony_ci 838062306a36Sopenharmony_ci /* 838162306a36Sopenharmony_ci * Set the sync_cmd flag so that the ISR knows not to complete this 838262306a36Sopenharmony_ci * cmd to the SCSI mid-layer 838362306a36Sopenharmony_ci */ 838462306a36Sopenharmony_ci cmd->sync_cmd = 1; 838562306a36Sopenharmony_ci 838662306a36Sopenharmony_ci ret = megasas_issue_blocked_cmd(instance, cmd, 0); 838762306a36Sopenharmony_ci switch (ret) { 838862306a36Sopenharmony_ci case DCMD_INIT: 838962306a36Sopenharmony_ci case DCMD_BUSY: 839062306a36Sopenharmony_ci cmd->sync_cmd = 0; 839162306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 839262306a36Sopenharmony_ci "return -EBUSY from %s %d cmd 0x%x opcode 0x%x cmd->cmd_status_drv 0x%x\n", 839362306a36Sopenharmony_ci __func__, __LINE__, cmd->frame->hdr.cmd, opcode, 839462306a36Sopenharmony_ci cmd->cmd_status_drv); 839562306a36Sopenharmony_ci error = -EBUSY; 839662306a36Sopenharmony_ci goto out; 839762306a36Sopenharmony_ci } 839862306a36Sopenharmony_ci 839962306a36Sopenharmony_ci cmd->sync_cmd = 0; 840062306a36Sopenharmony_ci 840162306a36Sopenharmony_ci if (instance->unload == 1) { 840262306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "Driver unload is in progress " 840362306a36Sopenharmony_ci "don't submit data to application\n"); 840462306a36Sopenharmony_ci goto out; 840562306a36Sopenharmony_ci } 840662306a36Sopenharmony_ci /* 840762306a36Sopenharmony_ci * copy out the kernel buffers to user buffers 840862306a36Sopenharmony_ci */ 840962306a36Sopenharmony_ci for (i = 0; i < ioc->sge_count; i++) { 841062306a36Sopenharmony_ci if (copy_to_user(ioc->sgl[i].iov_base, kbuff_arr[i], 841162306a36Sopenharmony_ci ioc->sgl[i].iov_len)) { 841262306a36Sopenharmony_ci error = -EFAULT; 841362306a36Sopenharmony_ci goto out; 841462306a36Sopenharmony_ci } 841562306a36Sopenharmony_ci } 841662306a36Sopenharmony_ci 841762306a36Sopenharmony_ci /* 841862306a36Sopenharmony_ci * copy out the sense 841962306a36Sopenharmony_ci */ 842062306a36Sopenharmony_ci if (ioc->sense_len) { 842162306a36Sopenharmony_ci void __user *uptr; 842262306a36Sopenharmony_ci /* 842362306a36Sopenharmony_ci * sense_ptr points to the location that has the user 842462306a36Sopenharmony_ci * sense buffer address 842562306a36Sopenharmony_ci */ 842662306a36Sopenharmony_ci sense_ptr = (void *)ioc->frame.raw + ioc->sense_off; 842762306a36Sopenharmony_ci if (in_compat_syscall()) 842862306a36Sopenharmony_ci uptr = compat_ptr(get_unaligned((compat_uptr_t *) 842962306a36Sopenharmony_ci sense_ptr)); 843062306a36Sopenharmony_ci else 843162306a36Sopenharmony_ci uptr = get_unaligned((void __user **)sense_ptr); 843262306a36Sopenharmony_ci 843362306a36Sopenharmony_ci if (copy_to_user(uptr, sense, ioc->sense_len)) { 843462306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Failed to copy out to user " 843562306a36Sopenharmony_ci "sense data\n"); 843662306a36Sopenharmony_ci error = -EFAULT; 843762306a36Sopenharmony_ci goto out; 843862306a36Sopenharmony_ci } 843962306a36Sopenharmony_ci } 844062306a36Sopenharmony_ci 844162306a36Sopenharmony_ci /* 844262306a36Sopenharmony_ci * copy the status codes returned by the fw 844362306a36Sopenharmony_ci */ 844462306a36Sopenharmony_ci if (copy_to_user(&user_ioc->frame.hdr.cmd_status, 844562306a36Sopenharmony_ci &cmd->frame->hdr.cmd_status, sizeof(u8))) { 844662306a36Sopenharmony_ci dev_printk(KERN_DEBUG, &instance->pdev->dev, "Error copying out cmd_status\n"); 844762306a36Sopenharmony_ci error = -EFAULT; 844862306a36Sopenharmony_ci } 844962306a36Sopenharmony_ci 845062306a36Sopenharmony_ciout: 845162306a36Sopenharmony_ci if (sense) { 845262306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, ioc->sense_len, 845362306a36Sopenharmony_ci sense, sense_handle); 845462306a36Sopenharmony_ci } 845562306a36Sopenharmony_ci 845662306a36Sopenharmony_ci for (i = 0; i < ioc->sge_count; i++) { 845762306a36Sopenharmony_ci if (kbuff_arr[i]) { 845862306a36Sopenharmony_ci if (instance->consistent_mask_64bit) 845962306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 846062306a36Sopenharmony_ci le32_to_cpu(kern_sge64[i].length), 846162306a36Sopenharmony_ci kbuff_arr[i], 846262306a36Sopenharmony_ci le64_to_cpu(kern_sge64[i].phys_addr)); 846362306a36Sopenharmony_ci else 846462306a36Sopenharmony_ci dma_free_coherent(&instance->pdev->dev, 846562306a36Sopenharmony_ci le32_to_cpu(kern_sge32[i].length), 846662306a36Sopenharmony_ci kbuff_arr[i], 846762306a36Sopenharmony_ci le32_to_cpu(kern_sge32[i].phys_addr)); 846862306a36Sopenharmony_ci kbuff_arr[i] = NULL; 846962306a36Sopenharmony_ci } 847062306a36Sopenharmony_ci } 847162306a36Sopenharmony_ci 847262306a36Sopenharmony_ci megasas_return_cmd(instance, cmd); 847362306a36Sopenharmony_ci return error; 847462306a36Sopenharmony_ci} 847562306a36Sopenharmony_ci 847662306a36Sopenharmony_cistatic struct megasas_iocpacket * 847762306a36Sopenharmony_cimegasas_compat_iocpacket_get_user(void __user *arg) 847862306a36Sopenharmony_ci{ 847962306a36Sopenharmony_ci struct megasas_iocpacket *ioc; 848062306a36Sopenharmony_ci struct compat_megasas_iocpacket __user *cioc = arg; 848162306a36Sopenharmony_ci size_t size; 848262306a36Sopenharmony_ci int err = -EFAULT; 848362306a36Sopenharmony_ci int i; 848462306a36Sopenharmony_ci 848562306a36Sopenharmony_ci ioc = kzalloc(sizeof(*ioc), GFP_KERNEL); 848662306a36Sopenharmony_ci if (!ioc) 848762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 848862306a36Sopenharmony_ci size = offsetof(struct megasas_iocpacket, frame) + sizeof(ioc->frame); 848962306a36Sopenharmony_ci if (copy_from_user(ioc, arg, size)) 849062306a36Sopenharmony_ci goto out; 849162306a36Sopenharmony_ci 849262306a36Sopenharmony_ci for (i = 0; i < MAX_IOCTL_SGE; i++) { 849362306a36Sopenharmony_ci compat_uptr_t iov_base; 849462306a36Sopenharmony_ci 849562306a36Sopenharmony_ci if (get_user(iov_base, &cioc->sgl[i].iov_base) || 849662306a36Sopenharmony_ci get_user(ioc->sgl[i].iov_len, &cioc->sgl[i].iov_len)) 849762306a36Sopenharmony_ci goto out; 849862306a36Sopenharmony_ci 849962306a36Sopenharmony_ci ioc->sgl[i].iov_base = compat_ptr(iov_base); 850062306a36Sopenharmony_ci } 850162306a36Sopenharmony_ci 850262306a36Sopenharmony_ci return ioc; 850362306a36Sopenharmony_ciout: 850462306a36Sopenharmony_ci kfree(ioc); 850562306a36Sopenharmony_ci return ERR_PTR(err); 850662306a36Sopenharmony_ci} 850762306a36Sopenharmony_ci 850862306a36Sopenharmony_cistatic int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) 850962306a36Sopenharmony_ci{ 851062306a36Sopenharmony_ci struct megasas_iocpacket __user *user_ioc = 851162306a36Sopenharmony_ci (struct megasas_iocpacket __user *)arg; 851262306a36Sopenharmony_ci struct megasas_iocpacket *ioc; 851362306a36Sopenharmony_ci struct megasas_instance *instance; 851462306a36Sopenharmony_ci int error; 851562306a36Sopenharmony_ci 851662306a36Sopenharmony_ci if (in_compat_syscall()) 851762306a36Sopenharmony_ci ioc = megasas_compat_iocpacket_get_user(user_ioc); 851862306a36Sopenharmony_ci else 851962306a36Sopenharmony_ci ioc = memdup_user(user_ioc, sizeof(struct megasas_iocpacket)); 852062306a36Sopenharmony_ci 852162306a36Sopenharmony_ci if (IS_ERR(ioc)) 852262306a36Sopenharmony_ci return PTR_ERR(ioc); 852362306a36Sopenharmony_ci 852462306a36Sopenharmony_ci instance = megasas_lookup_instance(ioc->host_no); 852562306a36Sopenharmony_ci if (!instance) { 852662306a36Sopenharmony_ci error = -ENODEV; 852762306a36Sopenharmony_ci goto out_kfree_ioc; 852862306a36Sopenharmony_ci } 852962306a36Sopenharmony_ci 853062306a36Sopenharmony_ci /* Block ioctls in VF mode */ 853162306a36Sopenharmony_ci if (instance->requestorId && !allow_vf_ioctls) { 853262306a36Sopenharmony_ci error = -ENODEV; 853362306a36Sopenharmony_ci goto out_kfree_ioc; 853462306a36Sopenharmony_ci } 853562306a36Sopenharmony_ci 853662306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 853762306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "Controller in crit error\n"); 853862306a36Sopenharmony_ci error = -ENODEV; 853962306a36Sopenharmony_ci goto out_kfree_ioc; 854062306a36Sopenharmony_ci } 854162306a36Sopenharmony_ci 854262306a36Sopenharmony_ci if (instance->unload == 1) { 854362306a36Sopenharmony_ci error = -ENODEV; 854462306a36Sopenharmony_ci goto out_kfree_ioc; 854562306a36Sopenharmony_ci } 854662306a36Sopenharmony_ci 854762306a36Sopenharmony_ci if (down_interruptible(&instance->ioctl_sem)) { 854862306a36Sopenharmony_ci error = -ERESTARTSYS; 854962306a36Sopenharmony_ci goto out_kfree_ioc; 855062306a36Sopenharmony_ci } 855162306a36Sopenharmony_ci 855262306a36Sopenharmony_ci if (megasas_wait_for_adapter_operational(instance)) { 855362306a36Sopenharmony_ci error = -ENODEV; 855462306a36Sopenharmony_ci goto out_up; 855562306a36Sopenharmony_ci } 855662306a36Sopenharmony_ci 855762306a36Sopenharmony_ci error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc); 855862306a36Sopenharmony_ciout_up: 855962306a36Sopenharmony_ci up(&instance->ioctl_sem); 856062306a36Sopenharmony_ci 856162306a36Sopenharmony_ciout_kfree_ioc: 856262306a36Sopenharmony_ci kfree(ioc); 856362306a36Sopenharmony_ci return error; 856462306a36Sopenharmony_ci} 856562306a36Sopenharmony_ci 856662306a36Sopenharmony_cistatic int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) 856762306a36Sopenharmony_ci{ 856862306a36Sopenharmony_ci struct megasas_instance *instance; 856962306a36Sopenharmony_ci struct megasas_aen aen; 857062306a36Sopenharmony_ci int error; 857162306a36Sopenharmony_ci 857262306a36Sopenharmony_ci if (file->private_data != file) { 857362306a36Sopenharmony_ci printk(KERN_DEBUG "megasas: fasync_helper was not " 857462306a36Sopenharmony_ci "called first\n"); 857562306a36Sopenharmony_ci return -EINVAL; 857662306a36Sopenharmony_ci } 857762306a36Sopenharmony_ci 857862306a36Sopenharmony_ci if (copy_from_user(&aen, (void __user *)arg, sizeof(aen))) 857962306a36Sopenharmony_ci return -EFAULT; 858062306a36Sopenharmony_ci 858162306a36Sopenharmony_ci instance = megasas_lookup_instance(aen.host_no); 858262306a36Sopenharmony_ci 858362306a36Sopenharmony_ci if (!instance) 858462306a36Sopenharmony_ci return -ENODEV; 858562306a36Sopenharmony_ci 858662306a36Sopenharmony_ci if (atomic_read(&instance->adprecovery) == MEGASAS_HW_CRITICAL_ERROR) { 858762306a36Sopenharmony_ci return -ENODEV; 858862306a36Sopenharmony_ci } 858962306a36Sopenharmony_ci 859062306a36Sopenharmony_ci if (instance->unload == 1) { 859162306a36Sopenharmony_ci return -ENODEV; 859262306a36Sopenharmony_ci } 859362306a36Sopenharmony_ci 859462306a36Sopenharmony_ci if (megasas_wait_for_adapter_operational(instance)) 859562306a36Sopenharmony_ci return -ENODEV; 859662306a36Sopenharmony_ci 859762306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 859862306a36Sopenharmony_ci error = megasas_register_aen(instance, aen.seq_num, 859962306a36Sopenharmony_ci aen.class_locale_word); 860062306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 860162306a36Sopenharmony_ci return error; 860262306a36Sopenharmony_ci} 860362306a36Sopenharmony_ci 860462306a36Sopenharmony_ci/** 860562306a36Sopenharmony_ci * megasas_mgmt_ioctl - char node ioctl entry point 860662306a36Sopenharmony_ci * @file: char device file pointer 860762306a36Sopenharmony_ci * @cmd: ioctl command 860862306a36Sopenharmony_ci * @arg: ioctl command arguments address 860962306a36Sopenharmony_ci */ 861062306a36Sopenharmony_cistatic long 861162306a36Sopenharmony_cimegasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 861262306a36Sopenharmony_ci{ 861362306a36Sopenharmony_ci switch (cmd) { 861462306a36Sopenharmony_ci case MEGASAS_IOC_FIRMWARE: 861562306a36Sopenharmony_ci return megasas_mgmt_ioctl_fw(file, arg); 861662306a36Sopenharmony_ci 861762306a36Sopenharmony_ci case MEGASAS_IOC_GET_AEN: 861862306a36Sopenharmony_ci return megasas_mgmt_ioctl_aen(file, arg); 861962306a36Sopenharmony_ci } 862062306a36Sopenharmony_ci 862162306a36Sopenharmony_ci return -ENOTTY; 862262306a36Sopenharmony_ci} 862362306a36Sopenharmony_ci 862462306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 862562306a36Sopenharmony_cistatic long 862662306a36Sopenharmony_cimegasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, 862762306a36Sopenharmony_ci unsigned long arg) 862862306a36Sopenharmony_ci{ 862962306a36Sopenharmony_ci switch (cmd) { 863062306a36Sopenharmony_ci case MEGASAS_IOC_FIRMWARE32: 863162306a36Sopenharmony_ci return megasas_mgmt_ioctl_fw(file, arg); 863262306a36Sopenharmony_ci case MEGASAS_IOC_GET_AEN: 863362306a36Sopenharmony_ci return megasas_mgmt_ioctl_aen(file, arg); 863462306a36Sopenharmony_ci } 863562306a36Sopenharmony_ci 863662306a36Sopenharmony_ci return -ENOTTY; 863762306a36Sopenharmony_ci} 863862306a36Sopenharmony_ci#endif 863962306a36Sopenharmony_ci 864062306a36Sopenharmony_ci/* 864162306a36Sopenharmony_ci * File operations structure for management interface 864262306a36Sopenharmony_ci */ 864362306a36Sopenharmony_cistatic const struct file_operations megasas_mgmt_fops = { 864462306a36Sopenharmony_ci .owner = THIS_MODULE, 864562306a36Sopenharmony_ci .open = megasas_mgmt_open, 864662306a36Sopenharmony_ci .fasync = megasas_mgmt_fasync, 864762306a36Sopenharmony_ci .unlocked_ioctl = megasas_mgmt_ioctl, 864862306a36Sopenharmony_ci .poll = megasas_mgmt_poll, 864962306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 865062306a36Sopenharmony_ci .compat_ioctl = megasas_mgmt_compat_ioctl, 865162306a36Sopenharmony_ci#endif 865262306a36Sopenharmony_ci .llseek = noop_llseek, 865362306a36Sopenharmony_ci}; 865462306a36Sopenharmony_ci 865562306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(megasas_pm_ops, megasas_suspend, megasas_resume); 865662306a36Sopenharmony_ci 865762306a36Sopenharmony_ci/* 865862306a36Sopenharmony_ci * PCI hotplug support registration structure 865962306a36Sopenharmony_ci */ 866062306a36Sopenharmony_cistatic struct pci_driver megasas_pci_driver = { 866162306a36Sopenharmony_ci 866262306a36Sopenharmony_ci .name = "megaraid_sas", 866362306a36Sopenharmony_ci .id_table = megasas_pci_table, 866462306a36Sopenharmony_ci .probe = megasas_probe_one, 866562306a36Sopenharmony_ci .remove = megasas_detach_one, 866662306a36Sopenharmony_ci .driver.pm = &megasas_pm_ops, 866762306a36Sopenharmony_ci .shutdown = megasas_shutdown, 866862306a36Sopenharmony_ci}; 866962306a36Sopenharmony_ci 867062306a36Sopenharmony_ci/* 867162306a36Sopenharmony_ci * Sysfs driver attributes 867262306a36Sopenharmony_ci */ 867362306a36Sopenharmony_cistatic ssize_t version_show(struct device_driver *dd, char *buf) 867462306a36Sopenharmony_ci{ 867562306a36Sopenharmony_ci return snprintf(buf, strlen(MEGASAS_VERSION) + 2, "%s\n", 867662306a36Sopenharmony_ci MEGASAS_VERSION); 867762306a36Sopenharmony_ci} 867862306a36Sopenharmony_cistatic DRIVER_ATTR_RO(version); 867962306a36Sopenharmony_ci 868062306a36Sopenharmony_cistatic ssize_t release_date_show(struct device_driver *dd, char *buf) 868162306a36Sopenharmony_ci{ 868262306a36Sopenharmony_ci return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n", 868362306a36Sopenharmony_ci MEGASAS_RELDATE); 868462306a36Sopenharmony_ci} 868562306a36Sopenharmony_cistatic DRIVER_ATTR_RO(release_date); 868662306a36Sopenharmony_ci 868762306a36Sopenharmony_cistatic ssize_t support_poll_for_event_show(struct device_driver *dd, char *buf) 868862306a36Sopenharmony_ci{ 868962306a36Sopenharmony_ci return sprintf(buf, "%u\n", support_poll_for_event); 869062306a36Sopenharmony_ci} 869162306a36Sopenharmony_cistatic DRIVER_ATTR_RO(support_poll_for_event); 869262306a36Sopenharmony_ci 869362306a36Sopenharmony_cistatic ssize_t support_device_change_show(struct device_driver *dd, char *buf) 869462306a36Sopenharmony_ci{ 869562306a36Sopenharmony_ci return sprintf(buf, "%u\n", support_device_change); 869662306a36Sopenharmony_ci} 869762306a36Sopenharmony_cistatic DRIVER_ATTR_RO(support_device_change); 869862306a36Sopenharmony_ci 869962306a36Sopenharmony_cistatic ssize_t dbg_lvl_show(struct device_driver *dd, char *buf) 870062306a36Sopenharmony_ci{ 870162306a36Sopenharmony_ci return sprintf(buf, "%u\n", megasas_dbg_lvl); 870262306a36Sopenharmony_ci} 870362306a36Sopenharmony_ci 870462306a36Sopenharmony_cistatic ssize_t dbg_lvl_store(struct device_driver *dd, const char *buf, 870562306a36Sopenharmony_ci size_t count) 870662306a36Sopenharmony_ci{ 870762306a36Sopenharmony_ci int retval = count; 870862306a36Sopenharmony_ci 870962306a36Sopenharmony_ci if (sscanf(buf, "%u", &megasas_dbg_lvl) < 1) { 871062306a36Sopenharmony_ci printk(KERN_ERR "megasas: could not set dbg_lvl\n"); 871162306a36Sopenharmony_ci retval = -EINVAL; 871262306a36Sopenharmony_ci } 871362306a36Sopenharmony_ci return retval; 871462306a36Sopenharmony_ci} 871562306a36Sopenharmony_cistatic DRIVER_ATTR_RW(dbg_lvl); 871662306a36Sopenharmony_ci 871762306a36Sopenharmony_cistatic ssize_t 871862306a36Sopenharmony_cisupport_nvme_encapsulation_show(struct device_driver *dd, char *buf) 871962306a36Sopenharmony_ci{ 872062306a36Sopenharmony_ci return sprintf(buf, "%u\n", support_nvme_encapsulation); 872162306a36Sopenharmony_ci} 872262306a36Sopenharmony_ci 872362306a36Sopenharmony_cistatic DRIVER_ATTR_RO(support_nvme_encapsulation); 872462306a36Sopenharmony_ci 872562306a36Sopenharmony_cistatic ssize_t 872662306a36Sopenharmony_cisupport_pci_lane_margining_show(struct device_driver *dd, char *buf) 872762306a36Sopenharmony_ci{ 872862306a36Sopenharmony_ci return sprintf(buf, "%u\n", support_pci_lane_margining); 872962306a36Sopenharmony_ci} 873062306a36Sopenharmony_ci 873162306a36Sopenharmony_cistatic DRIVER_ATTR_RO(support_pci_lane_margining); 873262306a36Sopenharmony_ci 873362306a36Sopenharmony_cistatic inline void megasas_remove_scsi_device(struct scsi_device *sdev) 873462306a36Sopenharmony_ci{ 873562306a36Sopenharmony_ci sdev_printk(KERN_INFO, sdev, "SCSI device is removed\n"); 873662306a36Sopenharmony_ci scsi_remove_device(sdev); 873762306a36Sopenharmony_ci scsi_device_put(sdev); 873862306a36Sopenharmony_ci} 873962306a36Sopenharmony_ci 874062306a36Sopenharmony_ci/** 874162306a36Sopenharmony_ci * megasas_update_device_list - Update the PD and LD device list from FW 874262306a36Sopenharmony_ci * after an AEN event notification 874362306a36Sopenharmony_ci * @instance: Adapter soft state 874462306a36Sopenharmony_ci * @event_type: Indicates type of event (PD or LD event) 874562306a36Sopenharmony_ci * 874662306a36Sopenharmony_ci * @return: Success or failure 874762306a36Sopenharmony_ci * 874862306a36Sopenharmony_ci * Issue DCMDs to Firmware to update the internal device list in driver. 874962306a36Sopenharmony_ci * Based on the FW support, driver sends the HOST_DEVICE_LIST or combination 875062306a36Sopenharmony_ci * of PD_LIST/LD_LIST_QUERY DCMDs to get the device list. 875162306a36Sopenharmony_ci */ 875262306a36Sopenharmony_cistatic 875362306a36Sopenharmony_ciint megasas_update_device_list(struct megasas_instance *instance, 875462306a36Sopenharmony_ci int event_type) 875562306a36Sopenharmony_ci{ 875662306a36Sopenharmony_ci int dcmd_ret; 875762306a36Sopenharmony_ci 875862306a36Sopenharmony_ci if (instance->enable_fw_dev_list) { 875962306a36Sopenharmony_ci return megasas_host_device_list_query(instance, false); 876062306a36Sopenharmony_ci } else { 876162306a36Sopenharmony_ci if (event_type & SCAN_PD_CHANNEL) { 876262306a36Sopenharmony_ci dcmd_ret = megasas_get_pd_list(instance); 876362306a36Sopenharmony_ci if (dcmd_ret != DCMD_SUCCESS) 876462306a36Sopenharmony_ci return dcmd_ret; 876562306a36Sopenharmony_ci } 876662306a36Sopenharmony_ci 876762306a36Sopenharmony_ci if (event_type & SCAN_VD_CHANNEL) { 876862306a36Sopenharmony_ci if (!instance->requestorId || 876962306a36Sopenharmony_ci megasas_get_ld_vf_affiliation(instance, 0)) { 877062306a36Sopenharmony_ci return megasas_ld_list_query(instance, 877162306a36Sopenharmony_ci MR_LD_QUERY_TYPE_EXPOSED_TO_HOST); 877262306a36Sopenharmony_ci } 877362306a36Sopenharmony_ci } 877462306a36Sopenharmony_ci } 877562306a36Sopenharmony_ci return DCMD_SUCCESS; 877662306a36Sopenharmony_ci} 877762306a36Sopenharmony_ci 877862306a36Sopenharmony_ci/** 877962306a36Sopenharmony_ci * megasas_add_remove_devices - Add/remove devices to SCSI mid-layer 878062306a36Sopenharmony_ci * after an AEN event notification 878162306a36Sopenharmony_ci * @instance: Adapter soft state 878262306a36Sopenharmony_ci * @scan_type: Indicates type of devices (PD/LD) to add 878362306a36Sopenharmony_ci * @return void 878462306a36Sopenharmony_ci */ 878562306a36Sopenharmony_cistatic 878662306a36Sopenharmony_civoid megasas_add_remove_devices(struct megasas_instance *instance, 878762306a36Sopenharmony_ci int scan_type) 878862306a36Sopenharmony_ci{ 878962306a36Sopenharmony_ci int i, j; 879062306a36Sopenharmony_ci u16 pd_index = 0; 879162306a36Sopenharmony_ci u16 ld_index = 0; 879262306a36Sopenharmony_ci u16 channel = 0, id = 0; 879362306a36Sopenharmony_ci struct Scsi_Host *host; 879462306a36Sopenharmony_ci struct scsi_device *sdev1; 879562306a36Sopenharmony_ci struct MR_HOST_DEVICE_LIST *targetid_list = NULL; 879662306a36Sopenharmony_ci struct MR_HOST_DEVICE_LIST_ENTRY *targetid_entry = NULL; 879762306a36Sopenharmony_ci 879862306a36Sopenharmony_ci host = instance->host; 879962306a36Sopenharmony_ci 880062306a36Sopenharmony_ci if (instance->enable_fw_dev_list) { 880162306a36Sopenharmony_ci targetid_list = instance->host_device_list_buf; 880262306a36Sopenharmony_ci for (i = 0; i < targetid_list->count; i++) { 880362306a36Sopenharmony_ci targetid_entry = &targetid_list->host_device_list[i]; 880462306a36Sopenharmony_ci if (targetid_entry->flags.u.bits.is_sys_pd) { 880562306a36Sopenharmony_ci channel = le16_to_cpu(targetid_entry->target_id) / 880662306a36Sopenharmony_ci MEGASAS_MAX_DEV_PER_CHANNEL; 880762306a36Sopenharmony_ci id = le16_to_cpu(targetid_entry->target_id) % 880862306a36Sopenharmony_ci MEGASAS_MAX_DEV_PER_CHANNEL; 880962306a36Sopenharmony_ci } else { 881062306a36Sopenharmony_ci channel = MEGASAS_MAX_PD_CHANNELS + 881162306a36Sopenharmony_ci (le16_to_cpu(targetid_entry->target_id) / 881262306a36Sopenharmony_ci MEGASAS_MAX_DEV_PER_CHANNEL); 881362306a36Sopenharmony_ci id = le16_to_cpu(targetid_entry->target_id) % 881462306a36Sopenharmony_ci MEGASAS_MAX_DEV_PER_CHANNEL; 881562306a36Sopenharmony_ci } 881662306a36Sopenharmony_ci sdev1 = scsi_device_lookup(host, channel, id, 0); 881762306a36Sopenharmony_ci if (!sdev1) { 881862306a36Sopenharmony_ci scsi_add_device(host, channel, id, 0); 881962306a36Sopenharmony_ci } else { 882062306a36Sopenharmony_ci scsi_device_put(sdev1); 882162306a36Sopenharmony_ci } 882262306a36Sopenharmony_ci } 882362306a36Sopenharmony_ci } 882462306a36Sopenharmony_ci 882562306a36Sopenharmony_ci if (scan_type & SCAN_PD_CHANNEL) { 882662306a36Sopenharmony_ci for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { 882762306a36Sopenharmony_ci for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { 882862306a36Sopenharmony_ci pd_index = i * MEGASAS_MAX_DEV_PER_CHANNEL + j; 882962306a36Sopenharmony_ci sdev1 = scsi_device_lookup(host, i, j, 0); 883062306a36Sopenharmony_ci if (instance->pd_list[pd_index].driveState == 883162306a36Sopenharmony_ci MR_PD_STATE_SYSTEM) { 883262306a36Sopenharmony_ci if (!sdev1) 883362306a36Sopenharmony_ci scsi_add_device(host, i, j, 0); 883462306a36Sopenharmony_ci else 883562306a36Sopenharmony_ci scsi_device_put(sdev1); 883662306a36Sopenharmony_ci } else { 883762306a36Sopenharmony_ci if (sdev1) 883862306a36Sopenharmony_ci megasas_remove_scsi_device(sdev1); 883962306a36Sopenharmony_ci } 884062306a36Sopenharmony_ci } 884162306a36Sopenharmony_ci } 884262306a36Sopenharmony_ci } 884362306a36Sopenharmony_ci 884462306a36Sopenharmony_ci if (scan_type & SCAN_VD_CHANNEL) { 884562306a36Sopenharmony_ci for (i = 0; i < MEGASAS_MAX_LD_CHANNELS; i++) { 884662306a36Sopenharmony_ci for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { 884762306a36Sopenharmony_ci ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; 884862306a36Sopenharmony_ci sdev1 = scsi_device_lookup(host, 884962306a36Sopenharmony_ci MEGASAS_MAX_PD_CHANNELS + i, j, 0); 885062306a36Sopenharmony_ci if (instance->ld_ids[ld_index] != 0xff) { 885162306a36Sopenharmony_ci if (!sdev1) 885262306a36Sopenharmony_ci scsi_add_device(host, MEGASAS_MAX_PD_CHANNELS + i, j, 0); 885362306a36Sopenharmony_ci else 885462306a36Sopenharmony_ci scsi_device_put(sdev1); 885562306a36Sopenharmony_ci } else { 885662306a36Sopenharmony_ci if (sdev1) 885762306a36Sopenharmony_ci megasas_remove_scsi_device(sdev1); 885862306a36Sopenharmony_ci } 885962306a36Sopenharmony_ci } 886062306a36Sopenharmony_ci } 886162306a36Sopenharmony_ci } 886262306a36Sopenharmony_ci 886362306a36Sopenharmony_ci} 886462306a36Sopenharmony_ci 886562306a36Sopenharmony_cistatic void 886662306a36Sopenharmony_cimegasas_aen_polling(struct work_struct *work) 886762306a36Sopenharmony_ci{ 886862306a36Sopenharmony_ci struct megasas_aen_event *ev = 886962306a36Sopenharmony_ci container_of(work, struct megasas_aen_event, hotplug_work.work); 887062306a36Sopenharmony_ci struct megasas_instance *instance = ev->instance; 887162306a36Sopenharmony_ci union megasas_evt_class_locale class_locale; 887262306a36Sopenharmony_ci int event_type = 0; 887362306a36Sopenharmony_ci u32 seq_num; 887462306a36Sopenharmony_ci u16 ld_target_id; 887562306a36Sopenharmony_ci int error; 887662306a36Sopenharmony_ci u8 dcmd_ret = DCMD_SUCCESS; 887762306a36Sopenharmony_ci struct scsi_device *sdev1; 887862306a36Sopenharmony_ci 887962306a36Sopenharmony_ci if (!instance) { 888062306a36Sopenharmony_ci printk(KERN_ERR "invalid instance!\n"); 888162306a36Sopenharmony_ci kfree(ev); 888262306a36Sopenharmony_ci return; 888362306a36Sopenharmony_ci } 888462306a36Sopenharmony_ci 888562306a36Sopenharmony_ci /* Don't run the event workqueue thread if OCR is running */ 888662306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 888762306a36Sopenharmony_ci 888862306a36Sopenharmony_ci instance->ev = NULL; 888962306a36Sopenharmony_ci if (instance->evt_detail) { 889062306a36Sopenharmony_ci megasas_decode_evt(instance); 889162306a36Sopenharmony_ci 889262306a36Sopenharmony_ci switch (le32_to_cpu(instance->evt_detail->code)) { 889362306a36Sopenharmony_ci 889462306a36Sopenharmony_ci case MR_EVT_PD_INSERTED: 889562306a36Sopenharmony_ci case MR_EVT_PD_REMOVED: 889662306a36Sopenharmony_ci event_type = SCAN_PD_CHANNEL; 889762306a36Sopenharmony_ci break; 889862306a36Sopenharmony_ci 889962306a36Sopenharmony_ci case MR_EVT_LD_OFFLINE: 890062306a36Sopenharmony_ci case MR_EVT_LD_DELETED: 890162306a36Sopenharmony_ci ld_target_id = instance->evt_detail->args.ld.target_id; 890262306a36Sopenharmony_ci sdev1 = scsi_device_lookup(instance->host, 890362306a36Sopenharmony_ci MEGASAS_MAX_PD_CHANNELS + 890462306a36Sopenharmony_ci (ld_target_id / MEGASAS_MAX_DEV_PER_CHANNEL), 890562306a36Sopenharmony_ci (ld_target_id % MEGASAS_MAX_DEV_PER_CHANNEL), 890662306a36Sopenharmony_ci 0); 890762306a36Sopenharmony_ci if (sdev1) 890862306a36Sopenharmony_ci megasas_remove_scsi_device(sdev1); 890962306a36Sopenharmony_ci 891062306a36Sopenharmony_ci event_type = SCAN_VD_CHANNEL; 891162306a36Sopenharmony_ci break; 891262306a36Sopenharmony_ci case MR_EVT_LD_CREATED: 891362306a36Sopenharmony_ci event_type = SCAN_VD_CHANNEL; 891462306a36Sopenharmony_ci break; 891562306a36Sopenharmony_ci 891662306a36Sopenharmony_ci case MR_EVT_CFG_CLEARED: 891762306a36Sopenharmony_ci case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: 891862306a36Sopenharmony_ci case MR_EVT_FOREIGN_CFG_IMPORTED: 891962306a36Sopenharmony_ci case MR_EVT_LD_STATE_CHANGE: 892062306a36Sopenharmony_ci event_type = SCAN_PD_CHANNEL | SCAN_VD_CHANNEL; 892162306a36Sopenharmony_ci dev_info(&instance->pdev->dev, "scanning for scsi%d...\n", 892262306a36Sopenharmony_ci instance->host->host_no); 892362306a36Sopenharmony_ci break; 892462306a36Sopenharmony_ci 892562306a36Sopenharmony_ci case MR_EVT_CTRL_PROP_CHANGED: 892662306a36Sopenharmony_ci dcmd_ret = megasas_get_ctrl_info(instance); 892762306a36Sopenharmony_ci if (dcmd_ret == DCMD_SUCCESS && 892862306a36Sopenharmony_ci instance->snapdump_wait_time) { 892962306a36Sopenharmony_ci megasas_get_snapdump_properties(instance); 893062306a36Sopenharmony_ci dev_info(&instance->pdev->dev, 893162306a36Sopenharmony_ci "Snap dump wait time\t: %d\n", 893262306a36Sopenharmony_ci instance->snapdump_wait_time); 893362306a36Sopenharmony_ci } 893462306a36Sopenharmony_ci break; 893562306a36Sopenharmony_ci default: 893662306a36Sopenharmony_ci event_type = 0; 893762306a36Sopenharmony_ci break; 893862306a36Sopenharmony_ci } 893962306a36Sopenharmony_ci } else { 894062306a36Sopenharmony_ci dev_err(&instance->pdev->dev, "invalid evt_detail!\n"); 894162306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 894262306a36Sopenharmony_ci kfree(ev); 894362306a36Sopenharmony_ci return; 894462306a36Sopenharmony_ci } 894562306a36Sopenharmony_ci 894662306a36Sopenharmony_ci if (event_type) 894762306a36Sopenharmony_ci dcmd_ret = megasas_update_device_list(instance, event_type); 894862306a36Sopenharmony_ci 894962306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 895062306a36Sopenharmony_ci 895162306a36Sopenharmony_ci if (event_type && dcmd_ret == DCMD_SUCCESS) 895262306a36Sopenharmony_ci megasas_add_remove_devices(instance, event_type); 895362306a36Sopenharmony_ci 895462306a36Sopenharmony_ci if (dcmd_ret == DCMD_SUCCESS) 895562306a36Sopenharmony_ci seq_num = le32_to_cpu(instance->evt_detail->seq_num) + 1; 895662306a36Sopenharmony_ci else 895762306a36Sopenharmony_ci seq_num = instance->last_seq_num; 895862306a36Sopenharmony_ci 895962306a36Sopenharmony_ci /* Register AEN with FW for latest sequence number plus 1 */ 896062306a36Sopenharmony_ci class_locale.members.reserved = 0; 896162306a36Sopenharmony_ci class_locale.members.locale = MR_EVT_LOCALE_ALL; 896262306a36Sopenharmony_ci class_locale.members.class = MR_EVT_CLASS_DEBUG; 896362306a36Sopenharmony_ci 896462306a36Sopenharmony_ci if (instance->aen_cmd != NULL) { 896562306a36Sopenharmony_ci kfree(ev); 896662306a36Sopenharmony_ci return; 896762306a36Sopenharmony_ci } 896862306a36Sopenharmony_ci 896962306a36Sopenharmony_ci mutex_lock(&instance->reset_mutex); 897062306a36Sopenharmony_ci error = megasas_register_aen(instance, seq_num, 897162306a36Sopenharmony_ci class_locale.word); 897262306a36Sopenharmony_ci if (error) 897362306a36Sopenharmony_ci dev_err(&instance->pdev->dev, 897462306a36Sopenharmony_ci "register aen failed error %x\n", error); 897562306a36Sopenharmony_ci 897662306a36Sopenharmony_ci mutex_unlock(&instance->reset_mutex); 897762306a36Sopenharmony_ci kfree(ev); 897862306a36Sopenharmony_ci} 897962306a36Sopenharmony_ci 898062306a36Sopenharmony_ci/** 898162306a36Sopenharmony_ci * megasas_init - Driver load entry point 898262306a36Sopenharmony_ci */ 898362306a36Sopenharmony_cistatic int __init megasas_init(void) 898462306a36Sopenharmony_ci{ 898562306a36Sopenharmony_ci int rval; 898662306a36Sopenharmony_ci 898762306a36Sopenharmony_ci /* 898862306a36Sopenharmony_ci * Booted in kdump kernel, minimize memory footprints by 898962306a36Sopenharmony_ci * disabling few features 899062306a36Sopenharmony_ci */ 899162306a36Sopenharmony_ci if (reset_devices) { 899262306a36Sopenharmony_ci msix_vectors = 1; 899362306a36Sopenharmony_ci rdpq_enable = 0; 899462306a36Sopenharmony_ci dual_qdepth_disable = 1; 899562306a36Sopenharmony_ci poll_queues = 0; 899662306a36Sopenharmony_ci } 899762306a36Sopenharmony_ci 899862306a36Sopenharmony_ci /* 899962306a36Sopenharmony_ci * Announce driver version and other information 900062306a36Sopenharmony_ci */ 900162306a36Sopenharmony_ci pr_info("megasas: %s\n", MEGASAS_VERSION); 900262306a36Sopenharmony_ci 900362306a36Sopenharmony_ci megasas_dbg_lvl = 0; 900462306a36Sopenharmony_ci support_poll_for_event = 2; 900562306a36Sopenharmony_ci support_device_change = 1; 900662306a36Sopenharmony_ci support_nvme_encapsulation = true; 900762306a36Sopenharmony_ci support_pci_lane_margining = true; 900862306a36Sopenharmony_ci 900962306a36Sopenharmony_ci memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info)); 901062306a36Sopenharmony_ci 901162306a36Sopenharmony_ci /* 901262306a36Sopenharmony_ci * Register character device node 901362306a36Sopenharmony_ci */ 901462306a36Sopenharmony_ci rval = register_chrdev(0, "megaraid_sas_ioctl", &megasas_mgmt_fops); 901562306a36Sopenharmony_ci 901662306a36Sopenharmony_ci if (rval < 0) { 901762306a36Sopenharmony_ci printk(KERN_DEBUG "megasas: failed to open device node\n"); 901862306a36Sopenharmony_ci return rval; 901962306a36Sopenharmony_ci } 902062306a36Sopenharmony_ci 902162306a36Sopenharmony_ci megasas_mgmt_majorno = rval; 902262306a36Sopenharmony_ci 902362306a36Sopenharmony_ci megasas_init_debugfs(); 902462306a36Sopenharmony_ci 902562306a36Sopenharmony_ci /* 902662306a36Sopenharmony_ci * Register ourselves as PCI hotplug module 902762306a36Sopenharmony_ci */ 902862306a36Sopenharmony_ci rval = pci_register_driver(&megasas_pci_driver); 902962306a36Sopenharmony_ci 903062306a36Sopenharmony_ci if (rval) { 903162306a36Sopenharmony_ci printk(KERN_DEBUG "megasas: PCI hotplug registration failed \n"); 903262306a36Sopenharmony_ci goto err_pcidrv; 903362306a36Sopenharmony_ci } 903462306a36Sopenharmony_ci 903562306a36Sopenharmony_ci if ((event_log_level < MFI_EVT_CLASS_DEBUG) || 903662306a36Sopenharmony_ci (event_log_level > MFI_EVT_CLASS_DEAD)) { 903762306a36Sopenharmony_ci pr_warn("megaraid_sas: provided event log level is out of range, setting it to default 2(CLASS_CRITICAL), permissible range is: -2 to 4\n"); 903862306a36Sopenharmony_ci event_log_level = MFI_EVT_CLASS_CRITICAL; 903962306a36Sopenharmony_ci } 904062306a36Sopenharmony_ci 904162306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 904262306a36Sopenharmony_ci &driver_attr_version); 904362306a36Sopenharmony_ci if (rval) 904462306a36Sopenharmony_ci goto err_dcf_attr_ver; 904562306a36Sopenharmony_ci 904662306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 904762306a36Sopenharmony_ci &driver_attr_release_date); 904862306a36Sopenharmony_ci if (rval) 904962306a36Sopenharmony_ci goto err_dcf_rel_date; 905062306a36Sopenharmony_ci 905162306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 905262306a36Sopenharmony_ci &driver_attr_support_poll_for_event); 905362306a36Sopenharmony_ci if (rval) 905462306a36Sopenharmony_ci goto err_dcf_support_poll_for_event; 905562306a36Sopenharmony_ci 905662306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 905762306a36Sopenharmony_ci &driver_attr_dbg_lvl); 905862306a36Sopenharmony_ci if (rval) 905962306a36Sopenharmony_ci goto err_dcf_dbg_lvl; 906062306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 906162306a36Sopenharmony_ci &driver_attr_support_device_change); 906262306a36Sopenharmony_ci if (rval) 906362306a36Sopenharmony_ci goto err_dcf_support_device_change; 906462306a36Sopenharmony_ci 906562306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 906662306a36Sopenharmony_ci &driver_attr_support_nvme_encapsulation); 906762306a36Sopenharmony_ci if (rval) 906862306a36Sopenharmony_ci goto err_dcf_support_nvme_encapsulation; 906962306a36Sopenharmony_ci 907062306a36Sopenharmony_ci rval = driver_create_file(&megasas_pci_driver.driver, 907162306a36Sopenharmony_ci &driver_attr_support_pci_lane_margining); 907262306a36Sopenharmony_ci if (rval) 907362306a36Sopenharmony_ci goto err_dcf_support_pci_lane_margining; 907462306a36Sopenharmony_ci 907562306a36Sopenharmony_ci return rval; 907662306a36Sopenharmony_ci 907762306a36Sopenharmony_cierr_dcf_support_pci_lane_margining: 907862306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 907962306a36Sopenharmony_ci &driver_attr_support_nvme_encapsulation); 908062306a36Sopenharmony_ci 908162306a36Sopenharmony_cierr_dcf_support_nvme_encapsulation: 908262306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 908362306a36Sopenharmony_ci &driver_attr_support_device_change); 908462306a36Sopenharmony_ci 908562306a36Sopenharmony_cierr_dcf_support_device_change: 908662306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 908762306a36Sopenharmony_ci &driver_attr_dbg_lvl); 908862306a36Sopenharmony_cierr_dcf_dbg_lvl: 908962306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 909062306a36Sopenharmony_ci &driver_attr_support_poll_for_event); 909162306a36Sopenharmony_cierr_dcf_support_poll_for_event: 909262306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 909362306a36Sopenharmony_ci &driver_attr_release_date); 909462306a36Sopenharmony_cierr_dcf_rel_date: 909562306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); 909662306a36Sopenharmony_cierr_dcf_attr_ver: 909762306a36Sopenharmony_ci pci_unregister_driver(&megasas_pci_driver); 909862306a36Sopenharmony_cierr_pcidrv: 909962306a36Sopenharmony_ci megasas_exit_debugfs(); 910062306a36Sopenharmony_ci unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); 910162306a36Sopenharmony_ci return rval; 910262306a36Sopenharmony_ci} 910362306a36Sopenharmony_ci 910462306a36Sopenharmony_ci/** 910562306a36Sopenharmony_ci * megasas_exit - Driver unload entry point 910662306a36Sopenharmony_ci */ 910762306a36Sopenharmony_cistatic void __exit megasas_exit(void) 910862306a36Sopenharmony_ci{ 910962306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 911062306a36Sopenharmony_ci &driver_attr_dbg_lvl); 911162306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 911262306a36Sopenharmony_ci &driver_attr_support_poll_for_event); 911362306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 911462306a36Sopenharmony_ci &driver_attr_support_device_change); 911562306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 911662306a36Sopenharmony_ci &driver_attr_release_date); 911762306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); 911862306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 911962306a36Sopenharmony_ci &driver_attr_support_nvme_encapsulation); 912062306a36Sopenharmony_ci driver_remove_file(&megasas_pci_driver.driver, 912162306a36Sopenharmony_ci &driver_attr_support_pci_lane_margining); 912262306a36Sopenharmony_ci 912362306a36Sopenharmony_ci pci_unregister_driver(&megasas_pci_driver); 912462306a36Sopenharmony_ci megasas_exit_debugfs(); 912562306a36Sopenharmony_ci unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); 912662306a36Sopenharmony_ci} 912762306a36Sopenharmony_ci 912862306a36Sopenharmony_cimodule_init(megasas_init); 912962306a36Sopenharmony_cimodule_exit(megasas_exit); 9130