mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
Merge mulgrave-w:git/scsi-misc-2.6
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>
This commit is contained in:
commit
c9802cd957
56
Documentation/scsi/ChangeLog.arcmsr
Normal file
56
Documentation/scsi/ChangeLog.arcmsr
Normal file
@ -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
|
||||
**************************************************************************
|
574
Documentation/scsi/arcmsr_spec.txt
Normal file
574
Documentation/scsi/arcmsr_spec.txt
Normal file
@ -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
|
||||
**************************************************************************
|
484
Documentation/scsi/libsas.txt
Normal file
484
Documentation/scsi/libsas.txt
Normal file
@ -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.
|
@ -847,6 +847,35 @@ struct request *blk_queue_find_tag(request_queue_t *q, int tag)
|
||||
|
||||
EXPORT_SYMBOL(blk_queue_find_tag);
|
||||
|
||||
/**
|
||||
* __blk_free_tags - release a given set of tag maintenance info
|
||||
* @bqt: the tag map to free
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
int retval;
|
||||
|
||||
retval = atomic_dec_and_test(&bqt->refcnt);
|
||||
if (retval) {
|
||||
BUG_ON(bqt->busy);
|
||||
BUG_ON(!list_empty(&bqt->busy_list));
|
||||
|
||||
kfree(bqt->tag_index);
|
||||
bqt->tag_index = NULL;
|
||||
|
||||
kfree(bqt->tag_map);
|
||||
bqt->tag_map = NULL;
|
||||
|
||||
kfree(bqt);
|
||||
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* __blk_queue_free_tags - release tag maintenance info
|
||||
* @q: the request queue for the device
|
||||
@ -862,23 +891,28 @@ static void __blk_queue_free_tags(request_queue_t *q)
|
||||
if (!bqt)
|
||||
return;
|
||||
|
||||
if (atomic_dec_and_test(&bqt->refcnt)) {
|
||||
BUG_ON(bqt->busy);
|
||||
BUG_ON(!list_empty(&bqt->busy_list));
|
||||
|
||||
kfree(bqt->tag_index);
|
||||
bqt->tag_index = NULL;
|
||||
|
||||
kfree(bqt->tag_map);
|
||||
bqt->tag_map = NULL;
|
||||
|
||||
kfree(bqt);
|
||||
}
|
||||
__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);
|
||||
tags = __blk_queue_init_tags(q, depth);
|
||||
|
||||
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);
|
||||
} else if (q->queue_tags) {
|
||||
if ((rc = blk_queue_resize_tags(q, depth)))
|
||||
return rc;
|
||||
@ -1001,6 +1061,13 @@ int blk_queue_resize_tags(request_queue_t *q, int new_depth)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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(¤tsd[ncurrent].scsi3addr[0],
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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*/
|
||||
|
@ -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;
|
||||
|
||||
fc_host_node_name(ioc->sh) =
|
||||
(u64)ioc->fc_port_page0[portnum].WWNN.High << 32
|
||||
| (u64)ioc->fc_port_page0[portnum].WWNN.Low;
|
||||
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;
|
||||
|
||||
fc_host_port_name(ioc->sh) =
|
||||
(u64)ioc->fc_port_page0[portnum].WWPN.High << 32
|
||||
| (u64)ioc->fc_port_page0[portnum].WWPN.Low;
|
||||
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;
|
||||
|
||||
fc_host_port_id(ioc->sh) = ioc->fc_port_page0[portnum].PortIdentifier;
|
||||
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;
|
||||
|
||||
fc_host_supported_classes(ioc->sh) = cos;
|
||||
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_tgtid_bind_type(ioc->sh) = FC_TGTID_BIND_BY_WWPN;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -13,10 +13,6 @@
|
||||
|
||||
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
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -13,10 +13,6 @@
|
||||
|
||||
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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -40,8 +40,10 @@
|
||||
#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
|
||||
|
@ -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);
|
||||
|
@ -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 = {
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
@ -423,13 +445,6 @@ int aac_rx_init(struct aac_dev *dev)
|
||||
printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
|
||||
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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -253,6 +253,7 @@
|
||||
#include <linux/isapnp.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/list.h>
|
||||
#include <asm/semaphore.h>
|
||||
#include <scsi/scsicam.h>
|
||||
|
||||
@ -262,6 +263,8 @@
|
||||
#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;
|
||||
|
||||
for(i=0; i<ARRAY_SIZE(setup); i++) {
|
||||
aha152x_release(aha152x_host[i]);
|
||||
aha152x_host[i]=NULL;
|
||||
list_for_each_entry(hd, &aha152x_host_list, host_list) {
|
||||
struct Scsi_Host *shost = container_of((void *)hd, struct Scsi_Host, hostdata);
|
||||
|
||||
aha152x_release(shost);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 *,
|
||||
|
@ -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"
|
||||
|
@ -249,8 +249,6 @@
|
||||
#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
|
||||
|
41
drivers/scsi/aic94xx/Kconfig
Normal file
41
drivers/scsi/aic94xx/Kconfig
Normal file
@ -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.
|
39
drivers/scsi/aic94xx/Makefile
Normal file
39
drivers/scsi/aic94xx/Makefile
Normal file
@ -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
|
114
drivers/scsi/aic94xx/aic94xx.h
Normal file
114
drivers/scsi/aic94xx/aic94xx.h
Normal file
@ -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
|
353
drivers/scsi/aic94xx/aic94xx_dev.c
Normal file
353
drivers/scsi/aic94xx/aic94xx_dev.c
Normal file
@ -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;
|
||||
}
|
959
drivers/scsi/aic94xx/aic94xx_dump.c
Normal file
959
drivers/scsi/aic94xx/aic94xx_dump.c
Normal file
@ -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 */
|
52
drivers/scsi/aic94xx/aic94xx_dump.h
Normal file
52
drivers/scsi/aic94xx/aic94xx_dump.h
Normal file
@ -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_ */
|
1376
drivers/scsi/aic94xx/aic94xx_hwi.c
Normal file
1376
drivers/scsi/aic94xx/aic94xx_hwi.c
Normal file
File diff suppressed because it is too large
Load Diff
397
drivers/scsi/aic94xx/aic94xx_hwi.h
Normal file
397
drivers/scsi/aic94xx/aic94xx_hwi.h
Normal file
@ -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
|
866
drivers/scsi/aic94xx/aic94xx_init.c
Normal file
866
drivers/scsi/aic94xx/aic94xx_init.c
Normal file
@ -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)
|
||||
kmem_cache_destroy(asd_dma_token_cache);
|
||||
asd_dma_token_cache = NULL;
|
||||
|
||||
if (asd_ascb_cache)
|
||||
kmem_cache_destroy(asd_ascb_cache);
|
||||
asd_ascb_cache = NULL;
|
||||
}
|
||||
|
||||
static int asd_register_sas_ha(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int i;
|
||||
struct asd_sas_phy **sas_phys =
|
||||
kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_phy), GFP_KERNEL);
|
||||
struct asd_sas_port **sas_ports =
|
||||
kmalloc(ASD_MAX_PHYS * sizeof(struct asd_sas_port), GFP_KERNEL);
|
||||
|
||||
if (!sas_phys || !sas_ports) {
|
||||
kfree(sas_phys);
|
||||
kfree(sas_ports);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
asd_ha->sas_ha.sas_ha_name = (char *) asd_ha->name;
|
||||
asd_ha->sas_ha.lldd_module = THIS_MODULE;
|
||||
asd_ha->sas_ha.sas_addr = &asd_ha->hw_prof.sas_addr[0];
|
||||
|
||||
for (i = 0; i < ASD_MAX_PHYS; i++) {
|
||||
sas_phys[i] = &asd_ha->phys[i].sas_phy;
|
||||
sas_ports[i] = &asd_ha->ports[i];
|
||||
}
|
||||
|
||||
asd_ha->sas_ha.sas_phy = sas_phys;
|
||||
asd_ha->sas_ha.sas_port= sas_ports;
|
||||
asd_ha->sas_ha.num_phys= ASD_MAX_PHYS;
|
||||
|
||||
asd_ha->sas_ha.lldd_queue_size = asd_ha->seq.can_queue;
|
||||
|
||||
return sas_register_ha(&asd_ha->sas_ha);
|
||||
}
|
||||
|
||||
static int asd_unregister_sas_ha(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = sas_unregister_ha(&asd_ha->sas_ha);
|
||||
|
||||
sas_remove_host(asd_ha->sas_ha.core.shost);
|
||||
scsi_remove_host(asd_ha->sas_ha.core.shost);
|
||||
scsi_host_put(asd_ha->sas_ha.core.shost);
|
||||
|
||||
kfree(asd_ha->sas_ha.sas_phy);
|
||||
kfree(asd_ha->sas_ha.sas_port);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit asd_pci_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
struct asd_pcidev_struct *asd_dev;
|
||||
unsigned asd_id = (unsigned) id->driver_data;
|
||||
struct asd_ha_struct *asd_ha;
|
||||
struct Scsi_Host *shost;
|
||||
int err;
|
||||
|
||||
if (asd_id >= ARRAY_SIZE(asd_pcidev_data)) {
|
||||
asd_printk("wrong driver_data in PCI table\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if ((err = pci_enable_device(dev))) {
|
||||
asd_printk("couldn't enable device %s\n", pci_name(dev));
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
err = -ENOMEM;
|
||||
|
||||
shost = scsi_host_alloc(&aic94xx_sht, sizeof(void *));
|
||||
if (!shost)
|
||||
goto Err;
|
||||
|
||||
asd_dev = &asd_pcidev_data[asd_id];
|
||||
|
||||
asd_ha = kzalloc(sizeof(*asd_ha), GFP_KERNEL);
|
||||
if (!asd_ha) {
|
||||
asd_printk("out of memory\n");
|
||||
goto Err;
|
||||
}
|
||||
asd_ha->pcidev = dev;
|
||||
asd_ha->sas_ha.pcidev = asd_ha->pcidev;
|
||||
asd_ha->sas_ha.lldd_ha = asd_ha;
|
||||
|
||||
asd_ha->name = asd_dev->name;
|
||||
asd_printk("found %s, device %s\n", asd_ha->name, pci_name(dev));
|
||||
|
||||
SHOST_TO_SAS_HA(shost) = &asd_ha->sas_ha;
|
||||
asd_ha->sas_ha.core.shost = shost;
|
||||
shost->transportt = aic94xx_transport_template;
|
||||
shost->max_id = ~0;
|
||||
shost->max_lun = ~0;
|
||||
shost->max_cmd_len = 16;
|
||||
|
||||
err = scsi_add_host(shost, &dev->dev);
|
||||
if (err) {
|
||||
scsi_host_put(shost);
|
||||
goto Err_free;
|
||||
}
|
||||
|
||||
|
||||
|
||||
err = asd_dev->setup(asd_ha);
|
||||
if (err)
|
||||
goto Err_free;
|
||||
|
||||
err = -ENODEV;
|
||||
if (!pci_set_dma_mask(dev, DMA_64BIT_MASK)
|
||||
&& !pci_set_consistent_dma_mask(dev, DMA_64BIT_MASK))
|
||||
;
|
||||
else if (!pci_set_dma_mask(dev, DMA_32BIT_MASK)
|
||||
&& !pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK))
|
||||
;
|
||||
else {
|
||||
asd_printk("no suitable DMA mask for %s\n", pci_name(dev));
|
||||
goto Err_free;
|
||||
}
|
||||
|
||||
pci_set_drvdata(dev, asd_ha);
|
||||
|
||||
err = asd_map_ha(asd_ha);
|
||||
if (err)
|
||||
goto Err_free;
|
||||
|
||||
err = asd_create_ha_caches(asd_ha);
|
||||
if (err)
|
||||
goto Err_unmap;
|
||||
|
||||
err = asd_init_hw(asd_ha);
|
||||
if (err)
|
||||
goto Err_free_cache;
|
||||
|
||||
asd_printk("device %s: SAS addr %llx, PCBA SN %s, %d phys, %d enabled "
|
||||
"phys, flash %s, BIOS %s%d\n",
|
||||
pci_name(dev), SAS_ADDR(asd_ha->hw_prof.sas_addr),
|
||||
asd_ha->hw_prof.pcba_sn, asd_ha->hw_prof.max_phys,
|
||||
asd_ha->hw_prof.num_phys,
|
||||
asd_ha->hw_prof.flash.present ? "present" : "not present",
|
||||
asd_ha->hw_prof.bios.present ? "build " : "not present",
|
||||
asd_ha->hw_prof.bios.bld);
|
||||
|
||||
shost->can_queue = asd_ha->seq.can_queue;
|
||||
|
||||
if (use_msi)
|
||||
pci_enable_msi(asd_ha->pcidev);
|
||||
|
||||
err = request_irq(asd_ha->pcidev->irq, asd_hw_isr, SA_SHIRQ,
|
||||
ASD_DRIVER_NAME, asd_ha);
|
||||
if (err) {
|
||||
asd_printk("couldn't get irq %d for %s\n",
|
||||
asd_ha->pcidev->irq, pci_name(asd_ha->pcidev));
|
||||
goto Err_irq;
|
||||
}
|
||||
asd_enable_ints(asd_ha);
|
||||
|
||||
err = asd_init_post_escbs(asd_ha);
|
||||
if (err) {
|
||||
asd_printk("couldn't post escbs for %s\n",
|
||||
pci_name(asd_ha->pcidev));
|
||||
goto Err_escbs;
|
||||
}
|
||||
ASD_DPRINTK("escbs posted\n");
|
||||
|
||||
asd_create_dev_attrs(asd_ha);
|
||||
|
||||
err = asd_register_sas_ha(asd_ha);
|
||||
if (err)
|
||||
goto Err_reg_sas;
|
||||
|
||||
err = asd_enable_phys(asd_ha, asd_ha->hw_prof.enabled_phys);
|
||||
if (err) {
|
||||
asd_printk("coudln't enable phys, err:%d\n", err);
|
||||
goto Err_en_phys;
|
||||
}
|
||||
ASD_DPRINTK("enabled phys\n");
|
||||
/* give the phy enabling interrupt event time to come in (1s
|
||||
* is empirically about all it takes) */
|
||||
ssleep(1);
|
||||
/* Wait for discovery to finish */
|
||||
scsi_flush_work(asd_ha->sas_ha.core.shost);
|
||||
|
||||
return 0;
|
||||
Err_en_phys:
|
||||
asd_unregister_sas_ha(asd_ha);
|
||||
Err_reg_sas:
|
||||
asd_remove_dev_attrs(asd_ha);
|
||||
Err_escbs:
|
||||
asd_disable_ints(asd_ha);
|
||||
free_irq(dev->irq, asd_ha);
|
||||
Err_irq:
|
||||
if (use_msi)
|
||||
pci_disable_msi(dev);
|
||||
asd_chip_hardrst(asd_ha);
|
||||
Err_free_cache:
|
||||
asd_destroy_ha_caches(asd_ha);
|
||||
Err_unmap:
|
||||
asd_unmap_ha(asd_ha);
|
||||
Err_free:
|
||||
kfree(asd_ha);
|
||||
scsi_remove_host(shost);
|
||||
Err:
|
||||
pci_disable_device(dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void asd_free_queues(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
unsigned long flags;
|
||||
LIST_HEAD(pending);
|
||||
struct list_head *n, *pos;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
|
||||
asd_ha->seq.pending = 0;
|
||||
list_splice_init(&asd_ha->seq.pend_q, &pending);
|
||||
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
|
||||
|
||||
if (!list_empty(&pending))
|
||||
ASD_DPRINTK("Uh-oh! Pending is not empty!\n");
|
||||
|
||||
list_for_each_safe(pos, n, &pending) {
|
||||
struct asd_ascb *ascb = list_entry(pos, struct asd_ascb, list);
|
||||
list_del_init(pos);
|
||||
ASD_DPRINTK("freeing from pending\n");
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
}
|
||||
|
||||
static void asd_turn_off_leds(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
u8 phy_mask = asd_ha->hw_prof.enabled_phys;
|
||||
u8 i;
|
||||
|
||||
for_each_phy(phy_mask, phy_mask, i) {
|
||||
asd_turn_led(asd_ha, i, 0);
|
||||
asd_control_led(asd_ha, i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void __devexit asd_pci_remove(struct pci_dev *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = pci_get_drvdata(dev);
|
||||
|
||||
if (!asd_ha)
|
||||
return;
|
||||
|
||||
asd_unregister_sas_ha(asd_ha);
|
||||
|
||||
asd_disable_ints(asd_ha);
|
||||
|
||||
asd_remove_dev_attrs(asd_ha);
|
||||
|
||||
/* XXX more here as needed */
|
||||
|
||||
free_irq(dev->irq, asd_ha);
|
||||
if (use_msi)
|
||||
pci_disable_msi(asd_ha->pcidev);
|
||||
asd_turn_off_leds(asd_ha);
|
||||
asd_chip_hardrst(asd_ha);
|
||||
asd_free_queues(asd_ha);
|
||||
asd_destroy_ha_caches(asd_ha);
|
||||
asd_unmap_ha(asd_ha);
|
||||
kfree(asd_ha);
|
||||
pci_disable_device(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
static ssize_t asd_version_show(struct device_driver *driver, char *buf)
|
||||
{
|
||||
return snprintf(buf, PAGE_SIZE, "%s\n", ASD_DRIVER_VERSION);
|
||||
}
|
||||
static DRIVER_ATTR(version, S_IRUGO, asd_version_show, NULL);
|
||||
|
||||
static void asd_create_driver_attrs(struct device_driver *driver)
|
||||
{
|
||||
driver_create_file(driver, &driver_attr_version);
|
||||
}
|
||||
|
||||
static void asd_remove_driver_attrs(struct device_driver *driver)
|
||||
{
|
||||
driver_remove_file(driver, &driver_attr_version);
|
||||
}
|
||||
|
||||
static struct sas_domain_function_template aic94xx_transport_functions = {
|
||||
.lldd_port_formed = asd_update_port_links,
|
||||
|
||||
.lldd_dev_found = asd_dev_found,
|
||||
.lldd_dev_gone = asd_dev_gone,
|
||||
|
||||
.lldd_execute_task = asd_execute_task,
|
||||
|
||||
.lldd_abort_task = asd_abort_task,
|
||||
.lldd_abort_task_set = asd_abort_task_set,
|
||||
.lldd_clear_aca = asd_clear_aca,
|
||||
.lldd_clear_task_set = asd_clear_task_set,
|
||||
.lldd_I_T_nexus_reset = NULL,
|
||||
.lldd_lu_reset = asd_lu_reset,
|
||||
.lldd_query_task = asd_query_task,
|
||||
|
||||
.lldd_clear_nexus_port = asd_clear_nexus_port,
|
||||
.lldd_clear_nexus_ha = asd_clear_nexus_ha,
|
||||
|
||||
.lldd_control_phy = asd_control_phy,
|
||||
};
|
||||
|
||||
static const struct pci_device_id aic94xx_pci_table[] __devinitdata = {
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR10),
|
||||
0, 0, 1},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR12),
|
||||
0, 0, 1},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR1E),
|
||||
0, 0, 1},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR30),
|
||||
0, 0, 2},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR32),
|
||||
0, 0, 2},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3E),
|
||||
0, 0, 2},
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_RAZOR3F),
|
||||
0, 0, 2},
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, aic94xx_pci_table);
|
||||
|
||||
static struct pci_driver aic94xx_pci_driver = {
|
||||
.name = ASD_DRIVER_NAME,
|
||||
.id_table = aic94xx_pci_table,
|
||||
.probe = asd_pci_probe,
|
||||
.remove = __devexit_p(asd_pci_remove),
|
||||
};
|
||||
|
||||
static int __init aic94xx_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
|
||||
asd_printk("%s version %s loaded\n", ASD_DRIVER_DESCRIPTION,
|
||||
ASD_DRIVER_VERSION);
|
||||
|
||||
err = asd_create_global_caches();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
aic94xx_transport_template =
|
||||
sas_domain_attach_transport(&aic94xx_transport_functions);
|
||||
if (!aic94xx_transport_template)
|
||||
goto out_destroy_caches;
|
||||
|
||||
err = pci_register_driver(&aic94xx_pci_driver);
|
||||
if (err)
|
||||
goto out_release_transport;
|
||||
|
||||
asd_create_driver_attrs(&aic94xx_pci_driver.driver);
|
||||
|
||||
return err;
|
||||
|
||||
out_release_transport:
|
||||
sas_release_transport(aic94xx_transport_template);
|
||||
out_destroy_caches:
|
||||
asd_destroy_global_caches();
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit aic94xx_exit(void)
|
||||
{
|
||||
asd_remove_driver_attrs(&aic94xx_pci_driver.driver);
|
||||
pci_unregister_driver(&aic94xx_pci_driver);
|
||||
sas_release_transport(aic94xx_transport_template);
|
||||
asd_destroy_global_caches();
|
||||
asd_printk("%s version %s unloaded\n", ASD_DRIVER_DESCRIPTION,
|
||||
ASD_DRIVER_VERSION);
|
||||
}
|
||||
|
||||
module_init(aic94xx_init);
|
||||
module_exit(aic94xx_exit);
|
||||
|
||||
MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
|
||||
MODULE_DESCRIPTION(ASD_DRIVER_DESCRIPTION);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_VERSION(ASD_DRIVER_VERSION);
|
332
drivers/scsi/aic94xx/aic94xx_reg.c
Normal file
332
drivers/scsi/aic94xx/aic94xx_reg.c
Normal file
@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver register access.
|
||||
*
|
||||
* 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 "aic94xx_reg.h"
|
||||
#include "aic94xx.h"
|
||||
|
||||
/* Writing to device address space.
|
||||
* Offset comes before value to remind that the operation of
|
||||
* this function is *offs = val.
|
||||
*/
|
||||
static inline void asd_write_byte(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u8 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outb(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writeb(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline void asd_write_word(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u16 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outw(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writew(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
static inline void asd_write_dword(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs, u32 val)
|
||||
{
|
||||
if (unlikely(asd_ha->iospace))
|
||||
outl(val,
|
||||
(unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
|
||||
else
|
||||
writel(val, asd_ha->io_handle[0].addr + offs);
|
||||
wmb();
|
||||
}
|
||||
|
||||
/* Reading from device address space.
|
||||
*/
|
||||
static inline u8 asd_read_byte(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u8 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inb((unsigned long) asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readb(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u16 asd_read_word(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u16 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inw((unsigned long)asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readw(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 asd_read_dword(struct asd_ha_struct *asd_ha,
|
||||
unsigned long offs)
|
||||
{
|
||||
u32 val;
|
||||
if (unlikely(asd_ha->iospace))
|
||||
val = inl((unsigned long) asd_ha->io_handle[0].addr
|
||||
+ (offs & 0xFF));
|
||||
else
|
||||
val = readl(asd_ha->io_handle[0].addr + offs);
|
||||
rmb();
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swa(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swc(void)
|
||||
{
|
||||
return asd_mem_offs_swa() + MBAR0_SWA_SIZE;
|
||||
}
|
||||
|
||||
static inline u32 asd_mem_offs_swb(void)
|
||||
{
|
||||
return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20;
|
||||
}
|
||||
|
||||
/* We know that the register wanted is in the range
|
||||
* of the sliding window.
|
||||
*/
|
||||
#define ASD_READ_SW(ww, type, ord) \
|
||||
static inline type asd_read_##ww##_##ord (struct asd_ha_struct *asd_ha,\
|
||||
u32 reg) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \
|
||||
u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
|
||||
return asd_read_##ord (asd_ha, (unsigned long) map_offs); \
|
||||
}
|
||||
|
||||
#define ASD_WRITE_SW(ww, type, ord) \
|
||||
static inline void asd_write_##ww##_##ord (struct asd_ha_struct *asd_ha,\
|
||||
u32 reg, type val) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \
|
||||
u32 map_offs=(reg - io_handle-> ww##_base )+asd_mem_offs_##ww ();\
|
||||
asd_write_##ord (asd_ha, (unsigned long) map_offs, val); \
|
||||
}
|
||||
|
||||
ASD_READ_SW(swa, u8, byte);
|
||||
ASD_READ_SW(swa, u16, word);
|
||||
ASD_READ_SW(swa, u32, dword);
|
||||
|
||||
ASD_READ_SW(swb, u8, byte);
|
||||
ASD_READ_SW(swb, u16, word);
|
||||
ASD_READ_SW(swb, u32, dword);
|
||||
|
||||
ASD_READ_SW(swc, u8, byte);
|
||||
ASD_READ_SW(swc, u16, word);
|
||||
ASD_READ_SW(swc, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swa, u8, byte);
|
||||
ASD_WRITE_SW(swa, u16, word);
|
||||
ASD_WRITE_SW(swa, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swb, u8, byte);
|
||||
ASD_WRITE_SW(swb, u16, word);
|
||||
ASD_WRITE_SW(swb, u32, dword);
|
||||
|
||||
ASD_WRITE_SW(swc, u8, byte);
|
||||
ASD_WRITE_SW(swc, u16, word);
|
||||
ASD_WRITE_SW(swc, u32, dword);
|
||||
|
||||
/*
|
||||
* A word about sliding windows:
|
||||
* MBAR0 is divided into sliding windows A, C and B, in that order.
|
||||
* SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes.
|
||||
* SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes.
|
||||
* From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F.
|
||||
* SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0.
|
||||
* See asd_init_sw() in aic94xx_hwi.c
|
||||
*
|
||||
* We map the most common registers we'd access of the internal 4GB
|
||||
* host adapter memory space. If a register/internal memory location
|
||||
* is wanted which is not mapped, we slide SWB, by paging it,
|
||||
* see asd_move_swb() in aic94xx_reg.c.
|
||||
*/
|
||||
|
||||
/**
|
||||
* asd_move_swb -- move sliding window B
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @reg: register desired to be within range of the new window
|
||||
*/
|
||||
static inline void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg)
|
||||
{
|
||||
u32 base = reg & ~(MBAR0_SWB_SIZE-1);
|
||||
pci_write_config_dword(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, base);
|
||||
asd_ha->io_handle[0].swb_base = base;
|
||||
}
|
||||
|
||||
static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
|
||||
if (io_handle->swa_base <= reg
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE)
|
||||
asd_write_swa_byte (asd_ha, reg,val);
|
||||
else if (io_handle->swb_base <= reg
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE)
|
||||
asd_write_swb_byte (asd_ha, reg, val);
|
||||
else if (io_handle->swc_base <= reg
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE)
|
||||
asd_write_swc_byte (asd_ha, reg, val);
|
||||
else {
|
||||
/* Ok, we have to move SWB */
|
||||
asd_move_swb(asd_ha, reg);
|
||||
asd_write_swb_byte (asd_ha, reg, val);
|
||||
}
|
||||
}
|
||||
|
||||
#define ASD_WRITE_REG(type, ord) \
|
||||
void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
|
||||
unsigned long flags; \
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags); \
|
||||
if (io_handle->swa_base <= reg \
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE) \
|
||||
asd_write_swa_##ord (asd_ha, reg,val); \
|
||||
else if (io_handle->swb_base <= reg \
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE) \
|
||||
asd_write_swb_##ord (asd_ha, reg, val); \
|
||||
else if (io_handle->swc_base <= reg \
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE) \
|
||||
asd_write_swc_##ord (asd_ha, reg, val); \
|
||||
else { \
|
||||
/* Ok, we have to move SWB */ \
|
||||
asd_move_swb(asd_ha, reg); \
|
||||
asd_write_swb_##ord (asd_ha, reg, val); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags); \
|
||||
}
|
||||
|
||||
ASD_WRITE_REG(u8, byte);
|
||||
ASD_WRITE_REG(u16,word);
|
||||
ASD_WRITE_REG(u32,dword);
|
||||
|
||||
static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg)
|
||||
{
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0];
|
||||
u8 val;
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR);
|
||||
if (io_handle->swa_base <= reg
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE)
|
||||
val = asd_read_swa_byte (asd_ha, reg);
|
||||
else if (io_handle->swb_base <= reg
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE)
|
||||
val = asd_read_swb_byte (asd_ha, reg);
|
||||
else if (io_handle->swc_base <= reg
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE)
|
||||
val = asd_read_swc_byte (asd_ha, reg);
|
||||
else {
|
||||
/* Ok, we have to move SWB */
|
||||
asd_move_swb(asd_ha, reg);
|
||||
val = asd_read_swb_byte (asd_ha, reg);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
#define ASD_READ_REG(type, ord) \
|
||||
type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \
|
||||
type val; \
|
||||
unsigned long flags; \
|
||||
BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags); \
|
||||
if (io_handle->swa_base <= reg \
|
||||
&& reg < io_handle->swa_base + MBAR0_SWA_SIZE) \
|
||||
val = asd_read_swa_##ord (asd_ha, reg); \
|
||||
else if (io_handle->swb_base <= reg \
|
||||
&& reg < io_handle->swb_base + MBAR0_SWB_SIZE) \
|
||||
val = asd_read_swb_##ord (asd_ha, reg); \
|
||||
else if (io_handle->swc_base <= reg \
|
||||
&& reg < io_handle->swc_base + MBAR0_SWC_SIZE) \
|
||||
val = asd_read_swc_##ord (asd_ha, reg); \
|
||||
else { \
|
||||
/* Ok, we have to move SWB */ \
|
||||
asd_move_swb(asd_ha, reg); \
|
||||
val = asd_read_swb_##ord (asd_ha, reg); \
|
||||
} \
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
ASD_READ_REG(u8, byte);
|
||||
ASD_READ_REG(u16,word);
|
||||
ASD_READ_REG(u32,dword);
|
||||
|
||||
/**
|
||||
* asd_read_reg_string -- read a string of bytes from io space memory
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @dst: pointer to a destination buffer where data will be written to
|
||||
* @offs: start offset (register) to read from
|
||||
* @count: number of bytes to read
|
||||
*/
|
||||
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
|
||||
u32 offs, int count)
|
||||
{
|
||||
u8 *p = dst;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags);
|
||||
for ( ; count > 0; count--, offs++, p++)
|
||||
*p = __asd_read_reg_byte(asd_ha, offs);
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_write_reg_string -- write a string of bytes to io space memory
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @src: pointer to source buffer where data will be read from
|
||||
* @offs: start offset (register) to write to
|
||||
* @count: number of bytes to write
|
||||
*/
|
||||
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
|
||||
u32 offs, int count)
|
||||
{
|
||||
u8 *p = src;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->iolock, flags);
|
||||
for ( ; count > 0; count--, offs++, p++)
|
||||
__asd_write_reg_byte(asd_ha, offs, *p);
|
||||
spin_unlock_irqrestore(&asd_ha->iolock, flags);
|
||||
}
|
302
drivers/scsi/aic94xx/aic94xx_reg.h
Normal file
302
drivers/scsi/aic94xx/aic94xx_reg.h
Normal file
@ -0,0 +1,302 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver hardware registers definitions.
|
||||
*
|
||||
* 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_REG_H_
|
||||
#define _AIC94XX_REG_H_
|
||||
|
||||
#include <asm/io.h>
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
/* Values */
|
||||
#define AIC9410_DEV_REV_B0 0x8
|
||||
|
||||
/* MBAR0, SWA, SWB, SWC, internal memory space addresses */
|
||||
#define REG_BASE_ADDR 0xB8000000
|
||||
#define REG_BASE_ADDR_CSEQCIO 0xB8002000
|
||||
#define REG_BASE_ADDR_EXSI 0xB8042800
|
||||
|
||||
#define MBAR0_SWA_SIZE 0x58
|
||||
extern u32 MBAR0_SWB_SIZE;
|
||||
#define MBAR0_SWC_SIZE 0x8
|
||||
|
||||
/* MBAR1, points to On Chip Memory */
|
||||
#define OCM_BASE_ADDR 0xA0000000
|
||||
#define OCM_MAX_SIZE 0x20000
|
||||
|
||||
/* Smallest address possible to reference */
|
||||
#define ALL_BASE_ADDR OCM_BASE_ADDR
|
||||
|
||||
/* PCI configuration space registers */
|
||||
#define PCI_IOBAR_OFFSET 4
|
||||
|
||||
#define PCI_CONF_MBAR1 0x6C
|
||||
#define PCI_CONF_MBAR0_SWA 0x70
|
||||
#define PCI_CONF_MBAR0_SWB 0x74
|
||||
#define PCI_CONF_MBAR0_SWC 0x78
|
||||
#define PCI_CONF_MBAR_KEY 0x7C
|
||||
#define PCI_CONF_FLSH_BAR 0xB8
|
||||
|
||||
#include "aic94xx_reg_def.h"
|
||||
|
||||
u8 asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
u16 asd_read_reg_word(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
u32 asd_read_reg_dword(struct asd_ha_struct *asd_ha, u32 reg);
|
||||
|
||||
void asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val);
|
||||
void asd_write_reg_word(struct asd_ha_struct *asd_ha, u32 reg, u16 val);
|
||||
void asd_write_reg_dword(struct asd_ha_struct *asd_ha, u32 reg, u32 val);
|
||||
|
||||
void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst,
|
||||
u32 offs, int count);
|
||||
void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src,
|
||||
u32 offs, int count);
|
||||
|
||||
#define ASD_READ_OCM(type, ord, S) \
|
||||
static inline type asd_read_ocm_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u32 offs) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \
|
||||
type val = read##S (io_handle->addr + (unsigned long) offs); \
|
||||
rmb(); \
|
||||
return val; \
|
||||
}
|
||||
|
||||
ASD_READ_OCM(u8, byte, b);
|
||||
ASD_READ_OCM(u16,word, w);
|
||||
ASD_READ_OCM(u32,dword,l);
|
||||
|
||||
#define ASD_WRITE_OCM(type, ord, S) \
|
||||
static inline void asd_write_ocm_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u32 offs, type val) \
|
||||
{ \
|
||||
struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[1]; \
|
||||
write##S (val, io_handle->addr + (unsigned long) offs); \
|
||||
return; \
|
||||
}
|
||||
|
||||
ASD_WRITE_OCM(u8, byte, b);
|
||||
ASD_WRITE_OCM(u16,word, w);
|
||||
ASD_WRITE_OCM(u32,dword,l);
|
||||
|
||||
#define ASD_DDBSITE_READ(type, ord) \
|
||||
static inline type asd_ddbsite_read_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 ddb_site_no, \
|
||||
u16 offs) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \
|
||||
return asd_read_reg_##ord (asd_ha, CTXACCESS); \
|
||||
}
|
||||
|
||||
ASD_DDBSITE_READ(u32, dword);
|
||||
ASD_DDBSITE_READ(u16, word);
|
||||
|
||||
static inline u8 asd_ddbsite_read_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no,
|
||||
u16 offs)
|
||||
{
|
||||
if (offs & 1)
|
||||
return asd_ddbsite_read_word(asd_ha, ddb_site_no,
|
||||
offs & ~1) >> 8;
|
||||
else
|
||||
return asd_ddbsite_read_word(asd_ha, ddb_site_no,
|
||||
offs) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#define ASD_DDBSITE_WRITE(type, ord) \
|
||||
static inline void asd_ddbsite_write_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 ddb_site_no, \
|
||||
u16 offs, type val) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnDDB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ADDBPTR, ddb_site_no); \
|
||||
asd_write_reg_##ord (asd_ha, CTXACCESS, val); \
|
||||
}
|
||||
|
||||
ASD_DDBSITE_WRITE(u32, dword);
|
||||
ASD_DDBSITE_WRITE(u16, word);
|
||||
|
||||
static inline void asd_ddbsite_write_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no,
|
||||
u16 offs, u8 val)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 rval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
|
||||
if (offs & 1)
|
||||
rval = (val << 8) | (rval & 0xFF);
|
||||
else
|
||||
rval = (rval & 0xFF00) | val;
|
||||
asd_ddbsite_write_word(asd_ha, ddb_site_no, base, rval);
|
||||
}
|
||||
|
||||
|
||||
#define ASD_SCBSITE_READ(type, ord) \
|
||||
static inline type asd_scbsite_read_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 scb_site_no, \
|
||||
u16 offs) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \
|
||||
return asd_read_reg_##ord (asd_ha, CTXACCESS); \
|
||||
}
|
||||
|
||||
ASD_SCBSITE_READ(u32, dword);
|
||||
ASD_SCBSITE_READ(u16, word);
|
||||
|
||||
static inline u8 asd_scbsite_read_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 scb_site_no,
|
||||
u16 offs)
|
||||
{
|
||||
if (offs & 1)
|
||||
return asd_scbsite_read_word(asd_ha, scb_site_no,
|
||||
offs & ~1) >> 8;
|
||||
else
|
||||
return asd_scbsite_read_word(asd_ha, scb_site_no,
|
||||
offs) & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
#define ASD_SCBSITE_WRITE(type, ord) \
|
||||
static inline void asd_scbsite_write_##ord (struct asd_ha_struct *asd_ha, \
|
||||
u16 scb_site_no, \
|
||||
u16 offs, type val) \
|
||||
{ \
|
||||
asd_write_reg_word(asd_ha, ALTCIOADR, MnSCB_SITE + offs); \
|
||||
asd_write_reg_word(asd_ha, ASCBPTR, scb_site_no); \
|
||||
asd_write_reg_##ord (asd_ha, CTXACCESS, val); \
|
||||
}
|
||||
|
||||
ASD_SCBSITE_WRITE(u32, dword);
|
||||
ASD_SCBSITE_WRITE(u16, word);
|
||||
|
||||
static inline void asd_scbsite_write_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 scb_site_no,
|
||||
u16 offs, u8 val)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 rval = asd_scbsite_read_word(asd_ha, scb_site_no, base);
|
||||
if (offs & 1)
|
||||
rval = (val << 8) | (rval & 0xFF);
|
||||
else
|
||||
rval = (rval & 0xFF00) | val;
|
||||
asd_scbsite_write_word(asd_ha, scb_site_no, base, rval);
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_ddbsite_update_word -- atomically update a word in a ddb site
|
||||
* @asd_ha: pointer to host adapter structure
|
||||
* @ddb_site_no: the DDB site number
|
||||
* @offs: the offset into the DDB
|
||||
* @oldval: old value found in that offset
|
||||
* @newval: the new value to replace it
|
||||
*
|
||||
* This function is used when the sequencers are running and we need to
|
||||
* update a DDB site atomically without expensive pausing and upausing
|
||||
* of the sequencers and accessing the DDB site through the CIO bus.
|
||||
*
|
||||
* Return 0 on success; -EFAULT on parity error; -EAGAIN if the old value
|
||||
* is different than the current value at that offset.
|
||||
*/
|
||||
static inline int asd_ddbsite_update_word(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no, u16 offs,
|
||||
u16 oldval, u16 newval)
|
||||
{
|
||||
u8 done;
|
||||
u16 oval = asd_ddbsite_read_word(asd_ha, ddb_site_no, offs);
|
||||
if (oval != oldval)
|
||||
return -EAGAIN;
|
||||
asd_write_reg_word(asd_ha, AOLDDATA, oldval);
|
||||
asd_write_reg_word(asd_ha, ANEWDATA, newval);
|
||||
do {
|
||||
done = asd_read_reg_byte(asd_ha, ATOMICSTATCTL);
|
||||
} while (!(done & ATOMICDONE));
|
||||
if (done & ATOMICERR)
|
||||
return -EFAULT; /* parity error */
|
||||
else if (done & ATOMICWIN)
|
||||
return 0; /* success */
|
||||
else
|
||||
return -EAGAIN; /* oldval different than current value */
|
||||
}
|
||||
|
||||
static inline int asd_ddbsite_update_byte(struct asd_ha_struct *asd_ha,
|
||||
u16 ddb_site_no, u16 offs,
|
||||
u8 _oldval, u8 _newval)
|
||||
{
|
||||
u16 base = offs & ~1;
|
||||
u16 oval;
|
||||
u16 nval = asd_ddbsite_read_word(asd_ha, ddb_site_no, base);
|
||||
if (offs & 1) {
|
||||
if ((nval >> 8) != _oldval)
|
||||
return -EAGAIN;
|
||||
nval = (_newval << 8) | (nval & 0xFF);
|
||||
oval = (_oldval << 8) | (nval & 0xFF);
|
||||
} else {
|
||||
if ((nval & 0xFF) != _oldval)
|
||||
return -EAGAIN;
|
||||
nval = (nval & 0xFF00) | _newval;
|
||||
oval = (nval & 0xFF00) | _oldval;
|
||||
}
|
||||
return asd_ddbsite_update_word(asd_ha, ddb_site_no, base, oval, nval);
|
||||
}
|
||||
|
||||
static inline void asd_write_reg_addr(struct asd_ha_struct *asd_ha, u32 reg,
|
||||
dma_addr_t dma_handle)
|
||||
{
|
||||
asd_write_reg_dword(asd_ha, reg, ASD_BUSADDR_LO(dma_handle));
|
||||
asd_write_reg_dword(asd_ha, reg+4, ASD_BUSADDR_HI(dma_handle));
|
||||
}
|
||||
|
||||
static inline u32 asd_get_cmdctx_size(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
/* DCHREVISION returns 0, possibly broken */
|
||||
u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
|
||||
return ctxmemsize ? 65536 : 32768;
|
||||
}
|
||||
|
||||
static inline u32 asd_get_devctx_size(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
u32 ctxmemsize = asd_read_reg_dword(asd_ha, LmMnINT(0,0)) & CTXMEMSIZE;
|
||||
return ctxmemsize ? 8192 : 4096;
|
||||
}
|
||||
|
||||
static inline void asd_disable_ints(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
asd_write_reg_dword(asd_ha, CHIMINTEN, RST_CHIMINTEN);
|
||||
}
|
||||
|
||||
static inline void asd_enable_ints(struct asd_ha_struct *asd_ha)
|
||||
{
|
||||
/* Enable COM SAS interrupt on errors, COMSTAT */
|
||||
asd_write_reg_dword(asd_ha, COMSTATEN,
|
||||
EN_CSBUFPERR | EN_CSERR | EN_OVLYERR);
|
||||
/* Enable DCH SAS CFIFTOERR */
|
||||
asd_write_reg_dword(asd_ha, DCHSTATUS, EN_CFIFTOERR);
|
||||
/* Enable Host Device interrupts */
|
||||
asd_write_reg_dword(asd_ha, CHIMINTEN, SET_CHIMINTEN);
|
||||
}
|
||||
|
||||
#endif
|
2398
drivers/scsi/aic94xx/aic94xx_reg_def.h
Normal file
2398
drivers/scsi/aic94xx/aic94xx_reg_def.h
Normal file
File diff suppressed because it is too large
Load Diff
785
drivers/scsi/aic94xx/aic94xx_sas.h
Normal file
785
drivers/scsi/aic94xx/aic94xx_sas.h
Normal file
@ -0,0 +1,785 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver SAS definitions and 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_SAS_H_
|
||||
#define _AIC94XX_SAS_H_
|
||||
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
/* ---------- DDBs ---------- */
|
||||
/* DDBs are device descriptor blocks which describe a device in the
|
||||
* domain that this sequencer can maintain low-level connections for
|
||||
* us. They are be 64 bytes.
|
||||
*/
|
||||
|
||||
struct asd_ddb_ssp_smp_target_port {
|
||||
u8 conn_type; /* byte 0 */
|
||||
#define DDB_TP_CONN_TYPE 0x81 /* Initiator port and addr frame type 0x01 */
|
||||
|
||||
u8 conn_rate;
|
||||
__be16 init_conn_tag;
|
||||
u8 dest_sas_addr[8]; /* bytes 4-11 */
|
||||
|
||||
__le16 send_queue_head;
|
||||
u8 sq_suspended;
|
||||
u8 ddb_type; /* DDB_TYPE_TARGET */
|
||||
#define DDB_TYPE_UNUSED 0xFF
|
||||
#define DDB_TYPE_TARGET 0xFE
|
||||
#define DDB_TYPE_INITIATOR 0xFD
|
||||
#define DDB_TYPE_PM_PORT 0xFC
|
||||
|
||||
__le16 _r_a;
|
||||
__be16 awt_def;
|
||||
|
||||
u8 compat_features; /* byte 20 */
|
||||
u8 pathway_blocked_count;
|
||||
__be16 arb_wait_time;
|
||||
__be32 more_compat_features; /* byte 24 */
|
||||
|
||||
u8 conn_mask;
|
||||
u8 flags; /* concurrent conn:2,2 and open:0(1) */
|
||||
#define CONCURRENT_CONN_SUPP 0x04
|
||||
#define OPEN_REQUIRED 0x01
|
||||
|
||||
u16 _r_b;
|
||||
__le16 exec_queue_tail;
|
||||
__le16 send_queue_tail;
|
||||
__le16 sister_ddb;
|
||||
|
||||
__le16 _r_c;
|
||||
|
||||
u8 max_concurrent_conn;
|
||||
u8 num_concurrent_conn;
|
||||
u8 num_contexts;
|
||||
|
||||
u8 _r_d;
|
||||
|
||||
__le16 active_task_count;
|
||||
|
||||
u8 _r_e[9];
|
||||
|
||||
u8 itnl_reason; /* I_T nexus loss reason */
|
||||
|
||||
__le16 _r_f;
|
||||
|
||||
__le16 itnl_timeout;
|
||||
#define ITNL_TIMEOUT_CONST 0x7D0 /* 2 seconds */
|
||||
|
||||
__le32 itnl_timestamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct asd_ddb_stp_sata_target_port {
|
||||
u8 conn_type; /* byte 0 */
|
||||
u8 conn_rate;
|
||||
__be16 init_conn_tag;
|
||||
u8 dest_sas_addr[8]; /* bytes 4-11 */
|
||||
|
||||
__le16 send_queue_head;
|
||||
u8 sq_suspended;
|
||||
u8 ddb_type; /* DDB_TYPE_TARGET */
|
||||
|
||||
__le16 _r_a;
|
||||
|
||||
__be16 awt_def;
|
||||
u8 compat_features; /* byte 20 */
|
||||
u8 pathway_blocked_count;
|
||||
__be16 arb_wait_time;
|
||||
__be32 more_compat_features; /* byte 24 */
|
||||
|
||||
u8 conn_mask;
|
||||
u8 flags; /* concurrent conn:2,2 and open:0(1) */
|
||||
#define SATA_MULTIPORT 0x80
|
||||
#define SUPPORTS_AFFIL 0x40
|
||||
#define STP_AFFIL_POL 0x20
|
||||
|
||||
u8 _r_b;
|
||||
u8 flags2; /* STP close policy:0 */
|
||||
#define STP_CL_POL_NO_TX 0x00
|
||||
#define STP_CL_POL_BTW_CMDS 0x01
|
||||
|
||||
__le16 exec_queue_tail;
|
||||
__le16 send_queue_tail;
|
||||
__le16 sister_ddb;
|
||||
__le16 ata_cmd_scbptr;
|
||||
__le32 sata_tag_alloc_mask;
|
||||
__le16 active_task_count;
|
||||
__le16 _r_c;
|
||||
__le32 sata_sactive;
|
||||
u8 num_sata_tags;
|
||||
u8 sata_status;
|
||||
u8 sata_ending_status;
|
||||
u8 itnl_reason; /* I_T nexus loss reason */
|
||||
__le16 ncq_data_scb_ptr;
|
||||
__le16 itnl_timeout;
|
||||
__le32 itnl_timestamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_init_port, describes the device descriptor block
|
||||
* of an initiator port (when the sequencer is operating in target mode).
|
||||
* Bytes [0,11] and [20,27] are from the OPEN address frame.
|
||||
* The sequencer allocates an initiator port DDB entry.
|
||||
*/
|
||||
struct asd_ddb_init_port {
|
||||
u8 conn_type; /* byte 0 */
|
||||
u8 conn_rate;
|
||||
__be16 init_conn_tag; /* BE */
|
||||
u8 dest_sas_addr[8];
|
||||
__le16 send_queue_head; /* LE, byte 12 */
|
||||
u8 sq_suspended;
|
||||
u8 ddb_type; /* DDB_TYPE_INITIATOR */
|
||||
__le16 _r_a;
|
||||
__be16 awt_def; /* BE */
|
||||
u8 compat_features;
|
||||
u8 pathway_blocked_count;
|
||||
__be16 arb_wait_time; /* BE */
|
||||
__be32 more_compat_features; /* BE */
|
||||
u8 conn_mask;
|
||||
u8 flags; /* == 5 */
|
||||
u16 _r_b;
|
||||
__le16 exec_queue_tail; /* execution queue tail */
|
||||
__le16 send_queue_tail;
|
||||
__le16 sister_ddb;
|
||||
__le16 init_resp_timeout; /* initiator response timeout */
|
||||
__le32 _r_c;
|
||||
__le16 active_tasks; /* active task count */
|
||||
__le16 init_list; /* initiator list link pointer */
|
||||
__le32 _r_d;
|
||||
u8 max_conn_to[3]; /* from Conn-Disc mode page, in us, LE */
|
||||
u8 itnl_reason; /* I_T nexus loss reason */
|
||||
__le16 bus_inact_to; /* from Conn-Disc mode page, in 100 us, LE */
|
||||
__le16 itnl_to; /* from the Protocol Specific Port Ctrl MP */
|
||||
__le32 itnl_timestamp;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_sata_tag, describes a look-up table to be used
|
||||
* by the sequencers. SATA II, IDENTIFY DEVICE data, word 76, bit 8:
|
||||
* NCQ support. This table is used by the sequencers to find the
|
||||
* corresponding SCB, given a SATA II tag value.
|
||||
*/
|
||||
struct asd_ddb_sata_tag {
|
||||
__le16 scb_pointer[32];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_sata_pm_table, describes a port number to
|
||||
* connection handle look-up table. SATA targets attached to a port
|
||||
* multiplier require a 4-bit port number value. There is one DDB
|
||||
* entry of this type for each SATA port multiplier (sister DDB).
|
||||
* Given a SATA PM port number, this table gives us the SATA PM Port
|
||||
* DDB of the SATA port multiplier port (i.e. the SATA target
|
||||
* discovered on the port).
|
||||
*/
|
||||
struct asd_ddb_sata_pm_table {
|
||||
__le16 ddb_pointer[16];
|
||||
__le16 _r_a[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_sata_pm_port, describes the SATA port multiplier
|
||||
* port format DDB.
|
||||
*/
|
||||
struct asd_ddb_sata_pm_port {
|
||||
u8 _r_a[15];
|
||||
u8 ddb_type;
|
||||
u8 _r_b[13];
|
||||
u8 pm_port_flags;
|
||||
#define PM_PORT_MASK 0xF0
|
||||
#define PM_PORT_SET 0x02
|
||||
u8 _r_c[6];
|
||||
__le16 sister_ddb;
|
||||
__le16 ata_cmd_scbptr;
|
||||
__le32 sata_tag_alloc_mask;
|
||||
__le16 active_task_count;
|
||||
__le16 parent_ddb;
|
||||
__le32 sata_sactive;
|
||||
u8 num_sata_tags;
|
||||
u8 sata_status;
|
||||
u8 sata_ending_status;
|
||||
u8 _r_d[9];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This struct asd_ddb_seq_shared, describes a DDB shared by the
|
||||
* central and link sequencers. port_map_by_links is indexed phy
|
||||
* number [0,7]; each byte is a bit mask of all the phys that are in
|
||||
* the same port as the indexed phy.
|
||||
*/
|
||||
struct asd_ddb_seq_shared {
|
||||
__le16 q_free_ddb_head;
|
||||
__le16 q_free_ddb_tail;
|
||||
__le16 q_free_ddb_cnt;
|
||||
__le16 q_used_ddb_head;
|
||||
__le16 q_used_ddb_tail;
|
||||
__le16 shared_mem_lock;
|
||||
__le16 smp_conn_tag;
|
||||
__le16 est_nexus_buf_cnt;
|
||||
__le16 est_nexus_buf_thresh;
|
||||
u32 _r_a;
|
||||
u8 settable_max_contexts;
|
||||
u8 _r_b[23];
|
||||
u8 conn_not_active;
|
||||
u8 phy_is_up;
|
||||
u8 _r_c[8];
|
||||
u8 port_map_by_links[8];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- SG Element ---------- */
|
||||
|
||||
/* This struct sg_el, describes the hardware scatter gather buffer
|
||||
* element. All entries are little endian. In an SCB, there are 2 of
|
||||
* this, plus one more, called a link element of this indicating a
|
||||
* sublist if needed.
|
||||
*
|
||||
* A link element has only the bus address set and the flags (DS) bit
|
||||
* valid. The bus address points to the start of the sublist.
|
||||
*
|
||||
* If a sublist is needed, then that sublist should also include the 2
|
||||
* sg_el embedded in the SCB, in which case next_sg_offset is 32,
|
||||
* since sizeof(sg_el) = 16; EOS should be 1 and EOL 0 in this case.
|
||||
*/
|
||||
struct sg_el {
|
||||
__le64 bus_addr;
|
||||
__le32 size;
|
||||
__le16 _r;
|
||||
u8 next_sg_offs;
|
||||
u8 flags;
|
||||
#define ASD_SG_EL_DS_MASK 0x30
|
||||
#define ASD_SG_EL_DS_OCM 0x10
|
||||
#define ASD_SG_EL_DS_HM 0x00
|
||||
#define ASD_SG_EL_LIST_MASK 0xC0
|
||||
#define ASD_SG_EL_LIST_EOL 0x40
|
||||
#define ASD_SG_EL_LIST_EOS 0x80
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- SCBs ---------- */
|
||||
|
||||
/* An SCB (sequencer control block) is comprised of a common header
|
||||
* and a task part, for a total of 128 bytes. All fields are in LE
|
||||
* order, unless otherwise noted.
|
||||
*/
|
||||
|
||||
/* This struct scb_header, defines the SCB header format.
|
||||
*/
|
||||
struct scb_header {
|
||||
__le64 next_scb;
|
||||
__le16 index; /* transaction context */
|
||||
u8 opcode;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* SCB opcodes: Execution queue
|
||||
*/
|
||||
#define INITIATE_SSP_TASK 0x00
|
||||
#define INITIATE_LONG_SSP_TASK 0x01
|
||||
#define INITIATE_BIDIR_SSP_TASK 0x02
|
||||
#define ABORT_TASK 0x03
|
||||
#define INITIATE_SSP_TMF 0x04
|
||||
#define SSP_TARG_GET_DATA 0x05
|
||||
#define SSP_TARG_GET_DATA_GOOD 0x06
|
||||
#define SSP_TARG_SEND_RESP 0x07
|
||||
#define QUERY_SSP_TASK 0x08
|
||||
#define INITIATE_ATA_TASK 0x09
|
||||
#define INITIATE_ATAPI_TASK 0x0a
|
||||
#define CONTROL_ATA_DEV 0x0b
|
||||
#define INITIATE_SMP_TASK 0x0c
|
||||
#define SMP_TARG_SEND_RESP 0x0f
|
||||
|
||||
/* SCB opcodes: Send Queue
|
||||
*/
|
||||
#define SSP_TARG_SEND_DATA 0x40
|
||||
#define SSP_TARG_SEND_DATA_GOOD 0x41
|
||||
|
||||
/* SCB opcodes: Link Queue
|
||||
*/
|
||||
#define CONTROL_PHY 0x80
|
||||
#define SEND_PRIMITIVE 0x81
|
||||
#define INITIATE_LINK_ADM_TASK 0x82
|
||||
|
||||
/* SCB opcodes: other
|
||||
*/
|
||||
#define EMPTY_SCB 0xc0
|
||||
#define INITIATE_SEQ_ADM_TASK 0xc1
|
||||
#define EST_ICL_TARG_WINDOW 0xc2
|
||||
#define COPY_MEM 0xc3
|
||||
#define CLEAR_NEXUS 0xc4
|
||||
#define INITIATE_DDB_ADM_TASK 0xc6
|
||||
#define ESTABLISH_NEXUS_ESCB 0xd0
|
||||
|
||||
#define LUN_SIZE 8
|
||||
|
||||
/* See SAS spec, task IU
|
||||
*/
|
||||
struct ssp_task_iu {
|
||||
u8 lun[LUN_SIZE]; /* BE */
|
||||
u16 _r_a;
|
||||
u8 tmf;
|
||||
u8 _r_b;
|
||||
__be16 tag; /* BE */
|
||||
u8 _r_c[14];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* See SAS spec, command IU
|
||||
*/
|
||||
struct ssp_command_iu {
|
||||
u8 lun[LUN_SIZE];
|
||||
u8 _r_a;
|
||||
u8 efb_prio_attr; /* enable first burst, task prio & attr */
|
||||
#define EFB_MASK 0x80
|
||||
#define TASK_PRIO_MASK 0x78
|
||||
#define TASK_ATTR_MASK 0x07
|
||||
|
||||
u8 _r_b;
|
||||
u8 add_cdb_len; /* in dwords, since bit 0,1 are reserved */
|
||||
union {
|
||||
u8 cdb[16];
|
||||
struct {
|
||||
__le64 long_cdb_addr; /* bus address, LE */
|
||||
__le32 long_cdb_size; /* LE */
|
||||
u8 _r_c[3];
|
||||
u8 eol_ds; /* eol:6,6, ds:5,4 */
|
||||
} long_cdb; /* sequencer extension */
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct xfer_rdy_iu {
|
||||
__be32 requested_offset; /* BE */
|
||||
__be32 write_data_len; /* BE */
|
||||
__be32 _r_a;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- SCB tasks ---------- */
|
||||
|
||||
/* This is both ssp_task and long_ssp_task
|
||||
*/
|
||||
struct initiate_ssp_task {
|
||||
u8 proto_conn_rate; /* proto:6,4, conn_rate:3,0 */
|
||||
__le32 total_xfer_len;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct ssp_command_iu ssp_cmd;
|
||||
__le16 sister_scb; /* 0xFFFF */
|
||||
__le16 conn_handle; /* index to DDB for the intended target */
|
||||
u8 data_dir; /* :1,0 */
|
||||
#define DATA_DIR_NONE 0x00
|
||||
#define DATA_DIR_IN 0x01
|
||||
#define DATA_DIR_OUT 0x02
|
||||
#define DATA_DIR_BYRECIPIENT 0x03
|
||||
|
||||
u8 _r_a;
|
||||
u8 retry_count;
|
||||
u8 _r_b[5];
|
||||
struct sg_el sg_element[3]; /* 2 real and 1 link */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This defines both ata_task and atapi_task.
|
||||
* ata: C bit of FIS should be 1,
|
||||
* atapi: C bit of FIS should be 1, and command register should be 0xA0,
|
||||
* to indicate a packet command.
|
||||
*/
|
||||
struct initiate_ata_task {
|
||||
u8 proto_conn_rate;
|
||||
__le32 total_xfer_len;
|
||||
struct host_to_dev_fis fis;
|
||||
__le32 data_offs;
|
||||
u8 atapi_packet[16];
|
||||
u8 _r_a[12];
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 ata_flags; /* CSMI:6,6, DTM:4,4, QT:3,3, data dir:1,0 */
|
||||
#define CSMI_TASK 0x40
|
||||
#define DATA_XFER_MODE_DMA 0x10
|
||||
#define ATA_Q_TYPE_MASK 0x08
|
||||
#define ATA_Q_TYPE_UNTAGGED 0x00
|
||||
#define ATA_Q_TYPE_NCQ 0x08
|
||||
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c;
|
||||
u8 flags;
|
||||
#define STP_AFFIL_POLICY 0x20
|
||||
#define SET_AFFIL_POLICY 0x10
|
||||
#define RET_PARTIAL_SGLIST 0x02
|
||||
|
||||
u8 _r_d[3];
|
||||
struct sg_el sg_element[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct initiate_smp_task {
|
||||
u8 proto_conn_rate;
|
||||
u8 _r_a[40];
|
||||
struct sg_el smp_req;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 _r_c[8];
|
||||
struct sg_el smp_resp;
|
||||
u8 _r_d[32];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct control_phy {
|
||||
u8 phy_id;
|
||||
u8 sub_func;
|
||||
#define DISABLE_PHY 0x00
|
||||
#define ENABLE_PHY 0x01
|
||||
#define RELEASE_SPINUP_HOLD 0x02
|
||||
#define ENABLE_PHY_NO_SAS_OOB 0x03
|
||||
#define ENABLE_PHY_NO_SATA_OOB 0x04
|
||||
#define PHY_NO_OP 0x05
|
||||
#define EXECUTE_HARD_RESET 0x81
|
||||
|
||||
u8 func_mask;
|
||||
u8 speed_mask;
|
||||
u8 hot_plug_delay;
|
||||
u8 port_type;
|
||||
u8 flags;
|
||||
#define DEV_PRES_TIMER_OVERRIDE_ENABLE 0x01
|
||||
#define DISABLE_PHY_IF_OOB_FAILS 0x02
|
||||
|
||||
__le32 timeout_override;
|
||||
u8 link_reset_retries;
|
||||
u8 _r_a[47];
|
||||
__le16 conn_handle;
|
||||
u8 _r_b[56];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct control_ata_dev {
|
||||
u8 proto_conn_rate;
|
||||
__le32 _r_a;
|
||||
struct host_to_dev_fis fis;
|
||||
u8 _r_b[32];
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 ata_flags; /* 0 */
|
||||
u8 _r_c[55];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct empty_scb {
|
||||
u8 num_valid;
|
||||
__le32 _r_a;
|
||||
#define ASD_EDBS_PER_SCB 7
|
||||
/* header+data+CRC+DMA suffix data */
|
||||
#define ASD_EDB_SIZE (24+1024+4+16)
|
||||
struct sg_el eb[ASD_EDBS_PER_SCB];
|
||||
#define ELEMENT_NOT_VALID 0xC0
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct initiate_link_adm {
|
||||
u8 phy_id;
|
||||
u8 sub_func;
|
||||
#define GET_LINK_ERROR_COUNT 0x00
|
||||
#define RESET_LINK_ERROR_COUNT 0x01
|
||||
#define ENABLE_NOTIFY_SPINUP_INTS 0x02
|
||||
|
||||
u8 _r_a[57];
|
||||
__le16 conn_handle;
|
||||
u8 _r_b[56];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct copy_memory {
|
||||
u8 _r_a;
|
||||
__le16 xfer_len;
|
||||
__le16 _r_b;
|
||||
__le64 src_busaddr;
|
||||
u8 src_ds; /* See definition of sg_el */
|
||||
u8 _r_c[45];
|
||||
__le16 conn_handle;
|
||||
__le64 _r_d;
|
||||
__le64 dest_busaddr;
|
||||
u8 dest_ds; /* See definition of sg_el */
|
||||
u8 _r_e[39];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct abort_task {
|
||||
u8 proto_conn_rate;
|
||||
__le32 _r_a;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct ssp_task_iu ssp_task;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 flags; /* ovrd_itnl_timer:3,3, suspend_data_trans:2,2 */
|
||||
#define SUSPEND_DATA_TRANS 0x04
|
||||
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c[5];
|
||||
__le16 index; /* Transaction context of task to be queried */
|
||||
__le16 itnl_to;
|
||||
u8 _r_d[44];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct clear_nexus {
|
||||
u8 nexus;
|
||||
#define NEXUS_ADAPTER 0x00
|
||||
#define NEXUS_PORT 0x01
|
||||
#define NEXUS_I_T 0x02
|
||||
#define NEXUS_I_T_L 0x03
|
||||
#define NEXUS_TAG 0x04
|
||||
#define NEXUS_TRANS_CX 0x05
|
||||
#define NEXUS_SATA_TAG 0x06
|
||||
#define NEXUS_T_L 0x07
|
||||
#define NEXUS_L 0x08
|
||||
#define NEXUS_T_TAG 0x09
|
||||
|
||||
__le32 _r_a;
|
||||
u8 flags;
|
||||
#define SUSPEND_TX 0x80
|
||||
#define RESUME_TX 0x40
|
||||
#define SEND_Q 0x04
|
||||
#define EXEC_Q 0x02
|
||||
#define NOTINQ 0x01
|
||||
|
||||
u8 _r_b[3];
|
||||
u8 conn_mask;
|
||||
u8 _r_c[19];
|
||||
struct ssp_task_iu ssp_task; /* LUN and TAG */
|
||||
__le16 _r_d;
|
||||
__le16 conn_handle;
|
||||
__le64 _r_e;
|
||||
__le16 index; /* Transaction context of task to be cleared */
|
||||
__le16 context; /* Clear nexus context */
|
||||
u8 _r_f[44];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct initiate_ssp_tmf {
|
||||
u8 proto_conn_rate;
|
||||
__le32 _r_a;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct ssp_task_iu ssp_task;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 flags; /* itnl override and suspend data tx */
|
||||
#define OVERRIDE_ITNL_TIMER 8
|
||||
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c[5];
|
||||
__le16 index; /* Transaction context of task to be queried */
|
||||
__le16 itnl_to;
|
||||
u8 _r_d[44];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Transmits an arbitrary primitive on the link.
|
||||
* Used for NOTIFY and BROADCAST.
|
||||
*/
|
||||
struct send_prim {
|
||||
u8 phy_id;
|
||||
u8 wait_transmit; /* :0,0 */
|
||||
u8 xmit_flags;
|
||||
#define XMTPSIZE_MASK 0xF0
|
||||
#define XMTPSIZE_SINGLE 0x10
|
||||
#define XMTPSIZE_REPEATED 0x20
|
||||
#define XMTPSIZE_CONT 0x20
|
||||
#define XMTPSIZE_TRIPLE 0x30
|
||||
#define XMTPSIZE_REDUNDANT 0x60
|
||||
#define XMTPSIZE_INF 0
|
||||
|
||||
#define XMTCONTEN 0x04
|
||||
#define XMTPFRM 0x02 /* Transmit at the next frame boundary */
|
||||
#define XMTPIMM 0x01 /* Transmit immediately */
|
||||
|
||||
__le16 _r_a;
|
||||
u8 prim[4]; /* K, D0, D1, D2 */
|
||||
u8 _r_b[50];
|
||||
__le16 conn_handle;
|
||||
u8 _r_c[56];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* This describes both SSP Target Get Data and SSP Target Get Data And
|
||||
* Send Good Response SCBs. Used when the sequencer is operating in
|
||||
* target mode...
|
||||
*/
|
||||
struct ssp_targ_get_data {
|
||||
u8 proto_conn_rate;
|
||||
__le32 total_xfer_len;
|
||||
struct ssp_frame_hdr ssp_frame;
|
||||
struct xfer_rdy_iu xfer_rdy;
|
||||
u8 lun[LUN_SIZE];
|
||||
__le64 _r_a;
|
||||
__le16 sister_scb;
|
||||
__le16 conn_handle;
|
||||
u8 data_dir; /* 01b */
|
||||
u8 _r_b;
|
||||
u8 retry_count;
|
||||
u8 _r_c[5];
|
||||
struct sg_el sg_element[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- The actual SCB struct ---------- */
|
||||
|
||||
struct scb {
|
||||
struct scb_header header;
|
||||
union {
|
||||
struct initiate_ssp_task ssp_task;
|
||||
struct initiate_ata_task ata_task;
|
||||
struct initiate_smp_task smp_task;
|
||||
struct control_phy control_phy;
|
||||
struct control_ata_dev control_ata_dev;
|
||||
struct empty_scb escb;
|
||||
struct initiate_link_adm link_adm;
|
||||
struct copy_memory cp_mem;
|
||||
struct abort_task abort_task;
|
||||
struct clear_nexus clear_nexus;
|
||||
struct initiate_ssp_tmf ssp_tmf;
|
||||
};
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- Done List ---------- */
|
||||
/* The done list entry opcode field is defined below.
|
||||
* The mnemonic encoding and meaning is as follows:
|
||||
* TC - Task Complete, status was received and acknowledged
|
||||
* TF - Task Failed, indicates an error prior to receiving acknowledgment
|
||||
* for the command:
|
||||
* - no conn,
|
||||
* - NACK or R_ERR received in response to this command,
|
||||
* - credit blocked or not available, or in the case of SMP request,
|
||||
* - no SMP response was received.
|
||||
* In these four cases it is known that the target didn't receive the
|
||||
* command.
|
||||
* TI - Task Interrupted, error after the command was acknowledged. It is
|
||||
* known that the command was received by the target.
|
||||
* TU - Task Unacked, command was transmitted but neither ACK (R_OK) nor NAK
|
||||
* (R_ERR) was received due to loss of signal, broken connection, loss of
|
||||
* dword sync or other reason. The application client should send the
|
||||
* appropriate task query.
|
||||
* TA - Task Aborted, see TF.
|
||||
* _RESP - The completion includes an empty buffer containing status.
|
||||
* TO - Timeout.
|
||||
*/
|
||||
#define TC_NO_ERROR 0x00
|
||||
#define TC_UNDERRUN 0x01
|
||||
#define TC_OVERRUN 0x02
|
||||
#define TF_OPEN_TO 0x03
|
||||
#define TF_OPEN_REJECT 0x04
|
||||
#define TI_BREAK 0x05
|
||||
#define TI_PROTO_ERR 0x06
|
||||
#define TC_SSP_RESP 0x07
|
||||
#define TI_PHY_DOWN 0x08
|
||||
#define TF_PHY_DOWN 0x09
|
||||
#define TC_LINK_ADM_RESP 0x0a
|
||||
#define TC_CSMI 0x0b
|
||||
#define TC_ATA_RESP 0x0c
|
||||
#define TU_PHY_DOWN 0x0d
|
||||
#define TU_BREAK 0x0e
|
||||
#define TI_SATA_TO 0x0f
|
||||
#define TI_NAK 0x10
|
||||
#define TC_CONTROL_PHY 0x11
|
||||
#define TF_BREAK 0x12
|
||||
#define TC_RESUME 0x13
|
||||
#define TI_ACK_NAK_TO 0x14
|
||||
#define TF_SMPRSP_TO 0x15
|
||||
#define TF_SMP_XMIT_RCV_ERR 0x16
|
||||
#define TC_PARTIAL_SG_LIST 0x17
|
||||
#define TU_ACK_NAK_TO 0x18
|
||||
#define TU_SATA_TO 0x19
|
||||
#define TF_NAK_RECV 0x1a
|
||||
#define TA_I_T_NEXUS_LOSS 0x1b
|
||||
#define TC_ATA_R_ERR_RECV 0x1c
|
||||
#define TF_TMF_NO_CTX 0x1d
|
||||
#define TA_ON_REQ 0x1e
|
||||
#define TF_TMF_NO_TAG 0x1f
|
||||
#define TF_TMF_TAG_FREE 0x20
|
||||
#define TF_TMF_TASK_DONE 0x21
|
||||
#define TF_TMF_NO_CONN_HANDLE 0x22
|
||||
#define TC_TASK_CLEARED 0x23
|
||||
#define TI_SYNCS_RECV 0x24
|
||||
#define TU_SYNCS_RECV 0x25
|
||||
#define TF_IRTT_TO 0x26
|
||||
#define TF_NO_SMP_CONN 0x27
|
||||
#define TF_IU_SHORT 0x28
|
||||
#define TF_DATA_OFFS_ERR 0x29
|
||||
#define TF_INV_CONN_HANDLE 0x2a
|
||||
#define TF_REQUESTED_N_PENDING 0x2b
|
||||
|
||||
/* 0xc1 - 0xc7: empty buffer received,
|
||||
0xd1 - 0xd7: establish nexus empty buffer received
|
||||
*/
|
||||
/* This is the ESCB mask */
|
||||
#define ESCB_RECVD 0xC0
|
||||
|
||||
|
||||
/* This struct done_list_struct defines the done list entry.
|
||||
* All fields are LE.
|
||||
*/
|
||||
struct done_list_struct {
|
||||
__le16 index; /* aka transaction context */
|
||||
u8 opcode;
|
||||
u8 status_block[4];
|
||||
u8 toggle; /* bit 0 */
|
||||
#define DL_TOGGLE_MASK 0x01
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* ---------- PHYS ---------- */
|
||||
|
||||
struct asd_phy {
|
||||
struct asd_sas_phy sas_phy;
|
||||
struct asd_phy_desc *phy_desc; /* hw profile */
|
||||
|
||||
struct sas_identify_frame *identify_frame;
|
||||
struct asd_dma_tok *id_frm_tok;
|
||||
|
||||
u8 frame_rcvd[ASD_EDB_SIZE];
|
||||
};
|
||||
|
||||
|
||||
#define ASD_SCB_SIZE sizeof(struct scb)
|
||||
#define ASD_DDB_SIZE sizeof(struct asd_ddb_ssp_smp_target_port)
|
||||
|
||||
/* Define this to 0 if you do not want NOTIFY (ENABLE SPINIP) sent.
|
||||
* Default: 0x10 (it's a mask)
|
||||
*/
|
||||
#define ASD_NOTIFY_ENABLE_SPINUP 0x10
|
||||
|
||||
/* If enabled, set this to the interval between transmission
|
||||
* of NOTIFY (ENABLE SPINUP). In units of 200 us.
|
||||
*/
|
||||
#define ASD_NOTIFY_TIMEOUT 2500
|
||||
|
||||
/* Initial delay after OOB, before we transmit NOTIFY (ENABLE SPINUP).
|
||||
* If 0, transmit immediately. In milliseconds.
|
||||
*/
|
||||
#define ASD_NOTIFY_DOWN_COUNT 0
|
||||
|
||||
/* Device present timer timeout constant, 10 ms. */
|
||||
#define ASD_DEV_PRESENT_TIMEOUT 0x2710
|
||||
|
||||
#define ASD_SATA_INTERLOCK_TIMEOUT 0
|
||||
|
||||
/* How long to wait before shutting down an STP connection, unless
|
||||
* an STP target sent frame(s). 50 usec.
|
||||
* IGNORED by the sequencer (i.e. value 0 always).
|
||||
*/
|
||||
#define ASD_STP_SHUTDOWN_TIMEOUT 0x0
|
||||
|
||||
/* ATA soft reset timer timeout. 5 usec. */
|
||||
#define ASD_SRST_ASSERT_TIMEOUT 0x05
|
||||
|
||||
/* 31 sec */
|
||||
#define ASD_RCV_FIS_TIMEOUT 0x01D905C0
|
||||
|
||||
#define ASD_ONE_MILLISEC_TIMEOUT 0x03e8
|
||||
|
||||
/* COMINIT timer */
|
||||
#define ASD_TEN_MILLISEC_TIMEOUT 0x2710
|
||||
#define ASD_COMINIT_TIMEOUT ASD_TEN_MILLISEC_TIMEOUT
|
||||
|
||||
/* 1 sec */
|
||||
#define ASD_SMP_RCV_TIMEOUT 0x000F4240
|
||||
|
||||
#endif
|
758
drivers/scsi/aic94xx/aic94xx_scb.c
Normal file
758
drivers/scsi/aic94xx/aic94xx_scb.c
Normal file
@ -0,0 +1,758 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver SCB 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
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_reg.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
#include "aic94xx_seq.h"
|
||||
|
||||
#include "aic94xx_dump.h"
|
||||
|
||||
/* ---------- EMPTY SCB ---------- */
|
||||
|
||||
#define DL_PHY_MASK 7
|
||||
#define BYTES_DMAED 0
|
||||
#define PRIMITIVE_RECVD 0x08
|
||||
#define PHY_EVENT 0x10
|
||||
#define LINK_RESET_ERROR 0x18
|
||||
#define TIMER_EVENT 0x20
|
||||
#define REQ_TASK_ABORT 0xF0
|
||||
#define REQ_DEVICE_RESET 0xF1
|
||||
#define SIGNAL_NCQ_ERROR 0xF2
|
||||
#define CLEAR_NCQ_ERROR 0xF3
|
||||
|
||||
#define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE \
|
||||
| CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
|
||||
| CURRENT_OOB_ERROR)
|
||||
|
||||
static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode)
|
||||
{
|
||||
struct sas_phy *sas_phy = phy->sas_phy.phy;
|
||||
|
||||
switch (oob_mode & 7) {
|
||||
case PHY_SPEED_60:
|
||||
/* FIXME: sas transport class doesn't have this */
|
||||
phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS;
|
||||
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS;
|
||||
break;
|
||||
case PHY_SPEED_30:
|
||||
phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS;
|
||||
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS;
|
||||
break;
|
||||
case PHY_SPEED_15:
|
||||
phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS;
|
||||
phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS;
|
||||
break;
|
||||
}
|
||||
sas_phy->negotiated_linkrate = phy->sas_phy.linkrate;
|
||||
sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS;
|
||||
sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS;
|
||||
sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate;
|
||||
sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate;
|
||||
|
||||
if (oob_mode & SAS_MODE)
|
||||
phy->sas_phy.oob_mode = SAS_OOB_MODE;
|
||||
else if (oob_mode & SATA_MODE)
|
||||
phy->sas_phy.oob_mode = SATA_OOB_MODE;
|
||||
}
|
||||
|
||||
static inline void asd_phy_event_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
||||
int phy_id = dl->status_block[0] & DL_PHY_MASK;
|
||||
struct asd_phy *phy = &asd_ha->phys[phy_id];
|
||||
|
||||
u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS;
|
||||
u8 oob_mode = dl->status_block[2];
|
||||
|
||||
switch (oob_status) {
|
||||
case CURRENT_LOSS_OF_SIGNAL:
|
||||
/* directly attached device was removed */
|
||||
ASD_DPRINTK("phy%d: device unplugged\n", phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
sas_phy_disconnected(&phy->sas_phy);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL);
|
||||
break;
|
||||
case CURRENT_OOB_DONE:
|
||||
/* hot plugged device */
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
get_lrate_mode(phy, oob_mode);
|
||||
ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n",
|
||||
phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE);
|
||||
break;
|
||||
case CURRENT_SPINUP_HOLD:
|
||||
/* hot plug SATA, no COMWAKE sent */
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD);
|
||||
break;
|
||||
case CURRENT_GTO_TIMEOUT:
|
||||
case CURRENT_OOB_ERROR:
|
||||
ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id,
|
||||
dl->status_block[1]);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
sas_phy_disconnected(&phy->sas_phy);
|
||||
sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If phys are enabled sparsely, this will do the right thing. */
|
||||
static inline unsigned ord_phy(struct asd_ha_struct *asd_ha,
|
||||
struct asd_phy *phy)
|
||||
{
|
||||
u8 enabled_mask = asd_ha->hw_prof.enabled_phys;
|
||||
int i, k = 0;
|
||||
|
||||
for_each_phy(enabled_mask, enabled_mask, i) {
|
||||
if (&asd_ha->phys[i] == phy)
|
||||
return k;
|
||||
k++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_get_attached_sas_addr -- extract/generate attached SAS address
|
||||
* phy: pointer to asd_phy
|
||||
* sas_addr: pointer to buffer where the SAS address is to be written
|
||||
*
|
||||
* This function extracts the SAS address from an IDENTIFY frame
|
||||
* received. If OOB is SATA, then a SAS address is generated from the
|
||||
* HA tables.
|
||||
*
|
||||
* LOCKING: the frame_rcvd_lock needs to be held since this parses the frame
|
||||
* buffer.
|
||||
*/
|
||||
static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr)
|
||||
{
|
||||
if (phy->sas_phy.frame_rcvd[0] == 0x34
|
||||
&& phy->sas_phy.oob_mode == SATA_OOB_MODE) {
|
||||
struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha;
|
||||
/* FIS device-to-host */
|
||||
u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr);
|
||||
|
||||
addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy);
|
||||
*(__be64 *)sas_addr = cpu_to_be64(addr);
|
||||
} else {
|
||||
struct sas_identify_frame *idframe =
|
||||
(void *) phy->sas_phy.frame_rcvd;
|
||||
memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl,
|
||||
int edb_id, int phy_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
int edb_el = edb_id + ascb->edb_index;
|
||||
struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el];
|
||||
struct asd_phy *phy = &ascb->ha->phys[phy_id];
|
||||
struct sas_ha_struct *sas_ha = phy->sas_phy.ha;
|
||||
u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2];
|
||||
|
||||
size = min(size, (u16) sizeof(phy->frame_rcvd));
|
||||
|
||||
spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags);
|
||||
memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size);
|
||||
phy->sas_phy.frame_rcvd_size = size;
|
||||
asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr);
|
||||
spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags);
|
||||
asd_dump_frame_rcvd(phy, dl);
|
||||
sas_ha->notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED);
|
||||
}
|
||||
|
||||
static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl,
|
||||
int phy_id)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
||||
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
|
||||
u8 lr_error = dl->status_block[1];
|
||||
u8 retries_left = dl->status_block[2];
|
||||
|
||||
switch (lr_error) {
|
||||
case 0:
|
||||
ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id);
|
||||
break;
|
||||
case 1:
|
||||
ASD_DPRINTK("phy%d: Loss of signal\n", phy_id);
|
||||
break;
|
||||
case 2:
|
||||
ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id);
|
||||
break;
|
||||
case 3:
|
||||
ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id);
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n",
|
||||
phy_id, lr_error);
|
||||
break;
|
||||
}
|
||||
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
sas_phy_disconnected(sas_phy);
|
||||
sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR);
|
||||
|
||||
if (retries_left == 0) {
|
||||
int num = 1;
|
||||
struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num,
|
||||
GFP_ATOMIC);
|
||||
if (!cp) {
|
||||
asd_printk("%s: out of memory\n", __FUNCTION__);
|
||||
goto out;
|
||||
}
|
||||
ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n",
|
||||
phy_id);
|
||||
asd_build_control_phy(cp, phy_id, ENABLE_PHY);
|
||||
if (asd_post_ascb_list(ascb->ha, cp, 1) != 0)
|
||||
asd_ascb_free(cp);
|
||||
}
|
||||
out:
|
||||
;
|
||||
}
|
||||
|
||||
static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl,
|
||||
int phy_id)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha;
|
||||
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
|
||||
u8 reg = dl->status_block[1];
|
||||
u32 cont = dl->status_block[2] << ((reg & 3)*8);
|
||||
|
||||
reg &= ~3;
|
||||
switch (reg) {
|
||||
case LmPRMSTAT0BYTE0:
|
||||
switch (cont) {
|
||||
case LmBROADCH:
|
||||
case LmBROADRVCH0:
|
||||
case LmBROADRVCH1:
|
||||
case LmBROADSES:
|
||||
ASD_DPRINTK("phy%d: BROADCAST change received:%d\n",
|
||||
phy_id, cont);
|
||||
spin_lock_irqsave(&sas_phy->sas_prim_lock, flags);
|
||||
sas_phy->sas_prim = ffs(cont);
|
||||
spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags);
|
||||
sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD);
|
||||
break;
|
||||
|
||||
case LmUNKNOWNP:
|
||||
ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
|
||||
phy_id, reg, cont);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LmPRMSTAT1BYTE0:
|
||||
switch (cont) {
|
||||
case LmHARDRST:
|
||||
ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n",
|
||||
phy_id);
|
||||
/* The sequencer disables all phys on that port.
|
||||
* We have to re-enable the phys ourselves. */
|
||||
sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET);
|
||||
break;
|
||||
|
||||
default:
|
||||
ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n",
|
||||
phy_id, reg, cont);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("unknown primitive register:0x%x\n",
|
||||
dl->status_block[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB
|
||||
* @ascb: pointer to Empty SCB
|
||||
* @edb_id: index [0,6] to the empty data buffer which is to be invalidated
|
||||
*
|
||||
* After an EDB has been invalidated, if all EDBs in this ESCB have been
|
||||
* invalidated, the ESCB is posted back to the sequencer.
|
||||
* Context is tasklet/IRQ.
|
||||
*/
|
||||
void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id)
|
||||
{
|
||||
struct asd_seq_data *seq = &ascb->ha->seq;
|
||||
struct empty_scb *escb = &ascb->scb->escb;
|
||||
struct sg_el *eb = &escb->eb[edb_id];
|
||||
struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id];
|
||||
|
||||
memset(edb->vaddr, 0, ASD_EDB_SIZE);
|
||||
eb->flags |= ELEMENT_NOT_VALID;
|
||||
escb->num_valid--;
|
||||
|
||||
if (escb->num_valid == 0) {
|
||||
int i;
|
||||
/* ASD_DPRINTK("reposting escb: vaddr: 0x%p, "
|
||||
"dma_handle: 0x%08llx, next: 0x%08llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
ascb->dma_scb.vaddr,
|
||||
(u64)ascb->dma_scb.dma_handle,
|
||||
le64_to_cpu(ascb->scb->header.next_scb),
|
||||
le16_to_cpu(ascb->scb->header.index),
|
||||
ascb->scb->header.opcode);
|
||||
*/
|
||||
escb->num_valid = ASD_EDBS_PER_SCB;
|
||||
for (i = 0; i < ASD_EDBS_PER_SCB; i++)
|
||||
escb->eb[i].flags = 0;
|
||||
if (!list_empty(&ascb->list))
|
||||
list_del_init(&ascb->list);
|
||||
i = asd_post_escb_list(ascb->ha, ascb, 1);
|
||||
if (i)
|
||||
asd_printk("couldn't post escb, err:%d\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
static void escb_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_ha_struct *sas_ha = &asd_ha->sas_ha;
|
||||
int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */
|
||||
u8 sb_opcode = dl->status_block[0];
|
||||
int phy_id = sb_opcode & DL_PHY_MASK;
|
||||
struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id];
|
||||
|
||||
if (edb > 6 || edb < 0) {
|
||||
ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
|
||||
edb, dl->opcode);
|
||||
ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
|
||||
sb_opcode, phy_id);
|
||||
ASD_DPRINTK("escb: vaddr: 0x%p, "
|
||||
"dma_handle: 0x%llx, next: 0x%llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
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);
|
||||
}
|
||||
|
||||
sb_opcode &= ~DL_PHY_MASK;
|
||||
|
||||
switch (sb_opcode) {
|
||||
case BYTES_DMAED:
|
||||
ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id);
|
||||
asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id);
|
||||
break;
|
||||
case PRIMITIVE_RECVD:
|
||||
ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__,
|
||||
phy_id);
|
||||
asd_primitive_rcvd_tasklet(ascb, dl, phy_id);
|
||||
break;
|
||||
case PHY_EVENT:
|
||||
ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id);
|
||||
asd_phy_event_tasklet(ascb, dl);
|
||||
break;
|
||||
case LINK_RESET_ERROR:
|
||||
ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__,
|
||||
phy_id);
|
||||
asd_link_reset_err_tasklet(ascb, dl, phy_id);
|
||||
break;
|
||||
case TIMER_EVENT:
|
||||
ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n",
|
||||
__FUNCTION__, phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
/* the device is gone */
|
||||
sas_phy_disconnected(sas_phy);
|
||||
sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT);
|
||||
break;
|
||||
case REQ_TASK_ABORT:
|
||||
ASD_DPRINTK("%s: phy%d: REQ_TASK_ABORT\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
case REQ_DEVICE_RESET:
|
||||
ASD_DPRINTK("%s: phy%d: REQ_DEVICE_RESET\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
case SIGNAL_NCQ_ERROR:
|
||||
ASD_DPRINTK("%s: phy%d: SIGNAL_NCQ_ERROR\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
case CLEAR_NCQ_ERROR:
|
||||
ASD_DPRINTK("%s: phy%d: CLEAR_NCQ_ERROR\n", __FUNCTION__,
|
||||
phy_id);
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__,
|
||||
phy_id, sb_opcode);
|
||||
ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n",
|
||||
edb, dl->opcode);
|
||||
ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n",
|
||||
sb_opcode, phy_id);
|
||||
ASD_DPRINTK("escb: vaddr: 0x%p, "
|
||||
"dma_handle: 0x%llx, next: 0x%llx, "
|
||||
"index:%d, opcode:0x%02x\n",
|
||||
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);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
asd_invalidate_edb(ascb, edb);
|
||||
}
|
||||
|
||||
int asd_init_post_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++)
|
||||
seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete;
|
||||
|
||||
ASD_DPRINTK("posting %d escbs\n", i);
|
||||
return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs);
|
||||
}
|
||||
|
||||
/* ---------- CONTROL PHY ---------- */
|
||||
|
||||
#define CONTROL_PHY_STATUS (CURRENT_DEVICE_PRESENT | CURRENT_OOB_DONE \
|
||||
| CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \
|
||||
| CURRENT_OOB_ERROR)
|
||||
|
||||
/**
|
||||
* control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb
|
||||
* @ascb: pointer to an ascb
|
||||
* @dl: pointer to the done list entry
|
||||
*
|
||||
* This function completes a CONTROL PHY scb and frees the ascb.
|
||||
* A note on LEDs:
|
||||
* - an LED blinks if there is IO though it,
|
||||
* - if a device is connected to the LED, it is lit,
|
||||
* - if no device is connected to the LED, is is dimmed (off).
|
||||
*/
|
||||
static void control_phy_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct scb *scb = ascb->scb;
|
||||
struct control_phy *control_phy = &scb->control_phy;
|
||||
u8 phy_id = control_phy->phy_id;
|
||||
struct asd_phy *phy = &ascb->ha->phys[phy_id];
|
||||
|
||||
u8 status = dl->status_block[0];
|
||||
u8 oob_status = dl->status_block[1];
|
||||
u8 oob_mode = dl->status_block[2];
|
||||
/* u8 oob_signals= dl->status_block[3]; */
|
||||
|
||||
if (status != 0) {
|
||||
ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n",
|
||||
__FUNCTION__, phy_id, status);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (control_phy->sub_func) {
|
||||
case DISABLE_PHY:
|
||||
asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
asd_control_led(asd_ha, phy_id, 0);
|
||||
ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id);
|
||||
break;
|
||||
|
||||
case ENABLE_PHY:
|
||||
asd_control_led(asd_ha, phy_id, 1);
|
||||
if (oob_status & CURRENT_OOB_DONE) {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
get_lrate_mode(phy, oob_mode);
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n",
|
||||
__FUNCTION__, phy_id,phy->sas_phy.linkrate,
|
||||
phy->sas_phy.iproto);
|
||||
} else if (oob_status & CURRENT_SPINUP_HOLD) {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__,
|
||||
phy_id);
|
||||
} else if (oob_status & CURRENT_ERR_MASK) {
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n",
|
||||
__FUNCTION__, phy_id, oob_status);
|
||||
} else if (oob_status & (CURRENT_HOT_PLUG_CNCT
|
||||
| CURRENT_DEVICE_PRESENT)) {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 1);
|
||||
ASD_DPRINTK("%s: phy%d: hot plug or device present\n",
|
||||
__FUNCTION__, phy_id);
|
||||
} else {
|
||||
asd_ha->hw_prof.enabled_phys |= (1 << phy_id);
|
||||
asd_turn_led(asd_ha, phy_id, 0);
|
||||
ASD_DPRINTK("%s: phy%d: no device present: "
|
||||
"oob_status:0x%x\n",
|
||||
__FUNCTION__, phy_id, oob_status);
|
||||
}
|
||||
break;
|
||||
case RELEASE_SPINUP_HOLD:
|
||||
case PHY_NO_OP:
|
||||
case EXECUTE_HARD_RESET:
|
||||
ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__,
|
||||
phy_id, control_phy->sub_func);
|
||||
/* XXX finish */
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__,
|
||||
phy_id, control_phy->sub_func);
|
||||
break;
|
||||
}
|
||||
out:
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
|
||||
static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd)
|
||||
{
|
||||
/* disable all speeds, then enable defaults */
|
||||
*speed_mask = SAS_SPEED_60_DIS | SAS_SPEED_30_DIS | SAS_SPEED_15_DIS
|
||||
| SATA_SPEED_30_DIS | SATA_SPEED_15_DIS;
|
||||
|
||||
switch (pd->max_sas_lrate) {
|
||||
case SAS_LINK_RATE_6_0_GBPS:
|
||||
*speed_mask &= ~SAS_SPEED_60_DIS;
|
||||
default:
|
||||
case SAS_LINK_RATE_3_0_GBPS:
|
||||
*speed_mask &= ~SAS_SPEED_30_DIS;
|
||||
case SAS_LINK_RATE_1_5_GBPS:
|
||||
*speed_mask &= ~SAS_SPEED_15_DIS;
|
||||
}
|
||||
|
||||
switch (pd->min_sas_lrate) {
|
||||
case SAS_LINK_RATE_6_0_GBPS:
|
||||
*speed_mask |= SAS_SPEED_30_DIS;
|
||||
case SAS_LINK_RATE_3_0_GBPS:
|
||||
*speed_mask |= SAS_SPEED_15_DIS;
|
||||
default:
|
||||
case SAS_LINK_RATE_1_5_GBPS:
|
||||
/* nothing to do */
|
||||
;
|
||||
}
|
||||
|
||||
switch (pd->max_sata_lrate) {
|
||||
case SAS_LINK_RATE_3_0_GBPS:
|
||||
*speed_mask &= ~SATA_SPEED_30_DIS;
|
||||
default:
|
||||
case SAS_LINK_RATE_1_5_GBPS:
|
||||
*speed_mask &= ~SATA_SPEED_15_DIS;
|
||||
}
|
||||
|
||||
switch (pd->min_sata_lrate) {
|
||||
case SAS_LINK_RATE_3_0_GBPS:
|
||||
*speed_mask |= SATA_SPEED_15_DIS;
|
||||
default:
|
||||
case SAS_LINK_RATE_1_5_GBPS:
|
||||
/* nothing to do */
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_build_control_phy -- build a CONTROL PHY SCB
|
||||
* @ascb: pointer to an ascb
|
||||
* @phy_id: phy id to control, integer
|
||||
* @subfunc: subfunction, what to actually to do the phy
|
||||
*
|
||||
* This function builds a CONTROL PHY scb. No allocation of any kind
|
||||
* is performed. @ascb is allocated with the list function.
|
||||
* The caller can override the ascb->tasklet_complete to point
|
||||
* to its own callback function. It must call asd_ascb_free()
|
||||
* at its tasklet complete function.
|
||||
* See the default implementation.
|
||||
*/
|
||||
void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc)
|
||||
{
|
||||
struct asd_phy *phy = &ascb->ha->phys[phy_id];
|
||||
struct scb *scb = ascb->scb;
|
||||
struct control_phy *control_phy = &scb->control_phy;
|
||||
|
||||
scb->header.opcode = CONTROL_PHY;
|
||||
control_phy->phy_id = (u8) phy_id;
|
||||
control_phy->sub_func = subfunc;
|
||||
|
||||
switch (subfunc) {
|
||||
case EXECUTE_HARD_RESET: /* 0x81 */
|
||||
case ENABLE_PHY: /* 0x01 */
|
||||
/* decide hot plug delay */
|
||||
control_phy->hot_plug_delay = HOTPLUG_DELAY_TIMEOUT;
|
||||
|
||||
/* decide speed mask */
|
||||
set_speed_mask(&control_phy->speed_mask, phy->phy_desc);
|
||||
|
||||
/* initiator port settings are in the hi nibble */
|
||||
if (phy->sas_phy.role == PHY_ROLE_INITIATOR)
|
||||
control_phy->port_type = SAS_PROTO_ALL << 4;
|
||||
else if (phy->sas_phy.role == PHY_ROLE_TARGET)
|
||||
control_phy->port_type = SAS_PROTO_ALL;
|
||||
else
|
||||
control_phy->port_type =
|
||||
(SAS_PROTO_ALL << 4) | SAS_PROTO_ALL;
|
||||
|
||||
/* link reset retries, this should be nominal */
|
||||
control_phy->link_reset_retries = 10;
|
||||
|
||||
case RELEASE_SPINUP_HOLD: /* 0x02 */
|
||||
/* decide the func_mask */
|
||||
control_phy->func_mask = FUNCTION_MASK_DEFAULT;
|
||||
if (phy->phy_desc->flags & ASD_SATA_SPINUP_HOLD)
|
||||
control_phy->func_mask &= ~SPINUP_HOLD_DIS;
|
||||
else
|
||||
control_phy->func_mask |= SPINUP_HOLD_DIS;
|
||||
}
|
||||
|
||||
control_phy->conn_handle = cpu_to_le16(0xFFFF);
|
||||
|
||||
ascb->tasklet_complete = control_phy_tasklet_complete;
|
||||
}
|
||||
|
||||
/* ---------- INITIATE LINK ADM TASK ---------- */
|
||||
|
||||
static void link_adm_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
u8 opcode = dl->opcode;
|
||||
struct initiate_link_adm *link_adm = &ascb->scb->link_adm;
|
||||
u8 phy_id = link_adm->phy_id;
|
||||
|
||||
if (opcode != TC_NO_ERROR) {
|
||||
asd_printk("phy%d: link adm task 0x%x completed with error "
|
||||
"0x%x\n", phy_id, link_adm->sub_func, opcode);
|
||||
}
|
||||
ASD_DPRINTK("phy%d: link adm task 0x%x: 0x%x\n",
|
||||
phy_id, link_adm->sub_func, opcode);
|
||||
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
|
||||
void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id,
|
||||
u8 subfunc)
|
||||
{
|
||||
struct scb *scb = ascb->scb;
|
||||
struct initiate_link_adm *link_adm = &scb->link_adm;
|
||||
|
||||
scb->header.opcode = INITIATE_LINK_ADM_TASK;
|
||||
|
||||
link_adm->phy_id = phy_id;
|
||||
link_adm->sub_func = subfunc;
|
||||
link_adm->conn_handle = cpu_to_le16(0xFFFF);
|
||||
|
||||
ascb->tasklet_complete = link_adm_tasklet_complete;
|
||||
}
|
||||
|
||||
/* ---------- SCB timer ---------- */
|
||||
|
||||
/**
|
||||
* asd_ascb_timedout -- called when a pending SCB's timer has expired
|
||||
* @data: unsigned long, a pointer to the ascb in question
|
||||
*
|
||||
* This is the default timeout function which does the most necessary.
|
||||
* Upper layers can implement their own timeout function, say to free
|
||||
* resources they have with this SCB, and then call this one at the
|
||||
* end of their timeout function. To do this, one should initialize
|
||||
* the ascb->timer.{function, data, expires} prior to calling the post
|
||||
* funcion. The timer is started by the post function.
|
||||
*/
|
||||
void asd_ascb_timedout(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
struct asd_seq_data *seq = &ascb->ha->seq;
|
||||
unsigned long flags;
|
||||
|
||||
ASD_DPRINTK("scb:0x%x timed out\n", ascb->scb->header.opcode);
|
||||
|
||||
spin_lock_irqsave(&seq->pend_q_lock, flags);
|
||||
seq->pending--;
|
||||
list_del_init(&ascb->list);
|
||||
spin_unlock_irqrestore(&seq->pend_q_lock, flags);
|
||||
|
||||
asd_ascb_free(ascb);
|
||||
}
|
||||
|
||||
/* ---------- CONTROL PHY ---------- */
|
||||
|
||||
/* Given the spec value, return a driver value. */
|
||||
static const int phy_func_table[] = {
|
||||
[PHY_FUNC_NOP] = PHY_NO_OP,
|
||||
[PHY_FUNC_LINK_RESET] = ENABLE_PHY,
|
||||
[PHY_FUNC_HARD_RESET] = EXECUTE_HARD_RESET,
|
||||
[PHY_FUNC_DISABLE] = DISABLE_PHY,
|
||||
[PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD,
|
||||
};
|
||||
|
||||
int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = phy->ha->lldd_ha;
|
||||
struct asd_phy_desc *pd = asd_ha->phys[phy->id].phy_desc;
|
||||
struct asd_ascb *ascb;
|
||||
struct sas_phy_linkrates *rates;
|
||||
int res = 1;
|
||||
|
||||
switch (func) {
|
||||
case PHY_FUNC_CLEAR_ERROR_LOG:
|
||||
return -ENOSYS;
|
||||
case PHY_FUNC_SET_LINK_RATE:
|
||||
rates = arg;
|
||||
if (rates->minimum_linkrate) {
|
||||
pd->min_sas_lrate = rates->minimum_linkrate;
|
||||
pd->min_sata_lrate = rates->minimum_linkrate;
|
||||
}
|
||||
if (rates->maximum_linkrate) {
|
||||
pd->max_sas_lrate = rates->maximum_linkrate;
|
||||
pd->max_sata_lrate = rates->maximum_linkrate;
|
||||
}
|
||||
func = PHY_FUNC_LINK_RESET;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
|
||||
if (!ascb)
|
||||
return -ENOMEM;
|
||||
|
||||
asd_build_control_phy(ascb, phy->id, phy_func_table[func]);
|
||||
res = asd_post_ascb_list(asd_ha, ascb , 1);
|
||||
if (res)
|
||||
asd_ascb_free(ascb);
|
||||
|
||||
return res;
|
||||
}
|
1089
drivers/scsi/aic94xx/aic94xx_sds.c
Normal file
1089
drivers/scsi/aic94xx/aic94xx_sds.c
Normal file
File diff suppressed because it is too large
Load Diff
1404
drivers/scsi/aic94xx/aic94xx_seq.c
Normal file
1404
drivers/scsi/aic94xx/aic94xx_seq.c
Normal file
File diff suppressed because it is too large
Load Diff
70
drivers/scsi/aic94xx/aic94xx_seq.h
Normal file
70
drivers/scsi/aic94xx/aic94xx_seq.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA driver sequencer 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_SEQ_H_
|
||||
#define _AIC94XX_SEQ_H_
|
||||
|
||||
#define CSEQ_NUM_VECS 3
|
||||
#define LSEQ_NUM_VECS 11
|
||||
|
||||
#define SAS_RAZOR_SEQUENCER_FW_FILE "aic94xx-seq.fw"
|
||||
|
||||
/* Note: All quantites in the sequencer file are little endian */
|
||||
struct sequencer_file_header {
|
||||
/* Checksum of the entire contents of the sequencer excluding
|
||||
* these four bytes */
|
||||
u32 csum;
|
||||
/* numeric major version */
|
||||
u32 major;
|
||||
/* numeric minor version */
|
||||
u32 minor;
|
||||
/* version string printed by driver */
|
||||
char version[16];
|
||||
u32 cseq_table_offset;
|
||||
u32 cseq_table_size;
|
||||
u32 lseq_table_offset;
|
||||
u32 lseq_table_size;
|
||||
u32 cseq_code_offset;
|
||||
u32 cseq_code_size;
|
||||
u32 lseq_code_offset;
|
||||
u32 lseq_code_size;
|
||||
u16 mode2_task;
|
||||
u16 cseq_idle_loop;
|
||||
u16 lseq_idle_loop;
|
||||
} __attribute__((packed));
|
||||
|
||||
#ifdef __KERNEL__
|
||||
int asd_pause_cseq(struct asd_ha_struct *asd_ha);
|
||||
int asd_unpause_cseq(struct asd_ha_struct *asd_ha);
|
||||
int asd_pause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
int asd_unpause_lseq(struct asd_ha_struct *asd_ha, u8 lseq_mask);
|
||||
int asd_init_seqs(struct asd_ha_struct *asd_ha);
|
||||
int asd_start_seqs(struct asd_ha_struct *asd_ha);
|
||||
|
||||
void asd_update_port_links(struct asd_sas_phy *phy);
|
||||
#endif
|
||||
|
||||
#endif
|
642
drivers/scsi/aic94xx/aic94xx_task.c
Normal file
642
drivers/scsi/aic94xx/aic94xx_task.c
Normal file
@ -0,0 +1,642 @@
|
||||
/*
|
||||
* Aic94xx SAS/SATA Tasks
|
||||
*
|
||||
* 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/spinlock.h>
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_sas.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
static void asd_unbuild_ata_ascb(struct asd_ascb *a);
|
||||
static void asd_unbuild_smp_ascb(struct asd_ascb *a);
|
||||
static void asd_unbuild_ssp_ascb(struct asd_ascb *a);
|
||||
|
||||
static inline void asd_can_dequeue(struct asd_ha_struct *asd_ha, int num)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
|
||||
asd_ha->seq.can_queue += num;
|
||||
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
|
||||
}
|
||||
|
||||
/* PCI_DMA_... to our direction translation.
|
||||
*/
|
||||
static const u8 data_dir_flags[] = {
|
||||
[PCI_DMA_BIDIRECTIONAL] = DATA_DIR_BYRECIPIENT, /* UNSPECIFIED */
|
||||
[PCI_DMA_TODEVICE] = DATA_DIR_OUT, /* OUTBOUND */
|
||||
[PCI_DMA_FROMDEVICE] = DATA_DIR_IN, /* INBOUND */
|
||||
[PCI_DMA_NONE] = DATA_DIR_NONE, /* NO TRANSFER */
|
||||
};
|
||||
|
||||
static inline int asd_map_scatterlist(struct sas_task *task,
|
||||
struct sg_el *sg_arr,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct asd_ascb *ascb = task->lldd_task;
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct scatterlist *sc;
|
||||
int num_sg, res;
|
||||
|
||||
if (task->data_dir == PCI_DMA_NONE)
|
||||
return 0;
|
||||
|
||||
if (task->num_scatter == 0) {
|
||||
void *p = task->scatter;
|
||||
dma_addr_t dma = pci_map_single(asd_ha->pcidev, p,
|
||||
task->total_xfer_len,
|
||||
task->data_dir);
|
||||
sg_arr[0].bus_addr = cpu_to_le64((u64)dma);
|
||||
sg_arr[0].size = cpu_to_le32(task->total_xfer_len);
|
||||
sg_arr[0].flags |= ASD_SG_EL_LIST_EOL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
num_sg = pci_map_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
|
||||
task->data_dir);
|
||||
if (num_sg == 0)
|
||||
return -ENOMEM;
|
||||
|
||||
if (num_sg > 3) {
|
||||
int i;
|
||||
|
||||
ascb->sg_arr = asd_alloc_coherent(asd_ha,
|
||||
num_sg*sizeof(struct sg_el),
|
||||
gfp_flags);
|
||||
if (!ascb->sg_arr) {
|
||||
res = -ENOMEM;
|
||||
goto err_unmap;
|
||||
}
|
||||
for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
|
||||
struct sg_el *sg =
|
||||
&((struct sg_el *)ascb->sg_arr->vaddr)[i];
|
||||
sg->bus_addr = cpu_to_le64((u64)sg_dma_address(sc));
|
||||
sg->size = cpu_to_le32((u32)sg_dma_len(sc));
|
||||
if (i == num_sg-1)
|
||||
sg->flags |= ASD_SG_EL_LIST_EOL;
|
||||
}
|
||||
|
||||
for (sc = task->scatter, i = 0; i < 2; i++, sc++) {
|
||||
sg_arr[i].bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(sc));
|
||||
sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc));
|
||||
}
|
||||
sg_arr[1].next_sg_offs = 2 * sizeof(*sg_arr);
|
||||
sg_arr[1].flags |= ASD_SG_EL_LIST_EOS;
|
||||
|
||||
memset(&sg_arr[2], 0, sizeof(*sg_arr));
|
||||
sg_arr[2].bus_addr=cpu_to_le64((u64)ascb->sg_arr->dma_handle);
|
||||
} else {
|
||||
int i;
|
||||
for (sc = task->scatter, i = 0; i < num_sg; i++, sc++) {
|
||||
sg_arr[i].bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(sc));
|
||||
sg_arr[i].size = cpu_to_le32((u32)sg_dma_len(sc));
|
||||
}
|
||||
sg_arr[i-1].flags |= ASD_SG_EL_LIST_EOL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_unmap:
|
||||
pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
|
||||
task->data_dir);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void asd_unmap_scatterlist(struct asd_ascb *ascb)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_task *task = ascb->uldd_task;
|
||||
|
||||
if (task->data_dir == PCI_DMA_NONE)
|
||||
return;
|
||||
|
||||
if (task->num_scatter == 0) {
|
||||
dma_addr_t dma = (dma_addr_t)
|
||||
le64_to_cpu(ascb->scb->ssp_task.sg_element[0].bus_addr);
|
||||
pci_unmap_single(ascb->ha->pcidev, dma, task->total_xfer_len,
|
||||
task->data_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
asd_free_coherent(asd_ha, ascb->sg_arr);
|
||||
pci_unmap_sg(asd_ha->pcidev, task->scatter, task->num_scatter,
|
||||
task->data_dir);
|
||||
}
|
||||
|
||||
/* ---------- Task complete tasklet ---------- */
|
||||
|
||||
static void asd_get_response_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct sas_task *task = ascb->uldd_task;
|
||||
struct task_status_struct *ts = &task->task_status;
|
||||
unsigned long flags;
|
||||
struct tc_resp_sb_struct {
|
||||
__le16 index_escb;
|
||||
u8 len_lsb;
|
||||
u8 flags;
|
||||
} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
|
||||
|
||||
/* int size = ((resp_sb->flags & 7) << 8) | resp_sb->len_lsb; */
|
||||
int edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
|
||||
struct asd_ascb *escb;
|
||||
struct asd_dma_tok *edb;
|
||||
void *r;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
|
||||
escb = asd_tc_index_find(&asd_ha->seq,
|
||||
(int)le16_to_cpu(resp_sb->index_escb));
|
||||
spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
|
||||
|
||||
if (!escb) {
|
||||
ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ts->buf_valid_size = 0;
|
||||
edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
|
||||
r = edb->vaddr;
|
||||
if (task->task_proto == SAS_PROTO_SSP) {
|
||||
struct ssp_response_iu *iu =
|
||||
r + 16 + sizeof(struct ssp_frame_hdr);
|
||||
|
||||
ts->residual = le32_to_cpu(*(__le32 *)r);
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
if (iu->datapres == 0)
|
||||
ts->stat = iu->status;
|
||||
else if (iu->datapres == 1)
|
||||
ts->stat = iu->resp_data[3];
|
||||
else if (iu->datapres == 2) {
|
||||
ts->stat = SAM_CHECK_COND;
|
||||
ts->buf_valid_size = min((u32) SAS_STATUS_BUF_SIZE,
|
||||
be32_to_cpu(iu->sense_data_len));
|
||||
memcpy(ts->buf, iu->sense_data, ts->buf_valid_size);
|
||||
if (iu->status != SAM_CHECK_COND) {
|
||||
ASD_DPRINTK("device %llx sent sense data, but "
|
||||
"stat(0x%x) is not CHECK_CONDITION"
|
||||
"\n",
|
||||
SAS_ADDR(task->dev->sas_addr),
|
||||
ts->stat);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
struct ata_task_resp *resp = (void *) &ts->buf[0];
|
||||
|
||||
ts->residual = le32_to_cpu(*(__le32 *)r);
|
||||
|
||||
if (SAS_STATUS_BUF_SIZE >= sizeof(*resp)) {
|
||||
resp->frame_len = le16_to_cpu(*(__le16 *)(r+6));
|
||||
memcpy(&resp->ending_fis[0], r+16, 24);
|
||||
ts->buf_valid_size = sizeof(*resp);
|
||||
}
|
||||
}
|
||||
|
||||
asd_invalidate_edb(escb, edb_id);
|
||||
}
|
||||
|
||||
static void asd_task_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct sas_task *task = ascb->uldd_task;
|
||||
struct task_status_struct *ts = &task->task_status;
|
||||
unsigned long flags;
|
||||
u8 opcode = dl->opcode;
|
||||
|
||||
asd_can_dequeue(ascb->ha, 1);
|
||||
|
||||
Again:
|
||||
switch (opcode) {
|
||||
case TC_NO_ERROR:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAM_GOOD;
|
||||
break;
|
||||
case TC_UNDERRUN:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_DATA_UNDERRUN;
|
||||
ts->residual = le32_to_cpu(*(__le32 *)dl->status_block);
|
||||
break;
|
||||
case TC_OVERRUN:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_DATA_OVERRUN;
|
||||
ts->residual = 0;
|
||||
break;
|
||||
case TC_SSP_RESP:
|
||||
case TC_ATA_RESP:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_PROTO_RESPONSE;
|
||||
asd_get_response_tasklet(ascb, dl);
|
||||
break;
|
||||
case TF_OPEN_REJECT:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_OPEN_REJECT;
|
||||
if (dl->status_block[1] & 2)
|
||||
ts->open_rej_reason = 1 + dl->status_block[2];
|
||||
else if (dl->status_block[1] & 1)
|
||||
ts->open_rej_reason = (dl->status_block[2] >> 4)+10;
|
||||
else
|
||||
ts->open_rej_reason = SAS_OREJ_UNKNOWN;
|
||||
break;
|
||||
case TF_OPEN_TO:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_OPEN_TO;
|
||||
break;
|
||||
case TF_PHY_DOWN:
|
||||
case TU_PHY_DOWN:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_PHY_DOWN;
|
||||
break;
|
||||
case TI_PHY_DOWN:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_PHY_DOWN;
|
||||
break;
|
||||
case TI_BREAK:
|
||||
case TI_PROTO_ERR:
|
||||
case TI_NAK:
|
||||
case TI_ACK_NAK_TO:
|
||||
case TF_SMP_XMIT_RCV_ERR:
|
||||
case TC_ATA_R_ERR_RECV:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_INTERRUPTED;
|
||||
break;
|
||||
case TF_BREAK:
|
||||
case TU_BREAK:
|
||||
case TU_ACK_NAK_TO:
|
||||
case TF_SMPRSP_TO:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_DEV_NO_RESPONSE;
|
||||
break;
|
||||
case TF_NAK_RECV:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_NAK_R_ERR;
|
||||
break;
|
||||
case TA_I_T_NEXUS_LOSS:
|
||||
opcode = dl->status_block[0];
|
||||
goto Again;
|
||||
break;
|
||||
case TF_INV_CONN_HANDLE:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_DEVICE_UNKNOWN;
|
||||
break;
|
||||
case TF_REQUESTED_N_PENDING:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_PENDING;
|
||||
break;
|
||||
case TC_TASK_CLEARED:
|
||||
case TA_ON_REQ:
|
||||
ts->resp = SAS_TASK_COMPLETE;
|
||||
ts->stat = SAS_ABORTED_TASK;
|
||||
break;
|
||||
|
||||
case TF_NO_SMP_CONN:
|
||||
case TF_TMF_NO_CTX:
|
||||
case TF_TMF_NO_TAG:
|
||||
case TF_TMF_TAG_FREE:
|
||||
case TF_TMF_TASK_DONE:
|
||||
case TF_TMF_NO_CONN_HANDLE:
|
||||
case TF_IRTT_TO:
|
||||
case TF_IU_SHORT:
|
||||
case TF_DATA_OFFS_ERR:
|
||||
ts->resp = SAS_TASK_UNDELIVERED;
|
||||
ts->stat = SAS_DEV_NO_RESPONSE;
|
||||
break;
|
||||
|
||||
case TC_LINK_ADM_RESP:
|
||||
case TC_CONTROL_PHY:
|
||||
case TC_RESUME:
|
||||
case TC_PARTIAL_SG_LIST:
|
||||
default:
|
||||
ASD_DPRINTK("%s: dl opcode: 0x%x?\n", __FUNCTION__, opcode);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (task->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
asd_unbuild_ata_ascb(ascb);
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
asd_unbuild_smp_ascb(ascb);
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
asd_unbuild_ssp_ascb(ascb);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
task->task_state_flags &= ~SAS_TASK_STATE_PENDING;
|
||||
task->task_state_flags |= SAS_TASK_STATE_DONE;
|
||||
if (unlikely((task->task_state_flags & SAS_TASK_STATE_ABORTED))) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
ASD_DPRINTK("task 0x%p done with opcode 0x%x resp 0x%x "
|
||||
"stat 0x%x but aborted by upper layer!\n",
|
||||
task, opcode, ts->resp, ts->stat);
|
||||
complete(&ascb->completion);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
task->lldd_task = NULL;
|
||||
asd_ascb_free(ascb);
|
||||
mb();
|
||||
task->task_done(task);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- ATA ---------- */
|
||||
|
||||
static int asd_build_ata_ascb(struct asd_ascb *ascb, struct sas_task *task,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct domain_device *dev = task->dev;
|
||||
struct scb *scb;
|
||||
u8 flags;
|
||||
int res = 0;
|
||||
|
||||
scb = ascb->scb;
|
||||
|
||||
if (unlikely(task->ata_task.device_control_reg_update))
|
||||
scb->header.opcode = CONTROL_ATA_DEV;
|
||||
else if (dev->sata_dev.command_set == ATA_COMMAND_SET)
|
||||
scb->header.opcode = INITIATE_ATA_TASK;
|
||||
else
|
||||
scb->header.opcode = INITIATE_ATAPI_TASK;
|
||||
|
||||
scb->ata_task.proto_conn_rate = (1 << 5); /* STP */
|
||||
if (dev->port->oob_mode == SAS_OOB_MODE)
|
||||
scb->ata_task.proto_conn_rate |= dev->linkrate;
|
||||
|
||||
scb->ata_task.total_xfer_len = cpu_to_le32(task->total_xfer_len);
|
||||
scb->ata_task.fis = task->ata_task.fis;
|
||||
scb->ata_task.fis.fis_type = 0x27;
|
||||
if (likely(!task->ata_task.device_control_reg_update))
|
||||
scb->ata_task.fis.flags |= 0x80; /* C=1: update ATA cmd reg */
|
||||
scb->ata_task.fis.flags &= 0xF0; /* PM_PORT field shall be 0 */
|
||||
if (dev->sata_dev.command_set == ATAPI_COMMAND_SET)
|
||||
memcpy(scb->ata_task.atapi_packet, task->ata_task.atapi_packet,
|
||||
16);
|
||||
scb->ata_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->ata_task.conn_handle = cpu_to_le16(
|
||||
(u16)(unsigned long)dev->lldd_dev);
|
||||
|
||||
if (likely(!task->ata_task.device_control_reg_update)) {
|
||||
flags = 0;
|
||||
if (task->ata_task.dma_xfer)
|
||||
flags |= DATA_XFER_MODE_DMA;
|
||||
if (task->ata_task.use_ncq &&
|
||||
dev->sata_dev.command_set != ATAPI_COMMAND_SET)
|
||||
flags |= ATA_Q_TYPE_NCQ;
|
||||
flags |= data_dir_flags[task->data_dir];
|
||||
scb->ata_task.ata_flags = flags;
|
||||
|
||||
scb->ata_task.retry_count = task->ata_task.retry_count;
|
||||
|
||||
flags = 0;
|
||||
if (task->ata_task.set_affil_pol)
|
||||
flags |= SET_AFFIL_POLICY;
|
||||
if (task->ata_task.stp_affil_pol)
|
||||
flags |= STP_AFFIL_POLICY;
|
||||
scb->ata_task.flags = flags;
|
||||
}
|
||||
ascb->tasklet_complete = asd_task_tasklet_complete;
|
||||
|
||||
if (likely(!task->ata_task.device_control_reg_update))
|
||||
res = asd_map_scatterlist(task, scb->ata_task.sg_element,
|
||||
gfp_flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void asd_unbuild_ata_ascb(struct asd_ascb *a)
|
||||
{
|
||||
asd_unmap_scatterlist(a);
|
||||
}
|
||||
|
||||
/* ---------- SMP ---------- */
|
||||
|
||||
static int asd_build_smp_ascb(struct asd_ascb *ascb, struct sas_task *task,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
struct domain_device *dev = task->dev;
|
||||
struct scb *scb;
|
||||
|
||||
pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_req, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
pci_map_sg(asd_ha->pcidev, &task->smp_task.smp_resp, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
|
||||
scb = ascb->scb;
|
||||
|
||||
scb->header.opcode = INITIATE_SMP_TASK;
|
||||
|
||||
scb->smp_task.proto_conn_rate = dev->linkrate;
|
||||
|
||||
scb->smp_task.smp_req.bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_req));
|
||||
scb->smp_task.smp_req.size =
|
||||
cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_req)-4);
|
||||
|
||||
scb->smp_task.smp_resp.bus_addr =
|
||||
cpu_to_le64((u64)sg_dma_address(&task->smp_task.smp_resp));
|
||||
scb->smp_task.smp_resp.size =
|
||||
cpu_to_le32((u32)sg_dma_len(&task->smp_task.smp_resp)-4);
|
||||
|
||||
scb->smp_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->smp_task.conn_handle = cpu_to_le16((u16)
|
||||
(unsigned long)dev->lldd_dev);
|
||||
|
||||
ascb->tasklet_complete = asd_task_tasklet_complete;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void asd_unbuild_smp_ascb(struct asd_ascb *a)
|
||||
{
|
||||
struct sas_task *task = a->uldd_task;
|
||||
|
||||
BUG_ON(!task);
|
||||
pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_req, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
pci_unmap_sg(a->ha->pcidev, &task->smp_task.smp_resp, 1,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
}
|
||||
|
||||
/* ---------- SSP ---------- */
|
||||
|
||||
static int asd_build_ssp_ascb(struct asd_ascb *ascb, struct sas_task *task,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct domain_device *dev = task->dev;
|
||||
struct scb *scb;
|
||||
int res = 0;
|
||||
|
||||
scb = ascb->scb;
|
||||
|
||||
scb->header.opcode = INITIATE_SSP_TASK;
|
||||
|
||||
scb->ssp_task.proto_conn_rate = (1 << 4); /* SSP */
|
||||
scb->ssp_task.proto_conn_rate |= dev->linkrate;
|
||||
scb->ssp_task.total_xfer_len = cpu_to_le32(task->total_xfer_len);
|
||||
scb->ssp_task.ssp_frame.frame_type = SSP_DATA;
|
||||
memcpy(scb->ssp_task.ssp_frame.hashed_dest_addr, dev->hashed_sas_addr,
|
||||
HASHED_SAS_ADDR_SIZE);
|
||||
memcpy(scb->ssp_task.ssp_frame.hashed_src_addr,
|
||||
dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
scb->ssp_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
|
||||
|
||||
memcpy(scb->ssp_task.ssp_cmd.lun, task->ssp_task.LUN, 8);
|
||||
if (task->ssp_task.enable_first_burst)
|
||||
scb->ssp_task.ssp_cmd.efb_prio_attr |= EFB_MASK;
|
||||
scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_prio << 3);
|
||||
scb->ssp_task.ssp_cmd.efb_prio_attr |= (task->ssp_task.task_attr & 7);
|
||||
memcpy(scb->ssp_task.ssp_cmd.cdb, task->ssp_task.cdb, 16);
|
||||
|
||||
scb->ssp_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->ssp_task.conn_handle = cpu_to_le16(
|
||||
(u16)(unsigned long)dev->lldd_dev);
|
||||
scb->ssp_task.data_dir = data_dir_flags[task->data_dir];
|
||||
scb->ssp_task.retry_count = scb->ssp_task.retry_count;
|
||||
|
||||
ascb->tasklet_complete = asd_task_tasklet_complete;
|
||||
|
||||
res = asd_map_scatterlist(task, scb->ssp_task.sg_element, gfp_flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void asd_unbuild_ssp_ascb(struct asd_ascb *a)
|
||||
{
|
||||
asd_unmap_scatterlist(a);
|
||||
}
|
||||
|
||||
/* ---------- Execute Task ---------- */
|
||||
|
||||
static inline int asd_can_queue(struct asd_ha_struct *asd_ha, int num)
|
||||
{
|
||||
int res = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.pend_q_lock, flags);
|
||||
if ((asd_ha->seq.can_queue - num) < 0)
|
||||
res = -SAS_QUEUE_FULL;
|
||||
else
|
||||
asd_ha->seq.can_queue -= num;
|
||||
spin_unlock_irqrestore(&asd_ha->seq.pend_q_lock, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_execute_task(struct sas_task *task, const int num,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
int res = 0;
|
||||
LIST_HEAD(alist);
|
||||
struct sas_task *t = task;
|
||||
struct asd_ascb *ascb = NULL, *a;
|
||||
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
|
||||
|
||||
res = asd_can_queue(asd_ha, num);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
res = num;
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, gfp_flags);
|
||||
if (res) {
|
||||
res = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
__list_add(&alist, ascb->list.prev, &ascb->list);
|
||||
list_for_each_entry(a, &alist, list) {
|
||||
a->uldd_task = t;
|
||||
t->lldd_task = a;
|
||||
t = list_entry(t->list.next, struct sas_task, list);
|
||||
}
|
||||
list_for_each_entry(a, &alist, list) {
|
||||
t = a->uldd_task;
|
||||
a->uldd_timer = 1;
|
||||
if (t->task_proto & SAS_PROTO_STP)
|
||||
t->task_proto = SAS_PROTO_STP;
|
||||
switch (t->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
res = asd_build_ata_ascb(a, t, gfp_flags);
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
res = asd_build_smp_ascb(a, t, gfp_flags);
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
res = asd_build_ssp_ascb(a, t, gfp_flags);
|
||||
break;
|
||||
default:
|
||||
asd_printk("unknown sas_task proto: 0x%x\n",
|
||||
t->task_proto);
|
||||
res = -ENOMEM;
|
||||
break;
|
||||
}
|
||||
if (res)
|
||||
goto out_err_unmap;
|
||||
}
|
||||
list_del_init(&alist);
|
||||
|
||||
res = asd_post_ascb_list(asd_ha, ascb, num);
|
||||
if (unlikely(res)) {
|
||||
a = NULL;
|
||||
__list_add(&alist, ascb->list.prev, &ascb->list);
|
||||
goto out_err_unmap;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out_err_unmap:
|
||||
{
|
||||
struct asd_ascb *b = a;
|
||||
list_for_each_entry(a, &alist, list) {
|
||||
if (a == b)
|
||||
break;
|
||||
t = a->uldd_task;
|
||||
switch (t->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
asd_unbuild_ata_ascb(a);
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
asd_unbuild_smp_ascb(a);
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
asd_unbuild_ssp_ascb(a);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
t->lldd_task = NULL;
|
||||
}
|
||||
}
|
||||
list_del_init(&alist);
|
||||
out_err:
|
||||
if (ascb)
|
||||
asd_ascb_free_list(ascb);
|
||||
asd_can_dequeue(asd_ha, num);
|
||||
return res;
|
||||
}
|
636
drivers/scsi/aic94xx/aic94xx_tmf.c
Normal file
636
drivers/scsi/aic94xx/aic94xx_tmf.c
Normal file
@ -0,0 +1,636 @@
|
||||
/*
|
||||
* Aic94xx Task Management Functions
|
||||
*
|
||||
* 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/spinlock.h>
|
||||
#include "aic94xx.h"
|
||||
#include "aic94xx_sas.h"
|
||||
#include "aic94xx_hwi.h"
|
||||
|
||||
/* ---------- Internal enqueue ---------- */
|
||||
|
||||
static int asd_enqueue_internal(struct asd_ascb *ascb,
|
||||
void (*tasklet_complete)(struct asd_ascb *,
|
||||
struct done_list_struct *),
|
||||
void (*timed_out)(unsigned long))
|
||||
{
|
||||
int res;
|
||||
|
||||
ascb->tasklet_complete = tasklet_complete;
|
||||
ascb->uldd_timer = 1;
|
||||
|
||||
ascb->timer.data = (unsigned long) ascb;
|
||||
ascb->timer.function = timed_out;
|
||||
ascb->timer.expires = jiffies + AIC94XX_SCB_TIMEOUT;
|
||||
|
||||
add_timer(&ascb->timer);
|
||||
|
||||
res = asd_post_ascb_list(ascb->ha, ascb, 1);
|
||||
if (unlikely(res))
|
||||
del_timer(&ascb->timer);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline void asd_timedout_common(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
struct asd_seq_data *seq = &ascb->ha->seq;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&seq->pend_q_lock, flags);
|
||||
seq->pending--;
|
||||
list_del_init(&ascb->list);
|
||||
spin_unlock_irqrestore(&seq->pend_q_lock, flags);
|
||||
}
|
||||
|
||||
/* ---------- CLEAR NEXUS ---------- */
|
||||
|
||||
static void asd_clear_nexus_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
ASD_DPRINTK("%s: here\n", __FUNCTION__);
|
||||
if (!del_timer(&ascb->timer)) {
|
||||
ASD_DPRINTK("%s: couldn't delete timer\n", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
ASD_DPRINTK("%s: opcode: 0x%x\n", __FUNCTION__, dl->opcode);
|
||||
ascb->uldd_task = (void *) (unsigned long) dl->opcode;
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
static void asd_clear_nexus_timedout(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
|
||||
ASD_DPRINTK("%s: here\n", __FUNCTION__);
|
||||
asd_timedout_common(data);
|
||||
ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
#define CLEAR_NEXUS_PRE \
|
||||
ASD_DPRINTK("%s: PRE\n", __FUNCTION__); \
|
||||
res = 1; \
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); \
|
||||
if (!ascb) \
|
||||
return -ENOMEM; \
|
||||
\
|
||||
scb = ascb->scb; \
|
||||
scb->header.opcode = CLEAR_NEXUS
|
||||
|
||||
#define CLEAR_NEXUS_POST \
|
||||
ASD_DPRINTK("%s: POST\n", __FUNCTION__); \
|
||||
res = asd_enqueue_internal(ascb, asd_clear_nexus_tasklet_complete, \
|
||||
asd_clear_nexus_timedout); \
|
||||
if (res) \
|
||||
goto out_err; \
|
||||
ASD_DPRINTK("%s: clear nexus posted, waiting...\n", __FUNCTION__); \
|
||||
wait_for_completion(&ascb->completion); \
|
||||
res = (int) (unsigned long) ascb->uldd_task; \
|
||||
if (res == TC_NO_ERROR) \
|
||||
res = TMF_RESP_FUNC_COMPLETE; \
|
||||
out_err: \
|
||||
asd_ascb_free(ascb); \
|
||||
return res
|
||||
|
||||
int asd_clear_nexus_ha(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = sas_ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_ADAPTER;
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
int asd_clear_nexus_port(struct asd_sas_port *port)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_PORT;
|
||||
scb->clear_nexus.conn_mask = port->phy_mask;
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int asd_clear_nexus_I_T(struct domain_device *dev)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_I_T;
|
||||
scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
|
||||
if (dev->tproto)
|
||||
scb->clear_nexus.flags |= SUSPEND_TX;
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
dev->lldd_dev);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int asd_clear_nexus_I_T_L(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_I_T_L;
|
||||
scb->clear_nexus.flags = SEND_Q | EXEC_Q | NOTINQ;
|
||||
if (dev->tproto)
|
||||
scb->clear_nexus.flags |= SUSPEND_TX;
|
||||
memcpy(scb->clear_nexus.ssp_task.lun, lun, 8);
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
dev->lldd_dev);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
static int asd_clear_nexus_tag(struct sas_task *task)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_TAG;
|
||||
memcpy(scb->clear_nexus.ssp_task.lun, task->ssp_task.LUN, 8);
|
||||
scb->clear_nexus.ssp_task.tag = tascb->tag;
|
||||
if (task->dev->tproto)
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
task->dev->lldd_dev);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
static int asd_clear_nexus_index(struct sas_task *task)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = task->dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
struct asd_ascb *ascb;
|
||||
struct scb *scb;
|
||||
int res;
|
||||
|
||||
CLEAR_NEXUS_PRE;
|
||||
scb->clear_nexus.nexus = NEXUS_TRANS_CX;
|
||||
if (task->dev->tproto)
|
||||
scb->clear_nexus.conn_handle = cpu_to_le16((u16)(unsigned long)
|
||||
task->dev->lldd_dev);
|
||||
scb->clear_nexus.index = cpu_to_le16(tascb->tc_index);
|
||||
CLEAR_NEXUS_POST;
|
||||
}
|
||||
|
||||
/* ---------- TMFs ---------- */
|
||||
|
||||
static void asd_tmf_timedout(unsigned long data)
|
||||
{
|
||||
struct asd_ascb *ascb = (void *) data;
|
||||
|
||||
ASD_DPRINTK("tmf timed out\n");
|
||||
asd_timedout_common(data);
|
||||
ascb->uldd_task = (void *) TMF_RESP_FUNC_FAILED;
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
static int asd_get_tmf_resp_tasklet(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = ascb->ha;
|
||||
unsigned long flags;
|
||||
struct tc_resp_sb_struct {
|
||||
__le16 index_escb;
|
||||
u8 len_lsb;
|
||||
u8 flags;
|
||||
} __attribute__ ((packed)) *resp_sb = (void *) dl->status_block;
|
||||
|
||||
int edb_id = ((resp_sb->flags & 0x70) >> 4)-1;
|
||||
struct asd_ascb *escb;
|
||||
struct asd_dma_tok *edb;
|
||||
struct ssp_frame_hdr *fh;
|
||||
struct ssp_response_iu *ru;
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
|
||||
ASD_DPRINTK("tmf resp tasklet\n");
|
||||
|
||||
spin_lock_irqsave(&asd_ha->seq.tc_index_lock, flags);
|
||||
escb = asd_tc_index_find(&asd_ha->seq,
|
||||
(int)le16_to_cpu(resp_sb->index_escb));
|
||||
spin_unlock_irqrestore(&asd_ha->seq.tc_index_lock, flags);
|
||||
|
||||
if (!escb) {
|
||||
ASD_DPRINTK("Uh-oh! No escb for this dl?!\n");
|
||||
return res;
|
||||
}
|
||||
|
||||
edb = asd_ha->seq.edb_arr[edb_id + escb->edb_index];
|
||||
ascb->tag = *(__be16 *)(edb->vaddr+4);
|
||||
fh = edb->vaddr + 16;
|
||||
ru = edb->vaddr + 16 + sizeof(*fh);
|
||||
res = ru->status;
|
||||
if (ru->datapres == 1) /* Response data present */
|
||||
res = ru->resp_data[3];
|
||||
#if 0
|
||||
ascb->tag = fh->tag;
|
||||
#endif
|
||||
ascb->tag_valid = 1;
|
||||
|
||||
asd_invalidate_edb(escb, edb_id);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void asd_tmf_tasklet_complete(struct asd_ascb *ascb,
|
||||
struct done_list_struct *dl)
|
||||
{
|
||||
if (!del_timer(&ascb->timer))
|
||||
return;
|
||||
|
||||
ASD_DPRINTK("tmf tasklet complete\n");
|
||||
|
||||
if (dl->opcode == TC_SSP_RESP)
|
||||
ascb->uldd_task = (void *) (unsigned long)
|
||||
asd_get_tmf_resp_tasklet(ascb, dl);
|
||||
else
|
||||
ascb->uldd_task = (void *) 0xFF00 + (unsigned long) dl->opcode;
|
||||
|
||||
complete(&ascb->completion);
|
||||
}
|
||||
|
||||
static inline int asd_clear_nexus(struct sas_task *task)
|
||||
{
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
unsigned long flags;
|
||||
|
||||
ASD_DPRINTK("task not done, clearing nexus\n");
|
||||
if (tascb->tag_valid)
|
||||
res = asd_clear_nexus_tag(task);
|
||||
else
|
||||
res = asd_clear_nexus_index(task);
|
||||
wait_for_completion_timeout(&tascb->completion,
|
||||
AIC94XX_SCB_TIMEOUT);
|
||||
ASD_DPRINTK("came back from clear nexus\n");
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE)
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_abort_task -- ABORT TASK TMF
|
||||
* @task: the task to be aborted
|
||||
*
|
||||
* Before calling ABORT TASK the task state flags should be ORed with
|
||||
* SAS_TASK_STATE_ABORTED (unless SAS_TASK_STATE_DONE is set) under
|
||||
* the task_state_lock IRQ spinlock, then ABORT TASK *must* be called.
|
||||
*
|
||||
* Implements the ABORT TASK TMF, I_T_L_Q nexus.
|
||||
* Returns: SAS TMF responses (see sas_task.h),
|
||||
* -ENOMEM,
|
||||
* -SAS_QUEUE_FULL.
|
||||
*
|
||||
* When ABORT TASK returns, the caller of ABORT TASK checks first the
|
||||
* task->task_state_flags, and then the return value of ABORT TASK.
|
||||
*
|
||||
* If the task has task state bit SAS_TASK_STATE_DONE set, then the
|
||||
* task was completed successfully prior to it being aborted. The
|
||||
* caller of ABORT TASK has responsibility to call task->task_done()
|
||||
* xor free the task, depending on their framework. The return code
|
||||
* is TMF_RESP_FUNC_FAILED in this case.
|
||||
*
|
||||
* Else the SAS_TASK_STATE_DONE bit is not set,
|
||||
* If the return code is TMF_RESP_FUNC_COMPLETE, then
|
||||
* the task was aborted successfully. The caller of
|
||||
* ABORT TASK has responsibility to call task->task_done()
|
||||
* to finish the task, xor free the task depending on their
|
||||
* framework.
|
||||
* else
|
||||
* the ABORT TASK returned some kind of error. The task
|
||||
* was _not_ cancelled. Nothing can be assumed.
|
||||
* The caller of ABORT TASK may wish to retry.
|
||||
*/
|
||||
int asd_abort_task(struct sas_task *task)
|
||||
{
|
||||
struct asd_ascb *tascb = task->lldd_task;
|
||||
struct asd_ha_struct *asd_ha = tascb->ha;
|
||||
int res = 1;
|
||||
unsigned long flags;
|
||||
struct asd_ascb *ascb = NULL;
|
||||
struct scb *scb;
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
|
||||
goto out_done;
|
||||
}
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
|
||||
if (!ascb)
|
||||
return -ENOMEM;
|
||||
scb = ascb->scb;
|
||||
|
||||
scb->header.opcode = ABORT_TASK;
|
||||
|
||||
switch (task->task_proto) {
|
||||
case SATA_PROTO:
|
||||
case SAS_PROTO_STP:
|
||||
scb->abort_task.proto_conn_rate = (1 << 5); /* STP */
|
||||
break;
|
||||
case SAS_PROTO_SSP:
|
||||
scb->abort_task.proto_conn_rate = (1 << 4); /* SSP */
|
||||
scb->abort_task.proto_conn_rate |= task->dev->linkrate;
|
||||
break;
|
||||
case SAS_PROTO_SMP:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (task->task_proto == SAS_PROTO_SSP) {
|
||||
scb->abort_task.ssp_frame.frame_type = SSP_TASK;
|
||||
memcpy(scb->abort_task.ssp_frame.hashed_dest_addr,
|
||||
task->dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
memcpy(scb->abort_task.ssp_frame.hashed_src_addr,
|
||||
task->dev->port->ha->hashed_sas_addr,
|
||||
HASHED_SAS_ADDR_SIZE);
|
||||
scb->abort_task.ssp_frame.tptt = cpu_to_be16(0xFFFF);
|
||||
|
||||
memcpy(scb->abort_task.ssp_task.lun, task->ssp_task.LUN, 8);
|
||||
scb->abort_task.ssp_task.tmf = TMF_ABORT_TASK;
|
||||
scb->abort_task.ssp_task.tag = cpu_to_be16(0xFFFF);
|
||||
}
|
||||
|
||||
scb->abort_task.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->abort_task.conn_handle = cpu_to_le16(
|
||||
(u16)(unsigned long)task->dev->lldd_dev);
|
||||
scb->abort_task.retry_count = 1;
|
||||
scb->abort_task.index = cpu_to_le16((u16)tascb->tc_index);
|
||||
scb->abort_task.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
|
||||
|
||||
res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
|
||||
asd_tmf_timedout);
|
||||
if (res)
|
||||
goto out;
|
||||
wait_for_completion(&ascb->completion);
|
||||
ASD_DPRINTK("tmf came back\n");
|
||||
|
||||
res = (int) (unsigned long) ascb->uldd_task;
|
||||
tascb->tag = ascb->tag;
|
||||
tascb->tag_valid = ascb->tag_valid;
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
ASD_DPRINTK("%s: task 0x%p done\n", __FUNCTION__, task);
|
||||
goto out_done;
|
||||
}
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
switch (res) {
|
||||
/* The task to be aborted has been sent to the device.
|
||||
* We got a Response IU for the ABORT TASK TMF. */
|
||||
case TC_NO_ERROR + 0xFF00:
|
||||
case TMF_RESP_FUNC_COMPLETE:
|
||||
case TMF_RESP_FUNC_FAILED:
|
||||
res = asd_clear_nexus(task);
|
||||
break;
|
||||
case TMF_RESP_INVALID_FRAME:
|
||||
case TMF_RESP_OVERLAPPED_TAG:
|
||||
case TMF_RESP_FUNC_ESUPP:
|
||||
case TMF_RESP_NO_LUN:
|
||||
goto out_done; break;
|
||||
}
|
||||
/* In the following we assume that the managing layer
|
||||
* will _never_ make a mistake, when issuing ABORT TASK.
|
||||
*/
|
||||
switch (res) {
|
||||
default:
|
||||
res = asd_clear_nexus(task);
|
||||
/* fallthrough */
|
||||
case TC_NO_ERROR + 0xFF00:
|
||||
case TMF_RESP_FUNC_COMPLETE:
|
||||
break;
|
||||
/* The task hasn't been sent to the device xor we never got
|
||||
* a (sane) Response IU for the ABORT TASK TMF.
|
||||
*/
|
||||
case TF_NAK_RECV + 0xFF00:
|
||||
res = TMF_RESP_INVALID_FRAME;
|
||||
break;
|
||||
case TF_TMF_TASK_DONE + 0xFF00: /* done but not reported yet */
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
wait_for_completion_timeout(&tascb->completion,
|
||||
AIC94XX_SCB_TIMEOUT);
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE)
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
goto out_done;
|
||||
case TF_TMF_NO_TAG + 0xFF00:
|
||||
case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */
|
||||
case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
goto out_done;
|
||||
case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
|
||||
res = TMF_RESP_FUNC_ESUPP;
|
||||
goto out;
|
||||
}
|
||||
out_done:
|
||||
if (res == TMF_RESP_FUNC_COMPLETE) {
|
||||
task->lldd_task = NULL;
|
||||
mb();
|
||||
asd_ascb_free(tascb);
|
||||
}
|
||||
out:
|
||||
asd_ascb_free(ascb);
|
||||
ASD_DPRINTK("task 0x%p aborted, res: 0x%x\n", task, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_initiate_ssp_tmf -- send a TMF to an I_T_L or I_T_L_Q nexus
|
||||
* @dev: pointer to struct domain_device of interest
|
||||
* @lun: pointer to u8[8] which is the LUN
|
||||
* @tmf: the TMF to be performed (see sas_task.h or the SAS spec)
|
||||
* @index: the transaction context of the task to be queried if QT TMF
|
||||
*
|
||||
* This function is used to send ABORT TASK SET, CLEAR ACA,
|
||||
* CLEAR TASK SET, LU RESET and QUERY TASK TMFs.
|
||||
*
|
||||
* No SCBs should be queued to the I_T_L nexus when this SCB is
|
||||
* pending.
|
||||
*
|
||||
* Returns: TMF response code (see sas_task.h or the SAS spec)
|
||||
*/
|
||||
static int asd_initiate_ssp_tmf(struct domain_device *dev, u8 *lun,
|
||||
int tmf, int index)
|
||||
{
|
||||
struct asd_ha_struct *asd_ha = dev->port->ha->lldd_ha;
|
||||
struct asd_ascb *ascb;
|
||||
int res = 1;
|
||||
struct scb *scb;
|
||||
|
||||
if (!(dev->tproto & SAS_PROTO_SSP))
|
||||
return TMF_RESP_FUNC_ESUPP;
|
||||
|
||||
ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL);
|
||||
if (!ascb)
|
||||
return -ENOMEM;
|
||||
scb = ascb->scb;
|
||||
|
||||
if (tmf == TMF_QUERY_TASK)
|
||||
scb->header.opcode = QUERY_SSP_TASK;
|
||||
else
|
||||
scb->header.opcode = INITIATE_SSP_TMF;
|
||||
|
||||
scb->ssp_tmf.proto_conn_rate = (1 << 4); /* SSP */
|
||||
scb->ssp_tmf.proto_conn_rate |= dev->linkrate;
|
||||
/* SSP frame header */
|
||||
scb->ssp_tmf.ssp_frame.frame_type = SSP_TASK;
|
||||
memcpy(scb->ssp_tmf.ssp_frame.hashed_dest_addr,
|
||||
dev->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
memcpy(scb->ssp_tmf.ssp_frame.hashed_src_addr,
|
||||
dev->port->ha->hashed_sas_addr, HASHED_SAS_ADDR_SIZE);
|
||||
scb->ssp_tmf.ssp_frame.tptt = cpu_to_be16(0xFFFF);
|
||||
/* SSP Task IU */
|
||||
memcpy(scb->ssp_tmf.ssp_task.lun, lun, 8);
|
||||
scb->ssp_tmf.ssp_task.tmf = tmf;
|
||||
|
||||
scb->ssp_tmf.sister_scb = cpu_to_le16(0xFFFF);
|
||||
scb->ssp_tmf.conn_handle= cpu_to_le16((u16)(unsigned long)
|
||||
dev->lldd_dev);
|
||||
scb->ssp_tmf.retry_count = 1;
|
||||
scb->ssp_tmf.itnl_to = cpu_to_le16(ITNL_TIMEOUT_CONST);
|
||||
if (tmf == TMF_QUERY_TASK)
|
||||
scb->ssp_tmf.index = cpu_to_le16(index);
|
||||
|
||||
res = asd_enqueue_internal(ascb, asd_tmf_tasklet_complete,
|
||||
asd_tmf_timedout);
|
||||
if (res)
|
||||
goto out_err;
|
||||
wait_for_completion(&ascb->completion);
|
||||
res = (int) (unsigned long) ascb->uldd_task;
|
||||
|
||||
switch (res) {
|
||||
case TC_NO_ERROR + 0xFF00:
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
break;
|
||||
case TF_NAK_RECV + 0xFF00:
|
||||
res = TMF_RESP_INVALID_FRAME;
|
||||
break;
|
||||
case TF_TMF_TASK_DONE + 0xFF00:
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
break;
|
||||
case TF_TMF_NO_TAG + 0xFF00:
|
||||
case TF_TMF_TAG_FREE + 0xFF00: /* the tag is in the free list */
|
||||
case TF_TMF_NO_CONN_HANDLE + 0xFF00: /* no such device */
|
||||
res = TMF_RESP_FUNC_COMPLETE;
|
||||
break;
|
||||
case TF_TMF_NO_CTX + 0xFF00: /* not in seq, or proto != SSP */
|
||||
res = TMF_RESP_FUNC_ESUPP;
|
||||
break;
|
||||
default:
|
||||
ASD_DPRINTK("%s: converting result 0x%x to TMF_RESP_FUNC_FAILED\n",
|
||||
__FUNCTION__, res);
|
||||
res = TMF_RESP_FUNC_FAILED;
|
||||
break;
|
||||
}
|
||||
out_err:
|
||||
asd_ascb_free(ascb);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_abort_task_set(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_ABORT_TASK_SET, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_clear_aca(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_ACA, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_clear_task_set(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_CLEAR_TASK_SET, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
int asd_lu_reset(struct domain_device *dev, u8 *lun)
|
||||
{
|
||||
int res = asd_initiate_ssp_tmf(dev, lun, TMF_LU_RESET, 0);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE)
|
||||
asd_clear_nexus_I_T_L(dev, lun);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* asd_query_task -- send a QUERY TASK TMF to an I_T_L_Q nexus
|
||||
* task: pointer to sas_task struct of interest
|
||||
*
|
||||
* Returns: TMF_RESP_FUNC_COMPLETE if the task is not in the task set,
|
||||
* or TMF_RESP_FUNC_SUCC if the task is in the task set.
|
||||
*
|
||||
* Normally the management layer sets the task to aborted state,
|
||||
* and then calls query task and then abort task.
|
||||
*/
|
||||
int asd_query_task(struct sas_task *task)
|
||||
{
|
||||
struct asd_ascb *ascb = task->lldd_task;
|
||||
int index;
|
||||
|
||||
if (ascb) {
|
||||
index = ascb->tc_index;
|
||||
return asd_initiate_ssp_tmf(task->dev, task->ssp_task.LUN,
|
||||
TMF_QUERY_TASK, index);
|
||||
}
|
||||
return TMF_RESP_FUNC_COMPLETE;
|
||||
}
|
6
drivers/scsi/arcmsr/Makefile
Normal file
6
drivers/scsi/arcmsr/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# File: drivers/arcmsr/Makefile
|
||||
# Makefile for the ARECA PCI-X PCI-EXPRESS SATA RAID controllers SCSI driver.
|
||||
|
||||
arcmsr-objs := arcmsr_attr.o arcmsr_hba.o
|
||||
|
||||
obj-$(CONFIG_SCSI_ARCMSR) := arcmsr.o
|
472
drivers/scsi/arcmsr/arcmsr.h
Normal file
472
drivers/scsi/arcmsr/arcmsr.h
Normal file
@ -0,0 +1,472 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
** O.S : Linux
|
||||
** FILE NAME : arcmsr.h
|
||||
** BY : Erich Chen
|
||||
** Description: SCSI RAID Device Driver for
|
||||
** ARECA RAID Host adapter
|
||||
*******************************************************************************
|
||||
** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved.
|
||||
**
|
||||
** Web site: www.areca.com.tw
|
||||
** E-mail: erich@areca.com.tw
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License version 2 as
|
||||
** published by the Free Software Foundation.
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
*******************************************************************************
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
**(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
struct class_device_attribute;
|
||||
|
||||
#define ARCMSR_MAX_OUTSTANDING_CMD 256
|
||||
#define ARCMSR_MAX_FREECCB_NUM 288
|
||||
#define ARCMSR_DRIVER_VERSION "Driver Version 1.20.00.13"
|
||||
#define ARCMSR_SCSI_INITIATOR_ID 255
|
||||
#define ARCMSR_MAX_XFER_SECTORS 512
|
||||
#define ARCMSR_MAX_TARGETID 17
|
||||
#define ARCMSR_MAX_TARGETLUN 8
|
||||
#define ARCMSR_MAX_CMD_PERLUN ARCMSR_MAX_OUTSTANDING_CMD
|
||||
#define ARCMSR_MAX_QBUFFER 4096
|
||||
#define ARCMSR_MAX_SG_ENTRIES 38
|
||||
|
||||
/*
|
||||
*******************************************************************************
|
||||
** split 64bits dma addressing
|
||||
*******************************************************************************
|
||||
*/
|
||||
#define dma_addr_hi32(addr) (uint32_t) ((addr>>16)>>16)
|
||||
#define dma_addr_lo32(addr) (uint32_t) (addr & 0xffffffff)
|
||||
/*
|
||||
*******************************************************************************
|
||||
** MESSAGE CONTROL CODE
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct CMD_MESSAGE
|
||||
{
|
||||
uint32_t HeaderLength;
|
||||
uint8_t Signature[8];
|
||||
uint32_t Timeout;
|
||||
uint32_t ControlCode;
|
||||
uint32_t ReturnCode;
|
||||
uint32_t Length;
|
||||
};
|
||||
/*
|
||||
*******************************************************************************
|
||||
** IOP Message Transfer Data for user space
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct CMD_MESSAGE_FIELD
|
||||
{
|
||||
struct CMD_MESSAGE cmdmessage;
|
||||
uint8_t messagedatabuffer[1032];
|
||||
};
|
||||
/* IOP message transfer */
|
||||
#define ARCMSR_MESSAGE_FAIL 0x0001
|
||||
/* DeviceType */
|
||||
#define ARECA_SATA_RAID 0x90000000
|
||||
/* FunctionCode */
|
||||
#define FUNCTION_READ_RQBUFFER 0x0801
|
||||
#define FUNCTION_WRITE_WQBUFFER 0x0802
|
||||
#define FUNCTION_CLEAR_RQBUFFER 0x0803
|
||||
#define FUNCTION_CLEAR_WQBUFFER 0x0804
|
||||
#define FUNCTION_CLEAR_ALLQBUFFER 0x0805
|
||||
#define FUNCTION_RETURN_CODE_3F 0x0806
|
||||
#define FUNCTION_SAY_HELLO 0x0807
|
||||
#define FUNCTION_SAY_GOODBYE 0x0808
|
||||
#define FUNCTION_FLUSH_ADAPTER_CACHE 0x0809
|
||||
/* ARECA IO CONTROL CODE*/
|
||||
#define ARCMSR_MESSAGE_READ_RQBUFFER \
|
||||
ARECA_SATA_RAID | FUNCTION_READ_RQBUFFER
|
||||
#define ARCMSR_MESSAGE_WRITE_WQBUFFER \
|
||||
ARECA_SATA_RAID | FUNCTION_WRITE_WQBUFFER
|
||||
#define ARCMSR_MESSAGE_CLEAR_RQBUFFER \
|
||||
ARECA_SATA_RAID | FUNCTION_CLEAR_RQBUFFER
|
||||
#define ARCMSR_MESSAGE_CLEAR_WQBUFFER \
|
||||
ARECA_SATA_RAID | FUNCTION_CLEAR_WQBUFFER
|
||||
#define ARCMSR_MESSAGE_CLEAR_ALLQBUFFER \
|
||||
ARECA_SATA_RAID | FUNCTION_CLEAR_ALLQBUFFER
|
||||
#define ARCMSR_MESSAGE_RETURN_CODE_3F \
|
||||
ARECA_SATA_RAID | FUNCTION_RETURN_CODE_3F
|
||||
#define ARCMSR_MESSAGE_SAY_HELLO \
|
||||
ARECA_SATA_RAID | FUNCTION_SAY_HELLO
|
||||
#define ARCMSR_MESSAGE_SAY_GOODBYE \
|
||||
ARECA_SATA_RAID | FUNCTION_SAY_GOODBYE
|
||||
#define ARCMSR_MESSAGE_FLUSH_ADAPTER_CACHE \
|
||||
ARECA_SATA_RAID | FUNCTION_FLUSH_ADAPTER_CACHE
|
||||
/* ARECA IOCTL ReturnCode */
|
||||
#define ARCMSR_MESSAGE_RETURNCODE_OK 0x00000001
|
||||
#define ARCMSR_MESSAGE_RETURNCODE_ERROR 0x00000006
|
||||
#define ARCMSR_MESSAGE_RETURNCODE_3F 0x0000003F
|
||||
/*
|
||||
*************************************************************
|
||||
** structure for holding DMA address data
|
||||
*************************************************************
|
||||
*/
|
||||
#define IS_SG64_ADDR 0x01000000 /* bit24 */
|
||||
struct SG32ENTRY
|
||||
{
|
||||
uint32_t length;
|
||||
uint32_t address;
|
||||
};
|
||||
struct SG64ENTRY
|
||||
{
|
||||
uint32_t length;
|
||||
uint32_t address;
|
||||
uint32_t addresshigh;
|
||||
};
|
||||
struct SGENTRY_UNION
|
||||
{
|
||||
union
|
||||
{
|
||||
struct SG32ENTRY sg32entry;
|
||||
struct SG64ENTRY sg64entry;
|
||||
}u;
|
||||
};
|
||||
/*
|
||||
********************************************************************
|
||||
** Q Buffer of IOP Message Transfer
|
||||
********************************************************************
|
||||
*/
|
||||
struct QBUFFER
|
||||
{
|
||||
uint32_t data_len;
|
||||
uint8_t data[124];
|
||||
};
|
||||
/*
|
||||
*******************************************************************************
|
||||
** FIRMWARE INFO
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct FIRMWARE_INFO
|
||||
{
|
||||
uint32_t signature; /*0, 00-03*/
|
||||
uint32_t request_len; /*1, 04-07*/
|
||||
uint32_t numbers_queue; /*2, 08-11*/
|
||||
uint32_t sdram_size; /*3, 12-15*/
|
||||
uint32_t ide_channels; /*4, 16-19*/
|
||||
char vendor[40]; /*5, 20-59*/
|
||||
char model[8]; /*15, 60-67*/
|
||||
char firmware_ver[16]; /*17, 68-83*/
|
||||
char device_map[16]; /*21, 84-99*/
|
||||
};
|
||||
/* signature of set and get firmware config */
|
||||
#define ARCMSR_SIGNATURE_GET_CONFIG 0x87974060
|
||||
#define ARCMSR_SIGNATURE_SET_CONFIG 0x87974063
|
||||
/* message code of inbound message register */
|
||||
#define ARCMSR_INBOUND_MESG0_NOP 0x00000000
|
||||
#define ARCMSR_INBOUND_MESG0_GET_CONFIG 0x00000001
|
||||
#define ARCMSR_INBOUND_MESG0_SET_CONFIG 0x00000002
|
||||
#define ARCMSR_INBOUND_MESG0_ABORT_CMD 0x00000003
|
||||
#define ARCMSR_INBOUND_MESG0_STOP_BGRB 0x00000004
|
||||
#define ARCMSR_INBOUND_MESG0_FLUSH_CACHE 0x00000005
|
||||
#define ARCMSR_INBOUND_MESG0_START_BGRB 0x00000006
|
||||
#define ARCMSR_INBOUND_MESG0_CHK331PENDING 0x00000007
|
||||
#define ARCMSR_INBOUND_MESG0_SYNC_TIMER 0x00000008
|
||||
/* doorbell interrupt generator */
|
||||
#define ARCMSR_INBOUND_DRIVER_DATA_WRITE_OK 0x00000001
|
||||
#define ARCMSR_INBOUND_DRIVER_DATA_READ_OK 0x00000002
|
||||
#define ARCMSR_OUTBOUND_IOP331_DATA_WRITE_OK 0x00000001
|
||||
#define ARCMSR_OUTBOUND_IOP331_DATA_READ_OK 0x00000002
|
||||
/* ccb areca cdb flag */
|
||||
#define ARCMSR_CCBPOST_FLAG_SGL_BSIZE 0x80000000
|
||||
#define ARCMSR_CCBPOST_FLAG_IAM_BIOS 0x40000000
|
||||
#define ARCMSR_CCBREPLY_FLAG_IAM_BIOS 0x40000000
|
||||
#define ARCMSR_CCBREPLY_FLAG_ERROR 0x10000000
|
||||
/* outbound firmware ok */
|
||||
#define ARCMSR_OUTBOUND_MESG1_FIRMWARE_OK 0x80000000
|
||||
/*
|
||||
*******************************************************************************
|
||||
** ARECA SCSI COMMAND DESCRIPTOR BLOCK size 0x1F8 (504)
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct ARCMSR_CDB
|
||||
{
|
||||
uint8_t Bus;
|
||||
uint8_t TargetID;
|
||||
uint8_t LUN;
|
||||
uint8_t Function;
|
||||
|
||||
uint8_t CdbLength;
|
||||
uint8_t sgcount;
|
||||
uint8_t Flags;
|
||||
#define ARCMSR_CDB_FLAG_SGL_BSIZE 0x01
|
||||
#define ARCMSR_CDB_FLAG_BIOS 0x02
|
||||
#define ARCMSR_CDB_FLAG_WRITE 0x04
|
||||
#define ARCMSR_CDB_FLAG_SIMPLEQ 0x00
|
||||
#define ARCMSR_CDB_FLAG_HEADQ 0x08
|
||||
#define ARCMSR_CDB_FLAG_ORDEREDQ 0x10
|
||||
uint8_t Reserved1;
|
||||
|
||||
uint32_t Context;
|
||||
uint32_t DataLength;
|
||||
|
||||
uint8_t Cdb[16];
|
||||
|
||||
uint8_t DeviceStatus;
|
||||
#define ARCMSR_DEV_CHECK_CONDITION 0x02
|
||||
#define ARCMSR_DEV_SELECT_TIMEOUT 0xF0
|
||||
#define ARCMSR_DEV_ABORTED 0xF1
|
||||
#define ARCMSR_DEV_INIT_FAIL 0xF2
|
||||
uint8_t SenseData[15];
|
||||
|
||||
union
|
||||
{
|
||||
struct SG32ENTRY sg32entry[ARCMSR_MAX_SG_ENTRIES];
|
||||
struct SG64ENTRY sg64entry[ARCMSR_MAX_SG_ENTRIES];
|
||||
} u;
|
||||
};
|
||||
/*
|
||||
*******************************************************************************
|
||||
** Messaging Unit (MU) of the Intel R 80331 I/O processor (80331)
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct MessageUnit
|
||||
{
|
||||
uint32_t resrved0[4]; /*0000 000F*/
|
||||
uint32_t inbound_msgaddr0; /*0010 0013*/
|
||||
uint32_t inbound_msgaddr1; /*0014 0017*/
|
||||
uint32_t outbound_msgaddr0; /*0018 001B*/
|
||||
uint32_t outbound_msgaddr1; /*001C 001F*/
|
||||
uint32_t inbound_doorbell; /*0020 0023*/
|
||||
uint32_t inbound_intstatus; /*0024 0027*/
|
||||
uint32_t inbound_intmask; /*0028 002B*/
|
||||
uint32_t outbound_doorbell; /*002C 002F*/
|
||||
uint32_t outbound_intstatus; /*0030 0033*/
|
||||
uint32_t outbound_intmask; /*0034 0037*/
|
||||
uint32_t reserved1[2]; /*0038 003F*/
|
||||
uint32_t inbound_queueport; /*0040 0043*/
|
||||
uint32_t outbound_queueport; /*0044 0047*/
|
||||
uint32_t reserved2[2]; /*0048 004F*/
|
||||
uint32_t reserved3[492]; /*0050 07FF 492*/
|
||||
uint32_t reserved4[128]; /*0800 09FF 128*/
|
||||
uint32_t message_rwbuffer[256]; /*0a00 0DFF 256*/
|
||||
uint32_t message_wbuffer[32]; /*0E00 0E7F 32*/
|
||||
uint32_t reserved5[32]; /*0E80 0EFF 32*/
|
||||
uint32_t message_rbuffer[32]; /*0F00 0F7F 32*/
|
||||
uint32_t reserved6[32]; /*0F80 0FFF 32*/
|
||||
};
|
||||
/*
|
||||
*******************************************************************************
|
||||
** Adapter Control Block
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct AdapterControlBlock
|
||||
{
|
||||
struct pci_dev * pdev;
|
||||
struct Scsi_Host * host;
|
||||
unsigned long vir2phy_offset;
|
||||
/* Offset is used in making arc cdb physical to virtual calculations */
|
||||
uint32_t outbound_int_enable;
|
||||
|
||||
struct MessageUnit __iomem * pmu;
|
||||
/* message unit ATU inbound base address0 */
|
||||
|
||||
uint32_t acb_flags;
|
||||
#define ACB_F_SCSISTOPADAPTER 0x0001
|
||||
#define ACB_F_MSG_STOP_BGRB 0x0002
|
||||
/* stop RAID background rebuild */
|
||||
#define ACB_F_MSG_START_BGRB 0x0004
|
||||
/* stop RAID background rebuild */
|
||||
#define ACB_F_IOPDATA_OVERFLOW 0x0008
|
||||
/* iop message data rqbuffer overflow */
|
||||
#define ACB_F_MESSAGE_WQBUFFER_CLEARED 0x0010
|
||||
/* message clear wqbuffer */
|
||||
#define ACB_F_MESSAGE_RQBUFFER_CLEARED 0x0020
|
||||
/* message clear rqbuffer */
|
||||
#define ACB_F_MESSAGE_WQBUFFER_READED 0x0040
|
||||
#define ACB_F_BUS_RESET 0x0080
|
||||
#define ACB_F_IOP_INITED 0x0100
|
||||
/* iop init */
|
||||
|
||||
struct CommandControlBlock * pccb_pool[ARCMSR_MAX_FREECCB_NUM];
|
||||
/* used for memory free */
|
||||
struct list_head ccb_free_list;
|
||||
/* head of free ccb list */
|
||||
atomic_t ccboutstandingcount;
|
||||
|
||||
void * dma_coherent;
|
||||
/* dma_coherent used for memory free */
|
||||
dma_addr_t dma_coherent_handle;
|
||||
/* dma_coherent_handle used for memory free */
|
||||
|
||||
uint8_t rqbuffer[ARCMSR_MAX_QBUFFER];
|
||||
/* data collection buffer for read from 80331 */
|
||||
int32_t rqbuf_firstindex;
|
||||
/* first of read buffer */
|
||||
int32_t rqbuf_lastindex;
|
||||
/* last of read buffer */
|
||||
uint8_t wqbuffer[ARCMSR_MAX_QBUFFER];
|
||||
/* data collection buffer for write to 80331 */
|
||||
int32_t wqbuf_firstindex;
|
||||
/* first of write buffer */
|
||||
int32_t wqbuf_lastindex;
|
||||
/* last of write buffer */
|
||||
uint8_t devstate[ARCMSR_MAX_TARGETID][ARCMSR_MAX_TARGETLUN];
|
||||
/* id0 ..... id15, lun0...lun7 */
|
||||
#define ARECA_RAID_GONE 0x55
|
||||
#define ARECA_RAID_GOOD 0xaa
|
||||
uint32_t num_resets;
|
||||
uint32_t num_aborts;
|
||||
uint32_t firm_request_len;
|
||||
uint32_t firm_numbers_queue;
|
||||
uint32_t firm_sdram_size;
|
||||
uint32_t firm_hd_channels;
|
||||
char firm_model[12];
|
||||
char firm_version[20];
|
||||
};/* HW_DEVICE_EXTENSION */
|
||||
/*
|
||||
*******************************************************************************
|
||||
** Command Control Block
|
||||
** this CCB length must be 32 bytes boundary
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct CommandControlBlock
|
||||
{
|
||||
struct ARCMSR_CDB arcmsr_cdb;
|
||||
/*
|
||||
** 0-503 (size of CDB=504):
|
||||
** arcmsr messenger scsi command descriptor size 504 bytes
|
||||
*/
|
||||
uint32_t cdb_shifted_phyaddr;
|
||||
/* 504-507 */
|
||||
uint32_t reserved1;
|
||||
/* 508-511 */
|
||||
#if BITS_PER_LONG == 64
|
||||
/* ======================512+64 bytes======================== */
|
||||
struct list_head list;
|
||||
/* 512-527 16 bytes next/prev ptrs for ccb lists */
|
||||
struct scsi_cmnd * pcmd;
|
||||
/* 528-535 8 bytes pointer of linux scsi command */
|
||||
struct AdapterControlBlock * acb;
|
||||
/* 536-543 8 bytes pointer of acb */
|
||||
|
||||
uint16_t ccb_flags;
|
||||
/* 544-545 */
|
||||
#define CCB_FLAG_READ 0x0000
|
||||
#define CCB_FLAG_WRITE 0x0001
|
||||
#define CCB_FLAG_ERROR 0x0002
|
||||
#define CCB_FLAG_FLUSHCACHE 0x0004
|
||||
#define CCB_FLAG_MASTER_ABORTED 0x0008
|
||||
uint16_t startdone;
|
||||
/* 546-547 */
|
||||
#define ARCMSR_CCB_DONE 0x0000
|
||||
#define ARCMSR_CCB_START 0x55AA
|
||||
#define ARCMSR_CCB_ABORTED 0xAA55
|
||||
#define ARCMSR_CCB_ILLEGAL 0xFFFF
|
||||
uint32_t reserved2[7];
|
||||
/* 548-551 552-555 556-559 560-563 564-567 568-571 572-575 */
|
||||
#else
|
||||
/* ======================512+32 bytes======================== */
|
||||
struct list_head list;
|
||||
/* 512-519 8 bytes next/prev ptrs for ccb lists */
|
||||
struct scsi_cmnd * pcmd;
|
||||
/* 520-523 4 bytes pointer of linux scsi command */
|
||||
struct AdapterControlBlock * acb;
|
||||
/* 524-527 4 bytes pointer of acb */
|
||||
|
||||
uint16_t ccb_flags;
|
||||
/* 528-529 */
|
||||
#define CCB_FLAG_READ 0x0000
|
||||
#define CCB_FLAG_WRITE 0x0001
|
||||
#define CCB_FLAG_ERROR 0x0002
|
||||
#define CCB_FLAG_FLUSHCACHE 0x0004
|
||||
#define CCB_FLAG_MASTER_ABORTED 0x0008
|
||||
uint16_t startdone;
|
||||
/* 530-531 */
|
||||
#define ARCMSR_CCB_DONE 0x0000
|
||||
#define ARCMSR_CCB_START 0x55AA
|
||||
#define ARCMSR_CCB_ABORTED 0xAA55
|
||||
#define ARCMSR_CCB_ILLEGAL 0xFFFF
|
||||
uint32_t reserved2[3];
|
||||
/* 532-535 536-539 540-543 */
|
||||
#endif
|
||||
/* ========================================================== */
|
||||
};
|
||||
/*
|
||||
*******************************************************************************
|
||||
** ARECA SCSI sense data
|
||||
*******************************************************************************
|
||||
*/
|
||||
struct SENSE_DATA
|
||||
{
|
||||
uint8_t ErrorCode:7;
|
||||
#define SCSI_SENSE_CURRENT_ERRORS 0x70
|
||||
#define SCSI_SENSE_DEFERRED_ERRORS 0x71
|
||||
uint8_t Valid:1;
|
||||
uint8_t SegmentNumber;
|
||||
uint8_t SenseKey:4;
|
||||
uint8_t Reserved:1;
|
||||
uint8_t IncorrectLength:1;
|
||||
uint8_t EndOfMedia:1;
|
||||
uint8_t FileMark:1;
|
||||
uint8_t Information[4];
|
||||
uint8_t AdditionalSenseLength;
|
||||
uint8_t CommandSpecificInformation[4];
|
||||
uint8_t AdditionalSenseCode;
|
||||
uint8_t AdditionalSenseCodeQualifier;
|
||||
uint8_t FieldReplaceableUnitCode;
|
||||
uint8_t SenseKeySpecific[3];
|
||||
};
|
||||
/*
|
||||
*******************************************************************************
|
||||
** Outbound Interrupt Status Register - OISR
|
||||
*******************************************************************************
|
||||
*/
|
||||
#define ARCMSR_MU_OUTBOUND_INTERRUPT_STATUS_REG 0x30
|
||||
#define ARCMSR_MU_OUTBOUND_PCI_INT 0x10
|
||||
#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INT 0x08
|
||||
#define ARCMSR_MU_OUTBOUND_DOORBELL_INT 0x04
|
||||
#define ARCMSR_MU_OUTBOUND_MESSAGE1_INT 0x02
|
||||
#define ARCMSR_MU_OUTBOUND_MESSAGE0_INT 0x01
|
||||
#define ARCMSR_MU_OUTBOUND_HANDLE_INT \
|
||||
(ARCMSR_MU_OUTBOUND_MESSAGE0_INT \
|
||||
|ARCMSR_MU_OUTBOUND_MESSAGE1_INT \
|
||||
|ARCMSR_MU_OUTBOUND_DOORBELL_INT \
|
||||
|ARCMSR_MU_OUTBOUND_POSTQUEUE_INT \
|
||||
|ARCMSR_MU_OUTBOUND_PCI_INT)
|
||||
/*
|
||||
*******************************************************************************
|
||||
** Outbound Interrupt Mask Register - OIMR
|
||||
*******************************************************************************
|
||||
*/
|
||||
#define ARCMSR_MU_OUTBOUND_INTERRUPT_MASK_REG 0x34
|
||||
#define ARCMSR_MU_OUTBOUND_PCI_INTMASKENABLE 0x10
|
||||
#define ARCMSR_MU_OUTBOUND_POSTQUEUE_INTMASKENABLE 0x08
|
||||
#define ARCMSR_MU_OUTBOUND_DOORBELL_INTMASKENABLE 0x04
|
||||
#define ARCMSR_MU_OUTBOUND_MESSAGE1_INTMASKENABLE 0x02
|
||||
#define ARCMSR_MU_OUTBOUND_MESSAGE0_INTMASKENABLE 0x01
|
||||
#define ARCMSR_MU_OUTBOUND_ALL_INTMASKENABLE 0x1F
|
||||
|
||||
extern void arcmsr_post_Qbuffer(struct AdapterControlBlock *acb);
|
||||
extern struct class_device_attribute *arcmsr_host_attrs[];
|
||||
extern int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb);
|
||||
void arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb);
|
||||
|
381
drivers/scsi/arcmsr/arcmsr_attr.c
Normal file
381
drivers/scsi/arcmsr/arcmsr_attr.c
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
*******************************************************************************
|
||||
** O.S : Linux
|
||||
** FILE NAME : arcmsr_attr.c
|
||||
** BY : Erich Chen
|
||||
** Description: attributes exported to sysfs and device host
|
||||
*******************************************************************************
|
||||
** Copyright (C) 2002 - 2005, Areca Technology Corporation All rights reserved
|
||||
**
|
||||
** Web site: www.areca.com.tw
|
||||
** E-mail: erich@areca.com.tw
|
||||
**
|
||||
** This program is free software; you can redistribute it and/or modify
|
||||
** it under the terms of the GNU General Public License version 2 as
|
||||
** published by the Free Software Foundation.
|
||||
** This program is distributed in the hope that it will be useful,
|
||||
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
** GNU General Public License for more details.
|
||||
*******************************************************************************
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES(INCLUDING,BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION)HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE)ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*******************************************************************************
|
||||
** For history of changes, see Documentation/scsi/ChangeLog.arcmsr
|
||||
** Firmware Specification, see Documentation/scsi/arcmsr_spec.txt
|
||||
*******************************************************************************
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_transport.h>
|
||||
#include "arcmsr.h"
|
||||
|
||||
struct class_device_attribute *arcmsr_host_attrs[];
|
||||
|
||||
static ssize_t
|
||||
arcmsr_sysfs_iop_message_read(struct kobject *kobj, char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
struct class_device *cdev = container_of(kobj,struct class_device,kobj);
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
struct MessageUnit __iomem *reg = acb->pmu;
|
||||
uint8_t *pQbuffer,*ptmpQbuffer;
|
||||
int32_t allxfer_len = 0;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
/* do message unit read. */
|
||||
ptmpQbuffer = (uint8_t *)buf;
|
||||
while ((acb->rqbuf_firstindex != acb->rqbuf_lastindex)
|
||||
&& (allxfer_len < 1031)) {
|
||||
pQbuffer = &acb->rqbuffer[acb->rqbuf_firstindex];
|
||||
memcpy(ptmpQbuffer, pQbuffer, 1);
|
||||
acb->rqbuf_firstindex++;
|
||||
acb->rqbuf_firstindex %= ARCMSR_MAX_QBUFFER;
|
||||
ptmpQbuffer++;
|
||||
allxfer_len++;
|
||||
}
|
||||
if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
|
||||
struct QBUFFER __iomem * prbuffer = (struct QBUFFER __iomem *)
|
||||
®->message_rbuffer;
|
||||
uint8_t __iomem * iop_data = (uint8_t __iomem *)prbuffer->data;
|
||||
int32_t iop_len;
|
||||
|
||||
acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
|
||||
iop_len = readl(&prbuffer->data_len);
|
||||
while (iop_len > 0) {
|
||||
acb->rqbuffer[acb->rqbuf_lastindex] = readb(iop_data);
|
||||
acb->rqbuf_lastindex++;
|
||||
acb->rqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
|
||||
iop_data++;
|
||||
iop_len--;
|
||||
}
|
||||
writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK,
|
||||
®->inbound_doorbell);
|
||||
}
|
||||
return (allxfer_len);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_sysfs_iop_message_write(struct kobject *kobj, char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
struct class_device *cdev = container_of(kobj,struct class_device,kobj);
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
int32_t my_empty_len, user_len, wqbuf_firstindex, wqbuf_lastindex;
|
||||
uint8_t *pQbuffer, *ptmpuserbuffer;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
if (count > 1032)
|
||||
return -EINVAL;
|
||||
/* do message unit write. */
|
||||
ptmpuserbuffer = (uint8_t *)buf;
|
||||
user_len = (int32_t)count;
|
||||
wqbuf_lastindex = acb->wqbuf_lastindex;
|
||||
wqbuf_firstindex = acb->wqbuf_firstindex;
|
||||
if (wqbuf_lastindex != wqbuf_firstindex) {
|
||||
arcmsr_post_Qbuffer(acb);
|
||||
return 0; /*need retry*/
|
||||
} else {
|
||||
my_empty_len = (wqbuf_firstindex-wqbuf_lastindex - 1)
|
||||
&(ARCMSR_MAX_QBUFFER - 1);
|
||||
if (my_empty_len >= user_len) {
|
||||
while (user_len > 0) {
|
||||
pQbuffer =
|
||||
&acb->wqbuffer[acb->wqbuf_lastindex];
|
||||
memcpy(pQbuffer, ptmpuserbuffer, 1);
|
||||
acb->wqbuf_lastindex++;
|
||||
acb->wqbuf_lastindex %= ARCMSR_MAX_QBUFFER;
|
||||
ptmpuserbuffer++;
|
||||
user_len--;
|
||||
}
|
||||
if (acb->acb_flags & ACB_F_MESSAGE_WQBUFFER_CLEARED) {
|
||||
acb->acb_flags &=
|
||||
~ACB_F_MESSAGE_WQBUFFER_CLEARED;
|
||||
arcmsr_post_Qbuffer(acb);
|
||||
}
|
||||
return count;
|
||||
} else {
|
||||
return 0; /*need retry*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_sysfs_iop_message_clear(struct kobject *kobj, char *buf, loff_t off,
|
||||
size_t count)
|
||||
{
|
||||
struct class_device *cdev = container_of(kobj,struct class_device,kobj);
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
struct MessageUnit __iomem *reg = acb->pmu;
|
||||
uint8_t *pQbuffer;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
if (acb->acb_flags & ACB_F_IOPDATA_OVERFLOW) {
|
||||
acb->acb_flags &= ~ACB_F_IOPDATA_OVERFLOW;
|
||||
writel(ARCMSR_INBOUND_DRIVER_DATA_READ_OK
|
||||
, ®->inbound_doorbell);
|
||||
}
|
||||
acb->acb_flags |=
|
||||
(ACB_F_MESSAGE_WQBUFFER_CLEARED
|
||||
| ACB_F_MESSAGE_RQBUFFER_CLEARED
|
||||
| ACB_F_MESSAGE_WQBUFFER_READED);
|
||||
acb->rqbuf_firstindex = 0;
|
||||
acb->rqbuf_lastindex = 0;
|
||||
acb->wqbuf_firstindex = 0;
|
||||
acb->wqbuf_lastindex = 0;
|
||||
pQbuffer = acb->rqbuffer;
|
||||
memset(pQbuffer, 0, sizeof (struct QBUFFER));
|
||||
pQbuffer = acb->wqbuffer;
|
||||
memset(pQbuffer, 0, sizeof (struct QBUFFER));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct bin_attribute arcmsr_sysfs_message_read_attr = {
|
||||
.attr = {
|
||||
.name = "mu_read",
|
||||
.mode = S_IRUSR ,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.size = 1032,
|
||||
.read = arcmsr_sysfs_iop_message_read,
|
||||
};
|
||||
|
||||
static struct bin_attribute arcmsr_sysfs_message_write_attr = {
|
||||
.attr = {
|
||||
.name = "mu_write",
|
||||
.mode = S_IWUSR,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.size = 1032,
|
||||
.write = arcmsr_sysfs_iop_message_write,
|
||||
};
|
||||
|
||||
static struct bin_attribute arcmsr_sysfs_message_clear_attr = {
|
||||
.attr = {
|
||||
.name = "mu_clear",
|
||||
.mode = S_IWUSR,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.size = 1,
|
||||
.write = arcmsr_sysfs_iop_message_clear,
|
||||
};
|
||||
|
||||
int arcmsr_alloc_sysfs_attr(struct AdapterControlBlock *acb)
|
||||
{
|
||||
struct Scsi_Host *host = acb->host;
|
||||
int error;
|
||||
|
||||
error = sysfs_create_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_read_attr);
|
||||
if (error) {
|
||||
printk(KERN_ERR "arcmsr: alloc sysfs mu_read failed\n");
|
||||
goto error_bin_file_message_read;
|
||||
}
|
||||
error = sysfs_create_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_write_attr);
|
||||
if (error) {
|
||||
printk(KERN_ERR "arcmsr: alloc sysfs mu_write failed\n");
|
||||
goto error_bin_file_message_write;
|
||||
}
|
||||
error = sysfs_create_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_clear_attr);
|
||||
if (error) {
|
||||
printk(KERN_ERR "arcmsr: alloc sysfs mu_clear failed\n");
|
||||
goto error_bin_file_message_clear;
|
||||
}
|
||||
return 0;
|
||||
error_bin_file_message_clear:
|
||||
sysfs_remove_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_write_attr);
|
||||
error_bin_file_message_write:
|
||||
sysfs_remove_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_read_attr);
|
||||
error_bin_file_message_read:
|
||||
return error;
|
||||
}
|
||||
|
||||
void
|
||||
arcmsr_free_sysfs_attr(struct AdapterControlBlock *acb) {
|
||||
struct Scsi_Host *host = acb->host;
|
||||
|
||||
sysfs_remove_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_clear_attr);
|
||||
sysfs_remove_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_write_attr);
|
||||
sysfs_remove_bin_file(&host->shost_classdev.kobj,
|
||||
&arcmsr_sysfs_message_read_attr);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_driver_version(struct class_device *cdev, char *buf) {
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%s\n",
|
||||
ARCMSR_DRIVER_VERSION);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_driver_posted_cmd(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
atomic_read(&acb->ccboutstandingcount));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_driver_reset(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
acb->num_resets);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_driver_abort(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
acb->num_aborts);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_fw_model(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%s\n",
|
||||
acb->firm_model);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_fw_version(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%s\n",
|
||||
acb->firm_version);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_fw_request_len(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
acb->firm_request_len);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_fw_numbers_queue(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
acb->firm_numbers_queue);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_fw_sdram_size(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
acb->firm_sdram_size);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
arcmsr_attr_host_fw_hd_channels(struct class_device *cdev, char *buf) {
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct AdapterControlBlock *acb = (struct AdapterControlBlock *) host->hostdata;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE,
|
||||
"%4d\n",
|
||||
acb->firm_hd_channels);
|
||||
}
|
||||
|
||||
static CLASS_DEVICE_ATTR(host_driver_version, S_IRUGO, arcmsr_attr_host_driver_version, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_driver_posted_cmd, S_IRUGO, arcmsr_attr_host_driver_posted_cmd, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_driver_reset, S_IRUGO, arcmsr_attr_host_driver_reset, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_driver_abort, S_IRUGO, arcmsr_attr_host_driver_abort, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_fw_model, S_IRUGO, arcmsr_attr_host_fw_model, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_fw_version, S_IRUGO, arcmsr_attr_host_fw_version, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_fw_request_len, S_IRUGO, arcmsr_attr_host_fw_request_len, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_fw_numbers_queue, S_IRUGO, arcmsr_attr_host_fw_numbers_queue, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_fw_sdram_size, S_IRUGO, arcmsr_attr_host_fw_sdram_size, NULL);
|
||||
static CLASS_DEVICE_ATTR(host_fw_hd_channels, S_IRUGO, arcmsr_attr_host_fw_hd_channels, NULL);
|
||||
|
||||
struct class_device_attribute *arcmsr_host_attrs[] = {
|
||||
&class_device_attr_host_driver_version,
|
||||
&class_device_attr_host_driver_posted_cmd,
|
||||
&class_device_attr_host_driver_reset,
|
||||
&class_device_attr_host_driver_abort,
|
||||
&class_device_attr_host_fw_model,
|
||||
&class_device_attr_host_fw_version,
|
||||
&class_device_attr_host_fw_request_len,
|
||||
&class_device_attr_host_fw_numbers_queue,
|
||||
&class_device_attr_host_fw_sdram_size,
|
||||
&class_device_attr_host_fw_hd_channels,
|
||||
NULL,
|
||||
};
|
1496
drivers/scsi/arcmsr/arcmsr_hba.c
Normal file
1496
drivers/scsi/arcmsr/arcmsr_hba.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -46,7 +46,6 @@ MODULE_DESCRIPTION("Adaptec I2O RAID Driver");
|
||||
|
||||
#include <linux/stat.h>
|
||||
#include <linux/slab.h> /* for kmalloc() */
|
||||
#include <linux/config.h> /* for CONFIG_PCI */
|
||||
#include <linux/pci.h> /* for PCI support */
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/blkdev.h>
|
||||
|
@ -248,8 +248,7 @@ int fcal_proc_info (struct Scsi_Host *host, char *buffer, char **start, off_t of
|
||||
if (scd->id == target) {
|
||||
SPRINTF (" [AL-PA: %02x, Id: %02d, Port WWN: %08x%08x, Node WWN: %08x%08x] ",
|
||||
alpa, target, u1[0], u1[1], u2[0], u2[1]);
|
||||
SPRINTF ("%s ", (scd->type < MAX_SCSI_DEVICE_CODE) ?
|
||||
scsi_device_types[(short) scd->type] : "Unknown device");
|
||||
SPRINTF ("%s ", scsi_device_type(scd->type));
|
||||
|
||||
for (j = 0; (j < 8) && (scd->vendor[j] >= 0x20); j++)
|
||||
SPRINTF ("%c", scd->vendor[j]);
|
||||
|
@ -811,7 +811,6 @@ static int generic_NCR5380_proc_info(struct Scsi_Host *scsi_ptr, char *buffer, c
|
||||
struct NCR5380_hostdata *hostdata;
|
||||
#ifdef NCR5380_STATS
|
||||
struct scsi_device *dev;
|
||||
extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
|
||||
#endif
|
||||
|
||||
NCR5380_setup(scsi_ptr);
|
||||
@ -851,7 +850,7 @@ static int generic_NCR5380_proc_info(struct Scsi_Host *scsi_ptr, char *buffer, c
|
||||
long tr = hostdata->time_read[dev->id] / HZ;
|
||||
long tw = hostdata->time_write[dev->id] / HZ;
|
||||
|
||||
PRINTP(" T:%d %s " ANDP dev->id ANDP(dev->type < MAX_SCSI_DEVICE_CODE) ? scsi_device_types[(int) dev->type] : "Unknown");
|
||||
PRINTP(" T:%d %s " ANDP dev->id ANDP scsi_device_type(dev->type));
|
||||
for (i = 0; i < 8; i++)
|
||||
if (dev->vendor[i] >= 0x20)
|
||||
*(buffer + (len++)) = dev->vendor[i];
|
||||
|
@ -47,7 +47,7 @@ void gvp11_setup (char *str, int *ints)
|
||||
gvp11_xfer_mask = ints[1];
|
||||
}
|
||||
|
||||
static int dma_setup (Scsi_Cmnd *cmd, int dir_in)
|
||||
static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
|
||||
{
|
||||
unsigned short cntr = GVP11_DMAC_INT_ENABLE;
|
||||
unsigned long addr = virt_to_bus(cmd->SCp.ptr);
|
||||
@ -142,8 +142,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)
|
||||
{
|
||||
/* stop DMA */
|
||||
DMA(instance)->SP_DMA = 1;
|
||||
@ -341,7 +341,7 @@ release:
|
||||
return num_gvp11;
|
||||
}
|
||||
|
||||
static int gvp11_bus_reset(Scsi_Cmnd *cmd)
|
||||
static int gvp11_bus_reset(struct scsi_cmnd *cmd)
|
||||
{
|
||||
/* FIXME perform bus-specific reset */
|
||||
|
||||
|
@ -13,10 +13,6 @@
|
||||
|
||||
int gvp11_detect(struct scsi_host_template *);
|
||||
int gvp11_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
|
||||
|
@ -265,6 +265,9 @@ static void scsi_host_dev_release(struct device *dev)
|
||||
destroy_workqueue(shost->work_q);
|
||||
|
||||
scsi_destroy_command_freelist(shost);
|
||||
if (shost->bqt)
|
||||
blk_free_tags(shost->bqt);
|
||||
|
||||
kfree(shost->shost_data);
|
||||
|
||||
if (parent)
|
||||
@ -487,7 +490,9 @@ EXPORT_SYMBOL(scsi_is_host_device);
|
||||
* @work: Work to queue for execution.
|
||||
*
|
||||
* Return value:
|
||||
* 0 on success / != 0 for error
|
||||
* 1 - work queued for execution
|
||||
* 0 - work is already queued
|
||||
* -EINVAL - work queue doesn't exist
|
||||
**/
|
||||
int scsi_queue_work(struct Scsi_Host *shost, struct work_struct *work)
|
||||
{
|
||||
|
@ -15,7 +15,6 @@
|
||||
*
|
||||
* For more information, visit http://www.highpoint-tech.com
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/string.h>
|
||||
|
@ -175,6 +175,8 @@ struct ipr_error_table_t ipr_error_table[] = {
|
||||
"Qualified success"},
|
||||
{0x01080000, 1, 1,
|
||||
"FFFE: Soft device bus error recovered by the IOA"},
|
||||
{0x01088100, 0, 1,
|
||||
"4101: Soft device bus fabric error"},
|
||||
{0x01170600, 0, 1,
|
||||
"FFF9: Device sector reassign successful"},
|
||||
{0x01170900, 0, 1,
|
||||
@ -225,6 +227,8 @@ struct ipr_error_table_t ipr_error_table[] = {
|
||||
"3109: IOA timed out a device command"},
|
||||
{0x04088000, 0, 0,
|
||||
"3120: SCSI bus is not operational"},
|
||||
{0x04088100, 0, 1,
|
||||
"4100: Hard device bus fabric error"},
|
||||
{0x04118000, 0, 1,
|
||||
"9000: IOA reserved area data check"},
|
||||
{0x04118100, 0, 1,
|
||||
@ -273,6 +277,14 @@ struct ipr_error_table_t ipr_error_table[] = {
|
||||
"9091: Incorrect hardware configuration change has been detected"},
|
||||
{0x04678000, 0, 1,
|
||||
"9073: Invalid multi-adapter configuration"},
|
||||
{0x04678100, 0, 1,
|
||||
"4010: Incorrect connection between cascaded expanders"},
|
||||
{0x04678200, 0, 1,
|
||||
"4020: Connections exceed IOA design limits"},
|
||||
{0x04678300, 0, 1,
|
||||
"4030: Incorrect multipath connection"},
|
||||
{0x04679000, 0, 1,
|
||||
"4110: Unsupported enclosure function"},
|
||||
{0x046E0000, 0, 1,
|
||||
"FFF4: Command to logical unit failed"},
|
||||
{0x05240000, 1, 0,
|
||||
@ -297,6 +309,8 @@ struct ipr_error_table_t ipr_error_table[] = {
|
||||
"9031: Array protection temporarily suspended, protection resuming"},
|
||||
{0x06040600, 0, 1,
|
||||
"9040: Array protection temporarily suspended, protection resuming"},
|
||||
{0x06288000, 0, 1,
|
||||
"3140: Device bus not ready to ready transition"},
|
||||
{0x06290000, 0, 1,
|
||||
"FFFB: SCSI bus was reset"},
|
||||
{0x06290500, 0, 0,
|
||||
@ -319,6 +333,16 @@ struct ipr_error_table_t ipr_error_table[] = {
|
||||
"3150: SCSI bus configuration error"},
|
||||
{0x06678100, 0, 1,
|
||||
"9074: Asymmetric advanced function disk configuration"},
|
||||
{0x06678300, 0, 1,
|
||||
"4040: Incomplete multipath connection between IOA and enclosure"},
|
||||
{0x06678400, 0, 1,
|
||||
"4041: Incomplete multipath connection between enclosure and device"},
|
||||
{0x06678500, 0, 1,
|
||||
"9075: Incomplete multipath connection between IOA and remote IOA"},
|
||||
{0x06678600, 0, 1,
|
||||
"9076: Configuration error, missing remote IOA"},
|
||||
{0x06679100, 0, 1,
|
||||
"4050: Enclosure does not support a required multipath function"},
|
||||
{0x06690200, 0, 1,
|
||||
"9041: Array protection temporarily suspended"},
|
||||
{0x06698200, 0, 1,
|
||||
@ -331,6 +355,10 @@ struct ipr_error_table_t ipr_error_table[] = {
|
||||
"9072: Link not operational transition"},
|
||||
{0x066B8200, 0, 1,
|
||||
"9032: Array exposed but still protected"},
|
||||
{0x066B9100, 0, 1,
|
||||
"4061: Multipath redundancy level got better"},
|
||||
{0x066B9200, 0, 1,
|
||||
"4060: Multipath redundancy level got worse"},
|
||||
{0x07270000, 0, 0,
|
||||
"Failure due to other device"},
|
||||
{0x07278000, 0, 1,
|
||||
@ -4099,8 +4127,7 @@ static int ipr_get_autosense(struct ipr_cmnd *ipr_cmd)
|
||||
{
|
||||
struct ipr_ioasa *ioasa = &ipr_cmd->ioasa;
|
||||
|
||||
if ((be32_to_cpu(ioasa->ioasc_specific) &
|
||||
(IPR_ADDITIONAL_STATUS_FMT | IPR_AUTOSENSE_VALID)) == 0)
|
||||
if ((be32_to_cpu(ioasa->ioasc_specific) & IPR_AUTOSENSE_VALID) == 0)
|
||||
return 0;
|
||||
|
||||
memcpy(ipr_cmd->scsi_cmd->sense_buffer, ioasa->auto_sense.data,
|
||||
@ -4190,7 +4217,8 @@ static void ipr_erp_start(struct ipr_ioa_cfg *ioa_cfg,
|
||||
case IPR_IOASC_NR_INIT_CMD_REQUIRED:
|
||||
break;
|
||||
default:
|
||||
scsi_cmd->result |= (DID_ERROR << 16);
|
||||
if (IPR_IOASC_SENSE_KEY(ioasc) > RECOVERED_ERROR)
|
||||
scsi_cmd->result |= (DID_ERROR << 16);
|
||||
if (!ipr_is_vset_device(res) && !ipr_is_naca_model(res))
|
||||
res->needs_sync_complete = 1;
|
||||
break;
|
||||
|
@ -36,8 +36,8 @@
|
||||
/*
|
||||
* Literals
|
||||
*/
|
||||
#define IPR_DRIVER_VERSION "2.1.3"
|
||||
#define IPR_DRIVER_DATE "(March 29, 2006)"
|
||||
#define IPR_DRIVER_VERSION "2.1.4"
|
||||
#define IPR_DRIVER_DATE "(August 2, 2006)"
|
||||
|
||||
/*
|
||||
* IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding
|
||||
@ -45,6 +45,7 @@
|
||||
* This can be adjusted at runtime through sysfs device attributes.
|
||||
*/
|
||||
#define IPR_MAX_CMD_PER_LUN 6
|
||||
#define IPR_MAX_CMD_PER_ATA_LUN 1
|
||||
|
||||
/*
|
||||
* IPR_NUM_BASE_CMD_BLKS: This defines the maximum number of
|
||||
@ -106,7 +107,7 @@
|
||||
#define IPR_IOA_BUS 0xff
|
||||
#define IPR_IOA_TARGET 0xff
|
||||
#define IPR_IOA_LUN 0xff
|
||||
#define IPR_MAX_NUM_BUSES 8
|
||||
#define IPR_MAX_NUM_BUSES 16
|
||||
#define IPR_MAX_BUS_TO_SCAN IPR_MAX_NUM_BUSES
|
||||
|
||||
#define IPR_NUM_RESET_RELOAD_RETRIES 3
|
||||
@ -145,6 +146,7 @@
|
||||
#define IPR_LUN_RESET 0x40
|
||||
#define IPR_TARGET_RESET 0x20
|
||||
#define IPR_BUS_RESET 0x10
|
||||
#define IPR_ATA_PHY_RESET 0x80
|
||||
#define IPR_ID_HOST_RR_Q 0xC4
|
||||
#define IPR_QUERY_IOA_CONFIG 0xC5
|
||||
#define IPR_CANCEL_ALL_REQUESTS 0xCE
|
||||
@ -295,7 +297,11 @@ struct ipr_std_inq_data {
|
||||
}__attribute__ ((packed));
|
||||
|
||||
struct ipr_config_table_entry {
|
||||
u8 service_level;
|
||||
u8 proto;
|
||||
#define IPR_PROTO_SATA 0x02
|
||||
#define IPR_PROTO_SATA_ATAPI 0x03
|
||||
#define IPR_PROTO_SAS_STP 0x06
|
||||
#define IPR_PROTO_SAS_STP_ATAPI 0x07
|
||||
u8 array_id;
|
||||
u8 flags;
|
||||
#define IPR_IS_IOA_RESOURCE 0x80
|
||||
@ -307,6 +313,7 @@ struct ipr_config_table_entry {
|
||||
#define IPR_SUBTYPE_AF_DASD 0
|
||||
#define IPR_SUBTYPE_GENERIC_SCSI 1
|
||||
#define IPR_SUBTYPE_VOLUME_SET 2
|
||||
#define IPR_SUBTYPE_GENERIC_ATA 4
|
||||
|
||||
#define IPR_QUEUEING_MODEL(res) ((((res)->cfgte.flags) & 0x70) >> 4)
|
||||
#define IPR_QUEUE_FROZEN_MODEL 0
|
||||
@ -350,6 +357,7 @@ struct ipr_cmd_pkt {
|
||||
#define IPR_RQTYPE_SCSICDB 0x00
|
||||
#define IPR_RQTYPE_IOACMD 0x01
|
||||
#define IPR_RQTYPE_HCAM 0x02
|
||||
#define IPR_RQTYPE_ATA_PASSTHRU 0x04
|
||||
|
||||
u8 luntar_luntrn;
|
||||
|
||||
@ -373,6 +381,37 @@ struct ipr_cmd_pkt {
|
||||
__be16 timeout;
|
||||
}__attribute__ ((packed, aligned(4)));
|
||||
|
||||
struct ipr_ioarcb_ata_regs {
|
||||
u8 flags;
|
||||
#define IPR_ATA_FLAG_PACKET_CMD 0x80
|
||||
#define IPR_ATA_FLAG_XFER_TYPE_DMA 0x40
|
||||
#define IPR_ATA_FLAG_STATUS_ON_GOOD_COMPLETION 0x20
|
||||
u8 reserved[3];
|
||||
|
||||
__be16 data;
|
||||
u8 feature;
|
||||
u8 nsect;
|
||||
u8 lbal;
|
||||
u8 lbam;
|
||||
u8 lbah;
|
||||
u8 device;
|
||||
u8 command;
|
||||
u8 reserved2[3];
|
||||
u8 hob_feature;
|
||||
u8 hob_nsect;
|
||||
u8 hob_lbal;
|
||||
u8 hob_lbam;
|
||||
u8 hob_lbah;
|
||||
u8 ctl;
|
||||
}__attribute__ ((packed, aligned(4)));
|
||||
|
||||
struct ipr_ioarcb_add_data {
|
||||
union {
|
||||
struct ipr_ioarcb_ata_regs regs;
|
||||
__be32 add_cmd_parms[10];
|
||||
}u;
|
||||
}__attribute__ ((packed, aligned(4)));
|
||||
|
||||
/* IOA Request Control Block 128 bytes */
|
||||
struct ipr_ioarcb {
|
||||
__be32 ioarcb_host_pci_addr;
|
||||
@ -397,7 +436,7 @@ struct ipr_ioarcb {
|
||||
struct ipr_cmd_pkt cmd_pkt;
|
||||
|
||||
__be32 add_cmd_parms_len;
|
||||
__be32 add_cmd_parms[10];
|
||||
struct ipr_ioarcb_add_data add_data;
|
||||
}__attribute__((packed, aligned (4)));
|
||||
|
||||
struct ipr_ioadl_desc {
|
||||
@ -433,6 +472,21 @@ struct ipr_ioasa_gpdd {
|
||||
__be32 ioa_data[2];
|
||||
}__attribute__((packed, aligned (4)));
|
||||
|
||||
struct ipr_ioasa_gata {
|
||||
u8 error;
|
||||
u8 nsect; /* Interrupt reason */
|
||||
u8 lbal;
|
||||
u8 lbam;
|
||||
u8 lbah;
|
||||
u8 device;
|
||||
u8 status;
|
||||
u8 alt_status; /* ATA CTL */
|
||||
u8 hob_nsect;
|
||||
u8 hob_lbal;
|
||||
u8 hob_lbam;
|
||||
u8 hob_lbah;
|
||||
}__attribute__((packed, aligned (4)));
|
||||
|
||||
struct ipr_auto_sense {
|
||||
__be16 auto_sense_len;
|
||||
__be16 ioa_data_len;
|
||||
@ -466,6 +520,7 @@ struct ipr_ioasa {
|
||||
__be32 ioasc_specific; /* status code specific field */
|
||||
#define IPR_ADDITIONAL_STATUS_FMT 0x80000000
|
||||
#define IPR_AUTOSENSE_VALID 0x40000000
|
||||
#define IPR_ATA_DEVICE_WAS_RESET 0x20000000
|
||||
#define IPR_IOASC_SPECIFIC_MASK 0x00ffffff
|
||||
#define IPR_FIELD_POINTER_VALID (0x80000000 >> 8)
|
||||
#define IPR_FIELD_POINTER_MASK 0x0000ffff
|
||||
@ -474,6 +529,7 @@ struct ipr_ioasa {
|
||||
struct ipr_ioasa_vset vset;
|
||||
struct ipr_ioasa_af_dasd dasd;
|
||||
struct ipr_ioasa_gpdd gpdd;
|
||||
struct ipr_ioasa_gata gata;
|
||||
} u;
|
||||
|
||||
struct ipr_auto_sense auto_sense;
|
||||
@ -1307,6 +1363,22 @@ static inline int ipr_is_scsi_disk(struct ipr_resource_entry *res)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipr_is_gata - Determine if a resource is a generic ATA resource
|
||||
* @res: resource entry struct
|
||||
*
|
||||
* Return value:
|
||||
* 1 if GATA / 0 if not GATA
|
||||
**/
|
||||
static inline int ipr_is_gata(struct ipr_resource_entry *res)
|
||||
{
|
||||
if (!ipr_is_ioa_resource(res) &&
|
||||
IPR_RES_SUBTYPE(res) == IPR_SUBTYPE_GENERIC_ATA)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipr_is_naca_model - Determine if a resource is using NACA queueing model
|
||||
* @res: resource entry struct
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -31,23 +31,21 @@
|
||||
#define IN_PROGRESS_DDIGEST_RECV 0x3
|
||||
|
||||
/* xmit state machine */
|
||||
#define XMSTATE_IDLE 0x0
|
||||
#define XMSTATE_R_HDR 0x1
|
||||
#define XMSTATE_W_HDR 0x2
|
||||
#define XMSTATE_IMM_HDR 0x4
|
||||
#define XMSTATE_IMM_DATA 0x8
|
||||
#define XMSTATE_UNS_INIT 0x10
|
||||
#define XMSTATE_UNS_HDR 0x20
|
||||
#define XMSTATE_UNS_DATA 0x40
|
||||
#define XMSTATE_SOL_HDR 0x80
|
||||
#define XMSTATE_SOL_DATA 0x100
|
||||
#define XMSTATE_W_PAD 0x200
|
||||
#define XMSTATE_DATA_DIGEST 0x400
|
||||
#define XMSTATE_IDLE 0x0
|
||||
#define XMSTATE_R_HDR 0x1
|
||||
#define XMSTATE_W_HDR 0x2
|
||||
#define XMSTATE_IMM_HDR 0x4
|
||||
#define XMSTATE_IMM_DATA 0x8
|
||||
#define XMSTATE_UNS_INIT 0x10
|
||||
#define XMSTATE_UNS_HDR 0x20
|
||||
#define XMSTATE_UNS_DATA 0x40
|
||||
#define XMSTATE_SOL_HDR 0x80
|
||||
#define XMSTATE_SOL_DATA 0x100
|
||||
#define XMSTATE_W_PAD 0x200
|
||||
#define XMSTATE_W_RESEND_PAD 0x400
|
||||
#define XMSTATE_W_RESEND_DATA_DIGEST 0x800
|
||||
|
||||
#define ISCSI_CONN_RCVBUF_MIN 262144
|
||||
#define ISCSI_CONN_SNDBUF_MIN 262144
|
||||
#define ISCSI_PAD_LEN 4
|
||||
#define ISCSI_R2T_MAX 16
|
||||
#define ISCSI_SG_TABLESIZE SG_ALL
|
||||
#define ISCSI_TCP_MAX_CMD_LEN 16
|
||||
|
||||
@ -85,9 +83,6 @@ struct iscsi_tcp_conn {
|
||||
/* iSCSI connection-wide sequencing */
|
||||
int hdr_size; /* PDU header size */
|
||||
|
||||
struct crypto_hash *rx_tfm; /* CRC32C (Rx) */
|
||||
struct hash_desc data_rx_hash; /* CRC32C (Rx) for data */
|
||||
|
||||
/* control data */
|
||||
struct iscsi_tcp_recv in; /* TCP receive context */
|
||||
int in_progress; /* connection state machine */
|
||||
@ -97,9 +92,9 @@ struct iscsi_tcp_conn {
|
||||
void (*old_state_change)(struct sock *);
|
||||
void (*old_write_space)(struct sock *);
|
||||
|
||||
/* xmit */
|
||||
struct crypto_hash *tx_tfm; /* CRC32C (Tx) */
|
||||
struct hash_desc data_tx_hash; /* CRC32C (Tx) for data */
|
||||
/* data and header digests */
|
||||
struct hash_desc tx_hash; /* CRC32C (Tx) */
|
||||
struct hash_desc rx_hash; /* CRC32C (Rx) */
|
||||
|
||||
/* MIB custom statistics */
|
||||
uint32_t sendpage_failures_cnt;
|
||||
@ -158,19 +153,15 @@ struct iscsi_tcp_cmd_task {
|
||||
struct scatterlist *bad_sg; /* assert statement */
|
||||
int sg_count; /* SG's to process */
|
||||
uint32_t exp_r2tsn;
|
||||
int r2t_data_count; /* R2T Data-Out bytes */
|
||||
int data_offset;
|
||||
struct iscsi_r2t_info *r2t; /* in progress R2T */
|
||||
struct iscsi_queue r2tpool;
|
||||
struct kfifo *r2tqueue;
|
||||
struct iscsi_r2t_info **r2ts;
|
||||
uint32_t datadigest; /* for recover digest */
|
||||
int digest_count;
|
||||
uint32_t immdigest; /* for imm data */
|
||||
struct iscsi_buf immbuf; /* for imm data digest */
|
||||
struct iscsi_data_task *dtask; /* data task in progress*/
|
||||
struct iscsi_data_task unsol_dtask; /* unsol data task */
|
||||
int digest_offset; /* for partial buff digest */
|
||||
};
|
||||
|
||||
#endif /* ISCSI_H */
|
||||
|
@ -32,7 +32,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
|
@ -68,8 +68,7 @@ iscsi_check_assign_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr)
|
||||
EXPORT_SYMBOL_GPL(iscsi_check_assign_cmdsn);
|
||||
|
||||
void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
|
||||
struct iscsi_data *hdr,
|
||||
int transport_data_cnt)
|
||||
struct iscsi_data *hdr)
|
||||
{
|
||||
struct iscsi_conn *conn = ctask->conn;
|
||||
|
||||
@ -82,14 +81,12 @@ void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,
|
||||
|
||||
hdr->itt = ctask->hdr->itt;
|
||||
hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);
|
||||
|
||||
hdr->offset = cpu_to_be32(ctask->total_length -
|
||||
transport_data_cnt -
|
||||
ctask->unsol_count);
|
||||
hdr->offset = cpu_to_be32(ctask->unsol_offset);
|
||||
|
||||
if (ctask->unsol_count > conn->max_xmit_dlength) {
|
||||
hton24(hdr->dlength, conn->max_xmit_dlength);
|
||||
ctask->data_count = conn->max_xmit_dlength;
|
||||
ctask->unsol_offset += ctask->data_count;
|
||||
hdr->flags = 0;
|
||||
} else {
|
||||
hton24(hdr->dlength, ctask->unsol_count);
|
||||
@ -125,6 +122,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
|
||||
memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);
|
||||
memset(&hdr->cdb[sc->cmd_len], 0, MAX_COMMAND_SIZE - sc->cmd_len);
|
||||
|
||||
ctask->data_count = 0;
|
||||
if (sc->sc_data_direction == DMA_TO_DEVICE) {
|
||||
hdr->flags |= ISCSI_FLAG_CMD_WRITE;
|
||||
/*
|
||||
@ -143,6 +141,7 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
|
||||
*/
|
||||
ctask->imm_count = 0;
|
||||
ctask->unsol_count = 0;
|
||||
ctask->unsol_offset = 0;
|
||||
ctask->unsol_datasn = 0;
|
||||
|
||||
if (session->imm_data_en) {
|
||||
@ -156,9 +155,12 @@ static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask)
|
||||
} else
|
||||
zero_data(ctask->hdr->dlength);
|
||||
|
||||
if (!session->initial_r2t_en)
|
||||
if (!session->initial_r2t_en) {
|
||||
ctask->unsol_count = min(session->first_burst,
|
||||
ctask->total_length) - ctask->imm_count;
|
||||
ctask->unsol_offset = ctask->imm_count;
|
||||
}
|
||||
|
||||
if (!ctask->unsol_count)
|
||||
/* No unsolicit Data-Out's */
|
||||
ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;
|
||||
@ -177,25 +179,51 @@ EXPORT_SYMBOL_GPL(iscsi_prep_scsi_cmd_pdu);
|
||||
|
||||
/**
|
||||
* iscsi_complete_command - return command back to scsi-ml
|
||||
* @session: iscsi session
|
||||
* @ctask: iscsi cmd task
|
||||
*
|
||||
* Must be called with session lock.
|
||||
* This function returns the scsi command to scsi-ml and returns
|
||||
* the cmd task to the pool of available cmd tasks.
|
||||
*/
|
||||
static void iscsi_complete_command(struct iscsi_session *session,
|
||||
struct iscsi_cmd_task *ctask)
|
||||
static void iscsi_complete_command(struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
struct iscsi_session *session = ctask->conn->session;
|
||||
struct scsi_cmnd *sc = ctask->sc;
|
||||
|
||||
ctask->state = ISCSI_TASK_COMPLETED;
|
||||
ctask->sc = NULL;
|
||||
/* SCSI eh reuses commands to verify us */
|
||||
sc->SCp.ptr = NULL;
|
||||
list_del_init(&ctask->running);
|
||||
__kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
|
||||
sc->scsi_done(sc);
|
||||
}
|
||||
|
||||
static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
atomic_inc(&ctask->refcount);
|
||||
}
|
||||
|
||||
static void iscsi_get_ctask(struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
spin_lock_bh(&ctask->conn->session->lock);
|
||||
__iscsi_get_ctask(ctask);
|
||||
spin_unlock_bh(&ctask->conn->session->lock);
|
||||
}
|
||||
|
||||
static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
if (atomic_dec_and_test(&ctask->refcount))
|
||||
iscsi_complete_command(ctask);
|
||||
}
|
||||
|
||||
static void iscsi_put_ctask(struct iscsi_cmd_task *ctask)
|
||||
{
|
||||
spin_lock_bh(&ctask->conn->session->lock);
|
||||
__iscsi_put_ctask(ctask);
|
||||
spin_unlock_bh(&ctask->conn->session->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* iscsi_cmd_rsp - SCSI Command Response processing
|
||||
* @conn: iscsi connection
|
||||
@ -272,7 +300,7 @@ out:
|
||||
(long)sc, sc->result, ctask->itt);
|
||||
conn->scsirsp_pdus_cnt++;
|
||||
|
||||
iscsi_complete_command(conn->session, ctask);
|
||||
__iscsi_put_ctask(ctask);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -295,6 +323,30 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
|
||||
wake_up(&conn->ehwait);
|
||||
}
|
||||
|
||||
static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
||||
char *data, int datalen)
|
||||
{
|
||||
struct iscsi_reject *reject = (struct iscsi_reject *)hdr;
|
||||
struct iscsi_hdr rejected_pdu;
|
||||
uint32_t itt;
|
||||
|
||||
conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;
|
||||
|
||||
if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) {
|
||||
if (ntoh24(reject->dlength) > datalen)
|
||||
return ISCSI_ERR_PROTO;
|
||||
|
||||
if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {
|
||||
memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));
|
||||
itt = rejected_pdu.itt & ISCSI_ITT_MASK;
|
||||
printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "
|
||||
"due to DataDigest error.\n", itt,
|
||||
rejected_pdu.opcode);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __iscsi_complete_pdu - complete pdu
|
||||
* @conn: iscsi conn
|
||||
@ -336,7 +388,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
||||
BUG_ON((void*)ctask != ctask->sc->SCp.ptr);
|
||||
if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
|
||||
conn->scsirsp_pdus_cnt++;
|
||||
iscsi_complete_command(session, ctask);
|
||||
__iscsi_put_ctask(ctask);
|
||||
}
|
||||
break;
|
||||
case ISCSI_OP_R2T:
|
||||
@ -406,6 +458,11 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
||||
break;
|
||||
}
|
||||
} else if (itt == ISCSI_RESERVED_TAG) {
|
||||
rc = iscsi_check_assign_cmdsn(session,
|
||||
(struct iscsi_nopin*)hdr);
|
||||
if (rc)
|
||||
goto done;
|
||||
|
||||
switch(opcode) {
|
||||
case ISCSI_OP_NOOP_IN:
|
||||
if (datalen) {
|
||||
@ -413,11 +470,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
||||
break;
|
||||
}
|
||||
|
||||
rc = iscsi_check_assign_cmdsn(session,
|
||||
(struct iscsi_nopin*)hdr);
|
||||
if (rc)
|
||||
break;
|
||||
|
||||
if (hdr->ttt == ISCSI_RESERVED_TAG)
|
||||
break;
|
||||
|
||||
@ -425,7 +477,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
|
||||
rc = ISCSI_ERR_CONN_FAILED;
|
||||
break;
|
||||
case ISCSI_OP_REJECT:
|
||||
/* we need sth like iscsi_reject_rsp()*/
|
||||
rc = iscsi_handle_reject(conn, hdr, data, datalen);
|
||||
break;
|
||||
case ISCSI_OP_ASYNC_EVENT:
|
||||
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
|
||||
/* we need sth like iscsi_async_event_rsp() */
|
||||
@ -561,7 +614,9 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
|
||||
BUG_ON(conn->ctask && conn->mtask);
|
||||
|
||||
if (conn->ctask) {
|
||||
iscsi_get_ctask(conn->ctask);
|
||||
rc = tt->xmit_cmd_task(conn, conn->ctask);
|
||||
iscsi_put_ctask(conn->ctask);
|
||||
if (rc)
|
||||
goto again;
|
||||
/* done with this in-progress ctask */
|
||||
@ -602,12 +657,19 @@ static int iscsi_data_xmit(struct iscsi_conn *conn)
|
||||
struct iscsi_cmd_task, running);
|
||||
conn->ctask->state = ISCSI_TASK_RUNNING;
|
||||
list_move_tail(conn->xmitqueue.next, &conn->run_list);
|
||||
__iscsi_get_ctask(conn->ctask);
|
||||
spin_unlock_bh(&conn->session->lock);
|
||||
|
||||
rc = tt->xmit_cmd_task(conn, conn->ctask);
|
||||
if (rc)
|
||||
goto again;
|
||||
|
||||
spin_lock_bh(&conn->session->lock);
|
||||
__iscsi_put_ctask(conn->ctask);
|
||||
if (rc) {
|
||||
spin_unlock_bh(&conn->session->lock);
|
||||
goto again;
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&conn->session->lock);
|
||||
/* done with this ctask */
|
||||
@ -657,6 +719,7 @@ enum {
|
||||
FAILURE_SESSION_FAILED,
|
||||
FAILURE_SESSION_FREED,
|
||||
FAILURE_WINDOW_CLOSED,
|
||||
FAILURE_OOM,
|
||||
FAILURE_SESSION_TERMINATE,
|
||||
FAILURE_SESSION_IN_RECOVERY,
|
||||
FAILURE_SESSION_RECOVERY_TIMEOUT,
|
||||
@ -672,6 +735,7 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
|
||||
|
||||
sc->scsi_done = done;
|
||||
sc->result = 0;
|
||||
sc->SCp.ptr = NULL;
|
||||
|
||||
host = sc->device->host;
|
||||
session = iscsi_hostdata(host->hostdata);
|
||||
@ -715,10 +779,15 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
|
||||
|
||||
conn = session->leadconn;
|
||||
|
||||
__kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*));
|
||||
if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask,
|
||||
sizeof(void*))) {
|
||||
reason = FAILURE_OOM;
|
||||
goto reject;
|
||||
}
|
||||
sc->SCp.phase = session->age;
|
||||
sc->SCp.ptr = (char *)ctask;
|
||||
|
||||
atomic_set(&ctask->refcount, 1);
|
||||
ctask->state = ISCSI_TASK_PENDING;
|
||||
ctask->mtask = NULL;
|
||||
ctask->conn = conn;
|
||||
@ -731,9 +800,10 @@ int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *))
|
||||
|
||||
list_add_tail(&ctask->running, &conn->xmitqueue);
|
||||
debug_scsi(
|
||||
"ctask enq [%s cid %d sc %lx itt 0x%x len %d cmdsn %d win %d]\n",
|
||||
"ctask enq [%s cid %d sc %p cdb 0x%x itt 0x%x len %d cmdsn %d "
|
||||
"win %d]\n",
|
||||
sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",
|
||||
conn->id, (long)sc, ctask->itt, sc->request_bufflen,
|
||||
conn->id, sc, sc->cmnd[0], ctask->itt, sc->request_bufflen,
|
||||
session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);
|
||||
spin_unlock(&session->lock);
|
||||
|
||||
@ -1061,16 +1131,30 @@ static void fail_command(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask,
|
||||
|
||||
sc->result = err;
|
||||
sc->resid = sc->request_bufflen;
|
||||
iscsi_complete_command(conn->session, ctask);
|
||||
/* release ref from queuecommand */
|
||||
__iscsi_put_ctask(ctask);
|
||||
}
|
||||
|
||||
int iscsi_eh_abort(struct scsi_cmnd *sc)
|
||||
{
|
||||
struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
|
||||
struct iscsi_conn *conn = ctask->conn;
|
||||
struct iscsi_session *session = conn->session;
|
||||
struct iscsi_cmd_task *ctask;
|
||||
struct iscsi_conn *conn;
|
||||
struct iscsi_session *session;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* if session was ISCSI_STATE_IN_RECOVERY then we may not have
|
||||
* got the command.
|
||||
*/
|
||||
if (!sc->SCp.ptr) {
|
||||
debug_scsi("sc never reached iscsi layer or it completed.\n");
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
ctask = (struct iscsi_cmd_task *)sc->SCp.ptr;
|
||||
conn = ctask->conn;
|
||||
session = conn->session;
|
||||
|
||||
conn->eh_abort_cnt++;
|
||||
debug_scsi("aborting [sc %p itt 0x%x]\n", sc, ctask->itt);
|
||||
|
||||
@ -1520,11 +1604,19 @@ int iscsi_conn_start(struct iscsi_cls_conn *cls_conn)
|
||||
struct iscsi_conn *conn = cls_conn->dd_data;
|
||||
struct iscsi_session *session = conn->session;
|
||||
|
||||
if (session == NULL) {
|
||||
if (!session) {
|
||||
printk(KERN_ERR "iscsi: can't start unbound connection\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if ((session->imm_data_en || !session->initial_r2t_en) &&
|
||||
session->first_burst > session->max_burst) {
|
||||
printk("iscsi: invalid burst lengths: "
|
||||
"first_burst %d max_burst %d\n",
|
||||
session->first_burst, session->max_burst);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock_bh(&session->lock);
|
||||
conn->c_stage = ISCSI_CONN_STARTED;
|
||||
session->state = ISCSI_STATE_LOGGED_IN;
|
||||
|
39
drivers/scsi/libsas/Kconfig
Normal file
39
drivers/scsi/libsas/Kconfig
Normal file
@ -0,0 +1,39 @@
|
||||
#
|
||||
# Kernel configuration file for the SAS Class
|
||||
#
|
||||
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
#
|
||||
# This file is licensed under GPLv2.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 of the
|
||||
# License.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
# USA
|
||||
#
|
||||
|
||||
config SCSI_SAS_LIBSAS
|
||||
tristate "SAS Domain Transport Attributes"
|
||||
depends on SCSI
|
||||
select SCSI_SAS_ATTRS
|
||||
help
|
||||
This provides transport specific helpers for SAS drivers which
|
||||
use the domain device construct (like the aic94xxx).
|
||||
|
||||
config SCSI_SAS_LIBSAS_DEBUG
|
||||
bool "Compile the SAS Domain Transport Attributes in debug mode"
|
||||
default y
|
||||
depends on SCSI_SAS_LIBSAS
|
||||
help
|
||||
Compiles the SAS Layer in debug mode. In debug mode, the
|
||||
SAS Layer prints diagnostic and debug messages.
|
36
drivers/scsi/libsas/Makefile
Normal file
36
drivers/scsi/libsas/Makefile
Normal file
@ -0,0 +1,36 @@
|
||||
#
|
||||
# Kernel Makefile for the libsas helpers
|
||||
#
|
||||
# Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
# Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
#
|
||||
# This file is licensed under GPLv2.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License as
|
||||
# published by the Free Software Foundation; version 2 of the
|
||||
# License.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
# USA
|
||||
|
||||
ifeq ($(CONFIG_SCSI_SAS_LIBSAS_DEBUG),y)
|
||||
EXTRA_CFLAGS += -DSAS_DEBUG
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_SCSI_SAS_LIBSAS) += libsas.o
|
||||
libsas-y += sas_init.o \
|
||||
sas_phy.o \
|
||||
sas_port.o \
|
||||
sas_event.o \
|
||||
sas_dump.o \
|
||||
sas_discover.o \
|
||||
sas_expander.o \
|
||||
sas_scsi_host.o
|
749
drivers/scsi/libsas/sas_discover.c
Normal file
749
drivers/scsi/libsas/sas_discover.c
Normal file
@ -0,0 +1,749 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Discover process
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_eh.h>
|
||||
#include "sas_internal.h"
|
||||
|
||||
#include <scsi/scsi_transport.h>
|
||||
#include <scsi/scsi_transport_sas.h>
|
||||
#include "../scsi_sas_internal.h"
|
||||
|
||||
/* ---------- Basic task processing for discovery purposes ---------- */
|
||||
|
||||
void sas_init_dev(struct domain_device *dev)
|
||||
{
|
||||
INIT_LIST_HEAD(&dev->siblings);
|
||||
INIT_LIST_HEAD(&dev->dev_list_node);
|
||||
switch (dev->dev_type) {
|
||||
case SAS_END_DEV:
|
||||
break;
|
||||
case EDGE_DEV:
|
||||
case FANOUT_DEV:
|
||||
INIT_LIST_HEAD(&dev->ex_dev.children);
|
||||
break;
|
||||
case SATA_DEV:
|
||||
case SATA_PM:
|
||||
case SATA_PM_PORT:
|
||||
INIT_LIST_HEAD(&dev->sata_dev.children);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sas_task_timedout(unsigned long _task)
|
||||
{
|
||||
struct sas_task *task = (void *) _task;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE))
|
||||
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
complete(&task->completion);
|
||||
}
|
||||
|
||||
static void sas_disc_task_done(struct sas_task *task)
|
||||
{
|
||||
if (!del_timer(&task->timer))
|
||||
return;
|
||||
complete(&task->completion);
|
||||
}
|
||||
|
||||
#define SAS_DEV_TIMEOUT 10
|
||||
|
||||
/**
|
||||
* sas_execute_task -- Basic task processing for discovery
|
||||
* @task: the task to be executed
|
||||
* @buffer: pointer to buffer to do I/O
|
||||
* @size: size of @buffer
|
||||
* @pci_dma_dir: PCI_DMA_...
|
||||
*/
|
||||
static int sas_execute_task(struct sas_task *task, void *buffer, int size,
|
||||
int pci_dma_dir)
|
||||
{
|
||||
int res = 0;
|
||||
struct scatterlist *scatter = NULL;
|
||||
struct task_status_struct *ts = &task->task_status;
|
||||
int num_scatter = 0;
|
||||
int retries = 0;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(task->dev->port->ha->core.shost->transportt);
|
||||
|
||||
if (pci_dma_dir != PCI_DMA_NONE) {
|
||||
scatter = kzalloc(sizeof(*scatter), GFP_KERNEL);
|
||||
if (!scatter)
|
||||
goto out;
|
||||
|
||||
sg_init_one(scatter, buffer, size);
|
||||
num_scatter = 1;
|
||||
}
|
||||
|
||||
task->task_proto = task->dev->tproto;
|
||||
task->scatter = scatter;
|
||||
task->num_scatter = num_scatter;
|
||||
task->total_xfer_len = size;
|
||||
task->data_dir = pci_dma_dir;
|
||||
task->task_done = sas_disc_task_done;
|
||||
|
||||
for (retries = 0; retries < 5; retries++) {
|
||||
task->task_state_flags = SAS_TASK_STATE_PENDING;
|
||||
init_completion(&task->completion);
|
||||
|
||||
task->timer.data = (unsigned long) task;
|
||||
task->timer.function = sas_task_timedout;
|
||||
task->timer.expires = jiffies + SAS_DEV_TIMEOUT*HZ;
|
||||
add_timer(&task->timer);
|
||||
|
||||
res = i->dft->lldd_execute_task(task, 1, GFP_KERNEL);
|
||||
if (res) {
|
||||
del_timer(&task->timer);
|
||||
SAS_DPRINTK("executing SAS discovery task failed:%d\n",
|
||||
res);
|
||||
goto ex_err;
|
||||
}
|
||||
wait_for_completion(&task->completion);
|
||||
res = -ETASK;
|
||||
if (task->task_state_flags & SAS_TASK_STATE_ABORTED) {
|
||||
int res2;
|
||||
SAS_DPRINTK("task aborted, flags:0x%x\n",
|
||||
task->task_state_flags);
|
||||
res2 = i->dft->lldd_abort_task(task);
|
||||
SAS_DPRINTK("came back from abort task\n");
|
||||
if (!(task->task_state_flags & SAS_TASK_STATE_DONE)) {
|
||||
if (res2 == TMF_RESP_FUNC_COMPLETE)
|
||||
continue; /* Retry the task */
|
||||
else
|
||||
goto ex_err;
|
||||
}
|
||||
}
|
||||
if (task->task_status.stat == SAM_BUSY ||
|
||||
task->task_status.stat == SAM_TASK_SET_FULL ||
|
||||
task->task_status.stat == SAS_QUEUE_FULL) {
|
||||
SAS_DPRINTK("task: q busy, sleeping...\n");
|
||||
schedule_timeout_interruptible(HZ);
|
||||
} else if (task->task_status.stat == SAM_CHECK_COND) {
|
||||
struct scsi_sense_hdr shdr;
|
||||
|
||||
if (!scsi_normalize_sense(ts->buf, ts->buf_valid_size,
|
||||
&shdr)) {
|
||||
SAS_DPRINTK("couldn't normalize sense\n");
|
||||
continue;
|
||||
}
|
||||
if ((shdr.sense_key == 6 && shdr.asc == 0x29) ||
|
||||
(shdr.sense_key == 2 && shdr.asc == 4 &&
|
||||
shdr.ascq == 1)) {
|
||||
SAS_DPRINTK("device %016llx LUN: %016llx "
|
||||
"powering up or not ready yet, "
|
||||
"sleeping...\n",
|
||||
SAS_ADDR(task->dev->sas_addr),
|
||||
SAS_ADDR(task->ssp_task.LUN));
|
||||
|
||||
schedule_timeout_interruptible(5*HZ);
|
||||
} else if (shdr.sense_key == 1) {
|
||||
res = 0;
|
||||
break;
|
||||
} else if (shdr.sense_key == 5) {
|
||||
break;
|
||||
} else {
|
||||
SAS_DPRINTK("dev %016llx LUN: %016llx "
|
||||
"sense key:0x%x ASC:0x%x ASCQ:0x%x"
|
||||
"\n",
|
||||
SAS_ADDR(task->dev->sas_addr),
|
||||
SAS_ADDR(task->ssp_task.LUN),
|
||||
shdr.sense_key,
|
||||
shdr.asc, shdr.ascq);
|
||||
}
|
||||
} else if (task->task_status.resp != SAS_TASK_COMPLETE ||
|
||||
task->task_status.stat != SAM_GOOD) {
|
||||
SAS_DPRINTK("task finished with resp:0x%x, "
|
||||
"stat:0x%x\n",
|
||||
task->task_status.resp,
|
||||
task->task_status.stat);
|
||||
goto ex_err;
|
||||
} else {
|
||||
res = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ex_err:
|
||||
if (pci_dma_dir != PCI_DMA_NONE)
|
||||
kfree(scatter);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ---------- Domain device discovery ---------- */
|
||||
|
||||
/**
|
||||
* sas_get_port_device -- Discover devices which caused port creation
|
||||
* @port: pointer to struct sas_port of interest
|
||||
*
|
||||
* Devices directly attached to a HA port, have no parent. This is
|
||||
* how we know they are (domain) "root" devices. All other devices
|
||||
* do, and should have their "parent" pointer set appropriately as
|
||||
* soon as a child device is discovered.
|
||||
*/
|
||||
static int sas_get_port_device(struct asd_sas_port *port)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct asd_sas_phy *phy;
|
||||
struct sas_rphy *rphy;
|
||||
struct domain_device *dev;
|
||||
|
||||
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_irqsave(&port->phy_list_lock, flags);
|
||||
if (list_empty(&port->phy_list)) {
|
||||
spin_unlock_irqrestore(&port->phy_list_lock, flags);
|
||||
kfree(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
phy = container_of(port->phy_list.next, struct asd_sas_phy, port_phy_el);
|
||||
spin_lock(&phy->frame_rcvd_lock);
|
||||
memcpy(dev->frame_rcvd, phy->frame_rcvd, min(sizeof(dev->frame_rcvd),
|
||||
(size_t)phy->frame_rcvd_size));
|
||||
spin_unlock(&phy->frame_rcvd_lock);
|
||||
spin_unlock_irqrestore(&port->phy_list_lock, flags);
|
||||
|
||||
if (dev->frame_rcvd[0] == 0x34 && port->oob_mode == SATA_OOB_MODE) {
|
||||
struct dev_to_host_fis *fis =
|
||||
(struct dev_to_host_fis *) dev->frame_rcvd;
|
||||
if (fis->interrupt_reason == 1 && fis->lbal == 1 &&
|
||||
fis->byte_count_low==0x69 && fis->byte_count_high == 0x96
|
||||
&& (fis->device & ~0x10) == 0)
|
||||
dev->dev_type = SATA_PM;
|
||||
else
|
||||
dev->dev_type = SATA_DEV;
|
||||
dev->tproto = SATA_PROTO;
|
||||
} else {
|
||||
struct sas_identify_frame *id =
|
||||
(struct sas_identify_frame *) dev->frame_rcvd;
|
||||
dev->dev_type = id->dev_type;
|
||||
dev->iproto = id->initiator_bits;
|
||||
dev->tproto = id->target_bits;
|
||||
}
|
||||
|
||||
sas_init_dev(dev);
|
||||
|
||||
switch (dev->dev_type) {
|
||||
case SAS_END_DEV:
|
||||
rphy = sas_end_device_alloc(port->port);
|
||||
break;
|
||||
case EDGE_DEV:
|
||||
rphy = sas_expander_alloc(port->port,
|
||||
SAS_EDGE_EXPANDER_DEVICE);
|
||||
break;
|
||||
case FANOUT_DEV:
|
||||
rphy = sas_expander_alloc(port->port,
|
||||
SAS_FANOUT_EXPANDER_DEVICE);
|
||||
break;
|
||||
case SATA_DEV:
|
||||
default:
|
||||
printk("ERROR: Unidentified device type %d\n", dev->dev_type);
|
||||
rphy = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!rphy) {
|
||||
kfree(dev);
|
||||
return -ENODEV;
|
||||
}
|
||||
rphy->identify.phy_identifier = phy->phy->identify.phy_identifier;
|
||||
memcpy(dev->sas_addr, port->attached_sas_addr, SAS_ADDR_SIZE);
|
||||
sas_fill_in_rphy(dev, rphy);
|
||||
sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
|
||||
port->port_dev = dev;
|
||||
dev->port = port;
|
||||
dev->linkrate = port->linkrate;
|
||||
dev->min_linkrate = port->linkrate;
|
||||
dev->max_linkrate = port->linkrate;
|
||||
dev->pathways = port->num_phys;
|
||||
memset(port->disc.fanout_sas_addr, 0, SAS_ADDR_SIZE);
|
||||
memset(port->disc.eeds_a, 0, SAS_ADDR_SIZE);
|
||||
memset(port->disc.eeds_b, 0, SAS_ADDR_SIZE);
|
||||
port->disc.max_level = 0;
|
||||
|
||||
dev->rphy = rphy;
|
||||
spin_lock(&port->dev_list_lock);
|
||||
list_add_tail(&dev->dev_list_node, &port->dev_list);
|
||||
spin_unlock(&port->dev_list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- Discover and Revalidate ---------- */
|
||||
|
||||
/* ---------- SATA ---------- */
|
||||
|
||||
static void sas_get_ata_command_set(struct domain_device *dev)
|
||||
{
|
||||
struct dev_to_host_fis *fis =
|
||||
(struct dev_to_host_fis *) dev->frame_rcvd;
|
||||
|
||||
if ((fis->sector_count == 1 && /* ATA */
|
||||
fis->lbal == 1 &&
|
||||
fis->lbam == 0 &&
|
||||
fis->lbah == 0 &&
|
||||
fis->device == 0)
|
||||
||
|
||||
(fis->sector_count == 0 && /* CE-ATA (mATA) */
|
||||
fis->lbal == 0 &&
|
||||
fis->lbam == 0xCE &&
|
||||
fis->lbah == 0xAA &&
|
||||
(fis->device & ~0x10) == 0))
|
||||
|
||||
dev->sata_dev.command_set = ATA_COMMAND_SET;
|
||||
|
||||
else if ((fis->interrupt_reason == 1 && /* ATAPI */
|
||||
fis->lbal == 1 &&
|
||||
fis->byte_count_low == 0x14 &&
|
||||
fis->byte_count_high == 0xEB &&
|
||||
(fis->device & ~0x10) == 0))
|
||||
|
||||
dev->sata_dev.command_set = ATAPI_COMMAND_SET;
|
||||
|
||||
else if ((fis->sector_count == 1 && /* SEMB */
|
||||
fis->lbal == 1 &&
|
||||
fis->lbam == 0x3C &&
|
||||
fis->lbah == 0xC3 &&
|
||||
fis->device == 0)
|
||||
||
|
||||
(fis->interrupt_reason == 1 && /* SATA PM */
|
||||
fis->lbal == 1 &&
|
||||
fis->byte_count_low == 0x69 &&
|
||||
fis->byte_count_high == 0x96 &&
|
||||
(fis->device & ~0x10) == 0))
|
||||
|
||||
/* Treat it as a superset? */
|
||||
dev->sata_dev.command_set = ATAPI_COMMAND_SET;
|
||||
}
|
||||
|
||||
/**
|
||||
* sas_issue_ata_cmd -- Basic SATA command processing for discovery
|
||||
* @dev: the device to send the command to
|
||||
* @command: the command register
|
||||
* @features: the features register
|
||||
* @buffer: pointer to buffer to do I/O
|
||||
* @size: size of @buffer
|
||||
* @pci_dma_dir: PCI_DMA_...
|
||||
*/
|
||||
static int sas_issue_ata_cmd(struct domain_device *dev, u8 command,
|
||||
u8 features, void *buffer, int size,
|
||||
int pci_dma_dir)
|
||||
{
|
||||
int res = 0;
|
||||
struct sas_task *task;
|
||||
struct dev_to_host_fis *d2h_fis = (struct dev_to_host_fis *)
|
||||
&dev->frame_rcvd[0];
|
||||
|
||||
res = -ENOMEM;
|
||||
task = sas_alloc_task(GFP_KERNEL);
|
||||
if (!task)
|
||||
goto out;
|
||||
|
||||
task->dev = dev;
|
||||
|
||||
task->ata_task.fis.command = command;
|
||||
task->ata_task.fis.features = features;
|
||||
task->ata_task.fis.device = d2h_fis->device;
|
||||
task->ata_task.retry_count = 1;
|
||||
|
||||
res = sas_execute_task(task, buffer, size, pci_dma_dir);
|
||||
|
||||
sas_free_task(task);
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sas_sata_propagate_sas_addr(struct domain_device *dev)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct asd_sas_port *port = dev->port;
|
||||
struct asd_sas_phy *phy;
|
||||
|
||||
BUG_ON(dev->parent);
|
||||
|
||||
memcpy(port->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE);
|
||||
spin_lock_irqsave(&port->phy_list_lock, flags);
|
||||
list_for_each_entry(phy, &port->phy_list, port_phy_el)
|
||||
memcpy(phy->attached_sas_addr, dev->sas_addr, SAS_ADDR_SIZE);
|
||||
spin_unlock_irqrestore(&port->phy_list_lock, flags);
|
||||
}
|
||||
|
||||
#define ATA_IDENTIFY_DEV 0xEC
|
||||
#define ATA_IDENTIFY_PACKET_DEV 0xA1
|
||||
#define ATA_SET_FEATURES 0xEF
|
||||
#define ATA_FEATURE_PUP_STBY_SPIN_UP 0x07
|
||||
|
||||
/**
|
||||
* sas_discover_sata_dev -- discover a STP/SATA device (SATA_DEV)
|
||||
* @dev: STP/SATA device of interest (ATA/ATAPI)
|
||||
*
|
||||
* The LLDD has already been notified of this device, so that we can
|
||||
* send FISes to it. Here we try to get IDENTIFY DEVICE or IDENTIFY
|
||||
* PACKET DEVICE, if ATAPI device, so that the LLDD can fine-tune its
|
||||
* performance for this device.
|
||||
*/
|
||||
static int sas_discover_sata_dev(struct domain_device *dev)
|
||||
{
|
||||
int res;
|
||||
__le16 *identify_x;
|
||||
u8 command;
|
||||
|
||||
identify_x = kzalloc(512, GFP_KERNEL);
|
||||
if (!identify_x)
|
||||
return -ENOMEM;
|
||||
|
||||
if (dev->sata_dev.command_set == ATA_COMMAND_SET) {
|
||||
dev->sata_dev.identify_device = identify_x;
|
||||
command = ATA_IDENTIFY_DEV;
|
||||
} else {
|
||||
dev->sata_dev.identify_packet_device = identify_x;
|
||||
command = ATA_IDENTIFY_PACKET_DEV;
|
||||
}
|
||||
|
||||
res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
if (res)
|
||||
goto out_err;
|
||||
|
||||
/* lives on the media? */
|
||||
if (le16_to_cpu(identify_x[0]) & 4) {
|
||||
/* incomplete response */
|
||||
SAS_DPRINTK("sending SET FEATURE/PUP_STBY_SPIN_UP to "
|
||||
"dev %llx\n", SAS_ADDR(dev->sas_addr));
|
||||
if (!le16_to_cpu(identify_x[83] & (1<<6)))
|
||||
goto cont1;
|
||||
res = sas_issue_ata_cmd(dev, ATA_SET_FEATURES,
|
||||
ATA_FEATURE_PUP_STBY_SPIN_UP,
|
||||
NULL, 0, PCI_DMA_NONE);
|
||||
if (res)
|
||||
goto cont1;
|
||||
|
||||
schedule_timeout_interruptible(5*HZ); /* More time? */
|
||||
res = sas_issue_ata_cmd(dev, command, 0, identify_x, 512,
|
||||
PCI_DMA_FROMDEVICE);
|
||||
if (res)
|
||||
goto out_err;
|
||||
}
|
||||
cont1:
|
||||
/* Get WWN */
|
||||
if (dev->port->oob_mode != SATA_OOB_MODE) {
|
||||
memcpy(dev->sas_addr, dev->sata_dev.rps_resp.rps.stp_sas_addr,
|
||||
SAS_ADDR_SIZE);
|
||||
} else if (dev->sata_dev.command_set == ATA_COMMAND_SET &&
|
||||
(le16_to_cpu(dev->sata_dev.identify_device[108]) & 0xF000)
|
||||
== 0x5000) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
dev->sas_addr[2*i] =
|
||||
(le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0xFF00) >> 8;
|
||||
dev->sas_addr[2*i+1] =
|
||||
le16_to_cpu(dev->sata_dev.identify_device[108+i]) & 0x00FF;
|
||||
}
|
||||
}
|
||||
sas_hash_addr(dev->hashed_sas_addr, dev->sas_addr);
|
||||
if (!dev->parent)
|
||||
sas_sata_propagate_sas_addr(dev);
|
||||
|
||||
/* XXX Hint: register this SATA device with SATL.
|
||||
When this returns, dev->sata_dev->lu is alive and
|
||||
present.
|
||||
sas_satl_register_dev(dev);
|
||||
*/
|
||||
return 0;
|
||||
out_err:
|
||||
dev->sata_dev.identify_packet_device = NULL;
|
||||
dev->sata_dev.identify_device = NULL;
|
||||
kfree(identify_x);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sas_discover_sata_pm(struct domain_device *dev)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int sas_notify_lldd_dev_found(struct domain_device *dev)
|
||||
{
|
||||
int res = 0;
|
||||
struct sas_ha_struct *sas_ha = dev->port->ha;
|
||||
struct Scsi_Host *shost = sas_ha->core.shost;
|
||||
struct sas_internal *i = to_sas_internal(shost->transportt);
|
||||
|
||||
if (i->dft->lldd_dev_found) {
|
||||
res = i->dft->lldd_dev_found(dev);
|
||||
if (res) {
|
||||
printk("sas: driver on pcidev %s cannot handle "
|
||||
"device %llx, error:%d\n",
|
||||
pci_name(sas_ha->pcidev),
|
||||
SAS_ADDR(dev->sas_addr), res);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void sas_notify_lldd_dev_gone(struct domain_device *dev)
|
||||
{
|
||||
struct sas_ha_struct *sas_ha = dev->port->ha;
|
||||
struct Scsi_Host *shost = sas_ha->core.shost;
|
||||
struct sas_internal *i = to_sas_internal(shost->transportt);
|
||||
|
||||
if (i->dft->lldd_dev_gone)
|
||||
i->dft->lldd_dev_gone(dev);
|
||||
}
|
||||
|
||||
/* ---------- Common/dispatchers ---------- */
|
||||
|
||||
/**
|
||||
* sas_discover_sata -- discover an STP/SATA domain device
|
||||
* @dev: pointer to struct domain_device of interest
|
||||
*
|
||||
* First we notify the LLDD of this device, so we can send frames to
|
||||
* it. Then depending on the type of device we call the appropriate
|
||||
* discover functions. Once device discover is done, we notify the
|
||||
* LLDD so that it can fine-tune its parameters for the device, by
|
||||
* removing it and then adding it. That is, the second time around,
|
||||
* the driver would have certain fields, that it is looking at, set.
|
||||
* Finally we initialize the kobj so that the device can be added to
|
||||
* the system at registration time. Devices directly attached to a HA
|
||||
* port, have no parents. All other devices do, and should have their
|
||||
* "parent" pointer set appropriately before calling this function.
|
||||
*/
|
||||
int sas_discover_sata(struct domain_device *dev)
|
||||
{
|
||||
int res;
|
||||
|
||||
sas_get_ata_command_set(dev);
|
||||
|
||||
res = sas_notify_lldd_dev_found(dev);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
switch (dev->dev_type) {
|
||||
case SATA_DEV:
|
||||
res = sas_discover_sata_dev(dev);
|
||||
break;
|
||||
case SATA_PM:
|
||||
res = sas_discover_sata_pm(dev);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sas_notify_lldd_dev_gone(dev);
|
||||
if (!res) {
|
||||
sas_notify_lldd_dev_found(dev);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* sas_discover_end_dev -- discover an end device (SSP, etc)
|
||||
* @end: pointer to domain device of interest
|
||||
*
|
||||
* See comment in sas_discover_sata().
|
||||
*/
|
||||
int sas_discover_end_dev(struct domain_device *dev)
|
||||
{
|
||||
int res;
|
||||
|
||||
res = sas_notify_lldd_dev_found(dev);
|
||||
if (res)
|
||||
return res;
|
||||
|
||||
res = sas_rphy_add(dev->rphy);
|
||||
if (res)
|
||||
goto out_err;
|
||||
|
||||
/* do this to get the end device port attributes which will have
|
||||
* been scanned in sas_rphy_add */
|
||||
sas_notify_lldd_dev_gone(dev);
|
||||
sas_notify_lldd_dev_found(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
sas_notify_lldd_dev_gone(dev);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* ---------- Device registration and unregistration ---------- */
|
||||
|
||||
static inline void sas_unregister_common_dev(struct domain_device *dev)
|
||||
{
|
||||
sas_notify_lldd_dev_gone(dev);
|
||||
if (!dev->parent)
|
||||
dev->port->port_dev = NULL;
|
||||
else
|
||||
list_del_init(&dev->siblings);
|
||||
list_del_init(&dev->dev_list_node);
|
||||
}
|
||||
|
||||
void sas_unregister_dev(struct domain_device *dev)
|
||||
{
|
||||
if (dev->rphy) {
|
||||
sas_remove_children(&dev->rphy->dev);
|
||||
sas_rphy_delete(dev->rphy);
|
||||
dev->rphy = NULL;
|
||||
}
|
||||
if (dev->dev_type == EDGE_DEV || dev->dev_type == FANOUT_DEV) {
|
||||
/* remove the phys and ports, everything else should be gone */
|
||||
kfree(dev->ex_dev.ex_phy);
|
||||
dev->ex_dev.ex_phy = NULL;
|
||||
}
|
||||
sas_unregister_common_dev(dev);
|
||||
}
|
||||
|
||||
void sas_unregister_domain_devices(struct asd_sas_port *port)
|
||||
{
|
||||
struct domain_device *dev, *n;
|
||||
|
||||
list_for_each_entry_safe_reverse(dev,n,&port->dev_list,dev_list_node)
|
||||
sas_unregister_dev(dev);
|
||||
|
||||
port->port->rphy = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* ---------- Discovery and Revalidation ---------- */
|
||||
|
||||
/**
|
||||
* sas_discover_domain -- discover the domain
|
||||
* @port: port to the domain of interest
|
||||
*
|
||||
* NOTE: this process _must_ quit (return) as soon as any connection
|
||||
* errors are encountered. Connection recovery is done elsewhere.
|
||||
* Discover process only interrogates devices in order to discover the
|
||||
* domain.
|
||||
*/
|
||||
static void sas_discover_domain(void *data)
|
||||
{
|
||||
int error = 0;
|
||||
struct asd_sas_port *port = data;
|
||||
|
||||
sas_begin_event(DISCE_DISCOVER_DOMAIN, &port->disc.disc_event_lock,
|
||||
&port->disc.pending);
|
||||
|
||||
if (port->port_dev)
|
||||
return ;
|
||||
else {
|
||||
error = sas_get_port_device(port);
|
||||
if (error)
|
||||
return;
|
||||
}
|
||||
|
||||
SAS_DPRINTK("DOING DISCOVERY on port %d, pid:%d\n", port->id,
|
||||
current->pid);
|
||||
|
||||
switch (port->port_dev->dev_type) {
|
||||
case SAS_END_DEV:
|
||||
error = sas_discover_end_dev(port->port_dev);
|
||||
break;
|
||||
case EDGE_DEV:
|
||||
case FANOUT_DEV:
|
||||
error = sas_discover_root_expander(port->port_dev);
|
||||
break;
|
||||
case SATA_DEV:
|
||||
case SATA_PM:
|
||||
error = sas_discover_sata(port->port_dev);
|
||||
break;
|
||||
default:
|
||||
SAS_DPRINTK("unhandled device %d\n", port->port_dev->dev_type);
|
||||
break;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
kfree(port->port_dev); /* not kobject_register-ed yet */
|
||||
port->port_dev = NULL;
|
||||
}
|
||||
|
||||
SAS_DPRINTK("DONE DISCOVERY on port %d, pid:%d, result:%d\n", port->id,
|
||||
current->pid, error);
|
||||
}
|
||||
|
||||
static void sas_revalidate_domain(void *data)
|
||||
{
|
||||
int res = 0;
|
||||
struct asd_sas_port *port = data;
|
||||
|
||||
sas_begin_event(DISCE_REVALIDATE_DOMAIN, &port->disc.disc_event_lock,
|
||||
&port->disc.pending);
|
||||
|
||||
SAS_DPRINTK("REVALIDATING DOMAIN on port %d, pid:%d\n", port->id,
|
||||
current->pid);
|
||||
if (port->port_dev)
|
||||
res = sas_ex_revalidate_domain(port->port_dev);
|
||||
|
||||
SAS_DPRINTK("done REVALIDATING DOMAIN on port %d, pid:%d, res 0x%x\n",
|
||||
port->id, current->pid, res);
|
||||
}
|
||||
|
||||
/* ---------- Events ---------- */
|
||||
|
||||
int sas_discover_event(struct asd_sas_port *port, enum discover_event ev)
|
||||
{
|
||||
struct sas_discovery *disc;
|
||||
|
||||
if (!port)
|
||||
return 0;
|
||||
disc = &port->disc;
|
||||
|
||||
BUG_ON(ev >= DISC_NUM_EVENTS);
|
||||
|
||||
sas_queue_event(ev, &disc->disc_event_lock, &disc->pending,
|
||||
&disc->disc_work[ev], port->ha->core.shost);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sas_init_disc -- initialize the discovery struct in the port
|
||||
* @port: pointer to struct port
|
||||
*
|
||||
* Called when the ports are being initialized.
|
||||
*/
|
||||
void sas_init_disc(struct sas_discovery *disc, struct asd_sas_port *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
static void (*sas_event_fns[DISC_NUM_EVENTS])(void *) = {
|
||||
[DISCE_DISCOVER_DOMAIN] = sas_discover_domain,
|
||||
[DISCE_REVALIDATE_DOMAIN] = sas_revalidate_domain,
|
||||
};
|
||||
|
||||
spin_lock_init(&disc->disc_event_lock);
|
||||
disc->pending = 0;
|
||||
for (i = 0; i < DISC_NUM_EVENTS; i++)
|
||||
INIT_WORK(&disc->disc_work[i], sas_event_fns[i], port);
|
||||
}
|
76
drivers/scsi/libsas/sas_dump.c
Normal file
76
drivers/scsi/libsas/sas_dump.c
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Dump/Debugging routines
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sas_dump.h"
|
||||
|
||||
#ifdef SAS_DEBUG
|
||||
|
||||
static const char *sas_hae_str[] = {
|
||||
[0] = "HAE_RESET",
|
||||
};
|
||||
|
||||
static const char *sas_porte_str[] = {
|
||||
[0] = "PORTE_BYTES_DMAED",
|
||||
[1] = "PORTE_BROADCAST_RCVD",
|
||||
[2] = "PORTE_LINK_RESET_ERR",
|
||||
[3] = "PORTE_TIMER_EVENT",
|
||||
[4] = "PORTE_HARD_RESET",
|
||||
};
|
||||
|
||||
static const char *sas_phye_str[] = {
|
||||
[0] = "PHYE_LOSS_OF_SIGNAL",
|
||||
[1] = "PHYE_OOB_DONE",
|
||||
[2] = "PHYE_OOB_ERROR",
|
||||
[3] = "PHYE_SPINUP_HOLD",
|
||||
};
|
||||
|
||||
void sas_dprint_porte(int phyid, enum port_event pe)
|
||||
{
|
||||
SAS_DPRINTK("phy%d: port event: %s\n", phyid, sas_porte_str[pe]);
|
||||
}
|
||||
void sas_dprint_phye(int phyid, enum phy_event pe)
|
||||
{
|
||||
SAS_DPRINTK("phy%d: phy event: %s\n", phyid, sas_phye_str[pe]);
|
||||
}
|
||||
|
||||
void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he)
|
||||
{
|
||||
SAS_DPRINTK("ha %s: %s event\n", pci_name(sas_ha->pcidev),
|
||||
sas_hae_str[he]);
|
||||
}
|
||||
|
||||
void sas_dump_port(struct asd_sas_port *port)
|
||||
{
|
||||
SAS_DPRINTK("port%d: class:0x%x\n", port->id, port->class);
|
||||
SAS_DPRINTK("port%d: sas_addr:%llx\n", port->id,
|
||||
SAS_ADDR(port->sas_addr));
|
||||
SAS_DPRINTK("port%d: attached_sas_addr:%llx\n", port->id,
|
||||
SAS_ADDR(port->attached_sas_addr));
|
||||
SAS_DPRINTK("port%d: iproto:0x%x\n", port->id, port->iproto);
|
||||
SAS_DPRINTK("port%d: tproto:0x%x\n", port->id, port->tproto);
|
||||
SAS_DPRINTK("port%d: oob_mode:0x%x\n", port->id, port->oob_mode);
|
||||
SAS_DPRINTK("port%d: num_phys:%d\n", port->id, port->num_phys);
|
||||
}
|
||||
|
||||
#endif /* SAS_DEBUG */
|
42
drivers/scsi/libsas/sas_dump.h
Normal file
42
drivers/scsi/libsas/sas_dump.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Dump/Debugging routines 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 program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sas_internal.h"
|
||||
|
||||
#ifdef SAS_DEBUG
|
||||
|
||||
void sas_dprint_porte(int phyid, enum port_event pe);
|
||||
void sas_dprint_phye(int phyid, enum phy_event pe);
|
||||
void sas_dprint_hae(struct sas_ha_struct *sas_ha, enum ha_event he);
|
||||
void sas_dump_port(struct asd_sas_port *port);
|
||||
|
||||
#else /* SAS_DEBUG */
|
||||
|
||||
static inline void sas_dprint_porte(int phyid, enum port_event pe) { }
|
||||
static inline void sas_dprint_phye(int phyid, enum phy_event pe) { }
|
||||
static inline void sas_dprint_hae(struct sas_ha_struct *sas_ha,
|
||||
enum ha_event he) { }
|
||||
static inline void sas_dump_port(struct asd_sas_port *port) { }
|
||||
|
||||
#endif /* SAS_DEBUG */
|
75
drivers/scsi/libsas/sas_event.c
Normal file
75
drivers/scsi/libsas/sas_event.c
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Event processing
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <scsi/scsi_host.h>
|
||||
#include "sas_internal.h"
|
||||
#include "sas_dump.h"
|
||||
|
||||
static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)
|
||||
{
|
||||
BUG_ON(event >= HA_NUM_EVENTS);
|
||||
|
||||
sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending,
|
||||
&sas_ha->ha_events[event], sas_ha->core.shost);
|
||||
}
|
||||
|
||||
static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)
|
||||
{
|
||||
struct sas_ha_struct *ha = phy->ha;
|
||||
|
||||
BUG_ON(event >= PORT_NUM_EVENTS);
|
||||
|
||||
sas_queue_event(event, &ha->event_lock, &phy->port_events_pending,
|
||||
&phy->port_events[event], ha->core.shost);
|
||||
}
|
||||
|
||||
static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
|
||||
{
|
||||
struct sas_ha_struct *ha = phy->ha;
|
||||
|
||||
BUG_ON(event >= PHY_NUM_EVENTS);
|
||||
|
||||
sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending,
|
||||
&phy->phy_events[event], ha->core.shost);
|
||||
}
|
||||
|
||||
int sas_init_events(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
static void (*sas_ha_event_fns[HA_NUM_EVENTS])(void *) = {
|
||||
[HAE_RESET] = sas_hae_reset,
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
spin_lock_init(&sas_ha->event_lock);
|
||||
|
||||
for (i = 0; i < HA_NUM_EVENTS; i++)
|
||||
INIT_WORK(&sas_ha->ha_events[i], sas_ha_event_fns[i], sas_ha);
|
||||
|
||||
sas_ha->notify_ha_event = notify_ha_event;
|
||||
sas_ha->notify_port_event = notify_port_event;
|
||||
sas_ha->notify_phy_event = notify_phy_event;
|
||||
|
||||
return 0;
|
||||
}
|
1855
drivers/scsi/libsas/sas_expander.c
Normal file
1855
drivers/scsi/libsas/sas_expander.c
Normal file
File diff suppressed because it is too large
Load Diff
267
drivers/scsi/libsas/sas_init.c
Normal file
267
drivers/scsi/libsas/sas_init.c
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Transport Layer 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 program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_transport.h>
|
||||
#include <scsi/scsi_transport_sas.h>
|
||||
|
||||
#include "sas_internal.h"
|
||||
|
||||
#include "../scsi_sas_internal.h"
|
||||
|
||||
kmem_cache_t *sas_task_cache;
|
||||
|
||||
/*------------ SAS addr hash -----------*/
|
||||
void sas_hash_addr(u8 *hashed, const u8 *sas_addr)
|
||||
{
|
||||
const u32 poly = 0x00DB2777;
|
||||
u32 r = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
int b;
|
||||
for (b = 7; b >= 0; b--) {
|
||||
r <<= 1;
|
||||
if ((1 << b) & sas_addr[i]) {
|
||||
if (!(r & 0x01000000))
|
||||
r ^= poly;
|
||||
} else if (r & 0x01000000)
|
||||
r ^= poly;
|
||||
}
|
||||
}
|
||||
|
||||
hashed[0] = (r >> 16) & 0xFF;
|
||||
hashed[1] = (r >> 8) & 0xFF ;
|
||||
hashed[2] = r & 0xFF;
|
||||
}
|
||||
|
||||
|
||||
/* ---------- HA events ---------- */
|
||||
|
||||
void sas_hae_reset(void *data)
|
||||
{
|
||||
struct sas_ha_struct *ha = data;
|
||||
|
||||
sas_begin_event(HAE_RESET, &ha->event_lock,
|
||||
&ha->pending);
|
||||
}
|
||||
|
||||
int sas_register_ha(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
spin_lock_init(&sas_ha->phy_port_lock);
|
||||
sas_hash_addr(sas_ha->hashed_sas_addr, sas_ha->sas_addr);
|
||||
|
||||
if (sas_ha->lldd_queue_size == 0)
|
||||
sas_ha->lldd_queue_size = 1;
|
||||
else if (sas_ha->lldd_queue_size == -1)
|
||||
sas_ha->lldd_queue_size = 128; /* Sanity */
|
||||
|
||||
error = sas_register_phys(sas_ha);
|
||||
if (error) {
|
||||
printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = sas_register_ports(sas_ha);
|
||||
if (error) {
|
||||
printk(KERN_NOTICE "couldn't register sas ports:%d\n", error);
|
||||
goto Undo_phys;
|
||||
}
|
||||
|
||||
error = sas_init_events(sas_ha);
|
||||
if (error) {
|
||||
printk(KERN_NOTICE "couldn't start event thread:%d\n", error);
|
||||
goto Undo_ports;
|
||||
}
|
||||
|
||||
if (sas_ha->lldd_max_execute_num > 1) {
|
||||
error = sas_init_queue(sas_ha);
|
||||
if (error) {
|
||||
printk(KERN_NOTICE "couldn't start queue thread:%d, "
|
||||
"running in direct mode\n", error);
|
||||
sas_ha->lldd_max_execute_num = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
Undo_ports:
|
||||
sas_unregister_ports(sas_ha);
|
||||
Undo_phys:
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int sas_unregister_ha(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
if (sas_ha->lldd_max_execute_num > 1) {
|
||||
sas_shutdown_queue(sas_ha);
|
||||
}
|
||||
|
||||
sas_unregister_ports(sas_ha);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sas_get_linkerrors(struct sas_phy *phy)
|
||||
{
|
||||
if (scsi_is_sas_phy_local(phy))
|
||||
/* FIXME: we have no local phy stats
|
||||
* gathering at this time */
|
||||
return -EINVAL;
|
||||
|
||||
return sas_smp_get_phy_events(phy);
|
||||
}
|
||||
|
||||
static int sas_phy_reset(struct sas_phy *phy, int hard_reset)
|
||||
{
|
||||
int ret;
|
||||
enum phy_func reset_type;
|
||||
|
||||
if (hard_reset)
|
||||
reset_type = PHY_FUNC_HARD_RESET;
|
||||
else
|
||||
reset_type = PHY_FUNC_LINK_RESET;
|
||||
|
||||
if (scsi_is_sas_phy_local(phy)) {
|
||||
struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
|
||||
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
|
||||
struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
ret = i->dft->lldd_control_phy(asd_phy, reset_type, NULL);
|
||||
} else {
|
||||
struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
|
||||
struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
|
||||
ret = sas_smp_phy_control(ddev, phy->number, reset_type, NULL);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sas_set_phy_speed(struct sas_phy *phy,
|
||||
struct sas_phy_linkrates *rates)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((rates->minimum_linkrate &&
|
||||
rates->minimum_linkrate > phy->maximum_linkrate) ||
|
||||
(rates->maximum_linkrate &&
|
||||
rates->maximum_linkrate < phy->minimum_linkrate))
|
||||
return -EINVAL;
|
||||
|
||||
if (rates->minimum_linkrate &&
|
||||
rates->minimum_linkrate < phy->minimum_linkrate_hw)
|
||||
rates->minimum_linkrate = phy->minimum_linkrate_hw;
|
||||
|
||||
if (rates->maximum_linkrate &&
|
||||
rates->maximum_linkrate > phy->maximum_linkrate_hw)
|
||||
rates->maximum_linkrate = phy->maximum_linkrate_hw;
|
||||
|
||||
if (scsi_is_sas_phy_local(phy)) {
|
||||
struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
|
||||
struct sas_ha_struct *sas_ha = SHOST_TO_SAS_HA(shost);
|
||||
struct asd_sas_phy *asd_phy = sas_ha->sas_phy[phy->number];
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
ret = i->dft->lldd_control_phy(asd_phy, PHY_FUNC_SET_LINK_RATE,
|
||||
rates);
|
||||
} else {
|
||||
struct sas_rphy *rphy = dev_to_rphy(phy->dev.parent);
|
||||
struct domain_device *ddev = sas_find_dev_by_rphy(rphy);
|
||||
ret = sas_smp_phy_control(ddev, phy->number,
|
||||
PHY_FUNC_LINK_RESET, rates);
|
||||
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct sas_function_template sft = {
|
||||
.phy_reset = sas_phy_reset,
|
||||
.set_phy_speed = sas_set_phy_speed,
|
||||
.get_linkerrors = sas_get_linkerrors,
|
||||
};
|
||||
|
||||
struct scsi_transport_template *
|
||||
sas_domain_attach_transport(struct sas_domain_function_template *dft)
|
||||
{
|
||||
struct scsi_transport_template *stt = sas_attach_transport(&sft);
|
||||
struct sas_internal *i;
|
||||
|
||||
if (!stt)
|
||||
return stt;
|
||||
|
||||
i = to_sas_internal(stt);
|
||||
i->dft = dft;
|
||||
stt->create_work_queue = 1;
|
||||
stt->eh_timed_out = sas_scsi_timed_out;
|
||||
stt->eh_strategy_handler = sas_scsi_recover_host;
|
||||
|
||||
return stt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sas_domain_attach_transport);
|
||||
|
||||
|
||||
void sas_domain_release_transport(struct scsi_transport_template *stt)
|
||||
{
|
||||
sas_release_transport(stt);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sas_domain_release_transport);
|
||||
|
||||
/* ---------- SAS Class register/unregister ---------- */
|
||||
|
||||
static int __init sas_class_init(void)
|
||||
{
|
||||
sas_task_cache = kmem_cache_create("sas_task", sizeof(struct sas_task),
|
||||
0, SLAB_HWCACHE_ALIGN, NULL, NULL);
|
||||
if (!sas_task_cache)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit sas_class_exit(void)
|
||||
{
|
||||
kmem_cache_destroy(sas_task_cache);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Luben Tuikov <luben_tuikov@adaptec.com>");
|
||||
MODULE_DESCRIPTION("SAS Transport Layer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
module_init(sas_class_init);
|
||||
module_exit(sas_class_exit);
|
||||
|
||||
EXPORT_SYMBOL_GPL(sas_register_ha);
|
||||
EXPORT_SYMBOL_GPL(sas_unregister_ha);
|
146
drivers/scsi/libsas/sas_internal.h
Normal file
146
drivers/scsi/libsas/sas_internal.h
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) class internal 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 program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _SAS_INTERNAL_H_
|
||||
#define _SAS_INTERNAL_H_
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_transport_sas.h>
|
||||
#include <scsi/libsas.h>
|
||||
|
||||
#define sas_printk(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
|
||||
|
||||
#ifdef SAS_DEBUG
|
||||
#define SAS_DPRINTK(fmt, ...) printk(KERN_NOTICE "sas: " fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define SAS_DPRINTK(fmt, ...)
|
||||
#endif
|
||||
|
||||
void sas_scsi_recover_host(struct Scsi_Host *shost);
|
||||
|
||||
int sas_show_class(enum sas_class class, char *buf);
|
||||
int sas_show_proto(enum sas_proto proto, char *buf);
|
||||
int sas_show_linkrate(enum sas_linkrate linkrate, char *buf);
|
||||
int sas_show_oob_mode(enum sas_oob_mode oob_mode, char *buf);
|
||||
|
||||
int sas_register_phys(struct sas_ha_struct *sas_ha);
|
||||
void sas_unregister_phys(struct sas_ha_struct *sas_ha);
|
||||
|
||||
int sas_register_ports(struct sas_ha_struct *sas_ha);
|
||||
void sas_unregister_ports(struct sas_ha_struct *sas_ha);
|
||||
|
||||
enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *);
|
||||
|
||||
int sas_init_queue(struct sas_ha_struct *sas_ha);
|
||||
int sas_init_events(struct sas_ha_struct *sas_ha);
|
||||
void sas_shutdown_queue(struct sas_ha_struct *sas_ha);
|
||||
|
||||
void sas_deform_port(struct asd_sas_phy *phy);
|
||||
|
||||
void sas_porte_bytes_dmaed(void *);
|
||||
void sas_porte_broadcast_rcvd(void *);
|
||||
void sas_porte_link_reset_err(void *);
|
||||
void sas_porte_timer_event(void *);
|
||||
void sas_porte_hard_reset(void *);
|
||||
|
||||
int sas_notify_lldd_dev_found(struct domain_device *);
|
||||
void sas_notify_lldd_dev_gone(struct domain_device *);
|
||||
|
||||
int sas_smp_phy_control(struct domain_device *dev, int phy_id,
|
||||
enum phy_func phy_func, struct sas_phy_linkrates *);
|
||||
int sas_smp_get_phy_events(struct sas_phy *phy);
|
||||
|
||||
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy);
|
||||
|
||||
void sas_hae_reset(void *);
|
||||
|
||||
static inline void sas_queue_event(int event, spinlock_t *lock,
|
||||
unsigned long *pending,
|
||||
struct work_struct *work,
|
||||
struct Scsi_Host *shost)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(lock, flags);
|
||||
if (test_bit(event, pending)) {
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
return;
|
||||
}
|
||||
__set_bit(event, pending);
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
scsi_queue_work(shost, work);
|
||||
}
|
||||
|
||||
static inline void sas_begin_event(int event, spinlock_t *lock,
|
||||
unsigned long *pending)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(lock, flags);
|
||||
__clear_bit(event, pending);
|
||||
spin_unlock_irqrestore(lock, flags);
|
||||
}
|
||||
|
||||
static inline void sas_fill_in_rphy(struct domain_device *dev,
|
||||
struct sas_rphy *rphy)
|
||||
{
|
||||
rphy->identify.sas_address = SAS_ADDR(dev->sas_addr);
|
||||
rphy->identify.initiator_port_protocols = dev->iproto;
|
||||
rphy->identify.target_port_protocols = dev->tproto;
|
||||
switch (dev->dev_type) {
|
||||
case SATA_DEV:
|
||||
/* FIXME: need sata device type */
|
||||
case SAS_END_DEV:
|
||||
rphy->identify.device_type = SAS_END_DEVICE;
|
||||
break;
|
||||
case EDGE_DEV:
|
||||
rphy->identify.device_type = SAS_EDGE_EXPANDER_DEVICE;
|
||||
break;
|
||||
case FANOUT_DEV:
|
||||
rphy->identify.device_type = SAS_FANOUT_EXPANDER_DEVICE;
|
||||
break;
|
||||
default:
|
||||
rphy->identify.device_type = SAS_PHY_UNUSED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void sas_add_parent_port(struct domain_device *dev, int phy_id)
|
||||
{
|
||||
struct expander_device *ex = &dev->ex_dev;
|
||||
struct ex_phy *ex_phy = &ex->ex_phy[phy_id];
|
||||
|
||||
if (!ex->parent_port) {
|
||||
ex->parent_port = sas_port_alloc(&dev->rphy->dev, phy_id);
|
||||
/* FIXME: error handling */
|
||||
BUG_ON(!ex->parent_port);
|
||||
BUG_ON(sas_port_add(ex->parent_port));
|
||||
sas_port_mark_backlink(ex->parent_port);
|
||||
}
|
||||
sas_port_add_phy(ex->parent_port, ex_phy->phy);
|
||||
}
|
||||
|
||||
#endif /* _SAS_INTERNAL_H_ */
|
158
drivers/scsi/libsas/sas_phy.c
Normal file
158
drivers/scsi/libsas/sas_phy.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Phy class
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sas_internal.h"
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_transport.h>
|
||||
#include <scsi/scsi_transport_sas.h>
|
||||
#include "../scsi_sas_internal.h"
|
||||
|
||||
/* ---------- Phy events ---------- */
|
||||
|
||||
static void sas_phye_loss_of_signal(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PHYE_LOSS_OF_SIGNAL, &phy->ha->event_lock,
|
||||
&phy->phy_events_pending);
|
||||
phy->error = 0;
|
||||
sas_deform_port(phy);
|
||||
}
|
||||
|
||||
static void sas_phye_oob_done(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PHYE_OOB_DONE, &phy->ha->event_lock,
|
||||
&phy->phy_events_pending);
|
||||
phy->error = 0;
|
||||
}
|
||||
|
||||
static void sas_phye_oob_error(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct asd_sas_port *port = phy->port;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
sas_begin_event(PHYE_OOB_ERROR, &phy->ha->event_lock,
|
||||
&phy->phy_events_pending);
|
||||
|
||||
sas_deform_port(phy);
|
||||
|
||||
if (!port && phy->enabled && i->dft->lldd_control_phy) {
|
||||
phy->error++;
|
||||
switch (phy->error) {
|
||||
case 1:
|
||||
case 2:
|
||||
i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
|
||||
NULL);
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
phy->error = 0;
|
||||
phy->enabled = 0;
|
||||
i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sas_phye_spinup_hold(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
sas_begin_event(PHYE_SPINUP_HOLD, &phy->ha->event_lock,
|
||||
&phy->phy_events_pending);
|
||||
|
||||
phy->error = 0;
|
||||
i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
|
||||
}
|
||||
|
||||
/* ---------- Phy class registration ---------- */
|
||||
|
||||
int sas_register_phys(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
int i;
|
||||
|
||||
static void (*sas_phy_event_fns[PHY_NUM_EVENTS])(void *) = {
|
||||
[PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
|
||||
[PHYE_OOB_DONE] = sas_phye_oob_done,
|
||||
[PHYE_OOB_ERROR] = sas_phye_oob_error,
|
||||
[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
|
||||
};
|
||||
|
||||
static void (*sas_port_event_fns[PORT_NUM_EVENTS])(void *) = {
|
||||
[PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
|
||||
[PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
|
||||
[PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
|
||||
[PORTE_TIMER_EVENT] = sas_porte_timer_event,
|
||||
[PORTE_HARD_RESET] = sas_porte_hard_reset,
|
||||
};
|
||||
|
||||
/* Now register the phys. */
|
||||
for (i = 0; i < sas_ha->num_phys; i++) {
|
||||
int k;
|
||||
struct asd_sas_phy *phy = sas_ha->sas_phy[i];
|
||||
|
||||
phy->error = 0;
|
||||
INIT_LIST_HEAD(&phy->port_phy_el);
|
||||
for (k = 0; k < PORT_NUM_EVENTS; k++)
|
||||
INIT_WORK(&phy->port_events[k], sas_port_event_fns[k],
|
||||
phy);
|
||||
|
||||
for (k = 0; k < PHY_NUM_EVENTS; k++)
|
||||
INIT_WORK(&phy->phy_events[k], sas_phy_event_fns[k],
|
||||
phy);
|
||||
phy->port = NULL;
|
||||
phy->ha = sas_ha;
|
||||
spin_lock_init(&phy->frame_rcvd_lock);
|
||||
spin_lock_init(&phy->sas_prim_lock);
|
||||
phy->frame_rcvd_size = 0;
|
||||
|
||||
phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev,
|
||||
i);
|
||||
if (!phy->phy)
|
||||
return -ENOMEM;
|
||||
|
||||
phy->phy->identify.initiator_port_protocols =
|
||||
phy->iproto;
|
||||
phy->phy->identify.target_port_protocols = phy->tproto;
|
||||
phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
|
||||
phy->phy->identify.phy_identifier = i;
|
||||
phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
|
||||
phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
|
||||
phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
|
||||
phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
|
||||
phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
|
||||
|
||||
sas_phy_add(phy->phy);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
279
drivers/scsi/libsas/sas_port.c
Normal file
279
drivers/scsi/libsas/sas_port.c
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) Port class
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sas_internal.h"
|
||||
|
||||
#include <scsi/scsi_transport.h>
|
||||
#include <scsi/scsi_transport_sas.h>
|
||||
#include "../scsi_sas_internal.h"
|
||||
|
||||
/**
|
||||
* sas_form_port -- add this phy to a port
|
||||
* @phy: the phy of interest
|
||||
*
|
||||
* This function adds this phy to an existing port, thus creating a wide
|
||||
* port, or it creates a port and adds the phy to the port.
|
||||
*/
|
||||
static void sas_form_port(struct asd_sas_phy *phy)
|
||||
{
|
||||
int i;
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct asd_sas_port *port = phy->port;
|
||||
struct sas_internal *si =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
if (port) {
|
||||
if (memcmp(port->attached_sas_addr, phy->attached_sas_addr,
|
||||
SAS_ADDR_SIZE) == 0)
|
||||
sas_deform_port(phy);
|
||||
else {
|
||||
SAS_DPRINTK("%s: phy%d belongs to port%d already(%d)!\n",
|
||||
__FUNCTION__, phy->id, phy->port->id,
|
||||
phy->port->num_phys);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* find a port */
|
||||
spin_lock(&sas_ha->phy_port_lock);
|
||||
for (i = 0; i < sas_ha->num_phys; i++) {
|
||||
port = sas_ha->sas_port[i];
|
||||
spin_lock(&port->phy_list_lock);
|
||||
if (*(u64 *) port->sas_addr &&
|
||||
memcmp(port->attached_sas_addr,
|
||||
phy->attached_sas_addr, SAS_ADDR_SIZE) == 0 &&
|
||||
port->num_phys > 0) {
|
||||
/* wide port */
|
||||
SAS_DPRINTK("phy%d matched wide port%d\n", phy->id,
|
||||
port->id);
|
||||
break;
|
||||
} else if (*(u64 *) port->sas_addr == 0 && port->num_phys==0) {
|
||||
memcpy(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE);
|
||||
break;
|
||||
}
|
||||
spin_unlock(&port->phy_list_lock);
|
||||
}
|
||||
|
||||
if (i >= sas_ha->num_phys) {
|
||||
printk(KERN_NOTICE "%s: couldn't find a free port, bug?\n",
|
||||
__FUNCTION__);
|
||||
spin_unlock(&sas_ha->phy_port_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
/* add the phy to the port */
|
||||
list_add_tail(&phy->port_phy_el, &port->phy_list);
|
||||
phy->port = port;
|
||||
port->num_phys++;
|
||||
port->phy_mask |= (1U << phy->id);
|
||||
|
||||
if (!port->phy)
|
||||
port->phy = phy->phy;
|
||||
|
||||
SAS_DPRINTK("phy%d added to port%d, phy_mask:0x%x\n", phy->id,
|
||||
port->id, port->phy_mask);
|
||||
|
||||
if (*(u64 *)port->attached_sas_addr == 0) {
|
||||
port->class = phy->class;
|
||||
memcpy(port->attached_sas_addr, phy->attached_sas_addr,
|
||||
SAS_ADDR_SIZE);
|
||||
port->iproto = phy->iproto;
|
||||
port->tproto = phy->tproto;
|
||||
port->oob_mode = phy->oob_mode;
|
||||
port->linkrate = phy->linkrate;
|
||||
} else
|
||||
port->linkrate = max(port->linkrate, phy->linkrate);
|
||||
spin_unlock(&port->phy_list_lock);
|
||||
spin_unlock(&sas_ha->phy_port_lock);
|
||||
|
||||
if (!port->port) {
|
||||
port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
|
||||
BUG_ON(!port->port);
|
||||
sas_port_add(port->port);
|
||||
}
|
||||
sas_port_add_phy(port->port, phy->phy);
|
||||
|
||||
if (port->port_dev)
|
||||
port->port_dev->pathways = port->num_phys;
|
||||
|
||||
/* Tell the LLDD about this port formation. */
|
||||
if (si->dft->lldd_port_formed)
|
||||
si->dft->lldd_port_formed(phy);
|
||||
|
||||
sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* sas_deform_port -- remove this phy from the port it belongs to
|
||||
* @phy: the phy of interest
|
||||
*
|
||||
* This is called when the physical link to the other phy has been
|
||||
* lost (on this phy), in Event thread context. We cannot delay here.
|
||||
*/
|
||||
void sas_deform_port(struct asd_sas_phy *phy)
|
||||
{
|
||||
struct sas_ha_struct *sas_ha = phy->ha;
|
||||
struct asd_sas_port *port = phy->port;
|
||||
struct sas_internal *si =
|
||||
to_sas_internal(sas_ha->core.shost->transportt);
|
||||
|
||||
if (!port)
|
||||
return; /* done by a phy event */
|
||||
|
||||
if (port->port_dev)
|
||||
port->port_dev->pathways--;
|
||||
|
||||
if (port->num_phys == 1) {
|
||||
sas_unregister_domain_devices(port);
|
||||
sas_port_delete(port->port);
|
||||
port->port = NULL;
|
||||
} else
|
||||
sas_port_delete_phy(port->port, phy->phy);
|
||||
|
||||
|
||||
if (si->dft->lldd_port_deformed)
|
||||
si->dft->lldd_port_deformed(phy);
|
||||
|
||||
spin_lock(&sas_ha->phy_port_lock);
|
||||
spin_lock(&port->phy_list_lock);
|
||||
|
||||
list_del_init(&phy->port_phy_el);
|
||||
phy->port = NULL;
|
||||
port->num_phys--;
|
||||
port->phy_mask &= ~(1U << phy->id);
|
||||
|
||||
if (port->num_phys == 0) {
|
||||
INIT_LIST_HEAD(&port->phy_list);
|
||||
memset(port->sas_addr, 0, SAS_ADDR_SIZE);
|
||||
memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
|
||||
port->class = 0;
|
||||
port->iproto = 0;
|
||||
port->tproto = 0;
|
||||
port->oob_mode = 0;
|
||||
port->phy_mask = 0;
|
||||
}
|
||||
spin_unlock(&port->phy_list_lock);
|
||||
spin_unlock(&sas_ha->phy_port_lock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* ---------- SAS port events ---------- */
|
||||
|
||||
void sas_porte_bytes_dmaed(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PORTE_BYTES_DMAED, &phy->ha->event_lock,
|
||||
&phy->port_events_pending);
|
||||
|
||||
sas_form_port(phy);
|
||||
}
|
||||
|
||||
void sas_porte_broadcast_rcvd(void *data)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 prim;
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PORTE_BROADCAST_RCVD, &phy->ha->event_lock,
|
||||
&phy->port_events_pending);
|
||||
|
||||
spin_lock_irqsave(&phy->sas_prim_lock, flags);
|
||||
prim = phy->sas_prim;
|
||||
spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
|
||||
|
||||
SAS_DPRINTK("broadcast received: %d\n", prim);
|
||||
sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
|
||||
}
|
||||
|
||||
void sas_porte_link_reset_err(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PORTE_LINK_RESET_ERR, &phy->ha->event_lock,
|
||||
&phy->port_events_pending);
|
||||
|
||||
sas_deform_port(phy);
|
||||
}
|
||||
|
||||
void sas_porte_timer_event(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PORTE_TIMER_EVENT, &phy->ha->event_lock,
|
||||
&phy->port_events_pending);
|
||||
|
||||
sas_deform_port(phy);
|
||||
}
|
||||
|
||||
void sas_porte_hard_reset(void *data)
|
||||
{
|
||||
struct asd_sas_phy *phy = data;
|
||||
|
||||
sas_begin_event(PORTE_HARD_RESET, &phy->ha->event_lock,
|
||||
&phy->port_events_pending);
|
||||
|
||||
sas_deform_port(phy);
|
||||
}
|
||||
|
||||
/* ---------- SAS port registration ---------- */
|
||||
|
||||
static void sas_init_port(struct asd_sas_port *port,
|
||||
struct sas_ha_struct *sas_ha, int i)
|
||||
{
|
||||
port->id = i;
|
||||
INIT_LIST_HEAD(&port->dev_list);
|
||||
spin_lock_init(&port->phy_list_lock);
|
||||
INIT_LIST_HEAD(&port->phy_list);
|
||||
port->num_phys = 0;
|
||||
port->phy_mask = 0;
|
||||
port->ha = sas_ha;
|
||||
|
||||
spin_lock_init(&port->dev_list_lock);
|
||||
}
|
||||
|
||||
int sas_register_ports(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* initialize the ports and discovery */
|
||||
for (i = 0; i < sas_ha->num_phys; i++) {
|
||||
struct asd_sas_port *port = sas_ha->sas_port[i];
|
||||
|
||||
sas_init_port(port, sas_ha, i);
|
||||
sas_init_disc(&port->disc, port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sas_unregister_ports(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < sas_ha->num_phys; i++)
|
||||
if (sas_ha->sas_phy[i]->port)
|
||||
sas_deform_port(sas_ha->sas_phy[i]);
|
||||
|
||||
}
|
786
drivers/scsi/libsas/sas_scsi_host.c
Normal file
786
drivers/scsi/libsas/sas_scsi_host.c
Normal file
@ -0,0 +1,786 @@
|
||||
/*
|
||||
* Serial Attached SCSI (SAS) class SCSI Host glue.
|
||||
*
|
||||
* Copyright (C) 2005 Adaptec, Inc. All rights reserved.
|
||||
* Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
|
||||
*
|
||||
* This file is licensed under GPLv2.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sas_internal.h"
|
||||
|
||||
#include <scsi/scsi_host.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_tcq.h>
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_transport.h>
|
||||
#include <scsi/scsi_transport_sas.h>
|
||||
#include "../scsi_sas_internal.h"
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
/* ---------- SCSI Host glue ---------- */
|
||||
|
||||
#define TO_SAS_TASK(_scsi_cmd) ((void *)(_scsi_cmd)->host_scribble)
|
||||
#define ASSIGN_SAS_TASK(_sc, _t) do { (_sc)->host_scribble = (void *) _t; } while (0)
|
||||
|
||||
static void sas_scsi_task_done(struct sas_task *task)
|
||||
{
|
||||
struct task_status_struct *ts = &task->task_status;
|
||||
struct scsi_cmnd *sc = task->uldd_task;
|
||||
unsigned ts_flags = task->task_state_flags;
|
||||
int hs = 0, stat = 0;
|
||||
|
||||
if (unlikely(!sc)) {
|
||||
SAS_DPRINTK("task_done called with non existing SCSI cmnd!\n");
|
||||
list_del_init(&task->list);
|
||||
sas_free_task(task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ts->resp == SAS_TASK_UNDELIVERED) {
|
||||
/* transport error */
|
||||
hs = DID_NO_CONNECT;
|
||||
} else { /* ts->resp == SAS_TASK_COMPLETE */
|
||||
/* task delivered, what happened afterwards? */
|
||||
switch (ts->stat) {
|
||||
case SAS_DEV_NO_RESPONSE:
|
||||
case SAS_INTERRUPTED:
|
||||
case SAS_PHY_DOWN:
|
||||
case SAS_NAK_R_ERR:
|
||||
case SAS_OPEN_TO:
|
||||
hs = DID_NO_CONNECT;
|
||||
break;
|
||||
case SAS_DATA_UNDERRUN:
|
||||
sc->resid = ts->residual;
|
||||
if (sc->request_bufflen - sc->resid < sc->underflow)
|
||||
hs = DID_ERROR;
|
||||
break;
|
||||
case SAS_DATA_OVERRUN:
|
||||
hs = DID_ERROR;
|
||||
break;
|
||||
case SAS_QUEUE_FULL:
|
||||
hs = DID_SOFT_ERROR; /* retry */
|
||||
break;
|
||||
case SAS_DEVICE_UNKNOWN:
|
||||
hs = DID_BAD_TARGET;
|
||||
break;
|
||||
case SAS_SG_ERR:
|
||||
hs = DID_PARITY;
|
||||
break;
|
||||
case SAS_OPEN_REJECT:
|
||||
if (ts->open_rej_reason == SAS_OREJ_RSVD_RETRY)
|
||||
hs = DID_SOFT_ERROR; /* retry */
|
||||
else
|
||||
hs = DID_ERROR;
|
||||
break;
|
||||
case SAS_PROTO_RESPONSE:
|
||||
SAS_DPRINTK("LLDD:%s sent SAS_PROTO_RESP for an SSP "
|
||||
"task; please report this\n",
|
||||
task->dev->port->ha->sas_ha_name);
|
||||
break;
|
||||
case SAS_ABORTED_TASK:
|
||||
hs = DID_ABORT;
|
||||
break;
|
||||
case SAM_CHECK_COND:
|
||||
memcpy(sc->sense_buffer, ts->buf,
|
||||
max(SCSI_SENSE_BUFFERSIZE, ts->buf_valid_size));
|
||||
stat = SAM_CHECK_COND;
|
||||
break;
|
||||
default:
|
||||
stat = ts->stat;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ASSIGN_SAS_TASK(sc, NULL);
|
||||
sc->result = (hs << 16) | stat;
|
||||
list_del_init(&task->list);
|
||||
sas_free_task(task);
|
||||
/* This is very ugly but this is how SCSI Core works. */
|
||||
if (ts_flags & SAS_TASK_STATE_ABORTED)
|
||||
scsi_finish_command(sc);
|
||||
else
|
||||
sc->scsi_done(sc);
|
||||
}
|
||||
|
||||
static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd)
|
||||
{
|
||||
enum task_attribute ta = TASK_ATTR_SIMPLE;
|
||||
if (cmd->request && blk_rq_tagged(cmd->request)) {
|
||||
if (cmd->device->ordered_tags &&
|
||||
(cmd->request->flags & REQ_HARDBARRIER))
|
||||
ta = TASK_ATTR_HOQ;
|
||||
}
|
||||
return ta;
|
||||
}
|
||||
|
||||
static struct sas_task *sas_create_task(struct scsi_cmnd *cmd,
|
||||
struct domain_device *dev,
|
||||
unsigned long gfp_flags)
|
||||
{
|
||||
struct sas_task *task = sas_alloc_task(gfp_flags);
|
||||
struct scsi_lun lun;
|
||||
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
*(u32 *)cmd->sense_buffer = 0;
|
||||
task->uldd_task = cmd;
|
||||
ASSIGN_SAS_TASK(cmd, task);
|
||||
|
||||
task->dev = dev;
|
||||
task->task_proto = task->dev->tproto; /* BUG_ON(!SSP) */
|
||||
|
||||
task->ssp_task.retry_count = 1;
|
||||
int_to_scsilun(cmd->device->lun, &lun);
|
||||
memcpy(task->ssp_task.LUN, &lun.scsi_lun, 8);
|
||||
task->ssp_task.task_attr = sas_scsi_get_task_attr(cmd);
|
||||
memcpy(task->ssp_task.cdb, cmd->cmnd, 16);
|
||||
|
||||
task->scatter = cmd->request_buffer;
|
||||
task->num_scatter = cmd->use_sg;
|
||||
task->total_xfer_len = cmd->request_bufflen;
|
||||
task->data_dir = cmd->sc_data_direction;
|
||||
|
||||
task->task_done = sas_scsi_task_done;
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
static int sas_queue_up(struct sas_task *task)
|
||||
{
|
||||
struct sas_ha_struct *sas_ha = task->dev->port->ha;
|
||||
struct scsi_core *core = &sas_ha->core;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(list);
|
||||
|
||||
spin_lock_irqsave(&core->task_queue_lock, flags);
|
||||
if (sas_ha->lldd_queue_size < core->task_queue_size + 1) {
|
||||
spin_unlock_irqrestore(&core->task_queue_lock, flags);
|
||||
return -SAS_QUEUE_FULL;
|
||||
}
|
||||
list_add_tail(&task->list, &core->task_queue);
|
||||
core->task_queue_size += 1;
|
||||
spin_unlock_irqrestore(&core->task_queue_lock, flags);
|
||||
up(&core->queue_thread_sema);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sas_queuecommand -- Enqueue a command for processing
|
||||
* @parameters: See SCSI Core documentation
|
||||
*
|
||||
* Note: XXX: Remove the host unlock/lock pair when SCSI Core can
|
||||
* call us without holding an IRQ spinlock...
|
||||
*/
|
||||
int sas_queuecommand(struct scsi_cmnd *cmd,
|
||||
void (*scsi_done)(struct scsi_cmnd *))
|
||||
{
|
||||
int res = 0;
|
||||
struct domain_device *dev = cmd_to_domain_dev(cmd);
|
||||
struct Scsi_Host *host = cmd->device->host;
|
||||
struct sas_internal *i = to_sas_internal(host->transportt);
|
||||
|
||||
spin_unlock_irq(host->host_lock);
|
||||
|
||||
{
|
||||
struct sas_ha_struct *sas_ha = dev->port->ha;
|
||||
struct sas_task *task;
|
||||
|
||||
res = -ENOMEM;
|
||||
task = sas_create_task(cmd, dev, GFP_ATOMIC);
|
||||
if (!task)
|
||||
goto out;
|
||||
|
||||
cmd->scsi_done = scsi_done;
|
||||
/* Queue up, Direct Mode or Task Collector Mode. */
|
||||
if (sas_ha->lldd_max_execute_num < 2)
|
||||
res = i->dft->lldd_execute_task(task, 1, GFP_ATOMIC);
|
||||
else
|
||||
res = sas_queue_up(task);
|
||||
|
||||
/* Examine */
|
||||
if (res) {
|
||||
SAS_DPRINTK("lldd_execute_task returned: %d\n", res);
|
||||
ASSIGN_SAS_TASK(cmd, NULL);
|
||||
sas_free_task(task);
|
||||
if (res == -SAS_QUEUE_FULL) {
|
||||
cmd->result = DID_SOFT_ERROR << 16; /* retry */
|
||||
res = 0;
|
||||
scsi_done(cmd);
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
out:
|
||||
spin_lock_irq(host->host_lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sas_scsi_clear_queue_lu(struct list_head *error_q, struct scsi_cmnd *my_cmd)
|
||||
{
|
||||
struct scsi_cmnd *cmd, *n;
|
||||
|
||||
list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
|
||||
if (cmd == my_cmd)
|
||||
list_del_init(&cmd->eh_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void sas_scsi_clear_queue_I_T(struct list_head *error_q,
|
||||
struct domain_device *dev)
|
||||
{
|
||||
struct scsi_cmnd *cmd, *n;
|
||||
|
||||
list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
|
||||
struct domain_device *x = cmd_to_domain_dev(cmd);
|
||||
|
||||
if (x == dev)
|
||||
list_del_init(&cmd->eh_entry);
|
||||
}
|
||||
}
|
||||
|
||||
static void sas_scsi_clear_queue_port(struct list_head *error_q,
|
||||
struct asd_sas_port *port)
|
||||
{
|
||||
struct scsi_cmnd *cmd, *n;
|
||||
|
||||
list_for_each_entry_safe(cmd, n, error_q, eh_entry) {
|
||||
struct domain_device *dev = cmd_to_domain_dev(cmd);
|
||||
struct asd_sas_port *x = dev->port;
|
||||
|
||||
if (x == port)
|
||||
list_del_init(&cmd->eh_entry);
|
||||
}
|
||||
}
|
||||
|
||||
enum task_disposition {
|
||||
TASK_IS_DONE,
|
||||
TASK_IS_ABORTED,
|
||||
TASK_IS_AT_LU,
|
||||
TASK_IS_NOT_AT_LU,
|
||||
};
|
||||
|
||||
static enum task_disposition sas_scsi_find_task(struct sas_task *task)
|
||||
{
|
||||
struct sas_ha_struct *ha = task->dev->port->ha;
|
||||
unsigned long flags;
|
||||
int i, res;
|
||||
struct sas_internal *si =
|
||||
to_sas_internal(task->dev->port->ha->core.shost->transportt);
|
||||
|
||||
if (ha->lldd_max_execute_num > 1) {
|
||||
struct scsi_core *core = &ha->core;
|
||||
struct sas_task *t, *n;
|
||||
|
||||
spin_lock_irqsave(&core->task_queue_lock, flags);
|
||||
list_for_each_entry_safe(t, n, &core->task_queue, list) {
|
||||
if (task == t) {
|
||||
list_del_init(&t->list);
|
||||
spin_unlock_irqrestore(&core->task_queue_lock,
|
||||
flags);
|
||||
SAS_DPRINTK("%s: task 0x%p aborted from "
|
||||
"task_queue\n",
|
||||
__FUNCTION__, task);
|
||||
return TASK_IS_ABORTED;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&core->task_queue_lock, flags);
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
SAS_DPRINTK("%s: aborting task 0x%p\n", __FUNCTION__, task);
|
||||
res = si->dft->lldd_abort_task(task);
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__,
|
||||
task);
|
||||
return TASK_IS_DONE;
|
||||
}
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
if (res == TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("%s: task 0x%p is aborted\n",
|
||||
__FUNCTION__, task);
|
||||
return TASK_IS_ABORTED;
|
||||
} else if (si->dft->lldd_query_task) {
|
||||
SAS_DPRINTK("%s: querying task 0x%p\n",
|
||||
__FUNCTION__, task);
|
||||
res = si->dft->lldd_query_task(task);
|
||||
if (res == TMF_RESP_FUNC_SUCC) {
|
||||
SAS_DPRINTK("%s: task 0x%p at LU\n",
|
||||
__FUNCTION__, task);
|
||||
return TASK_IS_AT_LU;
|
||||
} else if (res == TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("%s: task 0x%p not at LU\n",
|
||||
__FUNCTION__, task);
|
||||
return TASK_IS_NOT_AT_LU;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sas_recover_lu(struct domain_device *dev, struct scsi_cmnd *cmd)
|
||||
{
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
struct scsi_lun lun;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(dev->port->ha->core.shost->transportt);
|
||||
|
||||
int_to_scsilun(cmd->device->lun, &lun);
|
||||
|
||||
SAS_DPRINTK("eh: device %llx LUN %x has the task\n",
|
||||
SAS_ADDR(dev->sas_addr),
|
||||
cmd->device->lun);
|
||||
|
||||
if (i->dft->lldd_abort_task_set)
|
||||
res = i->dft->lldd_abort_task_set(dev, lun.scsi_lun);
|
||||
|
||||
if (res == TMF_RESP_FUNC_FAILED) {
|
||||
if (i->dft->lldd_clear_task_set)
|
||||
res = i->dft->lldd_clear_task_set(dev, lun.scsi_lun);
|
||||
}
|
||||
|
||||
if (res == TMF_RESP_FUNC_FAILED) {
|
||||
if (i->dft->lldd_lu_reset)
|
||||
res = i->dft->lldd_lu_reset(dev, lun.scsi_lun);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sas_recover_I_T(struct domain_device *dev)
|
||||
{
|
||||
int res = TMF_RESP_FUNC_FAILED;
|
||||
struct sas_internal *i =
|
||||
to_sas_internal(dev->port->ha->core.shost->transportt);
|
||||
|
||||
SAS_DPRINTK("I_T nexus reset for dev %016llx\n",
|
||||
SAS_ADDR(dev->sas_addr));
|
||||
|
||||
if (i->dft->lldd_I_T_nexus_reset)
|
||||
res = i->dft->lldd_I_T_nexus_reset(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void sas_scsi_recover_host(struct Scsi_Host *shost)
|
||||
{
|
||||
struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
|
||||
unsigned long flags;
|
||||
LIST_HEAD(error_q);
|
||||
struct scsi_cmnd *cmd, *n;
|
||||
enum task_disposition res = TASK_IS_DONE;
|
||||
int tmf_resp;
|
||||
struct sas_internal *i = to_sas_internal(shost->transportt);
|
||||
|
||||
spin_lock_irqsave(shost->host_lock, flags);
|
||||
list_splice_init(&shost->eh_cmd_q, &error_q);
|
||||
spin_unlock_irqrestore(shost->host_lock, flags);
|
||||
|
||||
SAS_DPRINTK("Enter %s\n", __FUNCTION__);
|
||||
|
||||
/* All tasks on this list were marked SAS_TASK_STATE_ABORTED
|
||||
* by sas_scsi_timed_out() callback.
|
||||
*/
|
||||
Again:
|
||||
SAS_DPRINTK("going over list...\n");
|
||||
list_for_each_entry_safe(cmd, n, &error_q, eh_entry) {
|
||||
struct sas_task *task = TO_SAS_TASK(cmd);
|
||||
|
||||
SAS_DPRINTK("trying to find task 0x%p\n", task);
|
||||
list_del_init(&cmd->eh_entry);
|
||||
res = sas_scsi_find_task(task);
|
||||
|
||||
cmd->eh_eflags = 0;
|
||||
shost->host_failed--;
|
||||
|
||||
switch (res) {
|
||||
case TASK_IS_DONE:
|
||||
SAS_DPRINTK("%s: task 0x%p is done\n", __FUNCTION__,
|
||||
task);
|
||||
task->task_done(task);
|
||||
continue;
|
||||
case TASK_IS_ABORTED:
|
||||
SAS_DPRINTK("%s: task 0x%p is aborted\n",
|
||||
__FUNCTION__, task);
|
||||
task->task_done(task);
|
||||
continue;
|
||||
case TASK_IS_AT_LU:
|
||||
SAS_DPRINTK("task 0x%p is at LU: lu recover\n", task);
|
||||
tmf_resp = sas_recover_lu(task->dev, cmd);
|
||||
if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("dev %016llx LU %x is "
|
||||
"recovered\n",
|
||||
SAS_ADDR(task->dev),
|
||||
cmd->device->lun);
|
||||
task->task_done(task);
|
||||
sas_scsi_clear_queue_lu(&error_q, cmd);
|
||||
goto Again;
|
||||
}
|
||||
/* fallthrough */
|
||||
case TASK_IS_NOT_AT_LU:
|
||||
SAS_DPRINTK("task 0x%p is not at LU: I_T recover\n",
|
||||
task);
|
||||
tmf_resp = sas_recover_I_T(task->dev);
|
||||
if (tmf_resp == TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("I_T %016llx recovered\n",
|
||||
SAS_ADDR(task->dev->sas_addr));
|
||||
task->task_done(task);
|
||||
sas_scsi_clear_queue_I_T(&error_q, task->dev);
|
||||
goto Again;
|
||||
}
|
||||
/* Hammer time :-) */
|
||||
if (i->dft->lldd_clear_nexus_port) {
|
||||
struct asd_sas_port *port = task->dev->port;
|
||||
SAS_DPRINTK("clearing nexus for port:%d\n",
|
||||
port->id);
|
||||
res = i->dft->lldd_clear_nexus_port(port);
|
||||
if (res == TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("clear nexus port:%d "
|
||||
"succeeded\n", port->id);
|
||||
task->task_done(task);
|
||||
sas_scsi_clear_queue_port(&error_q,
|
||||
port);
|
||||
goto Again;
|
||||
}
|
||||
}
|
||||
if (i->dft->lldd_clear_nexus_ha) {
|
||||
SAS_DPRINTK("clear nexus ha\n");
|
||||
res = i->dft->lldd_clear_nexus_ha(ha);
|
||||
if (res == TMF_RESP_FUNC_COMPLETE) {
|
||||
SAS_DPRINTK("clear nexus ha "
|
||||
"succeeded\n");
|
||||
task->task_done(task);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* If we are here -- this means that no amount
|
||||
* of effort could recover from errors. Quite
|
||||
* possibly the HA just disappeared.
|
||||
*/
|
||||
SAS_DPRINTK("error from device %llx, LUN %x "
|
||||
"couldn't be recovered in any way\n",
|
||||
SAS_ADDR(task->dev->sas_addr),
|
||||
cmd->device->lun);
|
||||
|
||||
task->task_done(task);
|
||||
goto clear_q;
|
||||
}
|
||||
}
|
||||
out:
|
||||
SAS_DPRINTK("--- Exit %s\n", __FUNCTION__);
|
||||
return;
|
||||
clear_q:
|
||||
SAS_DPRINTK("--- Exit %s -- clear_q\n", __FUNCTION__);
|
||||
list_for_each_entry_safe(cmd, n, &error_q, eh_entry) {
|
||||
struct sas_task *task = TO_SAS_TASK(cmd);
|
||||
list_del_init(&cmd->eh_entry);
|
||||
task->task_done(task);
|
||||
}
|
||||
}
|
||||
|
||||
enum scsi_eh_timer_return sas_scsi_timed_out(struct scsi_cmnd *cmd)
|
||||
{
|
||||
struct sas_task *task = TO_SAS_TASK(cmd);
|
||||
unsigned long flags;
|
||||
|
||||
if (!task) {
|
||||
SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
|
||||
cmd, task);
|
||||
return EH_HANDLED;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&task->task_state_lock, flags);
|
||||
if (task->task_state_flags & SAS_TASK_STATE_DONE) {
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_HANDLED\n",
|
||||
cmd, task);
|
||||
return EH_HANDLED;
|
||||
}
|
||||
task->task_state_flags |= SAS_TASK_STATE_ABORTED;
|
||||
spin_unlock_irqrestore(&task->task_state_lock, flags);
|
||||
|
||||
SAS_DPRINTK("command 0x%p, task 0x%p, timed out: EH_NOT_HANDLED\n",
|
||||
cmd, task);
|
||||
|
||||
return EH_NOT_HANDLED;
|
||||
}
|
||||
|
||||
struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy)
|
||||
{
|
||||
struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent);
|
||||
struct sas_ha_struct *ha = SHOST_TO_SAS_HA(shost);
|
||||
struct domain_device *found_dev = NULL;
|
||||
int i;
|
||||
|
||||
spin_lock(&ha->phy_port_lock);
|
||||
for (i = 0; i < ha->num_phys; i++) {
|
||||
struct asd_sas_port *port = ha->sas_port[i];
|
||||
struct domain_device *dev;
|
||||
|
||||
spin_lock(&port->dev_list_lock);
|
||||
list_for_each_entry(dev, &port->dev_list, dev_list_node) {
|
||||
if (rphy == dev->rphy) {
|
||||
found_dev = dev;
|
||||
spin_unlock(&port->dev_list_lock);
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
spin_unlock(&port->dev_list_lock);
|
||||
}
|
||||
found:
|
||||
spin_unlock(&ha->phy_port_lock);
|
||||
|
||||
return found_dev;
|
||||
}
|
||||
|
||||
static inline struct domain_device *sas_find_target(struct scsi_target *starget)
|
||||
{
|
||||
struct sas_rphy *rphy = dev_to_rphy(starget->dev.parent);
|
||||
|
||||
return sas_find_dev_by_rphy(rphy);
|
||||
}
|
||||
|
||||
int sas_target_alloc(struct scsi_target *starget)
|
||||
{
|
||||
struct domain_device *found_dev = sas_find_target(starget);
|
||||
|
||||
if (!found_dev)
|
||||
return -ENODEV;
|
||||
|
||||
starget->hostdata = found_dev;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define SAS_DEF_QD 32
|
||||
#define SAS_MAX_QD 64
|
||||
|
||||
int sas_slave_configure(struct scsi_device *scsi_dev)
|
||||
{
|
||||
struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
|
||||
struct sas_ha_struct *sas_ha;
|
||||
|
||||
BUG_ON(dev->rphy->identify.device_type != SAS_END_DEVICE);
|
||||
|
||||
sas_ha = dev->port->ha;
|
||||
|
||||
sas_read_port_mode_page(scsi_dev);
|
||||
|
||||
if (scsi_dev->tagged_supported) {
|
||||
scsi_set_tag_type(scsi_dev, MSG_SIMPLE_TAG);
|
||||
scsi_activate_tcq(scsi_dev, SAS_DEF_QD);
|
||||
} else {
|
||||
SAS_DPRINTK("device %llx, LUN %x doesn't support "
|
||||
"TCQ\n", SAS_ADDR(dev->sas_addr),
|
||||
scsi_dev->lun);
|
||||
scsi_dev->tagged_supported = 0;
|
||||
scsi_set_tag_type(scsi_dev, 0);
|
||||
scsi_deactivate_tcq(scsi_dev, 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sas_slave_destroy(struct scsi_device *scsi_dev)
|
||||
{
|
||||
}
|
||||
|
||||
int sas_change_queue_depth(struct scsi_device *scsi_dev, int new_depth)
|
||||
{
|
||||
int res = min(new_depth, SAS_MAX_QD);
|
||||
|
||||
if (scsi_dev->tagged_supported)
|
||||
scsi_adjust_queue_depth(scsi_dev, scsi_get_tag_type(scsi_dev),
|
||||
res);
|
||||
else {
|
||||
struct domain_device *dev = sdev_to_domain_dev(scsi_dev);
|
||||
sas_printk("device %llx LUN %x queue depth changed to 1\n",
|
||||
SAS_ADDR(dev->sas_addr),
|
||||
scsi_dev->lun);
|
||||
scsi_adjust_queue_depth(scsi_dev, 0, 1);
|
||||
res = 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int sas_change_queue_type(struct scsi_device *scsi_dev, int qt)
|
||||
{
|
||||
if (!scsi_dev->tagged_supported)
|
||||
return 0;
|
||||
|
||||
scsi_deactivate_tcq(scsi_dev, 1);
|
||||
|
||||
scsi_set_tag_type(scsi_dev, qt);
|
||||
scsi_activate_tcq(scsi_dev, scsi_dev->queue_depth);
|
||||
|
||||
return qt;
|
||||
}
|
||||
|
||||
int sas_bios_param(struct scsi_device *scsi_dev,
|
||||
struct block_device *bdev,
|
||||
sector_t capacity, int *hsc)
|
||||
{
|
||||
hsc[0] = 255;
|
||||
hsc[1] = 63;
|
||||
sector_div(capacity, 255*63);
|
||||
hsc[2] = capacity;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------- Task Collector Thread implementation ---------- */
|
||||
|
||||
static void sas_queue(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
struct scsi_core *core = &sas_ha->core;
|
||||
unsigned long flags;
|
||||
LIST_HEAD(q);
|
||||
int can_queue;
|
||||
int res;
|
||||
struct sas_internal *i = to_sas_internal(core->shost->transportt);
|
||||
|
||||
spin_lock_irqsave(&core->task_queue_lock, flags);
|
||||
while (!core->queue_thread_kill &&
|
||||
!list_empty(&core->task_queue)) {
|
||||
|
||||
can_queue = sas_ha->lldd_queue_size - core->task_queue_size;
|
||||
if (can_queue >= 0) {
|
||||
can_queue = core->task_queue_size;
|
||||
list_splice_init(&core->task_queue, &q);
|
||||
} else {
|
||||
struct list_head *a, *n;
|
||||
|
||||
can_queue = sas_ha->lldd_queue_size;
|
||||
list_for_each_safe(a, n, &core->task_queue) {
|
||||
list_move_tail(a, &q);
|
||||
if (--can_queue == 0)
|
||||
break;
|
||||
}
|
||||
can_queue = sas_ha->lldd_queue_size;
|
||||
}
|
||||
core->task_queue_size -= can_queue;
|
||||
spin_unlock_irqrestore(&core->task_queue_lock, flags);
|
||||
{
|
||||
struct sas_task *task = list_entry(q.next,
|
||||
struct sas_task,
|
||||
list);
|
||||
list_del_init(&q);
|
||||
res = i->dft->lldd_execute_task(task, can_queue,
|
||||
GFP_KERNEL);
|
||||
if (unlikely(res))
|
||||
__list_add(&q, task->list.prev, &task->list);
|
||||
}
|
||||
spin_lock_irqsave(&core->task_queue_lock, flags);
|
||||
if (res) {
|
||||
list_splice_init(&q, &core->task_queue); /*at head*/
|
||||
core->task_queue_size += can_queue;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&core->task_queue_lock, flags);
|
||||
}
|
||||
|
||||
static DECLARE_COMPLETION(queue_th_comp);
|
||||
|
||||
/**
|
||||
* sas_queue_thread -- The Task Collector thread
|
||||
* @_sas_ha: pointer to struct sas_ha
|
||||
*/
|
||||
static int sas_queue_thread(void *_sas_ha)
|
||||
{
|
||||
struct sas_ha_struct *sas_ha = _sas_ha;
|
||||
struct scsi_core *core = &sas_ha->core;
|
||||
|
||||
daemonize("sas_queue_%d", core->shost->host_no);
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
complete(&queue_th_comp);
|
||||
|
||||
while (1) {
|
||||
down_interruptible(&core->queue_thread_sema);
|
||||
sas_queue(sas_ha);
|
||||
if (core->queue_thread_kill)
|
||||
break;
|
||||
}
|
||||
|
||||
complete(&queue_th_comp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sas_init_queue(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
int res;
|
||||
struct scsi_core *core = &sas_ha->core;
|
||||
|
||||
spin_lock_init(&core->task_queue_lock);
|
||||
core->task_queue_size = 0;
|
||||
INIT_LIST_HEAD(&core->task_queue);
|
||||
init_MUTEX_LOCKED(&core->queue_thread_sema);
|
||||
|
||||
res = kernel_thread(sas_queue_thread, sas_ha, 0);
|
||||
if (res >= 0)
|
||||
wait_for_completion(&queue_th_comp);
|
||||
|
||||
return res < 0 ? res : 0;
|
||||
}
|
||||
|
||||
void sas_shutdown_queue(struct sas_ha_struct *sas_ha)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct scsi_core *core = &sas_ha->core;
|
||||
struct sas_task *task, *n;
|
||||
|
||||
init_completion(&queue_th_comp);
|
||||
core->queue_thread_kill = 1;
|
||||
up(&core->queue_thread_sema);
|
||||
wait_for_completion(&queue_th_comp);
|
||||
|
||||
if (!list_empty(&core->task_queue))
|
||||
SAS_DPRINTK("HA: %llx: scsi core task queue is NOT empty!?\n",
|
||||
SAS_ADDR(sas_ha->sas_addr));
|
||||
|
||||
spin_lock_irqsave(&core->task_queue_lock, flags);
|
||||
list_for_each_entry_safe(task, n, &core->task_queue, list) {
|
||||
struct scsi_cmnd *cmd = task->uldd_task;
|
||||
|
||||
list_del_init(&task->list);
|
||||
|
||||
ASSIGN_SAS_TASK(cmd, NULL);
|
||||
sas_free_task(task);
|
||||
cmd->result = DID_ABORT << 16;
|
||||
cmd->scsi_done(cmd);
|
||||
}
|
||||
spin_unlock_irqrestore(&core->task_queue_lock, flags);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(sas_queuecommand);
|
||||
EXPORT_SYMBOL_GPL(sas_target_alloc);
|
||||
EXPORT_SYMBOL_GPL(sas_slave_configure);
|
||||
EXPORT_SYMBOL_GPL(sas_slave_destroy);
|
||||
EXPORT_SYMBOL_GPL(sas_change_queue_depth);
|
||||
EXPORT_SYMBOL_GPL(sas_change_queue_type);
|
||||
EXPORT_SYMBOL_GPL(sas_bios_param);
|
@ -285,6 +285,7 @@ struct lpfc_hba {
|
||||
uint32_t cfg_log_verbose;
|
||||
uint32_t cfg_lun_queue_depth;
|
||||
uint32_t cfg_nodev_tmo;
|
||||
uint32_t cfg_devloss_tmo;
|
||||
uint32_t cfg_hba_queue_depth;
|
||||
uint32_t cfg_fcp_class;
|
||||
uint32_t cfg_use_adisc;
|
||||
@ -302,6 +303,9 @@ struct lpfc_hba {
|
||||
uint32_t cfg_poll_tmo;
|
||||
uint32_t cfg_sg_seg_cnt;
|
||||
uint32_t cfg_sg_dma_buf_size;
|
||||
uint64_t cfg_soft_wwpn;
|
||||
|
||||
uint32_t dev_loss_tmo_changed;
|
||||
|
||||
lpfc_vpd_t vpd; /* vital product data */
|
||||
|
||||
@ -351,6 +355,8 @@ struct lpfc_hba {
|
||||
#define VPD_PORT 0x8 /* valid vpd port data */
|
||||
#define VPD_MASK 0xf /* mask for any vpd data */
|
||||
|
||||
uint8_t soft_wwpn_enable;
|
||||
|
||||
struct timer_list fcp_poll_timer;
|
||||
struct timer_list els_tmofunc;
|
||||
|
||||
@ -391,3 +397,5 @@ struct rnidrsp {
|
||||
struct list_head list;
|
||||
uint32_t data;
|
||||
};
|
||||
|
||||
#define FC_REG_DUMP_EVENT 0x10 /* Register for Dump events */
|
||||
|
@ -39,6 +39,9 @@
|
||||
#include "lpfc_compat.h"
|
||||
#include "lpfc_crtn.h"
|
||||
|
||||
#define LPFC_DEF_DEVLOSS_TMO 30
|
||||
#define LPFC_MIN_DEVLOSS_TMO 1
|
||||
#define LPFC_MAX_DEVLOSS_TMO 255
|
||||
|
||||
static void
|
||||
lpfc_jedec_to_ascii(int incr, char hdw[])
|
||||
@ -548,6 +551,119 @@ static CLASS_DEVICE_ATTR(board_mode, S_IRUGO | S_IWUSR,
|
||||
lpfc_board_mode_show, lpfc_board_mode_store);
|
||||
static CLASS_DEVICE_ATTR(issue_reset, S_IWUSR, NULL, lpfc_issue_reset);
|
||||
|
||||
|
||||
static char *lpfc_soft_wwpn_key = "C99G71SL8032A";
|
||||
|
||||
static ssize_t
|
||||
lpfc_soft_wwpn_enable_store(struct class_device *cdev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata;
|
||||
unsigned int cnt = count;
|
||||
|
||||
/*
|
||||
* We're doing a simple sanity check for soft_wwpn setting.
|
||||
* We require that the user write a specific key to enable
|
||||
* the soft_wwpn attribute to be settable. Once the attribute
|
||||
* is written, the enable key resets. If further updates are
|
||||
* desired, the key must be written again to re-enable the
|
||||
* attribute.
|
||||
*
|
||||
* The "key" is not secret - it is a hardcoded string shown
|
||||
* here. The intent is to protect against the random user or
|
||||
* application that is just writing attributes.
|
||||
*/
|
||||
|
||||
/* count may include a LF at end of string */
|
||||
if (buf[cnt-1] == '\n')
|
||||
cnt--;
|
||||
|
||||
if ((cnt != strlen(lpfc_soft_wwpn_key)) ||
|
||||
(strncmp(buf, lpfc_soft_wwpn_key, strlen(lpfc_soft_wwpn_key)) != 0))
|
||||
return -EINVAL;
|
||||
|
||||
phba->soft_wwpn_enable = 1;
|
||||
return count;
|
||||
}
|
||||
static CLASS_DEVICE_ATTR(lpfc_soft_wwpn_enable, S_IWUSR, NULL,
|
||||
lpfc_soft_wwpn_enable_store);
|
||||
|
||||
static ssize_t
|
||||
lpfc_soft_wwpn_show(struct class_device *cdev, char *buf)
|
||||
{
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata;
|
||||
return snprintf(buf, PAGE_SIZE, "0x%llx\n", phba->cfg_soft_wwpn);
|
||||
}
|
||||
|
||||
|
||||
static ssize_t
|
||||
lpfc_soft_wwpn_store(struct class_device *cdev, const char *buf, size_t count)
|
||||
{
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata;
|
||||
struct completion online_compl;
|
||||
int stat1=0, stat2=0;
|
||||
unsigned int i, j, cnt=count;
|
||||
u8 wwpn[8];
|
||||
|
||||
/* count may include a LF at end of string */
|
||||
if (buf[cnt-1] == '\n')
|
||||
cnt--;
|
||||
|
||||
if (!phba->soft_wwpn_enable || (cnt < 16) || (cnt > 18) ||
|
||||
((cnt == 17) && (*buf++ != 'x')) ||
|
||||
((cnt == 18) && ((*buf++ != '0') || (*buf++ != 'x'))))
|
||||
return -EINVAL;
|
||||
|
||||
phba->soft_wwpn_enable = 0;
|
||||
|
||||
memset(wwpn, 0, sizeof(wwpn));
|
||||
|
||||
/* Validate and store the new name */
|
||||
for (i=0, j=0; i < 16; i++) {
|
||||
if ((*buf >= 'a') && (*buf <= 'f'))
|
||||
j = ((j << 4) | ((*buf++ -'a') + 10));
|
||||
else if ((*buf >= 'A') && (*buf <= 'F'))
|
||||
j = ((j << 4) | ((*buf++ -'A') + 10));
|
||||
else if ((*buf >= '0') && (*buf <= '9'))
|
||||
j = ((j << 4) | (*buf++ -'0'));
|
||||
else
|
||||
return -EINVAL;
|
||||
if (i % 2) {
|
||||
wwpn[i/2] = j & 0xff;
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
phba->cfg_soft_wwpn = wwn_to_u64(wwpn);
|
||||
fc_host_port_name(host) = phba->cfg_soft_wwpn;
|
||||
|
||||
dev_printk(KERN_NOTICE, &phba->pcidev->dev,
|
||||
"lpfc%d: Reinitializing to use soft_wwpn\n", phba->brd_no);
|
||||
|
||||
init_completion(&online_compl);
|
||||
lpfc_workq_post_event(phba, &stat1, &online_compl, LPFC_EVT_OFFLINE);
|
||||
wait_for_completion(&online_compl);
|
||||
if (stat1)
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0463 lpfc_soft_wwpn attribute set failed to reinit "
|
||||
"adapter - %d\n", phba->brd_no, stat1);
|
||||
|
||||
init_completion(&online_compl);
|
||||
lpfc_workq_post_event(phba, &stat2, &online_compl, LPFC_EVT_ONLINE);
|
||||
wait_for_completion(&online_compl);
|
||||
if (stat2)
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0464 lpfc_soft_wwpn attribute set failed to reinit "
|
||||
"adapter - %d\n", phba->brd_no, stat2);
|
||||
|
||||
return (stat1 || stat2) ? -EIO : count;
|
||||
}
|
||||
static CLASS_DEVICE_ATTR(lpfc_soft_wwpn, S_IRUGO | S_IWUSR,\
|
||||
lpfc_soft_wwpn_show, lpfc_soft_wwpn_store);
|
||||
|
||||
|
||||
static int lpfc_poll = 0;
|
||||
module_param(lpfc_poll, int, 0);
|
||||
MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:"
|
||||
@ -558,6 +674,123 @@ MODULE_PARM_DESC(lpfc_poll, "FCP ring polling mode control:"
|
||||
static CLASS_DEVICE_ATTR(lpfc_poll, S_IRUGO | S_IWUSR,
|
||||
lpfc_poll_show, lpfc_poll_store);
|
||||
|
||||
/*
|
||||
# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear
|
||||
# until the timer expires. Value range is [0,255]. Default value is 30.
|
||||
*/
|
||||
static int lpfc_nodev_tmo = LPFC_DEF_DEVLOSS_TMO;
|
||||
static int lpfc_devloss_tmo = LPFC_DEF_DEVLOSS_TMO;
|
||||
module_param(lpfc_nodev_tmo, int, 0);
|
||||
MODULE_PARM_DESC(lpfc_nodev_tmo,
|
||||
"Seconds driver will hold I/O waiting "
|
||||
"for a device to come back");
|
||||
static ssize_t
|
||||
lpfc_nodev_tmo_show(struct class_device *cdev, char *buf)
|
||||
{
|
||||
struct Scsi_Host *host = class_to_shost(cdev);
|
||||
struct lpfc_hba *phba = (struct lpfc_hba*)host->hostdata;
|
||||
int val = 0;
|
||||
val = phba->cfg_devloss_tmo;
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n",
|
||||
phba->cfg_devloss_tmo);
|
||||
}
|
||||
|
||||
static int
|
||||
lpfc_nodev_tmo_init(struct lpfc_hba *phba, int val)
|
||||
{
|
||||
static int warned;
|
||||
if (phba->cfg_devloss_tmo != LPFC_DEF_DEVLOSS_TMO) {
|
||||
phba->cfg_nodev_tmo = phba->cfg_devloss_tmo;
|
||||
if (!warned && val != LPFC_DEF_DEVLOSS_TMO) {
|
||||
warned = 1;
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0402 Ignoring nodev_tmo module "
|
||||
"parameter because devloss_tmo is"
|
||||
" set.\n",
|
||||
phba->brd_no);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) {
|
||||
phba->cfg_nodev_tmo = val;
|
||||
phba->cfg_devloss_tmo = val;
|
||||
return 0;
|
||||
}
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0400 lpfc_nodev_tmo attribute cannot be set to %d, "
|
||||
"allowed range is [%d, %d]\n",
|
||||
phba->brd_no, val,
|
||||
LPFC_MIN_DEVLOSS_TMO, LPFC_MAX_DEVLOSS_TMO);
|
||||
phba->cfg_nodev_tmo = LPFC_DEF_DEVLOSS_TMO;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
lpfc_nodev_tmo_set(struct lpfc_hba *phba, int val)
|
||||
{
|
||||
if (phba->dev_loss_tmo_changed ||
|
||||
(lpfc_devloss_tmo != LPFC_DEF_DEVLOSS_TMO)) {
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0401 Ignoring change to nodev_tmo "
|
||||
"because devloss_tmo is set.\n",
|
||||
phba->brd_no);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) {
|
||||
phba->cfg_nodev_tmo = val;
|
||||
phba->cfg_devloss_tmo = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0403 lpfc_nodev_tmo attribute cannot be set to %d, "
|
||||
"allowed range is [%d, %d]\n",
|
||||
phba->brd_no, val, LPFC_MIN_DEVLOSS_TMO,
|
||||
LPFC_MAX_DEVLOSS_TMO);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lpfc_param_store(nodev_tmo)
|
||||
|
||||
static CLASS_DEVICE_ATTR(lpfc_nodev_tmo, S_IRUGO | S_IWUSR,
|
||||
lpfc_nodev_tmo_show, lpfc_nodev_tmo_store);
|
||||
|
||||
/*
|
||||
# lpfc_devloss_tmo: If set, it will hold all I/O errors on devices that
|
||||
# disappear until the timer expires. Value range is [0,255]. Default
|
||||
# value is 30.
|
||||
*/
|
||||
module_param(lpfc_devloss_tmo, int, 0);
|
||||
MODULE_PARM_DESC(lpfc_devloss_tmo,
|
||||
"Seconds driver will hold I/O waiting "
|
||||
"for a device to come back");
|
||||
lpfc_param_init(devloss_tmo, LPFC_DEF_DEVLOSS_TMO,
|
||||
LPFC_MIN_DEVLOSS_TMO, LPFC_MAX_DEVLOSS_TMO)
|
||||
lpfc_param_show(devloss_tmo)
|
||||
static int
|
||||
lpfc_devloss_tmo_set(struct lpfc_hba *phba, int val)
|
||||
{
|
||||
if (val >= LPFC_MIN_DEVLOSS_TMO && val <= LPFC_MAX_DEVLOSS_TMO) {
|
||||
phba->cfg_nodev_tmo = val;
|
||||
phba->cfg_devloss_tmo = val;
|
||||
phba->dev_loss_tmo_changed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
|
||||
"%d:0404 lpfc_devloss_tmo attribute cannot be set to"
|
||||
" %d, allowed range is [%d, %d]\n",
|
||||
phba->brd_no, val, LPFC_MIN_DEVLOSS_TMO,
|
||||
LPFC_MAX_DEVLOSS_TMO);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lpfc_param_store(devloss_tmo)
|
||||
static CLASS_DEVICE_ATTR(lpfc_devloss_tmo, S_IRUGO | S_IWUSR,
|
||||
lpfc_devloss_tmo_show, lpfc_devloss_tmo_store);
|
||||
|
||||
/*
|
||||
# lpfc_log_verbose: Only turn this flag on if you are willing to risk being
|
||||
# deluged with LOTS of information.
|
||||
@ -616,14 +849,6 @@ LPFC_ATTR_R(hba_queue_depth, 8192, 32, 8192,
|
||||
LPFC_ATTR_R(scan_down, 1, 0, 1,
|
||||
"Start scanning for devices from highest ALPA to lowest");
|
||||
|
||||
/*
|
||||
# lpfc_nodev_tmo: If set, it will hold all I/O errors on devices that disappear
|
||||
# until the timer expires. Value range is [0,255]. Default value is 30.
|
||||
# NOTE: this MUST be less then the SCSI Layer command timeout - 1.
|
||||
*/
|
||||
LPFC_ATTR_RW(nodev_tmo, 30, 0, 255,
|
||||
"Seconds driver will hold I/O waiting for a device to come back");
|
||||
|
||||
/*
|
||||
# lpfc_topology: link topology for init link
|
||||
# 0x0 = attempt loop mode then point-to-point
|
||||
@ -720,6 +945,7 @@ LPFC_ATTR_R(max_luns, 255, 0, 65535,
|
||||
LPFC_ATTR_RW(poll_tmo, 10, 1, 255,
|
||||
"Milliseconds driver will wait between polling FCP ring");
|
||||
|
||||
|
||||
struct class_device_attribute *lpfc_host_attrs[] = {
|
||||
&class_device_attr_info,
|
||||
&class_device_attr_serialnum,
|
||||
@ -737,6 +963,7 @@ struct class_device_attribute *lpfc_host_attrs[] = {
|
||||
&class_device_attr_lpfc_lun_queue_depth,
|
||||
&class_device_attr_lpfc_hba_queue_depth,
|
||||
&class_device_attr_lpfc_nodev_tmo,
|
||||
&class_device_attr_lpfc_devloss_tmo,
|
||||
&class_device_attr_lpfc_fcp_class,
|
||||
&class_device_attr_lpfc_use_adisc,
|
||||
&class_device_attr_lpfc_ack0,
|
||||
@ -754,6 +981,8 @@ struct class_device_attribute *lpfc_host_attrs[] = {
|
||||
&class_device_attr_issue_reset,
|
||||
&class_device_attr_lpfc_poll,
|
||||
&class_device_attr_lpfc_poll_tmo,
|
||||
&class_device_attr_lpfc_soft_wwpn,
|
||||
&class_device_attr_lpfc_soft_wwpn_enable,
|
||||
NULL,
|
||||
};
|
||||
|
||||
@ -1204,6 +1433,15 @@ lpfc_get_host_fabric_name (struct Scsi_Host *shost)
|
||||
fc_host_fabric_name(shost) = node_name;
|
||||
}
|
||||
|
||||
static void
|
||||
lpfc_get_host_symbolic_name (struct Scsi_Host *shost)
|
||||
{
|
||||
struct lpfc_hba *phba = (struct lpfc_hba*)shost->hostdata;
|
||||
|
||||
spin_lock_irq(shost->host_lock);
|
||||
lpfc_get_hba_sym_node_name(phba, fc_host_symbolic_name(shost));
|
||||
spin_unlock_irq(shost->host_lock);
|
||||
}
|
||||
|
||||
static struct fc_host_statistics *
|
||||
lpfc_get_stats(struct Scsi_Host *shost)
|
||||
@ -1440,28 +1678,13 @@ lpfc_get_starget_port_name(struct scsi_target *starget)
|
||||
fc_starget_port_name(starget) = port_name;
|
||||
}
|
||||
|
||||
static void
|
||||
lpfc_get_rport_loss_tmo(struct fc_rport *rport)
|
||||
{
|
||||
/*
|
||||
* Return the driver's global value for device loss timeout plus
|
||||
* five seconds to allow the driver's nodev timer to run.
|
||||
*/
|
||||
rport->dev_loss_tmo = lpfc_nodev_tmo + 5;
|
||||
}
|
||||
|
||||
static void
|
||||
lpfc_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
|
||||
{
|
||||
/*
|
||||
* The driver doesn't have a per-target timeout setting. Set
|
||||
* this value globally. lpfc_nodev_tmo should be greater then 0.
|
||||
*/
|
||||
if (timeout)
|
||||
lpfc_nodev_tmo = timeout;
|
||||
rport->dev_loss_tmo = timeout;
|
||||
else
|
||||
lpfc_nodev_tmo = 1;
|
||||
rport->dev_loss_tmo = lpfc_nodev_tmo + 5;
|
||||
rport->dev_loss_tmo = 1;
|
||||
}
|
||||
|
||||
|
||||
@ -1486,7 +1709,6 @@ struct fc_function_template lpfc_transport_functions = {
|
||||
.show_host_port_name = 1,
|
||||
.show_host_supported_classes = 1,
|
||||
.show_host_supported_fc4s = 1,
|
||||
.show_host_symbolic_name = 1,
|
||||
.show_host_supported_speeds = 1,
|
||||
.show_host_maxframe_size = 1,
|
||||
|
||||
@ -1509,6 +1731,9 @@ struct fc_function_template lpfc_transport_functions = {
|
||||
.get_host_fabric_name = lpfc_get_host_fabric_name,
|
||||
.show_host_fabric_name = 1,
|
||||
|
||||
.get_host_symbolic_name = lpfc_get_host_symbolic_name,
|
||||
.show_host_symbolic_name = 1,
|
||||
|
||||
/*
|
||||
* The LPFC driver treats linkdown handling as target loss events
|
||||
* so there are no sysfs handlers for link_down_tmo.
|
||||
@ -1521,7 +1746,6 @@ struct fc_function_template lpfc_transport_functions = {
|
||||
.show_rport_maxframe_size = 1,
|
||||
.show_rport_supported_classes = 1,
|
||||
|
||||
.get_rport_dev_loss_tmo = lpfc_get_rport_loss_tmo,
|
||||
.set_rport_dev_loss_tmo = lpfc_set_rport_loss_tmo,
|
||||
.show_rport_dev_loss_tmo = 1,
|
||||
|
||||
@ -1535,6 +1759,8 @@ struct fc_function_template lpfc_transport_functions = {
|
||||
.show_starget_port_name = 1,
|
||||
|
||||
.issue_fc_host_lip = lpfc_issue_lip,
|
||||
.dev_loss_tmo_callbk = lpfc_dev_loss_tmo_callbk,
|
||||
.terminate_rport_io = lpfc_terminate_rport_io,
|
||||
};
|
||||
|
||||
void
|
||||
@ -1550,14 +1776,15 @@ lpfc_get_cfgparam(struct lpfc_hba *phba)
|
||||
lpfc_ack0_init(phba, lpfc_ack0);
|
||||
lpfc_topology_init(phba, lpfc_topology);
|
||||
lpfc_scan_down_init(phba, lpfc_scan_down);
|
||||
lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo);
|
||||
lpfc_link_speed_init(phba, lpfc_link_speed);
|
||||
lpfc_fdmi_on_init(phba, lpfc_fdmi_on);
|
||||
lpfc_discovery_threads_init(phba, lpfc_discovery_threads);
|
||||
lpfc_max_luns_init(phba, lpfc_max_luns);
|
||||
lpfc_poll_tmo_init(phba, lpfc_poll_tmo);
|
||||
|
||||
lpfc_devloss_tmo_init(phba, lpfc_devloss_tmo);
|
||||
lpfc_nodev_tmo_init(phba, lpfc_nodev_tmo);
|
||||
phba->cfg_poll = lpfc_poll;
|
||||
phba->cfg_soft_wwpn = 0L;
|
||||
|
||||
/*
|
||||
* The total number of segments is the configuration value plus 2
|
||||
|
@ -18,6 +18,7 @@
|
||||
* included with this package. *
|
||||
*******************************************************************/
|
||||
|
||||
struct fc_rport;
|
||||
void lpfc_dump_mem(struct lpfc_hba *, LPFC_MBOXQ_t *, uint16_t);
|
||||
void lpfc_read_nv(struct lpfc_hba *, LPFC_MBOXQ_t *);
|
||||
int lpfc_read_la(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb,
|
||||
@ -200,6 +201,8 @@ extern struct scsi_host_template lpfc_template;
|
||||
extern struct fc_function_template lpfc_transport_functions;
|
||||
|
||||
void lpfc_get_hba_sym_node_name(struct lpfc_hba * phba, uint8_t * symbp);
|
||||
void lpfc_terminate_rport_io(struct fc_rport *);
|
||||
void lpfc_dev_loss_tmo_callbk(struct fc_rport *rport);
|
||||
|
||||
#define ScsiResult(host_code, scsi_code) (((host_code) << 16) | scsi_code)
|
||||
#define HBA_EVENT_RSCN 5
|
||||
|
@ -324,7 +324,6 @@ lpfc_ns_rsp(struct lpfc_hba * phba, struct lpfc_dmabuf * mp, uint32_t Size)
|
||||
struct lpfc_sli_ct_request *Response =
|
||||
(struct lpfc_sli_ct_request *) mp->virt;
|
||||
struct lpfc_nodelist *ndlp = NULL;
|
||||
struct lpfc_nodelist *next_ndlp;
|
||||
struct lpfc_dmabuf *mlast, *next_mp;
|
||||
uint32_t *ctptr = (uint32_t *) & Response->un.gid.PortType;
|
||||
uint32_t Did;
|
||||
@ -399,30 +398,6 @@ nsout1:
|
||||
* current driver state.
|
||||
*/
|
||||
if (phba->hba_state == LPFC_HBA_READY) {
|
||||
|
||||
/*
|
||||
* Switch ports that connect a loop of multiple targets need
|
||||
* special consideration. The driver wants to unregister the
|
||||
* rpi only on the target that was pulled from the loop. On
|
||||
* RSCN, the driver wants to rediscover an NPort only if the
|
||||
* driver flagged it as NLP_NPR_2B_DISC. Provided adisc is
|
||||
* not enabled and the NPort is not capable of retransmissions
|
||||
* (FC Tape) prevent timing races with the scsi error handler by
|
||||
* unregistering the Nport's RPI. This action causes all
|
||||
* outstanding IO to flush back to the midlayer.
|
||||
*/
|
||||
list_for_each_entry_safe(ndlp, next_ndlp, &phba->fc_npr_list,
|
||||
nlp_listp) {
|
||||
if (!(ndlp->nlp_flag & NLP_NPR_2B_DISC) &&
|
||||
(lpfc_rscn_payload_check(phba, ndlp->nlp_DID))) {
|
||||
if ((phba->cfg_use_adisc == 0) &&
|
||||
!(ndlp->nlp_fcp_info &
|
||||
NLP_FCP_2_DEVICE)) {
|
||||
lpfc_unreg_rpi(phba, ndlp);
|
||||
ndlp->nlp_flag &= ~NLP_NPR_ADISC;
|
||||
}
|
||||
}
|
||||
}
|
||||
lpfc_els_flush_rscn(phba);
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
phba->fc_flag |= FC_RSCN_MODE; /* we are still in RSCN mode */
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
/* worker thread events */
|
||||
enum lpfc_work_type {
|
||||
LPFC_EVT_NODEV_TMO,
|
||||
LPFC_EVT_ONLINE,
|
||||
LPFC_EVT_OFFLINE,
|
||||
LPFC_EVT_WARM_START,
|
||||
@ -74,11 +73,9 @@ struct lpfc_nodelist {
|
||||
#define NLP_FCP_2_DEVICE 0x10 /* FCP-2 device */
|
||||
|
||||
struct timer_list nlp_delayfunc; /* Used for delayed ELS cmds */
|
||||
struct timer_list nlp_tmofunc; /* Used for nodev tmo */
|
||||
struct fc_rport *rport; /* Corresponding FC transport
|
||||
port structure */
|
||||
struct lpfc_hba *nlp_phba;
|
||||
struct lpfc_work_evt nodev_timeout_evt;
|
||||
struct lpfc_work_evt els_retry_evt;
|
||||
unsigned long last_ramp_up_time; /* jiffy of last ramp up */
|
||||
unsigned long last_q_full_time; /* jiffy of last queue full */
|
||||
@ -102,7 +99,6 @@ struct lpfc_nodelist {
|
||||
#define NLP_LOGO_SND 0x100 /* sent LOGO request for this entry */
|
||||
#define NLP_RNID_SND 0x400 /* sent RNID request for this entry */
|
||||
#define NLP_ELS_SND_MASK 0x7e0 /* sent ELS request for this entry */
|
||||
#define NLP_NODEV_TMO 0x10000 /* nodev timeout is running for node */
|
||||
#define NLP_DELAY_TMO 0x20000 /* delay timeout is running for node */
|
||||
#define NLP_NPR_2B_DISC 0x40000 /* node is included in num_disc_nodes */
|
||||
#define NLP_RCV_PLOGI 0x80000 /* Rcv'ed PLOGI from remote system */
|
||||
@ -169,7 +165,7 @@ struct lpfc_nodelist {
|
||||
*/
|
||||
/*
|
||||
* For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped
|
||||
* lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers
|
||||
* lists will receive a DEVICE_RECOVERY event. If the linkdown or devloss timers
|
||||
* expire, all effected nodes will receive a DEVICE_RM event.
|
||||
*/
|
||||
/*
|
||||
|
@ -2506,6 +2506,7 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba,
|
||||
uint32_t *lp;
|
||||
IOCB_t *icmd;
|
||||
uint32_t payload_len, cmd;
|
||||
int i;
|
||||
|
||||
icmd = &cmdiocb->iocb;
|
||||
pcmd = (struct lpfc_dmabuf *) cmdiocb->context2;
|
||||
@ -2524,6 +2525,10 @@ lpfc_els_rcv_rscn(struct lpfc_hba * phba,
|
||||
phba->brd_no,
|
||||
phba->fc_flag, payload_len, *lp, phba->fc_rscn_id_cnt);
|
||||
|
||||
for (i = 0; i < payload_len/sizeof(uint32_t); i++)
|
||||
fc_host_post_event(phba->host, fc_get_event_number(),
|
||||
FCH_EVT_RSCN, lp[i]);
|
||||
|
||||
/* If we are about to begin discovery, just ACC the RSCN.
|
||||
* Discovery processing will satisfy it.
|
||||
*/
|
||||
|
@ -56,28 +56,63 @@ static uint8_t lpfcAlpaArray[] = {
|
||||
|
||||
static void lpfc_disc_timeout_handler(struct lpfc_hba *);
|
||||
|
||||
static void
|
||||
lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
|
||||
void
|
||||
lpfc_terminate_rport_io(struct fc_rport *rport)
|
||||
{
|
||||
uint8_t *name = (uint8_t *)&ndlp->nlp_portname;
|
||||
int warn_on = 0;
|
||||
struct lpfc_rport_data *rdata;
|
||||
struct lpfc_nodelist * ndlp;
|
||||
struct lpfc_hba *phba;
|
||||
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
if (!(ndlp->nlp_flag & NLP_NODEV_TMO)) {
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
rdata = rport->dd_data;
|
||||
ndlp = rdata->pnode;
|
||||
|
||||
if (!ndlp) {
|
||||
if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)
|
||||
printk(KERN_ERR "Cannot find remote node"
|
||||
" to terminate I/O Data x%x\n",
|
||||
rport->port_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If a discovery event readded nodev_timer after timer
|
||||
* firing and before processing the timer, cancel the
|
||||
* nlp_tmofunc.
|
||||
*/
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
del_timer_sync(&ndlp->nlp_tmofunc);
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
phba = ndlp->nlp_phba;
|
||||
|
||||
ndlp->nlp_flag &= ~NLP_NODEV_TMO;
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
if (ndlp->nlp_sid != NLP_NO_SID) {
|
||||
lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
|
||||
ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT);
|
||||
}
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function will be called when dev_loss_tmo fire.
|
||||
*/
|
||||
void
|
||||
lpfc_dev_loss_tmo_callbk(struct fc_rport *rport)
|
||||
{
|
||||
struct lpfc_rport_data *rdata;
|
||||
struct lpfc_nodelist * ndlp;
|
||||
uint8_t *name;
|
||||
int warn_on = 0;
|
||||
struct lpfc_hba *phba;
|
||||
|
||||
rdata = rport->dd_data;
|
||||
ndlp = rdata->pnode;
|
||||
|
||||
if (!ndlp) {
|
||||
if (rport->roles & FC_RPORT_ROLE_FCP_TARGET)
|
||||
printk(KERN_ERR "Cannot find remote node"
|
||||
" for rport in dev_loss_tmo_callbk x%x\n",
|
||||
rport->port_id);
|
||||
return;
|
||||
}
|
||||
|
||||
name = (uint8_t *)&ndlp->nlp_portname;
|
||||
phba = ndlp->nlp_phba;
|
||||
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
|
||||
if (ndlp->nlp_sid != NLP_NO_SID) {
|
||||
warn_on = 1;
|
||||
@ -85,11 +120,14 @@ lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
|
||||
lpfc_sli_abort_iocb(phba, &phba->sli.ring[phba->sli.fcp_ring],
|
||||
ndlp->nlp_sid, 0, 0, LPFC_CTX_TGT);
|
||||
}
|
||||
if (phba->fc_flag & FC_UNLOADING)
|
||||
warn_on = 0;
|
||||
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
|
||||
if (warn_on) {
|
||||
lpfc_printf_log(phba, KERN_ERR, LOG_DISCOVERY,
|
||||
"%d:0203 Nodev timeout on "
|
||||
"%d:0203 Devloss timeout on "
|
||||
"WWPN %x:%x:%x:%x:%x:%x:%x:%x "
|
||||
"NPort x%x Data: x%x x%x x%x\n",
|
||||
phba->brd_no,
|
||||
@ -99,7 +137,7 @@ lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
|
||||
ndlp->nlp_state, ndlp->nlp_rpi);
|
||||
} else {
|
||||
lpfc_printf_log(phba, KERN_INFO, LOG_DISCOVERY,
|
||||
"%d:0204 Nodev timeout on "
|
||||
"%d:0204 Devloss timeout on "
|
||||
"WWPN %x:%x:%x:%x:%x:%x:%x:%x "
|
||||
"NPort x%x Data: x%x x%x x%x\n",
|
||||
phba->brd_no,
|
||||
@ -109,7 +147,12 @@ lpfc_process_nodev_timeout(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp)
|
||||
ndlp->nlp_state, ndlp->nlp_rpi);
|
||||
}
|
||||
|
||||
lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM);
|
||||
ndlp->rport = NULL;
|
||||
rdata->pnode = NULL;
|
||||
|
||||
if (!(phba->fc_flag & FC_UNLOADING))
|
||||
lpfc_disc_state_machine(phba, ndlp, NULL, NLP_EVT_DEVICE_RM);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -127,11 +170,6 @@ lpfc_work_list_done(struct lpfc_hba * phba)
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
free_evt = 1;
|
||||
switch (evtp->evt) {
|
||||
case LPFC_EVT_NODEV_TMO:
|
||||
ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
|
||||
lpfc_process_nodev_timeout(phba, ndlp);
|
||||
free_evt = 0;
|
||||
break;
|
||||
case LPFC_EVT_ELS_RETRY:
|
||||
ndlp = (struct lpfc_nodelist *)(evtp->evt_arg1);
|
||||
lpfc_els_retry_delay_handler(ndlp);
|
||||
@ -340,6 +378,9 @@ lpfc_linkdown(struct lpfc_hba * phba)
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
}
|
||||
|
||||
fc_host_post_event(phba->host, fc_get_event_number(),
|
||||
FCH_EVT_LINKDOWN, 0);
|
||||
|
||||
/* Clean up any firmware default rpi's */
|
||||
if ((mb = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL))) {
|
||||
lpfc_unreg_did(phba, 0xffffffff, mb);
|
||||
@ -374,16 +415,6 @@ lpfc_linkdown(struct lpfc_hba * phba)
|
||||
rc = lpfc_disc_state_machine(phba, ndlp, NULL,
|
||||
NLP_EVT_DEVICE_RECOVERY);
|
||||
|
||||
/* Check config parameter use-adisc or FCP-2 */
|
||||
if ((rc != NLP_STE_FREED_NODE) &&
|
||||
(phba->cfg_use_adisc == 0) &&
|
||||
!(ndlp->nlp_fcp_info & NLP_FCP_2_DEVICE)) {
|
||||
/* We know we will have to relogin, so
|
||||
* unreglogin the rpi right now to fail
|
||||
* any outstanding I/Os quickly.
|
||||
*/
|
||||
lpfc_unreg_rpi(phba, ndlp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -427,6 +458,9 @@ lpfc_linkup(struct lpfc_hba * phba)
|
||||
struct list_head *listp, *node_list[7];
|
||||
int i;
|
||||
|
||||
fc_host_post_event(phba->host, fc_get_event_number(),
|
||||
FCH_EVT_LINKUP, 0);
|
||||
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
phba->hba_state = LPFC_LINK_UP;
|
||||
phba->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY |
|
||||
@ -638,6 +672,8 @@ lpfc_mbx_cmpl_read_sparam(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmb)
|
||||
|
||||
memcpy((uint8_t *) & phba->fc_sparam, (uint8_t *) mp->virt,
|
||||
sizeof (struct serv_parm));
|
||||
if (phba->cfg_soft_wwpn)
|
||||
u64_to_wwn(phba->cfg_soft_wwpn, phba->fc_sparam.portName.u.wwn);
|
||||
memcpy((uint8_t *) & phba->fc_nodename,
|
||||
(uint8_t *) & phba->fc_sparam.nodeName,
|
||||
sizeof (struct lpfc_name));
|
||||
@ -1098,8 +1134,11 @@ lpfc_unregister_remote_port(struct lpfc_hba * phba,
|
||||
struct fc_rport *rport = ndlp->rport;
|
||||
struct lpfc_rport_data *rdata = rport->dd_data;
|
||||
|
||||
ndlp->rport = NULL;
|
||||
rdata->pnode = NULL;
|
||||
if (rport->scsi_target_id == -1) {
|
||||
ndlp->rport = NULL;
|
||||
rdata->pnode = NULL;
|
||||
}
|
||||
|
||||
fc_remote_port_delete(rport);
|
||||
|
||||
return;
|
||||
@ -1227,17 +1266,6 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list)
|
||||
list_add_tail(&nlp->nlp_listp, &phba->fc_nlpunmap_list);
|
||||
phba->fc_unmap_cnt++;
|
||||
phba->nport_event_cnt++;
|
||||
/* stop nodev tmo if running */
|
||||
if (nlp->nlp_flag & NLP_NODEV_TMO) {
|
||||
nlp->nlp_flag &= ~NLP_NODEV_TMO;
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
del_timer_sync(&nlp->nlp_tmofunc);
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
if (!list_empty(&nlp->nodev_timeout_evt.evt_listp))
|
||||
list_del_init(&nlp->nodev_timeout_evt.
|
||||
evt_listp);
|
||||
|
||||
}
|
||||
nlp->nlp_flag &= ~NLP_NODEV_REMOVE;
|
||||
nlp->nlp_type |= NLP_FC_NODE;
|
||||
break;
|
||||
@ -1248,17 +1276,6 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list)
|
||||
list_add_tail(&nlp->nlp_listp, &phba->fc_nlpmap_list);
|
||||
phba->fc_map_cnt++;
|
||||
phba->nport_event_cnt++;
|
||||
/* stop nodev tmo if running */
|
||||
if (nlp->nlp_flag & NLP_NODEV_TMO) {
|
||||
nlp->nlp_flag &= ~NLP_NODEV_TMO;
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
del_timer_sync(&nlp->nlp_tmofunc);
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
if (!list_empty(&nlp->nodev_timeout_evt.evt_listp))
|
||||
list_del_init(&nlp->nodev_timeout_evt.
|
||||
evt_listp);
|
||||
|
||||
}
|
||||
nlp->nlp_flag &= ~NLP_NODEV_REMOVE;
|
||||
break;
|
||||
case NLP_NPR_LIST:
|
||||
@ -1267,11 +1284,6 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list)
|
||||
list_add_tail(&nlp->nlp_listp, &phba->fc_npr_list);
|
||||
phba->fc_npr_cnt++;
|
||||
|
||||
if (!(nlp->nlp_flag & NLP_NODEV_TMO))
|
||||
mod_timer(&nlp->nlp_tmofunc,
|
||||
jiffies + HZ * phba->cfg_nodev_tmo);
|
||||
|
||||
nlp->nlp_flag |= NLP_NODEV_TMO;
|
||||
nlp->nlp_flag &= ~NLP_RCV_PLOGI;
|
||||
break;
|
||||
case NLP_JUST_DQ:
|
||||
@ -1301,7 +1313,8 @@ lpfc_nlp_list(struct lpfc_hba * phba, struct lpfc_nodelist * nlp, int list)
|
||||
* already. If we have, and it's a scsi entity, be
|
||||
* sure to unblock any attached scsi devices
|
||||
*/
|
||||
if (!nlp->rport)
|
||||
if ((!nlp->rport) || (nlp->rport->port_state ==
|
||||
FC_PORTSTATE_BLOCKED))
|
||||
lpfc_register_remote_port(phba, nlp);
|
||||
|
||||
/*
|
||||
@ -1575,15 +1588,12 @@ lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
|
||||
|
||||
lpfc_els_abort(phba,ndlp,0);
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
ndlp->nlp_flag &= ~(NLP_NODEV_TMO|NLP_DELAY_TMO);
|
||||
ndlp->nlp_flag &= ~NLP_DELAY_TMO;
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
del_timer_sync(&ndlp->nlp_tmofunc);
|
||||
|
||||
ndlp->nlp_last_elscmd = 0;
|
||||
del_timer_sync(&ndlp->nlp_delayfunc);
|
||||
|
||||
if (!list_empty(&ndlp->nodev_timeout_evt.evt_listp))
|
||||
list_del_init(&ndlp->nodev_timeout_evt.evt_listp);
|
||||
if (!list_empty(&ndlp->els_retry_evt.evt_listp))
|
||||
list_del_init(&ndlp->els_retry_evt.evt_listp);
|
||||
|
||||
@ -1600,16 +1610,6 @@ lpfc_freenode(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
|
||||
int
|
||||
lpfc_nlp_remove(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp)
|
||||
{
|
||||
if (ndlp->nlp_flag & NLP_NODEV_TMO) {
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
ndlp->nlp_flag &= ~NLP_NODEV_TMO;
|
||||
spin_unlock_irq(phba->host->host_lock);
|
||||
del_timer_sync(&ndlp->nlp_tmofunc);
|
||||
if (!list_empty(&ndlp->nodev_timeout_evt.evt_listp))
|
||||
list_del_init(&ndlp->nodev_timeout_evt.evt_listp);
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (ndlp->nlp_flag & NLP_DELAY_TMO) {
|
||||
lpfc_cancel_retry_delay_tmo(phba, ndlp);
|
||||
@ -2424,34 +2424,6 @@ lpfc_disc_timeout_handler(struct lpfc_hba *phba)
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
lpfc_nodev_timeout(unsigned long ptr)
|
||||
{
|
||||
struct lpfc_hba *phba;
|
||||
struct lpfc_nodelist *ndlp;
|
||||
unsigned long iflag;
|
||||
struct lpfc_work_evt *evtp;
|
||||
|
||||
ndlp = (struct lpfc_nodelist *)ptr;
|
||||
phba = ndlp->nlp_phba;
|
||||
evtp = &ndlp->nodev_timeout_evt;
|
||||
spin_lock_irqsave(phba->host->host_lock, iflag);
|
||||
|
||||
if (!list_empty(&evtp->evt_listp)) {
|
||||
spin_unlock_irqrestore(phba->host->host_lock, iflag);
|
||||
return;
|
||||
}
|
||||
evtp->evt_arg1 = ndlp;
|
||||
evtp->evt = LPFC_EVT_NODEV_TMO;
|
||||
list_add_tail(&evtp->evt_listp, &phba->work_list);
|
||||
if (phba->work_wait)
|
||||
wake_up(phba->work_wait);
|
||||
|
||||
spin_unlock_irqrestore(phba->host->host_lock, iflag);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This routine handles processing a NameServer REG_LOGIN mailbox
|
||||
* command upon completion. It is setup in the LPFC_MBOXQ
|
||||
@ -2575,11 +2547,7 @@ lpfc_nlp_init(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp,
|
||||
uint32_t did)
|
||||
{
|
||||
memset(ndlp, 0, sizeof (struct lpfc_nodelist));
|
||||
INIT_LIST_HEAD(&ndlp->nodev_timeout_evt.evt_listp);
|
||||
INIT_LIST_HEAD(&ndlp->els_retry_evt.evt_listp);
|
||||
init_timer(&ndlp->nlp_tmofunc);
|
||||
ndlp->nlp_tmofunc.function = lpfc_nodev_timeout;
|
||||
ndlp->nlp_tmofunc.data = (unsigned long)ndlp;
|
||||
init_timer(&ndlp->nlp_delayfunc);
|
||||
ndlp->nlp_delayfunc.function = lpfc_els_retry_delay;
|
||||
ndlp->nlp_delayfunc.data = (unsigned long)ndlp;
|
||||
|
@ -268,6 +268,8 @@ lpfc_config_port_post(struct lpfc_hba * phba)
|
||||
kfree(mp);
|
||||
pmb->context1 = NULL;
|
||||
|
||||
if (phba->cfg_soft_wwpn)
|
||||
u64_to_wwn(phba->cfg_soft_wwpn, phba->fc_sparam.portName.u.wwn);
|
||||
memcpy(&phba->fc_nodename, &phba->fc_sparam.nodeName,
|
||||
sizeof (struct lpfc_name));
|
||||
memcpy(&phba->fc_portname, &phba->fc_sparam.portName,
|
||||
@ -511,6 +513,7 @@ lpfc_handle_eratt(struct lpfc_hba * phba)
|
||||
{
|
||||
struct lpfc_sli *psli = &phba->sli;
|
||||
struct lpfc_sli_ring *pring;
|
||||
uint32_t event_data;
|
||||
|
||||
if (phba->work_hs & HS_FFER6) {
|
||||
/* Re-establishing Link */
|
||||
@ -555,6 +558,11 @@ lpfc_handle_eratt(struct lpfc_hba * phba)
|
||||
phba->brd_no, phba->work_hs,
|
||||
phba->work_status[0], phba->work_status[1]);
|
||||
|
||||
event_data = FC_REG_DUMP_EVENT;
|
||||
fc_host_post_vendor_event(phba->host, fc_get_event_number(),
|
||||
sizeof(event_data), (char *) &event_data,
|
||||
SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_EMULEX);
|
||||
|
||||
psli->sli_flag &= ~LPFC_SLI2_ACTIVE;
|
||||
lpfc_offline(phba);
|
||||
phba->hba_state = LPFC_HBA_ERROR;
|
||||
|
@ -1813,7 +1813,7 @@ lpfc_device_recov_npr_node(struct lpfc_hba * phba,
|
||||
*/
|
||||
/*
|
||||
* For a Link Down, all nodes on the ADISC, PLOGI, unmapped or mapped
|
||||
* lists will receive a DEVICE_RECOVERY event. If the linkdown or nodev timers
|
||||
* lists will receive a DEVICE_RECOVERY event. If the linkdown or devloss timers
|
||||
* expire, all effected nodes will receive a DEVICE_RM event.
|
||||
*/
|
||||
/*
|
||||
|
@ -935,7 +935,7 @@ lpfc_abort_handler(struct scsi_cmnd *cmnd)
|
||||
schedule_timeout_uninterruptible(LPFC_ABORT_WAIT*HZ);
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
if (++loop_count
|
||||
> (2 * phba->cfg_nodev_tmo)/LPFC_ABORT_WAIT)
|
||||
> (2 * phba->cfg_devloss_tmo)/LPFC_ABORT_WAIT)
|
||||
break;
|
||||
}
|
||||
|
||||
@ -978,7 +978,7 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd)
|
||||
spin_lock_irq(shost->host_lock);
|
||||
/*
|
||||
* If target is not in a MAPPED state, delay the reset until
|
||||
* target is rediscovered or nodev timeout expires.
|
||||
* target is rediscovered or devloss timeout expires.
|
||||
*/
|
||||
while ( 1 ) {
|
||||
if (!pnode)
|
||||
@ -1050,7 +1050,7 @@ lpfc_reset_lun_handler(struct scsi_cmnd *cmnd)
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
|
||||
if (++loopcnt
|
||||
> (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT)
|
||||
> (2 * phba->cfg_devloss_tmo)/LPFC_RESET_WAIT)
|
||||
break;
|
||||
|
||||
cnt = lpfc_sli_sum_iocb(phba,
|
||||
@ -1151,7 +1151,7 @@ lpfc_reset_bus_handler(struct scsi_cmnd *cmnd)
|
||||
spin_lock_irq(phba->host->host_lock);
|
||||
|
||||
if (++loopcnt
|
||||
> (2 * phba->cfg_nodev_tmo)/LPFC_RESET_WAIT)
|
||||
> (2 * phba->cfg_devloss_tmo)/LPFC_RESET_WAIT)
|
||||
break;
|
||||
|
||||
cnt = lpfc_sli_sum_iocb(phba,
|
||||
@ -1249,7 +1249,7 @@ lpfc_slave_configure(struct scsi_device *sdev)
|
||||
* target pointer is stored in the starget_data for the
|
||||
* driver's sysfs entry point functions.
|
||||
*/
|
||||
rport->dev_loss_tmo = phba->cfg_nodev_tmo + 5;
|
||||
rport->dev_loss_tmo = phba->cfg_devloss_tmo;
|
||||
|
||||
if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
|
||||
lpfc_sli_poll_fcp_ring(phba);
|
||||
|
@ -18,7 +18,7 @@
|
||||
* included with this package. *
|
||||
*******************************************************************/
|
||||
|
||||
#define LPFC_DRIVER_VERSION "8.1.9"
|
||||
#define LPFC_DRIVER_VERSION "8.1.10"
|
||||
|
||||
#define LPFC_DRIVER_NAME "lpfc"
|
||||
|
||||
|
@ -2822,9 +2822,7 @@ mega_print_inquiry(char *page, char *scsi_inq)
|
||||
|
||||
i = scsi_inq[0] & 0x1f;
|
||||
|
||||
len += sprintf(page+len, " Type: %s ",
|
||||
i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] :
|
||||
"Unknown ");
|
||||
len += sprintf(page+len, " Type: %s ", scsi_device_type(i));
|
||||
|
||||
len += sprintf(page+len,
|
||||
" ANSI SCSI revision: %02x", scsi_inq[2] & 0x07);
|
||||
@ -3658,8 +3656,9 @@ megadev_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
|
||||
* Send the request sense data also, irrespective of
|
||||
* whether the user has asked for it or not.
|
||||
*/
|
||||
copy_to_user(upthru->reqsensearea,
|
||||
pthru->reqsensearea, 14);
|
||||
if (copy_to_user(upthru->reqsensearea,
|
||||
pthru->reqsensearea, 14))
|
||||
rval = -EFAULT;
|
||||
|
||||
freemem_and_return:
|
||||
if( pthru->dataxferlen ) {
|
||||
|
@ -330,6 +330,21 @@ static struct device_attribute *megaraid_sdev_attrs[] = {
|
||||
NULL,
|
||||
};
|
||||
|
||||
/**
|
||||
* megaraid_change_queue_depth - Change the device's queue depth
|
||||
* @sdev: scsi device struct
|
||||
* @qdepth: depth to set
|
||||
*
|
||||
* Return value:
|
||||
* actual depth set
|
||||
**/
|
||||
static int megaraid_change_queue_depth(struct scsi_device *sdev, int qdepth)
|
||||
{
|
||||
if (qdepth > MBOX_MAX_SCSI_CMDS)
|
||||
qdepth = MBOX_MAX_SCSI_CMDS;
|
||||
scsi_adjust_queue_depth(sdev, 0, qdepth);
|
||||
return sdev->queue_depth;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scsi host template for megaraid unified driver
|
||||
@ -343,6 +358,7 @@ static struct scsi_host_template megaraid_template_g = {
|
||||
.eh_device_reset_handler = megaraid_reset_handler,
|
||||
.eh_bus_reset_handler = megaraid_reset_handler,
|
||||
.eh_host_reset_handler = megaraid_reset_handler,
|
||||
.change_queue_depth = megaraid_change_queue_depth,
|
||||
.use_clustering = ENABLE_CLUSTERING,
|
||||
.sdev_attrs = megaraid_sdev_attrs,
|
||||
.shost_attrs = megaraid_shost_attrs,
|
||||
|
@ -53,31 +53,15 @@ MODULE_DESCRIPTION("LSI Logic MegaRAID SAS Driver");
|
||||
*/
|
||||
static struct pci_device_id megasas_pci_table[] = {
|
||||
|
||||
{
|
||||
PCI_VENDOR_ID_LSI_LOGIC,
|
||||
PCI_DEVICE_ID_LSI_SAS1064R, /* xscale IOP */
|
||||
PCI_ANY_ID,
|
||||
PCI_ANY_ID,
|
||||
},
|
||||
{
|
||||
PCI_VENDOR_ID_LSI_LOGIC,
|
||||
PCI_DEVICE_ID_LSI_SAS1078R, /* ppc IOP */
|
||||
PCI_ANY_ID,
|
||||
PCI_ANY_ID,
|
||||
},
|
||||
{
|
||||
PCI_VENDOR_ID_LSI_LOGIC,
|
||||
PCI_DEVICE_ID_LSI_VERDE_ZCR, /* xscale IOP, vega */
|
||||
PCI_ANY_ID,
|
||||
PCI_ANY_ID,
|
||||
},
|
||||
{
|
||||
PCI_VENDOR_ID_DELL,
|
||||
PCI_DEVICE_ID_DELL_PERC5, /* xscale IOP */
|
||||
PCI_ANY_ID,
|
||||
PCI_ANY_ID,
|
||||
},
|
||||
{0} /* Terminating entry */
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1064R)},
|
||||
/* xscale IOP */
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_SAS1078R)},
|
||||
/* ppc IOP */
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_LSI_LOGIC, PCI_DEVICE_ID_LSI_VERDE_ZCR)},
|
||||
/* xscale IOP, vega */
|
||||
{PCI_DEVICE(PCI_VENDOR_ID_DELL, PCI_DEVICE_ID_DELL_PERC5)},
|
||||
/* xscale IOP */
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, megasas_pci_table);
|
||||
@ -2854,7 +2838,7 @@ static int __init megasas_init(void)
|
||||
/*
|
||||
* Register ourselves as PCI hotplug module
|
||||
*/
|
||||
rval = pci_module_init(&megasas_pci_driver);
|
||||
rval = pci_register_driver(&megasas_pci_driver);
|
||||
|
||||
if (rval) {
|
||||
printk(KERN_DEBUG "megasas: PCI hotplug regisration failed \n");
|
||||
|
@ -29,7 +29,7 @@ static irqreturn_t mvme147_intr (int irq, void *dummy, 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 char flags = 0x01;
|
||||
unsigned long addr = virt_to_bus(cmd->SCp.ptr);
|
||||
@ -57,7 +57,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)
|
||||
{
|
||||
m147_pcc->dma_cntrl = 0;
|
||||
@ -112,7 +112,7 @@ int mvme147_detect(struct scsi_host_template *tpnt)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvme147_bus_reset(Scsi_Cmnd *cmd)
|
||||
static int mvme147_bus_reset(struct scsi_cmnd *cmd)
|
||||
{
|
||||
/* FIXME perform bus-specific reset */
|
||||
|
||||
|
@ -12,10 +12,6 @@
|
||||
|
||||
int mvme147_detect(struct scsi_host_template *);
|
||||
int mvme147_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
|
||||
|
@ -96,24 +96,40 @@ unsigned int scsi_logging_level;
|
||||
EXPORT_SYMBOL(scsi_logging_level);
|
||||
#endif
|
||||
|
||||
const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = {
|
||||
"Direct-Access ",
|
||||
"Sequential-Access",
|
||||
static const char *const scsi_device_types[] = {
|
||||
"Direct access ",
|
||||
"Sequential access",
|
||||
"Printer ",
|
||||
"Processor ",
|
||||
"WORM ",
|
||||
"CD-ROM ",
|
||||
"CD/DVD ",
|
||||
"Scanner ",
|
||||
"Optical Device ",
|
||||
"Medium Changer ",
|
||||
"Optical memory ",
|
||||
"Media changer ",
|
||||
"Communications ",
|
||||
"Unknown ",
|
||||
"Unknown ",
|
||||
"ASC IT8 ",
|
||||
"ASC IT8 ",
|
||||
"RAID ",
|
||||
"Enclosure ",
|
||||
"Direct-Access-RBC",
|
||||
"Direct access RBC",
|
||||
"Optical card ",
|
||||
"Bridge controller",
|
||||
"Object storage ",
|
||||
"Automation/Drive ",
|
||||
};
|
||||
EXPORT_SYMBOL(scsi_device_types);
|
||||
|
||||
const char * scsi_device_type(unsigned type)
|
||||
{
|
||||
if (type == 0x1e)
|
||||
return "Well-known LUN ";
|
||||
if (type == 0x1f)
|
||||
return "No Device ";
|
||||
if (type > ARRAY_SIZE(scsi_device_types))
|
||||
return "Unknown ";
|
||||
return scsi_device_types[type];
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(scsi_device_type);
|
||||
|
||||
struct scsi_host_cmd_pool {
|
||||
kmem_cache_t *slab;
|
||||
@ -835,14 +851,14 @@ EXPORT_SYMBOL(scsi_track_queue_full);
|
||||
*/
|
||||
int scsi_device_get(struct scsi_device *sdev)
|
||||
{
|
||||
if (sdev->sdev_state == SDEV_DEL || sdev->sdev_state == SDEV_CANCEL)
|
||||
if (sdev->sdev_state == SDEV_DEL)
|
||||
return -ENXIO;
|
||||
if (!get_device(&sdev->sdev_gendev))
|
||||
return -ENXIO;
|
||||
if (!try_module_get(sdev->host->hostt->module)) {
|
||||
put_device(&sdev->sdev_gendev);
|
||||
return -ENXIO;
|
||||
}
|
||||
/* We can fail this if we're doing SCSI operations
|
||||
* from module exit (like cache flush) */
|
||||
try_module_get(sdev->host->hostt->module);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_device_get);
|
||||
@ -857,7 +873,14 @@ EXPORT_SYMBOL(scsi_device_get);
|
||||
*/
|
||||
void scsi_device_put(struct scsi_device *sdev)
|
||||
{
|
||||
module_put(sdev->host->hostt->module);
|
||||
struct module *module = sdev->host->hostt->module;
|
||||
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
/* The module refcount will be zero if scsi_device_get()
|
||||
* was called from a module removal routine */
|
||||
if (module && module_refcount(module) != 0)
|
||||
module_put(module);
|
||||
#endif
|
||||
put_device(&sdev->sdev_gendev);
|
||||
}
|
||||
EXPORT_SYMBOL(scsi_device_put);
|
||||
@ -1099,6 +1122,8 @@ static int __init init_scsi(void)
|
||||
for_each_possible_cpu(i)
|
||||
INIT_LIST_HEAD(&per_cpu(scsi_done_q, i));
|
||||
|
||||
scsi_netlink_init();
|
||||
|
||||
printk(KERN_NOTICE "SCSI subsystem initialized\n");
|
||||
return 0;
|
||||
|
||||
@ -1119,6 +1144,7 @@ cleanup_queue:
|
||||
|
||||
static void __exit exit_scsi(void)
|
||||
{
|
||||
scsi_netlink_exit();
|
||||
scsi_sysfs_unregister();
|
||||
scsi_exit_sysctl();
|
||||
scsi_exit_hosts();
|
||||
|
@ -20,8 +20,6 @@
|
||||
#ifndef _SCSI_H
|
||||
#define _SCSI_H
|
||||
|
||||
#include <linux/config.h> /* for CONFIG_SCSI_LOGGING */
|
||||
|
||||
#include <scsi/scsi_cmnd.h>
|
||||
#include <scsi/scsi_device.h>
|
||||
#include <scsi/scsi_eh.h>
|
||||
|
@ -551,7 +551,15 @@ static void scsi_run_queue(struct request_queue *q)
|
||||
list_del_init(&sdev->starved_entry);
|
||||
spin_unlock_irqrestore(shost->host_lock, flags);
|
||||
|
||||
blk_run_queue(sdev->request_queue);
|
||||
|
||||
if (test_bit(QUEUE_FLAG_REENTER, &q->queue_flags) &&
|
||||
!test_and_set_bit(QUEUE_FLAG_REENTER,
|
||||
&sdev->request_queue->queue_flags)) {
|
||||
blk_run_queue(sdev->request_queue);
|
||||
clear_bit(QUEUE_FLAG_REENTER,
|
||||
&sdev->request_queue->queue_flags);
|
||||
} else
|
||||
blk_run_queue(sdev->request_queue);
|
||||
|
||||
spin_lock_irqsave(shost->host_lock, flags);
|
||||
if (unlikely(!list_empty(&sdev->starved_entry)))
|
||||
|
199
drivers/scsi/scsi_netlink.c
Normal file
199
drivers/scsi/scsi_netlink.c
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* scsi_netlink.c - SCSI Transport Netlink Interface
|
||||
*
|
||||
* Copyright (C) 2006 James Smart, Emulex Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/time.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/security.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/netlink.h>
|
||||
|
||||
#include <scsi/scsi_netlink.h>
|
||||
#include "scsi_priv.h"
|
||||
|
||||
struct sock *scsi_nl_sock = NULL;
|
||||
EXPORT_SYMBOL_GPL(scsi_nl_sock);
|
||||
|
||||
|
||||
/**
|
||||
* scsi_nl_rcv_msg -
|
||||
* Receive message handler. Extracts message from a receive buffer.
|
||||
* Validates message header and calls appropriate transport message handler
|
||||
*
|
||||
* @skb: socket receive buffer
|
||||
*
|
||||
**/
|
||||
static void
|
||||
scsi_nl_rcv_msg(struct sk_buff *skb)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct scsi_nl_hdr *hdr;
|
||||
uint32_t rlen;
|
||||
int err;
|
||||
|
||||
while (skb->len >= NLMSG_SPACE(0)) {
|
||||
err = 0;
|
||||
|
||||
nlh = (struct nlmsghdr *) skb->data;
|
||||
if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) ||
|
||||
(skb->len < nlh->nlmsg_len)) {
|
||||
printk(KERN_WARNING "%s: discarding partial skb\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
rlen = NLMSG_ALIGN(nlh->nlmsg_len);
|
||||
if (rlen > skb->len)
|
||||
rlen = skb->len;
|
||||
|
||||
if (nlh->nlmsg_type != SCSI_TRANSPORT_MSG) {
|
||||
err = -EBADMSG;
|
||||
goto next_msg;
|
||||
}
|
||||
|
||||
hdr = NLMSG_DATA(nlh);
|
||||
if ((hdr->version != SCSI_NL_VERSION) ||
|
||||
(hdr->magic != SCSI_NL_MAGIC)) {
|
||||
err = -EPROTOTYPE;
|
||||
goto next_msg;
|
||||
}
|
||||
|
||||
if (security_netlink_recv(skb, CAP_SYS_ADMIN)) {
|
||||
err = -EPERM;
|
||||
goto next_msg;
|
||||
}
|
||||
|
||||
if (nlh->nlmsg_len < (sizeof(*nlh) + hdr->msglen)) {
|
||||
printk(KERN_WARNING "%s: discarding partial message\n",
|
||||
__FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* We currently don't support anyone sending us a message
|
||||
*/
|
||||
|
||||
next_msg:
|
||||
if ((err) || (nlh->nlmsg_flags & NLM_F_ACK))
|
||||
netlink_ack(skb, nlh, err);
|
||||
|
||||
skb_pull(skb, rlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* scsi_nl_rcv_msg -
|
||||
* Receive handler for a socket. Extracts a received message buffer from
|
||||
* the socket, and starts message processing.
|
||||
*
|
||||
* @sk: socket
|
||||
* @len: unused
|
||||
*
|
||||
**/
|
||||
static void
|
||||
scsi_nl_rcv(struct sock *sk, int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
|
||||
scsi_nl_rcv_msg(skb);
|
||||
kfree_skb(skb);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* scsi_nl_rcv_event -
|
||||
* Event handler for a netlink socket.
|
||||
*
|
||||
* @this: event notifier block
|
||||
* @event: event type
|
||||
* @ptr: event payload
|
||||
*
|
||||
**/
|
||||
static int
|
||||
scsi_nl_rcv_event(struct notifier_block *this, unsigned long event, void *ptr)
|
||||
{
|
||||
struct netlink_notify *n = ptr;
|
||||
|
||||
if (n->protocol != NETLINK_SCSITRANSPORT)
|
||||
return NOTIFY_DONE;
|
||||
|
||||
/*
|
||||
* Currently, we are not tracking PID's, etc. There is nothing
|
||||
* to handle.
|
||||
*/
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block scsi_netlink_notifier = {
|
||||
.notifier_call = scsi_nl_rcv_event,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* scsi_netlink_init -
|
||||
* Called by SCSI subsystem to intialize the SCSI transport netlink
|
||||
* interface
|
||||
*
|
||||
**/
|
||||
void
|
||||
scsi_netlink_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = netlink_register_notifier(&scsi_netlink_notifier);
|
||||
if (error) {
|
||||
printk(KERN_ERR "%s: register of event handler failed - %d\n",
|
||||
__FUNCTION__, error);
|
||||
return;
|
||||
}
|
||||
|
||||
scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT,
|
||||
SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE);
|
||||
if (!scsi_nl_sock) {
|
||||
printk(KERN_ERR "%s: register of recieve handler failed\n",
|
||||
__FUNCTION__);
|
||||
netlink_unregister_notifier(&scsi_netlink_notifier);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* scsi_netlink_exit -
|
||||
* Called by SCSI subsystem to disable the SCSI transport netlink
|
||||
* interface
|
||||
*
|
||||
**/
|
||||
void
|
||||
scsi_netlink_exit(void)
|
||||
{
|
||||
if (scsi_nl_sock) {
|
||||
sock_release(scsi_nl_sock->sk_socket);
|
||||
netlink_unregister_notifier(&scsi_netlink_notifier);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ struct scsi_cmnd;
|
||||
struct scsi_device;
|
||||
struct scsi_host_template;
|
||||
struct Scsi_Host;
|
||||
struct scsi_nl_hdr;
|
||||
|
||||
|
||||
/*
|
||||
@ -110,6 +111,16 @@ extern void __scsi_remove_device(struct scsi_device *);
|
||||
|
||||
extern struct bus_type scsi_bus_type;
|
||||
|
||||
/* scsi_netlink.c */
|
||||
#ifdef CONFIG_SCSI_NETLINK
|
||||
extern void scsi_netlink_init(void);
|
||||
extern void scsi_netlink_exit(void);
|
||||
extern struct sock *scsi_nl_sock;
|
||||
#else
|
||||
static inline void scsi_netlink_init(void) {}
|
||||
static inline void scsi_netlink_exit(void) {}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* internal scsi timeout functions: for use by mid-layer and transport
|
||||
* classes.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user