linux/drivers/scsi/3w-9xxx.c

/*
   3w-9xxx.c -- 3ware 9000 Storage Controller device driver for Linux.

   Written By: Adam Radford <[email protected]>
   Modifications By: Tom Couch

   Copyright (C) 2004-2009 Applied Micro Circuits Corporation.
   Copyright (C) 2010 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

   Bugs/Comments/Suggestions should be mailed to:
   [email protected]

   Note: This version of the driver does not contain a bundled firmware
         image.

   History
   -------
   2.26.02.000 - Driver cleanup for kernel submission.
   2.26.02.001 - Replace schedule_timeout() calls with msleep().
   2.26.02.002 - Add support for PAE mode.
                 Add lun support.
                 Fix twa_remove() to free irq handler/unregister_chrdev()
                 before shutting down card.
                 Change to new 'change_queue_depth' api.
                 Fix 'handled=1' ISR usage, remove bogus IRQ check.
                 Remove un-needed eh_abort handler.
                 Add support for embedded firmware error strings.
   2.26.02.003 - Correctly handle single sgl's with use_sg=1.
   2.26.02.004 - Add support for 9550SX controllers.
   2.26.02.005 - Fix use_sg == 0 mapping on systems with 4GB or higher.
   2.26.02.006 - Fix 9550SX pchip reset timeout.
                 Add big endian support.
   2.26.02.007 - Disable local interrupts during kmap/unmap_atomic().
   2.26.02.008 - Free irq handler in __twa_shutdown().
                 Serialize reset code.
                 Add support for 9650SE controllers.
   2.26.02.009 - Fix dma mask setting to fallback to 32-bit if 64-bit fails.
   2.26.02.010 - Add support for 9690SA controllers.
   2.26.02.011 - Increase max AENs drained to 256.
                 Add MSI support and "use_msi" module parameter.
                 Fix bug in twa_get_param() on 4GB+.
                 Use pci_resource_len() for ioremap().
   2.26.02.012 - Add power management support.
   2.26.02.013 - Fix bug in twa_load_sgl().
   2.26.02.014 - Force 60 second timeout default.
*/

#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-9xxx.h"

/* Globals */
#define TW_DRIVER_VERSION
static DEFINE_MUTEX(twa_chrdev_mutex);
static TW_Device_Extension *twa_device_extension_list[TW_MAX_SLOT];
static unsigned int twa_device_extension_count;
static int twa_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 void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header);
static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id);
static char *twa_aen_severity_lookup(unsigned char severity_code);
static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id);
static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static int twa_chrdev_open(struct inode *inode, struct file *file);
static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host);
static void twa_free_request_id(TW_Device_Extension *tw_dev,int request_id);
static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id);
static int twa_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);
static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length);
static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds);
static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds);
static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal);
static int twa_reset_device_extension(TW_Device_Extension *tw_dev);
static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset);
static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
				   unsigned char *cdb, int use_sg,
				   TW_SG_Entry *sglistarg);
static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id);
static char *twa_string_lookup(twa_message_type *table, unsigned int aen_code);

/* Functions */

/* Show some statistics about the card */
static ssize_t twa_show_stats(struct device *dev,
			      struct device_attribute *attr, char *buf)
{} /* End twa_show_stats() */

/* Create sysfs 'stats' entry */
static struct device_attribute twa_host_stats_attr =;

/* Host attributes initializer */
static struct attribute *twa_host_attrs[] =;

ATTRIBUTE_GROUPS();

/* File operations struct for character device */
static const struct file_operations twa_fops =;

/*
 * The controllers use an inline buffer instead of a mapped SGL for small,
 * single entry buffers.  Note that we treat a zero-length transfer like
 * a mapped SGL.
 */
static bool twa_command_mapped(struct scsi_cmnd *cmd)
{}

/* This function will complete an aen request from the isr */
static int twa_aen_complete(TW_Device_Extension *tw_dev, int request_id)
{} /* End twa_aen_complete() */

/* This function will drain aen queue */
static int twa_aen_drain_queue(TW_Device_Extension *tw_dev, int no_check_reset)
{} /* End twa_aen_drain_queue() */

/* This function will queue an event */
static void twa_aen_queue_event(TW_Device_Extension *tw_dev, TW_Command_Apache_Header *header)
{} /* End twa_aen_queue_event() */

/* This function will read the aen queue from the isr */
static int twa_aen_read_queue(TW_Device_Extension *tw_dev, int request_id)
{} /* End twa_aen_read_queue() */

/* This function will look up an AEN severity string */
static char *twa_aen_severity_lookup(unsigned char severity_code)
{} /* End twa_aen_severity_lookup() */

/* This function will sync firmware time with the host time */
static void twa_aen_sync_time(TW_Device_Extension *tw_dev, int request_id)
{} /* End twa_aen_sync_time() */

/* This function will allocate memory and check if it is correctly aligned */
static int twa_allocate_memory(TW_Device_Extension *tw_dev, int size, int which)
{} /* End twa_allocate_memory() */

/* This function will check the status register for unexpected bits */
static int twa_check_bits(u32 status_reg_value)
{} /* End twa_check_bits() */

/* This function will check the srl and decide if we are compatible  */
static int twa_check_srl(TW_Device_Extension *tw_dev, int *flashed)
{} /* End twa_check_srl() */

/* This function handles ioctl for the character device */
static long twa_chrdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{} /* End twa_chrdev_ioctl() */

/* This function handles open for the character device */
/* NOTE that this function will race with remove. */
static int twa_chrdev_open(struct inode *inode, struct file *file)
{} /* End twa_chrdev_open() */

/* This function will print readable messages from status register errors */
static int twa_decode_bits(TW_Device_Extension *tw_dev, u32 status_reg_value)
{} /* End twa_decode_bits() */

/* This function will empty the response queue */
static int twa_empty_response_queue(TW_Device_Extension *tw_dev)
{} /* End twa_empty_response_queue() */

/* This function will clear the pchip/response queue on 9550SX */
static int twa_empty_response_queue_large(TW_Device_Extension *tw_dev)
{} /* End twa_empty_response_queue_large() */

/* This function passes sense keys from firmware to scsi layer */
static int twa_fill_sense(TW_Device_Extension *tw_dev, int request_id, int copy_sense, int print_host)
{} /* End twa_fill_sense() */

/* This function will free up device extension resources */
static void twa_free_device_extension(TW_Device_Extension *tw_dev)
{} /* End twa_free_device_extension() */

/* This function will free a request id */
static void twa_free_request_id(TW_Device_Extension *tw_dev, int request_id)
{} /* End twa_free_request_id() */

/* This function will get parameter table entries from the firmware */
static void *twa_get_param(TW_Device_Extension *tw_dev, int request_id, int table_id, int parameter_id, int parameter_size_bytes)
{} /* End twa_get_param() */

/* This function will assign an available request id */
static void twa_get_request_id(TW_Device_Extension *tw_dev, int *request_id)
{} /* End twa_get_request_id() */

/* This function will send an initconnection command to controller */
static int twa_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 twa_initconnection() */

/* This function will initialize the fields of a device extension */
static int twa_initialize_device_extension(TW_Device_Extension *tw_dev)
{} /* End twa_initialize_device_extension() */

/* This function is the interrupt service routine */
static irqreturn_t twa_interrupt(int irq, void *dev_instance)
{} /* End twa_interrupt() */

/* This function will load the request id and various sgls for ioctls */
static void twa_load_sgl(TW_Device_Extension *tw_dev, TW_Command_Full *full_command_packet, int request_id, dma_addr_t dma_handle, int length)
{} /* End twa_load_sgl() */

/* This function will poll for a response interrupt of a request */
static int twa_poll_response(TW_Device_Extension *tw_dev, int request_id, int seconds)
{} /* End twa_poll_response() */

/* This function will poll the status register for a flag */
static int twa_poll_status(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{} /* End twa_poll_status() */

/* This function will poll the status register for disappearance of a flag */
static int twa_poll_status_gone(TW_Device_Extension *tw_dev, u32 flag, int seconds)
{} /* End twa_poll_status_gone() */

/* This function will attempt to post a command packet to the board */
static int twa_post_command_packet(TW_Device_Extension *tw_dev, int request_id, char internal)
{} /* End twa_post_command_packet() */

/* This function will reset a device extension */
static int twa_reset_device_extension(TW_Device_Extension *tw_dev)
{} /* End twa_reset_device_extension() */

/* This function will reset a controller */
static int twa_reset_sequence(TW_Device_Extension *tw_dev, int soft_reset)
{} /* End twa_reset_sequence() */

/* This funciton returns unit geometry in cylinders/heads/sectors */
static int twa_scsi_biosparam(struct scsi_device *sdev, struct block_device *bdev, sector_t capacity, int geom[])
{} /* End twa_scsi_biosparam() */

/* This is the new scsi eh reset function */
static int twa_scsi_eh_reset(struct scsi_cmnd *SCpnt)
{} /* End twa_scsi_eh_reset() */

/* This is the main scsi queue function to handle scsi opcodes */
static int twa_scsi_queue_lck(struct scsi_cmnd *SCpnt)
{} /* End twa_scsi_queue() */

static DEF_SCSI_QCMD(twa_scsi_queue)

/* This function hands scsi cdb's to the firmware */
static int twa_scsiop_execute_scsi(TW_Device_Extension *tw_dev, int request_id,
				   unsigned char *cdb, int use_sg,
				   TW_SG_Entry *sglistarg)
{} /* End twa_scsiop_execute_scsi() */

/* This function completes an execute scsi operation */
static void twa_scsiop_execute_scsi_complete(TW_Device_Extension *tw_dev, int request_id)
{} /* End twa_scsiop_execute_scsi_complete() */

/* This function tells the controller to shut down */
static void __twa_shutdown(TW_Device_Extension *tw_dev)
{} /* End __twa_shutdown() */

/* Wrapper for __twa_shutdown */
static void twa_shutdown(struct pci_dev *pdev)
{} /* End twa_shutdown() */

/* This function will look up a string */
static char *twa_string_lookup(twa_message_type *table, unsigned int code)
{} /* End twa_string_lookup() */

/* This function gets called when a disk is coming on-line */
static int twa_slave_configure(struct scsi_device *sdev)
{} /* End twa_slave_configure() */

static const struct scsi_host_template driver_template =;

/* This function will probe and initialize a card */
static int twa_probe(struct pci_dev *pdev, const struct pci_device_id *dev_id)
{} /* End twa_probe() */

/* This function is called to remove a device */
static void twa_remove(struct pci_dev *pdev)
{} /* End twa_remove() */

/* This function is called on PCI suspend */
static int __maybe_unused twa_suspend(struct device *dev)
{} /* End twa_suspend() */

/* This function is called on PCI resume */
static int __maybe_unused twa_resume(struct device *dev)
{} /* End twa_resume() */

/* PCI Devices supported by this driver */
static struct pci_device_id twa_pci_tbl[] =;
MODULE_DEVICE_TABLE(pci, twa_pci_tbl);

static SIMPLE_DEV_PM_OPS(twa_pm_ops, twa_suspend, twa_resume);

/* pci_driver initializer */
static struct pci_driver twa_driver =;

/* This function is called on driver initialization */
static int __init twa_init(void)
{} /* End twa_init() */

/* This function is called on driver exit */
static void __exit twa_exit(void)
{} /* End twa_exit() */

module_init();
module_exit(twa_exit);