diff -Nurp linux-2.6.22-810/drivers/scsi/megaraid/megaraid_sas.c linux-2.6.22-820/drivers/scsi/megaraid/megaraid_sas.c --- linux-2.6.22-810/drivers/scsi/megaraid/megaraid_sas.c 2007-07-08 19:32:17.000000000 -0400 +++ linux-2.6.22-820/drivers/scsi/megaraid/megaraid_sas.c 2010-12-08 15:32:31.000000000 -0500 @@ -2,26 +2,23 @@ * * Linux MegaRAID driver for SAS based RAID controllers * - * Copyright (c) 2003-2005 LSI Logic Corporation. + * Copyright (c) 2009 LSI Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. + *as published by the Free Software Foundation; either version 2 + *of the License, or (at your option) any later version. * - * FILE : megaraid_sas.c - * Version : v00.00.03.10-rc5 + *This program is distributed in the hope that it will be useful, + *but WITHOUT ANY WARRANTY; without even the implied warranty of + *MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + *GNU General Public License for more details. * - * Authors: - * (email-id : megaraidlinux@lsi.com) - * Sreenivas Bagalkote - * Sumant Patro - * Bo Yang + *You should have received a copy of the GNU General Public License + *along with this program; if not, write to the Free Software + *Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * List of supported controllers - * - * OEM Product Name VID DID SSVID SSID - * --- ------------ --- --- ---- ---- + * Send feedback to */ #include @@ -33,12 +30,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -46,10 +45,27 @@ #include #include "megaraid_sas.h" +/* + * poll_mode_io:1- schedule complete completion from q cmd + */ +static unsigned int poll_mode_io; +module_param_named(poll_mode_io, poll_mode_io, int, 0); +MODULE_PARM_DESC(poll_mode_io, + "Complete cmds from IO path, (default=0)"); + +/* + * Number of sectors per IO command + * Will be set in megasas_init_mfi if user does not provide + */ +static unsigned int max_sectors; +module_param_named(max_sectors, max_sectors, int, 0); +MODULE_PARM_DESC(max_sectors, + "Maximum number of sectors per IO command"); + MODULE_LICENSE("GPL"); MODULE_VERSION(MEGASAS_VERSION); MODULE_AUTHOR("megaraidlinux@lsi.com"); -MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver"); +MODULE_DESCRIPTION("LSI MegaRAID SAS Driver"); /* * PCI ID table for all supported controllers @@ -60,6 +76,16 @@ static struct pci_device_id megasas_pci_ /* xscale IOP */ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)}, /* ppc IOP */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078DE)}, + /* ppc IOP */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078GEN2)}, + /* gen2*/ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0079GEN2)}, + /* gen2*/ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0073SKINNY)}, + /* skinny*/ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS0071SKINNY)}, + /* skinny*/ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)}, /* xscale IOP, vega */ {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)}, @@ -73,9 +99,35 @@ static int megasas_mgmt_majorno; static struct megasas_mgmt_info megasas_mgmt_info; static struct fasync_struct *megasas_async_queue; static DEFINE_MUTEX(megasas_async_queue_mutex); +static DEFINE_MUTEX(megasas_poll_wait_mutex); + + +static int megasas_poll_wait_aen; +static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait); +static u32 support_poll_for_event; +static u32 support_device_change; + +/* define lock for aen poll */ +spinlock_t poll_aen_lock; static u32 megasas_dbg_lvl; +static void +megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, + u8 alt_status); + +static int megasas_transition_to_ready(struct megasas_instance* instance); +static int megasas_get_pd_list(struct megasas_instance *instance); +static int megasas_issue_init_mfi(struct megasas_instance *instance); +static int megasas_register_aen(struct megasas_instance *instance, u32 seq_num, u32 class_locale_word); +static int megasas_check_cpx_support( struct megasas_instance *instance); +static u32 megasas_remove_cpx( struct megasas_instance *instance); +static int megasas_send_cpx_queue_data( struct megasas_instance *instance ); +static int megasas_handle_cpx_requests( struct megasas_instance *instance); +static u32 megasas_read_fw_status_reg_gen2(struct megasas_register_set __iomem * regs); +static int megasas_adp_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem * reg_set); + + /** * megasas_get_cmd - Get a command from the free pool * @instance: Adapter soft state @@ -133,7 +185,7 @@ megasas_return_cmd(struct megasas_instan static inline void megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs) { - writel(1, &(regs)->outbound_intr_mask); + writel(0, &(regs)->outbound_intr_mask); /* Dummy readl to force pci flush */ readl(®s->outbound_intr_mask); @@ -169,21 +221,27 @@ static int megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) { u32 status; + u32 mfiStatus = 0; /* * Check if it is our interrupt */ status = readl(®s->outbound_intr_status); - if (!(status & MFI_OB_INTR_STATUS_MASK)) { - return 1; - } + if (status & MFI_OB_INTR_STATUS_MASK) + mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; + if (status & MFI_XSCALE_OMR0_CHANGE_INTERRUPT) + mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; /* * Clear the interrupt by writing back the same value */ + if (mfiStatus) writel(status, ®s->outbound_intr_status); - return 0; + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_status); + + return mfiStatus; } /** @@ -193,10 +251,69 @@ megasas_clear_intr_xscale(struct megasas * @regs : MFI register set */ static inline void -megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs) +megasas_fire_cmd_xscale(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) { + unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); writel((frame_phys_addr >> 3)|(frame_count), &(regs)->inbound_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); +} + +/** + * megasas_adp_reset_xscale - For controller reset + * @regs: MFI register set + */ +static int +megasas_adp_reset_xscale(struct megasas_instance *instance, struct megasas_register_set __iomem * regs) +{ + u32 i; + u32 pcidata; + writel(MFI_ADP_RESET, ®s->inbound_doorbell); + + for (i=0; i < 3; i++) + msleep(1000); /* sleep for 3 secs */ + pcidata =0; + pci_read_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, &pcidata); + printk("pcidata = %x\n", pcidata); + if (pcidata & 0x2) { + printk("mfi 1068 offset read=%x\n", pcidata); + pcidata &= ~0x2; + pci_write_config_dword(instance->pdev, MFI_1068_PCSR_OFFSET, pcidata); + + for (i=0; i<2; i++) + msleep(1000); /* need to wait 2 secs again */ + + pcidata =0; + pci_read_config_dword(instance->pdev, MFI_1068_FW_HANDSHAKE_OFFSET, &pcidata); + printk("mfi 1068 offset handshake read=%x\n", pcidata); + if ((pcidata & 0xffff0000) == MFI_1068_FW_READY) { + printk("mfi 1068 offset handshake=%x\n", pcidata); + pcidata = 0; + pci_write_config_dword(instance->pdev, MFI_1068_FW_HANDSHAKE_OFFSET, pcidata); + } + } + return 0; +} + +/** + * megasas_check_reset_xscale - For controller reset check + * @regs: MFI register set + */ +static int +megasas_check_reset_xscale(struct megasas_instance *instance, struct megasas_register_set __iomem * regs) +{ + u32 consumer; + consumer = *instance->consumer; + + if ((instance->adprecovery != MEGASAS_HBA_OPERATIONAL) && (*instance->consumer == MEGASAS_ADPRESET_INPROG_SIGN)) { + return 1; + } + + return 0; } static struct megasas_instance_template megasas_instance_template_xscale = { @@ -206,6 +323,8 @@ static struct megasas_instance_template .disable_intr = megasas_disable_intr_xscale, .clear_intr = megasas_clear_intr_xscale, .read_fw_status_reg = megasas_read_fw_status_reg_xscale, + .adp_reset = megasas_adp_reset_xscale, + .check_reset = megasas_check_reset_xscale, }; /** @@ -227,7 +346,7 @@ megasas_enable_intr_ppc(struct megasas_r { writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); - writel(~0x80000004, &(regs)->outbound_intr_mask); + writel(~0x80000000, &(regs)->outbound_intr_mask); /* Dummy readl to force pci flush */ readl(®s->outbound_intr_mask); @@ -270,7 +389,7 @@ megasas_clear_intr_ppc(struct megasas_re status = readl(®s->outbound_intr_status); if (!(status & MFI_REPLY_1078_MESSAGE_INTERRUPT)) { - return 1; + return 0; } /* @@ -278,7 +397,10 @@ megasas_clear_intr_ppc(struct megasas_re */ writel(status, ®s->outbound_doorbell_clear); - return 0; + /* Dummy readl to force pci flush */ + readl(®s->outbound_doorbell_clear); + + return 1; } /** * megasas_fire_cmd_ppc - Sends command to the FW @@ -287,10 +409,36 @@ megasas_clear_intr_ppc(struct megasas_re * @regs : MFI register set */ static inline void -megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) +megasas_fire_cmd_ppc(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) { + unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); writel((frame_phys_addr | (frame_count<<1))|1, &(regs)->inbound_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); +} + +/** + * megasas_adp_reset_ppc - For controller reset + * @regs: MFI register set + */ +static int +megasas_adp_reset_ppc(struct megasas_instance *instance, struct megasas_register_set __iomem * regs) +{ + return 0; +} + +/** + * megasas_check_reset_ppc - For controller reset check + * @regs: MFI register set + */ +static int +megasas_check_reset_ppc(struct megasas_instance *instance, struct megasas_register_set __iomem * regs) +{ + return 0; } static struct megasas_instance_template megasas_instance_template_ppc = { @@ -300,11 +448,321 @@ static struct megasas_instance_template .disable_intr = megasas_disable_intr_ppc, .clear_intr = megasas_clear_intr_ppc, .read_fw_status_reg = megasas_read_fw_status_reg_ppc, + .adp_reset = megasas_adp_reset_ppc, + .check_reset = megasas_check_reset_ppc, +}; + +/** + * megasas_enable_intr_skinny - Enables interrupts + * @regs: MFI register set + */ +static inline void +megasas_enable_intr_skinny(struct megasas_register_set __iomem *regs) +{ + writel(0xFFFFFFFF, &(regs)->outbound_intr_mask); + + /* write ~0x00000005 (4 & 1) to the intr mask*/ + writel(~MFI_SKINNY_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask); + + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_disable_intr_skinny - Disables interrupt + * @regs: MFI register set + */ +static inline void +megasas_disable_intr_skinny(struct megasas_register_set __iomem *regs) +{ + u32 mask = 0xFFFFFFFF; + writel(mask, ®s->outbound_intr_mask); + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_read_fw_status_reg_skinny - returns the current FW status value + * @regs: MFI register set + */ +static u32 +megasas_read_fw_status_reg_skinny(struct megasas_register_set __iomem *regs) +{ + return readl(&(regs)->outbound_scratch_pad); +} + +/** + * megasas_clear_interrupt_skinny - Check & clear interrupt + * @regs: MFI register set + */ +static int +megasas_clear_intr_skinny(struct megasas_register_set __iomem *regs) +{ + u32 status; + u32 mfiStatus = 0; + + /* + * Check if it is our interrupt + */ + status = readl(®s->outbound_intr_status); + + if (!(status & MFI_SKINNY_ENABLE_INTERRUPT_MASK)) { + return 0; + } + + + /* + * Check if it is our interrupt + */ + if ((megasas_read_fw_status_reg_gen2( regs) & MFI_STATE_MASK ) == MFI_STATE_FAULT ){ + mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; + }else + mfiStatus |= MFI_INTR_FLAG_REPLY_MESSAGE; + + /* + * Clear the interrupt by writing back the same value + */ + writel(status, ®s->outbound_intr_status); + + /* + * dummy read to flush PCI + */ + readl(®s->outbound_intr_status); + + return mfiStatus; +} + +/** + * megasas_fire_cmd_skinny - Sends command to the FW + * @frame_phys_addr : Physical address of cmd + * @frame_count : Number of frames for the command + * @regs : MFI register set + */ +static inline void +megasas_fire_cmd_skinny(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) +{ + unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); + writel(0, &(regs)->inbound_high_queue_port); + writel((frame_phys_addr | (frame_count<<1))|1, + &(regs)->inbound_low_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); + /*msleep(5);*/ +} + +/** + * megasas_check_reset_skinny - For controller reset check + * @regs: MFI register set + */ +static int +megasas_check_reset_skinny(struct megasas_instance *instance, struct megasas_register_set __iomem * regs) +{ + return 0; +} + +static struct megasas_instance_template megasas_instance_template_skinny = { + + .fire_cmd = megasas_fire_cmd_skinny, + .enable_intr = megasas_enable_intr_skinny, + .disable_intr = megasas_disable_intr_skinny, + .clear_intr = megasas_clear_intr_skinny, + .read_fw_status_reg = megasas_read_fw_status_reg_skinny, + .adp_reset = megasas_adp_reset_gen2, + .check_reset = megasas_check_reset_skinny, +}; + + +/** +* The following functions are defined for gen2 (deviceid : 0x78 0x79) +* controllers +*/ + +/** + * megasas_enable_intr_gen2 - Enables interrupts + * @regs: MFI register set + */ +static inline void +megasas_enable_intr_gen2(struct megasas_register_set __iomem *regs) +{ + writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); + + /* write ~0x00000005 (4 & 1) to the intr mask*/ + writel(~MFI_GEN2_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask); + + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_disable_intr_gen2 - Disables interrupt + * @regs: MFI register set + */ +static inline void +megasas_disable_intr_gen2(struct megasas_register_set __iomem *regs) +{ + u32 mask = 0xFFFFFFFF; + writel(mask, ®s->outbound_intr_mask); + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_read_fw_status_reg_gen2 - returns the current FW status value + * @regs: MFI register set + */ +static u32 +megasas_read_fw_status_reg_gen2(struct megasas_register_set __iomem *regs) +{ + return readl(&(regs)->outbound_scratch_pad); +} + +/** + * megasas_clear_interrupt_gen2 - Check & clear interrupt + * @regs: MFI register set + */ +static int +megasas_clear_intr_gen2(struct megasas_register_set __iomem *regs) +{ + u32 status; + u32 mfiStatus = 0; + /* + * Check if it is our interrupt + */ + status = readl(®s->outbound_intr_status); + + if (status & MFI_GEN2_ENABLE_INTERRUPT_MASK) + { + mfiStatus = MFI_INTR_FLAG_REPLY_MESSAGE; + } + if (status & MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT) + { + mfiStatus |= MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE; + } + + /* + * Clear the interrupt by writing back the same value + */ + if (mfiStatus) + writel(status, ®s->outbound_doorbell_clear); + + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_status); + + return mfiStatus; + +} +/** + * megasas_fire_cmd_gen2 - Sends command to the FW + * @frame_phys_addr : Physical address of cmd + * @frame_count : Number of frames for the command + * @regs : MFI register set + */ +static inline void +megasas_fire_cmd_gen2(struct megasas_instance *instance, + dma_addr_t frame_phys_addr, + u32 frame_count, + struct megasas_register_set __iomem *regs) +{ + unsigned long flags; + spin_lock_irqsave(&instance->hba_lock, flags); + writel((frame_phys_addr | (frame_count<<1))|1, + &(regs)->inbound_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); +} + +/** + * megasas_adp_reset_gen2 - For controller reset + * @regs: MFI register set + */ +static int +megasas_adp_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem * reg_set) +{ + u32 retry = 0, delay = 0; + u32 HostDiag; + u32 *seq_offset = ®_set->seq_offset; + u32 *hostdiag_offset = ®_set->host_diag; + + if ( instance->instancet == &megasas_instance_template_skinny ){ + seq_offset = ®_set->fusion_seq_offset; + hostdiag_offset = ®_set->fusion_host_diag; + } + + writel(0, seq_offset); + writel(4, seq_offset); + writel(0xb, seq_offset); + writel(2, seq_offset); + writel(7, seq_offset); + writel(0xd, seq_offset); + + msleep(1000); + + HostDiag = (u32)readl(hostdiag_offset); + + while ( !( HostDiag & DIAG_WRITE_ENABLE) ) + { + msleep(100); + HostDiag = (u32)readl(hostdiag_offset); + printk("ADP_RESET_GEN2: retry time=%x, hostdiag=%x\n", retry, HostDiag); + + if (retry++ >= 100) + return 1; + +} + + printk("ADP_RESET_GEN2: HostDiag=%x\n", HostDiag); + + writel((HostDiag | DIAG_RESET_ADAPTER), hostdiag_offset); + + for (delay=0; delay<10; delay++) + { + msleep(1000); + } + + HostDiag = (u32)readl(hostdiag_offset); + while ( ( HostDiag & DIAG_RESET_ADAPTER) ) + { + msleep(100); + HostDiag = (u32)readl(hostdiag_offset); + printk("ADP_RESET_GEN2: retry time=%x, hostdiag=%x\n", retry, HostDiag); + + if (retry++ >= 1000) + return 1; + + } + return 0; +} + +/** + * megasas_check_reset_gen2 - For controller reset check + * @regs: MFI register set + */ +static int +megasas_check_reset_gen2(struct megasas_instance *instance, struct megasas_register_set __iomem * regs) +{ + if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) { + return 1; + } + + return 0; +} + +static struct megasas_instance_template megasas_instance_template_gen2 = { + + .fire_cmd = megasas_fire_cmd_gen2, + .enable_intr = megasas_enable_intr_gen2, + .disable_intr = megasas_disable_intr_gen2, + .clear_intr = megasas_clear_intr_gen2, + .read_fw_status_reg = megasas_read_fw_status_reg_gen2, + .adp_reset = megasas_adp_reset_gen2, + .check_reset = megasas_check_reset_gen2, }; /** * This is the end of set of functions & definitions -* specific to ppc (deviceid : 0x60) controllers + * specific to gen2 (deviceid : 0x78, 0x79) controllers */ /** @@ -328,13 +786,17 @@ megasas_issue_polled(struct megasas_inst /* * Issue the frame using inbound queue port */ - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); /* * Wait for cmd_status to change */ for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i++) { rmb(); + //FW using xor/copy as soon as we enable cpx + if ( instance->cpx_supported ) + megasas_handle_cpx_requests( instance); msleep(1); } @@ -359,10 +821,10 @@ megasas_issue_blocked_cmd(struct megasas { cmd->cmd_status = ENODATA; - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); - wait_event_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), - MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); + wait_event(instance->int_cmd_wait_q, cmd->cmd_status != ENODATA); return 0; } @@ -404,13 +866,15 @@ megasas_issue_blocked_abort_cmd(struct m cmd->sync_cmd = 1; cmd->cmd_status = 0xFF; - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); /* * Wait for this cmd to complete */ - wait_event_timeout(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF), - MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); + wait_event(instance->abort_cmd_wait_q, cmd->cmd_status != 0xFF); + cmd->sync_cmd = 0; + megasas_return_cmd(instance, cmd); return 0; @@ -513,14 +977,66 @@ megasas_make_sgl64(struct megasas_instan return sge_count; } - /** - * megasas_get_frame_count - Computes the number of frames - * @sge_count : number of sg elements +/** + * megasas_make_sgl_skinny - Prepares IEEE SGL + * @instance: Adapter soft state + * @scp: SCSI command from the mid-layer + * @mfi_sgl: SGL to be filled in + * + * If successful, this function returns the number of SG elements. Otherwise, + * it returnes -1. + */ +static int +megasas_make_sgl_skinny(struct megasas_instance *instance, + struct scsi_cmnd *scp, union megasas_sgl *mfi_sgl) +{ + int i; + int sge_count; + struct scatterlist *os_sgl; + + /* + * Return 0 if there is no data transfer + */ + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { + mfi_sgl->sge_skinny[0].phys_addr = pci_map_single(instance->pdev, + scp-> + request_buffer, + scp-> + request_bufflen, + scp-> + sc_data_direction); + + mfi_sgl->sge_skinny[0].length = scp->request_bufflen; + + return 1; + } + + os_sgl = (struct scatterlist *)scp->request_buffer; + sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg, + scp->sc_data_direction); + + for (i = 0; i < sge_count; i++, os_sgl++) { + mfi_sgl->sge_skinny[i].length = sg_dma_len(os_sgl); + mfi_sgl->sge_skinny[i].phys_addr = sg_dma_address(os_sgl); + mfi_sgl->sge_skinny[i].flag = 0; + } + + return sge_count; +} + + /** + * megasas_get_frame_count - Computes the number of frames + * @frame_type : type of frame- io or pthru frame + * @sge_count : number of sg elements * * Returns the number of frames required for numnber of sge's (sge_count) */ -static u32 megasas_get_frame_count(u8 sge_count) +static u32 megasas_get_frame_count(struct megasas_instance *instance, + u8 sge_count, u8 frame_type) { int num_cnt; int sge_bytes; @@ -530,14 +1046,31 @@ static u32 megasas_get_frame_count(u8 sg sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); + if (instance->flag_ieee) { + sge_sz = sizeof(struct megasas_sge_skinny); + } + /* - * Main frame can contain 2 SGEs for 64-bit SGLs and - * 3 SGEs for 32-bit SGLs - */ - if (IS_DMA64) - num_cnt = sge_count - 2; - else - num_cnt = sge_count - 3; + * Main frame can contain 2 SGEs for 64-bit SGLs and + * 3 SGEs for 32-bit SGLs for ldio & + * 1 SGEs for 64-bit SGLs and + * 2 SGEs for 32-bit SGLs for pthru frame + */ + if (unlikely(frame_type == PTHRU_FRAME)) { + if (instance->flag_ieee == 1) { + num_cnt = sge_count - 1; + } else if (IS_DMA64) + num_cnt = sge_count - 1; + else + num_cnt = sge_count - 2; + } else { + if (instance->flag_ieee == 1) { + num_cnt = sge_count - 1; + } else if (IS_DMA64) + num_cnt = sge_count - 2; + else + num_cnt = sge_count - 3; + } if(num_cnt>0){ sge_bytes = sge_sz * num_cnt; @@ -582,6 +1115,10 @@ megasas_build_dcdb(struct megasas_instan else if (scp->sc_data_direction == PCI_DMA_NONE) flags = MFI_FRAME_DIR_NONE; + if (instance->flag_ieee == 1) { + flags |= MFI_FRAME_IEEE; + } + /* * Prepare the DCDB frame */ @@ -592,15 +1129,31 @@ megasas_build_dcdb(struct megasas_instan pthru->lun = scp->device->lun; pthru->cdb_len = scp->cmd_len; pthru->timeout = 0; + pthru->pad_0 = 0; pthru->flags = flags; pthru->data_xfer_len = scp->request_bufflen; memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); /* + * If the command is for the tape device, set the + * pthru timeout to the os layer timeout value. + */ + if (scp->device->type == TYPE_TAPE) { + if ((scp->request->timeout / HZ) > 0xFFFF) + pthru->timeout = 0xFFFF; + else + pthru->timeout = scp->request->timeout / HZ; + } + + /* * Construct SGL */ - if (IS_DMA64) { + if (instance->flag_ieee == 1) { + pthru->flags |= MFI_FRAME_SGL64; + pthru->sge_count = megasas_make_sgl_skinny(instance, scp, + &pthru->sgl); + } else if (IS_DMA64) { pthru->flags |= MFI_FRAME_SGL64; pthru->sge_count = megasas_make_sgl64(instance, scp, &pthru->sgl); @@ -608,6 +1161,10 @@ megasas_build_dcdb(struct megasas_instan pthru->sge_count = megasas_make_sgl32(instance, scp, &pthru->sgl); + if (pthru->sge_count > instance->max_num_sge) { + printk("megasas: build_dcdb error, two many SGE\n"); + return 0; + } /* * Sense info specific */ @@ -619,7 +1176,8 @@ megasas_build_dcdb(struct megasas_instan * Compute the total number of frames this command consumes. FW uses * this number to pull sufficient number of frames from host memory. */ - cmd->frame_count = megasas_get_frame_count(pthru->sge_count); + cmd->frame_count = megasas_get_frame_count(instance, pthru->sge_count, + PTHRU_FRAME); return cmd->frame_count; } @@ -628,7 +1186,7 @@ megasas_build_dcdb(struct megasas_instan * megasas_build_ldio - Prepares IOs to logical devices * @instance: Adapter soft state * @scp: SCSI command - * @cmd: Command to to be prepared + * @cmd: Command to be prepared * * Frames (and accompanying SGLs) for regular SCSI IOs use this function. */ @@ -649,6 +1207,10 @@ megasas_build_ldio(struct megasas_instan else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) flags = MFI_FRAME_DIR_READ; + if (instance->flag_ieee == 1) { + flags |= MFI_FRAME_IEEE; + } + /* * Prepare the Logical IO frame: 2nd bit is zero for all read cmds */ @@ -719,12 +1281,20 @@ megasas_build_ldio(struct megasas_instan /* * Construct SGL */ - if (IS_DMA64) { + if (instance->flag_ieee) { + ldio->flags |= MFI_FRAME_SGL64; + ldio->sge_count = megasas_make_sgl_skinny(instance, scp, + &ldio->sgl); + } else if (IS_DMA64) { ldio->flags |= MFI_FRAME_SGL64; ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl); } else ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl); + if (ldio->sge_count > instance->max_num_sge) { + printk("megasas: build_ld_io error, sge_count = %x\n", ldio->sge_count); + return 0; + } /* * Sense info specific */ @@ -736,7 +1306,8 @@ megasas_build_ldio(struct megasas_instan * Compute the total number of frames this command consumes. FW uses * this number to pull sufficient number of frames from host memory. */ - cmd->frame_count = megasas_get_frame_count(ldio->sge_count); + cmd->frame_count = megasas_get_frame_count(instance, + ldio->sge_count, IO_FRAME); return cmd->frame_count; } @@ -841,14 +1412,25 @@ megasas_queue_command(struct scsi_cmnd * u32 frame_count; struct megasas_cmd *cmd; struct megasas_instance *instance; + unsigned long flags; instance = (struct megasas_instance *) scmd->device->host->hostdata; - /* Don't process if we have already declared adapter dead */ - if (instance->hw_crit_error) + if (instance->issuepend_done == 0) return SCSI_MLQUEUE_HOST_BUSY; + spin_lock_irqsave(&instance->hba_lock, flags); + //Don't process if we have already declared adapter dead + // If we are in middle of bringing up the HBA, send the busy status to mid-layer + // till the process is complete + if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) { + spin_unlock_irqrestore(&instance->hba_lock, flags); + return SCSI_MLQUEUE_HOST_BUSY; + } + + spin_unlock_irqrestore(&instance->hba_lock, flags); + scmd->scsi_done = done; scmd->result = 0; @@ -893,7 +1475,14 @@ megasas_queue_command(struct scsi_cmnd * */ atomic_inc(&instance->fw_outstanding); - instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set); + instance->instancet->fire_cmd(instance, cmd->frame_phys_addr, + cmd->frame_count-1, instance->reg_set); + /* + * Check if we have pend cmds to be completed + */ + if (poll_mode_io && atomic_read(&instance->fw_outstanding)) + tasklet_schedule(&instance->isr_tasklet); + return 0; @@ -904,211 +1493,1527 @@ megasas_queue_command(struct scsi_cmnd * return 0; } +static struct megasas_instance *megasas_lookup_instance(u16 host_no) +{ + int i; + + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + + if ((megasas_mgmt_info.instance[i]) && + (megasas_mgmt_info.instance[i]->host->host_no == host_no)) + return megasas_mgmt_info.instance[i]; + } + + return NULL; +} + static int megasas_slave_configure(struct scsi_device *sdev) { + u16 pd_index = 0; + struct megasas_instance *instance ; + + instance = megasas_lookup_instance(sdev->host->host_no); + /* - * Don't export physical disk devices to the disk driver. - * - * FIXME: Currently we don't export them to the midlayer at all. - * That will be fixed once LSI engineers have audited the - * firmware for possible issues. - */ - if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK) + * Don't export physical disk devices to the disk driver. + * + * FIXME: Currently we don't export them to the midlayer at all. + * That will be fixed once LSI engineers have audited the + * firmware for possible issues. + */ + if (sdev->channel < MEGASAS_MAX_PD_CHANNELS) { + if (sdev->type == TYPE_TAPE) { + sdev->timeout = MEGASAS_DEFAULT_CMD_TIMEOUT * HZ; + return 0; + } else if (sdev->type == TYPE_DISK) { + + pd_index = (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; + + if ((instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) && + (instance->pd_list[pd_index].driveType == TYPE_DISK)) { + sdev->timeout = MEGASAS_DEFAULT_CMD_TIMEOUT * HZ; + return 0; + } + } return -ENXIO; + } /* - * The RAID firmware may require extended timeouts. - */ - if (sdev->channel >= MEGASAS_MAX_PD_CHANNELS) - sdev->timeout = MEGASAS_DEFAULT_CMD_TIMEOUT * HZ; + * The RAID firmware may require extended timeouts. + */ + sdev->timeout = MEGASAS_DEFAULT_CMD_TIMEOUT * HZ; return 0; } -/** - * megasas_wait_for_outstanding - Wait for all outstanding cmds - * @instance: Adapter soft state - * - * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to - * complete all its outstanding commands. Returns error if one or more IOs - * are pending after this time period. It also marks the controller dead. - */ -static int megasas_wait_for_outstanding(struct megasas_instance *instance) +static void megaraid_sas_kill_hba(struct megasas_instance *instance) { - int i; - u32 wait_time = MEGASAS_RESET_WAIT_TIME; + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) + { + writel(MFI_STOP_ADP, + &instance->reg_set->reserved_0); + } else { + writel(MFI_STOP_ADP, + &instance->reg_set->inbound_doorbell); + } +} - for (i = 0; i < wait_time; i++) { - int outstanding = atomic_read(&instance->fw_outstanding); - if (!outstanding) - break; - if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { - printk(KERN_NOTICE "megasas: [%2d]waiting for %d " - "commands to complete\n",i,outstanding); - } +void xor_gen_15x1(u32 *buff_ptrs[16], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12, *s13, *s14, *s15; - msleep(1000); - } + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + s13 = buff_ptrs[13]; + s14 = buff_ptrs[14]; + s15 = buff_ptrs[15]; - if (atomic_read(&instance->fw_outstanding)) { - /* - * Send signal to FW to stop processing any pending cmds. - * The controller will be taken offline by the OS now. - */ - writel(MFI_STOP_ADP, - &instance->reg_set->inbound_doorbell); - megasas_dump_pending_frames(instance); - instance->hw_crit_error = 1; - return FAILED; - } + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off] ^ s13[off] ^ s14[off] ^ s15[off]; - return SUCCESS; } -/** - * megasas_generic_reset - Generic reset routine - * @scmd: Mid-layer SCSI command - * - * This routine implements a generic reset handler for device, bus and host - * reset requests. Device, bus and host specific reset handlers can use this - * function after they do their specific tasks. - */ -static int megasas_generic_reset(struct scsi_cmnd *scmd) + +void xor_gen_14x1(u32 *buff_ptrs[15], u32 bytes) { - int ret_val; - struct megasas_instance *instance; + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12, *s13, *s14; - instance = (struct megasas_instance *)scmd->device->host->hostdata; + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + s13 = buff_ptrs[13]; + s14 = buff_ptrs[14]; - scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x retries=%x\n", - scmd->serial_number, scmd->cmnd[0], scmd->retries); + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off] ^ s13[off] ^ s14[off]; - if (instance->hw_crit_error) { - printk(KERN_ERR "megasas: cannot recover from previous reset " - "failures\n"); - return FAILED; - } +} - ret_val = megasas_wait_for_outstanding(instance); - if (ret_val == SUCCESS) - printk(KERN_NOTICE "megasas: reset successful \n"); - else - printk(KERN_ERR "megasas: failed to do reset\n"); - return ret_val; +void xor_gen_13x1(u32 *buff_ptrs[14], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12, *s13; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + s13 = buff_ptrs[13]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off] ^ s13[off]; + } -/** - * megasas_reset_timer - quiesce the adapter if required - * @scmd: scsi cmnd - * - * Sets the FW busy flag and reduces the host->can_queue if the - * cmd has not been completed within the timeout period. - */ -static enum -scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) -{ - struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr; - struct megasas_instance *instance; - unsigned long flags; - if (time_after(jiffies, scmd->jiffies_at_alloc + - (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) { - return EH_NOT_HANDLED; - } +void xor_gen_12x1(u32 *buff_ptrs[13], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12; - instance = cmd->instance; - if (!(instance->flag & MEGASAS_FW_BUSY)) { - /* FW is busy, throttle IO */ - spin_lock_irqsave(instance->host->host_lock, flags); + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; - instance->host->can_queue = 16; - instance->last_time = jiffies; - instance->flag |= MEGASAS_FW_BUSY; + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off]; - spin_unlock_irqrestore(instance->host->host_lock, flags); - } - return EH_RESET_TIMER; } -/** - * megasas_reset_device - Device reset handler entry point - */ -static int megasas_reset_device(struct scsi_cmnd *scmd) + +void xor_gen_11x1(u32 *buff_ptrs[12], u32 bytes) { - int ret; + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11; - /* - * First wait for all commands to complete - */ - ret = megasas_generic_reset(scmd); + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off]; - return ret; } -/** - * megasas_reset_bus_host - Bus & host reset handler entry point - */ -static int megasas_reset_bus_host(struct scsi_cmnd *scmd) + +void xor_gen_10x1(u32 *buff_ptrs[11], u32 bytes) { - int ret; + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10; - /* - * First wait for all commands to complete - */ - ret = megasas_generic_reset(scmd); + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off]; - return ret; } -/** - * megasas_bios_param - Returns disk geometry for a disk - * @sdev: device handle - * @bdev: block device - * @capacity: drive capacity - * @geom: geometry parameters - */ -static int -megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, - sector_t capacity, int geom[]) + +void xor_gen_9x1(u32 *buff_ptrs[10], u32 bytes) { - int heads; - int sectors; - sector_t cylinders; - unsigned long tmp; - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9; - tmp = heads * sectors; - cylinders = capacity; + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; - sector_div(cylinders, tmp); + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off]; - /* - * Handle extended translation size for logical drives > 1Gb - */ +} - if (capacity >= 0x200000) { - heads = 255; - sectors = 63; - tmp = heads*sectors; - cylinders = capacity; - sector_div(cylinders, tmp); - } - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; +void xor_gen_8x1(u32 *buff_ptrs[9], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^ s8[off]; - return 0; } -/** - * megasas_service_aen - Processes an event notification - * @instance: Adapter soft state + +void xor_gen_7x1(u32 *buff_ptrs[8], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off]; + +} + + +void xor_gen_6x1(u32 *buff_ptrs[7], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5, *s6; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off]; + +} + + +void xor_gen_5x1(u32 *buff_ptrs[6], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4, *s5; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off]; + +} + + +void xor_gen_4x1(u32 *buff_ptrs[5], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3, *s4; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off] ^ s4[off]; + +} + + +void xor_gen_3x1(u32 *buff_ptrs[4], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2, *s3; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off] ^ s3[off]; + +} + + +void xor_gen_2x1(u32 *buff_ptrs[3], u32 bytes) +{ + u32 off, words; + u32 *d, *s1, *s2; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off] ^ s2[off]; + +} + + +void xor_gen_1x1(u32 *buff_ptrs[2], u32 bytes) +{ + u32 off, words; + u32 *d, *s1; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + + for (words = bytes/4, off=0; words>0; words--, off++) + d[off] = s1[off]; + +} + + +u8 xor_chk_15x1(u32 *buff_ptrs[16], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12, *s13, *s14, *s15; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + s13 = buff_ptrs[13]; + s14 = buff_ptrs[14]; + s15 = buff_ptrs[15]; + + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off] ^ s13[off] ^ s14[off] ^ s15[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_14x1(u32 *buff_ptrs[15], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12, *s13, *s14; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + s13 = buff_ptrs[13]; + s14 = buff_ptrs[14]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off] ^ s13[off] ^ s14[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; + +} + + +u8 xor_chk_13x1(u32 *buff_ptrs[14], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12, *s13; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + s13 = buff_ptrs[13]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off] ^ s13[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; + +} + + +u8 xor_chk_12x1(u32 *buff_ptrs[13], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + s12 = buff_ptrs[12]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off] ^ s12[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; + +} + + +u8 xor_chk_11x1(u32 *buff_ptrs[12], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + s11 = buff_ptrs[11]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off] ^ s11[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; + +} + + +u8 xor_chk_10x1(u32 *buff_ptrs[11], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + s10 = buff_ptrs[10]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off] ^ s10[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_9x1(u32 *buff_ptrs[10], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + s9 = buff_ptrs[9]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^\ + s8[off] ^ s9[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_8x1(u32 *buff_ptrs[9], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + s8 = buff_ptrs[8]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off] ^ s8[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_7x1(u32 *buff_ptrs[8], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6, *s7; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + s7 = buff_ptrs[7]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off] ^ s7[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; + +} + + +u8 xor_chk_6x1(u32 *buff_ptrs[7], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5, *s6; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + s6 = buff_ptrs[6]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off] ^ s6[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_5x1(u32 *buff_ptrs[6], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4, *s5; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + s5 = buff_ptrs[5]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off] ^ s5[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_4x1(u32 *buff_ptrs[5], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3, *s4; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + s4 = buff_ptrs[4]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off] ^ s4[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_3x1(u32 *buff_ptrs[4], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2, *s3; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + s3 = buff_ptrs[3]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off] ^ s3[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_2x1(u32 *buff_ptrs[3], u32 bytes) +{ + u32 off, words; + u32 r, *d, *s1, *s2; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + s2 = buff_ptrs[2]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + r = d[off] ^ s1[off] ^ s2[off]; + if (r) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] ^= r; + } + } + return xor_result; +} + + +u8 xor_chk_1x1(u32 *buff_ptrs[2], u32 bytes) +{ + u32 off, words; + u32 *d, *s1; + u8 xor_result = MR_CPX_STATUS_SUCCESS; + + d = buff_ptrs[0]; + s1 = buff_ptrs[1]; + + for (words = bytes/4, off=0; words>0; words--, off++) { + if (d[off] != s1[off]) { + xor_result = MR_CPX_STATUS_INCONSISTENT; + d[off] = s1[off]; + } + } + return xor_result; +} + + +XOR_LOW_LEVEL_GEN_FUNC xor_gen_funcs[MAX_MR_ROW_SIZE] = { + xor_gen_1x1, + xor_gen_2x1, + xor_gen_3x1, + xor_gen_4x1, + xor_gen_5x1, + xor_gen_6x1, + xor_gen_7x1, + xor_gen_8x1, + xor_gen_9x1, + xor_gen_10x1, + xor_gen_11x1, + xor_gen_12x1, + xor_gen_13x1, + xor_gen_14x1, + xor_gen_15x1, + }; + +XOR_LOW_LEVEL_CHECK_FUNC xor_check_funcs[MAX_MR_ROW_SIZE] = { + xor_chk_1x1, + xor_chk_2x1, + xor_chk_3x1, + xor_chk_4x1, + xor_chk_5x1, + xor_chk_6x1, + xor_chk_7x1, + xor_chk_8x1, + xor_chk_9x1, + xor_chk_10x1, + xor_chk_11x1, + xor_chk_12x1, + xor_chk_13x1, + xor_chk_14x1, + xor_chk_15x1, +}; + +inline static u8 megasas_scan_set_bit(u32 bitmap) +{ + u8 bit = 0; + + while (bitmap) { + if (bitmap & 1) + return bit; + + bitmap >>= 1; + bit++; + } + return ~0; +} + + +/** + * megasas_do_cpx_xor - completes the xor operation + * @xor_des : soruce and dest buffers details. + * @host_mem : previously mapped memory for fw + * @host_mem_len : mapped memory length in bytes. + * + * @return 0 on success != 0 on failure + * + */ +static u8 megasas_do_cpx_xor( struct mr_cpx_xor_descriptor *xor_des, const u8 *host_mem, const u32 host_mem_len) +{ + + u32 buff_valid_bit_map = xor_des->buff_valid_bitmap; + u32 *buf_ptr_list[MAX_MR_ROW_SIZE]; + u32 tx_count = xor_des->size; + u8 dest_idx, buf_idx, bit; + u8 is_op_gen; // means is XOR generation (TRUE) or check (FALSE) + u8 status = MR_CPX_STATUS_SUCCESS; + + + //make the first buffer ptr as the destination. + if( xor_des->op == MR_CPX_XOR_OP_GEN_P || xor_des->op == MR_CPX_XOR_OP_CHECK_P ) + dest_idx = xor_des->p_idx; + else + dest_idx = xor_des->q_idx; + + buf_ptr_list[0] = (u32 *)(host_mem + xor_des->buff_list[dest_idx]); + + is_op_gen = MR_CPX_XOR_OP_IS_GEN(xor_des->op); + + if ( xor_des->buff_list[dest_idx]+tx_count > host_mem_len){ + printk("Error: 1st host memory over flow detected.\n"); + return MR_CPX_STATUS_FAILURE; + } + + /* + * For XOR_OP_CHECK_P, our check routine expects bufPtrs[0] to be both parity + * source and parity destination; clear out the P-index from our working bitmap + * so that the source-buffer scan loop below doesn't include P as one of the + * explicit source buffers in bufPtrs[]. + */ + if ( !is_op_gen) + buff_valid_bit_map &= ~(1<p_idx); + + //populate buf_ptr_list with valid buffer pointers. + for ( buf_idx =1 ; buff_valid_bit_map != 0 ; buf_idx++ ){ + bit = megasas_scan_set_bit( buff_valid_bit_map); + buf_ptr_list[buf_idx] = (u32 *)(host_mem + (u32)xor_des->buff_list[bit]); + if ( xor_des->buff_list[bit]+tx_count > host_mem_len) { + printk("Error: host memory over flow detected.\n"); + return MR_CPX_STATUS_FAILURE;; + } + buff_valid_bit_map &= ~(1 < 0 ){ + + bytes_copied = min((size_t)remaining, (size_t)(PAGE_SIZE - (off & ~PAGE_MASK))); + + page_addr = kmap_atomic(page+ (off >> PAGE_SHIFT), KM_SOFTIRQ0); + if ( page_addr == NULL ){ + printk("kmap_atomic is failed.\n"); + return MR_CPX_STATUS_FAILURE;; + } + if ( dir == MR_CPX_DIR_WRITE ) + memcpy( host_ptr, page_addr+(off & ~PAGE_MASK), bytes_copied ); + else + memcpy( page_addr+(off & ~PAGE_MASK), host_ptr, bytes_copied ); + + kunmap_atomic ( page_addr, KM_SOFTIRQ0 ); + + host_ptr += bytes_copied; + remaining -= bytes_copied; + off += bytes_copied; + } + + return MR_CPX_STATUS_SUCCESS; +} +/** + * megasas_do_cpx_copy - Completes the copy opreation + * @instance : Driver soft state. + * @cpy_des : input for copying the data. + * + * @return 0 on success != 0 on failure + * + */ +static u8 megasas_do_cpx_copy( struct megasas_instance *instance, struct mr_cpx_copy_descriptor *cpy_des) +{ + u8 status = MR_CPX_STATUS_SUCCESS ; + u32 total_remaining_len = cpy_des->total_byte_count; + u32 row_remaining_length; + u32 os_sge_sz = 0, os_sge_offset = 0, os_sge_idx =0, os_sge_len; + u8 sge_cnt, row_idx, *fw_ptr,host_skip_count_handled; + struct scsi_cmnd *os_cmd; + struct scatterlist *os_sgl; + struct megasas_cmd *mega_cmd; + struct megasas_io_frame *ldio; + + if ( cpy_des->mfi_cmd_cxt >= instance->max_fw_cmds ){ + printk("megasas: invalid context - 0x%x shoul be < 0x%x \n", cpy_des->mfi_cmd_cxt ,instance->max_fw_cmds ); + return MR_CPX_STATUS_FAILURE; + } + + mega_cmd = ( struct megasas_cmd *) instance->cmd_list[cpy_des->mfi_cmd_cxt]; + os_cmd = mega_cmd->scmd; + ldio = (struct megasas_io_frame *)mega_cmd->frame; + sge_cnt = ldio->sge_count; + + host_skip_count_handled = 0; + row_idx = 0; + fw_ptr = NULL; + row_remaining_length =0; + os_sgl = (struct scatterlist *)os_cmd->request_buffer; + + for ( os_sge_idx=0; os_sge_idx < sge_cnt; os_sge_idx++, os_sgl++ ){ + + os_sge_len = sg_dma_len(os_sgl); + + if ( !host_skip_count_handled && cpy_des->host_skip_count < ( os_sge_sz += os_sge_len ) ){ + os_sge_offset = cpy_des->host_skip_count - ( os_sge_sz -os_sge_len ); + os_sge_len -= os_sge_offset; + host_skip_count_handled = 1; + } else if ( !host_skip_count_handled && cpy_des->host_skip_count == os_sge_sz ){ + os_sge_offset = 0; + host_skip_count_handled = 1; + continue; + } + + if ( !host_skip_count_handled ) + continue; + + for( ;total_remaining_len && row_idx < MAX_MR_ROW_SIZE ; row_idx++ ){ + + if ( ! row_remaining_length ){ + fw_ptr = (u8 *)(instance->host_mem_virt+cpy_des->copy_buf[row_idx].buf); + row_remaining_length = cpy_des->copy_buf[row_idx].size; + } + + if ( (status = megasas_copy( os_sgl->page, os_sgl->offset, os_sge_offset, fw_ptr, + MIN(os_sge_len, row_remaining_length), cpy_des->dir) ) ) + break; + + total_remaining_len -= MIN(os_sge_len, row_remaining_length); + + if ( os_sge_len <= row_remaining_length ){ + fw_ptr += os_sge_len; + if ( !(row_remaining_length -= os_sge_len) ) row_idx++; + os_sge_offset = 0; + break; + }else{ + os_sge_len -= row_remaining_length; + os_sge_offset += row_remaining_length; + row_remaining_length =0; + } + + } + if ( row_idx >= MAX_MR_ROW_SIZE && total_remaining_len ) + printk("megasas: Reached end of fw sglist while pending data transfer,row_idx = 0x%x, total_remaining_len = 0x%x \n", + row_idx, total_remaining_len); + + if( total_remaining_len == 0 || status == MR_CPX_STATUS_FAILURE ) + break; + } + + if ( os_sge_idx >= sge_cnt && total_remaining_len ) + printk("megasas: Reached end of os sglist while pending data transfer, os_sge_idx = 0x%x, total_remaining_len = 0x%x \n", + os_sge_idx, total_remaining_len); + + return status; + +} +/** + * megasas_handle_cpx_requests - Manages the fw queues + * @instance : Driver soft state. + * + * @return 0 on success != 0 on failure + * + */ +static int megasas_handle_cpx_requests( struct megasas_instance *instance) +{ + struct mr_cpx_request_queue *req_q = instance->cpx_request_queue; + u32 producer_idx, consumer_idx; + u8 retval = 0; + unsigned long flags; + + producer_idx = req_q->producer_idx; + consumer_idx = req_q->consumer_idx; + + while (producer_idx != consumer_idx ){ + union mr_cpx_descriptor *cpx_des = &instance->cpx_dscrptr[consumer_idx]; + union mr_cpx_response_data rsp_data; + + if ( cpx_des->cpx_copy_desc.hdr.type == MR_CPX_DESCRIPTOR_TYPE_COPY ) + retval = megasas_do_cpx_copy( instance, ( struct mr_cpx_copy_descriptor *)cpx_des ); + + else if (cpx_des->cpx_copy_desc.hdr.type == MR_CPX_DESCRIPTOR_TYPE_XOR ) + retval = megasas_do_cpx_xor( ( struct mr_cpx_xor_descriptor *)cpx_des, + (u8 *)instance->host_mem_virt, instance->host_mem_len ); + else{ + printk("Fatal Error : Got invalid descriptor type...\n"); + retval = MR_CPX_STATUS_FAILURE; + break; + } + + rsp_data.r.status = retval; + rsp_data.r.context = cpx_des->cpx_copy_desc.hdr.context; + rsp_data.r.type = cpx_des->cpx_copy_desc.hdr.type; + + //notify fw. + spin_lock_irqsave(&instance->hba_lock, flags); + writel( ~0, &instance->reg_set->inbound_high_queue_port); + writel( rsp_data.w, &instance->reg_set->inbound_low_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); + + //take care of wrap around case. + consumer_idx++; + if ( consumer_idx == instance->cpx_dscrptr_cnt ) + consumer_idx = 0; + + } + req_q->consumer_idx = producer_idx; + + return 0; +} + +/** + * megasas_complete_cmd_dpc - Returns FW's controller structure + * @instance_addr: Address of adapter soft state + * + * Tasklet to complete cmds + */ +static void megasas_complete_cmd_dpc(unsigned long instance_addr) +{ + u32 producer; + u32 consumer; + u32 context; + struct megasas_cmd *cmd; + struct megasas_instance *instance = + (struct megasas_instance *)instance_addr; + unsigned long flags; + + /* If we have already declared adapter dead, donot complete cmds */ + spin_lock_irqsave(&instance->hba_lock, flags); + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR ) { + spin_unlock_irqrestore(&instance->hba_lock, flags); + return; + } + spin_unlock_irqrestore(&instance->hba_lock, flags); + + spin_lock_irqsave(&instance->completion_lock, flags); + + producer = *instance->producer; + consumer = *instance->consumer; + + while (consumer != producer) { + context = instance->reply_queue[consumer]; + if (context >= instance->max_fw_cmds) { + printk("ERROR ERROR: unexpected context value %x\n", context); + BUG(); + } + + cmd = instance->cmd_list[context]; + + megasas_complete_cmd(instance, cmd, DID_OK); + + consumer++; + if (consumer == (instance->max_fw_cmds + 1)) { + consumer = 0; + } + } + + *instance->consumer = producer; + + spin_unlock_irqrestore(&instance->completion_lock, flags); + + + + if ( instance->cpx_supported ) + megasas_handle_cpx_requests( instance); + + /* + * Check if we can restore can_queue + */ + if (instance->flag & MEGASAS_FW_BUSY + && time_after(jiffies, instance->last_time + 5 * HZ) + && atomic_read(&instance->fw_outstanding) < 17) { + + spin_lock_irqsave(instance->host->host_lock, flags); + instance->flag &= ~MEGASAS_FW_BUSY; + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + instance->host->can_queue = + instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS; + } else + instance->host->can_queue = + instance->max_fw_cmds - MEGASAS_INT_CMDS; + + spin_unlock_irqrestore(instance->host->host_lock, flags); + } +} + +static void megasas_internal_reset_defer_cmds(struct megasas_instance *instance); +static void process_fw_state_change_wq(struct work_struct *work); + +void megasas_do_ocr(struct megasas_instance *instance) +{ + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || + (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR)) + { + *instance->consumer = MEGASAS_ADPRESET_INPROG_SIGN; + } + + instance->instancet->disable_intr(instance->reg_set); + instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT; + instance->issuepend_done = 0; + + atomic_set(&instance->fw_outstanding, 0); + megasas_internal_reset_defer_cmds(instance); + process_fw_state_change_wq(&instance->work_init); +} + + +/** + * megasas_wait_for_outstanding - Wait for all outstanding cmds + * @instance: Adapter soft state + * + * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to + * complete all its outstanding commands. Returns error if one or more IOs + * are pending after this time period. It also marks the controller dead. + */ +static int megasas_wait_for_outstanding(struct megasas_instance *instance) +{ + int i, sl; + u32 reset_index; + u32 wait_time = MEGASAS_RESET_WAIT_TIME; + u8 adprecovery; + unsigned long flags; + struct list_head clist_local; + struct megasas_cmd *reset_cmd; + u32 fw_state; + u8 kill_adapter_flag; + + + // If we are in-process if internal reset, we should wait for that process to + // complete + spin_lock_irqsave(&instance->hba_lock, flags); + adprecovery = instance->adprecovery; + spin_unlock_irqrestore(&instance->hba_lock, flags); + + if (adprecovery != MEGASAS_HBA_OPERATIONAL) { + + // We take the ownership of all the pending commands. These would be failed to the OS + // after a successful recovery from adapter internal reset condition. + INIT_LIST_HEAD(&clist_local); + spin_lock_irqsave(&instance->hba_lock, flags); + list_splice_init(&instance->internal_reset_pending_q, &clist_local); + spin_unlock_irqrestore(&instance->hba_lock, flags); + + printk("megasas: HBA reset handler invoked while adapter internal reset in progress, wait till that's over...\n"); + for (i = 0; i < wait_time; i++) { + msleep(1000); + // Are we there yet? + spin_lock_irqsave(&instance->hba_lock, flags); + adprecovery = instance->adprecovery; + spin_unlock_irqrestore(&instance->hba_lock, flags); + if (adprecovery == MEGASAS_HBA_OPERATIONAL) + break; + } + + // Are we out of reset yet? If not, HBA is toasted :-( + if (adprecovery != MEGASAS_HBA_OPERATIONAL) { + printk("megasas: HBA reset handler timedout for internal reset. Stopping the HBA.\n"); + spin_lock_irqsave(&instance->hba_lock, flags); + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + spin_unlock_irqrestore(&instance->hba_lock, flags); + return FAILED; + } + + printk("megasas: HBA internal reset condition discovered to be cleared.\n"); + + // Send the pending commands back to the OS with reset condition + reset_index = 0; + while (!list_empty(&clist_local)) { + reset_cmd = list_entry((&clist_local)->next, struct megasas_cmd, list); + list_del_init(&reset_cmd->list); + if (reset_cmd->scmd) { + reset_cmd->scmd->result = DID_RESET << 16; + printk("megasas: %d:%p reset scsi command [%02x], %#lx\n", + reset_index, reset_cmd, reset_cmd->scmd->cmnd[0], reset_cmd->scmd->serial_number); + reset_cmd->scmd->scsi_done(reset_cmd->scmd); + megasas_return_cmd(instance, reset_cmd); + } + else if (reset_cmd->sync_cmd) { + // Such commands have no timeout, we re-issue this guy again. + printk("megasas: %p synchronous command detected on the internal reset queue, re-issuing it.\n", reset_cmd); + reset_cmd->cmd_status = ENODATA; + instance->instancet->fire_cmd(instance, reset_cmd->frame_phys_addr ,0,instance->reg_set); + } + else { + printk("megasas: %p unexpected command on the internal reset defer list.\n", reset_cmd); + } + reset_index++; + } + + printk("megaraid_sas: All pending commands have been cleared for reset condition.\n"); + + return SUCCESS; + } + + // Kernel reset without internal reset in progress. + printk("megaraid_sas: HBA reset handler invoked without an internal reset condition.\n"); + for (i = 0; i < wait_time; i++) { + int outstanding = atomic_read(&instance->fw_outstanding); + + if (!outstanding) + break; + + if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { + printk(KERN_NOTICE "megasas: [%2d]waiting for %d " + "commands to complete\n",i,outstanding); + /* + * Call cmd completion routine. Cmd to be + * be completed directly without depending on isr. + */ + megasas_complete_cmd_dpc((unsigned long)instance); + } + + msleep(1000); + } + + i = 0; + kill_adapter_flag = 0; + do { + fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; + if ((fw_state == MFI_STATE_FAULT) && (instance->disableOnlineCtrlReset == 0)) { + printk("megasas: waiting_for_outstanding: before issue OCR. FW state = %x\n", fw_state); + if (i == 3) { + kill_adapter_flag = 2; + break; + } + megasas_do_ocr(instance); + kill_adapter_flag = 1; + printk("megasas: waiting_for_outstanding: after issue OCR. \n"); + + /* wait for 5 secs to let the FW finish all the pending cmds*/ + for (sl=0; sl<10; sl++) + msleep(500); + } + i++; + } while (i <= 3); + + if (atomic_read(&instance->fw_outstanding) && !kill_adapter_flag) { + if (instance->disableOnlineCtrlReset == 0) { + printk("megasas: waiting_for_outstanding: before issue OCR. FW state = %x\n", fw_state); + megasas_do_ocr(instance); + printk("megasas: waiting_for_outstanding: after issue OCR. \n"); + + /* wait for 5 secs to let the FW finish all the pending cmds*/ + for (i = 0; i < wait_time; i++) { + int outstanding = atomic_read(&instance->fw_outstanding); + + if (!outstanding) + return SUCCESS; + msleep(1000); + } + } + } + + if (atomic_read(&instance->fw_outstanding) || (kill_adapter_flag == 2)) { + printk("megaraid_sas: pending commands remain even after reset handling.\n"); + /* + * Send signal to FW to stop processing any pending cmds. + * The controller will be taken offline by the OS now. + */ + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + writel(MFI_STOP_ADP, + &instance->reg_set->reserved_0); + } else { + writel(MFI_STOP_ADP, + &instance->reg_set->inbound_doorbell); + } + megasas_dump_pending_frames(instance); + spin_lock_irqsave(&instance->hba_lock, flags); + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + spin_unlock_irqrestore(&instance->hba_lock, flags); + return FAILED; + } + + printk("megaraid_sas: no more pending commands remain after reset handling.\n"); + + return SUCCESS; +} + +/** + * megasas_generic_reset - Generic reset routine + * @scmd: Mid-layer SCSI command + * + * This routine implements a generic reset handler for device, bus and host + * reset requests. Device, bus and host specific reset handlers can use this + * function after they do their specific tasks. + */ +static int megasas_generic_reset(struct scsi_cmnd *scmd) +{ + int ret_val; + struct megasas_instance *instance; + + instance = (struct megasas_instance *)scmd->device->host->hostdata; + + scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x retries=%x\n", + scmd->serial_number, scmd->cmnd[0], scmd->retries); + + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { + printk(KERN_ERR "megasas: cannot recover from previous reset " + "failures\n"); + return FAILED; + } + + ret_val = megasas_wait_for_outstanding(instance); + if (ret_val == SUCCESS) + printk(KERN_NOTICE "megasas: reset successful \n"); + else + printk(KERN_ERR "megasas: failed to do reset\n"); + + return ret_val; +} + +/** + * megasas_reset_timer - quiesce the adapter if required + * @scmd: scsi cmnd + * + * Sets the FW busy flag and reduces the host->can_queue if the + * cmd has not been completed within the timeout period. + */ +static enum +scsi_eh_timer_return megasas_reset_timer(struct scsi_cmnd *scmd) +{ + struct megasas_cmd *cmd = (struct megasas_cmd *)scmd->SCp.ptr; + struct megasas_instance *instance; + unsigned long flags; + + if (time_after(jiffies, scmd->jiffies_at_alloc + + (MEGASAS_DEFAULT_CMD_TIMEOUT * 2) * HZ)) { + return EH_NOT_HANDLED; + } + + instance = cmd->instance; + if (!(instance->flag & MEGASAS_FW_BUSY)) { + /* FW is busy, throttle IO */ + spin_lock_irqsave(instance->host->host_lock, flags); + + instance->host->can_queue = 16; + instance->last_time = jiffies; + instance->flag |= MEGASAS_FW_BUSY; + + spin_unlock_irqrestore(instance->host->host_lock, flags); + } + return EH_RESET_TIMER; +} + +/** + * megasas_reset_device - Device reset handler entry point + */ +static int megasas_reset_device(struct scsi_cmnd *scmd) +{ + int ret; + + /* + * First wait for all commands to complete + */ + ret = megasas_generic_reset(scmd); + + return ret; +} + +/** + * megasas_reset_bus_host - Bus & host reset handler entry point + */ +static int megasas_reset_bus_host(struct scsi_cmnd *scmd) +{ + int ret; + + /* + * First wait for all commands to complete + */ + ret = megasas_generic_reset(scmd); + + return ret; +} + +/** + * megasas_bios_param - Returns disk geometry for a disk + * @sdev: device handle + * @bdev: block device + * @capacity: drive capacity + * @geom: geometry parameters + */ +static int +megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int geom[]) +{ + int heads; + int sectors; + sector_t cylinders; + unsigned long tmp; + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + + tmp = heads * sectors; + cylinders = capacity; + + sector_div(cylinders, tmp); + + /* + * Handle extended translation size for logical drives > 1Gb + */ + + if (capacity >= 0x200000) { + heads = 255; + sectors = 63; + tmp = heads*sectors; + cylinders = capacity; + sector_div(cylinders, tmp); + } + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return 0; +} + +static void megasas_aen_polling(struct work_struct *work); + +/** + * megasas_service_aen - Processes an event notification + * @instance: Adapter soft state * @cmd: AEN command completed by the ISR * * For AEN, driver sends a command down to FW that is held by the FW till an @@ -1121,27 +3026,74 @@ megasas_bios_param(struct scsi_device *s static void megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) { + unsigned long flags; /* * Don't signal app if it is just an aborted previously registered aen */ - if (!cmd->abort_aen) + if ((!cmd->abort_aen) && (instance->unload == 0)) { + spin_lock_irqsave(&poll_aen_lock, flags); + megasas_poll_wait_aen = 1; + spin_unlock_irqrestore(&poll_aen_lock, flags); + wake_up(&megasas_poll_wait); kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); + } else cmd->abort_aen = 0; instance->aen_cmd = NULL; megasas_return_cmd(instance, cmd); + + if ((instance->unload == 0) && ((instance->issuepend_done == 1))) { + struct megasas_aen_event *ev; + ev = kzalloc(sizeof(*ev), GFP_ATOMIC); + if (!ev) { + printk(KERN_ERR "megasas_service_aen: out of memory\n"); + } else { + ev->instance = instance; + instance->ev = ev; + INIT_WORK(&ev->hotplug_work, megasas_aen_polling); + schedule_delayed_work( + (struct delayed_work *)&ev->hotplug_work, 0); + } + } } +static int megasas_slave_alloc(struct scsi_device *sdev) +{ + u16 pd_index = 0; + struct megasas_instance *instance ; + instance = megasas_lookup_instance(sdev->host->host_no); + + if ((sdev->channel < MEGASAS_MAX_PD_CHANNELS) && + (sdev->type == TYPE_DISK)) { + /* + * Open the OS scan to the SYSTEM PD + */ + pd_index = + (sdev->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + sdev->id; + if ((instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) && + (instance->pd_list[pd_index].driveType == + TYPE_DISK)) { + return 0; + } + + return -ENXIO; + } + return 0; +} + + /* * Scsi host template for megaraid_sas driver */ static struct scsi_host_template megasas_template = { .module = THIS_MODULE, - .name = "LSI Logic SAS based MegaRAID driver", + .name = "LSI SAS based MegaRAID driver", .proc_name = "megaraid_sas", .slave_configure = megasas_slave_configure, + .slave_alloc = megasas_slave_alloc, .queuecommand = megasas_queue_command, .eh_device_reset_handler = megasas_reset_device, .eh_bus_reset_handler = megasas_reset_bus_host, @@ -1193,7 +3145,6 @@ megasas_complete_abort(struct megasas_in return; } - /** * megasas_unmap_sgbuf - Unmap SG buffers * @instance: Adapter soft state @@ -1217,12 +3168,18 @@ megasas_unmap_sgbuf(struct megasas_insta opcode = cmd->frame->hdr.cmd; if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) { - if (IS_DMA64) + if (instance->flag_ieee) { + buf_h = cmd->frame->io.sgl.sge_skinny[0].phys_addr; + + } else if (IS_DMA64) buf_h = cmd->frame->io.sgl.sge64[0].phys_addr; else buf_h = cmd->frame->io.sgl.sge32[0].phys_addr; } else { - if (IS_DMA64) + if (instance->flag_ieee) { + buf_h = cmd->frame->pthru.sgl.sge_skinny[0].phys_addr; + + } else if (IS_DMA64) buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr; else buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr; @@ -1233,6 +3190,7 @@ megasas_unmap_sgbuf(struct megasas_insta return; } + /** * megasas_complete_cmd - Completes a command * @instance: Adapter soft state @@ -1247,9 +3205,14 @@ static void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status) { + unsigned long flags; int exception = 0; struct megasas_header *hdr = &cmd->frame->hdr; + // If the commands complete successfully, the retry counter should also be reset + // for future re-tries. + cmd->retry_for_fw_reset = 0; + if (cmd->scmd) cmd->scmd->SCp.ptr = NULL; @@ -1338,6 +3301,12 @@ megasas_complete_cmd(struct megasas_inst case MFI_CMD_SMP: case MFI_CMD_STP: case MFI_CMD_DCMD: + if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET_INFO || + cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_GET) { + spin_lock_irqsave(&poll_aen_lock, flags); + megasas_poll_wait_aen = 0; + spin_unlock_irqrestore(&poll_aen_lock, flags); + } /* * See if got an event notification @@ -1364,39 +3333,286 @@ megasas_complete_cmd(struct megasas_inst } /** + * megasas_issue_pending_cmds_again - issue all pending cmds + * in FW again because of the fw reset + * @instance: Adapter soft state + */ +static inline void +megasas_issue_pending_cmds_again(struct megasas_instance *instance) +{ + struct megasas_cmd *cmd; + struct list_head clist_local; + union megasas_evt_class_locale class_locale; + unsigned long flags; + u32 seq_num; + + INIT_LIST_HEAD(&clist_local); + spin_lock_irqsave(&instance->hba_lock, flags); + list_splice_init(&instance->internal_reset_pending_q, &clist_local); + spin_unlock_irqrestore(&instance->hba_lock, flags); + + while (!list_empty(&clist_local)) { + cmd = list_entry((&clist_local)->next, struct megasas_cmd, list); + list_del_init(&cmd->list); + + if (cmd->sync_cmd || cmd->scmd) { + printk("megaraid_sas: command %p, %p:%d detected to be pending while HBA reset.\n", cmd, cmd->scmd, cmd->sync_cmd); + + cmd->retry_for_fw_reset++; + + // If a command has continuously been tried multiple times and causing + // a FW reset condition, no further recoveries should be performed on + // the controller + if (cmd->retry_for_fw_reset == 3) { + printk("megaraid_sas: command %p, %p:%d was tried multiple times during adapter reset. Shutting down the HBA\n", cmd, cmd->scmd, cmd->sync_cmd); + megaraid_sas_kill_hba(instance); + + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + return; + } + } + + if (cmd->sync_cmd == 1) { + if (cmd->scmd) { + printk("megaraid_sas: unexpected SCSI command attached to internal command!\n"); + } + printk("megasas: %p synchronous command detected on the internal reset queue, issue it again.\n", cmd); + cmd->cmd_status = ENODATA; + instance->instancet->fire_cmd(instance,cmd->frame_phys_addr ,0,instance->reg_set); + } else if (cmd->scmd) { + printk("megasas: %p scsi command [%02x], %#lx detected on the internal reset queue, issue it again.\n", cmd, cmd->scmd->cmnd[0], cmd->scmd->serial_number); + atomic_inc(&instance->fw_outstanding); + instance->instancet->fire_cmd(instance, cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set); + } + else { + printk("megasas: %p unexpected command on the internal reset defer list while re-issue!!\n", cmd); + } + } + + // Re-register AEN + if (instance->aen_cmd) { + printk("megaraid_sas: existing aen_cmd discovered in deferred processing, freeing...\n"); + megasas_return_cmd(instance, instance->aen_cmd); + instance->aen_cmd = NULL; + } + + /* + * Initiate AEN (Asynchronous Event Notification) + */ + seq_num = instance->last_seq_num; + class_locale.members.reserved = 0; + class_locale.members.locale = MR_EVT_LOCALE_ALL; + class_locale.members.class = MR_EVT_CLASS_DEBUG; + + megasas_register_aen(instance, seq_num, class_locale.word); + + +} + +/** + * Move the internal reset pending commands to a deferred queue. + * + * We move the commands pending at internal reset time to a pending queue. This queue would + * be flushed after successful completion of the internal reset sequence. + * if the internal reset did not complete in time, the kernel reset handler would flush these + * commands. + **/ +static void megasas_internal_reset_defer_cmds(struct megasas_instance *instance) +{ + struct megasas_cmd *cmd; + int i; + u32 max_cmd = instance->max_fw_cmds; + u32 defer_index; + unsigned long flags; + + defer_index = 0; + spin_lock_irqsave(&instance->cmd_pool_lock, flags); + for (i = 0; i < max_cmd; i++) { + cmd = instance->cmd_list[i]; + if (cmd->sync_cmd == 1 || cmd->scmd) { + printk("megasas: moving cmd[%d]:%p:%d:%p on the defer queue as internal reset in progress.\n", + defer_index, cmd, cmd->sync_cmd, cmd->scmd); + if (!list_empty(&cmd->list)) { + printk("megaraid_sas: ERROR while moving this cmd:%p, %d %p, it was discovered on some list?\n", cmd, cmd->sync_cmd, cmd->scmd); + list_del_init(&cmd->list); + } + defer_index++; + list_add_tail(&cmd->list, &instance->internal_reset_pending_q); + } + } + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); +} + + +static void +process_fw_state_change_wq(struct work_struct *work) +{ + struct megasas_instance *instance = + container_of(work, struct megasas_instance, work_init); + u32 wait; + unsigned long flags; + + if (instance->adprecovery != MEGASAS_ADPRESET_SM_INFAULT) { + printk("megaraid_sas: error, unexpected adapter recovery state %x in %s\n", instance->adprecovery, __FUNCTION__); + return ; + } + + if (instance->adprecovery == MEGASAS_ADPRESET_SM_INFAULT) { + printk("megaraid_sas: FW detected to be in fault state, restarting it...\n"); + + instance->instancet->disable_intr(instance->reg_set); + atomic_set(&instance->fw_outstanding, 0); + + atomic_set(&instance->fw_reset_no_pci_access, 1); + instance->instancet->adp_reset(instance, instance->reg_set); + atomic_set(&instance->fw_reset_no_pci_access, 0 ); + + printk("megaraid_sas: FW was restarted successfully, initiating next stage...\n"); + + printk("megaraid_sas: HBA recovery state machine, state 2 starting...\n"); + + /*waitting for about 20 second before start the second init*/ + for(wait = 0; wait < 30; wait++) + msleep(1000); + + if (megasas_transition_to_ready(instance)) + { + printk("megaraid_sas: out: controller is not in ready state\n"); + + megaraid_sas_kill_hba(instance); + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + return ; + } + + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || + (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR)) + { + *instance->consumer = *instance->producer; + } else { + *instance->consumer = 0; + *instance->producer = 0; + } + + if ( megasas_check_cpx_support( instance ) == 0 ){ + if ( megasas_send_cpx_queue_data( instance ) ){ + printk("megasas: Sending cpx queue data to FW failed.\n"); + megasas_remove_cpx(instance); + } + } + + // Transition the FW to operational state + megasas_issue_init_mfi(instance); + + // Setting the adapter to OPERATIONAL at this point is very important. This would + // prevent other subsystems (reset, aen, and ioctls) to block till the recovery + // logic has run it's course. + spin_lock_irqsave(&instance->hba_lock, flags); + instance->adprecovery = MEGASAS_HBA_OPERATIONAL; + spin_unlock_irqrestore(&instance->hba_lock, flags); + instance->instancet->enable_intr(instance->reg_set); + + printk("megaraid_sas: second stage of reset complete, FW is ready now.\n"); + + megasas_issue_pending_cmds_again(instance); + instance->issuepend_done = 1; + + + } + return ; +} + +/** * megasas_deplete_reply_queue - Processes all completed commands * @instance: Adapter soft state * @alt_status: Alternate status to be returned to * SCSI mid-layer instead of the status * returned by the FW + * Note: this must be called with hba lock held */ static int megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) { - /* - * Check if it is our interrupt - * Clear the interrupt - */ - if(instance->instancet->clear_intr(instance->reg_set)) + u32 mfiStatus; + u32 fw_state; + + // If the adapter is under a reset recovery, all interrupts coming from it must be acknowledged + // if the consumer pointer value indicates so. + if((mfiStatus = instance->instancet->check_reset(instance, instance->reg_set)) == 1) { + return IRQ_HANDLED; + } + + // Clear the interrupt on the HBA + if((mfiStatus = instance->instancet->clear_intr(instance->reg_set)) == 0) { return IRQ_NONE; + } - if (instance->hw_crit_error) - goto out_done; - /* - * Schedule the tasklet for cmd completion - */ + instance->mfiStatus = mfiStatus; + + // If the current soft state indicates an OPERATIONAL state _and_ now we have + // detected state change, this should be FW FAULT case. + if ((mfiStatus & MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE)) { + fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; + + if (fw_state != MFI_STATE_FAULT) { + printk("megaraid_sas: fw state while internal state change operational, state:%x\n", fw_state); + } + + if ((fw_state == MFI_STATE_FAULT) && (instance->disableOnlineCtrlReset == 0)){ + printk("megaraid_sas: adapter reset condition is detected, waiting for it to restart...\n"); + + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1064R) || + (instance->pdev->device == PCI_DEVICE_ID_DELL_PERC5) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_VERDE_ZCR)) + { + *instance->consumer = MEGASAS_ADPRESET_INPROG_SIGN; + } + + + instance->instancet->disable_intr(instance->reg_set); + instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT; // indicates adapter restart stage 1 is in progress + instance->issuepend_done = 0; + + // The pending commands are moved to a deferred list. We would pick commands up and + // re-issue once the reset processing is over. + atomic_set(&instance->fw_outstanding, 0); + megasas_internal_reset_defer_cmds(instance); + + // Schedule a low-priorty thread to perform the function for current stage of + // adapter reset state machine. + printk("megaraid_sas: FW state detected, current:%x, reset stage:%d\n", fw_state, instance->adprecovery); + schedule_work(&instance->work_init); + return IRQ_HANDLED; + } + else { + printk("megaraid_sas: fw state while internal state changes, state:%x, disableOCR=%x\n", + fw_state, instance->disableOnlineCtrlReset); + } + } + + // Schedule the tasklet for cmd completion tasklet_schedule(&instance->isr_tasklet); -out_done: return IRQ_HANDLED; } - /** * megasas_isr - isr entry point */ static irqreturn_t megasas_isr(int irq, void *devp) { - return megasas_deplete_reply_queue((struct megasas_instance *)devp, - DID_OK); + struct megasas_instance *instance; + unsigned long flags; + irqreturn_t rc; + + if ( atomic_read( &(( (struct megasas_instance *)devp)->fw_reset_no_pci_access )) ) + return IRQ_HANDLED; + + instance = (struct megasas_instance *)devp; + + spin_lock_irqsave(&instance->hba_lock, flags); + rc = megasas_deplete_reply_queue(instance, DID_OK); + spin_unlock_irqrestore(&instance->hba_lock, flags); + + return rc; } /** @@ -1415,6 +3631,7 @@ megasas_transition_to_ready(struct megas u8 max_wait; u32 fw_state; u32 cur_state; + u32 abs_state, curr_abs_state; fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; @@ -1424,6 +3641,8 @@ megasas_transition_to_ready(struct megas while (fw_state != MFI_STATE_READY) { + abs_state = instance->instancet->read_fw_status_reg(instance->reg_set); + switch (fw_state) { case MFI_STATE_FAULT: @@ -1435,18 +3654,36 @@ megasas_transition_to_ready(struct megas /* * Set the CLR bit in inbound doorbell */ - writel(MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, - &instance->reg_set->inbound_doorbell); + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + + writel( + MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, + &instance->reg_set->reserved_0); + } else { + writel( + MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, + &instance->reg_set->inbound_doorbell); + } - max_wait = 2; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_WAIT_HANDSHAKE; break; case MFI_STATE_BOOT_MESSAGE_PENDING: - writel(MFI_INIT_HOTPLUG, - &instance->reg_set->inbound_doorbell); + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + writel(MFI_INIT_HOTPLUG, + &instance->reg_set->reserved_0); + } else + writel(MFI_INIT_HOTPLUG, + &instance->reg_set->inbound_doorbell); - max_wait = 10; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_BOOT_MESSAGE_PENDING; break; @@ -1455,9 +3692,17 @@ megasas_transition_to_ready(struct megas * Bring it to READY state; assuming max wait 10 secs */ instance->instancet->disable_intr(instance->reg_set); - writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell); + if ((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + writel(MFI_RESET_FLAGS, + &instance->reg_set->reserved_0); + } else + writel(MFI_RESET_FLAGS, + &instance->reg_set->inbound_doorbell); - max_wait = 10; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_OPERATIONAL; break; @@ -1465,32 +3710,32 @@ megasas_transition_to_ready(struct megas /* * This state should not last for more than 2 seconds */ - max_wait = 2; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_UNDEFINED; break; case MFI_STATE_BB_INIT: - max_wait = 2; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_BB_INIT; break; case MFI_STATE_FW_INIT: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FW_INIT; break; case MFI_STATE_FW_INIT_2: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FW_INIT_2; break; case MFI_STATE_DEVICE_SCAN: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_DEVICE_SCAN; break; case MFI_STATE_FLUSH_CACHE: - max_wait = 20; + max_wait = MEGASAS_RESET_WAIT_TIME; cur_state = MFI_STATE_FLUSH_CACHE; break; @@ -1506,8 +3751,10 @@ megasas_transition_to_ready(struct megas for (i = 0; i < (max_wait * 1000); i++) { fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK ; + curr_abs_state = + instance->instancet->read_fw_status_reg(instance->reg_set); - if (fw_state == cur_state) { + if (abs_state == curr_abs_state) { msleep(1); } else break; @@ -1516,12 +3763,12 @@ megasas_transition_to_ready(struct megas /* * Return error if fw_state hasn't changed after max_wait */ - if (fw_state == cur_state) { + if (curr_abs_state == abs_state) { printk(KERN_DEBUG "FW state [%d] hasn't changed " "in %d secs\n", fw_state, max_wait); return -ENODEV; } - }; + } printk(KERN_INFO "megasas: FW now in Ready state\n"); return 0; @@ -1594,11 +3841,16 @@ static int megasas_create_frame_pool(str sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : sizeof(struct megasas_sge32); + if (instance->flag_ieee) { + sge_sz = sizeof(struct megasas_sge_skinny); + } + /* * Calculated the number of 64byte frames required for SGL */ sgl_sz = sge_sz * instance->max_num_sge; frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE; + frame_count = 15; /* * We need one extra frame for the MFI command @@ -1655,7 +3907,15 @@ static int megasas_create_frame_pool(str return -ENOMEM; } + memset(cmd->frame, 0, total_sz); + cmd->frame->io.context = cmd->index; + + /* + * Initialize pad_0 to 0, otherwise it could corrupt + * the value of context and cause FW crash + */ + cmd->frame->io.pad_0 = 0; } return 0; @@ -1714,8 +3974,7 @@ static int megasas_alloc_cmds(struct meg * Allocate the dynamic array first and then allocate individual * commands. */ - instance->cmd_list = kmalloc(sizeof(struct megasas_cmd *) * max_cmd, - GFP_KERNEL); + instance->cmd_list = kcalloc(max_cmd, sizeof(struct megasas_cmd*), GFP_KERNEL); if (!instance->cmd_list) { printk(KERN_DEBUG "megasas: out of memory\n"); @@ -1747,6 +4006,7 @@ static int megasas_alloc_cmds(struct meg cmd = instance->cmd_list[i]; memset(cmd, 0, sizeof(struct megasas_cmd)); cmd->index = i; + cmd->scmd = NULL; cmd->instance = instance; list_add_tail(&cmd->list, &instance->cmd_pool); @@ -1763,6 +4023,181 @@ static int megasas_alloc_cmds(struct meg return 0; } +/* + * megasas_get_pd_list_info - Returns FW's pd_list structure + * @instance: Adapter soft state + * @pd_list: pd_list structure + * + * Issues an internal command (DCMD) to get the FW's controller PD + * list structure. This information is mainly used to find out SYSTEM + * supported by the FW. + */ +static int +megasas_get_pd_list(struct megasas_instance *instance) +{ + int ret = 0, pd_index = 0; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_PD_LIST *ci; + struct MR_PD_ADDRESS *pd_addr; + dma_addr_t ci_h = 0; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_DEBUG "megasas (get_pd_list): Failed to get cmd\n"); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + ci = pci_alloc_consistent(instance->pdev, + MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), &ci_h); + + if (!ci) { + printk(KERN_DEBUG "Failed to alloc mem for pd_list\n"); + megasas_return_cmd(instance, cmd); + return -ENOMEM; + } + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->mbox.b[0] = MR_PD_QUERY_TYPE_EXPOSED_TO_HOST; + dcmd->mbox.b[1] = 0; + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xFF; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST); + dcmd->opcode = MR_DCMD_PD_LIST_QUERY; + dcmd->sgl.sge32[0].phys_addr = ci_h; + dcmd->sgl.sge32[0].length = MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST); + + if (!megasas_issue_polled(instance, cmd)) { + ret = 0; + } else { + ret = -1; + } + + /* + * the following function will get the instance PD LIST. + */ + + pd_addr = ci->addr; + + if ( ret == 0 && + (ci->count < + (MEGASAS_MAX_PD_CHANNELS * MEGASAS_MAX_DEV_PER_CHANNEL))) { + + memset(instance->pd_list, 0, + MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); + + for (pd_index = 0; pd_index < ci->count; pd_index++) { + + instance->pd_list[pd_addr->deviceId].tid = + pd_addr->deviceId; + instance->pd_list[pd_addr->deviceId].driveType = + pd_addr->scsiDevType; + instance->pd_list[pd_addr->deviceId].driveState = + MR_PD_STATE_SYSTEM; + pd_addr++; + } + } + + pci_free_consistent(instance->pdev, + MEGASAS_MAX_PD * sizeof(struct MR_PD_LIST), + ci, ci_h); + + + megasas_return_cmd(instance, cmd); + + return ret; +} +/** + * megasas_get_ld_list_info - Returns FW's ld_list structure + * @instance: Adapter soft state + * @ld_list: ld_list structure + * + * Issues an internal command (DCMD) to get the FW's controller PD + * list structure. This information is mainly used to find out SYSTEM + * supported by the FW. + */ +static int +megasas_get_ld_list(struct megasas_instance *instance) +{ + int ret = 0, ld_index = 0, ids = 0; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_LD_LIST *ci; + dma_addr_t ci_h = 0; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_DEBUG "megasas (megasas_get_ld_list): Failed to get cmd\n"); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + ci = pci_alloc_consistent(instance->pdev, sizeof(struct MR_LD_LIST), &ci_h); + + if (!ci) { + printk(KERN_DEBUG "Failed to alloc mem for megasas_get_ld_list\n"); + megasas_return_cmd(instance, cmd); + return -ENOMEM; + } + + memset(ci, 0, sizeof(*ci)); + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xFF; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_READ; + dcmd->timeout = 0; + dcmd->data_xfer_len = sizeof(struct MR_LD_LIST); + dcmd->opcode = MR_DCMD_LD_GET_LIST; + dcmd->sgl.sge32[0].phys_addr = ci_h; + dcmd->sgl.sge32[0].length = sizeof(struct MR_LD_LIST); + dcmd->pad_0 = 0; + + if (!megasas_issue_polled(instance, cmd)) { + ret = 0; + + } else { + ret = -1; + } + + /* + * the following function will get the instance PD LIST. + */ + + if ( (ret == 0) && (ci->ldCount <= (MAX_LOGICAL_DRIVES))){ + + memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); + + for (ld_index = 0; ld_index < ci->ldCount; ld_index++) { + if (ci->ldList[ld_index].state != 0) { + ids = ci->ldList[ld_index].ref.targetId; + instance->ld_ids[ids] = ci->ldList[ld_index].ref.targetId; + } + + } + + } + + pci_free_consistent(instance->pdev, sizeof(struct MR_LD_LIST), ci, ci_h); + + + megasas_return_cmd(instance, cmd); + + return ret; +} + /** * megasas_get_controller_info - Returns FW's controller structure * @instance: Adapter soft state @@ -1808,6 +4243,7 @@ megasas_get_ctrl_info(struct megasas_ins dcmd->sge_count = 1; dcmd->flags = MFI_FRAME_DIR_READ; dcmd->timeout = 0; + dcmd->pad_0 = 0; dcmd->data_xfer_len = sizeof(struct megasas_ctrl_info); dcmd->opcode = MR_DCMD_CTRL_GET_INFO; dcmd->sgl.sge32[0].phys_addr = ci_h; @@ -1827,58 +4263,276 @@ megasas_get_ctrl_info(struct megasas_ins return ret; } + /** - * megasas_complete_cmd_dpc - Returns FW's controller structure - * @instance_addr: Address of adapter soft state + * megasas_check_cpx_support : Tries to get the host memory address if fails then cpx + not supported else cpx supported. + * @instance: Adapter soft state * - * Tasklet to complete cmds + * @return 0 on success non-zero on failure. */ -static void megasas_complete_cmd_dpc(unsigned long instance_addr) +static int megasas_check_cpx_support( struct megasas_instance *instance) +{ + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct mr_cpx_init_data *cpx_init_data; + dma_addr_t cpx_init_data_h; + int retval = 0; + + cmd = megasas_get_cmd(instance); + if (!cmd) { + printk(KERN_DEBUG "megasas (get_host_mem_addr): Failed to get cmd\n"); + return -ENOMEM; + } + + cpx_init_data = pci_alloc_consistent(instance->pdev, sizeof( struct mr_cpx_init_data), &cpx_init_data_h); + if (cpx_init_data == NULL) { + printk(KERN_DEBUG "Failed to alloc mem for cpx_init_data. \n"); + megasas_return_cmd(instance, cmd); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + dcmd->flags = 0; + dcmd->opcode = MR_DCMD_CTRL_MISC_CPX_INIT_DATA_GET; + dcmd->data_xfer_len = sizeof(struct mr_cpx_init_data ); + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xff; + dcmd->sge_count = 1; + dcmd->sgl.sge32[0].phys_addr = cpx_init_data_h; + dcmd->sgl.sge32[0].length = sizeof(struct mr_cpx_init_data); + + retval = megasas_issue_polled ( instance, cmd ); + + if ( retval == 0 && cmd->frame->hdr.cmd_status == 0 ){ + instance->host_mem_phys = cpx_init_data->phys_addr_cache_buf; + instance->host_mem_len = cpx_init_data->size; + instance->cpx_dscrptr_cnt = cpx_init_data->cpx_desc_count; + if ( instance->host_mem_len == 0 || instance->host_mem_phys == 0 || ! instance->cpx_dscrptr_cnt ){ + printk("Got host_mem_len = 0 OR host_mem_phys as NULL OR cpx_descriptor count = 0 \n"); + retval = 1; + } + } else { + printk("megasas: cpx is not supported.\n"); + retval = 1; + } + + + pci_free_consistent(instance->pdev, sizeof(struct mr_cpx_init_data), cpx_init_data, cpx_init_data_h); + megasas_return_cmd(instance, cmd); + + return retval; +} + +/** + * megasas_send_cpx_queue_data : Sends the queue setup info to fw. + * @instance: Adapter soft state + * + * @return 0 on success non-zero on failure. + */ + +static int megasas_send_cpx_queue_data( struct megasas_instance *instance ) +{ + + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + int retval = 0; + + cmd = megasas_get_cmd(instance); + if (!cmd) { + printk(KERN_DEBUG "megasas (get_host_mem_addr): Failed to get cmd\n"); + return -ENOMEM; + } + + //initialize dcmd data + dcmd = &cmd->frame->dcmd; + dcmd->flags = 0; + dcmd->opcode = MR_DCMD_CTRL_MISC_CPX_QUEUE_DATA; + dcmd->data_xfer_len = sizeof( struct mr_cpx_queue_data ); + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xff; + dcmd->sge_count = 1; + dcmd->mbox.w[0] = instance->cpx_request_queue_h; + dcmd->mbox.w[1] = 0; + dcmd->mbox.w[2] = instance->cpx_dscrptr_cnt; + + retval = megasas_issue_polled ( instance, cmd ); + + megasas_return_cmd( instance, cmd); + + if ( retval == 0 ){ + instance->cpx_request_queue->consumer_idx = instance->cpx_request_queue->producer_idx = 0; + instance->cpx_supported = 1; + } + + return retval; +} + +static u32 megasas_get_cpx_mem_len( u16 max_fw_cmds ) +{ + return (sizeof( struct mr_cpx_request_queue ) + sizeof( union mr_cpx_descriptor ) * ( max_fw_cmds) ); +} + +static u32 megasas_remove_cpx( struct megasas_instance *instance) +{ + if ( instance->host_mem_virt ) + iounmap(instance->host_mem_virt); + if ( instance->cpx_request_queue ) + pci_free_consistent(instance->pdev, megasas_get_cpx_mem_len( instance->cpx_dscrptr_cnt), + instance->cpx_request_queue,instance->cpx_request_queue_h ); + instance->cpx_supported = 0; + + return 0; +} + +/* should have host_mem_phys intialized before calling this function.*/ +static int megasas_init_cpx( struct megasas_instance *instance) +{ + + //map address + instance->host_mem_virt = ioremap(instance->host_mem_phys, instance->host_mem_len); + if ( instance->host_mem_virt == NULL ){ + printk("megasas: Failed to ioremap host memory.\n"); + goto error_unload; + } + + //allocate memory for indices and descriptors for reply and response array's + instance->cpx_request_queue = pci_alloc_consistent( instance->pdev, megasas_get_cpx_mem_len (instance->cpx_dscrptr_cnt), &instance->cpx_request_queue_h ); + if ( instance->cpx_request_queue == NULL ){ + printk(KERN_ERR "megasas: Out of DMA memory for cpx operations.\n"); + goto error_unload; + } + + //initialize queues + instance->cpx_dscrptr = (union mr_cpx_descriptor *)((u8*)instance->cpx_request_queue + ( sizeof(instance->cpx_request_queue->consumer_idx)*2 )); + + //send data to fw. + if ( megasas_send_cpx_queue_data( instance ) ){ + printk("megasas: Sending cpx queue data to FW failed.\n"); + goto error_unload; + } + + return 0; + +error_unload: + megasas_remove_cpx( instance ); + return -1; +} + +/** + * megasas_issue_init_mfi - Initializes the FW + * @instance: Adapter soft state + * + * Issues the INIT MFI cmd + */ +static int +megasas_issue_init_mfi(struct megasas_instance *instance) { - u32 producer; - u32 consumer; u32 context; + struct megasas_cmd *cmd; - struct megasas_instance *instance = (struct megasas_instance *)instance_addr; - unsigned long flags; - /* If we have already declared adapter dead, donot complete cmds */ - if (instance->hw_crit_error) - return; + struct megasas_init_frame *init_frame; + struct megasas_init_queue_info *initq_info; + dma_addr_t init_frame_h; + dma_addr_t initq_info_h; - producer = *instance->producer; - consumer = *instance->consumer; + /* + * Prepare a init frame. Note the init frame points to queue info + * structure. Each frame has SGL allocated after first 64 bytes. For + * this frame - since we don't need any SGL - we use SGL's space as + * queue info structure + * + * We will not get a NULL command below. We just created the pool. + */ + cmd = megasas_get_cmd(instance); - while (consumer != producer) { - context = instance->reply_queue[consumer]; + init_frame = (struct megasas_init_frame *)cmd->frame; + initq_info = (struct megasas_init_queue_info *) + ((unsigned long)init_frame + 64); - cmd = instance->cmd_list[context]; + init_frame_h = cmd->frame_phys_addr; + initq_info_h = init_frame_h + 64; + + context = init_frame->context; + memset(init_frame, 0, MEGAMFI_FRAME_SIZE); + memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); + init_frame->context = context; + + initq_info->reply_queue_entries = instance->max_fw_cmds + 1; + initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h; + + initq_info->producer_index_phys_addr_lo = instance->producer_h; + initq_info->consumer_index_phys_addr_lo = instance->consumer_h; + + init_frame->cmd = MFI_CMD_INIT; + init_frame->cmd_status = 0xFF; + init_frame->queue_info_new_phys_addr_lo = initq_info_h; + + init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info); + + /* + * disable the intr before firing the init frame to FW + */ + instance->instancet->disable_intr(instance->reg_set); + + /* + * Issue the init frame in polled mode + */ + + if (megasas_issue_polled(instance, cmd)) { + printk(KERN_ERR "megasas: Failed to init firmware\n"); + megasas_return_cmd(instance, cmd); + goto fail_fw_init; + } - megasas_complete_cmd(instance, cmd, DID_OK); + megasas_return_cmd(instance, cmd); - consumer++; - if (consumer == (instance->max_fw_cmds + 1)) { - consumer = 0; - } - } + return 0; - *instance->consumer = producer; +fail_fw_init: + return -EINVAL; +} - /* - * Check if we can restore can_queue - */ - if (instance->flag & MEGASAS_FW_BUSY - && time_after(jiffies, instance->last_time + 5 * HZ) - && atomic_read(&instance->fw_outstanding) < 17) { +/** + * megasas_start_timer - Initializes a timer object + * @instance: Adapter soft state + * @timer: timer object to be initialized + * @fn: timer function + * @interval: time interval between timer function call + */ +static inline void +megasas_start_timer(struct megasas_instance *instance, + struct timer_list *timer, + void *fn, unsigned long interval) +{ + init_timer(timer); + timer->expires = jiffies + interval; + timer->data = (unsigned long)instance; + timer->function = fn; + add_timer(timer); +} - spin_lock_irqsave(instance->host->host_lock, flags); - instance->flag &= ~MEGASAS_FW_BUSY; - instance->host->can_queue = - instance->max_fw_cmds - MEGASAS_INT_CMDS; +/** + * megasas_io_completion_timer - Timer fn + * @instance_addr: Address of adapter soft state + * + * Schedules tasklet for cmd completion + * if poll_mode_io is set + */ +static void +megasas_io_completion_timer(unsigned long instance_addr) +{ + struct megasas_instance *instance = + (struct megasas_instance *)instance_addr; - spin_unlock_irqrestore(instance->host->host_lock, flags); - } + if (atomic_read(&instance->fw_outstanding)) + tasklet_schedule(&instance->isr_tasklet); + /* Restart timer */ + if (poll_mode_io) + mod_timer(&instance->io_completion_timer, + jiffies + MEGASAS_COMPLETION_TIMER_INTERVAL); } /** @@ -1893,22 +4547,24 @@ static int megasas_init_mfi(struct megas u32 reply_q_sz; u32 max_sectors_1; u32 max_sectors_2; + u32 tmp_sectors; struct megasas_register_set __iomem *reg_set; - - struct megasas_cmd *cmd; struct megasas_ctrl_info *ctrl_info; - - struct megasas_init_frame *init_frame; - struct megasas_init_queue_info *initq_info; - dma_addr_t init_frame_h; - dma_addr_t initq_info_h; - /* * Map the message registers */ - instance->base_addr = pci_resource_start(instance->pdev, 0); + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078GEN2) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0079GEN2)) { + instance->base_addr = pci_resource_start(instance->pdev, 1); + } else { + instance->base_addr = pci_resource_start(instance->pdev, 0); + } - if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) { + if (pci_request_selected_regions(instance->pdev, + pci_select_bars(instance->pdev, IORESOURCE_MEM), + "megasas: LSI")) { printk(KERN_DEBUG "megasas: IO memory region busy!\n"); return -EBUSY; } @@ -1924,9 +4580,18 @@ static int megasas_init_mfi(struct megas switch(instance->pdev->device) { - case PCI_DEVICE_ID_LSI_SAS1078R: + case PCI_DEVICE_ID_LSI_SAS1078R: + case PCI_DEVICE_ID_LSI_SAS1078DE: instance->instancet = &megasas_instance_template_ppc; break; + case PCI_DEVICE_ID_LSI_SAS1078GEN2: + case PCI_DEVICE_ID_LSI_SAS0079GEN2: + instance->instancet = &megasas_instance_template_gen2; + break; + case PCI_DEVICE_ID_LSI_SAS0073SKINNY: + case PCI_DEVICE_ID_LSI_SAS0071SKINNY: + instance->instancet = &megasas_instance_template_skinny; + break; case PCI_DEVICE_ID_LSI_SAS1064R: case PCI_DEVICE_ID_DELL_PERC5: default: @@ -1979,52 +4644,29 @@ static int megasas_init_mfi(struct megas goto fail_reply_queue; } - /* - * Prepare a init frame. Note the init frame points to queue info - * structure. Each frame has SGL allocated after first 64 bytes. For - * this frame - since we don't need any SGL - we use SGL's space as - * queue info structure - * - * We will not get a NULL command below. We just created the pool. - */ - cmd = megasas_get_cmd(instance); - - init_frame = (struct megasas_init_frame *)cmd->frame; - initq_info = (struct megasas_init_queue_info *) - ((unsigned long)init_frame + 64); - - init_frame_h = cmd->frame_phys_addr; - initq_info_h = init_frame_h + 64; - - memset(init_frame, 0, MEGAMFI_FRAME_SIZE); - memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); - - initq_info->reply_queue_entries = instance->max_fw_cmds + 1; - initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h; - - initq_info->producer_index_phys_addr_lo = instance->producer_h; - initq_info->consumer_index_phys_addr_lo = instance->consumer_h; + if ( megasas_check_cpx_support( instance ) == 0 ) + if ( megasas_init_cpx( instance ) ) + printk("Error in initilizing cpx.\n"); - init_frame->cmd = MFI_CMD_INIT; - init_frame->cmd_status = 0xFF; - init_frame->queue_info_new_phys_addr_lo = initq_info_h; + if (megasas_issue_init_mfi(instance)) + goto fail_fw_init; - init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info); + instance->fw_support_ieee = 0; + instance->fw_support_ieee = (instance->instancet->read_fw_status_reg(reg_set) & 0x04000000); - /* - * disable the intr before firing the init frame to FW - */ - instance->instancet->disable_intr(instance->reg_set); + printk("megasas_init_mfi: fw_support_ieee=%d", instance->fw_support_ieee); + if (instance->fw_support_ieee) + instance->flag_ieee = 1; + + /** for passthrough + * the following function will get the PD LIST. + */ - /* - * Issue the init frame in polled mode - */ - if (megasas_issue_polled(instance, cmd)) { - printk(KERN_DEBUG "megasas: Failed to init firmware\n"); - goto fail_fw_init; - } + memset(instance->pd_list, 0, MEGASAS_MAX_PD * sizeof(struct megasas_pd_list)); + megasas_get_pd_list(instance); - megasas_return_cmd(instance, cmd); + memset(instance->ld_ids, 0xff, MEGASAS_MAX_LD_IDS); + megasas_get_ld_list(instance); ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL); @@ -2037,17 +4679,21 @@ static int megasas_init_mfi(struct megas * Note that older firmwares ( < FW ver 30) didn't report information * to calculate max_sectors_1. So the number ended up as zero always. */ + tmp_sectors = 0; if (ctrl_info && !megasas_get_ctrl_info(instance, ctrl_info)) { max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) * ctrl_info->max_strips_per_io; max_sectors_2 = ctrl_info->max_request_size; - instance->max_sectors_per_req = (max_sectors_1 < max_sectors_2) - ? max_sectors_1 : max_sectors_2; - } else - instance->max_sectors_per_req = instance->max_num_sge * - PAGE_SIZE / 512; + tmp_sectors = min_t(u32, max_sectors_1 , max_sectors_2); + instance->disableOnlineCtrlReset = ctrl_info->properties.OnOffProperties.disableOnlineCtrlReset; + } + + instance->max_sectors_per_req = instance->max_num_sge * + PAGE_SIZE / 512; + if (tmp_sectors && (instance->max_sectors_per_req > tmp_sectors)) + instance->max_sectors_per_req = tmp_sectors; kfree(ctrl_info); @@ -2055,12 +4701,17 @@ static int megasas_init_mfi(struct megas * Setup tasklet for cmd completion */ - tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc, - (unsigned long)instance); + tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc, + (unsigned long)instance); + + /* Initialize the cmd completion timer */ + if (poll_mode_io) + megasas_start_timer(instance, &instance->io_completion_timer, + megasas_io_completion_timer, + MEGASAS_COMPLETION_TIMER_INTERVAL); return 0; fail_fw_init: - megasas_return_cmd(instance, cmd); pci_free_consistent(instance->pdev, reply_q_sz, instance->reply_queue, instance->reply_queue_h); @@ -2072,7 +4723,8 @@ static int megasas_init_mfi(struct megas iounmap(instance->reg_set); fail_ioremap: - pci_release_regions(instance->pdev); + pci_release_selected_regions(instance->pdev, + pci_select_bars(instance->pdev, IORESOURCE_MEM)); return -EINVAL; } @@ -2092,7 +4744,10 @@ static void megasas_release_mfi(struct m iounmap(instance->reg_set); - pci_release_regions(instance->pdev); + megasas_remove_cpx( instance ); + + pci_release_selected_regions(instance->pdev, + pci_select_bars(instance->pdev, IORESOURCE_MEM)); } /** @@ -2140,6 +4795,7 @@ megasas_get_seq_num(struct megasas_insta dcmd->sge_count = 1; dcmd->flags = MFI_FRAME_DIR_READ; dcmd->timeout = 0; + dcmd->pad_0 = 0; dcmd->data_xfer_len = sizeof(struct megasas_evt_log_info); dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO; dcmd->sgl.sge32[0].phys_addr = el_info_h; @@ -2215,6 +4871,8 @@ megasas_register_aen(struct megasas_inst * Previously issued event registration includes * current request. Nothing to do. */ + printk(KERN_INFO "%s[%d]: already registered\n", + __FUNCTION__, instance->host->host_no); return 0; } else { curr_aen.members.locale |= prev_aen.members.locale; @@ -2254,13 +4912,20 @@ megasas_register_aen(struct megasas_inst dcmd->sge_count = 1; dcmd->flags = MFI_FRAME_DIR_READ; dcmd->timeout = 0; + dcmd->pad_0 = 0; dcmd->data_xfer_len = sizeof(struct megasas_evt_detail); dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT; dcmd->mbox.w[0] = seq_num; + instance->last_seq_num = seq_num; dcmd->mbox.w[1] = curr_aen.word; dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h; dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail); + if (instance->aen_cmd != NULL) { + megasas_return_cmd(instance, cmd); + return 0; + } + /* * Store reference to the cmd used to register for AEN. When an * application wants us to register for AEN, we have to abort this @@ -2271,7 +4936,8 @@ megasas_register_aen(struct megasas_inst /* * Issue the aen registration frame */ - instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); + instance->instancet->fire_cmd(instance, + cmd->frame_phys_addr, 0, instance->reg_set); return 0; } @@ -2304,6 +4970,30 @@ static int megasas_start_aen(struct mega class_locale.word); } +static ssize_t +sysfs_max_sectors_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct Scsi_Host *host = class_to_shost(container_of(kobj, + struct class_device, kobj)); + struct megasas_instance *instance = + (struct megasas_instance *)host->hostdata; + + count = sprintf(buf,"%u\n", instance->max_sectors_per_req); + + return count+1; +} + +static struct bin_attribute sysfs_max_sectors_attr = { + .attr = { + .name = "max_sectors", + .mode = S_IRUSR|S_IRGRP|S_IROTH, + .owner = THIS_MODULE, + }, + .size = 7, + .read = sysfs_max_sectors_read, +}; + /** * megasas_io_attach - Attaches this driver to SCSI mid-layer * @instance: Adapter soft state @@ -2311,17 +5001,48 @@ static int megasas_start_aen(struct mega static int megasas_io_attach(struct megasas_instance *instance) { struct Scsi_Host *host = instance->host; + u32 error; /* * Export parameters required by SCSI mid-layer */ host->irq = instance->pdev->irq; host->unique_id = instance->unique_id; - host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS; + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + host->can_queue = + instance->max_fw_cmds - MEGASAS_SKINNY_INT_CMDS; + } else + host->can_queue = + instance->max_fw_cmds - MEGASAS_INT_CMDS; host->this_id = instance->init_id; host->sg_tablesize = instance->max_num_sge; + + if (instance->fw_support_ieee) + instance->max_sectors_per_req = MEGASAS_MAX_SECTORS_IEEE; + /* + * Check if the module parameter value for max_sectors can be used + */ + if (max_sectors && max_sectors < instance->max_sectors_per_req) + instance->max_sectors_per_req = max_sectors; + else { + if (max_sectors) { + if (((instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS1078GEN2) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0079GEN2)) && + (max_sectors <= MEGASAS_MAX_SECTORS)) { + instance->max_sectors_per_req = max_sectors; + } else { + printk(KERN_INFO "megasas: max_sectors should be > 0 and" + "<= %d (or < 1MB for GEN2 controller)\n", + instance->max_sectors_per_req); + } + } + } + host->max_sectors = instance->max_sectors_per_req; - host->cmd_per_lun = 128; + host->cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN; host->max_channel = MEGASAS_MAX_CHANNELS - 1; host->max_id = MEGASAS_MAX_DEV_PER_CHANNEL; host->max_lun = MEGASAS_MAX_LUN; @@ -2335,11 +5056,48 @@ static int megasas_io_attach(struct mega return -ENODEV; } + /* + * Create sysfs entries for module paramaters + */ + error = sysfs_create_bin_file(&instance->host->shost_classdev.kobj, + &sysfs_max_sectors_attr); + if (error) { + printk(KERN_INFO "megasas: Error in creating the sysfs entry max_sectors.\n"); + goto out_remove_host; + } + /* * Trigger SCSI to scan our drives */ scsi_scan_host(host); return 0; + +out_remove_host: + scsi_remove_host(host); + return error; + +} + +static int +megasas_set_dma_mask(struct pci_dev *pdev) +{ + /* + * All our contollers are capable of performing 64-bit DMA + */ + if (IS_DMA64) { + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) { + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) + goto fail_set_dma_mask; + } + } else { + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) + goto fail_set_dma_mask; + } + return 0; + +fail_set_dma_mask: + return 1; } /** @@ -2375,19 +5133,8 @@ megasas_probe_one(struct pci_dev *pdev, pci_set_master(pdev); - /* - * All our contollers are capable of performing 64-bit DMA - */ - if (IS_DMA64) { - if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) { - - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) - goto fail_set_dma_mask; - } - } else { - if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) - goto fail_set_dma_mask; - } + if (megasas_set_dma_mask(pdev)) + goto fail_set_dma_mask; host = scsi_host_alloc(&megasas_template, sizeof(struct megasas_instance)); @@ -2399,6 +5146,7 @@ megasas_probe_one(struct pci_dev *pdev, instance = (struct megasas_instance *)host->hostdata; memset(instance, 0, sizeof(*instance)); + atomic_set( &instance->fw_reset_no_pci_access, 0 ); instance->producer = pci_alloc_consistent(pdev, sizeof(u32), &instance->producer_h); @@ -2413,6 +5161,11 @@ megasas_probe_one(struct pci_dev *pdev, *instance->producer = 0; *instance->consumer = 0; + instance->flag_ieee = 0; + instance->ev = NULL; + instance->issuepend_done = 1; + instance->adprecovery = MEGASAS_HBA_OPERATIONAL; + megasas_poll_wait_aen = 0; instance->evt_detail = pci_alloc_consistent(pdev, sizeof(struct @@ -2429,6 +5182,7 @@ megasas_probe_one(struct pci_dev *pdev, * Initialize locks and queues */ INIT_LIST_HEAD(&instance->cmd_pool); + INIT_LIST_HEAD(&instance->internal_reset_pending_q); atomic_set(&instance->fw_outstanding,0); @@ -2436,9 +5190,12 @@ megasas_probe_one(struct pci_dev *pdev, init_waitqueue_head(&instance->abort_cmd_wait_q); spin_lock_init(&instance->cmd_pool_lock); + spin_lock_init(&instance->hba_lock); + + spin_lock_init(&instance->completion_lock); + spin_lock_init(&poll_aen_lock); - sema_init(&instance->aen_mutex, 1); - sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); + mutex_init(&instance->aen_mutex); /* * Initialize PCI related and misc parameters @@ -2448,9 +5205,20 @@ megasas_probe_one(struct pci_dev *pdev, instance->unique_id = pdev->bus->number << 8 | pdev->devfn; instance->init_id = MEGASAS_DEFAULT_INIT_ID; + if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + instance->flag_ieee = 1; + sema_init(&instance->ioctl_sem, MEGASAS_SKINNY_INT_CMDS); + } else + sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); + megasas_dbg_lvl = 0; instance->flag = 0; + instance->unload = 1; instance->last_time = 0; + instance->disableOnlineCtrlReset = 1; + + INIT_WORK(&instance->work_init, process_fw_state_change_wq); /* * Initialize MFI Firmware @@ -2495,6 +5263,7 @@ megasas_probe_one(struct pci_dev *pdev, if (megasas_io_attach(instance)) goto fail_io_attach; + instance->unload = 0; return 0; fail_start_aen: @@ -2541,83 +5310,266 @@ static void megasas_flush_cache(struct m struct megasas_cmd *cmd; struct megasas_dcmd_frame *dcmd; - cmd = megasas_get_cmd(instance); + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) + return; + + cmd = megasas_get_cmd(instance); + + if (!cmd) + return; + + dcmd = &cmd->frame->dcmd; + + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0x0; + dcmd->sge_count = 0; + dcmd->flags = MFI_FRAME_DIR_NONE; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = 0; + dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH; + dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; + + megasas_issue_blocked_cmd(instance, cmd); + + megasas_return_cmd(instance, cmd); + + return; +} + +/** + * megasas_shutdown_controller - Instructs FW to shutdown the controller + * @instance: Adapter soft state + * @opcode: Shutdown/Hibernate + */ +static void megasas_shutdown_controller(struct megasas_instance *instance, + u32 opcode) +{ + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) + return; + + cmd = megasas_get_cmd(instance); + + if (!cmd) + return; + + if (instance->aen_cmd) + megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd); + + dcmd = &cmd->frame->dcmd; + + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0x0; + dcmd->sge_count = 0; + dcmd->flags = MFI_FRAME_DIR_NONE; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = 0; + dcmd->opcode = opcode; + + megasas_issue_blocked_cmd(instance, cmd); + + megasas_return_cmd(instance, cmd); + + return; +} + +#ifdef CONFIG_PM +/** + * megasas_suspend - driver suspend entry point + * @pdev: PCI device structure + * @state: PCI power state to suspend routine + */ +static int +megasas_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct Scsi_Host *host; + struct megasas_instance *instance; + + instance = pci_get_drvdata(pdev); + host = instance->host; + instance->unload = 1; + + if (poll_mode_io) + del_timer_sync(&instance->io_completion_timer); + + megasas_flush_cache(instance); + megasas_shutdown_controller(instance, MR_DCMD_HIBERNATE_SHUTDOWN); + + /* cancel the delayed work if this work still in queue */ + if (instance->ev != NULL) { + struct megasas_aen_event *ev = instance->ev; + cancel_delayed_work( + (struct delayed_work *)&ev->hotplug_work); + flush_scheduled_work(); + instance->ev = NULL; + } + + tasklet_kill(&instance->isr_tasklet); + + pci_set_drvdata(instance->pdev, instance); + instance->instancet->disable_intr(instance->reg_set); + free_irq(instance->pdev->irq, instance); + + pci_save_state(pdev); + pci_disable_device(pdev); + + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +/** + * megasas_resume- driver resume entry point + * @pdev: PCI device structure + */ +static int +megasas_resume(struct pci_dev *pdev) +{ + int rval; + struct Scsi_Host *host; + struct megasas_instance *instance; + + instance = pci_get_drvdata(pdev); + host = instance->host; + pci_set_power_state(pdev, PCI_D0); + pci_enable_wake(pdev, PCI_D0, 0); + pci_restore_state(pdev); + + /* + * PCI prepping: enable device set bus mastering and dma mask + */ + rval = pci_enable_device(pdev); + + if (rval) { + printk(KERN_ERR "megasas: Enable device failed\n"); + return rval; + } + + pci_set_master(pdev); + + if (megasas_set_dma_mask(pdev)) + goto fail_set_dma_mask; - if (!cmd) - return; + /* + * Initialize MFI Firmware + */ - dcmd = &cmd->frame->dcmd; + *instance->producer = 0; + *instance->consumer = 0; - memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + atomic_set(&instance->fw_outstanding, 0); - dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0x0; - dcmd->sge_count = 0; - dcmd->flags = MFI_FRAME_DIR_NONE; - dcmd->timeout = 0; - dcmd->data_xfer_len = 0; - dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH; - dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; + /* + * We expect the FW state to be READY + */ + if (megasas_transition_to_ready(instance)) + goto fail_ready_state; - megasas_issue_blocked_cmd(instance, cmd); + if ( megasas_check_cpx_support( instance ) == 0 ){ + if ( megasas_send_cpx_queue_data( instance ) ){ + printk("megasas: Sending cpx queue data to FW failed.\n"); + megasas_remove_cpx(instance); + }else + instance->cpx_request_queue->consumer_idx = instance->cpx_request_queue->producer_idx = 0; + } - megasas_return_cmd(instance, cmd); + if (megasas_issue_init_mfi(instance)) + goto fail_init_mfi; - return; -} + tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc, + (unsigned long)instance); -/** - * megasas_shutdown_controller - Instructs FW to shutdown the controller - * @instance: Adapter soft state - */ -static void megasas_shutdown_controller(struct megasas_instance *instance) -{ - struct megasas_cmd *cmd; - struct megasas_dcmd_frame *dcmd; + /* + * Register IRQ + */ + if (request_irq(pdev->irq, megasas_isr, IRQF_SHARED, + "megasas", instance)) { + printk(KERN_ERR "megasas: Failed to register IRQ\n"); + goto fail_irq; + } - cmd = megasas_get_cmd(instance); + instance->instancet->enable_intr(instance->reg_set); - if (!cmd) - return; + /* + * Initiate AEN (Asynchronous Event Notification) + */ + if (megasas_start_aen(instance)) + printk(KERN_ERR "megasas: Start AEN failed\n"); - if (instance->aen_cmd) - megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd); + /* Initialize the cmd completion timer */ + if (poll_mode_io) + megasas_start_timer(instance, &instance->io_completion_timer, + megasas_io_completion_timer, + MEGASAS_COMPLETION_TIMER_INTERVAL); + instance->unload = 0; - dcmd = &cmd->frame->dcmd; + return 0; - memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); +fail_irq: +fail_init_mfi: + if (instance->evt_detail) + pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), + instance->evt_detail, + instance->evt_detail_h); - dcmd->cmd = MFI_CMD_DCMD; - dcmd->cmd_status = 0x0; - dcmd->sge_count = 0; - dcmd->flags = MFI_FRAME_DIR_NONE; - dcmd->timeout = 0; - dcmd->data_xfer_len = 0; - dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN; + if (instance->producer) + pci_free_consistent(pdev, sizeof(u32), instance->producer, + instance->producer_h); + if (instance->consumer) + pci_free_consistent(pdev, sizeof(u32), instance->consumer, + instance->consumer_h); + scsi_host_put(host); - megasas_issue_blocked_cmd(instance, cmd); +fail_set_dma_mask: +fail_ready_state: - megasas_return_cmd(instance, cmd); + pci_disable_device(pdev); - return; + return -ENODEV; } +#else +#define megasas_suspend NULL +#define megasas_resume NULL +#endif /** * megasas_detach_one - PCI hot"un"plug entry point * @pdev: PCI device structure */ -static void megasas_detach_one(struct pci_dev *pdev) +static void __devexit megasas_detach_one(struct pci_dev *pdev) { int i; struct Scsi_Host *host; struct megasas_instance *instance; instance = pci_get_drvdata(pdev); + instance->unload = 1; host = instance->host; + if (poll_mode_io) + del_timer_sync(&instance->io_completion_timer); + scsi_remove_host(instance->host); megasas_flush_cache(instance); - megasas_shutdown_controller(instance); + megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); + + /* cancel the delayed work if this work still in queue*/ + if (instance->ev != NULL) { + struct megasas_aen_event *ev = instance->ev; + cancel_delayed_work( + (struct delayed_work *)&ev->hotplug_work); + flush_scheduled_work(); + instance->ev = NULL; + } + tasklet_kill(&instance->isr_tasklet); /* @@ -2666,7 +5618,9 @@ static void megasas_detach_one(struct pc static void megasas_shutdown(struct pci_dev *pdev) { struct megasas_instance *instance = pci_get_drvdata(pdev); + instance->unload = 1; megasas_flush_cache(instance); + megasas_shutdown_controller(instance, MR_DCMD_CTRL_SHUTDOWN); } /** @@ -2722,6 +5676,23 @@ static int megasas_mgmt_fasync(int fd, s } /** + * megasas_mgmt_poll - char node "poll" entry point + * */ +static unsigned int megasas_mgmt_poll(struct file *file, poll_table *wait) +{ + unsigned int mask; + unsigned long flags; + poll_wait(file, &megasas_poll_wait, wait); + spin_lock_irqsave(&poll_aen_lock, flags); + if (megasas_poll_wait_aen) + mask = (POLLIN | POLLRDNORM); + else + mask = 0; + spin_unlock_irqrestore(&poll_aen_lock, flags); + return mask; +} + +/** * megasas_mgmt_fw_ioctl - Issues management ioctls to FW * @instance: Adapter soft state * @argp: User's ioctl packet @@ -2738,7 +5709,7 @@ megasas_mgmt_fw_ioctl(struct megasas_ins int error = 0, i; void *sense = NULL; dma_addr_t sense_handle; - u32 *sense_ptr; + unsigned long *sense_ptr; memset(kbuff_arr, 0, sizeof(kbuff_arr)); @@ -2762,6 +5733,7 @@ megasas_mgmt_fw_ioctl(struct megasas_ins */ memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); cmd->frame->hdr.context = cmd->index; + cmd->frame->hdr.pad_0 = 0; /* * The management interface between applications and the fw uses @@ -2815,7 +5787,7 @@ megasas_mgmt_fw_ioctl(struct megasas_ins } sense_ptr = - (u32 *) ((unsigned long)cmd->frame + ioc->sense_off); + (unsigned long *) ((unsigned long)cmd->frame + ioc->sense_off); *sense_ptr = sense_handle; } @@ -2843,14 +5815,16 @@ megasas_mgmt_fw_ioctl(struct megasas_ins */ if (ioc->sense_len) { /* - * sense_ptr points to the location that has the user + * sense_buff points to the location that has the user * sense buffer address */ - sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw + - ioc->sense_off); + sense_ptr = (unsigned long *) ((unsigned long)ioc->frame.raw + + ioc->sense_off); if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)), sense, ioc->sense_len)) { + printk(KERN_ERR "megasas: Failed to copy out to user " + "sense data\n"); error = -EFAULT; goto out; } @@ -2881,20 +5855,6 @@ megasas_mgmt_fw_ioctl(struct megasas_ins return error; } -static struct megasas_instance *megasas_lookup_instance(u16 host_no) -{ - int i; - - for (i = 0; i < megasas_mgmt_info.max_index; i++) { - - if ((megasas_mgmt_info.instance[i]) && - (megasas_mgmt_info.instance[i]->host->host_no == host_no)) - return megasas_mgmt_info.instance[i]; - } - - return NULL; -} - static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) { struct megasas_iocpacket __user *user_ioc = @@ -2902,6 +5862,9 @@ static int megasas_mgmt_ioctl_fw(struct struct megasas_iocpacket *ioc; struct megasas_instance *instance; int error; + int i; + unsigned long flags; + u32 wait_time = MEGASAS_RESET_WAIT_TIME; ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); if (!ioc) @@ -2918,6 +5881,17 @@ static int megasas_mgmt_ioctl_fw(struct goto out_kfree_ioc; } + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { + printk("Controller in crit error\n"); + error = -ENODEV; + goto out_kfree_ioc; + } + + if (instance->unload == 1) { + error = -ENODEV; + goto out_kfree_ioc; + } + /* * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds */ @@ -2925,6 +5899,35 @@ static int megasas_mgmt_ioctl_fw(struct error = -ERESTARTSYS; goto out_kfree_ioc; } + + // If HBA is undergoing a reset recovery, wait for that to complete + // before issuing this command + + for (i = 0; i < wait_time; i++) { + + spin_lock_irqsave(&instance->hba_lock, flags); + if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) { + spin_unlock_irqrestore(&instance->hba_lock, flags); + break; + } + spin_unlock_irqrestore(&instance->hba_lock, flags); + + if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { + printk(KERN_NOTICE "megasas: waiting for controller reset to finish\n"); + } + + msleep(1000); + } + + spin_lock_irqsave(&instance->hba_lock, flags); + if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) { + spin_unlock_irqrestore(&instance->hba_lock, flags); + printk("megaraid_sas: %s timed out while waiting for HBA to recover.\n", __FUNCTION__); + error = -ENODEV; + goto out_kfree_ioc; + } + spin_unlock_irqrestore(&instance->hba_lock, flags); + error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc); up(&instance->ioctl_sem); @@ -2938,6 +5941,9 @@ static int megasas_mgmt_ioctl_aen(struct struct megasas_instance *instance; struct megasas_aen aen; int error; + int i; + unsigned long flags; + u32 wait_time = MEGASAS_RESET_WAIT_TIME; if (file->private_data != file) { printk(KERN_DEBUG "megasas: fasync_helper was not " @@ -2953,10 +5959,41 @@ static int megasas_mgmt_ioctl_aen(struct if (!instance) return -ENODEV; - down(&instance->aen_mutex); + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { + return -ENODEV; + } + + if (instance->unload == 1) { + return -ENODEV; + } + for (i = 0; i < wait_time; i++) { + + spin_lock_irqsave(&instance->hba_lock, flags); + if (instance->adprecovery == MEGASAS_HBA_OPERATIONAL) { + spin_unlock_irqrestore(&instance->hba_lock, flags); + break; + } + spin_unlock_irqrestore(&instance->hba_lock, flags); + + if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { + printk(KERN_NOTICE "megasas: waiting for controller reset to finish\n"); + } + + msleep(1000); + } + + spin_lock_irqsave(&instance->hba_lock, flags); + if (instance->adprecovery != MEGASAS_HBA_OPERATIONAL) { + spin_unlock_irqrestore(&instance->hba_lock, flags); + printk("megaraid_sas: %s timed out while waiting for HBA to recover.\n", __FUNCTION__); + return -ENODEV; + } + spin_unlock_irqrestore(&instance->hba_lock, flags); + + mutex_lock(&instance->aen_mutex); error = megasas_register_aen(instance, aen.seq_num, aen.class_locale_word); - up(&instance->aen_mutex); + mutex_unlock(&instance->aen_mutex); return error; } @@ -2986,6 +6023,8 @@ static int megasas_mgmt_compat_ioctl_fw( compat_alloc_user_space(sizeof(struct megasas_iocpacket)); int i; int error = 0; + compat_uptr_t ptr; + u8 *raw_ptr; if (clear_user(ioc, sizeof(*ioc))) return -EFAULT; @@ -2998,9 +6037,14 @@ static int megasas_mgmt_compat_ioctl_fw( copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32))) return -EFAULT; - for (i = 0; i < MAX_IOCTL_SGE; i++) { - compat_uptr_t ptr; + if (ioc->sense_len) { + raw_ptr = ioc->frame.raw + ioc->sense_off; + if (get_user(ptr, (compat_uptr_t *)raw_ptr) || + put_user(ptr, (unsigned long *)raw_ptr)) + return -EFAULT; + } + for (i = 0; i < MAX_IOCTL_SGE; i++) { if (get_user(ptr, &cioc->sgl[i].iov_base) || put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) || copy_in_user(&ioc->sgl[i].iov_len, @@ -3042,6 +6086,7 @@ static const struct file_operations mega .release = megasas_mgmt_release, .fasync = megasas_mgmt_fasync, .unlocked_ioctl = megasas_mgmt_ioctl, + .poll = megasas_mgmt_poll, #ifdef CONFIG_COMPAT .compat_ioctl = megasas_mgmt_compat_ioctl, #endif @@ -3056,6 +6101,8 @@ static struct pci_driver megasas_pci_dri .id_table = megasas_pci_table, .probe = megasas_probe_one, .remove = __devexit_p(megasas_detach_one), + .suspend = megasas_suspend, + .resume = megasas_resume, .shutdown = megasas_shutdown, }; @@ -3081,9 +6128,27 @@ static DRIVER_ATTR(release_date, S_IRUGO NULL); static ssize_t +megasas_sysfs_show_support_poll_for_event(struct device_driver *dd, char *buf) +{ + return sprintf(buf, "%u\n", support_poll_for_event); +} + +static DRIVER_ATTR(support_poll_for_event, S_IRUGO, + megasas_sysfs_show_support_poll_for_event, NULL); + +static ssize_t +megasas_sysfs_show_support_device_change(struct device_driver *dd, char *buf) +{ + return sprintf(buf, "%u\n", support_device_change); +} + +static DRIVER_ATTR(support_device_change, S_IRUGO, + megasas_sysfs_show_support_device_change, NULL); + +static ssize_t megasas_sysfs_show_dbg_lvl(struct device_driver *dd, char *buf) { - return sprintf(buf,"%u",megasas_dbg_lvl); + return sprintf(buf, "%u\n", megasas_dbg_lvl); } static ssize_t @@ -3097,8 +6162,262 @@ megasas_sysfs_set_dbg_lvl(struct device_ return retval; } -static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUGO, megasas_sysfs_show_dbg_lvl, - megasas_sysfs_set_dbg_lvl); +static DRIVER_ATTR(dbg_lvl, S_IRUGO|S_IWUSR, megasas_sysfs_show_dbg_lvl, + megasas_sysfs_set_dbg_lvl); + +static ssize_t +megasas_sysfs_show_poll_mode_io(struct device_driver *dd, char *buf) +{ + return sprintf(buf, "%u\n", poll_mode_io); +} + +static ssize_t +megasas_sysfs_set_poll_mode_io(struct device_driver *dd, + const char *buf, size_t count) +{ + int retval = count; + int tmp = poll_mode_io; + int i; + struct megasas_instance *instance; + + if (sscanf(buf, "%u", &poll_mode_io) < 1) { + printk(KERN_ERR "megasas: could not set poll_mode_io\n"); + retval = -EINVAL; + } + + /* + * Check if poll_mode_io is already set or is same as previous value + */ + if ((tmp && poll_mode_io) || (tmp == poll_mode_io)) + goto out; + + if (poll_mode_io) { + /* + * Start timers for all adapters + */ + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + instance = megasas_mgmt_info.instance[i]; + if (instance) { + megasas_start_timer(instance, + &instance->io_completion_timer, + megasas_io_completion_timer, + MEGASAS_COMPLETION_TIMER_INTERVAL); + } + } + } else { + /* + * Delete timers for all adapters + */ + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + instance = megasas_mgmt_info.instance[i]; + if (instance) + del_timer_sync(&instance->io_completion_timer); + } + } + +out: + return retval; +} + +static void +megasas_aen_polling(struct work_struct *work) +{ + struct Scsi_Host *host; + struct scsi_device *sdev1; + u16 pd_index = 0; + u16 ld_index = 0; + + struct megasas_aen_event *ev = + container_of(work, struct megasas_aen_event, hotplug_work); + struct megasas_instance *instance = ev->instance; + union megasas_evt_class_locale class_locale; + int i, j, doscan = 0; + u32 seq_num; + int error; + + if (!instance) { + printk(KERN_ERR "invalid instance!\n"); + kfree(ev); + return; + } + instance->ev = NULL; + host = instance->host; + if (instance->evt_detail) { + + switch (instance->evt_detail->code) { + case MR_EVT_PD_INSERTED: + if(megasas_get_pd_list(instance) == 0) { + + for (i=0; i < MEGASAS_MAX_PD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + pd_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { + if (!sdev1) { + scsi_add_device(host, i, j, 0); + } + } + if (sdev1) { + scsi_device_put(sdev1); + } + + } + } + } + doscan = 0; + break; + + case MR_EVT_PD_REMOVED: + if(megasas_get_pd_list(instance) == 0) { + megasas_get_pd_list(instance); + for (i=0; i < MEGASAS_MAX_PD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + pd_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) { + if (sdev1) { + scsi_device_put(sdev1); + } + } else { + if (sdev1) { + scsi_remove_device(sdev1); + scsi_device_put(sdev1); + } + } + } + + } + } + doscan = 0; + break; + + case MR_EVT_LD_OFFLINE: + case MR_EVT_CFG_CLEARED: + case MR_EVT_LD_DELETED: + megasas_get_ld_list(instance); + for (i=0; i < MEGASAS_MAX_LD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, i+MEGASAS_MAX_LD_CHANNELS, j, 0); + if (instance->ld_ids[ld_index] != 0xff) { + if (sdev1) { + scsi_device_put(sdev1); + } + + } else { + if (sdev1) { + scsi_remove_device(sdev1); + scsi_device_put(sdev1); + } + } + } + } + doscan = 0; + break; + case MR_EVT_LD_CREATED: + megasas_get_ld_list(instance); + for (i=0; i < MEGASAS_MAX_LD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, i+MEGASAS_MAX_LD_CHANNELS, j, 0); + if (instance->ld_ids[ld_index] != 0xff) { + if (!sdev1) { + scsi_add_device(host, i+2, j, 0); + } + } + if (sdev1) { + scsi_device_put(sdev1); + } + } + } + doscan = 0; + break; + case MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED: + case MR_EVT_FOREIGN_CFG_IMPORTED: + case MR_EVT_LD_STATE_CHANGE: + doscan = 1; + break; + default: + doscan = 0; + break; + } + } else { + printk(KERN_ERR "invalid evt_detail!\n"); + kfree(ev); + return; + } + + if (doscan) { + printk(KERN_INFO "scanning ...\n"); + megasas_get_pd_list(instance); + for (i = 0; i < MEGASAS_MAX_PD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + pd_index = i*MEGASAS_MAX_DEV_PER_CHANNEL + j; + sdev1 = scsi_device_lookup(host, i, j, 0); + if (instance->pd_list[pd_index].driveState == + MR_PD_STATE_SYSTEM) { + if (!sdev1) { + scsi_add_device(host, i, j, 0); + } else { + scsi_device_put(sdev1); + } + } else { + if (sdev1) { + scsi_remove_device(sdev1); + scsi_device_put(sdev1); + } + } + } + } + + megasas_get_ld_list(instance); + for (i=0; i < MEGASAS_MAX_LD_CHANNELS; i++) { + for (j = 0; j < MEGASAS_MAX_DEV_PER_CHANNEL; j++) { + ld_index = (i * MEGASAS_MAX_DEV_PER_CHANNEL) + j; + sdev1 = scsi_device_lookup(host, i+MEGASAS_MAX_LD_CHANNELS, j, 0); + if (instance->ld_ids[ld_index] != 0xff) { + if (!sdev1) { + scsi_add_device(host, i+2, j, 0); + } else { + scsi_device_put(sdev1); + } + } else { + if (sdev1) { + scsi_remove_device(sdev1); + scsi_device_put(sdev1); + } + } + } + } + } + + seq_num = instance->evt_detail->seq_num + 1; + + /* Register AEN with FW for latest sequence number plus 1 */ + class_locale.members.reserved = 0; + class_locale.members.locale = MR_EVT_LOCALE_ALL; + class_locale.members.class = MR_EVT_CLASS_DEBUG; + + if ( instance->aen_cmd != NULL ) { + kfree(ev); + return ; + } + + mutex_lock(&instance->aen_mutex); + error = megasas_register_aen(instance, seq_num, + class_locale.word); + mutex_unlock(&instance->aen_mutex); + + if (error) + printk(KERN_ERR "register aen failed error %x\n", error); + + kfree(ev); +} + + +static DRIVER_ATTR(poll_mode_io, S_IRUGO|S_IWUSR, + megasas_sysfs_show_poll_mode_io, + megasas_sysfs_set_poll_mode_io); /** * megasas_init - Driver load entry point @@ -3113,6 +6432,9 @@ static int __init megasas_init(void) printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION, MEGASAS_EXT_VERSION); + support_poll_for_event = 2; + support_device_change = 1; + memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info)); /* @@ -3145,15 +6467,41 @@ static int __init megasas_init(void) &driver_attr_release_date); if (rval) goto err_dcf_rel_date; + + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_support_poll_for_event); + if (rval) + goto err_dcf_support_poll_for_event; + rval = driver_create_file(&megasas_pci_driver.driver, &driver_attr_dbg_lvl); if (rval) goto err_dcf_dbg_lvl; + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_poll_mode_io); + if (rval) + goto err_dcf_poll_mode_io; + rval = driver_create_file(&megasas_pci_driver.driver, + &driver_attr_support_device_change); + if (rval) + goto err_dcf_support_device_change; return rval; +err_dcf_support_device_change: + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_poll_mode_io); + +err_dcf_poll_mode_io: + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_dbg_lvl); err_dcf_dbg_lvl: driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_support_poll_for_event); + +err_dcf_support_poll_for_event: + driver_remove_file(&megasas_pci_driver.driver, &driver_attr_release_date); + err_dcf_rel_date: driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); err_dcf_attr_ver: @@ -3169,8 +6517,14 @@ err_pcidrv: static void __exit megasas_exit(void) { driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_poll_mode_io); + driver_remove_file(&megasas_pci_driver.driver, &driver_attr_dbg_lvl); driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_support_poll_for_event); + driver_remove_file(&megasas_pci_driver.driver, + &driver_attr_support_device_change); + driver_remove_file(&megasas_pci_driver.driver, &driver_attr_release_date); driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); diff -Nurp linux-2.6.22-810/drivers/scsi/megaraid/megaraid_sas.h linux-2.6.22-820/drivers/scsi/megaraid/megaraid_sas.h --- linux-2.6.22-810/drivers/scsi/megaraid/megaraid_sas.h 2007-07-08 19:32:17.000000000 -0400 +++ linux-2.6.22-820/drivers/scsi/megaraid/megaraid_sas.h 2010-12-03 10:42:20.000000000 -0500 @@ -18,15 +18,21 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.03.10-rc5" -#define MEGASAS_RELDATE "May 17, 2007" -#define MEGASAS_EXT_VERSION "Thu May 17 10:09:32 PDT 2007" +#define MEGASAS_VERSION "00.00.04.33" +#define MEGASAS_RELDATE "July 20, 2010" +#define MEGASAS_EXT_VERSION "Tue Jul 20 12:24:32 EDT 2010" + /* * Device IDs */ #define PCI_DEVICE_ID_LSI_SAS1078R 0x0060 +#define PCI_DEVICE_ID_LSI_SAS1078DE 0x007C #define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 +#define PCI_DEVICE_ID_LSI_SAS1078GEN2 0x0078 +#define PCI_DEVICE_ID_LSI_SAS0079GEN2 0x0079 +#define PCI_DEVICE_ID_LSI_SAS0073SKINNY 0x0073 +#define PCI_DEVICE_ID_LSI_SAS0071SKINNY 0x0071 /* * ===================================== @@ -55,6 +61,7 @@ #define MFI_STATE_READY 0xB0000000 #define MFI_STATE_OPERATIONAL 0xC0000000 #define MFI_STATE_FAULT 0xF0000000 +#define MFI_RESET_REQUIRED 0x00000001 #define MEGAMFI_FRAME_SIZE 64 @@ -68,6 +75,13 @@ * HOTPLUG : Resume from Hotplug * MFI_STOP_ADP : Send signal to FW to stop processing */ + +#define WRITE_SEQUENCE_OFFSET (0x0000000FC) // I20 +#define HOST_DIAGNOSTIC_OFFSET (0x000000F8) // I20 +#define DIAG_WRITE_ENABLE (0x00000080) +#define DIAG_RESET_ADAPTER (0x00000004) + +#define MFI_ADP_RESET 0x00000040 #define MFI_INIT_ABORT 0x00000001 #define MFI_INIT_READY 0x00000002 #define MFI_INIT_MFIMODE 0x00000004 @@ -91,6 +105,7 @@ #define MFI_FRAME_DIR_WRITE 0x0008 #define MFI_FRAME_DIR_READ 0x0010 #define MFI_FRAME_DIR_BOTH 0x0018 +#define MFI_FRAME_IEEE 0x0020 /* * Definition for cmd_status @@ -111,12 +126,14 @@ #define MFI_CMD_STP 0x08 #define MR_DCMD_CTRL_GET_INFO 0x01010000 +#define MR_DCMD_LD_GET_LIST 0x03010000 #define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000 #define MR_FLUSH_CTRL_CACHE 0x01 #define MR_FLUSH_DISK_CACHE 0x02 #define MR_DCMD_CTRL_SHUTDOWN 0x01050000 +#define MR_DCMD_HIBERNATE_SHUTDOWN 0x01060000 #define MR_ENABLE_DRIVE_SPINDOWN 0x01 #define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100 @@ -127,6 +144,29 @@ #define MR_DCMD_CLUSTER 0x08000000 #define MR_DCMD_CLUSTER_RESET_ALL 0x08010100 #define MR_DCMD_CLUSTER_RESET_LD 0x08010200 +#define MR_DCMD_PD_LIST_QUERY 0x02010100 + +#define MR_DCMD_CTRL_MISC_CPX 0x0100e200 +#define MR_DCMD_CTRL_MISC_CPX_INIT_DATA_GET 0x0100e201 +#define MR_DCMD_CTRL_MISC_CPX_QUEUE_DATA 0x0100e202 +#define MR_DCMD_CTRL_MISC_CPX_UNREGISTER 0x0100e203 +#define MAX_MR_ROW_SIZE 32 +#define MR_CPX_DIR_WRITE 1 +#define MR_CPX_DIR_READ 0 +#define MR_CPX_VERSION 1 + +#define MR_EVT_CFG_CLEARED 0x0004 + +#define MR_EVT_LD_STATE_CHANGE 0x0051 +#define MR_EVT_PD_INSERTED 0x005b +#define MR_EVT_PD_REMOVED 0x0070 +#define MR_EVT_LD_CREATED 0x008a +#define MR_EVT_LD_DELETED 0x008b +#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db +#define MR_EVT_LD_OFFLINE 0x00fc +#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 +#define MAX_LOGICAL_DRIVES 64 + /* * MFI command completion codes @@ -247,8 +287,128 @@ enum MR_EVT_ARGS { MR_EVT_ARGS_STR, MR_EVT_ARGS_TIME, MR_EVT_ARGS_ECC, + MR_EVT_ARGS_LD_PROP, + MR_EVT_ARGS_PD_SPARE, + MR_EVT_ARGS_PD_INDEX, + MR_EVT_ARGS_DIAG_PASS, + MR_EVT_ARGS_DIAG_FAIL, + MR_EVT_ARGS_PD_LBA_LBA, + MR_EVT_ARGS_PORT_PHY, + MR_EVT_ARGS_PD_MISSING, + MR_EVT_ARGS_PD_ADDRESS, + MR_EVT_ARGS_BITMAP, + MR_EVT_ARGS_CONNECTOR, + MR_EVT_ARGS_PD_PD, + MR_EVT_ARGS_PD_FRU, + MR_EVT_ARGS_PD_PATHINFO, + MR_EVT_ARGS_PD_POWER_STATE, + MR_EVT_ARGS_GENERIC, + +}; +/* + * define constants for device list query options + */ +enum MR_PD_QUERY_TYPE { + MR_PD_QUERY_TYPE_ALL = 0, + MR_PD_QUERY_TYPE_STATE = 1, + MR_PD_QUERY_TYPE_POWER_STATE = 2, + MR_PD_QUERY_TYPE_MEDIA_TYPE = 3, + MR_PD_QUERY_TYPE_SPEED = 4, + MR_PD_QUERY_TYPE_EXPOSED_TO_HOST = 5, +} __attribute__ ((packed)); + +#define MR_EVT_CFG_CLEARED 0x0004 +#define MR_EVT_LD_STATE_CHANGE 0x0051 +#define MR_EVT_PD_INSERTED 0x005b +#define MR_EVT_PD_REMOVED 0x0070 +#define MR_EVT_LD_CREATED 0x008a +#define MR_EVT_LD_DELETED 0x008b +#define MR_EVT_FOREIGN_CFG_IMPORTED 0x00db +#define MR_EVT_LD_OFFLINE 0x00fc +#define MR_EVT_CTRL_HOST_BUS_SCAN_REQUESTED 0x0152 + +enum MR_PD_STATE { + MR_PD_STATE_UNCONFIGURED_GOOD = 0x00, + MR_PD_STATE_UNCONFIGURED_BAD = 0x01, + MR_PD_STATE_HOT_SPARE = 0x02, + MR_PD_STATE_OFFLINE = 0x10, + MR_PD_STATE_FAILED = 0x11, + MR_PD_STATE_REBUILD = 0x14, + MR_PD_STATE_ONLINE = 0x18, + MR_PD_STATE_COPYBACK = 0x20, + MR_PD_STATE_SYSTEM = 0x40, }; + + /* + * defines the physical drive address structure + */ +struct MR_PD_ADDRESS { + u16 deviceId; + u16 enclDeviceId; + + union { + struct { + + u8 enclIndex; + u8 slotNumber; + } mrPdAddress; + struct { + u8 enclPosition; + u8 enclConnectorIndex; + } mrEnclAddress; + }; + u8 scsiDevType; + union { + u8 connectedPortBitmap; + u8 connectedPortNumbers; + + }; + u64 sasAddr[2]; +} __attribute__ ((packed)); + +/* + * defines the physical drive list structure + */ +struct MR_PD_LIST { + u32 size; + u32 count; + struct MR_PD_ADDRESS addr[1]; +} __attribute__ ((packed)); + + +struct megasas_pd_list { + u16 tid; + u8 driveType; + u8 driveState; +} __attribute__ ((packed)); + + /* + * defines the logical drive reference structure + */ +typedef union _MR_LD_REF { // LD reference structure + struct { + u8 targetId; // LD target id (0 to MAX_TARGET_ID) + u8 reserved; // reserved to make in line with MR_PD_REF + u16 seqNum; // Sequence Number + }; + u32 ref; // shorthand reference to full 32-bits +} MR_LD_REF; // 4 bytes + + +/* + * defines the logical drive list structure + */ +struct MR_LD_LIST { + u32 ldCount; // number of LDs + u32 reserved; // pad to 8-byte boundary + struct { + MR_LD_REF ref; // LD reference + u8 state; // current LD state (MR_LD_STATE) + u8 reserved[3]; // pad to 8-byte boundary + u64 size; // LD size + } ldList[MAX_LOGICAL_DRIVES]; +} __attribute__ ((packed)); /* * SAS controller properties @@ -276,7 +436,45 @@ struct megasas_ctrl_prop { u16 ecc_bucket_leak_rate; u8 restore_hotspare_on_insertion; u8 expose_encl_devices; - u8 reserved[38]; + u8 maintainPdFailHistory; + u8 disallowHostRequestReordering; + u8 abortCCOnError; // set TRUE to abort CC on detecting an inconsistency + u8 loadBalanceMode; // load balance mode (MR_LOAD_BALANCE_MODE) + u8 disableAutoDetectBackplane; // 0 - use auto detect logic of backplanes like SGPIO, i2c SEP using h/w mechansim like GPIO pins + // 1 - disable auto detect SGPIO, + // 2 - disable i2c SEP auto detect + // 3 - disable both auto detect + u8 snapVDSpace; // % of source LD to be reserved for a VDs snapshot in snapshot repository, for metadata and user data + // 1=5%, 2=10%, 3=15% and so on + + /* + * Add properties that can be controlled by a bit in the following structure. + */ + struct { + u32 copyBackDisabled : 1; // set TRUE to disable copyBack (0=copback enabled) + u32 SMARTerEnabled : 1; + u32 prCorrectUnconfiguredAreas : 1; + u32 useFdeOnly : 1; + u32 disableNCQ : 1; + u32 SSDSMARTerEnabled : 1; + u32 SSDPatrolReadEnabled : 1; + u32 enableSpinDownUnconfigured : 1; + u32 autoEnhancedImport : 1; + u32 enableSecretKeyControl : 1; + u32 disableOnlineCtrlReset : 1; + u32 allowBootWithPinnedCache : 1; + u32 disableSpinDownHS : 1; + u32 enableJBOD : 1; + u32 reserved :18; + } OnOffProperties; + u8 autoSnapVDSpace; // % of source LD to be reserved for auto snapshot in snapshot repository, for metadata and user data + // 1=5%, 2=10%, 3=15% and so on + u8 viewSpace; // snapshot writeable VIEWs capacity as a % of source LD capacity. 0=READ only + // 1=5%, 2=10%, 3=15% and so on + + u16 spinDownTime; // # of idle minutes before device is spun down (0=use FW defaults) + + u8 reserved[24]; } __attribute__ ((packed)); @@ -536,10 +734,20 @@ struct megasas_ctrl_info { #define MEGASAS_DEFAULT_INIT_ID -1 #define MEGASAS_MAX_LUN 8 #define MEGASAS_MAX_LD 64 +#define MEGASAS_DEFAULT_CMD_PER_LUN 128 +#define MEGASAS_MAX_PD (MEGASAS_MAX_PD_CHANNELS * \ + MEGASAS_MAX_DEV_PER_CHANNEL) +#define MEGASAS_MAX_LD_IDS (MEGASAS_MAX_LD_CHANNELS * \ + MEGASAS_MAX_DEV_PER_CHANNEL) -#define MEGASAS_DBG_LVL 1 +#define MEGASAS_MAX_SECTORS (2*1024) +#define MEGASAS_MAX_SECTORS_IEEE (2*128) +#define MEGASAS_DBG_LVL 1 #define MEGASAS_FW_BUSY 1 +/* Frame Type */ +#define IO_FRAME 0 +#define PTHRU_FRAME 1 /* * When SCSI mid-layer calls driver's reset routine, driver waits for @@ -551,6 +759,7 @@ struct megasas_ctrl_info { #define MEGASAS_RESET_WAIT_TIME 180 #define MEGASAS_INTERNAL_CMD_WAIT_TIME 180 #define MEGASAS_RESET_NOTICE_INTERVAL 5 + #define MEGASAS_IOCTL_CMD 0 #define MEGASAS_DEFAULT_CMD_TIMEOUT 90 @@ -562,6 +771,7 @@ struct megasas_ctrl_info { * is shown below */ #define MEGASAS_INT_CMDS 32 +#define MEGASAS_SKINNY_INT_CMDS 5 /* * FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit @@ -569,10 +779,25 @@ struct megasas_ctrl_info { */ #define IS_DMA64 (sizeof(dma_addr_t) == 8) +#define MFI_XSCALE_OMR0_CHANGE_INTERRUPT 0x00000001 /* MFI state change interrupt */ + +#define MFI_INTR_FLAG_REPLY_MESSAGE 0x00000001 +#define MFI_INTR_FLAG_FIRMWARE_STATE_CHANGE 0x00000002 +#define MFI_G2_OUTBOUND_DOORBELL_CHANGE_INTERRUPT 0x00000004 /* MFI state change interrrupt */ + #define MFI_OB_INTR_STATUS_MASK 0x00000002 -#define MFI_POLL_TIMEOUT_SECS 10 +#define MFI_POLL_TIMEOUT_SECS 60 +#define MEGASAS_COMPLETION_TIMER_INTERVAL (HZ/10) #define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000 +#define MFI_REPLY_GEN2_MESSAGE_INTERRUPT 0x00000001 +#define MFI_GEN2_ENABLE_INTERRUPT_MASK 0x00000001 +#define MFI_REPLY_SKINNY_MESSAGE_INTERRUPT 0x40000000 +#define MFI_SKINNY_ENABLE_INTERRUPT_MASK (0x00000001) +#define MFI_1068_PCSR_OFFSET 0x84 +#define MFI_1068_FW_HANDSHAKE_OFFSET 0x64 +#define MFI_1068_FW_READY 0xDDDD0000 + /* * register set for both 1068 and 1078 controllers @@ -580,7 +805,11 @@ struct megasas_ctrl_info { */ struct megasas_register_set { - u32 reserved_0[4]; /*0000h*/ + u32 reserved_0; /*0000h*/ + u32 fusion_seq_offset; /*0008h*/ + u32 fusion_host_diag; /*0004h*/ + u32 reserved_01; /*000Ch*/ + u32 inbound_msg_0; /*0010h*/ u32 inbound_msg_1; /*0014h*/ @@ -615,7 +844,10 @@ struct megasas_register_set { u32 inbound_high_queue_port ; /*00C4h*/ u32 reserved_5; /*00C8h*/ - u32 index_registers[820]; /*00CCh*/ + u32 res_6[11]; /*CCh*/ + u32 host_diag; + u32 seq_offset; + u32 index_registers[807]; /*00CCh*/ } __attribute__ ((packed)); @@ -632,11 +864,20 @@ struct megasas_sge64 { u32 length; } __attribute__ ((packed)); + +struct megasas_sge_skinny { + + u64 phys_addr; + u32 length; + u32 flag; + +} __attribute__ ((packed)); union megasas_sgl { struct megasas_sge32 sge32[1]; struct megasas_sge64 sge64[1]; + struct megasas_sge_skinny sge_skinny[1]; } __attribute__ ((packed)); @@ -1050,16 +1291,177 @@ struct megasas_evt_detail { } __attribute__ ((packed)); - struct megasas_instance_template { - void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *); +#define MIN(a,b) ((a)<(b) ? (a):(b)) +typedef void (*XOR_LOW_LEVEL_GEN_FUNC)(u32 **, u32); +typedef u8 (*XOR_LOW_LEVEL_CHECK_FUNC)(u32 **, u32); +/* + * enumerates type of descriptor + */ +typedef enum _MR_CPX_DESCRIPTOR_TYPE { + MR_CPX_DESCRIPTOR_TYPE_COPY = 1, + MR_CPX_DESCRIPTOR_TYPE_XOR = 2 +} MR_CPX_DESCRIPTOR_TYPE; - void (*enable_intr)(struct megasas_register_set __iomem *) ; - void (*disable_intr)(struct megasas_register_set __iomem *); +/* + * status information of copy or xor operation + */ +typedef enum _MR_CPX_STATUS { + MR_CPX_STATUS_SUCCESS = 0, + MR_CPX_STATUS_INCONSISTENT = 1, + MR_CPX_STATUS_FAILURE = 2, +} MR_CPX_STATUS; - int (*clear_intr)(struct megasas_register_set __iomem *); +/* + * define the XOR opcodes + */ +typedef enum _mr_cpx_xor_op { + MR_CPX_XOR_OP_GEN_P = 0x01, // generate P buffer + MR_CPX_XOR_OP_GEN_Q = 0x02, // generate Q buffer + MR_CPX_XOR_OP_GEN_PQ = 0x03, // generate P+Q buffers + MR_CPX_XOR_OP_CHECK_P = 0x11, // check P buffer (and generate if bad) + MR_CPX_XOR_OP_CHECK_Q = 0x12, // check Q buffer (and generate if bad) + MR_CPX_XOR_OP_CHECK_PQ = 0x13, // check P+Q buffers (and generate if bad) +} MR_CPX_XOR_OP; + +#define MR_CPX_XOR_OP_IS_CHECK(xorOp) ((xorOp & 0x10)!=0) // TRUE if operation is a CHECK operation +#define MR_CPX_XOR_OP_IS_GEN(xorOp) (!MR_CPX_XOR_OP_IS_CHECK(xorOp)) // TRUE if operation is a GEN operation +#define MR_CPX_XOR_OP_IS_P(xorOp) ((xorOp & 0x01)!=0) // TRUE if operation is for P or P/Q +#define MR_CPX_XOR_OP_IS_Q(xorOp) ((xorOp & 0x02)!=0) // TRUE if operation is for Q or P/Q +#define MR_CPX_XOR_OP_IS_PQ(xorOp) ((xorOp & 0x03)==3) // TRUE if operation is for P/Q - u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); - }; + +/* + * this data is passed to driver during driver init. + */ +struct mr_cpx_init_data { + u32 cpx_desc_count; // Number of cpx desc required by fw. + u32 size; // size of the buffer + u64 phys_addr_cache_buf; // physical address of cache buffer allocated by pre-boot + +} __attribute__ ((packed)); + +/* + * header passed with each descriptor + */ +struct mr_cpx_header { + u32 context : 24; // context information passed by firmware, to be passed back in response data + u32 type : 4; // type of descriptor + u32 resvd : 4; +} __attribute__ ((packed)); + +/* + * xor descriptor data + */ +struct mr_cpx_xor_descriptor { + struct mr_cpx_header hdr; + MR_CPX_XOR_OP op; // xor operation for gen/check of p/q/p+q + u32 size; // number of bytes to gen/check for this operation + u32 buff_valid_bitmap; // bitmap of valid buffers for input + u8 p_idx; // index of p buffer within list (for p/pq gen/check functions) + u8 q_idx; // index of q buffer within list (for q/pq gen/check functions) + u8 pad[2]; + u32 buff_list[MAX_MR_ROW_SIZE]; // list of buffers for this xor operation (32 bit offset) + u32 mult_list[MAX_MR_ROW_SIZE]; // list of coefficient multipliers for q operations +} __attribute__ ((packed)); + + +/* + * copy buffer for each transfer. each such tranfer between + * user spare host address and firmware allocated cache data. + */ +struct mr_cpx_copy_mr_buffer { + u32 buf; // buffer address/offset + u32 size; // size of copy +} __attribute__ ((packed)); + +/* + * copy descriptor data + */ +struct mr_cpx_copy_descriptor { + struct mr_cpx_header hdr; + u32 mfi_cmd_cxt; // mfi context + u32 total_byte_count; // total byte count for this transfer + u32 host_skip_count; // skip count from starting address of host buffer + u8 dir; // direction of transfer + u8 pad[3]; + struct mr_cpx_copy_mr_buffer copy_buf[MAX_MR_ROW_SIZE]; +} __attribute__ ((packed)) ; + +/* + * users of this interface must allocate memory for the size of + * this structure while allocating memory for descriptors + */ +union mr_cpx_descriptor { + struct mr_cpx_xor_descriptor cpx_xor_desc; + struct mr_cpx_copy_descriptor cpx_copy_desc; + u8 pad[512]; +} __attribute__ ((packed)); + + +/* + * request queue. + * firmware manages producerindex, driver manages consumerindex. + * number of decriptors is kept as variable. driver must use + * max host commands supported for allocation. + */ +struct mr_cpx_request_queue { + u32 consumer_idx; + u32 producer_idx; + union mr_cpx_descriptor cpxdescriptor[1]; // use max host commands +} __attribute__ ((packed)); + + +/* + * response data. this data will be posted by driver after copy/xor + * operation is compete. + */ +union mr_cpx_response_data { + struct { + u32 context : 24; // context + u32 status : 4; // status in the form of cpx_status + u32 type : 4; + } r; + u32 w; +} __attribute__ ((packed)); + + +/* + * response queue. + * driver manages producerindex, firmware manages consumerindex. + * number of decriptors is kept as variable. driver must use + * max host commands supported for allocation. + */ +struct mr_cpx_response_queue { + u32 consumer_idx; + u32 producer_idx; + union mr_cpx_response_data cpx_resp_data[1]; // use max host commands +} __attribute__ ((packed)); + + +/* + * the size of each of the structure within this is determined at run time. + * this structure is for document purpose and shows that the structures + * lay as shown below in memory + */ +struct mr_cpx_queues { + struct mr_cpx_request_queue requestqueue; + struct mr_cpx_response_queue responsequeue; +} __attribute__ ((packed)); + +/* + * driver sends this queue data during mfi init. firmware + * will not use the interface if the versions do not match. + */ +struct mr_cpx_queue_data { + u32 version; + u32 count_queue_entries; + u64 phys_addr_cpx_queues; +} __attribute__ ((packed)); + +struct megasas_aen_event { + struct work_struct hotplug_work; + struct megasas_instance *instance; +}; struct megasas_instance { @@ -1074,22 +1476,30 @@ struct megasas_instance { unsigned long base_addr; struct megasas_register_set __iomem *reg_set; + struct megasas_pd_list pd_list[MEGASAS_MAX_PD]; + u8 ld_ids[MEGASAS_MAX_LD_IDS]; + s8 init_id; u16 max_num_sge; u16 max_fw_cmds; u32 max_sectors_per_req; + u32 cmd_per_lun; struct megasas_cmd **cmd_list; struct list_head cmd_pool; spinlock_t cmd_pool_lock; + struct megasas_aen_event *ev; + spinlock_t hba_lock; + /* used to synch producer, consumer ptrs in dpc */ + spinlock_t completion_lock; struct dma_pool *frame_dma_pool; struct dma_pool *sense_dma_pool; struct megasas_evt_detail *evt_detail; dma_addr_t evt_detail_h; struct megasas_cmd *aen_cmd; - struct semaphore aen_mutex; + struct mutex aen_mutex; struct semaphore ioctl_sem; struct Scsi_Host *host; @@ -1099,17 +1509,62 @@ struct megasas_instance { struct pci_dev *pdev; u32 unique_id; + u32 fw_support_ieee; atomic_t fw_outstanding; - u32 hw_crit_error; + atomic_t fw_reset_no_pci_access; struct megasas_instance_template *instancet; struct tasklet_struct isr_tasklet; + struct work_struct work_init; u8 flag; + u8 unload; + u8 flag_ieee; + u8 issuepend_done; + u8 disableOnlineCtrlReset; + u8 adprecovery; unsigned long last_time; + u32 mfiStatus; + u32 last_seq_num; + + struct timer_list io_completion_timer; + struct list_head internal_reset_pending_q; + + u32 cpx_supported; + struct mr_cpx_request_queue *cpx_request_queue; + dma_addr_t cpx_request_queue_h; + union mr_cpx_descriptor *cpx_dscrptr; + u32 cpx_dscrptr_cnt; + u64 host_mem_phys; + u32 host_mem_len; + u8 *host_mem_virt; + +}; + +enum { + MEGASAS_HBA_OPERATIONAL = 0, + MEGASAS_ADPRESET_SM_INFAULT = 1, + MEGASAS_ADPRESET_SM_FW_RESET_SUCCESS = 2, + MEGASAS_ADPRESET_SM_OPERATIONAL = 3, + MEGASAS_HW_CRITICAL_ERROR = 4, + MEGASAS_ADPRESET_INPROG_SIGN = 0xDEADDEAD, }; + + struct megasas_instance_template { + void (*fire_cmd)(struct megasas_instance *, dma_addr_t ,u32 ,struct megasas_register_set __iomem *); + + void (*enable_intr)(struct megasas_register_set __iomem *) ; + void (*disable_intr)(struct megasas_register_set __iomem *); + + int (*clear_intr)(struct megasas_register_set __iomem *); + + u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); + int (*adp_reset)(struct megasas_instance *, struct megasas_register_set __iomem *); + int (*check_reset)(struct megasas_instance *, struct megasas_register_set __iomem *); + }; + #define MEGASAS_IS_LOGICAL(scp) \ (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 @@ -1127,7 +1582,9 @@ struct megasas_cmd { u32 index; u8 sync_cmd; u8 cmd_status; - u16 abort_aen; + u8 abort_aen; + u8 retry_for_fw_reset; + struct list_head list; struct scsi_cmnd *scmd;