From: "Bagalkote, Sreenivas" We are announcing a driver for LSI Logic's new SAS based MegaRAID controllers. Signed-off-by: Sreenivas Bagalkote Signed-off-by: Andrew Morton --- 25-akpm/Documentation/scsi/ChangeLog.megaraid_sas | 9 25-akpm/drivers/scsi/Kconfig | 1 25-akpm/drivers/scsi/Makefile | 1 25-akpm/drivers/scsi/megaraid/Kconfig.megaraid_sas | 9 25-akpm/drivers/scsi/megaraid/Makefile | 1 25-akpm/drivers/scsi/megaraid/megaraid_sas.c | 2746 +++++++++++++++++++++ 25-akpm/drivers/scsi/megaraid/megaraid_sas.h | 1118 ++++++++ 7 files changed, 3885 insertions(+) diff -puN /dev/null Documentation/scsi/ChangeLog.megaraid_sas --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/Documentation/scsi/ChangeLog.megaraid_sas Sun Mar 6 16:24:15 2005 @@ -0,0 +1,9 @@ +Release Date : Fri Mar 4 21:06:57 EST 2005 +Released by : Sreenivas Bagalkote (sreenivas.bagalkote@lsil.com) +Current Version : 00.00.01.00 +Older Version : NA + +1. Initial announcement to community - Module for LSI Logic's SAS based + RAID controllers. + + diff -puN drivers/scsi/Kconfig~megaraid_sas-announcing-new-module-for drivers/scsi/Kconfig --- 25/drivers/scsi/Kconfig~megaraid_sas-announcing-new-module-for Sun Mar 6 16:24:15 2005 +++ 25-akpm/drivers/scsi/Kconfig Sun Mar 6 16:24:15 2005 @@ -404,6 +404,7 @@ config SCSI_IN2000 module will be called in2000. source "drivers/scsi/megaraid/Kconfig.megaraid" +source "drivers/scsi/megaraid/Kconfig.megaraid_sas" config SCSI_SATA bool "Serial ATA (SATA) support" diff -puN drivers/scsi/Makefile~megaraid_sas-announcing-new-module-for drivers/scsi/Makefile --- 25/drivers/scsi/Makefile~megaraid_sas-announcing-new-module-for Sun Mar 6 16:24:15 2005 +++ 25-akpm/drivers/scsi/Makefile Sun Mar 6 16:24:15 2005 @@ -97,6 +97,7 @@ obj-$(CONFIG_SCSI_DC395x) += dc395x.o 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 diff -puN /dev/null drivers/scsi/megaraid/Kconfig.megaraid_sas --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/scsi/megaraid/Kconfig.megaraid_sas Sun Mar 6 16:24:15 2005 @@ -0,0 +1,9 @@ +config MEGARAID_SAS + tristate "LSI Logic MegaRAID SAS RAID module (New Driver)" + 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 + + diff -puN drivers/scsi/megaraid/Makefile~megaraid_sas-announcing-new-module-for drivers/scsi/megaraid/Makefile --- 25/drivers/scsi/megaraid/Makefile~megaraid_sas-announcing-new-module-for Sun Mar 6 16:24:15 2005 +++ 25-akpm/drivers/scsi/megaraid/Makefile Sun Mar 6 16:24:15 2005 @@ -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 diff -puN /dev/null drivers/scsi/megaraid/megaraid_sas.c --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/scsi/megaraid/megaraid_sas.c Sun Mar 6 16:24:15 2005 @@ -0,0 +1,2746 @@ +/* + * + * 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.01.00 + * + * Authors: + * Sreenivas Bagalkote + * + * List of supported controllers + * + * OEM Product Name VID DID SSVID SSID + * --- ------------ --- --- ---- ---- + */ + +#include "megaraid_sas.h" + +static int megasas_init (void); +static void megasas_exit (void); + +static int megasas_probe_one (struct pci_dev*, + const struct pci_device_id*); +static void megasas_detach_one (struct pci_dev*); +static void megasas_shutdown (struct device*); +static void megasas_flush_cache (struct megasas_instance*); +static void megasas_shutdown_controller (struct megasas_instance*); + +static int megasas_init_mfi (struct megasas_instance*); +static void megasas_reset_mfi (struct megasas_instance*); + +static int megasas_transition_to_ready (struct megasas_register_set*); + +static int megasas_alloc_cmds (struct megasas_instance*); +static void megasas_free_cmds (struct megasas_instance*); +static int megasas_create_frame_pool (struct megasas_instance*); +static void megasas_teardown_frame_pool (struct megasas_instance*); + +static int megasas_start_aen (struct megasas_instance*); +static int megasas_get_seq_num (struct megasas_instance*, + struct megasas_evt_log_info*); +static int megasas_register_aen (struct megasas_instance*, + uint32_t, uint32_t); +static void megasas_service_aen (struct megasas_instance*, + struct megasas_cmd*); + +static int megasas_io_attach (struct megasas_instance*); +static void megasas_io_detach (struct megasas_instance*); + +static int megasas_abort_handler (struct scsi_cmnd*); +static int megasas_reset_handler (struct scsi_cmnd*); +static int megasas_queue_command (struct scsi_cmnd*, + void (*) (struct scsi_cmnd*)); + +static int megasas_issue_polled (struct megasas_instance*, + struct megasas_cmd*); +static int megasas_issue_blocked_cmd (struct megasas_instance*, + struct megasas_cmd*); + +static int megasas_sync_abort_cmd (struct megasas_instance*, + struct megasas_cmd*); +static void megasas_complete_abort (struct megasas_instance*, + struct megasas_cmd*); + +static struct megasas_cmd* megasas_build_cmd (struct megasas_instance*, + struct scsi_cmnd*, int*); +static int megasas_build_dcdb (struct megasas_instance*, + struct scsi_cmnd*, + struct megasas_cmd*); +static int megasas_build_ldio (struct megasas_instance*, + struct scsi_cmnd*, + struct megasas_cmd*); + +static int megasas_make_sgl32 (struct megasas_instance*, + struct scsi_cmnd*, + union megasas_sgl*); +static int megasas_make_sgl64 (struct megasas_instance*, + struct scsi_cmnd*, + union megasas_sgl*); + +static irqreturn_t megasas_isr (int, void *, struct pt_regs *); +static void megasas_complete_cmd (struct megasas_instance*, + struct megasas_cmd*); +static void megasas_complete_int_cmd (struct megasas_instance*, + struct megasas_cmd*); + +static int megasas_mgmt_open (struct inode*, struct file*); +static int megasas_mgmt_release (struct inode*, struct file*); +static int megasas_mgmt_fasync (int, struct file*, int); +static int megasas_mgmt_ioctl (struct inode*, struct file*, + unsigned int, unsigned long); + +static int megasas_mgmt_fw_ioctl (struct megasas_instance*, + void __user*); +static int megasas_mgmt_fw_dcmd (struct megasas_instance*, + struct iocpacket*, void __user*, + struct megasas_cmd*); +static int megasas_mgmt_fw_smp (struct megasas_instance*, + struct iocpacket*, void __user*, + struct megasas_cmd*); + +static ssize_t megasas_sysfs_show_app_hndl (struct class_device*, char*); + +static void megasas_fill_drv_ver (struct megasas_drv_ver*); +static int megasas_get_ctrl_info (struct megasas_instance*, + struct megasas_ctrl_info* ); + +MODULE_LICENSE ("GPL"); +MODULE_VERSION (MEGASAS_VERSION); +MODULE_AUTHOR ("LSI Logic Corporation"); +MODULE_DESCRIPTION ("LSI Logic MegaRAID SAS Driver"); + +/* + * PCI ID table for all supported controllers + */ +static struct pci_device_id megasas_pci_table_g[] = { + + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_LSI_1064, + PCI_SUBVENDOR_x0000, + PCI_SUBSYSTEM_x0000, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_LSI_1064, + PCI_SUBVENDOR_x1000, + PCI_SUBSYSTEM_x0002, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_LSI_1064, + PCI_SUBVENDOR_x1000, + PCI_SUBSYSTEM_x1001, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC5, + PCI_VENDOR_ID_DELL, + PCI_SUBSYSTEM_PERC5H, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC5, + PCI_VENDOR_ID_DELL, + PCI_SUBSYSTEM_PERC5L, + }, + { + PCI_VENDOR_ID_DELL, + PCI_DEVICE_ID_PERC5, + PCI_VENDOR_ID_DELL, + PCI_SUBSYSTEM_PERC5I, + }, + { + PCI_VENDOR_ID_LSI_LOGIC, + PCI_DEVICE_LSI_1064, + PCI_SUB_DEVICEID_FSC, + PCI_SUB_VENDORID_FSC, + }, + { 0 } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(pci, megasas_pci_table_g); + +/* + * PCI hotplug support registration structure + */ +static struct pci_driver megasas_pci_driver = { + + .name = "megaraid_sas", + .id_table = megasas_pci_table_g, + .probe = megasas_probe_one, + .remove = __devexit_p(megasas_detach_one), + .driver = { + .shutdown = megasas_shutdown, + } +}; + +/* + * Sysfs attribute definition: Exports driver specific controller handle + */ +CLASS_DEVICE_ATTR(megaraid_sas_app_hndl, S_IRUSR, megasas_sysfs_show_app_hndl, + NULL); +/* + * Host template initializer for sysfs attributes + */ +static struct class_device_attribute* megasas_shost_attrs[] = { + &class_device_attr_megaraid_sas_app_hndl, + NULL, +}; + +/* + * Scsi host template for megaraid mfi driver + */ +static struct scsi_host_template megasas_template_g = { + + .module = THIS_MODULE, + .name = "LSI Logic SAS based MegaRAID driver", + .proc_name = "megaraid_sas", + .queuecommand = megasas_queue_command, + .eh_abort_handler = megasas_abort_handler, + .eh_device_reset_handler = megasas_reset_handler, + .eh_bus_reset_handler = megasas_reset_handler, + .eh_host_reset_handler = megasas_reset_handler, + .use_clustering = ENABLE_CLUSTERING, + .shost_attrs = megasas_shost_attrs, +}; + +/* + * File opereations 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, + .ioctl = megasas_mgmt_ioctl, +}; + +static int megasas_mgmt_majorno; +static struct megasas_mgmt_info megasas_mgmt_info; +static struct fasync_struct* megasas_async_queue; +static DECLARE_MUTEX (megasas_async_queue_mutex); +static int is_dma64; + +/** + * megasas_get_cmd : Get a command from the free pool + */ +static inline struct megasas_cmd* +megasas_get_cmd( struct megasas_instance* instance ) +{ + unsigned long flags; + struct megasas_cmd* cmd; + + spin_lock_irqsave(&instance->cmd_pool_lock, flags); + + if ( list_empty(&instance->cmd_pool)) { + spin_unlock_irqrestore(&instance->cmd_pool_lock, flags); + return NULL; + } + + cmd = list_entry((&instance->cmd_pool)->next, struct megasas_cmd, list); + + list_del_init( &cmd->list ); + + spin_unlock_irqrestore( &instance->cmd_pool_lock, flags ); + + return cmd; +} + +/** + * megasas_return_cmd : Return a cmd 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 ); + + list_add( &cmd->list, &instance->cmd_pool ); + + spin_unlock_irqrestore( &instance->cmd_pool_lock, flags ); +} + +/** + * megasas_make_sgl32 : return -1 or sge_count + */ +static inline 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; + struct page* page; + unsigned long offset; + + /* + * Return 0 if there is no data transfer + */ + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { + page = virt_to_page( scp->request_buffer ); + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK); + + mfi_sgl->sge32[0].phys_addr = pci_map_page(instance->pdev, + page, offset, + 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 : return -1 or sge_count + */ +static inline 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; + struct page* page; + unsigned long offset; + + /* + * Return 0 if there is no data transfer + */ + if (!scp->request_buffer || !scp->request_bufflen) + return 0; + + if (!scp->use_sg) { + page = virt_to_page( scp->request_buffer ); + offset = ((unsigned long)scp->request_buffer & ~PAGE_MASK); + + mfi_sgl->sge64[0].phys_addr = pci_map_page(instance->pdev, + page, offset, + 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_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); + + /* + * Initialize driver-wide structures + */ + memset( &megasas_mgmt_info, 0, sizeof(struct megasas_mgmt_info)); + + is_dma64 = (sizeof(dma_addr_t) == 8) ? 1 : 0; + + /* + * Register character device node + */ + rval = register_chrdev(0, "megaraid_sas_ioctl", &megasas_mgmt_fops); + + if (rval < 0) { + printk( KERN_ERR "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_ERR "megasas: PCI hotplug regisration failed \n"); + + return rval; +} + +/** + * megasas_exit : Driver unload entry point + */ +static void __exit +megasas_exit( void ) +{ + pci_unregister_driver( &megasas_pci_driver ); + unregister_chrdev( megasas_mgmt_majorno, "megaraid_sas_ioctl" ); + + printk( KERN_NOTICE "megasas: unloaded the driver\n" ); + + return; +} + +/** + * megasas_probe_one + */ +static int __devinit +megasas_probe_one( struct pci_dev *pdev, const struct pci_device_id *id ) +{ + dma_addr_t instance_h; + struct megasas_instance* instance; + + /* + * Announce PCI information + */ + printk( KERN_INFO "megasas: probe new device " + "%#4.04x:%#4.04x:%#4.04x:%#4.04x: ", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + printk( KERN_INFO "megasas: 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 + */ + if (pci_enable_device(pdev)) { + printk( KERN_ERR "megasas: pci_enable_device failed\n"); + return -ENODEV; + } + + 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) { + + printk( KERN_WARNING "megasas: failed to set 64 bit \ + dma mask; trying 32 bit mask\n" ); + + if (pci_set_dma_mask( pdev, DMA_32BIT_MASK ) != 0) { + printk( KERN_WARNING "megasas: failed to set \ + 32 bit dma mask \n" ); + + goto fail_set_dma_mask; + } + } + } + else { + if (pci_set_dma_mask( pdev, DMA_32BIT_MASK ) != 0) { + + printk( KERN_WARNING "megasas: failed to set 32 bit \ + dma mask \n" ); + goto fail_set_dma_mask; + } + } + + /* + * We allocate DMA memory for instance soft state so that we can + * can directly pass adp->{member variable} to FW to get FW data. + * E.g, product information, configuration data etc. + */ + instance = pci_alloc_consistent( pdev, sizeof(struct megasas_instance), + &instance_h ); + + if (!instance) { + printk(KERN_WARNING "megasas: out of memory!\n" ); + goto fail_alloc_instance; + } + + memset( instance, 0, sizeof(struct megasas_instance) ); + + /* + * Initialize locks and queues + */ + INIT_LIST_HEAD( &instance->cmd_pool ); + + init_waitqueue_head( &instance->int_cmd_wait_q ); + init_waitqueue_head( &instance->abort_cmd_wait_q ); + + spin_lock_init( &instance->cmd_pool_lock ); + spin_lock_init( &instance->lock ); + + instance->host_lock = &instance->lock; + + /* + * Initialize PCI related and misc parameters + */ + instance->phys_addr = instance_h; + instance->pdev = pdev; + instance->unique_id = pdev->bus->number << 8 | pdev->devfn; + instance->init_id = MEGADRV_DEFAULT_INIT_ID; + instance->aen_cmd = NULL; + + /* + * 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_ERR "megasas: Failed to register IRQ\n" ); + goto fail_irq; + } + + MFI_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 + */ + if (megasas_start_aen(instance)) { + printk( KERN_WARNING "megasas: failed to initiate aen\n" ); + } + + /* + * Register with SCSI mid-layer + */ + if (megasas_io_attach( instance )) + goto fail_io_attach; + + return 0; + +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 ); + MFI_DISABLE_INTR(instance->reg_set); + free_irq( instance->pdev->irq, instance ); + + megasas_reset_mfi( instance ); + +fail_irq: +fail_init_mfi: + pci_free_consistent( pdev, sizeof(struct megasas_instance), + instance, instance_h ); +fail_alloc_instance: +fail_set_dma_mask: + pci_disable_device( pdev ); + + return -ENODEV; +} + +/** + * megasas_detach_one + */ +static void +megasas_detach_one( struct pci_dev *pdev ) +{ + int i; + struct Scsi_Host* host; + struct megasas_instance* instance; + dma_addr_t instance_h; + + instance = pci_get_drvdata( pdev ); + + if( !instance ) { + printk( KERN_ERR "megasas: Invalid detach\n" ); + return; + } + + host = instance->host; + instance_h = instance->phys_addr; + + megasas_io_detach( instance ); + + 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 ); + + MFI_DISABLE_INTR(instance->reg_set); + + free_irq( instance->pdev->irq, instance ); + + megasas_reset_mfi( instance ); + + pci_free_consistent( instance->pdev, sizeof(struct megasas_instance), + instance, instance_h ); + scsi_host_put( host ); + + pci_set_drvdata( pdev, NULL ); + + pci_disable_device( pdev ); + + return; +} + +/** + * megasas_shutdown + */ +static void +megasas_shutdown( struct device* device ) +{ + int i; + struct megasas_instance* instance; + + printk( KERN_NOTICE "megasas: shutting down...\n" ); + + for( i = 0; i < megasas_mgmt_info.max_index; i++ ) { + instance = megasas_mgmt_info.instance[i]; + + if (instance) + megasas_shutdown_controller( instance ); + } +} + +/** + * megasas_flush_cache + */ +static void +megasas_flush_cache( struct megasas_instance* instance ) +{ + struct megasas_cmd* cmd; + struct megasas_dcmd_frame* dcmd; + + if (!(cmd = megasas_get_cmd( instance ))) + return; + + dcmd = &cmd->frame->dcmd; + + memset( dcmd->mbox, 0, 12 ); + + 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[0] = MR_FLUSH_CTRL_CACHE | + MR_FLUSH_DISK_CACHE; + + megasas_issue_blocked_cmd( instance, cmd ); + + megasas_return_cmd( instance, cmd ); + + return; +} + +/** + * megasas_shutdown_controller + */ +static void +megasas_shutdown_controller( struct megasas_instance* instance ) +{ + struct megasas_cmd* cmd; + struct megasas_dcmd_frame* dcmd; + + if (!(cmd = megasas_get_cmd( instance ))) + return; + + dcmd = &cmd->frame->dcmd; + + memset( dcmd->mbox, 0, 12 ); + + 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; + dcmd->mbox[0] = MR_ENABLE_DRIVE_SPINDOWN; + + megasas_issue_blocked_cmd( instance, cmd ); + + megasas_return_cmd( instance, cmd ); + + return; +} + +/** + * megasas_init_mfi + */ +static int +megasas_init_mfi( struct megasas_instance* instance ) +{ + uint32_t context_sz; + uint32_t reply_q_sz; + struct megasas_register_set* 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; + dma_addr_t instance_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_ERR "megasas: mem region busy!\n"); + return -EBUSY; + } + + instance->reg_set = (struct megasas_register_set*) ioremap_nocache( + instance->base_addr, 8192); + + if (!instance->reg_set) { + printk( KERN_ERR "megasas: failed to map io mem\n" ); + goto fail_ioremap; + } + + reg_set = instance->reg_set; + + /* + * We expect the FW state to be READY + */ + if (megasas_transition_to_ready(instance->reg_set)) + goto fail_ready_state; + + /* + * Get various operational parameters from status register + */ + instance->max_num_sge = MFI_MAX_SUPP_SGES(reg_set); + instance->max_fw_cmds = MFI_MAX_SUPP_CMDS(reg_set); + + /* + * 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. + */ + context_sz = sizeof(uint32_t); + reply_q_sz = context_sz * (instance->max_fw_cmds + 1); + + instance->reply_queue = pci_alloc_consistent( instance->pdev, + reply_q_sz, &instance->reply_queue_phys_addr ); + + if (!instance->reply_queue) { + printk( KERN_ERR "megasas: Out of DMA memory\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 + */ + 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); + + instance_h = instance->phys_addr; + 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->init_flags = 0; + + initq_info->reply_queue_entries = instance->max_fw_cmds + 1; + initq_info->reply_queue_start_phys_addr_lo = + instance->reply_queue_phys_addr; + initq_info->reply_queue_start_phys_addr_hi = 0; + + initq_info->producer_index_phys_addr_hi = 0; + initq_info->producer_index_phys_addr_lo = instance_h + offsetof( + struct megasas_instance, + producer); + + initq_info->consumer_index_phys_addr_hi = 0; + initq_info->consumer_index_phys_addr_lo = instance_h + offsetof( + struct megasas_instance, + consumer); + + init_frame->cmd = MFI_CMD_INIT; + init_frame->cmd_status = 0xFF; + init_frame->flags = 0; + init_frame->queue_info_new_phys_addr_lo = initq_info_h; + init_frame->queue_info_new_phys_addr_hi = 0; + + init_frame->data_xfer_len = sizeof( struct megasas_init_queue_info); + + /* + * Issue the init frame in polled mode + */ + if (megasas_issue_polled(instance, cmd )) { + printk( KERN_ERR "megasas: failed to init firmware\n" ); + goto fail_fw_init; + } + + megasas_return_cmd( instance, cmd ); + + /* + * Gather misc FW related information + */ + if (!megasas_get_ctrl_info( instance, &ctrl_info )) + instance->max_sectors_per_req = ctrl_info.max_request_size; + else + instance->max_sectors_per_req = instance->max_num_sge * + PAGE_SIZE / 512; + + return 0; + +fail_fw_init: + megasas_return_cmd( instance, cmd ); + + pci_free_consistent( instance->pdev, reply_q_sz, + instance->reply_queue, + instance->reply_queue_phys_addr ); +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_reset_mfi + */ +static void +megasas_reset_mfi( struct megasas_instance* instance ) +{ + uint32_t reply_q_sz = sizeof(uint32_t) * instance->max_fw_cmds; + + pci_free_consistent( instance->pdev, reply_q_sz, + instance->reply_queue, + instance->reply_queue_phys_addr ); + + megasas_free_cmds( instance ); + + iounmap( instance->reg_set ); + + pci_release_regions( instance->pdev ); +} + +/** + * megasas_transition_to_ready : Move the FW to READY state + * + * @reg_set : MFI register set + */ +static int +megasas_transition_to_ready( struct megasas_register_set* reg_set ) +{ + int i; + uint8_t max_wait; + uint32_t fw_state; + uint32_t cur_state; + + fw_state = RD_OB_MSG_0(reg_set) & MFI_STATE_MASK; + + while( fw_state != MFI_STATE_READY ) { + + switch( fw_state ) { + + case MFI_STATE_FAULT: + + printk(KERN_WARNING "megasas: FW in FAULT state!!\n"); + return -ENODEV; + + case MFI_STATE_WAIT_HANDSHAKE: + /* + * Set the CLR bit in IMR0 + */ + printk(KERN_INFO "megasas: FW waiting for HANDSHAKE\n"); + WR_IN_MSG_0( MFI_INIT_CLEAR_HANDSHAKE, reg_set ); + + max_wait = 2; + cur_state = MFI_STATE_WAIT_HANDSHAKE; + break; + + case MFI_STATE_OPERATIONAL: + /* + * Bring it to READY state; assuming max wait 2 secs + */ + MFI_DISABLE_INTR(reg_set); + printk(KERN_INFO "megasas: FW in OPERATIONAL state\n"); + WR_IN_DOORBELL( MFI_INIT_READY, reg_set ); + + max_wait = 10; + cur_state = MFI_STATE_OPERATIONAL; + break; + + case MFI_STATE_UNDEFINED: + /* + * This state should not last for more than 2 seconds + */ + printk(KERN_INFO "FW state undefined\n"); + 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 = 2; + cur_state = MFI_STATE_FW_INIT; + break; + + case MFI_STATE_DEVICE_SCAN: + max_wait = 10; + cur_state = MFI_STATE_DEVICE_SCAN; + break; + + default: + printk(KERN_ERR "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 = RD_OB_MSG_0(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_ERR "FW state hasn't changed in %d secs\n", + max_wait); + return -ENODEV; + } + }; + + return 0; +} + +/** + * megasas_alloc_cmds + */ +static int +megasas_alloc_cmds( struct megasas_instance* instance ) +{ + int i; + uint32_t max_cmd; + struct megasas_cmd* cmd; + + max_cmd = instance->max_fw_cmds; + + /* + * Alloc mem for all cmds in one chunk + */ + instance->cmd_list = kmalloc( sizeof(struct megasas_cmd) * max_cmd, + GFP_KERNEL); + + if (!instance->cmd_list) { + printk( KERN_ERR "megasas: out of memory\n" ); + return -ENOMEM; + } + + memset( instance->cmd_list, 0, sizeof(struct megasas_cmd)*max_cmd ); + + /* + * Slice cmd_list into individual cmds and add to cmd_pool + */ + for( i = 0, cmd = instance->cmd_list; i < max_cmd; i++, cmd++ ) { + cmd->index = i; + 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_ERR "megasas: error creating DMA pool\n"); + megasas_free_cmds( instance ); + } + + return 0; +} + +/** + * megasas_free_cmds + */ +static void +megasas_free_cmds( struct megasas_instance* instance ) +{ + /* First free the MFI frame pool */ + megasas_teardown_frame_pool( instance ); + + /* Free the cmd_list buffer itself */ + if (instance->cmd_list ) { + kfree( instance->cmd_list ); + instance->cmd_list = NULL; + } + + INIT_LIST_HEAD( &instance->cmd_pool ); +} + +/** + * megasas_create_frame_pool + */ +static int +megasas_create_frame_pool( struct megasas_instance* instance ) +{ + int i; + uint32_t max_cmd; + uint32_t sge_sz; + uint32_t sgl_sz; + uint32_t total_sz ; + uint32_t 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 ); + + instance->sense_dma_pool = pci_pool_create( "megasas sense pool", + instance->pdev, 128, 4, 0 ); + + if (!instance->frame_dma_pool || !instance->sense_dma_pool) { + printk( KERN_ERR "megasas: failed to setup DMA pool\n" ); + 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, cmd = instance->cmd_list; i < max_cmd; i++, cmd++ ) { + + 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 ); + + if (!cmd->frame || !cmd->sense) { + printk(KERN_ERR "megasas: pci_pool_alloc failed \n"); + megasas_teardown_frame_pool( instance ); + return -ENOMEM; + } + + cmd->frame->io.context = cmd->index; + } + + return 0; +} + +/** + * megasas_teardown_frame_pool + */ +static void +megasas_teardown_frame_pool( struct megasas_instance* instance ) +{ + int i; + uint32_t max_cmd = instance->max_fw_cmds; + struct megasas_cmd* cmd; + + if (!instance->frame_dma_pool) + return; + + /* + * Return all frames to pool + */ + for( i = 0, cmd = instance->cmd_list; i < max_cmd; i++, cmd++ ) { + 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_start_aen + */ +static int +megasas_start_aen( struct megasas_instance* instance ) +{ + int ret; + struct megasas_evt_log_info eli; + union megasas_evt_class_locale class_locale; + + /* + * Get the latest sequence number from FW + */ + memset( &eli, 0, sizeof(struct megasas_evt_log_info) ); + + if (megasas_get_seq_num( instance, &eli )) { + printk( KERN_WARNING "megasas: failed to get seq num\n" ); + 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; + + ret = megasas_register_aen( instance, eli.newest_seq_num + 1, + class_locale.word ); + if (ret) { + printk( KERN_WARNING "megasas: aen registration failed\n" ); + return -1; + } + + return 0; +} + +/** + * megasas_get_seq_num + */ +static int +megasas_get_seq_num( struct megasas_instance* instance, + struct megasas_evt_log_info* eli) +{ + int i; + int ret = 0; + struct megasas_cmd* cmd; + struct megasas_dcmd_frame* dcmd; + struct megasas_evt_log_info* el_info; + dma_addr_t el_info_h; + + cmd = megasas_get_cmd( instance ); + + if (!cmd) { + printk( KERN_ERR "megasas: failed to get a cmd\n" ); + 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) { + printk( KERN_ERR "megasas: cannot alloc mem for el_info\n" ); + + megasas_return_cmd( instance, cmd ); + return -ENOMEM; + } + + memset( el_info, 0, sizeof(struct megasas_evt_log_info) ); + for( i = 0; i < 12; i++ ) dcmd->mbox[i] = 0; + + 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); + + if (!megasas_issue_blocked_cmd( instance, cmd )) { + ret = 0; + } + else { + ret = -1; + printk( KERN_WARNING "megasas: failed to issue el_info\n" ); + } + + /* + * Copy the data back into callers buffer + */ + if (!ret) + 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 ret; +} + +/** + * megasas_register_aen + */ +static int +megasas_register_aen( struct megasas_instance* instance, uint32_t seq_num, + uint32_t locale ) +{ + struct megasas_cmd* cmd; + struct megasas_dcmd_frame* dcmd; + struct megasas_evt_detail* evt_detail; + dma_addr_t evt_detail_h; + uint32_t* mbox_word; + + cmd = megasas_get_cmd( instance ); + + if (!cmd) { + printk( KERN_ERR "megasas: failed to get a cmd for aen\n" ); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + mbox_word = (uint32_t*) dcmd->mbox; + evt_detail = &instance->evt_detail; + evt_detail_h = instance->phys_addr + + offsetof(struct megasas_instance, evt_detail); + + memset( evt_detail, 0, sizeof(struct megasas_evt_detail)); + + /* + * Prepare DCMD for aen registration + */ + memset( dcmd->mbox, 0, 12 ); + + 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; + mbox_word[0] = seq_num; + mbox_word[1] = locale; + dcmd->sgl.sge32[0].phys_addr = (uint32_t)(evt_detail_h & 0xFFFF); + 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 + */ + WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set ); + + return 0; +} + +/** + * megasas_service_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 ); +} + +/** + * megasas_ioc_attach + */ +static int +megasas_io_attach( struct megasas_instance* instance ) +{ + struct Scsi_Host* host; + + host = scsi_host_alloc(&megasas_template_g, sizeof(void*)); + + if (!host) { + printk( KERN_ERR "megasas: scsi_host_alloc failed\n" ); + return -ENODEV; + } + + SCSIHOST2ADAP(host) = (caddr_t) instance; + instance->host = host; + + /* + * Export parameters required by SCSI mid-layer + */ + scsi_assign_lock( host, instance->host_lock ); + scsi_set_device( host, &instance->pdev->dev ); + + host->irq = instance->pdev->irq; + host->unique_id = instance->unique_id; + host->can_queue = instance->max_fw_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 = MEGADRV_MAX_CMD_PER_LUN; + host->max_channel = MEGADRV_MAX_CHANNELS - 1; + host->max_id = MEGADRV_MAX_DEV_PER_CHANNEL; + host->max_lun = MEGADRV_MAX_LUN; + + /* + * Notify the mid-layer about the new controller + */ + if (scsi_add_host(host, &instance->pdev->dev)) { + + printk( KERN_ERR "megasas: scsi_add_host failed\n" ); + scsi_host_put( host ); + + return -ENODEV; + } + + /* + * Trigger SCSI to scan our drives + */ + scsi_scan_host( host ); + + return 0; +} + +/** + * megasas_io_detach + */ +static void +megasas_io_detach( struct megasas_instance* instance ) +{ + if (instance->host) + scsi_remove_host( instance->host ); +} + +/** + * megasas_abort_handler + */ +static int +megasas_abort_handler( struct scsi_cmnd* scmd ) +{ + printk( KERN_NOTICE "megasas: ABORT -%ld cmd=%x \n", + scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd), + SCP2TARGET(scmd), SCP2LUN(scmd)); + + return FAILED; +} + +/** + * megasas_reset_handler + */ +static int +megasas_reset_handler( struct scsi_cmnd* scmd ) +{ + int i; + uint32_t wait_time = MEGADRV_RESET_WAIT_TIME; + uint32_t outstanding; + struct megasas_instance* instance = SCP2ADAPTER(scmd); + + spin_unlock( instance->host_lock ); + + printk( KERN_NOTICE "megasas: RESET -%ld cmd=%x \n", + scmd->serial_number, scmd->cmnd[0], SCP2CHANNEL(scmd), + SCP2TARGET(scmd), SCP2LUN(scmd)); + + + for( i = 0; i < wait_time; i++ ) { + + outstanding = instance->producer - instance->consumer; + + if (!outstanding) + break; + + if (outstanding < 0) + outstanding = instance->max_fw_cmds + 1 + - instance->consumer; + if (!(i % MEGADRV_RESET_NOTICE_INTERVAL)) { + printk( KERN_NOTICE "megasas: [%2d]waiting for %d \ + commands to complete\n", i, outstanding ); + } + + msleep(1000); + } + + spin_lock( instance->host_lock ); + + if (outstanding) { + printk( KERN_CRIT "megasas: failed to do reset\n"); + return FAILED; + } + + printk( KERN_NOTICE "megasas: reset successful \n" ); + + return SUCCESS; +} + +/** + * megasas_queue_command + */ +static int +megasas_queue_command( struct scsi_cmnd* scmd, void (*done)(struct scsi_cmnd*) ) +{ + uint32_t frame_count; + struct megasas_cmd* cmd; + struct megasas_instance* instance; + uint32_t msg_frame; + + instance = SCP2ADAPTER(scmd); + scmd->scsi_done = done; + scmd->result = 0; + + cmd = megasas_build_cmd( instance, scmd, &frame_count ); + + if (!cmd) { + done(scmd); + return 0; + } + + cmd->scmd = scmd; + + /* + * Issue the command to the FW + */ + msg_frame = (cmd->frame_phys_addr >> 3) | (cmd->frame_count - 1); + + WR_IN_QPORT( msg_frame, instance->reg_set ); + + return 0; +} + +/** + * megasas_issue_polled + */ +static int +megasas_issue_polled(struct megasas_instance* instance, struct megasas_cmd* cmd) +{ + int i; + uint32_t msecs = MFI_POLL_TIMEOUT_SECS * 1000; + + struct megasas_header* frame_hdr = (struct megasas_header*)cmd->frame; + + frame_hdr->cmd_status = 0xFF; + frame_hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; + + /* + * Issue the frame using inbound queue port + */ + WR_IN_QPORT( (cmd->frame_phys_addr >> 3), 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 + */ +static int +megasas_issue_blocked_cmd( struct megasas_instance* instance, + struct megasas_cmd* cmd ) +{ + uint32_t msg_frame; + + cmd->cmd_status = ENODATA; + msg_frame = (cmd->frame_phys_addr >> 3); + + WR_IN_QPORT( msg_frame, instance->reg_set ); + + wait_event( instance->int_cmd_wait_q, (cmd->cmd_status != ENODATA)); + + return 0; +} + +/** + * megasas_sync_abort_cmd + */ +static int +megasas_sync_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; + + WR_IN_QPORT( (cmd->frame_phys_addr >> 3), instance->reg_set ); + + /* + * Wait for this cmd to complete + */ + cmd->sync_cmd = 1; + wait_event( instance->abort_cmd_wait_q, (cmd->cmd_status != 0xFF)); + + megasas_return_cmd( instance, cmd ); + + return 0; +} + +/** + * megasas_complete_abort + */ +static void +megasas_complete_abort( struct megasas_instance* instance, + struct megasas_cmd* cmd ) +{ + if (cmd->sync_cmd) { + cmd->sync_cmd = 0; + wake_up( &instance->abort_cmd_wait_q ); + } + + return; +} + +/** + * megasas_build_cmd + */ +static struct megasas_cmd* +megasas_build_cmd( struct megasas_instance* instance, struct scsi_cmnd* scp, + int* frame_count ) +{ + uint32_t logical_cmd; + struct megasas_cmd* cmd; + + /* + * Find out if this is logical or physical drive command. + */ + logical_cmd = MEGADRV_IS_LOGICAL(scp); + + /* + * Logical drive command + */ + if (logical_cmd) { + + if (SCP2TARGET(scp) >= MEGADRV_MAX_LD) { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + switch(scp->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: + /* + * Fail for LUN > 0 + */ + if (SCP2LUN(scp)) { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + if (!(cmd = megasas_get_cmd(instance))) { + scp->result = DID_ERROR << 16; + return NULL; + } + + *frame_count = megasas_build_ldio(instance, scp, cmd); + + if (! (*frame_count) ) { + printk( "megasas: build_ldio error\n" ); + + megasas_return_cmd( instance, cmd ); + + return NULL; + } + + return cmd; + + case REPORT_LUNS: + scp->result = DID_ERROR << 16; + return NULL; + + default: + /* + * Fail for LUN > 0 + */ + if (SCP2LUN(scp)) { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + if (!(cmd = megasas_get_cmd( instance ))) { + scp->result = DID_ERROR << 16; + return NULL; + } + + *frame_count = megasas_build_dcdb(instance, scp, cmd); + + if (! (*frame_count) ) { + printk( "megasas: build_dcdb error\n" ); + + megasas_return_cmd( instance, cmd ); + + return NULL; + } + + return cmd; + } + } + else { + scp->result = DID_BAD_TARGET << 16; + return NULL; + } + + return NULL; +} + +/** + * megasas_build_dcdb + */ +static int +megasas_build_dcdb( struct megasas_instance* instance, struct scsi_cmnd* scp, + struct megasas_cmd* cmd ) +{ + uint32_t sge_sz; + int sge_bytes; + uint32_t is_logical; + uint32_t device_id; + uint16_t flags = 0; + struct megasas_pthru_frame* pthru; + + is_logical = MEGADRV_IS_LOGICAL(scp); + device_id = MEGADRV_DEV_INDEX(instance, scp); + pthru = (struct megasas_pthru_frame_t*) 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 = SCP2LUN(scp); + 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; + + 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 + */ +static int +megasas_build_ldio( struct megasas_instance* instance, struct scsi_cmnd* scp, + struct megasas_cmd* cmd ) +{ + uint32_t sge_sz; + int sge_bytes; + uint32_t device_id; + uint8_t sc = scp->cmnd[0]; + uint16_t flags = 0; + + struct megasas_io_frame* ldio; + + device_id = MEGADRV_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 = (uint32_t)scp->cmnd[4]; + ldio->start_lba_lo = ((uint32_t)scp->cmnd[1] << 16)| + ((uint32_t)scp->cmnd[2] << 8) | + (uint32_t)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 = (uint32_t)scp->cmnd[8] | + ((uint32_t)scp->cmnd[7] << 8); + ldio->start_lba_lo = ((uint32_t)scp->cmnd[2] << 24)| + ((uint32_t)scp->cmnd[3] << 16)| + ((uint32_t)scp->cmnd[4] << 8)| + (uint32_t)scp->cmnd[5]; + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + else if (scp->cmd_len == 12) { + ldio->lba_count = ((uint32_t)scp->cmnd[6] << 24)| + ((uint32_t)scp->cmnd[7] << 16)| + ((uint32_t)scp->cmnd[8] << 8) | + (uint32_t)scp->cmnd[9]; + + ldio->start_lba_lo = ((uint32_t)scp->cmnd[2] << 24)| + ((uint32_t)scp->cmnd[3] << 16)| + ((uint32_t)scp->cmnd[4] << 8) | + (uint32_t)scp->cmnd[5]; + } + + /* + * 16-byte READ(0x88) or WRITE(0x8A) cdb + */ + else if (scp->cmd_len == 16) { + ldio->lba_count = ((uint32_t)scp->cmnd[10] << 24)| + ((uint32_t)scp->cmnd[11] << 16)| + ((uint32_t)scp->cmnd[12] << 8) | + (uint32_t)scp->cmnd[13]; + + ldio->start_lba_lo = ((uint32_t)scp->cmnd[6] << 24)| + ((uint32_t)scp->cmnd[7] << 16)| + ((uint32_t)scp->cmnd[8] << 8) | + (uint32_t)scp->cmnd[9]; + + ldio->start_lba_hi = ((uint32_t)scp->cmnd[2] << 24)| + ((uint32_t)scp->cmnd[3] << 16)| + ((uint32_t)scp->cmnd[4] << 8) | + (uint32_t)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_isr + */ +static irqreturn_t +megasas_isr(int irq, void *devp, struct pt_regs *regs) +{ + uint32_t status; + int32_t completed; + uint32_t producer; + uint32_t consumer; + uint32_t context; + + struct megasas_instance* instance; + struct megasas_register_set* reg_set; + struct megasas_cmd* cmd; + + instance = (struct megasas_instance*) devp; + reg_set = instance->reg_set; + + /* + * Check if it is our interrupt + */ + status = RD_OB_INTR_STATUS(reg_set); + + if (! (status & MFI_OB_INTR_STATUS_MASK)) { + return IRQ_NONE; + } + + /* + * Clear the interrupt by writing back the same value + */ + WR_OB_INTR_STATUS(status, reg_set); + + producer = instance->producer; + consumer = instance->consumer; + completed = producer - consumer; + + if (completed < 0) { + completed = instance->max_fw_cmds + 1 - consumer; + } + + while( completed-- ) { + context = instance->reply_queue[consumer]; + + cmd = &(instance->cmd_list)[context]; + + megasas_complete_cmd( instance, cmd ); + + consumer++; + if (consumer > instance->max_fw_cmds + 1) { + consumer = 0; + } + } + + wmb(); + instance->consumer = producer; + + return IRQ_HANDLED; +} + +static inline void +megasas_sync_buffers(struct megasas_instance* instance, struct megasas_cmd* cmd) +{ + dma_addr_t buf_h; + uint8_t 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_page( instance->pdev, buf_h, cmd->scmd->request_bufflen, + cmd->scmd->sc_data_direction ); + return; +} + +/** + * megasas_complete_cmd + */ +static void +megasas_complete_cmd(struct megasas_instance* instance, struct megasas_cmd* cmd) +{ + struct megasas_header* hdr = &cmd->frame->hdr; + + switch( hdr->cmd ) { + + case MFI_CMD_LD_READ: + case MFI_CMD_LD_WRITE: + case MFI_CMD_LD_SCSI_IO: + case MFI_CMD_PD_SCSI_IO: + + switch (hdr->cmd_status) { + + case MFI_STAT_OK: + cmd->scmd->result = DID_OK << 16; + break; + + case MFI_STAT_SCSI_DONE_WITH_ERROR: + cmd->scmd->result = hdr->scsi_status << 1 | + hdr->cmd_status << 8; + + if (hdr->scsi_status == 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 | + DID_OK << 16; + } + else + cmd->scmd->result |= DID_ERROR << 16; + + break; + + case MFI_STAT_DEVICE_NOT_FOUND: + cmd->scmd->result = DID_BAD_TARGET << 16; + break; + + default: + printk( KERN_NOTICE "megasas: unhandled status %#x\n", + hdr->cmd_status); + cmd->scmd->result = DID_ERROR << 16; + } + + spin_lock( instance->host_lock ); + cmd->scmd->scsi_done( cmd->scmd ); + spin_unlock( instance->host_lock ); + + megasas_return_cmd( instance, cmd ); + + megasas_sync_buffers( instance, cmd ); + break; + + 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(KERN_ERR "megasas isr: unknown cmd 0x%x\n completed!!\n", hdr->cmd ); + break; + } +} + +/** + * megasas_complete_int_cmd + */ +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_mgmt_open + */ +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 + */ +static int +megasas_mgmt_release( struct inode* inode, struct file* filep ) +{ + return 0; +} + +/** + * megasas_mgmt_fasync + */ +static int +megasas_mgmt_fasync( int fd, struct file* filep, int mode ) +{ + int rc; + + down( &megasas_async_queue_mutex ); + + rc = fasync_helper( fd, filep, mode, &megasas_async_queue ); + + up( &megasas_async_queue_mutex ); + + if (rc >0) + return 0; + + printk( KERN_WARNING "megasas: fasync_helper failed %d\n", rc ); + + return rc; +} + +/** + * megasas_mgmt_ioctl + */ +static int +megasas_mgmt_ioctl( struct inode* inode, struct file* filep, + unsigned int cmd, unsigned long arg ) +{ + int i; + int j; + int rc; + uint8_t fw_status; + struct iocpacket uioc; + void __user* argp; + void __user* udata_addr; + uint8_t user_64bit_sgl = 0; + uint32_t opcode; + uint32_t locale; + uint32_t seq_num; + uint32_t* mbox_word; + struct megasas_cmd* old_cmd; + + struct megasas_instance* instance; + struct megasas_dcmd_frame* kdcmd; + struct megasas_dcmd_frame __user* udcmd; + struct megasas_drv_ver dv; + + argp = (void __user*) arg; + + if (copy_from_user( &uioc, argp, sizeof(struct iocpacket))) { + printk( KERN_WARNING "megasas: copy_from_user failed\n" ); + return -EINVAL; + } + + if (strncmp(uioc.signature, IOC_SIGNATURE, strlen(IOC_SIGNATURE)) !=0){ + printk( KERN_WARNING "megasas: invalid ioctl signature\n" ); + return -EINVAL; + } + + if (uioc.version != 0) { + printk( KERN_WARNING "megasas: invalid ioctl version %d\n", + uioc.version ); + return -EINVAL; + } + + instance = NULL; + kdcmd = (struct megasas_dcmd_frame*) uioc.frame; + udcmd = (struct megasas_dcmd_frame*) + (((struct iocpacket*)argp)->frame); + + /* + * Find out if user has used 32 or 64 bit SGL + */ + if (kdcmd->flags & MFI_FRAME_SGL64 ) + user_64bit_sgl = 1; + + if (!user_64bit_sgl) + udata_addr = (void __user *) kdcmd->sgl.sge32[0].phys_addr; + else + udata_addr = (void __user *) (unsigned long) + kdcmd->sgl.sge64[0].phys_addr; + + i = ((uioc.controller_id & 0xF0) >> 4) - 1; + + if (i < megasas_mgmt_info.max_index) + instance = megasas_mgmt_info.instance[i]; + else + instance = NULL; + + if ((uioc.control_code == MR_DRIVER_IOCTL_LINUX) || + (uioc.control_code == MR_DRIVER_IOCTL_COMMON)) { + /* + * If MR_DRIVER_IOCTL_LINUX or MR_DRIVER_IOCTL_COMMON + * look at dcmd->opcode for the actual operation + */ + opcode = kdcmd->opcode; + } + else { + /* FW Command */ + opcode = uioc.control_code; + } + + switch (opcode) { + + case MR_DRIVER_IOCTL_DRIVER_VERSION: + + megasas_fill_drv_ver( &dv ); + + if (copy_to_user(udata_addr, &dv, sizeof(dv))) { + printk( KERN_WARNING "megasas: copy_to_user failed\n" ); + return -EFAULT; + } + + rc = 0; + fw_status = MFI_STAT_OK; + + if (copy_to_user( &udcmd->cmd_status, &fw_status, + sizeof(uint8_t))) { + rc = -EFAULT; + printk( KERN_WARNING "megasas: copy_to_user failed\n" ); + } + + break; + + case MR_LINUX_GET_ADAPTER_COUNT: + + if (copy_to_user(udata_addr, &megasas_mgmt_info.count, + sizeof(uint16_t))) { + printk( KERN_WARNING "megasas: copy_to_user failed\n" ); + return -EFAULT; + } + + rc = 0; + fw_status = MFI_STAT_OK; + + if (copy_to_user( &udcmd->cmd_status, &fw_status, + sizeof(uint8_t))) { + rc = -EFAULT; + printk( KERN_WARNING "megasas: copy_to_user failed\n" ); + } + + break; + + case MR_LINUX_GET_ADAPTER_MAP: + /* + * README: encrypting logic for adapter map + * The adpater field size allows up to 16-bit adapter number, + * which translates into 65536 possible adapters, which + * obviously is too much. So we reserve the lower 4-bits to + * put the coding nibble (0xF) and add 1 to the adapter + * number. Applications shall have (12-bits - 1) to provide + * the adapter number. This still translates in 4095 possible + * adapters, which should be sufficient :-) + */ + memset(megasas_mgmt_info.map, 0, + sizeof(uint16_t) * MAX_MGMT_ADAPTERS); + + j = 0; + for (i = 0; i < megasas_mgmt_info.max_index; i++) { + if (megasas_mgmt_info.instance[i]) { + megasas_mgmt_info.map[j++] = + ((i + 1) << 4) | 0xF; + } + } + + if ((j) && (copy_to_user(udata_addr, megasas_mgmt_info.map, + sizeof(uint16_t) * j))) { + + printk(KERN_ERR "megasas:invalid uaddr for hba map\n" ); + return -EFAULT; + } + + fw_status = MFI_STAT_OK; + rc = 0; + + if (copy_to_user( &udcmd->cmd_status, &fw_status, + sizeof(uint8_t))) { + rc = -EFAULT; + printk( KERN_ERR "megasas: invalid uaddr\n" ); + } + + break; + + case MR_LINUX_GET_AEN: + + if (!instance) { + printk( KERN_WARNING "megasas: invalid instance \n" ); + return -ENODEV; + } + + mbox_word = (uint32_t*) kdcmd->mbox; + seq_num = mbox_word[0]; + locale = mbox_word[1]; + old_cmd = instance->aen_cmd; + + if (old_cmd) { + old_cmd->abort_aen = 1; + rc = megasas_sync_abort_cmd(instance, old_cmd); + + if (rc) { + printk(KERN_WARNING "megasas: failed to abort \ + prev aen\n" ); + break; + } + } + + rc = megasas_register_aen( instance, seq_num, locale ); + + break; + + case IOC_CMD_FIRMWARE: + + if (!instance) { + printk( KERN_WARNING "megasas: invalid instance \n" ); + return -ENODEV; + } + + rc = megasas_mgmt_fw_ioctl( instance, argp ); + + break; + + default: + printk( KERN_WARNING "megasas: unsupported ioctl %d\n", + uioc.control_code ); + return -ENOTTY; + } + + return rc; +} + +/** + * megasas_mgmt_fw_ioctl + */ +static int +megasas_mgmt_fw_ioctl( struct megasas_instance* instance, void __user* argp ) +{ + struct iocpacket uioc; + struct megasas_header* hdr; + struct megasas_cmd* cmd; + + if (copy_from_user(&uioc, argp, sizeof(struct iocpacket))) { + printk( KERN_ERR "megasas ioctl: copy_from_user failed\n" ); + return -EFAULT; + } + + cmd = megasas_get_cmd( instance ); + + if (!cmd) { + printk( KERN_WARNING "megasas: failed to get a cmd packet\n" ); + return -ENOMEM; + } + + hdr = (struct megasas_header*) &uioc.frame; + + switch( hdr->cmd ) { + + case MFI_CMD_DCMD: + return megasas_mgmt_fw_dcmd(instance, &uioc, argp, cmd); + + case MFI_CMD_SMP: + return megasas_mgmt_fw_smp(instance, &uioc, argp, cmd); + + default: + printk( KERN_WARNING "megasas: invalid fw ioctl \n" ); + return -EINVAL; + } + + megasas_return_cmd( instance, cmd ); + return 0; +} + +/** + * megasas_mgmt_fw_dcmd + */ +static int +megasas_mgmt_fw_dcmd( struct megasas_instance* instance, struct iocpacket* uioc, + void __user* argp, struct megasas_cmd* cmd ) +{ + int rc = 0; + void __user* ubuff; + struct megasas_dcmd_frame* kdcmd; + struct megasas_dcmd_frame __user* udcmd; + struct megasas_dcmd_frame* cmd_dcmd; + caddr_t kbuff; + dma_addr_t kbuff_h; + uint32_t xferlen; + uint8_t user_64bit_sgl = 0; + + cmd_dcmd = &cmd->frame->dcmd; + kdcmd = (struct megasas_dcmd_frame_t*) &uioc->frame; + udcmd = (struct megasas_dcmd_frame_t*) + (((struct iocpacket*)argp)->frame); + + if (kdcmd->flags & MFI_FRAME_SGL64 ) + user_64bit_sgl = 1; + + if (!user_64bit_sgl) { + xferlen = kdcmd->sgl.sge32[0].length; + ubuff = (void __user*) udcmd->sgl.sge32[0].phys_addr; + } + else { + xferlen = kdcmd->sgl.sge64[0].length; + ubuff = (void __user*) (ulong) udcmd->sgl.sge64[0].phys_addr; + } + + /* + * Allocate internal buffer for data transfer + */ + if (xferlen) + kbuff = pci_alloc_consistent(instance->pdev, xferlen, &kbuff_h); + else + kbuff = NULL; + + if (xferlen && !kbuff) { + printk( KERN_ERR "megasas: memalloc failed for int buff \n" ); + return -ENOMEM; + } + + if (xferlen && (kdcmd->flags & MFI_FRAME_DIR_WRITE)) { + + if (copy_from_user(kbuff, ubuff, xferlen)) { + printk( KERN_ERR "megasas: cp_from_usr failed\n" ); + return -EFAULT; + } + } + + cmd_dcmd->cmd = kdcmd->cmd; + cmd_dcmd->cmd_status = kdcmd->cmd_status; + cmd_dcmd->sge_count = kdcmd->sge_count; + cmd_dcmd->timeout = kdcmd->timeout; + cmd_dcmd->data_xfer_len = kdcmd->data_xfer_len; + cmd_dcmd->opcode = kdcmd->opcode; + + memcpy( cmd_dcmd->mbox, kdcmd->mbox, 12 ); + + if (!user_64bit_sgl) { + cmd_dcmd->flags = kdcmd->flags; + cmd_dcmd->sgl.sge32[0].length = kdcmd->sgl.sge32[0].length; + cmd_dcmd->sgl.sge32[0].phys_addr= kbuff_h; + } + else { + cmd_dcmd->flags = kdcmd->flags |MFI_FRAME_SGL64; + cmd_dcmd->sgl.sge64[0].length = kdcmd->sgl.sge64[0].length; + cmd_dcmd->sgl.sge64[0].phys_addr= kbuff_h; + } + + if (!megasas_issue_blocked_cmd( instance, cmd )) { + + if (xferlen && (kdcmd->flags & MFI_FRAME_DIR_READ)) { + + if (copy_to_user( ubuff, kbuff, xferlen)) { + + printk(KERN_ERR "megasas: cp_to_usr failed\n"); + rc = -EFAULT; + goto exit_label; + } + } + + if (copy_to_user( &udcmd->cmd_status, &cmd_dcmd->cmd_status, + sizeof(uint8_t))) { + printk(KERN_ERR "megasas: cp_to_usr failed\n"); + rc = -EFAULT; + goto exit_label; + } + } + else { + printk( KERN_WARNING "megasas: fw_ioctl failed\n" ); + } + +exit_label: + pci_free_consistent( instance->pdev, xferlen, kbuff, kbuff_h ); + return rc; +} + +/** + * megasas_mgmt_fw_smp + */ +static int +megasas_mgmt_fw_smp( struct megasas_instance* instance, struct iocpacket* uioc, + void __user* argp, struct megasas_cmd* cmd ) +{ + int rc = 0; + struct megasas_smp_frame* ksmp; + struct megasas_smp_frame __user* usmp; + struct megasas_smp_frame* cmd_smp; + + caddr_t kreq; + caddr_t kresp; + dma_addr_t kreq_h; + dma_addr_t kresp_h; + void __user* ureq; + void __user* uresp; + uint32_t req_len; + uint32_t resp_len; + + uint8_t user_64bit_sgl = 0; + + cmd_smp = &cmd->frame->smp; + ksmp = (struct megasas_smp_frame*) &uioc->frame; + usmp = (struct megasas_smp_frame_t*) + (((struct iocpacket*)argp)->frame); + + if (ksmp->flags & MFI_FRAME_SGL64 ) + user_64bit_sgl = 1; + + if (!user_64bit_sgl) { + req_len = ksmp->request_sgl.sge32[0].length; + resp_len = ksmp->response_sgl.sge32[0].length; + + ureq = (void __user*) usmp->request_sgl.sge32[0].phys_addr; + uresp = (void __user*) usmp->response_sgl.sge32[0].phys_addr; + } + else { + req_len = ksmp->request_sgl.sge64[0].length; + resp_len = ksmp->response_sgl.sge64[0].length; + + ureq = (void __user*) (ulong) + usmp->request_sgl.sge64[0].phys_addr; + uresp = (void __user*) (ulong) + usmp->response_sgl.sge64[0].phys_addr; + } + + if (!req_len || !resp_len) { + printk( KERN_WARNING "megasas: invalid req/resp lenghth\n" ); + return -EINVAL; + } + + /* + * Allocate kernel buffers for SMP request and response + */ + + kreq = NULL; + kresp = NULL; + + if (!(kreq = pci_alloc_consistent(instance->pdev, req_len, &kreq_h))){ + + printk( KERN_ERR "megasas: memalloc err for req\n" ); + rc = -ENOMEM; + goto exit_label; + } + + if(!(kresp = pci_alloc_consistent(instance->pdev, resp_len, &kresp_h))){ + printk( KERN_ERR "megasas: memalloc err for resp\n" ); + rc = -ENOMEM; + goto exit_label; + } + + if (copy_from_user(kreq, ureq, req_len)) { + printk( KERN_ERR "megasas: copy_from_user failed\n" ); + rc = -EFAULT; + goto exit_label; + } + + memcpy (cmd_smp, ksmp, MEGAMFI_FRAME_SIZE ); + cmd_smp->context = cmd->index; + + if (!user_64bit_sgl) { + cmd_smp->flags = ksmp->flags; + cmd_smp->request_sgl.sge32[0].length = req_len; + cmd_smp->request_sgl.sge32[0].phys_addr = kreq_h; + cmd_smp->response_sgl.sge32[0].length = resp_len; + cmd_smp->response_sgl.sge32[0].phys_addr = kresp_h; + } + else { + cmd_smp->flags = ksmp->flags | + MFI_FRAME_SGL64; + cmd_smp->request_sgl.sge64[0].length = req_len; + cmd_smp->request_sgl.sge64[0].phys_addr = kreq_h; + cmd_smp->response_sgl.sge64[0].length = resp_len; + cmd_smp->response_sgl.sge64[0].phys_addr = kresp_h; + } + + if (!megasas_issue_blocked_cmd( instance, cmd )) { + + if (copy_to_user( uresp, kresp, resp_len)) { + printk( KERN_ERR "megasas: copy_to_user failed\n" ); + rc = -EFAULT; + goto exit_label; + } + + if (copy_to_user( &usmp->cmd_status, &cmd_smp->cmd_status, + sizeof(uint8_t))) { + printk( KERN_ERR "megasas: copy_to_user failed\n" ); + rc = -EFAULT; + goto exit_label; + } + } + else { + printk( KERN_WARNING "megasas: smp failed\n" ); + } + +exit_label: + + if (kreq) + pci_free_consistent(instance->pdev, req_len, kreq, kreq_h); + if (kresp) + pci_free_consistent(instance->pdev, resp_len, kresp, kresp_h); + + return rc; +} + +/** + * megasas_sysfs_show_app_hndl + */ +static ssize_t +megasas_sysfs_show_app_hndl( struct class_device* cdev, char* buf ) +{ + int i; + uint32_t hndl = 0; + struct Scsi_Host* shost; + struct megasas_instance* instance; + + shost = class_to_shost( cdev ); + instance = (struct megasas_instance*)SCSIHOST2ADAP( shost ); + + for (i = 0; i < megasas_mgmt_info.max_index; i++ ) { + + if (instance == megasas_mgmt_info.instance[i]) + hndl = ((i + 1) << 4) | 0xF; + } + + return snprintf( buf, 8, "%u\n", hndl ); +} + +/** + * megasas_fill_drv_ver + */ +static void +megasas_fill_drv_ver( struct megasas_drv_ver* dv ) +{ + memset( dv, 0, sizeof(struct megasas_drv_ver) ); + + memcpy(dv->signature, "$LSI LOGIC$", strlen("$LSI LOGIC$") ); + memcpy(dv->os_name, "Linux", strlen("Linux") ); + memcpy(dv->os_ver, "Ver Indpndt", strlen("ver indpndt") ); + memcpy(dv->drv_name, "megaraid_sas", strlen("megaraid_sas") ); + memcpy(dv->drv_ver, MEGASAS_VERSION,strlen(MEGASAS_VERSION) ); + memcpy(dv->drv_rel_date,MEGASAS_RELDATE,strlen(MEGASAS_RELDATE) ); +} + +/** + * megasas_get_controller_info + */ +static int +megasas_get_ctrl_info( struct megasas_instance* instance, + struct megasas_ctrl_info* ctrl_info ) +{ + int i; + int ret = 0; + struct megasas_cmd* cmd; + struct megasas_dcmd_frame* dcmd; + struct megasas_ctrl_info* ci; + dma_addr_t ci_h; + + cmd = megasas_get_cmd( instance ); + + if (!cmd) { + printk( KERN_WARNING "Failed to get a cmd for ctrl info\n" ); + return -ENOMEM; + } + + dcmd = &cmd->frame->dcmd; + + ci = pci_alloc_consistent( instance->pdev, + sizeof(struct megasas_ctrl_info), &ci_h ); + + if (!ci) { + printk( KERN_WARNING "Failed to alloc mem for ctrl info\n" ); + megasas_return_cmd( instance, cmd ); + return -ENOMEM; + } + + memset( ci, 0, sizeof(struct megasas_ctrl_info)); + for( i = 0; i < 12; i++ ) dcmd->mbox[i] = 0; + + 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 { + printk( KERN_WARNING "Ctrl info failed\n" ); + ret = -1; + } + + pci_free_consistent( instance->pdev, sizeof(struct megasas_ctrl_info), + ci, ci_h ); + + megasas_return_cmd( instance, cmd ); + return ret; +} + +module_init( megasas_init ); +module_exit( megasas_exit ); + +/* vim: set ts=8 sw=8 tw=78 ai si: */ diff -puN /dev/null drivers/scsi/megaraid/megaraid_sas.h --- /dev/null Thu Apr 11 07:25:15 2002 +++ 25-akpm/drivers/scsi/megaraid/megaraid_sas.h Sun Mar 6 16:24:15 2005 @@ -0,0 +1,1118 @@ +/* + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * MegaRAID SAS Driver meta data + */ +#define MEGASAS_VERSION "00.00.01.00" +#define MEGASAS_RELDATE "Mar 05, 2005" +#define MEGASAS_EXT_VERSION "" + +/** + * MegaRAID SAS supported controllers + */ +#define PCI_DEVICE_LSI_1064 0x0411 +#define PCI_SUBVENDOR_x0000 0x0000 +#define PCI_SUBVENDOR_x1000 0x1000 +#define PCI_SUBSYSTEM_x0000 0x0000 +#define PCI_SUBSYSTEM_x0001 0x0001 +#define PCI_SUBSYSTEM_x0002 0x0002 +#define PCI_SUBSYSTEM_x1001 0x1001 + +#define PCI_DEVICE_ID_PERC5 0x0015 +#define PCI_SUBSYSTEM_PERC5H 0x1F01 +#define PCI_SUBSYSTEM_PERC5L 0x1F02 +#define PCI_SUBSYSTEM_PERC5I 0x1F03 + +#define PCI_SUB_DEVICEID_FSC 0x1081 +#define PCI_SUB_VENDORID_FSC 0x1734 +/* + * ===================================== + * MegaRAID SAS MFI firmware definitions + * ===================================== + */ + +/** + * 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_DEVICE_SCAN 0x80000000 +#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 + +/** + * 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_INVALID_STATUS = 0xFF + +}; + +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 { + + uint16_t seq_num; + uint16_t pred_fail_poll_interval; + uint16_t intr_throttle_count; + uint16_t intr_throttle_timeouts; + + uint8_t rebuild_rate; + uint8_t patrol_read_rate; + uint8_t bgi_rate; + uint8_t cc_rate; + uint8_t recon_rate; + + uint8_t cache_flush_interval; + + uint8_t spinup_drv_count; + uint8_t spinup_delay; + + uint8_t cluster_enable; + uint8_t coercion_mode; + uint8_t disk_write_cache_disable; + uint8_t alarm_enable; + + uint8_t reserved[44]; + +} __attribute__ ((packed)); + +/* + * SAS controller information + */ +struct megasas_ctrl_info { + + /* + * PCI device information + */ + struct { + + uint16_t vendor_id; + uint16_t device_id; + uint16_t sub_vendor_id; + uint16_t sub_device_id; + uint8_t reserved[24]; + + } __attribute__ ((packed)) pci; + + /* + * Host interface information + */ + struct { + + uint8_t PCIX : 1; + uint8_t PCIE : 1; + uint8_t iSCSI : 1; + uint8_t SAS_3G : 1; + uint8_t reserved_0 : 4; + uint8_t reserved_1[6]; + uint8_t port_count; + uint64_t port_addr[8]; + + } __attribute__ ((packed)) host_interface; + + /* + * Device (backend) interface information + */ + struct { + + uint8_t SPI : 1; + uint8_t SAS_3G : 1; + uint8_t SATA_1_5G : 1; + uint8_t SATA_3G : 1; + uint8_t reserved_0 : 4; + uint8_t reserved_1[6]; + uint8_t port_count; + uint64_t port_addr[8]; + + } __attribute__ ((packed)) device_interface; + + /* + * List of components residing in flash. All str are null terminated + */ + uint32_t image_check_word; + uint32_t 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 + */ + uint32_t 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]; + + uint8_t max_arms; + uint8_t max_spans; + uint8_t max_arrays; + uint8_t max_lds; + + char product_name[80]; + char serial_no[32]; + + /* + * Other physical/controller/operation information. Indicates the + * presence of the hardware + */ + struct { + + uint32_t bbu : 1; + uint32_t alarm : 1; + uint32_t nvram : 1; + uint32_t uart : 1; + uint32_t reserved : 28; + + } __attribute__ ((packed)) hw_present; + + uint32_t current_fw_time; + + /* + * Maximum data transfer sizes + */ + uint16_t max_concurrent_cmds; + uint16_t max_sge_count; + uint32_t max_request_size; + + /* + * Logical and physical device counts + */ + uint16_t ld_present_count; + uint16_t ld_degraded_count; + uint16_t ld_offline_count; + + uint16_t pd_present_count; + uint16_t pd_disk_present_count; + uint16_t pd_disk_pred_failure_count; + uint16_t pd_disk_failed_count; + + /* + * Memory size information + */ + uint16_t nvram_size; + uint16_t memory_size; + uint16_t flash_size; + + /* + * Error counters + */ + uint16_t mem_correctable_error_count; + uint16_t mem_uncorrectable_error_count; + + /* + * Cluster information + */ + uint8_t cluster_permitted; + uint8_t cluster_active; + uint8_t reserved_1[2]; + + /* + * Controller capabilities structures + */ + struct { + + uint32_t raid_level_0 : 1; + uint32_t raid_level_1 : 1; + uint32_t raid_level_5 : 1; + uint32_t raid_level_1E : 1; + uint32_t reserved : 28; + + } __attribute__ ((packed)) raid_levels; + + struct { + + uint32_t rbld_rate : 1; + uint32_t cc_rate : 1; + uint32_t bgi_rate : 1; + uint32_t recon_rate : 1; + uint32_t patrol_rate : 1; + uint32_t alarm_control : 1; + uint32_t cluster_supported : 1; + uint32_t bbu : 1; + uint32_t spanning_allowed : 1; + uint32_t dedicated_hotspares : 1; + uint32_t revertible_hotspares : 1; + uint32_t foreign_config_import : 1; + uint32_t self_diagnostic : 1; + uint32_t reserved : 19; + + } __attribute__ ((packed)) adapter_operations; + + struct { + + uint32_t read_policy : 1; + uint32_t write_policy : 1; + uint32_t io_policy : 1; + uint32_t access_policy : 1; + uint32_t reserved : 28; + + } __attribute__ ((packed)) ld_operations; + + struct { + + uint8_t min; + uint8_t max; + uint8_t reserved[2]; + + } __attribute__ ((packed)) stripe_size_operations; + + struct { + + uint32_t force_online : 1; + uint32_t force_offline : 1; + uint32_t force_rebuild : 1; + uint32_t reserved : 29; + + } __attribute__ ((packed)) pd_operations; + + struct { + + uint32_t ctrl_supports_sas : 1; + uint32_t ctrl_supports_sata : 1; + uint32_t allow_mix_in_encl : 1; + uint32_t allow_mix_in_ld : 1; + uint32_t allow_sata_in_cluster : 1; + uint32_t reserved : 27; + + } __attribute__ ((packed)) pd_mix_support; + + /* + * Include the controller properties (changeable items) + */ + uint8_t reserved_2[12]; + struct megasas_ctrl_prop properties; + + uint8_t pad[0x800 - 0x640]; + +} __attribute__ ((packed)); + +/* + * =============================== + * MegaRAID SAS driver definitions + * =============================== + */ + + +#define MEGADRV_MAX_NUM_CMD 1024 + +#define MEGADRV_MAX_PD_CHANNELS 2 +#define MEGADRV_MAX_LD_CHANNELS 2 +#define MEGADRV_MAX_CHANNELS (MEGADRV_MAX_PD_CHANNELS + \ + MEGADRV_MAX_LD_CHANNELS) +#define MEGADRV_MAX_DEV_PER_CHANNEL 128 +#define MEGADRV_DEFAULT_INIT_ID -1 +#define MEGADRV_MAX_CMD_PER_LUN 1000 +#define MEGADRV_MAX_LUN 8 +#define MEGADRV_MAX_LD 64 + +#define MEGADRV_RESET_WAIT_TIME 300 +#define MEGADRV_RESET_NOTICE_INTERVAL 5 + +/* + * All MFI register set macros accept megasas_register_set* + */ +#define RD_OB_MSG_0(regs) readl((void*)(&(regs)->outbound_msg_0)) +#define WR_IN_MSG_0(v, regs) writel((v),(void*)(&(regs)->inbound_msg_0)) +#define WR_IN_DOORBELL(v, regs) writel((v),(void*)(&(regs)->inbound_doorbell)) +#define WR_IN_QPORT(v, regs) writel((v),(void*)(&(regs)->inbound_queue_port)) + +#define RD_OB_INTR_STATUS(regs) readl((void*)(&(regs)->outbound_intr_status)) +#define WR_OB_INTR_STATUS(v, regs) writel((v),(&(regs)->outbound_intr_status)) + +/* + * When FW is in MFI_STATE_READY or MFI_STATE_OPERATIONAL, the state data + * of Outbound Msg Reg 0 indicates max concurrent cmds supported, max SGEs + * supported per cmd and if 64-bit MFAs (M64) is enabled or disabled. + */ +#define MFI_MAX_SUPP_CMDS(regs) ((RD_OB_MSG_0(regs)) & 0x00FFFF) +#define MFI_MAX_SUPP_SGES(regs) (((RD_OB_MSG_0(regs)) & 0x0FF0000) >> 0x10) +#define MFI_M64_ENABLED(regs) ((RD_OB_MSG_0(regs)) & 0x1000000) + +#define MFI_OB_INTR_STATUS_MASK 0x00000002 +#define MFI_POLL_TIMEOUT_SECS 10 + +#define MFI_ENABLE_INTR(regs) writel(1,(void*)(&(regs)->outbound_intr_mask)) + +#define MFI_DISABLE_INTR(regs) \ +{ \ + uint32_t disable = ~0x00000001; \ + uint32_t mask = readl((void*)(&(regs)->outbound_intr_mask)); \ + mask &= disable; \ + writel(mask, (void*)(&(regs)->outbound_intr_mask)); \ +} + + +struct megasas_register_set { + + uint32_t reserved_0[4]; /*0000h*/ + + uint32_t inbound_msg_0; /*0010h*/ + uint32_t inbound_msg_1; /*0014h*/ + uint32_t outbound_msg_0; /*0018h*/ + uint32_t outbound_msg_1; /*001Ch*/ + + uint32_t inbound_doorbell; /*0020h*/ + uint32_t inbound_intr_status; /*0024h*/ + uint32_t inbound_intr_mask; /*0028h*/ + + uint32_t outbound_doorbell; /*002Ch*/ + uint32_t outbound_intr_status; /*0030h*/ + uint32_t outbound_intr_mask; /*0034h*/ + + uint32_t reserved_1[2]; /*0038h*/ + + uint32_t inbound_queue_port; /*0040h*/ + uint32_t outbound_queue_port; /*0044h*/ + + uint32_t reserved_2; /*004Ch*/ + + uint32_t index_registers[1004]; /*0050h*/ + +} __attribute__ ((packed)); + +struct megasas_sge32 { + + uint32_t phys_addr; + uint32_t length; + +} __attribute__ ((packed)); + +struct megasas_sge64 { + + uint64_t phys_addr; + uint32_t length; + +} __attribute__ ((packed)); + +union megasas_sgl { + + struct megasas_sge32 sge32[1]; + struct megasas_sge64 sge64[1]; + +} __attribute__ ((packed)); + +struct megasas_header { + + uint8_t cmd; /*00h*/ + uint8_t sense_len; /*01h*/ + uint8_t cmd_status; /*02h*/ + uint8_t scsi_status; /*03h*/ + + uint8_t target_id; /*04h*/ + uint8_t lun; /*05h*/ + uint8_t cdb_len; /*06h*/ + uint8_t sge_count; /*07h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t timeout; /*12h*/ + uint32_t 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 { + + uint8_t cmd; /*00h*/ + uint8_t reserved_0; /*01h*/ + uint8_t cmd_status; /*02h*/ + + uint8_t reserved_1; /*03h*/ + uint32_t reserved_2; /*04h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t reserved_3; /*12h*/ + uint32_t data_xfer_len; /*14h*/ + + uint32_t queue_info_new_phys_addr_lo; /*18h*/ + uint32_t queue_info_new_phys_addr_hi; /*1Ch*/ + uint32_t queue_info_old_phys_addr_lo; /*20h*/ + uint32_t queue_info_old_phys_addr_hi; /*24h*/ + + uint32_t reserved_4[6]; /*28h*/ + +} __attribute__ ((packed)); + +struct megasas_init_queue_info { + + uint32_t init_flags; /*00h*/ + uint32_t reply_queue_entries; /*04h*/ + + uint32_t reply_queue_start_phys_addr_lo; /*08h*/ + uint32_t reply_queue_start_phys_addr_hi; /*0Ch*/ + uint32_t producer_index_phys_addr_lo; /*10h*/ + uint32_t producer_index_phys_addr_hi; /*14h*/ + uint32_t consumer_index_phys_addr_lo; /*18h*/ + uint32_t consumer_index_phys_addr_hi; /*1Ch*/ + +} __attribute__ ((packed)); + +struct megasas_io_frame { + + uint8_t cmd; /*00h*/ + uint8_t sense_len; /*01h*/ + uint8_t cmd_status; /*02h*/ + uint8_t scsi_status; /*03h*/ + + uint8_t target_id; /*04h*/ + uint8_t access_byte; /*05h*/ + uint8_t reserved_0; /*06h*/ + uint8_t sge_count; /*07h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t timeout; /*12h*/ + uint32_t lba_count; /*14h*/ + + uint32_t sense_buf_phys_addr_lo; /*18h*/ + uint32_t sense_buf_phys_addr_hi; /*1Ch*/ + + unsigned long start_lba_lo; /*20h*/ + unsigned long start_lba_hi; /*24h*/ + + union megasas_sgl sgl; /*28h*/ + +} __attribute__ ((packed)); + +struct megasas_pthru_frame { + + uint8_t cmd; /*00h*/ + uint8_t sense_len; /*01h*/ + uint8_t cmd_status; /*02h*/ + uint8_t scsi_status; /*03h*/ + + uint8_t target_id; /*04h*/ + uint8_t lun; /*05h*/ + uint8_t cdb_len; /*06h*/ + uint8_t sge_count; /*07h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t timeout; /*12h*/ + uint32_t data_xfer_len; /*14h*/ + + uint32_t sense_buf_phys_addr_lo; /*18h*/ + uint32_t sense_buf_phys_addr_hi; /*1Ch*/ + + uint8_t cdb[16]; /*20h*/ + union megasas_sgl sgl; /*30h*/ + +} __attribute__ ((packed)); + +struct megasas_dcmd_frame { + + uint8_t cmd; /*00h*/ + uint8_t reserved_0; /*01h*/ + uint8_t cmd_status; /*02h*/ + uint8_t reserved_1[4]; /*03h*/ + uint8_t sge_count; /*07h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t timeout; /*12h*/ + + uint32_t data_xfer_len; /*14h*/ + uint32_t opcode; /*18h*/ + + uint8_t mbox[12]; /*1Ch*/ + + union megasas_sgl sgl; /*28h*/ + +} __attribute__ ((packed)); + +struct megasas_abort_frame { + + uint8_t cmd; /*00h*/ + uint8_t reserved_0; /*01h*/ + uint8_t cmd_status; /*02h*/ + + uint8_t reserved_1; /*03h*/ + uint32_t reserved_2; /*04h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t reserved_3; /*12h*/ + uint32_t reserved_4; /*14h*/ + + uint32_t abort_context; /*18h*/ + uint32_t pad_1; /*1Ch*/ + + uint32_t abort_mfi_phys_addr_lo; /*20h*/ + uint32_t abort_mfi_phys_addr_hi; /*24h*/ + + uint32_t reserved_5[6]; /*28h*/ + +} __attribute__ ((packed)); + +struct megasas_smp_frame { + + uint8_t cmd; /*00h*/ + uint8_t reserved_1; /*01h*/ + uint8_t cmd_status; /*02h*/ + uint8_t connection_status; /*03h*/ + + uint8_t reserved_2[3]; /*04h*/ + uint8_t sge_count; /*07h*/ + + uint32_t context; /*08h*/ + uint32_t pad_0; /*0Ch*/ + + uint16_t flags; /*10h*/ + uint16_t timeout; /*12h*/ + + uint32_t data_xfer_len; /*14h*/ + + uint8_t phy_id; /*18h*/ + uint8_t port_id; /*19h*/ + uint8_t connection_rate; /*1Ah*/ + uint8_t reserved_3; /*1Bh*/ + + uint32_t reserved_4; /*1Ch*/ + + uint64_t sas_addr; /*20h*/ + + union megasas_sgl request_sgl; /*28h*/ + union megasas_sgl response_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; + + uint8_t raw_bytes[64]; + +}; + +struct megasas_cmd; + +union megasas_evt_class_locale { + + struct { + uint16_t locale; + uint8_t reserved; + int8_t class; + } __attribute__ ((packed)) members; + + uint32_t word; + +} __attribute__ ((packed)); + +struct megasas_evt_log_info { + uint32_t newest_seq_num; + uint32_t oldest_seq_num; + uint32_t clear_seq_num; + uint32_t shutdown_seq_num; + uint32_t boot_seq_num; + +} __attribute__ ((packed)); + +struct megasas_progress { + + uint16_t progress; + uint16_t elapsed_seconds; + +} __attribute__ ((packed)); + +struct megasas_evtarg_ld { + + uint16_t target_id; + uint8_t ld_index; + uint8_t reserved; + +} __attribute__ ((packed)); + +struct megasas_evtarg_pd { + uint16_t device_id; + uint8_t encl_index; + uint8_t slot_number; + +} __attribute__ ((packed)); + +struct megasas_evt_detail { + + uint32_t seq_num; + uint32_t time_stamp; + uint32_t code; + union megasas_evt_class_locale cl; + uint8_t arg_type; + uint8_t reserved1[15]; + + union { + struct { + struct megasas_evtarg_pd pd; + uint8_t cdb_length; + uint8_t sense_length; + uint8_t reserved[2]; + uint8_t cdb[16]; + uint8_t sense[64]; + } __attribute__ ((packed)) cdbSense; + + struct megasas_evtarg_ld ld; + + struct { + struct megasas_evtarg_ld ld; + uint64_t count; + } __attribute__ ((packed)) ld_count; + + struct { + uint64_t lba; + struct megasas_evtarg_ld ld; + } __attribute__ ((packed)) ld_lba; + + struct { + struct megasas_evtarg_ld ld; + uint32_t prevOwner; + uint32_t newOwner; + } __attribute__ ((packed)) ld_owner; + + struct { + uint64_t ld_lba; + uint64_t 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; + uint32_t prev_state; + uint32_t new_state; + } __attribute__ ((packed)) ld_state; + + struct { + uint64_t strip; + struct megasas_evtarg_ld ld; + } __attribute__ ((packed)) ld_strip; + + struct megasas_evtarg_pd pd; + + struct { + struct megasas_evtarg_pd pd; + uint32_t err; + } __attribute__ ((packed)) pd_err; + + struct { + uint64_t lba; + struct megasas_evtarg_pd pd; + } __attribute__ ((packed)) pd_lba; + + struct { + uint64_t 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; + uint32_t prevState; + uint32_t newState; + } __attribute__ ((packed)) pd_state; + + struct { + uint16_t vendorId; + uint16_t deviceId; + uint16_t subVendorId; + uint16_t subDeviceId; + } __attribute__ ((packed)) pci; + + uint32_t rate; + char str[96]; + + struct { + uint32_t rtc; + uint32_t elapsedSeconds; + } __attribute__ ((packed)) time; + + struct { + uint32_t ecar; + uint32_t elog; + char str[64]; + } __attribute__ ((packed)) ecc; + + uint8_t b[96]; + uint16_t s[48]; + uint32_t w[24]; + uint64_t d[12]; + } args; + + char description[128]; + +} __attribute__ ((packed)); + +struct megasas_instance { + + uint32_t producer; + uint32_t consumer; + + uint32_t* reply_queue; + dma_addr_t reply_queue_phys_addr; + + dma_addr_t phys_addr; + + unsigned long base_addr; + struct megasas_register_set __iomem* reg_set; + + int8_t init_id; + uint8_t reserved[3]; + + uint16_t max_num_sge; + uint16_t max_fw_cmds; + uint32_t 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; + struct megasas_cmd* aen_cmd; + + struct Scsi_Host* host; + spinlock_t lock; + spinlock_t* host_lock; + + wait_queue_head_t int_cmd_wait_q; + wait_queue_head_t abort_cmd_wait_q; + + struct pci_dev* pdev; + uint32_t unique_id; +}; + +/* + * Various conversion macros from SCSI command + */ +#define SCP2HOST(scp) (scp)->device->host // to host +#define SCP2HOSTDATA(scp) SCP2HOST(scp)->hostdata // to soft state +#define SCP2CHANNEL(scp) (scp)->device->channel // to channel +#define SCP2TARGET(scp) (scp)->device->id // to target +#define SCP2LUN(scp) (scp)->device->lun // to LUN + +#define SCSIHOST2ADAP(host) (((caddr_t *)(host->hostdata))[0]) +#define SCP2ADAPTER(scp) (struct megasas_instance*)SCSIHOST2ADAP(SCP2HOST(scp)) + +#define MEGADRV_IS_LOGICAL(scp) \ + (SCP2CHANNEL(scp) < MEGADRV_MAX_PD_CHANNELS) ? 0 : 1 + +#define MEGADRV_DEV_INDEX(inst, scp) \ + ((SCP2CHANNEL(scp) % 2) * MEGADRV_MAX_DEV_PER_CHANNEL) + SCP2TARGET(scp) + +struct megasas_cmd { + + union megasas_frame* frame; + dma_addr_t frame_phys_addr; + uint8_t* sense; + dma_addr_t sense_phys_addr; + + uint32_t index; + uint8_t sync_cmd; + uint8_t cmd_status; + uint16_t abort_aen; + + struct list_head list; + struct scsi_cmnd* scmd; + uint32_t frame_count; +}; + +#define MAX_MGMT_ADAPTERS 1024 +#define IOC_SIGNATURE "LSILOGIC" + +#define IOC_CMD_FIRMWARE 0x0 +#define MR_DRIVER_IOCTL_COMMON 0xF0010000 +#define MR_DRIVER_IOCTL_DRIVER_VERSION 0xF0010100 +#define MR_DRIVER_IOCTL_PCI_INFORMATION 0xF0010200 +#define MR_DRIVER_IOCTL_MEGARAID_STATISTICS 0xF0010300 + +#define MR_DRIVER_IOCTL_LINUX 0xF0040000 +#define MR_LINUX_GET_ADAPTER_COUNT (MR_DRIVER_IOCTL_LINUX | 0x0100) +#define MR_LINUX_GET_ADAPTER_MAP (MR_DRIVER_IOCTL_LINUX | 0x0200) +#define MR_LINUX_GET_AEN (MR_DRIVER_IOCTL_LINUX | 0x0300) + +#define MR_MAX_SENSE_LENGTH 32 + +struct iocpacket { + + uint16_t version; + uint16_t controller_id; + uint8_t signature[8]; + uint32_t reserved_1; + uint32_t control_code; + uint32_t reserved_2[2]; + uint8_t frame[64]; + union megasas_sgl_frame sgl_frame; + uint8_t sense_buff[MR_MAX_SENSE_LENGTH]; + uint8_t data[1]; + +} __attribute__ ((packed)); + +struct megasas_mgmt_info { + + uint16_t count; + struct megasas_instance* instance[MAX_MGMT_ADAPTERS]; + uint16_t map[MAX_MGMT_ADAPTERS]; + int max_index; +}; + +struct megasas_drv_ver { + uint8_t signature[12]; + uint8_t os_name[16]; + uint8_t os_ver[12]; + uint8_t drv_name[20]; + uint8_t drv_ver[32]; + uint8_t drv_rel_date[20]; + +} __attribute__ ((packed)); + +#endif /*LSI_MEGARAID_SAS_H*/ _