Login
[x]
Log in using an account from:
Fedora Account System
Red Hat Associate
Red Hat Customer
Or login using a Red Hat Bugzilla account
Forgot Password
Login:
Hide Forgot
Create an Account
Red Hat Bugzilla – Attachment 149170 Details for
Bug 230826
megaraid_sas driver doesn't support new devices
[?]
New
Simple Search
Advanced Search
My Links
Browse
Requests
Reports
Current State
Search
Tabular reports
Graphical reports
Duplicates
Other Reports
User Changes
Plotly Reports
Bug Status
Bug Severity
Non-Defaults
|
Product Dashboard
Help
Page Help!
Bug Writing Guidelines
What's new
Browser Support Policy
5.0.4.rh83 Release notes
FAQ
Guides index
User guide
Web Services
Contact
Legal
This site requires JavaScript to be enabled to function correctly, please enable it.
[patch]
replacement patch for Patch2320
linux-2.6.9-megaraid_sas-00.00.03.01-rhel4.patch (text/plain), 113.85 KB, created by
Tuomo Soini
on 2007-03-03 06:57:57 UTC
(
hide
)
Description:
replacement patch for Patch2320
Filename:
MIME Type:
Creator:
Tuomo Soini
Created:
2007-03-03 06:57:57 UTC
Size:
113.85 KB
patch
obsolete
>--- linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.c.megaraid_sas 2007-02-28 10:15:45.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.c 2007-02-28 10:17:39.000000000 +0200 >@@ -0,0 +1,2881 @@ >+/* >+ * >+ * Linux MegaRAID driver for SAS based RAID controllers >+ * >+ * Copyright (c) 2003-2005 LSI Logic 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. >+ * >+ * FILE : megaraid_sas.c >+ * Version : v03.01 >+ * >+ * Authors: >+ * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsil.com> >+ * Sumant Patro <Sumant.Patro@lsil.com> >+ * >+ * List of supported controllers >+ * >+ * OEM Product Name VID DID SSVID SSID >+ * --- ------------ --- --- ---- ---- >+ */ >+ >+#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/uio.h> >+#include <asm/uaccess.h> >+#include <linux/fs.h> >+#include <linux/compat.h> >+#include <linux/mutex.h> >+ >+#include <scsi/scsi.h> >+#include <scsi/scsi_cmnd.h> >+#include <scsi/scsi_device.h> >+#include <scsi/scsi_host.h> >+#include "megaraid_sas.h" >+ >+MODULE_LICENSE("GPL"); >+MODULE_VERSION(MEGASAS_VERSION); >+MODULE_AUTHOR("sreenivas.bagalkote@lsil.com"); >+MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver"); >+ >+/* >+ * PCI ID table for all supported controllers >+ */ >+static struct pci_device_id megasas_pci_table[] = { >+ >+ { >+ PCI_VENDOR_ID_LSI_LOGIC, >+ PCI_DEVICE_ID_LSI_SAS1064R, // xscale IOP >+ PCI_ANY_ID, >+ PCI_ANY_ID, >+ }, >+ { >+ PCI_VENDOR_ID_LSI_LOGIC, >+ PCI_DEVICE_ID_LSI_SAS1078R, // ppc IOP >+ PCI_ANY_ID, >+ PCI_ANY_ID, >+ }, >+ { >+ PCI_VENDOR_ID_LSI_LOGIC, >+ PCI_DEVICE_ID_LSI_VERDE_ZCR, // vega >+ PCI_ANY_ID, >+ PCI_ANY_ID, >+ }, >+ { >+ PCI_VENDOR_ID_DELL, >+ PCI_DEVICE_ID_DELL_PERC5, // xscale IOP >+ PCI_ANY_ID, >+ PCI_ANY_ID, >+ }, >+ {0} /* Terminating entry */ >+}; >+ >+MODULE_DEVICE_TABLE(pci, megasas_pci_table); >+ >+static int megasas_mgmt_majorno; >+static struct megasas_mgmt_info megasas_mgmt_info; >+static struct fasync_struct *megasas_async_queue; >+static DEFINE_MUTEX(megasas_async_queue_mutex); >+ >+/** >+ * megasas_get_cmd - Get a command from the free pool >+ * @instance: Adapter soft state >+ * >+ * Returns a free command from the pool >+ */ >+static struct megasas_cmd *megasas_get_cmd(struct megasas_instance >+ *instance) >+{ >+ unsigned long flags; >+ struct megasas_cmd *cmd = NULL; >+ >+ spin_lock_irqsave(&instance->cmd_pool_lock, flags); >+ >+ if (!list_empty(&instance->cmd_pool)) { >+ cmd = list_entry((&instance->cmd_pool)->next, >+ struct megasas_cmd, list); >+ list_del_init(&cmd->list); >+ } else { >+ printk(KERN_ERR "megasas: Command pool empty!\n"); >+ } >+ >+ spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); >+ return cmd; >+} >+ >+/** >+ * megasas_return_cmd - 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(struct megasas_instance *instance, struct megasas_cmd *cmd) >+{ >+ unsigned long flags; >+ >+ spin_lock_irqsave(&instance->cmd_pool_lock, flags); >+ >+ cmd->scmd = NULL; >+ list_add_tail(&cmd->list, &instance->cmd_pool); >+ >+ spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); >+} >+ >+ >+/** >+* The following functions are defined for xscale >+* (deviceid : 1064R, PERC5) controllers >+*/ >+ >+/** >+ * megasas_enable_intr_xscale - Enables interrupts >+ * @regs: MFI register set >+ */ >+static inline void >+megasas_enable_intr_xscale(struct megasas_register_set __iomem * regs) >+{ >+ writel(1, &(regs)->outbound_intr_mask); >+ >+ /* Dummy readl to force pci flush */ >+ readl(®s->outbound_intr_mask); >+} >+ >+/** >+ * megasas_read_fw_status_reg_xscale - returns the current FW status value >+ * @regs: MFI register set >+ */ >+static u32 >+megasas_read_fw_status_reg_xscale(struct megasas_register_set __iomem * regs) >+{ >+ return readl(&(regs)->outbound_msg_0); >+} >+/** >+ * megasas_clear_interrupt_xscale - Check & clear interrupt >+ * @regs: MFI register set >+ */ >+static int >+megasas_clear_intr_xscale(struct megasas_register_set __iomem * regs) >+{ >+ u32 status; >+ /* >+ * Check if it is our interrupt >+ */ >+ status = readl(®s->outbound_intr_status); >+ >+ if (!(status & MFI_OB_INTR_STATUS_MASK)) { >+ return 1; >+ } >+ >+ /* >+ * Clear the interrupt by writing back the same value >+ */ >+ writel(status, ®s->outbound_intr_status); >+ >+ return 0; >+} >+ >+/** >+ * megasas_fire_cmd_xscale - Sends command to the FW >+ * @frame_phys_addr : Physical address of cmd >+ * @frame_count : Number of frames for the command >+ * @regs : MFI register set >+ */ >+static inline void >+megasas_fire_cmd_xscale(dma_addr_t frame_phys_addr,u32 frame_count, struct megasas_register_set __iomem *regs) >+{ >+ writel((frame_phys_addr >> 3)|(frame_count), >+ &(regs)->inbound_queue_port); >+} >+ >+static struct megasas_instance_template megasas_instance_template_xscale = { >+ >+ .fire_cmd = megasas_fire_cmd_xscale, >+ .enable_intr = megasas_enable_intr_xscale, >+ .clear_intr = megasas_clear_intr_xscale, >+ .read_fw_status_reg = megasas_read_fw_status_reg_xscale, >+}; >+ >+/** >+* This is the end of set of functions & definitions specific >+* to xscale (deviceid : 1064R, PERC5) controllers >+*/ >+ >+/** >+* The following functions are defined for ppc (deviceid : 0x60) >+* controllers >+*/ >+ >+/** >+ * megasas_enable_intr_ppc - Enables interrupts >+ * @regs: MFI register set >+ */ >+static inline void >+megasas_enable_intr_ppc(struct megasas_register_set __iomem * regs) >+{ >+ writel(0xFFFFFFFF, &(regs)->outbound_doorbell_clear); >+ >+ writel(~0x80000004, &(regs)->outbound_intr_mask); >+ >+ /* Dummy readl to force pci flush */ >+ readl(®s->outbound_intr_mask); >+} >+ >+/** >+ * megasas_read_fw_status_reg_ppc - returns the current FW status value >+ * @regs: MFI register set >+ */ >+static u32 >+megasas_read_fw_status_reg_ppc(struct megasas_register_set __iomem * regs) >+{ >+ return readl(&(regs)->outbound_scratch_pad); >+} >+ >+/** >+ * megasas_clear_interrupt_ppc - Check & clear interrupt >+ * @regs: MFI register set >+ */ >+static int >+megasas_clear_intr_ppc(struct megasas_register_set __iomem * regs) >+{ >+ u32 status; >+ /* >+ * Check if it is our interrupt >+ */ >+ status = readl(®s->outbound_intr_status); >+ >+ if (!(status & MFI_REPLY_1078_MESSAGE_INTERRUPT)) { >+ return 1; >+ } >+ >+ /* >+ * Clear the interrupt by writing back the same value >+ */ >+ writel(status, ®s->outbound_doorbell_clear); >+ >+ return 0; >+} >+/** >+ * megasas_fire_cmd_ppc - Sends command to the FW >+ * @frame_phys_addr : Physical address of cmd >+ * @frame_count : Number of frames for the command >+ * @regs : MFI register set >+ */ >+static inline void >+megasas_fire_cmd_ppc(dma_addr_t frame_phys_addr, u32 frame_count, struct megasas_register_set __iomem *regs) >+{ >+ writel((frame_phys_addr | (frame_count<<1))|1, >+ &(regs)->inbound_queue_port); >+} >+ >+static struct megasas_instance_template megasas_instance_template_ppc = { >+ >+ .fire_cmd = megasas_fire_cmd_ppc, >+ .enable_intr = megasas_enable_intr_ppc, >+ .clear_intr = megasas_clear_intr_ppc, >+ .read_fw_status_reg = megasas_read_fw_status_reg_ppc, >+}; >+ >+/** >+* This is the end of set of functions & definitions >+* specific to ppc (deviceid : 0x60) controllers >+*/ >+ >+/** >+ * megasas_disable_intr - Disables interrupts >+ * @regs: MFI register set >+ * FIXME : Disable intr mask value is different for 1068/1078. >+ * Check if it can be put in template >+ */ >+static inline void >+megasas_disable_intr(struct megasas_instance *instance) >+{ >+ u32 mask = 0x1f; >+ struct megasas_register_set __iomem *regs = instance->reg_set; >+ >+ if(instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078R) >+ mask = 0xffffffff; >+ >+ writel(mask, ®s->outbound_intr_mask); >+ >+ /* Dummy readl to force pci flush */ >+ readl(®s->outbound_intr_mask); >+} >+ >+/** >+ * megasas_issue_polled - 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. >+ */ >+static 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; >+ >+ frame_hdr->cmd_status = 0xFF; >+ frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; >+ >+ /* >+ * Issue the frame using inbound queue port >+ */ >+ instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); >+ >+ /* >+ * Wait for cmd_status to change >+ */ >+ for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i++) { >+ rmb(); >+ msleep(1); >+ } >+ >+ if (frame_hdr->cmd_status == 0xff) >+ return -ETIME; >+ >+ return 0; >+} >+ >+/** >+ * megasas_issue_blocked_cmd - Synchronous wrapper around regular FW cmds >+ * @instance: Adapter soft state >+ * @cmd: Command to be issued >+ * >+ * This function waits on an event for the command to be returned from ISR. >+ * Used to issue ioctl commands. >+ */ >+static int >+megasas_issue_blocked_cmd(struct megasas_instance *instance, >+ struct megasas_cmd *cmd) >+{ >+ cmd->cmd_status = ENODATA; >+ >+ instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); >+ >+ wait_event(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA)); >+ >+ return 0; >+} >+ >+/** >+ * megasas_issue_blocked_abort_cmd - Aborts previously issued cmd >+ * @instance: Adapter soft state >+ * @cmd_to_abort: Previously issued cmd to be aborted >+ * >+ * MFI firmware can abort previously issued AEN comamnd (automatic event >+ * notification). The megasas_issue_blocked_abort_cmd() issues such abort >+ * cmd and blocks till it is completed. >+ */ >+static int >+megasas_issue_blocked_abort_cmd(struct megasas_instance *instance, >+ struct megasas_cmd *cmd_to_abort) >+{ >+ struct megasas_cmd *cmd; >+ struct megasas_abort_frame *abort_fr; >+ >+ cmd = megasas_get_cmd(instance); >+ >+ if (!cmd) >+ return -1; >+ >+ abort_fr = &cmd->frame->abort; >+ >+ /* >+ * Prepare and issue the abort frame >+ */ >+ abort_fr->cmd = MFI_CMD_ABORT; >+ abort_fr->cmd_status = 0xFF; >+ abort_fr->flags = 0; >+ abort_fr->abort_context = cmd_to_abort->index; >+ abort_fr->abort_mfi_phys_addr_lo = cmd_to_abort->frame_phys_addr; >+ abort_fr->abort_mfi_phys_addr_hi = 0; >+ >+ cmd->sync_cmd = 1; >+ cmd->cmd_status = 0xFF; >+ >+ instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); >+ >+ /* >+ * Wait for this cmd to complete >+ */ >+ wait_event(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF)); >+ >+ megasas_return_cmd(instance, cmd); >+ return 0; >+} >+ >+/** >+ * megasas_make_sgl32 - Prepares 32-bit SGL >+ * @instance: Adapter soft state >+ * @scp: SCSI command from the mid-layer >+ * @mfi_sgl: SGL to be filled in >+ * >+ * If successful, this function returns the number of SG elements. Otherwise, >+ * it returnes -1. >+ */ >+static int >+megasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp, >+ union megasas_sgl *mfi_sgl) >+{ >+ int i; >+ int sge_count; >+ struct scatterlist *os_sgl; >+ >+ /* >+ * Return 0 if there is no data transfer >+ */ >+ if (!scp->request_buffer || !scp->request_bufflen) >+ return 0; >+ >+ if (!scp->use_sg) { >+ mfi_sgl->sge32[0].phys_addr = pci_map_single(instance->pdev, >+ scp-> >+ request_buffer, >+ scp-> >+ request_bufflen, >+ scp-> >+ sc_data_direction); >+ mfi_sgl->sge32[0].length = scp->request_bufflen; >+ >+ return 1; >+ } >+ >+ os_sgl = (struct scatterlist *)scp->request_buffer; >+ sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg, >+ scp->sc_data_direction); >+ >+ for (i = 0; i < sge_count; i++, os_sgl++) { >+ mfi_sgl->sge32[i].length = sg_dma_len(os_sgl); >+ mfi_sgl->sge32[i].phys_addr = sg_dma_address(os_sgl); >+ } >+ >+ return sge_count; >+} >+ >+/** >+ * megasas_make_sgl64 - Prepares 64-bit SGL >+ * @instance: Adapter soft state >+ * @scp: SCSI command from the mid-layer >+ * @mfi_sgl: SGL to be filled in >+ * >+ * If successful, this function returns the number of SG elements. Otherwise, >+ * it returnes -1. >+ */ >+static int >+megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, >+ union megasas_sgl *mfi_sgl) >+{ >+ int i; >+ int sge_count; >+ struct scatterlist *os_sgl; >+ >+ /* >+ * Return 0 if there is no data transfer >+ */ >+ if (!scp->request_buffer || !scp->request_bufflen) >+ return 0; >+ >+ if (!scp->use_sg) { >+ mfi_sgl->sge64[0].phys_addr = pci_map_single(instance->pdev, >+ scp-> >+ request_buffer, >+ scp-> >+ request_bufflen, >+ scp-> >+ sc_data_direction); >+ >+ mfi_sgl->sge64[0].length = scp->request_bufflen; >+ >+ return 1; >+ } >+ >+ os_sgl = (struct scatterlist *)scp->request_buffer; >+ sge_count = pci_map_sg(instance->pdev, os_sgl, scp->use_sg, >+ scp->sc_data_direction); >+ >+ for (i = 0; i < sge_count; i++, os_sgl++) { >+ mfi_sgl->sge64[i].length = sg_dma_len(os_sgl); >+ mfi_sgl->sge64[i].phys_addr = sg_dma_address(os_sgl); >+ } >+ >+ return sge_count; >+} >+ >+/** >+ * megasas_build_dcdb - Prepares a direct cdb (DCDB) command >+ * @instance: Adapter soft state >+ * @scp: SCSI command >+ * @cmd: Command to be prepared in >+ * >+ * This function prepares CDB commands. These are typcially pass-through >+ * commands to the devices. >+ */ >+static int >+megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, >+ struct megasas_cmd *cmd) >+{ >+ u32 sge_sz; >+ int sge_bytes; >+ u32 is_logical; >+ u32 device_id; >+ u16 flags = 0; >+ struct megasas_pthru_frame *pthru; >+ >+ is_logical = MEGASAS_IS_LOGICAL(scp); >+ device_id = MEGASAS_DEV_INDEX(instance, scp); >+ pthru = (struct megasas_pthru_frame *)cmd->frame; >+ >+ if (scp->sc_data_direction == PCI_DMA_TODEVICE) >+ flags = MFI_FRAME_DIR_WRITE; >+ else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) >+ flags = MFI_FRAME_DIR_READ; >+ else if (scp->sc_data_direction == PCI_DMA_NONE) >+ flags = MFI_FRAME_DIR_NONE; >+ >+ /* >+ * Prepare the DCDB frame >+ */ >+ pthru->cmd = (is_logical) ? MFI_CMD_LD_SCSI_IO : MFI_CMD_PD_SCSI_IO; >+ pthru->cmd_status = 0x0; >+ pthru->scsi_status = 0x0; >+ pthru->target_id = device_id; >+ pthru->lun = scp->device->lun; >+ pthru->cdb_len = scp->cmd_len; >+ pthru->timeout = 0; >+ pthru->flags = flags; >+ pthru->data_xfer_len = scp->request_bufflen; >+ >+ memcpy(pthru->cdb, scp->cmnd, scp->cmd_len); >+ >+ /* >+ * Construct SGL >+ */ >+ sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : >+ sizeof(struct megasas_sge32); >+ >+ if (IS_DMA64) { >+ pthru->flags |= MFI_FRAME_SGL64; >+ pthru->sge_count = megasas_make_sgl64(instance, scp, >+ &pthru->sgl); >+ } else >+ pthru->sge_count = megasas_make_sgl32(instance, scp, >+ &pthru->sgl); >+ >+ /* >+ * Sense info specific >+ */ >+ pthru->sense_len = SCSI_SENSE_BUFFERSIZE; >+ pthru->sense_buf_phys_addr_hi = 0; >+ pthru->sense_buf_phys_addr_lo = cmd->sense_phys_addr; >+ >+ sge_bytes = sge_sz * pthru->sge_count; >+ >+ /* >+ * Compute the total number of frames this command consumes. FW uses >+ * this number to pull sufficient number of frames from host memory. >+ */ >+ cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + >+ ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1; >+ >+ if (cmd->frame_count > 7) >+ cmd->frame_count = 8; >+ >+ return cmd->frame_count; >+} >+ >+/** >+ * megasas_build_ldio - Prepares IOs to logical devices >+ * @instance: Adapter soft state >+ * @scp: SCSI command >+ * @cmd: Command to to be prepared >+ * >+ * Frames (and accompanying SGLs) for regular SCSI IOs use this function. >+ */ >+static int >+megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, >+ struct megasas_cmd *cmd) >+{ >+ u32 sge_sz; >+ int sge_bytes; >+ u32 device_id; >+ u8 sc = scp->cmnd[0]; >+ u16 flags = 0; >+ struct megasas_io_frame *ldio; >+ >+ device_id = MEGASAS_DEV_INDEX(instance, scp); >+ ldio = (struct megasas_io_frame *)cmd->frame; >+ >+ if (scp->sc_data_direction == PCI_DMA_TODEVICE) >+ flags = MFI_FRAME_DIR_WRITE; >+ else if (scp->sc_data_direction == PCI_DMA_FROMDEVICE) >+ flags = MFI_FRAME_DIR_READ; >+ >+ /* >+ * Preare the Logical IO frame: 2nd bit is zero for all read cmds >+ */ >+ ldio->cmd = (sc & 0x02) ? MFI_CMD_LD_WRITE : MFI_CMD_LD_READ; >+ ldio->cmd_status = 0x0; >+ ldio->scsi_status = 0x0; >+ ldio->target_id = device_id; >+ ldio->timeout = 0; >+ ldio->reserved_0 = 0; >+ ldio->pad_0 = 0; >+ ldio->flags = flags; >+ ldio->start_lba_hi = 0; >+ ldio->access_byte = (scp->cmd_len != 6) ? scp->cmnd[1] : 0; >+ >+ /* >+ * 6-byte READ(0x08) or WRITE(0x0A) cdb >+ */ >+ if (scp->cmd_len == 6) { >+ ldio->lba_count = (u32) scp->cmnd[4]; >+ ldio->start_lba_lo = ((u32) scp->cmnd[1] << 16) | >+ ((u32) scp->cmnd[2] << 8) | (u32) scp->cmnd[3]; >+ >+ ldio->start_lba_lo &= 0x1FFFFF; >+ } >+ >+ /* >+ * 10-byte READ(0x28) or WRITE(0x2A) cdb >+ */ >+ else if (scp->cmd_len == 10) { >+ ldio->lba_count = (u32) scp->cmnd[8] | >+ ((u32) scp->cmnd[7] << 8); >+ ldio->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) { >+ ldio->lba_count = ((u32) scp->cmnd[6] << 24) | >+ ((u32) scp->cmnd[7] << 16) | >+ ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; >+ >+ ldio->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) { >+ ldio->lba_count = ((u32) scp->cmnd[10] << 24) | >+ ((u32) scp->cmnd[11] << 16) | >+ ((u32) scp->cmnd[12] << 8) | (u32) scp->cmnd[13]; >+ >+ ldio->start_lba_lo = ((u32) scp->cmnd[6] << 24) | >+ ((u32) scp->cmnd[7] << 16) | >+ ((u32) scp->cmnd[8] << 8) | (u32) scp->cmnd[9]; >+ >+ ldio->start_lba_hi = ((u32) scp->cmnd[2] << 24) | >+ ((u32) scp->cmnd[3] << 16) | >+ ((u32) scp->cmnd[4] << 8) | (u32) scp->cmnd[5]; >+ >+ } >+ >+ /* >+ * Construct SGL >+ */ >+ sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : >+ sizeof(struct megasas_sge32); >+ >+ if (IS_DMA64) { >+ ldio->flags |= MFI_FRAME_SGL64; >+ ldio->sge_count = megasas_make_sgl64(instance, scp, &ldio->sgl); >+ } else >+ ldio->sge_count = megasas_make_sgl32(instance, scp, &ldio->sgl); >+ >+ /* >+ * Sense info specific >+ */ >+ ldio->sense_len = SCSI_SENSE_BUFFERSIZE; >+ ldio->sense_buf_phys_addr_hi = 0; >+ ldio->sense_buf_phys_addr_lo = cmd->sense_phys_addr; >+ >+ sge_bytes = sge_sz * ldio->sge_count; >+ >+ cmd->frame_count = (sge_bytes / MEGAMFI_FRAME_SIZE) + >+ ((sge_bytes % MEGAMFI_FRAME_SIZE) ? 1 : 0) + 1; >+ >+ if (cmd->frame_count > 7) >+ cmd->frame_count = 8; >+ >+ return cmd->frame_count; >+} >+ >+/** >+ * megasas_is_ldio - Checks if the cmd is for logical drive >+ * @scmd: SCSI command >+ * >+ * 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) >+{ >+ if (!MEGASAS_IS_LOGICAL(cmd)) >+ return 0; >+ switch (cmd->cmnd[0]) { >+ case READ_10: >+ case WRITE_10: >+ case READ_12: >+ case WRITE_12: >+ case READ_6: >+ case WRITE_6: >+ case READ_16: >+ case WRITE_16: >+ return 1; >+ default: >+ return 0; >+ } >+} >+ >+/** >+ * megasas_queue_command - Queue entry point >+ * @scmd: SCSI command to be queued >+ * @done: Callback entry point >+ */ >+static int >+megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) >+{ >+ u32 frame_count; >+ struct megasas_cmd *cmd; >+ struct megasas_instance *instance; >+ >+ instance = (struct megasas_instance *) >+ scmd->device->host->hostdata; >+ scmd->scsi_done = done; >+ scmd->result = 0; >+ >+ if (MEGASAS_IS_LOGICAL(scmd) && >+ (scmd->device->id >= MEGASAS_MAX_LD || scmd->device->lun)) { >+ scmd->result = DID_BAD_TARGET << 16; >+ goto out_done; >+ } >+ >+ cmd = megasas_get_cmd(instance); >+ if (!cmd) >+ return SCSI_MLQUEUE_HOST_BUSY; >+ >+ /* >+ * Logical drive command >+ */ >+ if (megasas_is_ldio(scmd)) >+ frame_count = megasas_build_ldio(instance, scmd, cmd); >+ else >+ frame_count = megasas_build_dcdb(instance, scmd, cmd); >+ >+ if (!frame_count) >+ goto out_return_cmd; >+ >+ cmd->scmd = scmd; >+ >+ /* >+ * Issue the command to the FW >+ */ >+ atomic_inc(&instance->fw_outstanding); >+ >+ instance->instancet->fire_cmd(cmd->frame_phys_addr ,cmd->frame_count-1,instance->reg_set); >+ >+ return 0; >+ >+ out_return_cmd: >+ megasas_return_cmd(instance, cmd); >+ out_done: >+ done(scmd); >+ return 0; >+} >+ >+/** >+ * megasas_wait_for_outstanding - Wait for all outstanding cmds >+ * @instance: Adapter soft state >+ * >+ * This function waits for upto MEGASAS_RESET_WAIT_TIME seconds for FW to >+ * complete all its outstanding commands. Returns error if one or more IOs >+ * are pending after this time period. It also marks the controller dead. >+ */ >+static int megasas_wait_for_outstanding(struct megasas_instance *instance) >+{ >+ int i; >+ u32 wait_time = MEGASAS_RESET_WAIT_TIME; >+ >+ for (i = 0; i < wait_time; i++) { >+ >+ int outstanding = atomic_read(&instance->fw_outstanding); >+ >+ if (!outstanding) >+ break; >+ >+ if (!(i % MEGASAS_RESET_NOTICE_INTERVAL)) { >+ printk(KERN_NOTICE "megasas: [%2d]waiting for %d " >+ "commands to complete\n",i,outstanding); >+ } >+ >+ msleep(1000); >+ } >+ >+ if (atomic_read(&instance->fw_outstanding)) { >+ instance->hw_crit_error = 1; >+ return FAILED; >+ } >+ >+ return SUCCESS; >+} >+ >+/** >+ * megasas_generic_reset - Generic reset routine >+ * @scmd: Mid-layer SCSI command >+ * >+ * This routine implements a generic reset handler for device, bus and host >+ * reset requests. Device, bus and host specific reset handlers can use this >+ * function after they do their specific tasks. >+ */ >+static int megasas_generic_reset(struct scsi_cmnd *scmd) >+{ >+ int ret_val; >+ struct megasas_instance *instance; >+ >+ instance = (struct megasas_instance *)scmd->device->host->hostdata; >+ >+ scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x\n", >+ scmd->serial_number, scmd->cmnd[0]); >+ >+ if (instance->hw_crit_error) { >+ printk(KERN_ERR "megasas: cannot recover from previous reset " >+ "failures\n"); >+ return FAILED; >+ } >+ >+ ret_val = megasas_wait_for_outstanding(instance); >+ if (ret_val == SUCCESS) >+ printk(KERN_NOTICE "megasas: reset successful \n"); >+ else >+ printk(KERN_ERR "megasas: failed to do reset\n"); >+ >+ return ret_val; >+} >+ >+/** >+ * megasas_reset_device - Device reset handler entry point >+ */ >+static int megasas_reset_device(struct scsi_cmnd *scmd) >+{ >+ int ret; >+ >+ /* >+ * First wait for all commands to complete >+ */ >+ ret = megasas_generic_reset(scmd); >+ >+ return ret; >+} >+ >+/** >+ * megasas_reset_bus_host - Bus & host reset handler entry point >+ */ >+static int megasas_reset_bus_host(struct scsi_cmnd *scmd) >+{ >+ int ret; >+ >+ /* >+ * Frist wait for all commands to complete >+ */ >+ ret = megasas_generic_reset(scmd); >+ >+ return ret; >+} >+ >+/** >+ * megasas_service_aen - Processes an event notification >+ * @instance: Adapter soft state >+ * @cmd: AEN command completed by the ISR >+ * >+ * For AEN, driver sends a command down to FW that is held by the FW till an >+ * event occurs. When an event of interest occurs, FW completes the command >+ * that it was previously holding. >+ * >+ * This routines sends SIGIO signal to processes that have registered with the >+ * driver for AEN. >+ */ >+static void >+megasas_service_aen(struct megasas_instance *instance, struct megasas_cmd *cmd) >+{ >+ /* >+ * Don't signal app if it is just an aborted previously registered aen >+ */ >+ if (!cmd->abort_aen) >+ kill_fasync(&megasas_async_queue, SIGIO, POLL_IN); >+ else >+ cmd->abort_aen = 0; >+ >+ instance->aen_cmd = NULL; >+ megasas_return_cmd(instance, cmd); >+} >+ >+static int megasas_slave_configure(struct scsi_device *sdev) >+{ >+ /* >+ * Don't export physical disk devices to the disk driver. >+ * >+ * FIXME: Currently we don't export them to the midlayer at all. >+ * That will be fixed once LSI engineers have audited the >+ * firmware for possible issues. >+ */ >+ if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK) >+ return -ENXIO; >+ /* >+ * The RAID firmware may require extended timeouts >+ */ >+ if(sdev->channel >= MEGASAS_MAX_PD_CHANNELS) >+ sdev->timeout = 90 * HZ ; >+ return 0; >+} >+ >+/* >+ * Scsi host template for megaraid_sas driver >+ */ >+static struct scsi_host_template megasas_template = { >+ >+ .module = THIS_MODULE, >+ .name = "LSI Logic SAS based MegaRAID driver", >+ .proc_name = "megaraid_sas", >+ .slave_configure = megasas_slave_configure, >+ .queuecommand = megasas_queue_command, >+ .eh_device_reset_handler = megasas_reset_device, >+ .eh_bus_reset_handler = megasas_reset_bus_host, >+ .eh_host_reset_handler = megasas_reset_bus_host, >+ .use_clustering = ENABLE_CLUSTERING, >+}; >+ >+/** >+ * megasas_complete_int_cmd - Completes an internal command >+ * @instance: Adapter soft state >+ * @cmd: Command to be completed >+ * >+ * The megasas_issue_blocked_cmd() function waits for a command to complete >+ * after it issues a command. This function wakes up that waiting routine by >+ * calling wake_up() on the wait queue. >+ */ >+static void >+megasas_complete_int_cmd(struct megasas_instance *instance, >+ struct megasas_cmd *cmd) >+{ >+ cmd->cmd_status = cmd->frame->io.cmd_status; >+ >+ if (cmd->cmd_status == ENODATA) { >+ cmd->cmd_status = 0; >+ } >+ wake_up(&instance->int_cmd_wait_q); >+} >+ >+/** >+ * megasas_complete_abort - Completes aborting a command >+ * @instance: Adapter soft state >+ * @cmd: Cmd that was issued to abort another cmd >+ * >+ * The megasas_issue_blocked_abort_cmd() function waits on abort_cmd_wait_q >+ * after it issues an abort on a previously issued command. This function >+ * wakes up all functions waiting on the same wait queue. >+ */ >+static void >+megasas_complete_abort(struct megasas_instance *instance, >+ struct megasas_cmd *cmd) >+{ >+ if (cmd->sync_cmd) { >+ cmd->sync_cmd = 0; >+ cmd->cmd_status = 0; >+ wake_up(&instance->abort_cmd_wait_q); >+ } >+ >+ return; >+} >+ >+/** >+ * megasas_unmap_sgbuf - Unmap SG buffers >+ * @instance: Adapter soft state >+ * @cmd: Completed command >+ */ >+static void >+megasas_unmap_sgbuf(struct megasas_instance *instance, struct megasas_cmd *cmd) >+{ >+ dma_addr_t buf_h; >+ u8 opcode; >+ >+ if (cmd->scmd->use_sg) { >+ pci_unmap_sg(instance->pdev, cmd->scmd->request_buffer, >+ cmd->scmd->use_sg, cmd->scmd->sc_data_direction); >+ return; >+ } >+ >+ if (!cmd->scmd->request_bufflen) >+ return; >+ >+ opcode = cmd->frame->hdr.cmd; >+ >+ if ((opcode == MFI_CMD_LD_READ) || (opcode == MFI_CMD_LD_WRITE)) { >+ if (IS_DMA64) >+ buf_h = cmd->frame->io.sgl.sge64[0].phys_addr; >+ else >+ buf_h = cmd->frame->io.sgl.sge32[0].phys_addr; >+ } else { >+ if (IS_DMA64) >+ buf_h = cmd->frame->pthru.sgl.sge64[0].phys_addr; >+ else >+ buf_h = cmd->frame->pthru.sgl.sge32[0].phys_addr; >+ } >+ >+ pci_unmap_single(instance->pdev, buf_h, cmd->scmd->request_bufflen, >+ cmd->scmd->sc_data_direction); >+ return; >+} >+ >+/** >+ * megasas_complete_cmd - Completes a command >+ * @instance: Adapter soft state >+ * @cmd: Command to be completed >+ * @alt_status: If non-zero, use this value as status to >+ * SCSI mid-layer instead of the value returned >+ * by the FW. This should be used if caller wants >+ * an alternate status (as in the case of aborted >+ * commands) >+ */ >+static 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; >+ >+ switch (hdr->cmd) { >+ >+ case MFI_CMD_PD_SCSI_IO: >+ case MFI_CMD_LD_SCSI_IO: >+ >+ /* >+ * MFI_CMD_PD_SCSI_IO and MFI_CMD_LD_SCSI_IO could have been >+ * issued either through an IO path or an IOCTL path. If it >+ * was via IOCTL, we will send it to internal completion. >+ */ >+ if (cmd->sync_cmd) { >+ cmd->sync_cmd = 0; >+ megasas_complete_int_cmd(instance, cmd); >+ break; >+ } >+ >+ case MFI_CMD_LD_READ: >+ case MFI_CMD_LD_WRITE: >+ >+ if (alt_status) { >+ cmd->scmd->result = alt_status << 16; >+ exception = 1; >+ } >+ >+ if (exception) { >+ >+ atomic_dec(&instance->fw_outstanding); >+ >+ megasas_unmap_sgbuf(instance, cmd); >+ cmd->scmd->scsi_done(cmd->scmd); >+ megasas_return_cmd(instance, cmd); >+ >+ break; >+ } >+ >+ switch (hdr->cmd_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) | hdr->scsi_status; >+ break; >+ >+ case MFI_STAT_SCSI_DONE_WITH_ERROR: >+ >+ cmd->scmd->result = (DID_OK << 16) | hdr->scsi_status; >+ >+ if (hdr->scsi_status == SAM_STAT_CHECK_CONDITION) { >+ memset(cmd->scmd->sense_buffer, 0, >+ SCSI_SENSE_BUFFERSIZE); >+ memcpy(cmd->scmd->sense_buffer, cmd->sense, >+ hdr->sense_len); >+ >+ 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: MFI FW status %#x\n", >+ hdr->cmd_status); >+ cmd->scmd->result = DID_ERROR << 16; >+ break; >+ } >+ >+ atomic_dec(&instance->fw_outstanding); >+ >+ megasas_unmap_sgbuf(instance, cmd); >+ cmd->scmd->scsi_done(cmd->scmd); >+ megasas_return_cmd(instance, cmd); >+ >+ break; >+ >+ case MFI_CMD_SMP: >+ case MFI_CMD_STP: >+ case MFI_CMD_DCMD: >+ >+ /* >+ * See if got an event notification >+ */ >+ if (cmd->frame->dcmd.opcode == MR_DCMD_CTRL_EVENT_WAIT) >+ megasas_service_aen(instance, cmd); >+ else >+ megasas_complete_int_cmd(instance, cmd); >+ >+ break; >+ >+ case MFI_CMD_ABORT: >+ /* >+ * Cmd issued to abort another cmd returned >+ */ >+ megasas_complete_abort(instance, cmd); >+ break; >+ >+ default: >+ printk("megasas: Unknown command completed! [0x%X]\n", >+ hdr->cmd); >+ break; >+ } >+} >+ >+/** >+ * megasas_deplete_reply_queue - Processes all completed commands >+ * @instance: Adapter soft state >+ * @alt_status: Alternate status to be returned to >+ * SCSI mid-layer instead of the status >+ * returned by the FW >+ */ >+static int >+megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) >+{ >+ u32 producer; >+ u32 consumer; >+ u32 context; >+ struct megasas_cmd *cmd; >+ >+ /* >+ * Check if it is our interrupt >+ * Clear the interrupt >+ */ >+ if(instance->instancet->clear_intr(instance->reg_set)) >+ return IRQ_NONE; >+ >+ producer = *instance->producer; >+ consumer = *instance->consumer; >+ >+ while (consumer != producer) { >+ context = instance->reply_queue[consumer]; >+ >+ cmd = instance->cmd_list[context]; >+ >+ megasas_complete_cmd(instance, cmd, alt_status); >+ >+ consumer++; >+ if (consumer == (instance->max_fw_cmds + 1)) { >+ consumer = 0; >+ } >+ } >+ >+ *instance->consumer = producer; >+ >+ return IRQ_HANDLED; >+} >+ >+/** >+ * megasas_isr - isr entry point >+ */ >+static irqreturn_t megasas_isr(int irq, void *devp, struct pt_regs *regs) >+{ >+ return megasas_deplete_reply_queue((struct megasas_instance *)devp, >+ DID_OK); >+} >+ >+/** >+ * megasas_transition_to_ready - Move the FW to READY state >+ * @instance: Adapter soft state >+ * >+ * During the initialization, FW passes can potentially be in any one of >+ * several possible states. If the FW in operational, waiting-for-handshake >+ * states, driver must take steps to bring it to ready state. Otherwise, it >+ * has to wait for the ready state. >+ */ >+static int >+megasas_transition_to_ready(struct megasas_instance* instance) >+{ >+ int i; >+ u8 max_wait; >+ u32 fw_state; >+ u32 cur_state; >+ >+ fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & MFI_STATE_MASK; >+ >+ while (fw_state != MFI_STATE_READY) { >+ >+ printk(KERN_INFO "megasas: Waiting for FW to come to ready" >+ " state\n"); >+ switch (fw_state) { >+ >+ case MFI_STATE_FAULT: >+ >+ printk(KERN_DEBUG "megasas: FW in FAULT state!!\n"); >+ return -ENODEV; >+ >+ case MFI_STATE_WAIT_HANDSHAKE: >+ /* >+ * Set the CLR bit in inbound doorbell >+ */ >+ writel(MFI_INIT_CLEAR_HANDSHAKE, >+ &instance->reg_set->inbound_doorbell); >+ >+ max_wait = 2; >+ cur_state = MFI_STATE_WAIT_HANDSHAKE; >+ break; >+ >+ case MFI_STATE_OPERATIONAL: >+ /* >+ * Bring it to READY state; assuming max wait 2 secs >+ */ >+ megasas_disable_intr(instance); >+ writel(MFI_INIT_READY, &instance->reg_set->inbound_doorbell); >+ >+ max_wait = 10; >+ cur_state = MFI_STATE_OPERATIONAL; >+ break; >+ >+ case MFI_STATE_UNDEFINED: >+ /* >+ * This state should not last for more than 2 seconds >+ */ >+ max_wait = 2; >+ cur_state = MFI_STATE_UNDEFINED; >+ break; >+ >+ case MFI_STATE_BB_INIT: >+ max_wait = 2; >+ cur_state = MFI_STATE_BB_INIT; >+ break; >+ >+ case MFI_STATE_FW_INIT: >+ max_wait = 20; >+ cur_state = MFI_STATE_FW_INIT; >+ break; >+ >+ case MFI_STATE_FW_INIT_2: >+ max_wait = 20; >+ cur_state = MFI_STATE_FW_INIT_2; >+ break; >+ >+ case MFI_STATE_DEVICE_SCAN: >+ max_wait = 20; >+ cur_state = MFI_STATE_DEVICE_SCAN; >+ break; >+ >+ case MFI_STATE_FLUSH_CACHE: >+ max_wait = 20; >+ cur_state = MFI_STATE_FLUSH_CACHE; >+ break; >+ >+ default: >+ printk(KERN_DEBUG "megasas: Unknown state 0x%x\n", >+ fw_state); >+ return -ENODEV; >+ } >+ >+ /* >+ * The cur_state should not last for more than max_wait secs >+ */ >+ for (i = 0; i < (max_wait * 1000); i++) { >+ fw_state = instance->instancet->read_fw_status_reg(instance->reg_set) & >+ MFI_STATE_MASK ; >+ >+ if (fw_state == cur_state) { >+ msleep(1); >+ } else >+ break; >+ } >+ >+ /* >+ * Return error if fw_state hasn't changed after max_wait >+ */ >+ if (fw_state == cur_state) { >+ printk(KERN_DEBUG "FW state [%d] hasn't changed " >+ "in %d secs\n", fw_state, max_wait); >+ return -ENODEV; >+ } >+ }; >+ >+ return 0; >+} >+ >+/** >+ * megasas_teardown_frame_pool - Destroy the cmd frame DMA pool >+ * @instance: Adapter soft state >+ */ >+static void megasas_teardown_frame_pool(struct megasas_instance *instance) >+{ >+ int i; >+ u32 max_cmd = instance->max_fw_cmds; >+ struct megasas_cmd *cmd; >+ >+ if (!instance->frame_dma_pool) >+ return; >+ >+ /* >+ * Return all frames to pool >+ */ >+ for (i = 0; i < max_cmd; i++) { >+ >+ cmd = instance->cmd_list[i]; >+ >+ if (cmd->frame) >+ pci_pool_free(instance->frame_dma_pool, cmd->frame, >+ cmd->frame_phys_addr); >+ >+ if (cmd->sense) >+ pci_pool_free(instance->sense_dma_pool, cmd->frame, >+ cmd->sense_phys_addr); >+ } >+ >+ /* >+ * Now destroy the pool itself >+ */ >+ pci_pool_destroy(instance->frame_dma_pool); >+ pci_pool_destroy(instance->sense_dma_pool); >+ >+ instance->frame_dma_pool = NULL; >+ instance->sense_dma_pool = NULL; >+} >+ >+/** >+ * megasas_create_frame_pool - Creates DMA pool for cmd frames >+ * @instance: Adapter soft state >+ * >+ * Each command packet has an embedded DMA memory buffer that is used for >+ * filling MFI frame and the SG list that immediately follows the frame. This >+ * function creates those DMA memory buffers for each command packet by using >+ * PCI pool facility. >+ */ >+static int megasas_create_frame_pool(struct megasas_instance *instance) >+{ >+ int i; >+ u32 max_cmd; >+ u32 sge_sz; >+ u32 sgl_sz; >+ u32 total_sz; >+ u32 frame_count; >+ struct megasas_cmd *cmd; >+ >+ max_cmd = instance->max_fw_cmds; >+ >+ /* >+ * Size of our frame is 64 bytes for MFI frame, followed by max SG >+ * elements and finally SCSI_SENSE_BUFFERSIZE bytes for sense buffer >+ */ >+ sge_sz = (IS_DMA64) ? sizeof(struct megasas_sge64) : >+ sizeof(struct megasas_sge32); >+ >+ /* >+ * Calculated the number of 64byte frames required for SGL >+ */ >+ sgl_sz = sge_sz * instance->max_num_sge; >+ frame_count = (sgl_sz + MEGAMFI_FRAME_SIZE - 1) / MEGAMFI_FRAME_SIZE; >+ >+ /* >+ * We need one extra frame for the MFI command >+ */ >+ frame_count++; >+ >+ total_sz = MEGAMFI_FRAME_SIZE * frame_count; >+ /* >+ * Use DMA pool facility provided by PCI layer >+ */ >+ instance->frame_dma_pool = pci_pool_create("megasas frame pool", >+ instance->pdev, total_sz, 64, >+ 0); >+ >+ if (!instance->frame_dma_pool) { >+ printk(KERN_DEBUG "megasas: failed to setup frame pool\n"); >+ return -ENOMEM; >+ } >+ >+ instance->sense_dma_pool = pci_pool_create("megasas sense pool", >+ instance->pdev, 128, 4, 0); >+ >+ if (!instance->sense_dma_pool) { >+ printk(KERN_DEBUG "megasas: failed to setup sense pool\n"); >+ >+ pci_pool_destroy(instance->frame_dma_pool); >+ instance->frame_dma_pool = NULL; >+ >+ return -ENOMEM; >+ } >+ >+ /* >+ * Allocate and attach a frame to each of the commands in cmd_list. >+ * By making cmd->index as the context instead of the &cmd, we can >+ * always use 32bit context regardless of the architecture >+ */ >+ for (i = 0; i < max_cmd; i++) { >+ >+ cmd = instance->cmd_list[i]; >+ >+ cmd->frame = pci_pool_alloc(instance->frame_dma_pool, >+ GFP_KERNEL, &cmd->frame_phys_addr); >+ >+ cmd->sense = pci_pool_alloc(instance->sense_dma_pool, >+ GFP_KERNEL, &cmd->sense_phys_addr); >+ >+ /* >+ * megasas_teardown_frame_pool() takes care of freeing >+ * whatever has been allocated >+ */ >+ if (!cmd->frame || !cmd->sense) { >+ printk(KERN_DEBUG "megasas: pci_pool_alloc failed \n"); >+ megasas_teardown_frame_pool(instance); >+ return -ENOMEM; >+ } >+ >+ cmd->frame->io.context = cmd->index; >+ } >+ >+ return 0; >+} >+ >+/** >+ * 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) >+{ >+ 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++) >+ kfree(instance->cmd_list[i]); >+ >+ /* Free the cmd_list buffer itself */ >+ kfree(instance->cmd_list); >+ instance->cmd_list = NULL; >+ >+ INIT_LIST_HEAD(&instance->cmd_pool); >+} >+ >+/** >+ * megasas_alloc_cmds - Allocates the command packets >+ * @instance: Adapter soft state >+ * >+ * Each command that is issued to the FW, whether IO commands from the OS or >+ * internal commands like IOCTLs, are wrapped in local data structure called >+ * megasas_cmd. The frame embedded in this megasas_cmd is actually issued to >+ * the FW. >+ * >+ * Each frame has a 32-bit field called context (tag). This context is used >+ * to get back the megasas_cmd from the frame when a frame gets completed in >+ * the ISR. Typically the address of the megasas_cmd itself would be used as >+ * the context. But we wanted to keep the differences between 32 and 64 bit >+ * systems to the mininum. We always use 32 bit integers for the context. 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 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 i; >+ int j; >+ u32 max_cmd; >+ struct megasas_cmd *cmd; >+ >+ max_cmd = instance->max_fw_cmds; >+ >+ /* >+ * instance->cmd_list is an array of struct megasas_cmd pointers. >+ * Allocate the dynamic array first and then allocate individual >+ * commands. >+ */ >+ instance->cmd_list = kmalloc(sizeof(struct megasas_cmd *) * max_cmd, >+ GFP_KERNEL); >+ >+ if (!instance->cmd_list) { >+ printk(KERN_DEBUG "megasas: out of memory\n"); >+ 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), >+ GFP_KERNEL); >+ >+ if (!instance->cmd_list[i]) { >+ >+ for (j = 0; j < i; j++) >+ kfree(instance->cmd_list[j]); >+ >+ kfree(instance->cmd_list); >+ instance->cmd_list = NULL; >+ >+ return -ENOMEM; >+ } >+ } >+ >+ /* >+ * Add all the commands to command pool (instance->cmd_pool) >+ */ >+ for (i = 0; i < max_cmd; i++) { >+ cmd = instance->cmd_list[i]; >+ memset(cmd, 0, sizeof(struct megasas_cmd)); >+ cmd->index = i; >+ cmd->instance = instance; >+ >+ list_add_tail(&cmd->list, &instance->cmd_pool); >+ } >+ >+ /* >+ * Create a frame pool and assign one frame to each cmd >+ */ >+ if (megasas_create_frame_pool(instance)) { >+ printk(KERN_DEBUG "megasas: Error creating frame DMA pool\n"); >+ megasas_free_cmds(instance); >+ } >+ >+ return 0; >+} >+ >+/** >+ * megasas_get_controller_info - Returns FW's controller structure >+ * @instance: Adapter soft state >+ * @ctrl_info: Controller information structure >+ * >+ * Issues an internal command (DCMD) to get the FW's controller structure. >+ * This information is mainly used to find out the maximum IO transfer per >+ * command supported by the FW. >+ */ >+static int >+megasas_get_ctrl_info(struct megasas_instance *instance, >+ struct megasas_ctrl_info *ctrl_info) >+{ >+ int ret = 0; >+ struct megasas_cmd *cmd; >+ struct megasas_dcmd_frame *dcmd; >+ struct megasas_ctrl_info *ci; >+ dma_addr_t ci_h = 0; >+ >+ cmd = megasas_get_cmd(instance); >+ >+ if (!cmd) { >+ printk(KERN_DEBUG "megasas: Failed to get a free cmd\n"); >+ return -ENOMEM; >+ } >+ >+ dcmd = &cmd->frame->dcmd; >+ >+ ci = pci_alloc_consistent(instance->pdev, >+ sizeof(struct megasas_ctrl_info), &ci_h); >+ >+ if (!ci) { >+ printk(KERN_DEBUG "Failed to alloc mem for ctrl 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->data_xfer_len = sizeof(struct megasas_ctrl_info); >+ dcmd->opcode = MR_DCMD_CTRL_GET_INFO; >+ dcmd->sgl.sge32[0].phys_addr = ci_h; >+ dcmd->sgl.sge32[0].length = sizeof(struct megasas_ctrl_info); >+ >+ if (!megasas_issue_polled(instance, cmd)) { >+ ret = 0; >+ memcpy(ctrl_info, ci, sizeof(struct megasas_ctrl_info)); >+ } else { >+ ret = -1; >+ } >+ >+ pci_free_consistent(instance->pdev, sizeof(struct megasas_ctrl_info), >+ ci, ci_h); >+ >+ megasas_return_cmd(instance, cmd); >+ return ret; >+} >+ >+/** >+ * megasas_init_mfi - Initializes the FW >+ * @instance: Adapter soft state >+ * >+ * This is the main function for initializing MFI firmware. >+ */ >+static int megasas_init_mfi(struct megasas_instance *instance) >+{ >+ u32 context_sz; >+ u32 reply_q_sz; >+ u32 max_sectors_1; >+ u32 max_sectors_2; >+ struct megasas_register_set __iomem *reg_set; >+ >+ struct megasas_cmd *cmd; >+ struct megasas_ctrl_info *ctrl_info; >+ >+ struct megasas_init_frame *init_frame; >+ struct megasas_init_queue_info *initq_info; >+ dma_addr_t init_frame_h; >+ dma_addr_t initq_info_h; >+ >+ /* >+ * Map the message registers >+ */ >+ instance->base_addr = pci_resource_start(instance->pdev, 0); >+ >+ if (pci_request_regions(instance->pdev, "megasas: LSI Logic")) { >+ printk(KERN_DEBUG "megasas: IO memory region busy!\n"); >+ return -EBUSY; >+ } >+ >+ instance->reg_set = ioremap_nocache(instance->base_addr, 8192); >+ >+ if (!instance->reg_set) { >+ printk(KERN_DEBUG "megasas: Failed to map IO mem\n"); >+ goto fail_ioremap; >+ } >+ >+ reg_set = instance->reg_set; >+ >+ switch(instance->pdev->device) >+ { >+ case PCI_DEVICE_ID_LSI_SAS1078R: >+ instance->instancet = &megasas_instance_template_ppc; >+ break; >+ case PCI_DEVICE_ID_LSI_SAS1064R: >+ case PCI_DEVICE_ID_DELL_PERC5: >+ default: >+ instance->instancet = &megasas_instance_template_xscale; >+ break; >+ } >+ >+ /* >+ * We expect the FW state to be READY >+ */ >+ if (megasas_transition_to_ready(instance)) >+ goto fail_ready_state; >+ >+ /* >+ * Get various operational parameters from status register >+ */ >+ instance->max_fw_cmds = instance->instancet->read_fw_status_reg(reg_set) & 0x00FFFF; >+ instance->max_num_sge = (instance->instancet->read_fw_status_reg(reg_set) & 0xFF0000) >> >+ 0x10; >+ /* >+ * Create a pool of commands >+ */ >+ if (megasas_alloc_cmds(instance)) >+ goto fail_alloc_cmds; >+ >+ /* >+ * Allocate memory for reply queue. Length of reply queue should >+ * be _one_ more than the maximum commands handled by the firmware. >+ * >+ * Note: When FW completes commands, it places corresponding contex >+ * values in this circular reply queue. This circular queue is a fairly >+ * typical producer-consumer queue. FW is the producer (of completed >+ * commands) and the driver is the consumer. >+ */ >+ context_sz = sizeof(u32); >+ reply_q_sz = context_sz * (instance->max_fw_cmds + 1); >+ >+ instance->reply_queue = pci_alloc_consistent(instance->pdev, >+ reply_q_sz, >+ &instance->reply_queue_h); >+ >+ if (!instance->reply_queue) { >+ printk(KERN_DEBUG "megasas: Out of DMA mem for reply queue\n"); >+ goto fail_reply_queue; >+ } >+ >+ /* >+ * Prepare a init frame. Note the init frame points to queue info >+ * structure. Each frame has SGL allocated after first 64 bytes. For >+ * this frame - since we don't need any SGL - we use SGL's space as >+ * queue info structure >+ * >+ * We will not get a NULL command below. We just created the pool. >+ */ >+ cmd = megasas_get_cmd(instance); >+ >+ init_frame = (struct megasas_init_frame *)cmd->frame; >+ initq_info = (struct megasas_init_queue_info *) >+ ((unsigned long)init_frame + 64); >+ >+ init_frame_h = cmd->frame_phys_addr; >+ initq_info_h = init_frame_h + 64; >+ >+ memset(init_frame, 0, MEGAMFI_FRAME_SIZE); >+ memset(initq_info, 0, sizeof(struct megasas_init_queue_info)); >+ >+ initq_info->reply_queue_entries = instance->max_fw_cmds + 1; >+ initq_info->reply_queue_start_phys_addr_lo = instance->reply_queue_h; >+ >+ initq_info->producer_index_phys_addr_lo = instance->producer_h; >+ initq_info->consumer_index_phys_addr_lo = instance->consumer_h; >+ >+ init_frame->cmd = MFI_CMD_INIT; >+ init_frame->cmd_status = 0xFF; >+ init_frame->queue_info_new_phys_addr_lo = initq_info_h; >+ >+ init_frame->data_xfer_len = sizeof(struct megasas_init_queue_info); >+ >+ /* >+ * disable the intr before fire the init frame to FW >+ */ >+ megasas_disable_intr(instance); >+ >+ /* >+ * Issue the init frame in polled mode >+ */ >+ if (megasas_issue_polled(instance, cmd)) { >+ printk(KERN_DEBUG "megasas: Failed to init firmware\n"); >+ goto fail_fw_init; >+ } >+ >+ megasas_return_cmd(instance, cmd); >+ >+ ctrl_info = kmalloc(sizeof(struct megasas_ctrl_info), GFP_KERNEL); >+ >+ /* >+ * Compute the max allowed sectors per IO: The controller info has two >+ * limits on max sectors. Driver should use the minimum of these two. >+ * >+ * 1 << stripe_sz_ops.min = max sectors per strip >+ * >+ * Note that older firmwares ( < FW ver 30) didn't report information >+ * to calculate max_sectors_1. So the number ended up as zero always. >+ */ >+ if (ctrl_info && !megasas_get_ctrl_info(instance, ctrl_info)) { >+ >+ max_sectors_1 = (1 << ctrl_info->stripe_sz_ops.min) * >+ ctrl_info->max_strips_per_io; >+ max_sectors_2 = ctrl_info->max_request_size; >+ >+ instance->max_sectors_per_req = (max_sectors_1 < max_sectors_2) >+ ? max_sectors_1 : max_sectors_2; >+ } else >+ instance->max_sectors_per_req = instance->max_num_sge * >+ PAGE_SIZE / 512; >+ >+ kfree(ctrl_info); >+ >+ return 0; >+ >+ fail_fw_init: >+ megasas_return_cmd(instance, cmd); >+ >+ pci_free_consistent(instance->pdev, reply_q_sz, >+ instance->reply_queue, instance->reply_queue_h); >+ fail_reply_queue: >+ megasas_free_cmds(instance); >+ >+ fail_alloc_cmds: >+ fail_ready_state: >+ iounmap(instance->reg_set); >+ >+ fail_ioremap: >+ pci_release_regions(instance->pdev); >+ >+ return -EINVAL; >+} >+ >+/** >+ * megasas_release_mfi - Reverses the FW initialization >+ * @intance: Adapter soft state >+ */ >+static void megasas_release_mfi(struct megasas_instance *instance) >+{ >+ u32 reply_q_sz = sizeof(u32) * (instance->max_fw_cmds + 1); >+ >+ pci_free_consistent(instance->pdev, reply_q_sz, >+ instance->reply_queue, instance->reply_queue_h); >+ >+ megasas_free_cmds(instance); >+ >+ iounmap(instance->reg_set); >+ >+ pci_release_regions(instance->pdev); >+} >+ >+/** >+ * megasas_get_seq_num - Gets latest event sequence numbers >+ * @instance: Adapter soft state >+ * @eli: FW event log sequence numbers information >+ * >+ * FW maintains a log of all events in a non-volatile area. Upper layers would >+ * usually find out the latest sequence number of the events, the seq number at >+ * the boot etc. They would "read" all the events below the latest seq number >+ * by issuing a direct fw cmd (DCMD). For the future events (beyond latest seq >+ * number), they would subsribe to AEN (asynchronous event notification) and >+ * wait for the events to happen. >+ */ >+static int >+megasas_get_seq_num(struct megasas_instance *instance, >+ struct megasas_evt_log_info *eli) >+{ >+ struct megasas_cmd *cmd; >+ struct megasas_dcmd_frame *dcmd; >+ struct megasas_evt_log_info *el_info; >+ dma_addr_t el_info_h = 0; >+ >+ cmd = megasas_get_cmd(instance); >+ >+ if (!cmd) { >+ return -ENOMEM; >+ } >+ >+ dcmd = &cmd->frame->dcmd; >+ el_info = pci_alloc_consistent(instance->pdev, >+ sizeof(struct megasas_evt_log_info), >+ &el_info_h); >+ >+ if (!el_info) { >+ megasas_return_cmd(instance, cmd); >+ return -ENOMEM; >+ } >+ >+ memset(el_info, 0, sizeof(*el_info)); >+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); >+ >+ dcmd->cmd = MFI_CMD_DCMD; >+ dcmd->cmd_status = 0x0; >+ dcmd->sge_count = 1; >+ dcmd->flags = MFI_FRAME_DIR_READ; >+ dcmd->timeout = 0; >+ dcmd->data_xfer_len = sizeof(struct megasas_evt_log_info); >+ dcmd->opcode = MR_DCMD_CTRL_EVENT_GET_INFO; >+ dcmd->sgl.sge32[0].phys_addr = el_info_h; >+ dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_log_info); >+ >+ megasas_issue_blocked_cmd(instance, cmd); >+ >+ /* >+ * Copy the data back into callers buffer >+ */ >+ memcpy(eli, el_info, sizeof(struct megasas_evt_log_info)); >+ >+ pci_free_consistent(instance->pdev, sizeof(struct megasas_evt_log_info), >+ el_info, el_info_h); >+ >+ megasas_return_cmd(instance, cmd); >+ >+ return 0; >+} >+ >+/** >+ * megasas_register_aen - Registers for asynchronous event notification >+ * @instance: Adapter soft state >+ * @seq_num: The starting sequence number >+ * @class_locale: Class of the event >+ * >+ * This function subscribes for AEN for events beyond the @seq_num. It requests >+ * to be notified if and only if the event is of type @class_locale >+ */ >+static int >+megasas_register_aen(struct megasas_instance *instance, u32 seq_num, >+ u32 class_locale_word) >+{ >+ int ret_val; >+ struct megasas_cmd *cmd; >+ struct megasas_dcmd_frame *dcmd; >+ union megasas_evt_class_locale curr_aen; >+ union megasas_evt_class_locale prev_aen; >+ >+ /* >+ * If there an AEN pending already (aen_cmd), check if the >+ * class_locale of that pending AEN is inclusive of the new >+ * AEN request we currently have. If it is, then we don't have >+ * to do anything. In other words, whichever events the current >+ * AEN request is subscribing to, have already been subscribed >+ * to. >+ * >+ * If the old_cmd is _not_ inclusive, then we have to abort >+ * that command, form a class_locale that is superset of both >+ * old and current and re-issue to the FW >+ */ >+ >+ curr_aen.word = class_locale_word; >+ >+ if (instance->aen_cmd) { >+ >+ prev_aen.word = instance->aen_cmd->frame->dcmd.mbox.w[1]; >+ >+ /* >+ * A class whose enum value is smaller is inclusive of all >+ * higher values. If a PROGRESS (= -1) was previously >+ * registered, then a new registration requests for higher >+ * classes need not be sent to FW. They are automatically >+ * included. >+ * >+ * Locale numbers don't have such hierarchy. They are bitmap >+ * values >+ */ >+ if ((prev_aen.members.class <= curr_aen.members.class) && >+ !((prev_aen.members.locale & curr_aen.members.locale) ^ >+ curr_aen.members.locale)) { >+ /* >+ * Previously issued event registration includes >+ * current request. Nothing to do. >+ */ >+ return 0; >+ } else { >+ curr_aen.members.locale |= prev_aen.members.locale; >+ >+ if (prev_aen.members.class < curr_aen.members.class) >+ curr_aen.members.class = prev_aen.members.class; >+ >+ instance->aen_cmd->abort_aen = 1; >+ ret_val = megasas_issue_blocked_abort_cmd(instance, >+ instance-> >+ aen_cmd); >+ >+ if (ret_val) { >+ printk(KERN_DEBUG "megasas: Failed to abort " >+ "previous AEN command\n"); >+ return ret_val; >+ } >+ } >+ } >+ >+ cmd = megasas_get_cmd(instance); >+ >+ if (!cmd) >+ return -ENOMEM; >+ >+ dcmd = &cmd->frame->dcmd; >+ >+ memset(instance->evt_detail, 0, sizeof(struct megasas_evt_detail)); >+ >+ /* >+ * Prepare DCMD for aen registration >+ */ >+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); >+ >+ dcmd->cmd = MFI_CMD_DCMD; >+ dcmd->cmd_status = 0x0; >+ dcmd->sge_count = 1; >+ dcmd->flags = MFI_FRAME_DIR_READ; >+ dcmd->timeout = 0; >+ dcmd->data_xfer_len = sizeof(struct megasas_evt_detail); >+ dcmd->opcode = MR_DCMD_CTRL_EVENT_WAIT; >+ dcmd->mbox.w[0] = seq_num; >+ dcmd->mbox.w[1] = curr_aen.word; >+ dcmd->sgl.sge32[0].phys_addr = (u32) instance->evt_detail_h; >+ dcmd->sgl.sge32[0].length = sizeof(struct megasas_evt_detail); >+ >+ /* >+ * Store reference to the cmd used to register for AEN. When an >+ * application wants us to register for AEN, we have to abort this >+ * cmd and re-register with a new EVENT LOCALE supplied by that app >+ */ >+ instance->aen_cmd = cmd; >+ >+ /* >+ * Issue the aen registration frame >+ */ >+ instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); >+ >+ return 0; >+} >+ >+/** >+ * megasas_start_aen - Subscribes to AEN during driver load time >+ * @instance: Adapter soft state >+ */ >+static int megasas_start_aen(struct megasas_instance *instance) >+{ >+ struct megasas_evt_log_info eli; >+ union megasas_evt_class_locale class_locale; >+ >+ /* >+ * Get the latest sequence number from FW >+ */ >+ memset(&eli, 0, sizeof(eli)); >+ >+ if (megasas_get_seq_num(instance, &eli)) >+ return -1; >+ >+ /* >+ * Register AEN with FW for latest sequence number plus 1 >+ */ >+ class_locale.members.reserved = 0; >+ class_locale.members.locale = MR_EVT_LOCALE_ALL; >+ class_locale.members.class = MR_EVT_CLASS_DEBUG; >+ >+ return megasas_register_aen(instance, eli.newest_seq_num + 1, >+ class_locale.word); >+} >+ >+/** >+ * megasas_io_attach - Attaches this driver to SCSI mid-layer >+ * @instance: Adapter soft state >+ */ >+static int megasas_io_attach(struct megasas_instance *instance) >+{ >+ struct Scsi_Host *host = instance->host; >+ >+ /* >+ * Export parameters required by SCSI mid-layer >+ */ >+ host->irq = instance->pdev->irq; >+ host->unique_id = instance->unique_id; >+ host->can_queue = instance->max_fw_cmds - MEGASAS_INT_CMDS; >+ host->this_id = instance->init_id; >+ host->sg_tablesize = instance->max_num_sge; >+ host->max_sectors = instance->max_sectors_per_req; >+ host->cmd_per_lun = 128; >+ 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; >+ >+ /* >+ * Notify the mid-layer about the new controller >+ */ >+ if (scsi_add_host(host, &instance->pdev->dev)) { >+ printk(KERN_DEBUG "megasas: scsi_add_host failed\n"); >+ return -ENODEV; >+ } >+ >+ /* >+ * Trigger SCSI to scan our drives >+ */ >+ scsi_scan_host(host); >+ return 0; >+} >+ >+/** >+ * megasas_probe_one - PCI hotplug entry point >+ * @pdev: PCI device structure >+ * @id: PCI ids of supported hotplugged adapter >+ */ >+static int __devinit >+megasas_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) >+{ >+ int rval; >+ struct Scsi_Host *host; >+ struct megasas_instance *instance; >+ >+ /* >+ * Announce PCI information >+ */ >+ printk(KERN_INFO "megasas: %#4.04x:%#4.04x:%#4.04x:%#4.04x: ", >+ pdev->vendor, pdev->device, pdev->subsystem_vendor, >+ pdev->subsystem_device); >+ >+ printk("bus %d:slot %d:func %d\n", >+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); >+ >+ /* >+ * PCI prepping: enable device set bus mastering and dma mask >+ */ >+ rval = pci_enable_device(pdev); >+ >+ if (rval) { >+ return rval; >+ } >+ >+ pci_set_master(pdev); >+ >+ /* >+ * All our contollers are capable of performing 64-bit DMA >+ */ >+ if (IS_DMA64) { >+ if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) != 0) { >+ >+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) >+ goto fail_set_dma_mask; >+ } >+ } else { >+ if (pci_set_dma_mask(pdev, DMA_32BIT_MASK) != 0) >+ goto fail_set_dma_mask; >+ } >+ >+ host = scsi_host_alloc(&megasas_template, >+ sizeof(struct megasas_instance)); >+ >+ if (!host) { >+ printk(KERN_DEBUG "megasas: scsi_host_alloc failed\n"); >+ goto fail_alloc_instance; >+ } >+ >+ instance = (struct megasas_instance *)host->hostdata; >+ memset(instance, 0, sizeof(*instance)); >+ >+ 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; >+ } >+ >+ *instance->producer = 0; >+ *instance->consumer = 0; >+ >+ instance->evt_detail = pci_alloc_consistent(pdev, >+ sizeof(struct >+ megasas_evt_detail), >+ &instance->evt_detail_h); >+ >+ if (!instance->evt_detail) { >+ printk(KERN_DEBUG "megasas: Failed to allocate memory for " >+ "event detail structure\n"); >+ goto fail_alloc_dma_buf; >+ } >+ >+ /* >+ * Initialize locks and queues >+ */ >+ INIT_LIST_HEAD(&instance->cmd_pool); >+ >+ atomic_set(&instance->fw_outstanding,0); >+ >+ init_waitqueue_head(&instance->int_cmd_wait_q); >+ init_waitqueue_head(&instance->abort_cmd_wait_q); >+ >+ spin_lock_init(&instance->cmd_pool_lock); >+ >+ sema_init(&instance->aen_mutex, 1); >+ sema_init(&instance->ioctl_sem, MEGASAS_INT_CMDS); >+ >+ /* >+ * 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; >+ >+ /* >+ * Initialize MFI Firmware >+ */ >+ if (megasas_init_mfi(instance)) >+ goto fail_init_mfi; >+ >+ /* >+ * Register IRQ >+ */ >+ if (request_irq(pdev->irq, megasas_isr, SA_SHIRQ, "megasas", instance)) { >+ printk(KERN_DEBUG "megasas: Failed to register IRQ\n"); >+ goto fail_irq; >+ } >+ >+ instance->instancet->enable_intr(instance->reg_set); >+ >+ /* >+ * Store instance in PCI softstate >+ */ >+ pci_set_drvdata(pdev, instance); >+ >+ /* >+ * Add this controller to megasas_mgmt_info structure so that it >+ * can be exported to management applications >+ */ >+ megasas_mgmt_info.count++; >+ megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = instance; >+ megasas_mgmt_info.max_index++; >+ >+ /* >+ * Initiate AEN (Asynchronous Event Notification) >+ */ >+ if (megasas_start_aen(instance)) { >+ printk(KERN_DEBUG "megasas: start aen failed\n"); >+ goto fail_start_aen; >+ } >+ >+ /* >+ * Register with SCSI mid-layer >+ */ >+ if (megasas_io_attach(instance)) >+ goto fail_io_attach; >+ >+ return 0; >+ >+ fail_start_aen: >+ fail_io_attach: >+ megasas_mgmt_info.count--; >+ megasas_mgmt_info.instance[megasas_mgmt_info.max_index] = NULL; >+ megasas_mgmt_info.max_index--; >+ >+ pci_set_drvdata(pdev, NULL); >+ megasas_disable_intr(instance); >+ free_irq(instance->pdev->irq, instance); >+ >+ megasas_release_mfi(instance); >+ >+ fail_irq: >+ fail_init_mfi: >+ fail_alloc_dma_buf: >+ if (instance->evt_detail) >+ pci_free_consistent(pdev, sizeof(struct megasas_evt_detail), >+ instance->evt_detail, >+ instance->evt_detail_h); >+ >+ if (instance->producer) >+ pci_free_consistent(pdev, sizeof(u32), instance->producer, >+ instance->producer_h); >+ if (instance->consumer) >+ pci_free_consistent(pdev, sizeof(u32), instance->consumer, >+ instance->consumer_h); >+ scsi_host_put(host); >+ >+ fail_alloc_instance: >+ fail_set_dma_mask: >+ pci_disable_device(pdev); >+ >+ return -ENODEV; >+} >+ >+/** >+ * megasas_flush_cache - Requests FW to flush all its caches >+ * @instance: Adapter soft state >+ */ >+static void megasas_flush_cache(struct megasas_instance *instance) >+{ >+ struct megasas_cmd *cmd; >+ struct megasas_dcmd_frame *dcmd; >+ >+ cmd = megasas_get_cmd(instance); >+ >+ if (!cmd) >+ return; >+ >+ dcmd = &cmd->frame->dcmd; >+ >+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); >+ >+ dcmd->cmd = MFI_CMD_DCMD; >+ dcmd->cmd_status = 0x0; >+ dcmd->sge_count = 0; >+ dcmd->flags = MFI_FRAME_DIR_NONE; >+ dcmd->timeout = 0; >+ dcmd->data_xfer_len = 0; >+ dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH; >+ dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; >+ >+ megasas_issue_blocked_cmd(instance, cmd); >+ >+ megasas_return_cmd(instance, cmd); >+ >+ return; >+} >+ >+/** >+ * megasas_shutdown_controller - Instructs FW to shutdown the controller >+ * @instance: Adapter soft state >+ */ >+static void megasas_shutdown_controller(struct megasas_instance *instance) >+{ >+ struct megasas_cmd *cmd; >+ struct megasas_dcmd_frame *dcmd; >+ >+ cmd = megasas_get_cmd(instance); >+ >+ if (!cmd) >+ return; >+ >+ if (instance->aen_cmd) >+ megasas_issue_blocked_abort_cmd(instance, instance->aen_cmd); >+ >+ dcmd = &cmd->frame->dcmd; >+ >+ memset(dcmd->mbox.b, 0, MFI_MBOX_SIZE); >+ >+ dcmd->cmd = MFI_CMD_DCMD; >+ dcmd->cmd_status = 0x0; >+ dcmd->sge_count = 0; >+ dcmd->flags = MFI_FRAME_DIR_NONE; >+ dcmd->timeout = 0; >+ dcmd->data_xfer_len = 0; >+ dcmd->opcode = MR_DCMD_CTRL_SHUTDOWN; >+ >+ megasas_issue_blocked_cmd(instance, cmd); >+ >+ megasas_return_cmd(instance, cmd); >+ >+ return; >+} >+ >+/** >+ * megasas_detach_one - PCI hot"un"plug entry point >+ * @pdev: PCI device structure >+ */ >+static void megasas_detach_one(struct pci_dev *pdev) >+{ >+ int i; >+ struct Scsi_Host *host; >+ struct megasas_instance *instance; >+ >+ instance = pci_get_drvdata(pdev); >+ host = instance->host; >+ >+ scsi_remove_host(instance->host); >+ megasas_flush_cache(instance); >+ megasas_shutdown_controller(instance); >+ >+ /* >+ * Take the instance off the instance array. Note that we will not >+ * decrement the max_index. We let this array be sparse array >+ */ >+ for (i = 0; i < megasas_mgmt_info.max_index; i++) { >+ if (megasas_mgmt_info.instance[i] == instance) { >+ megasas_mgmt_info.count--; >+ megasas_mgmt_info.instance[i] = NULL; >+ >+ break; >+ } >+ } >+ >+ pci_set_drvdata(instance->pdev, NULL); >+ >+ megasas_disable_intr(instance); >+ >+ free_irq(instance->pdev->irq, instance); >+ >+ 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); >+ >+ scsi_host_put(host); >+ >+ pci_set_drvdata(pdev, NULL); >+ >+ pci_disable_device(pdev); >+ >+ return; >+} >+ >+/** >+ * megasas_shutdown - Shutdown entry point >+ * @device: Generic device structure >+ */ >+static void megasas_shutdown(struct pci_dev *pdev) >+{ >+ struct megasas_instance *instance = pci_get_drvdata(pdev); >+ megasas_flush_cache(instance); >+} >+ >+/** >+ * megasas_mgmt_open - char node "open" entry point >+ */ >+static int megasas_mgmt_open(struct inode *inode, struct file *filep) >+{ >+ /* >+ * Allow only those users with admin rights >+ */ >+ if (!capable(CAP_SYS_ADMIN)) >+ return -EACCES; >+ >+ return 0; >+} >+ >+/** >+ * megasas_mgmt_release - char node "release" entry point >+ */ >+static int megasas_mgmt_release(struct inode *inode, struct file *filep) >+{ >+ filep->private_data = NULL; >+ fasync_helper(-1, filep, 0, &megasas_async_queue); >+ >+ return 0; >+} >+ >+/** >+ * megasas_mgmt_fasync - Async notifier registration from applications >+ * >+ * This function adds the calling process to a driver global queue. When an >+ * event occurs, SIGIO will be sent to all processes in this queue. >+ */ >+static int megasas_mgmt_fasync(int fd, struct file *filep, int mode) >+{ >+ int rc; >+ >+ mutex_lock(&megasas_async_queue_mutex); >+ >+ rc = fasync_helper(fd, filep, mode, &megasas_async_queue); >+ >+ mutex_unlock(&megasas_async_queue_mutex); >+ >+ if (rc >= 0) { >+ /* For sanity check when we get ioctl */ >+ filep->private_data = filep; >+ return 0; >+ } >+ >+ printk(KERN_DEBUG "megasas: fasync_helper failed [%d]\n", rc); >+ >+ return rc; >+} >+ >+/** >+ * megasas_mgmt_fw_ioctl - Issues management ioctls to FW >+ * @instance: Adapter soft state >+ * @argp: User's ioctl packet >+ */ >+static int >+megasas_mgmt_fw_ioctl(struct megasas_instance *instance, >+ struct megasas_iocpacket __user * user_ioc, >+ struct megasas_iocpacket *ioc) >+{ >+ struct megasas_sge32 *kern_sge32; >+ struct megasas_cmd *cmd; >+ void *kbuff_arr[MAX_IOCTL_SGE]; >+ dma_addr_t buf_handle = 0; >+ int error = 0, i; >+ void *sense = NULL; >+ dma_addr_t sense_handle; >+ u32 *sense_ptr; >+ >+ memset(kbuff_arr, 0, sizeof(kbuff_arr)); >+ >+ if (ioc->sge_count > MAX_IOCTL_SGE) { >+ printk(KERN_DEBUG "megasas: SGE count [%d] > max limit [%d]\n", >+ ioc->sge_count, MAX_IOCTL_SGE); >+ return -EINVAL; >+ } >+ >+ cmd = megasas_get_cmd(instance); >+ if (!cmd) { >+ printk(KERN_DEBUG "megasas: Failed to get a cmd packet\n"); >+ return -ENOMEM; >+ } >+ >+ /* >+ * User's IOCTL packet has 2 frames (maximum). Copy those two >+ * frames into our cmd's frames. cmd->frame's context will get >+ * overwritten when we copy from user's frames. So set that value >+ * alone separately >+ */ >+ memcpy(cmd->frame, ioc->frame.raw, 2 * MEGAMFI_FRAME_SIZE); >+ cmd->frame->hdr.context = cmd->index; >+ >+ /* >+ * The management interface between applications and the fw uses >+ * MFI frames. E.g, RAID configuration changes, LD property changes >+ * etc are accomplishes through different kinds of MFI frames. The >+ * driver needs to care only about substituting user buffers with >+ * kernel buffers in SGLs. The location of SGL is embedded in the >+ * struct iocpacket itself. >+ */ >+ kern_sge32 = (struct megasas_sge32 *) >+ ((unsigned long)cmd->frame + ioc->sgl_off); >+ >+ /* >+ * For each user buffer, create a mirror buffer and copy in >+ */ >+ for (i = 0; i < ioc->sge_count; i++) { >+ kbuff_arr[i] = pci_alloc_consistent(instance->pdev, >+ ioc->sgl[i].iov_len, >+ &buf_handle); >+ if (!kbuff_arr[i]) { >+ printk(KERN_DEBUG "megasas: Failed to alloc " >+ "kernel SGL buffer for IOCTL \n"); >+ error = -ENOMEM; >+ goto out; >+ } >+ >+ /* >+ * We don't change the dma_coherent_mask, so >+ * pci_alloc_consistent only returns 32bit addresses >+ */ >+ kern_sge32[i].phys_addr = (u32) buf_handle; >+ kern_sge32[i].length = ioc->sgl[i].iov_len; >+ >+ /* >+ * We created a kernel buffer corresponding to the >+ * user buffer. Now copy in from the user buffer >+ */ >+ if (copy_from_user(kbuff_arr[i], ioc->sgl[i].iov_base, >+ (u32) (ioc->sgl[i].iov_len))) { >+ error = -EFAULT; >+ goto out; >+ } >+ } >+ >+ if (ioc->sense_len) { >+ sense = pci_alloc_consistent(instance->pdev, ioc->sense_len, >+ &sense_handle); >+ if (!sense) { >+ error = -ENOMEM; >+ goto out; >+ } >+ >+ sense_ptr = >+ (u32 *) ((unsigned long)cmd->frame + ioc->sense_off); >+ *sense_ptr = sense_handle; >+ } >+ >+ /* >+ * Set the sync_cmd flag so that the ISR knows not to complete this >+ * cmd to the SCSI mid-layer >+ */ >+ cmd->sync_cmd = 1; >+ megasas_issue_blocked_cmd(instance, cmd); >+ cmd->sync_cmd = 0; >+ >+ /* >+ * copy out the kernel buffers to user buffers >+ */ >+ for (i = 0; i < ioc->sge_count; i++) { >+ if (copy_to_user(ioc->sgl[i].iov_base, kbuff_arr[i], >+ ioc->sgl[i].iov_len)) { >+ error = -EFAULT; >+ goto out; >+ } >+ } >+ >+ /* >+ * copy out the sense >+ */ >+ if (ioc->sense_len) { >+ /* >+ * sense_ptr points to the location that has the user >+ * sense buffer address >+ */ >+ sense_ptr = (u32 *) ((unsigned long)ioc->frame.raw + >+ ioc->sense_off); >+ >+ if (copy_to_user((void __user *)((unsigned long)(*sense_ptr)), >+ sense, ioc->sense_len)) { >+ error = -EFAULT; >+ goto out; >+ } >+ } >+ >+ /* >+ * copy the status codes returned by the fw >+ */ >+ if (copy_to_user(&user_ioc->frame.hdr.cmd_status, >+ &cmd->frame->hdr.cmd_status, sizeof(u8))) { >+ printk(KERN_DEBUG "megasas: Error copying out cmd_status\n"); >+ error = -EFAULT; >+ } >+ >+ out: >+ if (sense) { >+ pci_free_consistent(instance->pdev, ioc->sense_len, >+ sense, sense_handle); >+ } >+ >+ for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) { >+ pci_free_consistent(instance->pdev, >+ kern_sge32[i].length, >+ kbuff_arr[i], kern_sge32[i].phys_addr); >+ } >+ >+ megasas_return_cmd(instance, cmd); >+ return error; >+} >+ >+static struct megasas_instance *megasas_lookup_instance(u16 host_no) >+{ >+ int i; >+ >+ for (i = 0; i < megasas_mgmt_info.max_index; i++) { >+ >+ if ((megasas_mgmt_info.instance[i]) && >+ (megasas_mgmt_info.instance[i]->host->host_no == host_no)) >+ return megasas_mgmt_info.instance[i]; >+ } >+ >+ return NULL; >+} >+ >+static int megasas_mgmt_ioctl_fw(struct file *file, unsigned long arg) >+{ >+ struct megasas_iocpacket __user *user_ioc = >+ (struct megasas_iocpacket __user *)arg; >+ struct megasas_iocpacket *ioc; >+ struct megasas_instance *instance; >+ int error; >+ >+ ioc = kmalloc(sizeof(*ioc), GFP_KERNEL); >+ if (!ioc) >+ return -ENOMEM; >+ >+ if (copy_from_user(ioc, user_ioc, sizeof(*ioc))) { >+ error = -EFAULT; >+ goto out_kfree_ioc; >+ } >+ >+ instance = megasas_lookup_instance(ioc->host_no); >+ if (!instance) { >+ error = -ENODEV; >+ goto out_kfree_ioc; >+ } >+ >+ /* >+ * We will allow only MEGASAS_INT_CMDS number of parallel ioctl cmds >+ */ >+ if (down_interruptible(&instance->ioctl_sem)) { >+ error = -ERESTARTSYS; >+ goto out_kfree_ioc; >+ } >+ error = megasas_mgmt_fw_ioctl(instance, user_ioc, ioc); >+ up(&instance->ioctl_sem); >+ >+ out_kfree_ioc: >+ kfree(ioc); >+ return error; >+} >+ >+static int megasas_mgmt_ioctl_aen(struct file *file, unsigned long arg) >+{ >+ struct megasas_instance *instance; >+ struct megasas_aen aen; >+ int error; >+ >+ if (file->private_data != file) { >+ printk(KERN_DEBUG "megasas: fasync_helper was not " >+ "called first\n"); >+ return -EINVAL; >+ } >+ >+ if (copy_from_user(&aen, (void __user *)arg, sizeof(aen))) >+ return -EFAULT; >+ >+ instance = megasas_lookup_instance(aen.host_no); >+ >+ if (!instance) >+ return -ENODEV; >+ >+ down(&instance->aen_mutex); >+ error = megasas_register_aen(instance, aen.seq_num, >+ aen.class_locale_word); >+ up(&instance->aen_mutex); >+ return error; >+} >+ >+/** >+ * megasas_mgmt_ioctl - char node ioctl entry point >+ */ >+static long >+megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) >+{ >+ switch (cmd) { >+ case MEGASAS_IOC_FIRMWARE: >+ return megasas_mgmt_ioctl_fw(file, arg); >+ case MEGASAS_IOC_GET_AEN: >+ return megasas_mgmt_ioctl_aen(file, arg); >+ } >+ >+ return -ENOTTY; >+} >+ >+#ifdef CONFIG_COMPAT >+static int megasas_mgmt_compat_ioctl_fw(struct file *file, unsigned long arg) >+{ >+ struct compat_megasas_iocpacket __user *cioc = >+ (struct compat_megasas_iocpacket __user *)arg; >+ struct megasas_iocpacket __user *ioc = >+ compat_alloc_user_space(sizeof(struct megasas_iocpacket)); >+ int i; >+ int error = 0; >+ >+ clear_user(ioc, sizeof(*ioc)); >+ >+ if (copy_in_user(&ioc->host_no, &cioc->host_no, sizeof(u16)) || >+ copy_in_user(&ioc->sgl_off, &cioc->sgl_off, sizeof(u32)) || >+ copy_in_user(&ioc->sense_off, &cioc->sense_off, sizeof(u32)) || >+ copy_in_user(&ioc->sense_len, &cioc->sense_len, sizeof(u32)) || >+ copy_in_user(ioc->frame.raw, cioc->frame.raw, 128) || >+ copy_in_user(&ioc->sge_count, &cioc->sge_count, sizeof(u32))) >+ return -EFAULT; >+ >+ for (i = 0; i < MAX_IOCTL_SGE; i++) { >+ compat_uptr_t ptr; >+ >+ if (get_user(ptr, &cioc->sgl[i].iov_base) || >+ put_user(compat_ptr(ptr), &ioc->sgl[i].iov_base) || >+ copy_in_user(&ioc->sgl[i].iov_len, >+ &cioc->sgl[i].iov_len, sizeof(compat_size_t))) >+ return -EFAULT; >+ } >+ >+ error = megasas_mgmt_ioctl_fw(file, (unsigned long)ioc); >+ >+ if (copy_in_user(&cioc->frame.hdr.cmd_status, >+ &ioc->frame.hdr.cmd_status, sizeof(u8))) { >+ printk(KERN_DEBUG "megasas: error copy_in_user cmd_status\n"); >+ return -EFAULT; >+ } >+ return error; >+} >+ >+static long >+megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, >+ unsigned long arg) >+{ >+ switch (cmd) { >+ case MEGASAS_IOC_FIRMWARE32: >+ return megasas_mgmt_compat_ioctl_fw(file, arg); >+ case MEGASAS_IOC_GET_AEN: >+ return megasas_mgmt_ioctl_aen(file, arg); >+ } >+ >+ return -ENOTTY; >+} >+#endif >+ >+/* >+ * File operations structure for management interface >+ */ >+static struct file_operations megasas_mgmt_fops = { >+ .owner = THIS_MODULE, >+ .open = megasas_mgmt_open, >+ .release = megasas_mgmt_release, >+ .fasync = megasas_mgmt_fasync, >+ .unlocked_ioctl = megasas_mgmt_ioctl, >+#ifdef CONFIG_COMPAT >+ .compat_ioctl = megasas_mgmt_compat_ioctl, >+#endif >+}; >+ >+/* >+ * PCI hotplug support registration structure >+ */ >+static struct pci_driver megasas_pci_driver = { >+ >+ .name = "megaraid_sas", >+ .id_table = megasas_pci_table, >+ .probe = megasas_probe_one, >+ .remove = __devexit_p(megasas_detach_one), >+ .shutdown = megasas_shutdown, >+}; >+ >+/* >+ * Sysfs driver attributes >+ */ >+static ssize_t megasas_sysfs_show_version(struct device_driver *dd, char *buf) >+{ >+ return snprintf(buf, strlen(MEGASAS_VERSION) + 2, "%s\n", >+ MEGASAS_VERSION); >+} >+ >+static DRIVER_ATTR(version, S_IRUGO, megasas_sysfs_show_version, NULL); >+ >+static ssize_t >+megasas_sysfs_show_release_date(struct device_driver *dd, char *buf) >+{ >+ return snprintf(buf, strlen(MEGASAS_RELDATE) + 2, "%s\n", >+ MEGASAS_RELDATE); >+} >+ >+static DRIVER_ATTR(release_date, S_IRUGO, megasas_sysfs_show_release_date, >+ NULL); >+ >+/** >+ * megasas_init - Driver load entry point >+ */ >+static int __init megasas_init(void) >+{ >+ int rval; >+ >+ /* >+ * Announce driver version and other information >+ */ >+ printk(KERN_INFO "megasas: %s %s\n", MEGASAS_VERSION, >+ MEGASAS_EXT_VERSION); >+ >+ memset(&megasas_mgmt_info, 0, sizeof(megasas_mgmt_info)); >+ >+ /* >+ * Register character device node >+ */ >+ rval = register_chrdev(0, "megaraid_sas_ioctl", &megasas_mgmt_fops); >+ >+ if (rval < 0) { >+ printk(KERN_DEBUG "megasas: failed to open device node\n"); >+ return rval; >+ } >+ >+ megasas_mgmt_majorno = rval; >+ >+ /* >+ * Register ourselves as PCI hotplug module >+ */ >+ rval = pci_module_init(&megasas_pci_driver); >+ >+ if (rval) { >+ printk(KERN_DEBUG "megasas: PCI hotplug regisration failed \n"); >+ unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); >+ } >+ >+ driver_create_file(&megasas_pci_driver.driver, &driver_attr_version); >+ driver_create_file(&megasas_pci_driver.driver, >+ &driver_attr_release_date); >+ >+ return rval; >+} >+ >+/** >+ * megasas_exit - Driver unload entry point >+ */ >+static void __exit megasas_exit(void) >+{ >+ driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); >+ driver_remove_file(&megasas_pci_driver.driver, >+ &driver_attr_release_date); >+ >+ pci_unregister_driver(&megasas_pci_driver); >+ unregister_chrdev(megasas_mgmt_majorno, "megaraid_sas_ioctl"); >+} >+ >+module_init(megasas_init); >+module_exit(megasas_exit); >--- linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.h.megaraid_sas 2007-02-28 10:15:54.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.h 2007-02-28 10:17:39.000000000 +0200 >@@ -0,0 +1,1177 @@ >+/* >+ * >+ * Linux MegaRAID driver for SAS based RAID controllers >+ * >+ * Copyright (c) 2003-2005 LSI Logic 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. >+ * >+ * FILE : megaraid_sas.h >+ */ >+ >+#ifndef LSI_MEGARAID_SAS_H >+#define LSI_MEGARAID_SAS_H >+ >+/** >+ * MegaRAID SAS Driver meta data >+ */ >+#define MEGASAS_VERSION "00.00.03.01" >+#define MEGASAS_RELDATE "May 14, 2006" >+#define MEGASAS_EXT_VERSION "Sun May 14 22:49:52 PDT 2006" >+ >+/* >+ * Device IDs >+ */ >+#define PCI_DEVICE_ID_LSI_SAS1078R 0x0060 >+#define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 >+ >+/* >+ * ===================================== >+ * MegaRAID SAS MFI firmware definitions >+ * ===================================== >+ */ >+ >+/* >+ * MFI stands for MegaRAID SAS FW Interface. This is just a moniker for >+ * protocol between the software and firmware. Commands are issued using >+ * "message frames" >+ */ >+ >+/** >+ * FW posts its state in upper 4 bits of outbound_msg_0 register >+ */ >+#define MFI_STATE_MASK 0xF0000000 >+#define MFI_STATE_UNDEFINED 0x00000000 >+#define MFI_STATE_BB_INIT 0x10000000 >+#define MFI_STATE_FW_INIT 0x40000000 >+#define MFI_STATE_WAIT_HANDSHAKE 0x60000000 >+#define MFI_STATE_FW_INIT_2 0x70000000 >+#define MFI_STATE_DEVICE_SCAN 0x80000000 >+#define MFI_STATE_FLUSH_CACHE 0xA0000000 >+#define MFI_STATE_READY 0xB0000000 >+#define MFI_STATE_OPERATIONAL 0xC0000000 >+#define MFI_STATE_FAULT 0xF0000000 >+ >+#define MEGAMFI_FRAME_SIZE 64 >+ >+/** >+ * During FW init, clear pending cmds & reset state using inbound_msg_0 >+ * >+ * ABORT : Abort all pending cmds >+ * READY : Move from OPERATIONAL to READY state; discard queue info >+ * MFIMODE : Discard (possible) low MFA posted in 64-bit mode (??) >+ * CLR_HANDSHAKE: FW is waiting for HANDSHAKE from BIOS or Driver >+ */ >+#define MFI_INIT_ABORT 0x00000000 >+#define MFI_INIT_READY 0x00000002 >+#define MFI_INIT_MFIMODE 0x00000004 >+#define MFI_INIT_CLEAR_HANDSHAKE 0x00000008 >+#define MFI_RESET_FLAGS MFI_INIT_READY|MFI_INIT_MFIMODE >+ >+/** >+ * MFI frame flags >+ */ >+#define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000 >+#define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001 >+#define MFI_FRAME_SGL32 0x0000 >+#define MFI_FRAME_SGL64 0x0002 >+#define MFI_FRAME_SENSE32 0x0000 >+#define MFI_FRAME_SENSE64 0x0004 >+#define MFI_FRAME_DIR_NONE 0x0000 >+#define MFI_FRAME_DIR_WRITE 0x0008 >+#define MFI_FRAME_DIR_READ 0x0010 >+#define MFI_FRAME_DIR_BOTH 0x0018 >+ >+/** >+ * Definition for cmd_status >+ */ >+#define MFI_CMD_STATUS_POLL_MODE 0xFF >+ >+/** >+ * MFI command opcodes >+ */ >+#define MFI_CMD_INIT 0x00 >+#define MFI_CMD_LD_READ 0x01 >+#define MFI_CMD_LD_WRITE 0x02 >+#define MFI_CMD_LD_SCSI_IO 0x03 >+#define MFI_CMD_PD_SCSI_IO 0x04 >+#define MFI_CMD_DCMD 0x05 >+#define MFI_CMD_ABORT 0x06 >+#define MFI_CMD_SMP 0x07 >+#define MFI_CMD_STP 0x08 >+ >+#define MR_DCMD_CTRL_GET_INFO 0x01010000 >+ >+#define MR_DCMD_CTRL_CACHE_FLUSH 0x01101000 >+#define MR_FLUSH_CTRL_CACHE 0x01 >+#define MR_FLUSH_DISK_CACHE 0x02 >+ >+#define MR_DCMD_CTRL_SHUTDOWN 0x01050000 >+#define MR_ENABLE_DRIVE_SPINDOWN 0x01 >+ >+#define MR_DCMD_CTRL_EVENT_GET_INFO 0x01040100 >+#define MR_DCMD_CTRL_EVENT_GET 0x01040300 >+#define MR_DCMD_CTRL_EVENT_WAIT 0x01040500 >+#define MR_DCMD_LD_GET_PROPERTIES 0x03030000 >+ >+#define MR_DCMD_CLUSTER 0x08000000 >+#define MR_DCMD_CLUSTER_RESET_ALL 0x08010100 >+#define MR_DCMD_CLUSTER_RESET_LD 0x08010200 >+ >+/** >+ * MFI command completion codes >+ */ >+enum MFI_STAT { >+ MFI_STAT_OK = 0x00, >+ MFI_STAT_INVALID_CMD = 0x01, >+ MFI_STAT_INVALID_DCMD = 0x02, >+ MFI_STAT_INVALID_PARAMETER = 0x03, >+ MFI_STAT_INVALID_SEQUENCE_NUMBER = 0x04, >+ MFI_STAT_ABORT_NOT_POSSIBLE = 0x05, >+ MFI_STAT_APP_HOST_CODE_NOT_FOUND = 0x06, >+ MFI_STAT_APP_IN_USE = 0x07, >+ MFI_STAT_APP_NOT_INITIALIZED = 0x08, >+ MFI_STAT_ARRAY_INDEX_INVALID = 0x09, >+ MFI_STAT_ARRAY_ROW_NOT_EMPTY = 0x0a, >+ MFI_STAT_CONFIG_RESOURCE_CONFLICT = 0x0b, >+ MFI_STAT_DEVICE_NOT_FOUND = 0x0c, >+ MFI_STAT_DRIVE_TOO_SMALL = 0x0d, >+ MFI_STAT_FLASH_ALLOC_FAIL = 0x0e, >+ MFI_STAT_FLASH_BUSY = 0x0f, >+ MFI_STAT_FLASH_ERROR = 0x10, >+ MFI_STAT_FLASH_IMAGE_BAD = 0x11, >+ MFI_STAT_FLASH_IMAGE_INCOMPLETE = 0x12, >+ MFI_STAT_FLASH_NOT_OPEN = 0x13, >+ MFI_STAT_FLASH_NOT_STARTED = 0x14, >+ MFI_STAT_FLUSH_FAILED = 0x15, >+ MFI_STAT_HOST_CODE_NOT_FOUNT = 0x16, >+ MFI_STAT_LD_CC_IN_PROGRESS = 0x17, >+ MFI_STAT_LD_INIT_IN_PROGRESS = 0x18, >+ MFI_STAT_LD_LBA_OUT_OF_RANGE = 0x19, >+ MFI_STAT_LD_MAX_CONFIGURED = 0x1a, >+ MFI_STAT_LD_NOT_OPTIMAL = 0x1b, >+ MFI_STAT_LD_RBLD_IN_PROGRESS = 0x1c, >+ MFI_STAT_LD_RECON_IN_PROGRESS = 0x1d, >+ MFI_STAT_LD_WRONG_RAID_LEVEL = 0x1e, >+ MFI_STAT_MAX_SPARES_EXCEEDED = 0x1f, >+ MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20, >+ MFI_STAT_MFC_HW_ERROR = 0x21, >+ MFI_STAT_NO_HW_PRESENT = 0x22, >+ MFI_STAT_NOT_FOUND = 0x23, >+ MFI_STAT_NOT_IN_ENCL = 0x24, >+ MFI_STAT_PD_CLEAR_IN_PROGRESS = 0x25, >+ MFI_STAT_PD_TYPE_WRONG = 0x26, >+ MFI_STAT_PR_DISABLED = 0x27, >+ MFI_STAT_ROW_INDEX_INVALID = 0x28, >+ MFI_STAT_SAS_CONFIG_INVALID_ACTION = 0x29, >+ MFI_STAT_SAS_CONFIG_INVALID_DATA = 0x2a, >+ MFI_STAT_SAS_CONFIG_INVALID_PAGE = 0x2b, >+ MFI_STAT_SAS_CONFIG_INVALID_TYPE = 0x2c, >+ MFI_STAT_SCSI_DONE_WITH_ERROR = 0x2d, >+ MFI_STAT_SCSI_IO_FAILED = 0x2e, >+ MFI_STAT_SCSI_RESERVATION_CONFLICT = 0x2f, >+ MFI_STAT_SHUTDOWN_FAILED = 0x30, >+ MFI_STAT_TIME_NOT_SET = 0x31, >+ MFI_STAT_WRONG_STATE = 0x32, >+ MFI_STAT_LD_OFFLINE = 0x33, >+ MFI_STAT_PEER_NOTIFICATION_REJECTED = 0x34, >+ MFI_STAT_PEER_NOTIFICATION_FAILED = 0x35, >+ MFI_STAT_RESERVATION_IN_PROGRESS = 0x36, >+ MFI_STAT_I2C_ERRORS_DETECTED = 0x37, >+ MFI_STAT_PCI_ERRORS_DETECTED = 0x38, >+ >+ MFI_STAT_INVALID_STATUS = 0xFF >+}; >+ >+/* >+ * Number of mailbox bytes in DCMD message frame >+ */ >+#define MFI_MBOX_SIZE 12 >+ >+enum MR_EVT_CLASS { >+ >+ MR_EVT_CLASS_DEBUG = -2, >+ MR_EVT_CLASS_PROGRESS = -1, >+ MR_EVT_CLASS_INFO = 0, >+ MR_EVT_CLASS_WARNING = 1, >+ MR_EVT_CLASS_CRITICAL = 2, >+ MR_EVT_CLASS_FATAL = 3, >+ MR_EVT_CLASS_DEAD = 4, >+ >+}; >+ >+enum MR_EVT_LOCALE { >+ >+ MR_EVT_LOCALE_LD = 0x0001, >+ MR_EVT_LOCALE_PD = 0x0002, >+ MR_EVT_LOCALE_ENCL = 0x0004, >+ MR_EVT_LOCALE_BBU = 0x0008, >+ MR_EVT_LOCALE_SAS = 0x0010, >+ MR_EVT_LOCALE_CTRL = 0x0020, >+ MR_EVT_LOCALE_CONFIG = 0x0040, >+ MR_EVT_LOCALE_CLUSTER = 0x0080, >+ MR_EVT_LOCALE_ALL = 0xffff, >+ >+}; >+ >+enum MR_EVT_ARGS { >+ >+ MR_EVT_ARGS_NONE, >+ MR_EVT_ARGS_CDB_SENSE, >+ MR_EVT_ARGS_LD, >+ MR_EVT_ARGS_LD_COUNT, >+ MR_EVT_ARGS_LD_LBA, >+ MR_EVT_ARGS_LD_OWNER, >+ MR_EVT_ARGS_LD_LBA_PD_LBA, >+ MR_EVT_ARGS_LD_PROG, >+ MR_EVT_ARGS_LD_STATE, >+ MR_EVT_ARGS_LD_STRIP, >+ MR_EVT_ARGS_PD, >+ MR_EVT_ARGS_PD_ERR, >+ MR_EVT_ARGS_PD_LBA, >+ MR_EVT_ARGS_PD_LBA_LD, >+ MR_EVT_ARGS_PD_PROG, >+ MR_EVT_ARGS_PD_STATE, >+ MR_EVT_ARGS_PCI, >+ MR_EVT_ARGS_RATE, >+ MR_EVT_ARGS_STR, >+ MR_EVT_ARGS_TIME, >+ MR_EVT_ARGS_ECC, >+ >+}; >+ >+/* >+ * SAS controller properties >+ */ >+struct megasas_ctrl_prop { >+ >+ u16 seq_num; >+ u16 pred_fail_poll_interval; >+ u16 intr_throttle_count; >+ u16 intr_throttle_timeouts; >+ u8 rebuild_rate; >+ u8 patrol_read_rate; >+ u8 bgi_rate; >+ u8 cc_rate; >+ u8 recon_rate; >+ u8 cache_flush_interval; >+ u8 spinup_drv_count; >+ u8 spinup_delay; >+ u8 cluster_enable; >+ u8 coercion_mode; >+ u8 alarm_enable; >+ u8 disable_auto_rebuild; >+ u8 disable_battery_warn; >+ u8 ecc_bucket_size; >+ u16 ecc_bucket_leak_rate; >+ u8 restore_hotspare_on_insertion; >+ u8 expose_encl_devices; >+ u8 reserved[38]; >+ >+} __attribute__ ((packed)); >+ >+/* >+ * SAS controller information >+ */ >+struct megasas_ctrl_info { >+ >+ /* >+ * PCI device information >+ */ >+ struct { >+ >+ u16 vendor_id; >+ u16 device_id; >+ u16 sub_vendor_id; >+ u16 sub_device_id; >+ u8 reserved[24]; >+ >+ } __attribute__ ((packed)) pci; >+ >+ /* >+ * Host interface information >+ */ >+ struct { >+ >+ u8 PCIX:1; >+ u8 PCIE:1; >+ u8 iSCSI:1; >+ u8 SAS_3G:1; >+ u8 reserved_0:4; >+ u8 reserved_1[6]; >+ u8 port_count; >+ u64 port_addr[8]; >+ >+ } __attribute__ ((packed)) host_interface; >+ >+ /* >+ * Device (backend) interface information >+ */ >+ struct { >+ >+ u8 SPI:1; >+ u8 SAS_3G:1; >+ u8 SATA_1_5G:1; >+ u8 SATA_3G:1; >+ u8 reserved_0:4; >+ u8 reserved_1[6]; >+ u8 port_count; >+ u64 port_addr[8]; >+ >+ } __attribute__ ((packed)) device_interface; >+ >+ /* >+ * List of components residing in flash. All str are null terminated >+ */ >+ u32 image_check_word; >+ u32 image_component_count; >+ >+ struct { >+ >+ char name[8]; >+ char version[32]; >+ char build_date[16]; >+ char built_time[16]; >+ >+ } __attribute__ ((packed)) image_component[8]; >+ >+ /* >+ * List of flash components that have been flashed on the card, but >+ * are not in use, pending reset of the adapter. This list will be >+ * empty if a flash operation has not occurred. All stings are null >+ * terminated >+ */ >+ u32 pending_image_component_count; >+ >+ struct { >+ >+ char name[8]; >+ char version[32]; >+ char build_date[16]; >+ char build_time[16]; >+ >+ } __attribute__ ((packed)) pending_image_component[8]; >+ >+ u8 max_arms; >+ u8 max_spans; >+ u8 max_arrays; >+ u8 max_lds; >+ >+ char product_name[80]; >+ char serial_no[32]; >+ >+ /* >+ * Other physical/controller/operation information. Indicates the >+ * presence of the hardware >+ */ >+ struct { >+ >+ u32 bbu:1; >+ u32 alarm:1; >+ u32 nvram:1; >+ u32 uart:1; >+ u32 reserved:28; >+ >+ } __attribute__ ((packed)) hw_present; >+ >+ u32 current_fw_time; >+ >+ /* >+ * Maximum data transfer sizes >+ */ >+ u16 max_concurrent_cmds; >+ u16 max_sge_count; >+ u32 max_request_size; >+ >+ /* >+ * Logical and physical device counts >+ */ >+ u16 ld_present_count; >+ u16 ld_degraded_count; >+ u16 ld_offline_count; >+ >+ u16 pd_present_count; >+ u16 pd_disk_present_count; >+ u16 pd_disk_pred_failure_count; >+ u16 pd_disk_failed_count; >+ >+ /* >+ * Memory size information >+ */ >+ u16 nvram_size; >+ u16 memory_size; >+ u16 flash_size; >+ >+ /* >+ * Error counters >+ */ >+ u16 mem_correctable_error_count; >+ u16 mem_uncorrectable_error_count; >+ >+ /* >+ * Cluster information >+ */ >+ u8 cluster_permitted; >+ u8 cluster_active; >+ >+ /* >+ * Additional max data transfer sizes >+ */ >+ u16 max_strips_per_io; >+ >+ /* >+ * Controller capabilities structures >+ */ >+ struct { >+ >+ u32 raid_level_0:1; >+ u32 raid_level_1:1; >+ u32 raid_level_5:1; >+ u32 raid_level_1E:1; >+ u32 raid_level_6:1; >+ u32 reserved:27; >+ >+ } __attribute__ ((packed)) raid_levels; >+ >+ struct { >+ >+ u32 rbld_rate:1; >+ u32 cc_rate:1; >+ u32 bgi_rate:1; >+ u32 recon_rate:1; >+ u32 patrol_rate:1; >+ u32 alarm_control:1; >+ u32 cluster_supported:1; >+ u32 bbu:1; >+ u32 spanning_allowed:1; >+ u32 dedicated_hotspares:1; >+ u32 revertible_hotspares:1; >+ u32 foreign_config_import:1; >+ u32 self_diagnostic:1; >+ u32 mixed_redundancy_arr:1; >+ u32 global_hot_spares:1; >+ u32 reserved:17; >+ >+ } __attribute__ ((packed)) adapter_operations; >+ >+ struct { >+ >+ u32 read_policy:1; >+ u32 write_policy:1; >+ u32 io_policy:1; >+ u32 access_policy:1; >+ u32 disk_cache_policy:1; >+ u32 reserved:27; >+ >+ } __attribute__ ((packed)) ld_operations; >+ >+ struct { >+ >+ u8 min; >+ u8 max; >+ u8 reserved[2]; >+ >+ } __attribute__ ((packed)) stripe_sz_ops; >+ >+ struct { >+ >+ u32 force_online:1; >+ u32 force_offline:1; >+ u32 force_rebuild:1; >+ u32 reserved:29; >+ >+ } __attribute__ ((packed)) pd_operations; >+ >+ struct { >+ >+ u32 ctrl_supports_sas:1; >+ u32 ctrl_supports_sata:1; >+ u32 allow_mix_in_encl:1; >+ u32 allow_mix_in_ld:1; >+ u32 allow_sata_in_cluster:1; >+ u32 reserved:27; >+ >+ } __attribute__ ((packed)) pd_mix_support; >+ >+ /* >+ * Define ECC single-bit-error bucket information >+ */ >+ u8 ecc_bucket_count; >+ u8 reserved_2[11]; >+ >+ /* >+ * Include the controller properties (changeable items) >+ */ >+ struct megasas_ctrl_prop properties; >+ >+ /* >+ * Define FW pkg version (set in envt v'bles on OEM basis) >+ */ >+ char package_version[0x60]; >+ >+ u8 pad[0x800 - 0x6a0]; >+ >+} __attribute__ ((packed)); >+ >+/* >+ * =============================== >+ * MegaRAID SAS driver definitions >+ * =============================== >+ */ >+#define MEGASAS_MAX_PD_CHANNELS 2 >+#define MEGASAS_MAX_LD_CHANNELS 2 >+#define MEGASAS_MAX_CHANNELS (MEGASAS_MAX_PD_CHANNELS + \ >+ MEGASAS_MAX_LD_CHANNELS) >+#define MEGASAS_MAX_DEV_PER_CHANNEL 128 >+#define MEGASAS_DEFAULT_INIT_ID -1 >+#define MEGASAS_MAX_LUN 8 >+#define MEGASAS_MAX_LD 64 >+ >+/* >+ * When SCSI mid-layer calls driver's reset routine, driver waits for >+ * MEGASAS_RESET_WAIT_TIME seconds for all outstanding IO to complete. Note >+ * that the driver cannot _actually_ abort or reset pending commands. While >+ * it is waiting for the commands to complete, it prints a diagnostic message >+ * every MEGASAS_RESET_NOTICE_INTERVAL seconds >+ */ >+#define MEGASAS_RESET_WAIT_TIME 180 >+#define MEGASAS_RESET_NOTICE_INTERVAL 5 >+ >+#define MEGASAS_IOCTL_CMD 0 >+ >+/* >+ * FW reports the maximum of number of commands that it can accept (maximum >+ * commands that can be outstanding) at any time. The driver must report a >+ * lower number to the mid layer because it can issue a few internal commands >+ * itself (E.g, AEN, abort cmd, IOCTLs etc). The number of commands it needs >+ * is shown below >+ */ >+#define MEGASAS_INT_CMDS 32 >+ >+/* >+ * FW can accept both 32 and 64 bit SGLs. We want to allocate 32/64 bit >+ * SGLs based on the size of dma_addr_t >+ */ >+#define IS_DMA64 (sizeof(dma_addr_t) == 8) >+ >+#define MFI_OB_INTR_STATUS_MASK 0x00000002 >+#define MFI_POLL_TIMEOUT_SECS 10 >+ >+#define MFI_REPLY_1078_MESSAGE_INTERRUPT 0x80000000 >+ >+/* >+* register set for both 1068 and 1078 controllers >+* structure extended for 1078 registers >+*/ >+ >+struct megasas_register_set { >+ u32 reserved_0[4]; /*0000h*/ >+ >+ u32 inbound_msg_0; /*0010h*/ >+ u32 inbound_msg_1; /*0014h*/ >+ u32 outbound_msg_0; /*0018h*/ >+ u32 outbound_msg_1; /*001Ch*/ >+ >+ u32 inbound_doorbell; /*0020h*/ >+ u32 inbound_intr_status; /*0024h*/ >+ u32 inbound_intr_mask; /*0028h*/ >+ >+ u32 outbound_doorbell; /*002Ch*/ >+ u32 outbound_intr_status; /*0030h*/ >+ u32 outbound_intr_mask; /*0034h*/ >+ >+ u32 reserved_1[2]; /*0038h*/ >+ >+ u32 inbound_queue_port; /*0040h*/ >+ u32 outbound_queue_port; /*0044h*/ >+ >+ u32 reserved_2[22]; /*0048h*/ >+ >+ u32 outbound_doorbell_clear; /*00A0h*/ >+ >+ u32 reserved_3[3]; /*00A4h*/ >+ >+ u32 outbound_scratch_pad ; /*00B0h*/ >+ >+ u32 reserved_4[3]; /*00B4h*/ >+ >+ u32 inbound_low_queue_port ; /*00C0h*/ >+ >+ u32 inbound_high_queue_port ; /*00C4h*/ >+ >+ u32 reserved_5; /*00C8h*/ >+ u32 index_registers[820]; /*00CCh*/ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_sge32 { >+ >+ u32 phys_addr; >+ u32 length; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_sge64 { >+ >+ u64 phys_addr; >+ u32 length; >+ >+} __attribute__ ((packed)); >+ >+union megasas_sgl { >+ >+ struct megasas_sge32 sge32[1]; >+ struct megasas_sge64 sge64[1]; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_header { >+ >+ u8 cmd; /*00h */ >+ u8 sense_len; /*01h */ >+ u8 cmd_status; /*02h */ >+ u8 scsi_status; /*03h */ >+ >+ u8 target_id; /*04h */ >+ u8 lun; /*05h */ >+ u8 cdb_len; /*06h */ >+ u8 sge_count; /*07h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 timeout; /*12h */ >+ u32 data_xferlen; /*14h */ >+ >+} __attribute__ ((packed)); >+ >+union megasas_sgl_frame { >+ >+ struct megasas_sge32 sge32[8]; >+ struct megasas_sge64 sge64[5]; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_init_frame { >+ >+ u8 cmd; /*00h */ >+ u8 reserved_0; /*01h */ >+ u8 cmd_status; /*02h */ >+ >+ u8 reserved_1; /*03h */ >+ u32 reserved_2; /*04h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 reserved_3; /*12h */ >+ u32 data_xfer_len; /*14h */ >+ >+ u32 queue_info_new_phys_addr_lo; /*18h */ >+ u32 queue_info_new_phys_addr_hi; /*1Ch */ >+ u32 queue_info_old_phys_addr_lo; /*20h */ >+ u32 queue_info_old_phys_addr_hi; /*24h */ >+ >+ u32 reserved_4[6]; /*28h */ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_init_queue_info { >+ >+ u32 init_flags; /*00h */ >+ u32 reply_queue_entries; /*04h */ >+ >+ u32 reply_queue_start_phys_addr_lo; /*08h */ >+ u32 reply_queue_start_phys_addr_hi; /*0Ch */ >+ u32 producer_index_phys_addr_lo; /*10h */ >+ u32 producer_index_phys_addr_hi; /*14h */ >+ u32 consumer_index_phys_addr_lo; /*18h */ >+ u32 consumer_index_phys_addr_hi; /*1Ch */ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_io_frame { >+ >+ u8 cmd; /*00h */ >+ u8 sense_len; /*01h */ >+ u8 cmd_status; /*02h */ >+ u8 scsi_status; /*03h */ >+ >+ u8 target_id; /*04h */ >+ u8 access_byte; /*05h */ >+ u8 reserved_0; /*06h */ >+ u8 sge_count; /*07h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 timeout; /*12h */ >+ u32 lba_count; /*14h */ >+ >+ u32 sense_buf_phys_addr_lo; /*18h */ >+ u32 sense_buf_phys_addr_hi; /*1Ch */ >+ >+ u32 start_lba_lo; /*20h */ >+ u32 start_lba_hi; /*24h */ >+ >+ union megasas_sgl sgl; /*28h */ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_pthru_frame { >+ >+ u8 cmd; /*00h */ >+ u8 sense_len; /*01h */ >+ u8 cmd_status; /*02h */ >+ u8 scsi_status; /*03h */ >+ >+ u8 target_id; /*04h */ >+ u8 lun; /*05h */ >+ u8 cdb_len; /*06h */ >+ u8 sge_count; /*07h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 timeout; /*12h */ >+ u32 data_xfer_len; /*14h */ >+ >+ u32 sense_buf_phys_addr_lo; /*18h */ >+ u32 sense_buf_phys_addr_hi; /*1Ch */ >+ >+ u8 cdb[16]; /*20h */ >+ union megasas_sgl sgl; /*30h */ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_dcmd_frame { >+ >+ u8 cmd; /*00h */ >+ u8 reserved_0; /*01h */ >+ u8 cmd_status; /*02h */ >+ u8 reserved_1[4]; /*03h */ >+ u8 sge_count; /*07h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 timeout; /*12h */ >+ >+ u32 data_xfer_len; /*14h */ >+ u32 opcode; /*18h */ >+ >+ union { /*1Ch */ >+ u8 b[12]; >+ u16 s[6]; >+ u32 w[3]; >+ } mbox; >+ >+ union megasas_sgl sgl; /*28h */ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_abort_frame { >+ >+ u8 cmd; /*00h */ >+ u8 reserved_0; /*01h */ >+ u8 cmd_status; /*02h */ >+ >+ u8 reserved_1; /*03h */ >+ u32 reserved_2; /*04h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 reserved_3; /*12h */ >+ u32 reserved_4; /*14h */ >+ >+ u32 abort_context; /*18h */ >+ u32 pad_1; /*1Ch */ >+ >+ u32 abort_mfi_phys_addr_lo; /*20h */ >+ u32 abort_mfi_phys_addr_hi; /*24h */ >+ >+ u32 reserved_5[6]; /*28h */ >+ >+} __attribute__ ((packed)); >+ >+struct megasas_smp_frame { >+ >+ u8 cmd; /*00h */ >+ u8 reserved_1; /*01h */ >+ u8 cmd_status; /*02h */ >+ u8 connection_status; /*03h */ >+ >+ u8 reserved_2[3]; /*04h */ >+ u8 sge_count; /*07h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 timeout; /*12h */ >+ >+ u32 data_xfer_len; /*14h */ >+ u64 sas_addr; /*18h */ >+ >+ union { >+ struct megasas_sge32 sge32[2]; /* [0]: resp [1]: req */ >+ struct megasas_sge64 sge64[2]; /* [0]: resp [1]: req */ >+ } sgl; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_stp_frame { >+ >+ u8 cmd; /*00h */ >+ u8 reserved_1; /*01h */ >+ u8 cmd_status; /*02h */ >+ u8 reserved_2; /*03h */ >+ >+ u8 target_id; /*04h */ >+ u8 reserved_3[2]; /*05h */ >+ u8 sge_count; /*07h */ >+ >+ u32 context; /*08h */ >+ u32 pad_0; /*0Ch */ >+ >+ u16 flags; /*10h */ >+ u16 timeout; /*12h */ >+ >+ u32 data_xfer_len; /*14h */ >+ >+ u16 fis[10]; /*18h */ >+ u32 stp_flags; >+ >+ union { >+ struct megasas_sge32 sge32[2]; /* [0]: resp [1]: data */ >+ struct megasas_sge64 sge64[2]; /* [0]: resp [1]: data */ >+ } sgl; >+ >+} __attribute__ ((packed)); >+ >+union megasas_frame { >+ >+ struct megasas_header hdr; >+ struct megasas_init_frame init; >+ struct megasas_io_frame io; >+ struct megasas_pthru_frame pthru; >+ struct megasas_dcmd_frame dcmd; >+ struct megasas_abort_frame abort; >+ struct megasas_smp_frame smp; >+ struct megasas_stp_frame stp; >+ >+ u8 raw_bytes[64]; >+}; >+ >+struct megasas_cmd; >+ >+union megasas_evt_class_locale { >+ >+ struct { >+ u16 locale; >+ u8 reserved; >+ s8 class; >+ } __attribute__ ((packed)) members; >+ >+ u32 word; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_evt_log_info { >+ u32 newest_seq_num; >+ u32 oldest_seq_num; >+ u32 clear_seq_num; >+ u32 shutdown_seq_num; >+ u32 boot_seq_num; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_progress { >+ >+ u16 progress; >+ u16 elapsed_seconds; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_evtarg_ld { >+ >+ u16 target_id; >+ u8 ld_index; >+ u8 reserved; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_evtarg_pd { >+ u16 device_id; >+ u8 encl_index; >+ u8 slot_number; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_evt_detail { >+ >+ u32 seq_num; >+ u32 time_stamp; >+ u32 code; >+ union megasas_evt_class_locale cl; >+ u8 arg_type; >+ u8 reserved1[15]; >+ >+ union { >+ struct { >+ struct megasas_evtarg_pd pd; >+ u8 cdb_length; >+ u8 sense_length; >+ u8 reserved[2]; >+ u8 cdb[16]; >+ u8 sense[64]; >+ } __attribute__ ((packed)) cdbSense; >+ >+ struct megasas_evtarg_ld ld; >+ >+ struct { >+ struct megasas_evtarg_ld ld; >+ u64 count; >+ } __attribute__ ((packed)) ld_count; >+ >+ struct { >+ u64 lba; >+ struct megasas_evtarg_ld ld; >+ } __attribute__ ((packed)) ld_lba; >+ >+ struct { >+ struct megasas_evtarg_ld ld; >+ u32 prevOwner; >+ u32 newOwner; >+ } __attribute__ ((packed)) ld_owner; >+ >+ struct { >+ u64 ld_lba; >+ u64 pd_lba; >+ struct megasas_evtarg_ld ld; >+ struct megasas_evtarg_pd pd; >+ } __attribute__ ((packed)) ld_lba_pd_lba; >+ >+ struct { >+ struct megasas_evtarg_ld ld; >+ struct megasas_progress prog; >+ } __attribute__ ((packed)) ld_prog; >+ >+ struct { >+ struct megasas_evtarg_ld ld; >+ u32 prev_state; >+ u32 new_state; >+ } __attribute__ ((packed)) ld_state; >+ >+ struct { >+ u64 strip; >+ struct megasas_evtarg_ld ld; >+ } __attribute__ ((packed)) ld_strip; >+ >+ struct megasas_evtarg_pd pd; >+ >+ struct { >+ struct megasas_evtarg_pd pd; >+ u32 err; >+ } __attribute__ ((packed)) pd_err; >+ >+ struct { >+ u64 lba; >+ struct megasas_evtarg_pd pd; >+ } __attribute__ ((packed)) pd_lba; >+ >+ struct { >+ u64 lba; >+ struct megasas_evtarg_pd pd; >+ struct megasas_evtarg_ld ld; >+ } __attribute__ ((packed)) pd_lba_ld; >+ >+ struct { >+ struct megasas_evtarg_pd pd; >+ struct megasas_progress prog; >+ } __attribute__ ((packed)) pd_prog; >+ >+ struct { >+ struct megasas_evtarg_pd pd; >+ u32 prevState; >+ u32 newState; >+ } __attribute__ ((packed)) pd_state; >+ >+ struct { >+ u16 vendorId; >+ u16 deviceId; >+ u16 subVendorId; >+ u16 subDeviceId; >+ } __attribute__ ((packed)) pci; >+ >+ u32 rate; >+ char str[96]; >+ >+ struct { >+ u32 rtc; >+ u32 elapsedSeconds; >+ } __attribute__ ((packed)) time; >+ >+ struct { >+ u32 ecar; >+ u32 elog; >+ char str[64]; >+ } __attribute__ ((packed)) ecc; >+ >+ u8 b[96]; >+ u16 s[48]; >+ u32 w[24]; >+ u64 d[12]; >+ } args; >+ >+ char description[128]; >+ >+} __attribute__ ((packed)); >+ >+ struct megasas_instance_template { >+ void (*fire_cmd)(dma_addr_t ,u32 ,struct megasas_register_set __iomem *); >+ >+ void (*enable_intr)(struct megasas_register_set __iomem *) ; >+ >+ int (*clear_intr)(struct megasas_register_set __iomem *); >+ >+ u32 (*read_fw_status_reg)(struct megasas_register_set __iomem *); >+ }; >+ >+struct megasas_instance { >+ >+ u32 *producer; >+ dma_addr_t producer_h; >+ u32 *consumer; >+ dma_addr_t consumer_h; >+ >+ u32 *reply_queue; >+ dma_addr_t reply_queue_h; >+ >+ unsigned long base_addr; >+ struct megasas_register_set __iomem *reg_set; >+ >+ s8 init_id; >+ u8 reserved[3]; >+ >+ u16 max_num_sge; >+ u16 max_fw_cmds; >+ u32 max_sectors_per_req; >+ >+ struct megasas_cmd **cmd_list; >+ struct list_head cmd_pool; >+ spinlock_t cmd_pool_lock; >+ struct dma_pool *frame_dma_pool; >+ struct dma_pool *sense_dma_pool; >+ >+ struct megasas_evt_detail *evt_detail; >+ dma_addr_t evt_detail_h; >+ struct megasas_cmd *aen_cmd; >+ struct semaphore aen_mutex; >+ struct semaphore ioctl_sem; >+ >+ struct Scsi_Host *host; >+ >+ wait_queue_head_t int_cmd_wait_q; >+ wait_queue_head_t abort_cmd_wait_q; >+ >+ struct pci_dev *pdev; >+ u32 unique_id; >+ >+ atomic_t fw_outstanding; >+ u32 hw_crit_error; >+ >+ struct megasas_instance_template *instancet; >+}; >+ >+#define MEGASAS_IS_LOGICAL(scp) \ >+ (scp->device->channel < MEGASAS_MAX_PD_CHANNELS) ? 0 : 1 >+ >+#define MEGASAS_DEV_INDEX(inst, scp) \ >+ ((scp->device->channel % 2) * MEGASAS_MAX_DEV_PER_CHANNEL) + \ >+ scp->device->id >+ >+struct megasas_cmd { >+ >+ union megasas_frame *frame; >+ dma_addr_t frame_phys_addr; >+ u8 *sense; >+ dma_addr_t sense_phys_addr; >+ >+ u32 index; >+ u8 sync_cmd; >+ u8 cmd_status; >+ u16 abort_aen; >+ >+ struct list_head list; >+ struct scsi_cmnd *scmd; >+ struct megasas_instance *instance; >+ u32 frame_count; >+}; >+ >+#define MAX_MGMT_ADAPTERS 1024 >+#define MAX_IOCTL_SGE 16 >+ >+struct megasas_iocpacket { >+ >+ u16 host_no; >+ u16 __pad1; >+ u32 sgl_off; >+ u32 sge_count; >+ u32 sense_off; >+ u32 sense_len; >+ union { >+ u8 raw[128]; >+ struct megasas_header hdr; >+ } frame; >+ >+ struct iovec sgl[MAX_IOCTL_SGE]; >+ >+} __attribute__ ((packed)); >+ >+struct megasas_aen { >+ u16 host_no; >+ u16 __pad1; >+ u32 seq_num; >+ u32 class_locale_word; >+} __attribute__ ((packed)); >+ >+#ifdef CONFIG_COMPAT >+struct compat_megasas_iocpacket { >+ u16 host_no; >+ u16 __pad1; >+ u32 sgl_off; >+ u32 sge_count; >+ u32 sense_off; >+ u32 sense_len; >+ union { >+ u8 raw[128]; >+ struct megasas_header hdr; >+ } frame; >+ struct compat_iovec sgl[MAX_IOCTL_SGE]; >+} __attribute__ ((packed)); >+ >+#define MEGASAS_IOC_FIRMWARE32 _IOWR('M', 1, struct compat_megasas_iocpacket) >+#endif >+ >+#define MEGASAS_IOC_FIRMWARE _IOWR('M', 1, struct megasas_iocpacket) >+#define MEGASAS_IOC_GET_AEN _IOW('M', 3, struct megasas_aen) >+ >+struct megasas_mgmt_info { >+ >+ u16 count; >+ struct megasas_instance *instance[MAX_MGMT_ADAPTERS]; >+ int max_index; >+}; >+ >+#endif /*LSI_MEGARAID_SAS_H */ >--- linux-2.6.9/drivers/scsi/megaraid/Kconfig.megaraid.megaraid_sas 2007-02-28 09:48:27.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/megaraid/Kconfig.megaraid 2007-02-28 10:12:08.000000000 +0200 >@@ -76,3 +76,12 @@ > To compile this driver as a module, choose M here: the > module will be called megaraid > endif >+ >+config MEGARAID_SAS >+ tristate "LSI Logic MegaRAID SAS RAID Module" >+ depends on PCI && SCSI >+ help >+ Module for LSI Logic's SAS based RAID controllers. >+ To compile this driver as a module, choose 'm' here. >+ Module will be called megaraid_sas >+ >--- linux-2.6.9/drivers/scsi/megaraid/Makefile.megaraid_sas 2004-10-19 00:55:07.000000000 +0300 >+++ linux-2.6.9/drivers/scsi/megaraid/Makefile 2007-02-28 10:12:58.000000000 +0200 >@@ -1,2 +1,3 @@ > obj-$(CONFIG_MEGARAID_MM) += megaraid_mm.o > obj-$(CONFIG_MEGARAID_MAILBOX) += megaraid_mbox.o >+obj-$(CONFIG_MEGARAID_SAS) += megaraid_sas.o >--- linux-2.6.9/drivers/scsi/Makefile.megaraid_sas 2007-02-28 09:48:27.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/Makefile 2007-02-28 10:09:16.000000000 +0200 >@@ -98,6 +98,7 @@ > obj-$(CONFIG_SCSI_DC390T) += tmscsim.o > obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o > obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ >+obj-$(CONFIG_MEGARAID_SAS) += megaraid/ > obj-$(CONFIG_SCSI_ACARD) += atp870u.o > obj-$(CONFIG_SCSI_SUNESP) += esp.o > obj-$(CONFIG_SCSI_GDTH) += gdth.o >--- linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.h.megaraid_sas_rhel4 2007-02-28 10:17:39.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.h 2007-02-28 10:23:53.000000000 +0200 >@@ -27,6 +27,8 @@ > */ > #define PCI_DEVICE_ID_LSI_SAS1078R 0x0060 > #define PCI_DEVICE_ID_LSI_VERDE_ZCR 0x0413 >+#define PCI_DEVICE_ID_LSI_SAS1064R 0x0411 >+#define PCI_DEVICE_ID_DELL_PERC5 0x0015 > > /* > * ===================================== >@@ -566,7 +568,7 @@ > * register set for both 1068 and 1078 controllers > * structure extended for 1078 registers > */ >- >+ > struct megasas_register_set { > u32 reserved_0[4]; /*0000h*/ > >--- linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.c.megaraid_sas_rhel4 2007-02-28 10:17:39.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.c 2007-02-28 10:23:53.000000000 +0200 >@@ -9,7 +9,7 @@ > * as published by the Free Software Foundation; either version > * 2 of the License, or (at your option) any later version. > * >- * FILE : megaraid_sas.c >+ * FILE : megaraid_sas.c > * Version : v03.01 > * > * Authors: >@@ -26,6 +26,7 @@ > #include <linux/types.h> > #include <linux/pci.h> > #include <linux/list.h> >+#include <linux/version.h> > #include <linux/moduleparam.h> > #include <linux/module.h> > #include <linux/spinlock.h> >@@ -34,8 +35,8 @@ > #include <linux/uio.h> > #include <asm/uaccess.h> > #include <linux/fs.h> >+#include <linux/ioctl32.h> > #include <linux/compat.h> >-#include <linux/mutex.h> > > #include <scsi/scsi.h> > #include <scsi/scsi_cmnd.h> >@@ -85,7 +86,7 @@ > static int megasas_mgmt_majorno; > static struct megasas_mgmt_info megasas_mgmt_info; > static struct fasync_struct *megasas_async_queue; >-static DEFINE_MUTEX(megasas_async_queue_mutex); >+static DECLARE_MUTEX(megasas_async_queue_mutex); > > /** > * megasas_get_cmd - Get a command from the free pool >@@ -93,7 +94,7 @@ > * > * Returns a free command from the pool > */ >-static struct megasas_cmd *megasas_get_cmd(struct megasas_instance >+static inline struct megasas_cmd *megasas_get_cmd(struct megasas_instance > *instance) > { > unsigned long flags; >@@ -131,7 +132,6 @@ > spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); > } > >- > /** > * The following functions are defined for xscale > * (deviceid : 1064R, PERC5) controllers >@@ -304,7 +304,7 @@ > > if(instance->pdev->device == PCI_DEVICE_ID_LSI_SAS1078R) > mask = 0xffffffff; >- >+ > writel(mask, ®s->outbound_intr_mask); > > /* Dummy readl to force pci flush */ >@@ -329,7 +329,7 @@ > frame_hdr->cmd_status = 0xFF; > frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; > >- /* >+ /** > * Issue the frame using inbound queue port > */ > instance->instancet->fire_cmd(cmd->frame_phys_addr ,0,instance->reg_set); >@@ -425,7 +425,7 @@ > * If successful, this function returns the number of SG elements. Otherwise, > * it returnes -1. > */ >-static int >+static inline int > megasas_make_sgl32(struct megasas_instance *instance, struct scsi_cmnd *scp, > union megasas_sgl *mfi_sgl) > { >@@ -473,7 +473,7 @@ > * If successful, this function returns the number of SG elements. Otherwise, > * it returnes -1. > */ >-static int >+static inline int > megasas_make_sgl64(struct megasas_instance *instance, struct scsi_cmnd *scp, > union megasas_sgl *mfi_sgl) > { >@@ -522,7 +522,7 @@ > * This function prepares CDB commands. These are typcially pass-through > * commands to the devices. > */ >-static int >+static inline int > megasas_build_dcdb(struct megasas_instance *instance, struct scsi_cmnd *scp, > struct megasas_cmd *cmd) > { >@@ -603,7 +603,7 @@ > * > * Frames (and accompanying SGLs) for regular SCSI IOs use this function. > */ >-static int >+static inline int > megasas_build_ldio(struct megasas_instance *instance, struct scsi_cmnd *scp, > struct megasas_cmd *cmd) > { >@@ -852,8 +852,9 @@ > > instance = (struct megasas_instance *)scmd->device->host->hostdata; > >- scmd_printk(KERN_NOTICE, scmd, "megasas: RESET -%ld cmd=%x\n", >- scmd->serial_number, scmd->cmnd[0]); >+ printk(KERN_NOTICE "megasas: RESET -%ld cmd=%x <c=%d t=%d l=%d>\n", >+ scmd->serial_number, scmd->cmnd[0], scmd->device->channel, >+ scmd->device->id, scmd->device->lun); > > if (instance->hw_crit_error) { > printk(KERN_ERR "megasas: cannot recover from previous reset " >@@ -861,12 +862,17 @@ > return FAILED; > } > >+ spin_unlock(scmd->device->host->host_lock); >+ > ret_val = megasas_wait_for_outstanding(instance); >+ > if (ret_val == SUCCESS) > printk(KERN_NOTICE "megasas: reset successful \n"); > else > printk(KERN_ERR "megasas: failed to do reset\n"); > >+ spin_lock(scmd->device->host->host_lock); >+ > return ret_val; > } > >@@ -930,15 +936,6 @@ > static int megasas_slave_configure(struct scsi_device *sdev) > { > /* >- * Don't export physical disk devices to the disk driver. >- * >- * FIXME: Currently we don't export them to the midlayer at all. >- * That will be fixed once LSI engineers have audited the >- * firmware for possible issues. >- */ >- if (sdev->channel < MEGASAS_MAX_PD_CHANNELS && sdev->type == TYPE_DISK) >- return -ENXIO; >- /* > * The RAID firmware may require extended timeouts > */ > if(sdev->channel >= MEGASAS_MAX_PD_CHANNELS) >@@ -1010,7 +1007,7 @@ > * @instance: Adapter soft state > * @cmd: Completed command > */ >-static void >+static inline void > megasas_unmap_sgbuf(struct megasas_instance *instance, struct megasas_cmd *cmd) > { > dma_addr_t buf_h; >@@ -1054,7 +1051,7 @@ > * an alternate status (as in the case of aborted > * commands) > */ >-static void >+static inline void > megasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, > u8 alt_status) > { >@@ -1077,6 +1074,20 @@ > break; > } > >+ /* >+ * Don't export physical disk devices to mid-layer. >+ */ >+ if (!MEGASAS_IS_LOGICAL(cmd->scmd) && >+ (hdr->cmd_status == MFI_STAT_OK) && >+ (cmd->scmd->cmnd[0] == INQUIRY)) { >+ >+ if (((*(u8 *) cmd->scmd->request_buffer) & 0x1F) == >+ TYPE_DISK) { >+ cmd->scmd->result = DID_BAD_TARGET << 16; >+ exception = 1; >+ } >+ } >+ > case MFI_CMD_LD_READ: > case MFI_CMD_LD_WRITE: > >@@ -1178,7 +1189,7 @@ > * SCSI mid-layer instead of the status > * returned by the FW > */ >-static int >+static inline int > megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) > { > u32 producer; >@@ -1225,7 +1236,7 @@ > > /** > * megasas_transition_to_ready - Move the FW to READY state >- * @instance: Adapter soft state >+ * @reg_set: MFI register set > * > * During the initialization, FW passes can potentially be in any one of > * several possible states. If the FW in operational, waiting-for-handshake >@@ -2176,7 +2187,7 @@ > * Initialize locks and queues > */ > INIT_LIST_HEAD(&instance->cmd_pool); >- >+ > atomic_set(&instance->fw_outstanding,0); > > init_waitqueue_head(&instance->int_cmd_wait_q); >@@ -2405,9 +2416,10 @@ > * megasas_shutdown - Shutdown entry point > * @device: Generic device structure > */ >-static void megasas_shutdown(struct pci_dev *pdev) >+static void megasas_shutdown(struct device *device) > { >- struct megasas_instance *instance = pci_get_drvdata(pdev); >+ struct megasas_instance *instance = (struct megasas_instance *) >+ dev_get_drvdata(device); > megasas_flush_cache(instance); > } > >@@ -2446,11 +2458,11 @@ > { > int rc; > >- mutex_lock(&megasas_async_queue_mutex); >+ down(&megasas_async_queue_mutex); > > rc = fasync_helper(fd, filep, mode, &megasas_async_queue); > >- mutex_unlock(&megasas_async_queue_mutex); >+ up(&megasas_async_queue_mutex); > > if (rc >= 0) { > /* For sanity check when we get ioctl */ >@@ -2705,12 +2717,14 @@ > /** > * megasas_mgmt_ioctl - char node ioctl entry point > */ >-static long >-megasas_mgmt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) >+static int >+megasas_mgmt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, >+ unsigned long arg) > { > switch (cmd) { > case MEGASAS_IOC_FIRMWARE: > return megasas_mgmt_ioctl_fw(file, arg); >+ > case MEGASAS_IOC_GET_AEN: > return megasas_mgmt_ioctl_aen(file, arg); > } >@@ -2759,8 +2773,8 @@ > } > > static long >-megasas_mgmt_compat_ioctl(struct file *file, unsigned int cmd, >- unsigned long arg) >+megasas_mgmt_compat_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg, >+ struct file *file) > { > switch (cmd) { > case MEGASAS_IOC_FIRMWARE32: >@@ -2781,10 +2795,7 @@ > .open = megasas_mgmt_open, > .release = megasas_mgmt_release, > .fasync = megasas_mgmt_fasync, >- .unlocked_ioctl = megasas_mgmt_ioctl, >-#ifdef CONFIG_COMPAT >- .compat_ioctl = megasas_mgmt_compat_ioctl, >-#endif >+ .ioctl = megasas_mgmt_ioctl, > }; > > /* >@@ -2796,7 +2807,9 @@ > .id_table = megasas_pci_table, > .probe = megasas_probe_one, > .remove = __devexit_p(megasas_detach_one), >- .shutdown = megasas_shutdown, >+ .driver = { >+ .shutdown = megasas_shutdown, >+ } > }; > > /* >@@ -2861,6 +2874,11 @@ > driver_create_file(&megasas_pci_driver.driver, > &driver_attr_release_date); > >+ register_ioctl32_conversion(MEGASAS_IOC_FIRMWARE32, >+ megasas_mgmt_compat_ioctl); >+ register_ioctl32_conversion(MEGASAS_IOC_GET_AEN, >+ megasas_mgmt_compat_ioctl); >+ > return rval; > } > >@@ -2869,6 +2887,9 @@ > */ > static void __exit megasas_exit(void) > { >+ unregister_ioctl32_conversion(MEGASAS_IOC_FIRMWARE32); >+ unregister_ioctl32_conversion(MEGASAS_IOC_GET_AEN); >+ > driver_remove_file(&megasas_pci_driver.driver, &driver_attr_version); > driver_remove_file(&megasas_pci_driver.driver, > &driver_attr_release_date); >--- linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.c.megaraid_sas_diskdump 2007-02-28 10:23:53.000000000 +0200 >+++ linux-2.6.9/drivers/scsi/megaraid/megaraid_sas.c 2007-02-28 10:25:50.000000000 +0200 >@@ -48,6 +48,19 @@ > MODULE_VERSION(MEGASAS_VERSION); > MODULE_AUTHOR("sreenivas.bagalkote@lsil.com"); > MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver"); >+ >+ /* >+ * diskdump helper function. We can't call msleep() or mdelay() while dumping. >+ */ >+static void megasas_msleep(unsigned long ms) >+{ >+ if(unlikely(crashdump_mode())) { >+ for (; ms > 1000; ms -= 1000) >+ udelay(1000); >+ udelay(ms); >+ } else >+ msleep(ms); >+} > > /* > * PCI ID table for all supported controllers >@@ -88,6 +101,11 @@ > static struct fasync_struct *megasas_async_queue; > static DECLARE_MUTEX(megasas_async_queue_mutex); > >+static void >+megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status); >+static void megasas_flush_cache(struct megasas_instance *instance); >+static irqreturn_t megasas_isr(int irq, void *devp, struct pt_regs *regs); >+ > /** > * megasas_get_cmd - Get a command from the free pool > * @instance: Adapter soft state >@@ -97,7 +115,7 @@ > static inline struct megasas_cmd *megasas_get_cmd(struct megasas_instance > *instance) > { >- unsigned long flags; >+ unsigned long flags = 0; > struct megasas_cmd *cmd = NULL; > > spin_lock_irqsave(&instance->cmd_pool_lock, flags); >@@ -339,7 +357,7 @@ > */ > for (i = 0; (i < msecs) && (frame_hdr->cmd_status == 0xff); i++) { > rmb(); >- msleep(1); >+ megasas_msleep(1); > } > > if (frame_hdr->cmd_status == 0xff) >@@ -772,6 +790,19 @@ > if (!cmd) > return SCSI_MLQUEUE_HOST_BUSY; > >+ if (MEGASAS_IS_LOGICAL(scmd)) { >+ switch (scmd->cmnd[0]) { >+ case SYNCHRONIZE_CACHE: >+ if (crashdump_mode()) >+ megasas_flush_cache(instance); >+ case REQUEST_SENSE: >+ case MODE_SELECT: >+ if (crashdump_mode()) { >+ scmd->result = (DID_OK << 16); >+ goto out_done; >+ } >+ } >+ } > /* > * Logical drive command > */ >@@ -826,7 +857,14 @@ > "commands to complete\n",i,outstanding); > } > >- msleep(1000); >+ /* >+ * In crash dump mode cannot complete cmds in interrupt context >+ * Complete cmds from here >+ */ >+ if(crashdump_mode()) >+ megasas_deplete_reply_queue(instance, DID_OK); >+ >+ megasas_msleep(1000); > } > > if (atomic_read(&instance->fw_outstanding)) { >@@ -943,6 +981,43 @@ > return 0; > } > >+/** >+ * megasas_diskdump_sanity_check - Entry point for diskdump >+ */ >+static int >+megasas_diskdump_sanity_check(struct scsi_device *device) >+{ >+ struct megasas_instance *instance; >+ >+ instance = (struct megasas_instance *)device->host->hostdata; >+ >+ if (!instance) >+ return -ENXIO; >+ >+ if (spin_is_locked(&instance->cmd_pool_lock)) >+ return -EBUSY; >+ >+ return 0; >+} >+ >+/** >+ * megasas_diskdump_poll - Interrupts are disabled during diskdump >+ * so this fn is called by diskdump driver to >+ * complete IO >+ */ >+static void >+megasas_diskdump_poll(struct scsi_device *device) >+{ >+ struct megasas_instance *instance; >+ >+ instance = (struct megasas_instance *)device->host->hostdata; >+ >+ if (!instance) >+ return ; >+ >+ megasas_isr(0,instance, NULL); >+} >+ > /* > * Scsi host template for megaraid_sas driver > */ >@@ -957,6 +1032,8 @@ > .eh_bus_reset_handler = megasas_reset_bus_host, > .eh_host_reset_handler = megasas_reset_bus_host, > .use_clustering = ENABLE_CLUSTERING, >+ .dump_sanity_check = megasas_diskdump_sanity_check, >+ .dump_poll = megasas_diskdump_poll, > }; > > /** >@@ -1189,7 +1266,7 @@ > * SCSI mid-layer instead of the status > * returned by the FW > */ >-static inline int >+static void > megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) > { > u32 producer; >@@ -1197,13 +1274,6 @@ > u32 context; > struct megasas_cmd *cmd; > >- /* >- * Check if it is our interrupt >- * Clear the interrupt >- */ >- if(instance->instancet->clear_intr(instance->reg_set)) >- return IRQ_NONE; >- > producer = *instance->producer; > consumer = *instance->consumer; > >@@ -1221,8 +1291,6 @@ > } > > *instance->consumer = producer; >- >- return IRQ_HANDLED; > } > > /** >@@ -1230,8 +1298,18 @@ > */ > static irqreturn_t megasas_isr(int irq, void *devp, struct pt_regs *regs) > { >- return megasas_deplete_reply_queue((struct megasas_instance *)devp, >- DID_OK); >+ struct megasas_instance *instance ; >+ instance = (struct megasas_instance *)devp; >+ /* >+ * Check if it is our interrupt >+ * Clear the interrupt >+ */ >+ if(instance->instancet->clear_intr(instance->reg_set)) >+ return IRQ_NONE; >+ >+ megasas_deplete_reply_queue(instance, DID_OK); >+ >+ return IRQ_HANDLED; > } > > /** >@@ -1333,7 +1411,7 @@ > MFI_STATE_MASK ; > > if (fw_state == cur_state) { >- msleep(1); >+ megasas_msleep(1); > } else > break; > } >@@ -2313,7 +2391,10 @@ > dcmd->opcode = MR_DCMD_CTRL_CACHE_FLUSH; > dcmd->mbox.b[0] = MR_FLUSH_CTRL_CACHE | MR_FLUSH_DISK_CACHE; > >- megasas_issue_blocked_cmd(instance, cmd); >+ if (crashdump_mode()) >+ megasas_issue_polled(instance,cmd); >+ else >+ megasas_issue_blocked_cmd(instance, cmd); > > megasas_return_cmd(instance, cmd); >
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 230826
: 149170