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/megaraid | |
parent | cd50ba8ede5cd3c4606a8e5d163913da5ff36ad7 (diff) |
[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/megaraid')
-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 6613a2ceea0..5826ed509e3 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 a0b8ee1a5d2..1b5e375732c 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 5938267e472..5d6d07bd1cd 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 00000000000..53fa96ae2b3 --- /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; +} + |