diff -urN linux-2.4.21-rc1/drivers/scsi/megaraid.c linux-2.4.21-rc1-megaraid-2.00.5/drivers/scsi/megaraid.c --- linux-2.4.21-rc1/drivers/scsi/megaraid.c Tue Apr 22 03:04:53 2003 +++ linux-2.4.21-rc1-megaraid-2.00.5/drivers/scsi/megaraid.c Fri Apr 25 23:56:09 2003 @@ -1,579 +1,43 @@ -/*=================================================================== - * - * Linux MegaRAID device driver - * - * Copyright 2001 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. - * - * Version : v1.18f (Dec 10, 2002) - * - * Description: Linux device driver for LSI Logic MegaRAID controller - * - * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490 - * 493. - * History: - * - * Version 0.90: - * Original source contributed by Dell; integrated it into the kernel and - * cleaned up some things. Added support for 438/466 controllers. - * Version 0.91: - * Aligned mailbox area on 16-byte boundary. - * Added schedule() at the end to properly clean up. - * Made improvements for conformity to linux driver standards. - * - * Version 0.92: - * Added support for 2.1 kernels. - * Reads from pci_dev struct, so it's not dependent on pcibios. - * Added some missing virt_to_bus() translations. - * Added support for SMP. - * Changed global cli()'s to spinlocks for 2.1, and simulated - * spinlocks for 2.0. - * Removed setting of SA_INTERRUPT flag when requesting Irq. - * - * Version 0.92ac: - * Small changes to the comments/formatting. Plus a couple of - * added notes. Returned to the authors. No actual code changes - * save printk levels. - * 8 Oct 98 Alan Cox - * - * Merged with 2.1.131 source tree. - * 12 Dec 98 K. Baranowski - * - * Version 0.93: - * Added support for vendor specific ioctl commands (M_RD_IOCTL_CMD+xxh) - * Changed some fields in MEGARAID struct to better values. - * Added signature check for Rp controllers under 2.0 kernels - * Changed busy-wait loop to be time-based - * Fixed SMP race condition in isr - * Added kfree (sgList) on release - * Added #include linux/version.h to megaraid.h for hosts.h - * Changed max_id to represent max logical drives instead of targets. - * - * Version 0.94: - * Got rid of some excess locking/unlocking - * Fixed slight memory corruption problem while memcpy'ing into mailbox - * Changed logical drives to be reported as luns rather than targets - * Changed max_id to 16 since it is now max targets/chan again. - * Improved ioctl interface for upcoming megamgr - * - * Version 0.95: - * Fixed problem of queueing multiple commands to adapter; - * still has some strange problems on some setups, so still - * defaults to single. To enable parallel commands change - * #define MULTI_IO in megaraid.h - * Changed kmalloc allocation to be done in beginning. - * Got rid of C++ style comments - * - * Version 0.96: - * 762 fully supported. - * - * Version 0.97: - * Changed megaraid_command to use wait_queue. - * - * Version 1.00: - * Checks to see if an irq occurred while in isr, and runs through - * routine again. - * Copies mailbox to temp area before processing in isr - * Added barrier() in busy wait to fix volatility bug - * Uses separate list for freed Scbs, keeps track of cmd state - * Put spinlocks around entire queue function for now... - * Full multi-io commands working stablely without previous problems - * Added skipXX LILO option for Madrona motherboard support - * - * Version 1.01: - * Fixed bug in mega_cmd_done() for megamgr control commands, - * the host_byte in the result code from the scsi request to - * scsi midlayer is set to DID_BAD_TARGET when adapter's - * returned codes are 0xF0 and 0xF4. - * - * Version 1.02: - * Fixed the tape drive bug by extending the adapter timeout value - * for passthrough command to 60 seconds in mega_build_cmd(). - * - * Version 1.03: - * Fixed Madrona support. - * Changed the adapter timeout value from 60 sec in 1.02 to 10 min - * for bigger and slower tape drive. - * Added driver version printout at driver loadup time - * - * Version 1.04 - * Added code for 40 ld FW support. - * Added new ioctl command 0x81 to support NEW_READ/WRITE_CONFIG with - * data area greater than 4 KB, which is the upper bound for data - * tranfer through scsi_ioctl interface. - * The additional 32 bit field for 64bit address in the newly defined - * mailbox64 structure is set to 0 at this point. - * - * Version 1.05 - * Changed the queing implementation for handling SCBs and completed - * commands. - * Added spinlocks in the interrupt service routine to enable the driver - * function in the SMP environment. - * Fixed the problem of unnecessary aborts in the abort entry point, which - * also enables the driver to handle large amount of I/O requests for - * long duration of time. - * Version 1.06 - * Intel Release - * Version 1.07 - * Removed the usage of uaccess.h file for kernel versions less than - * 2.0.36, as this file is not present in those versions. - * - * Version 108 - * Modified mega_ioctl so that 40LD megamanager would run - * Made some changes for 2.3.XX compilation , esp wait structures - * Code merge between 1.05 and 1.06 . - * Bug fixed problem with ioctl interface for concurrency between - * 8ld and 40ld firwmare - * Removed the flawed semaphore logic for handling new config command - * Added support for building own scatter / gather list for big user - * mode buffers - * Added /proc file system support ,so that information is available in - * human readable format - * - * Version 1a08 - * Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag - * - * Version 1b08 - * Include file changes. - * Version 1b08b - * Change PCI ID value for the 471 card, use #defines when searching - * for megaraid cards. - * - * Version 1.10 - * - * I) Changes made to make following ioctl commands work in 0x81 interface - * a)DCMD_DELETE_LOGDRV - * b)DCMD_GET_DISK_CONFIG - * c)DCMD_DELETE_DRIVEGROUP - * d)NC_SUBOP_ENQUIRY3 - * e)DCMD_CHANGE_LDNO - * f)DCMD_CHANGE_LOOPID - * g)DCMD_FC_READ_NVRAM_CONFIG - * h)DCMD_WRITE_CONFIG - * II) Added mega_build_kernel_sg function - * III)Firmware flashing option added - * - * Version 1.10a - * - * I)Dell updates included in the source code. - * Note: This change is not tested due to the unavailability of IA64 kernel - * and it is in the #ifdef DELL_MODIFICATION macro which is not defined - * - * Version 1.10b - * - * I)In M_RD_IOCTL_CMD_NEW command the wrong way of copying the data - * to the user address corrected - * - * Version 1.10c - * - * I) DCMD_GET_DISK_CONFIG opcode updated for the firmware changes. - * - * Version 1.11 - * I) Version number changed from 1.10c to 1.11 - * II) DCMD_WRITE_CONFIG(0x0D) command in the driver changed from - * scatter/gather list mode to direct pointer mode.. - * Fixed bug of undesirably detecting HP onboard controllers which - * are disabled. - * - * Version 1.12 (Sep 21, 2000) - * - * I. Changes have been made for Dynamic DMA mapping in IA64 platform. - * To enable all these changes define M_RD_DYNAMIC_DMA_SUPPORT in megaraid.h - * II. Got rid of windows mode comments - * III. Removed unwanted code segments - * IV. Fixed bug of HP onboard controller information (commented with - * MEGA_HP_FIX) - * - * Version 1a12 - * I. reboot notifier and new ioctl changes ported from 1c09 - * - * Version 1b12 - * I. Changes in new ioctl interface routines ( Nov 06, 2000 ) - * - * Version 1c12 - * I. Changes in new ioctl interface routines ( Nov 07, 2000 ) - * - * Version 1d12 - * I. Compilation error under kernel 2.4.0 for 32-bit machine in mega_ioctl - * - * Version 1e12, 1f12 - * 1. Fixes for pci_map_single, pci_alloc_consistent along with mailbox - * alignment - * - * Version 1.13beta - * Added Support for Full 64bit address space support. If firmware - * supports 64bit, it goes to 64 bit mode even on x86 32bit - * systems. Data Corruption Issues while running on test9 kernel - * on IA64 systems. This issue not seen on test11 on x86 system - * - * Version 1.13c - * 1. Resolved Memory Leak when using M_RD_IOCTL_CMD interface - * 2. Resolved Queuing problem when MailBox Blocks - * 3. Added unregister_reboot_notifier support - * - * Version 1.13d - * Experimental changes in interfacing with the controller in ISR - * - * Version 1.13e - * Fixed Broken 2.2.XX compilation changes + misc changes - * - * Version 1.13f to 1.13i - * misc changes + code clean up - * Cleaned up the ioctl code and added set_mbox_xfer_addr() - * Support for START_DEV (6) - * - * Version 1.13j - * Moved some code to megaraid.h file, replaced some hard coded values - * with respective macros. Changed some functions to static - * - * Version 1.13k - * Only some idendation correction to 1.13j - * - * Version 1.13l , 1.13m, 1.13n, 1.13o - * Minor Identation changes + misc changes - * - * Version 1.13q - * Paded the new uioctl_t MIMD structure for maintaining alignment - * and size across 32 / 64 bit platforms - * Changed the way MIMD IOCTL interface used virt_to_bus() to use pci - * memory location - * - * Version 1.13r - * 2.4.xx SCSI Changes. - * - * Version 1.13s - * Stats counter fixes - * Temporary fix for some 64 bit firmwares in 2.4.XX kernels - * - * Version 1.13t - * Support for 64bit version of READ/WRITE/VIEW DISK CONFIG - * - * Version 1.14 - * Did away with MEGADEV_IOCTL flag. It is now standard part of driver - * without need for a special #define flag - * Disabled old scsi ioctl path for kernel versions > 2.3.xx. This is due - * to the nature in which the new scsi code queues a new scsi command to - * controller during SCSI IO Completion - * Driver now checks for sub-system vendor id before taking ownership of - * the controller - * - * Version 1.14a - * Added Host re-ordering - * - * Version 1.14b - * Corrected some issue which caused the older cards not to work - * - * Version 1.14c - * IOCTL changes for not handling the non-64bit firmwares under 2.4.XX - * kernel - * - * Version 1.14d - * Fixed Various MIMD Synchronization Issues - * - * Version 1.14e - * Fixed the error handling during card initialization - * - * Version 1.14f - * Multiple invocations of mimd phase I ioctl stalls the cpu. Replaced - * spinlock with semaphore(mutex) - * - * Version 1.14g - * Fixed running out of scbs issues while running MIMD apps under heavy IO - * - * Version 1.14g-ac - 02/03/01 - * Reformatted to Linux format so I could compare to old one and cross - * check bug fixes - * Re fixed the assorted missing 'static' cases - * Removed some unneeded version checks - * Cleaned up some of the VERSION checks in the code - * Left 2.0 support but removed 2.1.x support. - * Collected much of the compat glue into one spot - * - * Version 1.14g-ac2 - 22/03/01 - * Fixed a non obvious dereference after free in the driver unload path - * - * Version 1.14i - * changes for making 32bit application run on IA64 - * - * Version 1.14j - * Tue Mar 13 14:27:54 EST 2001 - AM - * Changes made in the driver to be able to run applications if the - * system has memory >4GB. - * - * - * Version 1.14k - * Thu Mar 15 18:38:11 EST 2001 - AM - * - * Firmware version check removed if subsysid==0x1111 and - * subsysvid==0x1111, since its not yet initialized. - * - * changes made to correctly calculate the base in mega_findCard. - * - * Driver informational messages now appear on the console as well as - * with dmesg - * - * Older ioctl interface is returned failure on newer(2.4.xx) kernels. - * - * Inclusion of "modversions.h" is still a debatable question. It is - * included anyway with this release. - * - * Version 1.14l - * Mon Mar 19 17:39:46 EST 2001 - AM - * - * Assorted changes to remove compilation error in 1.14k when compiled - * with kernel < 2.4.0 - * - * Version 1.14m - * Tue Mar 27 12:09:22 EST 2001 - AM - * - * Added support for extended CDBs ( > 10 bytes ) and OBDR ( One Button - * Disaster Recovery ) feature. - * - * - * Version 1.14n - * Tue Apr 10 14:28:13 EDT 2001 - AM - * - * "modeversions.h" is no longer included in the code. - * 2.4.xx style mutex initialization used for older kernels also - * - * Version 1.14o - * Wed Apr 18 17:47:26 EDT 2001 - PJ - * - * Before returning status for 'inquiry', we first check if request buffer - * is SG list, and then return appropriate status - * - * Version 1.14p - * Wed Apr 25 13:44:48 EDT 2001 - PJ - * - * SCSI result made appropriate in case of check conditions for extended - * passthru commands - * - * Do not support lun >7 for physically accessed devices - * - * - * Version 1.15 - * Thu Apr 19 09:38:38 EDT 2001 - AM - * - * 1.14l rollover to 1.15 - merged with main trunk after 1.15d - * - * Version 1.15b - * Wed May 16 20:10:01 EDT 2001 - AM - * - * "modeversions.h" is no longer included in the code. - * 2.4.xx style mutex initialization used for older kernels also - * Brought in-sync with Alan's changes in 2.4.4 - * Note: 1.15a is on OBDR branch(main trunk), and is not merged with yet. - * - * Version 1.15c - * Mon May 21 23:10:42 EDT 2001 - AM - * - * ioctl interface uses 2.4.x conforming pci dma calls - * similar calls used for older kernels - * - * Version 1.15d - * Wed May 30 17:30:41 EDT 2001 - AM - * - * NULL is not a valid first argument for pci_alloc_consistent() on - * IA64(2.4.3-2.10.1). Code shuffling done in ioctl interface to get - * "pci_dev" before making calls to pci interface routines. - * - * Version 1.16pre - * Fri Jun 1 19:40:48 EDT 2001 - AM - * - * 1.14p and 1.15d merged - * ROMB support added - * - * Version 1.16-pre1 - * Mon Jun 4 15:01:01 EDT 2001 - AM - * - * Non-ROMB firmware do no DMA support 0xA9 command. Value 0xFF - * (all channels are raid ) is chosen for those firmware. - * - * Version 1.16-pre2 - * Mon Jun 11 18:15:31 EDT 2001 - AM - * - * Changes for boot from any logical drive - * - * Version 1.16 - * Tue Jun 26 18:07:02 EDT 2001 - AM - * - * branched at 1.14p - * - * Check added for HP 1M/2M controllers if having firmware H.01.07 or - * H.01.08. If found, disable 64 bit support since these firmware have - * limitations for 64 bit addressing - * - * - * Version 1.17 - * Thu Jul 12 11:14:09 EDT 2001 - AM - * - * 1.16pre2 and 1.16 merged. - * - * init_MUTEX and init_MUTEX_LOCKED are defined in 2.2.19. Pre-processor - * statements are added for them - * - * Linus's 2.4.7pre3 kernel introduces a new field 'max_sectors' in Scsi_Host - * structure, to improve IO performance. - * - * - * Version 1.17a - * Fri Jul 13 18:44:01 EDT 2001 - AM - * - * Starting from kernel 2.4.x, LUN is not < 8 - following SCSI-III. So to have - * our current formula working to calculate logical drive number, return - * failure for LUN > 7 - * - * - * Version 1.17b - * Mon Jul 30 19:24:02 EDT 2001 - AM - * - * Added support for random deletion of logical drives - * - * Version 1.17c - * Tue Sep 25 09:37:49 EDT 2001 - Atul Mukker - * - * With single and dual channel controllers, some virtaul channels are - * displayed negative. - * - * Version 1.17a-ac - * Mon Aug 6 14:59:29 BST 2001 - "Michael Johnson" - * - * Make the HP print formatting and check for buggy firmware runtime not - * ifdef dependant. - * - * - * Version 1.17d - * Thu Oct 11 10:48:45 EDT 2001 - Atul Mukker - * - * Driver 1.17c oops when loaded without controller. - * - * Special case for "use_sg == 1" removed while building the scatter gather - * list. - * - * Version 1.18 - * Thu Oct 11 15:02:53 EDT 2001 - Atul Mukker - * - * References to AMI have been changed to LSI Logic. - * - * Version 1.18a - * Mon Mar 11 11:38:38 EST 2002 - Atul Mukker - * - * RAID On MotherBoard (ROMB) - boot from logical or physical drives - * - * Support added for discovery(ROMB) vendor and device ids. - * - * Data transfer length for passthru commands must be valid even if the - * command has an associated scatter-gather list. - * - * - * Version 1.18b - * Tue Apr 23 11:01:58 EDT 2002 - Atul Mukker - * - * typo corrected for scsi condition CHECK_CONDITION in mega_cmd_done() - * - * Support added for PCI_VENDOR_ID_LSI_LOGIC with device id - * PCI_DEVICE_ID_AMI_MEGARAID3. - * - * - * Version 1.18c - * Thu May 16 10:27:55 EDT 2002 - Atul Mukker - * - * Retrun valid return status for mega passthru commands resulting in - * contingent allegiance condition. Check for 64-bit passthru commands also. - * - * Do not check_region() anymore and check for return value of - * request_region() - * - * Send valid sense data to appliations using the private ioctl interface. - * - * - * Version 1.18d - * Wed Aug 7 18:51:51 EDT 2002 - Atul Mukker - * - * Added support for yellowstone and verde controller - * - * Version 1.18e - * Mon Nov 18 12:11:02 EST 2002 - Atul Mukker +/* * - * Don't use virt_to_bus in mega_register_mailbox when you've got the DMA - * address already. Submitted by Jens Axboe and is included in SuSE Linux - * Enterprise Server 7. + * Linux MegaRAID device driver * - * s/pcibios_read_config/pci_read_config - Matt Domsch + * Copyright © 2002 LSI Logic Corporation. * - * remove an unsed variable + * 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. * - * Version 1.18f - * Tue Dec 10 09:54:39 EST 2002 - Atul Mukker + * Copyright (c) 2002 Red Hat, Inc. All rights reserved. + * - fixes + * - speed-ups (list handling fixes, issued_list, optimizations.) + * - lots of cleanups. * - * remove GFP_DMA flag for ioctl. This was causing overrun of DMA buffers. + * Version : v2.00.5 (Apr 24, 2003) - Atul Mukker * - * BUGS: - * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that - * fails to detect the controller as a pci device on the system. + * Description: Linux device driver for LSI Logic MegaRAID controller * - * Timeout period for upper scsi layer, i.e. SD_TIMEOUT in - * /drivers/scsi/sd.c, is too short for this controller. SD_TIMEOUT - * value must be increased to (30 * HZ) otherwise false timeouts - * will occur in the upper layer. + * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490, 493 + * 518, 520, 531, 532 * - * Never set skip_id. The existing PCI code the megaraid uses fails - * to properly check the vendor subid in some cases. Setting this then - * makes it steal other i960's and crashes some boxes + * This driver is supported by LSI Logic, with assistance from Red Hat, Dell, + * and others. Please send updates to the public mailing list + * linux-megaraid-devel@dell.com, and subscribe to and read archives of this + * list at http://lists.us.dell.com/. * - * Far too many ifdefs for versions. + * For history of changes, see ChangeLog.megaraid. * - *===================================================================*/ + */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include - -#include -#include -#include /* for kmalloc() */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ -#include -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */ -#include -#else -#include -#endif -#endif - -#include -#include - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */ +#include +#include #include -#endif - -/* - * These header files are required for Shutdown Notification routines - */ -#include +#include #include -#include +#include +#include #include "sd.h" #include "scsi.h" @@ -581,4912 +45,5530 @@ #include "megaraid.h" +MODULE_AUTHOR ("LSI Logic Corporation"); +MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); +MODULE_LICENSE ("GPL"); + +static unsigned int max_cmd_per_lun = DEF_CMD_PER_LUN; +MODULE_PARM(max_cmd_per_lun, "i"); +MODULE_PARM_DESC(max_cmd_per_lun, "Maximum number of commands which can be issued to a single LUN (default=DEF_CMD_PER_LUN=63)"); + +static unsigned short int max_sectors_per_io = MAX_SECTORS_PER_IO; +MODULE_PARM(max_sectors_per_io, "h"); +MODULE_PARM_DESC(max_sectors_per_io, "Maximum number of sectors per I/O request (default=MAX_SECTORS_PER_IO=128)"); + + +static unsigned short int max_mbox_busy_wait = MBOX_BUSY_WAIT; +MODULE_PARM(max_mbox_busy_wait, "h"); +MODULE_PARM_DESC(max_mbox_busy_wait, "Maximum wait for mailbox in microseconds if busy (default=MBOX_BUSY_WAIT=10)"); + +#define RDINDOOR(adapter) readl((adapter)->base + 0x20) +#define RDOUTDOOR(adapter) readl((adapter)->base + 0x2C) +#define WRINDOOR(adapter,value) writel(value, (adapter)->base + 0x20) +#define WROUTDOOR(adapter,value) writel(value, (adapter)->base + 0x2C) + /* - *================================================================ - * #Defines - *================================================================ + * Global variables */ -#define MAX_SERBUF 160 -#define COM_BASE 0x2f8 +static int hba_count; +static adapter_t *hba_soft_state[MAX_CONTROLLERS]; +static struct proc_dir_entry *mega_proc_dir_entry; -static ulong RDINDOOR (mega_host_config * megaCfg) -{ - return readl (megaCfg->base + 0x20); -} +static struct notifier_block mega_notifier = { + .notifier_call = megaraid_reboot_notify +}; -static void WRINDOOR (mega_host_config * megaCfg, ulong value) -{ - writel (value, megaCfg->base + 0x20); -} +/* For controller re-ordering */ +static struct mega_hbas mega_hbas[MAX_CONTROLLERS]; -static ulong RDOUTDOOR (mega_host_config * megaCfg) -{ - return readl (megaCfg->base + 0x2C); -} +/* + * The File Operations structure for the serial/ioctl interface of the driver + */ +static struct file_operations megadev_fops = { + .ioctl = megadev_ioctl, + .open = megadev_open, + .release = megadev_close, + .owner = THIS_MODULE, +}; -static void WROUTDOOR (mega_host_config * megaCfg, ulong value) -{ - writel (value, megaCfg->base + 0x2C); -} +/* + * Array to structures for storing the information about the controllers. This + * information is sent to the user level applications, when they do an ioctl + * for this information. + */ +static struct mcontroller mcontroller[MAX_CONTROLLERS]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ -#include -#define cpuid smp_processor_id() -#endif +/* The current driver version */ +static u32 driver_ver = 0x02000000; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) -#define scsi_set_pci_device(x,y) -#endif +/* major number used by the device for character interface */ +static int major; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ +#define IS_RAID_CH(hba, ch) (((hba)->mega_ch_class >> (ch)) & 0x01) + + +/* + * Debug variable to print some diagnostic messages + */ +static int trace_level; /* - * Linux 2.4 and higher + * megaraid_validate_parms() * - * No driver private lock - * Use the io_request_lock not cli/sti - * queue task is a simple api without irq forms + * Validate that any module parms passed in + * have proper values. */ +static void +megaraid_validate_parms(void) +{ + if( (max_cmd_per_lun <= 0) || (max_cmd_per_lun > MAX_CMD_PER_LUN) ) + max_cmd_per_lun = MAX_CMD_PER_LUN; + if( max_mbox_busy_wait > MBOX_BUSY_WAIT ) + max_mbox_busy_wait = MBOX_BUSY_WAIT; +} -MODULE_AUTHOR ("LSI Logic Corporation"); -MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); -MODULE_LICENSE ("GPL"); -#define DRIVER_LOCK_T -#define DRIVER_LOCK_INIT(p) -#define DRIVER_LOCK(p) -#define DRIVER_UNLOCK(p) -#define IO_LOCK_T unsigned long io_flags = 0 -#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); -#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags); +/** + * megaraid_detect() + * @host_template - Our soft state maintained by mid-layer + * + * the detect entry point for the mid-layer. + * We scan the PCI bus for our controllers and start them. + * + * Note: PCI_DEVICE_ID_PERC4_DI below represents the PERC4/Di class of + * products. All of them share the same vendor id, device id, and subsystem + * vendor id but different subsystem ids. As of now, driver does not use the + * subsystem id. + */ +static int +megaraid_detect(Scsi_Host_Template *host_template) +{ + int i; + u16 dev_sw_table[] = { /* Table of all supported + vendor/device ids */ -#define queue_task_irq(a,b) queue_task(a,b) -#define queue_task_irq_off(a,b) queue_task(a,b) + PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DISCOVERY, + PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_PERC4_DI, + PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_PERC4_QC_VERDE, + PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID, + PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID2, + PCI_VENDOR_ID_AMI, PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_AMI_MEGARAID3, + PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_AMI_MEGARAID3 }; -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */ -/* - * Linux 2.2 and higher - * - * No driver private lock - * Use the io_request_lock not cli/sti - * No pci region api - * queue_task is now a single simple API - */ + printk(KERN_NOTICE "megaraid: " MEGARAID_VERSION); -static char kernel_version[] = UTS_RELEASE; -MODULE_AUTHOR ("LSI Logic Corporation"); -MODULE_DESCRIPTION ("LSI Logic MegaRAID driver"); + megaraid_validate_parms(); -#define DRIVER_LOCK_T -#define DRIVER_LOCK_INIT(p) -#define DRIVER_LOCK(p) -#define DRIVER_UNLOCK(p) -#define IO_LOCK_T unsigned long io_flags = 0 -#define IO_LOCK spin_lock_irqsave(&io_request_lock,io_flags); -#define IO_UNLOCK spin_unlock_irqrestore(&io_request_lock,io_flags); - -#define pci_free_consistent(a,b,c,d) -#define pci_unmap_single(a,b,c,d) -#define pci_enable_device(x) (0) -#define queue_task_irq(a,b) queue_task(a,b) -#define queue_task_irq_off(a,b) queue_task(a,b) - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ -#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) -#define init_MUTEX(x) (*(x)=MUTEX) -#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL + /* + * Scan PCI bus for our all devices. + */ + for( i = 0; i < sizeof(dev_sw_table)/sizeof(u16); i += 2 ) { + + mega_find_card(host_template, dev_sw_table[i], + dev_sw_table[i+1]); + } + + if(hba_count) { + /* + * re-order hosts so that one with bootable logical drive + * comes first + */ + mega_reorder_hosts(); + +#ifdef CONFIG_PROC_FS + mega_proc_dir_entry = proc_mkdir("megaraid", &proc_root); + + if(!mega_proc_dir_entry) { + printk(KERN_WARNING + "megaraid: failed to create megaraid root\n"); + } + else { + for(i = 0; i < hba_count; i++) { + mega_create_proc_entry(i, mega_proc_dir_entry); + } + } #endif + /* + * Register the driver as a character device, for applications + * to access it for ioctls. + * First argument (major) to register_chrdev implies a dynamic + * major number allocation. + */ + major = register_chrdev(0, "megadev", &megadev_fops); -#else + /* + * Register the Shutdown Notification hook in kernel + */ + if(register_reboot_notifier(&mega_notifier)) { + printk(KERN_WARNING + "MegaRAID Shutdown routine not registered!!\n"); + } -/* - * Linux 2.0 macros. Here we have to provide some of our own - * functionality. We also only work little endian 32bit. - * Again no pci_alloc/free api - * IO_LOCK/IO_LOCK_T were never used in 2.0 so now are empty - */ - -#define cpuid 0 -#define DRIVER_LOCK_T long cpu_flags; -#define DRIVER_LOCK_INIT(p) -#define DRIVER_LOCK(p) \ - save_flags(cpu_flags); \ - cli(); -#define DRIVER_UNLOCK(p) \ - restore_flags(cpu_flags); -#define IO_LOCK_T -#define IO_LOCK(p) -#define IO_UNLOCK(p) -#define le32_to_cpu(x) (x) -#define cpu_to_le32(x) (x) + } -#define pci_free_consistent(a,b,c,d) -#define pci_unmap_single(a,b,c,d) + return hba_count; +} -#define init_MUTEX_LOCKED(x) (*(x)=MUTEX_LOCKED) -#define init_MUTEX(x) (*(x)=MUTEX) -#define pci_enable_device(x) (0) -/* - * 2.0 lacks spinlocks, iounmap/ioremap +/** + * mega_find_card() - find and start this controller + * @host_template - Our soft state maintained by mid-layer + * @pci_vendor - pci vendor id for this controller + * @pci_device - pci device id for this controller + * + * Scans the PCI bus for this vendor and device id combination, setup the + * resources, and register ourselves as a SCSI HBA driver, and setup all + * parameters for our soft state. + * + * This routine also checks for some buggy firmware and ajust the flags + * accordingly. */ +static void +mega_find_card(Scsi_Host_Template *host_template, u16 pci_vendor, + u16 pci_device) +{ + struct Scsi_Host *host = NULL; + adapter_t *adapter = NULL; + u32 magic64; + unsigned long mega_baseport; + u16 subsysid, subsysvid; + u8 pci_bus; + u8 pci_dev_func; + u8 irq; + struct pci_dev *pdev = NULL; + u8 did_ioremap_f = 0; + u8 did_req_region_f = 0; + u8 did_scsi_reg_f = 0; + u8 got_ipdev_f = 0; + u8 alloc_int_buf_f = 0; + u8 alloc_scb_f = 0; + u8 got_irq_f = 0; + u8 did_setup_mbox_f = 0; + unsigned long tbase; + unsigned long flag = 0; + int i, j; -#define ioremap vremap -#define iounmap vfree + while((pdev = pci_find_device(pci_vendor, pci_device, pdev))) { - /* simulate spin locks */ -typedef struct { - volatile char lock; -} spinlock_t; + if(pci_enable_device (pdev)) continue; -#define spin_lock_init(x) { (x)->lock = 0;} -#define spin_lock_irqsave(x,flags) { while ((x)->lock) barrier();\ - (x)->lock=1; save_flags(flags);\ - cli();} -#define spin_unlock_irqrestore(x,flags) { (x)->lock=0; restore_flags(flags);} + pci_bus = pdev->bus->number; + pci_dev_func = pdev->devfn; -#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL + /* + * For these vendor and device ids, signature offsets are not + * valid and 64 bit is implicit + */ + if( (pci_vendor == PCI_VENDOR_ID_DELL && + pci_device == PCI_DEVICE_ID_PERC4_DI) || + (pci_vendor == PCI_VENDOR_ID_LSI_LOGIC && + pci_device == PCI_DEVICE_ID_PERC4_QC_VERDE) ) { -#endif + flag |= BOARD_64BIT; + } + else { + pci_read_config_dword(pdev, PCI_CONF_AMISIG64, + &magic64); + if (magic64 == HBA_SIGNATURE_64BIT) + flag |= BOARD_64BIT; + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ -#define dma_alloc_consistent pci_alloc_consistent -#define dma_free_consistent pci_free_consistent -#else -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,19) /* 0x020219 */ -typedef unsigned long dma_addr_t; -#endif -void *dma_alloc_consistent(void *, size_t, dma_addr_t *); -void dma_free_consistent(void *, size_t, void *, dma_addr_t); -int mega_get_order(int); -int pow_2(int); -#endif + subsysvid = pdev->subsystem_vendor; + subsysid = pdev->subsystem_device; -/* set SERDEBUG to 1 to enable serial debugging */ -#define SERDEBUG 0 -#if SERDEBUG -static void ser_init (void); -static void ser_puts (char *str); -static void ser_putc (char c); -static int ser_printk (const char *fmt, ...); -#endif + /* + * If we do not find the valid subsys vendor id, refuse to + * load the driver. This is part of PCI200X compliance + * We load the driver if subsysvid is 0. + */ + if( subsysvid && (subsysvid != AMI_SUBSYS_VID) && + (subsysvid != DELL_SUBSYS_VID) && + (subsysvid != HP_SUBSYS_VID) && + (subsysvid != LSI_SUBSYS_VID) ) continue; -#ifdef CONFIG_PROC_FS -#define COPY_BACK if (offset > megaCfg->procidx) { \ - *eof = TRUE; \ - megaCfg->procidx = 0; \ - megaCfg->procbuf[0] = 0; \ - return 0;} \ - if ((count + offset) > megaCfg->procidx) { \ - count = megaCfg->procidx - offset; \ - *eof = TRUE; } \ - memcpy(page, &megaCfg->procbuf[offset], count); \ - megaCfg->procidx = 0; \ - megaCfg->procbuf[0] = 0; -#endif -/* - * ================================================================ - * Global variables - *================================================================ - */ + printk(KERN_NOTICE "megaraid: found 0x%4.04x:0x%4.04x:bus %d:", + pci_vendor, pci_device, pci_bus); -/* Use "megaraid=skipXX" as LILO option to prohibit driver from scanning - XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE - processor id cannot be scanned */ - -static char *megaraid; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0) /* 0x20100 */ -#ifdef MODULE -MODULE_PARM (megaraid, "s"); -#endif -#endif -static int skip_id = -1; -static int numCtlrs = 0; -static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = { 0 }; -static struct proc_dir_entry *mega_proc_dir_entry; + printk("slot %d:func %d\n", + PCI_SLOT(pci_dev_func), PCI_FUNC(pci_dev_func)); -#if DEBUG -static u32 maxCmdTime = 0; + /* Read the base port and IRQ from PCI */ + mega_baseport = pci_resource_start(pdev, 0); + irq = pdev->irq; + + tbase = mega_baseport; + + if( pci_resource_flags(pdev, 0) & IORESOURCE_MEM ) { + + if( check_mem_region(mega_baseport, 128) ) { + printk(KERN_WARNING + "megaraid: mem region busy!\n"); + continue; + } + request_mem_region(mega_baseport, 128, + "MegaRAID: LSI Logic Corporation."); + + mega_baseport = + (unsigned long)ioremap(mega_baseport, 128); + + if( !mega_baseport ) { + printk(KERN_WARNING + "megaraid: could not map hba memory\n"); + + release_mem_region(tbase, 128); + + continue; + } + + flag |= BOARD_MEMMAP; + + did_ioremap_f = 1; + } + else { + mega_baseport += 0x10; + + if( !request_region(mega_baseport, 16, "megaraid") ) + goto fail_attach; + + flag |= BOARD_IOMAP; + + did_req_region_f = 1; + } + + /* Initialize SCSI Host structure */ + host = scsi_register(host_template, sizeof(adapter_t)); + + if(!host) goto fail_attach; + + did_scsi_reg_f = 1; + + scsi_set_pci_device(host, pdev); + + adapter = (adapter_t *)host->hostdata; + memset(adapter, 0, sizeof(adapter_t)); + + /* + * Allocate a pci device structure for allocations done + * internally - all of which would be in memory <4GB + */ + adapter->ipdev = kmalloc(sizeof(struct pci_dev), GFP_KERNEL); + + if( adapter->ipdev == NULL ) goto fail_attach; + + got_ipdev_f = 1; + + memcpy(adapter->ipdev, pdev, sizeof(struct pci_dev)); + + if( pci_set_dma_mask(adapter->ipdev, 0xffffffff) != 0 ) + goto fail_attach; + + printk(KERN_NOTICE + "scsi%d:Found MegaRAID controller at 0x%lx, IRQ:%d\n", + host->host_no, mega_baseport, irq); + + adapter->base = mega_baseport; + + /* Copy resource info into structure */ + INIT_LIST_HEAD(&adapter->free_list); + INIT_LIST_HEAD(&adapter->pending_list); + + adapter->flag = flag; + spin_lock_init(&adapter->lock); + host->lock = &adapter->lock; + + host->cmd_per_lun = max_cmd_per_lun; + host->max_sectors = max_sectors_per_io; + + adapter->dev = pdev; + adapter->host = host; + + adapter->host->irq = irq; + + if( flag & BOARD_MEMMAP ) { + adapter->host->base = tbase; + } + else { + adapter->host->io_port = tbase; + adapter->host->n_io_port = 16; + } + + adapter->host->unique_id = (pci_bus << 8) | pci_dev_func; + + /* + * Allocate buffer to issue internal commands. + */ + adapter->mega_buffer = pci_alloc_consistent(adapter->dev, + MEGA_BUFFER_SIZE, &adapter->buf_dma_handle); + + if( !adapter->mega_buffer ) { + printk(KERN_WARNING "megaraid: out of RAM.\n"); + goto fail_attach; + } + alloc_int_buf_f = 1; + + adapter->scb_list = kmalloc(sizeof(scb_t)*MAX_COMMANDS, + GFP_KERNEL); + + if(!adapter->scb_list) { + printk(KERN_WARNING "megaraid: out of RAM.\n"); + goto fail_attach; + } + + alloc_scb_f = 1; + + /* Request our IRQ */ + if( adapter->flag & BOARD_MEMMAP ) { + if(request_irq(irq, megaraid_isr_memmapped, SA_SHIRQ, + "megaraid", adapter)) { + printk(KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", + irq); + goto fail_attach; + } + } + else { + if(request_irq(irq, megaraid_isr_iomapped, SA_SHIRQ, + "megaraid", adapter)) { + printk(KERN_WARNING + "megaraid: Couldn't register IRQ %d!\n", + irq); + goto fail_attach; + } + } + got_irq_f = 1; + + if( mega_setup_mailbox(adapter) != 0 ) + goto fail_attach; + + did_setup_mbox_f = 1; + + if( mega_query_adapter(adapter) != 0 ) + goto fail_attach; + + /* + * Have checks for some buggy f/w + */ + if((subsysid == 0x1111) && (subsysvid == 0x1111)) { + /* + * Which firmware + */ + if (!strcmp(adapter->fw_version, "3.00") || + !strcmp(adapter->fw_version, "3.01")) { + + printk( KERN_WARNING + "megaraid: Your card is a Dell PERC " + "2/SC RAID controller with " + "firmware\nmegaraid: 3.00 or 3.01. " + "This driver is known to have " + "corruption issues\nmegaraid: with " + "those firmware versions on this " + "specific card. In order\nmegaraid: " + "to protect your data, please upgrade " + "your firmware to version\nmegaraid: " + "3.10 or later, available from the " + "Dell Technical Support web\n" + "megaraid: site at\nhttp://support." + "dell.com/us/en/filelib/download/" + "index.asp?fileid=2940\n" + ); + } + } + + /* + * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with + * firmware H.01.07, H.01.08, and H.01.09 disable 64 bit + * support, since this firmware cannot handle 64 bit + * addressing + */ + + if((subsysvid == HP_SUBSYS_VID) && + ((subsysid == 0x60E7)||(subsysid == 0x60E8))) { + + /* + * which firmware + */ + if( !strcmp(adapter->fw_version, "H01.07") || + !strcmp(adapter->fw_version, "H01.08") || + !strcmp(adapter->fw_version, "H01.09") ) { + + printk(KERN_WARNING + "megaraid: Firmware H.01.07, " + "H.01.08, and H.01.09 on 1M/2M " + "controllers\n" + "megaraid: do not support 64 bit " + "addressing.\nmegaraid: DISABLING " + "64 bit support.\n"); + adapter->flag &= ~BOARD_64BIT; + } + } + + + if(mega_is_bios_enabled(adapter)) { + mega_hbas[hba_count].is_bios_enabled = 1; + } + mega_hbas[hba_count].hostdata_addr = adapter; + + /* + * Find out which channel is raid and which is scsi. This is + * for ROMB support. + */ + mega_enum_raid_scsi(adapter); + + /* + * Find out if a logical drive is set as the boot drive. If + * there is one, will make that as the first logical drive. + * ROMB: Do we have to boot from a physical drive. Then all + * the physical drives would appear before the logical disks. + * Else, all the physical drives would be exported to the mid + * layer after logical drives. + */ + mega_get_boot_drv(adapter); + + if( ! adapter->boot_pdrv_enabled ) { + for( i = 0; i < NVIRT_CHAN; i++ ) + adapter->logdrv_chan[i] = 1; + + for( i = NVIRT_CHAN; ilogdrv_chan[i] = 0; + + adapter->mega_ch_class <<= NVIRT_CHAN; + } + else { + j = adapter->product_info.nchannels; + for( i = 0; i < j; i++ ) + adapter->logdrv_chan[i] = 0; + + for( i = j; i < NVIRT_CHAN + j; i++ ) + adapter->logdrv_chan[i] = 1; + } + + + /* + * Do we support random deletion and addition of logical + * drives + */ + adapter->read_ldidmap = 0; /* set it after first logdrv + delete cmd */ + adapter->support_random_del = mega_support_random_del(adapter); + + /* Initialize SCBs */ + if(mega_init_scb(adapter)) { + goto fail_attach; + } + + /* + * Reset the pending commands counter + */ + atomic_set(&adapter->pend_cmds, 0); + + /* + * Reset the adapter quiescent flag + */ + atomic_set(&adapter->quiescent, 0); + + hba_soft_state[hba_count] = adapter; + + /* + * Fill in the structure which needs to be passed back to the + * application when it does an ioctl() for controller related + * information. + */ + i = hba_count; + + mcontroller[i].base = mega_baseport; + mcontroller[i].irq = irq; + mcontroller[i].numldrv = adapter->numldrv; + mcontroller[i].pcibus = pci_bus; + mcontroller[i].pcidev = pci_device; + mcontroller[i].pcifun = PCI_FUNC (pci_dev_func); + mcontroller[i].pciid = -1; + mcontroller[i].pcivendor = pci_vendor; + mcontroller[i].pcislot = PCI_SLOT (pci_dev_func); + mcontroller[i].uid = (pci_bus << 8) | pci_dev_func; + + + /* Set the Mode of addressing to 64 bit if we can */ + if((adapter->flag & BOARD_64BIT)&&(sizeof(dma_addr_t) == 8)) { + pci_set_dma_mask(pdev, 0xffffffffffffffff); + adapter->has_64bit_addr = 1; + } + else { + pci_set_dma_mask(pdev, 0xffffffff); + adapter->has_64bit_addr = 0; + } + + init_MUTEX(&adapter->int_mtx); + init_waitqueue_head(&adapter->int_waitq); + + adapter->this_id = DEFAULT_INITIATOR_ID; + adapter->host->this_id = DEFAULT_INITIATOR_ID; + +#if MEGA_HAVE_CLUSTERING + /* + * Is cluster support enabled on this controller + * Note: In a cluster the HBAs ( the initiators ) will have + * different target IDs and we cannot assume it to be 7. Call + * to mega_support_cluster() will get the target ids also if + * the cluster support is available + */ + adapter->has_cluster = mega_support_cluster(adapter); + + if( adapter->has_cluster ) { + printk(KERN_NOTICE + "megaraid: Cluster driver, initiator id:%d\n", + adapter->this_id); + } #endif -static mega_scb *pLastScb = NULL; -static struct notifier_block mega_notifier = { - megaraid_reboot_notify, - NULL, - 0 -}; + hba_count++; + continue; + +fail_attach: + if( did_setup_mbox_f ) { + pci_free_consistent(adapter->dev, sizeof(mbox64_t), + (void *)adapter->una_mbox64, + adapter->una_mbox64_dma); + } + + if( got_irq_f ) { + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); + } + + if( alloc_scb_f ) { + kfree(adapter->scb_list); + } + + if( alloc_int_buf_f ) { + pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE, + (void *)adapter->mega_buffer, + adapter->buf_dma_handle); + } + + if( got_ipdev_f ) kfree(adapter->ipdev); + + if( did_scsi_reg_f ) scsi_unregister(host); + + if( did_ioremap_f ) { + iounmap((void *)mega_baseport); + release_mem_region(tbase, 128); + } + + if( did_req_region_f ) + release_region(mega_baseport, 16); + } + + return; +} + + +/** + * mega_setup_mailbox() + * @adapter - pointer to our soft state + * + * Allocates a 8 byte aligned memory for the handshake mailbox. + */ +static int +mega_setup_mailbox(adapter_t *adapter) +{ + unsigned long align; + + adapter->una_mbox64 = pci_alloc_consistent(adapter->dev, + sizeof(mbox64_t), &adapter->una_mbox64_dma); + + if( !adapter->una_mbox64 ) return -1; + + adapter->mbox = &adapter->una_mbox64->mbox; + + adapter->mbox = (mbox_t *)((((unsigned long) adapter->mbox) + 15) & + (~0UL ^ 0xFUL)); + + adapter->mbox64 = (mbox64_t *)(((unsigned long)adapter->mbox) - 8); + + align = ((void *)adapter->mbox) - ((void *)&adapter->una_mbox64->mbox); + + adapter->mbox_dma = adapter->una_mbox64_dma + 8 + align; + + /* + * Register the mailbox if the controller is an io-mapped controller + */ + if( adapter->flag & BOARD_IOMAP ) { + + outb_p(adapter->mbox_dma & 0xFF, + adapter->host->io_port + MBOX_PORT0); + + outb_p((adapter->mbox_dma >> 8) & 0xFF, + adapter->host->io_port + MBOX_PORT1); + + outb_p((adapter->mbox_dma >> 16) & 0xFF, + adapter->host->io_port + MBOX_PORT2); + + outb_p((adapter->mbox_dma >> 24) & 0xFF, + adapter->host->io_port + MBOX_PORT3); + + outb_p(ENABLE_MBOX_BYTE, + adapter->host->io_port + ENABLE_MBOX_REGION); + + irq_ack(adapter); + + irq_enable(adapter); + } + + return 0; +} -/* For controller re-ordering */ -struct mega_hbas mega_hbas[MAX_CONTROLLERS]; /* - * The File Operations structure for the serial/ioctl interface of the driver + * mega_query_adapter() + * @adapter - pointer to our soft state + * + * Issue the adapter inquiry commands to the controller and find out + * information and parameter about the devices attached */ -/* For controller re-ordering */ +static int +mega_query_adapter(adapter_t *adapter) +{ + dma_addr_t prod_info_dma_handle; + mega_inquiry3 *inquiry3; + u8 raw_mbox[16]; + mbox_t *mbox; + int retval; + + /* Initialize adapter inquiry mailbox */ + + mbox = (mbox_t *)raw_mbox; + + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); + memset(mbox, 0, 16); + + /* + * Try to issue Inquiry3 command + * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and + * update enquiry3 structure + */ + mbox->xferaddr = (u32)adapter->buf_dma_handle; + + inquiry3 = (mega_inquiry3 *)adapter->mega_buffer; + + raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ + raw_mbox[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ + raw_mbox[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ + + /* Issue a blocking command to the card */ + if ((retval = issue_scb_block(adapter, raw_mbox))) { + /* the adapter does not support 40ld */ + + mraid_ext_inquiry *ext_inq; + mraid_inquiry *inq; + dma_addr_t dma_handle; + + ext_inq = pci_alloc_consistent(adapter->dev, + sizeof(mraid_ext_inquiry), &dma_handle); + + if( ext_inq == NULL ) return -1; + + inq = &ext_inq->raid_inq; + + mbox->xferaddr = (u32)dma_handle; + + /*issue old 0x04 command to adapter */ + mbox->cmd = MEGA_MBOXCMD_ADPEXTINQ; + + issue_scb_block(adapter, raw_mbox); + + /* + * update Enquiry3 and ProductInfo structures with + * mraid_inquiry structure + */ + mega_8_to_40ld(inq, inquiry3, + (mega_product_info *)&adapter->product_info); + + pci_free_consistent(adapter->dev, sizeof(mraid_ext_inquiry), + ext_inq, dma_handle); + + } else { /*adapter supports 40ld */ + adapter->flag |= BOARD_40LD; + + /* + * get product_info, which is static information and will be + * unchanged + */ + prod_info_dma_handle = pci_map_single(adapter->dev, (void *) + &adapter->product_info, + sizeof(mega_product_info), PCI_DMA_FROMDEVICE); + + mbox->xferaddr = prod_info_dma_handle; + + raw_mbox[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ + raw_mbox[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ + + if ((retval = issue_scb_block(adapter, raw_mbox))) + printk(KERN_WARNING + "megaraid: Product_info cmd failed with error: %d\n", + retval); + + pci_dma_sync_single(adapter->dev, prod_info_dma_handle, + sizeof(mega_product_info), + PCI_DMA_FROMDEVICE); + + pci_unmap_single(adapter->dev, prod_info_dma_handle, + sizeof(mega_product_info), PCI_DMA_FROMDEVICE); + } + + + /* + * kernel scans the channels from 0 to <= max_channel + */ + adapter->host->max_channel = + adapter->product_info.nchannels + NVIRT_CHAN -1; + + adapter->host->max_id = 16; /* max targets per channel */ + + adapter->host->max_lun = 7; /* Upto 7 luns for non disk devices */ + + adapter->host->cmd_per_lun = max_cmd_per_lun; + + adapter->numldrv = inquiry3->num_ldrv; + + adapter->max_cmds = adapter->product_info.max_commands; + + if(adapter->max_cmds > MAX_COMMANDS) + adapter->max_cmds = MAX_COMMANDS; + + adapter->host->can_queue = adapter->max_cmds - 1; + + /* + * Get the maximum number of scatter-gather elements supported by this + * firmware + */ + mega_get_max_sgl(adapter); + + adapter->host->sg_tablesize = adapter->sglen; + + + /* use HP firmware and bios version encoding */ + if (adapter->product_info.subsysvid == HP_SUBSYS_VID) { + sprintf (adapter->fw_version, "%c%d%d.%d%d", + adapter->product_info.fw_version[2], + adapter->product_info.fw_version[1] >> 8, + adapter->product_info.fw_version[1] & 0x0f, + adapter->product_info.fw_version[0] >> 8, + adapter->product_info.fw_version[0] & 0x0f); + sprintf (adapter->bios_version, "%c%d%d.%d%d", + adapter->product_info.bios_version[2], + adapter->product_info.bios_version[1] >> 8, + adapter->product_info.bios_version[1] & 0x0f, + adapter->product_info.bios_version[0] >> 8, + adapter->product_info.bios_version[0] & 0x0f); + } else { + memcpy(adapter->fw_version, + (char *)adapter->product_info.fw_version, 4); + adapter->fw_version[4] = 0; + + memcpy(adapter->bios_version, + (char *)adapter->product_info.bios_version, 4); + + adapter->bios_version[4] = 0; + } + + printk(KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives.\n", + adapter->fw_version, adapter->bios_version, adapter->numldrv); + + /* + * Do we support extended (>10 bytes) cdbs + */ + adapter->support_ext_cdb = mega_support_ext_cdb(adapter); + if (adapter->support_ext_cdb) + printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); + + + return 0; +} + + +/* + * megaraid_queue() + * @scmd - Issue this scsi command + * @done - the callback hook into the scsi mid-layer + * + * The command queuing entry point for the mid-layer. + */ +static int +megaraid_queue(Scsi_Cmnd *scmd, void (*done)(Scsi_Cmnd *)) +{ + adapter_t *adapter; + scb_t *scb; + int busy=0; + + adapter = (adapter_t *)scmd->host->hostdata; + + scmd->scsi_done = done; + + + /* + * Allocate and build a SCB request + * busy flag will be set if mega_build_cmd() command could not + * allocate scb. We will return non-zero status in that case. + * NOTE: scb can be null even though certain commands completed + * successfully, e.g., MODE_SENSE and TEST_UNIT_READY, we would + * return 0 in that case. + */ + + scb = mega_build_cmd(adapter, scmd, &busy); + + if(scb) { + scb->state |= SCB_PENDQ; + list_add_tail(&scb->list, &adapter->pending_list); + + /* + * Check if the HBA is in quiescent state, e.g., during a + * delete logical drive opertion. If it is, don't run + * the pending_list. + */ + if(atomic_read(&adapter->quiescent) == 0) { + mega_runpendq(adapter); + } + return 0; + } + + return busy; +} + + +/** + * mega_build_cmd() + * @adapter - pointer to our soft state + * @cmd - Prepare using this scsi command + * @busy - busy flag if no resources + * + * Prepares a command and scatter gather list for the controller. This routine + * also finds out if the commands is intended for a logical drive or a + * physical device and prepares the controller command accordingly. + * + * We also re-order the logical drives and physical devices based on their + * boot settings. + */ +static scb_t * +mega_build_cmd(adapter_t *adapter, Scsi_Cmnd *cmd, int *busy) +{ + mega_ext_passthru *epthru; + mega_passthru *pthru; + scb_t *scb; + mbox_t *mbox; + long seg; + char islogical; + int max_ldrv_num; + int channel = 0; + int target = 0; + int ldrv_num = 0; /* logical drive number */ + + + /* + * filter the internal and ioctl commands + */ + if((cmd->cmnd[0] == MEGA_INTERNAL_CMD)) { + return cmd->buffer; + } + + + /* + * We know what channels our logical drives are on - mega_find_card() + */ + islogical = adapter->logdrv_chan[cmd->channel]; + + /* + * The theory: If physical drive is chosen for boot, all the physical + * devices are exported before the logical drives, otherwise physical + * devices are pushed after logical drives, in which case - Kernel sees + * the physical devices on virtual channel which is obviously converted + * to actual channel on the HBA. + */ + if( adapter->boot_pdrv_enabled ) { + if( islogical ) { + /* logical channel */ + channel = cmd->channel - + adapter->product_info.nchannels; + } + else { + channel = cmd->channel; /* this is physical channel */ + target = cmd->target; + + /* + * boot from a physical disk, that disk needs to be + * exposed first IF both the channels are SCSI, then + * booting from the second channel is not allowed. + */ + if( target == 0 ) { + target = adapter->boot_pdrv_tgt; + } + else if( target == adapter->boot_pdrv_tgt ) { + target = 0; + } + } + } + else { + if( islogical ) { + channel = cmd->channel; /* this is the logical channel + */ + } + else { + channel = cmd->channel - NVIRT_CHAN; /* physical + channel */ + target = cmd->target; + } + } + + + if(islogical) { + + /* have just LUN 0 for each target on virtual channels */ + if (cmd->lun) { + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + + ldrv_num = mega_get_ldrv_num(adapter, cmd, channel); + + + max_ldrv_num = (adapter->flag & BOARD_40LD) ? + MAX_LOGICAL_DRIVES_40LD : MAX_LOGICAL_DRIVES_8LD; + + /* + * max_ldrv_num increases by 0x80 if some logical drive was + * deleted. + */ + if(adapter->read_ldidmap) + max_ldrv_num += 0x80; + + if(ldrv_num > max_ldrv_num ) { + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + + } + else { + if( cmd->lun > 7) { + /* + * Do not support lun >7 for physically accessed + * devices + */ + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + } + + /* + * + * Logical drive commands + * + */ + if(islogical) { + switch (cmd->cmnd[0]) { + case TEST_UNIT_READY: + memset(cmd->request_buffer, 0, cmd->request_bufflen); + +#if MEGA_HAVE_CLUSTERING + /* + * Do we support clustering and is the support enabled + * If no, return success always + */ + if( !adapter->has_cluster ) { + cmd->result = (DID_OK << 16); + cmd->scsi_done(cmd); + return NULL; + } + + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + + scb->raw_mbox[0] = MEGA_CLUSTER_CMD; + scb->raw_mbox[2] = MEGA_RESERVATION_STATUS; + scb->raw_mbox[3] = ldrv_num; + + scb->dma_direction = PCI_DMA_NONE; + + return scb; +#else + cmd->result = (DID_OK << 16); + cmd->scsi_done(cmd); + return NULL; +#endif + + case MODE_SENSE: + memset(cmd->request_buffer, 0, cmd->cmnd[4]); + cmd->result = (DID_OK << 16); + cmd->scsi_done(cmd); + return NULL; + + case READ_CAPACITY: + case INQUIRY: + + if(!(adapter->flag & (1L << cmd->channel))) { + + printk(KERN_NOTICE + "scsi%d: scanning scsi channel %d ", + adapter->host->host_no, + cmd->channel); + printk("for logical drives.\n"); + + adapter->flag |= (1L << cmd->channel); + } + + /* Allocate a SCB and initialize passthru */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + pthru = scb->pthru; + + mbox = (mbox_t *)scb->raw_mbox; + memset(mbox, 0, sizeof(scb->raw_mbox)); + memset(pthru, 0, sizeof(mega_passthru)); + + pthru->timeout = 0; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 1; + pthru->logdrv = ldrv_num; + pthru->cdblen = cmd->cmd_len; + memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len); + + if( adapter->has_64bit_addr ) { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU64; + } + else { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + } + + scb->dma_direction = PCI_DMA_FROMDEVICE; + + pthru->numsgelements = mega_build_sglist(adapter, scb, + &pthru->dataxferaddr, &pthru->dataxferlen); + + mbox->xferaddr = scb->pthru_dma_addr; + + return scb; + + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + + /* Allocate a SCB and initialize mailbox */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + mbox = (mbox_t *)scb->raw_mbox; + + memset(mbox, 0, sizeof(scb->raw_mbox)); + mbox->logdrv = ldrv_num; + + /* + * A little hack: 2nd bit is zero for all scsi read + * commands and is set for all scsi write commands + */ + if( adapter->has_64bit_addr ) { + mbox->cmd = (*cmd->cmnd & 0x02) ? + MEGA_MBOXCMD_LWRITE64: + MEGA_MBOXCMD_LREAD64 ; + } + else { + mbox->cmd = (*cmd->cmnd & 0x02) ? + MEGA_MBOXCMD_LWRITE: + MEGA_MBOXCMD_LREAD ; + } + + /* + * 6-byte READ(0x08) or WRITE(0x0A) cdb + */ + if( cmd->cmd_len == 6 ) { + mbox->numsectors = (u32) cmd->cmnd[4]; + mbox->lba = + ((u32)cmd->cmnd[1] << 16) | + ((u32)cmd->cmnd[2] << 8) | + (u32)cmd->cmnd[3]; + + mbox->lba &= 0x1FFFFF; + +#if MEGA_HAVE_STATS + /* + * Take modulo 0x80, since the logical drive + * number increases by 0x80 when a logical + * drive was deleted + */ + if (*cmd->cmnd == READ_6) { + adapter->nreads[ldrv_num%0x80]++; + adapter->nreadblocks[ldrv_num%0x80] += + mbox->numsectors; + } else { + adapter->nwrites[ldrv_num%0x80]++; + adapter->nwriteblocks[ldrv_num%0x80] += + mbox->numsectors; + } +#endif + } + + /* + * 10-byte READ(0x28) or WRITE(0x2A) cdb + */ + if( cmd->cmd_len == 10 ) { + mbox->numsectors = + (u32)cmd->cmnd[8] | + ((u32)cmd->cmnd[7] << 8); + mbox->lba = + ((u32)cmd->cmnd[2] << 24) | + ((u32)cmd->cmnd[3] << 16) | + ((u32)cmd->cmnd[4] << 8) | + (u32)cmd->cmnd[5]; + +#if MEGA_HAVE_STATS + if (*cmd->cmnd == READ_10) { + adapter->nreads[ldrv_num%0x80]++; + adapter->nreadblocks[ldrv_num%0x80] += + mbox->numsectors; + } else { + adapter->nwrites[ldrv_num%0x80]++; + adapter->nwriteblocks[ldrv_num%0x80] += + mbox->numsectors; + } +#endif + } + + /* + * 12-byte READ(0xA8) or WRITE(0xAA) cdb + */ + if( cmd->cmd_len == 12 ) { + mbox->lba = + ((u32)cmd->cmnd[2] << 24) | + ((u32)cmd->cmnd[3] << 16) | + ((u32)cmd->cmnd[4] << 8) | + (u32)cmd->cmnd[5]; + + mbox->numsectors = + ((u32)cmd->cmnd[6] << 24) | + ((u32)cmd->cmnd[7] << 16) | + ((u32)cmd->cmnd[8] << 8) | + (u32)cmd->cmnd[9]; + +#if MEGA_HAVE_STATS + if (*cmd->cmnd == READ_12) { + adapter->nreads[ldrv_num%0x80]++; + adapter->nreadblocks[ldrv_num%0x80] += + mbox->numsectors; + } else { + adapter->nwrites[ldrv_num%0x80]++; + adapter->nwriteblocks[ldrv_num%0x80] += + mbox->numsectors; + } +#endif + } + + /* + * If it is a read command + */ + if( (*cmd->cmnd & 0x0F) == 0x08 ) { + scb->dma_direction = PCI_DMA_FROMDEVICE; + } + else { + scb->dma_direction = PCI_DMA_TODEVICE; + } + + /* Calculate Scatter-Gather info */ + mbox->numsgelements = mega_build_sglist(adapter, scb, + (u32 *)&mbox->xferaddr, (u32 *)&seg); + + return scb; + +#if MEGA_HAVE_CLUSTERING + case RESERVE: /* Fall through */ + case RELEASE: + + /* + * Do we support clustering and is the support enabled + */ + if( ! adapter->has_cluster ) { + + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + + /* Allocate a SCB and initialize mailbox */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + + scb->raw_mbox[0] = MEGA_CLUSTER_CMD; + scb->raw_mbox[2] = ( *cmd->cmnd == RESERVE ) ? + MEGA_RESERVE_LD : MEGA_RELEASE_LD; + + scb->raw_mbox[3] = ldrv_num; + + scb->dma_direction = PCI_DMA_NONE; + + return scb; +#endif + + default: + cmd->result = (DID_BAD_TARGET << 16); + cmd->scsi_done(cmd); + return NULL; + } + } + + /* + * Passthru drive commands + */ + else { + /* Allocate a SCB and initialize passthru */ + if(!(scb = mega_allocate_scb(adapter, cmd))) { + + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); + *busy = 1; + + return NULL; + } + + mbox = (mbox_t *)scb->raw_mbox; + memset(mbox, 0, sizeof(scb->raw_mbox)); + + if( adapter->support_ext_cdb ) { + + epthru = mega_prepare_extpassthru(adapter, scb, cmd, + channel, target); + + mbox->cmd = MEGA_MBOXCMD_EXTPTHRU; + + mbox->xferaddr = scb->epthru_dma_addr; + + } + else { + + pthru = mega_prepare_passthru(adapter, scb, cmd, + channel, target); + + /* Initialize mailbox */ + if( adapter->has_64bit_addr ) { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU64; + } + else { + mbox->cmd = MEGA_MBOXCMD_PASSTHRU; + } + + mbox->xferaddr = scb->pthru_dma_addr; + + } + return scb; + } + return NULL; +} + + +/** + * mega_prepare_passthru() + * @adapter - pointer to our soft state + * @scb - our scsi control block + * @cmd - scsi command from the mid-layer + * @channel - actual channel on the controller + * @target - actual id on the controller. + * + * prepare a command for the scsi physical devices. + */ +static mega_passthru * +mega_prepare_passthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd, + int channel, int target) +{ + mega_passthru *pthru; + + pthru = scb->pthru; + memset(pthru, 0, sizeof (mega_passthru)); + + /* 0=6sec/1=60sec/2=10min/3=3hrs */ + pthru->timeout = 2; + + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 0; + + pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel; + + pthru->target = (adapter->flag & BOARD_40LD) ? + (channel << 4) | target : target; + + pthru->cdblen = cmd->cmd_len; + pthru->logdrv = cmd->lun; + + memcpy(pthru->cdb, cmd->cmnd, cmd->cmd_len); + + /* Not sure about the direction */ + scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + + /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ + switch (cmd->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + if(!(adapter->flag & (1L << cmd->channel))) { + + printk(KERN_NOTICE + "scsi%d: scanning scsi channel %d [P%d] ", + adapter->host->host_no, + cmd->channel, channel); + printk("for physical devices.\n"); + + adapter->flag |= (1L << cmd->channel); + } + /* Fall through */ + default: + pthru->numsgelements = mega_build_sglist(adapter, scb, + &pthru->dataxferaddr, &pthru->dataxferlen); + break; + } + return pthru; +} + + +/** + * mega_prepare_extpassthru() + * @adapter - pointer to our soft state + * @scb - our scsi control block + * @cmd - scsi command from the mid-layer + * @channel - actual channel on the controller + * @target - actual id on the controller. + * + * prepare a command for the scsi physical devices. This rountine prepares + * commands for devices which can take extended CDBs (>10 bytes) + */ +static mega_ext_passthru * +mega_prepare_extpassthru(adapter_t *adapter, scb_t *scb, Scsi_Cmnd *cmd, + int channel, int target) +{ + mega_ext_passthru *epthru; + + epthru = scb->epthru; + memset(epthru, 0, sizeof(mega_ext_passthru)); + + /* 0=6sec/1=60sec/2=10min/3=3hrs */ + epthru->timeout = 2; + + epthru->ars = 1; + epthru->reqsenselen = 14; + epthru->islogical = 0; + + epthru->channel = (adapter->flag & BOARD_40LD) ? 0 : channel; + epthru->target = (adapter->flag & BOARD_40LD) ? + (channel << 4) | target : target; + + epthru->cdblen = cmd->cmd_len; + epthru->logdrv = cmd->lun; + + memcpy(epthru->cdb, cmd->cmnd, cmd->cmd_len); + + /* Not sure about the direction */ + scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + + switch(cmd->cmnd[0]) { + case INQUIRY: + case READ_CAPACITY: + if(!(adapter->flag & (1L << cmd->channel))) { + + printk(KERN_NOTICE + "scsi%d: scanning scsi channel %d [P%d] ", + adapter->host->host_no, + cmd->channel, channel); + printk("for physical devices.\n"); + + adapter->flag |= (1L << cmd->channel); + } + /* Fall through */ + default: + epthru->numsgelements = mega_build_sglist(adapter, scb, + &epthru->dataxferaddr, &epthru->dataxferlen); + break; + } + + return epthru; +} + + +/** + * mega_allocate_scb() + * @adapter - pointer to our soft state + * @cmd - scsi command from the mid-layer + * + * Allocate a SCB structure. This is the central structure for controller + * commands. + */ +static inline scb_t * +mega_allocate_scb(adapter_t *adapter, Scsi_Cmnd *cmd) +{ + struct list_head *head = &adapter->free_list; + scb_t *scb; + + /* Unlink command from Free List */ + if( !list_empty(head) ) { + + scb = list_entry(head->next, scb_t, list); + + list_del_init(head->next); + + scb->state = SCB_ACTIVE; + scb->cmd = cmd; + scb->dma_type = MEGA_DMA_TYPE_NONE; + + return scb; + } + + return NULL; +} + + +/** + * mega_runpendq() + * @adapter - pointer to our soft state + * + * Runs through the list of pending requests. + */ +static inline void +mega_runpendq(adapter_t *adapter) +{ + if(!list_empty(&adapter->pending_list)) + __mega_runpendq(adapter); +} + +static void +__mega_runpendq(adapter_t *adapter) +{ + scb_t *scb; + struct list_head *pos, *next; + + /* Issue any pending commands to the card */ + list_for_each_safe(pos, next, &adapter->pending_list) { + + scb = list_entry(pos, scb_t, list); + + if( !(scb->state & SCB_ISSUED) ) { + + if( issue_scb(adapter, scb) != 0 ) + return; + } + } + + return; +} + + +/** + * issue_scb() + * @adapter - pointer to our soft state + * @scb - scsi control block + * + * Post a command to the card if the mailbox is available, otherwise return + * busy. We also take the scb from the pending list if the mailbox is + * available. + */ +static inline int +issue_scb(adapter_t *adapter, scb_t *scb) +{ + volatile mbox64_t *mbox64 = adapter->mbox64; + volatile mbox_t *mbox = adapter->mbox; + unsigned int i = 0; + + if(unlikely(mbox->busy)) { + do { + udelay(1); + i++; + } while( mbox->busy && (i < max_mbox_busy_wait) ); + + if(mbox->busy) return -1; + } + + /* Copy mailbox data into host structure */ + memcpy((char *)mbox, (char *)scb->raw_mbox, 16); + + mbox->cmdid = scb->idx; /* Set cmdid */ + mbox->busy = 1; /* Set busy */ + + + /* + * Increment the pending queue counter + */ + atomic_inc(&adapter->pend_cmds); + + switch (mbox->cmd) { + case MEGA_MBOXCMD_LREAD64: + case MEGA_MBOXCMD_LWRITE64: + case MEGA_MBOXCMD_PASSTHRU64: + case MEGA_MBOXCMD_EXTPTHRU: + mbox64->xfer_segment_lo = mbox->xferaddr; + mbox64->xfer_segment_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + break; + default: + mbox64->xfer_segment_lo = 0; + mbox64->xfer_segment_hi = 0; + } + + /* + * post the command + */ + scb->state |= SCB_ISSUED; + + if( likely(adapter->flag & BOARD_MEMMAP) ) { + mbox->poll = 0; + mbox->ack = 0; + WRINDOOR(adapter, adapter->mbox_dma | 0x1); + } + else { + irq_enable(adapter); + issue_command(adapter); + } + + return 0; +} + + +/** + * issue_scb_block() + * @adapter - pointer to our soft state + * @raw_mbox - the mailbox + * + * Issue a scb in synchronous and non-interrupt mode + */ +static int +issue_scb_block(adapter_t *adapter, u_char *raw_mbox) +{ + volatile mbox64_t *mbox64 = adapter->mbox64; + volatile mbox_t *mbox = adapter->mbox; + u8 byte; + + raw_mbox[0x1] = 0xFE; /* Set cmdid */ + raw_mbox[0xF] = 1; /* Set busy */ + + /* Wait until mailbox is free */ + if(mega_busywait_mbox (adapter)) + goto bug_blocked_mailbox; + + /* Copy mailbox data into host structure */ + memcpy((char *) mbox, raw_mbox, 16); + + switch (raw_mbox[0]) { + case MEGA_MBOXCMD_LREAD64: + case MEGA_MBOXCMD_LWRITE64: + case MEGA_MBOXCMD_PASSTHRU64: + case MEGA_MBOXCMD_EXTPTHRU: + mbox64->xfer_segment_lo = mbox->xferaddr; + mbox64->xfer_segment_hi = 0; + mbox->xferaddr = 0xFFFFFFFF; + break; + default: + mbox64->xfer_segment_lo = 0; + mbox64->xfer_segment_hi = 0; + } + + if( likely(adapter->flag & BOARD_MEMMAP) ) { + mbox->poll = 0; + mbox->ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; + WRINDOOR(adapter, adapter->mbox_dma | 0x1); + + while((volatile u8)mbox->numstatus == 0xFF) + cpu_relax(); + + mbox->numstatus = 0xFF; + + while( (volatile u8)mbox->poll != 0x77 ) + cpu_relax(); + + mbox->poll = 0; + mbox->ack = 0x77; + + WRINDOOR(adapter, adapter->mbox_dma | 0x2); + + while(RDINDOOR(adapter) & 0x2) + cpu_relax(); + } + else { + irq_disable(adapter); + issue_command(adapter); + + while (!((byte = irq_state(adapter)) & INTR_VALID)) + cpu_relax(); + + set_irq_state(adapter, byte); + irq_enable(adapter); + irq_ack(adapter); + } + + return mbox->status; + +bug_blocked_mailbox: + printk(KERN_WARNING "megaraid: Blocked mailbox......!!\n"); + udelay (1000); + return -1; +} + + +/** + * megaraid_isr_iomapped() + * @irq - irq + * @devp - pointer to our soft state + * @regs - unused + * + * Interrupt service routine for io-mapped controllers. + * Find out if our device is interrupting. If yes, acknowledge the interrupt + * and service the completed commands. + */ +static void +megaraid_isr_iomapped(int irq, void *devp, struct pt_regs *regs) +{ + adapter_t *adapter = devp; + unsigned long flags; + + + spin_lock_irqsave(&adapter->lock, flags); + + megaraid_iombox_ack_sequence(adapter); + + /* Loop through any pending requests */ + if( atomic_read(&adapter->quiescent ) == 0) { + mega_runpendq(adapter); + } + + spin_unlock_irqrestore(&adapter->lock, flags); + + return; +} + + +/** + * megaraid_iombox_ack_sequence - interrupt ack sequence for IO mapped HBAs + * @adapter - controller's soft state + * + * Interrupt ackrowledgement sequence for IO mapped HBAs + */ +static inline void +megaraid_iombox_ack_sequence(adapter_t *adapter) +{ + u8 status; + u8 nstatus; + u8 completed[MAX_FIRMWARE_STATUS]; + u8 byte; + + + /* + * loop till F/W has more commands for us to complete. + */ + do { + /* Check if a valid interrupt is pending */ + byte = irq_state(adapter); + if( (byte & VALID_INTR_BYTE) == 0 ) { + return; + } + set_irq_state(adapter, byte); + + while((nstatus = (volatile u8)adapter->mbox->numstatus) + == 0xFF) + cpu_relax(); + adapter->mbox->numstatus = 0xFF; + + status = adapter->mbox->status; + + /* + * decrement the pending queue counter + */ + atomic_sub(nstatus, &adapter->pend_cmds); + + memcpy(completed, (void *)adapter->mbox->completed, nstatus); + + /* Acknowledge interrupt */ + irq_ack(adapter); + + mega_cmd_done(adapter, completed, nstatus, status); + + } while(1); +} + + +/** + * megaraid_isr_memmapped() + * @irq - irq + * @devp - pointer to our soft state + * @regs - unused + * + * Interrupt service routine for memory-mapped controllers. + * Find out if our device is interrupting. If yes, acknowledge the interrupt + * and service the completed commands. + */ +static void +megaraid_isr_memmapped(int irq, void *devp, struct pt_regs *regs) +{ + adapter_t *adapter = devp; + unsigned long flags; + + + spin_lock_irqsave(&adapter->lock, flags); + + megaraid_memmbox_ack_sequence(adapter); + + /* Loop through any pending requests */ + if(atomic_read(&adapter->quiescent) == 0) { + mega_runpendq(adapter); + } + + spin_unlock_irqrestore(&adapter->lock, flags); + + return; +} + + +/** + * megaraid_memmbox_ack_sequence - interrupt ack sequence for memory mapped HBAs + * @adapter - controller's soft state + * + * Interrupt ackrowledgement sequence for memory mapped HBAs + */ +static inline void +megaraid_memmbox_ack_sequence(adapter_t *adapter) +{ + u8 status; + u32 dword = 0; + u8 nstatus; + u8 completed[MAX_FIRMWARE_STATUS]; + + + /* + * loop till F/W has more commands for us to complete. + */ + do { + /* Check if a valid interrupt is pending */ + dword = RDOUTDOOR(adapter); + if( dword != 0x10001234 ) { + /* + * No more pending commands + */ + return; + } + WROUTDOOR(adapter, 0x10001234); + + while((nstatus = adapter->mbox->numstatus) == 0xFF) { + cpu_relax(); + } + adapter->mbox->numstatus = 0xFF; + + status = adapter->mbox->status; + + /* + * decrement the pending queue counter + */ + atomic_sub(nstatus, &adapter->pend_cmds); + + memcpy(completed, (void *)adapter->mbox->completed, nstatus); + + /* Acknowledge interrupt */ + WRINDOOR(adapter, 0x2); + + while( RDINDOOR(adapter) & 0x02 ) cpu_relax(); + + mega_cmd_done(adapter, completed, nstatus, status); + + } while(1); +} + + +/** + * mega_cmd_done() + * @adapter - pointer to our soft state + * @completed - array of ids of completed commands + * @nstatus - number of completed commands + * @status - status of the last command completed + * + * Complete the comamnds and call the scsi mid-layer callback hooks. + */ +static inline void +mega_cmd_done(adapter_t *adapter, u8 completed[], int nstatus, int status) +{ + mega_ext_passthru *epthru = NULL; + struct scatterlist *sgl; + Scsi_Cmnd *cmd = NULL; + mega_passthru *pthru = NULL; + mbox_t *mbox = NULL; + u8 c; + scb_t *scb; + int cmdid; + int i; + + /* + * for all the commands completed, call the mid-layer callback routine + * and free the scb. + */ + for( i = 0; i < nstatus; i++ ) { + + cmdid = completed[i]; + + if( cmdid == CMDID_INT_CMDS ) { /* internal command */ + scb = &adapter->int_scb; + cmd = scb->cmd; + mbox = (mbox_t *)scb->raw_mbox; + + /* + * Internal command interface do not fire the extended + * passthru or 64-bit passthru + */ + pthru = scb->pthru; + + } + else { + scb = &adapter->scb_list[cmdid]; + cmd = scb->cmd; + pthru = scb->pthru; + epthru = scb->epthru; + mbox = (mbox_t *)scb->raw_mbox; + + /* + * Make sure f/w has completed a valid command + */ + if( !(scb->state & SCB_ISSUED) || scb->cmd == NULL ) { + printk(KERN_CRIT + "megaraid: invalid command "); + printk("Id %d, scb->state:%x, scsi cmd:%p\n", + cmdid, scb->state, scb->cmd); + + continue; + } + + /* + * Was an abort issued for this command + */ + if( scb->state & SCB_ABORT ) { + + printk(KERN_NOTICE + "megaraid: aborted cmd %lx[%x] complete.\n", + scb->cmd->serial_number, scb->idx); + + cmd->result = (DID_ABORT << 16); + + mega_free_scb(adapter, scb); + + cmd->scsi_done(cmd); + + continue; + } + + /* + * Was a reset issued for this command + */ + if( scb->state & SCB_RESET ) { + + printk(KERN_WARNING + "megaraid: reset cmd %lx[%x] complete.\n", + scb->cmd->serial_number, scb->idx); + + scb->cmd->result = (DID_RESET << 16); + + mega_free_scb (adapter, scb); + + cmd->scsi_done(cmd); + + continue; + } + +#if MEGA_HAVE_STATS + { + + int islogical = adapter->logdrv_chan[cmd->channel]; + int logdrv = mbox->logdrv; + + /* + * Maintain an error counter for the logical drive. + * Some application like SNMP agent need such + * statistics + */ + if( status && islogical && (cmd->cmnd[0] == READ_6 || + cmd->cmnd[0] == READ_10 || + cmd->cmnd[0] == READ_12)) { + /* + * Logical drive number increases by 0x80 when + * a logical drive is deleted + */ + adapter->rd_errors[logdrv%0x80]++; + } + + if( status && islogical && (cmd->cmnd[0] == WRITE_6 || + cmd->cmnd[0] == WRITE_10 || + cmd->cmnd[0] == WRITE_12)) { + /* + * Logical drive number increases by 0x80 when + * a logical drive is deleted + */ + adapter->wr_errors[logdrv%0x80]++; + } + + } +#endif + } + + /* + * Do not return the presence of hard disk on the channel so, + * inquiry sent, and returned data==hard disk or removable + * hard disk and not logical, request should return failure! - + * PJ + */ + if(cmd->cmnd[0] == INQUIRY) { + int islogical = adapter->logdrv_chan[cmd->channel]; + + if(!islogical) { + if( cmd->use_sg ) { + sgl = (struct scatterlist *) + cmd->request_buffer; + c = *(u8 *)sgl[0].address; + } + else { + c = *(u8 *)cmd->request_buffer; + } + + if(IS_RAID_CH(adapter, cmd->channel) && + ((c & 0x1F ) == TYPE_DISK)) { + status = 0xF0; + } + } + } + + /* clear result; otherwise, success returns corrupt value */ + cmd->result = 0; + + /* Convert MegaRAID status to Linux error code */ + switch (status) { + case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */ + cmd->result |= (DID_OK << 16); + break; + + case 0x02: /* ERROR_ABORTED, i.e. + SCSI_STATUS_CHECK_CONDITION */ + + /* set sense_buffer and result fields */ + if( mbox->cmd == MEGA_MBOXCMD_PASSTHRU || + mbox->cmd == MEGA_MBOXCMD_PASSTHRU64 ) { + + memcpy(cmd->sense_buffer, pthru->reqsensearea, + 14); + + cmd->result = (DRIVER_SENSE << 24) | + (DID_OK << 16) | + (CHECK_CONDITION << 1); + } + else { + if (mbox->cmd == MEGA_MBOXCMD_EXTPTHRU) { + + memcpy(cmd->sense_buffer, + epthru->reqsensearea, 14); + + cmd->result = (DRIVER_SENSE << 24) | + (DID_OK << 16) | + (CHECK_CONDITION << 1); + } else { + cmd->sense_buffer[0] = 0x70; + cmd->sense_buffer[2] = ABORTED_COMMAND; + cmd->result |= (CHECK_CONDITION << 1); + } + } + break; + + case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. + SCSI_STATUS_BUSY */ + cmd->result |= (DID_BUS_BUSY << 16) | status; + break; + + default: +#if MEGA_HAVE_CLUSTERING + /* + * If TEST_UNIT_READY fails, we know + * MEGA_RESERVATION_STATUS failed + */ + if( cmd->cmnd[0] == TEST_UNIT_READY ) { + cmd->result |= (DID_ERROR << 16) | + (RESERVATION_CONFLICT << 1); + } + else + /* + * Error code returned is 1 if Reserve or Release + * failed or the input parameter is invalid + */ + if( status == 1 && + (cmd->cmnd[0] == RESERVE || + cmd->cmnd[0] == RELEASE) ) { + + cmd->result |= (DID_ERROR << 16) | + (RESERVATION_CONFLICT << 1); + } + else +#endif + cmd->result |= (DID_BAD_TARGET << 16)|status; + } + + /* + * Only free SCBs for the commands coming down from the + * mid-layer, not for which were issued internally + * + * For internal command, restore the status returned by the + * firmware so that user can interpret it. + */ + if( cmdid == CMDID_INT_CMDS ) { /* internal command */ + cmd->result = status; + + /* + * Remove the internal command from the pending list + */ + list_del_init(&scb->list); + scb->state = SCB_FREE; + } + else { + mega_free_scb(adapter, scb); + } + + /* + * Call the mid-layer callback for this command + */ + cmd->scsi_done(cmd); + } +} -static struct file_operations megadev_fops = { - ioctl:megadev_ioctl_entry, - open:megadev_open, - release:megadev_close, -}; /* - * Array to structures for storing the information about the controllers. This - * information is sent to the user level applications, when they do an ioctl - * for this information. + * Free a SCB structure + * Note: We assume the scsi commands associated with this scb is not free yet. */ -static struct mcontroller mcontroller[MAX_CONTROLLERS]; - -/* The current driver version */ -static u32 driver_ver = 0x118C; +static void +mega_free_scb(adapter_t *adapter, scb_t *scb) +{ + switch( scb->dma_type ) { -/* major number used by the device for character interface */ -static int major; + case MEGA_DMA_TYPE_NONE: + break; -static struct semaphore mimd_ioctl_sem; -static struct semaphore mimd_entry_mtx; + case MEGA_BULK_DATA: + pci_unmap_page(adapter->host->pci_dev, scb->dma_h_bulkdata, + scb->cmd->request_bufflen, scb->dma_direction); + + if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { + pci_dma_sync_single(adapter->host->pci_dev, + scb->dma_h_bulkdata, + scb->cmd->request_bufflen, + PCI_DMA_FROMDEVICE); + } -#if SERDEBUG -volatile static spinlock_t serial_lock; -#endif + break; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x20300 */ -static struct proc_dir_entry proc_scsi_megaraid = { - PROC_SCSI_MEGARAID, 8, "megaraid", - S_IFDIR | S_IRUGO | S_IXUGO, 2 -}; -#endif + case MEGA_SGLIST: + pci_unmap_sg(adapter->host->pci_dev, scb->cmd->request_buffer, + scb->cmd->use_sg, scb->dma_direction); + + if( scb->dma_direction == PCI_DMA_FROMDEVICE ) { + pci_dma_sync_sg(adapter->host->pci_dev, + scb->cmd->request_buffer, + scb->cmd->use_sg, PCI_DMA_FROMDEVICE); + } -#ifdef CONFIG_PROC_FS -extern struct proc_dir_entry proc_root; -#endif + break; -#define IS_RAID_CH(this, ch) ( (this->mega_ch_class >> (ch)) & 0x01 ) + default: + break; + } -#if SERDEBUG -static char strbuf[MAX_SERBUF + 1]; + /* + * Remove from the pending list + */ + list_del_init(&scb->list); -static void ser_init (void) -{ - unsigned port = COM_BASE; + /* Link the scb back into free list */ + scb->state = SCB_FREE; + scb->cmd = NULL; - outb (0x80, port + 3); - outb (0, port + 1); - /* 9600 Baud, if 19200: outb(6,port) */ - outb (12, port); - outb (3, port + 3); - outb (0, port + 1); + list_add(&scb->list, &adapter->free_list); } -static void ser_puts (char *str) -{ - char *ptr; - ser_init (); - for (ptr = str; *ptr; ++ptr) - ser_putc (*ptr); +/* + * Wait until the controller's mailbox is available + */ +static inline int +mega_busywait_mbox (adapter_t *adapter) +{ + if (adapter->mbox->busy) + return __mega_busywait_mbox(adapter); + return 0; } -static void ser_putc (char c) +static int +__mega_busywait_mbox (adapter_t *adapter) { - unsigned port = COM_BASE; + volatile mbox_t *mbox = adapter->mbox; + long counter; - while ((inb (port + 5) & 0x20) == 0) ; - outb (c, port); - if (c == 0x0a) { - while ((inb (port + 5) & 0x20) == 0) ; - outb (0x0d, port); + for (counter = 0; counter < 10000; counter++) { + if (!mbox->busy) + return 0; + udelay(100); yield(); } + return -1; /* give up after 1 second */ } -static int ser_printk (const char *fmt, ...) +/* + * Copies data to SGLIST + * Note: For 64 bit cards, we need a minimum of one SG element for read/write + */ +static int +mega_build_sglist(adapter_t *adapter, scb_t *scb, u32 *buf, u32 *len) { - va_list args; - int i; - long flags; - - spin_lock_irqsave (&serial_lock, flags); - va_start (args, fmt); - i = vsprintf (strbuf, fmt, args); - ser_puts (strbuf); - va_end (args); - spin_unlock_irqrestore (&serial_lock, flags); - - return i; -} - -#define TRACE(a) { ser_printk a;} - -#else -#define TRACE(A) -#endif + struct scatterlist *sgl; + struct page *page; + unsigned long offset; + Scsi_Cmnd *cmd; + int sgcnt; + int idx; -#define TRACE1(a) + cmd = scb->cmd; -static void callDone (Scsi_Cmnd * SCpnt) -{ - if (SCpnt->result) { - TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n", - SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, - SCpnt->target, SCpnt->lun, SCpnt->result)); - } - SCpnt->scsi_done (SCpnt); -} + /* Scatter-gather not used */ + if( !cmd->use_sg ) { -/*------------------------------------------------------------------------- - * - * Local functions - * - *-------------------------------------------------------------------------*/ + page = virt_to_page(cmd->request_buffer); -/*======================= - * Free a SCB structure - *======================= - */ -static void mega_freeSCB (mega_host_config * megaCfg, mega_scb * pScb) -{ + offset = ((unsigned long)cmd->request_buffer & ~PAGE_MASK); - mega_scb *pScbtmp; + scb->dma_h_bulkdata = pci_map_page(adapter->host->pci_dev, + page, offset, + cmd->request_bufflen, + scb->dma_direction); + scb->dma_type = MEGA_BULK_DATA; - if ((pScb == NULL) || (pScb->idx >= 0xFE)) { - return; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - switch (pScb->dma_type) { - case M_RD_DMA_TYPE_NONE: - break; - case M_RD_PTHRU_WITH_BULK_DATA: - pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, - pScb->pthru->dataxferlen, - pScb->dma_direction); - break; - case M_RD_EPTHRU_WITH_BULK_DATA: - pci_unmap_single (megaCfg->dev, pScb->dma_h_bulkdata, - pScb->epthru->dataxferlen, - pScb->dma_direction); - break; - case M_RD_PTHRU_WITH_SGLIST: - { - int count; - for (count = 0; count < pScb->sglist_count; count++) { - pci_unmap_single (megaCfg->dev, - pScb->dma_h_sglist[count], - pScb->sgList[count].length, - pScb->dma_direction); + /* + * We need to handle special 64-bit commands that need a + * minimum of 1 SG + */ + if( adapter->has_64bit_addr ) { + scb->sgl64[0].address = scb->dma_h_bulkdata; + scb->sgl64[0].length = cmd->request_bufflen; + *buf = (u32)scb->sgl_dma_addr; + *len = (u32)cmd->request_bufflen; + return 1; + } + else { + *buf = (u32)scb->dma_h_bulkdata; + *len = (u32)cmd->request_bufflen; + } + if( scb->dma_direction == PCI_DMA_TODEVICE ) { + pci_dma_sync_single(adapter->host->pci_dev, + scb->dma_h_bulkdata, + cmd->request_bufflen, + PCI_DMA_TODEVICE); } - break; - } - case M_RD_BULK_DATA_ONLY: - pci_unmap_single (megaCfg->dev, - pScb->dma_h_bulkdata, - pScb->iDataSize, pScb->dma_direction); - break; - case M_RD_SGLIST_ONLY: - pci_unmap_sg (megaCfg->dev, - pScb->SCpnt->request_buffer, - pScb->SCpnt->use_sg, pScb->dma_direction); - break; - default: - break; + return 0; } -#endif - - /* Unlink from pending queue */ - if (pScb == megaCfg->qPendingH) { - - if (megaCfg->qPendingH == megaCfg->qPendingT) - megaCfg->qPendingH = megaCfg->qPendingT = NULL; - else - megaCfg->qPendingH = megaCfg->qPendingH->next; - megaCfg->qPcnt--; + sgl = (struct scatterlist *)cmd->request_buffer; - } else { - for (pScbtmp = megaCfg->qPendingH; pScbtmp; - pScbtmp = pScbtmp->next) { + /* + * Copy Scatter-Gather list info into controller structure. + * + * The number of sg elements returned must not exceed our limit + */ + sgcnt = pci_map_sg(adapter->host->pci_dev, sgl, cmd->use_sg, + scb->dma_direction); - if (pScbtmp->next == pScb) { + scb->dma_type = MEGA_SGLIST; - pScbtmp->next = pScb->next; + if( sgcnt > adapter->sglen ) BUG(); - if (pScb == megaCfg->qPendingT) { - megaCfg->qPendingT = pScbtmp; - } + for( idx = 0; idx < sgcnt; idx++, sgl++ ) { - megaCfg->qPcnt--; - break; - } + if( adapter->has_64bit_addr ) { + scb->sgl64[idx].address = sg_dma_address(sgl); + scb->sgl64[idx].length = sg_dma_len(sgl); + } + else { + scb->sgl[idx].address = sg_dma_address(sgl); + scb->sgl[idx].length = sg_dma_len(sgl); } } - /* Link back into free list */ - pScb->state = SCB_FREE; - pScb->SCpnt = NULL; + /* Reset pointer and length fields */ + *buf = scb->sgl_dma_addr; - if (megaCfg->qFreeH == (mega_scb *) NULL) { - megaCfg->qFreeH = megaCfg->qFreeT = pScb; - } else { - megaCfg->qFreeT->next = pScb; - megaCfg->qFreeT = pScb; - } + /* + * For passthru command, dataxferlen must be set, even for commands + * with a sg list + */ + *len = (u32)cmd->request_bufflen; - megaCfg->qFreeT->next = NULL; - megaCfg->qFcnt++; + if( scb->dma_direction == PCI_DMA_TODEVICE ) { + pci_dma_sync_sg(adapter->host->pci_dev, sgl, cmd->use_sg, + PCI_DMA_TODEVICE); + } + /* Return count of SG requests */ + return sgcnt; } -/*=========================== - * Allocate a SCB structure - *=========================== + +/* + * mega_8_to_40ld() + * + * takes all info in AdapterInquiry structure and puts it into ProductInfo and + * Enquiry3 structures for later use */ -static mega_scb *mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +static void +mega_8_to_40ld(mraid_inquiry *inquiry, mega_inquiry3 *enquiry3, + mega_product_info *product_info) { - mega_scb *pScb; + int i; - /* Unlink command from Free List */ - if ((pScb = megaCfg->qFreeH) != NULL) { - megaCfg->qFreeH = pScb->next; - megaCfg->qFcnt--; - - pScb->isrcount = jiffies; - pScb->next = NULL; - pScb->state = SCB_ACTIVE; - pScb->SCpnt = SCpnt; + product_info->max_commands = inquiry->adapter_info.max_commands; + enquiry3->rebuild_rate = inquiry->adapter_info.rebuild_rate; + product_info->nchannels = inquiry->adapter_info.nchannels; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pScb->dma_type = M_RD_DMA_TYPE_NONE; -#endif + for (i = 0; i < 4; i++) { + product_info->fw_version[i] = + inquiry->adapter_info.fw_version[i]; - return pScb; + product_info->bios_version[i] = + inquiry->adapter_info.bios_version[i]; } + enquiry3->cache_flush_interval = + inquiry->adapter_info.cache_flush_interval; - printk (KERN_WARNING "Megaraid: Could not allocate free SCB!!!\n"); - - return NULL; -} - -/* Run through the list of completed requests and finish it */ -static void mega_rundoneq (mega_host_config * megaCfg) -{ - Scsi_Cmnd *SCpnt; + product_info->dram_size = inquiry->adapter_info.dram_size; - while ((SCpnt = megaCfg->qCompletedH) != NULL) { - megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; - megaCfg->qCcnt--; + enquiry3->num_ldrv = inquiry->logdrv_info.num_ldrv; - SCpnt->host_scribble = (unsigned char *) NULL; /* XC : sep 14 */ - /* Callback */ - callDone (SCpnt); + for (i = 0; i < MAX_LOGICAL_DRIVES_8LD; i++) { + enquiry3->ldrv_size[i] = inquiry->logdrv_info.ldrv_size[i]; + enquiry3->ldrv_prop[i] = inquiry->logdrv_info.ldrv_prop[i]; + enquiry3->ldrv_state[i] = inquiry->logdrv_info.ldrv_state[i]; } - megaCfg->qCompletedH = megaCfg->qCompletedT = NULL; + for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) + enquiry3->pdrv_state[i] = inquiry->pdrv_info.pdrv_state[i]; } + /* - * Runs through the list of pending requests - * Assumes that mega_lock spin_lock has been acquired. + * Release the controller's resources */ -static int mega_runpendq (mega_host_config * megaCfg) +static int +megaraid_release(struct Scsi_Host *host) { - mega_scb *pScb; - int rc; - - /* Issue any pending commands to the card */ - for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { - if (pScb->state == SCB_ACTIVE) { - if ((rc = - megaIssueCmd (megaCfg, pScb->mboxData, pScb, 1)) == -1) - return rc; - } - } - return 0; -} + adapter_t *adapter; + mbox_t *mbox; + u_char raw_mbox[16]; + char buf[12] = { 0 }; -/* Add command to the list of completed requests */ + adapter = (adapter_t *)host->hostdata; + mbox = (mbox_t *)raw_mbox; -static void mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, int status) -{ - int islogical; - Scsi_Cmnd *SCpnt; - mega_passthru *pthru; - mega_ext_passthru *epthru; - mega_mailbox *mbox; - struct scatterlist *sgList; - u8 c; + printk(KERN_NOTICE "megaraid: being unloaded..."); - if (pScb == NULL) { - TRACE (("NULL pScb in mega_cmd_done!")); - printk(KERN_CRIT "NULL pScb in mega_cmd_done!"); - } + /* Flush adapter cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_ADAPTER; - SCpnt = pScb->SCpnt; + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = pScb->pthru; - epthru = pScb->epthru; -#else - pthru = &pScb->pthru; - epthru = &pScb->epthru; -#endif + /* Issue a blocking (interrupts disabled) command to the card */ + issue_scb_block(adapter, raw_mbox); - mbox = (mega_mailbox *) & pScb->mboxData; + /* Flush disks cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_SYSTEM; - if (SCpnt == NULL) { - TRACE (("NULL SCpnt in mega_cmd_done!")); - TRACE (("pScb->idx = ", pScb->idx)); - TRACE (("pScb->state = ", pScb->state)); - TRACE (("pScb->state = ", pScb->state)); - panic(KERN_ERR "megaraid:Problem...!\n"); - } + /* Issue a blocking (interrupts disabled) command to the card */ + issue_scb_block(adapter, raw_mbox); -#if 0 - islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) && - (SCpnt->channel <= megaCfg->host->max_channel) ); -#endif -#if 0 - islogical = (SCpnt->channel == megaCfg->host->max_channel); -#endif - islogical = megaCfg->logdrv_chan[SCpnt->channel]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Special Case to handle PassThrough->XferAddrress > 4GB */ - switch (SCpnt->cmnd[0]) { - case INQUIRY: - case READ_CAPACITY: - memcpy (SCpnt->request_buffer, - pScb->bounce_buffer, SCpnt->request_bufflen); - break; + /* Free our resources */ + if( adapter->flag & BOARD_MEMMAP ) { + iounmap((void *)adapter->base); + release_mem_region(adapter->host->base, 128); + } + else { + release_region(adapter->base, 16); } -#endif - mega_freeSCB (megaCfg, pScb); + mega_free_sgl(adapter); - /* - * Do not return the presence of hard disk on the channel so, inquiry - * sent, and returned data==hard disk or removable hard disk and not - * logical, request should return failure! - PJ - */ -#if 0 - if (SCpnt->cmnd[0] == INQUIRY && ((((u_char *) SCpnt->request_buffer)[0] & 0x1F) == TYPE_DISK) && !islogical) { - status = 0xF0; +#ifdef CONFIG_PROC_FS + if( adapter->controller_proc_dir_entry ) { + remove_proc_entry("stat", adapter->controller_proc_dir_entry); + remove_proc_entry("config", + adapter->controller_proc_dir_entry); + remove_proc_entry("mailbox", + adapter->controller_proc_dir_entry); +#if MEGA_HAVE_ENH_PROC + remove_proc_entry("rebuild-rate", + adapter->controller_proc_dir_entry); + remove_proc_entry("battery-status", + adapter->controller_proc_dir_entry); + + remove_proc_entry("diskdrives-ch0", + adapter->controller_proc_dir_entry); + remove_proc_entry("diskdrives-ch1", + adapter->controller_proc_dir_entry); + remove_proc_entry("diskdrives-ch2", + adapter->controller_proc_dir_entry); + remove_proc_entry("diskdrives-ch3", + adapter->controller_proc_dir_entry); + + remove_proc_entry("raiddrives-0-9", + adapter->controller_proc_dir_entry); + remove_proc_entry("raiddrives-10-19", + adapter->controller_proc_dir_entry); + remove_proc_entry("raiddrives-20-29", + adapter->controller_proc_dir_entry); + remove_proc_entry("raiddrives-30-39", + adapter->controller_proc_dir_entry); +#endif + + sprintf(buf, "hba%d", adapter->host->host_no); + remove_proc_entry(buf, mega_proc_dir_entry); } #endif - if (SCpnt->cmnd[0] == INQUIRY && !islogical) { - if ( SCpnt->use_sg ) { - sgList = (struct scatterlist *)SCpnt->request_buffer; - memcpy(&c, sgList[0].address, 0x1); - } else { - memcpy(&c, SCpnt->request_buffer, 0x1); - } -#if 0 - if( (c & 0x1F ) == TYPE_DISK ) { - status = 0xF0; - } -#endif - if(IS_RAID_CH(megaCfg, SCpnt->channel) && ((c & 0x1F) == TYPE_DISK)) { - status = 0xF0; - } - } - - - /* clear result; otherwise, success returns corrupt value */ - SCpnt->result = 0; - - if ( 0 && SCpnt->cmnd[0] & M_RD_IOCTL_CMD ) { /* i.e. ioctl cmd such as M_RD_IOCTL_CMD, M_RD_IOCTL_CMD_NEW of megamgr */ - switch (status) { - case 2: - case 0xF0: - case 0xF4: - SCpnt->result = (DID_BAD_TARGET << 16) | status; - break; - default: - SCpnt->result |= status; - } /*end of switch */ - } else { - /* Convert MegaRAID status to Linux error code */ - switch (status) { - case 0x00: /* SUCCESS , i.e. SCSI_STATUS_GOOD */ - SCpnt->result |= (DID_OK << 16); - break; - case 0x02: /* ERROR_ABORTED, i.e. SCSI_STATUS_CHECK_CONDITION */ + pci_free_consistent(adapter->dev, MEGA_BUFFER_SIZE, + adapter->mega_buffer, adapter->buf_dma_handle); + kfree(adapter->scb_list); + pci_free_consistent(adapter->dev, sizeof(mbox64_t), + (void *)adapter->una_mbox64, adapter->una_mbox64_dma); - /*set sense_buffer and result fields */ - if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU || mbox->cmd == - MEGA_MBOXCMD_PASSTHRU64 ) { + kfree(adapter->ipdev); - memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14); + hba_count--; - SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | - (CHECK_CONDITION << 1); + if( hba_count == 0 ) { - } else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) { + /* + * Unregister the character device interface to the driver. + */ + unregister_chrdev(major, "megadev"); - memcpy( SCpnt->sense_buffer, epthru->reqsensearea, 14); + unregister_reboot_notifier(&mega_notifier); - SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | - (CHECK_CONDITION << 1); +#ifdef CONFIG_PROC_FS + if( adapter->controller_proc_dir_entry ) { + remove_proc_entry ("megaraid", &proc_root); + } +#endif - } else { - SCpnt->sense_buffer[0] = 0x70; - SCpnt->sense_buffer[2] = ABORTED_COMMAND; - SCpnt->result |= (CHECK_CONDITION << 1); - } - break; + } - case 0x08: /* ERR_DEST_DRIVE_FAILED, i.e. SCSI_STATUS_BUSY */ - SCpnt->result |= (DID_BUS_BUSY << 16) | status; - break; + /* + * Release the controller memory. A word of warning this frees + * hostdata and that includes adapter-> so be careful what you + * dereference beyond this point + */ + scsi_unregister(host); - default: - SCpnt->result |= (DID_BAD_TARGET << 16) | status; - break; - } - } - /* Add Scsi_Command to end of completed queue */ - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } + printk("ok.\n"); - megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; - megaCfg->qCcnt++; + return 0; } -/*------------------------------------------------------------------- - * - * Build a SCB from a Scsi_Cmnd - * - * Returns a SCB pointer, or NULL - * If NULL is returned, the scsi_done function MUST have been called - * - *-------------------------------------------------------------------*/ - -static mega_scb *mega_build_cmd (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +static inline void +mega_free_sgl(adapter_t *adapter) { - mega_scb *pScb; - mega_mailbox *mbox; - mega_passthru *pthru; - mega_ext_passthru *epthru; - long seg; - char islogical; - int max_ldrv_num; - int channel = 0; - int target = 0; - int ldrv_num = 0; /* logical drive number */ - - if ((SCpnt->cmnd[0] == MEGADEVIOC)) - return megadev_doioctl (megaCfg, SCpnt); - - if ((SCpnt->cmnd[0] == M_RD_IOCTL_CMD) - || (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW)) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - return mega_ioctl (megaCfg, SCpnt); /* Handle IOCTL command */ -#else - { - printk(KERN_WARNING "megaraid ioctl: older interface - " - "not supported.\n"); - return NULL; - } -#endif - -#if 0 - islogical = ( (SCpnt->channel >= megaCfg->productInfo.SCSIChanPresent) && - (SCpnt->channel <= megaCfg->host->max_channel) ); -#endif -#if 0 - islogical = (IS_RAID_CH(SCpnt->channel) && /* virtual ch is raid - AM */ - (SCpnt->channel == megaCfg->host->max_channel)); -#endif + scb_t *scb; + int i; - /* - * We know on what channels are our logical drives - mega_findCard() - */ - islogical = megaCfg->logdrv_chan[SCpnt->channel]; + for(i = 0; i < adapter->max_cmds; i++) { - /* - * The theory: If physical drive is chosen for boot, all the physical - * devices are exported before the logical drives, otherwise physical - * devices are pushed after logical drives, in which case - Kernel sees - * the physical devices on virtual channel which is obviously converted - * to actual channel on the HBA. - */ - if( megaCfg->boot_pdrv_enabled ) { - if( islogical ) { - /* logical channel */ - channel = SCpnt->channel - megaCfg->productInfo.SCSIChanPresent; - } - else { - channel = SCpnt->channel; /* this is physical channel */ - target = SCpnt->target; + scb = &adapter->scb_list[i]; - /* - * boot from a physical disk, that disk needs to be exposed first - * IF both the channels are SCSI, then booting from the second - * channel is not allowed. - */ - if( target == 0 ) { - target = megaCfg->boot_pdrv_tgt; - } - else if( target == megaCfg->boot_pdrv_tgt ) { - target = 0; - } - } - } - else { - if( islogical ) { - channel = SCpnt->channel; /* this is the logical channel */ - } - else { - channel = SCpnt->channel - NVIRT_CHAN; /* physical channel */ - target = SCpnt->target; - } - } + if( scb->sgl64 ) { + pci_free_consistent(adapter->dev, + sizeof(mega_sgl64) * adapter->sglen, + scb->sgl64, + scb->sgl_dma_addr); - if ( ! megaCfg->support_ext_cdb ) { - if (!islogical && SCpnt->lun != 0) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; + scb->sgl64 = NULL; } - } - - if (!islogical && SCpnt->target == skip_id) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } - if (islogical) { + if( scb->pthru ) { + pci_free_consistent(adapter->dev, sizeof(mega_passthru), + scb->pthru, scb->pthru_dma_addr); - /* have just LUN 0 for each target on virtual channels */ - if( SCpnt->lun != 0 ) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; + scb->pthru = NULL; } - ldrv_num = mega_get_ldrv_num(megaCfg, SCpnt, channel); - - max_ldrv_num = (megaCfg->flag & BOARD_40LD) ? - FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES; + if( scb->epthru ) { + pci_free_consistent(adapter->dev, + sizeof(mega_ext_passthru), + scb->epthru, scb->epthru_dma_addr); - /* - * max_ldrv_num increases by 0x80 if some logical drive was deleted. - */ - if(megaCfg->read_ldidmap) { - max_ldrv_num += 0x80; - } - - if( ldrv_num > max_ldrv_num ) { - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; + scb->epthru = NULL; } - } else { - if ( SCpnt->lun > 7) { - /* Do not support lun >7 for physically accessed devices */ - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; - } } - /*----------------------------------------------------- - * - * Logical drive commands - * - *-----------------------------------------------------*/ - if (islogical) { - switch (SCpnt->cmnd[0]) { - case TEST_UNIT_READY: - memset (SCpnt->request_buffer, 0, SCpnt->request_bufflen); - SCpnt->result = (DID_OK << 16); - callDone (SCpnt); - return NULL; - - case MODE_SENSE: - memset (SCpnt->request_buffer, 0, SCpnt->cmnd[4]); - SCpnt->result = (DID_OK << 16); - callDone (SCpnt); - return NULL; - - case READ_CAPACITY: - case INQUIRY: - if(!(megaCfg->flag & (1L << SCpnt->channel))) { - printk(KERN_NOTICE - "scsi%d: scanning virtual channel %d for logical drives.\n", - megaCfg->host->host_no, channel); +} - megaCfg->flag |= (1L << SCpnt->channel); - } - /* Allocate a SCB and initialize passthru */ - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = pScb->pthru; -#else - pthru = &pScb->pthru; -#endif +/* + * Get information about the card/driver + */ +const char * +megaraid_info(struct Scsi_Host *host) +{ + static char buffer[512]; + adapter_t *adapter; - mbox = (mega_mailbox *) & pScb->mboxData; - memset (mbox, 0, sizeof (pScb->mboxData)); - memset (pthru, 0, sizeof (mega_passthru)); - pthru->timeout = 0; - pthru->ars = 1; - pthru->reqsenselen = 14; - pthru->islogical = 1; - pthru->logdrv = ldrv_num; - pthru->cdblen = SCpnt->cmd_len; + adapter = (adapter_t *)host->hostdata; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /*Not sure about the direction */ - pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; - pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; + sprintf (buffer, + "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", + adapter->fw_version, adapter->product_info.max_commands, + adapter->host->max_id, adapter->host->max_channel, + adapter->host->max_lun); + return buffer; +} -#if 0 -/* Normal Code w/o the need for bounce buffer */ - pScb->dma_h_bulkdata - = pci_map_single (megaCfg->dev, - SCpnt->request_buffer, - SCpnt->request_bufflen, - pScb->dma_direction); +/* shouldn't be used, but included for completeness */ +static int +megaraid_command (Scsi_Cmnd *cmd) +{ + printk(KERN_WARNING + "megaraid critcal error: synchronous interface is not implemented.\n"); - pthru->dataxferaddr = pScb->dma_h_bulkdata; -#else -/* Special Code to use bounce buffer for READ_CAPA/INQ */ - pthru->dataxferaddr = pScb->dma_bounce_buffer; - pScb->dma_type = M_RD_DMA_TYPE_NONE; -#endif + cmd->result = (DID_ERROR << 16); + cmd->scsi_done(cmd); -#else - pthru->dataxferaddr = - virt_to_bus (SCpnt->request_buffer); -#endif + return 1; +} - pthru->dataxferlen = SCpnt->request_bufflen; - memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len); - /* Initialize mailbox area */ - mbox->cmd = MEGA_MBOXCMD_PASSTHRU; +/** + * megaraid_abort - abort the scsi command + * @scp - command to be aborted + * + * Abort a previous SCSI request. Only commands on the pending list can be + * aborted. All the commands issued to the F/W must complete. + */ +static int +megaraid_abort(Scsi_Cmnd *scp) +{ + adapter_t *adapter; + struct list_head *pos, *next; + scb_t *scb; + long iter; + int rval = SUCCESS; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mbox->xferaddr = pScb->dma_passthruhandle64; - TRACE1 (("M_RD_PTHRU_WITH_BULK_DATA Enabled \n")); -#else - mbox->xferaddr = virt_to_bus (pthru); -#endif - return pScb; + adapter = (adapter_t *)scp->host->hostdata; - case READ_6: - case WRITE_6: - case READ_10: - case WRITE_10: - /* Allocate a SCB and initialize mailbox */ - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - mbox = (mega_mailbox *) & pScb->mboxData; + ASSERT( spin_is_locked(&adapter->lock) ); - memset (mbox, 0, sizeof (pScb->mboxData)); - mbox->logdrv = ldrv_num; + printk("megaraid: aborting-%ld cmd=%x \n", + scp->serial_number, scp->cmnd[0], scp->channel, scp->target, + scp->lun); - if (megaCfg->flag & BOARD_64BIT) { - mbox->cmd = (*SCpnt->cmnd == READ_6 - || *SCpnt->cmnd == - READ_10) ? MEGA_MBOXCMD_LREAD64 : - MEGA_MBOXCMD_LWRITE64; - } else { - mbox->cmd = (*SCpnt->cmnd == READ_6 - || *SCpnt->cmnd == - READ_10) ? MEGA_MBOXCMD_LREAD : - MEGA_MBOXCMD_LWRITE; - } - /* 6-byte */ - if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == WRITE_6) { - mbox->numsectors = (u32) SCpnt->cmnd[4]; - mbox->lba = - ((u32) SCpnt->cmnd[1] << 16) | - ((u32) SCpnt->cmnd[2] << 8) | - (u32) SCpnt->cmnd[3]; - mbox->lba &= 0x1FFFFF; + list_for_each_safe( pos, next, &adapter->pending_list ) { - if (*SCpnt->cmnd == READ_6) { - megaCfg->nReads[(int)ldrv_num]++; - megaCfg->nReadBlocks[(int)ldrv_num] += - mbox->numsectors; - } else { - megaCfg->nWrites[(int)ldrv_num]++; - megaCfg->nWriteBlocks[(int)ldrv_num] += - mbox->numsectors; - } - } + scb = list_entry(pos, scb_t, list); - /* 10-byte */ - if (*SCpnt->cmnd == READ_10 || *SCpnt->cmnd == WRITE_10) { - mbox->numsectors = - (u32) SCpnt->cmnd[8] | - ((u32) SCpnt->cmnd[7] << 8); - mbox->lba = - ((u32) SCpnt->cmnd[2] << 24) | - ((u32) SCpnt->cmnd[3] << 16) | - ((u32) SCpnt->cmnd[4] << 8) | - (u32) SCpnt->cmnd[5]; - - if (*SCpnt->cmnd == READ_10) { - megaCfg->nReads[(int)ldrv_num]++; - megaCfg->nReadBlocks[(int)ldrv_num] += - mbox->numsectors; - } else { - megaCfg->nWrites[(int)ldrv_num]++; - megaCfg->nWriteBlocks[(int)ldrv_num] += - mbox->numsectors; - } - } + if( scb->cmd == scp ) { /* Found command */ - /* 12-byte */ - if (*SCpnt->cmnd == READ_12 || *SCpnt->cmnd == WRITE_12) { - mbox->lba = - ((u32) SCpnt->cmnd[2] << 24) | - ((u32) SCpnt->cmnd[3] << 16) | - ((u32) SCpnt->cmnd[4] << 8) | - (u32) SCpnt->cmnd[5]; + scb->state |= SCB_ABORT; - mbox->numsectors = - ((u32) SCpnt->cmnd[6] << 24) | - ((u32) SCpnt->cmnd[7] << 16) | - ((u32) SCpnt->cmnd[8] << 8) | - (u32) SCpnt->cmnd[9]; - - if (*SCpnt->cmnd == READ_12) { - megaCfg->nReads[(int)ldrv_num]++; - megaCfg->nReadBlocks[(int)ldrv_num] += - mbox->numsectors; - } else { - megaCfg->nWrites[(int)ldrv_num]++; - megaCfg->nWriteBlocks[(int)ldrv_num] += - mbox->numsectors; - } - } + /* + * Check if this command was never issued. If this is + * the case, take it off from the pending list and + * complete. + */ + if( !(scb->state & SCB_ISSUED) ) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if (*SCpnt->cmnd == READ_6 || *SCpnt->cmnd == READ_10 - || *SCpnt->cmnd == READ_12) { - pScb->dma_direction = PCI_DMA_FROMDEVICE; - } else { /*WRITE_6 or WRITE_10 */ - pScb->dma_direction = PCI_DMA_TODEVICE; - } -#endif + printk(KERN_WARNING + "megaraid: %ld:%d, driver owner.\n", + scp->serial_number, scb->idx); - /* Calculate Scatter-Gather info */ - mbox->numsgelements = mega_build_sglist (megaCfg, pScb, - (u32 *)&mbox->xferaddr, (u32 *)&seg); + scp->result = (DID_ABORT << 16); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pScb->iDataSize = seg; + mega_free_scb(adapter, scb); - if (mbox->numsgelements) { - pScb->dma_type = M_RD_SGLIST_ONLY; - TRACE1 (("M_RD_SGLIST_ONLY Enabled \n")); - } else { - pScb->dma_type = M_RD_BULK_DATA_ONLY; - TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); - } -#endif + scp->scsi_done(scp); - return pScb; - default: - SCpnt->result = (DID_BAD_TARGET << 16); - callDone (SCpnt); - return NULL; + break; + } } } - /*----------------------------------------------------- - * - * Passthru drive commands - * - *-----------------------------------------------------*/ - else { - /* Allocate a SCB and initialize passthru */ - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; + + /* + * By this time, either all commands are completed or aborted by + * mid-layer. Do not return until all the commands are actually + * completed by the firmware + */ + iter = 0; + while( !list_empty(&adapter->pending_list) ) { + + /* + * Perform the ack sequence, since interrupts are not + * available right now! + */ + if( adapter->flag & BOARD_MEMMAP ) { + megaraid_memmbox_ack_sequence(adapter); + } + else { + megaraid_iombox_ack_sequence(adapter); } - mbox = (mega_mailbox *) pScb->mboxData; - memset (mbox, 0, sizeof (pScb->mboxData)); + /* + * print a message once every second only + */ + if( !(iter % 1000) ) { + printk( + "megarid: Waiting for %d commands to flush: iter:%ld\n", + atomic_read(&adapter->pend_cmds), iter); + } - if ( megaCfg->support_ext_cdb && SCpnt->cmd_len > 10 ) { - epthru = mega_prepare_extpassthru(megaCfg, pScb, SCpnt, channel, - target); - mbox->cmd = MEGA_MBOXCMD_EXTPASSTHRU; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mbox->xferaddr = pScb->dma_ext_passthruhandle64; - - if(epthru->numsgelements) { - pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; - } else { - pScb->dma_type = M_RD_EPTHRU_WITH_BULK_DATA; - } -#else - mbox->xferaddr = virt_to_bus(epthru); -#endif + if( iter++ < MBOX_ABORT_SLEEP*1000 ) { + mdelay(1); } else { - pthru = mega_prepare_passthru(megaCfg, pScb, SCpnt, channel, - target); + printk(KERN_WARNING + "megaraid: critical hardware error!\n"); - /* Initialize mailbox */ - mbox->cmd = MEGA_MBOXCMD_PASSTHRU; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mbox->xferaddr = pScb->dma_passthruhandle64; - - if (pthru->numsgelements) { - pScb->dma_type = M_RD_PTHRU_WITH_SGLIST; - } else { - pScb->dma_type = M_RD_PTHRU_WITH_BULK_DATA; - } -#else - mbox->xferaddr = virt_to_bus(pthru); -#endif + rval = FAILED; + + break; } - return pScb; } - return NULL; + + if( rval == SUCCESS ) { + printk(KERN_INFO + "megaraid: abort sequence successfully complete.\n"); + } + + return rval; } + static int -mega_get_ldrv_num(mega_host_config *this_hba, Scsi_Cmnd *sc, int channel) +megaraid_reset(Scsi_Cmnd *cmd) { - int tgt; - int ldrv_num; + adapter_t *adapter; + megacmd_t mc; + long iter; + int rval = SUCCESS; + + adapter = (adapter_t *)cmd->host->hostdata; + + ASSERT( spin_is_locked(&adapter->lock) ); + + printk("megaraid: reset-%ld cmd=%x \n", + cmd->serial_number, cmd->cmnd[0], cmd->channel, cmd->target, + cmd->lun); - tgt = sc->target; - - if ( tgt > 7 ) tgt--; /* we do not get inquires for tgt 7 */ - ldrv_num = (channel * 15) + tgt; /* 14 targets per channel */ +#if MEGA_HAVE_CLUSTERING + mc.cmd = MEGA_CLUSTER_CMD; + mc.opcode = MEGA_RESET_RESERVATIONS; + + spin_unlock_irq(&adapter->lock); + if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) != 0 ) { + printk(KERN_WARNING + "megaraid: reservation reset failed.\n"); + } + else { + printk(KERN_INFO "megaraid: reservation reset.\n"); + } + spin_lock_irq(&adapter->lock); +#endif /* - * If we have a logical drive with boot enabled, project it first + * Do not return until all the commands are actually completed by the + * firmware */ - if( this_hba->boot_ldrv_enabled ) { - if( ldrv_num == 0 ) { - ldrv_num = this_hba->boot_ldrv; + iter = 0; + while( !list_empty(&adapter->pending_list) ) { + + /* + * Perform the ack sequence, since interrupts are not + * available right now! + */ + if( adapter->flag & BOARD_MEMMAP ) { + megaraid_memmbox_ack_sequence(adapter); } else { - if( ldrv_num <= this_hba->boot_ldrv ) { - ldrv_num--; - } + megaraid_iombox_ack_sequence(adapter); } - } - - /* - * If "delete logical drive" feature is enabled on this controller. - * Do only if at least one delete logical drive operation was done. - * - * Also, after logical drive deletion, instead of logical drive number, - * the value returned should be 0x80+logical drive id. - * - * These is valid only for IO commands. - */ - if( this_hba->support_random_del && this_hba->read_ldidmap ) { - switch(sc->cmnd[0]) { - case READ_6: /* fall through */ - case WRITE_6: /* fall through */ - case READ_10: /* fall through */ - case WRITE_10: - ldrv_num += 0x80; + /* + * print a message once every second only + */ + if( !(iter % 1000) ) { + printk( + "megarid: Waiting for %d commands to flush: iter:%ld\n", + atomic_read(&adapter->pend_cmds), iter); } - } - return ldrv_num; -} + if( iter++ < MBOX_RESET_SLEEP*1000 ) { + mdelay(1); + } + else { + printk(KERN_WARNING + "megaraid: critical hardware error!\n"); + rval = FAILED; -static mega_passthru * -mega_prepare_passthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc, - int channel, int target) -{ - mega_passthru *pthru; + break; + } + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = scb->pthru; -#else - pthru = &scb->pthru; -#endif - memset (pthru, 0, sizeof (mega_passthru)); + if( rval == SUCCESS ) { + printk(KERN_INFO + "megaraid: reset sequence successfully complete.\n"); + } - /* set adapter timeout value to 10 min. for tape drive */ - /* 0=6sec/1=60sec/2=10min/3=3hrs */ - pthru->timeout = 2; - pthru->ars = 1; - pthru->reqsenselen = 14; - pthru->islogical = 0; - pthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : channel; - pthru->target = (megacfg->flag & BOARD_40LD) ? - (channel << 4) | target : target; - pthru->cdblen = sc->cmd_len; - pthru->logdrv = sc->lun; + return rval; +} - memcpy (pthru->cdb, sc->cmnd, sc->cmd_len); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Not sure about the direction */ - scb->dma_direction = PCI_DMA_BIDIRECTIONAL; +#ifdef CONFIG_PROC_FS +/* Following code handles /proc fs */ - /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ - switch (sc->cmnd[0]) { - case INQUIRY: - case READ_CAPACITY: +#define CREATE_READ_PROC(string, func) create_proc_read_entry(string, \ + S_IRUSR | S_IFREG, \ + controller_proc_dir_entry, \ + func, adapter) + +/** + * mega_create_proc_entry() + * @index - index in soft state array + * @parent - parent node for this /proc entry + * + * Creates /proc entries for our controllers. + */ +static void +mega_create_proc_entry(int index, struct proc_dir_entry *parent) +{ + struct proc_dir_entry *controller_proc_dir_entry = NULL; + u8 string[64] = { 0 }; + adapter_t *adapter = hba_soft_state[index]; - if(!(megacfg->flag & (1L << sc->channel))) { - printk(KERN_NOTICE - "scsi%d: scanning physical channel %d for devices.\n", - megacfg->host->host_no, channel); + sprintf(string, "hba%d", adapter->host->host_no); - megacfg->flag |= (1L << sc->channel); - } + controller_proc_dir_entry = + adapter->controller_proc_dir_entry = proc_mkdir(string, parent); - pthru->numsgelements = 0; - pthru->dataxferaddr = scb->dma_bounce_buffer; - pthru->dataxferlen = sc->request_bufflen; - break; - default: - pthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&pthru->dataxferaddr, - (u32 *)&pthru->dataxferlen - ); - break; + if(!controller_proc_dir_entry) { + printk(KERN_WARNING "\nmegaraid: proc_mkdir failed\n"); + return; } -#else - pthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&pthru->dataxferaddr, - (u32 *)&pthru->dataxferlen - ); + adapter->proc_read = CREATE_READ_PROC("config", proc_read_config); + adapter->proc_stat = CREATE_READ_PROC("stat", proc_read_stat); + adapter->proc_mbox = CREATE_READ_PROC("mailbox", proc_read_mbox); +#if MEGA_HAVE_ENH_PROC + adapter->proc_rr = CREATE_READ_PROC("rebuild-rate", proc_rebuild_rate); + adapter->proc_battery = CREATE_READ_PROC("battery-status", + proc_battery); + + /* + * Display each physical drive on its channel + */ + adapter->proc_pdrvstat[0] = CREATE_READ_PROC("diskdrives-ch0", + proc_pdrv_ch0); + adapter->proc_pdrvstat[1] = CREATE_READ_PROC("diskdrives-ch1", + proc_pdrv_ch1); + adapter->proc_pdrvstat[2] = CREATE_READ_PROC("diskdrives-ch2", + proc_pdrv_ch2); + adapter->proc_pdrvstat[3] = CREATE_READ_PROC("diskdrives-ch3", + proc_pdrv_ch3); + + /* + * Display a set of up to 10 logical drive through each of following + * /proc entries + */ + adapter->proc_rdrvstat[0] = CREATE_READ_PROC("raiddrives-0-9", + proc_rdrv_10); + adapter->proc_rdrvstat[1] = CREATE_READ_PROC("raiddrives-10-19", + proc_rdrv_20); + adapter->proc_rdrvstat[2] = CREATE_READ_PROC("raiddrives-20-29", + proc_rdrv_30); + adapter->proc_rdrvstat[3] = CREATE_READ_PROC("raiddrives-30-39", + proc_rdrv_40); #endif - return pthru; } -static mega_ext_passthru * -mega_prepare_extpassthru(mega_host_config *megacfg, mega_scb *scb, - Scsi_Cmnd *sc, int channel, int target) + +/** + * proc_read_config() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display configuration information about the controller. + */ +static int +proc_read_config(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - mega_ext_passthru *epthru; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - epthru = scb->epthru; -#else - epthru = &scb->epthru; -#endif - memset(epthru, 0, sizeof(mega_ext_passthru)); + adapter_t *adapter = (adapter_t *)data; + int len = 0; - /* set adapter timeout value to 10 min. for tape drive */ - /* 0=6sec/1=60sec/2=10min/3=3hrs */ - epthru->timeout = 2; - epthru->ars = 1; - epthru->reqsenselen = 14; - epthru->islogical = 0; - epthru->channel = (megacfg->flag & BOARD_40LD) ? 0 : channel; - epthru->target = (megacfg->flag & BOARD_40LD) ? - (channel << 4) | target : target; - epthru->cdblen = sc->cmd_len; - epthru->logdrv = sc->lun; + len += sprintf(page+len, "%s", MEGARAID_VERSION); - memcpy(epthru->cdb, sc->cmnd, sc->cmd_len); + if(adapter->product_info.product_name[0]) + len += sprintf(page+len, "%s\n", + adapter->product_info.product_name); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Not sure about the direction */ - scb->dma_direction = PCI_DMA_BIDIRECTIONAL; + len += sprintf(page+len, "Controller Type: "); - /* Special Code for Handling READ_CAPA/ INQ using bounce buffers */ - switch (sc->cmnd[0]) { - case INQUIRY: - case READ_CAPACITY: - if(!(megacfg->flag & (1L << sc->channel))) { - printk(KERN_NOTICE - "scsi%d: scanning physical channel %d for devices.\n", - megacfg->host->host_no, channel); + if( adapter->flag & BOARD_MEMMAP ) { + len += sprintf(page+len, + "438/466/467/471/493/518/520/531/532\n"); + } + else { + len += sprintf(page+len, + "418/428/434\n"); + } - megacfg->flag |= (1L << sc->channel); - } + if(adapter->flag & BOARD_40LD) { + len += sprintf(page+len, + "Controller Supports 40 Logical Drives\n"); + } - epthru->numsgelements = 0; - epthru->dataxferaddr = scb->dma_bounce_buffer; - epthru->dataxferlen = sc->request_bufflen; - break; - default: - epthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&epthru->dataxferaddr, - (u32 *)&epthru->dataxferlen - ); - break; + if(adapter->flag & BOARD_64BIT) { + len += sprintf(page+len, + "Controller capable of 64-bit memory addressing\n"); } -#else - epthru->numsgelements = - mega_build_sglist( - megacfg, scb, (u32 *)&epthru->dataxferaddr, - (u32 *)&epthru->dataxferlen - ); -#endif - return epthru; + if( adapter->has_64bit_addr ) { + len += sprintf(page+len, + "Controller using 64-bit memory addressing\n"); + } + else { + len += sprintf(page+len, + "Controller is not using 64-bit memory addressing\n"); + } + + len += sprintf(page+len, "Base = %08lx, Irq = %d, ", adapter->base, + adapter->host->irq); + + len += sprintf(page+len, "Logical Drives = %d, Channels = %d\n", + adapter->numldrv, adapter->product_info.nchannels); + + len += sprintf(page+len, "Version =%s:%s, DRAM = %dMb\n", + adapter->fw_version, adapter->bios_version, + adapter->product_info.dram_size); + + len += sprintf(page+len, + "Controller Queue Depth = %d, Driver Queue Depth = %d\n", + adapter->product_info.max_commands, adapter->max_cmds); + + len += sprintf(page+len, "support_ext_cdb = %d\n", + adapter->support_ext_cdb); + len += sprintf(page+len, "support_random_del = %d\n", + adapter->support_random_del); + len += sprintf(page+len, "boot_ldrv_enabled = %d\n", + adapter->boot_ldrv_enabled); + len += sprintf(page+len, "boot_ldrv = %d\n", + adapter->boot_ldrv); + len += sprintf(page+len, "boot_pdrv_enabled = %d\n", + adapter->boot_pdrv_enabled); + len += sprintf(page+len, "boot_pdrv_ch = %d\n", + adapter->boot_pdrv_ch); + len += sprintf(page+len, "boot_pdrv_tgt = %d\n", + adapter->boot_pdrv_tgt); + len += sprintf(page+len, "quiescent = %d\n", + atomic_read(&adapter->quiescent)); + len += sprintf(page+len, "has_cluster = %d\n", + adapter->has_cluster); + + len += sprintf(page+len, "\nModule Parameters:\n"); + len += sprintf(page+len, "max_cmd_per_lun = %d\n", + max_cmd_per_lun); + len += sprintf(page+len, "max_sectors_per_io = %d\n", + max_sectors_per_io); + + *eof = 1; + + return len; } -/* Handle Driver Level IOCTLs - * Return value of 0 indicates this function could not handle , so continue - * processing -*/ -static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) + +/** + * proc_read_stat() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Diaplay statistical information about the I/O activity. + */ +static int +proc_read_stat(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - unsigned char *data = (unsigned char *) SCpnt->request_buffer; - mega_driver_info driver_info; + adapter_t *adapter; + int len; + int i; - /* If this is not our command dont do anything */ - if (SCpnt->cmnd[0] != M_RD_DRIVER_IOCTL_INTERFACE) - return 0; + i = 0; /* avoid compilation warnings */ + len = 0; + adapter = (adapter_t *)data; - switch (SCpnt->cmnd[1]) { - case GET_DRIVER_INFO: - if (SCpnt->request_bufflen < sizeof (driver_info)) { - SCpnt->result = DID_BAD_TARGET << 16; - callDone (SCpnt); - return 1; - } + len = sprintf(page, "Statistical Information for this controller\n"); + len += sprintf(page+len, "pend_cmds = %d\n", + atomic_read(&adapter->pend_cmds)); +#if MEGA_HAVE_STATS + for(i = 0; i < adapter->numldrv; i++) { + len += sprintf(page+len, "Logical Drive %d:\n", i); - driver_info.size = sizeof (driver_info) - sizeof (int); - driver_info.version = MEGARAID_IOCTL_VERSION; - memcpy (data, &driver_info, sizeof (driver_info)); - break; - default: - SCpnt->result = DID_BAD_TARGET << 16; + len += sprintf(page+len, + "\tReads Issued = %lu, Writes Issued = %lu\n", + adapter->nreads[i], adapter->nwrites[i]); + + len += sprintf(page+len, + "\tSectors Read = %lu, Sectors Written = %lu\n", + adapter->nreadblocks[i], adapter->nwriteblocks[i]); + + len += sprintf(page+len, + "\tRead errors = %lu, Write errors = %lu\n\n", + adapter->rd_errors[i], adapter->wr_errors[i]); } +#else + len += sprintf(page+len, + "IO and error counters not compiled in driver.\n"); +#endif - callDone (SCpnt); - return 1; + *eof = 1; + + return len; } -static void inline set_mbox_xfer_addr (mega_host_config * megaCfg, mega_scb * pScb, - mega_ioctl_mbox * mbox, u32 direction) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - switch (direction) { - case TO_DEVICE: - pScb->dma_direction = PCI_DMA_TODEVICE; - break; - case FROM_DEVICE: - pScb->dma_direction = PCI_DMA_FROMDEVICE; - break; - case FROMTO_DEVICE: - pScb->dma_direction = PCI_DMA_BIDIRECTIONAL; - break; - } +/** + * proc_read_mbox() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display mailbox information for the last command issued. This information + * is good for debugging. + */ +static int +proc_read_mbox(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ - pScb->dma_h_bulkdata - = pci_map_single (megaCfg->dev, - pScb->buff_ptr, - pScb->iDataSize, pScb->dma_direction); - mbox->xferaddr = pScb->dma_h_bulkdata; - pScb->dma_type = M_RD_BULK_DATA_ONLY; - TRACE1 (("M_RD_BULK_DATA_ONLY Enabled \n")); -#else - mbox->xferaddr = virt_to_bus (pScb->buff_ptr); -#endif + adapter_t *adapter = (adapter_t *)data; + volatile mbox_t *mbox = adapter->mbox; + int len = 0; + + len = sprintf(page, "Contents of Mail Box Structure\n"); + len += sprintf(page+len, " Fw Command = 0x%02x\n", mbox->cmd); + len += sprintf(page+len, " Cmd Sequence = 0x%02x\n", mbox->cmdid); + len += sprintf(page+len, " No of Sectors= %04d\n", mbox->numsectors); + len += sprintf(page+len, " LBA = 0x%02x\n", mbox->lba); + len += sprintf(page+len, " DTA = 0x%08x\n", mbox->xferaddr); + len += sprintf(page+len, " Logical Drive= 0x%02x\n", mbox->logdrv); + len += sprintf(page+len, " No of SG Elmt= 0x%02x\n", + mbox->numsgelements); + len += sprintf(page+len, " Busy = %01x\n", mbox->busy); + len += sprintf(page+len, " Status = 0x%02x\n", mbox->status); + + *eof = 1; + + return len; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -/*-------------------------------------------------------------------- - * build RAID commands for controller, passed down through ioctl() - *--------------------------------------------------------------------*/ -static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) -{ - mega_scb *pScb; - mega_ioctl_mbox *mbox; - mega_mailbox *mailbox; - mega_passthru *pthru; - u8 *mboxdata; - long seg, i = 0; - unsigned char *data = (unsigned char *) SCpnt->request_buffer; - - if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - pthru = &pScb->pthru; - - mboxdata = (u8 *) & pScb->mboxData; - mbox = (mega_ioctl_mbox *) & pScb->mboxData; - mailbox = (mega_mailbox *) & pScb->mboxData; - memset (mailbox, 0, sizeof (pScb->mboxData)); - - if (data[0] == 0x03) { /* passthrough command */ - unsigned char cdblen = data[2]; - memset (pthru, 0, sizeof (mega_passthru)); - pthru->islogical = (data[cdblen + 3] & 0x80) ? 1 : 0; - pthru->timeout = data[cdblen + 3] & 0x07; - pthru->reqsenselen = 14; - pthru->ars = (data[cdblen + 3] & 0x08) ? 1 : 0; - pthru->logdrv = data[cdblen + 4]; - pthru->channel = data[cdblen + 5]; - pthru->target = data[cdblen + 6]; - pthru->cdblen = cdblen; - memcpy (pthru->cdb, &data[3], cdblen); - - mailbox->cmd = MEGA_MBOXCMD_PASSTHRU; - - - pthru->numsgelements = mega_build_sglist (megaCfg, pScb, - (u32 *) & pthru-> - dataxferaddr, - (u32 *) & pthru-> - dataxferlen); - - mailbox->xferaddr = virt_to_bus (pthru); - - for (i = 0; i < (SCpnt->request_bufflen - cdblen - 7); i++) { - data[i] = data[i + cdblen + 7]; - } - return pScb; - } - /* else normal (nonpassthru) command */ - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /*0x020024 */ - /* - *usage of the function copy from user is used in case of data more than - *4KB.This is used only with adapters which supports more than 8 logical - * drives.This feature is disabled on kernels earlier or same as 2.0.36 - * as the uaccess.h file is not available with those kernels. - */ - - if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - /* use external data area for large xfers */ - /* If cmnd[0] is set to M_RD_IOCTL_CMD_NEW then * - * cmnd[4..7] = external user buffer * - * cmnd[8..11] = length of buffer * - * */ - char *user_area = (char *)*((u32*)&SCpnt->cmnd[4]); - u32 xfer_size = *((u32 *) & SCpnt->cmnd[8]); - switch (data[0]) { - case FW_FIRE_WRITE: - case FW_FIRE_FLASH: - if ((ulong) user_area & (PAGE_SIZE - 1)) { - printk - ("megaraid:user address not aligned on 4K boundary.Error.\n"); - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - break; - default: - break; - } +/** + * proc_rebuild_rate() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display current rebuild rate + */ +static int +proc_rebuild_rate(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + dma_addr_t dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + int len = 0; - if (!(pScb->buff_ptr = kmalloc (xfer_size, GFP_KERNEL))) { - printk - ("megaraid: Insufficient mem for M_RD_IOCTL_CMD_NEW.\n"); - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } + pdev = adapter->ipdev; + + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + *eof = 1; + return len; + } + + if( mega_adapinq(adapter, dma_handle) != 0 ) { + + len = sprintf(page, "Adapter inquiry failed.\n"); - copy_from_user (pScb->buff_ptr, user_area, xfer_size); - pScb->iDataSize = xfer_size; + printk(KERN_WARNING "megaraid: inquiry failed.\n"); - switch (data[0]) { - case DCMD_FC_CMD: - switch (data[1]) { - case DCMD_FC_READ_NVRAM_CONFIG: - case DCMD_GET_DISK_CONFIG: - { - if ((ulong) pScb-> - buff_ptr & (PAGE_SIZE - 1)) { - printk - ("megaraid:user address not sufficient Error.\n"); - SCpnt->result = - (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } + mega_free_inquiry(inquiry, dma_handle, pdev); - /*building SG list */ - mega_build_kernel_sg (pScb->buff_ptr, - xfer_size, - pScb, mbox); - break; - } - default: - break; - } /*switch (data[1]) */ - break; - } + *eof = 1; + return len; } -#endif - mbox->cmd = data[0]; - mbox->channel = data[1]; - mbox->param = data[2]; - mbox->pad[0] = data[3]; - mbox->logdrv = data[4]; - - if (SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - switch (data[0]) { - case FW_FIRE_WRITE: - mbox->cmd = FW_FIRE_WRITE; - mbox->channel = data[1]; /* Current Block Number */ - set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); - mbox->numsgelements = 0; - break; - case FW_FIRE_FLASH: - mbox->cmd = FW_FIRE_FLASH; - mbox->channel = data[1] | 0x80; /* Origin */ - set_mbox_xfer_addr (megaCfg, pScb, mbox, TO_DEVICE); - mbox->numsgelements = 0; - break; - case DCMD_FC_CMD: - *(mboxdata + 0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD */ - *(mboxdata + 2) = data[1]; /*sub command */ - switch (data[1]) { - case DCMD_FC_READ_NVRAM_CONFIG: - case DCMD_FC_READ_NVRAM_CONFIG_64: - /* number of elements in SG list */ - *(mboxdata + 3) = mbox->numsgelements; - if (megaCfg->flag & BOARD_64BIT) - *(mboxdata + 2) = - DCMD_FC_READ_NVRAM_CONFIG_64; - break; - case DCMD_WRITE_CONFIG: - case DCMD_WRITE_CONFIG_64: - if (megaCfg->flag & BOARD_64BIT) - *(mboxdata + 2) = DCMD_WRITE_CONFIG_64; - set_mbox_xfer_addr (megaCfg, pScb, mbox, - TO_DEVICE); - mbox->numsgelements = 0; - break; - case DCMD_GET_DISK_CONFIG: - case DCMD_GET_DISK_CONFIG_64: - if (megaCfg->flag & BOARD_64BIT) - *(mboxdata + 2) = - DCMD_GET_DISK_CONFIG_64; - *(mboxdata + 3) = data[2]; /*number of elements in SG list */ - /*nr of elements in SG list */ - *(mboxdata + 4) = mbox->numsgelements; - break; - case DCMD_DELETE_LOGDRV: - case DCMD_DELETE_DRIVEGROUP: - case NC_SUBOP_ENQUIRY3: - *(mboxdata + 3) = data[2]; - set_mbox_xfer_addr (megaCfg, pScb, mbox, - FROMTO_DEVICE); - mbox->numsgelements = 0; - break; - case DCMD_CHANGE_LDNO: - case DCMD_CHANGE_LOOPID: - *(mboxdata + 3) = data[2]; - *(mboxdata + 4) = data[3]; - set_mbox_xfer_addr (megaCfg, pScb, mbox, - TO_DEVICE); - mbox->numsgelements = 0; - break; - default: - set_mbox_xfer_addr (megaCfg, pScb, mbox, - FROMTO_DEVICE); - mbox->numsgelements = 0; - break; - } /*switch */ - break; - default: - set_mbox_xfer_addr (megaCfg, pScb, mbox, FROMTO_DEVICE); - mbox->numsgelements = 0; - break; - } - } else { + if( adapter->flag & BOARD_40LD ) { + len = sprintf(page, "Rebuild Rate: [%d%%]\n", + ((mega_inquiry3 *)inquiry)->rebuild_rate); + } + else { + len = sprintf(page, "Rebuild Rate: [%d%%]\n", + ((mraid_ext_inquiry *) + inquiry)->raid_inq.adapter_info.rebuild_rate); + } - mbox->numsgelements = mega_build_sglist (megaCfg, pScb, - (u32 *) & mbox-> - xferaddr, - (u32 *) & seg); - - /* Handling some of the fw special commands */ - switch (data[0]) { - case 6: /* START_DEV */ - mbox->xferaddr = *((u32 *) & data[i + 6]); - break; - default: - break; - } - for (i = 0; i < (SCpnt->request_bufflen - 6); i++) { - data[i] = data[i + 6]; - } - } + mega_free_inquiry(inquiry, dma_handle, pdev); - return (pScb); + *eof = 1; + + return len; } -static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, mega_ioctl_mbox * mbox) +/** + * proc_battery() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the battery module on the controller. + */ +static int +proc_battery(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - ulong i, buffer_area, len, end, end_page, x, idx = 0; + adapter_t *adapter = (adapter_t *)data; + dma_addr_t dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + u8 battery_status = 0; + char str[256]; + int len = 0; - buffer_area = (ulong) barea; - i = buffer_area; - end = buffer_area + xfersize; - end_page = (end) & ~(PAGE_SIZE - 1); + pdev = adapter->ipdev; - do { - len = PAGE_SIZE - (i % PAGE_SIZE); - x = pScb->sgList[idx].address = - virt_to_bus ((volatile void *) i); - pScb->sgList[idx].length = len; - i += len; - idx++; - } while (i < end_page); - - if ((end - i) < 0) { - printk ("megaraid:Error in user address\n"); - } - - if (end - i) { - pScb->sgList[idx].address = virt_to_bus ((volatile void *) i); - pScb->sgList[idx].length = end - i; - idx++; + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + *eof = 1; + return len; } - mbox->xferaddr = virt_to_bus (pScb->sgList); - mbox->numsgelements = idx; -} -#endif + if( mega_adapinq(adapter, dma_handle) != 0 ) { -#if DEBUG -static unsigned int cum_time = 0; -static unsigned int cum_time_cnt = 0; + len = sprintf(page, "Adapter inquiry failed.\n"); -static void showMbox (mega_scb * pScb) -{ - mega_mailbox *mbox; + printk(KERN_WARNING "megaraid: inquiry failed.\n"); - if (pScb == NULL) - return; + mega_free_inquiry(inquiry, dma_handle, pdev); - mbox = (mega_mailbox *) pScb->mboxData; - printk ("%u cmd:%x id:%x #scts:%x lba:%x addr:%x logdrv:%x #sg:%x\n", - pScb->SCpnt->pid, - mbox->cmd, mbox->cmdid, mbox->numsectors, - mbox->lba, mbox->xferaddr, mbox->logdrv, mbox->numsgelements); -} + *eof = 1; -#endif + return len; + } -/*-------------------------------------------------------------------- - * Interrupt service routine - *--------------------------------------------------------------------*/ -static void megaraid_isr (int irq, void *devp, struct pt_regs *regs) -{ - IO_LOCK_T; - mega_host_config * megaCfg; - u_char byte, idx, sIdx, tmpBox[MAILBOX_SIZE]; - u32 dword = 0; - mega_mailbox *mbox; - mega_scb *pScb; - u_char qCnt, qStatus; - u_char completed[MAX_FIRMWARE_STATUS]; - Scsi_Cmnd *SCpnt; - - megaCfg = (mega_host_config *) devp; - mbox = (mega_mailbox *) tmpBox; - - if (megaCfg->host->irq == irq) { - if (megaCfg->flag & IN_ISR) { - TRACE (("ISR called reentrantly!!\n")); - printk ("ISR called reentrantly!!\n"); - } - megaCfg->flag |= IN_ISR; + if( adapter->flag & BOARD_40LD ) { + battery_status = ((mega_inquiry3 *)inquiry)->battery_status; + } + else { + battery_status = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.adapter_info.battery_status; + } - if (mega_busyWaitMbox (megaCfg)) { - printk (KERN_WARNING "Error: mailbox busy in isr!\n"); - } + /* + * Decode the battery status + */ + sprintf(str, "Battery Status:[%d]", battery_status); - /* Check if a valid interrupt is pending */ - if (megaCfg->flag & BOARD_QUARTZ) { - dword = RDOUTDOOR (megaCfg); - if (dword != 0x10001234) { - /* Spurious interrupt */ - megaCfg->flag &= ~IN_ISR; - return; - } - } else { - byte = READ_PORT (megaCfg->host->io_port, INTR_PORT); - if ((byte & VALID_INTR_BYTE) == 0) { - /* Spurious interrupt */ - megaCfg->flag &= ~IN_ISR; - return; - } - WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); - } + if(battery_status == MEGA_BATT_CHARGE_DONE) + strcat(str, " Charge Done"); - for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++) - completed[idx] = 0; + if(battery_status & MEGA_BATT_MODULE_MISSING) + strcat(str, " Module Missing"); + + if(battery_status & MEGA_BATT_LOW_VOLTAGE) + strcat(str, " Low Voltage"); + + if(battery_status & MEGA_BATT_TEMP_HIGH) + strcat(str, " Temperature High"); + + if(battery_status & MEGA_BATT_PACK_MISSING) + strcat(str, " Pack Missing"); + + if(battery_status & MEGA_BATT_CHARGE_INPROG) + strcat(str, " Charge In-progress"); + + if(battery_status & MEGA_BATT_CHARGE_FAIL) + strcat(str, " Charge Fail"); + + if(battery_status & MEGA_BATT_CYCLES_EXCEEDED) + strcat(str, " Cycles Exceeded"); - IO_LOCK; + len = sprintf(page, "%s\n", str); - megaCfg->nInterrupts++; - qCnt = 0xff; - while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ; - qStatus = 0xff; - while ((qStatus = megaCfg->mbox->status) == 0xFF) ; + mega_free_inquiry(inquiry, dma_handle, pdev); - /* Get list of completed requests */ - for (idx = 0; idx < qCnt; idx++) { - while ((sIdx = megaCfg->mbox->completed[idx]) == 0xFF) { - printk ("p"); - } - completed[idx] = sIdx; - sIdx = 0xFF; - } + *eof = 1; - if (megaCfg->flag & BOARD_QUARTZ) { - WROUTDOOR (megaCfg, dword); - /* Acknowledge interrupt */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* In this case mbox contains physical address */ -#if 0 - WRINDOOR (megaCfg, megaCfg->adjdmahandle64 | 0x2); -#else - WRINDOOR (megaCfg, 0x2); -#endif + return len; +} -#else -#if 0 - WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2); -#else - WRINDOOR (megaCfg, 0x2); -#endif +/** + * proc_pdrv_ch0() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 0. + */ +static int +proc_pdrv_ch0(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; -#endif + *eof = 1; -#if 0 - while (RDINDOOR (megaCfg) & 0x02) ; -#endif - } else { - CLEAR_INTR (megaCfg->host->io_port); - } + return (proc_pdrv(adapter, page, 0)); +} -#if DEBUG - if (qCnt >= MAX_FIRMWARE_STATUS) { - printk ("megaraid_isr: cmplt=%d ", qCnt); - } -#endif - for (idx = 0; idx < qCnt; idx++) { - sIdx = completed[idx]; - if ((sIdx > 0) && (sIdx <= MAX_COMMANDS)) { - pScb = &megaCfg->scbList[sIdx - 1]; - - /* ASSERT(pScb->state == SCB_ISSUED); */ - -#if DEBUG - if (((jiffies) - pScb->isrcount) > maxCmdTime) { - maxCmdTime = (jiffies) - pScb->isrcount; - printk - ("megaraid_isr : cmd time = %u\n", - maxCmdTime); - } -#endif - /* - * Assuming that the scsi command, for which - * an abort request was received earlier, has - * completed. - */ - if (pScb->state == SCB_ABORTED) { - SCpnt = pScb->SCpnt; - } - if (pScb->state == SCB_RESET) { - SCpnt = pScb->SCpnt; - mega_freeSCB (megaCfg, pScb); - SCpnt->result = (DID_RESET << 16); - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = - megaCfg->qCompletedT = - SCpnt; - } else { - megaCfg->qCompletedT-> - host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = - (unsigned char *) NULL; - megaCfg->qCcnt++; - continue; - } +/** + * proc_pdrv_ch1() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 1. + */ +static int +proc_pdrv_ch1(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; - /* We don't want the ISR routine to touch M_RD_IOCTL_CMD_NEW commands, so - * don't mark them as complete, instead we pop their semaphore so - * that the queue routine can finish them off - */ - if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - /* save the status byte for the queue routine to use */ - pScb->SCpnt->result = qStatus; - up (&pScb->ioctl_sem); - } else { - /* Mark command as completed */ - mega_cmd_done (megaCfg, pScb, qStatus); - } - } else { - printk - ("megaraid: wrong cmd id completed from firmware:id=%x\n", - sIdx); - } - } + *eof = 1; - mega_rundoneq (megaCfg); + return (proc_pdrv(adapter, page, 1)); +} - megaCfg->flag &= ~IN_ISR; - /* Loop through any pending requests */ - mega_runpendq (megaCfg); - IO_UNLOCK; - } +/** + * proc_pdrv_ch2() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 2. + */ +static int +proc_pdrv_ch2(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + return (proc_pdrv(adapter, page, 2)); } -/*==================================================*/ -/* Wait until the controller's mailbox is available */ -/*==================================================*/ -static int mega_busyWaitMbox (mega_host_config * megaCfg) +/** + * proc_pdrv_ch3() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display information about the physical drives on physical channel 3. + */ +static int +proc_pdrv_ch3(char *page, char **start, off_t offset, int count, int *eof, + void *data) { - mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; - long counter; + adapter_t *adapter = (adapter_t *)data; - for (counter = 0; counter < 10000; counter++) { - if (!mbox->busy) { - return 0; - } - udelay (100); - } - return -1; /* give up after 1 second */ + *eof = 1; + + return (proc_pdrv(adapter, page, 3)); } -/*===================================================== - * Post a command to the card + +/** + * proc_pdrv() + * @page - buffer to write the data in + * @adapter - pointer to our soft state * - * Arguments: - * mega_host_config *megaCfg - Controller structure - * u_char *mboxData - Mailbox area, 16 bytes - * mega_scb *pScb - SCB posting (or NULL if N/A) - * int intr - if 1, interrupt, 0 is blocking - * Return Value: (added on 7/26 for 40ld/64bit) - * -1: the command was not actually issued out - * other cases: - * intr==0, return ScsiStatus, i.e. mbox->status - * intr==1, return 0 - *===================================================== + * Display information about the physical drives. */ -static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, - mega_scb * pScb, int intr) +static int +proc_pdrv(adapter_t *adapter, char *page, int channel) { - volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; + dma_addr_t dma_handle; + char *scsi_inq; + dma_addr_t scsi_inq_dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + u8 *pdrv_state; + u8 state; + int tgt; + int max_channels; + int len = 0; + char str[80]; + int i; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - volatile mega_mailbox64 *mbox64 = (mega_mailbox64 *) megaCfg->mbox64; -#endif + pdev = adapter->ipdev; - u_char byte; + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + return len; + } -#if BITS_PER_LONG==64 - u64 phys_mbox; -#else - u32 phys_mbox; -#endif - u8 retval = -1; + if( mega_adapinq(adapter, dma_handle) != 0 ) { - mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE); /* Set cmdid */ - mboxData[0xF] = 1; /* Set busy */ + len = sprintf(page, "Adapter inquiry failed.\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* In this case mbox contains physical address */ - phys_mbox = megaCfg->adjdmahandle64; -#else - phys_mbox = virt_to_bus (megaCfg->mbox); -#endif + printk(KERN_WARNING "megaraid: inquiry failed.\n"); -#if DEBUG - ShowMbox (pScb); -#endif + mega_free_inquiry(inquiry, dma_handle, pdev); - /* Wait until mailbox is free */ - if (mega_busyWaitMbox (megaCfg)) { - printk ("Blocked mailbox......!!\n"); - udelay (1000); + return len; + } -#if DEBUG - showMbox (pLastScb); -#endif - /* Abort command */ - if (pScb == NULL) { - TRACE (("NULL pScb in megaIssue\n")); - printk ("NULL pScb in megaIssue\n"); - } - mega_cmd_done (megaCfg, pScb, 0x08); - return -1; - } + scsi_inq = pci_alloc_consistent(pdev, 256, &scsi_inq_dma_handle); - pLastScb = pScb; + if( scsi_inq == NULL ) { + len = sprintf(page, "memory not available for scsi inq.\n"); - /* Copy mailbox data into host structure */ - megaCfg->mbox64->xferSegment_lo = 0; - megaCfg->mbox64->xferSegment_hi = 0; + mega_free_inquiry(inquiry, dma_handle, pdev); - memcpy ((char *) mbox, mboxData, 16); + return len; + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - switch (mboxData[0]) { - case MEGA_MBOXCMD_LREAD64: - case MEGA_MBOXCMD_LWRITE64: - mbox64->xferSegment_lo = mbox->xferaddr; - mbox64->xferSegment_hi = 0; - mbox->xferaddr = 0xFFFFFFFF; - break; + if( adapter->flag & BOARD_40LD ) { + pdrv_state = ((mega_inquiry3 *)inquiry)->pdrv_state; + } + else { + pdrv_state = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.pdrv_info.pdrv_state; } -#endif - /* Kick IO */ - if (intr) { - /* Issue interrupt (non-blocking) command */ - if (megaCfg->flag & BOARD_QUARTZ) { - mbox->mraid_poll = 0; - mbox->mraid_ack = 0; + max_channels = adapter->product_info.nchannels; - WRINDOOR (megaCfg, phys_mbox | 0x1); - } else { - ENABLE_INTR (megaCfg->host->io_port); - ISSUE_COMMAND (megaCfg->host->io_port); - } - pScb->state = SCB_ISSUED; + if( channel >= max_channels ) return 0; - retval = 0; - } else { /* Issue non-ISR (blocking) command */ - disable_irq (megaCfg->host->irq); - if (megaCfg->flag & BOARD_QUARTZ) { - mbox->mraid_poll = 0; - mbox->mraid_ack = 0; - mbox->numstatus = 0xFF; - mbox->status = 0xFF; - WRINDOOR (megaCfg, phys_mbox | 0x1); - - while (mbox->numstatus == 0xFF) ; - while (mbox->status == 0xFF) ; - while (mbox->mraid_poll != 0x77) ; - mbox->mraid_poll = 0; - mbox->mraid_ack = 0x77; - - /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); - WROUTDOOR (megaCfg, cmdDone); */ + for( tgt = 0; tgt <= MAX_TARGET; tgt++ ) { - if (pScb) { - mega_cmd_done (megaCfg, pScb, mbox->status); - } + i = channel*16 + tgt; - WRINDOOR (megaCfg, phys_mbox | 0x2); - while (RDINDOOR (megaCfg) & 0x2) ; + state = *(pdrv_state + i); - } else { - DISABLE_INTR (megaCfg->host->io_port); - ISSUE_COMMAND (megaCfg->host->io_port); + switch( state & 0x0F ) { + + case PDRV_ONLINE: + sprintf(str, + "Channel:%2d Id:%2d State: Online", + channel, tgt); + break; + + case PDRV_FAILED: + sprintf(str, + "Channel:%2d Id:%2d State: Failed", + channel, tgt); + break; + + case PDRV_RBLD: + sprintf(str, + "Channel:%2d Id:%2d State: Rebuild", + channel, tgt); + break; + + case PDRV_HOTSPARE: + sprintf(str, + "Channel:%2d Id:%2d State: Hot spare", + channel, tgt); + break; + + default: + sprintf(str, + "Channel:%2d Id:%2d State: Un-configured", + channel, tgt); + break; + + } + + /* + * This interface displays inquiries for disk drives + * only. Inquries for logical drives and non-disk + * devices are available through /proc/scsi/scsi + */ + memset(scsi_inq, 0, 256); + if( mega_internal_dev_inquiry(adapter, channel, tgt, + scsi_inq_dma_handle) || + (scsi_inq[0] & 0x1F) != TYPE_DISK ) { + continue; + } + + /* + * Check for overflow. We print less than 240 + * characters for inquiry + */ + if( (len + 240) >= PAGE_SIZE ) break; - while (! - ((byte = - READ_PORT (megaCfg->host->io_port, - INTR_PORT)) & INTR_VALID)) ; - WRITE_PORT (megaCfg->host->io_port, INTR_PORT, byte); - - ENABLE_INTR (megaCfg->host->io_port); - CLEAR_INTR (megaCfg->host->io_port); - - if (pScb) { - mega_cmd_done (megaCfg, pScb, mbox->status); - } else { - TRACE (("Error: NULL pScb!\n")); - } - } - enable_irq (megaCfg->host->irq); - retval = mbox->status; - } -#if DEBUG - while (mega_busyWaitMbox (megaCfg)) { - printk(KERN_ERR "Blocked mailbox on exit......!\n"); - udelay (1000); + len += sprintf(page+len, "%s.\n", str); + + len += mega_print_inquiry(page+len, scsi_inq); } -#endif - return retval; + pci_free_consistent(pdev, 256, scsi_inq, scsi_inq_dma_handle); + + mega_free_inquiry(inquiry, dma_handle, pdev); + + return len; } -/*------------------------------------------------------------------- - * Copies data to SGLIST - *-------------------------------------------------------------------*/ -/* Note: - For 64 bit cards, we need a minimum of one SG element for read/write -*/ +/* + * Display scsi inquiry + */ static int -mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, - u32 * buffer, u32 * length) +mega_print_inquiry(char *page, char *scsi_inq) { - struct scatterlist *sgList; - int idx; + int len = 0; + int i; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - int sgcnt; -#endif + len = sprintf(page, " Vendor: "); + for( i = 8; i < 16; i++ ) { + len += sprintf(page+len, "%c", scsi_inq[i]); + } - mega_mailbox *mbox = NULL; + len += sprintf(page+len, " Model: "); - mbox = (mega_mailbox *) scb->mboxData; - /* Scatter-gather not used */ - if (scb->SCpnt->use_sg == 0) { + for( i = 16; i < 32; i++ ) { + len += sprintf(page+len, "%c", scsi_inq[i]); + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, - scb->SCpnt->request_buffer, - scb->SCpnt->request_bufflen, - scb->dma_direction); - /* We need to handle special commands like READ64, WRITE64 - as they need a minimum of 1 SG irrespective of actually SG - */ - if ((megaCfg->flag & BOARD_64BIT) && - ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || - (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { - scb->sg64List[0].address = scb->dma_h_bulkdata; - scb->sg64List[0].length = scb->SCpnt->request_bufflen; - *buffer = scb->dma_sghandle64; - *length = (u32)scb->SCpnt->request_bufflen; - scb->sglist_count = 1; - return 1; - } else { - *buffer = scb->dma_h_bulkdata; - *length = (u32) scb->SCpnt->request_bufflen; - } -#else - *buffer = virt_to_bus (scb->SCpnt->request_buffer); - *length = (u32) scb->SCpnt->request_bufflen; -#endif - return 0; + len += sprintf(page+len, " Rev: "); + + for( i = 32; i < 36; i++ ) { + len += sprintf(page+len, "%c", scsi_inq[i]); } - sgList = (struct scatterlist *) scb->SCpnt->request_buffer; -#if 0 - if (scb->SCpnt->use_sg == 1) { + len += sprintf(page+len, "\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scb->dma_h_bulkdata = pci_map_single (megaCfg->dev, - sgList[0].address, - sgList[0].length, scb->dma_direction); - - if ((megaCfg->flag & BOARD_64BIT) && - ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || - (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { - scb->sg64List[0].address = scb->dma_h_bulkdata; - scb->sg64List[0].length = scb->SCpnt->request_bufflen; - *buffer = scb->dma_sghandle64; - *length = 0; - scb->sglist_count = 1; - return 1; - } else { - *buffer = scb->dma_h_bulkdata; - *length = (u32) sgList[0].length; - } -#else - *buffer = virt_to_bus (sgList[0].address); - *length = (u32) sgList[0].length; -#endif + i = scsi_inq[0] & 0x1f; - return 0; - } -#endif - /* Copy Scatter-Gather list info into controller structure */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - sgcnt = pci_map_sg (megaCfg->dev, - sgList, scb->SCpnt->use_sg, scb->dma_direction); - - /* Determine the validity of the new count */ - if (sgcnt == 0) - printk ("pci_map_sg returned zero!!! "); - - for (idx = 0; idx < sgcnt; idx++, sgList++) { - - if ((megaCfg->flag & BOARD_64BIT) && - ((mbox->cmd == MEGA_MBOXCMD_LREAD64) || - (mbox->cmd == MEGA_MBOXCMD_LWRITE64))) { - scb->sg64List[idx].address = sg_dma_address (sgList); - scb->sg64List[idx].length = sg_dma_len (sgList); - } else { - scb->sgList[idx].address = sg_dma_address (sgList); - scb->sgList[idx].length = sg_dma_len (sgList); - } + len += sprintf(page+len, " Type: %s ", + i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : + "Unknown "); - } + len += sprintf(page+len, + " ANSI SCSI revision: %02x", scsi_inq[2] & 0x07); -#else - for (idx = 0; idx < scb->SCpnt->use_sg; idx++) { - scb->sgList[idx].address = virt_to_bus (sgList[idx].address); - scb->sgList[idx].length = (u32) sgList[idx].length; - } -#endif + if( (scsi_inq[2] & 0x07) == 1 && (scsi_inq[3] & 0x0f) == 1 ) + len += sprintf(page+len, " CCS\n"); + else + len += sprintf(page+len, "\n"); - /* Reset pointer and length fields */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - *buffer = scb->dma_sghandle64; - scb->sglist_count = scb->SCpnt->use_sg; -#else - *buffer = virt_to_bus (scb->sgList); -#endif + return len; +} -#if 0 - *length = 0; -#endif - /* - * For passthru command, dataxferlen must be set, even for commands with a - * sg list - */ - *length = (u32)scb->SCpnt->request_bufflen; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* Return count of SG requests */ - return sgcnt; -#else - /* Return count of SG requests */ - return scb->SCpnt->use_sg; -#endif +/** + * proc_rdrv_10() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_10(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + + return (proc_rdrv(adapter, page, 0, 9)); } -/*-------------------------------------------------------------------- - * Initializes the address of the controller's mailbox register - * The mailbox register is used to issue commands to the card. - * Format of the mailbox area: - * 00 01 command - * 01 01 command id - * 02 02 # of sectors - * 04 04 logical bus address - * 08 04 physical buffer address - * 0C 01 logical drive # - * 0D 01 length of scatter/gather list - * 0E 01 reserved - * 0F 01 mailbox busy - * 10 01 numstatus byte - * 11 01 status byte - *--------------------------------------------------------------------*/ -static int -mega_register_mailbox (mega_host_config * megaCfg, u32 paddr) -{ - /* align on 16-byte boundary */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megaCfg->mbox = &megaCfg->mailbox64ptr->mailbox; -#else - megaCfg->mbox = &megaCfg->mailbox64.mailbox; -#endif -#if BITS_PER_LONG==64 - megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ((u64) (-1) ^ 0x0F)); - megaCfg->adjdmahandle64 = (megaCfg->dma_handle64 + 16) & ((u64) (-1) ^ 0x0F); - megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - sizeof (u64)); - paddr = (paddr + 4 + 16) & ((u64) (-1) ^ 0x0F); -#else - megaCfg->mbox - = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0); +/** + * proc_rdrv_20() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_20(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megaCfg->adjdmahandle64 = ((megaCfg->dma_handle64 + 16) & 0xFFFFFFF0); -#endif + *eof = 1; - megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - 8); - paddr = (paddr + 4 + 16) & 0xFFFFFFF0; -#endif + return (proc_rdrv(adapter, page, 10, 19)); +} - /* Register mailbox area with the firmware */ - if (!(megaCfg->flag & BOARD_QUARTZ)) { - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT0, paddr & 0xFF); - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT1, - (paddr >> 8) & 0xFF); - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT2, - (paddr >> 16) & 0xFF); - WRITE_PORT (megaCfg->host->io_port, MBOX_PORT3, - (paddr >> 24) & 0xFF); - WRITE_PORT (megaCfg->host->io_port, ENABLE_MBOX_REGION, - ENABLE_MBOX_BYTE); - CLEAR_INTR (megaCfg->host->io_port); - ENABLE_INTR (megaCfg->host->io_port); - } - return 0; +/** + * proc_rdrv_30() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_30(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + + return (proc_rdrv(adapter, page, 20, 29)); +} + + +/** + * proc_rdrv_40() + * @page - buffer to write the data in + * @start - where the actual data has been written in page + * @offset - same meaning as the read system call + * @count - same meaning as the read system call + * @eof - set if no more data needs to be returned + * @data - pointer to our soft state + * + * Display real time information about the logical drives 0 through 9. + */ +static int +proc_rdrv_40(char *page, char **start, off_t offset, int count, int *eof, + void *data) +{ + adapter_t *adapter = (adapter_t *)data; + + *eof = 1; + + return (proc_rdrv(adapter, page, 30, 39)); } -/*--------------------------------------------------------------------------- - * mega_Convert8ldTo40ld() -- takes all info in AdapterInquiry structure and - * puts it into ProductInfo and Enquiry3 structures for later use - *---------------------------------------------------------------------------*/ -static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, - mega_Enquiry3 * enquiry3, - megaRaidProductInfo * productInfo) + +/** + * proc_rdrv() + * @page - buffer to write the data in + * @adapter - pointer to our soft state + * @start - starting logical drive to display + * @end - ending logical drive to display + * + * We do not print the inquiry information since its already available through + * /proc/scsi/scsi interface + */ +static int +proc_rdrv(adapter_t *adapter, char *page, int start, int end ) { - int i; + dma_addr_t dma_handle; + logdrv_param *lparam; + megacmd_t mc; + char *disk_array; + dma_addr_t disk_array_dma_handle; + caddr_t inquiry; + struct pci_dev *pdev; + u8 *rdrv_state; + int num_ldrv; + u32 array_sz; + int len = 0; + int i; - productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds; - enquiry3->rbldRate = inquiry->AdpInfo.RbldRate; - productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent; + pdev = adapter->ipdev; - for (i = 0; i < 4; i++) { - productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i]; - productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i]; + if( (inquiry = mega_allocate_inquiry(&dma_handle, pdev)) == NULL ) { + return len; } - enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval; - productInfo->DramSize = inquiry->AdpInfo.DramSize; - enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv; + if( mega_adapinq(adapter, dma_handle) != 0 ) { + + len = sprintf(page, "Adapter inquiry failed.\n"); + + printk(KERN_WARNING "megaraid: inquiry failed.\n"); + + mega_free_inquiry(inquiry, dma_handle, pdev); - for (i = 0; i < MAX_LOGICAL_DRIVES; i++) { - enquiry3->lDrvSize[i] = inquiry->LogdrvInfo.LDrvSize[i]; - enquiry3->lDrvProp[i] = inquiry->LogdrvInfo.LDrvProp[i]; - enquiry3->lDrvState[i] - = inquiry->LogdrvInfo.LDrvState[i]; + return len; } - for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) { - enquiry3->pDrvState[i] - = inquiry->PhysdrvInfo.PDrvState[i]; - } -} + memset(&mc, 0, sizeof(megacmd_t)); -/*------------------------------------------------------------------- - * Issue an adapter info query to the controller - *-------------------------------------------------------------------*/ -static int mega_i_query_adapter (mega_host_config * megaCfg) -{ - mega_Enquiry3 *enquiry3Pnt; - mega_mailbox *mbox; - u_char mboxData[16]; + if( adapter->flag & BOARD_40LD ) { + array_sz = sizeof(disk_array_40ld); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t raid_inq_dma_handle = 0, prod_info_dma_handle = 0, enquiry3_dma_handle = 0; -#endif - u8 retval; + rdrv_state = ((mega_inquiry3 *)inquiry)->ldrv_state; - /* Initialize adapter inquiry mailbox */ + num_ldrv = ((mega_inquiry3 *)inquiry)->num_ldrv; + } + else { + array_sz = sizeof(disk_array_8ld); - mbox = (mega_mailbox *) mboxData; + rdrv_state = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.logdrv_info.ldrv_state; - memset ((void *) megaCfg->mega_buffer, 0, - sizeof (megaCfg->mega_buffer)); - memset (mbox, 0, 16); + num_ldrv = ((mraid_ext_inquiry *)inquiry)-> + raid_inq.logdrv_info.num_ldrv; + } -/* - * Try to issue Enquiry3 command - * if not succeeded, then issue MEGA_MBOXCMD_ADAPTERINQ command and - * update enquiry3 structure - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - enquiry3_dma_handle = pci_map_single (megaCfg->dev, - (void *) megaCfg->mega_buffer, - (2 * 1024L), PCI_DMA_FROMDEVICE); + disk_array = pci_alloc_consistent(pdev, array_sz, + &disk_array_dma_handle); - mbox->xferaddr = enquiry3_dma_handle; -#else - /*Taken care */ - mbox->xferaddr = virt_to_bus ((void *) megaCfg->mega_buffer); -#endif + if( disk_array == NULL ) { + len = sprintf(page, "memory not available.\n"); - /* Initialize mailbox databuffer addr */ - enquiry3Pnt = (mega_Enquiry3 *) megaCfg->mega_buffer; - /* point mega_Enguiry3 to the data buf */ - - mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ - mboxData[2] = NC_SUBOP_ENQUIRY3; /* i.e. 0x0F */ - mboxData[3] = ENQ3_GET_SOLICITED_FULL; /* i.e. 0x02 */ + mega_free_inquiry(inquiry, dma_handle, pdev); - /* Issue a blocking command to the card */ - if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) { /* the adapter does not support 40ld */ - mega_RAIDINQ adapterInquiryData; - mega_RAIDINQ *adapterInquiryPnt = &adapterInquiryData; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - raid_inq_dma_handle = pci_map_single (megaCfg->dev, - (void *) adapterInquiryPnt, - sizeof (mega_RAIDINQ), - PCI_DMA_FROMDEVICE); - mbox->xferaddr = raid_inq_dma_handle; -#else - /*taken care */ - mbox->xferaddr = virt_to_bus ((void *) adapterInquiryPnt); -#endif + return len; + } - mbox->cmd = MEGA_MBOXCMD_ADAPTERINQ; /*issue old 0x05 command to adapter */ - /* Issue a blocking command to the card */ ; - retval = megaIssueCmd (megaCfg, mboxData, NULL, 0); - - pci_unmap_single (megaCfg->dev, - raid_inq_dma_handle, - sizeof (mega_RAIDINQ), PCI_DMA_FROMDEVICE); - - /*update Enquiry3 and ProductInfo structures with mega_RAIDINQ structure*/ - mega_Convert8ldTo40ld (adapterInquiryPnt, - enquiry3Pnt, - (megaRaidProductInfo *) & megaCfg-> - productInfo); - - } else { /* adapter supports 40ld */ - megaCfg->flag |= BOARD_40LD; - - pci_unmap_single (megaCfg->dev, - enquiry3_dma_handle, - (2 * 1024L), PCI_DMA_FROMDEVICE); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -/*get productInfo, which is static information and will be unchanged*/ - prod_info_dma_handle - = pci_map_single (megaCfg->dev, - (void *) &megaCfg->productInfo, - sizeof (megaRaidProductInfo), - PCI_DMA_FROMDEVICE); - mbox->xferaddr = prod_info_dma_handle; -#else - /*taken care */ - mbox->xferaddr = virt_to_bus ((void *) &megaCfg->productInfo); -#endif + mc.xferaddr = (u32)disk_array_dma_handle; - mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */ - mboxData[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */ + if( adapter->flag & BOARD_40LD ) { + mc.cmd = FC_NEW_CONFIG; + mc.opcode = OP_DCMD_READ_CONFIG; - if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0) - printk ("megaraid: Product_info cmd failed with error: %d\n", - retval); + if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) ) { - pci_unmap_single (megaCfg->dev, - prod_info_dma_handle, - sizeof (megaRaidProductInfo), - PCI_DMA_FROMDEVICE); - } + len = sprintf(page, "40LD read config failed.\n"); - /* - * kernel scans the channels from 0 to <= max_channel - */ - megaCfg->host->max_channel = - megaCfg->productInfo.SCSIChanPresent + NVIRT_CHAN -1; + mega_free_inquiry(inquiry, dma_handle, pdev); + + pci_free_consistent(pdev, array_sz, disk_array, + disk_array_dma_handle); - megaCfg->host->max_id = 16; /* max targets per channel */ + return len; + } - megaCfg->host->max_lun = 7; /* Upto 7 luns for non disk devices */ + } + else { + mc.cmd = NEW_READ_CONFIG_8LD; - megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN; + if( mega_internal_command(adapter, LOCK_INT, &mc, NULL) ) { - megaCfg->numldrv = enquiry3Pnt->numLDrv; - megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds; - if (megaCfg->max_cmds > MAX_COMMANDS) - megaCfg->max_cmds = MAX_COMMANDS - 1; + mc.cmd = READ_CONFIG_8LD; - megaCfg->host->can_queue = megaCfg->max_cmds - 1; + if( mega_internal_command(adapter, LOCK_INT, &mc, + NULL) ){ - /* use HP firmware and bios version encoding */ - if (megaCfg->productInfo.subSystemVendorID == HP_SUBSYS_ID) { - sprintf (megaCfg->fwVer, "%c%d%d.%d%d", - megaCfg->productInfo.FwVer[2], - megaCfg->productInfo.FwVer[1] >> 8, - megaCfg->productInfo.FwVer[1] & 0x0f, - megaCfg->productInfo.FwVer[0] >> 8, - megaCfg->productInfo.FwVer[0] & 0x0f); - sprintf (megaCfg->biosVer, "%c%d%d.%d%d", - megaCfg->productInfo.BiosVer[2], - megaCfg->productInfo.BiosVer[1] >> 8, - megaCfg->productInfo.BiosVer[1] & 0x0f, - megaCfg->productInfo.BiosVer[0] >> 8, - megaCfg->productInfo.BiosVer[0] & 0x0f); - } else { - memcpy (megaCfg->fwVer, (char *) megaCfg->productInfo.FwVer, 4); - megaCfg->fwVer[4] = 0; + len = sprintf(page, + "8LD read config failed.\n"); - memcpy (megaCfg->biosVer, (char *) megaCfg->productInfo.BiosVer, 4); - megaCfg->biosVer[4] = 0; - } - megaCfg->support_ext_cdb = mega_support_ext_cdb(megaCfg); + mega_free_inquiry(inquiry, dma_handle, pdev); - printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR, - megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv); + pci_free_consistent(pdev, array_sz, + disk_array, + disk_array_dma_handle); - if ( megaCfg->support_ext_cdb ) { - printk(KERN_NOTICE "megaraid: supports extended CDBs.\n"); + return len; + } + } } - /* - * I hope that I can unmap here, reason DMA transaction is not required any more - * after this - */ + for( i = start; i < ( (end+1 < num_ldrv) ? end+1 : num_ldrv ); i++ ) { - return 0; -} + if( adapter->flag & BOARD_40LD ) { + lparam = + &((disk_array_40ld *)disk_array)->ldrv[i].lparam; + } + else { + lparam = + &((disk_array_8ld *)disk_array)->ldrv[i].lparam; + } -/*------------------------------------------------------------------------- - * - * Driver interface functions - * - *-------------------------------------------------------------------------*/ + /* + * Check for overflow. We print less than 240 characters for + * information about each logical drive. + */ + if( (len + 240) >= PAGE_SIZE ) break; -/*---------------------------------------------------------- - * Returns data to be displayed in /proc/scsi/megaraid/X - *----------------------------------------------------------*/ + len += sprintf(page+len, "Logical drive:%2d:, ", i); -int megaraid_proc_info (char *buffer, char **start, off_t offset, - int length, int host_no, int inout) -{ - *start = buffer; - return 0; -} + switch( rdrv_state[i] & 0x0F ) { + case RDRV_OFFLINE: + len += sprintf(page+len, "state: offline"); + break; -static int mega_findCard (Scsi_Host_Template * pHostTmpl, - u16 pciVendor, u16 pciDev, long flag) -{ - mega_host_config *megaCfg = NULL; - struct Scsi_Host *host = NULL; - u_char pciBus, pciDevFun, megaIrq; - - u16 magic; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - u32 magic64; -#endif + case RDRV_DEGRADED: + len += sprintf(page+len, "state: degraded"); + break; - int i, j; + case RDRV_OPTIMAL: + len += sprintf(page+len, "state: optimal"); + break; -#if BITS_PER_LONG==64 - u64 megaBase; -#else - u32 megaBase; -#endif + case RDRV_DELETED: + len += sprintf(page+len, "state: deleted"); + break; - u16 pciIdx = 0; - u16 numFound = 0; - u16 subsysid, subsysvid; - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - while (!pcibios_find_device - (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) { -#else + default: + len += sprintf(page+len, "state: unknown"); + break; + } -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ - struct pci_dev *pdev = NULL; -#else - struct pci_dev *pdev = pci_devices; -#endif + /* + * Check if check consistency or initialization is going on + * for this logical drive. + */ + if( (rdrv_state[i] & 0xF0) == 0x20 ) { + len += sprintf(page+len, + ", check-consistency in progress"); + } + else if( (rdrv_state[i] & 0xF0) == 0x10 ) { + len += sprintf(page+len, + ", initialization in progress"); + } + + len += sprintf(page+len, "\n"); - while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) { - if(pci_enable_device (pdev)) - continue; - pciBus = pdev->bus->number; - pciDevFun = pdev->devfn; -#endif - if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { - if( (pciVendor == PCI_VENDOR_ID_PERC4_DI_YSTONE && - pciDev == PCI_DEVICE_ID_PERC4_DI_YSTONE) || - (pciVendor == PCI_VENDOR_ID_PERC4_QC_VERDE && - pciDev == PCI_DEVICE_ID_PERC4_QC_VERDE) ) { + len += sprintf(page+len, "Span depth:%3d, ", + lparam->span_depth); - flag |= BOARD_64BIT; - } - else { - pci_read_config_word (pdev, PCI_CONF_AMISIG, &magic); - if ((magic != AMI_SIGNATURE) - && (magic != AMI_SIGNATURE_471)) { - pciIdx++; - continue; /* not an AMI board */ - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pci_read_config_dword (pdev, PCI_CONF_AMISIG64, &magic64); + len += sprintf(page+len, "RAID level:%3d, ", + lparam->level); - if (magic64 == AMI_64BIT_SIGNATURE) - flag |= BOARD_64BIT; -#endif - } - } + len += sprintf(page+len, "Stripe size:%3d, ", + lparam->stripe_sz ? lparam->stripe_sz/2: 128); - /* Hmmm...Should we not make this more modularized so that in future we dont add - for each firmware */ + len += sprintf(page+len, "Row size:%3d\n", + lparam->row_size); - if (flag & BOARD_QUARTZ) { - /* Check to see if this is a Dell PERC RAID controller model 466 */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - pcibios_read_config_word (pciBus, pciDevFun, - PCI_SUBSYSTEM_VENDOR_ID, - &subsysvid); - pcibios_read_config_word (pciBus, pciDevFun, - PCI_SUBSYSTEM_ID, &subsysid); -#else - pci_read_config_word (pdev, - PCI_SUBSYSTEM_VENDOR_ID, - &subsysvid); - pci_read_config_word (pdev, - PCI_SUBSYSTEM_ID, &subsysid); -#endif - /* - * If we do not find the valid subsys vendor id, refuse to load - * the driver. This is part of PCI200X compliance - */ - if( (subsysvid != AMI_SUBSYS_ID) && - (subsysvid != DELL_SUBSYS_ID) && - (subsysvid != LSI_SUBSYS_ID) && - (subsysvid != HP_SUBSYS_ID) ) continue; + len += sprintf(page+len, "Read Policy: "); - } + switch(lparam->read_ahead) { - printk (KERN_NOTICE - "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n", - pciVendor, pciDev, pciIdx, pciBus, PCI_SLOT (pciDevFun), - PCI_FUNC (pciDevFun)); - /* Read the base port and IRQ from PCI */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - pcibios_read_config_dword (pciBus, pciDevFun, - PCI_BASE_ADDRESS_0, - (u_int *) & megaBase); - pcibios_read_config_byte (pciBus, pciDevFun, - PCI_INTERRUPT_LINE, &megaIrq); -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ - megaBase = pdev->base_address[0]; - megaIrq = pdev->irq; -#else + case NO_READ_AHEAD: + len += sprintf(page+len, "No read ahead, "); + break; - megaBase = pci_resource_start (pdev, 0); - megaIrq = pdev->irq; -#endif + case READ_AHEAD: + len += sprintf(page+len, "Read ahead, "); + break; - pciIdx++; + case ADAP_READ_AHEAD: + len += sprintf(page+len, "Adaptive, "); + break; - if (flag & BOARD_QUARTZ) { - megaBase &= PCI_BASE_ADDRESS_MEM_MASK; - megaBase = (long) ioremap (megaBase, 128); - if (!megaBase) - continue; - } else { - megaBase &= PCI_BASE_ADDRESS_IO_MASK; - megaBase += 0x10; } - /* Initialize SCSI Host structure */ - host = scsi_register (pHostTmpl, sizeof (mega_host_config)); - if (!host) - goto err_unmap; + len += sprintf(page+len, "Write Policy: "); - /* - * Comment the following initialization if you know 'max_sectors' is - * not defined for this kernel. - * This field was introduced in Linus's kernel 2.4.7pre3 and it - * greatly increases the IO performance - AM - */ - host->max_sectors = 1024; + switch(lparam->write_mode) { - scsi_set_pci_device(host, pdev); - megaCfg = (mega_host_config *) host->hostdata; - memset (megaCfg, 0, sizeof (mega_host_config)); + case WRMODE_WRITE_THRU: + len += sprintf(page+len, "Write thru, "); + break; - printk (KERN_NOTICE "scsi%d : Found a MegaRAID controller at 0x%x, IRQ: %d" - M_RD_CRLFSTR, host->host_no, (u_int) megaBase, megaIrq); + case WRMODE_WRITE_BACK: + len += sprintf(page+len, "Write back, "); + break; + } - if (flag & BOARD_64BIT) - printk (KERN_NOTICE "scsi%d : Enabling 64 bit support\n", - host->host_no); + len += sprintf(page+len, "Cache Policy: "); - /* Copy resource info into structure */ - megaCfg->qCompletedH = NULL; - megaCfg->qCompletedT = NULL; - megaCfg->qPendingH = NULL; - megaCfg->qPendingT = NULL; - megaCfg->qFreeH = NULL; - megaCfg->qFreeT = NULL; - megaCfg->qFcnt = 0; - megaCfg->qPcnt = 0; - megaCfg->qCcnt = 0; - megaCfg->lock_free = SPIN_LOCK_UNLOCKED; - megaCfg->lock_pend = SPIN_LOCK_UNLOCKED; - megaCfg->lock_scsicmd = SPIN_LOCK_UNLOCKED; - megaCfg->flag = flag; - megaCfg->int_qh = NULL; - megaCfg->int_qt = NULL; - megaCfg->int_qlen = 0; + switch(lparam->direct_io) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megaCfg->dev = pdev; -#endif - megaCfg->host = host; - megaCfg->base = megaBase; - megaCfg->host->irq = megaIrq; - megaCfg->host->io_port = megaBase; - megaCfg->host->n_io_port = 16; - megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; - megaCtlrs[numCtlrs] = megaCfg; - - if (!(flag & BOARD_QUARTZ)) { - - /* Request our IO Range */ - if( !request_region(megaBase, 16, "megaraid") ) - goto err_unregister; + case CACHED_IO: + len += sprintf(page+len, "Cached IO\n\n"); + break; + + case DIRECT_IO: + len += sprintf(page+len, "Direct IO\n\n"); + break; } + } - /* Request our IRQ */ - if (request_irq (megaIrq, megaraid_isr, SA_SHIRQ, - "megaraid", megaCfg)) { - printk (KERN_WARNING - "megaraid: Couldn't register IRQ %d!\n", - megaIrq); - goto err_release; - } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - /* - * unmap while releasing the driver, Is it required to be - * PCI_DMA_BIDIRECTIONAL - */ - - megaCfg->mailbox64ptr - = pci_alloc_consistent (megaCfg->dev, - sizeof (mega_mailbox64), - &(megaCfg->dma_handle64)); + mega_free_inquiry(inquiry, dma_handle, pdev); - mega_register_mailbox (megaCfg,megaCfg->dma_handle64); -#else - mega_register_mailbox (megaCfg, - virt_to_bus ((void *) &megaCfg-> - mailbox64)); -#endif + pci_free_consistent(pdev, array_sz, disk_array, + disk_array_dma_handle); - mega_i_query_adapter (megaCfg); + return len; +} - if ((subsysid == 0x1111) && (subsysvid == 0x1111)) { +#endif - /* - * Which firmware - */ - if( strcmp(megaCfg->fwVer, "3.00") == 0 || - strcmp(megaCfg->fwVer, "3.01") == 0 ) { - printk( KERN_WARNING - "megaraid: Your card is a Dell PERC 2/SC RAID controller " - "with firmware\nmegaraid: 3.00 or 3.01. This driver is " - "known to have corruption issues\nmegaraid: with those " - "firmware versions on this specific card. In order\n" - "megaraid: to protect your data, please upgrade your " - "firmware to version\nmegaraid: 3.10 or later, available " - "from the Dell Technical Support web\nmegaraid: site at\n" - "http://support.dell.com/us/en/filelib/download/" - "index.asp?fileid=2940\n" - ); - } - } +/** + * megaraid_biosparam() + * @disk + * @dev + * @geom + * + * Return the disk geometry for a particular disk + * Input: + * Disk *disk - Disk geometry + * kdev_t dev - Device node + * int *geom - Returns geometry fields + * geom[0] = heads + * geom[1] = sectors + * geom[2] = cylinders + */ +static int +megaraid_biosparam(Disk *disk, kdev_t dev, int *geom) +{ + int heads, sectors, cylinders; + adapter_t *adapter; - /* - * If we have a HP 1M(0x60E7)/2M(0x60E8) controller with - * firmware H.01.07 or H.01.08, disable 64 bit support, - * since this firmware cannot handle 64 bit addressing - */ + /* Get pointer to host config structure */ + adapter = (adapter_t *)disk->device->host->hostdata; - if( (subsysvid == HP_SUBSYS_ID) && - ((subsysid == 0x60E7)||(subsysid == 0x60E8)) ) { + if (IS_RAID_CH(adapter, disk->device->channel)) { + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); /* - * which firmware + * Handle extended translation size for logical drives + * > 1Gb */ - if( strcmp(megaCfg->fwVer, "H01.07") == 0 || - strcmp(megaCfg->fwVer, "H01.08") == 0 || - strcmp(megaCfg->fwVer, "H01.09") == 0 ) - { - printk(KERN_WARNING - "megaraid: Firmware H.01.07/8/9 on 1M/2M " - "controllers\nmegaraid: do not support 64 bit " - "addressing.\n" - "megaraid: DISABLING 64 bit support.\n"); - megaCfg->flag &= ~BOARD_64BIT; + if (disk->capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); } - } - - if (mega_is_bios_enabled (megaCfg)) { - mega_hbas[numCtlrs].is_bios_enabled = 1; - } - - /* - * Find out which channel is raid and which is scsi - */ - mega_enum_raid_scsi(megaCfg); - /* - * Find out if a logical drive is set as the boot drive. If there is - * one, will make that as the first logical drive. - * ROMB: Do we have to boot from a physical drive. Then all the - * physical drives would appear before the logical disks. Else, all - * the physical drives would be exported to the mid layer after - * logical disks. - */ - mega_get_boot_drv(megaCfg); - - if( ! megaCfg->boot_pdrv_enabled ) { - for( i = 0; i < NVIRT_CHAN; i++ ) - megaCfg->logdrv_chan[i] = 1; + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + } + else { + if( !mega_partsize(disk, dev, geom) ) + return 0; - for( i = NVIRT_CHAN; i < MAX_CHANNEL + NVIRT_CHAN; i++ ) - megaCfg->logdrv_chan[i] = 0; + printk(KERN_WARNING + "megaraid: invalid partition on this disk on channel %d\n", + disk->device->channel); - megaCfg->mega_ch_class <<= NVIRT_CHAN; - } - else { - j = megaCfg->productInfo.SCSIChanPresent; - for( i = 0; i < j; i++ ) - megaCfg->logdrv_chan[i] = 0; + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + cylinders = disk->capacity / (heads * sectors); - for( i = j; i < NVIRT_CHAN + j; i++ ) - megaCfg->logdrv_chan[i] = 1; + /* Handle extended translation size for logical drives > 1Gb */ + if (disk->capacity >= 0x200000) { + heads = 255; + sectors = 63; + cylinders = disk->capacity / (heads * sectors); } + /* return result */ + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + } - mega_hbas[numCtlrs].hostdata_addr = megaCfg; + return 0; +} - /* - * Do we support random deletion and addition of logical drives - */ - megaCfg->read_ldidmap = 0; /* set it after first logdrv delete cmd */ - megaCfg->support_random_del = mega_support_random_del(megaCfg); +/* + * mega_partsize() + * @disk + * @geom + * + * Purpose : to determine the BIOS mapping used to create the partition + * table, storing the results (cyls, hds, and secs) in geom + * + * Note: Code is picked from scsicam.h + * + * Returns : -1 on failure, 0 on success. + */ +static int +mega_partsize(Disk *disk, kdev_t dev, int *geom) +{ + struct buffer_head *bh; + struct partition *p, *largest = NULL; + int i, largest_cyl; + int heads, cyls, sectors; + int capacity = disk->capacity; - /* Initialize SCBs */ - if (mega_init_scb (megaCfg)) { - pci_free_consistent (megaCfg->dev, - sizeof (mega_mailbox64), - (void *) megaCfg->mailbox64ptr, - megaCfg->dma_handle64); - scsi_unregister (host); - continue; - } + int ma = MAJOR(dev); + int mi = (MINOR(dev) & ~0xf); - /* - * Fill in the structure which needs to be passed back to the - * application when it does an ioctl() for controller related - * information. - */ + int block = 1024; - i = numCtlrs; - numCtlrs++; + if (blksize_size[ma]) + block = blksize_size[ma][mi]; + + if (!(bh = bread(MKDEV(ma,mi), 0, block))) + return -1; - mcontroller[i].base = megaBase; - mcontroller[i].irq = megaIrq; - mcontroller[i].numldrv = megaCfg->numldrv; - mcontroller[i].pcibus = pciBus; - mcontroller[i].pcidev = pciDev; - mcontroller[i].pcifun = PCI_FUNC (pciDevFun); - mcontroller[i].pciid = pciIdx; - mcontroller[i].pcivendor = pciVendor; - mcontroller[i].pcislot = PCI_SLOT (pciDevFun); - mcontroller[i].uid = (pciBus << 8) | pciDevFun; - - numFound++; - - /* Set the Mode of addressing to 64 bit */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if ((megaCfg->flag & BOARD_64BIT) && BITS_PER_LONG == 64) -#if BITS_PER_LONG==64 - pdev->dma_mask = 0xffffffffffffffff; -#else - pdev->dma_mask = 0xffffffff; -#endif -#endif - continue; - err_release: - if (flag & BOARD_QUARTZ) - release_region (megaBase, 16); - err_unregister: - scsi_unregister (host); - err_unmap: - if (flag & BOARD_QUARTZ) - iounmap ((void *) megaBase); - } - return numFound; -} + if (*(unsigned short *)(bh->b_data + 510) == 0xAA55 ) { -/*--------------------------------------------------------- - * Detects if a megaraid controller exists in this system - *---------------------------------------------------------*/ + for (largest_cyl = -1, + p = (struct partition *)(0x1BE + bh->b_data), i = 0; + i < 4; ++i, ++p) { -int megaraid_detect (Scsi_Host_Template * pHostTmpl) -{ - int ctlridx = 0, count = 0; + if (!p->sys_ind) continue; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ - pHostTmpl->proc_dir = &proc_scsi_megaraid; -#else - pHostTmpl->proc_name = "megaraid"; -#endif + cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */ - if (!pcibios_present ()) { - printk (KERN_WARNING "megaraid: PCI bios not present." - M_RD_CRLFSTR); - return 0; + if (cyls >= largest_cyl) { + largest_cyl = cyls; + largest = p; + } + } } -#endif - skip_id = -1; - if (megaraid && !strncmp (megaraid, "skip", strlen ("skip"))) { - if (megaraid[4] != '\0') { - skip_id = megaraid[4] - '0'; - if (megaraid[5] != '\0') { - skip_id = (skip_id * 10) + (megaraid[5] - '0'); - } - } - skip_id = (skip_id > 15) ? -1 : skip_id; - } - - printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION); - - memset (mega_hbas, 0, sizeof (mega_hbas)); - - /* Detect ROMBs first */ - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_DISCOVERY, - PCI_DEVICE_ID_DISCOVERY, BOARD_QUARTZ); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_DI_YSTONE, - PCI_DEVICE_ID_PERC4_DI_YSTONE, BOARD_QUARTZ); - /* Then detect cards based on date they were produced, oldest first */ - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, - PCI_DEVICE_ID_AMI_MEGARAID, 0); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, - PCI_DEVICE_ID_AMI_MEGARAID2, 0); - count += mega_findCard (pHostTmpl, 0x8086, - PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, - PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_LSI_LOGIC, - PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); - count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_QC_VERDE, - PCI_DEVICE_ID_PERC4_QC_VERDE, BOARD_QUARTZ); - mega_reorder_hosts (); + if (largest) { + heads = largest->end_head + 1; + sectors = largest->end_sector & 0x3f; -#ifdef CONFIG_PROC_FS - if (count) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ - mega_proc_dir_entry = proc_mkdir ("megaraid", &proc_root); -#else - mega_proc_dir_entry = create_proc_entry ("megaraid", - S_IFDIR | S_IRUGO | - S_IXUGO, &proc_root); -#endif - if (!mega_proc_dir_entry) - printk ("megaraid: failed to create megaraid root\n"); - else - for (ctlridx = 0; ctlridx < count; ctlridx++) - mega_create_proc_entry (ctlridx, - mega_proc_dir_entry); - } -#endif + if (!heads || !sectors) { + brelse(bh); + return -1; + } - /* - * Register the driver as a character device, for applications to access - * it for ioctls. - * Ideally, this should go in the init_module() routine, but since it is - * hidden in the file "scsi_module.c" ( included in the end ), we define - * it here - * First argument (major) to register_chrdev implies a dynamic major - * number allocation. - */ - if (count) { - major = register_chrdev (0, "megadev", &megadev_fops); + cyls = capacity/(heads * sectors); - /* - * Register the Shutdown Notification hook in kernel - */ - if (register_reboot_notifier (&mega_notifier)) { - printk ("MegaRAID Shutdown routine not registered!!\n"); - } + geom[0] = heads; + geom[1] = sectors; + geom[2] = cyls; - init_MUTEX (&mimd_entry_mtx); + brelse(bh); + return 0; } - return count; + brelse(bh); + return -1; } -/*--------------------------------------------------------------------- - * Release the controller's resources - *---------------------------------------------------------------------*/ -int megaraid_release (struct Scsi_Host *pSHost) + +/** + * megaraid_reboot_notify() + * @this - unused + * @code - shutdown code + * @unused - unused + * + * This routine will be called when the use has done a forced shutdown on the + * system. Flush the Adapter and disks cache. + */ +static int +megaraid_reboot_notify (struct notifier_block *this, unsigned long code, + void *unused) { - mega_host_config *megaCfg; - mega_mailbox *mbox; - u_char mboxData[16]; + adapter_t *adapter; + struct Scsi_Host *host; + u8 raw_mbox[16]; + mbox_t *mbox; int i; - megaCfg = (mega_host_config *) pSHost->hostdata; - mbox = (mega_mailbox *) mboxData; + /* + * Flush the controller's cache irrespective of the codes coming down. + * SYS_DOWN, SYS_HALT, SYS_RESTART, SYS_POWER_OFF + */ + for( i = 0; i < hba_count; i++ ) { + printk(KERN_INFO "megaraid: flushing adapter %d..", i); + host = hba_soft_state[i]->host; + + adapter = (adapter_t *)host->hostdata; + mbox = (mbox_t *)raw_mbox; + + /* Flush adapter cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_ADAPTER; - /* Flush cache to disk */ - memset (mbox, 0, 16); - mboxData[0] = 0xA; + irq_disable(adapter); + free_irq(adapter->host->irq, adapter); - free_irq (megaCfg->host->irq, megaCfg); /* Must be freed first, otherwise - extra interrupt is generated */ + /* + * Issue a blocking (interrupts disabled) command to + * the card + */ + issue_scb_block(adapter, raw_mbox); - /* Issue a blocking (interrupts disabled) command to the card */ - megaIssueCmd (megaCfg, mboxData, NULL, 0); + /* Flush disks cache */ + memset(mbox, 0, 16); + raw_mbox[0] = FLUSH_SYSTEM; - /* Free our resources */ - if (megaCfg->flag & BOARD_QUARTZ) { - iounmap ((void *) megaCfg->base); - } else { - release_region (megaCfg->host->io_port, 16); - } + issue_scb_block(adapter, raw_mbox); - mega_freeSgList (megaCfg); - pci_free_consistent (megaCfg->dev, - sizeof (mega_mailbox64), - (void *) megaCfg->mailbox64ptr, - megaCfg->dma_handle64); + printk("Done.\n"); -#ifdef CONFIG_PROC_FS - if (megaCfg->controller_proc_dir_entry) { - remove_proc_entry ("stat", megaCfg->controller_proc_dir_entry); - remove_proc_entry ("status", - megaCfg->controller_proc_dir_entry); - remove_proc_entry ("config", - megaCfg->controller_proc_dir_entry); - remove_proc_entry ("mailbox", - megaCfg->controller_proc_dir_entry); - for (i = 0; i < numCtlrs; i++) { - char buf[12] = { 0 }; - sprintf (buf, "%d", i); - remove_proc_entry (buf, mega_proc_dir_entry); + if( atomic_read(&adapter->pend_cmds) > 0 ) { + printk(KERN_WARNING "megaraid: pending commands!!\n"); } - remove_proc_entry ("megaraid", &proc_root); } -#endif - - /* - * Release the controller memory. A word of warning this frees - * hostdata and that includes megaCfg-> so be careful what you - * dereference beyond this point - */ - - scsi_unregister (pSHost); /* - * Unregister the character device interface to the driver. Ideally this - * should have been done in cleanup_module routine. Since this is hidden - * in file "scsi_module.c", we do it here. - * major is the major number of the character device returned by call to - * register_chrdev() routine. + * Have a delibrate delay to make sure all the caches are + * actually flushed. */ + printk(KERN_INFO "megaraid: cache flush delay: "); + for( i = 9; i >= 0; i-- ) { + printk("\b\b\b[%d]", i); + mdelay(1000); + } + printk("\b\b\b[done]\n"); + mdelay(1000); - unregister_chrdev (major, "megadev"); - unregister_reboot_notifier (&mega_notifier); - - return 0; + return NOTIFY_DONE; } -static int mega_is_bios_enabled (mega_host_config * megacfg) +/** + * mega_init_scb() + * @adapter - pointer to our soft state + * + * Allocate memory for the various pointers in the scb structures: + * scatter-gather list pointer, passthru and extended passthru structure + * pointers. + */ +static int +mega_init_scb(adapter_t *adapter) { - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int ret; - - mboxpnt = (mega_mailbox *) mbox; + scb_t *scb; + int i; - memset (mbox, 0, sizeof (mbox)); - memset ((void *) megacfg->mega_buffer, - 0, sizeof (megacfg->mega_buffer)); + for( i = 0; i < adapter->max_cmds; i++ ) { - /* - * issue command to find out if the BIOS is enabled for this controller - */ - mbox[0] = IS_BIOS_ENABLED; - mbox[2] = GET_BIOS; + scb = &adapter->scb_list[i]; - mboxpnt->xferaddr = virt_to_bus ((void *) megacfg->mega_buffer); + scb->sgl64 = NULL; + scb->sgl = NULL; + scb->pthru = NULL; + scb->epthru = NULL; + } - ret = megaIssueCmd (megacfg, mbox, NULL, 0); + for( i = 0; i < adapter->max_cmds; i++ ) { - return (*(char *) megacfg->mega_buffer); -} + scb = &adapter->scb_list[i]; -/* - * Find out what channels are RAID/SCSI - */ -static void -mega_enum_raid_scsi(mega_host_config *megacfg) -{ - mega_mailbox *mboxp; - unsigned char mbox[16]; - int i; + scb->idx = i; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t dma_handle; -#endif + scb->sgl64 = pci_alloc_consistent(adapter->dev, + sizeof(mega_sgl64) * adapter->sglen, + &scb->sgl_dma_addr); - mboxp = (mega_mailbox *)mbox; + scb->sgl = (mega_sglist *)scb->sgl64; - memset(mbox, 0, sizeof(mbox)); - /* - * issue command to find out what channels are raid/scsi - */ - mbox[0] = CHNL_CLASS; - mbox[2] = GET_CHNL_CLASS; + if( !scb->sgl ) { + printk(KERN_WARNING "RAID: Can't allocate sglist.\n"); + mega_free_sgl(adapter); + return -1; + } - memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); + scb->pthru = pci_alloc_consistent(adapter->dev, + sizeof(mega_passthru), + &scb->pthru_dma_addr); + + if( !scb->pthru ) { + printk(KERN_WARNING "RAID: Can't allocate passthru.\n"); + mega_free_sgl(adapter); + return -1; + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, - (2 * 1024L), PCI_DMA_FROMDEVICE); + scb->epthru = pci_alloc_consistent(adapter->dev, + sizeof(mega_ext_passthru), + &scb->epthru_dma_addr); + + if( !scb->epthru ) { + printk(KERN_WARNING + "Can't allocate extended passthru.\n"); + mega_free_sgl(adapter); + return -1; + } - mboxp->xferaddr = dma_handle; -#else - mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); -#endif - /* - * Non-ROMB firware fail this command, so all channels - * must be shown RAID - */ - megacfg->mega_ch_class = 0xFF; - if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { - megacfg->mega_ch_class = *((char *)megacfg->mega_buffer); - } + scb->dma_type = MEGA_DMA_TYPE_NONE; - for( i = 0; i < megacfg->productInfo.SCSIChanPresent; i++ ) { - if( (megacfg->mega_ch_class >> i) & 0x01 ) - printk(KERN_NOTICE"megaraid: channel[%d] is raid.\n", i+1); - else - printk(KERN_NOTICE"megaraid: channel[%d] is scsi.\n", i+1); + /* + * Link to free list + * lock not required since we are loading the driver, so no + * commands possible right now. + */ + scb->state = SCB_FREE; + scb->cmd = NULL; + list_add(&scb->list, &adapter->free_list); } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pci_unmap_single(megacfg->dev, dma_handle, - (2 * 1024L), PCI_DMA_FROMDEVICE); -#endif - + return 0; } -/* - * get the boot logical drive number if enabled +/** + * megadev_open() + * @inode - unused + * @filep - unused + * + * Routines for the character/ioctl interface to the driver. Find out if this + * is a valid open. If yes, increment the module use count so that it cannot + * be unloaded. */ -void -mega_get_boot_drv(mega_host_config *megacfg) +static int +megadev_open (struct inode *inode, struct file *filep) { - mega_mailbox *mboxp; - unsigned char mbox[16]; - struct private_bios_data *prv_bios_data; - u16 cksum = 0; - u8 *cksum_p; - u8 boot_pdrv; - int i; + /* + * Only allow superuser to access private ioctl interface + */ + if( !capable(CAP_SYS_ADMIN) ) return -EACCES; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t dma_handle; -#endif + MOD_INC_USE_COUNT; + return 0; +} - mboxp = (mega_mailbox *)mbox; - memset(mbox, 0, sizeof(mbox)); +/** + * megadev_ioctl() + * @inode - Our device inode + * @filep - unused + * @cmd - ioctl command + * @arg - user buffer + * + * ioctl entry point for our private ioctl interface. We move the data in from + * the user space, prepare the command (if necessary, convert the old MIMD + * ioctl to new ioctl command), and issue a synchronous command to the + * controller. + */ +static int +megadev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd, + unsigned long arg) +{ + adapter_t *adapter; + nitioctl_t uioc; + int adapno; + int rval; + mega_passthru *upthru; /* user address for passthru */ + mega_passthru *pthru; /* copy user passthru here */ + dma_addr_t pthru_dma_hndl; + void *data = NULL; /* data to be transferred */ + dma_addr_t data_dma_hndl; /* dma handle for data xfer area */ + megacmd_t mc; + megastat_t *ustats; + int num_ldrv; + u32 uxferaddr = 0; + struct pci_dev *pdev; - mbox[0] = BIOS_PVT_DATA; - mbox[2] = GET_BIOS_PVT_DATA; + ustats = NULL; /* avoid compilation warnings */ + num_ldrv = 0; - memset((void *)megacfg->mega_buffer, 0, sizeof(megacfg->mega_buffer)); + /* + * Make sure only USCSICMD are issued through this interface. + * MIMD application would still fire different command. + */ + if( (_IOC_TYPE(cmd) != MEGAIOC_MAGIC) && (cmd != USCSICMD) ) { + return -EINVAL; + } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_handle = pci_map_single(megacfg->dev, (void *)megacfg->mega_buffer, - (2 * 1024L), PCI_DMA_FROMDEVICE); + /* + * Check and convert a possible MIMD command to NIT command. + * mega_m_to_n() copies the data from the user space, so we do not + * have to do it here. + * NOTE: We will need some user address to copyout the data, therefore + * the inteface layer will also provide us with the required user + * addresses. + */ + memset(&uioc, 0, sizeof(nitioctl_t)); + if( (rval = mega_m_to_n( (void *)arg, &uioc)) != 0 ) + return rval; - mboxp->xferaddr = dma_handle; -#else - mboxp->xferaddr = virt_to_bus((void *)megacfg->mega_buffer); -#endif - megacfg->boot_ldrv_enabled = 0; - megacfg->boot_ldrv = 0; + switch( uioc.opcode ) { - megacfg->boot_pdrv_enabled = 0; - megacfg->boot_pdrv_ch = 0; - megacfg->boot_pdrv_tgt = 0; + case GET_DRIVER_VER: + if( put_user(driver_ver, (u32 *)uioc.uioc_uaddr) ) + return (-EFAULT); - if( megaIssueCmd(megacfg, mbox, NULL, 0) == 0 ) { - prv_bios_data = (struct private_bios_data *)megacfg->mega_buffer; + break; - cksum = 0; - cksum_p = (u8 *)prv_bios_data; - for( i = 0; i < 14; i++ ) { - cksum += *cksum_p++; - } + case GET_N_ADAP: + if( put_user(hba_count, (u32 *)uioc.uioc_uaddr) ) + return (-EFAULT); - if( prv_bios_data->cksum == (u16)(0-cksum) ) { + /* + * Shucks. MIMD interface returns a positive value for number + * of adapters. TODO: Change it to return 0 when there is no + * applicatio using mimd interface. + */ + return hba_count; - /* - * If MSB is set, a physical drive is set as boot device - */ - if( prv_bios_data->boot_drv & 0x80 ) { - megacfg->boot_pdrv_enabled = 1; - boot_pdrv = prv_bios_data->boot_drv & 0x7F; - megacfg->boot_pdrv_ch = boot_pdrv / 16; - megacfg->boot_pdrv_tgt = boot_pdrv % 16; - } - else { - megacfg->boot_ldrv_enabled = 1; - megacfg->boot_ldrv = prv_bios_data->boot_drv; - } - } - } + case GET_ADAP_INFO: -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pci_unmap_single(megacfg->dev, dma_handle, - (2 * 1024L), PCI_DMA_FROMDEVICE); -#endif + /* + * Which adapter + */ + if( (adapno = GETADAP(uioc.adapno)) >= hba_count ) + return (-ENODEV); -} + if( copy_to_user(uioc.uioc_uaddr, mcontroller+adapno, + sizeof(struct mcontroller)) ) + return (-EFAULT); + break; +#if MEGA_HAVE_STATS -static void mega_reorder_hosts (void) -{ - struct Scsi_Host *shpnt; - struct Scsi_Host *shone; - struct Scsi_Host *shtwo; - mega_host_config *boot_host; - int i; + case GET_STATS: + /* + * Which adapter + */ + if( (adapno = GETADAP(uioc.adapno)) >= hba_count ) + return (-ENODEV); - /* - * Find the (first) host which has it's BIOS enabled - */ - boot_host = NULL; - for (i = 0; i < MAX_CONTROLLERS; i++) { - if (mega_hbas[i].is_bios_enabled) { - boot_host = mega_hbas[i].hostdata_addr; - break; - } - } + adapter = hba_soft_state[adapno]; - if (boot_host == NULL) { - printk (KERN_WARNING "megaraid: no BIOS enabled.\n"); - return; - } + ustats = (megastat_t *)uioc.uioc_uaddr; - /* - * Traverse through the list of SCSI hosts for our HBA locations - */ - shone = shtwo = NULL; - for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { - /* Is it one of ours? */ - for (i = 0; i < MAX_CONTROLLERS; i++) { - if ((mega_host_config *) shpnt->hostdata == - mega_hbas[i].hostdata_addr) { - /* Does this one has BIOS enabled */ - if (mega_hbas[i].hostdata_addr == boot_host) { + if( copy_from_user(&num_ldrv, &ustats->num_ldrv, sizeof(int)) ) + return (-EFAULT); - /* Are we first */ - if (shtwo == NULL) /* Yes! */ - return; - else { /* :-( */ - shone = shpnt; - } - } else { - if (!shtwo) { - /* were we here before? xchng first */ - shtwo = shpnt; - } - } - break; - } - } /* - * Have we got the boot host and one which does not have the bios - * enabled. + * Check for the validity of the logical drive number */ - if (shone && shtwo) - break; - } - if (shone && shtwo) { - mega_swap_hosts (shone, shtwo); - } - - return; -} - -static void mega_swap_hosts (struct Scsi_Host *shone, struct Scsi_Host *shtwo) -{ - struct Scsi_Host *prevtoshtwo; - struct Scsi_Host *prevtoshone; - struct Scsi_Host *save = NULL;; - - /* Are these two nodes adjacent */ - if (shtwo->next == shone) { + if( num_ldrv >= MAX_LOGICAL_DRIVES_40LD ) return -EINVAL; - if (shtwo == scsi_hostlist && shone->next == NULL) { + if( copy_to_user(ustats->nreads, adapter->nreads, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->nreadblocks, adapter->nreadblocks, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->nwrites, adapter->nwrites, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->nwriteblocks, adapter->nwriteblocks, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->rd_errors, adapter->rd_errors, + num_ldrv*sizeof(u32)) ) + return -EFAULT; + + if( copy_to_user(ustats->wr_errors, adapter->wr_errors, + num_ldrv*sizeof(u32)) ) + return -EFAULT; - /* just two nodes */ - scsi_hostlist = shone; - shone->next = shtwo; - shtwo->next = NULL; - } else if (shtwo == scsi_hostlist) { - /* first two nodes of the list */ + return 0; - scsi_hostlist = shone; - shtwo->next = shone->next; - scsi_hostlist->next = shtwo; - } else if (shone->next == NULL) { - /* last two nodes of the list */ +#endif + case MBOX_CMD: - prevtoshtwo = scsi_hostlist; + /* + * Which adapter + */ + if( (adapno = GETADAP(uioc.adapno)) >= hba_count ) + return (-ENODEV); - while (prevtoshtwo->next != shtwo) - prevtoshtwo = prevtoshtwo->next; + adapter = hba_soft_state[adapno]; - prevtoshtwo->next = shone; - shone->next = shtwo; - shtwo->next = NULL; - } else { - prevtoshtwo = scsi_hostlist; + /* + * Deletion of logical drive is a special case. The adapter + * should be quiescent before this command is issued. + */ + if( uioc.uioc_rmbox[0] == FC_DEL_LOGDRV && + uioc.uioc_rmbox[2] == OP_DEL_LOGDRV ) { - while (prevtoshtwo->next != shtwo) - prevtoshtwo = prevtoshtwo->next; + /* + * Do we support this feature + */ + if( !adapter->support_random_del ) { + printk(KERN_WARNING "megaraid: logdrv "); + printk("delete on non-supporting F/W.\n"); - prevtoshtwo->next = shone; - shtwo->next = shone->next; - shone->next = shtwo; - } + return (-EINVAL); + } - } else if (shtwo == scsi_hostlist && shone->next == NULL) { - /* shtwo at head, shone at tail, not adjacent */ + rval = mega_del_logdrv( adapter, uioc.uioc_rmbox[3] ); - prevtoshone = scsi_hostlist; + if( rval == 0 ) { + memset(&mc, 0, sizeof(megacmd_t)); - while (prevtoshone->next != shone) - prevtoshone = prevtoshone->next; + mc.status = rval; - scsi_hostlist = shone; - shone->next = shtwo->next; - prevtoshone->next = shtwo; - shtwo->next = NULL; - } else if (shtwo == scsi_hostlist && shone->next != NULL) { - /* shtwo at head, shone is not at tail */ + rval = mega_n_to_m((void *)arg, &mc); + } - prevtoshone = scsi_hostlist; - while (prevtoshone->next != shone) - prevtoshone = prevtoshone->next; + return rval; + } + /* + * This interface only support the regular passthru commands. + * Reject extended passthru and 64-bit passthru + */ + if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU64 || + uioc.uioc_rmbox[0] == MEGA_MBOXCMD_EXTPTHRU ) { - scsi_hostlist = shone; - prevtoshone->next = shtwo; - save = shtwo->next; - shtwo->next = shone->next; - shone->next = save; - } else if (shone->next == NULL) { - /* shtwo not at head, shone at tail */ + printk(KERN_WARNING "megaraid: rejected passthru.\n"); - prevtoshtwo = scsi_hostlist; - prevtoshone = scsi_hostlist; + return (-EINVAL); + } - while (prevtoshtwo->next != shtwo) - prevtoshtwo = prevtoshtwo->next; - while (prevtoshone->next != shone) - prevtoshone = prevtoshone->next; + /* + * For all internal commands, the buffer must be allocated in + * <4GB address range + */ + pdev = adapter->ipdev; - prevtoshtwo->next = shone; - shone->next = shtwo->next; - prevtoshone->next = shtwo; - shtwo->next = NULL; + /* Is it a passthru command or a DCMD */ + if( uioc.uioc_rmbox[0] == MEGA_MBOXCMD_PASSTHRU ) { + /* Passthru commands */ + + pthru = pci_alloc_consistent(pdev, + sizeof(mega_passthru), + &pthru_dma_hndl); - } else { - prevtoshtwo = scsi_hostlist; - prevtoshone = scsi_hostlist; - save = NULL;; + if( pthru == NULL ) { + return (-ENOMEM); + } - while (prevtoshtwo->next != shtwo) - prevtoshtwo = prevtoshtwo->next; - while (prevtoshone->next != shone) - prevtoshone = prevtoshone->next; + /* + * The user passthru structure + */ + upthru = (mega_passthru *)MBOX(uioc)->xferaddr; - prevtoshtwo->next = shone; - save = shone->next; - shone->next = shtwo->next; - prevtoshone->next = shtwo; - shtwo->next = save; - } - return; -} + /* + * Copy in the user passthru here. + */ + if( copy_from_user(pthru, (char *)upthru, + sizeof(mega_passthru)) ) { -static inline void mega_freeSgList (mega_host_config * megaCfg) -{ - int i; + pci_free_consistent(pdev, + sizeof(mega_passthru), pthru, + pthru_dma_hndl); - for (i = 0; i < megaCfg->max_cmds; i++) { - if (megaCfg->scbList[i].sgList) - pci_free_consistent (megaCfg->dev, - sizeof (mega_64sglist) * - MAX_SGLIST, - megaCfg->scbList[i].sgList, - megaCfg->scbList[i]. - dma_sghandle64); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) /* 0x020400 */ - kfree (megaCfg->scbList[i].sgList); /* free sgList */ -#endif - } -} + return (-EFAULT); + } -/*---------------------------------------------- - * Get information about the card/driver - *----------------------------------------------*/ -const char *megaraid_info (struct Scsi_Host *pSHost) -{ - static char buffer[512]; - mega_host_config *megaCfg; + /* + * Is there a data transfer + */ + if( pthru->dataxferlen ) { + data = pci_alloc_consistent(pdev, + pthru->dataxferlen, + &data_dma_hndl); + + if( data == NULL ) { + pci_free_consistent(pdev, + sizeof(mega_passthru), + pthru, + pthru_dma_hndl); - megaCfg = (mega_host_config *) pSHost->hostdata; + return (-ENOMEM); + } - sprintf (buffer, - "LSI Logic MegaRAID %s %d commands %d targs %d chans %d luns", - megaCfg->fwVer, megaCfg->productInfo.MaxConcCmds, - megaCfg->host->max_id-1, megaCfg->host->max_channel, - megaCfg->host->max_lun); - return buffer; -} + /* + * Save the user address and point the kernel + * address at just allocated memory + */ + uxferaddr = pthru->dataxferaddr; + pthru->dataxferaddr = data_dma_hndl; + } -/*----------------------------------------------------------------- - * Perform a SCSI command - * Mailbox area: - * 00 01 command - * 01 01 command id - * 02 02 # of sectors - * 04 04 logical bus address - * 08 04 physical buffer address - * 0C 01 logical drive # - * 0D 01 length of scatter/gather list - * 0E 01 reserved - * 0F 01 mailbox busy - * 10 01 numstatus byte - * 11 01 status byte - *-----------------------------------------------------------------*/ -int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) -{ - DRIVER_LOCK_T mega_host_config * megaCfg; - mega_scb *pScb; - char *user_area = NULL; - megaCfg = (mega_host_config *) SCpnt->host->hostdata; - DRIVER_LOCK (megaCfg); + /* + * Is data coming down-stream + */ + if( pthru->dataxferlen && (uioc.flags & UIOC_WR) ) { + /* + * Get the user data + */ + if( copy_from_user(data, (char *)uxferaddr, + pthru->dataxferlen) ) { + rval = (-EFAULT); + goto freemem_and_return; + } + } -#if 0 - if (!(megaCfg->flag & (1L << SCpnt->channel))) { - if (SCpnt->channel < megaCfg->productInfo.SCSIChanPresent) - printk ( KERN_NOTICE - "scsi%d: scanning channel %d for devices.\n", - megaCfg->host->host_no, SCpnt->channel); - else - printk ( KERN_NOTICE - "scsi%d: scanning virtual channel %d for logical drives.\n", - megaCfg->host->host_no, - SCpnt->channel-megaCfg->productInfo.SCSIChanPresent+1); + memset(&mc, 0, sizeof(megacmd_t)); - megaCfg->flag |= (1L << SCpnt->channel); - } -#endif + mc.cmd = MEGA_MBOXCMD_PASSTHRU; + mc.xferaddr = (u32)pthru_dma_hndl; - SCpnt->scsi_done = pktComp; + /* + * Issue the command + */ + mega_internal_command(adapter, LOCK_INT, &mc, pthru); - if (mega_driver_ioctl (megaCfg, SCpnt)) - return 0; + rval = mega_n_to_m((void *)arg, &mc); - /* If driver in abort or reset.. cancel this command */ - if (megaCfg->flag & IN_ABORT) { - SCpnt->result = (DID_ABORT << 16); - /* Add Scsi_Command to end of completed queue */ - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; - megaCfg->qCcnt++; + if( rval ) goto freemem_and_return; - DRIVER_UNLOCK (megaCfg); - return 0; - } else if (megaCfg->flag & IN_RESET) { - SCpnt->result = (DID_RESET << 16); - /* Add Scsi_Command to end of completed queue */ - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; - } - megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL; - megaCfg->qCcnt++; - DRIVER_UNLOCK (megaCfg); - return 0; - } + /* + * Is data going up-stream + */ + if( pthru->dataxferlen && (uioc.flags & UIOC_RD) ) { + if( copy_to_user((char *)uxferaddr, data, + pthru->dataxferlen) ) { + rval = (-EFAULT); + } + } + + /* + * Send the request sense data also, irrespective of + * whether the user has asked for it or not. + */ + copy_to_user(upthru->reqsensearea, + pthru->reqsensearea, 14); - megaCfg->flag |= IN_QUEUE; - /* Allocate and build a SCB request */ - if ((pScb = mega_build_cmd (megaCfg, SCpnt)) != NULL) { - - /* - * Check if the HBA is in quiescent state, e.g., during a delete - * logical drive opertion. If it is, queue the commands in the - * internal queue until the delete operation is complete. - */ - if( ! megaCfg->quiescent ) { - /* Add SCB to the head of the pending queue */ - if (megaCfg->qPendingH == NULL) { - megaCfg->qPendingH = megaCfg->qPendingT = pScb; - } else { - megaCfg->qPendingT->next = pScb; - megaCfg->qPendingT = pScb; - } - megaCfg->qPendingT->next = NULL; - megaCfg->qPcnt++; - - if (mega_runpendq (megaCfg) == -1) { - DRIVER_UNLOCK (megaCfg); - return 0; +freemem_and_return: + if( pthru->dataxferlen ) { + pci_free_consistent(pdev, + pthru->dataxferlen, data, + data_dma_hndl); } - } - else { - /* Add SCB to the internal queue */ - if (megaCfg->int_qh == NULL) { - megaCfg->int_qh = megaCfg->int_qt = pScb; - } else { - megaCfg->int_qt->next = pScb; - megaCfg->int_qt = pScb; - } - megaCfg->int_qt->next = NULL; - megaCfg->int_qlen++; - } - - if (pScb->SCpnt->cmnd[0] == M_RD_IOCTL_CMD_NEW) { - init_MUTEX_LOCKED (&pScb->ioctl_sem); - spin_unlock_irq (&io_request_lock); - down (&pScb->ioctl_sem); - user_area = (char *)*((u32*)&pScb->SCpnt->cmnd[4]); - if (copy_to_user - (user_area, pScb->buff_ptr, pScb->iDataSize)) { - printk - ("megaraid: Error copying ioctl return value to user buffer.\n"); - pScb->SCpnt->result = (DID_ERROR << 16); - } - spin_lock_irq (&io_request_lock); - DRIVER_LOCK (megaCfg); - kfree (pScb->buff_ptr); - pScb->buff_ptr = NULL; - mega_cmd_done (megaCfg, pScb, pScb->SCpnt->result); - mega_rundoneq (megaCfg); - mega_runpendq (megaCfg); - DRIVER_UNLOCK (megaCfg); - } - megaCfg->flag &= ~IN_QUEUE; + pci_free_consistent(pdev, sizeof(mega_passthru), + pthru, pthru_dma_hndl); - } + return rval; + } + else { + /* DCMD commands */ - DRIVER_UNLOCK (megaCfg); - return 0; -} + /* + * Is there a data transfer + */ + if( uioc.xferlen ) { + data = pci_alloc_consistent(pdev, + uioc.xferlen, &data_dma_hndl); -/*---------------------------------------------------------------------- - * Issue a blocking command to the controller - *----------------------------------------------------------------------*/ -volatile static int internal_done_flag = 0; -volatile static int internal_done_errcode = 0; - -static DECLARE_WAIT_QUEUE_HEAD (internal_wait); - -static void internal_done (Scsi_Cmnd * SCpnt) -{ - internal_done_errcode = SCpnt->result; - internal_done_flag++; - wake_up (&internal_wait); -} + if( data == NULL ) { + return (-ENOMEM); + } -/* shouldn't be used, but included for completeness */ + uxferaddr = MBOX(uioc)->xferaddr; + } -int megaraid_command (Scsi_Cmnd * SCpnt) -{ - internal_done_flag = 0; + /* + * Is data coming down-stream + */ + if( uioc.xferlen && (uioc.flags & UIOC_WR) ) { + /* + * Get the user data + */ + if( copy_from_user(data, (char *)uxferaddr, + uioc.xferlen) ) { - /* Queue command, and wait until it has completed */ - megaraid_queue (SCpnt, internal_done); + pci_free_consistent(pdev, + uioc.xferlen, + data, data_dma_hndl); - while (!internal_done_flag) { - interruptible_sleep_on (&internal_wait); - } + return (-EFAULT); + } + } - return internal_done_errcode; -} + memcpy(&mc, MBOX(uioc), sizeof(megacmd_t)); -/*--------------------------------------------------------------------- - * Abort a previous SCSI request - *---------------------------------------------------------------------*/ -int megaraid_abort (Scsi_Cmnd * SCpnt) -{ - mega_host_config *megaCfg; - int rc; /*, idx; */ - mega_scb *pScb; + mc.xferaddr = (u32)data_dma_hndl; - rc = SCSI_ABORT_NOT_RUNNING; + /* + * Issue the command + */ + mega_internal_command(adapter, LOCK_INT, &mc, NULL); - megaCfg = (mega_host_config *) SCpnt->host->hostdata; + rval = mega_n_to_m((void *)arg, &mc); - megaCfg->flag |= IN_ABORT; + if( rval ) { + if( uioc.xferlen ) { + pci_free_consistent(pdev, + uioc.xferlen, data, + data_dma_hndl); + } - for (pScb = megaCfg->qPendingH; pScb; pScb = pScb->next) { - if (pScb->SCpnt == SCpnt) { - /* Found an aborting command */ -#if DEBUG - showMbox (pScb); -#endif + return rval; + } - /* - * If the command is queued to be issued to the firmware, abort the scsi cmd, - * If the command is already aborted in a previous call to the _abort entry - * point, return SCSI_ABORT_SNOOZE, suggesting a reset. - * If the command is issued to the firmware, which might complete after - * some time, we will mark the scb as aborted, and return to the mid layer, - * that abort could not be done. - * In the ISR, when this command actually completes, we will perform a normal - * completion. - * - * Oct 27, 1999 - */ + /* + * Is data going up-stream + */ + if( uioc.xferlen && (uioc.flags & UIOC_RD) ) { + if( copy_to_user((char *)uxferaddr, data, + uioc.xferlen) ) { - switch (pScb->state) { - case SCB_ABORTED: /* Already aborted */ - rc = SCSI_ABORT_SNOOZE; - break; - case SCB_ISSUED: /* Waiting on ISR result */ - rc = SCSI_ABORT_NOT_RUNNING; - pScb->state = SCB_ABORTED; - break; - case SCB_ACTIVE: /* still on the pending queue */ - mega_freeSCB (megaCfg, pScb); - SCpnt->result = (DID_ABORT << 16); - if (megaCfg->qCompletedH == NULL) { - megaCfg->qCompletedH = - megaCfg->qCompletedT = SCpnt; - } else { - megaCfg->qCompletedT->host_scribble = - (unsigned char *) SCpnt; - megaCfg->qCompletedT = SCpnt; + rval = (-EFAULT); } - megaCfg->qCompletedT->host_scribble = - (unsigned char *) NULL; - megaCfg->qCcnt++; - rc = SCSI_ABORT_SUCCESS; - break; - default: - printk - ("megaraid_abort: unknown command state!!\n"); - rc = SCSI_ABORT_NOT_RUNNING; - break; } - break; - } - } - megaCfg->flag &= ~IN_ABORT; - -#if DEBUG - if (megaCfg->flag & IN_QUEUE) - printk ("ma:flag is in queue\n"); - if (megaCfg->qCompletedH == NULL) - printk ("ma:qchead == null\n"); -#endif + if( uioc.xferlen ) { + pci_free_consistent(pdev, + uioc.xferlen, data, + data_dma_hndl); + } - /* - * This is required here to complete any completed requests to be communicated - * over to the mid layer. - * Calling just mega_rundoneq() did not work. - */ - if (megaCfg->qCompletedH) { - SCpnt = megaCfg->qCompletedH; - megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble; - megaCfg->qCcnt--; + return rval; + } - SCpnt->host_scribble = (unsigned char *) NULL; - /* Callback */ - callDone (SCpnt); + default: + return (-EINVAL); } - mega_rundoneq (megaCfg); - return rc; + return 0; } -/*--------------------------------------------------------------------- - * Reset a previous SCSI request - *---------------------------------------------------------------------*/ - -int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags) +/** + * mega_m_to_n() + * @arg - user address + * @uioc - new ioctl structure + * + * A thin layer to convert older mimd interface ioctl structure to NIT ioctl + * structure + * + * Converts the older mimd ioctl structure to newer NIT structure + */ +static int +mega_m_to_n(void *arg, nitioctl_t *uioc) { - mega_host_config *megaCfg; - int idx; - int rc; - mega_scb *pScb; + struct uioctl_t uioc_mimd; + char signature[8] = {0}; + u8 opcode; + u8 subopcode; - rc = SCSI_RESET_NOT_RUNNING; - megaCfg = (mega_host_config *) SCpnt->host->hostdata; - megaCfg->flag |= IN_RESET; + /* + * check is the application conforms to NIT. We do not have to do much + * in that case. + * We exploit the fact that the signature is stored in the very + * begining of the structure. + */ - printk - ("megaraid_RESET: %.08lx cmd=%.02x , flag = %x\n", - SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, - SCpnt->target, SCpnt->lun, rstflags); + if( copy_from_user(signature, (char *)arg, 7) ) + return (-EFAULT); - TRACE (("RESET: %.08lx %.02x <%d.%d.%d>\n", - SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->channel, - SCpnt->target, SCpnt->lun)); + if( memcmp(signature, "MEGANIT", 7) == 0 ) { - /* - * Walk list of SCBs for any that are still outstanding - */ - for (idx = 0; idx < megaCfg->max_cmds; idx++) { - if (megaCfg->scbList[idx].state != SCB_FREE) { - SCpnt = megaCfg->scbList[idx].SCpnt; - pScb = &megaCfg->scbList[idx]; - if (SCpnt != NULL) { - pScb->state = SCB_RESET; - break; - } - } + /* + * NOTE NOTE: The nit ioctl is still under flux because of + * change of mailbox definition, in HPE. No applications yet + * use this interface and let's not have applications use this + * interface till the new specifitions are in place. + */ + return -EINVAL; +#if 0 + if( copy_from_user(uioc, (char *)arg, sizeof(nitioctl_t)) ) + return (-EFAULT); + return 0; +#endif } - megaCfg->flag &= ~IN_RESET; + /* + * Else assume we have mimd uioctl_t as arg. Convert to nitioctl_t + * + * Get the user ioctl structure + */ + if( copy_from_user(&uioc_mimd, (char *)arg, sizeof(struct uioctl_t)) ) + return (-EFAULT); - mega_rundoneq (megaCfg); - return rc; -} -#ifdef CONFIG_PROC_FS -/* Following code handles /proc fs */ -static int proc_printf (mega_host_config * megaCfg, const char *fmt, ...) -{ - va_list args; - int i; + /* + * Get the opcode and subopcode for the commands + */ + opcode = uioc_mimd.ui.fcs.opcode; + subopcode = uioc_mimd.ui.fcs.subopcode; - if (megaCfg->procidx > PROCBUFSIZE) - return 0; + switch (opcode) { + case 0x82: - va_start (args, fmt); - i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args); - va_end (args); + switch (subopcode) { - megaCfg->procidx += i; - return i; -} + case MEGAIOC_QDRVRVER: /* Query driver version */ + uioc->opcode = GET_DRIVER_VER; + uioc->uioc_uaddr = uioc_mimd.data; + break; -static int proc_read_config (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ + case MEGAIOC_QNADAP: /* Get # of adapters */ + uioc->opcode = GET_N_ADAP; + uioc->uioc_uaddr = uioc_mimd.data; + break; - mega_host_config *megaCfg = (mega_host_config *) data; + case MEGAIOC_QADAPINFO: /* Get adapter information */ + uioc->opcode = GET_ADAP_INFO; + uioc->adapno = uioc_mimd.ui.fcs.adapno; + uioc->uioc_uaddr = uioc_mimd.data; + break; - *start = page; + default: + return(-EINVAL); + } - if (megaCfg->productInfo.ProductName[0] != 0) - proc_printf (megaCfg, "%s\n", megaCfg->productInfo.ProductName); + break; - proc_printf (megaCfg, "Controller Type: "); - if (megaCfg->flag & BOARD_QUARTZ) - proc_printf (megaCfg, "438/466/467/471/493\n"); - else - proc_printf (megaCfg, "418/428/434\n"); + case 0x81: - if (megaCfg->flag & BOARD_40LD) - proc_printf (megaCfg, - "Controller Supports 40 Logical Drives\n"); + uioc->opcode = MBOX_CMD; + uioc->adapno = uioc_mimd.ui.fcs.adapno; - if (megaCfg->flag & BOARD_64BIT) - proc_printf (megaCfg, - "Controller / Driver uses 64 bit memory addressing\n"); + memcpy(uioc->uioc_rmbox, uioc_mimd.mbox, 18); - proc_printf (megaCfg, "Base = %08x, Irq = %d, ", megaCfg->base, - megaCfg->host->irq); + uioc->xferlen = uioc_mimd.ui.fcs.length; - proc_printf (megaCfg, "Logical Drives = %d, Channels = %d\n", - megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent); + if( uioc_mimd.outlen ) uioc->flags = UIOC_RD; + if( uioc_mimd.inlen ) uioc->flags |= UIOC_WR; - proc_printf (megaCfg, "Version =%s:%s, DRAM = %dMb\n", - megaCfg->fwVer, megaCfg->biosVer, - megaCfg->productInfo.DramSize); + break; - proc_printf (megaCfg, - "Controller Queue Depth = %d, Driver Queue Depth = %d\n", - megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds); - COPY_BACK; - return count; -} + case 0x80: -static int proc_read_stat (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ - int i; - mega_host_config *megaCfg = (mega_host_config *) data; + uioc->opcode = MBOX_CMD; + uioc->adapno = uioc_mimd.ui.fcs.adapno; - *start = page; + memcpy(uioc->uioc_rmbox, uioc_mimd.mbox, 18); - proc_printf (megaCfg, "Statistical Information for this controller\n"); - proc_printf (megaCfg, "Interrupts Collected = %lu\n", - megaCfg->nInterrupts); + /* + * Choose the xferlen bigger of input and output data + */ + uioc->xferlen = uioc_mimd.outlen > uioc_mimd.inlen ? + uioc_mimd.outlen : uioc_mimd.inlen; - for (i = 0; i < megaCfg->numldrv; i++) { - proc_printf (megaCfg, "Logical Drive %d:\n", i); + if( uioc_mimd.outlen ) uioc->flags = UIOC_RD; + if( uioc_mimd.inlen ) uioc->flags |= UIOC_WR; - proc_printf (megaCfg, - "\tReads Issued = %lu, Writes Issued = %lu\n", - megaCfg->nReads[i], megaCfg->nWrites[i]); + break; - proc_printf (megaCfg, - "\tSectors Read = %lu, Sectors Written = %lu\n\n", - megaCfg->nReadBlocks[i], megaCfg->nWriteBlocks[i]); + default: + return (-EINVAL); } - COPY_BACK; - return count; + return 0; } -static int proc_read_status (char *page, char **start, off_t offset, - int count, int *eof, void *data) +/* + * mega_n_to_m() + * @arg - user address + * @mc - mailbox command + * + * Updates the status information to the application, depending on application + * conforms to older mimd ioctl interface or newer NIT ioctl interface + */ +static int +mega_n_to_m(void *arg, megacmd_t *mc) { - mega_host_config *megaCfg = (mega_host_config *) data; - *start = page; + nitioctl_t *uiocp; + megacmd_t *umc; + mega_passthru *upthru; + struct uioctl_t *uioc_mimd; + char signature[8] = {0}; - proc_printf (megaCfg, "TBD\n"); - COPY_BACK; - return count; -} + /* + * check is the application conforms to NIT. + */ + if( copy_from_user(signature, (char *)arg, 7) ) + return -EFAULT; -static int proc_read_mbox (char *page, char **start, off_t offset, - int count, int *eof, void *data) -{ + if( memcmp(signature, "MEGANIT", 7) == 0 ) { - mega_host_config *megaCfg = (mega_host_config *) data; - volatile mega_mailbox *mbox = megaCfg->mbox; + uiocp = (nitioctl_t *)arg; - *start = page; + if( put_user(mc->status, (u8 *)&MBOX_P(uiocp)->status) ) + return (-EFAULT); - proc_printf (megaCfg, "Contents of Mail Box Structure\n"); - proc_printf (megaCfg, " Fw Command = 0x%02x\n", mbox->cmd); - proc_printf (megaCfg, " Cmd Sequence = 0x%02x\n", mbox->cmdid); - proc_printf (megaCfg, " No of Sectors= %04d\n", mbox->numsectors); - proc_printf (megaCfg, " LBA = 0x%02x\n", mbox->lba); - proc_printf (megaCfg, " DTA = 0x%08x\n", mbox->xferaddr); - proc_printf (megaCfg, " Logical Drive= 0x%02x\n", mbox->logdrv); - proc_printf (megaCfg, " No of SG Elmt= 0x%02x\n", mbox->numsgelements); - proc_printf (megaCfg, " Busy = %01x\n", mbox->busy); - proc_printf (megaCfg, " Status = 0x%02x\n", mbox->status); + if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) { - /* proc_printf(megaCfg, "Dump of MailBox\n"); - for (i = 0; i < 16; i++) - proc_printf(megaCfg, "%02x ",*(mbox + i)); + umc = MBOX_P(uiocp); - proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus); + upthru = (mega_passthru *)umc->xferaddr; - for (i = 0; i < 46; i++) { - proc_printf(megaCfg,"%02d ",*(mbox + 16 + i)); - if (i%16) - proc_printf(megaCfg,"\n"); + if( put_user(mc->status, (u8 *)&upthru->scsistatus) ) + return (-EFAULT); + } } + else { + uioc_mimd = (struct uioctl_t *)arg; - if (!mbox->numsgelements) { - dta = phys_to_virt(mbox->xferaddr); - for (i = 0; i < mbox->numsgelements; i++) - if (dta) { - proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i)); proc_printf(megaCfg,"Length = %08x\n", - (ulong)*(dta + i + 4)); - } - }*/ - COPY_BACK; - return count; -} - -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ -#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \ - S_IRUSR | S_IFREG,\ - controller_proc_dir_entry,\ - fxn, megaCfg) -#else -#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg) - -static struct proc_dir_entry * -create_proc_read_entry (const char *string, - int mode, - struct proc_dir_entry *parent, - read_proc_t * fxn, mega_host_config * megaCfg) -{ - struct proc_dir_entry *temp = NULL; - - temp = kmalloc (sizeof (struct proc_dir_entry), GFP_KERNEL); - if (!temp) - return NULL; - memset (temp, 0, sizeof (struct proc_dir_entry)); - - if ((temp->name = kmalloc (strlen (string) + 1, GFP_KERNEL)) == NULL) { - kfree (temp); - return NULL; - } - - strcpy ((char *) temp->name, string); - temp->namelen = strlen (string); - temp->mode = mode; /*S_IFREG | S_IRUSR */ ; - temp->data = (void *) megaCfg; - temp->read_proc = fxn; - proc_register (parent, temp); - return temp; -} -#endif + if( put_user(mc->status, (u8 *)&uioc_mimd->mbox[17]) ) + return (-EFAULT); -static void mega_create_proc_entry (int index, struct proc_dir_entry *parent) -{ - u_char string[64] = { 0 }; - mega_host_config *megaCfg = megaCtlrs[index]; - struct proc_dir_entry *controller_proc_dir_entry = NULL; + if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) { - sprintf (string, "%d", index); + umc = (megacmd_t *)uioc_mimd->mbox; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */ - controller_proc_dir_entry = - megaCfg->controller_proc_dir_entry = proc_mkdir (string, parent); -#else - controller_proc_dir_entry = - megaCfg->controller_proc_dir_entry = - create_proc_entry (string, S_IFDIR | S_IRUGO | S_IXUGO, parent); -#endif + upthru = (mega_passthru *)umc->xferaddr; - if (!controller_proc_dir_entry) - printk ("\nmegaraid: proc_mkdir failed\n"); - else { - megaCfg->proc_read = - CREATE_READ_PROC ("config", proc_read_config); - megaCfg->proc_status = - CREATE_READ_PROC ("status", proc_read_status); - megaCfg->proc_stat = CREATE_READ_PROC ("stat", proc_read_stat); - megaCfg->proc_mbox = - CREATE_READ_PROC ("mailbox", proc_read_mbox); + if( put_user(mc->status, (u8 *)&upthru->scsistatus) ) + return (-EFAULT); + } } + return 0; } -#endif /* CONFIG_PROC_FS */ -/*------------------------------------------------------------- - * Return the disk geometry for a particular disk - * Input: - * Disk *disk - Disk geometry - * kdev_t dev - Device node - * int *geom - Returns geometry fields - * geom[0] = heads - * geom[1] = sectors - * geom[2] = cylinders - *-------------------------------------------------------------*/ -int megaraid_biosparam (Disk * disk, kdev_t dev, int *geom) + +static int +megadev_close (struct inode *inode, struct file *filep) { - int heads, sectors, cylinders; - mega_host_config *megaCfg; + MOD_DEC_USE_COUNT; + return 0; +} - /* Get pointer to host config structure */ - megaCfg = (mega_host_config *) disk->device->host->hostdata; - if( IS_RAID_CH(megaCfg, disk->device->channel)) { - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); +/* + * MEGARAID 'FW' commands. + */ - /* Handle extended translation size for logical drives > 1Gb */ - if (disk->capacity >= 0x200000) { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (heads * sectors); - } +/** + * mega_is_bios_enabled() + * @adapter - pointer to our soft state + * + * issue command to find out if the BIOS is enabled for this controller + */ +static int +mega_is_bios_enabled(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; + int ret; - /* return result */ - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } - else { - if( mega_partsize(disk, dev, geom) == 0 ) return 0; + mbox = (mbox_t *)raw_mbox; - printk(KERN_WARNING - "megaraid: invalid partition on this disk on channel %d\n", - disk->device->channel); + memset(mbox, 0, sizeof(mbox)); - /* Default heads (64) & sectors (32) */ - heads = 64; - sectors = 32; - cylinders = disk->capacity / (heads * sectors); + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - /* Handle extended translation size for logical drives > 1Gb */ - if (disk->capacity >= 0x200000) { - heads = 255; - sectors = 63; - cylinders = disk->capacity / (heads * sectors); - } + mbox->xferaddr = (u32)adapter->buf_dma_handle; - /* return result */ - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - } + raw_mbox[0] = IS_BIOS_ENABLED; + raw_mbox[2] = GET_BIOS; - return 0; + + ret = issue_scb_block(adapter, raw_mbox); + + return *(char *)adapter->mega_buffer; } -/* - * Function : static int mega_partsize(Disk * disk, kdev_t dev, int *geom) - * - * Purpose : to determine the BIOS mapping used to create the partition - * table, storing the results (cyls, hds, and secs) in geom - * - * Note: Code is picked from scsicam.h + +/** + * mega_enum_raid_scsi() + * @adapter - pointer to our soft state * - * Returns : -1 on failure, 0 on success. + * Find out what channels are RAID/SCSI. This information is used to + * differentiate the virtual channels and physical channels and to support + * ROMB feature and non-disk devices. */ -static int -mega_partsize(Disk * disk, kdev_t dev, int *geom) +static void +mega_enum_raid_scsi(adapter_t *adapter) { - struct buffer_head *bh; - struct partition *p, *largest = NULL; - int i, largest_cyl; - int heads, cyls, sectors; - int capacity = disk->capacity; - - int ma = MAJOR(dev); - int mi = (MINOR(dev) & ~0xf); - - int block = 1024; - - if(blksize_size[ma]) - block = blksize_size[ma][mi]; - - if(!(bh = bread(MKDEV(ma,mi), 0, block))) - return -1; - - if( *(unsigned short *)(bh->b_data + 510) == 0xAA55 ) { + unsigned char raw_mbox[16]; + mbox_t *mbox; + int i; - for( largest_cyl = -1, p = (struct partition *)(0x1BE + bh->b_data), - i = 0; i < 4; ++i, ++p) { + mbox = (mbox_t *)raw_mbox; - if (!p->sys_ind) continue; + memset(mbox, 0, sizeof(mbox)); - cyls = p->end_cyl + ((p->end_sector & 0xc0) << 2); + /* + * issue command to find out what channels are raid/scsi + */ + raw_mbox[0] = CHNL_CLASS; + raw_mbox[2] = GET_CHNL_CLASS; - if(cyls >= largest_cyl) { - largest_cyl = cyls; - largest = p; - } - } - } + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - if (largest) { - heads = largest->end_head + 1; - sectors = largest->end_sector & 0x3f; + mbox->xferaddr = (u32)adapter->buf_dma_handle; - if (heads == 0 || sectors == 0) { - brelse(bh); - return -1; - } + /* + * Non-ROMB firware fail this command, so all channels + * must be shown RAID + */ + adapter->mega_ch_class = 0xFF; - cyls = capacity/(heads * sectors); + if(!issue_scb_block(adapter, raw_mbox)) { + adapter->mega_ch_class = *((char *)adapter->mega_buffer); - geom[0] = heads; - geom[1] = sectors; - geom[2] = cyls; + } - brelse(bh); - return 0; + for( i = 0; i < adapter->product_info.nchannels; i++ ) { + if( (adapter->mega_ch_class >> i) & 0x01 ) { + printk(KERN_INFO "megaraid: channel[%d] is raid.\n", + i); + } + else { + printk(KERN_INFO "megaraid: channel[%d] is scsi.\n", + i); + } } - brelse(bh); - return -1; + return; } -/* - * This routine will be called when the use has done a forced shutdown on the - * system. Flush the Adapter cache, that's the most we can do. +/** + * mega_get_boot_drv() + * @adapter - pointer to our soft state + * + * Find out which device is the boot device. Note, any logical drive or any + * phyical device (e.g., a CDROM) can be designated as a boot device. */ -static int megaraid_reboot_notify (struct notifier_block *this, unsigned long code, - void *unused) +static void +mega_get_boot_drv(adapter_t *adapter) { - struct Scsi_Host *pSHost; - mega_host_config *megaCfg; - mega_mailbox *mbox; - u_char mboxData[16]; - int i; - - if (code == SYS_DOWN || code == SYS_HALT) { - for (i = 0; i < numCtlrs; i++) { - pSHost = megaCtlrs[i]->host; - - megaCfg = (mega_host_config *) pSHost->hostdata; - mbox = (mega_mailbox *) mboxData; - - /* Flush cache to disk */ - memset (mbox, 0, 16); - mboxData[0] = 0xA; + struct private_bios_data *prv_bios_data; + unsigned char raw_mbox[16]; + mbox_t *mbox; + u16 cksum = 0; + u8 *cksum_p; + u8 boot_pdrv; + int i; - /* - * Free irq, otherwise extra interrupt is generated - */ - free_irq (megaCfg->host->irq, megaCfg); - - /* - * Issue a blocking (interrupts disabled) command to - * the card - */ - megaIssueCmd (megaCfg, mboxData, NULL, 0); - } - } - return NOTIFY_DONE; -} + mbox = (mbox_t *)raw_mbox; -static int mega_init_scb (mega_host_config * megacfg) -{ - int idx; + memset(mbox, 0, sizeof(raw_mbox)); -#if DEBUG - if (megacfg->max_cmds >= MAX_COMMANDS) { - printk ("megaraid:ctlr max cmds = %x : MAX_CMDS = %x", - megacfg->max_cmds, MAX_COMMANDS); - } -#endif + raw_mbox[0] = BIOS_PVT_DATA; + raw_mbox[2] = GET_BIOS_PVT_DATA; - for (idx = megacfg->max_cmds - 1; idx >= 0; idx--) { + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - megacfg->scbList[idx].idx = idx; + mbox->xferaddr = (u32)adapter->buf_dma_handle; - /* - * ISR will make this flag zero to indicate the command has been - * completed. This is only for user ioctl calls. Rest of the driver - * and the mid-layer operations are not connected with this flag. - */ + adapter->boot_ldrv_enabled = 0; + adapter->boot_ldrv = 0; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megacfg->scbList[idx].sgList = - pci_alloc_consistent (megacfg->dev, - sizeof (mega_64sglist) * MAX_SGLIST, - &(megacfg->scbList[idx]. - dma_sghandle64)); + adapter->boot_pdrv_enabled = 0; + adapter->boot_pdrv_ch = 0; + adapter->boot_pdrv_tgt = 0; - megacfg->scbList[idx].sg64List = - (mega_64sglist *) megacfg->scbList[idx].sgList; -#else - megacfg->scbList[idx].sgList = kmalloc (sizeof (mega_sglist) * MAX_SGLIST, GFP_ATOMIC | GFP_DMA); -#endif + if(issue_scb_block(adapter, raw_mbox) == 0) { + prv_bios_data = + (struct private_bios_data *)adapter->mega_buffer; - if (megacfg->scbList[idx].sgList == NULL) { - printk (KERN_WARNING - "Can't allocate sglist for id %d\n", idx); - mega_freeSgList (megacfg); - return -1; + cksum = 0; + cksum_p = (char *)prv_bios_data; + for (i = 0; i < 14; i++ ) { + cksum += (u16)(*cksum_p++); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - megacfg->scbList[idx].pthru = pci_alloc_consistent (megacfg->dev, - sizeof (mega_passthru), - &(megacfg->scbList[idx]. - dma_passthruhandle64)); - - if (megacfg->scbList[idx].pthru == NULL) { - printk (KERN_WARNING - "Can't allocate passthru for id %d\n", idx); - } - - megacfg->scbList[idx].epthru = - pci_alloc_consistent( - megacfg->dev, sizeof(mega_ext_passthru), - &(megacfg->scbList[idx].dma_ext_passthruhandle64) - ); - - if (megacfg->scbList[idx].epthru == NULL) { - printk (KERN_WARNING - "Can't allocate extended passthru for id %d\n", idx); - } - /* - * Allocate a 256 Byte Bounce Buffer for handling INQ/RD_CAPA - */ - megacfg->scbList[idx].bounce_buffer = pci_alloc_consistent (megacfg->dev, - 256, - &(megacfg->scbList[idx]. - dma_bounce_buffer)); - - if (!megacfg->scbList[idx].bounce_buffer) - printk - ("megaraid: allocation for bounce buffer failed\n"); - megacfg->scbList[idx].dma_type = M_RD_DMA_TYPE_NONE; -#endif + if (prv_bios_data->cksum == (u16)(0-cksum) ) { - if (idx < MAX_COMMANDS) { /* - * Link to free list - * lock not required since we are loading the driver, so no - * commands possible right now. + * If MSB is set, a physical drive is set as boot + * device */ - enq_scb_freelist (megacfg, &megacfg->scbList[idx], - NO_LOCK, INTR_ENB); - + if( prv_bios_data->boot_drv & 0x80 ) { + adapter->boot_pdrv_enabled = 1; + boot_pdrv = prv_bios_data->boot_drv & 0x7F; + adapter->boot_pdrv_ch = boot_pdrv / 16; + adapter->boot_pdrv_tgt = boot_pdrv % 16; + } + else { + adapter->boot_ldrv_enabled = 1; + adapter->boot_ldrv = prv_bios_data->boot_drv; + } } } - return 0; } -/* - * Enqueues a SCB +/** + * mega_support_random_del() + * @adapter - pointer to our soft state + * + * Find out if this controller supports random deletion and addition of + * logical drives */ -static void enq_scb_freelist (mega_host_config * megacfg, mega_scb * scb, int lock, - int intr) +static int +mega_support_random_del(adapter_t *adapter) { + unsigned char raw_mbox[16]; + mbox_t *mbox; + int rval; - if (lock == INTERNAL_LOCK || intr == INTR_DIS) { - if (intr == INTR_DIS) - spin_lock_irq (&megacfg->lock_free); - else - spin_lock (&megacfg->lock_free); - } + mbox = (mbox_t *)raw_mbox; - scb->state = SCB_FREE; - scb->SCpnt = NULL; + memset(mbox, 0, sizeof(mbox)); - if (megacfg->qFreeH == (mega_scb *) NULL) { - megacfg->qFreeH = megacfg->qFreeT = scb; - } else { - megacfg->qFreeT->next = scb; - megacfg->qFreeT = scb; - } + /* + * issue command + */ + raw_mbox[0] = FC_DEL_LOGDRV; + raw_mbox[2] = OP_SUP_DEL_LOGDRV; - megacfg->qFreeT->next = NULL; - megacfg->qFcnt++; + rval = issue_scb_block(adapter, raw_mbox); - if (lock == INTERNAL_LOCK || intr == INTR_DIS) { - if (intr == INTR_DIS) - spin_unlock_irq (&megacfg->lock_free); - else - spin_unlock (&megacfg->lock_free); - } + return !rval; } -/* - * Routines for the character/ioctl interface to the driver + +/** + * mega_support_ext_cdb() + * @adapter - pointer to our soft state + * + * Find out if this firmware support cdblen > 10 */ -static int megadev_open (struct inode *inode, struct file *filep) +static int +mega_support_ext_cdb(adapter_t *adapter) { - MOD_INC_USE_COUNT; - return 0; /* success */ -} + unsigned char raw_mbox[16]; + mbox_t *mbox; + int rval; -static int megadev_ioctl_entry (struct inode *inode, struct file *filep, - unsigned int cmd, unsigned long arg) -{ - int ret = -1; + mbox = (mbox_t *)raw_mbox; + memset(mbox, 0, sizeof (mbox)); /* - * We do not allow parallel ioctls to the driver as of now. + * issue command to find out if controller supports extended CDBs. */ - down (&mimd_entry_mtx); - ret = megadev_ioctl (inode, filep, cmd, arg); - up (&mimd_entry_mtx); + raw_mbox[0] = 0xA4; + raw_mbox[2] = 0x16; - return ret; + rval = issue_scb_block(adapter, raw_mbox); + return !rval; } -static int megadev_ioctl (struct inode *inode, struct file *filep, - unsigned int cmd, unsigned long arg) + +/** + * mega_del_logdrv() + * @adapter - pointer to our soft state + * @logdrv - logical drive to be deleted + * + * Delete the specified logical drive. It is the responsibility of the user + * app to let the OS know about this operation. + */ +static int +mega_del_logdrv(adapter_t *adapter, int logdrv) { - int adapno; - kdev_t dev; - u32 inlen; - struct uioctl_t ioc; - char *kvaddr = NULL; - int nadap = numCtlrs; - u8 opcode; - u32 outlen; - int ret; - u8 subopcode; - Scsi_Cmnd *scsicmd; - struct Scsi_Host *shpnt; - char *uaddr; - struct uioctl_t *uioc; - dma_addr_t dma_addr; - u32 length; - mega_host_config *megacfg = NULL; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */ - struct pci_dev pdev; - struct pci_dev *pdevp = &pdev; -#else - char *pdevp = NULL; -#endif - IO_LOCK_T; + DECLARE_WAIT_QUEUE_HEAD(wq); + unsigned long flags; + scb_t *scb; + int rval; - if (!inode || !(dev = inode->i_rdev)) - return -EINVAL; + ASSERT( !spin_is_locked(&adapter->lock) ); - if (_IOC_TYPE (cmd) != MEGAIOC_MAGIC) - return (-EINVAL); + /* + * Stop sending commands to the controller, queue them internally. + * When deletion is complete, ISR will flush the queue. + */ + atomic_set(&adapter->quiescent, 1); /* - * Get the user ioctl structure + * Wait till all the issued commands are complete and there are no + * commands in the pending queue */ - ret = verify_area (VERIFY_WRITE, (char *) arg, sizeof (struct uioctl_t)); + while( atomic_read(&adapter->pend_cmds) > 0 ) { + + sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */ + } - if (ret) - return ret; + rval = mega_do_del_logdrv(adapter, logdrv); - if(copy_from_user (&ioc, (char *) arg, sizeof (struct uioctl_t))) - return -EFAULT; + + spin_lock_irqsave(&adapter->lock, flags); /* - * The first call the applications should make is to find out the - * number of controllers in the system. The next logical call should - * be for getting the list of controllers in the system as detected - * by the driver. + * If delete operation was successful, add 0x80 to the logical drive + * ids for commands in the pending queue. */ + if (adapter->read_ldidmap) { + struct list_head *pos; + list_for_each(pos, &adapter->pending_list) { + scb = list_entry(pos, scb_t, list); + if (((mbox_t *)scb->raw_mbox)->logdrv < 0x80 ) + ((mbox_t *)scb->raw_mbox)->logdrv += 0x80 ; + } + } + + atomic_set(&adapter->quiescent, 0); + + mega_runpendq(adapter); + + spin_unlock_irqrestore(&adapter->lock, flags); + + return rval; +} + + +static int +mega_do_del_logdrv(adapter_t *adapter, int logdrv) +{ + int rval; + u8 raw_mbox[16]; + + raw_mbox[0] = FC_DEL_LOGDRV; + raw_mbox[2] = OP_DEL_LOGDRV; + raw_mbox[3] = logdrv; + + /* Issue a blocking command to the card */ + rval = issue_scb_block(adapter, raw_mbox); + + /* log this event */ + if(rval) { + printk(KERN_WARNING "megaraid: Delete LD-%d failed.", logdrv); + return rval; + } /* - * Get the opcode and subopcode for the commands + * After deleting first logical drive, the logical drives must be + * addressed by adding 0x80 to the logical drive id. */ - opcode = ioc.ui.fcs.opcode; - subopcode = ioc.ui.fcs.subopcode; - - switch (opcode) { - case M_RD_DRIVER_IOCTL_INTERFACE: - switch (subopcode) { - case MEGAIOC_QDRVRVER: /* Query driver version */ - put_user (driver_ver, (u32 *) ioc.data); - return 0; + adapter->read_ldidmap = 1; - case MEGAIOC_QNADAP: /* Get # of adapters */ - put_user (nadap, (int *) ioc.data); - return nadap; + return rval; +} - case MEGAIOC_QADAPINFO: /* Get adapter information */ - /* - * which adapter? - */ - adapno = ioc.ui.fcs.adapno; - /* - * The adapter numbers do not start with 0, at least in - * the user space. This is just to make sure, 0 is not the - * default value which will refer to adapter 1. So the - * user needs to make use of macros MKADAP() and GETADAP() - * (See megaraid.h) while making ioctl() call. - */ - adapno = GETADAP (adapno); +/** + * mega_get_max_sgl() + * @adapter - pointer to our soft state + * + * Find out the maximum number of scatter-gather elements supported by this + * version of the firmware + */ +static void +mega_get_max_sgl(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; - if (adapno >= numCtlrs) - return (-ENODEV); + mbox = (mbox_t *)raw_mbox; - ret = verify_area (VERIFY_WRITE, - ioc.data, - sizeof (struct mcontroller)); - if (ret) - return ret; + memset(mbox, 0, sizeof(raw_mbox)); - /* - * Copy struct mcontroller to user area - */ - if (copy_to_user (ioc.data, - mcontroller + adapno, - sizeof (struct mcontroller))) - return -EFAULT; - return 0; + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - default: - return (-EINVAL); + mbox->xferaddr = (u32)adapter->buf_dma_handle; - } /* inner switch */ - break; + raw_mbox[0] = MAIN_MISC_OPCODE; + raw_mbox[2] = GET_MAX_SG_SUPPORT; - case M_RD_IOCTL_CMD_NEW: + if( issue_scb_block(adapter, raw_mbox) ) { /* - * Deletion of logical drives is only handled in 0x80 commands + * f/w does not support this command. Choose the default value */ - if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { - return -EINVAL; - } + adapter->sglen = MIN_SGLIST; + } + else { + adapter->sglen = *((char *)adapter->mega_buffer); + + /* + * Make sure this is not more than the resources we are + * planning to allocate + */ + if ( adapter->sglen > MAX_SGLIST ) + adapter->sglen = MAX_SGLIST; + } - /* which adapter? */ - adapno = ioc.ui.fcs.adapno; + return; +} - /* See comment above: MEGAIOC_QADAPINFO */ - adapno = GETADAP(adapno); - if (adapno >= numCtlrs) - return(-ENODEV); +/** + * mega_support_cluster() + * @adapter - pointer to our soft state + * + * Find out if this firmware support cluster calls. + */ +static int +mega_support_cluster(adapter_t *adapter) +{ + unsigned char raw_mbox[16]; + mbox_t *mbox; - length = ioc.ui.fcs.length; + mbox = (mbox_t *)raw_mbox; - /* Check for zero length buffer or very large buffers */ - if( !length || length > 32*1024 ) - return -EINVAL; + memset(mbox, 0, sizeof(raw_mbox)); - /* save the user address */ - uaddr = ioc.ui.fcs.buffer; + memset((void *)adapter->mega_buffer, 0, MEGA_BUFFER_SIZE); - /* - * For M_RD_IOCTL_CMD_NEW commands, the fields outlen and inlen of - * uioctl_t structure are treated as flags. If outlen is 1, the - * data is transferred from the device and if inlen is 1, the data - * is transferred to the device. - */ - outlen = ioc.outlen; - inlen = ioc.inlen; + mbox->xferaddr = (u32)adapter->buf_dma_handle; - if(outlen) { - ret = verify_area(VERIFY_WRITE, (char *)ioc.ui.fcs.buffer, length); - if (ret) return ret; - } - if(inlen) { - ret = verify_area(VERIFY_READ, (char *) ioc.ui.fcs.buffer, length); - if (ret) return ret; - } + /* + * Try to get the initiator id. This command will succeed iff the + * clustering is available on this HBA. + */ + raw_mbox[0] = MEGA_GET_TARGET_ID; + + if( issue_scb_block(adapter, raw_mbox) == 0 ) { /* - * Find this host + * Cluster support available. Get the initiator target id. + * Tell our id to mid-layer too. */ - for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) { - if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) { - megacfg = (mega_host_config *)shpnt->hostdata; - break; - } - } - if(shpnt == NULL) return -ENODEV; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL); -#else - scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd), - GFP_ATOMIC | GFP_DMA); -#endif - if(scsicmd == NULL) return -ENOMEM; + adapter->this_id = *(u32 *)adapter->mega_buffer; + adapter->host->this_id = adapter->this_id; - memset(scsicmd, 0, sizeof(Scsi_Cmnd)); - scsicmd->host = shpnt; + return 1; + } - if( outlen || inlen ) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pdevp = &pdev; - memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev)); - pdevp->dma_mask = 0xffffffff; -#else - pdevp = NULL; -#endif - kvaddr = dma_alloc_consistent(pdevp, length, &dma_addr); + return 0; +} - if( kvaddr == NULL ) { - printk(KERN_WARNING "megaraid:allocation failed\n"); - ret = -ENOMEM; - goto out; - } - ioc.ui.fcs.buffer = kvaddr; - if (inlen) { - /* copyin the user data */ - if (copy_from_user(kvaddr, (char *)uaddr, length )) { - ret = -EFAULT; - goto out; - } - } - } +/** + * mega_get_ldrv_num() + * @adapter - pointer to our soft state + * @cmd - scsi mid layer command + * @channel - channel on the controller + * + * Calculate the logical drive number based on the information in scsi command + * and the channel number. + */ +static inline int +mega_get_ldrv_num(adapter_t *adapter, Scsi_Cmnd *cmd, int channel) +{ + int tgt; + int ldrv_num; - scsicmd->cmnd[0] = MEGADEVIOC; - scsicmd->request_buffer = (void *)&ioc; + tgt = cmd->target; + + if ( tgt > adapter->this_id ) + tgt--; /* we do not get inquires for initiator id */ - init_MUTEX_LOCKED(&mimd_ioctl_sem); + ldrv_num = (channel * 15) + tgt; - IO_LOCK; - megaraid_queue(scsicmd, megadev_ioctl_done); - IO_UNLOCK; + /* + * If we have a logical drive with boot enabled, project it first + */ + if( adapter->boot_ldrv_enabled ) { + if( ldrv_num == 0 ) { + ldrv_num = adapter->boot_ldrv; + } + else { + if( ldrv_num <= adapter->boot_ldrv ) { + ldrv_num--; + } + } + } - down(&mimd_ioctl_sem); + /* + * If "delete logical drive" feature is enabled on this controller. + * Do only if at least one delete logical drive operation was done. + * + * Also, after logical drive deletion, instead of logical drive number, + * the value returned should be 0x80+logical drive id. + * + * These is valid only for IO commands. + */ - if( !scsicmd->result && outlen ) { - if (copy_to_user(uaddr, kvaddr, length)) - ret = -EFAULT; - goto out; + if (adapter->support_random_del && adapter->read_ldidmap ) + switch (cmd->cmnd[0]) { + case READ_6: /* fall through */ + case WRITE_6: /* fall through */ + case READ_10: /* fall through */ + case WRITE_10: + ldrv_num += 0x80; } - /* - * copyout the result - */ - uioc = (struct uioctl_t *)arg; + return ldrv_num; +} - if( ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU ) { - put_user( scsicmd->result, &uioc->pthru.scsistatus ); - if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer, - MAX_REQ_SENSE_LEN )) - ret= -EFAULT; - } else { - put_user(1, &uioc->mbox[16]); /* numstatus */ - /* status */ - put_user (scsicmd->result, &uioc->mbox[17]); - } -out: - if (kvaddr) { - dma_free_consistent(pdevp, length, kvaddr, dma_addr); +/** + * mega_reorder_hosts() + * + * Hack: reorder the scsi hosts in mid-layer so that the controller with the + * boot device on it appears first in the list. + */ +static void +mega_reorder_hosts(void) +{ + struct Scsi_Host *shpnt; + struct Scsi_Host *shone; + struct Scsi_Host *shtwo; + adapter_t *boot_host; + int i; + + /* + * Find the (first) host which has it's BIOS enabled + */ + boot_host = NULL; + for (i = 0; i < MAX_CONTROLLERS; i++) { + if (mega_hbas[i].is_bios_enabled) { + boot_host = mega_hbas[i].hostdata_addr; + break; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ - kfree (scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif + } - /* restore the user address */ - ioc.ui.fcs.buffer = uaddr; + if (!boot_host) { + printk(KERN_NOTICE "megaraid: no BIOS enabled.\n"); + return; + } - return ret; + /* + * Traverse through the list of SCSI hosts for our HBA locations + */ + shone = shtwo = NULL; + for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next) { + /* Is it one of ours? */ + for (i = 0; i < MAX_CONTROLLERS; i++) { + if ((adapter_t *) shpnt->hostdata == + mega_hbas[i].hostdata_addr) { + /* Does this one has BIOS enabled */ + if (mega_hbas[i].hostdata_addr == boot_host) { - case M_RD_IOCTL_CMD: - /* which adapter? */ - adapno = ioc.ui.fcs.adapno; + /* Are we first */ + if (!shtwo) /* Yes! */ + return; + else /* :-( */ + shone = shpnt; + } else { + if (!shtwo) { + /* were we here before? xchng + * first */ + shtwo = shpnt; + } + } + break; + } + } + /* + * Have we got the boot host and one which does not have the + * bios enabled. + */ + if (shone && shtwo) + break; + } + if (shone && shtwo) { + mega_swap_hosts (shone, shtwo); + } - /* See comment above: MEGAIOC_QADAPINFO */ - adapno = GETADAP (adapno); + return; +} - if (adapno >= numCtlrs) - return (-ENODEV); - /* save the user address */ - uaddr = ioc.data; - outlen = ioc.outlen; - inlen = ioc.inlen; +static void +mega_swap_hosts (struct Scsi_Host *shone, struct Scsi_Host *shtwo) +{ + struct Scsi_Host *prevtoshtwo; + struct Scsi_Host *prevtoshone; + struct Scsi_Host *save = NULL; - if ((outlen >= IOCTL_MAX_DATALEN) || (inlen >= IOCTL_MAX_DATALEN)) - return (-EINVAL); + /* Are these two nodes adjacent */ + if (shtwo->next == shone) { - if (outlen) { - ret = verify_area (VERIFY_WRITE, ioc.data, outlen); - if (ret) return ret; - } - if (inlen) { - ret = verify_area (VERIFY_READ, ioc.data, inlen); - if (ret) return ret; - } + if (shtwo == scsi_hostlist && !shone->next) { - /* - * Find this host - */ - for( shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next ) { - if( shpnt->hostdata == (unsigned long *)megaCtlrs[adapno] ) { - megacfg = (mega_host_config *)shpnt->hostdata; - break; - } - } - if(shpnt == NULL) return -ENODEV; + /* just two nodes */ + scsi_hostlist = shone; + shone->next = shtwo; + shtwo->next = NULL; + } else if (shtwo == scsi_hostlist) { + /* first two nodes of the list */ - /* - * ioctls for deleting logical drives is a special case, so check - * for it first - */ - if( ioc.mbox[0] == FC_DEL_LOGDRV && ioc.mbox[2] == OP_DEL_LOGDRV ) { + scsi_hostlist = shone; + shtwo->next = shone->next; + scsi_hostlist->next = shtwo; + } else if (!shone->next) { + /* last two nodes of the list */ - if( !megacfg->support_random_del ) { - printk("megaraid: logdrv delete on non supporting f/w.\n"); - return -EINVAL; - } + prevtoshtwo = scsi_hostlist; - uioc = (struct uioctl_t *)arg; + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; - ret = mega_del_logdrv(megacfg, ioc.mbox[3]); + prevtoshtwo->next = shone; + shone->next = shtwo; + shtwo->next = NULL; + } else { + prevtoshtwo = scsi_hostlist; - put_user(1, &uioc->mbox[16]); /* numstatus */ - put_user(ret, &uioc->mbox[17]); /* status */ + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; - /* if deletion failed, let the user know by failing ioctl */ - return ret; + prevtoshtwo->next = shone; + shtwo->next = shone->next; + shone->next = shtwo; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - scsicmd = (Scsi_Cmnd *)kmalloc(sizeof(Scsi_Cmnd), GFP_KERNEL); -#else - scsicmd = (Scsi_Cmnd *)scsi_init_malloc(sizeof(Scsi_Cmnd), - GFP_ATOMIC | GFP_DMA); -#endif - if(scsicmd == NULL) return -ENOMEM; - - memset(scsicmd, 0, sizeof(Scsi_Cmnd)); - scsicmd->host = shpnt; + } else if (shtwo == scsi_hostlist && !shone->next) { + /* shtwo at head, shone at tail, not adjacent */ - if (outlen || inlen) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pdevp = &pdev; - memcpy(pdevp, megacfg->dev, sizeof(struct pci_dev)); - pdevp->dma_mask = 0xffffffff; -#else - pdevp = NULL; -#endif - /* - * Allocate a page of kernel space. - */ - kvaddr = dma_alloc_consistent(pdevp, PAGE_SIZE, &dma_addr); + prevtoshone = scsi_hostlist; - if( kvaddr == NULL ) { - printk (KERN_WARNING "megaraid:allocation failed\n"); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /*0x20400 */ - kfree(scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif - return -ENOMEM; - } + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; - ioc.data = kvaddr; + scsi_hostlist = shone; + shone->next = shtwo->next; + prevtoshone->next = shtwo; + shtwo->next = NULL; + } else if (shtwo == scsi_hostlist && shone->next) { + /* shtwo at head, shone is not at tail */ - if (inlen) { - if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { - /* copyin the user data */ - copy_from_user (kvaddr, uaddr, ioc.pthru.dataxferlen); - } else { - copy_from_user (kvaddr, uaddr, inlen); - } - } - } + prevtoshone = scsi_hostlist; + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; - scsicmd->cmnd[0] = MEGADEVIOC; - scsicmd->request_buffer = (void *) &ioc; + scsi_hostlist = shone; + prevtoshone->next = shtwo; + save = shtwo->next; + shtwo->next = shone->next; + shone->next = save; + } else if (!shone->next) { + /* shtwo not at head, shone at tail */ - init_MUTEX_LOCKED (&mimd_ioctl_sem); + prevtoshtwo = scsi_hostlist; + prevtoshone = scsi_hostlist; - IO_LOCK; - megaraid_queue (scsicmd, megadev_ioctl_done); + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; - IO_UNLOCK; - down (&mimd_ioctl_sem); + prevtoshtwo->next = shone; + shone->next = shtwo->next; + prevtoshone->next = shtwo; + shtwo->next = NULL; - if (!scsicmd->result && outlen) { - if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { - if (copy_to_user (uaddr, kvaddr, ioc.pthru.dataxferlen)) - ret = -EFAULT; - } else { - if (copy_to_user (uaddr, kvaddr, outlen)) - ret = -EFAULT; - } - } + } else { + prevtoshtwo = scsi_hostlist; + prevtoshone = scsi_hostlist; + save = NULL; - /* - * copyout the result - */ - uioc = (struct uioctl_t *) arg; + while (prevtoshtwo->next != shtwo) + prevtoshtwo = prevtoshtwo->next; + while (prevtoshone->next != shone) + prevtoshone = prevtoshone->next; - if (ioc.mbox[0] == MEGA_MBOXCMD_PASSTHRU) { - put_user (scsicmd->result, &uioc->pthru.scsistatus); + prevtoshtwo->next = shone; + save = shone->next; + shone->next = shtwo->next; + prevtoshone->next = shtwo; + shtwo->next = save; + } + return; +} - /* - * If scsicmd->result is 0x02 (CHECK CONDITION) then copy the - * SCSI sense data into user area - */ - if (copy_to_user( uioc->pthru.reqsensearea, scsicmd->sense_buffer, - MAX_REQ_SENSE_LEN )) - ret = -EFAULT; - - } else { - put_user (1, &uioc->mbox[16]); /* numstatus */ - put_user (scsicmd->result, &uioc->mbox[17]); /* status */ - } - if (kvaddr) { - dma_free_consistent(pdevp, PAGE_SIZE, kvaddr, dma_addr ); - } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - kfree (scsicmd); -#else - scsi_init_free((char *)scsicmd, sizeof(Scsi_Cmnd)); -#endif +/** + * mega_adapinq() + * @adapter - pointer to our soft state + * @dma_handle - DMA address of the buffer + * + * Issue internal comamnds while interrupts are available. + * We only issue direct mailbox commands from within the driver. ioctl() + * interface using these routines can issue passthru commands. + */ +static int +mega_adapinq(adapter_t *adapter, dma_addr_t dma_handle) +{ + megacmd_t mc; - /* restore user pointer */ - ioc.data = uaddr; + memset(&mc, 0, sizeof(megacmd_t)); - return ret; + if( adapter->flag & BOARD_40LD ) { + mc.cmd = FC_NEW_CONFIG; + mc.opcode = NC_SUBOP_ENQUIRY3; + mc.subopcode = ENQ3_GET_SOLICITED_FULL; + } + else { + mc.cmd = MEGA_MBOXCMD_ADPEXTINQ; + } - default: - return (-EINVAL); + mc.xferaddr = (u32)dma_handle; - }/* Outer switch */ + if ( mega_internal_command(adapter, LOCK_INT, &mc, NULL) != 0 ) { + return -1; + } return 0; } -static void -megadev_ioctl_done(Scsi_Cmnd *sc) + +/** + * mega_allocate_inquiry() + * @dma_handle - handle returned for dma address + * @pdev - handle to pci device + * + * allocates memory for inquiry structure + */ +static inline caddr_t +mega_allocate_inquiry(dma_addr_t *dma_handle, struct pci_dev *pdev) { - up (&mimd_ioctl_sem); + return pci_alloc_consistent(pdev, sizeof(mega_inquiry3), dma_handle); } -static mega_scb * -megadev_doioctl (mega_host_config * megacfg, Scsi_Cmnd * sc) -{ - u8 cmd; - struct uioctl_t *ioc = NULL; - mega_mailbox *mbox = NULL; - mega_ioctl_mbox *mboxioc = NULL; - struct mbox_passthru *mboxpthru = NULL; - mega_scb *scb = NULL; - mega_passthru *pthru = NULL; - if ((scb = mega_allocateSCB (megacfg, sc)) == NULL) { - sc->result = (DID_ERROR << 16); - callDone (sc); - return NULL; - } +static inline void +mega_free_inquiry(caddr_t inquiry, dma_addr_t dma_handle, struct pci_dev *pdev) +{ + pci_free_consistent(pdev, sizeof(mega_inquiry3), inquiry, dma_handle); +} - ioc = (struct uioctl_t *) sc->request_buffer; - memcpy (scb->mboxData, ioc->mbox, sizeof (scb->mboxData)); +/** mega_internal_dev_inquiry() + * @adapter - pointer to our soft state + * @ch - channel for this device + * @tgt - ID of this device + * @buf_dma_handle - DMA address of the buffer + * + * Issue the scsi inquiry for the specified device. + */ +static int +mega_internal_dev_inquiry(adapter_t *adapter, u8 ch, u8 tgt, + dma_addr_t buf_dma_handle) +{ + mega_passthru *pthru; + dma_addr_t pthru_dma_handle; + megacmd_t mc; + int rval; + struct pci_dev *pdev; - /* The generic mailbox */ - mbox = (mega_mailbox *) ioc->mbox; /* - * Get the user command + * For all internal commands, the buffer must be allocated in <4GB + * address range */ - cmd = ioc->mbox[0]; + pdev = adapter->ipdev; - switch (cmd) { - case MEGA_MBOXCMD_PASSTHRU: - /* - * prepare the SCB with information from the user ioctl structure - */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - pthru = scb->pthru; -#else - pthru = &scb->pthru; -#endif - memcpy (pthru, &ioc->pthru, sizeof (mega_passthru)); - mboxpthru = (struct mbox_passthru *) scb->mboxData; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if (megacfg->flag & BOARD_64BIT) { - /* This is just a sample with one element - * This if executes onlu on 2.4 kernels - */ - mboxpthru->dataxferaddr = scb->dma_passthruhandle64; - scb->sg64List[0].address = - pci_map_single (megacfg->dev, - ioc->data, - 4096, PCI_DMA_BIDIRECTIONAL); - scb->sg64List[0].length = 4096; // TODO: Check this - pthru->dataxferaddr = scb->dma_sghandle64; - pthru->numsgelements = 1; - mboxpthru->cmd = MEGA_MBOXCMD_PASSTHRU64; - } else { - mboxpthru->dataxferaddr = scb->dma_passthruhandle64; - pthru->dataxferaddr = - pci_map_single (megacfg->dev, - ioc->data, - 4096, PCI_DMA_BIDIRECTIONAL); - pthru->numsgelements = 0; - } + pthru = pci_alloc_consistent(pdev, sizeof(mega_passthru), + &pthru_dma_handle); -#else - { - mboxpthru->dataxferaddr = virt_to_bus (&scb->pthru); - pthru->dataxferaddr = virt_to_bus (ioc->data); - pthru->numsgelements = 0; - } -#endif + if( pthru == NULL ) { + return -1; + } - pthru->reqsenselen = 14; - break; + pthru->timeout = 2; + pthru->ars = 1; + pthru->reqsenselen = 14; + pthru->islogical = 0; - default: /* Normal command */ - mboxioc = (mega_ioctl_mbox *) scb->mboxData; + pthru->channel = (adapter->flag & BOARD_40LD) ? 0 : ch; - if (ioc->ui.fcs.opcode == M_RD_IOCTL_CMD_NEW) { - scb->buff_ptr = ioc->ui.fcs.buffer; - scb->iDataSize = ioc->ui.fcs.length; - } else { - scb->buff_ptr = ioc->data; - scb->iDataSize = 4096; // TODO:check it - } + pthru->target = (adapter->flag & BOARD_40LD) ? (ch << 4)|tgt : tgt; - set_mbox_xfer_addr (megacfg, scb, mboxioc, FROMTO_DEVICE); - mboxioc->numsgelements = 0; - break; - } + pthru->cdblen = 6; - return scb; -} + pthru->cdb[0] = INQUIRY; + pthru->cdb[1] = 0; + pthru->cdb[2] = 0; + pthru->cdb[3] = 0; + pthru->cdb[4] = 255; + pthru->cdb[5] = 0; -static int -megadev_close (struct inode *inode, struct file *filep) -{ -#ifdef MODULE - MOD_DEC_USE_COUNT; -#endif - return 0; -} + pthru->dataxferaddr = (u32)buf_dma_handle; + pthru->dataxferlen = 256; -static int -mega_support_ext_cdb(mega_host_config *this_hba) -{ - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int ret; + memset(&mc, 0, sizeof(megacmd_t)); - mboxpnt = (mega_mailbox *) mbox; + mc.cmd = MEGA_MBOXCMD_PASSTHRU; + mc.xferaddr = (u32)pthru_dma_handle; - memset(mbox, 0, sizeof (mbox)); - /* - * issue command to find out if controller supports extended CDBs. - */ - mbox[0] = 0xA4; - mbox[2] = 0x16; + rval = mega_internal_command(adapter, LOCK_INT, &mc, pthru); - ret = megaIssueCmd(this_hba, mbox, NULL, 0); + pci_free_consistent(pdev, sizeof(mega_passthru), pthru, + pthru_dma_handle); - return !ret; + return rval; } -/* - * Find out if this controller supports random deletion and addition of - * logical drives +/** + * mega_internal_command() + * @adapter - pointer to our soft state + * @ls - the scope of the exclusion lock. + * @mc - the mailbox command + * @pthru - Passthru structure for DCDB commands + * + * Issue the internal commands in interrupt mode. + * The last argument is the address of the passthru structure if the command + * to be fired is a passthru command + * + * lockscope specifies whether the caller has already acquired the lock. Of + * course, the caller must know which lock we are talking about. + * + * Note: parameter 'pthru' is null for non-passthru commands. */ static int -mega_support_random_del(mega_host_config *this_hba) +mega_internal_command(adapter_t *adapter, lockscope_t ls, megacmd_t *mc, + mega_passthru *pthru ) { - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int ret; - - mboxpnt = (mega_mailbox *)mbox; - - memset(mbox, 0, sizeof(mbox)); + Scsi_Cmnd *scmd; + unsigned long flags = 0; + scb_t *scb; + int rval; /* - * issue command + * The internal commands share one command id and hence are + * serialized. This is so because we want to reserve maximum number of + * available command ids for the I/O commands. */ - mbox[0] = FC_DEL_LOGDRV; - mbox[2] = OP_SUP_DEL_LOGDRV; + down(&adapter->int_mtx); - ret = megaIssueCmd(this_hba, mbox, NULL, 0); + scb = &adapter->int_scb; + memset(scb, 0, sizeof(scb_t)); - return !ret; -} + scmd = &adapter->int_scmd; + memset(scmd, 0, sizeof(Scsi_Cmnd)); -static int -mega_del_logdrv(mega_host_config *this_hba, int logdrv) -{ - int rval; - IO_LOCK_T; - DECLARE_WAIT_QUEUE_HEAD(wq); - mega_scb *scbp; + scmd->host = adapter->host; + scmd->buffer = (void *)scb; + scmd->cmnd[0] = MEGA_INTERNAL_CMD; - /* - * Stop sending commands to the controller, queue them internally. - * When deletion is complete, ISR will flush the queue. - */ - IO_LOCK; - this_hba->quiescent = 1; - IO_UNLOCK; + scb->state |= SCB_ACTIVE; + scb->cmd = scmd; - while( this_hba->qPcnt ) { - sleep_on_timeout( &wq, 1*HZ ); /* sleep for 1s */ - } - rval = mega_do_del_logdrv(this_hba, logdrv); + memcpy(scb->raw_mbox, mc, sizeof(megacmd_t)); - IO_LOCK; /* - * Attach the internal queue to the pending queue + * Is it a passthru command */ - if( this_hba->qPendingH == NULL ) { - /* - * If pending queue head is null, make internal queue as - * pending queue - */ - this_hba->qPendingH = this_hba->int_qh; - this_hba->qPendingT = this_hba->int_qt; - this_hba->qPcnt = this_hba->int_qlen; - } - else { - /* - * Append pending queue to internal queue - */ - if( this_hba->int_qt ) { - this_hba->int_qt->next = this_hba->qPendingH; + if( mc->cmd == MEGA_MBOXCMD_PASSTHRU ) { - this_hba->qPendingH = this_hba->int_qh; - this_hba->qPcnt += this_hba->int_qlen; - } + scb->pthru = pthru; } - this_hba->int_qh = this_hba->int_qt = NULL; - this_hba->int_qlen = 0; + scb->idx = CMDID_INT_CMDS; + + scmd->state = 0; /* - * If delete operation was successful, add 0x80 to the logical drive - * ids for commands in the pending queue. + * Get the lock only if the caller has not acquired it already */ - if( this_hba->read_ldidmap) { - for( scbp = this_hba->qPendingH; scbp; scbp = scbp->next ) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - if( scbp->pthru->logdrv < 0x80 ) - scbp->pthru->logdrv += 0x80; -#else - if( scbp->pthru.logdrv < 0x80 ) - scbp->pthru.logdrv += 0x80; -#endif - } - } - this_hba->quiescent = 0; - - IO_UNLOCK; - - return rval; -} - - -static int -mega_do_del_logdrv(mega_host_config *this_hba, int logdrv) -{ - mega_mailbox *mboxpnt; - unsigned char mbox[16]; - int rval; - - mboxpnt = (mega_mailbox *)mbox; - - memset(mbox, 0, sizeof(mbox)); - - mbox[0] = FC_DEL_LOGDRV; - mbox[2] = OP_DEL_LOGDRV; - mbox[3] = logdrv; + if( ls == LOCK_INT ) spin_lock_irqsave(&adapter->lock, flags); - rval = megaIssueCmd(this_hba, mbox, NULL, 0); + megaraid_queue(scmd, mega_internal_done); - /* log this event */ - if( rval != 0 ) { - printk("megaraid: Attempt to delete logical drive %d failed.", - logdrv); - return rval; - } + if( ls == LOCK_INT ) spin_unlock_irqrestore(&adapter->lock, flags); /* - * After deleting first logical drive, the logical drives must be - * addressed by adding 0x80 to the logical drive id. + * Wait till this command finishes. Do not use + * wait_event_interruptible(). It causes panic if CTRL-C is hit when + * dumping e.g., physical disk information through /proc interface. + * Catching the return value should solve the issue but for now keep + * the call non-interruptible. */ - this_hba->read_ldidmap = 1; - - return rval; -} +#if 0 + wait_event_interruptible(adapter->int_waitq, scmd->state); +#endif + wait_event(adapter->int_waitq, scmd->state); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) -void * -dma_alloc_consistent(void *dev, size_t size, dma_addr_t *dma_addr) -{ - void *_tv; - int npages; - int order = 0; + rval = scmd->result; + mc->status = scmd->result; /* - * How many pages application needs + * Print a debug message for all failed commands. Applications can use + * this information. */ - npages = size / PAGE_SIZE; - - /* Do we need one more page */ - if(size % PAGE_SIZE) - npages++; - - order = mega_get_order(npages); - - _tv = (void *)__get_free_pages(GFP_DMA, order); - - if( _tv != NULL ) { - memset(_tv, 0, size); - *(dma_addr) = virt_to_bus(_tv); + if( scmd->result && trace_level ) { + printk("megaraid: cmd [%x, %x, %x] status:[%x]\n", + mc->cmd, mc->opcode, mc->subopcode, scmd->result); } - return _tv; -} - -/* - * int mega_get_order(int) - * - * returns the order to be used as 2nd argument to __get_free_pages() - which - * return pages equal to pow(2, order) - AM - */ -int -mega_get_order(int n) -{ - int i = 0; - - while( pow_2(i++) < n ) - ; /* null statement */ + up(&adapter->int_mtx); - return i-1; + return rval; } -/* - * int pow_2(int) + +/** + * mega_internal_done() + * @scmd - internal scsi command * - * calculates pow(2, i) + * Callback routine for internal commands. */ -int -pow_2(int i) -{ - unsigned int v = 1; - - while(i--) - v <<= 1; - - return v; -} - -void -dma_free_consistent(void *dev, size_t size, void *vaddr, dma_addr_t dma_addr) +static void +mega_internal_done(Scsi_Cmnd *scmd) { - int npages; - int order = 0; - - npages = size / PAGE_SIZE; + adapter_t *adapter; - if(size % PAGE_SIZE) - npages++; + adapter = (adapter_t *)scmd->host->hostdata; - if (npages == 1) - order = 0; - else if (npages == 2) - order = 1; - else if (npages <= 4) - order = 2; - else - order = 3; + scmd->state = 1; /* thread waiting for its command to complete */ - free_pages((unsigned long)vaddr, order); + /* + * See comment in mega_internal_command() routine for + * wait_event_interruptible() + */ +#if 0 + wake_up_interruptible(&adapter->int_waitq); +#endif + wake_up(&adapter->int_waitq); } -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -static -#endif /* LINUX VERSION 2.4.XX */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) || defined(MODULE) -Scsi_Host_Template driver_template = MEGARAID; +static Scsi_Host_Template driver_template = MEGARAID; #include "scsi_module.c" -#endif /* LINUX VERSION 2.4.XX || MODULE */ -/* vi: set ts=4: */ +/* vi: set ts=8 sw=8 tw=78: */ diff -urN linux-2.4.21-rc1/drivers/scsi/megaraid.h linux-2.4.21-rc1-megaraid-2.00.5/drivers/scsi/megaraid.h --- linux-2.4.21-rc1/drivers/scsi/megaraid.h Tue Apr 22 03:04:53 2003 +++ linux-2.4.21-rc1-megaraid-2.00.5/drivers/scsi/megaraid.h Fri Apr 25 00:19:58 2003 @@ -1,136 +1,67 @@ #ifndef __MEGARAID_H__ #define __MEGARAID_H__ -#ifndef LINUX_VERSION_CODE #include -#endif +#include -/* - * For state flag. Do not use LSB(8 bits) which are - * reserved for storing info about channels. - */ -#define IN_ISR 0x80000000L -#define IN_ABORT 0x40000000L -#define IN_RESET 0x20000000L -#define IN_QUEUE 0x10000000L -#define BOARD_QUARTZ 0x08000000L -#define BOARD_40LD 0x04000000L -#define BOARD_64BIT 0x02000000L - -#define SCB_FREE 0x0 -#define SCB_ACTIVE 0x1 -#define SCB_WAITQ 0x2 -#define SCB_ISSUED 0x3 -#define SCB_COMPLETE 0x4 -#define SCB_ABORTED 0x5 -#define SCB_RESET 0x6 - -#define M_RD_CRLFSTR "\n" -#define M_RD_IOCTL_CMD 0x80 -#define M_RD_IOCTL_CMD_NEW 0x81 -#define M_RD_DRIVER_IOCTL_INTERFACE 0x82 - -#define MEGARAID_VERSION "v1.18f (Release Date: Tue Dec 10 09:54:39 EST 2002)\n" - -#define MEGARAID_IOCTL_VERSION 114 - -/* Methods */ -#define GET_DRIVER_INFO 0x1 - -#define MEGA_CMD_TIMEOUT 10 - -/* Feel free to fiddle with these.. max values are: - SGLIST 0..26 - COMMANDS 0..253 - CMDPERLUN 0..63 -*/ - -#define MAX_SGLIST 0x1A -#define MAX_COMMANDS 127 -#define MAX_CMD_PER_LUN 63 -#define MAX_FIRMWARE_STATUS 46 - -#define MAX_LOGICAL_DRIVES 8 -#define MAX_CHANNEL 5 -#define MAX_TARGET 15 -#define MAX_PHYSICAL_DRIVES MAX_CHANNEL*MAX_TARGET -#define INQUIRY_DATA_SIZE 0x24 -#define MAX_CDB_LEN 0x0A -#define MAX_REQ_SENSE_LEN 0x20 +#define MEGARAID_VERSION \ + "v2.00.5 (Release Date: Thu Apr 24 14:06:55 EDT 2003)\n" -#define INTR_VALID 0x40 - -/* Direction Macros for MBOX Data direction */ -#define TO_DEVICE 0x0 -#define FROM_DEVICE 0x1 -#define FROMTO_DEVICE 0x2 - -/* Mailbox commands */ -#define MEGA_MBOXCMD_LREAD 0x01 -#define MEGA_MBOXCMD_LWRITE 0x02 -#define MEGA_MBOXCMD_LREAD64 0xA7 -#define MEGA_MBOXCMD_LWRITE64 0xA8 -#define MEGA_MBOXCMD_PASSTHRU 0x03 -#define MEGA_MBOXCMD_EXTPASSTHRU 0xE3 -#define MEGA_MBOXCMD_PASSTHRU64 0xC3 -#define MEGA_MBOXCMD_ADAPTERINQ 0x05 - - -/* Offsets into Mailbox */ -#define COMMAND_PORT 0x00 -#define COMMAND_ID_PORT 0x01 -#define SG_LIST_PORT0 0x08 -#define SG_LIST_PORT1 0x09 -#define SG_LIST_PORT2 0x0a -#define SG_LIST_PORT3 0x0b -#define SG_ELEMENT_PORT 0x0d -#define NO_FIRED_PORT 0x0f - -/* I/O Port offsets */ -#define I_CMD_PORT 0x00 -#define I_ACK_PORT 0x00 -#define I_TOGGLE_PORT 0x01 -#define INTR_PORT 0x0a +/* + * Driver features - change the values to enable or disable features in the + * driver. + */ -#define MAILBOX_SIZE (sizeof(mega_mailbox)-16) -#define MBOX_BUSY_PORT 0x00 -#define MBOX_PORT0 0x04 -#define MBOX_PORT1 0x05 -#define MBOX_PORT2 0x06 -#define MBOX_PORT3 0x07 -#define ENABLE_MBOX_REGION 0x0B +/* + * Comand coalescing - This feature allows the driver to be able to combine + * two or more commands and issue as one command in order to boost I/O + * performance. Useful if the nature of the I/O is sequential. It is not very + * useful for random natured I/Os. + */ +#define MEGA_HAVE_COALESCING 0 -/* I/O Port Values */ -#define ISSUE_BYTE 0x10 -#define ACK_BYTE 0x08 -#define ENABLE_INTR_BYTE 0xc0 -#define DISABLE_INTR_BYTE 0x00 -#define VALID_INTR_BYTE 0x40 -#define MBOX_BUSY_BYTE 0x10 -#define ENABLE_MBOX_BYTE 0x00 +/* + * Clustering support - Set this flag if you are planning to use the + * clustering services provided by the megaraid controllers and planning to + * setup a cluster + */ +#define MEGA_HAVE_CLUSTERING 1 -/* Setup some port macros here */ -#define WRITE_MAILBOX(base,offset,value) *(base+offset)=value -#define READ_MAILBOX(base,offset) *(base+offset) +/* + * Driver statistics - Set this flag if you are interested in statics about + * number of I/O completed on each logical drive and how many interrupts + * generated. If enabled, this information is available through /proc + * interface and through the private ioctl. Setting this flag has a + * performance penalty. + */ +#define MEGA_HAVE_STATS 0 -#define WRITE_PORT(base,offset,value) outb_p(value,base+offset) -#define READ_PORT(base,offset) inb_p(base+offset) +/* + * Enhanced /proc interface - This feature will allow you to have a more + * detailed /proc interface for megaraid driver. E.g., a real time update of + * the status of the logical drives, battery status, physical drives etc. + */ +#define MEGA_HAVE_ENH_PROC 1 -#define ISSUE_COMMAND(base) WRITE_PORT(base,I_CMD_PORT,ISSUE_BYTE) -#define CLEAR_INTR(base) WRITE_PORT(base,I_ACK_PORT,ACK_BYTE) -#define ENABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,ENABLE_INTR_BYTE) -#define DISABLE_INTR(base) WRITE_PORT(base,I_TOGGLE_PORT,DISABLE_INTR_BYTE) +#define MAX_DEV_TYPE 32 #ifndef PCI_VENDOR_ID_LSI_LOGIC #define PCI_VENDOR_ID_LSI_LOGIC 0x1000 #endif -/* Define AMI's PCI codes */ #ifndef PCI_VENDOR_ID_AMI #define PCI_VENDOR_ID_AMI 0x101E #endif +#ifndef PCI_VENDOR_ID_DELL +#define PCI_VENDOR_ID_DELL 0x1028 +#endif + +#ifndef PCI_VENDOR_ID_INTEL +#define PCI_VENDOR_ID_INTEL 0x8086 +#endif + #ifndef PCI_DEVICE_ID_AMI_MEGARAID #define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 #endif @@ -143,430 +74,112 @@ #define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 #endif -#define PCI_VENDOR_ID_DISCOVERY 0x1028 #define PCI_DEVICE_ID_DISCOVERY 0x000E - -#define PCI_VENDOR_ID_PERC4_DI_YSTONE 0x1028 -#define PCI_DEVICE_ID_PERC4_DI_YSTONE 0x000F - -#define PCI_VENDOR_ID_PERC4_QC_VERDE 0x1000 +#define PCI_DEVICE_ID_PERC4_DI 0x000F #define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407 -/* Special Adapter Commands */ -#define FW_FIRE_WRITE 0x2C -#define FW_FIRE_FLASH 0x2D - -#define FC_NEW_CONFIG 0xA1 -#define DCMD_FC_CMD 0xA1 -#define DCMD_FC_PROCEED 0x02 -#define DCMD_DELETE_LOGDRV 0x03 -#define DCMD_FC_READ_NVRAM_CONFIG 0x04 -#define DCMD_FC_READ_NVRAM_CONFIG_64 0xC0 -#define DCMD_FC_READ_FINAL_CONFIG 0x05 -#define DCMD_GET_DISK_CONFIG 0x06 -#define DCMD_GET_DISK_CONFIG_64 0xC2 -#define DCMD_CHANGE_LDNO 0x07 -#define DCMD_COMPACT_CONFIG 0x08 -#define DCMD_DELETE_DRIVEGROUP 0x09 -#define DCMD_GET_LOOPID_INFO 0x0A -#define DCMD_CHANGE_LOOPID 0x0B -#define DCMD_GET_NUM_SCSI_CHANS 0x0C -#define DCMD_WRITE_CONFIG 0x0D -#define DCMD_WRITE_CONFIG_64 0xC1 - -#define NC_SUBOP_PRODUCT_INFO 0x0E -#define NC_SUBOP_ENQUIRY3 0x0F -#define ENQ3_GET_SOLICITED_NOTIFY_ONLY 0x01 -#define ENQ3_GET_SOLICITED_FULL 0x02 -#define ENQ3_GET_UNSOLICITED 0x03 - -#define PCI_CONF_BASE_ADDR_OFFSET 0x10 -#define PCI_CONF_IRQ_OFFSET 0x3c -#define PCI_CONF_AMISIG 0xa0 -#define PCI_CONF_AMISIG64 0xa4 +/* Sub-System Vendor IDs */ +#define AMI_SUBSYS_VID 0x101E +#define DELL_SUBSYS_VID 0x1028 +#define HP_SUBSYS_VID 0x103C +#define LSI_SUBSYS_VID 0x1000 + +#define HBA_SIGNATURE 0x3344 +#define HBA_SIGNATURE_471 0xCCCC +#define HBA_SIGNATURE_64BIT 0x0299 + +#define MBOX_BUSY_WAIT 10 /* wait for up to 10 usec for + mailbox to be free */ +#define DEFAULT_INITIATOR_ID 7 + +#define MAX_SGLIST 64 /* max supported in f/w */ +#define MIN_SGLIST 26 /* guaranteed to support these many */ +#define MAX_COMMANDS 126 +#define CMDID_INT_CMDS MAX_COMMANDS+1 /* make sure CMDID_INT_CMDS + is less than max commands + supported by any f/w */ + +#define MAX_CDB_LEN 10 +#define MAX_EXT_CDB_LEN 16 /* we support cdb length up to 16 */ + +#define DEF_CMD_PER_LUN 63 +#define MAX_CMD_PER_LUN MAX_COMMANDS +#define MAX_FIRMWARE_STATUS 46 +#define MAX_XFER_PER_CMD (64*1024) +#define MAX_SECTORS_PER_IO 128 + +#define MAX_LOGICAL_DRIVES_40LD 40 +#define FC_MAX_PHYSICAL_DEVICES 256 +#define MAX_LOGICAL_DRIVES_8LD 8 +#define MAX_CHANNELS 5 +#define MAX_TARGET 15 +#define MAX_PHYSICAL_DRIVES MAX_CHANNELS*MAX_TARGET +#define MAX_ROW_SIZE_40LD 32 +#define MAX_ROW_SIZE_8LD 8 +#define MAX_SPAN_DEPTH 8 + +#define NVIRT_CHAN 4 /* # of virtual channels to represent + up to 60 logical drives */ + +#define MEGARAID \ +{ \ + .name = "MegaRAID", \ + .proc_name = "megaraid", \ + .detect = megaraid_detect, \ + .release = megaraid_release, \ + .info = megaraid_info, \ + .command = megaraid_command, \ + .queuecommand = megaraid_queue, \ + .bios_param = megaraid_biosparam, \ + .max_sectors = MAX_SECTORS_PER_IO, \ + .can_queue = MAX_COMMANDS, \ + .this_id = DEFAULT_INITIATOR_ID, \ + .sg_tablesize = MAX_SGLIST, \ + .cmd_per_lun = DEF_CMD_PER_LUN, \ + .present = 0, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING, \ + .use_new_eh_code = 1, \ + .eh_abort_handler = megaraid_abort, \ + .eh_device_reset_handler = megaraid_reset, \ + .eh_bus_reset_handler = megaraid_reset, \ + .eh_host_reset_handler = megaraid_reset, \ + .highmem_io = 1, \ + .can_do_varyio = 1 \ +} -/* Sub-System Vendor ID sorted on alphabetical order*/ -#define LSI_SUBSYS_ID 0x1000 -#define AMI_SUBSYS_ID 0x101E -#define DELL_SUBSYS_ID 0x1028 -#define HP_SUBSYS_ID 0x103C - -#define AMI_SIGNATURE 0x3344 -#define AMI_SIGNATURE_471 0xCCCC -#define AMI_64BIT_SIGNATURE 0x0299 - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /*0x20100 */ -#define MEGARAID \ - { NULL, /* Next */\ - NULL, /* Usage Count Pointer */\ - NULL, /* proc Directory Entry */\ - megaraid_proc_info, /* proc Info Function */\ - "MegaRAID", /* Driver Name */\ - megaraid_detect, /* Detect Host Adapter */\ - megaraid_release, /* Release Host Adapter */\ - megaraid_info, /* Driver Info Function */\ - megaraid_command, /* Command Function */\ - megaraid_queue, /* Queue Command Function */\ - megaraid_abort, /* Abort Command Function */\ - megaraid_reset, /* Reset Command Function */\ - NULL, /* Slave Attach Function */\ - megaraid_biosparam, /* Disk BIOS Parameters */\ - MAX_COMMANDS, /* # of cmds that can be\ - outstanding at any time */\ - 7, /* HBA Target ID */\ - MAX_SGLIST, /* Scatter/Gather Table Size */\ - MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\ - 0, /* Present */\ - 0, /* Default Unchecked ISA DMA */\ - ENABLE_CLUSTERING } /* Enable Clustering */ -#else -#define MEGARAID \ - {\ - name: "MegaRAID", /* Driver Name */\ - proc_info: megaraid_proc_info, /* /proc driver info */\ - detect: megaraid_detect, /* Detect Host Adapter */\ - release: megaraid_release, /* Release Host Adapter */\ - info: megaraid_info, /* Driver Info Function */\ - command: megaraid_command, /* Command Function */\ - queuecommand: megaraid_queue, /* Queue Command Function */\ - abort: megaraid_abort, /* Abort Command Function */\ - reset: megaraid_reset, /* Reset Command Function */\ - bios_param: megaraid_biosparam, /* Disk BIOS Parameters */\ - can_queue: MAX_COMMANDS, /* Can Queue */\ - this_id: 7, /* HBA Target ID */\ - sg_tablesize: MAX_SGLIST, /* Scatter/Gather Table Size */\ - cmd_per_lun: MAX_CMD_PER_LUN, /* SCSI Commands per LUN */\ - present: 0, /* Present */\ - unchecked_isa_dma: 0, /* Default Unchecked ISA DMA */\ - use_clustering: ENABLE_CLUSTERING, /* Enable Clustering */\ - highmem_io: 1, /* enable HIGHMEM I/O */ \ - } -#endif - -/*********************************************************************** - * Structure Declarations for the Firmware supporting 40 Logical Drives - * and 256 Physical Drives. - ***********************************************************************/ - -#define FC_MAX_LOGICAL_DRIVES 40 -#define FC_MAX_LOG_DEVICES FC_MAX_LOGICAL_DRIVES -#define FC_MAX_SPAN_DEPTH 8 -#define FC_MAX_ROW_SIZE 32 - -#define FC_MAX_CHANNELS 16 -#define FC_MAX_TARGETS_PER_CHANNEL 16 -#define FC_MAX_PHYSICAL_DEVICES 256 - -/******************************************** - * PRODUCT_INFO - ********************************************/ - -#define SIG_40LOG_32STR_8SPN 0x00282008 - -/* - * Utilities declare this strcture size as 1024 bytes. So more fields can - * be added in future. - */ - -struct MRaidProductInfo { - u32 DataSize; /* current size in bytes (not including resvd) */ - u32 ConfigSignature; - /* Current value is 0x00282008 - * 0x28=MAX_LOGICAL_DRIVES, - * 0x20=Number of stripes and - * 0x08=Number of spans */ - u8 FwVer[16]; /* printable ASCI string */ - u8 BiosVer[16]; /* printable ASCI string */ - u8 ProductName[80]; /* printable ASCI string */ - - u8 MaxConcCmds; /* Max. concurrent commands supported */ - u8 SCSIChanPresent; /* Number of SCSI Channels detected */ - u8 FCLoopPresent; /* Number of Fibre Loops detected */ - u8 memType; /* EDO, FPM, SDRAM etc */ - - u32 signature; - u16 DramSize; /* In terms of MB */ - u16 subSystemID; - - u16 subSystemVendorID; - u8 numNotifyCounters; - u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */ -} __attribute__ ((packed)); -typedef struct MRaidProductInfo megaRaidProductInfo; - -/******************************************** - * Standard ENQUIRY - ********************************************/ -struct FC_ADP_INFO { - u8 MaxConcCmds; /* Max. concurrent commands supported. */ - u8 RbldRate; /* Rebuild Rate. Varies from 0%-100% */ - u8 MaxTargPerChan; /* Max. Targets supported per chan. */ - u8 ChanPresent; /* No. of Chans present on this adapter. */ - u8 FwVer[4]; /* Firmware version. */ - u16 AgeOfFlash; /* No. of times FW has been downloaded. */ - u8 ChipSetValue; /* Contents of 0xC0000832 */ - u8 DramSize; /* In terms of MB */ - u8 CacheFlushInterval; /* In terms of Seconds */ - u8 BiosVersion[4]; - u8 BoardType; - u8 sense_alert; - u8 write_config_count; /* Increase with evry configuration change */ - u8 drive_inserted_count;/* Increase with every drive inserted */ - u8 inserted_drive; /* Channel: Id of inserted drive */ - u8 battery_status; - /* - BIT 0 : battery module missing - BIT 1 : VBAD - BIT 2 : temp high - BIT 3 : battery pack missing - BIT 4,5 : 00 - charge complete - 01 - fast charge in prog - 10 - fast charge fail - 11 - undefined - BIt 6 : counter > 1000 - Bit 7 : undefined - */ - u8 dec_fault_bus_info; /* was resvd */ -} __attribute__ ((packed)); - -struct FC_LDRV_INFO { - u8 NumLDrv; /* No. of Log. Drvs configured. */ - u8 recon_state[FC_MAX_LOGICAL_DRIVES / 8]; - /* bit field for State of reconstruct */ - u16 LDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; - /* bit field Status of Long Operations. */ - - u32 LDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv. */ - u8 LDrvProp[FC_MAX_LOGICAL_DRIVES]; - u8 LDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives. */ -} __attribute__ ((packed)); - -#define PREVSTAT_MASK 0xf0 -#define CURRSTAT_MASK 0x0f - -struct FC_PDRV_INFO { - u8 PDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys Drvs. */ -} __attribute__ ((packed)); - -struct FC_AdapterInq { - struct FC_ADP_INFO AdpInfo; - struct FC_LDRV_INFO LogdrvInfo; - struct FC_PDRV_INFO PhysdrvInfo; -} __attribute__ ((packed)); - -typedef struct FC_AdapterInq mega_RAIDINQ_FC; -/******************************************** - * NOTIFICATION - ********************************************/ -#define MAX_NOTIFY_SIZE 0x80 -#define CUR_NOTIFY_SIZE sizeof(struct MegaRAID_Notify) - -/* - * Utilities declare this strcture size as ?? bytes. So more fields can - * be added in future. - */ -struct MegaRAID_Notify { - u32 globalCounter; /* Any change increments this counter */ +typedef struct { + /* 0x0 */ u8 cmd; + /* 0x1 */ u8 cmdid; + /* 0x2 */ u16 numsectors; + /* 0x4 */ u32 lba; + /* 0x8 */ u32 xferaddr; + /* 0xC */ u8 logdrv; + /* 0xD */ u8 numsgelements; + /* 0xE */ u8 resvd; + /* 0xF */ volatile u8 busy; + /* 0x10 */ volatile u8 numstatus; + /* 0x11 */ volatile u8 status; + /* 0x12 */ volatile u8 completed[MAX_FIRMWARE_STATUS]; + volatile u8 poll; + volatile u8 ack; +} __attribute__ ((packed)) mbox_t; - u8 paramCounter; /* Indicates any params changed */ - u8 paramId; /* Param modified - defined below */ - u16 paramVal; /* New val of last param modified */ - - u8 writeConfigCounter; /* write config occurred */ - u8 writeConfigRsvd[3]; - - u8 ldrvOpCounter; /* Indicates ldrv op started/completed */ - u8 ldrvOpId; /* ldrv num */ - u8 ldrvOpCmd; /* ldrv operation - defined below */ - u8 ldrvOpStatus; /* status of the operation */ - - u8 ldrvStateCounter; /* Indicates change of ldrv state */ - u8 ldrvStateId; /* ldrv num */ - u8 ldrvStateNew; /* New state */ - u8 ldrvStateOld; /* old state */ - - u8 pdrvStateCounter; /* Indicates change of ldrv state */ - u8 pdrvStateId; /* pdrv id */ - u8 pdrvStateNew; /* New state */ - u8 pdrvStateOld; /* old state */ - - u8 pdrvFmtCounter; /* Indicates pdrv format started/over */ - u8 pdrvFmtId; /* pdrv id */ - u8 pdrvFmtVal; /* format started/over */ - u8 pdrvFmtRsvd; - - u8 targXferCounter; /* Indicates SCSI-2 Xfer rate change */ - u8 targXferId; /* pdrv Id */ - u8 targXferVal; /* new Xfer params of last pdrv */ - u8 targXferRsvd; - - u8 fcLoopIdChgCounter; /* Indicates loopid changed */ - u8 fcLoopIdPdrvId; /* pdrv id */ - u8 fcLoopId0; /* loopid on fc loop 0 */ - u8 fcLoopId1; /* loopid on fc loop 1 */ - - u8 fcLoopStateCounter; /* Indicates loop state changed */ - u8 fcLoopState0; /* state of fc loop 0 */ - u8 fcLoopState1; /* state of fc loop 1 */ - u8 fcLoopStateRsvd; -} __attribute__ ((packed)); +typedef struct { + u32 xfer_segment_lo; + u32 xfer_segment_hi; + mbox_t mbox; +} __attribute__ ((packed)) mbox64_t; -/******************************************** - * PARAM IDs in Notify struct - ********************************************/ -#define PARAM_RBLD_RATE 0x01 - /*-------------------------------------- - * Param val = - * byte 0: new rbld rate - *--------------------------------------*/ -#define PARAM_CACHE_FLUSH_INTERVAL 0x02 - /*-------------------------------------- - * Param val = - * byte 0: new cache flush interval - *--------------------------------------*/ -#define PARAM_SENSE_ALERT 0x03 - /*-------------------------------------- - * Param val = - * byte 0: last pdrv id causing chkcond - *--------------------------------------*/ -#define PARAM_DRIVE_INSERTED 0x04 - /*-------------------------------------- - * Param val = - * byte 0: last pdrv id inserted - *--------------------------------------*/ -#define PARAM_BATTERY_STATUS 0x05 - /*-------------------------------------- - * Param val = - * byte 0: battery status - *--------------------------------------*/ - -/******************************************** - * Ldrv operation cmd in Notify struct - ********************************************/ -#define LDRV_CMD_CHKCONSISTANCY 0x01 -#define LDRV_CMD_INITIALIZE 0x02 -#define LDRV_CMD_RECONSTRUCTION 0x03 - -/******************************************** - * Ldrv operation status in Notify struct - ********************************************/ -#define LDRV_OP_SUCCESS 0x00 -#define LDRV_OP_FAILED 0x01 -#define LDRV_OP_ABORTED 0x02 -#define LDRV_OP_CORRECTED 0x03 -#define LDRV_OP_STARTED 0x04 - -/******************************************** - * Raid Logical drive states. - ********************************************/ -#define RDRV_OFFLINE 0 -#define RDRV_DEGRADED 1 -#define RDRV_OPTIMAL 2 -#define RDRV_DELETED 3 -/******************************************* - * Physical drive states. - *******************************************/ -#define PDRV_UNCNF 0 -#define PDRV_ONLINE 3 -#define PDRV_FAILED 4 -#define PDRV_RBLD 5 - -/******************************************* - * Formal val in Notify struct - *******************************************/ -#define PDRV_FMT_START 0x01 -#define PDRV_FMT_OVER 0x02 - -/******************************************** - * FC Loop State in Notify Struct - ********************************************/ -#define ENQ_FCLOOP_FAILED 0 -#define ENQ_FCLOOP_ACTIVE 1 -#define ENQ_FCLOOP_TRANSIENT 2 - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -#define M_RD_DMA_TYPE_NONE 0xFFFF -#define M_RD_PTHRU_WITH_BULK_DATA 0x0001 -#define M_RD_PTHRU_WITH_SGLIST 0x0002 -#define M_RD_BULK_DATA_ONLY 0x0004 -#define M_RD_SGLIST_ONLY 0x0008 -#define M_RD_EPTHRU_WITH_BULK_DATA 0x0010 -#endif -/******************************************** - * ENQUIRY3 - ********************************************/ /* - * Utilities declare this strcture size as 1024 bytes. So more fields can - * be added in future. + * Passthru definitions */ -struct MegaRAID_Enquiry3 { - u32 dataSize; /* current size in bytes (not including resvd) */ - - struct MegaRAID_Notify notify; - - u8 notifyRsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; - - u8 rbldRate; /* Rebuild rate (0% - 100%) */ - u8 cacheFlushInterval; /* In terms of Seconds */ - u8 senseAlert; - u8 driveInsertedCount; /* drive insertion count */ - - u8 batteryStatus; - u8 numLDrv; /* No. of Log Drives configured */ - u8 reconState[FC_MAX_LOGICAL_DRIVES / 8]; /* State of reconstruct */ - u16 lDrvOpStatus[FC_MAX_LOGICAL_DRIVES / 8]; /* log. Drv Status */ - - u32 lDrvSize[FC_MAX_LOGICAL_DRIVES]; /* Size of each log. Drv */ - u8 lDrvProp[FC_MAX_LOGICAL_DRIVES]; - u8 lDrvState[FC_MAX_LOGICAL_DRIVES]; /* State of Logical Drives */ - u8 pDrvState[FC_MAX_PHYSICAL_DEVICES]; /* State of Phys. Drvs. */ - u16 physDrvFormat[FC_MAX_PHYSICAL_DEVICES / 16]; - - u8 targXfer[80]; /* phys device transfer rate */ - u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */ -} __attribute__ ((packed)); -typedef struct MegaRAID_Enquiry3 mega_Enquiry3; - -/* Structures */ -typedef struct _mega_ADP_INFO { - u8 MaxConcCmds; - u8 RbldRate; - u8 MaxTargPerChan; - u8 ChanPresent; - u8 FwVer[4]; - u16 AgeOfFlash; - u8 ChipSetValue; - u8 DramSize; - u8 CacheFlushInterval; - u8 BiosVer[4]; - u8 resvd[7]; -} mega_ADP_INFO; - -typedef struct _mega_LDRV_INFO { - u8 NumLDrv; - u8 resvd[3]; - u32 LDrvSize[MAX_LOGICAL_DRIVES]; - u8 LDrvProp[MAX_LOGICAL_DRIVES]; - u8 LDrvState[MAX_LOGICAL_DRIVES]; -} mega_LDRV_INFO; - -typedef struct _mega_PDRV_INFO { - u8 PDrvState[MAX_PHYSICAL_DRIVES]; - u8 resvd; -} mega_PDRV_INFO; - -/* RAID inquiry: Mailbox command 0x5*/ -typedef struct _mega_RAIDINQ { - mega_ADP_INFO AdpInfo; - mega_LDRV_INFO LogdrvInfo; - mega_PDRV_INFO PhysdrvInfo; -} mega_RAIDINQ; +#define MAX_REQ_SENSE_LEN 0x20 -/* Passthrough command: Mailbox command 0x3*/ -typedef struct mega_passthru { +typedef struct { u8 timeout:3; /* 0=6sec/1=60sec/2=10min/3=3hrs */ u8 ars:1; u8 reserved:3; @@ -584,7 +197,8 @@ u8 scsistatus; u32 dataxferaddr; u32 dataxferlen; -} mega_passthru; +} __attribute__ ((packed)) mega_passthru; + /* * Extended passthru: support CDB > 10 bytes @@ -596,238 +210,318 @@ u8 cd_rom:1; u8 rsvd2:1; u8 islogical:1; - u8 logdrv; /* if islogical == 1 */ u8 channel; /* if islogical == 0 */ u8 target; /* if islogical == 0 */ - u8 queuetag; /* unused */ u8 queueaction; /* unused */ - u8 cdblen; u8 rsvd3; - u8 cdb[16]; - + u8 cdb[MAX_EXT_CDB_LEN]; u8 numsgelements; u8 status; u8 reqsenselen; u8 reqsensearea[MAX_REQ_SENSE_LEN]; u8 rsvd4; - u32 dataxferaddr; u32 dataxferlen; -}mega_ext_passthru; - -struct _mega_mailbox { - /* 0x0 */ u8 cmd; - /* 0x1 */ u8 cmdid; - /* 0x2 */ u16 numsectors; - /* 0x4 */ u32 lba; - /* 0x8 */ u32 xferaddr; - /* 0xC */ u8 logdrv; - /* 0xD */ u8 numsgelements; - /* 0xE */ u8 resvd; - /* 0xF */ u8 busy; - /* 0x10 */ u8 numstatus; - /* 0x11 */ u8 status; - /* 0x12 */ u8 completed[46]; - volatile u8 mraid_poll; - volatile u8 mraid_ack; - u8 pad[16]; /* for alignment purposes */ -} __attribute__ ((packed)); -typedef struct _mega_mailbox mega_mailbox; +} __attribute__ ((packed)) mega_ext_passthru; typedef struct { - u32 xferSegment_lo; - u32 xferSegment_hi; - mega_mailbox mailbox; -} mega_mailbox64; - -typedef struct _mega_ioctl_mbox { - /* 0x0 */ u8 cmd; - /* 0x1 */ u8 cmdid; - /* 0x2 */ u8 channel; - /* 0x3 */ u8 param; - /* 0x4 */ u8 pad[4]; - /* 0x8 */ u32 xferaddr; - /* 0xC */ u8 logdrv; - /* 0xD */ u8 numsgelements; - /* 0xE */ u8 resvd; - /* 0xF */ u8 busy; - /* 0x10 */ u8 numstatus; - /* 0x11 */ u8 status; - /* 0x12 */ u8 completed[46]; - u8 mraid_poll; - u8 mraid_ack; - u8 malign[16]; -} mega_ioctl_mbox; - -typedef struct _mega_64sglist32 { u64 address; u32 length; -} __attribute__ ((packed)) mega_64sglist; +} __attribute__ ((packed)) mega_sgl64; -typedef struct _mega_sglist { +typedef struct { u32 address; u32 length; -} mega_sglist; +} __attribute__ ((packed)) mega_sglist; + /* Queued command data */ -typedef struct _mega_scb mega_scb; +typedef struct { + int idx; + u32 state; + struct list_head list; + u8 raw_mbox[66]; + u32 dma_type; + u32 dma_direction; + + Scsi_Cmnd *cmd; + dma_addr_t dma_h_bulkdata; + dma_addr_t dma_h_sgdata; + + mega_sglist *sgl; + mega_sgl64 *sgl64; + dma_addr_t sgl_dma_addr; + + mega_passthru *pthru; + dma_addr_t pthru_dma_addr; + mega_ext_passthru *epthru; + dma_addr_t epthru_dma_addr; +} scb_t; -struct _mega_scb { - int idx; - u32 state; - u32 isrcount; - u8 mboxData[16]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - u32 dma_type; - dma_addr_t dma_h_bulkdata; /*Dma handle for bulk data transfter */ - u32 dma_direction; /*Dma direction */ - dma_addr_t dma_h_sgdata; /*Dma handle for the sglist structure */ - dma_addr_t dma_h_sglist[MAX_SGLIST]; /*Dma handle for all SGL elements */ - u8 sglist_count; - dma_addr_t dma_sghandle64; - dma_addr_t dma_passthruhandle64; - dma_addr_t dma_ext_passthruhandle64; - dma_addr_t dma_bounce_buffer; - u8 *bounce_buffer; -#endif +/* + * Flags to follow the scb as it transitions between various stages + */ +#define SCB_FREE 0x0000 /* on the free list */ +#define SCB_ACTIVE 0x0001 /* off the free list */ +#define SCB_PENDQ 0x0002 /* on the pending queue */ +#define SCB_ISSUED 0x0004 /* issued - owner f/w */ +#define SCB_ABORT 0x0008 /* Got an abort for this one */ +#define SCB_RESET 0x0010 /* Got a reset for this one */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - mega_passthru *pthru; - mega_ext_passthru *epthru; -#else - mega_passthru pthru; - mega_ext_passthru epthru; -#endif +/* + * Utilities declare this strcture size as 1024 bytes. So more fields can + * be added in future. + */ +typedef struct { + u32 data_size; /* current size in bytes (not including resvd) */ - Scsi_Cmnd *SCpnt; - mega_sglist *sgList; - mega_64sglist *sg64List; - struct semaphore ioctl_sem; - void *buff_ptr; - u32 iDataSize; - mega_scb *next; -}; + u32 config_signature; + /* Current value is 0x00282008 + * 0x28=MAX_LOGICAL_DRIVES, + * 0x20=Number of stripes and + * 0x08=Number of spans */ + + u8 fw_version[16]; /* printable ASCI string */ + u8 bios_version[16]; /* printable ASCI string */ + u8 product_name[80]; /* printable ASCI string */ + + u8 max_commands; /* Max. concurrent commands supported */ + u8 nchannels; /* Number of SCSI Channels detected */ + u8 fc_loop_present; /* Number of Fibre Loops detected */ + u8 mem_type; /* EDO, FPM, SDRAM etc */ + + u32 signature; + u16 dram_size; /* In terms of MB */ + u16 subsysid; + + u16 subsysvid; + u8 notify_counters; + u8 pad1k[889]; /* 135 + 889 resvd = 1024 total size */ +} __attribute__ ((packed)) mega_product_info; + +struct notify { + u32 global_counter; /* Any change increments this counter */ + + u8 param_counter; /* Indicates any params changed */ + u8 param_id; /* Param modified - defined below */ + u16 param_val; /* New val of last param modified */ + + u8 write_config_counter; /* write config occurred */ + u8 write_config_rsvd[3]; + + u8 ldrv_op_counter; /* Indicates ldrv op started/completed */ + u8 ldrv_opid; /* ldrv num */ + u8 ldrv_opcmd; /* ldrv operation - defined below */ + u8 ldrv_opstatus; /* status of the operation */ + + u8 ldrv_state_counter; /* Indicates change of ldrv state */ + u8 ldrv_state_id; /* ldrv num */ + u8 ldrv_state_new; /* New state */ + u8 ldrv_state_old; /* old state */ + + u8 pdrv_state_counter; /* Indicates change of ldrv state */ + u8 pdrv_state_id; /* pdrv id */ + u8 pdrv_state_new; /* New state */ + u8 pdrv_state_old; /* old state */ + + u8 pdrv_fmt_counter; /* Indicates pdrv format started/over */ + u8 pdrv_fmt_id; /* pdrv id */ + u8 pdrv_fmt_val; /* format started/over */ + u8 pdrv_fmt_rsvd; + + u8 targ_xfer_counter; /* Indicates SCSI-2 Xfer rate change */ + u8 targ_xfer_id; /* pdrv Id */ + u8 targ_xfer_val; /* new Xfer params of last pdrv */ + u8 targ_xfer_rsvd; + + u8 fcloop_id_chg_counter; /* Indicates loopid changed */ + u8 fcloopid_pdrvid; /* pdrv id */ + u8 fcloop_id0; /* loopid on fc loop 0 */ + u8 fcloop_id1; /* loopid on fc loop 1 */ + + u8 fcloop_state_counter; /* Indicates loop state changed */ + u8 fcloop_state0; /* state of fc loop 0 */ + u8 fcloop_state1; /* state of fc loop 1 */ + u8 fcloop_state_rsvd; +} __attribute__ ((packed)); -/* internal locking by the queue manipulting routines */ -#define INTERNAL_LOCK 0 -/* external locking by the queue manipulting routines */ -#define EXTERNAL_LOCK 1 -#define NO_LOCK 2 -#define INTR_ENB 0 /* do not disable interrupt while manipulating */ -#define INTR_DIS 1 /* disable interrupt while manipulating */ +#define MAX_NOTIFY_SIZE 0x80 +#define CUR_NOTIFY_SIZE sizeof(struct notify) -#define NVIRT_CHAN 4 /* # of virtual channels to represent 60 logical - drives */ +typedef struct { + u32 data_size; /* current size in bytes (not including resvd) */ -/* Per-controller data */ -typedef struct _mega_host_config { - u8 numldrv; - u32 flag; + struct notify notify; -#ifdef __LP64__ - u64 base; -#else - u32 base; -#endif + u8 notify_rsvd[MAX_NOTIFY_SIZE - CUR_NOTIFY_SIZE]; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) - dma_addr_t dma_handle64, adjdmahandle64; - struct pci_dev *dev; -#endif + u8 rebuild_rate; /* Rebuild rate (0% - 100%) */ + u8 cache_flush_interval; /* In terms of Seconds */ + u8 sense_alert; + u8 drive_insert_count; /* drive insertion count */ + + u8 battery_status; + u8 num_ldrv; /* No. of Log Drives configured */ + u8 recon_state[MAX_LOGICAL_DRIVES_40LD / 8]; /* State of + reconstruct */ + u16 ldrv_op_status[MAX_LOGICAL_DRIVES_40LD / 8]; /* logdrv + Status */ + + u32 ldrv_size[MAX_LOGICAL_DRIVES_40LD];/* Size of each log drv */ + u8 ldrv_prop[MAX_LOGICAL_DRIVES_40LD]; + u8 ldrv_state[MAX_LOGICAL_DRIVES_40LD];/* State of log drives */ + u8 pdrv_state[FC_MAX_PHYSICAL_DEVICES];/* State of phys drvs. */ + u16 pdrv_format[FC_MAX_PHYSICAL_DEVICES / 16]; + + u8 targ_xfer[80]; /* phys device transfer rate */ + u8 pad1k[263]; /* 761 + 263reserved = 1024 bytes total size */ +} __attribute__ ((packed)) mega_inquiry3; - mega_scb *qFreeH; - mega_scb *qFreeT; - spinlock_t lock_free; - - mega_scb *qPendingH; - mega_scb *qPendingT; - spinlock_t lock_pend; - - Scsi_Cmnd *qCompletedH; - Scsi_Cmnd *qCompletedT; - spinlock_t lock_scsicmd; - - u32 qFcnt; - u32 qPcnt; - u32 qCcnt; - - unsigned long nReads[FC_MAX_LOGICAL_DRIVES]; - unsigned long nReadBlocks[FC_MAX_LOGICAL_DRIVES]; - unsigned long nWrites[FC_MAX_LOGICAL_DRIVES]; - unsigned long nWriteBlocks[FC_MAX_LOGICAL_DRIVES]; - unsigned long nInterrupts; - /* Host adapter parameters */ - u8 fwVer[7]; - u8 biosVer[7]; - struct Scsi_Host *host; +/* Structures */ +typedef struct { + u8 max_commands; /* Max concurrent commands supported */ + u8 rebuild_rate; /* Rebuild rate - 0% thru 100% */ + u8 max_targ_per_chan; /* Max targ per channel */ + u8 nchannels; /* Number of channels on HBA */ + u8 fw_version[4]; /* Firmware version */ + u16 age_of_flash; /* Number of times FW has been flashed */ + u8 chip_set_value; /* Contents of 0xC0000832 */ + u8 dram_size; /* In MB */ + u8 cache_flush_interval; /* in seconds */ + u8 bios_version[4]; + u8 board_type; + u8 sense_alert; + u8 write_config_count; /* Increase with every configuration + change */ + u8 drive_inserted_count; /* Increase with every drive inserted + */ + u8 inserted_drive; /* Channel:Id of inserted drive */ + u8 battery_status; /* + * BIT 0: battery module missing + * BIT 1: VBAD + * BIT 2: temprature high + * BIT 3: battery pack missing + * BIT 4,5: + * 00 - charge complete + * 01 - fast charge in progress + * 10 - fast charge fail + * 11 - undefined + * Bit 6: counter > 1000 + * Bit 7: Undefined + */ + u8 dec_fault_bus_info; +} __attribute__ ((packed)) mega_adp_info; + - volatile mega_mailbox64 *mbox64; /* ptr to beginning of 64-bit mailbox */ - volatile mega_mailbox *mbox; /* ptr to beginning of standard mailbox */ +typedef struct { + u8 num_ldrv; /* Number of logical drives configured */ + u8 rsvd[3]; + u32 ldrv_size[MAX_LOGICAL_DRIVES_8LD]; + u8 ldrv_prop[MAX_LOGICAL_DRIVES_8LD]; + u8 ldrv_state[MAX_LOGICAL_DRIVES_8LD]; +} __attribute__ ((packed)) mega_ldrv_info; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) -/* ptr to beginning of standard mailbox */ - volatile mega_mailbox64 *mailbox64ptr; -#else - volatile mega_mailbox64 mailbox64; -#endif +typedef struct { + u8 pdrv_state[MAX_PHYSICAL_DRIVES]; + u8 rsvd; +} __attribute__ ((packed)) mega_pdrv_info; + +/* RAID inquiry: Mailbox command 0x05*/ +typedef struct { + mega_adp_info adapter_info; + mega_ldrv_info logdrv_info; + mega_pdrv_info pdrv_info; +} __attribute__ ((packed)) mraid_inquiry; + + +/* RAID extended inquiry: Mailbox command 0x04*/ +typedef struct { + mraid_inquiry raid_inq; + u16 phys_drv_format[MAX_CHANNELS]; + u8 stack_attn; + u8 modem_status; + u8 rsvd[2]; +} __attribute__ ((packed)) mraid_ext_inquiry; + + +typedef struct { + u8 channel; + u8 target; +}__attribute__ ((packed)) adp_device; + +typedef struct { + u32 start_blk; /* starting block */ + u32 num_blks; /* # of blocks */ + adp_device device[MAX_ROW_SIZE_40LD]; +}__attribute__ ((packed)) adp_span_40ld; + +typedef struct { + u32 start_blk; /* starting block */ + u32 num_blks; /* # of blocks */ + adp_device device[MAX_ROW_SIZE_8LD]; +}__attribute__ ((packed)) adp_span_8ld; + +typedef struct { + u8 span_depth; /* Total # of spans */ + u8 level; /* RAID level */ + u8 read_ahead; /* read ahead, no read ahead, adaptive read + ahead */ + u8 stripe_sz; /* Encoded stripe size */ + u8 status; /* Status of the logical drive */ + u8 write_mode; /* write mode, write_through/write_back */ + u8 direct_io; /* direct io or through cache */ + u8 row_size; /* Number of stripes in a row */ +} __attribute__ ((packed)) logdrv_param; + +typedef struct { + logdrv_param lparam; + adp_span_40ld span[MAX_SPAN_DEPTH]; +}__attribute__ ((packed)) logdrv_40ld; + +typedef struct { + logdrv_param lparam; + adp_span_8ld span[MAX_SPAN_DEPTH]; +}__attribute__ ((packed)) logdrv_8ld; + +typedef struct { + u8 type; /* Type of the device */ + u8 cur_status; /* current status of the device */ + u8 tag_depth; /* Level of tagging */ + u8 sync_neg; /* sync negotiation - ENABLE or DISBALE */ + u32 size; /* configurable size in terms of 512 byte + blocks */ +}__attribute__ ((packed)) phys_drv; - volatile u8 mega_buffer[2 * 1024L]; - volatile megaRaidProductInfo productInfo; +typedef struct { + u8 nlog_drives; /* number of logical drives */ + u8 resvd[3]; + logdrv_40ld ldrv[MAX_LOGICAL_DRIVES_40LD]; + phys_drv pdrv[MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_40ld; - u8 max_cmds; - mega_scb scbList[MAX_COMMANDS]; +typedef struct { + u8 nlog_drives; /* number of logical drives */ + u8 resvd[3]; + logdrv_8ld ldrv[MAX_LOGICAL_DRIVES_8LD]; + phys_drv pdrv[MAX_PHYSICAL_DRIVES]; +}__attribute__ ((packed)) disk_array_8ld; -#define PROCBUFSIZE 4096 - char procbuf[PROCBUFSIZE]; - int procidx; - struct proc_dir_entry *controller_proc_dir_entry; - struct proc_dir_entry *proc_read, *proc_stat, *proc_status, *proc_mbox; - int support_ext_cdb; - - u8 boot_ldrv_enabled; /* boot from logical drive */ - u8 boot_ldrv; /* boot logical drive */ - u8 boot_pdrv_enabled; /* boot from physical drive */ - u8 boot_pdrv_ch; /* boot physical drive channel */ - u8 boot_pdrv_tgt; /* boot physical drive target */ - - int support_random_del; /* Do we support random deletion of logdrvs */ - int read_ldidmap; /* set after logical drive deltion. The logical - drive number must be read from the map */ - int quiescent; /* a stage reached when delete logical drive needs to - be done. Stop sending requests to the hba till - delete operation is completed */ - - mega_scb *int_qh; /* commands are queued in the internal queue */ - mega_scb *int_qt; /* while the hba is quiescent */ - int int_qlen; - char logdrv_chan[MAX_CHANNEL+NVIRT_CHAN]; /* logical drive are on - what channels. */ - int mega_ch_class; -} mega_host_config; - -typedef struct _driver_info { - int size; - ulong version; -} mega_driver_info; /* * User ioctl structure. * This structure will be used for Traditional Method ioctl interface - * commands (M_RD_IOCTL_CMD),Alternate Buffer Method (M_RD_IOCTL_CMD_NEW) - * ioctl commands and the Driver ioctls(M_RD_DRIVER_IOCTL_INTERFACE). - * The Driver ioctl interface handles the commands at - * the driver level, without being sent to the card. + * commands (0x80),Alternate Buffer Method (0x81) ioctl commands and the + * Driver ioctls. + * The Driver ioctl interface handles the commands at the driver level, + * without being sent to the card. */ -#define MEGADEVIOC 0x84 - /* system call imposed limit. Change accordingly */ #define IOCTL_MAX_DATALEN 4096 -#pragma pack(1) struct uioctl_t { u32 inlen; u32 outlen; @@ -845,8 +539,8 @@ u8 *buffer; #endif u32 length; - } fcs; - } ui; + } __attribute__ ((packed)) fcs; + } __attribute__ ((packed)) ui; u8 mbox[18]; /* 16 bytes + 2 status bytes */ mega_passthru pthru; #if BITS_PER_LONG == 32 @@ -856,8 +550,7 @@ #if BITS_PER_LONG == 64 char *data; #endif -}; -#pragma pack() +} __attribute__ ((packed)); /* * struct mcontroller is used to pass information about the controllers in the @@ -881,25 +574,26 @@ u32 uid; }; -struct mbox_passthru { - u8 cmd; - u8 cmdid; - u16 pad1; - u32 pad2; - u32 dataxferaddr; - u8 pad3; - u8 pad4; - u8 rsvd; - u8 mboxbusy; - u8 nstatus; - u8 status; -}; +/* + * mailbox structure used for internal commands + */ +typedef struct { + u8 cmd; + u8 cmdid; + u8 opcode; + u8 subopcode; + u32 lba; + u32 xferaddr; + u8 logdrv; + u8 rsvd[3]; + u8 numstatus; + u8 status; +} __attribute__ ((packed)) megacmd_t; /* - * Defines for Driver IOCTL interface, Op-code:M_RD_DRIVER_IOCTL_INTERFACE + * Defines for Driver IOCTL interface */ #define MEGAIOC_MAGIC 'm' -#define MEGAIOCCMD _IOWR(MEGAIOC_MAGIC, 0) /* Mega IOCTL command */ #define MEGAIOC_QNADAP 'm' /* Query # of adapters */ #define MEGAIOC_QDRVRVER 'e' /* Query driver version */ @@ -907,126 +601,522 @@ #define MKADAP(adapno) (MEGAIOC_MAGIC << 8 | (adapno) ) #define GETADAP(mkadap) ( (mkadap) ^ MEGAIOC_MAGIC << 8 ) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /*0x20300 */ -extern struct proc_dir_entry proc_scsi_megaraid; -#endif +/* + * Definition for the new ioctl interface (NIT) + */ -/* For Host Re-Ordering */ -#define MAX_CONTROLLERS 32 +/* + * Vendor specific Group-7 commands + */ +#define VENDOR_SPECIFIC_COMMANDS 0xE0 +#define MEGA_INTERNAL_CMD VENDOR_SPECIFIC_COMMANDS + 0x01 -struct mega_hbas { - int is_bios_enabled; - mega_host_config *hostdata_addr; -}; +/* + * The ioctl command. No other command shall be used for this interface + */ +#define USCSICMD VENDOR_SPECIFIC_COMMANDS -#define IS_BIOS_ENABLED 0x62 -#define GET_BIOS 0x01 -#define CHNL_CLASS 0xA9 -#define GET_CHNL_CLASS 0x00 -#define SET_CHNL_CLASS 0x01 -#define CH_RAID 0x01 -#define CH_SCSI 0x00 +/* + * Data direction flags + */ +#define UIOC_RD 0x00001 +#define UIOC_WR 0x00002 +/* + * ioctl opcodes + */ +#define MBOX_CMD 0x00000 /* DCMD or passthru command */ +#define GET_DRIVER_VER 0x10000 /* Get driver version */ +#define GET_N_ADAP 0x20000 /* Get number of adapters */ +#define GET_ADAP_INFO 0x30000 /* Get information about a adapter */ +#define GET_CAP 0x40000 /* Get ioctl capabilities */ +#define GET_STATS 0x50000 /* Get statistics, including error info */ + + +/* + * The ioctl structure. + * MBOX macro converts a nitioctl_t structure to megacmd_t pointer and + * MBOX_P macro converts a nitioctl_t pointer to megacmd_t pointer. + */ +typedef struct { + char signature[8]; /* Must contain "MEGANIT" */ + u32 opcode; /* opcode for the command */ + u32 adapno; /* adapter number */ + union { + u8 __raw_mbox[18]; + caddr_t __uaddr; /* xferaddr for non-mbox cmds */ + }__ua; + +#define uioc_rmbox __ua.__raw_mbox +#define MBOX(uioc) ((megacmd_t *)&((uioc).__ua.__raw_mbox[0])) +#define MBOX_P(uioc) ((megacmd_t *)&((uioc)->__ua.__raw_mbox[0])) +#define uioc_uaddr __ua.__uaddr + + u32 xferlen; /* xferlen for DCMD and non-mbox + commands */ + u32 flags; /* data direction flags */ +}nitioctl_t; + + +/* + * I/O statistics for some applications like SNMP agent. The caller must + * provide the number of logical drives for which status should be reported. + */ +typedef struct { + int num_ldrv; /* Number for logical drives for which the + status should be reported. */ + u32 nreads[MAX_LOGICAL_DRIVES_40LD]; /* number of reads for + each logical drive */ + u32 nreadblocks[MAX_LOGICAL_DRIVES_40LD]; /* number of blocks + read for each logical + drive */ + u32 nwrites[MAX_LOGICAL_DRIVES_40LD]; /* number of writes + for each logical + drive */ + u32 nwriteblocks[MAX_LOGICAL_DRIVES_40LD]; /* number of blocks + writes for each + logical drive */ + u32 rd_errors[MAX_LOGICAL_DRIVES_40LD]; /* number of read + errors for each + logical drive */ + u32 wr_errors[MAX_LOGICAL_DRIVES_40LD]; /* number of write + errors for each + logical drive */ +}megastat_t; -#define BIOS_PVT_DATA 0x40 -#define GET_BIOS_PVT_DATA 0x00 -#pragma pack(1) struct private_bios_data { - u8 geometry:4; /* - * bits 0-3 - BIOS geometry - * 0x0001 - 1GB - * 0x0010 - 2GB - * 0x1000 - 8GB - * Others values are invalid - */ - u8 unused:4; /* bits 4-7 are unused */ - u8 boot_drv; /* - * logical/physical drive set as boot drive - * 0..7 - for 8LD cards - * 0..39 - for 40LD cards + u8 geometry:4; /* + * bits 0-3 - BIOS geometry + * 0x0001 - 1GB + * 0x0010 - 2GB + * 0x1000 - 8GB + * Others values are invalid */ - u8 rsvd[12]; - u16 cksum; /* 0-(sum of first 13 bytes of this structure) */ -}; -#pragma pack() + u8 unused:4; /* bits 4-7 are unused */ + u8 boot_drv; /* + * logical drive set as boot drive + * 0..7 - for 8LD cards + * 0..39 - for 40LD cards + */ + u8 rsvd[12]; + u16 cksum; /* 0-(sum of first 13 bytes of this structure) */ +} __attribute__ ((packed)); + + + + +/* + * Mailbox and firmware commands and subopcodes used in this driver. + */ + +#define MEGA_MBOXCMD_LREAD 0x01 +#define MEGA_MBOXCMD_LWRITE 0x02 +#define MEGA_MBOXCMD_PASSTHRU 0x03 +#define MEGA_MBOXCMD_ADPEXTINQ 0x04 +#define MEGA_MBOXCMD_ADAPTERINQ 0x05 +#define MEGA_MBOXCMD_LREAD64 0xA7 +#define MEGA_MBOXCMD_LWRITE64 0xA8 +#define MEGA_MBOXCMD_PASSTHRU64 0xC3 +#define MEGA_MBOXCMD_EXTPTHRU 0xE3 + +#define MAIN_MISC_OPCODE 0xA4 /* f/w misc opcode */ +#define GET_MAX_SG_SUPPORT 0x01 /* get max sg len supported by f/w */ + +#define FC_NEW_CONFIG 0xA1 +#define NC_SUBOP_PRODUCT_INFO 0x0E +#define NC_SUBOP_ENQUIRY3 0x0F +#define ENQ3_GET_SOLICITED_FULL 0x02 +#define OP_DCMD_READ_CONFIG 0x04 +#define NEW_READ_CONFIG_8LD 0x67 +#define READ_CONFIG_8LD 0x07 +#define FLUSH_ADAPTER 0x0A +#define FLUSH_SYSTEM 0xFE /* * Command for random deletion of logical drives */ #define FC_DEL_LOGDRV 0xA4 /* f/w command */ #define OP_SUP_DEL_LOGDRV 0x2A /* is feature supported */ -#define OP_GET_LDID_MAP 0x18 /* get logdrv id and logdrv number map */ +#define OP_GET_LDID_MAP 0x18 /* get ldid and logdrv number map */ #define OP_DEL_LOGDRV 0x1C /* delete logical drive */ -/*================================================================ - * - * Function prototypes - * - *================================================================ +/* + * BIOS commands + */ +#define IS_BIOS_ENABLED 0x62 +#define GET_BIOS 0x01 +#define CHNL_CLASS 0xA9 +#define GET_CHNL_CLASS 0x00 +#define SET_CHNL_CLASS 0x01 +#define CH_RAID 0x01 +#define CH_SCSI 0x00 +#define BIOS_PVT_DATA 0x40 +#define GET_BIOS_PVT_DATA 0x00 + + +/* + * Commands to support clustering + */ +#define MEGA_GET_TARGET_ID 0x7D +#define MEGA_CLUSTER_OP 0x70 +#define MEGA_GET_CLUSTER_MODE 0x02 +#define MEGA_CLUSTER_CMD 0x6E +#define MEGA_RESERVE_LD 0x01 +#define MEGA_RELEASE_LD 0x02 +#define MEGA_RESET_RESERVATIONS 0x03 +#define MEGA_RESERVATION_STATUS 0x04 +#define MEGA_RESERVE_PD 0x05 +#define MEGA_RELEASE_PD 0x06 + + +/* + * Module battery status + */ +#define MEGA_BATT_MODULE_MISSING 0x01 +#define MEGA_BATT_LOW_VOLTAGE 0x02 +#define MEGA_BATT_TEMP_HIGH 0x04 +#define MEGA_BATT_PACK_MISSING 0x08 +#define MEGA_BATT_CHARGE_MASK 0x30 +#define MEGA_BATT_CHARGE_DONE 0x00 +#define MEGA_BATT_CHARGE_INPROG 0x10 +#define MEGA_BATT_CHARGE_FAIL 0x20 +#define MEGA_BATT_CYCLES_EXCEEDED 0x40 + +/* + * Physical drive states. + */ +#define PDRV_UNCNF 0 +#define PDRV_ONLINE 3 +#define PDRV_FAILED 4 +#define PDRV_RBLD 5 +#define PDRV_HOTSPARE 6 + + +/* + * Raid logical drive states. + */ +#define RDRV_OFFLINE 0 +#define RDRV_DEGRADED 1 +#define RDRV_OPTIMAL 2 +#define RDRV_DELETED 3 + +/* + * Read, write and cache policies + */ +#define NO_READ_AHEAD 0 +#define READ_AHEAD 1 +#define ADAP_READ_AHEAD 2 +#define WRMODE_WRITE_THRU 0 +#define WRMODE_WRITE_BACK 1 +#define CACHED_IO 0 +#define DIRECT_IO 1 + + +/* + * Each controller's soft state + */ +typedef struct { + int this_id; /* our id, may set to different than 7 if + clustering is available */ + u32 flag; + + unsigned long base; + + /* mbox64 with mbox not aligned on 16-byte boundry */ + mbox64_t *una_mbox64; + dma_addr_t una_mbox64_dma; + + volatile mbox64_t *mbox64;/* ptr to 64-bit mailbox */ + volatile mbox_t *mbox; /* ptr to standard mailbox */ + dma_addr_t mbox_dma; + + struct pci_dev *dev; + struct pci_dev *ipdev; /* for internal allocation */ + + struct list_head free_list; + struct list_head pending_list; + + struct Scsi_Host *host; + +#define MEGA_BUFFER_SIZE (2*1024) + u8 *mega_buffer; + dma_addr_t buf_dma_handle; + + mega_product_info product_info; + + u8 max_cmds; + scb_t *scb_list; + + atomic_t pend_cmds; /* maintain a counter for pending + commands in firmware */ + +#if MEGA_HAVE_STATS + u32 nreads[MAX_LOGICAL_DRIVES_40LD]; + u32 nreadblocks[MAX_LOGICAL_DRIVES_40LD]; + u32 nwrites[MAX_LOGICAL_DRIVES_40LD]; + u32 nwriteblocks[MAX_LOGICAL_DRIVES_40LD]; + u32 rd_errors[MAX_LOGICAL_DRIVES_40LD]; + u32 wr_errors[MAX_LOGICAL_DRIVES_40LD]; +#endif + + /* Host adapter parameters */ + u8 numldrv; + u8 fw_version[7]; + u8 bios_version[7]; + +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *controller_proc_dir_entry; + struct proc_dir_entry *proc_read; + struct proc_dir_entry *proc_stat; + struct proc_dir_entry *proc_mbox; + +#if MEGA_HAVE_ENH_PROC + struct proc_dir_entry *proc_rr; + struct proc_dir_entry *proc_battery; +#define MAX_PROC_CHANNELS 4 + struct proc_dir_entry *proc_pdrvstat[MAX_PROC_CHANNELS]; + struct proc_dir_entry *proc_rdrvstat[MAX_PROC_CHANNELS]; +#endif + +#endif + + int has_64bit_addr; /* are we using 64-bit addressing */ + int support_ext_cdb; + int boot_ldrv_enabled; + int boot_ldrv; + int boot_pdrv_enabled; /* boot from physical drive */ + int boot_pdrv_ch; /* boot physical drive channel */ + int boot_pdrv_tgt; /* boot physical drive target */ + + + int support_random_del; /* Do we support random deletion of + logdrvs */ + int read_ldidmap; /* set after logical drive deltion. The + logical drive number must be read from the + map */ + atomic_t quiescent; /* a stage reached when delete logical + drive needs to be done. Stop + sending requests to the hba till + delete operation is completed */ + spinlock_t lock; + + u8 logdrv_chan[MAX_CHANNELS+NVIRT_CHAN]; /* logical drive are on + what channels. */ + int mega_ch_class; + + u8 sglen; /* f/w supported scatter-gather list length */ + + scb_t int_scb; + Scsi_Cmnd int_scmd; + struct semaphore int_mtx; /* To synchronize the internal + commands */ + wait_queue_head_t int_waitq; /* wait queue for internal + cmds */ + + int has_cluster; /* cluster support on this HBA */ +}adapter_t; + + +struct mega_hbas { + int is_bios_enabled; + adapter_t *hostdata_addr; +}; + + +/* + * For state flag. Do not use LSB(8 bits) which are + * reserved for storing info about channels. */ +#define IN_ABORT 0x80000000L +#define IN_RESET 0x40000000L +#define BOARD_MEMMAP 0x20000000L +#define BOARD_IOMAP 0x10000000L +#define BOARD_40LD 0x08000000L +#define BOARD_64BIT 0x04000000L + +#define INTR_VALID 0x40 + +#define PCI_CONF_AMISIG 0xa0 +#define PCI_CONF_AMISIG64 0xa4 + + +#define MEGA_DMA_TYPE_NONE 0xFFFF +#define MEGA_BULK_DATA 0x0001 +#define MEGA_SGLIST 0x0002 + +/* + * lockscope definitions, callers can specify the lock scope with this data + * type. LOCK_INT would mean the caller has not acquired the lock before + * making the call and LOCK_EXT would mean otherwise. + */ +typedef enum { LOCK_INT, LOCK_EXT } lockscope_t; + +/* + * Parameters for the io-mapped controllers + */ + +/* I/O Port offsets */ +#define CMD_PORT 0x00 +#define ACK_PORT 0x00 +#define TOGGLE_PORT 0x01 +#define INTR_PORT 0x0a + +#define MBOX_BUSY_PORT 0x00 +#define MBOX_PORT0 0x04 +#define MBOX_PORT1 0x05 +#define MBOX_PORT2 0x06 +#define MBOX_PORT3 0x07 +#define ENABLE_MBOX_REGION 0x0B + +/* I/O Port Values */ +#define ISSUE_BYTE 0x10 +#define ACK_BYTE 0x08 +#define ENABLE_INTR_BYTE 0xc0 +#define DISABLE_INTR_BYTE 0x00 +#define VALID_INTR_BYTE 0x40 +#define MBOX_BUSY_BYTE 0x10 +#define ENABLE_MBOX_BYTE 0x00 + + +/* Setup some port macros here */ +#define issue_command(adapter) \ + outb_p(ISSUE_BYTE, (adapter)->base + CMD_PORT) + +#define irq_state(adapter) inb_p((adapter)->base + INTR_PORT) + +#define set_irq_state(adapter, value) \ + outb_p((value), (adapter)->base + INTR_PORT) + +#define irq_ack(adapter) \ + outb_p(ACK_BYTE, (adapter)->base + ACK_PORT) + +#define irq_enable(adapter) \ + outb_p(ENABLE_INTR_BYTE, (adapter)->base + TOGGLE_PORT) + +#define irq_disable(adapter) \ + outb_p(DISABLE_INTR_BYTE, (adapter)->base + TOGGLE_PORT) + + +/* + * This is our SYSDEP area. All kernel specific detail should be placed here - + * as much as possible + */ + +/* + * End of SYSDEP area + */ + +/* + * ASSERT macro for megaraid. This should panic but printk should do for now + */ +#ifdef DEBUG +#define ASSERT( expression ) \ + if( !(expression) ) { \ + panic("assertion failed: %s, file: %s, line: %d\n", \ + #expression, __FILE__, __LINE__); \ + } +#else +#define ASSERT(expression) +#endif + +#define MBOX_ABORT_SLEEP 60 +#define MBOX_RESET_SLEEP 30 + const char *megaraid_info (struct Scsi_Host *); -int megaraid_detect (Scsi_Host_Template *); -int megaraid_release (struct Scsi_Host *); -int megaraid_command (Scsi_Cmnd *); -int megaraid_abort (Scsi_Cmnd *); -int megaraid_reset (Scsi_Cmnd *, unsigned int); -int megaraid_queue (Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -int megaraid_biosparam (Disk *, kdev_t, int *); -int megaraid_proc_info (char *buffer, char **start, off_t offset, - int length, int hostno, int inout); - -static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData, - mega_scb * scb, int intr); -static int mega_build_sglist (mega_host_config * megaCfg, mega_scb * scb, - u32 * buffer, u32 * length); -static int mega_busyWaitMbox (mega_host_config *); -static int mega_runpendq (mega_host_config *); -static void mega_rundoneq (mega_host_config *); -static void mega_cmd_done (mega_host_config *, mega_scb *, int); -static inline void mega_freeSgList (mega_host_config * megaCfg); -static void mega_Convert8ldTo40ld (mega_RAIDINQ * inquiry, - mega_Enquiry3 * enquiry3, - megaRaidProductInfo * productInfo); + +static int megaraid_detect(Scsi_Host_Template *); +static void mega_find_card(Scsi_Host_Template *, u16, u16); +static int mega_query_adapter(adapter_t *); +static inline int issue_scb(adapter_t *, scb_t *); +static int mega_setup_mailbox(adapter_t *); + +static int megaraid_queue (Scsi_Cmnd *, void (*)(Scsi_Cmnd *)); +static scb_t * mega_build_cmd(adapter_t *, Scsi_Cmnd *, int *); +static inline scb_t *mega_allocate_scb(adapter_t *, Scsi_Cmnd *); +static void __mega_runpendq(adapter_t *); +static inline void mega_runpendq(adapter_t *); +static int issue_scb_block(adapter_t *, u_char *); + +static void megaraid_isr_memmapped(int, void *, struct pt_regs *); +static inline void megaraid_memmbox_ack_sequence(adapter_t *); +static void megaraid_isr_iomapped(int, void *, struct pt_regs *); +static inline void megaraid_iombox_ack_sequence(adapter_t *); + +static void mega_free_scb(adapter_t *, scb_t *); + +static int megaraid_release (struct Scsi_Host *); +static int megaraid_command (Scsi_Cmnd *); +static int megaraid_abort(Scsi_Cmnd *); +static int megaraid_reset(Scsi_Cmnd *); +static int megaraid_biosparam (Disk *, kdev_t, int *); +static int mega_print_inquiry(char *, char *); + +static int mega_build_sglist (adapter_t *adapter, scb_t *scb, + u32 *buffer, u32 *length); +static inline int mega_busywait_mbox (adapter_t *); +static int __mega_busywait_mbox (adapter_t *); +static inline void mega_cmd_done(adapter_t *, u8 [], int, int); +static inline void mega_free_sgl (adapter_t *adapter); +static void mega_8_to_40ld (mraid_inquiry *inquiry, + mega_inquiry3 *enquiry3, mega_product_info *); static int megaraid_reboot_notify (struct notifier_block *, unsigned long, void *); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -static mega_scb *mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt); -static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, - mega_ioctl_mbox * mbox); -#endif - static int megadev_open (struct inode *, struct file *); -static int megadev_ioctl_entry (struct inode *, struct file *, - unsigned int, unsigned long); -static int megadev_ioctl (struct inode *, struct file *, - unsigned int, unsigned long); -static mega_scb *megadev_doioctl (mega_host_config *, Scsi_Cmnd *); +static int megadev_ioctl (struct inode *, struct file *, unsigned int, + unsigned long); +static int mega_m_to_n(void *, nitioctl_t *); +static int mega_n_to_m(void *, megacmd_t *); static int megadev_close (struct inode *, struct file *); -static void megadev_ioctl_done (Scsi_Cmnd *); -static int mega_init_scb (mega_host_config *); -static void enq_scb_freelist (mega_host_config *, mega_scb *, - int lock, int intr); -static int mega_is_bios_enabled (mega_host_config *); +static int mega_init_scb (adapter_t *); + +static int mega_is_bios_enabled (adapter_t *); static void mega_reorder_hosts (void); static void mega_swap_hosts (struct Scsi_Host *, struct Scsi_Host *); -static void mega_create_proc_entry (int index, struct proc_dir_entry *); -static int mega_support_ext_cdb(mega_host_config *); -static mega_passthru* mega_prepare_passthru(mega_host_config *, mega_scb *, +#ifdef CONFIG_PROC_FS +static void mega_create_proc_entry(int, struct proc_dir_entry *); +static int proc_read_config(char *, char **, off_t, int, int *, void *); +static int proc_read_stat(char *, char **, off_t, int, int *, void *); +static int proc_read_mbox(char *, char **, off_t, int, int *, void *); +static int proc_rebuild_rate(char *, char **, off_t, int, int *, void *); +static int proc_battery(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch0(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch1(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch2(char *, char **, off_t, int, int *, void *); +static int proc_pdrv_ch3(char *, char **, off_t, int, int *, void *); +static int proc_pdrv(adapter_t *, char *, int); +static int proc_rdrv_10(char *, char **, off_t, int, int *, void *); +static int proc_rdrv_20(char *, char **, off_t, int, int *, void *); +static int proc_rdrv_30(char *, char **, off_t, int, int *, void *); +static int proc_rdrv_40(char *, char **, off_t, int, int *, void *); +static int proc_rdrv(adapter_t *, char *, int, int); +#endif + +static int mega_adapinq(adapter_t *, dma_addr_t); +static int mega_internal_dev_inquiry(adapter_t *, u8, u8, dma_addr_t); +static inline caddr_t mega_allocate_inquiry(dma_addr_t *, struct pci_dev *); +static inline void mega_free_inquiry(caddr_t, dma_addr_t, struct pci_dev *); + +static int mega_support_ext_cdb(adapter_t *); +static mega_passthru* mega_prepare_passthru(adapter_t *, scb_t *, Scsi_Cmnd *, int, int); -static mega_ext_passthru* mega_prepare_extpassthru(mega_host_config *, - mega_scb *, Scsi_Cmnd *, int, int); -static void mega_enum_raid_scsi(mega_host_config *); +static mega_ext_passthru* mega_prepare_extpassthru(adapter_t *, + scb_t *, Scsi_Cmnd *, int, int); +static void mega_enum_raid_scsi(adapter_t *); static int mega_partsize(Disk *, kdev_t, int *); -static void mega_get_boot_drv(mega_host_config *); -static int mega_get_ldrv_num(mega_host_config *, Scsi_Cmnd *, int); -static int mega_support_random_del(mega_host_config *); -static int mega_del_logdrv(mega_host_config *, int); -static int mega_do_del_logdrv(mega_host_config *, int); - +static void mega_get_boot_drv(adapter_t *); +static inline int mega_get_ldrv_num(adapter_t *, Scsi_Cmnd *, int); +static int mega_support_random_del(adapter_t *); +static int mega_del_logdrv(adapter_t *, int); +static int mega_do_del_logdrv(adapter_t *, int); +static void mega_get_max_sgl(adapter_t *); +static int mega_internal_command(adapter_t *, lockscope_t, megacmd_t *, + mega_passthru *); +static void mega_internal_done(Scsi_Cmnd *); +static int mega_support_cluster(adapter_t *); #endif -/* vi: set ts=4: */ +/* vi: set ts=8 sw=8 tw=78: */