diff options
author | adam radford <aradford@gmail.com> | 2010-12-21 13:34:31 -0800 |
---|---|---|
committer | James Bottomley <James.Bottomley@suse.de> | 2010-12-22 23:26:53 -0600 |
commit | 9c915a8c99bce637226aa09cb05fc18486b229cb (patch) | |
tree | 7bc2330661366db6407548a12366f7340f1b31e9 /drivers/scsi | |
parent | cd50ba8ede5cd3c4606a8e5d163913da5ff36ad7 (diff) | |
download | linux-stable-9c915a8c99bce637226aa09cb05fc18486b229cb.tar.gz linux-stable-9c915a8c99bce637226aa09cb05fc18486b229cb.tar.bz2 linux-stable-9c915a8c99bce637226aa09cb05fc18486b229cb.zip |
[SCSI] megaraid_sas: Add 9565/9285 specific code
This patch adds MegaRAID 9265/9285 (Device id 0x5b) specific code
Signed-off-by: Adam Radford <aradford@gmail.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/megaraid/Makefile | 3 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas.h | 38 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_base.c | 362 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_fp.c | 516 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_fusion.c | 2248 | ||||
-rw-r--r-- | drivers/scsi/megaraid/megaraid_sas_fusion.h | 695 |
6 files changed, 3751 insertions, 111 deletions
diff --git a/drivers/scsi/megaraid/Makefile b/drivers/scsi/megaraid/Makefile index 6613a2ceea03..5826ed509e3e 100644 --- a/drivers/scsi/megaraid/Makefile +++ b/drivers/scsi/megaraid/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o obj-$(CONFIG_MEGARAID_SAS) += megaraid_sas.o -megaraid_sas-objs := megaraid_sas_base.o +megaraid_sas-objs := megaraid_sas_base.o megaraid_sas_fusion.o \ + megaraid_sas_fp.o diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index a0b8ee1a5d2d..1b5e375732c0 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -33,9 +33,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.04.31-rc1" -#define MEGASAS_RELDATE "May 3, 2010" -#define MEGASAS_EXT_VERSION "Mon. May 3, 11:41:51 PST 2010" +#define MEGASAS_VERSION "00.00.05.29-rc1" +#define MEGASAS_RELDATE "Dec. 7, 2010" +#define MEGASAS_EXT_VERSION "Tue. Dec. 7 17:00:00 PDT 2010" /* * Device IDs @@ -47,6 +47,7 @@ #define PCI_DEVICE_ID_LSI_SAS0079GEN2 0x0079 #define PCI_DEVICE_ID_LSI_SAS0073SKINNY 0x0073 #define PCI_DEVICE_ID_LSI_SAS0071SKINNY 0x0071 +#define PCI_DEVICE_ID_LSI_FUSION 0x005b /* * ===================================== @@ -436,7 +437,6 @@ struct megasas_ctrl_prop { * Add properties that can be controlled by * a bit in the following structure. */ - struct { u32 copyBackDisabled : 1; u32 SMARTerEnabled : 1; @@ -716,6 +716,7 @@ 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 * \ @@ -784,7 +785,10 @@ struct megasas_ctrl_info { */ struct megasas_register_set { - u32 reserved_0[4]; /*0000h*/ + u32 doorbell; /*0000h*/ + u32 fusion_seq_offset; /*0004h*/ + u32 fusion_host_diag; /*0008h*/ + u32 reserved_01; /*000Ch*/ u32 inbound_msg_0; /*0010h*/ u32 inbound_msg_1; /*0014h*/ @@ -804,15 +808,18 @@ struct megasas_register_set { u32 inbound_queue_port; /*0040h*/ u32 outbound_queue_port; /*0044h*/ - u32 reserved_2[22]; /*0048h*/ + u32 reserved_2[9]; /*0048h*/ + u32 reply_post_host_index; /*006Ch*/ + u32 reserved_2_2[12]; /*0070h*/ u32 outbound_doorbell_clear; /*00A0h*/ u32 reserved_3[3]; /*00A4h*/ u32 outbound_scratch_pad ; /*00B0h*/ + u32 outbound_scratch_pad_2; /*00B4h*/ - u32 reserved_4[3]; /*00B4h*/ + u32 reserved_4[2]; /*00B8h*/ u32 inbound_low_queue_port ; /*00C0h*/ @@ -1287,6 +1294,9 @@ struct megasas_instance { u16 max_num_sge; u16 max_fw_cmds; + /* For Fusion its num IOCTL cmds, for others MFI based its + max_fw_cmds */ + u16 max_mfi_cmds; u32 max_sectors_per_req; struct megasas_aen_event *ev; @@ -1336,9 +1346,15 @@ struct megasas_instance { struct timer_list io_completion_timer; struct list_head internal_reset_pending_q; + /* Ptr to hba specfic information */ + void *ctrl_context; u8 msi_flag; struct msix_entry msixentry; + u64 map_id; + struct megasas_cmd *map_update_cmd; unsigned long bar; + long reset_flags; + struct mutex reset_mutex; }; enum { @@ -1397,7 +1413,13 @@ struct megasas_cmd { struct list_head list; struct scsi_cmnd *scmd; struct megasas_instance *instance; - u32 frame_count; + union { + struct { + u16 smid; + u16 resvd; + } context; + u32 frame_count; + }; }; #define MAX_MGMT_ADAPTERS 1024 diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 5938267e472b..5d6d07bd1cd0 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -53,6 +53,7 @@ #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_host.h> +#include "megaraid_sas_fusion.h" #include "megaraid_sas.h" /* @@ -81,7 +82,7 @@ MODULE_VERSION(MEGASAS_VERSION); MODULE_AUTHOR("megaraidlinux@lsi.com"); MODULE_DESCRIPTION("LSI MegaRAID SAS Driver"); -static int megasas_transition_to_ready(struct megasas_instance *instance); +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, @@ -109,6 +110,8 @@ static struct pci_device_id megasas_pci_table[] = { /* xscale IOP, vega */ {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)}, /* xscale IOP */ + {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_FUSION)}, + /* Fusion */ {} }; @@ -122,15 +125,16 @@ static DEFINE_MUTEX(megasas_async_queue_mutex); static int megasas_poll_wait_aen; static DECLARE_WAIT_QUEUE_HEAD(megasas_poll_wait); static u32 support_poll_for_event; -static u32 megasas_dbg_lvl; +u32 megasas_dbg_lvl; static u32 support_device_change; /* define lock for aen poll */ spinlock_t poll_aen_lock; -static void +void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status); + static irqreturn_t megasas_isr(int irq, void *devp); static u32 megasas_init_adapter_mfi(struct megasas_instance *instance); @@ -138,6 +142,23 @@ u32 megasas_build_and_issue_cmd(struct megasas_instance *instance, struct scsi_cmnd *scmd); static void megasas_complete_cmd_dpc(unsigned long instance_addr); +void +megasas_release_fusion(struct megasas_instance *instance); +int +megasas_ioc_init_fusion(struct megasas_instance *instance); +void +megasas_free_cmds_fusion(struct megasas_instance *instance); +u8 +megasas_get_map_info(struct megasas_instance *instance); +int +megasas_sync_map_info(struct megasas_instance *instance); +int +wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd); +void megasas_reset_reply_desc(struct megasas_instance *instance); +u8 MR_ValidateMapInfo(struct MR_FW_RAID_MAP_ALL *map, + struct LD_LOAD_BALANCE_INFO *lbInfo); +int megasas_reset_fusion(struct Scsi_Host *shost); +void megasas_fusion_ocr_wq(struct work_struct *work); void megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) @@ -152,7 +173,7 @@ megasas_issue_dcmd(struct megasas_instance *instance, struct megasas_cmd *cmd) * * Returns a free command from the pool */ -static struct megasas_cmd *megasas_get_cmd(struct megasas_instance +struct megasas_cmd *megasas_get_cmd(struct megasas_instance *instance) { unsigned long flags; @@ -177,7 +198,7 @@ static struct megasas_cmd *megasas_get_cmd(struct megasas_instance * @instance: Adapter soft state * @cmd: Command packet to be returned to free command pool */ -static inline void +inline void megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) { unsigned long flags; @@ -185,6 +206,7 @@ megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) spin_lock_irqsave(&instance->cmd_pool_lock, flags); cmd->scmd = NULL; + cmd->frame_count = 0; list_add_tail(&cmd->list, &instance->cmd_pool); spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); @@ -796,6 +818,11 @@ static struct megasas_instance_template megasas_instance_template_gen2 = { * specific to gen2 (deviceid : 0x78, 0x79) controllers */ +/* + * Template added for TB (Fusion) + */ +extern struct megasas_instance_template megasas_instance_template_fusion; + /** * megasas_issue_polled - Issues a polling command * @instance: Adapter soft state @@ -803,11 +830,9 @@ static struct megasas_instance_template megasas_instance_template_gen2 = { * * For polling, MFI requires the cmd_status to be set to 0xFF before posting. */ -static int +int megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) { - int i; - u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000; struct megasas_header *frame_hdr = &cmd->frame->hdr; @@ -817,21 +842,12 @@ megasas_issue_polled(struct megasas_instance *instance, struct megasas_cmd *cmd) /* * Issue the frame using inbound queue port */ - instance->instancet->fire_cmd(instance, - cmd->frame_phys_addr, 0, instance->reg_set); + instance->instancet->issue_dcmd(instance, cmd); /* * Wait for cmd_status to change */ - for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i++) { - rmb(); - msleep(1); - } - - if (frame_hdr->cmd_status == 0xff) - return -ETIME; - - return 0; + return wait_and_poll(instance, cmd); } /** @@ -849,8 +865,7 @@ megasas_issue_blocked_cmd(struct megasas_instance *instance, { cmd->cmd_status = ENODATA; - instance->instancet->fire_cmd(instance, - cmd->frame_phys_addr, 0, instance->reg_set); + instance->instancet->issue_dcmd(instance, cmd); wait_event(instance->int_cmd_wait_q, cmd->cmd_status != ENODATA); @@ -894,8 +909,7 @@ megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, cmd->sync_cmd = 1; cmd->cmd_status = 0xFF; - instance->instancet->fire_cmd(instance, - cmd->frame_phys_addr, 0, instance->reg_set); + instance->instancet->issue_dcmd(instance, cmd); /* * Wait for this cmd to complete @@ -1291,7 +1305,7 @@ megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, * Called by megasas_queue_command to find out if the command to be queued * is a logical drive command */ -static inline int megasas_is_ldio(struct scsi_cmnd *cmd) +inline int megasas_is_ldio(struct scsi_cmnd *cmd) { if (!MEGASAS_IS_LOGICAL(cmd)) return 0; @@ -1551,15 +1565,44 @@ static int megasas_slave_alloc(struct scsi_device *sdev) return 0; } -static void megaraid_sas_kill_hba(struct megasas_instance *instance) +void megaraid_sas_kill_hba(struct megasas_instance *instance) { 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[0]); + (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION)) { + writel(MFI_STOP_ADP, &instance->reg_set->doorbell); } else { - writel(MFI_STOP_ADP, - &instance->reg_set->inbound_doorbell); + writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); + } +} + + /** + * megasas_check_and_restore_queue_depth - Check if queue depth needs to be + * restored to max value + * @instance: Adapter soft state + * + */ +void +megasas_check_and_restore_queue_depth(struct megasas_instance *instance) +{ + unsigned long flags; + 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); } } @@ -1613,24 +1656,7 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) /* * 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); - } + megasas_check_and_restore_queue_depth(instance); } static void @@ -1808,7 +1834,7 @@ static int megasas_wait_for_outstanding(struct megasas_instance *instance) (instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { writel(MFI_STOP_ADP, - &instance->reg_set->reserved_0[0]); + &instance->reg_set->doorbell); } else { writel(MFI_STOP_ADP, &instance->reg_set->inbound_doorbell); @@ -1912,11 +1938,16 @@ static int megasas_reset_device(struct scsi_cmnd *scmd) static int megasas_reset_bus_host(struct scsi_cmnd *scmd) { int ret; + struct megasas_instance *instance; + instance = (struct megasas_instance *)scmd->device->host->hostdata; /* * First wait for all commands to complete */ - ret = megasas_generic_reset(scmd); + if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) + ret = megasas_reset_fusion(scmd->device->host); + else + ret = megasas_generic_reset(scmd); return ret; } @@ -2086,13 +2117,14 @@ megasas_complete_abort(struct megasas_instance *instance, * an alternate status (as in the case of aborted * commands) */ -static void +void megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status) { int exception = 0; struct megasas_header *hdr = &cmd->frame->hdr; unsigned long flags; + struct fusion_context *fusion = instance->ctrl_context; /* flag for the retry reset */ cmd->retry_for_fw_reset = 0; @@ -2185,6 +2217,37 @@ megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, case MFI_CMD_SMP: case MFI_CMD_STP: case MFI_CMD_DCMD: + /* Check for LD map update */ + if ((cmd->frame->dcmd.opcode == MR_DCMD_LD_MAP_GET_INFO) && + (cmd->frame->dcmd.mbox.b[1] == 1)) { + spin_lock_irqsave(instance->host->host_lock, flags); + if (cmd->frame->hdr.cmd_status != 0) { + if (cmd->frame->hdr.cmd_status != + MFI_STAT_NOT_FOUND) + printk(KERN_WARNING "megasas: map sync" + "failed, status = 0x%x.\n", + cmd->frame->hdr.cmd_status); + else { + megasas_return_cmd(instance, cmd); + spin_unlock_irqrestore( + instance->host->host_lock, + flags); + break; + } + } else + instance->map_id++; + megasas_return_cmd(instance, cmd); + if (MR_ValidateMapInfo( + fusion->ld_map[(instance->map_id & 1)], + fusion->load_balance_info)) + fusion->fast_path_io = 1; + else + fusion->fast_path_io = 0; + megasas_sync_map_info(instance); + spin_unlock_irqrestore(instance->host->host_lock, + flags); + break; + } 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); @@ -2523,7 +2586,7 @@ static irqreturn_t megasas_isr(int irq, void *devp) * states, driver must take steps to bring it to ready state. Otherwise, it * has to wait for the ready state. */ -static int +int megasas_transition_to_ready(struct megasas_instance* instance) { int i; @@ -2557,11 +2620,12 @@ megasas_transition_to_ready(struct megasas_instance* instance) if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == - PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { - + PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_FUSION)) { writel( MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, - &instance->reg_set->reserved_0[0]); + &instance->reg_set->doorbell); } else { writel( MFI_INIT_CLEAR_HANDSHAKE|MFI_INIT_HOTPLUG, @@ -2574,11 +2638,13 @@ megasas_transition_to_ready(struct megasas_instance* instance) case MFI_STATE_BOOT_MESSAGE_PENDING: if ((instance->pdev->device == - PCI_DEVICE_ID_LSI_SAS0073SKINNY) || - (instance->pdev->device == - PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + PCI_DEVICE_ID_LSI_SAS0073SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device == + PCI_DEVICE_ID_LSI_FUSION)) { writel(MFI_INIT_HOTPLUG, - &instance->reg_set->reserved_0[0]); + &instance->reg_set->doorbell); } else writel(MFI_INIT_HOTPLUG, &instance->reg_set->inbound_doorbell); @@ -2595,9 +2661,23 @@ megasas_transition_to_ready(struct megasas_instance* instance) if ((instance->pdev->device == PCI_DEVICE_ID_LSI_SAS0073SKINNY) || (instance->pdev->device == - PCI_DEVICE_ID_LSI_SAS0071SKINNY)) { + PCI_DEVICE_ID_LSI_SAS0071SKINNY) || + (instance->pdev->device + == PCI_DEVICE_ID_LSI_FUSION)) { writel(MFI_RESET_FLAGS, - &instance->reg_set->reserved_0[0]); + &instance->reg_set->doorbell); + if (instance->pdev->device == + PCI_DEVICE_ID_LSI_FUSION) { + for (i = 0; i < (10 * 1000); i += 20) { + if (readl( + &instance-> + reg_set-> + doorbell) & 1) + msleep(20); + else + break; + } + } } else writel(MFI_RESET_FLAGS, &instance->reg_set->inbound_doorbell); @@ -2681,7 +2761,7 @@ megasas_transition_to_ready(struct megasas_instance* instance) static void megasas_teardown_frame_pool(struct megasas_instance *instance) { int i; - u32 max_cmd = instance->max_fw_cmds; + u32 max_cmd = instance->max_mfi_cmds; struct megasas_cmd *cmd; if (!instance->frame_dma_pool) @@ -2732,7 +2812,7 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) u32 frame_count; struct megasas_cmd *cmd; - max_cmd = instance->max_fw_cmds; + max_cmd = instance->max_mfi_cmds; /* * Size of our frame is 64 bytes for MFI frame, followed by max SG @@ -2819,14 +2899,15 @@ static int megasas_create_frame_pool(struct megasas_instance *instance) * megasas_free_cmds - Free all the cmds in the free cmd pool * @instance: Adapter soft state */ -static void megasas_free_cmds(struct megasas_instance *instance) +void megasas_free_cmds(struct megasas_instance *instance) { int i; /* First free the MFI frame pool */ megasas_teardown_frame_pool(instance); /* Free all the commands in the cmd_list */ - for (i = 0; i < instance->max_fw_cmds; i++) + for (i = 0; i < instance->max_mfi_cmds; i++) + kfree(instance->cmd_list[i]); /* Free the cmd_list buffer itself */ @@ -2854,14 +2935,14 @@ static void megasas_free_cmds(struct megasas_instance *instance) * This array is used only to look up the megasas_cmd given the context. The * free commands themselves are maintained in a linked list called cmd_pool. */ -static int megasas_alloc_cmds(struct megasas_instance *instance) +int megasas_alloc_cmds(struct megasas_instance *instance) { int i; int j; u32 max_cmd; struct megasas_cmd *cmd; - max_cmd = instance->max_fw_cmds; + max_cmd = instance->max_mfi_cmds; /* * instance->cmd_list is an array of struct megasas_cmd pointers. @@ -2875,6 +2956,7 @@ static int megasas_alloc_cmds(struct megasas_instance *instance) return -ENOMEM; } + memset(instance->cmd_list, 0, sizeof(struct megasas_cmd *) *max_cmd); for (i = 0; i < max_cmd; i++) { instance->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd), @@ -3288,6 +3370,7 @@ megasas_init_adapter_mfi(struct megasas_instance *instance) * does not exceed max cmds that the FW can support */ instance->max_fw_cmds = instance->max_fw_cmds-1; + instance->max_mfi_cmds = instance->max_fw_cmds; instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >> 0x10; /* @@ -3381,6 +3464,9 @@ static int megasas_init_fw(struct megasas_instance *instance) reg_set = instance->reg_set; switch (instance->pdev->device) { + case PCI_DEVICE_ID_LSI_FUSION: + instance->instancet = &megasas_instance_template_fusion; + break; case PCI_DEVICE_ID_LSI_SAS1078R: case PCI_DEVICE_ID_LSI_SAS1078DE: instance->instancet = &megasas_instance_template_ppc; @@ -3482,9 +3568,10 @@ fail_ready_state: */ static void megasas_release_mfi(struct megasas_instance *instance) { - u32 reply_q_sz = sizeof(u32) * (instance->max_fw_cmds + 1); + u32 reply_q_sz = sizeof(u32) *(instance->max_mfi_cmds + 1); - pci_free_consistent(instance->pdev, reply_q_sz, + if (instance->reply_queue) + pci_free_consistent(instance->pdev, reply_q_sz, instance->reply_queue, instance->reply_queue_h); megasas_free_cmds(instance); @@ -3678,8 +3765,7 @@ megasas_register_aen(struct megasas_instance *instance, u32 seq_num, /* * Issue the aen registration frame */ - instance->instancet->fire_cmd(instance, - cmd->frame_phys_addr, 0, instance->reg_set); + instance->instancet->issue_dcmd(instance, cmd); return 0; } @@ -3756,12 +3842,18 @@ static int megasas_io_attach(struct megasas_instance *instance) } 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; host->max_cmd_len = 16; + /* Fusion only supports host reset */ + if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) { + host->hostt->eh_device_reset_handler = NULL; + host->hostt->eh_bus_reset_handler = NULL; + } + /* * Notify the mid-layer about the new controller */ @@ -3846,20 +3938,45 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) instance = (struct megasas_instance *)host->hostdata; memset(instance, 0, sizeof(*instance)); atomic_set( &instance->fw_reset_no_pci_access, 0 ); + instance->pdev = pdev; - instance->producer = pci_alloc_consistent(pdev, sizeof(u32), - &instance->producer_h); - instance->consumer = pci_alloc_consistent(pdev, sizeof(u32), - &instance->consumer_h); + switch (instance->pdev->device) { + case PCI_DEVICE_ID_LSI_FUSION: + { + struct fusion_context *fusion; + + instance->ctrl_context = + kzalloc(sizeof(struct fusion_context), GFP_KERNEL); + if (!instance->ctrl_context) { + printk(KERN_DEBUG "megasas: Failed to allocate " + "memory for Fusion context info\n"); + goto fail_alloc_dma_buf; + } + fusion = instance->ctrl_context; + INIT_LIST_HEAD(&fusion->cmd_pool); + spin_lock_init(&fusion->cmd_pool_lock); + } + break; + default: /* For all other supported controllers */ + + instance->producer = + pci_alloc_consistent(pdev, sizeof(u32), + &instance->producer_h); + instance->consumer = + pci_alloc_consistent(pdev, sizeof(u32), + &instance->consumer_h); + + if (!instance->producer || !instance->consumer) { + printk(KERN_DEBUG "megasas: Failed to allocate" + "memory for producer, consumer\n"); + goto fail_alloc_dma_buf; + } - if (!instance->producer || !instance->consumer) { - printk(KERN_DEBUG "megasas: Failed to allocate memory for " - "producer, consumer\n"); - goto fail_alloc_dma_buf; + *instance->producer = 0; + *instance->consumer = 0; + break; } - *instance->producer = 0; - *instance->consumer = 0; megasas_poll_wait_aen = 0; instance->flag_ieee = 0; instance->ev = NULL; @@ -3895,11 +4012,11 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) spin_lock_init(&poll_aen_lock); mutex_init(&instance->aen_mutex); + mutex_init(&instance->reset_mutex); /* * Initialize PCI related and misc parameters */ - instance->pdev = pdev; instance->host = host; instance->unique_id = pdev->bus->number << 8 | pdev->devfn; instance->init_id = MEGASAS_DEFAULT_INIT_ID; @@ -3917,7 +4034,10 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) instance->last_time = 0; instance->disableOnlineCtrlReset = 1; - INIT_WORK(&instance->work_init, process_fw_state_change_wq); + if (instance->pdev->device == PCI_DEVICE_ID_LSI_FUSION) + INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq); + else + INIT_WORK(&instance->work_init, process_fw_state_change_wq); /* * Initialize MFI Firmware @@ -4000,6 +4120,8 @@ megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_free_consistent(pdev, sizeof(u32), instance->producer, instance->producer_h); megasas_release_mfi(instance); + } else { + megasas_release_fusion(instance); } if (instance->consumer) pci_free_consistent(pdev, sizeof(u32), instance->consumer, @@ -4072,7 +4194,9 @@ static void megasas_shutdown_controller(struct megasas_instance *instance, if (instance->aen_cmd) megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd); - + if (instance->map_update_cmd) + megasas_issue_blocked_abort_cmd(instance, + instance->map_update_cmd); dcmd = &cmd->frame->dcmd; memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); @@ -4177,9 +4301,6 @@ megasas_resume(struct pci_dev *pdev) * Initialize MFI Firmware */ - *instance->producer = 0; - *instance->consumer = 0; - atomic_set(&instance->fw_outstanding, 0); /* @@ -4188,11 +4309,29 @@ megasas_resume(struct pci_dev *pdev) if (megasas_transition_to_ready(instance)) goto fail_ready_state; - if (megasas_issue_init_mfi(instance)) - goto fail_init_mfi; + switch (instance->pdev->device) { + case PCI_DEVICE_ID_LSI_FUSION: + { + megasas_reset_reply_desc(instance); + if (megasas_ioc_init_fusion(instance)) { + megasas_free_cmds(instance); + megasas_free_cmds_fusion(instance); + goto fail_init_mfi; + } + if (!megasas_get_map_info(instance)) + megasas_sync_map_info(instance); + } + break; + default: + *instance->producer = 0; + *instance->consumer = 0; + if (megasas_issue_init_mfi(instance)) + goto fail_init_mfi; + break; + } - tasklet_init(&instance->isr_tasklet, megasas_complete_cmd_dpc, - (unsigned long)instance); + tasklet_init(&instance->isr_tasklet, instance->instancet->tasklet, + (unsigned long)instance); /* Now re-enable MSI-X */ if (instance->msi_flag) @@ -4261,10 +4400,12 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) int i; struct Scsi_Host *host; struct megasas_instance *instance; + struct fusion_context *fusion; instance = pci_get_drvdata(pdev); instance->unload = 1; host = instance->host; + fusion = instance->ctrl_context; if (poll_mode_io) del_timer_sync(&instance->io_completion_timer); @@ -4306,16 +4447,32 @@ static void __devexit megasas_detach_one(struct pci_dev *pdev) if (instance->msi_flag) pci_disable_msix(instance->pdev); - megasas_release_mfi(instance); - - pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), - instance->evt_detail, instance->evt_detail_h); - - pci_free_consistent(pdev, sizeof(u32), instance->producer, - instance->producer_h); - - pci_free_consistent(pdev, sizeof(u32), instance->consumer, - instance->consumer_h); + switch (instance->pdev->device) { + case PCI_DEVICE_ID_LSI_FUSION: + megasas_release_fusion(instance); + for (i = 0; i < 2 ; i++) + if (fusion->ld_map[i]) + dma_free_coherent(&instance->pdev->dev, + fusion->map_sz, + fusion->ld_map[i], + fusion-> + ld_map_phys[i]); + kfree(instance->ctrl_context); + break; + default: + megasas_release_mfi(instance); + pci_free_consistent(pdev, + sizeof(struct megasas_evt_detail), + instance->evt_detail, + instance->evt_detail_h); + pci_free_consistent(pdev, sizeof(u32), + instance->producer, + instance->producer_h); + pci_free_consistent(pdev, sizeof(u32), + instance->consumer, + instance->consumer_h); + break; + } scsi_host_put(host); @@ -5079,6 +5236,7 @@ megasas_aen_polling(struct work_struct *work) 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: diff --git a/drivers/scsi/megaraid/megaraid_sas_fp.c b/drivers/scsi/megaraid/megaraid_sas_fp.c new file mode 100644 index 000000000000..53fa96ae2b3e --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_sas_fp.c @@ -0,0 +1,516 @@ +/* + * Linux MegaRAID driver for SAS based RAID controllers + * + * Copyright (c) 2009-2011 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FILE: megaraid_sas_fp.c + * + * Authors: LSI Corporation + * Sumant Patro + * Varad Talamacki + * Manoj Jose + * + * Send feedback to: <megaraidlinux@lsi.com> + * + * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 + * ATTN: Linuxraid + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/smp_lock.h> +#include <linux/uio.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/compat.h> +#include <linux/blkdev.h> +#include <linux/poll.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> + +#include "megaraid_sas_fusion.h" +#include <asm/div64.h> + +#define ABS_DIFF(a, b) (((a) > (b)) ? ((a) - (b)) : ((b) - (a))) +#define MR_LD_STATE_OPTIMAL 3 +#define FALSE 0 +#define TRUE 1 + +/* Prototypes */ +void +mr_update_load_balance_params(struct MR_FW_RAID_MAP_ALL *map, + struct LD_LOAD_BALANCE_INFO *lbInfo); + +u32 mega_mod64(u64 dividend, u32 divisor) +{ + u64 d; + u32 remainder; + + if (!divisor) + printk(KERN_ERR "megasas : DIVISOR is zero, in div fn\n"); + d = dividend; + remainder = do_div(d, divisor); + return remainder; +} + +/** + * @param dividend : Dividend + * @param divisor : Divisor + * + * @return quotient + **/ +u64 mega_div64_32(uint64_t dividend, uint32_t divisor) +{ + u32 remainder; + u64 d; + + if (!divisor) + printk(KERN_ERR "megasas : DIVISOR is zero in mod fn\n"); + + d = dividend; + remainder = do_div(d, divisor); + + return d; +} + +struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_FW_RAID_MAP_ALL *map) +{ + return &map->raidMap.ldSpanMap[ld].ldRaid; +} + +static struct MR_SPAN_BLOCK_INFO *MR_LdSpanInfoGet(u32 ld, + struct MR_FW_RAID_MAP_ALL + *map) +{ + return &map->raidMap.ldSpanMap[ld].spanBlock[0]; +} + +static u8 MR_LdDataArmGet(u32 ld, u32 armIdx, struct MR_FW_RAID_MAP_ALL *map) +{ + return map->raidMap.ldSpanMap[ld].dataArmMap[armIdx]; +} + +static u16 MR_ArPdGet(u32 ar, u32 arm, struct MR_FW_RAID_MAP_ALL *map) +{ + return map->raidMap.arMapInfo[ar].pd[arm]; +} + +static u16 MR_LdSpanArrayGet(u32 ld, u32 span, struct MR_FW_RAID_MAP_ALL *map) +{ + return map->raidMap.ldSpanMap[ld].spanBlock[span].span.arrayRef; +} + +static u16 MR_PdDevHandleGet(u32 pd, struct MR_FW_RAID_MAP_ALL *map) +{ + return map->raidMap.devHndlInfo[pd].curDevHdl; +} + +u16 MR_GetLDTgtId(u32 ld, struct MR_FW_RAID_MAP_ALL *map) +{ + return map->raidMap.ldSpanMap[ld].ldRaid.targetId; +} + +u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map) +{ + return map->raidMap.ldTgtIdToLd[ldTgtId]; +} + +static struct MR_LD_SPAN *MR_LdSpanPtrGet(u32 ld, u32 span, + struct MR_FW_RAID_MAP_ALL *map) +{ + return &map->raidMap.ldSpanMap[ld].spanBlock[span].span; +} + +/* + * This function will validate Map info data provided by FW + */ +u8 MR_ValidateMapInfo(struct MR_FW_RAID_MAP_ALL *map, + struct LD_LOAD_BALANCE_INFO *lbInfo) +{ + struct MR_FW_RAID_MAP *pFwRaidMap = &map->raidMap; + + if (pFwRaidMap->totalSize != + (sizeof(struct MR_FW_RAID_MAP) -sizeof(struct MR_LD_SPAN_MAP) + + (sizeof(struct MR_LD_SPAN_MAP) *pFwRaidMap->ldCount))) { + printk(KERN_ERR "megasas: map info structure size 0x%x is not matching with ld count\n", + (unsigned int)((sizeof(struct MR_FW_RAID_MAP) - + sizeof(struct MR_LD_SPAN_MAP)) + + (sizeof(struct MR_LD_SPAN_MAP) * + pFwRaidMap->ldCount))); + printk(KERN_ERR "megasas: span map %x, pFwRaidMap->totalSize " + ": %x\n", (unsigned int)sizeof(struct MR_LD_SPAN_MAP), + pFwRaidMap->totalSize); + return 0; + } + + mr_update_load_balance_params(map, lbInfo); + + return 1; +} + +u32 MR_GetSpanBlock(u32 ld, u64 row, u64 *span_blk, + struct MR_FW_RAID_MAP_ALL *map, int *div_error) +{ + struct MR_SPAN_BLOCK_INFO *pSpanBlock = MR_LdSpanInfoGet(ld, map); + struct MR_QUAD_ELEMENT *quad; + struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map); + u32 span, j; + + for (span = 0; span < raid->spanDepth; span++, pSpanBlock++) { + + for (j = 0; j < pSpanBlock->block_span_info.noElements; j++) { + quad = &pSpanBlock->block_span_info.quad[j]; + + if (quad->diff == 0) { + *div_error = 1; + return span; + } + if (quad->logStart <= row && row <= quad->logEnd && + (mega_mod64(row-quad->logStart, quad->diff)) == 0) { + if (span_blk != NULL) { + u64 blk, debugBlk; + blk = + mega_div64_32( + (row-quad->logStart), + quad->diff); + debugBlk = blk; + + blk = (blk + quad->offsetInSpan) << + raid->stripeShift; + *span_blk = blk; + } + return span; + } + } + } + return span; +} + +/* +****************************************************************************** +* +* This routine calculates the arm, span and block for the specified stripe and +* reference in stripe. +* +* Inputs : +* +* ld - Logical drive number +* stripRow - Stripe number +* stripRef - Reference in stripe +* +* Outputs : +* +* span - Span number +* block - Absolute Block number in the physical disk +*/ +u8 MR_GetPhyParams(u32 ld, u64 stripRow, u16 stripRef, u64 *pdBlock, + u16 *pDevHandle, struct RAID_CONTEXT *pRAID_Context, + struct MR_FW_RAID_MAP_ALL *map) +{ + struct MR_LD_RAID *raid = MR_LdRaidGet(ld, map); + u32 pd, arRef; + u8 physArm, span; + u64 row; + u8 retval = TRUE; + int error_code = 0; + + row = mega_div64_32(stripRow, raid->rowDataSize); + + if (raid->level == 6) { + /* logical arm within row */ + u32 logArm = mega_mod64(stripRow, raid->rowDataSize); + u32 rowMod, armQ, arm; + + if (raid->rowSize == 0) + return FALSE; + /* get logical row mod */ + rowMod = mega_mod64(row, raid->rowSize); + armQ = raid->rowSize-1-rowMod; /* index of Q drive */ + arm = armQ+1+logArm; /* data always logically follows Q */ + if (arm >= raid->rowSize) /* handle wrap condition */ + arm -= raid->rowSize; + physArm = (u8)arm; + } else { + if (raid->modFactor == 0) + return FALSE; + physArm = MR_LdDataArmGet(ld, mega_mod64(stripRow, + raid->modFactor), + map); + } + + if (raid->spanDepth == 1) { + span = 0; + *pdBlock = row << raid->stripeShift; + } else { + span = (u8)MR_GetSpanBlock(ld, row, pdBlock, map, &error_code); + if (error_code == 1) + return FALSE; + } + + /* Get the array on which this span is present */ + arRef = MR_LdSpanArrayGet(ld, span, map); + pd = MR_ArPdGet(arRef, physArm, map); /* Get the pd */ + + if (pd != MR_PD_INVALID) + /* Get dev handle from Pd. */ + *pDevHandle = MR_PdDevHandleGet(pd, map); + else { + *pDevHandle = MR_PD_INVALID; /* set dev handle as invalid. */ + if (raid->level >= 5) + pRAID_Context->regLockFlags = REGION_TYPE_EXCLUSIVE; + else if (raid->level == 1) { + /* Get alternate Pd. */ + pd = MR_ArPdGet(arRef, physArm + 1, map); + if (pd != MR_PD_INVALID) + /* Get dev handle from Pd */ + *pDevHandle = MR_PdDevHandleGet(pd, map); + } + retval = FALSE; + } + + *pdBlock += stripRef + MR_LdSpanPtrGet(ld, span, map)->startBlk; + pRAID_Context->spanArm = (span << RAID_CTX_SPANARM_SPAN_SHIFT) | + physArm; + return retval; +} + +/* +****************************************************************************** +* +* MR_BuildRaidContext function +* +* This function will initiate command processing. The start/end row and strip +* information is calculated then the lock is acquired. +* This function will return 0 if region lock was acquired OR return num strips +*/ +u8 +MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info, + struct RAID_CONTEXT *pRAID_Context, + struct MR_FW_RAID_MAP_ALL *map) +{ + struct MR_LD_RAID *raid; + u32 ld, stripSize, stripe_mask; + u64 endLba, endStrip, endRow, start_row, start_strip; + u64 regStart; + u32 regSize; + u8 num_strips, numRows; + u16 ref_in_start_stripe, ref_in_end_stripe; + u64 ldStartBlock; + u32 numBlocks, ldTgtId; + u8 isRead; + u8 retval = 0; + + ldStartBlock = io_info->ldStartBlock; + numBlocks = io_info->numBlocks; + ldTgtId = io_info->ldTgtId; + isRead = io_info->isRead; + + ld = MR_TargetIdToLdGet(ldTgtId, map); + raid = MR_LdRaidGet(ld, map); + + stripSize = 1 << raid->stripeShift; + stripe_mask = stripSize-1; + /* + * calculate starting row and stripe, and number of strips and rows + */ + start_strip = ldStartBlock >> raid->stripeShift; + ref_in_start_stripe = (u16)(ldStartBlock & stripe_mask); + endLba = ldStartBlock + numBlocks - 1; + ref_in_end_stripe = (u16)(endLba & stripe_mask); + endStrip = endLba >> raid->stripeShift; + num_strips = (u8)(endStrip - start_strip + 1); /* End strip */ + if (raid->rowDataSize == 0) + return FALSE; + start_row = mega_div64_32(start_strip, raid->rowDataSize); + endRow = mega_div64_32(endStrip, raid->rowDataSize); + numRows = (u8)(endRow - start_row + 1); + + /* + * calculate region info. + */ + + /* assume region is at the start of the first row */ + regStart = start_row << raid->stripeShift; + /* assume this IO needs the full row - we'll adjust if not true */ + regSize = stripSize; + + /* If IO spans more than 1 strip, fp is not possible + FP is not possible for writes on non-0 raid levels + FP is not possible if LD is not capable */ + if (num_strips > 1 || (!isRead && raid->level != 0) || + !raid->capability.fpCapable) { + io_info->fpOkForIo = FALSE; + } else { + io_info->fpOkForIo = TRUE; + } + + if (numRows == 1) { + /* single-strip IOs can always lock only the data needed */ + if (num_strips == 1) { + regStart += ref_in_start_stripe; + regSize = numBlocks; + } + /* multi-strip IOs always need to full stripe locked */ + } else { + if (start_strip == (start_row + 1) * raid->rowDataSize - 1) { + /* If the start strip is the last in the start row */ + regStart += ref_in_start_stripe; + regSize = stripSize - ref_in_start_stripe; + /* initialize count to sectors from startref to end + of strip */ + } + + if (numRows > 2) + /* Add complete rows in the middle of the transfer */ + regSize += (numRows-2) << raid->stripeShift; + + /* if IO ends within first strip of last row */ + if (endStrip == endRow*raid->rowDataSize) + regSize += ref_in_end_stripe+1; + else + regSize += stripSize; + } + + pRAID_Context->timeoutValue = map->raidMap.fpPdIoTimeoutSec; + pRAID_Context->regLockFlags = (isRead) ? REGION_TYPE_SHARED_READ : + raid->regTypeReqOnWrite; + pRAID_Context->VirtualDiskTgtId = raid->targetId; + pRAID_Context->regLockRowLBA = regStart; + pRAID_Context->regLockLength = regSize; + pRAID_Context->configSeqNum = raid->seqNum; + + /*Get Phy Params only if FP capable, or else leave it to MR firmware + to do the calculation.*/ + if (io_info->fpOkForIo) { + retval = MR_GetPhyParams(ld, start_strip, ref_in_start_stripe, + &io_info->pdBlock, + &io_info->devHandle, pRAID_Context, + map); + /* If IO on an invalid Pd, then FP i snot possible */ + if (io_info->devHandle == MR_PD_INVALID) + io_info->fpOkForIo = FALSE; + return retval; + } else if (isRead) { + uint stripIdx; + for (stripIdx = 0; stripIdx < num_strips; stripIdx++) { + if (!MR_GetPhyParams(ld, start_strip + stripIdx, + ref_in_start_stripe, + &io_info->pdBlock, + &io_info->devHandle, + pRAID_Context, map)) + return TRUE; + } + } + return TRUE; +} + +void +mr_update_load_balance_params(struct MR_FW_RAID_MAP_ALL *map, + struct LD_LOAD_BALANCE_INFO *lbInfo) +{ + int ldCount; + u16 ld; + struct MR_LD_RAID *raid; + + for (ldCount = 0; ldCount < MAX_LOGICAL_DRIVES; ldCount++) { + ld = MR_TargetIdToLdGet(ldCount, map); + if (ld >= MAX_LOGICAL_DRIVES) { + lbInfo[ldCount].loadBalanceFlag = 0; + continue; + } + + raid = MR_LdRaidGet(ld, map); + + /* Two drive Optimal RAID 1 */ + if ((raid->level == 1) && (raid->rowSize == 2) && + (raid->spanDepth == 1) && raid->ldState == + MR_LD_STATE_OPTIMAL) { + u32 pd, arRef; + + lbInfo[ldCount].loadBalanceFlag = 1; + + /* Get the array on which this span is present */ + arRef = MR_LdSpanArrayGet(ld, 0, map); + + /* Get the Pd */ + pd = MR_ArPdGet(arRef, 0, map); + /* Get dev handle from Pd */ + lbInfo[ldCount].raid1DevHandle[0] = + MR_PdDevHandleGet(pd, map); + /* Get the Pd */ + pd = MR_ArPdGet(arRef, 1, map); + + /* Get the dev handle from Pd */ + lbInfo[ldCount].raid1DevHandle[1] = + MR_PdDevHandleGet(pd, map); + } else + lbInfo[ldCount].loadBalanceFlag = 0; + } +} + +u8 megasas_get_best_arm(struct LD_LOAD_BALANCE_INFO *lbInfo, u8 arm, u64 block, + u32 count) +{ + u16 pend0, pend1; + u64 diff0, diff1; + u8 bestArm; + + /* get the pending cmds for the data and mirror arms */ + pend0 = atomic_read(&lbInfo->scsi_pending_cmds[0]); + pend1 = atomic_read(&lbInfo->scsi_pending_cmds[1]); + + /* Determine the disk whose head is nearer to the req. block */ + diff0 = ABS_DIFF(block, lbInfo->last_accessed_block[0]); + diff1 = ABS_DIFF(block, lbInfo->last_accessed_block[1]); + bestArm = (diff0 <= diff1 ? 0 : 1); + + if ((bestArm == arm && pend0 > pend1 + 16) || + (bestArm != arm && pend1 > pend0 + 16)) + bestArm ^= 1; + + /* Update the last accessed block on the correct pd */ + lbInfo->last_accessed_block[bestArm] = block + count - 1; + + return bestArm; +} + +u16 get_updated_dev_handle(struct LD_LOAD_BALANCE_INFO *lbInfo, + struct IO_REQUEST_INFO *io_info) +{ + u8 arm, old_arm; + u16 devHandle; + + old_arm = lbInfo->raid1DevHandle[0] == io_info->devHandle ? 0 : 1; + + /* get best new arm */ + arm = megasas_get_best_arm(lbInfo, old_arm, io_info->ldStartBlock, + io_info->numBlocks); + devHandle = lbInfo->raid1DevHandle[arm]; + atomic_inc(&lbInfo->scsi_pending_cmds[arm]); + + return devHandle; +} diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c new file mode 100644 index 000000000000..c1e09d5a6196 --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c @@ -0,0 +1,2248 @@ +/* + * Linux MegaRAID driver for SAS based RAID controllers + * + * Copyright (c) 2009-2011 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FILE: megaraid_sas_fusion.c + * + * Authors: LSI Corporation + * Sumant Patro + * Adam Radford <linuxraid@lsi.com> + * + * Send feedback to: <megaraidlinux@lsi.com> + * + * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 + * ATTN: Linuxraid + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/list.h> +#include <linux/moduleparam.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/smp_lock.h> +#include <linux/uio.h> +#include <linux/uaccess.h> +#include <linux/fs.h> +#include <linux/compat.h> +#include <linux/blkdev.h> +#include <linux/mutex.h> +#include <linux/poll.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_host.h> + +#include "megaraid_sas_fusion.h" +#include "megaraid_sas.h" + +extern void megasas_free_cmds(struct megasas_instance *instance); +extern struct megasas_cmd *megasas_get_cmd(struct megasas_instance + *instance); +extern void +megasas_complete_cmd(struct megasas_instance *instance, + struct megasas_cmd *cmd, u8 alt_status); +int megasas_is_ldio(struct scsi_cmnd *cmd); +int +wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd); + +void +megasas_return_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd); +int megasas_alloc_cmds(struct megasas_instance *instance); +int +megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs); +int +megasas_issue_polled(struct megasas_instance *instance, + struct megasas_cmd *cmd); + +u8 +MR_BuildRaidContext(struct IO_REQUEST_INFO *io_info, + struct RAID_CONTEXT *pRAID_Context, + struct MR_FW_RAID_MAP_ALL *map); +u16 MR_TargetIdToLdGet(u32 ldTgtId, struct MR_FW_RAID_MAP_ALL *map); +struct MR_LD_RAID *MR_LdRaidGet(u32 ld, struct MR_FW_RAID_MAP_ALL *map); + +u16 MR_GetLDTgtId(u32 ld, struct MR_FW_RAID_MAP_ALL *map); +u8 MR_ValidateMapInfo(struct MR_FW_RAID_MAP_ALL *map, + struct LD_LOAD_BALANCE_INFO *lbInfo); +u16 get_updated_dev_handle(struct LD_LOAD_BALANCE_INFO *lbInfo, + struct IO_REQUEST_INFO *in_info); +int megasas_transition_to_ready(struct megasas_instance *instance); +void megaraid_sas_kill_hba(struct megasas_instance *instance); + +extern u32 megasas_dbg_lvl; + +/** + * megasas_enable_intr_fusion - Enables interrupts + * @regs: MFI register set + */ +void +megasas_enable_intr_fusion(struct megasas_register_set __iomem *regs) +{ + writel(~MFI_FUSION_ENABLE_INTERRUPT_MASK, &(regs)->outbound_intr_mask); + + /* Dummy readl to force pci flush */ + readl(®s->outbound_intr_mask); +} + +/** + * megasas_disable_intr_fusion - Disables interrupt + * @regs: MFI register set + */ +void +megasas_disable_intr_fusion(struct megasas_register_set __iomem *regs) +{ + u32 mask = 0xFFFFFFFF; + u32 status; + + writel(mask, ®s->outbound_intr_mask); + /* Dummy readl to force pci flush */ + status = readl(®s->outbound_intr_mask); +} + +int +megasas_clear_intr_fusion(struct megasas_register_set __iomem *regs) +{ + u32 status; + /* + * Check if it is our interrupt + */ + status = readl(®s->outbound_intr_status); + + if (status & 1) { + writel(status, ®s->outbound_intr_status); + readl(®s->outbound_intr_status); + return 1; + } + if (!(status & MFI_FUSION_ENABLE_INTERRUPT_MASK)) + return 0; + + /* + * dummy read to flush PCI + */ + readl(®s->outbound_intr_status); + + return 1; +} + +/** + * megasas_get_cmd_fusion - Get a command from the free pool + * @instance: Adapter soft state + * + * Returns a free command from the pool + */ +struct megasas_cmd_fusion *megasas_get_cmd_fusion(struct megasas_instance + *instance) +{ + unsigned long flags; + struct fusion_context *fusion = + (struct fusion_context *)instance->ctrl_context; + struct megasas_cmd_fusion *cmd = NULL; + + spin_lock_irqsave(&fusion->cmd_pool_lock, flags); + + if (!list_empty(&fusion->cmd_pool)) { + cmd = list_entry((&fusion->cmd_pool)->next, + struct megasas_cmd_fusion, list); + list_del_init(&cmd->list); + } else { + printk(KERN_ERR "megasas: Command pool (fusion) empty!\n"); + } + + spin_unlock_irqrestore(&fusion->cmd_pool_lock, flags); + return cmd; +} + +/** + * megasas_return_cmd_fusion - Return a cmd to free command pool + * @instance: Adapter soft state + * @cmd: Command packet to be returned to free command pool + */ +static inline void +megasas_return_cmd_fusion(struct megasas_instance *instance, + struct megasas_cmd_fusion *cmd) +{ + unsigned long flags; + struct fusion_context *fusion = + (struct fusion_context *)instance->ctrl_context; + + spin_lock_irqsave(&fusion->cmd_pool_lock, flags); + + cmd->scmd = NULL; + cmd->sync_cmd_idx = (u32)ULONG_MAX; + list_add_tail(&cmd->list, &fusion->cmd_pool); + + spin_unlock_irqrestore(&fusion->cmd_pool_lock, flags); +} + +/** + * megasas_teardown_frame_pool_fusion - Destroy the cmd frame DMA pool + * @instance: Adapter soft state + */ +static void megasas_teardown_frame_pool_fusion( + struct megasas_instance *instance) +{ + int i; + struct fusion_context *fusion = instance->ctrl_context; + + u16 max_cmd = instance->max_fw_cmds; + + struct megasas_cmd_fusion *cmd; + + if (!fusion->sg_dma_pool || !fusion->sense_dma_pool) { + printk(KERN_ERR "megasas: dma pool is null. SG Pool %p, " + "sense pool : %p\n", fusion->sg_dma_pool, + fusion->sense_dma_pool); + return; + } + + /* + * Return all frames to pool + */ + for (i = 0; i < max_cmd; i++) { + + cmd = fusion->cmd_list[i]; + + if (cmd->sg_frame) + pci_pool_free(fusion->sg_dma_pool, cmd->sg_frame, + cmd->sg_frame_phys_addr); + + if (cmd->sense) + pci_pool_free(fusion->sense_dma_pool, cmd->sense, + cmd->sense_phys_addr); + } + + /* + * Now destroy the pool itself + */ + pci_pool_destroy(fusion->sg_dma_pool); + pci_pool_destroy(fusion->sense_dma_pool); + + fusion->sg_dma_pool = NULL; + fusion->sense_dma_pool = NULL; +} + +/** + * megasas_free_cmds_fusion - Free all the cmds in the free cmd pool + * @instance: Adapter soft state + */ +void +megasas_free_cmds_fusion(struct megasas_instance *instance) +{ + int i; + struct fusion_context *fusion = instance->ctrl_context; + + u32 max_cmds, req_sz, reply_sz, io_frames_sz; + + + req_sz = fusion->request_alloc_sz; + reply_sz = fusion->reply_alloc_sz; + io_frames_sz = fusion->io_frames_alloc_sz; + + max_cmds = instance->max_fw_cmds; + + /* Free descriptors and request Frames memory */ + if (fusion->req_frames_desc) + dma_free_coherent(&instance->pdev->dev, req_sz, + fusion->req_frames_desc, + fusion->req_frames_desc_phys); + + if (fusion->reply_frames_desc) { + pci_pool_free(fusion->reply_frames_desc_pool, + fusion->reply_frames_desc, + fusion->reply_frames_desc_phys); + pci_pool_destroy(fusion->reply_frames_desc_pool); + } + + if (fusion->io_request_frames) { + pci_pool_free(fusion->io_request_frames_pool, + fusion->io_request_frames, + fusion->io_request_frames_phys); + pci_pool_destroy(fusion->io_request_frames_pool); + } + + /* Free the Fusion frame pool */ + megasas_teardown_frame_pool_fusion(instance); + + /* Free all the commands in the cmd_list */ + for (i = 0; i < max_cmds; i++) + kfree(fusion->cmd_list[i]); + + /* Free the cmd_list buffer itself */ + kfree(fusion->cmd_list); + fusion->cmd_list = NULL; + + INIT_LIST_HEAD(&fusion->cmd_pool); +} + +/** + * megasas_create_frame_pool_fusion - Creates DMA pool for cmd frames + * @instance: Adapter soft state + * + */ +static int megasas_create_frame_pool_fusion(struct megasas_instance *instance) +{ + int i; + u32 max_cmd; + struct fusion_context *fusion; + struct megasas_cmd_fusion *cmd; + u32 total_sz_chain_frame; + + fusion = instance->ctrl_context; + max_cmd = instance->max_fw_cmds; + + total_sz_chain_frame = MEGASAS_MAX_SZ_CHAIN_FRAME; + + /* + * Use DMA pool facility provided by PCI layer + */ + + fusion->sg_dma_pool = pci_pool_create("megasas sg pool fusion", + instance->pdev, + total_sz_chain_frame, 4, + 0); + if (!fusion->sg_dma_pool) { + printk(KERN_DEBUG "megasas: failed to setup request pool " + "fusion\n"); + return -ENOMEM; + } + fusion->sense_dma_pool = pci_pool_create("megasas sense pool fusion", + instance->pdev, + SCSI_SENSE_BUFFERSIZE, 64, 0); + + if (!fusion->sense_dma_pool) { + printk(KERN_DEBUG "megasas: failed to setup sense pool " + "fusion\n"); + pci_pool_destroy(fusion->sg_dma_pool); + fusion->sg_dma_pool = NULL; + return -ENOMEM; + } + + /* + * Allocate and attach a frame to each of the commands in cmd_list + */ + for (i = 0; i < max_cmd; i++) { + + cmd = fusion->cmd_list[i]; + + cmd->sg_frame = pci_pool_alloc(fusion->sg_dma_pool, + GFP_KERNEL, + &cmd->sg_frame_phys_addr); + + cmd->sense = pci_pool_alloc(fusion->sense_dma_pool, + GFP_KERNEL, &cmd->sense_phys_addr); + /* + * megasas_teardown_frame_pool_fusion() takes care of freeing + * whatever has been allocated + */ + if (!cmd->sg_frame || !cmd->sense) { + printk(KERN_DEBUG "megasas: pci_pool_alloc failed\n"); + megasas_teardown_frame_pool_fusion(instance); + return -ENOMEM; + } + } + return 0; +} + +/** + * megasas_alloc_cmds_fusion - Allocates the command packets + * @instance: Adapter soft state + * + * + * Each frame has a 32-bit field called context. This context is used to get + * back the megasas_cmd_fusion from the frame when a frame gets completed + * In this driver, the 32 bit values are the indices into an array cmd_list. + * This array is used only to look up the megasas_cmd_fusion given the context. + * The free commands themselves are maintained in a linked list called cmd_pool. + * + * cmds are formed in the io_request and sg_frame members of the + * megasas_cmd_fusion. The context field is used to get a request descriptor + * and is used as SMID of the cmd. + * SMID value range is from 1 to max_fw_cmds. + */ +int +megasas_alloc_cmds_fusion(struct megasas_instance *instance) +{ + int i, j; + u32 max_cmd, io_frames_sz; + struct fusion_context *fusion; + struct megasas_cmd_fusion *cmd; + union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc; + u32 offset; + dma_addr_t io_req_base_phys; + u8 *io_req_base; + + fusion = instance->ctrl_context; + + max_cmd = instance->max_fw_cmds; + + fusion->req_frames_desc = + dma_alloc_coherent(&instance->pdev->dev, + fusion->request_alloc_sz, + &fusion->req_frames_desc_phys, GFP_KERNEL); + + if (!fusion->req_frames_desc) { + printk(KERN_ERR "megasas; Could not allocate memory for " + "request_frames\n"); + goto fail_req_desc; + } + + fusion->reply_frames_desc_pool = + pci_pool_create("reply_frames pool", instance->pdev, + fusion->reply_alloc_sz, 16, 0); + + if (!fusion->reply_frames_desc_pool) { + printk(KERN_ERR "megasas; Could not allocate memory for " + "reply_frame pool\n"); + goto fail_reply_desc; + } + + fusion->reply_frames_desc = + pci_pool_alloc(fusion->reply_frames_desc_pool, GFP_KERNEL, + &fusion->reply_frames_desc_phys); + if (!fusion->reply_frames_desc) { + printk(KERN_ERR "megasas; Could not allocate memory for " + "reply_frame pool\n"); + pci_pool_destroy(fusion->reply_frames_desc_pool); + goto fail_reply_desc; + } + + reply_desc = fusion->reply_frames_desc; + for (i = 0; i < fusion->reply_q_depth; i++, reply_desc++) + reply_desc->Words = ULLONG_MAX; + + io_frames_sz = fusion->io_frames_alloc_sz; + + fusion->io_request_frames_pool = + pci_pool_create("io_request_frames pool", instance->pdev, + fusion->io_frames_alloc_sz, 16, 0); + + if (!fusion->io_request_frames_pool) { + printk(KERN_ERR "megasas: Could not allocate memory for " + "io_request_frame pool\n"); + goto fail_io_frames; + } + + fusion->io_request_frames = + pci_pool_alloc(fusion->io_request_frames_pool, GFP_KERNEL, + &fusion->io_request_frames_phys); + if (!fusion->io_request_frames) { + printk(KERN_ERR "megasas: Could not allocate memory for " + "io_request_frames frames\n"); + pci_pool_destroy(fusion->io_request_frames_pool); + goto fail_io_frames; + } + + /* + * fusion->cmd_list is an array of struct megasas_cmd_fusion pointers. + * Allocate the dynamic array first and then allocate individual + * commands. + */ + fusion->cmd_list = kmalloc(sizeof(struct megasas_cmd_fusion *) + *max_cmd, GFP_KERNEL); + + if (!fusion->cmd_list) { + printk(KERN_DEBUG "megasas: out of memory. Could not alloc " + "memory for cmd_list_fusion\n"); + goto fail_cmd_list; + } + + memset(fusion->cmd_list, 0, sizeof(struct megasas_cmd_fusion *) + *max_cmd); + + max_cmd = instance->max_fw_cmds; + for (i = 0; i < max_cmd; i++) { + fusion->cmd_list[i] = kmalloc(sizeof(struct megasas_cmd_fusion), + GFP_KERNEL); + if (!fusion->cmd_list[i]) { + printk(KERN_ERR "Could not alloc cmd list fusion\n"); + + for (j = 0; j < i; j++) + kfree(fusion->cmd_list[j]); + + kfree(fusion->cmd_list); + fusion->cmd_list = NULL; + goto fail_cmd_list; + } + } + + /* The first 256 bytes (SMID 0) is not used. Don't add to cmd list */ + io_req_base = fusion->io_request_frames + + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE; + io_req_base_phys = fusion->io_request_frames_phys + + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE; + + /* + * Add all the commands to command pool (fusion->cmd_pool) + */ + + /* SMID 0 is reserved. Set SMID/index from 1 */ + for (i = 0; i < max_cmd; i++) { + cmd = fusion->cmd_list[i]; + offset = MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE * i; + memset(cmd, 0, sizeof(struct megasas_cmd_fusion)); + cmd->index = i + 1; + cmd->scmd = NULL; + cmd->sync_cmd_idx = (u32)ULONG_MAX; /* Set to Invalid */ + cmd->instance = instance; + cmd->io_request = + (struct MPI2_RAID_SCSI_IO_REQUEST *) + (io_req_base + offset); + memset(cmd->io_request, 0, + sizeof(struct MPI2_RAID_SCSI_IO_REQUEST)); + cmd->io_request_phys_addr = io_req_base_phys + offset; + + list_add_tail(&cmd->list, &fusion->cmd_pool); + } + + /* + * Create a frame pool and assign one frame to each cmd + */ + if (megasas_create_frame_pool_fusion(instance)) { + printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n"); + megasas_free_cmds_fusion(instance); + goto fail_req_desc; + } + + return 0; + +fail_cmd_list: + pci_pool_free(fusion->io_request_frames_pool, fusion->io_request_frames, + fusion->io_request_frames_phys); + pci_pool_destroy(fusion->io_request_frames_pool); +fail_io_frames: + dma_free_coherent(&instance->pdev->dev, fusion->request_alloc_sz, + fusion->reply_frames_desc, + fusion->reply_frames_desc_phys); + pci_pool_free(fusion->reply_frames_desc_pool, + fusion->reply_frames_desc, + fusion->reply_frames_desc_phys); + pci_pool_destroy(fusion->reply_frames_desc_pool); + +fail_reply_desc: + dma_free_coherent(&instance->pdev->dev, fusion->request_alloc_sz, + fusion->req_frames_desc, + fusion->req_frames_desc_phys); +fail_req_desc: + return -ENOMEM; +} + +/** + * wait_and_poll - Issues a polling command + * @instance: Adapter soft state + * @cmd: Command packet to be issued + * + * For polling, MFI requires the cmd_status to be set to 0xFF before posting. + */ +int +wait_and_poll(struct megasas_instance *instance, struct megasas_cmd *cmd) +{ + int i; + struct megasas_header *frame_hdr = &cmd->frame->hdr; + + u32 msecs = MFI_POLL_TIMEOUT_SECS * 1000; + + /* + * Wait for cmd_status to change + */ + for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i += 20) { + rmb(); + msleep(20); + } + + if (frame_hdr->cmd_status == 0xff) + return -ETIME; + + return 0; +} + +/** + * megasas_ioc_init_fusion - Initializes the FW + * @instance: Adapter soft state + * + * Issues the IOC Init cmd + */ +int +megasas_ioc_init_fusion(struct megasas_instance *instance) +{ + struct megasas_init_frame *init_frame; + struct MPI2_IOC_INIT_REQUEST *IOCInitMessage; + dma_addr_t ioc_init_handle; + u32 context; + struct megasas_cmd *cmd; + u8 ret; + struct fusion_context *fusion; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + int i; + struct megasas_header *frame_hdr; + + fusion = instance->ctrl_context; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_ERR "Could not allocate cmd for INIT Frame\n"); + ret = 1; + goto fail_get_cmd; + } + + IOCInitMessage = + dma_alloc_coherent(&instance->pdev->dev, + sizeof(struct MPI2_IOC_INIT_REQUEST), + &ioc_init_handle, GFP_KERNEL); + + if (!IOCInitMessage) { + printk(KERN_ERR "Could not allocate memory for " + "IOCInitMessage\n"); + ret = 1; + goto fail_fw_init; + } + + memset(IOCInitMessage, 0, sizeof(struct MPI2_IOC_INIT_REQUEST)); + + IOCInitMessage->Function = MPI2_FUNCTION_IOC_INIT; + IOCInitMessage->WhoInit = MPI2_WHOINIT_HOST_DRIVER; + IOCInitMessage->MsgVersion = MPI2_VERSION; + IOCInitMessage->HeaderVersion = MPI2_HEADER_VERSION; + IOCInitMessage->SystemRequestFrameSize = + MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE / 4; + + IOCInitMessage->ReplyDescriptorPostQueueDepth = fusion->reply_q_depth; + IOCInitMessage->ReplyDescriptorPostQueueAddress = + fusion->reply_frames_desc_phys; + IOCInitMessage->SystemRequestFrameBaseAddress = + fusion->io_request_frames_phys; + + init_frame = (struct megasas_init_frame *)cmd->frame; + memset(init_frame, 0, MEGAMFI_FRAME_SIZE); + + frame_hdr = &cmd->frame->hdr; + context = init_frame->context; + init_frame->context = context; + + frame_hdr->cmd_status = 0xFF; + frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + + init_frame->cmd = MFI_CMD_INIT; + init_frame->cmd_status = 0xFF; + + init_frame->queue_info_new_phys_addr_lo = ioc_init_handle; + init_frame->data_xfer_len = sizeof(struct MPI2_IOC_INIT_REQUEST); + + req_desc = + (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)fusion->req_frames_desc; + + req_desc->Words = cmd->frame_phys_addr; + req_desc->MFAIo.RequestFlags = + (MEGASAS_REQ_DESCRIPT_FLAGS_MFA << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + + /* + * disable the intr before firing the init frame + */ + instance->instancet->disable_intr(instance->reg_set); + + for (i = 0; i < (10 * 1000); i += 20) { + if (readl(&instance->reg_set->doorbell) & 1) + msleep(20); + else + break; + } + + instance->instancet->fire_cmd(instance, req_desc->u.low, + req_desc->u.high, instance->reg_set); + + wait_and_poll(instance, cmd); + + frame_hdr = &cmd->frame->hdr; + if (frame_hdr->cmd_status != 0) { + ret = 1; + goto fail_fw_init; + } + printk(KERN_ERR "megasas:IOC Init cmd success\n"); + + ret = 0; + +fail_fw_init: + megasas_return_cmd(instance, cmd); + if (IOCInitMessage) + dma_free_coherent(&instance->pdev->dev, + sizeof(struct MPI2_IOC_INIT_REQUEST), + IOCInitMessage, ioc_init_handle); +fail_get_cmd: + return ret; +} + +/* + * megasas_return_cmd_for_smid - Returns a cmd_fusion for a SMID + * @instance: Adapter soft state + * + */ +void +megasas_return_cmd_for_smid(struct megasas_instance *instance, u16 smid) +{ + struct fusion_context *fusion; + struct megasas_cmd_fusion *cmd; + + fusion = instance->ctrl_context; + cmd = fusion->cmd_list[smid - 1]; + megasas_return_cmd_fusion(instance, cmd); +} + +/* + * megasas_get_ld_map_info - Returns FW's ld_map structure + * @instance: Adapter soft state + * @pend: Pend the command or not + * 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_map_info(struct megasas_instance *instance) +{ + int ret = 0; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + struct MR_FW_RAID_MAP_ALL *ci; + dma_addr_t ci_h = 0; + u32 size_map_info; + struct fusion_context *fusion; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_DEBUG "megasas: Failed to get cmd for map info.\n"); + return -ENOMEM; + } + + fusion = instance->ctrl_context; + + if (!fusion) { + megasas_return_cmd(instance, cmd); + return 1; + } + + dcmd = &cmd->frame->dcmd; + + size_map_info = sizeof(struct MR_FW_RAID_MAP) + + (sizeof(struct MR_LD_SPAN_MAP) *(MAX_LOGICAL_DRIVES - 1)); + + ci = fusion->ld_map[(instance->map_id & 1)]; + ci_h = fusion->ld_map_phys[(instance->map_id & 1)]; + + if (!ci) { + printk(KERN_DEBUG "Failed to alloc mem for ld_map_info\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->pad_0 = 0; + dcmd->data_xfer_len = size_map_info; + dcmd->opcode = MR_DCMD_LD_MAP_GET_INFO; + dcmd->sgl.sge32[0].phys_addr = ci_h; + dcmd->sgl.sge32[0].length = size_map_info; + + if (!megasas_issue_polled(instance, cmd)) + ret = 0; + else { + printk(KERN_ERR "megasas: Get LD Map Info Failed\n"); + ret = -1; + } + + megasas_return_cmd(instance, cmd); + + return ret; +} + +u8 +megasas_get_map_info(struct megasas_instance *instance) +{ + struct fusion_context *fusion = instance->ctrl_context; + + fusion->fast_path_io = 0; + if (!megasas_get_ld_map_info(instance)) { + if (MR_ValidateMapInfo(fusion->ld_map[(instance->map_id & 1)], + fusion->load_balance_info)) { + fusion->fast_path_io = 1; + return 0; + } + } + return 1; +} + +/* + * megasas_sync_map_info - Returns FW's ld_map structure + * @instance: Adapter soft state + * + * 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. + */ +int +megasas_sync_map_info(struct megasas_instance *instance) +{ + int ret = 0, i; + struct megasas_cmd *cmd; + struct megasas_dcmd_frame *dcmd; + u32 size_sync_info, num_lds; + struct fusion_context *fusion; + struct MR_LD_TARGET_SYNC *ci = NULL; + struct MR_FW_RAID_MAP_ALL *map; + struct MR_LD_RAID *raid; + struct MR_LD_TARGET_SYNC *ld_sync; + dma_addr_t ci_h = 0; + u32 size_map_info; + + cmd = megasas_get_cmd(instance); + + if (!cmd) { + printk(KERN_DEBUG "megasas: Failed to get cmd for sync" + "info.\n"); + return -ENOMEM; + } + + fusion = instance->ctrl_context; + + if (!fusion) { + megasas_return_cmd(instance, cmd); + return 1; + } + + map = fusion->ld_map[instance->map_id & 1]; + + num_lds = map->raidMap.ldCount; + + dcmd = &cmd->frame->dcmd; + + size_sync_info = sizeof(struct MR_LD_TARGET_SYNC) *num_lds; + + memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); + + ci = (struct MR_LD_TARGET_SYNC *) + fusion->ld_map[(instance->map_id - 1) & 1]; + memset(ci, 0, sizeof(struct MR_FW_RAID_MAP_ALL)); + + ci_h = fusion->ld_map_phys[(instance->map_id - 1) & 1]; + + ld_sync = (struct MR_LD_TARGET_SYNC *)ci; + + for (i = 0; i < num_lds; i++, ld_sync++) { + raid = MR_LdRaidGet(i, map); + ld_sync->targetId = MR_GetLDTgtId(i, map); + ld_sync->seqNum = raid->seqNum; + } + + size_map_info = sizeof(struct MR_FW_RAID_MAP) + + (sizeof(struct MR_LD_SPAN_MAP) *(MAX_LOGICAL_DRIVES - 1)); + + dcmd->cmd = MFI_CMD_DCMD; + dcmd->cmd_status = 0xFF; + dcmd->sge_count = 1; + dcmd->flags = MFI_FRAME_DIR_WRITE; + dcmd->timeout = 0; + dcmd->pad_0 = 0; + dcmd->data_xfer_len = size_map_info; + dcmd->mbox.b[0] = num_lds; + dcmd->mbox.b[1] = MEGASAS_DCMD_MBOX_PEND_FLAG; + dcmd->opcode = MR_DCMD_LD_MAP_GET_INFO; + dcmd->sgl.sge32[0].phys_addr = ci_h; + dcmd->sgl.sge32[0].length = size_map_info; + + instance->map_update_cmd = cmd; + + instance->instancet->issue_dcmd(instance, cmd); + + return ret; +} + +/** + * megasas_init_adapter_fusion - Initializes the FW + * @instance: Adapter soft state + * + * This is the main function for initializing firmware. + */ +u32 +megasas_init_adapter_fusion(struct megasas_instance *instance) +{ + struct megasas_register_set __iomem *reg_set; + struct fusion_context *fusion; + u32 max_cmd; + int i = 0; + + fusion = instance->ctrl_context; + + reg_set = instance->reg_set; + + /* + * Get various operational parameters from status register + */ + instance->max_fw_cmds = + instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF; + instance->max_fw_cmds = min(instance->max_fw_cmds, (u16)1008); + + /* + * Reduce the max supported cmds by 1. This is to ensure that the + * reply_q_sz (1 more than the max cmd that driver may send) + * does not exceed max cmds that the FW can support + */ + instance->max_fw_cmds = instance->max_fw_cmds-1; + /* Only internal cmds (DCMD) need to have MFI frames */ + instance->max_mfi_cmds = MEGASAS_INT_CMDS; + + max_cmd = instance->max_fw_cmds; + + fusion->reply_q_depth = ((max_cmd + 1 + 15)/16)*16; + + fusion->request_alloc_sz = + sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) *max_cmd; + fusion->reply_alloc_sz = sizeof(union MPI2_REPLY_DESCRIPTORS_UNION) + *(fusion->reply_q_depth); + fusion->io_frames_alloc_sz = MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE + + (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE * + (max_cmd + 1)); /* Extra 1 for SMID 0 */ + + fusion->max_sge_in_main_msg = + (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE - + offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL))/16; + + fusion->max_sge_in_chain = + MEGASAS_MAX_SZ_CHAIN_FRAME / sizeof(union MPI2_SGE_IO_UNION); + + instance->max_num_sge = fusion->max_sge_in_main_msg + + fusion->max_sge_in_chain - 2; + + /* Used for pass thru MFI frame (DCMD) */ + fusion->chain_offset_mfi_pthru = + offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL)/16; + + fusion->chain_offset_io_request = + (MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE - + sizeof(union MPI2_SGE_IO_UNION))/16; + + fusion->last_reply_idx = 0; + + /* + * Allocate memory for descriptors + * Create a pool of commands + */ + if (megasas_alloc_cmds(instance)) + goto fail_alloc_mfi_cmds; + if (megasas_alloc_cmds_fusion(instance)) + goto fail_alloc_cmds; + + if (megasas_ioc_init_fusion(instance)) + goto fail_ioc_init; + + instance->flag_ieee = 1; + + fusion->map_sz = sizeof(struct MR_FW_RAID_MAP) + + (sizeof(struct MR_LD_SPAN_MAP) *(MAX_LOGICAL_DRIVES - 1)); + + fusion->fast_path_io = 0; + + for (i = 0; i < 2; i++) { + fusion->ld_map[i] = dma_alloc_coherent(&instance->pdev->dev, + fusion->map_sz, + &fusion->ld_map_phys[i], + GFP_KERNEL); + if (!fusion->ld_map[i]) { + printk(KERN_ERR "megasas: Could not allocate memory " + "for map info\n"); + goto fail_map_info; + } + } + + if (!megasas_get_map_info(instance)) + megasas_sync_map_info(instance); + + return 0; + +fail_alloc_cmds: +fail_alloc_mfi_cmds: +fail_map_info: + if (i == 1) + dma_free_coherent(&instance->pdev->dev, fusion->map_sz, + fusion->ld_map[0], fusion->ld_map_phys[0]); +fail_ioc_init: + return 1; +} + +/** + * megasas_fire_cmd_fusion - Sends command to the FW + * @frame_phys_addr : Physical address of cmd + * @frame_count : Number of frames for the command + * @regs : MFI register set + */ +void +megasas_fire_cmd_fusion(struct megasas_instance *instance, + dma_addr_t req_desc_lo, + u32 req_desc_hi, + struct megasas_register_set __iomem *regs) +{ + unsigned long flags; + + spin_lock_irqsave(&instance->hba_lock, flags); + + writel(req_desc_lo, + &(regs)->inbound_low_queue_port); + writel(req_desc_hi, &(regs)->inbound_high_queue_port); + spin_unlock_irqrestore(&instance->hba_lock, flags); +} + +/** + * map_cmd_status - Maps FW cmd status to OS cmd status + * @cmd : Pointer to cmd + * @status : status of cmd returned by FW + * @ext_status : ext status of cmd returned by FW + */ + +void +map_cmd_status(struct megasas_cmd_fusion *cmd, u8 status, u8 ext_status) +{ + + switch (status) { + + case MFI_STAT_OK: + cmd->scmd->result = DID_OK << 16; + break; + + case MFI_STAT_SCSI_IO_FAILED: + case MFI_STAT_LD_INIT_IN_PROGRESS: + cmd->scmd->result = (DID_ERROR << 16) | ext_status; + break; + + case MFI_STAT_SCSI_DONE_WITH_ERROR: + + cmd->scmd->result = (DID_OK << 16) | ext_status; + if (ext_status == SAM_STAT_CHECK_CONDITION) { + memset(cmd->scmd->sense_buffer, 0, + SCSI_SENSE_BUFFERSIZE); + memcpy(cmd->scmd->sense_buffer, cmd->sense, + SCSI_SENSE_BUFFERSIZE); + cmd->scmd->result |= DRIVER_SENSE << 24; + } + break; + + case MFI_STAT_LD_OFFLINE: + case MFI_STAT_DEVICE_NOT_FOUND: + cmd->scmd->result = DID_BAD_TARGET << 16; + break; + + default: + printk(KERN_DEBUG "megasas: FW status %#x\n", status); + cmd->scmd->result = DID_ERROR << 16; + break; + } +} + +/** + * megasas_make_sgl_fusion - Prepares 32-bit SGL + * @instance: Adapter soft state + * @scp: SCSI command from the mid-layer + * @sgl_ptr: SGL to be filled in + * @cmd: cmd we are working on + * + * If successful, this function returns the number of SG elements. + */ +static int +megasas_make_sgl_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scp, + struct MPI25_IEEE_SGE_CHAIN64 *sgl_ptr, + struct megasas_cmd_fusion *cmd) +{ + int i, sg_processed; + int sge_count, sge_idx; + struct scatterlist *os_sgl; + struct fusion_context *fusion; + + fusion = instance->ctrl_context; + + cmd->io_request->ChainOffset = 0; + + sge_count = scsi_dma_map(scp); + + BUG_ON(sge_count < 0); + + if (sge_count > instance->max_num_sge || !sge_count) + return sge_count; + + if (sge_count > fusion->max_sge_in_main_msg) { + /* One element to store the chain info */ + sge_idx = fusion->max_sge_in_main_msg - 1; + } else + sge_idx = sge_count; + + scsi_for_each_sg(scp, os_sgl, sge_count, i) { + sgl_ptr->Length = sg_dma_len(os_sgl); + sgl_ptr->Address = sg_dma_address(os_sgl); + sgl_ptr->Flags = 0; + sgl_ptr++; + + sg_processed = i + 1; + + if ((sg_processed == (fusion->max_sge_in_main_msg - 1)) && + (sge_count > fusion->max_sge_in_main_msg)) { + + struct MPI25_IEEE_SGE_CHAIN64 *sg_chain; + cmd->io_request->ChainOffset = + fusion->chain_offset_io_request; + sg_chain = sgl_ptr; + /* Prepare chain element */ + sg_chain->NextChainOffset = 0; + sg_chain->Flags = (IEEE_SGE_FLAGS_CHAIN_ELEMENT | + MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR); + sg_chain->Length = (sizeof(union MPI2_SGE_IO_UNION) + *(sge_count - sg_processed)); + sg_chain->Address = cmd->sg_frame_phys_addr; + + sgl_ptr = + (struct MPI25_IEEE_SGE_CHAIN64 *)cmd->sg_frame; + } + } + + return sge_count; +} + +/** + * megasas_set_pd_lba - Sets PD LBA + * @cdb: CDB + * @cdb_len: cdb length + * @start_blk: Start block of IO + * + * Used to set the PD LBA in CDB for FP IOs + */ +void +megasas_set_pd_lba(struct MPI2_RAID_SCSI_IO_REQUEST *io_request, u8 cdb_len, + struct IO_REQUEST_INFO *io_info, struct scsi_cmnd *scp, + struct MR_FW_RAID_MAP_ALL *local_map_ptr, u32 ref_tag) +{ + struct MR_LD_RAID *raid; + u32 ld; + u64 start_blk = io_info->pdBlock; + u8 *cdb = io_request->CDB.CDB32; + u32 num_blocks = io_info->numBlocks; + u8 opcode, flagvals, groupnum, control; + + /* Check if T10 PI (DIF) is enabled for this LD */ + ld = MR_TargetIdToLdGet(io_info->ldTgtId, local_map_ptr); + raid = MR_LdRaidGet(ld, local_map_ptr); + if (raid->capability.ldPiMode == MR_PROT_INFO_TYPE_CONTROLLER) { + memset(cdb, 0, sizeof(io_request->CDB.CDB32)); + cdb[0] = MEGASAS_SCSI_VARIABLE_LENGTH_CMD; + cdb[7] = MEGASAS_SCSI_ADDL_CDB_LEN; + + if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + cdb[9] = MEGASAS_SCSI_SERVICE_ACTION_READ32; + else + cdb[9] = MEGASAS_SCSI_SERVICE_ACTION_WRITE32; + cdb[10] = MEGASAS_RD_WR_PROTECT_CHECK_ALL; + + /* LBA */ + cdb[12] = (u8)((start_blk >> 56) & 0xff); + cdb[13] = (u8)((start_blk >> 48) & 0xff); + cdb[14] = (u8)((start_blk >> 40) & 0xff); + cdb[15] = (u8)((start_blk >> 32) & 0xff); + cdb[16] = (u8)((start_blk >> 24) & 0xff); + cdb[17] = (u8)((start_blk >> 16) & 0xff); + cdb[18] = (u8)((start_blk >> 8) & 0xff); + cdb[19] = (u8)(start_blk & 0xff); + + /* Logical block reference tag */ + io_request->CDB.EEDP32.PrimaryReferenceTag = + cpu_to_be32(ref_tag); + io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0xffff; + + io_request->DataLength = num_blocks * 512; + io_request->IoFlags = 32; /* Specify 32-byte cdb */ + + /* Transfer length */ + cdb[28] = (u8)((num_blocks >> 24) & 0xff); + cdb[29] = (u8)((num_blocks >> 16) & 0xff); + cdb[30] = (u8)((num_blocks >> 8) & 0xff); + cdb[31] = (u8)(num_blocks & 0xff); + + /* set SCSI IO EEDPFlags */ + if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) { + io_request->EEDPFlags = + MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | + MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | + MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP | + MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG | + MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD; + } else { + io_request->EEDPFlags = + MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | + MPI2_SCSIIO_EEDPFLAGS_INSERT_OP; + } + io_request->Control |= (0x4 << 26); + io_request->EEDPBlockSize = MEGASAS_EEDPBLOCKSIZE; + } else { + /* Some drives don't support 16/12 byte CDB's, convert to 10 */ + if (((cdb_len == 12) || (cdb_len == 16)) && + (start_blk <= 0xffffffff)) { + if (cdb_len == 16) { + opcode = cdb[0] == READ_16 ? READ_10 : WRITE_10; + flagvals = cdb[1]; + groupnum = cdb[14]; + control = cdb[15]; + } else { + opcode = cdb[0] == READ_12 ? READ_10 : WRITE_10; + flagvals = cdb[1]; + groupnum = cdb[10]; + control = cdb[11]; + } + + memset(cdb, 0, sizeof(io_request->CDB.CDB32)); + + cdb[0] = opcode; + cdb[1] = flagvals; + cdb[6] = groupnum; + cdb[9] = control; + + /* Transfer length */ + cdb[8] = (u8)(num_blocks & 0xff); + cdb[7] = (u8)((num_blocks >> 8) & 0xff); + + cdb_len = 10; + } + + /* Normal case, just load LBA here */ + switch (cdb_len) { + case 6: + { + u8 val = cdb[1] & 0xE0; + cdb[3] = (u8)(start_blk & 0xff); + cdb[2] = (u8)((start_blk >> 8) & 0xff); + cdb[1] = val | ((u8)(start_blk >> 16) & 0x1f); + break; + } + case 10: + cdb[5] = (u8)(start_blk & 0xff); + cdb[4] = (u8)((start_blk >> 8) & 0xff); + cdb[3] = (u8)((start_blk >> 16) & 0xff); + cdb[2] = (u8)((start_blk >> 24) & 0xff); + break; + case 12: + cdb[5] = (u8)(start_blk & 0xff); + cdb[4] = (u8)((start_blk >> 8) & 0xff); + cdb[3] = (u8)((start_blk >> 16) & 0xff); + cdb[2] = (u8)((start_blk >> 24) & 0xff); + break; + case 16: + cdb[9] = (u8)(start_blk & 0xff); + cdb[8] = (u8)((start_blk >> 8) & 0xff); + cdb[7] = (u8)((start_blk >> 16) & 0xff); + cdb[6] = (u8)((start_blk >> 24) & 0xff); + cdb[5] = (u8)((start_blk >> 32) & 0xff); + cdb[4] = (u8)((start_blk >> 40) & 0xff); + cdb[3] = (u8)((start_blk >> 48) & 0xff); + cdb[2] = (u8)((start_blk >> 56) & 0xff); + break; + } + } +} + +/** + * megasas_build_ldio_fusion - Prepares IOs to devices + * @instance: Adapter soft state + * @scp: SCSI command + * @cmd: Command to be prepared + * + * Prepares the io_request and chain elements (sg_frame) for IO + * The IO can be for PD (Fast Path) or LD + */ +void +megasas_build_ldio_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scp, + struct megasas_cmd_fusion *cmd) +{ + u8 fp_possible; + u32 start_lba_lo, start_lba_hi, device_id; + struct MPI2_RAID_SCSI_IO_REQUEST *io_request; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + struct IO_REQUEST_INFO io_info; + struct fusion_context *fusion; + struct MR_FW_RAID_MAP_ALL *local_map_ptr; + + device_id = MEGASAS_DEV_INDEX(instance, scp); + + fusion = instance->ctrl_context; + + io_request = cmd->io_request; + io_request->RaidContext.VirtualDiskTgtId = device_id; + io_request->RaidContext.status = 0; + io_request->RaidContext.exStatus = 0; + + req_desc = (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)cmd->request_desc; + + start_lba_lo = 0; + start_lba_hi = 0; + fp_possible = 0; + + /* + * 6-byte READ(0x08) or WRITE(0x0A) cdb + */ + if (scp->cmd_len == 6) { + io_request->DataLength = (u32) scp->cmnd[4]; + start_lba_lo = ((u32) scp->cmnd[1] << 16) | + ((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3]; + + start_lba_lo &= 0x1FFFFF; + } + + /* + * 10-byte READ(0x28) or WRITE(0x2A) cdb + */ + else if (scp->cmd_len == 10) { + io_request->DataLength = (u32) scp->cmnd[8] | + ((u32) scp->cmnd[7] << 8); + start_lba_lo = ((u32) scp->cmnd[2] << 24) | + ((u32) scp->cmnd[3] << 16) | + ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + else if (scp->cmd_len == 12) { + io_request->DataLength = ((u32) scp->cmnd[6] << 24) | + ((u32) scp->cmnd[7] << 16) | + ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; + start_lba_lo = ((u32) scp->cmnd[2] << 24) | + ((u32) scp->cmnd[3] << 16) | + ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; + } + + /* + * 16-byte READ(0x88) or WRITE(0x8A) cdb + */ + else if (scp->cmd_len == 16) { + io_request->DataLength = ((u32) scp->cmnd[10] << 24) | + ((u32) scp->cmnd[11] << 16) | + ((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13]; + start_lba_lo = ((u32) scp->cmnd[6] << 24) | + ((u32) scp->cmnd[7] << 16) | + ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; + + start_lba_hi = ((u32) scp->cmnd[2] << 24) | + ((u32) scp->cmnd[3] << 16) | + ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; + } + + memset(&io_info, 0, sizeof(struct IO_REQUEST_INFO)); + io_info.ldStartBlock = ((u64)start_lba_hi << 32) | start_lba_lo; + io_info.numBlocks = io_request->DataLength; + io_info.ldTgtId = device_id; + + if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + io_info.isRead = 1; + + local_map_ptr = fusion->ld_map[(instance->map_id & 1)]; + + if ((MR_TargetIdToLdGet(device_id, local_map_ptr) >= + MAX_LOGICAL_DRIVES) || (!fusion->fast_path_io)) { + io_request->RaidContext.regLockFlags = 0; + fp_possible = 0; + } else { + if (MR_BuildRaidContext(&io_info, &io_request->RaidContext, + local_map_ptr)) + fp_possible = io_info.fpOkForIo; + } + + if (fp_possible) { + megasas_set_pd_lba(io_request, scp->cmd_len, &io_info, scp, + local_map_ptr, start_lba_lo); + io_request->DataLength = scsi_bufflen(scp); + io_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY + << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + if ((fusion->load_balance_info[device_id].loadBalanceFlag) && + (io_info.isRead)) { + io_info.devHandle = + get_updated_dev_handle( + &fusion->load_balance_info[device_id], + &io_info); + scp->SCp.Status |= MEGASAS_LOAD_BALANCE_FLAG; + } else + scp->SCp.Status &= ~MEGASAS_LOAD_BALANCE_FLAG; + cmd->request_desc->SCSIIO.DevHandle = io_info.devHandle; + io_request->DevHandle = io_info.devHandle; + } else { + io_request->RaidContext.timeoutValue = + local_map_ptr->raidMap.fpPdIoTimeoutSec; + io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; + io_request->DevHandle = device_id; + cmd->request_desc->SCSIIO.RequestFlags = + (MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO + << MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + } /* Not FP */ +} + +/** + * megasas_build_dcdb_fusion - Prepares IOs to devices + * @instance: Adapter soft state + * @scp: SCSI command + * @cmd: Command to be prepared + * + * Prepares the io_request frame for non-io cmds + */ +static void +megasas_build_dcdb_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scmd, + struct megasas_cmd_fusion *cmd) +{ + u32 device_id; + struct MPI2_RAID_SCSI_IO_REQUEST *io_request; + u16 pd_index = 0; + struct MR_FW_RAID_MAP_ALL *local_map_ptr; + struct fusion_context *fusion = instance->ctrl_context; + + io_request = cmd->io_request; + device_id = MEGASAS_DEV_INDEX(instance, scmd); + pd_index = (scmd->device->channel * MEGASAS_MAX_DEV_PER_CHANNEL) + +scmd->device->id; + local_map_ptr = fusion->ld_map[(instance->map_id & 1)]; + + /* Check if this is a system PD I/O */ + if ((instance->pd_list[pd_index].driveState == MR_PD_STATE_SYSTEM) && + (instance->pd_list[pd_index].driveType == TYPE_DISK)) { + io_request->Function = 0; + io_request->DevHandle = + local_map_ptr->raidMap.devHndlInfo[device_id].curDevHdl; + io_request->RaidContext.timeoutValue = + local_map_ptr->raidMap.fpPdIoTimeoutSec; + io_request->RaidContext.regLockFlags = 0; + io_request->RaidContext.regLockRowLBA = 0; + io_request->RaidContext.regLockLength = 0; + io_request->RaidContext.RAIDFlags = + MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD << + MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT; + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + } else { + io_request->Function = MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST; + io_request->DevHandle = device_id; + cmd->request_desc->SCSIIO.RequestFlags = + (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + } + io_request->RaidContext.VirtualDiskTgtId = device_id; + io_request->LUN[0] = scmd->device->lun; + io_request->DataLength = scsi_bufflen(scmd); +} + +/** + * megasas_build_io_fusion - Prepares IOs to devices + * @instance: Adapter soft state + * @scp: SCSI command + * @cmd: Command to be prepared + * + * Invokes helper functions to prepare request frames + * and sets flags appropriate for IO/Non-IO cmd + */ +int +megasas_build_io_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scp, + struct megasas_cmd_fusion *cmd) +{ + u32 device_id, sge_count; + struct MPI2_RAID_SCSI_IO_REQUEST *io_request = cmd->io_request; + + device_id = MEGASAS_DEV_INDEX(instance, scp); + + /* Zero out some fields so they don't get reused */ + io_request->LUN[0] = 0; + io_request->CDB.EEDP32.PrimaryReferenceTag = 0; + io_request->CDB.EEDP32.PrimaryApplicationTagMask = 0; + io_request->EEDPFlags = 0; + io_request->Control = 0; + io_request->EEDPBlockSize = 0; + io_request->IoFlags = 0; + io_request->RaidContext.RAIDFlags = 0; + + memcpy(io_request->CDB.CDB32, scp->cmnd, scp->cmd_len); + /* + * Just the CDB length,rest of the Flags are zero + * This will be modified for FP in build_ldio_fusion + */ + io_request->IoFlags = scp->cmd_len; + + if (megasas_is_ldio(scp)) + megasas_build_ldio_fusion(instance, scp, cmd); + else + megasas_build_dcdb_fusion(instance, scp, cmd); + + /* + * Construct SGL + */ + + sge_count = + megasas_make_sgl_fusion(instance, scp, + (struct MPI25_IEEE_SGE_CHAIN64 *) + &io_request->SGL, cmd); + + if (sge_count > instance->max_num_sge) { + printk(KERN_ERR "megasas: Error. sge_count (0x%x) exceeds " + "max (0x%x) allowed\n", sge_count, + instance->max_num_sge); + return 1; + } + + io_request->RaidContext.numSGE = sge_count; + + io_request->SGLFlags = MPI2_SGE_FLAGS_64_BIT_ADDRESSING; + + if (scp->sc_data_direction == PCI_DMA_TODEVICE) + io_request->Control |= MPI2_SCSIIO_CONTROL_WRITE; + else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) + io_request->Control |= MPI2_SCSIIO_CONTROL_READ; + + io_request->SGLOffset0 = + offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, SGL) / 4; + + io_request->SenseBufferLowAddress = cmd->sense_phys_addr; + io_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; + + cmd->scmd = scp; + scp->SCp.ptr = (char *)cmd; + + return 0; +} + +union MEGASAS_REQUEST_DESCRIPTOR_UNION * +megasas_get_request_descriptor(struct megasas_instance *instance, u16 index) +{ + u8 *p; + struct fusion_context *fusion; + + if (index >= instance->max_fw_cmds) { + printk(KERN_ERR "megasas: Invalid SMID (0x%x)request for " + "descriptor\n", index); + return NULL; + } + fusion = instance->ctrl_context; + p = fusion->req_frames_desc + +sizeof(union MEGASAS_REQUEST_DESCRIPTOR_UNION) *index; + + return (union MEGASAS_REQUEST_DESCRIPTOR_UNION *)p; +} + +/** + * megasas_build_and_issue_cmd_fusion -Main routine for building and + * issuing non IOCTL cmd + * @instance: Adapter soft state + * @scmd: pointer to scsi cmd from OS + */ +static u32 +megasas_build_and_issue_cmd_fusion(struct megasas_instance *instance, + struct scsi_cmnd *scmd) +{ + struct megasas_cmd_fusion *cmd; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + u32 index; + struct fusion_context *fusion; + + fusion = instance->ctrl_context; + + cmd = megasas_get_cmd_fusion(instance); + if (!cmd) + return SCSI_MLQUEUE_HOST_BUSY; + + index = cmd->index; + + req_desc = megasas_get_request_descriptor(instance, index-1); + if (!req_desc) + return 1; + + req_desc->Words = 0; + cmd->request_desc = req_desc; + cmd->request_desc->Words = 0; + + if (megasas_build_io_fusion(instance, scmd, cmd)) { + megasas_return_cmd_fusion(instance, cmd); + printk(KERN_ERR "megasas: Error building command.\n"); + cmd->request_desc = NULL; + return 1; + } + + req_desc = cmd->request_desc; + req_desc->SCSIIO.SMID = index; + + if (cmd->io_request->ChainOffset != 0 && + cmd->io_request->ChainOffset != 0xF) + printk(KERN_ERR "megasas: The chain offset value is not " + "correct : %x\n", cmd->io_request->ChainOffset); + + /* + * Issue the command to the FW + */ + atomic_inc(&instance->fw_outstanding); + + instance->instancet->fire_cmd(instance, + req_desc->u.low, req_desc->u.high, + instance->reg_set); + + return 0; +} + +/** + * complete_cmd_fusion - Completes command + * @instance: Adapter soft state + * Completes all commands that is in reply descriptor queue + */ +int +complete_cmd_fusion(struct megasas_instance *instance) +{ + union MPI2_REPLY_DESCRIPTORS_UNION *desc; + struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *reply_desc; + struct MPI2_RAID_SCSI_IO_REQUEST *scsi_io_req; + struct fusion_context *fusion; + struct megasas_cmd *cmd_mfi; + struct megasas_cmd_fusion *cmd_fusion; + u16 smid, num_completed; + u8 reply_descript_type, arm; + u32 status, extStatus, device_id; + union desc_value d_val; + struct LD_LOAD_BALANCE_INFO *lbinfo; + + fusion = instance->ctrl_context; + + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) + return IRQ_HANDLED; + + desc = fusion->reply_frames_desc; + desc += fusion->last_reply_idx; + + reply_desc = (struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *)desc; + + d_val.word = desc->Words; + + reply_descript_type = reply_desc->ReplyFlags & + MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + + if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + return IRQ_NONE; + + d_val.word = desc->Words; + + num_completed = 0; + + while ((d_val.u.low != UINT_MAX) && (d_val.u.high != UINT_MAX)) { + smid = reply_desc->SMID; + + cmd_fusion = fusion->cmd_list[smid - 1]; + + scsi_io_req = + (struct MPI2_RAID_SCSI_IO_REQUEST *) + cmd_fusion->io_request; + + if (cmd_fusion->scmd) + cmd_fusion->scmd->SCp.ptr = NULL; + + status = scsi_io_req->RaidContext.status; + extStatus = scsi_io_req->RaidContext.exStatus; + + switch (scsi_io_req->Function) { + case MPI2_FUNCTION_SCSI_IO_REQUEST: /*Fast Path IO.*/ + /* Update load balancing info */ + device_id = MEGASAS_DEV_INDEX(instance, + cmd_fusion->scmd); + lbinfo = &fusion->load_balance_info[device_id]; + if (cmd_fusion->scmd->SCp.Status & + MEGASAS_LOAD_BALANCE_FLAG) { + arm = lbinfo->raid1DevHandle[0] == + cmd_fusion->io_request->DevHandle ? 0 : + 1; + atomic_dec(&lbinfo->scsi_pending_cmds[arm]); + cmd_fusion->scmd->SCp.Status &= + ~MEGASAS_LOAD_BALANCE_FLAG; + } + if (reply_descript_type == + MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS) { + if (megasas_dbg_lvl == 5) + printk(KERN_ERR "\nmegasas: FAST Path " + "IO Success\n"); + } + /* Fall thru and complete IO */ + case MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST: /* LD-IO Path */ + /* Map the FW Cmd Status */ + map_cmd_status(cmd_fusion, status, extStatus); + scsi_dma_unmap(cmd_fusion->scmd); + cmd_fusion->scmd->scsi_done(cmd_fusion->scmd); + scsi_io_req->RaidContext.status = 0; + scsi_io_req->RaidContext.exStatus = 0; + megasas_return_cmd_fusion(instance, cmd_fusion); + atomic_dec(&instance->fw_outstanding); + + break; + case MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST: /*MFI command */ + cmd_mfi = instance->cmd_list[cmd_fusion->sync_cmd_idx]; + megasas_complete_cmd(instance, cmd_mfi, DID_OK); + cmd_fusion->flags = 0; + megasas_return_cmd_fusion(instance, cmd_fusion); + + break; + } + + fusion->last_reply_idx++; + if (fusion->last_reply_idx >= fusion->reply_q_depth) + fusion->last_reply_idx = 0; + + desc->Words = ULLONG_MAX; + num_completed++; + + /* Get the next reply descriptor */ + if (!fusion->last_reply_idx) + desc = fusion->reply_frames_desc; + else + desc++; + + reply_desc = + (struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR *)desc; + + d_val.word = desc->Words; + + reply_descript_type = reply_desc->ReplyFlags & + MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK; + + if (reply_descript_type == MPI2_RPY_DESCRIPT_FLAGS_UNUSED) + break; + } + + if (!num_completed) + return IRQ_NONE; + + wmb(); + writel(fusion->last_reply_idx, + &instance->reg_set->reply_post_host_index); + + return IRQ_HANDLED; +} + +/** + * megasas_complete_cmd_dpc_fusion - Completes command + * @instance: Adapter soft state + * + * Tasklet to complete cmds + */ +void +megasas_complete_cmd_dpc_fusion(unsigned long instance_addr) +{ + 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); + complete_cmd_fusion(instance); + spin_unlock_irqrestore(&instance->completion_lock, flags); +} + +/** + * megasas_isr_fusion - isr entry point + */ +irqreturn_t megasas_isr_fusion(int irq, void *devp) +{ + struct megasas_instance *instance = (struct megasas_instance *)devp; + u32 mfiStatus, fw_state; + + if (!instance->msi_flag) { + mfiStatus = instance->instancet->clear_intr(instance->reg_set); + if (!mfiStatus) + return IRQ_NONE; + } + + /* If we are resetting, bail */ + if (test_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags)) + return IRQ_HANDLED; + + if (!complete_cmd_fusion(instance)) { + /* If we didn't complete any commands, check for FW fault */ + fw_state = instance->instancet->read_fw_status_reg( + instance->reg_set) & MFI_STATE_MASK; + if (fw_state == MFI_STATE_FAULT) + schedule_work(&instance->work_init); + } + + return IRQ_HANDLED; +} + +/** + * build_mpt_mfi_pass_thru - builds a cmd fo MFI Pass thru + * @instance: Adapter soft state + * mfi_cmd: megasas_cmd pointer + * + */ +u8 +build_mpt_mfi_pass_thru(struct megasas_instance *instance, + struct megasas_cmd *mfi_cmd) +{ + struct MPI25_IEEE_SGE_CHAIN64 *mpi25_ieee_chain; + struct MPI2_RAID_SCSI_IO_REQUEST *io_req; + struct megasas_cmd_fusion *cmd; + struct fusion_context *fusion; + struct megasas_header *frame_hdr = &mfi_cmd->frame->hdr; + + cmd = megasas_get_cmd_fusion(instance); + if (!cmd) + return 1; + + /* Save the smid. To be used for returning the cmd */ + mfi_cmd->context.smid = cmd->index; + + cmd->sync_cmd_idx = mfi_cmd->index; + + /* + * For cmds where the flag is set, store the flag and check + * on completion. For cmds with this flag, don't call + * megasas_complete_cmd + */ + + if (frame_hdr->flags & MFI_FRAME_DONT_POST_IN_REPLY_QUEUE) + cmd->flags = MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + + fusion = instance->ctrl_context; + io_req = cmd->io_request; + mpi25_ieee_chain = + (struct MPI25_IEEE_SGE_CHAIN64 *)&io_req->SGL.IeeeChain; + + io_req->Function = MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST; + io_req->SGLOffset0 = offsetof(struct MPI2_RAID_SCSI_IO_REQUEST, + SGL) / 4; + io_req->ChainOffset = fusion->chain_offset_mfi_pthru; + + mpi25_ieee_chain->Address = mfi_cmd->frame_phys_addr; + + mpi25_ieee_chain->Flags = IEEE_SGE_FLAGS_CHAIN_ELEMENT | + MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR; + + mpi25_ieee_chain->Length = MEGASAS_MAX_SZ_CHAIN_FRAME; + + return 0; +} + +/** + * build_mpt_cmd - Calls helper function to build a cmd MFI Pass thru cmd + * @instance: Adapter soft state + * @cmd: mfi cmd to build + * + */ +union MEGASAS_REQUEST_DESCRIPTOR_UNION * +build_mpt_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd) +{ + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + u16 index; + + if (build_mpt_mfi_pass_thru(instance, cmd)) { + printk(KERN_ERR "Couldn't build MFI pass thru cmd\n"); + return NULL; + } + + index = cmd->context.smid; + + req_desc = megasas_get_request_descriptor(instance, index - 1); + + if (!req_desc) + return NULL; + + req_desc->Words = 0; + req_desc->SCSIIO.RequestFlags = (MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO << + MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT); + + req_desc->SCSIIO.SMID = index; + + return req_desc; +} + +/** + * megasas_issue_dcmd_fusion - Issues a MFI Pass thru cmd + * @instance: Adapter soft state + * @cmd: mfi cmd pointer + * + */ +void +megasas_issue_dcmd_fusion(struct megasas_instance *instance, + struct megasas_cmd *cmd) +{ + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + union desc_value d_val; + + req_desc = build_mpt_cmd(instance, cmd); + if (!req_desc) { + printk(KERN_ERR "Couldn't issue MFI pass thru cmd\n"); + return; + } + d_val.word = req_desc->Words; + + instance->instancet->fire_cmd(instance, req_desc->u.low, + req_desc->u.high, instance->reg_set); +} + +/** + * megasas_release_fusion - Reverses the FW initialization + * @intance: Adapter soft state + */ +void +megasas_release_fusion(struct megasas_instance *instance) +{ + megasas_free_cmds(instance); + megasas_free_cmds_fusion(instance); + + iounmap(instance->reg_set); + + pci_release_selected_regions(instance->pdev, instance->bar); +} + +/** + * megasas_read_fw_status_reg_fusion - returns the current FW status value + * @regs: MFI register set + */ +static u32 +megasas_read_fw_status_reg_fusion(struct megasas_register_set __iomem *regs) +{ + return readl(&(regs)->outbound_scratch_pad); +} + +/** + * megasas_adp_reset_fusion - For controller reset + * @regs: MFI register set + */ +static int +megasas_adp_reset_fusion(struct megasas_instance *instance, + struct megasas_register_set __iomem *regs) +{ + return 0; +} + +/** + * megasas_check_reset_fusion - For controller reset check + * @regs: MFI register set + */ +static int +megasas_check_reset_fusion(struct megasas_instance *instance, + struct megasas_register_set __iomem *regs) +{ + return 0; +} + +/* This function waits for outstanding commands on fusion to complete */ +int megasas_wait_for_outstanding_fusion(struct megasas_instance *instance) +{ + int i, outstanding, retval = 0; + u32 fw_state, wait_time = MEGASAS_RESET_WAIT_TIME; + + for (i = 0; i < wait_time; i++) { + /* Check if firmware is in fault state */ + fw_state = instance->instancet->read_fw_status_reg( + instance->reg_set) & MFI_STATE_MASK; + if (fw_state == MFI_STATE_FAULT) { + printk(KERN_WARNING "megasas: Found FW in FAULT state," + " will reset adapter.\n"); + retval = 1; + goto out; + } + + outstanding = atomic_read(&instance->fw_outstanding); + if (!outstanding) + goto out; + + if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { + printk(KERN_NOTICE "megasas: [%2d]waiting for %d " + "commands to complete\n", i, outstanding); + megasas_complete_cmd_dpc_fusion( + (unsigned long)instance); + } + msleep(1000); + } + + if (atomic_read(&instance->fw_outstanding)) { + printk("megaraid_sas: pending commands remain after waiting, " + "will reset adapter.\n"); + retval = 1; + } +out: + return retval; +} + +void megasas_reset_reply_desc(struct megasas_instance *instance) +{ + int i; + struct fusion_context *fusion; + union MPI2_REPLY_DESCRIPTORS_UNION *reply_desc; + + fusion = instance->ctrl_context; + fusion->last_reply_idx = 0; + reply_desc = fusion->reply_frames_desc; + for (i = 0 ; i < fusion->reply_q_depth; i++, reply_desc++) + reply_desc->Words = ULLONG_MAX; +} + +/* Core fusion reset function */ +int megasas_reset_fusion(struct Scsi_Host *shost) +{ + int retval = SUCCESS, i, j, retry = 0; + struct megasas_instance *instance; + struct megasas_cmd_fusion *cmd_fusion; + struct fusion_context *fusion; + struct megasas_cmd *cmd_mfi; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *req_desc; + u32 host_diag, abs_state; + + instance = (struct megasas_instance *)shost->hostdata; + fusion = instance->ctrl_context; + + mutex_lock(&instance->reset_mutex); + set_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); + instance->adprecovery = MEGASAS_ADPRESET_SM_INFAULT; + instance->instancet->disable_intr(instance->reg_set); + msleep(1000); + + if (instance->adprecovery == MEGASAS_HW_CRITICAL_ERROR) { + printk(KERN_WARNING "megaraid_sas: Hardware critical error, " + "returning FAILED.\n"); + retval = FAILED; + goto out; + } + + /* First try waiting for commands to complete */ + if (megasas_wait_for_outstanding_fusion(instance)) { + printk(KERN_WARNING "megaraid_sas: resetting fusion " + "adapter.\n"); + /* Now return commands back to the OS */ + for (i = 0 ; i < instance->max_fw_cmds; i++) { + cmd_fusion = fusion->cmd_list[i]; + if (cmd_fusion->scmd) { + scsi_dma_unmap(cmd_fusion->scmd); + cmd_fusion->scmd->result = (DID_RESET << 16); + cmd_fusion->scmd->scsi_done(cmd_fusion->scmd); + megasas_return_cmd_fusion(instance, cmd_fusion); + atomic_dec(&instance->fw_outstanding); + } + } + + if (instance->disableOnlineCtrlReset == 1) { + /* Reset not supported, kill adapter */ + printk(KERN_WARNING "megaraid_sas: Reset not supported" + ", killing adapter.\n"); + megaraid_sas_kill_hba(instance); + instance->adprecovery = MEGASAS_HW_CRITICAL_ERROR; + retval = FAILED; + goto out; + } + + /* Now try to reset the chip */ + for (i = 0; i < MEGASAS_FUSION_MAX_RESET_TRIES; i++) { + writel(MPI2_WRSEQ_FLUSH_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_1ST_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_2ND_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_3RD_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_4TH_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_5TH_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + writel(MPI2_WRSEQ_6TH_KEY_VALUE, + &instance->reg_set->fusion_seq_offset); + + /* Check that the diag write enable (DRWE) bit is on */ + host_diag = readl(&instance->reg_set->fusion_host_diag); + while (!(host_diag & HOST_DIAG_WRITE_ENABLE)) { + msleep(100); + host_diag = + readl(&instance->reg_set->fusion_host_diag); + if (retry++ == 100) { + printk(KERN_WARNING "megaraid_sas: " + "Host diag unlock failed!\n"); + break; + } + } + if (!(host_diag & HOST_DIAG_WRITE_ENABLE)) + continue; + + /* Send chip reset command */ + writel(host_diag | HOST_DIAG_RESET_ADAPTER, + &instance->reg_set->fusion_host_diag); + msleep(3000); + + /* Make sure reset adapter bit is cleared */ + host_diag = readl(&instance->reg_set->fusion_host_diag); + retry = 0; + while (host_diag & HOST_DIAG_RESET_ADAPTER) { + msleep(100); + host_diag = + readl(&instance->reg_set->fusion_host_diag); + if (retry++ == 1000) { + printk(KERN_WARNING "megaraid_sas: " + "Diag reset adapter never " + "cleared!\n"); + break; + } + } + if (host_diag & HOST_DIAG_RESET_ADAPTER) + continue; + + abs_state = + instance->instancet->read_fw_status_reg( + instance->reg_set); + retry = 0; + + while ((abs_state <= MFI_STATE_FW_INIT) && + (retry++ < 1000)) { + msleep(100); + abs_state = + instance->instancet->read_fw_status_reg( + instance->reg_set); + } + if (abs_state <= MFI_STATE_FW_INIT) { + printk(KERN_WARNING "megaraid_sas: firmware " + "state < MFI_STATE_FW_INIT, state = " + "0x%x\n", abs_state); + continue; + } + + /* Wait for FW to become ready */ + if (megasas_transition_to_ready(instance)) { + printk(KERN_WARNING "megaraid_sas: Failed to " + "transition controller to ready.\n"); + continue; + } + + megasas_reset_reply_desc(instance); + if (megasas_ioc_init_fusion(instance)) { + printk(KERN_WARNING "megaraid_sas: " + "megasas_ioc_init_fusion() failed!\n"); + continue; + } + + instance->instancet->enable_intr(instance->reg_set); + instance->adprecovery = MEGASAS_HBA_OPERATIONAL; + + /* Re-fire management commands */ + for (j = 0 ; j < instance->max_fw_cmds; j++) { + cmd_fusion = fusion->cmd_list[j]; + if (cmd_fusion->sync_cmd_idx != + (u32)ULONG_MAX) { + cmd_mfi = + instance-> + cmd_list[cmd_fusion->sync_cmd_idx]; + if (cmd_mfi->frame->dcmd.opcode == + MR_DCMD_LD_MAP_GET_INFO) { + megasas_return_cmd(instance, + cmd_mfi); + megasas_return_cmd_fusion( + instance, cmd_fusion); + } else { + req_desc = + megasas_get_request_descriptor( + instance, + cmd_mfi->context.smid + -1); + if (!req_desc) + printk(KERN_WARNING + "req_desc NULL" + "\n"); + else { + instance->instancet-> + fire_cmd(instance, + req_desc-> + u.low, + req_desc-> + u.high, + instance-> + reg_set); + } + } + } + } + + /* Reset load balance info */ + memset(fusion->load_balance_info, 0, + sizeof(struct LD_LOAD_BALANCE_INFO) + *MAX_LOGICAL_DRIVES); + + if (!megasas_get_map_info(instance)) + megasas_sync_map_info(instance); + + /* Adapter reset completed successfully */ + printk(KERN_WARNING "megaraid_sas: Reset " + "successful.\n"); + retval = SUCCESS; + goto out; + } + /* Reset failed, kill the adapter */ + printk(KERN_WARNING "megaraid_sas: Reset failed, killing " + "adapter.\n"); + megaraid_sas_kill_hba(instance); + retval = FAILED; + } else { + instance->instancet->enable_intr(instance->reg_set); + instance->adprecovery = MEGASAS_HBA_OPERATIONAL; + } +out: + clear_bit(MEGASAS_FUSION_IN_RESET, &instance->reset_flags); + mutex_unlock(&instance->reset_mutex); + return retval; +} + +/* Fusion OCR work queue */ +void megasas_fusion_ocr_wq(struct work_struct *work) +{ + struct megasas_instance *instance = + container_of(work, struct megasas_instance, work_init); + + megasas_reset_fusion(instance->host); +} + +struct megasas_instance_template megasas_instance_template_fusion = { + .fire_cmd = megasas_fire_cmd_fusion, + .enable_intr = megasas_enable_intr_fusion, + .disable_intr = megasas_disable_intr_fusion, + .clear_intr = megasas_clear_intr_fusion, + .read_fw_status_reg = megasas_read_fw_status_reg_fusion, + .adp_reset = megasas_adp_reset_fusion, + .check_reset = megasas_check_reset_fusion, + .service_isr = megasas_isr_fusion, + .tasklet = megasas_complete_cmd_dpc_fusion, + .init_adapter = megasas_init_adapter_fusion, + .build_and_issue_cmd = megasas_build_and_issue_cmd_fusion, + .issue_dcmd = megasas_issue_dcmd_fusion, +}; diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.h b/drivers/scsi/megaraid/megaraid_sas_fusion.h new file mode 100644 index 000000000000..82b577a72c8b --- /dev/null +++ b/drivers/scsi/megaraid/megaraid_sas_fusion.h @@ -0,0 +1,695 @@ +/* + * Linux MegaRAID driver for SAS based RAID controllers + * + * Copyright (c) 2009-2011 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. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * FILE: megaraid_sas_fusion.h + * + * Authors: LSI Corporation + * Manoj Jose + * Sumant Patro + * + * Send feedback to: <megaraidlinux@lsi.com> + * + * Mail to: LSI Corporation, 1621 Barber Lane, Milpitas, CA 95035 + * ATTN: Linuxraid + */ + +#ifndef _MEGARAID_SAS_FUSION_H_ +#define _MEGARAID_SAS_FUSION_H_ + +/* Fusion defines */ +#define MEGASAS_MAX_SZ_CHAIN_FRAME 1024 +#define MFI_FUSION_ENABLE_INTERRUPT_MASK (0x00000009) +#define MEGA_MPI2_RAID_DEFAULT_IO_FRAME_SIZE 256 +#define MEGASAS_MPI2_FUNCTION_PASSTHRU_IO_REQUEST 0xF0 +#define MEGASAS_MPI2_FUNCTION_LD_IO_REQUEST 0xF1 +#define MEGASAS_LOAD_BALANCE_FLAG 0x1 +#define MEGASAS_DCMD_MBOX_PEND_FLAG 0x1 +#define HOST_DIAG_WRITE_ENABLE 0x80 +#define HOST_DIAG_RESET_ADAPTER 0x4 +#define MEGASAS_FUSION_MAX_RESET_TRIES 3 + +/* T10 PI defines */ +#define MR_PROT_INFO_TYPE_CONTROLLER 0x8 +#define MEGASAS_SCSI_VARIABLE_LENGTH_CMD 0x7f +#define MEGASAS_SCSI_SERVICE_ACTION_READ32 0x9 +#define MEGASAS_SCSI_SERVICE_ACTION_WRITE32 0xB +#define MEGASAS_SCSI_ADDL_CDB_LEN 0x18 +#define MEGASAS_RD_WR_PROTECT_CHECK_ALL 0x20 +#define MEGASAS_RD_WR_PROTECT_CHECK_NONE 0x60 +#define MEGASAS_EEDPBLOCKSIZE 512 + +/* + * Raid context flags + */ + +#define MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_SHIFT 0x4 +#define MR_RAID_CTX_RAID_FLAGS_IO_SUB_TYPE_MASK 0x30 +enum MR_RAID_FLAGS_IO_SUB_TYPE { + MR_RAID_FLAGS_IO_SUB_TYPE_NONE = 0, + MR_RAID_FLAGS_IO_SUB_TYPE_SYSTEM_PD = 1, +}; + +/* + * Request descriptor types + */ +#define MEGASAS_REQ_DESCRIPT_FLAGS_LD_IO 0x7 +#define MEGASAS_REQ_DESCRIPT_FLAGS_MFA 0x1 + +#define MEGASAS_REQ_DESCRIPT_FLAGS_TYPE_SHIFT 1 + +#define MEGASAS_FP_CMD_LEN 16 +#define MEGASAS_FUSION_IN_RESET 0 + +/* + * Raid Context structure which describes MegaRAID specific IO Paramenters + * This resides at offset 0x60 where the SGL normally starts in MPT IO Frames + */ + +struct RAID_CONTEXT { + u16 resvd0; + u16 timeoutValue; + u8 regLockFlags; + u8 resvd1; + u16 VirtualDiskTgtId; + u64 regLockRowLBA; + u32 regLockLength; + u16 nextLMId; + u8 exStatus; + u8 status; + u8 RAIDFlags; + u8 numSGE; + u16 configSeqNum; + u8 spanArm; + u8 resvd2[3]; +}; + +#define RAID_CTX_SPANARM_ARM_SHIFT (0) +#define RAID_CTX_SPANARM_ARM_MASK (0x1f) + +#define RAID_CTX_SPANARM_SPAN_SHIFT (5) +#define RAID_CTX_SPANARM_SPAN_MASK (0xE0) + +/* + * define region lock types + */ +enum REGION_TYPE { + REGION_TYPE_UNUSED = 0, + REGION_TYPE_SHARED_READ = 1, + REGION_TYPE_SHARED_WRITE = 2, + REGION_TYPE_EXCLUSIVE = 3, +}; + +/* MPI2 defines */ +#define MPI2_FUNCTION_IOC_INIT (0x02) /* IOC Init */ +#define MPI2_WHOINIT_HOST_DRIVER (0x04) +#define MPI2_VERSION_MAJOR (0x02) +#define MPI2_VERSION_MINOR (0x00) +#define MPI2_VERSION_MAJOR_MASK (0xFF00) +#define MPI2_VERSION_MAJOR_SHIFT (8) +#define MPI2_VERSION_MINOR_MASK (0x00FF) +#define MPI2_VERSION_MINOR_SHIFT (0) +#define MPI2_VERSION ((MPI2_VERSION_MAJOR << MPI2_VERSION_MAJOR_SHIFT) | \ + MPI2_VERSION_MINOR) +#define MPI2_HEADER_VERSION_UNIT (0x10) +#define MPI2_HEADER_VERSION_DEV (0x00) +#define MPI2_HEADER_VERSION_UNIT_MASK (0xFF00) +#define MPI2_HEADER_VERSION_UNIT_SHIFT (8) +#define MPI2_HEADER_VERSION_DEV_MASK (0x00FF) +#define MPI2_HEADER_VERSION_DEV_SHIFT (0) +#define MPI2_HEADER_VERSION ((MPI2_HEADER_VERSION_UNIT << 8) | \ + MPI2_HEADER_VERSION_DEV) +#define MPI2_IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) +#define MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG (0x8000) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG (0x0400) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP (0x0003) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_APPTAG (0x0200) +#define MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD (0x0100) +#define MPI2_SCSIIO_EEDPFLAGS_INSERT_OP (0x0004) +#define MPI2_FUNCTION_SCSI_IO_REQUEST (0x00) /* SCSI IO */ +#define MPI2_REQ_DESCRIPT_FLAGS_HIGH_PRIORITY (0x06) +#define MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO (0x00) +#define MPI2_SGE_FLAGS_64_BIT_ADDRESSING (0x02) +#define MPI2_SCSIIO_CONTROL_WRITE (0x01000000) +#define MPI2_SCSIIO_CONTROL_READ (0x02000000) +#define MPI2_REQ_DESCRIPT_FLAGS_TYPE_MASK (0x0E) +#define MPI2_RPY_DESCRIPT_FLAGS_UNUSED (0x0F) +#define MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS (0x00) +#define MPI2_RPY_DESCRIPT_FLAGS_TYPE_MASK (0x0F) +#define MPI2_WRSEQ_FLUSH_KEY_VALUE (0x0) +#define MPI2_WRITE_SEQUENCE_OFFSET (0x00000004) +#define MPI2_WRSEQ_1ST_KEY_VALUE (0xF) +#define MPI2_WRSEQ_2ND_KEY_VALUE (0x4) +#define MPI2_WRSEQ_3RD_KEY_VALUE (0xB) +#define MPI2_WRSEQ_4TH_KEY_VALUE (0x2) +#define MPI2_WRSEQ_5TH_KEY_VALUE (0x7) +#define MPI2_WRSEQ_6TH_KEY_VALUE (0xD) + +struct MPI25_IEEE_SGE_CHAIN64 { + u64 Address; + u32 Length; + u16 Reserved1; + u8 NextChainOffset; + u8 Flags; +}; + +struct MPI2_SGE_SIMPLE_UNION { + u32 FlagsLength; + union { + u32 Address32; + u64 Address64; + } u; +}; + +struct MPI2_SCSI_IO_CDB_EEDP32 { + u8 CDB[20]; /* 0x00 */ + u32 PrimaryReferenceTag; /* 0x14 */ + u16 PrimaryApplicationTag; /* 0x18 */ + u16 PrimaryApplicationTagMask; /* 0x1A */ + u32 TransferLength; /* 0x1C */ +}; + +struct MPI2_SGE_CHAIN_UNION { + u16 Length; + u8 NextChainOffset; + u8 Flags; + union { + u32 Address32; + u64 Address64; + } u; +}; + +struct MPI2_IEEE_SGE_SIMPLE32 { + u32 Address; + u32 FlagsLength; +}; + +struct MPI2_IEEE_SGE_CHAIN32 { + u32 Address; + u32 FlagsLength; +}; + +struct MPI2_IEEE_SGE_SIMPLE64 { + u64 Address; + u32 Length; + u16 Reserved1; + u8 Reserved2; + u8 Flags; +}; + +struct MPI2_IEEE_SGE_CHAIN64 { + u64 Address; + u32 Length; + u16 Reserved1; + u8 Reserved2; + u8 Flags; +}; + +union MPI2_IEEE_SGE_SIMPLE_UNION { + struct MPI2_IEEE_SGE_SIMPLE32 Simple32; + struct MPI2_IEEE_SGE_SIMPLE64 Simple64; +}; + +union MPI2_IEEE_SGE_CHAIN_UNION { + struct MPI2_IEEE_SGE_CHAIN32 Chain32; + struct MPI2_IEEE_SGE_CHAIN64 Chain64; +}; + +union MPI2_SGE_IO_UNION { + struct MPI2_SGE_SIMPLE_UNION MpiSimple; + struct MPI2_SGE_CHAIN_UNION MpiChain; + union MPI2_IEEE_SGE_SIMPLE_UNION IeeeSimple; + union MPI2_IEEE_SGE_CHAIN_UNION IeeeChain; +}; + +union MPI2_SCSI_IO_CDB_UNION { + u8 CDB32[32]; + struct MPI2_SCSI_IO_CDB_EEDP32 EEDP32; + struct MPI2_SGE_SIMPLE_UNION SGE; +}; + +/* + * RAID SCSI IO Request Message + * Total SGE count will be one less than _MPI2_SCSI_IO_REQUEST + */ +struct MPI2_RAID_SCSI_IO_REQUEST { + u16 DevHandle; /* 0x00 */ + u8 ChainOffset; /* 0x02 */ + u8 Function; /* 0x03 */ + u16 Reserved1; /* 0x04 */ + u8 Reserved2; /* 0x06 */ + u8 MsgFlags; /* 0x07 */ + u8 VP_ID; /* 0x08 */ + u8 VF_ID; /* 0x09 */ + u16 Reserved3; /* 0x0A */ + u32 SenseBufferLowAddress; /* 0x0C */ + u16 SGLFlags; /* 0x10 */ + u8 SenseBufferLength; /* 0x12 */ + u8 Reserved4; /* 0x13 */ + u8 SGLOffset0; /* 0x14 */ + u8 SGLOffset1; /* 0x15 */ + u8 SGLOffset2; /* 0x16 */ + u8 SGLOffset3; /* 0x17 */ + u32 SkipCount; /* 0x18 */ + u32 DataLength; /* 0x1C */ + u32 BidirectionalDataLength; /* 0x20 */ + u16 IoFlags; /* 0x24 */ + u16 EEDPFlags; /* 0x26 */ + u32 EEDPBlockSize; /* 0x28 */ + u32 SecondaryReferenceTag; /* 0x2C */ + u16 SecondaryApplicationTag; /* 0x30 */ + u16 ApplicationTagTranslationMask; /* 0x32 */ + u8 LUN[8]; /* 0x34 */ + u32 Control; /* 0x3C */ + union MPI2_SCSI_IO_CDB_UNION CDB; /* 0x40 */ + struct RAID_CONTEXT RaidContext; /* 0x60 */ + union MPI2_SGE_IO_UNION SGL; /* 0x80 */ +}; + +/* + * MPT RAID MFA IO Descriptor. + */ +struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR { + u32 RequestFlags:8; + u32 MessageAddress1:24; /* bits 31:8*/ + u32 MessageAddress2; /* bits 61:32 */ +}; + +/* Default Request Descriptor */ +struct MPI2_DEFAULT_REQUEST_DESCRIPTOR { + u8 RequestFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u16 LMID; /* 0x04 */ + u16 DescriptorTypeDependent; /* 0x06 */ +}; + +/* High Priority Request Descriptor */ +struct MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR { + u8 RequestFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u16 LMID; /* 0x04 */ + u16 Reserved1; /* 0x06 */ +}; + +/* SCSI IO Request Descriptor */ +struct MPI2_SCSI_IO_REQUEST_DESCRIPTOR { + u8 RequestFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u16 LMID; /* 0x04 */ + u16 DevHandle; /* 0x06 */ +}; + +/* SCSI Target Request Descriptor */ +struct MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR { + u8 RequestFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u16 LMID; /* 0x04 */ + u16 IoIndex; /* 0x06 */ +}; + +/* RAID Accelerator Request Descriptor */ +struct MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR { + u8 RequestFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u16 LMID; /* 0x04 */ + u16 Reserved; /* 0x06 */ +}; + +/* union of Request Descriptors */ +union MEGASAS_REQUEST_DESCRIPTOR_UNION { + struct MPI2_DEFAULT_REQUEST_DESCRIPTOR Default; + struct MPI2_HIGH_PRIORITY_REQUEST_DESCRIPTOR HighPriority; + struct MPI2_SCSI_IO_REQUEST_DESCRIPTOR SCSIIO; + struct MPI2_SCSI_TARGET_REQUEST_DESCRIPTOR SCSITarget; + struct MPI2_RAID_ACCEL_REQUEST_DESCRIPTOR RAIDAccelerator; + struct MEGASAS_RAID_MFA_IO_REQUEST_DESCRIPTOR MFAIo; + union { + struct { + u32 low; + u32 high; + } u; + u64 Words; + }; +}; + +/* Default Reply Descriptor */ +struct MPI2_DEFAULT_REPLY_DESCRIPTOR { + u8 ReplyFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 DescriptorTypeDependent1; /* 0x02 */ + u32 DescriptorTypeDependent2; /* 0x04 */ +}; + +/* Address Reply Descriptor */ +struct MPI2_ADDRESS_REPLY_DESCRIPTOR { + u8 ReplyFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u32 ReplyFrameAddress; /* 0x04 */ +}; + +/* SCSI IO Success Reply Descriptor */ +struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR { + u8 ReplyFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u16 TaskTag; /* 0x04 */ + u16 Reserved1; /* 0x06 */ +}; + +/* TargetAssist Success Reply Descriptor */ +struct MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR { + u8 ReplyFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u8 SequenceNumber; /* 0x04 */ + u8 Reserved1; /* 0x05 */ + u16 IoIndex; /* 0x06 */ +}; + +/* Target Command Buffer Reply Descriptor */ +struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR { + u8 ReplyFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u8 VP_ID; /* 0x02 */ + u8 Flags; /* 0x03 */ + u16 InitiatorDevHandle; /* 0x04 */ + u16 IoIndex; /* 0x06 */ +}; + +/* RAID Accelerator Success Reply Descriptor */ +struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR { + u8 ReplyFlags; /* 0x00 */ + u8 MSIxIndex; /* 0x01 */ + u16 SMID; /* 0x02 */ + u32 Reserved; /* 0x04 */ +}; + +/* union of Reply Descriptors */ +union MPI2_REPLY_DESCRIPTORS_UNION { + struct MPI2_DEFAULT_REPLY_DESCRIPTOR Default; + struct MPI2_ADDRESS_REPLY_DESCRIPTOR AddressReply; + struct MPI2_SCSI_IO_SUCCESS_REPLY_DESCRIPTOR SCSIIOSuccess; + struct MPI2_TARGETASSIST_SUCCESS_REPLY_DESCRIPTOR TargetAssistSuccess; + struct MPI2_TARGET_COMMAND_BUFFER_REPLY_DESCRIPTOR TargetCommandBuffer; + struct MPI2_RAID_ACCELERATOR_SUCCESS_REPLY_DESCRIPTOR + RAIDAcceleratorSuccess; + u64 Words; +}; + +/* IOCInit Request message */ +struct MPI2_IOC_INIT_REQUEST { + u8 WhoInit; /* 0x00 */ + u8 Reserved1; /* 0x01 */ + u8 ChainOffset; /* 0x02 */ + u8 Function; /* 0x03 */ + u16 Reserved2; /* 0x04 */ + u8 Reserved3; /* 0x06 */ + u8 MsgFlags; /* 0x07 */ + u8 VP_ID; /* 0x08 */ + u8 VF_ID; /* 0x09 */ + u16 Reserved4; /* 0x0A */ + u16 MsgVersion; /* 0x0C */ + u16 HeaderVersion; /* 0x0E */ + u32 Reserved5; /* 0x10 */ + u16 Reserved6; /* 0x14 */ + u8 Reserved7; /* 0x16 */ + u8 HostMSIxVectors; /* 0x17 */ + u16 Reserved8; /* 0x18 */ + u16 SystemRequestFrameSize; /* 0x1A */ + u16 ReplyDescriptorPostQueueDepth; /* 0x1C */ + u16 ReplyFreeQueueDepth; /* 0x1E */ + u32 SenseBufferAddressHigh; /* 0x20 */ + u32 SystemReplyAddressHigh; /* 0x24 */ + u64 SystemRequestFrameBaseAddress; /* 0x28 */ + u64 ReplyDescriptorPostQueueAddress;/* 0x30 */ + u64 ReplyFreeQueueAddress; /* 0x38 */ + u64 TimeStamp; /* 0x40 */ +}; + +/* mrpriv defines */ +#define MR_PD_INVALID 0xFFFF +#define MAX_SPAN_DEPTH 8 +#define MAX_RAIDMAP_SPAN_DEPTH (MAX_SPAN_DEPTH) +#define MAX_ROW_SIZE 32 +#define MAX_RAIDMAP_ROW_SIZE (MAX_ROW_SIZE) +#define MAX_LOGICAL_DRIVES 64 +#define MAX_RAIDMAP_LOGICAL_DRIVES (MAX_LOGICAL_DRIVES) +#define MAX_RAIDMAP_VIEWS (MAX_LOGICAL_DRIVES) +#define MAX_ARRAYS 128 +#define MAX_RAIDMAP_ARRAYS (MAX_ARRAYS) +#define MAX_PHYSICAL_DEVICES 256 +#define MAX_RAIDMAP_PHYSICAL_DEVICES (MAX_PHYSICAL_DEVICES) +#define MR_DCMD_LD_MAP_GET_INFO 0x0300e101 + +struct MR_DEV_HANDLE_INFO { + u16 curDevHdl; + u8 validHandles; + u8 reserved; + u16 devHandle[2]; +}; + +struct MR_ARRAY_INFO { + u16 pd[MAX_RAIDMAP_ROW_SIZE]; +}; + +struct MR_QUAD_ELEMENT { + u64 logStart; + u64 logEnd; + u64 offsetInSpan; + u32 diff; + u32 reserved1; +}; + +struct MR_SPAN_INFO { + u32 noElements; + u32 reserved1; + struct MR_QUAD_ELEMENT quad[MAX_RAIDMAP_SPAN_DEPTH]; +}; + +struct MR_LD_SPAN { + u64 startBlk; + u64 numBlks; + u16 arrayRef; + u8 reserved[6]; +}; + +struct MR_SPAN_BLOCK_INFO { + u64 num_rows; + struct MR_LD_SPAN span; + struct MR_SPAN_INFO block_span_info; +}; + +struct MR_LD_RAID { + struct { + u32 fpCapable:1; + u32 reserved5:3; + u32 ldPiMode:4; + u32 pdPiMode:4; + u32 encryptionType:8; + u32 fpWriteCapable:1; + u32 fpReadCapable:1; + u32 fpWriteAcrossStripe:1; + u32 fpReadAcrossStripe:1; + u32 reserved4:8; + } capability; + u32 reserved6; + u64 size; + u8 spanDepth; + u8 level; + u8 stripeShift; + u8 rowSize; + u8 rowDataSize; + u8 writeMode; + u8 PRL; + u8 SRL; + u16 targetId; + u8 ldState; + u8 regTypeReqOnWrite; + u8 modFactor; + u8 reserved2[1]; + u16 seqNum; + + struct { + u32 ldSyncRequired:1; + u32 reserved:31; + } flags; + + u8 reserved3[0x5C]; +}; + +struct MR_LD_SPAN_MAP { + struct MR_LD_RAID ldRaid; + u8 dataArmMap[MAX_RAIDMAP_ROW_SIZE]; + struct MR_SPAN_BLOCK_INFO spanBlock[MAX_RAIDMAP_SPAN_DEPTH]; +}; + +struct MR_FW_RAID_MAP { + u32 totalSize; + union { + struct { + u32 maxLd; + u32 maxSpanDepth; + u32 maxRowSize; + u32 maxPdCount; + u32 maxArrays; + } validationInfo; + u32 version[5]; + u32 reserved1[5]; + }; + + u32 ldCount; + u32 Reserved1; + u8 ldTgtIdToLd[MAX_RAIDMAP_LOGICAL_DRIVES+ + MAX_RAIDMAP_VIEWS]; + u8 fpPdIoTimeoutSec; + u8 reserved2[7]; + struct MR_ARRAY_INFO arMapInfo[MAX_RAIDMAP_ARRAYS]; + struct MR_DEV_HANDLE_INFO devHndlInfo[MAX_RAIDMAP_PHYSICAL_DEVICES]; + struct MR_LD_SPAN_MAP ldSpanMap[1]; +}; + +struct IO_REQUEST_INFO { + u64 ldStartBlock; + u32 numBlocks; + u16 ldTgtId; + u8 isRead; + u16 devHandle; + u64 pdBlock; + u8 fpOkForIo; +}; + +struct MR_LD_TARGET_SYNC { + u8 targetId; + u8 reserved; + u16 seqNum; +}; + +#define IEEE_SGE_FLAGS_ADDR_MASK (0x03) +#define IEEE_SGE_FLAGS_SYSTEM_ADDR (0x00) +#define IEEE_SGE_FLAGS_IOCDDR_ADDR (0x01) +#define IEEE_SGE_FLAGS_IOCPLB_ADDR (0x02) +#define IEEE_SGE_FLAGS_IOCPLBNTA_ADDR (0x03) +#define IEEE_SGE_FLAGS_CHAIN_ELEMENT (0x80) +#define IEEE_SGE_FLAGS_END_OF_LIST (0x40) + +struct megasas_register_set; +struct megasas_instance; + +union desc_word { + u64 word; + struct { + u32 low; + u32 high; + } u; +}; + +struct megasas_cmd_fusion { + struct MPI2_RAID_SCSI_IO_REQUEST *io_request; + dma_addr_t io_request_phys_addr; + + union MPI2_SGE_IO_UNION *sg_frame; + dma_addr_t sg_frame_phys_addr; + + u8 *sense; + dma_addr_t sense_phys_addr; + + struct list_head list; + struct scsi_cmnd *scmd; + struct megasas_instance *instance; + + u8 retry_for_fw_reset; + union MEGASAS_REQUEST_DESCRIPTOR_UNION *request_desc; + + /* + * Context for a MFI frame. + * Used to get the mfi cmd from list when a MFI cmd is completed + */ + u32 sync_cmd_idx; + u32 index; + u8 flags; +}; + +struct LD_LOAD_BALANCE_INFO { + u8 loadBalanceFlag; + u8 reserved1; + u16 raid1DevHandle[2]; + atomic_t scsi_pending_cmds[2]; + u64 last_accessed_block[2]; +}; + +struct MR_FW_RAID_MAP_ALL { + struct MR_FW_RAID_MAP raidMap; + struct MR_LD_SPAN_MAP ldSpanMap[MAX_LOGICAL_DRIVES - 1]; +} __attribute__ ((packed)); + +struct fusion_context { + struct megasas_cmd_fusion **cmd_list; + struct list_head cmd_pool; + + spinlock_t cmd_pool_lock; + + dma_addr_t req_frames_desc_phys; + u8 *req_frames_desc; + + struct dma_pool *io_request_frames_pool; + dma_addr_t io_request_frames_phys; + u8 *io_request_frames; + + struct dma_pool *sg_dma_pool; + struct dma_pool *sense_dma_pool; + + dma_addr_t reply_frames_desc_phys; + union MPI2_REPLY_DESCRIPTORS_UNION *reply_frames_desc; + struct dma_pool *reply_frames_desc_pool; + + u16 last_reply_idx; + + u32 reply_q_depth; + u32 request_alloc_sz; + u32 reply_alloc_sz; + u32 io_frames_alloc_sz; + + u16 max_sge_in_main_msg; + u16 max_sge_in_chain; + + u8 chain_offset_io_request; + u8 chain_offset_mfi_pthru; + + struct MR_FW_RAID_MAP_ALL *ld_map[2]; + dma_addr_t ld_map_phys[2]; + + u32 map_sz; + u8 fast_path_io; + struct LD_LOAD_BALANCE_INFO load_balance_info[MAX_LOGICAL_DRIVES]; +}; + +union desc_value { + u64 word; + struct { + u32 low; + u32 high; + } u; +}; + +#endif /* _MEGARAID_SAS_FUSION_H_ */ |