📄 megaraid_sas.c
字号:
/* * * 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 : v00.00.03.16 * * Authors: * (email-id : megaraidlinux@lsi.com) * Sreenivas Bagalkote * Sumant Patro * Bo Yang * * 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/blkdev.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"/* * Modules parameters *//* * Fast driver load option, skip scanning for physical devices during load. * This would result in physical devices being skipped during driver load * time. These can be later added though, using /proc/scsi/scsi */static unsigned int fast_load = 0;module_param_named(fast_load, fast_load, int, 0);MODULE_PARM_DESC(fast_load, "megasas: Faster loading of the driver, skips physical devices! \ (default=0)");/* * Number of sectors per IO command * Will be set in megasas_init_mfi if user does not provide */static unsigned int max_sectors = 0;module_param_named(max_sectors, max_sectors, int, 0);MODULE_PARM_DESC(max_sectors, "Maximum number of sectors per IO command");/* * Number of cmds per logical unit */static unsigned int cmd_per_lun = MEGASAS_DEFAULT_CMD_PER_LUN;module_param_named(cmd_per_lun, cmd_per_lun, int, 0);MODULE_PARM_DESC(cmd_per_lun, "Maximum number of commands per logical unit (default=128)");MODULE_LICENSE("GPL");MODULE_VERSION(MEGASAS_VERSION);MODULE_AUTHOR("megaraidlinux@lsi.com");MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");/* * PCI ID table for all supported controllers */static struct pci_device_id megasas_pci_table[] = { {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)}, /* xscale IOP */ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)}, /* ppc IOP */ {PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)}, /* xscale IOP, vega */ {PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)}, /* xscale IOP */ {}};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);static u32 megasas_dbg_lvl;static voidmegasas_complete_cmd(struct megasas_instance *instance, struct megasas_cmd *cmd, u8 alt_status);/** * 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 voidmegasas_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 voidmegasas_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_disable_intr_xscale -Disables interrupt * @regs: MFI register set */static inline voidmegasas_disable_intr_xscale(struct megasas_register_set __iomem * regs){ u32 mask = 0x1f; writel(mask, ®s->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 u32megasas_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, .disable_intr = megasas_disable_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 voidmegasas_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_disable_intr_ppc - Disables interrupt * @regs: MFI register set */static inline voidmegasas_disable_intr_ppc(struct megasas_register_set __iomem * regs){ u32 mask = 0xFFFFFFFF; writel(mask, ®s->outbound_intr_mask); /* Dummy readl to force pci flush */ readl(®s->outbound_intr_mask);}/** * megasas_read_fw_status_reg_ppc - returns the current FW status value * @regs: MFI register set */static u32megasas_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, .disable_intr = megasas_disable_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_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 intmegasas_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. * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs * Used to issue ioctl commands. */static intmegasas_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_timeout(instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA), MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); 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 waits for return status. * Max wait time is MEGASAS_INTERNAL_CMD_WAIT_TIME secs */static intmegasas_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_timeout(instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF), MEGASAS_INTERNAL_CMD_WAIT_TIME*HZ); 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 intmegasas_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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -