blob: 6e6cf37b63f2dab657ac37ef57f80103d2810958 [file] [log] [blame]
/*===================================================================
*
* Linux MegaRAID device driver
*
* Copyright 2001 American Megatrends Inc.
*
* 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.18 (Oct 11, 2001)
*
* 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 <alan.cox@linux.org>
*
* Merged with 2.1.131 source tree.
* 12 Dec 98 K. Baranowski <kgb@knm.org.pl>
*
* 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 it's 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 <atulm@lsil.com>
*
* 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" <johnsom@home.com>
*
* Make the HP print formatting and check for buggy firmware runtime not
* ifdef dependent.
*
*
* Version 1.17d
* Thu Oct 11 10:48:45 EDT 2001 - Atul Mukker <atulm@lsil.com>
*
* 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 <atulm@lsil.com>
*
* References to AMI have been changed to LSI Logic.
*
*
* 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.
*
* 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.
*
* 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
*
* Far too many ifdefs for versions.
*
*===================================================================*/
#include <linux/config.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/proc_fs.h>
#include <linux/blk.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/pgtable.h>
#include <linux/sched.h>
#include <linux/stat.h>
#include <linux/slab.h> /* for kmalloc() */
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) /* 0x20100 */
#include <linux/bios32.h>
#else
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) /* 0x20300 */
#include <asm/spinlock.h>
#else
#include <linux/spinlock.h>
#endif
#endif
#include <asm/io.h>
#include <asm/irq.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,0,24) /* 0x020024 */
#include <asm/uaccess.h>
#endif
/*
* These header files are required for Shutdown Notification routines
*/
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include "scsi.h"
#include "hosts.h"
#include <scsi/scsicam.h>
#include "megaraid.h"
/*
*================================================================
* #Defines
*================================================================
*/
#define MAX_SERBUF 160
#define COM_BASE 0x2f8
static ulong RDINDOOR (mega_host_config * megaCfg)
{
return readl (megaCfg->base + 0x20);
}
static void WRINDOOR (mega_host_config * megaCfg, ulong value)
{
writel (value, megaCfg->base + 0x20);
}
static ulong RDOUTDOOR (mega_host_config * megaCfg)
{
return readl (megaCfg->base + 0x2C);
}
static void WROUTDOOR (mega_host_config * megaCfg, ulong value)
{
writel (value, megaCfg->base + 0x2C);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) /* 0x020200 */
#include <linux/smp.h>
#define cpuid smp_processor_id()
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
#define scsi_set_pci_device(x,y)
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) /* 0x020400 */
/*
* Linux 2.4 and higher
*
* No driver private lock
* Use the io_request_lock not cli/sti
* queue task is a simple api without irq forms
*/
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(host) spin_lock_irqsave(host->host_lock,io_flags)
#define IO_UNLOCK(host) spin_unlock_irqrestore(host->host_lock,io_flags)
#define IO_LOCK_IRQ(host) spin_lock_irq(host->host_lock)
#define IO_UNLOCK_IRQ(host) spin_unlock_irq(host->host_lock)
#define queue_task_irq(a,b) queue_task(a,b)
#define queue_task_irq_off(a,b) queue_task(a,b)
#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
*/
static char kernel_version[] = UTS_RELEASE;
MODULE_AUTHOR ("LSI Logic Corporation");
MODULE_DESCRIPTION ("LSI Logic MegaRAID driver");
#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(host) spin_lock_irqsave(host->host_lock,io_flags);
#define IO_UNLOCK(host) spin_unlock_irqrestore(host->host_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
#endif
#else
/*
* 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)
#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
*/
#define ioremap vremap
#define iounmap vfree
/* simulate spin locks */
typedef struct {
volatile char lock;
} spinlock_t;
#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);}
#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
#endif
#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
/* 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
#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
*================================================================
*/
/* 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;
#if DEBUG
static u32 maxCmdTime = 0;
#endif
static mega_scb *pLastScb = NULL;
static struct notifier_block mega_notifier = {
megaraid_reboot_notify,
NULL,
0
};
/* For controller re-ordering */
struct mega_hbas mega_hbas[MAX_CONTROLLERS];
/*
* The File Operations structure for the serial/ioctl interface of the driver
*/
/* For controller re-ordering */
static struct file_operations megadev_fops = {
.owner = THIS_MODULE,
.ioctl = megadev_ioctl_entry,
};
/*
* 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];
/* The current driver version */
static u32 driver_ver = 114;
/* major number used by the device for character interface */
static int major;
static struct semaphore mimd_ioctl_sem;
static struct semaphore mimd_entry_mtx;
#if SERDEBUG
volatile static spinlock_t serial_lock;
#endif
#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
#ifdef CONFIG_PROC_FS
extern struct proc_dir_entry proc_root;
#endif
static char mega_ch_class; /* channels are raid or scsi */
#define IS_RAID_CH(ch) ( (mega_ch_class >> (ch)) & 0x01 )
#if SERDEBUG
static char strbuf[MAX_SERBUF + 1];
static void ser_init (void)
{
unsigned port = COM_BASE;
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);
}
static void ser_puts (char *str)
{
char *ptr;
ser_init ();
for (ptr = str; *ptr; ++ptr)
ser_putc (*ptr);
}
static void ser_putc (char c)
{
unsigned port = COM_BASE;
while ((inb (port + 5) & 0x20) == 0) ;
outb (c, port);
if (c == 0x0a) {
while ((inb (port + 5) & 0x20) == 0) ;
outb (0x0d, port);
}
}
static int ser_printk (const char *fmt, ...)
{
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
#define TRACE1(a)
static void callDone (Scsi_Cmnd * SCpnt)
{
if (SCpnt->result) {
TRACE (("*** %.08lx %.02x <%d.%d.%d> = %x\n",
SCpnt->serial_number, SCpnt->cmnd[0], SCpnt->device->channel,
SCpnt->device->id, SCpnt->device->lun, SCpnt->result));
}
SCpnt->scsi_done (SCpnt);
}
/*-------------------------------------------------------------------------
*
* Local functions
*
*-------------------------------------------------------------------------*/
/*=======================
* Free a SCB structure
*=======================
*/
static void mega_freeSCB (mega_host_config * megaCfg, mega_scb * pScb)
{
mega_scb *pScbtmp;
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);
}
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;
}
#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--;
} else {
for (pScbtmp = megaCfg->qPendingH; pScbtmp;
pScbtmp = pScbtmp->next) {
if (pScbtmp->next == pScb) {
pScbtmp->next = pScb->next;
if (pScb == megaCfg->qPendingT) {
megaCfg->qPendingT = pScbtmp;
}
megaCfg->qPcnt--;
break;
}
}
}
/* Link back into free list */
pScb->state = SCB_FREE;
pScb->SCpnt = NULL;
if (megaCfg->qFreeH == (mega_scb *) NULL) {
megaCfg->qFreeH = megaCfg->qFreeT = pScb;
} else {
megaCfg->qFreeT->next = pScb;
megaCfg->qFreeT = pScb;
}
megaCfg->qFreeT->next = NULL;
megaCfg->qFcnt++;
}
/*===========================
* Allocate a SCB structure
*===========================
*/
static mega_scb *mega_allocateSCB (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt)
{
mega_scb *pScb;
/* 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;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
pScb->dma_type = M_RD_DMA_TYPE_NONE;
#endif
return pScb;
}
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;
while ((SCpnt = megaCfg->qCompletedH) != NULL) {
megaCfg->qCompletedH = (Scsi_Cmnd *) SCpnt->host_scribble;
megaCfg->qCcnt--;
SCpnt->host_scribble = (unsigned char *) NULL; /* XC : sep 14 */
/* Callback */
callDone (SCpnt);
}
megaCfg->qCompletedH = megaCfg->qCompletedT = NULL;
}
/*
* Runs through the list of pending requests
* Assumes that mega_lock spin_lock has been acquired.
*/
static int mega_runpendq (mega_host_config * megaCfg)
{
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;
}
/* Add command to the list of completed requests */
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;
if (pScb == NULL) {
TRACE (("NULL pScb in mega_cmd_done!"));
printk(KERN_CRIT "NULL pScb in mega_cmd_done!");
}
SCpnt = pScb->SCpnt;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
pthru = pScb->pthru;
epthru = pScb->epthru;
#else
pthru = &pScb->pthru;
epthru = &pScb->epthru;
#endif
mbox = (mega_mailbox *) & pScb->mboxData;
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");
}
islogical = ( (SCpnt->device->channel >= megaCfg->productInfo.SCSIChanPresent) &&
(SCpnt->device->channel <= megaCfg->host->max_channel) );
#if 0
islogical = (SCpnt->device->channel == megaCfg->host->max_channel);
#endif
#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;
}
#endif
mega_freeSCB (megaCfg, pScb);
/*
* 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;
}
#endif
if (SCpnt->cmnd[0] == INQUIRY && !islogical) {
if ( SCpnt->use_sg ) {
sgList = (struct scatterlist *)SCpnt->request_buffer;
memcpy(&c, cpu_to_le32(sg_dma_address(&sgList[0])), 0x1);
} else {
memcpy(&c, SCpnt->request_buffer, 0x1);
}
#if 0
if( (c & 0x1F ) == TYPE_DISK ) {
status = 0xF0;
}
#endif
if( IS_RAID_CH(SCpnt->device->channel) && ((c & 0x1F ) == TYPE_DISK) ) {
status = 0xF0;
}
}
/* clear result; otherwise, success returns corrupt value */
SCpnt->result = 0;
if ((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 */
/*set sense_buffer and result fields */
if (mbox->cmd == MEGA_MBOXCMD_PASSTHRU) {
memcpy (SCpnt->sense_buffer, pthru->reqsensearea, 14);
} else if (mbox->cmd == MEGA_MBOXCMD_EXTPASSTHRU) {
SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION < 1);
memcpy(
SCpnt->sense_buffer,
epthru->reqsensearea, 14
);
SCpnt->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION < 1);
/*SCpnt->result =
(DRIVER_SENSE << 24) |
(DID_ERROR << 16) | status;*/
} 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;
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;
}
megaCfg->qCompletedT->host_scribble = (unsigned char *) NULL;
megaCfg->qCcnt++;
}
/*-------------------------------------------------------------------
*
* 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)
{
mega_scb *pScb;
mega_mailbox *mbox;
mega_passthru *pthru;
mega_ext_passthru *epthru;
long seg;
char islogical;
int lun = SCpnt->device->lun;
int max_lun;
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
islogical = ( (SCpnt->device->channel >= megaCfg->productInfo.SCSIChanPresent) &&
(SCpnt->device->channel <= megaCfg->host->max_channel) );
#if 0
islogical = (IS_RAID_CH(SCpnt->device->channel) && /* virtual ch is raid - AM */
(SCpnt->device->channel == megaCfg->host->max_channel));
#endif
if ( ! megaCfg->support_ext_cdb ) {
if (!islogical && lun != 0) {
SCpnt->result = (DID_BAD_TARGET << 16);
callDone (SCpnt);
return NULL;
}
}
if (!islogical && SCpnt->device->id == skip_id) {
SCpnt->result = (DID_BAD_TARGET << 16);
callDone (SCpnt);
return NULL;
}
if (islogical) {
/* have just LUN 0 for each target on virtual channels */
if( SCpnt->device->lun != 0 ) {
SCpnt->result = (DID_BAD_TARGET << 16);
callDone (SCpnt);
return NULL;
}
lun = mega_get_lun(megaCfg, SCpnt);
max_lun = (megaCfg->flag & BOARD_40LD) ?
FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES;
/*
* max_lun increases by 0x80 if some logical drive was deleted.
*/
if(megaCfg->read_ldidmap) {
max_lun += 0x80;
}
if( lun > max_lun ) {
SCpnt->result = (DID_BAD_TARGET << 16);
callDone (SCpnt);
return NULL;
}
/*
* If we have a logical drive with boot enabled, project it first
*/
if( megaCfg->boot_ldrv_enabled ) {
if( lun == 0 ) {
lun = megaCfg->boot_ldrv;
}
else {
if( lun <= megaCfg->boot_ldrv ) {
lun--;
}
}
}
} else {
if ( 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:
/* 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
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 = lun;
pthru->cdblen = SCpnt->cmd_len;
#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;
#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);
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
#else
pthru->dataxferaddr =
virt_to_bus (SCpnt->request_buffer);
#endif
pthru->dataxferlen = SCpnt->request_bufflen;
memcpy (pthru->cdb, SCpnt->cmnd, SCpnt->cmd_len);
/* Initialize mailbox area */
mbox->cmd = MEGA_MBOXCMD_PASSTHRU;
#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;
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;
memset (mbox, 0, sizeof (pScb->mboxData));
mbox->logdrv = 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;
if (*SCpnt->cmnd == READ_6) {
megaCfg->nReads[(int) lun]++;
megaCfg->nReadBlocks[(int) lun] +=
mbox->numsectors;
} else {
megaCfg->nWrites[(int) lun]++;
megaCfg->nWriteBlocks[(int) lun] +=
mbox->numsectors;
}
}
/* 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) lun]++;
megaCfg->nReadBlocks[(int) lun] +=
mbox->numsectors;
} else {
megaCfg->nWrites[(int) lun]++;
megaCfg->nWriteBlocks[(int) lun] +=
mbox->numsectors;
}
}
/* 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];
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) lun]++;
megaCfg->nReadBlocks[(int) lun] +=
mbox->numsectors;
} else {
megaCfg->nWrites[(int) lun]++;
megaCfg->nWriteBlocks[(int) lun] +=
mbox->numsectors;
}
}
#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
/* Calculate Scatter-Gather info */
mbox->numsgelements = mega_build_sglist (megaCfg, pScb,
(u32 *)&mbox->xferaddr, (u32 *)&seg);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
pScb->iDataSize = seg;
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
return pScb;
default:
SCpnt->result = (DID_BAD_TARGET << 16);
callDone (SCpnt);
return NULL;
}
}
/*-----------------------------------------------------
*
* 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;
}
mbox = (mega_mailbox *) pScb->mboxData;
memset (mbox, 0, sizeof (pScb->mboxData));
if ( megaCfg->support_ext_cdb && SCpnt->cmd_len > 10 ) {
epthru = mega_prepare_extpassthru(megaCfg, pScb, SCpnt);
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
}
else {
pthru = mega_prepare_passthru(megaCfg, pScb, SCpnt);
/* 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
}
return pScb;
}
return NULL;
}
static int
mega_get_lun(mega_host_config *this_hba, Scsi_Cmnd *sc)
{
int tgt;
int lun;
int virt_chan;
tgt = sc->device->id;
if ( tgt > 7 ) tgt--; /* we do not get inquires for tgt 7 */
virt_chan = sc->device->channel - this_hba->productInfo.SCSIChanPresent;
lun = (virt_chan * 15) + tgt;
/*
* 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:
lun += 0x80;
}
}
return lun;
}
static mega_passthru *
mega_prepare_passthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc)
{
mega_passthru *pthru;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
pthru = scb->pthru;
#else
pthru = &scb->pthru;
#endif
memset (pthru, 0, sizeof (mega_passthru));
/* 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 : sc->device->channel;
pthru->target = (megacfg->flag & BOARD_40LD) ?
(sc->device->channel << 4) | sc->device->id : sc->device->id;
pthru->cdblen = sc->cmd_len;
pthru->logdrv = sc->device->lun;
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;
/* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
switch (sc->cmnd[0]) {
case INQUIRY:
case READ_CAPACITY:
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;
}
#else
pthru->numsgelements =
mega_build_sglist(
megacfg, scb, (u32 *)&pthru->dataxferaddr,
(u32 *)&pthru->dataxferlen
);
#endif
return pthru;
}
static mega_ext_passthru *
mega_prepare_extpassthru(mega_host_config *megacfg, mega_scb *scb, Scsi_Cmnd *sc)
{
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));
/* 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 : sc->device->channel;
epthru->target = (megacfg->flag & BOARD_40LD) ?
(sc->device->channel << 4) | sc->device->id : sc->device->id;
epthru->cdblen = sc->cmd_len;
epthru->logdrv = sc->device->lun;
memcpy(epthru->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;
/* Special Code for Handling READ_CAPA/ INQ using bounce buffers */
switch (sc->cmnd[0]) {
case INQUIRY:
case READ_CAPACITY:
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;
}
#else
epthru->numsgelements =
mega_build_sglist(
megacfg, scb, (u32 *)&epthru->dataxferaddr,
(u32 *)&epthru->dataxferlen
);
#endif
return epthru;
}
/* 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)
{
unsigned char *data = (unsigned char *) SCpnt->request_buffer;
mega_driver_info driver_info;
/* If this is not our command don't do anything */
if (SCpnt->cmnd[0] != M_RD_DRIVER_IOCTL_INTERFACE)
return 0;
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;
}
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;
}
callDone (SCpnt);
return 1;
}
static inline void 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;
}
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
}
#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;
}
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;
}
copy_from_user (pScb->buff_ptr, user_area, xfer_size);
pScb->iDataSize = xfer_size;
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;
}
/*building SG list */
mega_build_kernel_sg (pScb->buff_ptr,
xfer_size,
pScb, mbox);
break;
}
default:
break;
} /*switch (data[1]) */
break;
}
}
#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 {
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];
}
}
return (pScb);
}
static void mega_build_kernel_sg (char *barea, ulong xfersize, mega_scb * pScb, mega_ioctl_mbox * mbox)
{
ulong i, buffer_area, len, end, end_page, x, idx = 0;
buffer_area = (ulong) barea;
i = buffer_area;
end = buffer_area + xfersize;
end_page = (end) & ~(PAGE_SIZE - 1);
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++;
}
mbox->xferaddr = virt_to_bus (pScb->sgList);
mbox->numsgelements = idx;
}
#endif
#if DEBUG
static unsigned int cum_time = 0;
static unsigned int cum_time_cnt = 0;
static void showMbox (mega_scb * pScb)
{
mega_mailbox *mbox;
if (pScb == NULL)
return;
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);
}
#endif
/*--------------------------------------------------------------------
* 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 (mega_busyWaitMbox (megaCfg)) {
printk (KERN_WARNING "Error: mailbox busy in isr!\n");
}
/* 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);
}
for (idx = 0; idx < MAX_FIRMWARE_STATUS; idx++)
completed[idx] = 0;
IO_LOCK(megaCfg->host);
megaCfg->nInterrupts++;
qCnt = 0xff;
while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ;
qStatus = 0xff;
while ((qStatus = megaCfg->mbox->status) == 0xFF) ;
/* 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;
}
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
#else
#if 0
WRINDOOR (megaCfg, virt_to_bus (megaCfg->mbox) | 0x2);
#else
WRINDOOR (megaCfg, 0x2);
#endif
#endif
#if 0
while (RDINDOOR (megaCfg) & 0x02) ;
#endif
} else {
CLEAR_INTR (megaCfg->host->io_port);
}
#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;
}
/* 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);
}
}
mega_rundoneq (megaCfg);
megaCfg->flag &= ~IN_ISR;
/* Loop through any pending requests */
mega_runpendq (megaCfg);
IO_UNLOCK(megaCfg->host);
}
}
/*==================================================*/
/* Wait until the controller's mailbox is available */
/*==================================================*/
static int mega_busyWaitMbox (mega_host_config * megaCfg)
{
mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
long counter;
for (counter = 0; counter < 10000; counter++) {
if (!mbox->busy) {
return 0;
}
udelay (100);
barrier ();
}
return -1; /* give up after 1 second */
}
/*=====================================================
* Post a command to the card
*
* 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
*=====================================================
*/
static int megaIssueCmd (mega_host_config * megaCfg, u_char * mboxData,
mega_scb * pScb, int intr)
{
volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
volatile mega_mailbox64 *mbox64 = (mega_mailbox64 *) megaCfg->mbox64;
#endif
u_char byte;
#ifdef __LP64__
u64 phys_mbox;
#else
u32 phys_mbox;
#endif
u8 retval = -1;
mboxData[0x1] = (pScb ? pScb->idx + 1 : 0xFE); /* Set cmdid */
mboxData[0xF] = 1; /* Set busy */
#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
#if DEBUG
ShowMbox (pScb);
#endif
/* Wait until mailbox is free */
if (mega_busyWaitMbox (megaCfg)) {
printk ("Blocked mailbox......!!\n");
udelay (1000);
#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;
}
pLastScb = pScb;
/* Copy mailbox data into host structure */
megaCfg->mbox64->xferSegment_lo = 0;
megaCfg->mbox64->xferSegment_hi = 0;
memcpy ((char *) mbox, mboxData, 16);
#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;
}
#endif
/* Kick IO */
if (intr) {
/* Issue interrupt (non-blocking) command */
if (megaCfg->flag & BOARD_QUARTZ) {
mbox->mraid_poll = 0;
mbox->mraid_ack = 0;
WRINDOOR (megaCfg, phys_mbox | 0x1);
} else {
ENABLE_INTR (megaCfg->host->io_port);
ISSUE_COMMAND (megaCfg->host->io_port);
}
pScb->state = SCB_ISSUED;
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); */
if (pScb) {
mega_cmd_done (megaCfg, pScb, mbox->status);
}
WRINDOOR (megaCfg, phys_mbox | 0x2);
while (RDINDOOR (megaCfg) & 0x2) ;
} else {
DISABLE_INTR (megaCfg->host->io_port);
ISSUE_COMMAND (megaCfg->host->io_port);
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);
}
#endif
return retval;
}
/*-------------------------------------------------------------------
* 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 (mega_host_config * megaCfg, mega_scb * scb,
u32 * buffer, u32 * length)
{
struct scatterlist *sgList;
int idx;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
int sgcnt;
#endif
mega_mailbox *mbox = NULL;
mbox = (mega_mailbox *) scb->mboxData;
/* Scatter-gather not used */
if (scb->SCpnt->use_sg == 0) {
#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 = 0;
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;
}
sgList = (struct scatterlist *) scb->SCpnt->request_buffer;
#if 0
if (scb->SCpnt->use_sg == 1) {
#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
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);
}
}
#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
/* 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
*length = 0;
#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
}
/*--------------------------------------------------------------------
* 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
#ifdef __LP64__
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);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
megaCfg->adjdmahandle64 = ((megaCfg->dma_handle64 + 16) & 0xFFFFFFF0);
#endif
megaCfg->mbox64 = (mega_mailbox64 *) ((u_char *) megaCfg->mbox - 8);
paddr = (paddr + 4 + 16) & 0xFFFFFFF0;
#endif
/* 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;
}
/*---------------------------------------------------------------------------
* 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)
{
int i;
productInfo->MaxConcCmds = inquiry->AdpInfo.MaxConcCmds;
enquiry3->rbldRate = inquiry->AdpInfo.RbldRate;
productInfo->SCSIChanPresent = inquiry->AdpInfo.ChanPresent;
for (i = 0; i < 4; i++) {
productInfo->FwVer[i] = inquiry->AdpInfo.FwVer[i];
productInfo->BiosVer[i] = inquiry->AdpInfo.BiosVer[i];
}
enquiry3->cacheFlushInterval = inquiry->AdpInfo.CacheFlushInterval;
productInfo->DramSize = inquiry->AdpInfo.DramSize;
enquiry3->numLDrv = inquiry->LogdrvInfo.NumLDrv;
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];
}
for (i = 0; i < (MAX_PHYSICAL_DRIVES); i++) {
enquiry3->pDrvState[i]
= inquiry->PhysdrvInfo.PDrvState[i];
}
}
/*-------------------------------------------------------------------
* 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 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;
/* Initialize adapter inquiry mailbox */
mbox = (mega_mailbox *) mboxData;
memset ((void *) megaCfg->mega_buffer, 0,
sizeof (megaCfg->mega_buffer));
memset (mbox, 0, 16);
/*
* 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);
mbox->xferaddr = enquiry3_dma_handle;
#else
/*Taken care */
mbox->xferaddr = virt_to_bus ((void *) megaCfg->mega_buffer);
#endif
/* 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 */
/* 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
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
mboxData[0] = FC_NEW_CONFIG; /* i.e. mbox->cmd=0xA1 */
mboxData[2] = NC_SUBOP_PRODUCT_INFO; /* i.e. 0x0E */
if ((retval = megaIssueCmd (megaCfg, mboxData, NULL, 0)) != 0)
printk ("megaraid: Product_info cmd failed with error: %d\n",
retval);
pci_unmap_single (megaCfg->dev,
prod_info_dma_handle,
sizeof (megaRaidProductInfo),
PCI_DMA_FROMDEVICE);
}
/*
* kernel scans the channels from 0 to <= max_channel
*/
megaCfg->host->max_channel =
megaCfg->productInfo.SCSIChanPresent + NVIRT_CHAN -1;
megaCfg->host->max_id = 16; /* max targets per channel */
/*(megaCfg->flag & BOARD_40LD)?FC_MAX_TARGETS_PER_CHANNEL:MAX_TARGET+1; */
#if 0
megaCfg->host->max_lun = /* max lun */
(megaCfg->flag & BOARD_40LD) ?
FC_MAX_LOGICAL_DRIVES : MAX_LOGICAL_DRIVES;
#endif
megaCfg->host->max_lun = 7; /* Upto 7 luns for non disk devices */
megaCfg->host->cmd_per_lun = MAX_CMD_PER_LUN;
megaCfg->numldrv = enquiry3Pnt->numLDrv;
megaCfg->max_cmds = megaCfg->productInfo.MaxConcCmds;
if (megaCfg->max_cmds > MAX_COMMANDS)
megaCfg->max_cmds = MAX_COMMANDS - 1;
megaCfg->host->can_queue = megaCfg->max_cmds - 1;
#if 0
if (megaCfg->host->can_queue >= MAX_COMMANDS) {
megaCfg->host->can_queue = MAX_COMMANDS - 16;
}
#endif
/* 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;
memcpy (megaCfg->biosVer, (char *) megaCfg->productInfo.BiosVer, 4);
megaCfg->biosVer[4] = 0;
}
megaCfg->support_ext_cdb = mega_support_ext_cdb(megaCfg);
printk (KERN_NOTICE "megaraid: [%s:%s] detected %d logical drives" M_RD_CRLFSTR,
megaCfg->fwVer, megaCfg->biosVer, megaCfg->numldrv);
if ( megaCfg->support_ext_cdb ) {
printk(KERN_NOTICE "megaraid: supports extended CDBs.\n");
}
/*
* I hope that I can unmap here, reason DMA transaction is not required any more
* after this
*/
return 0;
}
/*-------------------------------------------------------------------------
*
* Driver interface functions
*
*-------------------------------------------------------------------------*/
/*----------------------------------------------------------
* Returns data to be displayed in /proc/scsi/megaraid/X
*----------------------------------------------------------*/
int megaraid_proc_info (char *buffer, char **start, off_t offset,
int length, int host_no, int inout)
{
*start = buffer;
return 0;
}
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
int i;
#ifdef __LP64__
u64 megaBase;
#else
u32 megaBase;
#endif
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
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,0) /*0x20300 */
struct pci_dev *pdev = NULL;
#else
struct pci_dev *pdev = pci_devices;
#endif
while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) {
if(pci_enable_device (pdev))
continue;