Merge mulgrave-w:git/scsi-misc-2.6
James Bottomley [Sat, 23 Sep 2006 20:33:43 +0000 (15:33 -0500)]
Conflicts:

drivers/scsi/iscsi_tcp.c
drivers/scsi/iscsi_tcp.h

Pretty horrible merge between crypto hash consolidation
and crypto_digest_...->crypto_hash_... conversion

Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>

125 files changed:
Documentation/scsi/ChangeLog.arcmsr [new file with mode: 0644]
Documentation/scsi/arcmsr_spec.txt [new file with mode: 0644]
Documentation/scsi/libsas.txt [new file with mode: 0644]
block/ll_rw_blk.c
drivers/block/DAC960.c
drivers/block/cciss_scsi.c
drivers/infiniband/ulp/iser/iscsi_iser.c
drivers/infiniband/ulp/iser/iscsi_iser.h
drivers/message/fusion/mptfc.c
drivers/message/fusion/mptsas.c
drivers/scsi/BusLogic.c
drivers/scsi/Kconfig
drivers/scsi/Makefile
drivers/scsi/a2091.c
drivers/scsi/a2091.h
drivers/scsi/a3000.c
drivers/scsi/a3000.h
drivers/scsi/aacraid/aachba.c
drivers/scsi/aacraid/aacraid.h
drivers/scsi/aacraid/commctrl.c
drivers/scsi/aacraid/comminit.c
drivers/scsi/aacraid/commsup.c
drivers/scsi/aacraid/dpcsup.c
drivers/scsi/aacraid/linit.c
drivers/scsi/aacraid/rkt.c
drivers/scsi/aacraid/rx.c
drivers/scsi/advansys.c
drivers/scsi/aha152x.c
drivers/scsi/aic7xxx/aic79xx_osm.c
drivers/scsi/aic7xxx/aic7xxx_osm.c
drivers/scsi/aic7xxx_old.c
drivers/scsi/aic94xx/Kconfig [new file with mode: 0644]
drivers/scsi/aic94xx/Makefile [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_dev.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_dump.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_dump.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_hwi.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_hwi.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_init.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_reg.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_reg.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_reg_def.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_sas.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_scb.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_sds.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_seq.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_seq.h [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_task.c [new file with mode: 0644]
drivers/scsi/aic94xx/aic94xx_tmf.c [new file with mode: 0644]
drivers/scsi/arcmsr/Makefile [new file with mode: 0644]
drivers/scsi/arcmsr/arcmsr.h [new file with mode: 0644]
drivers/scsi/arcmsr/arcmsr_attr.c [new file with mode: 0644]
drivers/scsi/arcmsr/arcmsr_hba.c [new file with mode: 0644]
drivers/scsi/dpt_i2o.c
drivers/scsi/fcal.c
drivers/scsi/g_NCR5380.c
drivers/scsi/gvp11.c
drivers/scsi/gvp11.h
drivers/scsi/hosts.c
drivers/scsi/hptiop.c
drivers/scsi/ipr.c
drivers/scsi/ipr.h
drivers/scsi/iscsi_tcp.c
drivers/scsi/iscsi_tcp.h
drivers/scsi/libata-eh.c
drivers/scsi/libiscsi.c
drivers/scsi/libsas/Kconfig [new file with mode: 0644]
drivers/scsi/libsas/Makefile [new file with mode: 0644]
drivers/scsi/libsas/sas_discover.c [new file with mode: 0644]
drivers/scsi/libsas/sas_dump.c [new file with mode: 0644]
drivers/scsi/libsas/sas_dump.h [new file with mode: 0644]
drivers/scsi/libsas/sas_event.c [new file with mode: 0644]
drivers/scsi/libsas/sas_expander.c [new file with mode: 0644]
drivers/scsi/libsas/sas_init.c [new file with mode: 0644]
drivers/scsi/libsas/sas_internal.h [new file with mode: 0644]
drivers/scsi/libsas/sas_phy.c [new file with mode: 0644]
drivers/scsi/libsas/sas_port.c [new file with mode: 0644]
drivers/scsi/libsas/sas_scsi_host.c [new file with mode: 0644]
drivers/scsi/lpfc/lpfc.h
drivers/scsi/lpfc/lpfc_attr.c
drivers/scsi/lpfc/lpfc_crtn.h
drivers/scsi/lpfc/lpfc_ct.c
drivers/scsi/lpfc/lpfc_disc.h
drivers/scsi/lpfc/lpfc_els.c
drivers/scsi/lpfc/lpfc_hbadisc.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_nportdisc.c
drivers/scsi/lpfc/lpfc_scsi.c
drivers/scsi/lpfc/lpfc_version.h
drivers/scsi/megaraid.c
drivers/scsi/megaraid/megaraid_mbox.c
drivers/scsi/megaraid/megaraid_sas.c
drivers/scsi/mvme147.c
drivers/scsi/mvme147.h
drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/scsi_lib.c
drivers/scsi/scsi_netlink.c [new file with mode: 0644]
drivers/scsi/scsi_priv.h
drivers/scsi/scsi_proc.c
drivers/scsi/scsi_scan.c
drivers/scsi/scsi_transport_fc.c
drivers/scsi/scsi_transport_iscsi.c
drivers/scsi/scsi_transport_sas.c
drivers/scsi/sd.c
drivers/scsi/sgiwd93.c
drivers/scsi/stex.c [new file with mode: 0644]
drivers/scsi/ultrastor.c
drivers/scsi/ultrastor.h
include/linux/blkdev.h
include/linux/module.h
include/linux/netlink.h
include/linux/pci_ids.h
include/scsi/libiscsi.h
include/scsi/libsas.h [new file with mode: 0644]
include/scsi/sas.h [new file with mode: 0644]
include/scsi/scsi.h
include/scsi/scsi_cmnd.h
include/scsi/scsi_host.h
include/scsi/scsi_netlink.h [new file with mode: 0644]
include/scsi/scsi_netlink_fc.h [new file with mode: 0644]
include/scsi/scsi_tcq.h
include/scsi/scsi_transport_fc.h
include/scsi/scsi_transport_sas.h

diff --git a/Documentation/scsi/ChangeLog.arcmsr b/Documentation/scsi/ChangeLog.arcmsr
new file mode 100644 (file)
index 0000000..162c47f
--- /dev/null
@@ -0,0 +1,56 @@
+**************************************************************************
+** History
+**
+**   REV#         DATE             NAME         DESCRIPTION
+** 1.00.00.00    3/31/2004       Erich Chen     First release
+** 1.10.00.04    7/28/2004       Erich Chen     modify for ioctl
+** 1.10.00.06    8/28/2004       Erich Chen     modify for 2.6.x
+** 1.10.00.08    9/28/2004       Erich Chen     modify for x86_64
+** 1.10.00.10   10/10/2004       Erich Chen     bug fix for SMP & ioctl
+** 1.20.00.00   11/29/2004       Erich Chen     bug fix with arcmsr_bus_reset when PHY error
+** 1.20.00.02   12/09/2004       Erich Chen     bug fix with over 2T bytes RAID Volume
+** 1.20.00.04    1/09/2005       Erich Chen     fits for Debian linux kernel version 2.2.xx
+** 1.20.00.05    2/20/2005       Erich Chen     cleanly as look like a Linux driver at 2.6.x
+**                                              thanks for peoples kindness comment
+**                                             Kornel Wieliczek
+**                                             Christoph Hellwig
+**                                             Adrian Bunk
+**                                             Andrew Morton
+**                                             Christoph Hellwig
+**                                             James Bottomley
+**                                             Arjan van de Ven
+** 1.20.00.06    3/12/2005       Erich Chen     fix with arcmsr_pci_unmap_dma "unsigned long" cast,
+**                                             modify PCCB POOL allocated by "dma_alloc_coherent"
+**                                             (Kornel Wieliczek's comment)
+** 1.20.00.07    3/23/2005       Erich Chen     bug fix with arcmsr_scsi_host_template_init
+**                                             occur segmentation fault,
+**                                             if RAID adapter does not on PCI slot
+**                                             and modprobe/rmmod this driver twice.
+**                                             bug fix enormous stack usage (Adrian Bunk's comment)
+** 1.20.00.08    6/23/2005       Erich Chen     bug fix with abort command,
+**                                             in case of heavy loading when sata cable
+**                                             working on low quality connection
+** 1.20.00.09    9/12/2005       Erich Chen     bug fix with abort command handling, firmware version check
+**                                             and firmware update notify for hardware bug fix
+** 1.20.00.10    9/23/2005       Erich Chen     enhance sysfs function for change driver's max tag Q number.
+**                                             add DMA_64BIT_MASK for backward compatible with all 2.6.x
+**                                             add some useful message for abort command
+**                                             add ioctl code 'ARCMSR_IOCTL_FLUSH_ADAPTER_CACHE'
+**                                             customer can send this command for sync raid volume data
+** 1.20.00.11    9/29/2005       Erich Chen     by comment of Arjan van de Ven fix incorrect msleep redefine
+**                                             cast off sizeof(dma_addr_t) condition for 64bit pci_set_dma_mask
+** 1.20.00.12    9/30/2005       Erich Chen     bug fix with 64bit platform's ccbs using if over 4G system memory
+**                                             change 64bit pci_set_consistent_dma_mask into 32bit
+**                                             increcct adapter count if adapter initialize fail.
+**                                             miss edit at arcmsr_build_ccb....
+**                                             psge += sizeof(struct _SG64ENTRY *) =>
+**                                             psge += sizeof(struct _SG64ENTRY)
+**                                             64 bits sg entry would be incorrectly calculated
+**                                             thanks Kornel Wieliczek give me kindly notify
+**                                             and detail description
+** 1.20.00.13   11/15/2005       Erich Chen     scheduling pending ccb with FIFO
+**                                             change the architecture of arcmsr command queue list
+**                                             for linux standard list
+**                                             enable usage of pci message signal interrupt
+**                                             follow Randy.Danlup kindness suggestion cleanup this code
+**************************************************************************
\ No newline at end of file
diff --git a/Documentation/scsi/arcmsr_spec.txt b/Documentation/scsi/arcmsr_spec.txt
new file mode 100644 (file)
index 0000000..5e00423
--- /dev/null
@@ -0,0 +1,574 @@
+*******************************************************************************
+**                            ARECA FIRMWARE SPEC
+*******************************************************************************
+**     Usage of IOP331 adapter
+**     (All In/Out is in IOP331's view)
+**     1. Message 0 --> InitThread message and retrun code
+**     2. Doorbell is used for RS-232 emulation
+**             inDoorBell :    bit0 -- data in ready
+**                     (DRIVER DATA WRITE OK)
+**                             bit1 -- data out has been read
+**                     (DRIVER DATA READ OK)
+**             outDooeBell:    bit0 -- data out ready
+**                     (IOP331 DATA WRITE OK)
+**                             bit1 -- data in has been read
+**                     (IOP331 DATA READ OK)
+**     3. Index Memory Usage
+**     offset 0xf00 : for RS232 out (request buffer)
+**     offset 0xe00 : for RS232 in  (scratch buffer)
+**     offset 0xa00 : for inbound message code message_rwbuffer
+**                     (driver send to IOP331)
+**     offset 0xa00 : for outbound message code message_rwbuffer
+**                     (IOP331 send to driver)
+**     4. RS-232 emulation
+**             Currently 128 byte buffer is used
+**                     1st uint32_t : Data length (1--124)
+**                     Byte 4--127  : Max 124 bytes of data
+**     5. PostQ
+**     All SCSI Command must be sent through postQ:
+**     (inbound queue port)    Request frame must be 32 bytes aligned
+**     #bit27--bit31 => flag for post ccb
+**     #bit0--bit26  => real address (bit27--bit31) of post arcmsr_cdb
+**             bit31 :
+**                     0 : 256 bytes frame
+**                     1 : 512 bytes frame
+**             bit30 :
+**                     0 : normal request
+**                     1 : BIOS request
+**             bit29 : reserved
+**             bit28 : reserved
+**             bit27 : reserved
+**  ---------------------------------------------------------------------------
+**     (outbount queue port)   Request reply
+**     #bit27--bit31
+**             => flag for reply
+**     #bit0--bit26
+**             => real address (bit27--bit31) of reply arcmsr_cdb
+**                     bit31 : must be 0 (for this type of reply)
+**                     bit30 : reserved for BIOS handshake
+**                     bit29 : reserved
+**                     bit28 :
+**                     0 : no error, ignore AdapStatus/DevStatus/SenseData
+**                     1 : Error, error code in AdapStatus/DevStatus/SenseData
+**                     bit27 : reserved
+**     6. BIOS request
+**             All BIOS request is the same with request from PostQ
+**             Except :
+**                     Request frame is sent from configuration space
+**             offset: 0x78 : Request Frame (bit30 == 1)
+**             offset: 0x18 : writeonly to generate
+**                                     IRQ to IOP331
+**             Completion of request:
+**                     (bit30 == 0, bit28==err flag)
+**     7. Definition of SGL entry (structure)
+**     8. Message1 Out - Diag Status Code (????)
+**     9. Message0 message code :
+**             0x00 : NOP
+**             0x01 : Get Config
+**             ->offset 0xa00 :for outbound message code message_rwbuffer
+**             (IOP331 send to driver)
+**             Signature             0x87974060(4)
+**             Request len           0x00000200(4)
+**             numbers of queue      0x00000100(4)
+**             SDRAM Size            0x00000100(4)-->256 MB
+**             IDE Channels          0x00000008(4)
+**             vendor                40 bytes char
+**             model                  8 bytes char
+**             FirmVer               16 bytes char
+**             Device Map            16 bytes char
+**             FirmwareVersion DWORD <== Added for checking of
+**                                             new firmware capability
+**             0x02 : Set Config
+**             ->offset 0xa00 :for inbound message code message_rwbuffer
+**             (driver send to IOP331)
+**             Signature             0x87974063(4)
+**             UPPER32 of Request Frame  (4)-->Driver Only
+**             0x03 : Reset (Abort all queued Command)
+**             0x04 : Stop Background Activity
+**             0x05 : Flush Cache
+**             0x06 : Start Background Activity
+**                     (re-start if background is halted)
+**             0x07 : Check If Host Command Pending
+**                     (Novell May Need This Function)
+**             0x08 : Set controller time
+**             ->offset 0xa00 : for inbound message code message_rwbuffer
+**             (driver to IOP331)
+**             byte 0 : 0xaa <-- signature
+**             byte 1 : 0x55 <-- signature
+**             byte 2 : year (04)
+**             byte 3 : month (1..12)
+**             byte 4 : date (1..31)
+**             byte 5 : hour (0..23)
+**             byte 6 : minute (0..59)
+**             byte 7 : second (0..59)
+*******************************************************************************
+*******************************************************************************
+**             RS-232 Interface for Areca Raid Controller
+**      The low level command interface is exclusive with VT100 terminal
+**  --------------------------------------------------------------------
+**      1. Sequence of command execution
+**  --------------------------------------------------------------------
+**     (A) Header : 3 bytes sequence (0x5E, 0x01, 0x61)
+**     (B) Command block : variable length of data including length,
+**             command code, data and checksum byte
+**     (C) Return data : variable length of data
+**  --------------------------------------------------------------------
+**    2. Command block
+**  --------------------------------------------------------------------
+**     (A) 1st byte : command block length (low byte)
+**     (B) 2nd byte : command block length (high byte)
+**                note ..command block length shouldn't > 2040 bytes,
+**             length excludes these two bytes
+**     (C) 3rd byte : command code
+**     (D) 4th and following bytes : variable length data bytes
+**             depends on command code
+**     (E) last byte : checksum byte (sum of 1st byte until last data byte)
+**  --------------------------------------------------------------------
+**    3. Command code and associated data
+**  --------------------------------------------------------------------
+**     The following are command code defined in raid controller Command
+**     code 0x10--0x1? are used for system level management,
+**     no password checking is needed and should be implemented in separate
+**     well controlled utility and not for end user access.
+**     Command code 0x20--0x?? always check the password,
+**     password must be entered to enable these command.
+**     enum
+**     {
+**             GUI_SET_SERIAL=0x10,
+**             GUI_SET_VENDOR,
+**             GUI_SET_MODEL,
+**             GUI_IDENTIFY,
+**             GUI_CHECK_PASSWORD,
+**             GUI_LOGOUT,
+**             GUI_HTTP,
+**             GUI_SET_ETHERNET_ADDR,
+**             GUI_SET_LOGO,
+**             GUI_POLL_EVENT,
+**             GUI_GET_EVENT,
+**             GUI_GET_HW_MONITOR,
+**             //    GUI_QUICK_CREATE=0x20, (function removed)
+**             GUI_GET_INFO_R=0x20,
+**             GUI_GET_INFO_V,
+**             GUI_GET_INFO_P,
+**             GUI_GET_INFO_S,
+**             GUI_CLEAR_EVENT,
+**             GUI_MUTE_BEEPER=0x30,
+**             GUI_BEEPER_SETTING,
+**             GUI_SET_PASSWORD,
+**             GUI_HOST_INTERFACE_MODE,
+**             GUI_REBUILD_PRIORITY,
+**             GUI_MAX_ATA_MODE,
+**             GUI_RESET_CONTROLLER,
+**             GUI_COM_PORT_SETTING,
+**             GUI_NO_OPERATION,
+**             GUI_DHCP_IP,
+**             GUI_CREATE_PASS_THROUGH=0x40,
+**             GUI_MODIFY_PASS_THROUGH,
+**             GUI_DELETE_PASS_THROUGH,
+**             GUI_IDENTIFY_DEVICE,
+**             GUI_CREATE_RAIDSET=0x50,
+**             GUI_DELETE_RAIDSET,
+**             GUI_EXPAND_RAIDSET,
+**             GUI_ACTIVATE_RAIDSET,
+**             GUI_CREATE_HOT_SPARE,
+**             GUI_DELETE_HOT_SPARE,
+**             GUI_CREATE_VOLUME=0x60,
+**             GUI_MODIFY_VOLUME,
+**             GUI_DELETE_VOLUME,
+**             GUI_START_CHECK_VOLUME,
+**             GUI_STOP_CHECK_VOLUME
+**     };
+**    Command description :
+**     GUI_SET_SERIAL : Set the controller serial#
+**             byte 0,1        : length
+**             byte 2          : command code 0x10
+**             byte 3          : password length (should be 0x0f)
+**             byte 4-0x13     : should be "ArEcATecHnoLogY"
+**             byte 0x14--0x23 : Serial number string (must be 16 bytes)
+**      GUI_SET_VENDOR : Set vendor string for the controller
+**             byte 0,1        : length
+**             byte 2          : command code 0x11
+**             byte 3          : password length (should be 0x08)
+**             byte 4-0x13     : should be "ArEcAvAr"
+**             byte 0x14--0x3B : vendor string (must be 40 bytes)
+**      GUI_SET_MODEL : Set the model name of the controller
+**             byte 0,1        : length
+**             byte 2          : command code 0x12
+**             byte 3          : password length (should be 0x08)
+**             byte 4-0x13     : should be "ArEcAvAr"
+**             byte 0x14--0x1B : model string (must be 8 bytes)
+**      GUI_IDENTIFY : Identify device
+**             byte 0,1        : length
+**             byte 2          : command code 0x13
+**                               return "Areca RAID Subsystem "
+**      GUI_CHECK_PASSWORD : Verify password
+**             byte 0,1        : length
+**             byte 2          : command code 0x14
+**             byte 3          : password length
+**             byte 4-0x??     : user password to be checked
+**      GUI_LOGOUT : Logout GUI (force password checking on next command)
+**             byte 0,1        : length
+**             byte 2          : command code 0x15
+**      GUI_HTTP : HTTP interface (reserved for Http proxy service)(0x16)
+**
+**      GUI_SET_ETHERNET_ADDR : Set the ethernet MAC address
+**             byte 0,1        : length
+**             byte 2          : command code 0x17
+**             byte 3          : password length (should be 0x08)
+**             byte 4-0x13     : should be "ArEcAvAr"
+**             byte 0x14--0x19 : Ethernet MAC address (must be 6 bytes)
+**      GUI_SET_LOGO : Set logo in HTTP
+**             byte 0,1        : length
+**             byte 2          : command code 0x18
+**             byte 3          : Page# (0/1/2/3) (0xff --> clear OEM logo)
+**             byte 4/5/6/7    : 0x55/0xaa/0xa5/0x5a
+**             byte 8          : TITLE.JPG data (each page must be 2000 bytes)
+**                               note page0 1st 2 byte must be
+**                                     actual length of the JPG file
+**      GUI_POLL_EVENT : Poll If Event Log Changed
+**             byte 0,1        : length
+**             byte 2          : command code 0x19
+**      GUI_GET_EVENT : Read Event
+**             byte 0,1        : length
+**             byte 2          : command code 0x1a
+**             byte 3          : Event Page (0:1st page/1/2/3:last page)
+**      GUI_GET_HW_MONITOR : Get HW monitor data
+**             byte 0,1        : length
+**             byte 2                  : command code 0x1b
+**             byte 3                  : # of FANs(example 2)
+**             byte 4                  : # of Voltage sensor(example 3)
+**             byte 5                  : # of temperature sensor(example 2)
+**             byte 6                  : # of power
+**             byte 7/8        : Fan#0 (RPM)
+**             byte 9/10       : Fan#1
+**             byte 11/12              : Voltage#0 original value in *1000
+**             byte 13/14              : Voltage#0 value
+**             byte 15/16              : Voltage#1 org
+**             byte 17/18              : Voltage#1
+**             byte 19/20              : Voltage#2 org
+**             byte 21/22              : Voltage#2
+**             byte 23                 : Temp#0
+**             byte 24                 : Temp#1
+**             byte 25                 : Power indicator (bit0 : power#0,
+**                                              bit1 : power#1)
+**             byte 26                 : UPS indicator
+**      GUI_QUICK_CREATE : Quick create raid/volume set
+**         byte 0,1        : length
+**         byte 2          : command code 0x20
+**         byte 3/4/5/6    : raw capacity
+**         byte 7                      : raid level
+**         byte 8                      : stripe size
+**         byte 9                      : spare
+**         byte 10/11/12/13: device mask (the devices to create raid/volume)
+**             This function is removed, application like
+**             to implement quick create function
+**     need to use GUI_CREATE_RAIDSET and GUI_CREATE_VOLUMESET function.
+**      GUI_GET_INFO_R : Get Raid Set Information
+**             byte 0,1        : length
+**             byte 2          : command code 0x20
+**             byte 3          : raidset#
+**     typedef struct sGUI_RAIDSET
+**     {
+**             BYTE grsRaidSetName[16];
+**             DWORD grsCapacity;
+**             DWORD grsCapacityX;
+**             DWORD grsFailMask;
+**             BYTE grsDevArray[32];
+**             BYTE grsMemberDevices;
+**             BYTE grsNewMemberDevices;
+**             BYTE grsRaidState;
+**             BYTE grsVolumes;
+**             BYTE grsVolumeList[16];
+**             BYTE grsRes1;
+**             BYTE grsRes2;
+**             BYTE grsRes3;
+**             BYTE grsFreeSegments;
+**             DWORD grsRawStripes[8];
+**             DWORD grsRes4;
+**             DWORD grsRes5; //     Total to 128 bytes
+**             DWORD grsRes6; //     Total to 128 bytes
+**     } sGUI_RAIDSET, *pGUI_RAIDSET;
+**      GUI_GET_INFO_V : Get Volume Set Information
+**             byte 0,1        : length
+**             byte 2          : command code 0x21
+**             byte 3          : volumeset#
+**     typedef struct sGUI_VOLUMESET
+**     {
+**             BYTE gvsVolumeName[16]; //     16
+**             DWORD gvsCapacity;
+**             DWORD gvsCapacityX;
+**             DWORD gvsFailMask;
+**             DWORD gvsStripeSize;
+**             DWORD gvsNewFailMask;
+**             DWORD gvsNewStripeSize;
+**             DWORD gvsVolumeStatus;
+**             DWORD gvsProgress; //     32
+**             sSCSI_ATTR gvsScsi;
+**             BYTE gvsMemberDisks;
+**             BYTE gvsRaidLevel; //     8
+**             BYTE gvsNewMemberDisks;
+**             BYTE gvsNewRaidLevel;
+**             BYTE gvsRaidSetNumber;
+**             BYTE gvsRes0; //     4
+**             BYTE gvsRes1[4]; //     64 bytes
+**     } sGUI_VOLUMESET, *pGUI_VOLUMESET;
+**      GUI_GET_INFO_P : Get Physical Drive Information
+**             byte 0,1        : length
+**             byte 2          : command code 0x22
+**             byte 3          : drive # (from 0 to max-channels - 1)
+**     typedef struct sGUI_PHY_DRV
+**     {
+**             BYTE gpdModelName[40];
+**             BYTE gpdSerialNumber[20];
+**             BYTE gpdFirmRev[8];
+**             DWORD gpdCapacity;
+**             DWORD gpdCapacityX; //     Reserved for expansion
+**             BYTE gpdDeviceState;
+**             BYTE gpdPioMode;
+**             BYTE gpdCurrentUdmaMode;
+**             BYTE gpdUdmaMode;
+**             BYTE gpdDriveSelect;
+**             BYTE gpdRaidNumber; //     0xff if not belongs to a raid set
+**             sSCSI_ATTR gpdScsi;
+**             BYTE gpdReserved[40]; //     Total to 128 bytes
+**     } sGUI_PHY_DRV, *pGUI_PHY_DRV;
+**     GUI_GET_INFO_S : Get System Information
+**             byte 0,1        : length
+**             byte 2          : command code 0x23
+**     typedef struct sCOM_ATTR
+**     {
+**             BYTE comBaudRate;
+**             BYTE comDataBits;
+**             BYTE comStopBits;
+**             BYTE comParity;
+**             BYTE comFlowControl;
+**     } sCOM_ATTR, *pCOM_ATTR;
+**     typedef struct sSYSTEM_INFO
+**     {
+**             BYTE gsiVendorName[40];
+**             BYTE gsiSerialNumber[16];
+**             BYTE gsiFirmVersion[16];
+**             BYTE gsiBootVersion[16];
+**             BYTE gsiMbVersion[16];
+**             BYTE gsiModelName[8];
+**             BYTE gsiLocalIp[4];
+**             BYTE gsiCurrentIp[4];
+**             DWORD gsiTimeTick;
+**             DWORD gsiCpuSpeed;
+**             DWORD gsiICache;
+**             DWORD gsiDCache;
+**             DWORD gsiScache;
+**             DWORD gsiMemorySize;
+**             DWORD gsiMemorySpeed;
+**             DWORD gsiEvents;
+**             BYTE gsiMacAddress[6];
+**             BYTE gsiDhcp;
+**             BYTE gsiBeeper;
+**             BYTE gsiChannelUsage;
+**             BYTE gsiMaxAtaMode;
+**             BYTE gsiSdramEcc; //     1:if ECC enabled
+**             BYTE gsiRebuildPriority;
+**             sCOM_ATTR gsiComA; //     5 bytes
+**             sCOM_ATTR gsiComB; //     5 bytes
+**             BYTE gsiIdeChannels;
+**             BYTE gsiScsiHostChannels;
+**             BYTE gsiIdeHostChannels;
+**             BYTE gsiMaxVolumeSet;
+**             BYTE gsiMaxRaidSet;
+**             BYTE gsiEtherPort; //     1:if ether net port supported
+**             BYTE gsiRaid6Engine; //     1:Raid6 engine supported
+**             BYTE gsiRes[75];
+**     } sSYSTEM_INFO, *pSYSTEM_INFO;
+**     GUI_CLEAR_EVENT : Clear System Event
+**             byte 0,1        : length
+**             byte 2          : command code 0x24
+**      GUI_MUTE_BEEPER : Mute current beeper
+**             byte 0,1        : length
+**             byte 2          : command code 0x30
+**      GUI_BEEPER_SETTING : Disable beeper
+**             byte 0,1        : length
+**             byte 2          : command code 0x31
+**             byte 3          : 0->disable, 1->enable
+**      GUI_SET_PASSWORD : Change password
+**             byte 0,1        : length
+**             byte 2                  : command code 0x32
+**             byte 3                  : pass word length ( must <= 15 )
+**             byte 4                  : password (must be alpha-numerical)
+**     GUI_HOST_INTERFACE_MODE : Set host interface mode
+**             byte 0,1        : length
+**             byte 2                  : command code 0x33
+**             byte 3                  : 0->Independent, 1->cluster
+**      GUI_REBUILD_PRIORITY : Set rebuild priority
+**             byte 0,1        : length
+**             byte 2                  : command code 0x34
+**             byte 3                  : 0/1/2/3 (low->high)
+**      GUI_MAX_ATA_MODE : Set maximum ATA mode to be used
+**             byte 0,1        : length
+**             byte 2                  : command code 0x35
+**             byte 3                  : 0/1/2/3 (133/100/66/33)
+**      GUI_RESET_CONTROLLER : Reset Controller
+**             byte 0,1        : length
+**             byte 2          : command code 0x36
+**                            *Response with VT100 screen (discard it)
+**      GUI_COM_PORT_SETTING : COM port setting
+**             byte 0,1        : length
+**             byte 2                  : command code 0x37
+**             byte 3                  : 0->COMA (term port),
+**                                       1->COMB (debug port)
+**             byte 4                  : 0/1/2/3/4/5/6/7
+**                     (1200/2400/4800/9600/19200/38400/57600/115200)
+**             byte 5                  : data bit
+**                                     (0:7 bit, 1:8 bit : must be 8 bit)
+**             byte 6                  : stop bit (0:1, 1:2 stop bits)
+**             byte 7                  : parity (0:none, 1:off, 2:even)
+**             byte 8                  : flow control
+**                     (0:none, 1:xon/xoff, 2:hardware => must use none)
+**      GUI_NO_OPERATION : No operation
+**             byte 0,1        : length
+**             byte 2          : command code 0x38
+**      GUI_DHCP_IP : Set DHCP option and local IP address
+**             byte 0,1        : length
+**             byte 2          : command code 0x39
+**             byte 3          : 0:dhcp disabled, 1:dhcp enabled
+**             byte 4/5/6/7    : IP address
+**      GUI_CREATE_PASS_THROUGH : Create pass through disk
+**             byte 0,1        : length
+**             byte 2                  : command code 0x40
+**             byte 3                  : device #
+**             byte 4                  : scsi channel (0/1)
+**             byte 5                  : scsi id (0-->15)
+**             byte 6                  : scsi lun (0-->7)
+**             byte 7                  : tagged queue (1 : enabled)
+**             byte 8                  : cache mode (1 : enabled)
+**             byte 9                  : max speed (0/1/2/3/4,
+**                     async/20/40/80/160 for scsi)
+**                     (0/1/2/3/4, 33/66/100/133/150 for ide  )
+**      GUI_MODIFY_PASS_THROUGH : Modify pass through disk
+**             byte 0,1        : length
+**             byte 2                  : command code 0x41
+**             byte 3                  : device #
+**             byte 4                  : scsi channel (0/1)
+**             byte 5                  : scsi id (0-->15)
+**             byte 6                  : scsi lun (0-->7)
+**             byte 7                  : tagged queue (1 : enabled)
+**             byte 8                  : cache mode (1 : enabled)
+**             byte 9                  : max speed (0/1/2/3/4,
+**                                     async/20/40/80/160 for scsi)
+**                     (0/1/2/3/4, 33/66/100/133/150 for ide  )
+**      GUI_DELETE_PASS_THROUGH : Delete pass through disk
+**             byte 0,1        : length
+**             byte 2          : command code 0x42
+**             byte 3          : device# to be deleted
+**      GUI_IDENTIFY_DEVICE : Identify Device
+**             byte 0,1        : length
+**             byte 2          : command code 0x43
+**             byte 3          : Flash Method
+**                             (0:flash selected, 1:flash not selected)
+**             byte 4/5/6/7    : IDE device mask to be flashed
+**                           note .... no response data available
+**     GUI_CREATE_RAIDSET : Create Raid Set
+**             byte 0,1        : length
+**             byte 2          : command code 0x50
+**             byte 3/4/5/6    : device mask
+**             byte 7-22       : raidset name (if byte 7 == 0:use default)
+**      GUI_DELETE_RAIDSET : Delete Raid Set
+**             byte 0,1        : length
+**             byte 2          : command code 0x51
+**             byte 3          : raidset#
+**     GUI_EXPAND_RAIDSET : Expand Raid Set
+**             byte 0,1        : length
+**             byte 2          : command code 0x52
+**             byte 3          : raidset#
+**             byte 4/5/6/7    : device mask for expansion
+**             byte 8/9/10     : (8:0 no change, 1 change, 0xff:terminate,
+**                             9:new raid level,
+**                             10:new stripe size
+**                             0/1/2/3/4/5->4/8/16/32/64/128K )
+**             byte 11/12/13   : repeat for each volume in the raidset
+**      GUI_ACTIVATE_RAIDSET : Activate incomplete raid set
+**             byte 0,1        : length
+**             byte 2          : command code 0x53
+**             byte 3          : raidset#
+**      GUI_CREATE_HOT_SPARE : Create hot spare disk
+**             byte 0,1        : length
+**             byte 2          : command code 0x54
+**             byte 3/4/5/6    : device mask for hot spare creation
+**     GUI_DELETE_HOT_SPARE : Delete hot spare disk
+**             byte 0,1        : length
+**             byte 2          : command code 0x55
+**             byte 3/4/5/6    : device mask for hot spare deletion
+**     GUI_CREATE_VOLUME : Create volume set
+**             byte 0,1        : length
+**             byte 2          : command code 0x60
+**             byte 3          : raidset#
+**             byte 4-19       : volume set name
+**                             (if byte4 == 0, use default)
+**             byte 20-27      : volume capacity (blocks)
+**             byte 28                 : raid level
+**             byte 29                 : stripe size
+**                             (0/1/2/3/4/5->4/8/16/32/64/128K)
+**             byte 30                 : channel
+**             byte 31                 : ID
+**             byte 32                 : LUN
+**             byte 33                 : 1 enable tag
+**             byte 34                 : 1 enable cache
+**             byte 35                 : speed
+**             (0/1/2/3/4->async/20/40/80/160 for scsi)
+**             (0/1/2/3/4->33/66/100/133/150 for IDE  )
+**             byte 36                 : 1 to select quick init
+**
+**     GUI_MODIFY_VOLUME : Modify volume Set
+**             byte 0,1        : length
+**             byte 2          : command code 0x61
+**             byte 3          : volumeset#
+**             byte 4-19       : new volume set name
+**             (if byte4 == 0, not change)
+**             byte 20-27      : new volume capacity (reserved)
+**             byte 28                 : new raid level
+**             byte 29                 : new stripe size
+**             (0/1/2/3/4/5->4/8/16/32/64/128K)
+**             byte 30                 : new channel
+**             byte 31                 : new ID
+**             byte 32                 : new LUN
+**             byte 33                 : 1 enable tag
+**             byte 34                 : 1 enable cache
+**             byte 35                 : speed
+**             (0/1/2/3/4->async/20/40/80/160 for scsi)
+**             (0/1/2/3/4->33/66/100/133/150 for IDE  )
+**     GUI_DELETE_VOLUME : Delete volume set
+**             byte 0,1        : length
+**             byte 2          : command code 0x62
+**             byte 3          : volumeset#
+**     GUI_START_CHECK_VOLUME : Start volume consistency check
+**             byte 0,1        : length
+**             byte 2          : command code 0x63
+**             byte 3          : volumeset#
+**     GUI_STOP_CHECK_VOLUME : Stop volume consistency check
+**             byte 0,1        : length
+**             byte 2          : command code 0x64
+** ---------------------------------------------------------------------
+**    4. Returned data
+** ---------------------------------------------------------------------
+**     (A) Header          : 3 bytes sequence (0x5E, 0x01, 0x61)
+**     (B) Length          : 2 bytes
+**                     (low byte 1st, excludes length and checksum byte)
+**     (C) status or data  :
+**           <1> If length == 1 ==> 1 byte status code
+**             #define GUI_OK                    0x41
+**             #define GUI_RAIDSET_NOT_NORMAL    0x42
+**             #define GUI_VOLUMESET_NOT_NORMAL  0x43
+**             #define GUI_NO_RAIDSET            0x44
+**             #define GUI_NO_VOLUMESET          0x45
+**             #define GUI_NO_PHYSICAL_DRIVE     0x46
+**             #define GUI_PARAMETER_ERROR       0x47
+**             #define GUI_UNSUPPORTED_COMMAND   0x48
+**             #define GUI_DISK_CONFIG_CHANGED   0x49
+**             #define GUI_INVALID_PASSWORD      0x4a
+**             #define GUI_NO_DISK_SPACE         0x4b
+**             #define GUI_CHECKSUM_ERROR        0x4c
+**             #define GUI_PASSWORD_REQUIRED     0x4d
+**           <2> If length > 1 ==>
+**             data block returned from controller
+**             and the contents depends on the command code
+**     (E) Checksum        : checksum of length and status or data byte
+**************************************************************************
diff --git a/Documentation/scsi/libsas.txt b/Documentation/scsi/libsas.txt
new file mode 100644 (file)
index 0000000..9e2078b
--- /dev/null
@@ -0,0 +1,484 @@
+SAS Layer
+---------
+
+The SAS Layer is a management infrastructure which manages
+SAS LLDDs.  It sits between SCSI Core and SAS LLDDs.  The
+layout is as follows: while SCSI Core is concerned with
+SAM/SPC issues, and a SAS LLDD+sequencer is concerned with
+phy/OOB/link management, the SAS layer is concerned with:
+
+      * SAS Phy/Port/HA event management (LLDD generates,
+        SAS Layer processes),
+      * SAS Port management (creation/destruction),
+      * SAS Domain discovery and revalidation,
+      * SAS Domain device management,
+      * SCSI Host registration/unregistration,
+      * Device registration with SCSI Core (SAS) or libata
+        (SATA), and
+      * Expander management and exporting expander control
+        to user space.
+
+A SAS LLDD is a PCI device driver.  It is concerned with
+phy/OOB management, and vendor specific tasks and generates
+events to the SAS layer.
+
+The SAS Layer does most SAS tasks as outlined in the SAS 1.1
+spec.
+
+The sas_ha_struct describes the SAS LLDD to the SAS layer.
+Most of it is used by the SAS Layer but a few fields need to
+be initialized by the LLDDs.
+
+After initializing your hardware, from the probe() function
+you call sas_register_ha(). It will register your LLDD with
+the SCSI subsystem, creating a SCSI host and it will
+register your SAS driver with the sysfs SAS tree it creates.
+It will then return.  Then you enable your phys to actually
+start OOB (at which point your driver will start calling the
+notify_* event callbacks).
+
+Structure descriptions:
+
+struct sas_phy --------------------
+Normally this is statically embedded to your driver's
+phy structure:
+       struct my_phy {
+              blah;
+              struct sas_phy sas_phy;
+              bleh;
+       };
+And then all the phys are an array of my_phy in your HA
+struct (shown below).
+
+Then as you go along and initialize your phys you also
+initialize the sas_phy struct, along with your own
+phy structure.
+
+In general, the phys are managed by the LLDD and the ports
+are managed by the SAS layer.  So the phys are initialized
+and updated by the LLDD and the ports are initialized and
+updated by the SAS layer.
+
+There is a scheme where the LLDD can RW certain fields,
+and the SAS layer can only read such ones, and vice versa.
+The idea is to avoid unnecessary locking.
+
+enabled -- must be set (0/1)
+id -- must be set [0,MAX_PHYS)
+class, proto, type, role, oob_mode, linkrate -- must be set
+oob_mode --  you set this when OOB has finished and then notify
+the SAS Layer.
+
+sas_addr -- this normally points to an array holding the sas
+address of the phy, possibly somewhere in your my_phy
+struct.
+
+attached_sas_addr -- set this when you (LLDD) receive an
+IDENTIFY frame or a FIS frame, _before_ notifying the SAS
+layer.  The idea is that sometimes the LLDD may want to fake
+or provide a different SAS address on that phy/port and this
+allows it to do this.  At best you should copy the sas
+address from the IDENTIFY frame or maybe generate a SAS
+address for SATA directly attached devices.  The Discover
+process may later change this.
+
+frame_rcvd -- this is where you copy the IDENTIFY/FIS frame
+when you get it; you lock, copy, set frame_rcvd_size and
+unlock the lock, and then call the event.  It is a pointer
+since there's no way to know your hw frame size _exactly_,
+so you define the actual array in your phy struct and let
+this pointer point to it.  You copy the frame from your
+DMAable memory to that area holding the lock.
+
+sas_prim -- this is where primitives go when they're
+received.  See sas.h. Grab the lock, set the primitive,
+release the lock, notify.
+
+port -- this points to the sas_port if the phy belongs
+to a port -- the LLDD only reads this. It points to the
+sas_port this phy is part of.  Set by the SAS Layer.
+
+ha -- may be set; the SAS layer sets it anyway.
+
+lldd_phy -- you should set this to point to your phy so you
+can find your way around faster when the SAS layer calls one
+of your callbacks and passes you a phy.  If the sas_phy is
+embedded you can also use container_of -- whatever you
+prefer.
+
+
+struct sas_port --------------------
+The LLDD doesn't set any fields of this struct -- it only
+reads them.  They should be self explanatory.
+
+phy_mask is 32 bit, this should be enough for now, as I
+haven't heard of a HA having more than 8 phys.
+
+lldd_port -- I haven't found use for that -- maybe other
+LLDD who wish to have internal port representation can make
+use of this.
+
+
+struct sas_ha_struct --------------------
+It normally is statically declared in your own LLDD
+structure describing your adapter:
+struct my_sas_ha {
+       blah;
+       struct sas_ha_struct sas_ha;
+       struct my_phy phys[MAX_PHYS];
+       struct sas_port sas_ports[MAX_PHYS]; /* (1) */
+       bleh;
+};
+
+(1) If your LLDD doesn't have its own port representation.
+
+What needs to be initialized (sample function given below).
+
+pcidev
+sas_addr -- since the SAS layer doesn't want to mess with
+        memory allocation, etc, this points to statically
+        allocated array somewhere (say in your host adapter
+        structure) and holds the SAS address of the host
+        adapter as given by you or the manufacturer, etc.
+sas_port
+sas_phy -- an array of pointers to structures. (see
+       note above on sas_addr).
+       These must be set.  See more notes below.
+num_phys -- the number of phys present in the sas_phy array,
+        and the number of ports present in the sas_port
+        array.  There can be a maximum num_phys ports (one per
+        port) so we drop the num_ports, and only use
+        num_phys.
+
+The event interface:
+
+       /* LLDD calls these to notify the class of an event. */
+       void (*notify_ha_event)(struct sas_ha_struct *, enum ha_event);
+       void (*notify_port_event)(struct sas_phy *, enum port_event);
+       void (*notify_phy_event)(struct sas_phy *, enum phy_event);
+
+When sas_register_ha() returns, those are set and can be
+called by the LLDD to notify the SAS layer of such events
+the SAS layer.
+
+The port notification:
+
+       /* The class calls these to notify the LLDD of an event. */
+       void (*lldd_port_formed)(struct sas_phy *);
+       void (*lldd_port_deformed)(struct sas_phy *);
+
+If the LLDD wants notification when a port has been formed
+or deformed it sets those to a function satisfying the type.
+
+A SAS LLDD should also implement at least one of the Task
+Management Functions (TMFs) described in SAM:
+
+       /* Task Management Functions. Must be called from process context. */
+       int (*lldd_abort_task)(struct sas_task *);
+       int (*lldd_abort_task_set)(struct domain_device *, u8 *lun);
+       int (*lldd_clear_aca)(struct domain_device *, u8 *lun);
+       int (*lldd_clear_task_set)(struct domain_device *, u8 *lun);
+       int (*lldd_I_T_nexus_reset)(struct domain_device *);
+       int (*lldd_lu_reset)(struct domain_device *, u8 *lun);
+       int (*lldd_query_task)(struct sas_task *);
+
+For more information please read SAM from T10.org.
+
+Port and Adapter management:
+
+       /* Port and Adapter management */
+       int (*lldd_clear_nexus_port)(struct sas_port *);
+       int (*lldd_clear_nexus_ha)(struct sas_ha_struct *);
+
+A SAS LLDD should implement at least one of those.
+
+Phy management:
+
+       /* Phy management */
+       int (*lldd_control_phy)(struct sas_phy *, enum phy_func);
+
+lldd_ha -- set this to point to your HA struct. You can also
+use container_of if you embedded it as shown above.
+
+A sample initialization and registration function
+can look like this (called last thing from probe())
+*but* before you enable the phys to do OOB:
+
+static int register_sas_ha(struct my_sas_ha *my_ha)
+{
+       int i;
+       static struct sas_phy   *sas_phys[MAX_PHYS];
+       static struct sas_port  *sas_ports[MAX_PHYS];
+
+       my_ha->sas_ha.sas_addr = &my_ha->sas_addr[0];
+
+       for (i = 0; i < MAX_PHYS; i++) {
+               sas_phys[i] = &my_ha->phys[i].sas_phy;
+               sas_ports[i] = &my_ha->sas_ports[i];
+       }
+
+       my_ha->sas_ha.sas_phy  = sas_phys;
+       my_ha->sas_ha.sas_port = sas_ports;
+       my_ha->sas_ha.num_phys = MAX_PHYS;
+
+       my_ha->sas_ha.lldd_port_formed = my_port_formed;
+
+       my_ha->sas_ha.lldd_dev_found = my_dev_found;
+       my_ha->sas_ha.lldd_dev_gone = my_dev_gone;
+
+       my_ha->sas_ha.lldd_max_execute_num = lldd_max_execute_num; (1)
+
+       my_ha->sas_ha.lldd_queue_size = ha_can_queue;
+       my_ha->sas_ha.lldd_execute_task = my_execute_task;
+
+       my_ha->sas_ha.lldd_abort_task     = my_abort_task;
+       my_ha->sas_ha.lldd_abort_task_set = my_abort_task_set;
+       my_ha->sas_ha.lldd_clear_aca      = my_clear_aca;
+       my_ha->sas_ha.lldd_clear_task_set = my_clear_task_set;
+       my_ha->sas_ha.lldd_I_T_nexus_reset= NULL; (2)
+       my_ha->sas_ha.lldd_lu_reset       = my_lu_reset;
+       my_ha->sas_ha.lldd_query_task     = my_query_task;
+
+       my_ha->sas_ha.lldd_clear_nexus_port = my_clear_nexus_port;
+       my_ha->sas_ha.lldd_clear_nexus_ha = my_clear_nexus_ha;
+
+       my_ha->sas_ha.lldd_control_phy = my_control_phy;
+
+       return sas_register_ha(&my_ha->sas_ha);
+}
+
+(1) This is normally a LLDD parameter, something of the
+lines of a task collector.  What it tells the SAS Layer is
+whether the SAS layer should run in Direct Mode (default:
+value 0 or 1) or Task Collector Mode (value greater than 1).
+
+In Direct Mode, the SAS Layer calls Execute Task as soon as
+it has a command to send to the SDS, _and_ this is a single
+command, i.e. not linked.
+
+Some hardware (e.g. aic94xx) has the capability to DMA more
+than one task at a time (interrupt) from host memory.  Task
+Collector Mode is an optional feature for HAs which support
+this in their hardware.  (Again, it is completely optional
+even if your hardware supports it.)
+
+In Task Collector Mode, the SAS Layer would do _natural_
+coalescing of tasks and at the appropriate moment it would
+call your driver to DMA more than one task in a single HA
+interrupt. DMBS may want to use this by insmod/modprobe
+setting the lldd_max_execute_num to something greater than
+1.
+
+(2) SAS 1.1 does not define I_T Nexus Reset TMF.
+
+Events
+------
+
+Events are _the only way_ a SAS LLDD notifies the SAS layer
+of anything.  There is no other method or way a LLDD to tell
+the SAS layer of anything happening internally or in the SAS
+domain.
+
+Phy events:
+       PHYE_LOSS_OF_SIGNAL, (C)
+       PHYE_OOB_DONE,
+       PHYE_OOB_ERROR,      (C)
+       PHYE_SPINUP_HOLD.
+
+Port events, passed on a _phy_:
+       PORTE_BYTES_DMAED,      (M)
+       PORTE_BROADCAST_RCVD,   (E)
+       PORTE_LINK_RESET_ERR,   (C)
+       PORTE_TIMER_EVENT,      (C)
+       PORTE_HARD_RESET.
+
+Host Adapter event:
+       HAE_RESET
+
+A SAS LLDD should be able to generate
+       - at least one event from group C (choice),
+       - events marked M (mandatory) are mandatory (only one),
+       - events marked E (expander) if it wants the SAS layer
+         to handle domain revalidation (only one such).
+       - Unmarked events are optional.
+
+Meaning:
+
+HAE_RESET -- when your HA got internal error and was reset.
+
+PORTE_BYTES_DMAED -- on receiving an IDENTIFY/FIS frame
+PORTE_BROADCAST_RCVD -- on receiving a primitive
+PORTE_LINK_RESET_ERR -- timer expired, loss of signal, loss
+of DWS, etc. (*)
+PORTE_TIMER_EVENT -- DWS reset timeout timer expired (*)
+PORTE_HARD_RESET -- Hard Reset primitive received.
+
+PHYE_LOSS_OF_SIGNAL -- the device is gone (*)
+PHYE_OOB_DONE -- OOB went fine and oob_mode is valid
+PHYE_OOB_ERROR -- Error while doing OOB, the device probably
+got disconnected. (*)
+PHYE_SPINUP_HOLD -- SATA is present, COMWAKE not sent.
+
+(*) should set/clear the appropriate fields in the phy,
+    or alternatively call the inlined sas_phy_disconnected()
+    which is just a helper, from their tasklet.
+
+The Execute Command SCSI RPC:
+
+       int (*lldd_execute_task)(struct sas_task *, int num,
+                                unsigned long gfp_flags);
+
+Used to queue a task to the SAS LLDD.  @task is the tasks to
+be executed.  @num should be the number of tasks being
+queued at this function call (they are linked listed via
+task::list), @gfp_mask should be the gfp_mask defining the
+context of the caller.
+
+This function should implement the Execute Command SCSI RPC,
+or if you're sending a SCSI Task as linked commands, you
+should also use this function.
+
+That is, when lldd_execute_task() is called, the command(s)
+go out on the transport *immediately*.  There is *no*
+queuing of any sort and at any level in a SAS LLDD.
+
+The use of task::list is two-fold, one for linked commands,
+the other discussed below.
+
+It is possible to queue up more than one task at a time, by
+initializing the list element of struct sas_task, and
+passing the number of tasks enlisted in this manner in num.
+
+Returns: -SAS_QUEUE_FULL, -ENOMEM, nothing was queued;
+        0, the task(s) were queued.
+
+If you want to pass num > 1, then either
+A) you're the only caller of this function and keep track
+   of what you've queued to the LLDD, or
+B) you know what you're doing and have a strategy of
+   retrying.
+
+As opposed to queuing one task at a time (function call),
+batch queuing of tasks, by having num > 1, greatly
+simplifies LLDD code, sequencer code, and _hardware design_,
+and has some performance advantages in certain situations
+(DBMS).
+
+The LLDD advertises if it can take more than one command at
+a time at lldd_execute_task(), by setting the
+lldd_max_execute_num parameter (controlled by "collector"
+module parameter in aic94xx SAS LLDD).
+
+You should leave this to the default 1, unless you know what
+you're doing.
+
+This is a function of the LLDD, to which the SAS layer can
+cater to.
+
+int lldd_queue_size
+       The host adapter's queue size.  This is the maximum
+number of commands the lldd can have pending to domain
+devices on behalf of all upper layers submitting through
+lldd_execute_task().
+
+You really want to set this to something (much) larger than
+1.
+
+This _really_ has absolutely nothing to do with queuing.
+There is no queuing in SAS LLDDs.
+
+struct sas_task {
+       dev -- the device this task is destined to
+       list -- must be initialized (INIT_LIST_HEAD)
+       task_proto -- _one_ of enum sas_proto
+       scatter -- pointer to scatter gather list array
+       num_scatter -- number of elements in scatter
+       total_xfer_len -- total number of bytes expected to be transfered
+       data_dir -- PCI_DMA_...
+       task_done -- callback when the task has finished execution
+};
+
+When an external entity, entity other than the LLDD or the
+SAS Layer, wants to work with a struct domain_device, it
+_must_ call kobject_get() when getting a handle on the
+device and kobject_put() when it is done with the device.
+
+This does two things:
+     A) implements proper kfree() for the device;
+     B) increments/decrements the kref for all players:
+     domain_device
+       all domain_device's ... (if past an expander)
+           port
+               host adapter
+                    pci device
+                        and up the ladder, etc.
+
+DISCOVERY
+---------
+
+The sysfs tree has the following purposes:
+    a) It shows you the physical layout of the SAS domain at
+       the current time, i.e. how the domain looks in the
+       physical world right now.
+    b) Shows some device parameters _at_discovery_time_.
+
+This is a link to the tree(1) program, very useful in
+viewing the SAS domain:
+ftp://mama.indstate.edu/linux/tree/
+I expect user space applications to actually create a
+graphical interface of this.
+
+That is, the sysfs domain tree doesn't show or keep state if
+you e.g., change the meaning of the READY LED MEANING
+setting, but it does show you the current connection status
+of the domain device.
+
+Keeping internal device state changes is responsibility of
+upper layers (Command set drivers) and user space.
+
+When a device or devices are unplugged from the domain, this
+is reflected in the sysfs tree immediately, and the device(s)
+removed from the system.
+
+The structure domain_device describes any device in the SAS
+domain.  It is completely managed by the SAS layer.  A task
+points to a domain device, this is how the SAS LLDD knows
+where to send the task(s) to.  A SAS LLDD only reads the
+contents of the domain_device structure, but it never creates
+or destroys one.
+
+Expander management from User Space
+-----------------------------------
+
+In each expander directory in sysfs, there is a file called
+"smp_portal".  It is a binary sysfs attribute file, which
+implements an SMP portal (Note: this is *NOT* an SMP port),
+to which user space applications can send SMP requests and
+receive SMP responses.
+
+Functionality is deceptively simple:
+
+1. Build the SMP frame you want to send. The format and layout
+   is described in the SAS spec.  Leave the CRC field equal 0.
+open(2)
+2. Open the expander's SMP portal sysfs file in RW mode.
+write(2)
+3. Write the frame you built in 1.
+read(2)
+4. Read the amount of data you expect to receive for the frame you built.
+   If you receive different amount of data you expected to receive,
+   then there was some kind of error.
+close(2)
+All this process is shown in detail in the function do_smp_func()
+and its callers, in the file "expander_conf.c".
+
+The kernel functionality is implemented in the file
+"sas_expander.c".
+
+The program "expander_conf.c" implements this. It takes one
+argument, the sysfs file name of the SMP portal to the
+expander, and gives expander information, including routing
+tables.
+
+The SMP portal gives you complete control of the expander,
+so please be careful.
index ddd9253..556a3d3 100644 (file)
@@ -848,21 +848,18 @@ struct request *blk_queue_find_tag(request_queue_t *q, int tag)
 EXPORT_SYMBOL(blk_queue_find_tag);
 
 /**
- * __blk_queue_free_tags - release tag maintenance info
- * @q:  the request queue for the device
+ * __blk_free_tags - release a given set of tag maintenance info
+ * @bqt:       the tag map to free
  *
- *  Notes:
- *    blk_cleanup_queue() will take care of calling this function, if tagging
- *    has been used. So there's no need to call this directly.
- **/
-static void __blk_queue_free_tags(request_queue_t *q)
+ * Tries to free the specified @bqt@.  Returns true if it was
+ * actually freed and false if there are still references using it
+ */
+static int __blk_free_tags(struct blk_queue_tag *bqt)
 {
-       struct blk_queue_tag *bqt = q->queue_tags;
-
-       if (!bqt)
-               return;
+       int retval;
 
-       if (atomic_dec_and_test(&bqt->refcnt)) {
+       retval = atomic_dec_and_test(&bqt->refcnt);
+       if (retval) {
                BUG_ON(bqt->busy);
                BUG_ON(!list_empty(&bqt->busy_list));
 
@@ -873,12 +870,49 @@ static void __blk_queue_free_tags(request_queue_t *q)
                bqt->tag_map = NULL;
 
                kfree(bqt);
+
        }
 
+       return retval;
+}
+
+/**
+ * __blk_queue_free_tags - release tag maintenance info
+ * @q:  the request queue for the device
+ *
+ *  Notes:
+ *    blk_cleanup_queue() will take care of calling this function, if tagging
+ *    has been used. So there's no need to call this directly.
+ **/
+static void __blk_queue_free_tags(request_queue_t *q)
+{
+       struct blk_queue_tag *bqt = q->queue_tags;
+
+       if (!bqt)
+               return;
+
+       __blk_free_tags(bqt);
+
        q->queue_tags = NULL;
        q->queue_flags &= ~(1 << QUEUE_FLAG_QUEUED);
 }
 
+
+/**
+ * blk_free_tags - release a given set of tag maintenance info
+ * @bqt:       the tag map to free
+ *
+ * For externally managed @bqt@ frees the map.  Callers of this
+ * function must guarantee to have released all the queues that
+ * might have been using this tag map.
+ */
+void blk_free_tags(struct blk_queue_tag *bqt)
+{
+       if (unlikely(!__blk_free_tags(bqt)))
+               BUG();
+}
+EXPORT_SYMBOL(blk_free_tags);
+
 /**
  * blk_queue_free_tags - release tag maintenance info
  * @q:  the request queue for the device
@@ -901,7 +935,7 @@ init_tag_map(request_queue_t *q, struct blk_queue_tag *tags, int depth)
        unsigned long *tag_map;
        int nr_ulongs;
 
-       if (depth > q->nr_requests * 2) {
+       if (q && depth > q->nr_requests * 2) {
                depth = q->nr_requests * 2;
                printk(KERN_ERR "%s: adjusted depth to %d\n",
                                __FUNCTION__, depth);
@@ -927,6 +961,38 @@ fail:
        return -ENOMEM;
 }
 
+static struct blk_queue_tag *__blk_queue_init_tags(struct request_queue *q,
+                                                  int depth)
+{
+       struct blk_queue_tag *tags;
+
+       tags = kmalloc(sizeof(struct blk_queue_tag), GFP_ATOMIC);
+       if (!tags)
+               goto fail;
+
+       if (init_tag_map(q, tags, depth))
+               goto fail;
+
+       INIT_LIST_HEAD(&tags->busy_list);
+       tags->busy = 0;
+       atomic_set(&tags->refcnt, 1);
+       return tags;
+fail:
+       kfree(tags);
+       return NULL;
+}
+
+/**
+ * blk_init_tags - initialize the tag info for an external tag map
+ * @depth:     the maximum queue depth supported
+ * @tags: the tag to use
+ **/
+struct blk_queue_tag *blk_init_tags(int depth)
+{
+       return __blk_queue_init_tags(NULL, depth);
+}
+EXPORT_SYMBOL(blk_init_tags);
+
 /**
  * blk_queue_init_tags - initialize the queue tag info
  * @q:  the request queue for the device
@@ -941,16 +1007,10 @@ int blk_queue_init_tags(request_queue_t *q, int depth,
        BUG_ON(tags && q->queue_tags && tags != q->queue_tags);
 
        if (!tags && !q->queue_tags) {
-               tags = kmalloc(sizeof(struct blk_queue_tag), GFP_ATOMIC);
-               if (!tags)
-                       goto fail;
+               tags = __blk_queue_init_tags(q, depth);
 
-               if (init_tag_map(q, tags, depth))
+               if (!tags)
                        goto fail;
-
-               INIT_LIST_HEAD(&tags->busy_list);
-               tags->busy = 0;
-               atomic_set(&tags->refcnt, 1);
        } else if (q->queue_tags) {
                if ((rc = blk_queue_resize_tags(q, depth)))
                        return rc;
@@ -1002,6 +1062,13 @@ int blk_queue_resize_tags(request_queue_t *q, int new_depth)
        }
 
        /*
+        * Currently cannot replace a shared tag map with a new
+        * one, so error out if this is the case
+        */
+       if (atomic_read(&bqt->refcnt) != 1)
+               return -EBUSY;
+
+       /*
         * save the old state info, so we can copy it back
         */
        tag_index = bqt->tag_index;
index 4cd23c3..a360215 100644 (file)
@@ -7115,7 +7115,7 @@ static struct pci_device_id DAC960_id_table[] = {
        {
                .vendor         = PCI_VENDOR_ID_MYLEX,
                .device         = PCI_DEVICE_ID_MYLEX_DAC960_GEM,
-               .subvendor      = PCI_ANY_ID,
+               .subvendor      = PCI_VENDOR_ID_MYLEX,
                .subdevice      = PCI_ANY_ID,
                .driver_data    = (unsigned long) &DAC960_GEM_privdata,
        },
index afdff32..05f79d7 100644 (file)
@@ -251,10 +251,6 @@ scsi_cmd_stack_free(int ctlr)
        stk->pool = NULL;
 }
 
-/* scsi_device_types comes from scsi.h */
-#define DEVICETYPE(n) (n<0 || n>MAX_SCSI_DEVICE_CODE) ? \
-       "Unknown" : scsi_device_types[n]
-
 #if 0
 static int xmargin=8;
 static int amargin=60;
@@ -389,7 +385,7 @@ cciss_scsi_add_entry(int ctlr, int hostno,
           time anyway (the scsi layer's inquiries will show that info) */
        if (hostno != -1)
                printk("cciss%d: %s device c%db%dt%dl%d added.\n", 
-                       ctlr, DEVICETYPE(sd->devtype), hostno, 
+                       ctlr, scsi_device_type(sd->devtype), hostno,
                        sd->bus, sd->target, sd->lun);
        return 0;
 }
@@ -407,7 +403,7 @@ cciss_scsi_remove_entry(int ctlr, int hostno, int entry)
                ccissscsi[ctlr].dev[i] = ccissscsi[ctlr].dev[i+1];
        ccissscsi[ctlr].ndevices--;
        printk("cciss%d: %s device c%db%dt%dl%d removed.\n",
-               ctlr, DEVICETYPE(sd.devtype), hostno, 
+               ctlr, scsi_device_type(sd.devtype), hostno,
                        sd.bus, sd.target, sd.lun);
 }
 
@@ -458,7 +454,7 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
                if (found == 0) { /* device no longer present. */ 
                        changes++;
                        /* printk("cciss%d: %s device c%db%dt%dl%d removed.\n",
-                               ctlr, DEVICETYPE(csd->devtype), hostno, 
+                               ctlr, scsi_device_type(csd->devtype), hostno,
                                        csd->bus, csd->target, csd->lun); */
                        cciss_scsi_remove_entry(ctlr, hostno, i);
                        /* note, i not incremented */
@@ -468,7 +464,7 @@ adjust_cciss_scsi_table(int ctlr, int hostno,
                        printk("cciss%d: device c%db%dt%dl%d type changed "
                                "(device type now %s).\n",
                                ctlr, hostno, csd->bus, csd->target, csd->lun,
-                                       DEVICETYPE(csd->devtype));
+                                       scsi_device_type(csd->devtype));
                        csd->devtype = sd[j].devtype;
                        i++;    /* so just move along. */
                } else          /* device is same as it ever was, */
@@ -1098,7 +1094,7 @@ cciss_update_non_disk_devices(int cntl_num, int hostno)
                        if (ncurrent >= CCISS_MAX_SCSI_DEVS_PER_HBA) {
                                printk(KERN_INFO "cciss%d: %s ignored, "
                                        "too many devices.\n", cntl_num,
-                                       DEVICETYPE(devtype));
+                                       scsi_device_type(devtype));
                                break;
                        }
                        memcpy(&currentsd[ncurrent].scsi3addr[0], 
index e9cf1a9..2a14fe2 100644 (file)
@@ -141,18 +141,11 @@ iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
 
        if (sc->sc_data_direction == DMA_TO_DEVICE) {
                BUG_ON(ctask->total_length == 0);
-               /* bytes to be sent via RDMA operations */
-               iser_ctask->rdma_data_count = ctask->total_length -
-                                        ctask->imm_count -
-                                        ctask->unsol_count;
 
-               debug_scsi("cmd [itt %x total %d imm %d unsol_data %d "
-                          "rdma_data %d]\n",
+               debug_scsi("cmd [itt %x total %d imm %d unsol_data %d\n",
                           ctask->itt, ctask->total_length, ctask->imm_count,
-                          ctask->unsol_count, iser_ctask->rdma_data_count);
-       } else
-               /* bytes to be sent via RDMA operations */
-               iser_ctask->rdma_data_count = ctask->total_length;
+                          ctask->unsol_count);
+       }
 
        iser_ctask_rdma_init(iser_ctask);
 }
@@ -196,13 +189,10 @@ iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
 {
        struct iscsi_data  hdr;
        int error = 0;
-       struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
 
        /* Send data-out PDUs while there's still unsolicited data to send */
        while (ctask->unsol_count > 0) {
-               iscsi_prep_unsolicit_data_pdu(ctask, &hdr,
-                                             iser_ctask->rdma_data_count);
-
+               iscsi_prep_unsolicit_data_pdu(ctask, &hdr);
                debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
                           hdr.itt, ctask->data_count);
 
index 7e1a411..2cf9ae0 100644 (file)
@@ -262,7 +262,6 @@ struct iscsi_iser_conn {
 struct iscsi_iser_cmd_task {
        struct iser_desc             desc;
        struct iscsi_iser_conn       *iser_conn;
-       int                          rdma_data_count;/* RDMA bytes           */
        enum iser_task_status        status;
        int                          command_sent;  /* set if command  sent  */
        int                          dir[ISER_DIRS_NUM];      /* set if dir use*/
index 85696f3..e57bb03 100644 (file)
@@ -162,7 +162,13 @@ static struct fc_function_template mptfc_transport_functions = {
        .show_starget_port_id = 1,
        .set_rport_dev_loss_tmo = mptfc_set_rport_loss_tmo,
        .show_rport_dev_loss_tmo = 1,
-
+       .show_host_supported_speeds = 1,
+       .show_host_maxframe_size = 1,
+       .show_host_speed = 1,
+       .show_host_fabric_name = 1,
+       .show_host_port_type = 1,
+       .show_host_port_state = 1,
+       .show_host_symbolic_name = 1,
 };
 
 static void
@@ -839,33 +845,95 @@ mptfc_SetFcPortPage1_defaults(MPT_ADAPTER *ioc)
 static void
 mptfc_init_host_attr(MPT_ADAPTER *ioc,int portnum)
 {
-       unsigned class = 0, cos = 0;
+       unsigned        class = 0;
+       unsigned        cos = 0;
+       unsigned        speed;
+       unsigned        port_type;
+       unsigned        port_state;
+       FCPortPage0_t   *pp0;
+       struct Scsi_Host *sh;
+       char            *sn;
 
        /* don't know what to do as only one scsi (fc) host was allocated */
        if (portnum != 0)
                return;
 
-       class = ioc->fc_port_page0[portnum].SupportedServiceClass;
+       pp0 = &ioc->fc_port_page0[portnum];
+       sh = ioc->sh;
+
+       sn = fc_host_symbolic_name(sh);
+       snprintf(sn, FC_SYMBOLIC_NAME_SIZE, "%s %s%08xh",
+           ioc->prod_name,
+           MPT_FW_REV_MAGIC_ID_STRING,
+           ioc->facts.FWVersion.Word);
+
+       fc_host_tgtid_bind_type(sh) = FC_TGTID_BIND_BY_WWPN;
+
+       fc_host_maxframe_size(sh) = pp0->MaxFrameSize;
+
+       fc_host_node_name(sh) =
+               (u64)pp0->WWNN.High << 32 | (u64)pp0->WWNN.Low;
+
+       fc_host_port_name(sh) =
+               (u64)pp0->WWPN.High << 32 | (u64)pp0->WWPN.Low;
+
+       fc_host_port_id(sh) = pp0->PortIdentifier;
+
+       class = pp0->SupportedServiceClass;
        if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_1)
                cos |= FC_COS_CLASS1;
        if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_2)
                cos |= FC_COS_CLASS2;
        if (class & MPI_FCPORTPAGE0_SUPPORT_CLASS_3)
                cos |= FC_COS_CLASS3;
+       fc_host_supported_classes(sh) = cos;
+
+       if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT)
+               speed = FC_PORTSPEED_1GBIT;
+       else if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT)
+               speed = FC_PORTSPEED_2GBIT;
+       else if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_4GBIT)
+               speed = FC_PORTSPEED_4GBIT;
+       else if (pp0->CurrentSpeed == MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT)
+               speed = FC_PORTSPEED_10GBIT;
+       else
+               speed = FC_PORTSPEED_UNKNOWN;
+       fc_host_speed(sh) = speed;
+
+       speed = 0;
+       if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED)
+               speed |= FC_PORTSPEED_1GBIT;
+       if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED)
+               speed |= FC_PORTSPEED_2GBIT;
+       if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_4GBIT_SPEED)
+               speed |= FC_PORTSPEED_4GBIT;
+       if (pp0->SupportedSpeeds & MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED)
+               speed |= FC_PORTSPEED_10GBIT;
+       fc_host_supported_speeds(sh) = speed;
+
+       port_state = FC_PORTSTATE_UNKNOWN;
+       if (pp0->PortState == MPI_FCPORTPAGE0_PORTSTATE_ONLINE)
+               port_state = FC_PORTSTATE_ONLINE;
+       else if (pp0->PortState == MPI_FCPORTPAGE0_PORTSTATE_OFFLINE)
+               port_state = FC_PORTSTATE_LINKDOWN;
+       fc_host_port_state(sh) = port_state;
+
+       port_type = FC_PORTTYPE_UNKNOWN;
+       if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_POINT_TO_POINT)
+               port_type = FC_PORTTYPE_PTP;
+       else if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_PRIVATE_LOOP)
+               port_type = FC_PORTTYPE_LPORT;
+       else if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_PUBLIC_LOOP)
+               port_type = FC_PORTTYPE_NLPORT;
+       else if (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_ATTACH_FABRIC_DIRECT)
+               port_type = FC_PORTTYPE_NPORT;
+       fc_host_port_type(sh) = port_type;
+
+       fc_host_fabric_name(sh) =
+           (pp0->Flags & MPI_FCPORTPAGE0_FLAGS_FABRIC_WWN_VALID) ?
+               (u64) pp0->FabricWWNN.High << 32 | (u64) pp0->FabricWWPN.Low :
+               (u64)pp0->WWNN.High << 32 | (u64)pp0->WWNN.Low;
 
-       fc_host_node_name(ioc->sh) =
-               (u64)ioc->fc_port_page0[portnum].WWNN.High << 32
-                   | (u64)ioc->fc_port_page0[portnum].WWNN.Low;
-
-       fc_host_port_name(ioc->sh) =
-               (u64)ioc->fc_port_page0[portnum].WWPN.High << 32
-                   | (u64)ioc->fc_port_page0[portnum].WWPN.Low;
-
-       fc_host_port_id(ioc->sh) = ioc->fc_port_page0[portnum].PortIdentifier;
-
-       fc_host_supported_classes(ioc->sh) = cos;
-
-       fc_host_tgtid_bind_type(ioc->sh) = FC_TGTID_BIND_BY_WWPN;
 }
 
 static void
index f66f220..b752a47 100644 (file)
@@ -852,6 +852,10 @@ static int mptsas_get_linkerrors(struct sas_phy *phy)
        dma_addr_t dma_handle;
        int error;
 
+       /* FIXME: only have link errors on local phys */
+       if (!scsi_is_sas_phy_local(phy))
+               return -EINVAL;
+
        hdr.PageVersion = MPI_SASPHY1_PAGEVERSION;
        hdr.ExtPageLength = 0;
        hdr.PageNumber = 1 /* page number 1*/;
@@ -924,6 +928,10 @@ static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
        unsigned long timeleft;
        int error = -ERESTARTSYS;
 
+       /* FIXME: fusion doesn't allow non-local phy reset */
+       if (!scsi_is_sas_phy_local(phy))
+               return -EINVAL;
+
        /* not implemented for expanders */
        if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP)
                return -ENXIO;
@@ -1570,9 +1578,6 @@ static int mptsas_probe_one_phy(struct device *dev,
 
        if (!phy_info->phy) {
 
-               if (local)
-                       phy->local_attached = 1;
-
                error = sas_phy_add(phy);
                if (error) {
                        sas_phy_free(phy);
@@ -1642,14 +1647,18 @@ static int mptsas_probe_one_phy(struct device *dev,
 
                        for (i = 0; i < port_info->num_phys; i++)
                                if (port_info->phy_info[i].identify.sas_address ==
-                                   identify.sas_address)
+                                   identify.sas_address) {
+                                       sas_port_mark_backlink(port);
                                        goto out;
+                               }
 
                } else if (scsi_is_sas_rphy(parent)) {
                        struct sas_rphy *parent_rphy = dev_to_rphy(parent);
                        if (identify.sas_address ==
-                           parent_rphy->identify.sas_address)
+                           parent_rphy->identify.sas_address) {
+                               sas_port_mark_backlink(port);
                                goto out;
+                       }
                }
 
                switch (identify.device_type) {
index 16a12a3..59d1ada 100644 (file)
@@ -2176,6 +2176,7 @@ static int __init BusLogic_init(void)
 {
        int BusLogicHostAdapterCount = 0, DriverOptionsIndex = 0, ProbeIndex;
        struct BusLogic_HostAdapter *PrototypeHostAdapter;
+       int ret = 0;
 
 #ifdef MODULE
        if (BusLogic)
@@ -2282,25 +2283,49 @@ static int __init BusLogic_init(void)
                   perform Target Device Inquiry.
                 */
                if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
-                   BusLogic_ReportHostAdapterConfiguration(HostAdapter) && BusLogic_AcquireResources(HostAdapter) && BusLogic_CreateInitialCCBs(HostAdapter) && BusLogic_InitializeHostAdapter(HostAdapter) && BusLogic_TargetDeviceInquiry(HostAdapter)) {
+                   BusLogic_ReportHostAdapterConfiguration(HostAdapter) &&
+                   BusLogic_AcquireResources(HostAdapter) &&
+                   BusLogic_CreateInitialCCBs(HostAdapter) &&
+                   BusLogic_InitializeHostAdapter(HostAdapter) &&
+                   BusLogic_TargetDeviceInquiry(HostAdapter)) {
                        /*
                           Initialization has been completed successfully.  Release and
                           re-register usage of the I/O Address range so that the Model
                           Name of the Host Adapter will appear, and initialize the SCSI
                           Host structure.
                         */
-                       release_region(HostAdapter->IO_Address, HostAdapter->AddressCount);
-                       if (!request_region(HostAdapter->IO_Address, HostAdapter->AddressCount, HostAdapter->FullModelName)) {
-                               printk(KERN_WARNING "BusLogic: Release and re-register of " "port 0x%04lx failed \n", (unsigned long) HostAdapter->IO_Address);
+                       release_region(HostAdapter->IO_Address,
+                                      HostAdapter->AddressCount);
+                       if (!request_region(HostAdapter->IO_Address,
+                                           HostAdapter->AddressCount,
+                                           HostAdapter->FullModelName)) {
+                               printk(KERN_WARNING
+                                       "BusLogic: Release and re-register of "
+                                       "port 0x%04lx failed \n",
+                                       (unsigned long)HostAdapter->IO_Address);
                                BusLogic_DestroyCCBs(HostAdapter);
                                BusLogic_ReleaseResources(HostAdapter);
                                list_del(&HostAdapter->host_list);
                                scsi_host_put(Host);
+                               ret = -ENOMEM;
                        } else {
-                               BusLogic_InitializeHostStructure(HostAdapter, Host);
-                               scsi_add_host(Host, HostAdapter->PCI_Device ? &HostAdapter->PCI_Device->dev : NULL);
-                               scsi_scan_host(Host);
-                               BusLogicHostAdapterCount++;
+                               BusLogic_InitializeHostStructure(HostAdapter,
+                                                                Host);
+                               if (scsi_add_host(Host, HostAdapter->PCI_Device
+                                               ? &HostAdapter->PCI_Device->dev
+                                                 : NULL)) {
+                                       printk(KERN_WARNING
+                                              "BusLogic: scsi_add_host()"
+                                              "failed!\n");
+                                       BusLogic_DestroyCCBs(HostAdapter);
+                                       BusLogic_ReleaseResources(HostAdapter);
+                                       list_del(&HostAdapter->host_list);
+                                       scsi_host_put(Host);
+                                       ret = -ENODEV;
+                               } else {
+                                       scsi_scan_host(Host);
+                                       BusLogicHostAdapterCount++;
+                               }
                        }
                } else {
                        /*
@@ -2315,12 +2340,13 @@ static int __init BusLogic_init(void)
                        BusLogic_ReleaseResources(HostAdapter);
                        list_del(&HostAdapter->host_list);
                        scsi_host_put(Host);
+                       ret = -ENODEV;
                }
        }
        kfree(PrototypeHostAdapter);
        kfree(BusLogic_ProbeInfoList);
        BusLogic_ProbeInfoList = NULL;
-       return 0;
+       return ret;
 }
 
 
@@ -2954,6 +2980,7 @@ static int BusLogic_QueueCommand(struct scsi_cmnd *Command, void (*CompletionRou
 }
 
 
+#if 0
 /*
   BusLogic_AbortCommand aborts Command if possible.
 */
@@ -3024,6 +3051,7 @@ static int BusLogic_AbortCommand(struct scsi_cmnd *Command)
        return SUCCESS;
 }
 
+#endif
 /*
   BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
   currently executing SCSI Commands as having been Reset.
index 96a81cd..a6f920d 100644 (file)
@@ -27,6 +27,11 @@ config SCSI
          However, do not compile this as a module if your root file system
          (the one containing the directory /) is located on a SCSI device.
 
+config SCSI_NETLINK
+       bool
+       default n
+       select NET
+
 config SCSI_PROC_FS
        bool "legacy /proc/scsi/ support"
        depends on SCSI && PROC_FS
@@ -209,7 +214,7 @@ config SCSI_LOGGING
          there should be no noticeable performance impact as long as you have
          logging turned off.
 
-menu "SCSI Transport Attributes"
+menu "SCSI Transports"
        depends on SCSI
 
 config SCSI_SPI_ATTRS
@@ -222,6 +227,7 @@ config SCSI_SPI_ATTRS
 config SCSI_FC_ATTRS
        tristate "FiberChannel Transport Attributes"
        depends on SCSI
+       select SCSI_NETLINK
        help
          If you wish to export transport-specific information about
          each attached FiberChannel device to sysfs, say Y.
@@ -242,6 +248,8 @@ config SCSI_SAS_ATTRS
          If you wish to export transport-specific information about
          each attached SAS device to sysfs, say Y.
 
+source "drivers/scsi/libsas/Kconfig"
+
 endmenu
 
 menu "SCSI low-level drivers"
@@ -431,6 +439,7 @@ config SCSI_AIC7XXX_OLD
          module will be called aic7xxx_old.
 
 source "drivers/scsi/aic7xxx/Kconfig.aic79xx"
+source "drivers/scsi/aic94xx/Kconfig"
 
 # All the I2O code and drivers do not seem to be 64bit safe.
 config SCSI_DPT_I2O
@@ -469,6 +478,20 @@ config SCSI_IN2000
          To compile this driver as a module, choose M here: the
          module will be called in2000.
 
+config SCSI_ARCMSR
+       tristate "ARECA ARC11X0[PCI-X]/ARC12X0[PCI-EXPRESS] SATA-RAID support"
+       depends on PCI && SCSI
+       help
+         This driver supports all of ARECA's SATA RAID controller cards.
+         This is an ARECA-maintained driver by Erich Chen.
+         If you have any problems, please mail to: < erich@areca.com.tw >
+         Areca supports Linux RAID config tools.
+
+         < http://www.areca.com.tw >
+
+         To compile this driver as a module, choose M here: the
+         module will be called arcmsr (modprobe arcmsr).
+
 source "drivers/scsi/megaraid/Kconfig.megaraid"
 
 config SCSI_SATA
@@ -1053,6 +1076,13 @@ config 53C700_LE_ON_BE
        depends on SCSI_LASI700
        default y
 
+config SCSI_STEX
+       tristate "Promise SuperTrak EX Series support"
+       depends on PCI && SCSI
+       ---help---
+         This driver supports Promise SuperTrak EX8350/8300/16350/16300
+         Storage controllers.
+
 config SCSI_SYM53C8XX_2
        tristate "SYM53C8XX Version 2 SCSI support"
        depends on PCI && SCSI
index ebd0cf0..8fc2c59 100644 (file)
@@ -32,6 +32,7 @@ obj-$(CONFIG_SCSI_SPI_ATTRS)  += scsi_transport_spi.o
 obj-$(CONFIG_SCSI_FC_ATTRS)    += scsi_transport_fc.o
 obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
 obj-$(CONFIG_SCSI_SAS_ATTRS)   += scsi_transport_sas.o
+obj-$(CONFIG_SCSI_SAS_LIBSAS)  += libsas/
 
 obj-$(CONFIG_ISCSI_TCP)        += libiscsi.o   iscsi_tcp.o
 obj-$(CONFIG_INFINIBAND_ISER)  += libiscsi.o
@@ -59,6 +60,7 @@ obj-$(CONFIG_SCSI_PSI240I)    += psi240i.o
 obj-$(CONFIG_SCSI_BUSLOGIC)    += BusLogic.o
 obj-$(CONFIG_SCSI_DPT_I2O)     += dpt_i2o.o
 obj-$(CONFIG_SCSI_U14_34F)     += u14-34f.o
+obj-$(CONFIG_SCSI_ARCMSR)      += arcmsr/
 obj-$(CONFIG_SCSI_ULTRASTOR)   += ultrastor.o
 obj-$(CONFIG_SCSI_AHA152X)     += aha152x.o
 obj-$(CONFIG_SCSI_AHA1542)     += aha1542.o
@@ -67,6 +69,7 @@ obj-$(CONFIG_SCSI_AIC7XXX)    += aic7xxx/
 obj-$(CONFIG_SCSI_AIC79XX)     += aic7xxx/
 obj-$(CONFIG_SCSI_AACRAID)     += aacraid/
 obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o
+obj-$(CONFIG_SCSI_AIC94XX)     += aic94xx/
 obj-$(CONFIG_SCSI_IPS)         += ips.o
 obj-$(CONFIG_SCSI_FD_MCS)      += fd_mcs.o
 obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o
@@ -138,6 +141,7 @@ obj-$(CONFIG_SCSI_SATA_ULI) += libata.o sata_uli.o
 obj-$(CONFIG_SCSI_SATA_MV)     += libata.o sata_mv.o
 obj-$(CONFIG_SCSI_PDC_ADMA)    += libata.o pdc_adma.o
 obj-$(CONFIG_SCSI_HPTIOP)      += hptiop.o
+obj-$(CONFIG_SCSI_STEX)                += stex.o
 
 obj-$(CONFIG_ARM)              += arm/
 
@@ -155,6 +159,7 @@ scsi_mod-y                  += scsi.o hosts.o scsi_ioctl.o constants.o \
                                   scsicam.o scsi_error.o scsi_lib.o \
                                   scsi_scan.o scsi_sysfs.o \
                                   scsi_devinfo.o
+scsi_mod-$(CONFIG_SCSI_NETLINK)        += scsi_netlink.o
 scsi_mod-$(CONFIG_SYSCTL)      += scsi_sysctl.o
 scsi_mod-$(CONFIG_SCSI_PROC_FS)        += scsi_proc.o
 
index fddfa2e..0854069 100644 (file)
@@ -40,7 +40,7 @@ static irqreturn_t a2091_intr (int irq, void *_instance, struct pt_regs *fp)
     return IRQ_HANDLED;
 }
 
-static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
+static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
 {
     unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
     unsigned long addr = virt_to_bus(cmd->SCp.ptr);
@@ -115,7 +115,7 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
     return 0;
 }
 
-static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt, 
+static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
                      int status)
 {
     /* disable SCSI interrupts */
@@ -217,7 +217,7 @@ int __init a2091_detect(struct scsi_host_template *tpnt)
     return num_a2091;
 }
 
-static int a2091_bus_reset(Scsi_Cmnd *cmd)
+static int a2091_bus_reset(struct scsi_cmnd *cmd)
 {
        /* FIXME perform bus-specific reset */
 
index 22d6a13..fe809bc 100644 (file)
 
 int a2091_detect(struct scsi_host_template *);
 int a2091_release(struct Scsi_Host *);
-const char *wd33c93_info(void);
-int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int wd33c93_abort(Scsi_Cmnd *);
-int wd33c93_reset(Scsi_Cmnd *, unsigned int);
 
 #ifndef CMD_PER_LUN
 #define CMD_PER_LUN 2
index ae9ab4b..7bf46d4 100644 (file)
@@ -44,7 +44,7 @@ static irqreturn_t a3000_intr (int irq, void *dummy, struct pt_regs *fp)
        return IRQ_NONE;
 }
 
-static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
+static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
 {
     unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
     unsigned long addr = virt_to_bus(cmd->SCp.ptr);
@@ -110,8 +110,8 @@ static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
     return 0;
 }
 
-static void dma_stop (struct Scsi_Host *instance, Scsi_Cmnd *SCpnt,
-                     int status)
+static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
+                    int status)
 {
     /* disable SCSI interrupts */
     unsigned short cntr = CNTR_PDMD;
@@ -205,7 +205,7 @@ fail_register:
     return 0;
 }
 
-static int a3000_bus_reset(Scsi_Cmnd *cmd)
+static int a3000_bus_reset(struct scsi_cmnd *cmd)
 {
        /* FIXME perform bus-specific reset */
        
index 5535a65..44a4ec7 100644 (file)
 
 int a3000_detect(struct scsi_host_template *);
 int a3000_release(struct Scsi_Host *);
-const char *wd33c93_info(void);
-int wd33c93_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int wd33c93_abort(Scsi_Cmnd *);
-int wd33c93_reset(Scsi_Cmnd *, unsigned int);
 
 #ifndef CMD_PER_LUN
 #define CMD_PER_LUN 2
index 83b5c7d..37c55dd 100644 (file)
@@ -175,7 +175,7 @@ MODULE_PARM_DESC(acbsize, "Request a specific adapter control block (FIB) size.
  *
  *     Query config status, and commit the configuration if needed.
  */
-int aac_get_config_status(struct aac_dev *dev)
+int aac_get_config_status(struct aac_dev *dev, int commit_flag)
 {
        int status = 0;
        struct fib * fibptr;
@@ -219,7 +219,7 @@ int aac_get_config_status(struct aac_dev *dev)
        aac_fib_complete(fibptr);
        /* Send a CT_COMMIT_CONFIG to enable discovery of devices */
        if (status >= 0) {
-               if (commit == 1) {
+               if ((commit == 1) || commit_flag) {
                        struct aac_commit_config * dinfo;
                        aac_fib_init(fibptr);
                        dinfo = (struct aac_commit_config *) fib_data(fibptr);
@@ -489,6 +489,8 @@ int aac_probe_container(struct aac_dev *dev, int cid)
        unsigned instance;
 
        fsa_dev_ptr = dev->fsa_dev;
+       if (!fsa_dev_ptr)
+               return -ENOMEM;
        instance = dev->scsi_host_ptr->unique_id;
 
        if (!(fibptr = aac_fib_alloc(dev)))
@@ -782,8 +784,9 @@ int aac_get_adapter_info(struct aac_dev* dev)
                dev->maximum_num_channels = le32_to_cpu(bus_info->BusCount);
        }
 
-       tmp = le32_to_cpu(dev->adapter_info.kernelrev);
-       printk(KERN_INFO "%s%d: kernel %d.%d-%d[%d] %.*s\n", 
+       if (!dev->in_reset) {
+               tmp = le32_to_cpu(dev->adapter_info.kernelrev);
+               printk(KERN_INFO "%s%d: kernel %d.%d-%d[%d] %.*s\n",
                        dev->name, 
                        dev->id,
                        tmp>>24,
@@ -792,20 +795,21 @@ int aac_get_adapter_info(struct aac_dev* dev)
                        le32_to_cpu(dev->adapter_info.kernelbuild),
                        (int)sizeof(dev->supplement_adapter_info.BuildDate),
                        dev->supplement_adapter_info.BuildDate);
-       tmp = le32_to_cpu(dev->adapter_info.monitorrev);
-       printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n", 
+               tmp = le32_to_cpu(dev->adapter_info.monitorrev);
+               printk(KERN_INFO "%s%d: monitor %d.%d-%d[%d]\n",
                        dev->name, dev->id,
                        tmp>>24,(tmp>>16)&0xff,tmp&0xff,
                        le32_to_cpu(dev->adapter_info.monitorbuild));
-       tmp = le32_to_cpu(dev->adapter_info.biosrev);
-       printk(KERN_INFO "%s%d: bios %d.%d-%d[%d]\n", 
+               tmp = le32_to_cpu(dev->adapter_info.biosrev);
+               printk(KERN_INFO "%s%d: bios %d.%d-%d[%d]\n",
                        dev->name, dev->id,
                        tmp>>24,(tmp>>16)&0xff,tmp&0xff,
                        le32_to_cpu(dev->adapter_info.biosbuild));
-       if (le32_to_cpu(dev->adapter_info.serial[0]) != 0xBAD0)
-               printk(KERN_INFO "%s%d: serial %x\n",
-                       dev->name, dev->id,
-                       le32_to_cpu(dev->adapter_info.serial[0]));
+               if (le32_to_cpu(dev->adapter_info.serial[0]) != 0xBAD0)
+                       printk(KERN_INFO "%s%d: serial %x\n",
+                               dev->name, dev->id,
+                               le32_to_cpu(dev->adapter_info.serial[0]));
+       }
 
        dev->nondasd_support = 0;
        dev->raid_scsi_mode = 0;
@@ -1392,6 +1396,7 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd, int cid)
        struct scsi_cmnd *cmd;
        struct scsi_device *sdev = scsicmd->device;
        int active = 0;
+       struct aac_dev *aac;
        unsigned long flags;
 
        /*
@@ -1413,11 +1418,14 @@ static int aac_synchronize(struct scsi_cmnd *scsicmd, int cid)
        if (active)
                return SCSI_MLQUEUE_DEVICE_BUSY;
 
+       aac = (struct aac_dev *)scsicmd->device->host->hostdata;
+       if (aac->in_reset)
+               return SCSI_MLQUEUE_HOST_BUSY;
+
        /*
         *      Allocate and initialize a Fib
         */
-       if (!(cmd_fibcontext = 
-           aac_fib_alloc((struct aac_dev *)scsicmd->device->host->hostdata)))
+       if (!(cmd_fibcontext = aac_fib_alloc(aac)))
                return SCSI_MLQUEUE_HOST_BUSY;
 
        aac_fib_init(cmd_fibcontext);
@@ -1470,6 +1478,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
        struct aac_dev *dev = (struct aac_dev *)host->hostdata;
        struct fsa_dev_info *fsa_dev_ptr = dev->fsa_dev;
        
+       if (fsa_dev_ptr == NULL)
+               return -1;
        /*
         *      If the bus, id or lun is out of range, return fail
         *      Test does not apply to ID 16, the pseudo id for the controller
@@ -1499,6 +1509,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
                                case INQUIRY:
                                case READ_CAPACITY:
                                case TEST_UNIT_READY:
+                                       if (dev->in_reset)
+                                               return -1;
                                        spin_unlock_irq(host->host_lock);
                                        aac_probe_container(dev, cid);
                                        if ((fsa_dev_ptr[cid].valid & 1) == 0)
@@ -1524,6 +1536,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
                        }
                } else {  /* check for physical non-dasd devices */
                        if(dev->nondasd_support == 1){
+                               if (dev->in_reset)
+                                       return -1;
                                return aac_send_srb_fib(scsicmd);
                        } else {
                                scsicmd->result = DID_NO_CONNECT << 16;
@@ -1579,6 +1593,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
                        scsicmd->scsi_done(scsicmd);
                        return 0;
                }
+               if (dev->in_reset)
+                       return -1;
                setinqstr(dev, (void *) (inq_data.inqd_vid), fsa_dev_ptr[cid].type);
                inq_data.inqd_pdt = INQD_PDT_DA;        /* Direct/random access device */
                aac_internal_transfer(scsicmd, &inq_data, 0, sizeof(inq_data));
@@ -1734,6 +1750,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
                case READ_10:
                case READ_12:
                case READ_16:
+                       if (dev->in_reset)
+                               return -1;
                        /*
                         *      Hack to keep track of ordinal number of the device that
                         *      corresponds to a container. Needed to convert
@@ -1752,6 +1770,8 @@ int aac_scsi_cmd(struct scsi_cmnd * scsicmd)
                case WRITE_10:
                case WRITE_12:
                case WRITE_16:
+                       if (dev->in_reset)
+                               return -1;
                        return aac_write(scsicmd, cid);
 
                case SYNCHRONIZE_CACHE:
@@ -1782,6 +1802,8 @@ static int query_disk(struct aac_dev *dev, void __user *arg)
        struct fsa_dev_info *fsa_dev_ptr;
 
        fsa_dev_ptr = dev->fsa_dev;
+       if (!fsa_dev_ptr)
+               return -ENODEV;
        if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk)))
                return -EFAULT;
        if (qd.cnum == -1)
@@ -1843,6 +1865,10 @@ static int delete_disk(struct aac_dev *dev, void __user *arg)
        struct fsa_dev_info *fsa_dev_ptr;
 
        fsa_dev_ptr = dev->fsa_dev;
+       if (!fsa_dev_ptr)
+               return -ENODEV;
+       if (!fsa_dev_ptr)
+               return -ENODEV;
 
        if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
                return -EFAULT;
index d0eecd4..8924c18 100644 (file)
@@ -1029,6 +1029,7 @@ struct aac_dev
          init->InitStructRevision==cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION_4)
        u8                      raw_io_64;
        u8                      printf_enabled;
+       u8                      in_reset;
 };
 
 #define aac_adapter_interrupt(dev) \
@@ -1670,6 +1671,7 @@ extern struct aac_common aac_config;
 #define RCV_TEMP_READINGS              0x00000025
 #define GET_COMM_PREFERRED_SETTINGS    0x00000026
 #define IOP_RESET                      0x00001000
+#define IOP_RESET_ALWAYS               0x00001001
 #define RE_INIT_ADAPTER                        0x000000ee
 
 /*
@@ -1788,7 +1790,7 @@ void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum);
 int aac_fib_complete(struct fib * context);
 #define fib_data(fibctx) ((void *)(fibctx)->hw_fib->data)
 struct aac_dev *aac_init_adapter(struct aac_dev *dev);
-int aac_get_config_status(struct aac_dev *dev);
+int aac_get_config_status(struct aac_dev *dev, int commit_flag);
 int aac_get_containers(struct aac_dev *dev);
 int aac_scsi_cmd(struct scsi_cmnd *cmd);
 int aac_dev_ioctl(struct aac_dev *dev, int cmd, void __user *arg);
@@ -1799,6 +1801,7 @@ int aac_sa_init(struct aac_dev *dev);
 unsigned int aac_response_normal(struct aac_queue * q);
 unsigned int aac_command_normal(struct aac_queue * q);
 unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index);
+int aac_check_health(struct aac_dev * dev);
 int aac_command_thread(void *data);
 int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx);
 int aac_fib_adapter_complete(struct fib * fibptr, unsigned short size);
index 255421d..da1d3a9 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/completion.h>
 #include <linux/dma-mapping.h>
 #include <linux/blkdev.h>
-#include <linux/delay.h>
+#include <linux/delay.h> /* ssleep prototype */
 #include <linux/kthread.h>
 #include <asm/semaphore.h>
 #include <asm/uaccess.h>
@@ -140,7 +140,8 @@ cleanup:
                fibptr->hw_fib_pa = hw_fib_pa;
                fibptr->hw_fib = hw_fib;
        }
-       aac_fib_free(fibptr);
+       if (retval != -EINTR)
+               aac_fib_free(fibptr);
        return retval;
 }
 
@@ -297,7 +298,7 @@ return_fib:
                spin_unlock_irqrestore(&dev->fib_lock, flags);
                /* If someone killed the AIF aacraid thread, restart it */
                status = !dev->aif_thread;
-               if (status && dev->queues && dev->fsa_dev) {
+               if (status && !dev->in_reset && dev->queues && dev->fsa_dev) {
                        /* Be paranoid, be very paranoid! */
                        kthread_stop(dev->thread);
                        ssleep(1);
@@ -621,7 +622,13 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
 
                actual_fibsize = sizeof (struct aac_srb) + (((user_srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry));
                if(actual_fibsize != fibsize){ // User made a mistake - should not continue
-                       dprintk((KERN_DEBUG"aacraid: Bad Size specified in Raw SRB command\n"));
+                       dprintk((KERN_DEBUG"aacraid: Bad Size specified in "
+                         "Raw SRB command calculated fibsize=%d "
+                         "user_srbcmd->sg.count=%d aac_srb=%d sgentry=%d "
+                         "issued fibsize=%d\n",
+                         actual_fibsize, user_srbcmd->sg.count,
+                         sizeof(struct aac_srb), sizeof(struct sgentry),
+                         fibsize));
                        rcode = -EINVAL;
                        goto cleanup;
                }
@@ -663,6 +670,10 @@ static int aac_send_raw_srb(struct aac_dev* dev, void __user * arg)
                psg->count = cpu_to_le32(sg_indx+1);
                status = aac_fib_send(ScsiPortCommand, srbfib, actual_fibsize, FsaNormal, 1, 1, NULL, NULL);
        }
+       if (status == -EINTR) {
+               rcode = -EINTR;
+               goto cleanup;
+       }
 
        if (status != 0){
                dprintk((KERN_DEBUG"aacraid: Could not send raw srb fib to hba\n")); 
@@ -696,8 +707,10 @@ cleanup:
        for(i=0; i <= sg_indx; i++){
                kfree(sg_list[i]);
        }
-       aac_fib_complete(srbfib);
-       aac_fib_free(srbfib);
+       if (rcode != -EINTR) {
+               aac_fib_complete(srbfib);
+               aac_fib_free(srbfib);
+       }
 
        return rcode;
 }
index 1cd3584..87a9550 100644 (file)
@@ -180,7 +180,7 @@ int aac_send_shutdown(struct aac_dev * dev)
                          -2 /* Timeout silently */, 1,
                          NULL, NULL);
 
-       if (status == 0)
+       if (status >= 0)
                aac_fib_complete(fibctx);
        aac_fib_free(fibctx);
        return status;
index 3f27419..53add53 100644 (file)
 #include <linux/blkdev.h>
 #include <linux/delay.h>
 #include <linux/kthread.h>
+#include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
 #include <asm/semaphore.h>
 
 #include "aacraid.h"
@@ -464,6 +466,8 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
        dprintk((KERN_DEBUG "  hw_fib pa being sent=%lx\n",(ulong)fibptr->hw_fib_pa));
        dprintk((KERN_DEBUG "  fib being sent=%p\n",fibptr));
 
+       if (!dev->queues)
+               return -ENODEV;
        q = &dev->queues->queue[AdapNormCmdQueue];
 
        if(wait)
@@ -527,8 +531,15 @@ int aac_fib_send(u16 command, struct fib *fibptr, unsigned long size,
                                }
                                udelay(5);
                        }
-               } else
-                       down(&fibptr->event_wait);
+               } else if (down_interruptible(&fibptr->event_wait)) {
+                       spin_lock_irqsave(&fibptr->event_lock, flags);
+                       if (fibptr->done == 0) {
+                               fibptr->done = 2; /* Tell interrupt we aborted */
+                               spin_unlock_irqrestore(&fibptr->event_lock, flags);
+                               return -EINTR;
+                       }
+                       spin_unlock_irqrestore(&fibptr->event_lock, flags);
+               }
                BUG_ON(fibptr->done == 0);
                        
                if((fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT)){
@@ -795,7 +806,7 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
 
        /* Sniff for container changes */
 
-       if (!dev)
+       if (!dev || !dev->fsa_dev)
                return;
        container = (u32)-1;
 
@@ -1045,6 +1056,262 @@ static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
 
 }
 
+static int _aac_reset_adapter(struct aac_dev *aac)
+{
+       int index, quirks;
+       u32 ret;
+       int retval;
+       struct Scsi_Host *host;
+       struct scsi_device *dev;
+       struct scsi_cmnd *command;
+       struct scsi_cmnd *command_list;
+
+       /*
+        * Assumptions:
+        *      - host is locked.
+        *      - in_reset is asserted, so no new i/o is getting to the
+        *        card.
+        *      - The card is dead.
+        */
+       host = aac->scsi_host_ptr;
+       scsi_block_requests(host);
+       aac_adapter_disable_int(aac);
+       spin_unlock_irq(host->host_lock);
+       kthread_stop(aac->thread);
+
+       /*
+        *      If a positive health, means in a known DEAD PANIC
+        * state and the adapter could be reset to `try again'.
+        */
+       retval = aac_adapter_check_health(aac);
+       if (retval == 0)
+               retval = aac_adapter_sync_cmd(aac, IOP_RESET_ALWAYS,
+                 0, 0, 0, 0, 0, 0, &ret, NULL, NULL, NULL, NULL);
+       if (retval)
+               retval = aac_adapter_sync_cmd(aac, IOP_RESET,
+                 0, 0, 0, 0, 0, 0, &ret, NULL, NULL, NULL, NULL);
+
+       if (retval)
+               goto out;
+       if (ret != 0x00000001) {
+               retval = -ENODEV;
+               goto out;
+       }
+
+       index = aac->cardtype;
+
+       /*
+        * Re-initialize the adapter, first free resources, then carefully
+        * apply the initialization sequence to come back again. Only risk
+        * is a change in Firmware dropping cache, it is assumed the caller
+        * will ensure that i/o is queisced and the card is flushed in that
+        * case.
+        */
+       aac_fib_map_free(aac);
+       aac->hw_fib_va = NULL;
+       aac->hw_fib_pa = 0;
+       pci_free_consistent(aac->pdev, aac->comm_size, aac->comm_addr, aac->comm_phys);
+       aac->comm_addr = NULL;
+       aac->comm_phys = 0;
+       kfree(aac->queues);
+       aac->queues = NULL;
+       free_irq(aac->pdev->irq, aac);
+       kfree(aac->fsa_dev);
+       aac->fsa_dev = NULL;
+       if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT) {
+               if (((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK))) ||
+                 ((retval = pci_set_consistent_dma_mask(aac->pdev, DMA_32BIT_MASK))))
+                       goto out;
+       } else {
+               if (((retval = pci_set_dma_mask(aac->pdev, 0x7FFFFFFFULL))) ||
+                 ((retval = pci_set_consistent_dma_mask(aac->pdev, 0x7FFFFFFFULL))))
+                       goto out;
+       }
+       if ((retval = (*(aac_get_driver_ident(index)->init))(aac)))
+               goto out;
+       if (aac_get_driver_ident(index)->quirks & AAC_QUIRK_31BIT)
+               if ((retval = pci_set_dma_mask(aac->pdev, DMA_32BIT_MASK)))
+                       goto out;
+       aac->thread = kthread_run(aac_command_thread, aac, aac->name);
+       if (IS_ERR(aac->thread)) {
+               retval = PTR_ERR(aac->thread);
+               goto out;
+       }
+       (void)aac_get_adapter_info(aac);
+       quirks = aac_get_driver_ident(index)->quirks;
+       if ((quirks & AAC_QUIRK_34SG) && (host->sg_tablesize > 34)) {
+               host->sg_tablesize = 34;
+               host->max_sectors = (host->sg_tablesize * 8) + 112;
+       }
+       if ((quirks & AAC_QUIRK_17SG) && (host->sg_tablesize > 17)) {
+               host->sg_tablesize = 17;
+               host->max_sectors = (host->sg_tablesize * 8) + 112;
+       }
+       aac_get_config_status(aac, 1);
+       aac_get_containers(aac);
+       /*
+        * This is where the assumption that the Adapter is quiesced
+        * is important.
+        */
+       command_list = NULL;
+       __shost_for_each_device(dev, host) {
+               unsigned long flags;
+               spin_lock_irqsave(&dev->list_lock, flags);
+               list_for_each_entry(command, &dev->cmd_list, list)
+                       if (command->SCp.phase == AAC_OWNER_FIRMWARE) {
+                               command->SCp.buffer = (struct scatterlist *)command_list;
+                               command_list = command;
+                       }
+               spin_unlock_irqrestore(&dev->list_lock, flags);
+       }
+       while ((command = command_list)) {
+               command_list = (struct scsi_cmnd *)command->SCp.buffer;
+               command->SCp.buffer = NULL;
+               command->result = DID_OK << 16
+                 | COMMAND_COMPLETE << 8
+                 | SAM_STAT_TASK_SET_FULL;
+               command->SCp.phase = AAC_OWNER_ERROR_HANDLER;
+               command->scsi_done(command);
+       }
+       retval = 0;
+
+out:
+       aac->in_reset = 0;
+       scsi_unblock_requests(host);
+       spin_lock_irq(host->host_lock);
+       return retval;
+}
+
+int aac_check_health(struct aac_dev * aac)
+{
+       int BlinkLED;
+       unsigned long time_now, flagv = 0;
+       struct list_head * entry;
+       struct Scsi_Host * host;
+
+       /* Extending the scope of fib_lock slightly to protect aac->in_reset */
+       if (spin_trylock_irqsave(&aac->fib_lock, flagv) == 0)
+               return 0;
+
+       if (aac->in_reset || !(BlinkLED = aac_adapter_check_health(aac))) {
+               spin_unlock_irqrestore(&aac->fib_lock, flagv);
+               return 0; /* OK */
+       }
+
+       aac->in_reset = 1;
+
+       /* Fake up an AIF:
+        *      aac_aifcmd.command = AifCmdEventNotify = 1
+        *      aac_aifcmd.seqnum = 0xFFFFFFFF
+        *      aac_aifcmd.data[0] = AifEnExpEvent = 23
+        *      aac_aifcmd.data[1] = AifExeFirmwarePanic = 3
+        *      aac.aifcmd.data[2] = AifHighPriority = 3
+        *      aac.aifcmd.data[3] = BlinkLED
+        */
+
+       time_now = jiffies/HZ;
+       entry = aac->fib_list.next;
+
+       /*
+        * For each Context that is on the
+        * fibctxList, make a copy of the
+        * fib, and then set the event to wake up the
+        * thread that is waiting for it.
+        */
+       while (entry != &aac->fib_list) {
+               /*
+                * Extract the fibctx
+                */
+               struct aac_fib_context *fibctx = list_entry(entry, struct aac_fib_context, next);
+               struct hw_fib * hw_fib;
+               struct fib * fib;
+               /*
+                * Check if the queue is getting
+                * backlogged
+                */
+               if (fibctx->count > 20) {
+                       /*
+                        * It's *not* jiffies folks,
+                        * but jiffies / HZ, so do not
+                        * panic ...
+                        */
+                       u32 time_last = fibctx->jiffies;
+                       /*
+                        * Has it been > 2 minutes
+                        * since the last read off
+                        * the queue?
+                        */
+                       if ((time_now - time_last) > aif_timeout) {
+                               entry = entry->next;
+                               aac_close_fib_context(aac, fibctx);
+                               continue;
+                       }
+               }
+               /*
+                * Warning: no sleep allowed while
+                * holding spinlock
+                */
+               hw_fib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC);
+               fib = kmalloc(sizeof(struct fib), GFP_ATOMIC);
+               if (fib && hw_fib) {
+                       struct aac_aifcmd * aif;
+
+                       memset(hw_fib, 0, sizeof(struct hw_fib));
+                       memset(fib, 0, sizeof(struct fib));
+                       fib->hw_fib = hw_fib;
+                       fib->dev = aac;
+                       aac_fib_init(fib);
+                       fib->type = FSAFS_NTC_FIB_CONTEXT;
+                       fib->size = sizeof (struct fib);
+                       fib->data = hw_fib->data;
+                       aif = (struct aac_aifcmd *)hw_fib->data;
+                       aif->command = cpu_to_le32(AifCmdEventNotify);
+                       aif->seqnum = cpu_to_le32(0xFFFFFFFF);
+                       aif->data[0] = cpu_to_le32(AifEnExpEvent);
+                       aif->data[1] = cpu_to_le32(AifExeFirmwarePanic);
+                       aif->data[2] = cpu_to_le32(AifHighPriority);
+                       aif->data[3] = cpu_to_le32(BlinkLED);
+
+                       /*
+                        * Put the FIB onto the
+                        * fibctx's fibs
+                        */
+                       list_add_tail(&fib->fiblink, &fibctx->fib_list);
+                       fibctx->count++;
+                       /*
+                        * Set the event to wake up the
+                        * thread that will waiting.
+                        */
+                       up(&fibctx->wait_sem);
+               } else {
+                       printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
+                       kfree(fib);
+                       kfree(hw_fib);
+               }
+               entry = entry->next;
+       }
+
+       spin_unlock_irqrestore(&aac->fib_lock, flagv);
+
+       if (BlinkLED < 0) {
+               printk(KERN_ERR "%s: Host adapter dead %d\n", aac->name, BlinkLED);
+               goto out;
+       }
+
+       printk(KERN_ERR "%s: Host adapter BLINK LED 0x%x\n", aac->name, BlinkLED);
+
+       host = aac->scsi_host_ptr;
+       spin_lock_irqsave(host->host_lock, flagv);
+       BlinkLED = _aac_reset_adapter(aac);
+       spin_unlock_irqrestore(host->host_lock, flagv);
+       return BlinkLED;
+
+out:
+       aac->in_reset = 0;
+       return BlinkLED;
+}
+
+
 /**
  *     aac_command_thread      -       command processing thread
  *     @dev: Adapter to monitor
index b2a5c72..8335f07 100644 (file)
@@ -124,10 +124,15 @@ unsigned int aac_response_normal(struct aac_queue * q)
                } else {
                        unsigned long flagv;
                        spin_lock_irqsave(&fib->event_lock, flagv);
-                       fib->done = 1;
+                       if (!fib->done)
+                               fib->done = 1;
                        up(&fib->event_wait);
                        spin_unlock_irqrestore(&fib->event_lock, flagv);
                        FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
+                       if (fib->done == 2) {
+                               aac_fib_complete(fib);
+                               aac_fib_free(fib);
+                       }
                }
                consumed++;
                spin_lock_irqsave(q->lock, flags);
@@ -316,7 +321,8 @@ unsigned int aac_intr_normal(struct aac_dev * dev, u32 Index)
                        unsigned long flagv;
                        dprintk((KERN_INFO "event_wait up\n"));
                        spin_lock_irqsave(&fib->event_lock, flagv);
-                       fib->done = 1;
+                       if (!fib->done)
+                               fib->done = 1;
                        up(&fib->event_wait);
                        spin_unlock_irqrestore(&fib->event_lock, flagv);
                        FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
index e42a479..d67058f 100644 (file)
@@ -454,17 +454,17 @@ static int aac_eh_reset(struct scsi_cmnd* cmd)
        printk(KERN_ERR "%s: Host adapter reset request. SCSI hang ?\n", 
                                        AAC_DRIVERNAME);
        aac = (struct aac_dev *)host->hostdata;
-       if (aac_adapter_check_health(aac)) {
-               printk(KERN_ERR "%s: Host adapter appears dead\n", 
-                               AAC_DRIVERNAME);
-               return -ENODEV;
-       }
+
+       if ((count = aac_check_health(aac)))
+               return count;
        /*
         * Wait for all commands to complete to this specific
         * target (block maximum 60 seconds).
         */
        for (count = 60; count; --count) {
-               int active = 0;
+               int active = aac->in_reset;
+
+               if (active == 0)
                __shost_for_each_device(dev, host) {
                        spin_lock_irqsave(&dev->list_lock, flags);
                        list_for_each_entry(command, &dev->cmd_list, list) {
@@ -933,7 +933,7 @@ static int __devinit aac_probe_one(struct pci_dev *pdev,
        else
                shost->max_channel = 0;
 
-       aac_get_config_status(aac);
+       aac_get_config_status(aac, 0);
        aac_get_containers(aac);
        list_add(&aac->entry, insert);
 
@@ -1013,6 +1013,10 @@ static void __devexit aac_remove_one(struct pci_dev *pdev)
        list_del(&aac->entry);
        scsi_host_put(shost);
        pci_disable_device(pdev);
+       if (list_empty(&aac_devices)) {
+               unregister_chrdev(aac_cfg_major, "aac");
+               aac_cfg_major = -1;
+       }
 }
 
 static struct pci_driver aac_pci_driver = {
index 458ea89..f850c3a 100644 (file)
@@ -395,6 +395,25 @@ static int aac_rkt_send(struct fib * fib)
        return 0;
 }
 
+static int aac_rkt_restart_adapter(struct aac_dev *dev)
+{
+       u32 var;
+
+       printk(KERN_ERR "%s%d: adapter kernel panic'd.\n",
+                       dev->name, dev->id);
+
+       if (aac_rkt_check_health(dev) <= 0)
+               return 1;
+       if (rkt_sync_cmd(dev, IOP_RESET, 0, 0, 0, 0, 0, 0,
+                       &var, NULL, NULL, NULL, NULL))
+               return 1;
+       if (var != 0x00000001)
+                return 1;
+       if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC)
+               return 1;
+       return 0;
+}
+
 /**
  *     aac_rkt_init    -       initialize an i960 based AAC card
  *     @dev: device to configure
@@ -417,6 +436,9 @@ int aac_rkt_init(struct aac_dev *dev)
        /*
         *      Check to see if the board panic'd while booting.
         */
+       if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC)
+               if (aac_rkt_restart_adapter(dev))
+                       goto error_iounmap;
        /*
         *      Check to see if the board failed any self tests.
         */
@@ -431,13 +453,6 @@ int aac_rkt_init(struct aac_dev *dev)
                printk(KERN_ERR "%s%d: adapter monitor panic.\n", dev->name, instance);
                goto error_iounmap;
        }
-       /*
-        *      Check to see if the board panic'd while booting.
-        */
-       if (rkt_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) {
-               printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", dev->name, instance);
-               goto error_iounmap;
-       }
        start = jiffies;
        /*
         *      Wait for the adapter to be up and running. Wait up to 3 minutes
index 035018d..c715c4b 100644 (file)
@@ -394,6 +394,25 @@ static int aac_rx_send(struct fib * fib)
        return 0;
 }
 
+static int aac_rx_restart_adapter(struct aac_dev *dev)
+{
+       u32 var;
+
+       printk(KERN_ERR "%s%d: adapter kernel panic'd.\n",
+                       dev->name, dev->id);
+
+       if (aac_rx_check_health(dev) <= 0)
+               return 1;
+       if (rx_sync_cmd(dev, IOP_RESET, 0, 0, 0, 0, 0, 0,
+                       &var, NULL, NULL, NULL, NULL))
+               return 1;
+       if (var != 0x00000001)
+                return 1;
+       if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC)
+               return 1;
+       return 0;
+}
+
 /**
  *     aac_rx_init     -       initialize an i960 based AAC card
  *     @dev: device to configure
@@ -416,6 +435,9 @@ int aac_rx_init(struct aac_dev *dev)
        /*
         *      Check to see if the board panic'd while booting.
         */
+       if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC)
+               if (aac_rx_restart_adapter(dev))
+                       goto error_iounmap;
        /*
         *      Check to see if the board failed any self tests.
         */
@@ -424,13 +446,6 @@ int aac_rx_init(struct aac_dev *dev)
                goto error_iounmap;
        }
        /*
-        *      Check to see if the board panic'd while booting.
-        */
-       if (rx_readl(dev, MUnit.OMRx[0]) & KERNEL_PANIC) {
-               printk(KERN_ERR "%s%d: adapter kernel panic.\n", dev->name, instance);
-               goto error_iounmap;
-       }
-       /*
         *      Check to see if the monitor panic'd while booting.
         */
        if (rx_readl(dev, MUnit.OMRx[0]) & MONITOR_PANIC) {
index e32b4ab..773f02e 100644 (file)
@@ -888,10 +888,6 @@ typedef unsigned char uchar;
 #define ASC_PCI_ID2DEV(id)    (((id) >> 11) & 0x1F)
 #define ASC_PCI_ID2FUNC(id)   (((id) >> 8) & 0x7)
 #define ASC_PCI_MKID(bus, dev, func) ((((dev) & 0x1F) << 11) | (((func) & 0x7) << 8) | ((bus) & 0xFF))
-#define ASC_PCI_VENDORID                  0x10CD
-#define ASC_PCI_DEVICEID_1200A            0x1100
-#define ASC_PCI_DEVICEID_1200B            0x1200
-#define ASC_PCI_DEVICEID_ULTRA            0x1300
 #define ASC_PCI_REVISION_3150             0x02
 #define ASC_PCI_REVISION_3050             0x03
 
@@ -899,6 +895,14 @@ typedef unsigned char uchar;
 #define  ASC_DVCLIB_CALL_FAILED   (0)
 #define  ASC_DVCLIB_CALL_ERROR    (-1)
 
+#define PCI_VENDOR_ID_ASP              0x10cd
+#define PCI_DEVICE_ID_ASP_1200A                0x1100
+#define PCI_DEVICE_ID_ASP_ABP940       0x1200
+#define PCI_DEVICE_ID_ASP_ABP940U      0x1300
+#define PCI_DEVICE_ID_ASP_ABP940UW     0x2300
+#define PCI_DEVICE_ID_38C0800_REV1     0x2500
+#define PCI_DEVICE_ID_38C1600_REV1     0x2700
+
 /*
  * Enable CC_VERY_LONG_SG_LIST to support up to 64K element SG lists.
  * The SRB structure will have to be changed and the ASC_SRB2SCSIQ()
@@ -1492,8 +1496,6 @@ typedef struct asc_dvc_cfg {
 #define ASC_INIT_STATE_END_INQUIRY   0x0080
 #define ASC_INIT_RESET_SCSI_DONE     0x0100
 #define ASC_INIT_STATE_WITHOUT_EEP   0x8000
-#define ASC_PCI_DEVICE_ID_REV_A      0x1100
-#define ASC_PCI_DEVICE_ID_REV_B      0x1200
 #define ASC_BUG_FIX_IF_NOT_DWB       0x0001
 #define ASC_BUG_FIX_ASYN_USE_SYN     0x0002
 #define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41
@@ -2100,12 +2102,6 @@ STATIC ASC_DCNT  AscGetMaxDmaCount(ushort);
 #define ADV_NUM_PAGE_CROSSING \
     ((ADV_SG_TOTAL_MEM_SIZE + (ADV_PAGE_SIZE - 1))/ADV_PAGE_SIZE)
 
-/* a_condor.h */
-#define ADV_PCI_VENDOR_ID               0x10CD
-#define ADV_PCI_DEVICE_ID_REV_A         0x2300
-#define ADV_PCI_DEVID_38C0800_REV1      0x2500
-#define ADV_PCI_DEVID_38C1600_REV1      0x2700
-
 #define ADV_EEP_DVC_CFG_BEGIN           (0x00)
 #define ADV_EEP_DVC_CFG_END             (0x15)
 #define ADV_EEP_DVC_CTL_BEGIN           (0x16)  /* location of OEM name */
@@ -3569,14 +3565,7 @@ typedef struct scsi_cmnd     REQ, *REQP;
 #define PCI_MAX_SLOT            0x1F
 #define PCI_MAX_BUS             0xFF
 #define PCI_IOADDRESS_MASK      0xFFFE
-#define ASC_PCI_VENDORID        0x10CD
 #define ASC_PCI_DEVICE_ID_CNT   6       /* PCI Device ID count. */
-#define ASC_PCI_DEVICE_ID_1100  0x1100
-#define ASC_PCI_DEVICE_ID_1200  0x1200
-#define ASC_PCI_DEVICE_ID_1300  0x1300
-#define ASC_PCI_DEVICE_ID_2300  0x2300  /* ASC-3550 */
-#define ASC_PCI_DEVICE_ID_2500  0x2500  /* ASC-38C0800 */
-#define ASC_PCI_DEVICE_ID_2700  0x2700  /* ASC-38C1600 */
 
 #ifndef ADVANSYS_STATS
 #define ASC_STATS(shp, counter)
@@ -4330,12 +4319,12 @@ advansys_detect(struct scsi_host_template *tpnt)
     struct pci_dev      *pci_devp = NULL;
     int                 pci_device_id_cnt = 0;
     unsigned int        pci_device_id[ASC_PCI_DEVICE_ID_CNT] = {
-                                    ASC_PCI_DEVICE_ID_1100,
-                                    ASC_PCI_DEVICE_ID_1200,
-                                    ASC_PCI_DEVICE_ID_1300,
-                                    ASC_PCI_DEVICE_ID_2300,
-                                    ASC_PCI_DEVICE_ID_2500,
-                                    ASC_PCI_DEVICE_ID_2700
+                                    PCI_DEVICE_ID_ASP_1200A,
+                                    PCI_DEVICE_ID_ASP_ABP940,
+                                    PCI_DEVICE_ID_ASP_ABP940U,
+                                    PCI_DEVICE_ID_ASP_ABP940UW,
+                                    PCI_DEVICE_ID_38C0800_REV1,
+                                    PCI_DEVICE_ID_38C1600_REV1
                         };
     ADV_PADDR           pci_memory_address;
 #endif /* CONFIG_PCI */
@@ -4471,7 +4460,7 @@ advansys_detect(struct scsi_host_template *tpnt)
 
                     /* Find all PCI cards. */
                     while (pci_device_id_cnt < ASC_PCI_DEVICE_ID_CNT) {
-                        if ((pci_devp = pci_find_device(ASC_PCI_VENDORID,
+                        if ((pci_devp = pci_find_device(PCI_VENDOR_ID_ASP,
                             pci_device_id[pci_device_id_cnt], pci_devp)) ==
                             NULL) {
                             pci_device_id_cnt++;
@@ -4575,9 +4564,9 @@ advansys_detect(struct scsi_host_template *tpnt)
              */
 #ifdef CONFIG_PCI
             if (asc_bus[bus] == ASC_IS_PCI &&
-                (pci_devp->device == ASC_PCI_DEVICE_ID_2300 ||
-                 pci_devp->device == ASC_PCI_DEVICE_ID_2500 ||
-                 pci_devp->device == ASC_PCI_DEVICE_ID_2700))
+                (pci_devp->device == PCI_DEVICE_ID_ASP_ABP940UW ||
+                 pci_devp->device == PCI_DEVICE_ID_38C0800_REV1 ||
+                 pci_devp->device == PCI_DEVICE_ID_38C1600_REV1))
             {
                 boardp->flags |= ASC_IS_WIDE_BOARD;
             }
@@ -4600,11 +4589,11 @@ advansys_detect(struct scsi_host_template *tpnt)
                 adv_dvc_varp->isr_callback = adv_isr_callback;
                 adv_dvc_varp->async_callback = adv_async_callback;
 #ifdef CONFIG_PCI
-                if (pci_devp->device == ASC_PCI_DEVICE_ID_2300)
+                if (pci_devp->device == PCI_DEVICE_ID_ASP_ABP940UW)
                 {
                     ASC_DBG(1, "advansys_detect: ASC-3550\n");
                     adv_dvc_varp->chip_type = ADV_CHIP_ASC3550;
-                } else if (pci_devp->device == ASC_PCI_DEVICE_ID_2500)
+                } else if (pci_devp->device == PCI_DEVICE_ID_38C0800_REV1)
                 {
                     ASC_DBG(1, "advansys_detect: ASC-38C0800\n");
                     adv_dvc_varp->chip_type = ADV_CHIP_ASC38C0800;
@@ -11922,7 +11911,7 @@ AscInitGetConfig(
         PCIRevisionID = DvcReadPCIConfigByte(asc_dvc,
                                     AscPCIConfigRevisionIDRegister);
 
-        if (PCIVendorID != ASC_PCI_VENDORID) {
+        if (PCIVendorID != PCI_VENDOR_ID_ASP) {
             warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
         }
         prevCmdRegBits = DvcReadPCIConfigByte(asc_dvc,
@@ -11942,15 +11931,15 @@ AscInitGetConfig(
                 warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
             }
         }
-        if ((PCIDeviceID == ASC_PCI_DEVICEID_1200A) ||
-            (PCIDeviceID == ASC_PCI_DEVICEID_1200B)) {
+        if ((PCIDeviceID == PCI_DEVICE_ID_ASP_1200A) ||
+            (PCIDeviceID == PCI_DEVICE_ID_ASP_ABP940)) {
             DvcWritePCIConfigByte(asc_dvc,
                             AscPCIConfigLatencyTimer, 0x00);
             if (DvcReadPCIConfigByte(asc_dvc, AscPCIConfigLatencyTimer)
                 != 0x00) {
                 warn_code |= ASC_WARN_SET_PCI_CONFIG_SPACE;
             }
-        } else if (PCIDeviceID == ASC_PCI_DEVICEID_ULTRA) {
+        } else if (PCIDeviceID == PCI_DEVICE_ID_ASP_ABP940U) {
             if (DvcReadPCIConfigByte(asc_dvc,
                                 AscPCIConfigLatencyTimer) < 0x20) {
                 DvcWritePCIConfigByte(asc_dvc,
@@ -12037,8 +12026,8 @@ AscInitFromAscDvcVar(
         AscSetChipCfgMsw(iop_base, cfg_msw);
         if ((asc_dvc->bus_type & ASC_IS_PCI_ULTRA) == ASC_IS_PCI_ULTRA) {
         } else {
-            if ((pci_device_id == ASC_PCI_DEVICE_ID_REV_A) ||
-                (pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
+            if ((pci_device_id == PCI_DEVICE_ID_ASP_1200A) ||
+                (pci_device_id == PCI_DEVICE_ID_ASP_ABP940)) {
                 asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_IF_NOT_DWB;
                 asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ASYN_USE_SYN;
             }
@@ -14275,8 +14264,8 @@ Default_38C0800_EEPROM_Config __initdata = {
     0,                          /* 55 reserved */
     0,                          /* 56 cisptr_lsw */
     0,                          /* 57 cisprt_msw */
-    ADV_PCI_VENDOR_ID,          /* 58 subsysvid */
-    ADV_PCI_DEVID_38C0800_REV1, /* 59 subsysid */
+    PCI_VENDOR_ID_ASP,          /* 58 subsysvid */
+    PCI_DEVICE_ID_38C0800_REV1, /* 59 subsysid */
     0,                          /* 60 reserved */
     0,                          /* 61 reserved */
     0,                          /* 62 reserved */
@@ -14405,8 +14394,8 @@ Default_38C1600_EEPROM_Config __initdata = {
     0,                          /* 55 reserved */
     0,                          /* 56 cisptr_lsw */
     0,                          /* 57 cisprt_msw */
-    ADV_PCI_VENDOR_ID,          /* 58 subsysvid */
-    ADV_PCI_DEVID_38C1600_REV1, /* 59 subsysid */
+    PCI_VENDOR_ID_ASP,          /* 58 subsysvid */
+    PCI_DEVICE_ID_38C1600_REV1, /* 59 subsysid */
     0,                          /* 60 reserved */
     0,                          /* 61 reserved */
     0,                          /* 62 reserved */
@@ -18225,3 +18214,22 @@ AdvInquiryHandling(
     }
 }
 MODULE_LICENSE("Dual BSD/GPL");
+
+/* PCI Devices supported by this driver */
+static struct pci_device_id advansys_pci_tbl[] __devinitdata = {
+       { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_1200A,
+       PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940,
+       PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940U,
+       PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_ASP_ABP940UW,
+       PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_38C0800_REV1,
+       PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { PCI_VENDOR_ID_ASP, PCI_DEVICE_ID_38C1600_REV1,
+       PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       { }
+};
+MODULE_DEVICE_TABLE(pci, advansys_pci_tbl);
+
index f974869..fb6a476 100644 (file)
 #include <linux/isapnp.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
+#include <linux/list.h>
 #include <asm/semaphore.h>
 #include <scsi/scsicam.h>
 
 #include <scsi/scsi_transport_spi.h>
 #include "aha152x.h"
 
+static LIST_HEAD(aha152x_host_list);
+
 
 /* DEFINES */
 
@@ -423,8 +426,6 @@ MODULE_DEVICE_TABLE(isapnp, id_table);
 
 #endif /* !PCMCIA */
 
-static int registered_count=0;
-static struct Scsi_Host *aha152x_host[2];
 static struct scsi_host_template aha152x_driver_template;
 
 /*
@@ -541,6 +542,7 @@ struct aha152x_hostdata {
 #ifdef __ISAPNP__
        struct pnp_dev *pnpdev;
 #endif
+       struct list_head host_list;
 };
 
 
@@ -755,20 +757,9 @@ static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, Scsi_Cmnd *SCp)
        return ptr;
 }
 
-static inline struct Scsi_Host *lookup_irq(int irqno)
-{
-       int i;
-
-       for(i=0; i<ARRAY_SIZE(aha152x_host); i++)
-               if(aha152x_host[i] && aha152x_host[i]->irq==irqno)
-                       return aha152x_host[i];
-
-       return NULL;
-}
-
 static irqreturn_t swintr(int irqno, void *dev_id, struct pt_regs *regs)
 {
-       struct Scsi_Host *shpnt = lookup_irq(irqno);
+       struct Scsi_Host *shpnt = (struct Scsi_Host *)dev_id;
 
        if (!shpnt) {
                printk(KERN_ERR "aha152x: catched software interrupt %d for unknown controller.\n", irqno);
@@ -791,10 +782,11 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
                return NULL;
        }
 
-       /* need to have host registered before triggering any interrupt */
-       aha152x_host[registered_count] = shpnt;
-
        memset(HOSTDATA(shpnt), 0, sizeof *HOSTDATA(shpnt));
+       INIT_LIST_HEAD(&HOSTDATA(shpnt)->host_list);
+
+       /* need to have host registered before triggering any interrupt */
+       list_add_tail(&HOSTDATA(shpnt)->host_list, &aha152x_host_list);
 
        shpnt->io_port   = setup->io_port;
        shpnt->n_io_port = IO_RANGE;
@@ -907,12 +899,10 @@ struct Scsi_Host *aha152x_probe_one(struct aha152x_setup *setup)
 
        scsi_scan_host(shpnt);
 
-       registered_count++;
-
        return shpnt;
 
 out_host_put:
-       aha152x_host[registered_count]=NULL;
+       list_del(&HOSTDATA(shpnt)->host_list);
        scsi_host_put(shpnt);
 
        return NULL;
@@ -937,6 +927,7 @@ void aha152x_release(struct Scsi_Host *shpnt)
 #endif
 
        scsi_remove_host(shpnt);
+       list_del(&HOSTDATA(shpnt)->host_list);
        scsi_host_put(shpnt);
 }
 
@@ -1459,9 +1450,12 @@ static struct work_struct aha152x_tq;
  */
 static void run(void)
 {
-       int i;
-       for (i = 0; i<ARRAY_SIZE(aha152x_host); i++) {
-               is_complete(aha152x_host[i]);
+       struct aha152x_hostdata *hd;
+
+       list_for_each_entry(hd, &aha152x_host_list, host_list) {
+               struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
+
+               is_complete(shost);
        }
 }
 
@@ -1471,7 +1465,7 @@ static void run(void)
  */
 static irqreturn_t intr(int irqno, void *dev_id, struct pt_regs *regs)
 {
-       struct Scsi_Host *shpnt = lookup_irq(irqno);
+       struct Scsi_Host *shpnt = (struct Scsi_Host *)dev_id;
        unsigned long flags;
        unsigned char rev, dmacntrl0;
 
@@ -3953,16 +3947,17 @@ static int __init aha152x_init(void)
 #endif
        }
 
-       return registered_count>0;
+       return 1;
 }
 
 static void __exit aha152x_exit(void)
 {
-       int i;
+       struct aha152x_hostdata *hd;
+
+       list_for_each_entry(hd, &aha152x_host_list, host_list) {
+               struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
 
-       for(i=0; i<ARRAY_SIZE(setup); i++) {
-               aha152x_release(aha152x_host[i]);
-               aha152x_host[i]=NULL;
+               aha152x_release(shost);
        }
 }
 
index 998999c..c7eeace 100644 (file)
@@ -321,7 +321,7 @@ MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION(AIC79XX_DRIVER_VERSION);
 module_param(aic79xx, charp, 0444);
 MODULE_PARM_DESC(aic79xx,
-"period delimited, options string.\n"
+"period-delimited options string:\n"
 "      verbose                 Enable verbose/diagnostic logging\n"
 "      allow_memio             Allow device registers to be memory mapped\n"
 "      debug                   Bitmask of debug values to enable\n"
@@ -346,7 +346,7 @@ MODULE_PARM_DESC(aic79xx,
 "              Shorten the selection timeout to 128ms\n"
 "\n"
 "      options aic79xx 'aic79xx=verbose.tag_info:{{}.{}.{..10}}.seltime:1'\n"
-"\n");
+);
 
 static void ahd_linux_handle_scsi_status(struct ahd_softc *,
                                         struct scsi_device *,
index aa4be8a..e5bb4d8 100644 (file)
@@ -341,7 +341,7 @@ MODULE_LICENSE("Dual BSD/GPL");
 MODULE_VERSION(AIC7XXX_DRIVER_VERSION);
 module_param(aic7xxx, charp, 0444);
 MODULE_PARM_DESC(aic7xxx,
-"period delimited, options string.\n"
+"period-delimited options string:\n"
 "      verbose                 Enable verbose/diagnostic logging\n"
 "      allow_memio             Allow device registers to be memory mapped\n"
 "      debug                   Bitmask of debug values to enable\n"
index 3f85b5e..ba3bcca 100644 (file)
 #include <linux/stat.h>
 #include <linux/slab.h>        /* for kmalloc() */
 
-#include <linux/config.h>        /* for CONFIG_PCI */
-
 #define AIC7XXX_C_VERSION  "5.2.6"
 
 #define ALL_TARGETS -1
diff --git a/drivers/scsi/aic94xx/Kconfig b/drivers/scsi/aic94xx/Kconfig
new file mode 100644 (file)
index 0000000..0ed391d
--- /dev/null
@@ -0,0 +1,41 @@
+#
+# Kernel configuration file for aic94xx SAS/SATA driver.
+#
+# Copyright (c) 2005 Adaptec, Inc.  All rights reserved.
+# Copyright (c) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+#
+# This file is licensed under GPLv2.
+#
+# This file is part of the aic94xx driver.
+#
+# The aic94xx driver is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 of the
+# License.
+#
+# The aic94xx driver is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Aic94xx Driver; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+#
+
+config SCSI_AIC94XX
+       tristate "Adaptec AIC94xx SAS/SATA support"
+       depends on PCI
+       select SCSI_SAS_LIBSAS
+       help
+               This driver supports Adaptec's SAS/SATA 3Gb/s 64 bit PCI-X
+               AIC94xx chip based host adapters.
+
+config AIC94XX_DEBUG
+       bool "Compile in debug mode"
+       default y
+       depends on SCSI_AIC94XX
+       help
+               Compiles the aic94xx driver in debug mode.  In debug mode,
+               the driver prints some messages to the console.
diff --git a/drivers/scsi/aic94xx/Makefile b/drivers/scsi/aic94xx/Makefile
new file mode 100644 (file)
index 0000000..e6b7012
--- /dev/null
@@ -0,0 +1,39 @@
+#
+# Makefile for Adaptec aic94xx SAS/SATA driver.
+#
+# Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+#
+# This file is licensed under GPLv2.
+#
+# This file is part of the the aic94xx driver.
+#
+# The aic94xx driver is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 of the
+# License.
+#
+# The aic94xx driver is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with the aic94xx driver; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+ifeq ($(CONFIG_AIC94XX_DEBUG),y)
+       EXTRA_CFLAGS += -DASD_DEBUG -DASD_ENTER_EXIT
+endif
+
+obj-$(CONFIG_SCSI_AIC94XX) += aic94xx.o
+aic94xx-y += aic94xx_init.o \
+            aic94xx_hwi.o  \
+            aic94xx_reg.o  \
+            aic94xx_sds.o  \
+            aic94xx_seq.o  \
+            aic94xx_dump.o \
+            aic94xx_scb.o  \
+            aic94xx_dev.o  \
+            aic94xx_tmf.o  \
+            aic94xx_task.o
diff --git a/drivers/scsi/aic94xx/aic94xx.h b/drivers/scsi/aic94xx/aic94xx.h
new file mode 100644 (file)
index 0000000..1bd5b4e
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Aic94xx SAS/SATA driver header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx.h#31 $
+ */
+
+#ifndef _AIC94XX_H_
+#define _AIC94XX_H_
+
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <scsi/libsas.h>
+
+#define ASD_DRIVER_NAME                "aic94xx"
+#define ASD_DRIVER_DESCRIPTION "Adaptec aic94xx SAS/SATA driver"
+
+#define asd_printk(fmt, ...)   printk(KERN_NOTICE ASD_DRIVER_NAME ": " fmt, ## __VA_ARGS__)
+
+#ifdef ASD_ENTER_EXIT
+#define ENTER  printk(KERN_NOTICE "%s: ENTER %s\n", ASD_DRIVER_NAME, \
+               __FUNCTION__)
+#define EXIT   printk(KERN_NOTICE "%s: --EXIT %s\n", ASD_DRIVER_NAME, \
+               __FUNCTION__)
+#else
+#define ENTER
+#define EXIT
+#endif
+
+#ifdef ASD_DEBUG
+#define ASD_DPRINTK asd_printk
+#else
+#define ASD_DPRINTK(fmt, ...)
+#endif
+
+/* 2*ITNL timeout + 1 second */
+#define AIC94XX_SCB_TIMEOUT  (5*HZ)
+
+extern kmem_cache_t *asd_dma_token_cache;
+extern kmem_cache_t *asd_ascb_cache;
+extern char sas_addr_str[2*SAS_ADDR_SIZE + 1];
+
+static inline void asd_stringify_sas_addr(char *p, const u8 *sas_addr)
+{
+       int i;
+       for (i = 0; i < SAS_ADDR_SIZE; i++, p += 2)
+               snprintf(p, 3, "%02X", sas_addr[i]);
+       *p = '\0';
+}
+
+static inline void asd_destringify_sas_addr(u8 *sas_addr, const char *p)
+{
+       int i;
+       for (i = 0; i < SAS_ADDR_SIZE; i++) {
+               u8 h, l;
+               if (!*p)
+                       break;
+               h = isdigit(*p) ? *p-'0' : *p-'A'+10;
+               p++;
+               l = isdigit(*p) ? *p-'0' : *p-'A'+10;
+               p++;
+               sas_addr[i] = (h<<4) | l;
+       }
+}
+
+struct asd_ha_struct;
+struct asd_ascb;
+
+int  asd_read_ocm(struct asd_ha_struct *asd_ha);
+int  asd_read_flash(struct asd_ha_struct *asd_ha);
+
+int  asd_dev_found(struct domain_device *dev);
+void asd_dev_gone(struct domain_device *dev);
+
+void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id);
+
+int  asd_execute_task(struct sas_task *, int num, unsigned long gfp_flags);
+
+/* ---------- TMFs ---------- */
+int  asd_abort_task(struct sas_task *);
+int  asd_abort_task_set(struct domain_device *, u8 *lun);
+int  asd_clear_aca(struct domain_device *, u8 *lun);
+int  asd_clear_task_set(struct domain_device *, u8 *lun);
+int  asd_lu_reset(struct domain_device *, u8 *lun);
+int  asd_query_task(struct sas_task *);
+
+/* ---------- Adapter and Port management ---------- */
+int  asd_clear_nexus_port(struct asd_sas_port *port);
+int  asd_clear_nexus_ha(struct sas_ha_struct *sas_ha);
+
+/* ---------- Phy Management ---------- */
+int  asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg);
+
+#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c
new file mode 100644 (file)
index 0000000..6f8901b
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Aic94xx SAS/SATA DDB management
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/aic94xx/aic94xx_dev.c#21 $
+ */
+
+#include "aic94xx.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_sas.h"
+
+#define FIND_FREE_DDB(_ha) find_first_zero_bit((_ha)->hw_prof.ddb_bitmap, \
+                                              (_ha)->hw_prof.max_ddbs)
+#define SET_DDB(_ddb, _ha) set_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
+#define CLEAR_DDB(_ddb, _ha) clear_bit(_ddb, (_ha)->hw_prof.ddb_bitmap)
+
+static inline int asd_get_ddb(struct asd_ha_struct *asd_ha)
+{
+       unsigned long flags;
+       int ddb, i;
+
+       spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
+       ddb = FIND_FREE_DDB(asd_ha);
+       if (ddb >= asd_ha->hw_prof.max_ddbs) {
+               ddb = -ENOMEM;
+               spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+               goto out;
+       }
+       SET_DDB(ddb, asd_ha);
+       spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+
+       for (i = 0; i < sizeof(struct asd_ddb_ssp_smp_target_port); i+= 4)
+               asd_ddbsite_write_dword(asd_ha, ddb, i, 0);
+out:
+       return ddb;
+}
+
+#define INIT_CONN_TAG   offsetof(struct asd_ddb_ssp_smp_target_port, init_conn_tag)
+#define DEST_SAS_ADDR   offsetof(struct asd_ddb_ssp_smp_target_port, dest_sas_addr)
+#define SEND_QUEUE_HEAD offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_head)
+#define DDB_TYPE        offsetof(struct asd_ddb_ssp_smp_target_port, ddb_type)
+#define CONN_MASK       offsetof(struct asd_ddb_ssp_smp_target_port, conn_mask)
+#define DDB_TARG_FLAGS  offsetof(struct asd_ddb_ssp_smp_target_port, flags)
+#define DDB_TARG_FLAGS2 offsetof(struct asd_ddb_stp_sata_target_port, flags2)
+#define EXEC_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, exec_queue_tail)
+#define SEND_QUEUE_TAIL offsetof(struct asd_ddb_ssp_smp_target_port, send_queue_tail)
+#define SISTER_DDB      offsetof(struct asd_ddb_ssp_smp_target_port, sister_ddb)
+#define MAX_CCONN       offsetof(struct asd_ddb_ssp_smp_target_port, max_concurrent_conn)
+#define NUM_CTX         offsetof(struct asd_ddb_ssp_smp_target_port, num_contexts)
+#define ATA_CMD_SCBPTR  offsetof(struct asd_ddb_stp_sata_target_port, ata_cmd_scbptr)
+#define SATA_TAG_ALLOC_MASK offsetof(struct asd_ddb_stp_sata_target_port, sata_tag_alloc_mask)
+#define NUM_SATA_TAGS   offsetof(struct asd_ddb_stp_sata_target_port, num_sata_tags)
+#define SATA_STATUS     offsetof(struct asd_ddb_stp_sata_target_port, sata_status)
+#define NCQ_DATA_SCB_PTR offsetof(struct asd_ddb_stp_sata_target_port, ncq_data_scb_ptr)
+#define ITNL_TIMEOUT    offsetof(struct asd_ddb_ssp_smp_target_port, itnl_timeout)
+
+static inline void asd_free_ddb(struct asd_ha_struct *asd_ha, int ddb)
+{
+       unsigned long flags;
+
+       if (!ddb || ddb >= 0xFFFF)
+               return;
+       asd_ddbsite_write_byte(asd_ha, ddb, DDB_TYPE, DDB_TYPE_UNUSED);
+       spin_lock_irqsave(&asd_ha->hw_prof.ddb_lock, flags);
+       CLEAR_DDB(ddb, asd_ha);
+       spin_unlock_irqrestore(&asd_ha->hw_prof.ddb_lock, flags);
+}
+
+static inline void asd_set_ddb_type(struct domain_device *dev)
+{
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+       int ddb = (int) (unsigned long) dev->lldd_dev;
+
+       if (dev->dev_type == SATA_PM_PORT)
+               asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_PM_PORT);
+       else if (dev->tproto)
+               asd_ddbsite_write_byte(asd_ha,ddb, DDB_TYPE, DDB_TYPE_TARGET);
+       else
+               asd_ddbsite_write_byte(asd_ha,ddb,DDB_TYPE,DDB_TYPE_INITIATOR);
+}
+
+static int asd_init_sata_tag_ddb(struct domain_device *dev)
+{
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+       int ddb, i;
+
+       ddb = asd_get_ddb(asd_ha);
+       if (ddb < 0)
+               return ddb;
+
+       for (i = 0; i < sizeof(struct asd_ddb_sata_tag); i += 2)
+               asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
+
+       asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
+                              SISTER_DDB, ddb);
+       return 0;
+}
+
+static inline int asd_init_sata(struct domain_device *dev)
+{
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+       int ddb = (int) (unsigned long) dev->lldd_dev;
+       u32 qdepth = 0;
+       int res = 0;
+
+       asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
+       if ((dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM_PORT) &&
+           dev->sata_dev.identify_device &&
+           dev->sata_dev.identify_device[10] != 0) {
+               u16 w75 = le16_to_cpu(dev->sata_dev.identify_device[75]);
+               u16 w76 = le16_to_cpu(dev->sata_dev.identify_device[76]);
+
+               if (w76 & 0x100) /* NCQ? */
+                       qdepth = (w75 & 0x1F) + 1;
+               asd_ddbsite_write_dword(asd_ha, ddb, SATA_TAG_ALLOC_MASK,
+                                       (1<<qdepth)-1);
+               asd_ddbsite_write_byte(asd_ha, ddb, NUM_SATA_TAGS, qdepth);
+       }
+       if (dev->dev_type == SATA_DEV || dev->dev_type == SATA_PM ||
+           dev->dev_type == SATA_PM_PORT) {
+               struct dev_to_host_fis *fis = (struct dev_to_host_fis *)
+                       dev->frame_rcvd;
+               asd_ddbsite_write_byte(asd_ha, ddb, SATA_STATUS, fis->status);
+       }
+       asd_ddbsite_write_word(asd_ha, ddb, NCQ_DATA_SCB_PTR, 0xFFFF);
+       if (qdepth > 0)
+               res = asd_init_sata_tag_ddb(dev);
+       return res;
+}
+
+static int asd_init_target_ddb(struct domain_device *dev)
+{
+       int ddb, i;
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+       u8 flags = 0;
+
+       ddb = asd_get_ddb(asd_ha);
+       if (ddb < 0)
+               return ddb;
+
+       dev->lldd_dev = (void *) (unsigned long) ddb;
+
+       asd_ddbsite_write_byte(asd_ha, ddb, 0, DDB_TP_CONN_TYPE);
+       asd_ddbsite_write_byte(asd_ha, ddb, 1, 0);
+       asd_ddbsite_write_word(asd_ha, ddb, INIT_CONN_TAG, 0xFFFF);
+       for (i = 0; i < SAS_ADDR_SIZE; i++)
+               asd_ddbsite_write_byte(asd_ha, ddb, DEST_SAS_ADDR+i,
+                                      dev->sas_addr[i]);
+       asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_HEAD, 0xFFFF);
+       asd_set_ddb_type(dev);
+       asd_ddbsite_write_byte(asd_ha, ddb, CONN_MASK, dev->port->phy_mask);
+       if (dev->port->oob_mode != SATA_OOB_MODE) {
+               flags |= OPEN_REQUIRED;
+               if ((dev->dev_type == SATA_DEV) ||
+                   (dev->tproto & SAS_PROTO_STP)) {
+                       struct smp_resp *rps_resp = &dev->sata_dev.rps_resp;
+                       if (rps_resp->frame_type == SMP_RESPONSE &&
+                           rps_resp->function == SMP_REPORT_PHY_SATA &&
+                           rps_resp->result == SMP_RESP_FUNC_ACC) {
+                               if (rps_resp->rps.affil_valid)
+                                       flags |= STP_AFFIL_POL;
+                               if (rps_resp->rps.affil_supp)
+                                       flags |= SUPPORTS_AFFIL;
+                       }
+               } else {
+                       flags |= CONCURRENT_CONN_SUPP;
+                       if (!dev->parent &&
+                           (dev->dev_type == EDGE_DEV ||
+                            dev->dev_type == FANOUT_DEV))
+                               asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
+                                                      4);
+                       else
+                               asd_ddbsite_write_byte(asd_ha, ddb, MAX_CCONN,
+                                                      dev->pathways);
+                       asd_ddbsite_write_byte(asd_ha, ddb, NUM_CTX, 1);
+               }
+       }
+       if (dev->dev_type == SATA_PM)
+               flags |= SATA_MULTIPORT;
+       asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS, flags);
+
+       flags = 0;
+       if (dev->tproto & SAS_PROTO_STP)
+               flags |= STP_CL_POL_NO_TX;
+       asd_ddbsite_write_byte(asd_ha, ddb, DDB_TARG_FLAGS2, flags);
+
+       asd_ddbsite_write_word(asd_ha, ddb, EXEC_QUEUE_TAIL, 0xFFFF);
+       asd_ddbsite_write_word(asd_ha, ddb, SEND_QUEUE_TAIL, 0xFFFF);
+       asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
+
+       if (dev->dev_type == SATA_DEV || (dev->tproto & SAS_PROTO_STP)) {
+               i = asd_init_sata(dev);
+               if (i < 0) {
+                       asd_free_ddb(asd_ha, ddb);
+                       return i;
+               }
+       }
+
+       if (dev->dev_type == SAS_END_DEV) {
+               struct sas_end_device *rdev = rphy_to_end_device(dev->rphy);
+               if (rdev->I_T_nexus_loss_timeout > 0)
+                       asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
+                                              min(rdev->I_T_nexus_loss_timeout,
+                                                  (u16)ITNL_TIMEOUT_CONST));
+               else
+                       asd_ddbsite_write_word(asd_ha, ddb, ITNL_TIMEOUT,
+                                              (u16)ITNL_TIMEOUT_CONST);
+       }
+       return 0;
+}
+
+static int asd_init_sata_pm_table_ddb(struct domain_device *dev)
+{
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+       int ddb, i;
+
+       ddb = asd_get_ddb(asd_ha);
+       if (ddb < 0)
+               return ddb;
+
+       for (i = 0; i < 32; i += 2)
+               asd_ddbsite_write_word(asd_ha, ddb, i, 0xFFFF);
+
+       asd_ddbsite_write_word(asd_ha, (int) (unsigned long) dev->lldd_dev,
+                              SISTER_DDB, ddb);
+
+       return 0;
+}
+
+#define PM_PORT_FLAGS offsetof(struct asd_ddb_sata_pm_port, pm_port_flags)
+#define PARENT_DDB    offsetof(struct asd_ddb_sata_pm_port, parent_ddb)
+
+/**
+ * asd_init_sata_pm_port_ddb -- SATA Port Multiplier Port
+ * dev: pointer to domain device
+ *
+ * For SATA Port Multiplier Ports we need to allocate one SATA Port
+ * Multiplier Port DDB and depending on whether the target on it
+ * supports SATA II NCQ, one SATA Tag DDB.
+ */
+static int asd_init_sata_pm_port_ddb(struct domain_device *dev)
+{
+       int ddb, i, parent_ddb, pmtable_ddb;
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+       u8  flags;
+
+       ddb = asd_get_ddb(asd_ha);
+       if (ddb < 0)
+               return ddb;
+
+       asd_set_ddb_type(dev);
+       flags = (dev->sata_dev.port_no << 4) | PM_PORT_SET;
+       asd_ddbsite_write_byte(asd_ha, ddb, PM_PORT_FLAGS, flags);
+       asd_ddbsite_write_word(asd_ha, ddb, SISTER_DDB, 0xFFFF);
+       asd_ddbsite_write_word(asd_ha, ddb, ATA_CMD_SCBPTR, 0xFFFF);
+       asd_init_sata(dev);
+
+       parent_ddb = (int) (unsigned long) dev->parent->lldd_dev;
+       asd_ddbsite_write_word(asd_ha, ddb, PARENT_DDB, parent_ddb);
+       pmtable_ddb = asd_ddbsite_read_word(asd_ha, parent_ddb, SISTER_DDB);
+       asd_ddbsite_write_word(asd_ha, pmtable_ddb, dev->sata_dev.port_no,ddb);
+
+       if (asd_ddbsite_read_byte(asd_ha, ddb, NUM_SATA_TAGS) > 0) {
+               i = asd_init_sata_tag_ddb(dev);
+               if (i < 0) {
+                       asd_free_ddb(asd_ha, ddb);
+                       return i;
+               }
+       }
+       return 0;
+}
+
+static int asd_init_initiator_ddb(struct domain_device *dev)
+{
+       return -ENODEV;
+}
+
+/**
+ * asd_init_sata_pm_ddb -- SATA Port Multiplier
+ * dev: pointer to domain device
+ *
+ * For STP and direct-attached SATA Port Multipliers we need
+ * one target port DDB entry and one SATA PM table DDB entry.
+ */
+static int asd_init_sata_pm_ddb(struct domain_device *dev)
+{
+       int res = 0;
+
+       res = asd_init_target_ddb(dev);
+       if (res)
+               goto out;
+       res = asd_init_sata_pm_table_ddb(dev);
+       if (res)
+               asd_free_ddb(dev->port->ha->lldd_ha,
+                            (int) (unsigned long) dev->lldd_dev);
+out:
+       return res;
+}
+
+int asd_dev_found(struct domain_device *dev)
+{
+       int res = 0;
+
+       switch (dev->dev_type) {
+       case SATA_PM:
+               res = asd_init_sata_pm_ddb(dev);
+               break;
+       case SATA_PM_PORT:
+               res = asd_init_sata_pm_port_ddb(dev);
+               break;
+       default:
+               if (dev->tproto)
+                       res = asd_init_target_ddb(dev);
+               else
+                       res = asd_init_initiator_ddb(dev);
+       }
+       return res;
+}
+
+void asd_dev_gone(struct domain_device *dev)
+{
+       int ddb, sister_ddb;
+       struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
+
+       ddb = (int) (unsigned long) dev->lldd_dev;
+       sister_ddb = asd_ddbsite_read_word(asd_ha, ddb, SISTER_DDB);
+
+       if (sister_ddb != 0xFFFF)
+               asd_free_ddb(asd_ha, sister_ddb);
+       asd_free_ddb(asd_ha, ddb);
+       dev->lldd_dev = NULL;
+}
diff --git a/drivers/scsi/aic94xx/aic94xx_dump.c b/drivers/scsi/aic94xx/aic94xx_dump.c
new file mode 100644 (file)
index 0000000..e6ade59
--- /dev/null
@@ -0,0 +1,959 @@
+/*
+ * Aic94xx SAS/SATA driver dump interface.
+ *
+ * Copyright (C) 2004 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2004 David Chaw <david_chaw@adaptec.com>
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * 2005/07/14/LT  Complete overhaul of this file.  Update pages, register
+ * locations, names, etc.  Make use of macros.  Print more information.
+ * Print all cseq and lseq mip and mdp.
+ *
+ */
+
+#include "linux/pci.h"
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_reg_def.h"
+#include "aic94xx_sas.h"
+
+#include "aic94xx_dump.h"
+
+#ifdef ASD_DEBUG
+
+#define MD(x)      (1 << (x))
+#define MODE_COMMON (1 << 31)
+#define MODE_0_7    (0xFF)
+
+static const struct lseq_cio_regs {
+       char    *name;
+       u32     offs;
+       u8      width;
+       u32     mode;
+} LSEQmCIOREGS[] = {
+       {"LmMnSCBPTR",    0x20, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) },
+       {"LmMnDDBPTR",    0x22, 16, MD(0)|MD(1)|MD(2)|MD(3)|MD(4) },
+       {"LmREQMBX",      0x30, 32, MODE_COMMON },
+       {"LmRSPMBX",      0x34, 32, MODE_COMMON },
+       {"LmMnINT",       0x38, 32, MODE_0_7 },
+       {"LmMnINTEN",     0x3C, 32, MODE_0_7 },
+       {"LmXMTPRIMD",    0x40, 32, MODE_COMMON },
+       {"LmXMTPRIMCS",   0x44,  8, MODE_COMMON },
+       {"LmCONSTAT",     0x45,  8, MODE_COMMON },
+       {"LmMnDMAERRS",   0x46,  8, MD(0)|MD(1) },
+       {"LmMnSGDMAERRS", 0x47,  8, MD(0)|MD(1) },
+       {"LmMnEXPHDRP",   0x48,  8, MD(0) },
+       {"LmMnSASAALIGN", 0x48,  8, MD(1) },
+       {"LmMnMSKHDRP",   0x49,  8, MD(0) },
+       {"LmMnSTPALIGN",  0x49,  8, MD(1) },
+       {"LmMnRCVHDRP",   0x4A,  8, MD(0) },
+       {"LmMnXMTHDRP",   0x4A,  8, MD(1) },
+       {"LmALIGNMODE",   0x4B,  8, MD(1) },
+       {"LmMnEXPRCVCNT", 0x4C, 32, MD(0) },
+       {"LmMnXMTCNT",    0x4C, 32, MD(1) },
+       {"LmMnCURRTAG",   0x54, 16, MD(0) },
+       {"LmMnPREVTAG",   0x56, 16, MD(0) },
+       {"LmMnACKOFS",    0x58,  8, MD(1) },
+       {"LmMnXFRLVL",    0x59,  8, MD(0)|MD(1) },
+       {"LmMnSGDMACTL",  0x5A,  8, MD(0)|MD(1) },
+       {"LmMnSGDMASTAT", 0x5B,  8, MD(0)|MD(1) },
+       {"LmMnDDMACTL",   0x5C,  8, MD(0)|MD(1) },
+       {"LmMnDDMASTAT",  0x5D,  8, MD(0)|MD(1) },
+       {"LmMnDDMAMODE",  0x5E, 16, MD(0)|MD(1) },
+       {"LmMnPIPECTL",   0x61,  8, MD(0)|MD(1) },
+       {"LmMnACTSCB",    0x62, 16, MD(0)|MD(1) },
+       {"LmMnSGBHADR",   0x64,  8, MD(0)|MD(1) },
+       {"LmMnSGBADR",    0x65,  8, MD(0)|MD(1) },
+       {"LmMnSGDCNT",    0x66,  8, MD(0)|MD(1) },
+       {"LmMnSGDMADR",   0x68, 32, MD(0)|MD(1) },
+       {"LmMnSGDMADR",   0x6C, 32, MD(0)|MD(1) },
+       {"LmMnXFRCNT",    0x70, 32, MD(0)|MD(1) },
+       {"LmMnXMTCRC",    0x74, 32, MD(1) },
+       {"LmCURRTAG",     0x74, 16, MD(0) },
+       {"LmPREVTAG",     0x76, 16, MD(0) },
+       {"LmMnDPSEL",     0x7B,  8, MD(0)|MD(1) },
+       {"LmDPTHSTAT",    0x7C,  8, MODE_COMMON },
+       {"LmMnHOLDLVL",   0x7D,  8, MD(0) },
+       {"LmMnSATAFS",    0x7E,  8, MD(1) },
+       {"LmMnCMPLTSTAT", 0x7F,  8, MD(0)|MD(1) },
+       {"LmPRMSTAT0",    0x80, 32, MODE_COMMON },
+       {"LmPRMSTAT1",    0x84, 32, MODE_COMMON },
+       {"LmGPRMINT",     0x88,  8, MODE_COMMON },
+        {"LmMnCURRSCB",   0x8A, 16, MD(0) },
+       {"LmPRMICODE",    0x8C, 32, MODE_COMMON },
+       {"LmMnRCVCNT",    0x90, 16, MD(0) },
+       {"LmMnBUFSTAT",   0x92, 16, MD(0) },
+       {"LmMnXMTHDRSIZE",0x92,  8, MD(1) },
+       {"LmMnXMTSIZE",   0x93,  8, MD(1) },
+       {"LmMnTGTXFRCNT", 0x94, 32, MD(0) },
+       {"LmMnEXPROFS",   0x98, 32, MD(0) },
+       {"LmMnXMTROFS",   0x98, 32, MD(1) },
+       {"LmMnRCVROFS",   0x9C, 32, MD(0) },
+       {"LmCONCTL",      0xA0, 16, MODE_COMMON },
+       {"LmBITLTIMER",   0xA2, 16, MODE_COMMON },
+       {"LmWWNLOW",      0xA8, 32, MODE_COMMON },
+       {"LmWWNHIGH",     0xAC, 32, MODE_COMMON },
+       {"LmMnFRMERR",    0xB0, 32, MD(0) },
+       {"LmMnFRMERREN",  0xB4, 32, MD(0) },
+       {"LmAWTIMER",     0xB8, 16, MODE_COMMON },
+       {"LmAWTCTL",      0xBA,  8, MODE_COMMON },
+       {"LmMnHDRCMPS",   0xC0, 32, MD(0) },
+       {"LmMnXMTSTAT",   0xC4,  8, MD(1) },
+       {"LmHWTSTATEN",   0xC5,  8, MODE_COMMON },
+       {"LmMnRRDYRC",    0xC6,  8, MD(0) },
+        {"LmMnRRDYTC",    0xC6,  8, MD(1) },
+       {"LmHWTSTAT",     0xC7,  8, MODE_COMMON },
+       {"LmMnDATABUFADR",0xC8, 16, MD(0)|MD(1) },
+       {"LmDWSSTATUS",   0xCB,  8, MODE_COMMON },
+       {"LmMnACTSTAT",   0xCE, 16, MD(0)|MD(1) },
+       {"LmMnREQSCB",    0xD2, 16, MD(0)|MD(1) },
+       {"LmXXXPRIM",     0xD4, 32, MODE_COMMON },
+       {"LmRCVASTAT",    0xD9,  8, MODE_COMMON },
+       {"LmINTDIS1",     0xDA,  8, MODE_COMMON },
+       {"LmPSTORESEL",   0xDB,  8, MODE_COMMON },
+       {"LmPSTORE",      0xDC, 32, MODE_COMMON },
+       {"LmPRIMSTAT0EN", 0xE0, 32, MODE_COMMON },
+       {"LmPRIMSTAT1EN", 0xE4, 32, MODE_COMMON },
+       {"LmDONETCTL",    0xF2, 16, MODE_COMMON },
+       {NULL, 0, 0, 0 }
+};
+/*
+static struct lseq_cio_regs LSEQmOOBREGS[] = {
+   {"OOB_BFLTR"        ,0x100, 8, MD(5)},
+   {"OOB_INIT_MIN"     ,0x102,16, MD(5)},
+   {"OOB_INIT_MAX"     ,0x104,16, MD(5)},
+   {"OOB_INIT_NEG"     ,0x106,16, MD(5)},
+   {"OOB_SAS_MIN"      ,0x108,16, MD(5)},
+   {"OOB_SAS_MAX"      ,0x10A,16, MD(5)},
+   {"OOB_SAS_NEG"      ,0x10C,16, MD(5)},
+   {"OOB_WAKE_MIN"     ,0x10E,16, MD(5)},
+   {"OOB_WAKE_MAX"     ,0x110,16, MD(5)},
+   {"OOB_WAKE_NEG"     ,0x112,16, MD(5)},
+   {"OOB_IDLE_MAX"     ,0x114,16, MD(5)},
+   {"OOB_BURST_MAX"    ,0x116,16, MD(5)},
+   {"OOB_XMIT_BURST"   ,0x118, 8, MD(5)},
+   {"OOB_SEND_PAIRS"   ,0x119, 8, MD(5)},
+   {"OOB_INIT_IDLE"    ,0x11A, 8, MD(5)},
+   {"OOB_INIT_NEGO"    ,0x11C, 8, MD(5)},
+   {"OOB_SAS_IDLE"     ,0x11E, 8, MD(5)},
+   {"OOB_SAS_NEGO"     ,0x120, 8, MD(5)},
+   {"OOB_WAKE_IDLE"    ,0x122, 8, MD(5)},
+   {"OOB_WAKE_NEGO"    ,0x124, 8, MD(5)},
+   {"OOB_DATA_KBITS"   ,0x126, 8, MD(5)},
+   {"OOB_BURST_DATA"   ,0x128,32, MD(5)},
+   {"OOB_ALIGN_0_DATA" ,0x12C,32, MD(5)},
+   {"OOB_ALIGN_1_DATA" ,0x130,32, MD(5)},
+   {"OOB_SYNC_DATA"    ,0x134,32, MD(5)},
+   {"OOB_D10_2_DATA"   ,0x138,32, MD(5)},
+   {"OOB_PHY_RST_CNT"  ,0x13C,32, MD(5)},
+   {"OOB_SIG_GEN"      ,0x140, 8, MD(5)},
+   {"OOB_XMIT"         ,0x141, 8, MD(5)},
+   {"FUNCTION_MAKS"    ,0x142, 8, MD(5)},
+   {"OOB_MODE"         ,0x143, 8, MD(5)},
+   {"CURRENT_STATUS"   ,0x144, 8, MD(5)},
+   {"SPEED_MASK"       ,0x145, 8, MD(5)},
+   {"PRIM_COUNT"       ,0x146, 8, MD(5)},
+   {"OOB_SIGNALS"      ,0x148, 8, MD(5)},
+   {"OOB_DATA_DET"     ,0x149, 8, MD(5)},
+   {"OOB_TIME_OUT"     ,0x14C, 8, MD(5)},
+   {"OOB_TIMER_ENABLE" ,0x14D, 8, MD(5)},
+   {"OOB_STATUS"       ,0x14E, 8, MD(5)},
+   {"HOT_PLUG_DELAY"   ,0x150, 8, MD(5)},
+   {"RCD_DELAY"        ,0x151, 8, MD(5)},
+   {"COMSAS_TIMER"     ,0x152, 8, MD(5)},
+   {"SNTT_DELAY"       ,0x153, 8, MD(5)},
+   {"SPD_CHNG_DELAY"   ,0x154, 8, MD(5)},
+   {"SNLT_DELAY"       ,0x155, 8, MD(5)},
+   {"SNWT_DELAY"       ,0x156, 8, MD(5)},
+   {"ALIGN_DELAY"      ,0x157, 8, MD(5)},
+   {"INT_ENABLE_0"     ,0x158, 8, MD(5)},
+   {"INT_ENABLE_1"     ,0x159, 8, MD(5)},
+   {"INT_ENABLE_2"     ,0x15A, 8, MD(5)},
+   {"INT_ENABLE_3"     ,0x15B, 8, MD(5)},
+   {"OOB_TEST_REG"     ,0x15C, 8, MD(5)},
+   {"PHY_CONTROL_0"    ,0x160, 8, MD(5)},
+   {"PHY_CONTROL_1"    ,0x161, 8, MD(5)},
+   {"PHY_CONTROL_2"    ,0x162, 8, MD(5)},
+   {"PHY_CONTROL_3"    ,0x163, 8, MD(5)},
+   {"PHY_OOB_CAL_TX"   ,0x164, 8, MD(5)},
+   {"PHY_OOB_CAL_RX"   ,0x165, 8, MD(5)},
+   {"OOB_PHY_CAL_TX"   ,0x166, 8, MD(5)},
+   {"OOB_PHY_CAL_RX"   ,0x167, 8, MD(5)},
+   {"PHY_CONTROL_4"    ,0x168, 8, MD(5)},
+   {"PHY_TEST"         ,0x169, 8, MD(5)},
+   {"PHY_PWR_CTL"      ,0x16A, 8, MD(5)},
+   {"PHY_PWR_DELAY"    ,0x16B, 8, MD(5)},
+   {"OOB_SM_CON"       ,0x16C, 8, MD(5)},
+   {"ADDR_TRAP_1"      ,0x16D, 8, MD(5)},
+   {"ADDR_NEXT_1"      ,0x16E, 8, MD(5)},
+   {"NEXT_ST_1"        ,0x16F, 8, MD(5)},
+   {"OOB_SM_STATE"     ,0x170, 8, MD(5)},
+   {"ADDR_TRAP_2"      ,0x171, 8, MD(5)},
+   {"ADDR_NEXT_2"      ,0x172, 8, MD(5)},
+   {"NEXT_ST_2"        ,0x173, 8, MD(5)},
+   {NULL, 0, 0, 0 }
+};
+*/
+#define STR_8BIT   "   %30s[0x%04x]:0x%02x\n"
+#define STR_16BIT  "   %30s[0x%04x]:0x%04x\n"
+#define STR_32BIT  "   %30s[0x%04x]:0x%08x\n"
+#define STR_64BIT  "   %30s[0x%04x]:0x%llx\n"
+
+#define PRINT_REG_8bit(_ha, _n, _r) asd_printk(STR_8BIT, #_n, _n,      \
+                                            asd_read_reg_byte(_ha, _r))
+#define PRINT_REG_16bit(_ha, _n, _r) asd_printk(STR_16BIT, #_n, _n,     \
+                                             asd_read_reg_word(_ha, _r))
+#define PRINT_REG_32bit(_ha, _n, _r) asd_printk(STR_32BIT, #_n, _n,      \
+                                             asd_read_reg_dword(_ha, _r))
+
+#define PRINT_CREG_8bit(_ha, _n) asd_printk(STR_8BIT, #_n, _n,      \
+                                            asd_read_reg_byte(_ha, C##_n))
+#define PRINT_CREG_16bit(_ha, _n) asd_printk(STR_16BIT, #_n, _n,     \
+                                             asd_read_reg_word(_ha, C##_n))
+#define PRINT_CREG_32bit(_ha, _n) asd_printk(STR_32BIT, #_n, _n,      \
+                                             asd_read_reg_dword(_ha, C##_n))
+
+#define MSTR_8BIT   "   Mode:%02d %30s[0x%04x]:0x%02x\n"
+#define MSTR_16BIT  "   Mode:%02d %30s[0x%04x]:0x%04x\n"
+#define MSTR_32BIT  "   Mode:%02d %30s[0x%04x]:0x%08x\n"
+
+#define PRINT_MREG_8bit(_ha, _m, _n, _r) asd_printk(MSTR_8BIT, _m, #_n, _n,   \
+                                            asd_read_reg_byte(_ha, _r))
+#define PRINT_MREG_16bit(_ha, _m, _n, _r) asd_printk(MSTR_16BIT, _m, #_n, _n, \
+                                             asd_read_reg_word(_ha, _r))
+#define PRINT_MREG_32bit(_ha, _m, _n, _r) asd_printk(MSTR_32BIT, _m, #_n, _n, \
+                                             asd_read_reg_dword(_ha, _r))
+
+/* can also be used for MD when the register is mode aware already */
+#define PRINT_MIS_byte(_ha, _n) asd_printk(STR_8BIT, #_n,CSEQ_##_n-CMAPPEDSCR,\
+                                           asd_read_reg_byte(_ha, CSEQ_##_n))
+#define PRINT_MIS_word(_ha, _n) asd_printk(STR_16BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\
+                                           asd_read_reg_word(_ha, CSEQ_##_n))
+#define PRINT_MIS_dword(_ha, _n)                      \
+        asd_printk(STR_32BIT,#_n,CSEQ_##_n-CMAPPEDSCR,\
+                   asd_read_reg_dword(_ha, CSEQ_##_n))
+#define PRINT_MIS_qword(_ha, _n)                                       \
+        asd_printk(STR_64BIT, #_n,CSEQ_##_n-CMAPPEDSCR,                \
+                   (unsigned long long)(((u64)asd_read_reg_dword(_ha, CSEQ_##_n))     \
+                 | (((u64)asd_read_reg_dword(_ha, (CSEQ_##_n)+4))<<32)))
+
+#define CMDP_REG(_n, _m) (_m*(CSEQ_PAGE_SIZE*2)+CSEQ_##_n)
+#define PRINT_CMDP_word(_ha, _n) \
+asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \
+       #_n, \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 0)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 1)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 2)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 3)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 4)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 5)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 6)), \
+       asd_read_reg_word(_ha, CMDP_REG(_n, 7)))
+
+#define PRINT_CMDP_byte(_ha, _n) \
+asd_printk("%20s 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", \
+       #_n, \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 0)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 1)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 2)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 3)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 4)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 5)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 6)), \
+       asd_read_reg_byte(_ha, CMDP_REG(_n, 7)))
+
+static void asd_dump_cseq_state(struct asd_ha_struct *asd_ha)
+{
+       int mode;
+
+       asd_printk("CSEQ STATE\n");
+
+       asd_printk("ARP2 REGISTERS\n");
+
+       PRINT_CREG_32bit(asd_ha, ARP2CTL);
+       PRINT_CREG_32bit(asd_ha, ARP2INT);
+       PRINT_CREG_32bit(asd_ha, ARP2INTEN);
+       PRINT_CREG_8bit(asd_ha, MODEPTR);
+       PRINT_CREG_8bit(asd_ha, ALTMODE);
+       PRINT_CREG_8bit(asd_ha, FLAG);
+       PRINT_CREG_8bit(asd_ha, ARP2INTCTL);
+       PRINT_CREG_16bit(asd_ha, STACK);
+       PRINT_CREG_16bit(asd_ha, PRGMCNT);
+       PRINT_CREG_16bit(asd_ha, ACCUM);
+       PRINT_CREG_16bit(asd_ha, SINDEX);
+       PRINT_CREG_16bit(asd_ha, DINDEX);
+       PRINT_CREG_8bit(asd_ha, SINDIR);
+       PRINT_CREG_8bit(asd_ha, DINDIR);
+       PRINT_CREG_8bit(asd_ha, JUMLDIR);
+       PRINT_CREG_8bit(asd_ha, ARP2HALTCODE);
+       PRINT_CREG_16bit(asd_ha, CURRADDR);
+       PRINT_CREG_16bit(asd_ha, LASTADDR);
+       PRINT_CREG_16bit(asd_ha, NXTLADDR);
+
+       asd_printk("IOP REGISTERS\n");
+
+       PRINT_REG_32bit(asd_ha, BISTCTL1, CBISTCTL);
+       PRINT_CREG_32bit(asd_ha, MAPPEDSCR);
+
+       asd_printk("CIO REGISTERS\n");
+
+       for (mode = 0; mode < 9; mode++)
+               PRINT_MREG_16bit(asd_ha, mode, MnSCBPTR, CMnSCBPTR(mode));
+       PRINT_MREG_16bit(asd_ha, 15, MnSCBPTR, CMnSCBPTR(15));
+
+       for (mode = 0; mode < 9; mode++)
+               PRINT_MREG_16bit(asd_ha, mode, MnDDBPTR, CMnDDBPTR(mode));
+       PRINT_MREG_16bit(asd_ha, 15, MnDDBPTR, CMnDDBPTR(15));
+
+       for (mode = 0; mode < 8; mode++)
+               PRINT_MREG_32bit(asd_ha, mode, MnREQMBX, CMnREQMBX(mode));
+       for (mode = 0; mode < 8; mode++)
+               PRINT_MREG_32bit(asd_ha, mode, MnRSPMBX, CMnRSPMBX(mode));
+       for (mode = 0; mode < 8; mode++)
+               PRINT_MREG_32bit(asd_ha, mode, MnINT, CMnINT(mode));
+       for (mode = 0; mode < 8; mode++)
+               PRINT_MREG_32bit(asd_ha, mode, MnINTEN, CMnINTEN(mode));
+
+       PRINT_CREG_8bit(asd_ha, SCRATCHPAGE);
+       for (mode = 0; mode < 8; mode++)
+               PRINT_MREG_8bit(asd_ha, mode, MnSCRATCHPAGE,
+                               CMnSCRATCHPAGE(mode));
+
+       PRINT_REG_32bit(asd_ha, CLINKCON, CLINKCON);
+       PRINT_REG_8bit(asd_ha, CCONMSK, CCONMSK);
+       PRINT_REG_8bit(asd_ha, CCONEXIST, CCONEXIST);
+       PRINT_REG_16bit(asd_ha, CCONMODE, CCONMODE);
+       PRINT_REG_32bit(asd_ha, CTIMERCALC, CTIMERCALC);
+       PRINT_REG_8bit(asd_ha, CINTDIS, CINTDIS);
+
+       asd_printk("SCRATCH MEMORY\n");
+
+       asd_printk("MIP 4 >>>>>\n");
+       PRINT_MIS_word(asd_ha, Q_EXE_HEAD);
+       PRINT_MIS_word(asd_ha, Q_EXE_TAIL);
+       PRINT_MIS_word(asd_ha, Q_DONE_HEAD);
+       PRINT_MIS_word(asd_ha, Q_DONE_TAIL);
+       PRINT_MIS_word(asd_ha, Q_SEND_HEAD);
+       PRINT_MIS_word(asd_ha, Q_SEND_TAIL);
+       PRINT_MIS_word(asd_ha, Q_DMA2CHIM_HEAD);
+       PRINT_MIS_word(asd_ha, Q_DMA2CHIM_TAIL);
+       PRINT_MIS_word(asd_ha, Q_COPY_HEAD);
+       PRINT_MIS_word(asd_ha, Q_COPY_TAIL);
+       PRINT_MIS_word(asd_ha, REG0);
+       PRINT_MIS_word(asd_ha, REG1);
+       PRINT_MIS_dword(asd_ha, REG2);
+       PRINT_MIS_byte(asd_ha, LINK_CTL_Q_MAP);
+       PRINT_MIS_byte(asd_ha, MAX_CSEQ_MODE);
+       PRINT_MIS_byte(asd_ha, FREE_LIST_HACK_COUNT);
+
+       asd_printk("MIP 5 >>>>\n");
+       PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_QUEUE);
+       PRINT_MIS_qword(asd_ha, EST_NEXUS_REQ_COUNT);
+       PRINT_MIS_word(asd_ha, Q_EST_NEXUS_HEAD);
+       PRINT_MIS_word(asd_ha, Q_EST_NEXUS_TAIL);
+       PRINT_MIS_word(asd_ha, NEED_EST_NEXUS_SCB);
+       PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_HEAD);
+       PRINT_MIS_byte(asd_ha, EST_NEXUS_REQ_TAIL);
+       PRINT_MIS_byte(asd_ha, EST_NEXUS_SCB_OFFSET);
+
+       asd_printk("MIP 6 >>>>\n");
+       PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR0);
+       PRINT_MIS_word(asd_ha, INT_ROUT_RET_ADDR1);
+       PRINT_MIS_word(asd_ha, INT_ROUT_SCBPTR);
+       PRINT_MIS_byte(asd_ha, INT_ROUT_MODE);
+       PRINT_MIS_byte(asd_ha, ISR_SCRATCH_FLAGS);
+       PRINT_MIS_word(asd_ha, ISR_SAVE_SINDEX);
+       PRINT_MIS_word(asd_ha, ISR_SAVE_DINDEX);
+       PRINT_MIS_word(asd_ha, Q_MONIRTT_HEAD);
+       PRINT_MIS_word(asd_ha, Q_MONIRTT_TAIL);
+       PRINT_MIS_byte(asd_ha, FREE_SCB_MASK);
+       PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_HEAD);
+       PRINT_MIS_word(asd_ha, BUILTIN_FREE_SCB_TAIL);
+       PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_HEAD);
+       PRINT_MIS_word(asd_ha, EXTENDED_FREE_SCB_TAIL);
+
+       asd_printk("MIP 7 >>>>\n");
+       PRINT_MIS_qword(asd_ha, EMPTY_REQ_QUEUE);
+       PRINT_MIS_qword(asd_ha, EMPTY_REQ_COUNT);
+       PRINT_MIS_word(asd_ha, Q_EMPTY_HEAD);
+       PRINT_MIS_word(asd_ha, Q_EMPTY_TAIL);
+       PRINT_MIS_word(asd_ha, NEED_EMPTY_SCB);
+       PRINT_MIS_byte(asd_ha, EMPTY_REQ_HEAD);
+       PRINT_MIS_byte(asd_ha, EMPTY_REQ_TAIL);
+       PRINT_MIS_byte(asd_ha, EMPTY_SCB_OFFSET);
+       PRINT_MIS_word(asd_ha, PRIMITIVE_DATA);
+       PRINT_MIS_dword(asd_ha, TIMEOUT_CONST);
+
+       asd_printk("MDP 0 >>>>\n");
+       asd_printk("%-20s %6s %6s %6s %6s %6s %6s %6s %6s\n",
+                  "Mode: ", "0", "1", "2", "3", "4", "5", "6", "7");
+       PRINT_CMDP_word(asd_ha, LRM_SAVE_SINDEX);
+       PRINT_CMDP_word(asd_ha, LRM_SAVE_SCBPTR);
+       PRINT_CMDP_word(asd_ha, Q_LINK_HEAD);
+       PRINT_CMDP_word(asd_ha, Q_LINK_TAIL);
+       PRINT_CMDP_byte(asd_ha, LRM_SAVE_SCRPAGE);
+
+       asd_printk("MDP 0 Mode 8 >>>>\n");
+       PRINT_MIS_word(asd_ha, RET_ADDR);
+       PRINT_MIS_word(asd_ha, RET_SCBPTR);
+       PRINT_MIS_word(asd_ha, SAVE_SCBPTR);
+       PRINT_MIS_word(asd_ha, EMPTY_TRANS_CTX);
+       PRINT_MIS_word(asd_ha, RESP_LEN);
+       PRINT_MIS_word(asd_ha, TMF_SCBPTR);
+       PRINT_MIS_word(asd_ha, GLOBAL_PREV_SCB);
+       PRINT_MIS_word(asd_ha, GLOBAL_HEAD);
+       PRINT_MIS_word(asd_ha, CLEAR_LU_HEAD);
+       PRINT_MIS_byte(asd_ha, TMF_OPCODE);
+       PRINT_MIS_byte(asd_ha, SCRATCH_FLAGS);
+       PRINT_MIS_word(asd_ha, HSB_SITE);
+       PRINT_MIS_word(asd_ha, FIRST_INV_SCB_SITE);
+       PRINT_MIS_word(asd_ha, FIRST_INV_DDB_SITE);
+
+       asd_printk("MDP 1 Mode 8 >>>>\n");
+       PRINT_MIS_qword(asd_ha, LUN_TO_CLEAR);
+       PRINT_MIS_qword(asd_ha, LUN_TO_CHECK);
+
+       asd_printk("MDP 2 Mode 8 >>>>\n");
+       PRINT_MIS_qword(asd_ha, HQ_NEW_POINTER);
+       PRINT_MIS_qword(asd_ha, HQ_DONE_BASE);
+       PRINT_MIS_dword(asd_ha, HQ_DONE_POINTER);
+       PRINT_MIS_byte(asd_ha, HQ_DONE_PASS);
+}
+
+#define PRINT_LREG_8bit(_h, _lseq, _n) \
+        asd_printk(STR_8BIT, #_n, _n, asd_read_reg_byte(_h, Lm##_n(_lseq)))
+#define PRINT_LREG_16bit(_h, _lseq, _n) \
+        asd_printk(STR_16BIT, #_n, _n, asd_read_reg_word(_h, Lm##_n(_lseq)))
+#define PRINT_LREG_32bit(_h, _lseq, _n) \
+        asd_printk(STR_32BIT, #_n, _n, asd_read_reg_dword(_h, Lm##_n(_lseq)))
+
+#define PRINT_LMIP_byte(_h, _lseq, _n)                              \
+       asd_printk(STR_8BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+                  asd_read_reg_byte(_h, LmSEQ_##_n(_lseq)))
+#define PRINT_LMIP_word(_h, _lseq, _n)                              \
+       asd_printk(STR_16BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+                  asd_read_reg_word(_h, LmSEQ_##_n(_lseq)))
+#define PRINT_LMIP_dword(_h, _lseq, _n)                             \
+       asd_printk(STR_32BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+                  asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)))
+#define PRINT_LMIP_qword(_h, _lseq, _n)                                \
+       asd_printk(STR_64BIT, #_n, LmSEQ_##_n(_lseq)-LmSCRATCH(_lseq), \
+                (unsigned long long)(((unsigned long long) \
+                asd_read_reg_dword(_h, LmSEQ_##_n(_lseq))) \
+                 | (((unsigned long long) \
+                asd_read_reg_dword(_h, LmSEQ_##_n(_lseq)+4))<<32)))
+
+static void asd_print_lseq_cio_reg(struct asd_ha_struct *asd_ha,
+                                  u32 lseq_cio_addr, int i)
+{
+       switch (LSEQmCIOREGS[i].width) {
+       case 8:
+               asd_printk("%20s[0x%x]: 0x%02x\n", LSEQmCIOREGS[i].name,
+                          LSEQmCIOREGS[i].offs,
+                          asd_read_reg_byte(asd_ha, lseq_cio_addr +
+                                            LSEQmCIOREGS[i].offs));
+
+               break;
+       case 16:
+               asd_printk("%20s[0x%x]: 0x%04x\n", LSEQmCIOREGS[i].name,
+                          LSEQmCIOREGS[i].offs,
+                          asd_read_reg_word(asd_ha, lseq_cio_addr +
+                                            LSEQmCIOREGS[i].offs));
+
+               break;
+       case 32:
+               asd_printk("%20s[0x%x]: 0x%08x\n", LSEQmCIOREGS[i].name,
+                          LSEQmCIOREGS[i].offs,
+                          asd_read_reg_dword(asd_ha, lseq_cio_addr +
+                                             LSEQmCIOREGS[i].offs));
+               break;
+       }
+}
+
+static void asd_dump_lseq_state(struct asd_ha_struct *asd_ha, int lseq)
+{
+       u32 moffs;
+       int mode;
+
+       asd_printk("LSEQ %d STATE\n", lseq);
+
+       asd_printk("LSEQ%d: ARP2 REGISTERS\n", lseq);
+       PRINT_LREG_32bit(asd_ha, lseq, ARP2CTL);
+       PRINT_LREG_32bit(asd_ha, lseq, ARP2INT);
+       PRINT_LREG_32bit(asd_ha, lseq, ARP2INTEN);
+       PRINT_LREG_8bit(asd_ha, lseq, MODEPTR);
+       PRINT_LREG_8bit(asd_ha, lseq, ALTMODE);
+       PRINT_LREG_8bit(asd_ha, lseq, FLAG);
+       PRINT_LREG_8bit(asd_ha, lseq, ARP2INTCTL);
+       PRINT_LREG_16bit(asd_ha, lseq, STACK);
+       PRINT_LREG_16bit(asd_ha, lseq, PRGMCNT);
+       PRINT_LREG_16bit(asd_ha, lseq, ACCUM);
+       PRINT_LREG_16bit(asd_ha, lseq, SINDEX);
+       PRINT_LREG_16bit(asd_ha, lseq, DINDEX);
+       PRINT_LREG_8bit(asd_ha, lseq, SINDIR);
+       PRINT_LREG_8bit(asd_ha, lseq, DINDIR);
+       PRINT_LREG_8bit(asd_ha, lseq, JUMLDIR);
+       PRINT_LREG_8bit(asd_ha, lseq, ARP2HALTCODE);
+       PRINT_LREG_16bit(asd_ha, lseq, CURRADDR);
+       PRINT_LREG_16bit(asd_ha, lseq, LASTADDR);
+       PRINT_LREG_16bit(asd_ha, lseq, NXTLADDR);
+
+       asd_printk("LSEQ%d: IOP REGISTERS\n", lseq);
+
+       PRINT_LREG_32bit(asd_ha, lseq, MODECTL);
+       PRINT_LREG_32bit(asd_ha, lseq, DBGMODE);
+       PRINT_LREG_32bit(asd_ha, lseq, CONTROL);
+       PRINT_REG_32bit(asd_ha, BISTCTL0, LmBISTCTL0(lseq));
+       PRINT_REG_32bit(asd_ha, BISTCTL1, LmBISTCTL1(lseq));
+
+       asd_printk("LSEQ%d: CIO REGISTERS\n", lseq);
+       asd_printk("Mode common:\n");
+
+       for (mode = 0; mode < 8; mode++) {
+               u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq);
+               int i;
+
+               for (i = 0; LSEQmCIOREGS[i].name; i++)
+                       if (LSEQmCIOREGS[i].mode == MODE_COMMON)
+                               asd_print_lseq_cio_reg(asd_ha,lseq_cio_addr,i);
+       }
+
+       asd_printk("Mode unique:\n");
+       for (mode = 0; mode < 8; mode++) {
+               u32 lseq_cio_addr = LmSEQ_PHY_BASE(mode, lseq);
+               int i;
+
+               asd_printk("Mode %d\n", mode);
+               for  (i = 0; LSEQmCIOREGS[i].name; i++) {
+                       if (!(LSEQmCIOREGS[i].mode & (1 << mode)))
+                               continue;
+                       asd_print_lseq_cio_reg(asd_ha, lseq_cio_addr, i);
+               }
+       }
+
+       asd_printk("SCRATCH MEMORY\n");
+
+       asd_printk("LSEQ%d MIP 0 >>>>\n", lseq);
+       PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_HEAD);
+       PRINT_LMIP_word(asd_ha, lseq, Q_TGTXFR_TAIL);
+       PRINT_LMIP_byte(asd_ha, lseq, LINK_NUMBER);
+       PRINT_LMIP_byte(asd_ha, lseq, SCRATCH_FLAGS);
+       PRINT_LMIP_qword(asd_ha, lseq, CONNECTION_STATE);
+       PRINT_LMIP_word(asd_ha, lseq, CONCTL);
+       PRINT_LMIP_byte(asd_ha, lseq, CONSTAT);
+       PRINT_LMIP_byte(asd_ha, lseq, CONNECTION_MODES);
+       PRINT_LMIP_word(asd_ha, lseq, REG1_ISR);
+       PRINT_LMIP_word(asd_ha, lseq, REG2_ISR);
+       PRINT_LMIP_word(asd_ha, lseq, REG3_ISR);
+       PRINT_LMIP_qword(asd_ha, lseq,REG0_ISR);
+
+       asd_printk("LSEQ%d MIP 1 >>>>\n", lseq);
+       PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR0);
+       PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR1);
+       PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR2);
+       PRINT_LMIP_word(asd_ha, lseq, EST_NEXUS_SCBPTR3);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE0);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE1);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE2);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_OPCODE3);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_HEAD);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_SCB_TAIL);
+       PRINT_LMIP_byte(asd_ha, lseq, EST_NEXUS_BUF_AVAIL);
+       PRINT_LMIP_dword(asd_ha, lseq, TIMEOUT_CONST);
+       PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_SINDEX);
+       PRINT_LMIP_word(asd_ha, lseq, ISR_SAVE_DINDEX);
+
+       asd_printk("LSEQ%d MIP 2 >>>>\n", lseq);
+       PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR0);
+       PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR1);
+       PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR2);
+       PRINT_LMIP_word(asd_ha, lseq, EMPTY_SCB_PTR3);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD0);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD1);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD2);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_OPCD3);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_HEAD);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_SCB_TAIL);
+       PRINT_LMIP_byte(asd_ha, lseq, EMPTY_BUFS_AVAIL);
+
+       asd_printk("LSEQ%d MIP 3 >>>>\n", lseq);
+       PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TMR_TOUT_CONST);
+       PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMEOUT);
+       PRINT_LMIP_dword(asd_ha, lseq, SRST_ASSERT_TIMEOUT);
+       PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMEOUT);
+       PRINT_LMIP_dword(asd_ha, lseq, ONE_MILLISEC_TIMEOUT);
+       PRINT_LMIP_dword(asd_ha, lseq, TEN_MS_COMINIT_TIMEOUT);
+       PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMEOUT);
+
+       for (mode = 0; mode < 3; mode++) {
+               asd_printk("LSEQ%d MDP 0 MODE %d >>>>\n", lseq, mode);
+               moffs = mode * LSEQ_MODE_SCRATCH_SIZE;
+
+               asd_printk(STR_16BIT, "RET_ADDR", 0,
+                          asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq)
+                                            + moffs));
+               asd_printk(STR_16BIT, "REG0_MODE", 2,
+                          asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq)
+                                            + moffs));
+               asd_printk(STR_16BIT, "MODE_FLAGS", 4,
+                          asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq)
+                                            + moffs));
+               asd_printk(STR_16BIT, "RET_ADDR2", 0x6,
+                          asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq)
+                                            + moffs));
+               asd_printk(STR_16BIT, "RET_ADDR1", 0x8,
+                          asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq)
+                                            + moffs));
+               asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB,
+                          asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq)
+                                            + moffs));
+               asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC,
+                          asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq)
+                                            + moffs));
+       }
+
+       asd_printk("LSEQ%d MDP 0 MODE 5 >>>>\n", lseq);
+       moffs = LSEQ_MODE5_PAGE0_OFFSET;
+       asd_printk(STR_16BIT, "RET_ADDR", 0,
+                  asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR(lseq) + moffs));
+       asd_printk(STR_16BIT, "REG0_MODE", 2,
+                  asd_read_reg_word(asd_ha, LmSEQ_REG0_MODE(lseq) + moffs));
+       asd_printk(STR_16BIT, "MODE_FLAGS", 4,
+                  asd_read_reg_word(asd_ha, LmSEQ_MODE_FLAGS(lseq) + moffs));
+       asd_printk(STR_16BIT, "RET_ADDR2", 0x6,
+                  asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR2(lseq) + moffs));
+       asd_printk(STR_16BIT, "RET_ADDR1", 0x8,
+                  asd_read_reg_word(asd_ha, LmSEQ_RET_ADDR1(lseq) + moffs));
+       asd_printk(STR_8BIT, "OPCODE_TO_CSEQ", 0xB,
+          asd_read_reg_byte(asd_ha, LmSEQ_OPCODE_TO_CSEQ(lseq) + moffs));
+       asd_printk(STR_16BIT, "DATA_TO_CSEQ", 0xC,
+          asd_read_reg_word(asd_ha, LmSEQ_DATA_TO_CSEQ(lseq) + moffs));
+
+       asd_printk("LSEQ%d MDP 0 MODE 0 >>>>\n", lseq);
+       PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_DDB_SITE);
+       PRINT_LMIP_word(asd_ha, lseq, EMPTY_TRANS_CTX);
+       PRINT_LMIP_word(asd_ha, lseq, RESP_LEN);
+       PRINT_LMIP_word(asd_ha, lseq, FIRST_INV_SCB_SITE);
+       PRINT_LMIP_dword(asd_ha, lseq, INTEN_SAVE);
+       PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_FRM_LEN);
+       PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_PROTOCOL);
+       PRINT_LMIP_byte(asd_ha, lseq, RESP_STATUS);
+       PRINT_LMIP_byte(asd_ha, lseq, LAST_LOADED_SGE);
+       PRINT_LMIP_byte(asd_ha, lseq, SAVE_SCBPTR);
+
+       asd_printk("LSEQ%d MDP 0 MODE 1 >>>>\n", lseq);
+       PRINT_LMIP_word(asd_ha, lseq, Q_XMIT_HEAD);
+       PRINT_LMIP_word(asd_ha, lseq, M1_EMPTY_TRANS_CTX);
+       PRINT_LMIP_word(asd_ha, lseq, INI_CONN_TAG);
+       PRINT_LMIP_byte(asd_ha, lseq, FAILED_OPEN_STATUS);
+       PRINT_LMIP_byte(asd_ha, lseq, XMIT_REQUEST_TYPE);
+       PRINT_LMIP_byte(asd_ha, lseq, M1_RESP_STATUS);
+       PRINT_LMIP_byte(asd_ha, lseq, M1_LAST_LOADED_SGE);
+       PRINT_LMIP_word(asd_ha, lseq, M1_SAVE_SCBPTR);
+
+       asd_printk("LSEQ%d MDP 0 MODE 2 >>>>\n", lseq);
+       PRINT_LMIP_word(asd_ha, lseq, PORT_COUNTER);
+       PRINT_LMIP_word(asd_ha, lseq, PM_TABLE_PTR);
+       PRINT_LMIP_word(asd_ha, lseq, SATA_INTERLOCK_TMR_SAVE);
+       PRINT_LMIP_word(asd_ha, lseq, IP_BITL);
+       PRINT_LMIP_word(asd_ha, lseq, COPY_SMP_CONN_TAG);
+       PRINT_LMIP_byte(asd_ha, lseq, P0M2_OFFS1AH);
+
+       asd_printk("LSEQ%d MDP 0 MODE 4/5 >>>>\n", lseq);
+       PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_STATUS);
+       PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_MODE);
+       PRINT_LMIP_word(asd_ha, lseq, Q_LINK_HEAD);
+       PRINT_LMIP_byte(asd_ha, lseq, LINK_RST_ERR);
+       PRINT_LMIP_byte(asd_ha, lseq, SAVED_OOB_SIGNALS);
+       PRINT_LMIP_byte(asd_ha, lseq, SAS_RESET_MODE);
+       PRINT_LMIP_byte(asd_ha, lseq, LINK_RESET_RETRY_COUNT);
+       PRINT_LMIP_byte(asd_ha, lseq, NUM_LINK_RESET_RETRIES);
+       PRINT_LMIP_word(asd_ha, lseq, OOB_INT_ENABLES);
+       PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_TIMEOUT);
+       PRINT_LMIP_word(asd_ha, lseq, NOTIFY_TIMER_DOWN_COUNT);
+
+       asd_printk("LSEQ%d MDP 1 MODE 0 >>>>\n", lseq);
+       PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR0);
+       PRINT_LMIP_qword(asd_ha, lseq, SG_LIST_PTR_ADDR1);
+
+       asd_printk("LSEQ%d MDP 1 MODE 1 >>>>\n", lseq);
+       PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR0);
+       PRINT_LMIP_qword(asd_ha, lseq, M1_SG_LIST_PTR_ADDR1);
+
+       asd_printk("LSEQ%d MDP 1 MODE 2 >>>>\n", lseq);
+       PRINT_LMIP_dword(asd_ha, lseq, INVALID_DWORD_COUNT);
+       PRINT_LMIP_dword(asd_ha, lseq, DISPARITY_ERROR_COUNT);
+       PRINT_LMIP_dword(asd_ha, lseq, LOSS_OF_SYNC_COUNT);
+
+       asd_printk("LSEQ%d MDP 1 MODE 4/5 >>>>\n", lseq);
+       PRINT_LMIP_dword(asd_ha, lseq, FRAME_TYPE_MASK);
+       PRINT_LMIP_dword(asd_ha, lseq, HASHED_SRC_ADDR_MASK_PRINT);
+       PRINT_LMIP_byte(asd_ha, lseq, NUM_FILL_BYTES_MASK);
+       PRINT_LMIP_word(asd_ha, lseq, TAG_MASK);
+       PRINT_LMIP_word(asd_ha, lseq, TARGET_PORT_XFER_TAG);
+       PRINT_LMIP_dword(asd_ha, lseq, DATA_OFFSET);
+
+       asd_printk("LSEQ%d MDP 2 MODE 0 >>>>\n", lseq);
+       PRINT_LMIP_dword(asd_ha, lseq, SMP_RCV_TIMER_TERM_TS);
+       PRINT_LMIP_byte(asd_ha, lseq, DEVICE_BITS);
+       PRINT_LMIP_word(asd_ha, lseq, SDB_DDB);
+       PRINT_LMIP_word(asd_ha, lseq, SDB_NUM_TAGS);
+       PRINT_LMIP_word(asd_ha, lseq, SDB_CURR_TAG);
+
+       asd_printk("LSEQ%d MDP 2 MODE 1 >>>>\n", lseq);
+       PRINT_LMIP_qword(asd_ha, lseq, TX_ID_ADDR_FRAME);
+       PRINT_LMIP_dword(asd_ha, lseq, OPEN_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, SRST_AS_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, LAST_LOADED_SG_EL);
+
+       asd_printk("LSEQ%d MDP 2 MODE 2 >>>>\n", lseq);
+       PRINT_LMIP_dword(asd_ha, lseq, CLOSE_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, BREAK_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, DWS_RESET_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, SATA_INTERLOCK_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, MCTL_TIMER_TERM_TS);
+
+       asd_printk("LSEQ%d MDP 2 MODE 4/5 >>>>\n", lseq);
+       PRINT_LMIP_dword(asd_ha, lseq, COMINIT_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, RCV_ID_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, RCV_FIS_TIMER_TERM_TS);
+       PRINT_LMIP_dword(asd_ha, lseq, DEV_PRES_TIMER_TERM_TS);
+}
+
+/**
+ * asd_dump_ddb_site -- dump a CSEQ DDB site
+ * @asd_ha: pointer to host adapter structure
+ * @site_no: site number of interest
+ */
+void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no)
+{
+       if (site_no >= asd_ha->hw_prof.max_ddbs)
+               return;
+
+#define DDB_FIELDB(__name)                                        \
+       asd_ddbsite_read_byte(asd_ha, site_no,                    \
+                             offsetof(struct asd_ddb_ssp_smp_target_port, __name))
+#define DDB2_FIELDB(__name)                                       \
+       asd_ddbsite_read_byte(asd_ha, site_no,                    \
+                             offsetof(struct asd_ddb_stp_sata_target_port, __name))
+#define DDB_FIELDW(__name)                                        \
+       asd_ddbsite_read_word(asd_ha, site_no,                    \
+                             offsetof(struct asd_ddb_ssp_smp_target_port, __name))
+
+#define DDB_FIELDD(__name)                                         \
+       asd_ddbsite_read_dword(asd_ha, site_no,                    \
+                              offsetof(struct asd_ddb_ssp_smp_target_port, __name))
+
+       asd_printk("DDB: 0x%02x\n", site_no);
+       asd_printk("conn_type: 0x%02x\n", DDB_FIELDB(conn_type));
+       asd_printk("conn_rate: 0x%02x\n", DDB_FIELDB(conn_rate));
+       asd_printk("init_conn_tag: 0x%04x\n", be16_to_cpu(DDB_FIELDW(init_conn_tag)));
+       asd_printk("send_queue_head: 0x%04x\n", be16_to_cpu(DDB_FIELDW(send_queue_head)));
+       asd_printk("sq_suspended: 0x%02x\n", DDB_FIELDB(sq_suspended));
+       asd_printk("DDB Type: 0x%02x\n", DDB_FIELDB(ddb_type));
+       asd_printk("AWT Default: 0x%04x\n", DDB_FIELDW(awt_def));
+       asd_printk("compat_features: 0x%02x\n", DDB_FIELDB(compat_features));
+       asd_printk("Pathway Blocked Count: 0x%02x\n",
+                  DDB_FIELDB(pathway_blocked_count));
+       asd_printk("arb_wait_time: 0x%04x\n", DDB_FIELDW(arb_wait_time));
+       asd_printk("more_compat_features: 0x%08x\n",
+                  DDB_FIELDD(more_compat_features));
+       asd_printk("Conn Mask: 0x%02x\n", DDB_FIELDB(conn_mask));
+       asd_printk("flags: 0x%02x\n", DDB_FIELDB(flags));
+       asd_printk("flags2: 0x%02x\n", DDB2_FIELDB(flags2));
+       asd_printk("ExecQ Tail: 0x%04x\n",DDB_FIELDW(exec_queue_tail));
+       asd_printk("SendQ Tail: 0x%04x\n",DDB_FIELDW(send_queue_tail));
+       asd_printk("Active Task Count: 0x%04x\n",
+                  DDB_FIELDW(active_task_count));
+       asd_printk("ITNL Reason: 0x%02x\n", DDB_FIELDB(itnl_reason));
+       asd_printk("ITNL Timeout Const: 0x%04x\n", DDB_FIELDW(itnl_timeout));
+       asd_printk("ITNL timestamp: 0x%08x\n", DDB_FIELDD(itnl_timestamp));
+}
+
+void asd_dump_ddb_0(struct asd_ha_struct *asd_ha)
+{
+#define DDB0_FIELDB(__name)                                  \
+       asd_ddbsite_read_byte(asd_ha, 0,                     \
+                             offsetof(struct asd_ddb_seq_shared, __name))
+#define DDB0_FIELDW(__name)                                  \
+       asd_ddbsite_read_word(asd_ha, 0,                     \
+                             offsetof(struct asd_ddb_seq_shared, __name))
+
+#define DDB0_FIELDD(__name)                                  \
+       asd_ddbsite_read_dword(asd_ha,0 ,                    \
+                              offsetof(struct asd_ddb_seq_shared, __name))
+
+#define DDB0_FIELDA(__name, _o)                              \
+       asd_ddbsite_read_byte(asd_ha, 0,                     \
+                             offsetof(struct asd_ddb_seq_shared, __name)+_o)
+
+
+       asd_printk("DDB: 0\n");
+       asd_printk("q_free_ddb_head:%04x\n", DDB0_FIELDW(q_free_ddb_head));
+       asd_printk("q_free_ddb_tail:%04x\n", DDB0_FIELDW(q_free_ddb_tail));
+       asd_printk("q_free_ddb_cnt:%04x\n",  DDB0_FIELDW(q_free_ddb_cnt));
+       asd_printk("q_used_ddb_head:%04x\n", DDB0_FIELDW(q_used_ddb_head));
+       asd_printk("q_used_ddb_tail:%04x\n", DDB0_FIELDW(q_used_ddb_tail));
+       asd_printk("shared_mem_lock:%04x\n", DDB0_FIELDW(shared_mem_lock));
+       asd_printk("smp_conn_tag:%04x\n",    DDB0_FIELDW(smp_conn_tag));
+       asd_printk("est_nexus_buf_cnt:%04x\n", DDB0_FIELDW(est_nexus_buf_cnt));
+       asd_printk("est_nexus_buf_thresh:%04x\n",
+                  DDB0_FIELDW(est_nexus_buf_thresh));
+       asd_printk("conn_not_active:%02x\n", DDB0_FIELDB(conn_not_active));
+       asd_printk("phy_is_up:%02x\n",       DDB0_FIELDB(phy_is_up));
+       asd_printk("port_map_by_links:%02x %02x %02x %02x "
+                  "%02x %02x %02x %02x\n",
+                  DDB0_FIELDA(port_map_by_links, 0),
+                  DDB0_FIELDA(port_map_by_links, 1),
+                  DDB0_FIELDA(port_map_by_links, 2),
+                  DDB0_FIELDA(port_map_by_links, 3),
+                  DDB0_FIELDA(port_map_by_links, 4),
+                  DDB0_FIELDA(port_map_by_links, 5),
+                  DDB0_FIELDA(port_map_by_links, 6),
+                  DDB0_FIELDA(port_map_by_links, 7));
+}
+
+static void asd_dump_scb_site(struct asd_ha_struct *asd_ha, u16 site_no)
+{
+
+#define SCB_FIELDB(__name)                                                 \
+       asd_scbsite_read_byte(asd_ha, site_no, sizeof(struct scb_header)   \
+                             + offsetof(struct initiate_ssp_task, __name))
+#define SCB_FIELDW(__name)                                                 \
+       asd_scbsite_read_word(asd_ha, site_no, sizeof(struct scb_header)   \
+                             + offsetof(struct initiate_ssp_task, __name))
+#define SCB_FIELDD(__name)                                                 \
+       asd_scbsite_read_dword(asd_ha, site_no, sizeof(struct scb_header)  \
+                              + offsetof(struct initiate_ssp_task, __name))
+
+       asd_printk("Total Xfer Len: 0x%08x.\n", SCB_FIELDD(total_xfer_len));
+       asd_printk("Frame Type: 0x%02x.\n", SCB_FIELDB(ssp_frame.frame_type));
+       asd_printk("Tag: 0x%04x.\n", SCB_FIELDW(ssp_frame.tag));
+       asd_printk("Target Port Xfer Tag: 0x%04x.\n",
+                  SCB_FIELDW(ssp_frame.tptt));
+       asd_printk("Data Offset: 0x%08x.\n", SCB_FIELDW(ssp_frame.data_offs));
+       asd_printk("Retry Count: 0x%02x.\n", SCB_FIELDB(retry_count));
+}
+
+/**
+ * asd_dump_scb_sites -- dump currently used CSEQ SCB sites
+ * @asd_ha: pointer to host adapter struct
+ */
+void asd_dump_scb_sites(struct asd_ha_struct *asd_ha)
+{
+       u16     site_no;
+
+       for (site_no = 0; site_no < asd_ha->hw_prof.max_scbs; site_no++) {
+               u8 opcode;
+
+               if (!SCB_SITE_VALID(site_no))
+                       continue;
+
+               /* We are only interested in SCB sites currently used.
+                */
+               opcode = asd_scbsite_read_byte(asd_ha, site_no,
+                                              offsetof(struct scb_header,
+                                                       opcode));
+               if (opcode == 0xFF)
+                       continue;
+
+               asd_printk("\nSCB: 0x%x\n", site_no);
+               asd_dump_scb_site(asd_ha, site_no);
+       }
+}
+
+/**
+ * ads_dump_seq_state -- dump CSEQ and LSEQ states
+ * @asd_ha: pointer to host adapter structure
+ * @lseq_mask: mask of LSEQs of interest
+ */
+void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask)
+{
+       int lseq;
+
+       asd_dump_cseq_state(asd_ha);
+
+       if (lseq_mask != 0)
+               for_each_sequencer(lseq_mask, lseq_mask, lseq)
+                       asd_dump_lseq_state(asd_ha, lseq);
+}
+
+void asd_dump_frame_rcvd(struct asd_phy *phy,
+                        struct done_list_struct *dl)
+{
+       unsigned long flags;
+       int i;
+
+       switch ((dl->status_block[1] & 0x70) >> 3) {
+       case SAS_PROTO_STP:
+               ASD_DPRINTK("STP proto device-to-host FIS:\n");
+               break;
+       default:
+       case SAS_PROTO_SSP:
+               ASD_DPRINTK("SAS proto IDENTIFY:\n");
+               break;
+       }
+       spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
+       for (i = 0; i < phy->sas_phy.frame_rcvd_size; i+=4)
+               ASD_DPRINTK("%02x: %02x %02x %02x %02x\n",
+                           i,
+                           phy->frame_rcvd[i],
+                           phy->frame_rcvd[i+1],
+                           phy->frame_rcvd[i+2],
+                           phy->frame_rcvd[i+3]);
+       spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
+}
+
+static inline void asd_dump_scb(struct asd_ascb *ascb, int ind)
+{
+       asd_printk("scb%d: vaddr: 0x%p, dma_handle: 0x%llx, next: 0x%llx, "
+                  "index:%d, opcode:0x%02x\n",
+                  ind, ascb->dma_scb.vaddr,
+                  (unsigned long long)ascb->dma_scb.dma_handle,
+                  (unsigned long long)
+                  le64_to_cpu(ascb->scb->header.next_scb),
+                  le16_to_cpu(ascb->scb->header.index),
+                  ascb->scb->header.opcode);
+}
+
+void asd_dump_scb_list(struct asd_ascb *ascb, int num)
+{
+       int i = 0;
+
+       asd_printk("dumping %d scbs:\n", num);
+
+       asd_dump_scb(ascb, i++);
+       --num;
+
+       if (num > 0 && !list_empty(&ascb->list)) {
+               struct list_head *el;
+
+               list_for_each(el, &ascb->list) {
+                       struct asd_ascb *s = list_entry(el, struct asd_ascb,
+                                                       list);
+                       asd_dump_scb(s, i++);
+                       if (--num <= 0)
+                               break;
+               }
+       }
+}
+
+#endif /* ASD_DEBUG */
diff --git a/drivers/scsi/aic94xx/aic94xx_dump.h b/drivers/scsi/aic94xx/aic94xx_dump.h
new file mode 100644 (file)
index 0000000..0c388e7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Aic94xx SAS/SATA driver dump header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _AIC94XX_DUMP_H_
+#define _AIC94XX_DUMP_H_
+
+#ifdef ASD_DEBUG
+
+void asd_dump_ddb_0(struct asd_ha_struct *asd_ha);
+void asd_dump_target_ddb(struct asd_ha_struct *asd_ha, u16 site_no);
+void asd_dump_scb_sites(struct asd_ha_struct *asd_ha);
+void asd_dump_seq_state(struct asd_ha_struct *asd_ha, u8 lseq_mask);
+void asd_dump_frame_rcvd(struct asd_phy *phy,
+                        struct done_list_struct *dl);
+void asd_dump_scb_list(struct asd_ascb *ascb, int num);
+#else /* ASD_DEBUG */
+
+static inline void asd_dump_ddb_0(struct asd_ha_struct *asd_ha) { }
+static inline void asd_dump_target_ddb(struct asd_ha_struct *asd_ha,
+                                    u16 site_no) { }
+static inline void asd_dump_scb_sites(struct asd_ha_struct *asd_ha) { }
+static inline void asd_dump_seq_state(struct asd_ha_struct *asd_ha,
+                                     u8 lseq_mask) { }
+static inline void asd_dump_frame_rcvd(struct asd_phy *phy,
+                                      struct done_list_struct *dl) { }
+static inline void asd_dump_scb_list(struct asd_ascb *ascb, int num) { }
+#endif /* ASD_DEBUG */
+
+#endif /* _AIC94XX_DUMP_H_ */
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.c b/drivers/scsi/aic94xx/aic94xx_hwi.c
new file mode 100644 (file)
index 0000000..a242013
--- /dev/null
@@ -0,0 +1,1376 @@
+/*
+ * Aic94xx SAS/SATA driver hardware interface.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_seq.h"
+#include "aic94xx_dump.h"
+
+u32 MBAR0_SWB_SIZE;
+
+/* ---------- Initialization ---------- */
+
+static void asd_get_user_sas_addr(struct asd_ha_struct *asd_ha)
+{
+       extern char sas_addr_str[];
+       /* If the user has specified a WWN it overrides other settings
+        */
+       if (sas_addr_str[0] != '\0')
+               asd_destringify_sas_addr(asd_ha->hw_prof.sas_addr,
+                                        sas_addr_str);
+       else if (asd_ha->hw_prof.sas_addr[0] != 0)
+               asd_stringify_sas_addr(sas_addr_str, asd_ha->hw_prof.sas_addr);
+}
+
+static void asd_propagate_sas_addr(struct asd_ha_struct *asd_ha)
+{
+       int i;
+
+       for (i = 0; i < ASD_MAX_PHYS; i++) {
+               if (asd_ha->hw_prof.phy_desc[i].sas_addr[0] == 0)
+                       continue;
+               /* Set a phy's address only if it has none.
+                */
+               ASD_DPRINTK("setting phy%d addr to %llx\n", i,
+                           SAS_ADDR(asd_ha->hw_prof.sas_addr));
+               memcpy(asd_ha->hw_prof.phy_desc[i].sas_addr,
+                      asd_ha->hw_prof.sas_addr, SAS_ADDR_SIZE);
+       }
+}
+
+/* ---------- PHY initialization ---------- */
+
+static void asd_init_phy_identify(struct asd_phy *phy)
+{
+       phy->identify_frame = phy->id_frm_tok->vaddr;
+
+       memset(phy->identify_frame, 0, sizeof(*phy->identify_frame));
+
+       phy->identify_frame->dev_type = SAS_END_DEV;
+       if (phy->sas_phy.role & PHY_ROLE_INITIATOR)
+               phy->identify_frame->initiator_bits = phy->sas_phy.iproto;
+       if (phy->sas_phy.role & PHY_ROLE_TARGET)
+               phy->identify_frame->target_bits = phy->sas_phy.tproto;
+       memcpy(phy->identify_frame->sas_addr, phy->phy_desc->sas_addr,
+              SAS_ADDR_SIZE);
+       phy->identify_frame->phy_id = phy->sas_phy.id;
+}
+
+static int asd_init_phy(struct asd_phy *phy)
+{
+       struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
+       struct asd_sas_phy *sas_phy = &phy->sas_phy;
+
+       sas_phy->enabled = 1;
+       sas_phy->class = SAS;
+       sas_phy->iproto = SAS_PROTO_ALL;
+       sas_phy->tproto = 0;
+       sas_phy->type = PHY_TYPE_PHYSICAL;
+       sas_phy->role = PHY_ROLE_INITIATOR;
+       sas_phy->oob_mode = OOB_NOT_CONNECTED;
+       sas_phy->linkrate = SAS_LINK_RATE_UNKNOWN;
+
+       phy->id_frm_tok = asd_alloc_coherent(asd_ha,
+                                            sizeof(*phy->identify_frame),
+                                            GFP_KERNEL);
+       if (!phy->id_frm_tok) {
+               asd_printk("no mem for IDENTIFY for phy%d\n", sas_phy->id);
+               return -ENOMEM;
+       } else
+               asd_init_phy_identify(phy);
+
+       memset(phy->frame_rcvd, 0, sizeof(phy->frame_rcvd));
+
+       return 0;
+}
+
+static int asd_init_phys(struct asd_ha_struct *asd_ha)
+{
+       u8 i;
+       u8 phy_mask = asd_ha->hw_prof.enabled_phys;
+
+       for (i = 0; i < ASD_MAX_PHYS; i++) {
+               struct asd_phy *phy = &asd_ha->phys[i];
+
+               phy->phy_desc = &asd_ha->hw_prof.phy_desc[i];
+
+               phy->sas_phy.enabled = 0;
+               phy->sas_phy.id = i;
+               phy->sas_phy.sas_addr = &phy->phy_desc->sas_addr[0];
+               phy->sas_phy.frame_rcvd = &phy->frame_rcvd[0];
+               phy->sas_phy.ha = &asd_ha->sas_ha;
+               phy->sas_phy.lldd_phy = phy;
+       }
+
+       /* Now enable and initialize only the enabled phys. */
+       for_each_phy(phy_mask, phy_mask, i) {
+               int err = asd_init_phy(&asd_ha->phys[i]);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+/* ---------- Sliding windows ---------- */
+
+static int asd_init_sw(struct asd_ha_struct *asd_ha)
+{
+       struct pci_dev *pcidev = asd_ha->pcidev;
+       int err;
+       u32 v;
+
+       /* Unlock MBARs */
+       err = pci_read_config_dword(pcidev, PCI_CONF_MBAR_KEY, &v);
+       if (err) {
+               asd_printk("couldn't access conf. space of %s\n",
+                          pci_name(pcidev));
+               goto Err;
+       }
+       if (v)
+               err = pci_write_config_dword(pcidev, PCI_CONF_MBAR_KEY, v);
+       if (err) {
+               asd_printk("couldn't write to MBAR_KEY of %s\n",
+                          pci_name(pcidev));
+               goto Err;
+       }
+
+       /* Set sliding windows A, B and C to point to proper internal
+        * memory regions.
+        */
+       pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWA, REG_BASE_ADDR);
+       pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWB,
+                              REG_BASE_ADDR_CSEQCIO);
+       pci_write_config_dword(pcidev, PCI_CONF_MBAR0_SWC, REG_BASE_ADDR_EXSI);
+       asd_ha->io_handle[0].swa_base = REG_BASE_ADDR;
+       asd_ha->io_handle[0].swb_base = REG_BASE_ADDR_CSEQCIO;
+       asd_ha->io_handle[0].swc_base = REG_BASE_ADDR_EXSI;
+       MBAR0_SWB_SIZE = asd_ha->io_handle[0].len - 0x80;
+       if (!asd_ha->iospace) {
+               /* MBAR1 will point to OCM (On Chip Memory) */
+               pci_write_config_dword(pcidev, PCI_CONF_MBAR1, OCM_BASE_ADDR);
+               asd_ha->io_handle[1].swa_base = OCM_BASE_ADDR;
+       }
+       spin_lock_init(&asd_ha->iolock);
+Err:
+       return err;
+}
+
+/* ---------- SCB initialization ---------- */
+
+/**
+ * asd_init_scbs - manually allocate the first SCB.
+ * @asd_ha: pointer to host adapter structure
+ *
+ * This allocates the very first SCB which would be sent to the
+ * sequencer for execution.  Its bus address is written to
+ * CSEQ_Q_NEW_POINTER, mode page 2, mode 8.  Since the bus address of
+ * the _next_ scb to be DMA-ed to the host adapter is read from the last
+ * SCB DMA-ed to the host adapter, we have to always stay one step
+ * ahead of the sequencer and keep one SCB already allocated.
+ */
+static int asd_init_scbs(struct asd_ha_struct *asd_ha)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       int bitmap_bytes;
+
+       /* allocate the index array and bitmap */
+       asd_ha->seq.tc_index_bitmap_bits = asd_ha->hw_prof.max_scbs;
+       asd_ha->seq.tc_index_array = kzalloc(asd_ha->seq.tc_index_bitmap_bits*
+                                            sizeof(void *), GFP_KERNEL);
+       if (!asd_ha->seq.tc_index_array)
+               return -ENOMEM;
+
+       bitmap_bytes = (asd_ha->seq.tc_index_bitmap_bits+7)/8;
+       bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
+       asd_ha->seq.tc_index_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
+       if (!asd_ha->seq.tc_index_bitmap)
+               return -ENOMEM;
+
+       spin_lock_init(&seq->tc_index_lock);
+
+       seq->next_scb.size = sizeof(struct scb);
+       seq->next_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool, GFP_KERNEL,
+                                            &seq->next_scb.dma_handle);
+       if (!seq->next_scb.vaddr) {
+               kfree(asd_ha->seq.tc_index_bitmap);
+               kfree(asd_ha->seq.tc_index_array);
+               asd_ha->seq.tc_index_bitmap = NULL;
+               asd_ha->seq.tc_index_array = NULL;
+               return -ENOMEM;
+       }
+
+       seq->pending = 0;
+       spin_lock_init(&seq->pend_q_lock);
+       INIT_LIST_HEAD(&seq->pend_q);
+
+       return 0;
+}
+
+static inline void asd_get_max_scb_ddb(struct asd_ha_struct *asd_ha)
+{
+       asd_ha->hw_prof.max_scbs = asd_get_cmdctx_size(asd_ha)/ASD_SCB_SIZE;
+       asd_ha->hw_prof.max_ddbs = asd_get_devctx_size(asd_ha)/ASD_DDB_SIZE;
+       ASD_DPRINTK("max_scbs:%d, max_ddbs:%d\n",
+                   asd_ha->hw_prof.max_scbs,
+                   asd_ha->hw_prof.max_ddbs);
+}
+
+/* ---------- Done List initialization ---------- */
+
+static void asd_dl_tasklet_handler(unsigned long);
+
+static int asd_init_dl(struct asd_ha_struct *asd_ha)
+{
+       asd_ha->seq.actual_dl
+               = asd_alloc_coherent(asd_ha,
+                            ASD_DL_SIZE * sizeof(struct done_list_struct),
+                                    GFP_KERNEL);
+       if (!asd_ha->seq.actual_dl)
+               return -ENOMEM;
+       asd_ha->seq.dl = asd_ha->seq.actual_dl->vaddr;
+       asd_ha->seq.dl_toggle = ASD_DEF_DL_TOGGLE;
+       asd_ha->seq.dl_next = 0;
+       tasklet_init(&asd_ha->seq.dl_tasklet, asd_dl_tasklet_handler,
+                    (unsigned long) asd_ha);
+
+       return 0;
+}
+
+/* ---------- EDB and ESCB init ---------- */
+
+static int asd_alloc_edbs(struct asd_ha_struct *asd_ha, unsigned int gfp_flags)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       int i;
+
+       seq->edb_arr = kmalloc(seq->num_edbs*sizeof(*seq->edb_arr), gfp_flags);
+       if (!seq->edb_arr)
+               return -ENOMEM;
+
+       for (i = 0; i < seq->num_edbs; i++) {
+               seq->edb_arr[i] = asd_alloc_coherent(asd_ha, ASD_EDB_SIZE,
+                                                    gfp_flags);
+               if (!seq->edb_arr[i])
+                       goto Err_unroll;
+               memset(seq->edb_arr[i]->vaddr, 0, ASD_EDB_SIZE);
+       }
+
+       ASD_DPRINTK("num_edbs:%d\n", seq->num_edbs);
+
+       return 0;
+
+Err_unroll:
+       for (i-- ; i >= 0; i--)
+               asd_free_coherent(asd_ha, seq->edb_arr[i]);
+       kfree(seq->edb_arr);
+       seq->edb_arr = NULL;
+
+       return -ENOMEM;
+}
+
+static int asd_alloc_escbs(struct asd_ha_struct *asd_ha,
+                          unsigned int gfp_flags)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       struct asd_ascb *escb;
+       int i, escbs;
+
+       seq->escb_arr = kmalloc(seq->num_escbs*sizeof(*seq->escb_arr),
+                               gfp_flags);
+       if (!seq->escb_arr)
+               return -ENOMEM;
+
+       escbs = seq->num_escbs;
+       escb = asd_ascb_alloc_list(asd_ha, &escbs, gfp_flags);
+       if (!escb) {
+               asd_printk("couldn't allocate list of escbs\n");
+               goto Err;
+       }
+       seq->num_escbs -= escbs;  /* subtract what was not allocated */
+       ASD_DPRINTK("num_escbs:%d\n", seq->num_escbs);
+
+       for (i = 0; i < seq->num_escbs; i++, escb = list_entry(escb->list.next,
+                                                              struct asd_ascb,
+                                                              list)) {
+               seq->escb_arr[i] = escb;
+               escb->scb->header.opcode = EMPTY_SCB;
+       }
+
+       return 0;
+Err:
+       kfree(seq->escb_arr);
+       seq->escb_arr = NULL;
+       return -ENOMEM;
+
+}
+
+static void asd_assign_edbs2escbs(struct asd_ha_struct *asd_ha)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       int i, k, z = 0;
+
+       for (i = 0; i < seq->num_escbs; i++) {
+               struct asd_ascb *ascb = seq->escb_arr[i];
+               struct empty_scb *escb = &ascb->scb->escb;
+
+               ascb->edb_index = z;
+
+               escb->num_valid = ASD_EDBS_PER_SCB;
+
+               for (k = 0; k < ASD_EDBS_PER_SCB; k++) {
+                       struct sg_el *eb = &escb->eb[k];
+                       struct asd_dma_tok *edb = seq->edb_arr[z++];
+
+                       memset(eb, 0, sizeof(*eb));
+                       eb->bus_addr = cpu_to_le64(((u64) edb->dma_handle));
+                       eb->size = cpu_to_le32(((u32) edb->size));
+               }
+       }
+}
+
+/**
+ * asd_init_escbs -- allocate and initialize empty scbs
+ * @asd_ha: pointer to host adapter structure
+ *
+ * An empty SCB has sg_elements of ASD_EDBS_PER_SCB (7) buffers.
+ * They transport sense data, etc.
+ */
+static int asd_init_escbs(struct asd_ha_struct *asd_ha)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       int err = 0;
+
+       /* Allocate two empty data buffers (edb) per sequencer. */
+       int edbs = 2*(1+asd_ha->hw_prof.num_phys);
+
+       seq->num_escbs = (edbs+ASD_EDBS_PER_SCB-1)/ASD_EDBS_PER_SCB;
+       seq->num_edbs = seq->num_escbs * ASD_EDBS_PER_SCB;
+
+       err = asd_alloc_edbs(asd_ha, GFP_KERNEL);
+       if (err) {
+               asd_printk("couldn't allocate edbs\n");
+               return err;
+       }
+
+       err = asd_alloc_escbs(asd_ha, GFP_KERNEL);
+       if (err) {
+               asd_printk("couldn't allocate escbs\n");
+               return err;
+       }
+
+       asd_assign_edbs2escbs(asd_ha);
+       /* In order to insure that normal SCBs do not overfill sequencer
+        * memory and leave no space for escbs (halting condition),
+        * we increment pending here by the number of escbs.  However,
+        * escbs are never pending.
+        */
+       seq->pending   = seq->num_escbs;
+       seq->can_queue = 1 + (asd_ha->hw_prof.max_scbs - seq->pending)/2;
+
+       return 0;
+}
+
+/* ---------- HW initialization ---------- */
+
+/**
+ * asd_chip_hardrst -- hard reset the chip
+ * @asd_ha: pointer to host adapter structure
+ *
+ * This takes 16 cycles and is synchronous to CFCLK, which runs
+ * at 200 MHz, so this should take at most 80 nanoseconds.
+ */
+int asd_chip_hardrst(struct asd_ha_struct *asd_ha)
+{
+       int i;
+       int count = 100;
+       u32 reg;
+
+       for (i = 0 ; i < 4 ; i++) {
+               asd_write_reg_dword(asd_ha, COMBIST, HARDRST);
+       }
+
+       do {
+               udelay(1);
+               reg = asd_read_reg_dword(asd_ha, CHIMINT);
+               if (reg & HARDRSTDET) {
+                       asd_write_reg_dword(asd_ha, CHIMINT,
+                                           HARDRSTDET|PORRSTDET);
+                       return 0;
+               }
+       } while (--count > 0);
+
+       return -ENODEV;
+}
+
+/**
+ * asd_init_chip -- initialize the chip
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Hard resets the chip, disables HA interrupts, downloads the sequnecer
+ * microcode and starts the sequencers.  The caller has to explicitly
+ * enable HA interrupts with asd_enable_ints(asd_ha).
+ */
+static int asd_init_chip(struct asd_ha_struct *asd_ha)
+{
+       int err;
+
+       err = asd_chip_hardrst(asd_ha);
+       if (err) {
+               asd_printk("couldn't hard reset %s\n",
+                           pci_name(asd_ha->pcidev));
+               goto out;
+       }
+
+       asd_disable_ints(asd_ha);
+
+       err = asd_init_seqs(asd_ha);
+       if (err) {
+               asd_printk("couldn't init seqs for %s\n",
+                          pci_name(asd_ha->pcidev));
+               goto out;
+       }
+
+       err = asd_start_seqs(asd_ha);
+       if (err) {
+               asd_printk("coudln't start seqs for %s\n",
+                          pci_name(asd_ha->pcidev));
+               goto out;
+       }
+out:
+       return err;
+}
+
+#define MAX_DEVS ((OCM_MAX_SIZE) / (ASD_DDB_SIZE))
+
+static int max_devs = 0;
+module_param_named(max_devs, max_devs, int, S_IRUGO);
+MODULE_PARM_DESC(max_devs, "\n"
+       "\tMaximum number of SAS devices to support (not LUs).\n"
+       "\tDefault: 2176, Maximum: 65663.\n");
+
+static int max_cmnds = 0;
+module_param_named(max_cmnds, max_cmnds, int, S_IRUGO);
+MODULE_PARM_DESC(max_cmnds, "\n"
+       "\tMaximum number of commands queuable.\n"
+       "\tDefault: 512, Maximum: 66047.\n");
+
+static void asd_extend_devctx_ocm(struct asd_ha_struct *asd_ha)
+{
+       unsigned long dma_addr = OCM_BASE_ADDR;
+       u32 d;
+
+       dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE;
+       asd_write_reg_addr(asd_ha, DEVCTXBASE, (dma_addr_t) dma_addr);
+       d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
+       d |= 4;
+       asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
+       asd_ha->hw_prof.max_ddbs += MAX_DEVS;
+}
+
+static int asd_extend_devctx(struct asd_ha_struct *asd_ha)
+{
+       dma_addr_t dma_handle;
+       unsigned long dma_addr;
+       u32 d;
+       int size;
+
+       asd_extend_devctx_ocm(asd_ha);
+
+       asd_ha->hw_prof.ddb_ext = NULL;
+       if (max_devs <= asd_ha->hw_prof.max_ddbs || max_devs > 0xFFFF) {
+               max_devs = asd_ha->hw_prof.max_ddbs;
+               return 0;
+       }
+
+       size = (max_devs - asd_ha->hw_prof.max_ddbs + 1) * ASD_DDB_SIZE;
+
+       asd_ha->hw_prof.ddb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL);
+       if (!asd_ha->hw_prof.ddb_ext) {
+               asd_printk("couldn't allocate memory for %d devices\n",
+                          max_devs);
+               max_devs = asd_ha->hw_prof.max_ddbs;
+               return -ENOMEM;
+       }
+       dma_handle = asd_ha->hw_prof.ddb_ext->dma_handle;
+       dma_addr = ALIGN((unsigned long) dma_handle, ASD_DDB_SIZE);
+       dma_addr -= asd_ha->hw_prof.max_ddbs * ASD_DDB_SIZE;
+       dma_handle = (dma_addr_t) dma_addr;
+       asd_write_reg_addr(asd_ha, DEVCTXBASE, dma_handle);
+       d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
+       d &= ~4;
+       asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
+
+       asd_ha->hw_prof.max_ddbs = max_devs;
+
+       return 0;
+}
+
+static int asd_extend_cmdctx(struct asd_ha_struct *asd_ha)
+{
+       dma_addr_t dma_handle;
+       unsigned long dma_addr;
+       u32 d;
+       int size;
+
+       asd_ha->hw_prof.scb_ext = NULL;
+       if (max_cmnds <= asd_ha->hw_prof.max_scbs || max_cmnds > 0xFFFF) {
+               max_cmnds = asd_ha->hw_prof.max_scbs;
+               return 0;
+       }
+
+       size = (max_cmnds - asd_ha->hw_prof.max_scbs + 1) * ASD_SCB_SIZE;
+
+       asd_ha->hw_prof.scb_ext = asd_alloc_coherent(asd_ha, size, GFP_KERNEL);
+       if (!asd_ha->hw_prof.scb_ext) {
+               asd_printk("couldn't allocate memory for %d commands\n",
+                          max_cmnds);
+               max_cmnds = asd_ha->hw_prof.max_scbs;
+               return -ENOMEM;
+       }
+       dma_handle = asd_ha->hw_prof.scb_ext->dma_handle;
+       dma_addr = ALIGN((unsigned long) dma_handle, ASD_SCB_SIZE);
+       dma_addr -= asd_ha->hw_prof.max_scbs * ASD_SCB_SIZE;
+       dma_handle = (dma_addr_t) dma_addr;
+       asd_write_reg_addr(asd_ha, CMDCTXBASE, dma_handle);
+       d = asd_read_reg_dword(asd_ha, CTXDOMAIN);
+       d &= ~1;
+       asd_write_reg_dword(asd_ha, CTXDOMAIN, d);
+
+       asd_ha->hw_prof.max_scbs = max_cmnds;
+
+       return 0;
+}
+
+/**
+ * asd_init_ctxmem -- initialize context memory
+ * asd_ha: pointer to host adapter structure
+ *
+ * This function sets the maximum number of SCBs and
+ * DDBs which can be used by the sequencer.  This is normally
+ * 512 and 128 respectively.  If support for more SCBs or more DDBs
+ * is required then CMDCTXBASE, DEVCTXBASE and CTXDOMAIN are
+ * initialized here to extend context memory to point to host memory,
+ * thus allowing unlimited support for SCBs and DDBs -- only limited
+ * by host memory.
+ */
+static int asd_init_ctxmem(struct asd_ha_struct *asd_ha)
+{
+       int bitmap_bytes;
+
+       asd_get_max_scb_ddb(asd_ha);
+       asd_extend_devctx(asd_ha);
+       asd_extend_cmdctx(asd_ha);
+
+       /* The kernel wants bitmaps to be unsigned long sized. */
+       bitmap_bytes = (asd_ha->hw_prof.max_ddbs+7)/8;
+       bitmap_bytes = BITS_TO_LONGS(bitmap_bytes*8)*sizeof(unsigned long);
+       asd_ha->hw_prof.ddb_bitmap = kzalloc(bitmap_bytes, GFP_KERNEL);
+       if (!asd_ha->hw_prof.ddb_bitmap)
+               return -ENOMEM;
+       spin_lock_init(&asd_ha->hw_prof.ddb_lock);
+
+       return 0;
+}
+
+int asd_init_hw(struct asd_ha_struct *asd_ha)
+{
+       int err;
+       u32 v;
+
+       err = asd_init_sw(asd_ha);
+       if (err)
+               return err;
+
+       err = pci_read_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL, &v);
+       if (err) {
+               asd_printk("couldn't read PCIC_HSTPCIX_CNTRL of %s\n",
+                          pci_name(asd_ha->pcidev));
+               return err;
+       }
+       pci_write_config_dword(asd_ha->pcidev, PCIC_HSTPCIX_CNTRL,
+                                       v | SC_TMR_DIS);
+       if (err) {
+               asd_printk("couldn't disable split completion timer of %s\n",
+                          pci_name(asd_ha->pcidev));
+               return err;
+       }
+
+       err = asd_read_ocm(asd_ha);
+       if (err) {
+               asd_printk("couldn't read ocm(%d)\n", err);
+               /* While suspicios, it is not an error that we
+                * couldn't read the OCM. */
+       }
+
+       err = asd_read_flash(asd_ha);
+       if (err) {
+               asd_printk("couldn't read flash(%d)\n", err);
+               /* While suspicios, it is not an error that we
+                * couldn't read FLASH memory.
+                */
+       }
+
+       asd_init_ctxmem(asd_ha);
+
+       asd_get_user_sas_addr(asd_ha);
+       if (!asd_ha->hw_prof.sas_addr[0]) {
+               asd_printk("No SAS Address provided for %s\n",
+                          pci_name(asd_ha->pcidev));
+               err = -ENODEV;
+               goto Out;
+       }
+
+       asd_propagate_sas_addr(asd_ha);
+
+       err = asd_init_phys(asd_ha);
+       if (err) {
+               asd_printk("couldn't initialize phys for %s\n",
+                           pci_name(asd_ha->pcidev));
+               goto Out;
+       }
+
+       err = asd_init_scbs(asd_ha);
+       if (err) {
+               asd_printk("couldn't initialize scbs for %s\n",
+                           pci_name(asd_ha->pcidev));
+               goto Out;
+       }
+
+       err = asd_init_dl(asd_ha);
+       if (err) {
+               asd_printk("couldn't initialize the done list:%d\n",
+                           err);
+               goto Out;
+       }
+
+       err = asd_init_escbs(asd_ha);
+       if (err) {
+               asd_printk("couldn't initialize escbs\n");
+               goto Out;
+       }
+
+       err = asd_init_chip(asd_ha);
+       if (err) {
+               asd_printk("couldn't init the chip\n");
+               goto Out;
+       }
+Out:
+       return err;
+}
+
+/* ---------- Chip reset ---------- */
+
+/**
+ * asd_chip_reset -- reset the host adapter, etc
+ * @asd_ha: pointer to host adapter structure of interest
+ *
+ * Called from the ISR.  Hard reset the chip.  Let everything
+ * timeout.  This should be no different than hot-unplugging the
+ * host adapter.  Once everything times out we'll init the chip with
+ * a call to asd_init_chip() and enable interrupts with asd_enable_ints().
+ * XXX finish.
+ */
+static void asd_chip_reset(struct asd_ha_struct *asd_ha)
+{
+       struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
+
+       ASD_DPRINTK("chip reset for %s\n", pci_name(asd_ha->pcidev));
+       asd_chip_hardrst(asd_ha);
+       sas_ha->notify_ha_event(sas_ha, HAE_RESET);
+}
+
+/* ---------- Done List Routines ---------- */
+
+static void asd_dl_tasklet_handler(unsigned long data)
+{
+       struct asd_ha_struct *asd_ha = (struct asd_ha_struct *) data;
+       struct asd_seq_data *seq = &asd_ha->seq;
+       unsigned long flags;
+
+       while (1) {
+               struct done_list_struct *dl = &seq->dl[seq->dl_next];
+               struct asd_ascb *ascb;
+
+               if ((dl->toggle & DL_TOGGLE_MASK) != seq->dl_toggle)
+                       break;
+
+               /* find the aSCB */
+               spin_lock_irqsave(&seq->tc_index_lock, flags);
+               ascb = asd_tc_index_find(seq, (int)le16_to_cpu(dl->index));
+               spin_unlock_irqrestore(&seq->tc_index_lock, flags);
+               if (unlikely(!ascb)) {
+                       ASD_DPRINTK("BUG:sequencer:dl:no ascb?!\n");
+                       goto next_1;
+               } else if (ascb->scb->header.opcode == EMPTY_SCB) {
+                       goto out;
+               } else if (!ascb->uldd_timer && !del_timer(&ascb->timer)) {
+                       goto next_1;
+               }
+               spin_lock_irqsave(&seq->pend_q_lock, flags);
+               list_del_init(&ascb->list);
+               seq->pending--;
+               spin_unlock_irqrestore(&seq->pend_q_lock, flags);
+       out:
+               ascb->tasklet_complete(ascb, dl);
+
+       next_1:
+               seq->dl_next = (seq->dl_next + 1) & (ASD_DL_SIZE-1);
+               if (!seq->dl_next)
+                       seq->dl_toggle ^= DL_TOGGLE_MASK;
+       }
+}
+
+/* ---------- Interrupt Service Routines ---------- */
+
+/**
+ * asd_process_donelist_isr -- schedule processing of done list entries
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_process_donelist_isr(struct asd_ha_struct *asd_ha)
+{
+       tasklet_schedule(&asd_ha->seq.dl_tasklet);
+}
+
+/**
+ * asd_com_sas_isr -- process device communication interrupt (COMINT)
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_com_sas_isr(struct asd_ha_struct *asd_ha)
+{
+       u32 comstat = asd_read_reg_dword(asd_ha, COMSTAT);
+
+       /* clear COMSTAT int */
+       asd_write_reg_dword(asd_ha, COMSTAT, 0xFFFFFFFF);
+
+       if (comstat & CSBUFPERR) {
+               asd_printk("%s: command/status buffer dma parity error\n",
+                          pci_name(asd_ha->pcidev));
+       } else if (comstat & CSERR) {
+               int i;
+               u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR);
+               dmaerr &= 0xFF;
+               asd_printk("%s: command/status dma error, DMAERR: 0x%02x, "
+                          "CSDMAADR: 0x%04x, CSDMAADR+4: 0x%04x\n",
+                          pci_name(asd_ha->pcidev),
+                          dmaerr,
+                          asd_read_reg_dword(asd_ha, CSDMAADR),
+                          asd_read_reg_dword(asd_ha, CSDMAADR+4));
+               asd_printk("CSBUFFER:\n");
+               for (i = 0; i < 8; i++) {
+                       asd_printk("%08x %08x %08x %08x\n",
+                                  asd_read_reg_dword(asd_ha, CSBUFFER),
+                                  asd_read_reg_dword(asd_ha, CSBUFFER+4),
+                                  asd_read_reg_dword(asd_ha, CSBUFFER+8),
+                                  asd_read_reg_dword(asd_ha, CSBUFFER+12));
+               }
+               asd_dump_seq_state(asd_ha, 0);
+       } else if (comstat & OVLYERR) {
+               u32 dmaerr = asd_read_reg_dword(asd_ha, DMAERR);
+               dmaerr = (dmaerr >> 8) & 0xFF;
+               asd_printk("%s: overlay dma error:0x%x\n",
+                          pci_name(asd_ha->pcidev),
+                          dmaerr);
+       }
+       asd_chip_reset(asd_ha);
+}
+
+static inline void asd_arp2_err(struct asd_ha_struct *asd_ha, u32 dchstatus)
+{
+       static const char *halt_code[256] = {
+               "UNEXPECTED_INTERRUPT0",
+               "UNEXPECTED_INTERRUPT1",
+               "UNEXPECTED_INTERRUPT2",
+               "UNEXPECTED_INTERRUPT3",
+               "UNEXPECTED_INTERRUPT4",
+               "UNEXPECTED_INTERRUPT5",
+               "UNEXPECTED_INTERRUPT6",
+               "UNEXPECTED_INTERRUPT7",
+               "UNEXPECTED_INTERRUPT8",
+               "UNEXPECTED_INTERRUPT9",
+               "UNEXPECTED_INTERRUPT10",
+               [11 ... 19] = "unknown[11,19]",
+               "NO_FREE_SCB_AVAILABLE",
+               "INVALID_SCB_OPCODE",
+               "INVALID_MBX_OPCODE",
+               "INVALID_ATA_STATE",
+               "ATA_QUEUE_FULL",
+               "ATA_TAG_TABLE_FAULT",
+               "ATA_TAG_MASK_FAULT",
+               "BAD_LINK_QUEUE_STATE",
+               "DMA2CHIM_QUEUE_ERROR",
+               "EMPTY_SCB_LIST_FULL",
+               "unknown[30]",
+               "IN_USE_SCB_ON_FREE_LIST",
+               "BAD_OPEN_WAIT_STATE",
+               "INVALID_STP_AFFILIATION",
+               "unknown[34]",
+               "EXEC_QUEUE_ERROR",
+               "TOO_MANY_EMPTIES_NEEDED",
+               "EMPTY_REQ_QUEUE_ERROR",
+               "Q_MONIRTT_MGMT_ERROR",
+               "TARGET_MODE_FLOW_ERROR",
+               "DEVICE_QUEUE_NOT_FOUND",
+               "START_IRTT_TIMER_ERROR",
+               "ABORT_TASK_ILLEGAL_REQ",
+               [43 ... 255] = "unknown[43,255]"
+       };
+
+       if (dchstatus & CSEQINT) {
+               u32 arp2int = asd_read_reg_dword(asd_ha, CARP2INT);
+
+               if (arp2int & (ARP2WAITTO|ARP2ILLOPC|ARP2PERR|ARP2CIOPERR)) {
+                       asd_printk("%s: CSEQ arp2int:0x%x\n",
+                                  pci_name(asd_ha->pcidev),
+                                  arp2int);
+               } else if (arp2int & ARP2HALTC)
+                       asd_printk("%s: CSEQ halted: %s\n",
+                                  pci_name(asd_ha->pcidev),
+                                  halt_code[(arp2int>>16)&0xFF]);
+               else
+                       asd_printk("%s: CARP2INT:0x%x\n",
+                                  pci_name(asd_ha->pcidev),
+                                  arp2int);
+       }
+       if (dchstatus & LSEQINT_MASK) {
+               int lseq;
+               u8  lseq_mask = dchstatus & LSEQINT_MASK;
+
+               for_each_sequencer(lseq_mask, lseq_mask, lseq) {
+                       u32 arp2int = asd_read_reg_dword(asd_ha,
+                                                        LmARP2INT(lseq));
+                       if (arp2int & (ARP2WAITTO | ARP2ILLOPC | ARP2PERR
+                                      | ARP2CIOPERR)) {
+                               asd_printk("%s: LSEQ%d arp2int:0x%x\n",
+                                          pci_name(asd_ha->pcidev),
+                                          lseq, arp2int);
+                               /* XXX we should only do lseq reset */
+                       } else if (arp2int & ARP2HALTC)
+                               asd_printk("%s: LSEQ%d halted: %s\n",
+                                          pci_name(asd_ha->pcidev),
+                                          lseq,halt_code[(arp2int>>16)&0xFF]);
+                       else
+                               asd_printk("%s: LSEQ%d ARP2INT:0x%x\n",
+                                          pci_name(asd_ha->pcidev), lseq,
+                                          arp2int);
+               }
+       }
+       asd_chip_reset(asd_ha);
+}
+
+/**
+ * asd_dch_sas_isr -- process device channel interrupt (DEVINT)
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_dch_sas_isr(struct asd_ha_struct *asd_ha)
+{
+       u32 dchstatus = asd_read_reg_dword(asd_ha, DCHSTATUS);
+
+       if (dchstatus & CFIFTOERR) {
+               asd_printk("%s: CFIFTOERR\n", pci_name(asd_ha->pcidev));
+               asd_chip_reset(asd_ha);
+       } else
+               asd_arp2_err(asd_ha, dchstatus);
+}
+
+/**
+ * ads_rbi_exsi_isr -- process external system interface interrupt (INITERR)
+ * @asd_ha: pointer to host adapter structure
+ */
+static inline void asd_rbi_exsi_isr(struct asd_ha_struct *asd_ha)
+{
+       u32 stat0r = asd_read_reg_dword(asd_ha, ASISTAT0R);
+
+       if (!(stat0r & ASIERR)) {
+               asd_printk("hmm, EXSI interrupted but no error?\n");
+               return;
+       }
+
+       if (stat0r & ASIFMTERR) {
+               asd_printk("ASI SEEPROM format error for %s\n",
+                          pci_name(asd_ha->pcidev));
+       } else if (stat0r & ASISEECHKERR) {
+               u32 stat1r = asd_read_reg_dword(asd_ha, ASISTAT1R);
+               asd_printk("ASI SEEPROM checksum 0x%x error for %s\n",
+                          stat1r & CHECKSUM_MASK,
+                          pci_name(asd_ha->pcidev));
+       } else {
+               u32 statr = asd_read_reg_dword(asd_ha, ASIERRSTATR);
+
+               if (!(statr & CPI2ASIMSTERR_MASK)) {
+                       ASD_DPRINTK("hmm, ASIERR?\n");
+                       return;
+               } else {
+                       u32 addr = asd_read_reg_dword(asd_ha, ASIERRADDR);
+                       u32 data = asd_read_reg_dword(asd_ha, ASIERRDATAR);
+
+                       asd_printk("%s: CPI2 xfer err: addr: 0x%x, wdata: 0x%x, "
+                                  "count: 0x%x, byteen: 0x%x, targerr: 0x%x "
+                                  "master id: 0x%x, master err: 0x%x\n",
+                                  pci_name(asd_ha->pcidev),
+                                  addr, data,
+                                  (statr & CPI2ASIBYTECNT_MASK) >> 16,
+                                  (statr & CPI2ASIBYTEEN_MASK) >> 12,
+                                  (statr & CPI2ASITARGERR_MASK) >> 8,
+                                  (statr & CPI2ASITARGMID_MASK) >> 4,
+                                  (statr & CPI2ASIMSTERR_MASK));
+               }
+       }
+       asd_chip_reset(asd_ha);
+}
+
+/**
+ * asd_hst_pcix_isr -- process host interface interrupts
+ * @asd_ha: pointer to host adapter structure
+ *
+ * Asserted on PCIX errors: target abort, etc.
+ */
+static inline void asd_hst_pcix_isr(struct asd_ha_struct *asd_ha)
+{
+       u16 status;
+       u32 pcix_status;
+       u32 ecc_status;
+
+       pci_read_config_word(asd_ha->pcidev, PCI_STATUS, &status);
+       pci_read_config_dword(asd_ha->pcidev, PCIX_STATUS, &pcix_status);
+       pci_read_config_dword(asd_ha->pcidev, ECC_CTRL_STAT, &ecc_status);
+
+       if (status & PCI_STATUS_DETECTED_PARITY)
+               asd_printk("parity error for %s\n", pci_name(asd_ha->pcidev));
+       else if (status & PCI_STATUS_REC_MASTER_ABORT)
+               asd_printk("master abort for %s\n", pci_name(asd_ha->pcidev));
+       else if (status & PCI_STATUS_REC_TARGET_ABORT)
+               asd_printk("target abort for %s\n", pci_name(asd_ha->pcidev));
+       else if (status & PCI_STATUS_PARITY)
+               asd_printk("data parity for %s\n", pci_name(asd_ha->pcidev));
+       else if (pcix_status & RCV_SCE) {
+               asd_printk("received split completion error for %s\n",
+                          pci_name(asd_ha->pcidev));
+               pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);
+               /* XXX: Abort task? */
+               return;
+       } else if (pcix_status & UNEXP_SC) {
+               asd_printk("unexpected split completion for %s\n",
+                          pci_name(asd_ha->pcidev));
+               pci_write_config_dword(asd_ha->pcidev,PCIX_STATUS,pcix_status);
+               /* ignore */
+               return;
+       } else if (pcix_status & SC_DISCARD)
+               asd_printk("split completion discarded for %s\n",
+                          pci_name(asd_ha->pcidev));
+       else if (ecc_status & UNCOR_ECCERR)
+               asd_printk("uncorrectable ECC error for %s\n",
+                          pci_name(asd_ha->pcidev));
+       asd_chip_reset(asd_ha);
+}
+
+/**
+ * asd_hw_isr -- host adapter interrupt service routine
+ * @irq: ignored
+ * @dev_id: pointer to host adapter structure
+ * @regs: ignored
+ *
+ * The ISR processes done list entries and level 3 error handling.
+ */
+irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+       struct asd_ha_struct *asd_ha = dev_id;
+       u32 chimint = asd_read_reg_dword(asd_ha, CHIMINT);
+
+       if (!chimint)
+               return IRQ_NONE;
+
+       asd_write_reg_dword(asd_ha, CHIMINT, chimint);
+       (void) asd_read_reg_dword(asd_ha, CHIMINT);
+
+       if (chimint & DLAVAIL)
+               asd_process_donelist_isr(asd_ha);
+       if (chimint & COMINT)
+               asd_com_sas_isr(asd_ha);
+       if (chimint & DEVINT)
+               asd_dch_sas_isr(asd_ha);
+       if (chimint & INITERR)
+               asd_rbi_exsi_isr(asd_ha);
+       if (chimint & HOSTERR)
+               asd_hst_pcix_isr(asd_ha);
+
+       return IRQ_HANDLED;
+}
+
+/* ---------- SCB handling ---------- */
+
+static inline struct asd_ascb *asd_ascb_alloc(struct asd_ha_struct *asd_ha,
+                                             unsigned int gfp_flags)
+{
+       extern kmem_cache_t *asd_ascb_cache;
+       struct asd_seq_data *seq = &asd_ha->seq;
+       struct asd_ascb *ascb;
+       unsigned long flags;
+
+       ascb = kmem_cache_alloc(asd_ascb_cache, gfp_flags);
+
+       if (ascb) {
+               memset(ascb, 0, sizeof(*ascb));
+               ascb->dma_scb.size = sizeof(struct scb);
+               ascb->dma_scb.vaddr = dma_pool_alloc(asd_ha->scb_pool,
+                                                    gfp_flags,
+                                                   &ascb->dma_scb.dma_handle);
+               if (!ascb->dma_scb.vaddr) {
+                       kmem_cache_free(asd_ascb_cache, ascb);
+                       return NULL;
+               }
+               memset(ascb->dma_scb.vaddr, 0, sizeof(struct scb));
+               asd_init_ascb(asd_ha, ascb);
+
+               spin_lock_irqsave(&seq->tc_index_lock, flags);
+               ascb->tc_index = asd_tc_index_get(seq, ascb);
+               spin_unlock_irqrestore(&seq->tc_index_lock, flags);
+               if (ascb->tc_index == -1)
+                       goto undo;
+
+               ascb->scb->header.index = cpu_to_le16((u16)ascb->tc_index);
+       }
+
+       return ascb;
+undo:
+       dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
+                     ascb->dma_scb.dma_handle);
+       kmem_cache_free(asd_ascb_cache, ascb);
+       ASD_DPRINTK("no index for ascb\n");
+       return NULL;
+}
+
+/**
+ * asd_ascb_alloc_list -- allocate a list of aSCBs
+ * @asd_ha: pointer to host adapter structure
+ * @num: pointer to integer number of aSCBs
+ * @gfp_flags: GFP_ flags.
+ *
+ * This is the only function which is used to allocate aSCBs.
+ * It can allocate one or many. If more than one, then they form
+ * a linked list in two ways: by their list field of the ascb struct
+ * and by the next_scb field of the scb_header.
+ *
+ * Returns NULL if no memory was available, else pointer to a list
+ * of ascbs.  When this function returns, @num would be the number
+ * of SCBs which were not able to be allocated, 0 if all requested
+ * were able to be allocated.
+ */
+struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
+                                    *asd_ha, int *num,
+                                    unsigned int gfp_flags)
+{
+       struct asd_ascb *first = NULL;
+
+       for ( ; *num > 0; --*num) {
+               struct asd_ascb *ascb = asd_ascb_alloc(asd_ha, gfp_flags);
+
+               if (!ascb)
+                       break;
+               else if (!first)
+                       first = ascb;
+               else {
+                       struct asd_ascb *last = list_entry(first->list.prev,
+                                                          struct asd_ascb,
+                                                          list);
+                       list_add_tail(&ascb->list, &first->list);
+                       last->scb->header.next_scb =
+                               cpu_to_le64(((u64)ascb->dma_scb.dma_handle));
+               }
+       }
+
+       return first;
+}
+
+/**
+ * asd_swap_head_scb -- swap the head scb
+ * @asd_ha: pointer to host adapter structure
+ * @ascb: pointer to the head of an ascb list
+ *
+ * The sequencer knows the DMA address of the next SCB to be DMAed to
+ * the host adapter, from initialization or from the last list DMAed.
+ * seq->next_scb keeps the address of this SCB.  The sequencer will
+ * DMA to the host adapter this list of SCBs.  But the head (first
+ * element) of this list is not known to the sequencer.  Here we swap
+ * the head of the list with the known SCB (memcpy()).
+ * Only one memcpy() is required per list so it is in our interest
+ * to keep the list of SCB as long as possible so that the ratio
+ * of number of memcpy calls to the number of SCB DMA-ed is as small
+ * as possible.
+ *
+ * LOCKING: called with the pending list lock held.
+ */
+static inline void asd_swap_head_scb(struct asd_ha_struct *asd_ha,
+                                    struct asd_ascb *ascb)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       struct asd_ascb *last = list_entry(ascb->list.prev,
+                                          struct asd_ascb,
+                                          list);
+       struct asd_dma_tok t = ascb->dma_scb;
+
+       memcpy(seq->next_scb.vaddr, ascb->scb, sizeof(*ascb->scb));
+       ascb->dma_scb = seq->next_scb;
+       ascb->scb = ascb->dma_scb.vaddr;
+       seq->next_scb = t;
+       last->scb->header.next_scb =
+               cpu_to_le64(((u64)seq->next_scb.dma_handle));
+}
+
+/**
+ * asd_start_timers -- (add and) start timers of SCBs
+ * @list: pointer to struct list_head of the scbs
+ * @to: timeout in jiffies
+ *
+ * If an SCB in the @list has no timer function, assign the default
+ * one,  then start the timer of the SCB.  This function is
+ * intended to be called from asd_post_ascb_list(), just prior to
+ * posting the SCBs to the sequencer.
+ */
+static inline void asd_start_scb_timers(struct list_head *list)
+{
+       struct asd_ascb *ascb;
+       list_for_each_entry(ascb, list, list) {
+               if (!ascb->uldd_timer) {
+                       ascb->timer.data = (unsigned long) ascb;
+                       ascb->timer.function = asd_ascb_timedout;
+                       ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
+                       add_timer(&ascb->timer);
+               }
+       }
+}
+
+/**
+ * asd_post_ascb_list -- post a list of 1 or more aSCBs to the host adapter
+ * @asd_ha: pointer to a host adapter structure
+ * @ascb: pointer to the first aSCB in the list
+ * @num: number of aSCBs in the list (to be posted)
+ *
+ * See queueing comment in asd_post_escb_list().
+ *
+ * Additional note on queuing: In order to minimize the ratio of memcpy()
+ * to the number of ascbs sent, we try to batch-send as many ascbs as possible
+ * in one go.
+ * Two cases are possible:
+ *    A) can_queue >= num,
+ *    B) can_queue < num.
+ * Case A: we can send the whole batch at once.  Increment "pending"
+ * in the beginning of this function, when it is checked, in order to
+ * eliminate races when this function is called by multiple processes.
+ * Case B: should never happen if the managing layer considers
+ * lldd_queue_size.
+ */
+int asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+                      int num)
+{
+       unsigned long flags;
+       LIST_HEAD(list);
+       int can_queue;
+
+       spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+       can_queue = asd_ha->hw_prof.max_scbs - asd_ha->seq.pending;
+       if (can_queue >= num)
+               asd_ha->seq.pending += num;
+       else
+               can_queue = 0;
+
+       if (!can_queue) {
+               spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+               asd_printk("%s: scb queue full\n", pci_name(asd_ha->pcidev));
+               return -SAS_QUEUE_FULL;
+       }
+
+       asd_swap_head_scb(asd_ha, ascb);
+
+       __list_add(&list, ascb->list.prev, &ascb->list);
+
+       asd_start_scb_timers(&list);
+
+       asd_ha->seq.scbpro += num;
+       list_splice_init(&list, asd_ha->seq.pend_q.prev);
+       asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);
+       spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+
+       return 0;
+}
+
+/**
+ * asd_post_escb_list -- post a list of 1 or more empty scb
+ * @asd_ha: pointer to a host adapter structure
+ * @ascb: pointer to the first empty SCB in the list
+ * @num: number of aSCBs in the list (to be posted)
+ *
+ * This is essentially the same as asd_post_ascb_list, but we do not
+ * increment pending, add those to the pending list or get indexes.
+ * See asd_init_escbs() and asd_init_post_escbs().
+ *
+ * Since sending a list of ascbs is a superset of sending a single
+ * ascb, this function exists to generalize this.  More specifically,
+ * when sending a list of those, we want to do only a _single_
+ * memcpy() at swap head, as opposed to for each ascb sent (in the
+ * case of sending them one by one).  That is, we want to minimize the
+ * ratio of memcpy() operations to the number of ascbs sent.  The same
+ * logic applies to asd_post_ascb_list().
+ */
+int asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+                      int num)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
+       asd_swap_head_scb(asd_ha, ascb);
+       asd_ha->seq.scbpro += num;
+       asd_write_reg_dword(asd_ha, SCBPRO, (u32)asd_ha->seq.scbpro);
+       spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
+
+       return 0;
+}
+
+/* ---------- LED ---------- */
+
+/**
+ * asd_turn_led -- turn on/off an LED
+ * @asd_ha: pointer to host adapter structure
+ * @phy_id: the PHY id whose LED we want to manupulate
+ * @op: 1 to turn on, 0 to turn off
+ */
+void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op)
+{
+       if (phy_id < ASD_MAX_PHYS) {
+               u32 v = asd_read_reg_dword(asd_ha, LmCONTROL(phy_id));
+               if (op)
+                       v |= LEDPOL;
+               else
+                       v &= ~LEDPOL;
+               asd_write_reg_dword(asd_ha, LmCONTROL(phy_id), v);
+       }
+}
+
+/**
+ * asd_control_led -- enable/disable an LED on the board
+ * @asd_ha: pointer to host adapter structure
+ * @phy_id: integer, the phy id
+ * @op: integer, 1 to enable, 0 to disable the LED
+ *
+ * First we output enable the LED, then we set the source
+ * to be an external module.
+ */
+void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op)
+{
+       if (phy_id < ASD_MAX_PHYS) {
+               u32 v;
+
+               v = asd_read_reg_dword(asd_ha, GPIOOER);
+               if (op)
+                       v |= (1 << phy_id);
+               else
+                       v &= ~(1 << phy_id);
+               asd_write_reg_dword(asd_ha, GPIOOER, v);
+
+               v = asd_read_reg_dword(asd_ha, GPIOCNFGR);
+               if (op)
+                       v |= (1 << phy_id);
+               else
+                       v &= ~(1 << phy_id);
+               asd_write_reg_dword(asd_ha, GPIOCNFGR, v);
+       }
+}
+
+/* ---------- PHY enable ---------- */
+
+static int asd_enable_phy(struct asd_ha_struct *asd_ha, int phy_id)
+{
+       struct asd_phy *phy = &asd_ha->phys[phy_id];
+
+       asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, INT_ENABLE_2), 0);
+       asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, HOT_PLUG_DELAY),
+                          HOTPLUG_DELAY_TIMEOUT);
+
+       /* Get defaults from manuf. sector */
+       /* XXX we need defaults for those in case MS is broken. */
+       asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_0),
+                          phy->phy_desc->phy_control_0);
+       asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_1),
+                          phy->phy_desc->phy_control_1);
+       asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_2),
+                          phy->phy_desc->phy_control_2);
+       asd_write_reg_byte(asd_ha, LmSEQ_OOB_REG(phy_id, PHY_CONTROL_3),
+                          phy->phy_desc->phy_control_3);
+
+       asd_write_reg_dword(asd_ha, LmSEQ_TEN_MS_COMINIT_TIMEOUT(phy_id),
+                           ASD_COMINIT_TIMEOUT);
+
+       asd_write_reg_addr(asd_ha, LmSEQ_TX_ID_ADDR_FRAME(phy_id),
+                          phy->id_frm_tok->dma_handle);
+
+       asd_control_led(asd_ha, phy_id, 1);
+
+       return 0;
+}
+
+int asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask)
+{
+       u8  phy_m;
+       u8  i;
+       int num = 0, k;
+       struct asd_ascb *ascb;
+       struct asd_ascb *ascb_list;
+
+       if (!phy_mask) {
+               asd_printk("%s called with phy_mask of 0!?\n", __FUNCTION__);
+               return 0;
+       }
+
+       for_each_phy(phy_mask, phy_m, i) {
+               num++;
+               asd_enable_phy(asd_ha, i);
+       }
+
+       k = num;
+       ascb_list = asd_ascb_alloc_list(asd_ha, &k, GFP_KERNEL);
+       if (!ascb_list) {
+               asd_printk("no memory for control phy ascb list\n");
+               return -ENOMEM;
+       }
+       num -= k;
+
+       ascb = ascb_list;
+       for_each_phy(phy_mask, phy_m, i) {
+               asd_build_control_phy(ascb, i, ENABLE_PHY);
+               ascb = list_entry(ascb->list.next, struct asd_ascb, list);
+       }
+       ASD_DPRINTK("posting %d control phy scbs\n", num);
+       k = asd_post_ascb_list(asd_ha, ascb_list, num);
+       if (k)
+               asd_ascb_free_list(ascb_list);
+
+       return k;
+}
diff --git a/drivers/scsi/aic94xx/aic94xx_hwi.h b/drivers/scsi/aic94xx/aic94xx_hwi.h
new file mode 100644 (file)
index 0000000..c7d5053
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Aic94xx SAS/SATA driver hardware interface header file.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef _AIC94XX_HWI_H_
+#define _AIC94XX_HWI_H_
+
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+
+#include <scsi/libsas.h>
+
+#include "aic94xx.h"
+#include "aic94xx_sas.h"
+
+/* Define ASD_MAX_PHYS to the maximum phys ever. Currently 8. */
+#define ASD_MAX_PHYS       8
+#define ASD_PCBA_SN_SIZE   12
+
+/* Those are to be further named properly, the "RAZORx" part, and
+ * subsequently included in include/linux/pci_ids.h.
+ */
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR10 0x410
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR12 0x412
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR1E 0x41E
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR30 0x430
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR32 0x432
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3E 0x43E
+#define PCI_DEVICE_ID_ADAPTEC2_RAZOR3F 0x43F
+
+struct asd_ha_addrspace {
+       void __iomem  *addr;
+       unsigned long  start;       /* pci resource start */
+       unsigned long  len;         /* pci resource len */
+       unsigned long  flags;       /* pci resource flags */
+
+       /* addresses internal to the host adapter */
+       u32 swa_base; /* mmspace 1 (MBAR1) uses this only */
+       u32 swb_base;
+       u32 swc_base;
+};
+
+struct bios_struct {
+       int    present;
+       u8     maj;
+       u8     min;
+       u32    bld;
+};
+
+struct unit_element_struct {
+       u16    num;
+       u16    size;
+       void   *area;
+};
+
+struct flash_struct {
+       u32    bar;
+       int    present;
+       int    wide;
+       u8     manuf;
+       u8     dev_id;
+       u8     sec_prot;
+
+       u32    dir_offs;
+};
+
+struct asd_phy_desc {
+       /* From CTRL-A settings, then set to what is appropriate */
+       u8     sas_addr[SAS_ADDR_SIZE];
+       u8     max_sas_lrate;
+       u8     min_sas_lrate;
+       u8     max_sata_lrate;
+       u8     min_sata_lrate;
+       u8     flags;
+#define ASD_CRC_DIS  1
+#define ASD_SATA_SPINUP_HOLD 2
+
+       u8     phy_control_0; /* mode 5 reg 0x160 */
+       u8     phy_control_1; /* mode 5 reg 0x161 */
+       u8     phy_control_2; /* mode 5 reg 0x162 */
+       u8     phy_control_3; /* mode 5 reg 0x163 */
+};
+
+struct asd_dma_tok {
+       void *vaddr;
+       dma_addr_t dma_handle;
+       size_t size;
+};
+
+struct hw_profile {
+       struct bios_struct bios;
+       struct unit_element_struct ue;
+       struct flash_struct flash;
+
+       u8     sas_addr[SAS_ADDR_SIZE];
+       char   pcba_sn[ASD_PCBA_SN_SIZE+1];
+
+       u8     enabled_phys;      /* mask of enabled phys */
+       struct asd_phy_desc phy_desc[ASD_MAX_PHYS];
+       u32    max_scbs;          /* absolute sequencer scb queue size */
+       struct asd_dma_tok *scb_ext;
+       u32    max_ddbs;
+       struct asd_dma_tok *ddb_ext;
+
+       spinlock_t ddb_lock;
+       void  *ddb_bitmap;
+
+       int    num_phys;          /* ENABLEABLE */
+       int    max_phys;          /* REPORTED + ENABLEABLE */
+
+       unsigned addr_range;      /* max # of addrs; max # of possible ports */
+       unsigned port_name_base;
+       unsigned dev_name_base;
+       unsigned sata_name_base;
+};
+
+struct asd_ascb {
+       struct list_head list;
+       struct asd_ha_struct *ha;
+
+       struct scb *scb;          /* equals dma_scb->vaddr */
+       struct asd_dma_tok dma_scb;
+       struct asd_dma_tok *sg_arr;
+
+       void (*tasklet_complete)(struct asd_ascb *, struct done_list_struct *);
+       u8     uldd_timer:1;
+
+       /* internally generated command */
+       struct timer_list timer;
+       struct completion completion;
+       u8        tag_valid:1;
+       __be16    tag;            /* error recovery only */
+
+       /* If this is an Empty SCB, index of first edb in seq->edb_arr. */
+       int    edb_index;
+
+       /* Used by the timer timeout function. */
+       int    tc_index;
+
+       void   *uldd_task;
+};
+
+#define ASD_DL_SIZE_BITS   0x8
+#define ASD_DL_SIZE        (1<<(2+ASD_DL_SIZE_BITS))
+#define ASD_DEF_DL_TOGGLE  0x01
+
+struct asd_seq_data {
+       spinlock_t pend_q_lock;
+       u16    scbpro;
+       int    pending;
+       struct list_head pend_q;
+       int    can_queue;         /* per adapter */
+       struct asd_dma_tok next_scb; /* next scb to be delivered to CSEQ */
+
+       spinlock_t tc_index_lock;
+       void **tc_index_array;
+       void *tc_index_bitmap;
+       int   tc_index_bitmap_bits;
+
+       struct tasklet_struct dl_tasklet;
+       struct done_list_struct *dl; /* array of done list entries, equals */
+       struct asd_dma_tok *actual_dl; /* actual_dl->vaddr */
+       int    dl_toggle;
+       int    dl_next;
+
+       int    num_edbs;
+       struct asd_dma_tok **edb_arr;
+       int    num_escbs;
+       struct asd_ascb **escb_arr; /* array of pointers to escbs */
+};
+
+/* This is the Host Adapter structure.  It describes the hardware
+ * SAS adapter.
+ */
+struct asd_ha_struct {
+       struct pci_dev   *pcidev;
+       const char       *name;
+
+       struct sas_ha_struct sas_ha;
+
+       u8                revision_id;
+
+       int               iospace;
+       spinlock_t        iolock;
+       struct asd_ha_addrspace io_handle[2];
+
+       struct hw_profile hw_prof;
+
+       struct asd_phy    phys[ASD_MAX_PHYS];
+       struct asd_sas_port   ports[ASD_MAX_PHYS];
+
+       struct dma_pool  *scb_pool;
+
+       struct asd_seq_data  seq; /* sequencer related */
+};
+
+/* ---------- Common macros ---------- */
+
+#define ASD_BUSADDR_LO(__dma_handle) ((u32)(__dma_handle))
+#define ASD_BUSADDR_HI(__dma_handle) (((sizeof(dma_addr_t))==8)     \
+                                    ? ((u32)((__dma_handle) >> 32)) \
+                                    : ((u32)0))
+
+#define dev_to_asd_ha(__dev)  pci_get_drvdata(to_pci_dev(__dev))
+#define SCB_SITE_VALID(__site_no) (((__site_no) & 0xF0FF) != 0x00FF   \
+                                && ((__site_no) & 0xF0FF) > 0x001F)
+/* For each bit set in __lseq_mask, set __lseq to equal the bit
+ * position of the set bit and execute the statement following.
+ * __mc is the temporary mask, used as a mask "counter".
+ */
+#define for_each_sequencer(__lseq_mask, __mc, __lseq)                        \
+       for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
+               if (((__mc) & 1))
+#define for_each_phy(__lseq_mask, __mc, __lseq)                              \
+       for ((__mc)=(__lseq_mask),(__lseq)=0;(__mc)!=0;(__lseq++),(__mc)>>=1)\
+               if (((__mc) & 1))
+
+#define PHY_ENABLED(_HA, _I) ((_HA)->hw_prof.enabled_phys & (1<<(_I)))
+
+/* ---------- DMA allocs ---------- */
+
+static inline struct asd_dma_tok *asd_dmatok_alloc(unsigned int flags)
+{
+       return kmem_cache_alloc(asd_dma_token_cache, flags);
+}
+
+static inline void asd_dmatok_free(struct asd_dma_tok *token)
+{
+       kmem_cache_free(asd_dma_token_cache, token);
+}
+
+static inline struct asd_dma_tok *asd_alloc_coherent(struct asd_ha_struct *
+                                                    asd_ha, size_t size,
+                                                    unsigned int flags)
+{
+       struct asd_dma_tok *token = asd_dmatok_alloc(flags);
+       if (token) {
+               token->size = size;
+               token->vaddr = dma_alloc_coherent(&asd_ha->pcidev->dev,
+                                                 token->size,
+                                                 &token->dma_handle,
+                                                 flags);
+               if (!token->vaddr) {
+                       asd_dmatok_free(token);
+                       token = NULL;
+               }
+       }
+       return token;
+}
+
+static inline void asd_free_coherent(struct asd_ha_struct *asd_ha,
+                                    struct asd_dma_tok *token)
+{
+       if (token) {
+               dma_free_coherent(&asd_ha->pcidev->dev, token->size,
+                                 token->vaddr, token->dma_handle);
+               asd_dmatok_free(token);
+       }
+}
+
+static inline void asd_init_ascb(struct asd_ha_struct *asd_ha,
+                                struct asd_ascb *ascb)
+{
+       INIT_LIST_HEAD(&ascb->list);
+       ascb->scb = ascb->dma_scb.vaddr;
+       ascb->ha = asd_ha;
+       ascb->timer.function = NULL;
+       init_timer(&ascb->timer);
+       ascb->tc_index = -1;
+       init_completion(&ascb->completion);
+}
+
+/* Must be called with the tc_index_lock held!
+ */
+static inline void asd_tc_index_release(struct asd_seq_data *seq, int index)
+{
+       seq->tc_index_array[index] = NULL;
+       clear_bit(index, seq->tc_index_bitmap);
+}
+
+/* Must be called with the tc_index_lock held!
+ */
+static inline int asd_tc_index_get(struct asd_seq_data *seq, void *ptr)
+{
+       int index;
+
+       index = find_first_zero_bit(seq->tc_index_bitmap,
+                                   seq->tc_index_bitmap_bits);
+       if (index == seq->tc_index_bitmap_bits)
+               return -1;
+
+       seq->tc_index_array[index] = ptr;
+       set_bit(index, seq->tc_index_bitmap);
+
+       return index;
+}
+
+/* Must be called with the tc_index_lock held!
+ */
+static inline void *asd_tc_index_find(struct asd_seq_data *seq, int index)
+{
+       return seq->tc_index_array[index];
+}
+
+/**
+ * asd_ascb_free -- free a single aSCB after is has completed
+ * @ascb: pointer to the aSCB of interest
+ *
+ * This frees an aSCB after it has been executed/completed by
+ * the sequencer.
+ */
+static inline void asd_ascb_free(struct asd_ascb *ascb)
+{
+       if (ascb) {
+               struct asd_ha_struct *asd_ha = ascb->ha;
+               unsigned long flags;
+
+               BUG_ON(!list_empty(&ascb->list));
+               spin_lock_irqsave(&ascb->ha->seq.tc_index_lock, flags);
+               asd_tc_index_release(&ascb->ha->seq, ascb->tc_index);
+               spin_unlock_irqrestore(&ascb->ha->seq.tc_index_lock, flags);
+               dma_pool_free(asd_ha->scb_pool, ascb->dma_scb.vaddr,
+                             ascb->dma_scb.dma_handle);
+               kmem_cache_free(asd_ascb_cache, ascb);
+       }
+}
+
+/**
+ * asd_ascb_list_free -- free a list of ascbs
+ * @ascb_list: a list of ascbs
+ *
+ * This function will free a list of ascbs allocated by asd_ascb_alloc_list.
+ * It is used when say the scb queueing function returned QUEUE_FULL,
+ * and we do not need the ascbs any more.
+ */
+static inline void asd_ascb_free_list(struct asd_ascb *ascb_list)
+{
+       LIST_HEAD(list);
+       struct list_head *n, *pos;
+
+       __list_add(&list, ascb_list->list.prev, &ascb_list->list);
+       list_for_each_safe(pos, n, &list) {
+               list_del_init(pos);
+               asd_ascb_free(list_entry(pos, struct asd_ascb, list));
+       }
+}
+
+/* ---------- Function declarations ---------- */
+
+int  asd_init_hw(struct asd_ha_struct *asd_ha);
+irqreturn_t asd_hw_isr(int irq, void *dev_id, struct pt_regs *regs);
+
+
+struct asd_ascb *asd_ascb_alloc_list(struct asd_ha_struct
+                                    *asd_ha, int *num,
+                                    unsigned int gfp_mask);
+
+int  asd_post_ascb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+                       int num);
+int  asd_post_escb_list(struct asd_ha_struct *asd_ha, struct asd_ascb *ascb,
+                       int num);
+
+int  asd_init_post_escbs(struct asd_ha_struct *asd_ha);
+void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc);
+void asd_control_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
+void asd_turn_led(struct asd_ha_struct *asd_ha, int phy_id, int op);
+int  asd_enable_phys(struct asd_ha_struct *asd_ha, const u8 phy_mask);
+void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
+                                     u8 subfunc);
+
+void asd_ascb_timedout(unsigned long data);
+int  asd_chip_hardrst(struct asd_ha_struct *asd_ha);
+
+#endif
diff --git a/drivers/scsi/aic94xx/aic94xx_init.c b/drivers/scsi/aic94xx/aic94xx_init.c
new file mode 100644 (file)
index 0000000..ee2ccad
--- /dev/null
@@ -0,0 +1,866 @@
+/*
+ * Aic94xx SAS/SATA driver initialization.
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This file is part of the aic94xx driver.
+ *
+ * The aic94xx driver is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; version 2 of the
+ * License.
+ *
+ * The aic94xx driver is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the aic94xx driver; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi_host.h>
+
+#include "aic94xx.h"
+#include "aic94xx_reg.h"
+#include "aic94xx_hwi.h"
+#include "aic94xx_seq.h"
+
+/* The format is "version.release.patchlevel" */
+#define ASD_DRIVER_VERSION "1.0.2"
+
+static int use_msi = 0;
+module_param_named(use_msi, use_msi, int, S_IRUGO);
+MODULE_PARM_DESC(use_msi, "\n"
+       "\tEnable(1) or disable(0) using PCI MSI.\n"
+       "\tDefault: 0");
+
+static int lldd_max_execute_num = 0;
+module_param_named(collector, lldd_max_execute_num, int, S_IRUGO);
+MODULE_PARM_DESC(collector, "\n"
+       "\tIf greater than one, tells the SAS Layer to run in Task Collector\n"
+       "\tMode.  If 1 or 0, tells the SAS Layer to run in Direct Mode.\n"
+       "\tThe aic94xx SAS LLDD supports both modes.\n"
+       "\tDefault: 0 (Direct Mode).\n");
+
+char sas_addr_str[2*SAS_ADDR_SIZE + 1] = "";
+
+static struct scsi_transport_template *aic94xx_transport_template;
+
+static struct scsi_host_template aic94xx_sht = {
+       .module                 = THIS_MODULE,
+       /* .name is initialized */
+       .name                   = "aic94xx",
+       .queuecommand           = sas_queuecommand,
+       .target_alloc           = sas_target_alloc,
+       .slave_configure        = sas_slave_configure,
+       .slave_destroy          = sas_slave_destroy,
+       .change_queue_depth     = sas_change_queue_depth,
+       .change_queue_type      = sas_change_queue_type,
+       .bios_param             = sas_bios_param,
+       .can_queue              = 1,
+       .cmd_per_lun            = 1,
+       .this_id                = -1,
+       .sg_tablesize           = SG_ALL,
+       .max_sectors            = SCSI_DEFAULT_MAX_SECTORS,
+       .use_clustering         = ENABLE_CLUSTERING,
+};
+
+static int __devinit asd_map_memio(struct asd_ha_struct *asd_ha)
+{
+       int err, i;
+       struct asd_ha_addrspace *io_handle;
+
+       asd_ha->iospace = 0;
+       for (i = 0; i < 3; i += 2) {
+               io_handle = &asd_ha->io_handle[i==0?0:1];
+               io_handle->start = pci_resource_start(asd_ha->pcidev, i);
+               io_handle->len   = pci_resource_len(asd_ha->pcidev, i);
+               io_handle->flags = pci_resource_flags(asd_ha->pcidev, i);
+               err = -ENODEV;
+               if (!io_handle->start || !io_handle->len) {
+                       asd_printk("MBAR%d start or length for %s is 0.\n",
+                                  i==0?0:1, pci_name(asd_ha->pcidev));
+                       goto Err;
+               }
+               err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME);
+               if (err) {
+                       asd_printk("couldn't reserve memory region for %s\n",
+                                  pci_name(asd_ha->pcidev));
+                       goto Err;
+               }
+               if (io_handle->flags & IORESOURCE_CACHEABLE)
+                       io_handle->addr = ioremap(io_handle->start,
+                                                 io_handle->len);
+               else
+                       io_handle->addr = ioremap_nocache(io_handle->start,
+                                                         io_handle->len);
+               if (!io_handle->addr) {
+                       asd_printk("couldn't map MBAR%d of %s\n", i==0?0:1,
+                                  pci_name(asd_ha->pcidev));
+                       goto Err_unreq;
+               }
+       }
+
+       return 0;
+Err_unreq:
+       pci_release_region(asd_ha->pcidev, i);
+Err:
+       if (i > 0) {
+               io_handle = &asd_ha->io_handle[0];
+               iounmap(io_handle->addr);
+               pci_release_region(asd_ha->pcidev, 0);
+       }
+       return err;
+}
+
+static void __devexit asd_unmap_memio(struct asd_ha_struct *asd_ha)
+{
+       struct asd_ha_addrspace *io_handle;
+
+       io_handle = &asd_ha->io_handle[1];
+       iounmap(io_handle->addr);
+       pci_release_region(asd_ha->pcidev, 2);
+
+       io_handle = &asd_ha->io_handle[0];
+       iounmap(io_handle->addr);
+       pci_release_region(asd_ha->pcidev, 0);
+}
+
+static int __devinit asd_map_ioport(struct asd_ha_struct *asd_ha)
+{
+       int i = PCI_IOBAR_OFFSET, err;
+       struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0];
+
+       asd_ha->iospace = 1;
+       io_handle->start = pci_resource_start(asd_ha->pcidev, i);
+       io_handle->len   = pci_resource_len(asd_ha->pcidev, i);
+       io_handle->flags = pci_resource_flags(asd_ha->pcidev, i);
+       io_handle->addr  = (void __iomem *) io_handle->start;
+       if (!io_handle->start || !io_handle->len) {
+               asd_printk("couldn't get IO ports for %s\n",
+                          pci_name(asd_ha->pcidev));
+               return -ENODEV;
+       }
+       err = pci_request_region(asd_ha->pcidev, i, ASD_DRIVER_NAME);
+       if (err) {
+               asd_printk("couldn't reserve io space for %s\n",
+                          pci_name(asd_ha->pcidev));
+       }
+
+       return err;
+}
+
+static void __devexit asd_unmap_ioport(struct asd_ha_struct *asd_ha)
+{
+       pci_release_region(asd_ha->pcidev, PCI_IOBAR_OFFSET);
+}
+
+static int __devinit asd_map_ha(struct asd_ha_struct *asd_ha)
+{
+       int err;
+       u16 cmd_reg;
+
+       err = pci_read_config_word(asd_ha->pcidev, PCI_COMMAND, &cmd_reg);
+       if (err) {
+               asd_printk("couldn't read command register of %s\n",
+                          pci_name(asd_ha->pcidev));
+               goto Err;
+       }
+
+       err = -ENODEV;
+       if (cmd_reg & PCI_COMMAND_MEMORY) {
+               if ((err = asd_map_memio(asd_ha)))
+                       goto Err;
+       } else if (cmd_reg & PCI_COMMAND_IO) {
+               if ((err = asd_map_ioport(asd_ha)))
+                       goto Err;
+               asd_printk("%s ioport mapped -- upgrade your hardware\n",
+                          pci_name(asd_ha->pcidev));
+       } else {
+               asd_printk("no proper device access to %s\n",
+                          pci_name(asd_ha->pcidev));
+               goto Err;
+       }
+
+       return 0;
+Err:
+       return err;
+}
+
+static void __devexit asd_unmap_ha(struct asd_ha_struct *asd_ha)
+{
+       if (asd_ha->iospace)
+               asd_unmap_ioport(asd_ha);
+       else
+               asd_unmap_memio(asd_ha);
+}
+
+static const char *asd_dev_rev[30] = {
+       [0] = "A0",
+       [1] = "A1",
+       [8] = "B0",
+};
+
+static int __devinit asd_common_setup(struct asd_ha_struct *asd_ha)
+{
+       int err, i;
+
+       err = pci_read_config_byte(asd_ha->pcidev, PCI_REVISION_ID,
+                                  &asd_ha->revision_id);
+       if (err) {
+               asd_printk("couldn't read REVISION ID register of %s\n",
+                          pci_name(asd_ha->pcidev));
+               goto Err;
+       }
+       err = -ENODEV;
+       if (asd_ha->revision_id < AIC9410_DEV_REV_B0) {
+               asd_printk("%s is revision %s (%X), which is not supported\n",
+                          pci_name(asd_ha->pcidev),
+                          asd_dev_rev[asd_ha->revision_id],
+                          asd_ha->revision_id);
+               goto Err;
+       }
+       /* Provide some sane default values. */
+       asd_ha->hw_prof.max_scbs = 512;
+       asd_ha->hw_prof.max_ddbs = 128;
+       asd_ha->hw_prof.num_phys = ASD_MAX_PHYS;
+       /* All phys are enabled, by default. */
+       asd_ha->hw_prof.enabled_phys = 0xFF;
+       for (i = 0; i < ASD_MAX_PHYS; i++) {
+               asd_ha->hw_prof.phy_desc[i].max_sas_lrate =
+                       SAS_LINK_RATE_3_0_GBPS;
+               asd_ha->hw_prof.phy_desc[i].min_sas_lrate =
+                       SAS_LINK_RATE_1_5_GBPS;
+               asd_ha->hw_prof.phy_desc[i].max_sata_lrate =
+                       SAS_LINK_RATE_1_5_GBPS;
+               asd_ha->hw_prof.phy_desc[i].min_sata_lrate =
+                       SAS_LINK_RATE_1_5_GBPS;
+       }
+
+       return 0;
+Err:
+       return err;
+}
+
+static int __devinit asd_aic9410_setup(struct asd_ha_struct *asd_ha)
+{
+       int err = asd_common_setup(asd_ha);
+
+       if (err)
+               return err;
+
+       asd_ha->hw_prof.addr_range = 8;
+       asd_ha->hw_prof.port_name_base = 0;
+       asd_ha->hw_prof.dev_name_base = 8;
+       asd_ha->hw_prof.sata_name_base = 16;
+
+       return 0;
+}
+
+static int __devinit asd_aic9405_setup(struct asd_ha_struct *asd_ha)
+{
+       int err = asd_common_setup(asd_ha);
+
+       if (err)
+               return err;
+
+       asd_ha->hw_prof.addr_range = 4;
+       asd_ha->hw_prof.port_name_base = 0;
+       asd_ha->hw_prof.dev_name_base = 4;
+       asd_ha->hw_prof.sata_name_base = 8;
+
+       return 0;
+}
+
+static ssize_t asd_show_dev_rev(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+       return snprintf(buf, PAGE_SIZE, "%s\n",
+                       asd_dev_rev[asd_ha->revision_id]);
+}
+static DEVICE_ATTR(revision, S_IRUGO, asd_show_dev_rev, NULL);
+
+static ssize_t asd_show_dev_bios_build(struct device *dev,
+                                      struct device_attribute *attr,char *buf)
+{
+       struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+       return snprintf(buf, PAGE_SIZE, "%d\n", asd_ha->hw_prof.bios.bld);
+}
+static DEVICE_ATTR(bios_build, S_IRUGO, asd_show_dev_bios_build, NULL);
+
+static ssize_t asd_show_dev_pcba_sn(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct asd_ha_struct *asd_ha = dev_to_asd_ha(dev);
+       return snprintf(buf, PAGE_SIZE, "%s\n", asd_ha->hw_prof.pcba_sn);
+}
+static DEVICE_ATTR(pcba_sn, S_IRUGO, asd_show_dev_pcba_sn, NULL);
+
+static void asd_create_dev_attrs(struct asd_ha_struct *asd_ha)
+{
+       device_create_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+       device_create_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
+       device_create_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+}
+
+static void asd_remove_dev_attrs(struct asd_ha_struct *asd_ha)
+{
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_revision);
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_bios_build);
+       device_remove_file(&asd_ha->pcidev->dev, &dev_attr_pcba_sn);
+}
+
+/* The first entry, 0, is used for dynamic ids, the rest for devices
+ * we know about.
+ */
+static struct asd_pcidev_struct {
+       const char * name;
+       int (*setup)(struct asd_ha_struct *asd_ha);
+} asd_pcidev_data[] = {
+       /* Id 0 is used for dynamic ids. */
+       { .name  = "Adaptec AIC-94xx SAS/SATA Host Adapter",
+         .setup = asd_aic9410_setup
+       },
+       { .name  = "Adaptec AIC-9410W SAS/SATA Host Adapter",
+         .setup = asd_aic9410_setup
+       },
+       { .name  = "Adaptec AIC-9405W SAS/SATA Host Adapter",
+         .setup = asd_aic9405_setup
+       },
+};
+
+static inline int asd_create_ha_caches(struct asd_ha_struct *asd_ha)
+{
+       asd_ha->scb_pool = dma_pool_create(ASD_DRIVER_NAME "_scb_pool",
+                                          &asd_ha->pcidev->dev,
+                                          sizeof(struct scb),
+                                          8, 0);
+       if (!asd_ha->scb_pool) {
+               asd_printk("couldn't create scb pool\n");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+/**
+ * asd_free_edbs -- free empty data buffers
+ * asd_ha: pointer to host adapter structure
+ */
+static inline void asd_free_edbs(struct asd_ha_struct *asd_ha)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       int i;
+
+       for (i = 0; i < seq->num_edbs; i++)
+               asd_free_coherent(asd_ha, seq->edb_arr[i]);
+       kfree(seq->edb_arr);
+       seq->edb_arr = NULL;
+}
+
+static inline void asd_free_escbs(struct asd_ha_struct *asd_ha)
+{
+       struct asd_seq_data *seq = &asd_ha->seq;
+       int i;
+
+       for (i = 0; i < seq->num_escbs; i++) {
+               if (!list_empty(&seq->escb_arr[i]->list))
+                       list_del_init(&seq->escb_arr[i]->list);
+
+               asd_ascb_free(seq->escb_arr[i]);
+       }
+       kfree(seq->escb_arr);
+       seq->escb_arr = NULL;
+}
+
+static inline void asd_destroy_ha_caches(struct asd_ha_struct *asd_ha)
+{
+       int i;
+
+       if (asd_ha->hw_prof.ddb_ext)
+               asd_free_coherent(asd_ha, asd_ha->hw_prof.ddb_ext);
+       if (asd_ha->hw_prof.scb_ext)
+               asd_free_coherent(asd_ha, asd_ha->hw_prof.scb_ext);
+
+       if (asd_ha->hw_prof.ddb_bitmap)
+               kfree(asd_ha->hw_prof.ddb_bitmap);
+       asd_ha->hw_prof.ddb_bitmap = NULL;
+
+       for (i = 0; i < ASD_MAX_PHYS; i++) {
+               struct asd_phy *phy = &asd_ha->phys[i];
+
+               asd_free_coherent(asd_ha, phy->id_frm_tok);
+       }
+       if (asd_ha->seq.escb_arr)
+               asd_free_escbs(asd_ha);
+       if (asd_ha->seq.edb_arr)
+               asd_free_edbs(asd_ha);
+       if (asd_ha->hw_prof.ue.area) {
+               kfree(asd_ha->hw_prof.ue.area);
+               asd_ha->hw_prof.ue.area = NULL;
+       }
+       if (asd_ha->seq.tc_index_array) {
+               kfree(asd_ha->seq.tc_index_array);
+               kfree(asd_ha->seq.tc_index_bitmap);
+               asd_ha->seq.tc_index_array = NULL;
+               asd_ha->seq.tc_index_bitmap = NULL;
+       }
+       if (asd_ha->seq.actual_dl) {
+                       asd_free_coherent(asd_ha, asd_ha->seq.actual_dl);
+                       asd_ha->seq.actual_dl = NULL;
+                       asd_ha->seq.dl = NULL;
+       }
+       if (asd_ha->seq.next_scb.vaddr) {
+               dma_pool_free(asd_ha->scb_pool, asd_ha->seq.next_scb.vaddr,
+                             asd_ha->seq.next_scb.dma_handle);
+               asd_ha->seq.next_scb.vaddr = NULL;
+       }
+       dma_pool_destroy(asd_ha->scb_pool);
+       asd_ha->scb_pool = NULL;
+}
+
+kmem_cache_t *asd_dma_token_cache;
+kmem_cache_t *asd_ascb_cache;
+
+static int asd_create_global_caches(void)
+{
+       if (!asd_dma_token_cache) {
+               asd_dma_token_cache
+                       = kmem_cache_create(ASD_DRIVER_NAME "_dma_token",
+                                           sizeof(struct asd_dma_tok),
+                                           0,
+                                           SLAB_HWCACHE_ALIGN,
+                                           NULL, NULL);
+               if (!asd_dma_token_cache) {
+                       asd_printk("couldn't create dma token cache\n");
+                       return -ENOMEM;
+               }
+       }
+
+       if (!asd_ascb_cache) {
+               asd_ascb_cache = kmem_cache_create(ASD_DRIVER_NAME "_ascb",
+                                                  sizeof(struct asd_ascb),
+                                                  0,
+                                                  SLAB_HWCACHE_ALIGN,
+                                                  NULL, NULL);
+               if (!asd_ascb_cache) {
+                       asd_printk("couldn't create ascb cache\n");
+                       goto Err;
+               }
+       }
+
+       return 0;
+Err:
+       kmem_cache_destroy(asd_dma_token_cache);
+       asd_dma_token_cache = NULL;
+       return -ENOMEM;
+}
+
+static void asd_destroy_global_caches(void)
+{
+       if (asd_dma_token_cache)</