/* 3w-sas.c -- LSI 3ware SAS/SATA-RAID Controller device driver for Linux. Written By: Adam Radford <[email protected]> Copyright (C) 2009 LSI Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. NO WARRANTY THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. DISCLAIMER OF LIABILITY NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Controllers supported by this driver: LSI 3ware 9750 6Gb/s SAS/SATA-RAID Bugs/Comments/Suggestions should be mailed to: [email protected] History ------- 3.26.02.000 - Initial driver release. */ #include <linux/module.h> #include <linux/reboot.h> #include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/moduleparam.h> #include <linux/errno.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/pci.h> #include <linux/time.h> #include <linux/mutex.h> #include <linux/slab.h> #include <asm/io.h> #include <asm/irq.h> #include <linux/uaccess.h> #include <scsi/scsi.h> #include <scsi/scsi_host.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_cmnd.h> #include "3w-sas.h" /* Globals */ #define TW_DRIVER_VERSION … static DEFINE_MUTEX(twl_chrdev_mutex); static TW_Device_Extension *twl_device_extension_list[TW_MAX_SLOT]; static unsigned int twl_device_extension_count; static int twl_major = …; extern struct timezone sys_tz; /* Module parameters */ MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …; MODULE_VERSION(…); static int use_msi; module_param(use_msi, int, S_IRUGO); MODULE_PARM_DESC(…) …; /* Function prototypes */ static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset); /* Functions */ /* This function returns AENs through sysfs */ static ssize_t twl_sysfs_aen_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *outbuf, loff_t offset, size_t count) { … } /* End twl_sysfs_aen_read() */ /* aen_read sysfs attribute initializer */ static struct bin_attribute twl_sysfs_aen_read_attr = …; /* This function returns driver compatibility info through sysfs */ static ssize_t twl_sysfs_compat_info(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *outbuf, loff_t offset, size_t count) { … } /* End twl_sysfs_compat_info() */ /* compat_info sysfs attribute initializer */ static struct bin_attribute twl_sysfs_compat_info_attr = …; /* Show some statistics about the card */ static ssize_t twl_show_stats(struct device *dev, struct device_attribute *attr, char *buf) { … } /* End twl_show_stats() */ /* stats sysfs attribute initializer */ static struct device_attribute twl_host_stats_attr = …; /* Host attributes initializer */ static struct attribute *twl_host_attrs[] = …; ATTRIBUTE_GROUPS(…); /* This function will look up an AEN severity string */ static char *twl_aen_severity_lookup(unsigned char severity_code) { … } /* End twl_aen_severity_lookup() */ /* This function will queue an event */ static void twl_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header) { … } /* End twl_aen_queue_event() */ /* This function will attempt to post a command packet to the board */ static int twl_post_command_packet(TW_Device_Extension *tw_dev, int request_id) { … } /* End twl_post_command_packet() */ /* This function hands scsi cdb's to the firmware */ static int twl_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id, unsigned char *cdb, int use_sg, TW_SG_Entry_ISO *sglistarg) { … } /* End twl_scsiop_execute_scsi() */ /* This function will read the aen queue from the isr */ static int twl_aen_read_queue(TW_Device_Extension *tw_dev, int request_id) { … } /* End twl_aen_read_queue() */ /* This function will sync firmware time with the host time */ static void twl_aen_sync_time(TW_Device_Extension *tw_dev, int request_id) { … } /* End twl_aen_sync_time() */ /* This function will assign an available request id */ static void twl_get_request_id(TW_Device_Extension *tw_dev, int *request_id) { … } /* End twl_get_request_id() */ /* This function will free a request id */ static void twl_free_request_id(TW_Device_Extension *tw_dev, int request_id) { … } /* End twl_free_request_id() */ /* This function will complete an aen request from the isr */ static int twl_aen_complete(TW_Device_Extension *tw_dev, int request_id) { … } /* End twl_aen_complete() */ /* This function will poll for a response */ static int twl_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds) { … } /* End twl_poll_response() */ /* This function will drain the aen queue */ static int twl_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset) { … } /* End twl_aen_drain_queue() */ /* This function will allocate memory and check if it is correctly aligned */ static int twl_allocate_memory(TW_Device_Extension *tw_dev, int size, int which) { … } /* End twl_allocate_memory() */ /* This function will load the request id and various sgls for ioctls */ static void twl_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length) { … } /* End twl_load_sgl() */ /* This function handles ioctl for the character device This interface is used by smartmontools open source software */ static long twl_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { … } /* End twl_chrdev_ioctl() */ /* This function handles open for the character device */ static int twl_chrdev_open(struct inode *inode, struct file *file) { … } /* End twl_chrdev_open() */ /* File operations struct for character device */ static const struct file_operations twl_fops = …; /* This function passes sense data from firmware to scsi layer */ static int twl_fill_sense(TW_Device_Extension *tw_dev, int i, int request_id, int copy_sense, int print_host) { … } /* End twl_fill_sense() */ /* This function will free up device extension resources */ static void twl_free_device_extension(TW_Device_Extension *tw_dev) { … } /* End twl_free_device_extension() */ /* This function will get parameter table entries from the firmware */ static void *twl_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes) { … } /* End twl_get_param() */ /* This function will send an initconnection command to controller */ static int twl_initconnection(TW_Device_Extension *tw_dev, int message_credits, u32 set_features, unsigned short current_fw_srl, unsigned short current_fw_arch_id, unsigned short current_fw_branch, unsigned short current_fw_build, unsigned short *fw_on_ctlr_srl, unsigned short *fw_on_ctlr_arch_id, unsigned short *fw_on_ctlr_branch, unsigned short *fw_on_ctlr_build, u32 *init_connect_result) { … } /* End twl_initconnection() */ /* This function will initialize the fields of a device extension */ static int twl_initialize_device_extension(TW_Device_Extension *tw_dev) { … } /* End twl_initialize_device_extension() */ /* This function will handle attention interrupts */ static int twl_handle_attention_interrupt(TW_Device_Extension *tw_dev) { … } /* End twl_handle_attention_interrupt() */ /* Interrupt service routine */ static irqreturn_t twl_interrupt(int irq, void *dev_instance) { … } /* End twl_interrupt() */ /* This function will poll for a register change */ static int twl_poll_register(TW_Device_Extension *tw_dev, void *reg, u32 value, u32 result, int seconds) { … } /* End twl_poll_register() */ /* This function will reset a controller */ static int twl_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset) { … } /* End twl_reset_sequence() */ /* This function will reset a device extension */ static int twl_reset_device_extension(TW_Device_Extension *tw_dev, int ioctl_reset) { … } /* End twl_reset_device_extension() */ /* This funciton returns unit geometry in cylinders/heads/sectors */ static int twl_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[]) { … } /* End twl_scsi_biosparam() */ /* This is the new scsi eh reset function */ static int twl_scsi_eh_reset(struct scsi_cmnd *SCpnt) { … } /* End twl_scsi_eh_reset() */ /* This is the main scsi queue function to handle scsi opcodes */ static int twl_scsi_queue_lck(struct scsi_cmnd *SCpnt) { … } /* End twl_scsi_queue() */ static DEF_SCSI_QCMD(twl_scsi_queue) /* This function tells the controller to shut down */ static void __twl_shutdown(TW_Device_Extension *tw_dev) { … } /* End __twl_shutdown() */ /* Wrapper for __twl_shutdown */ static void twl_shutdown(struct pci_dev *pdev) { … } /* End twl_shutdown() */ /* This function configures unit settings when a unit is coming on-line */ static int twl_slave_configure(struct scsi_device *sdev) { … } /* End twl_slave_configure() */ static const struct scsi_host_template driver_template = …; /* This function will probe and initialize a card */ static int twl_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id) { … } /* End twl_probe() */ /* This function is called to remove a device */ static void twl_remove(struct pci_dev *pdev) { … } /* End twl_remove() */ /* This function is called on PCI suspend */ static int __maybe_unused twl_suspend(struct device *dev) { … } /* End twl_suspend() */ /* This function is called on PCI resume */ static int __maybe_unused twl_resume(struct device *dev) { … } /* End twl_resume() */ /* PCI Devices supported by this driver */ static struct pci_device_id twl_pci_tbl[] = …; MODULE_DEVICE_TABLE(pci, twl_pci_tbl); static SIMPLE_DEV_PM_OPS(twl_pm_ops, twl_suspend, twl_resume); /* pci_driver initializer */ static struct pci_driver twl_driver = …; /* This function is called on driver initialization */ static int __init twl_init(void) { … } /* End twl_init() */ /* This function is called on driver exit */ static void __exit twl_exit(void) { … } /* End twl_exit() */ module_init(…) …; module_exit(twl_exit);