diff options
Diffstat (limited to 'drivers/scsi')
102 files changed, 51218 insertions, 0 deletions
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 4a1f029c4fe9..3aa664fa892e 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -830,6 +830,40 @@ config SCSI_GDTH To compile this driver as a module, choose M here: the module will be called gdth. +config SCSI_ISCI + tristate "Intel(R) C600 Series Chipset SAS Controller" + depends on PCI && SCSI + # little endian host assumptions + depends on X86 + # (temporary): dma api misuse + depends on !DMAR + # (temporary): known alpha quality driver + depends on EXPERIMENTAL + select SCSI_SAS_LIBSAS + ---help--- + This driver supports the 6Gb/s SAS capabilities of the storage + control unit found in the Intel(R) C600 series chipset. + + The experimental tag will be removed after the driver exits alpha + +choice + prompt "Default Silicon Revision" + depends on SCSI_ISCI + default PBG_HBA_A2 + # temporary A-step silicon is pre-production + +config PBG_HBA_BETA + bool "B0" + +config PBG_HBA_A2 + bool "A2" + +config PBG_HBA_A0 + bool "A0" + +endchoice + + config SCSI_GENERIC_NCR5380 tristate "Generic NCR5380/53c400 SCSI PIO support" depends on ISA && SCSI diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 7ad0b8a79ae8..3c08f5352b2d 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -73,6 +73,7 @@ obj-$(CONFIG_SCSI_AACRAID) += aacraid/ obj-$(CONFIG_SCSI_AIC7XXX_OLD) += aic7xxx_old.o obj-$(CONFIG_SCSI_AIC94XX) += aic94xx/ obj-$(CONFIG_SCSI_PM8001) += pm8001/ +obj-$(CONFIG_SCSI_ISCI) += isci/ obj-$(CONFIG_SCSI_IPS) += ips.o obj-$(CONFIG_SCSI_FD_MCS) += fd_mcs.o obj-$(CONFIG_SCSI_FUTURE_DOMAIN)+= fdomain.o diff --git a/drivers/scsi/isci/Makefile b/drivers/scsi/isci/Makefile new file mode 100644 index 000000000000..34f7af3525f4 --- /dev/null +++ b/drivers/scsi/isci/Makefile @@ -0,0 +1,30 @@ +#TODO kill SCIC_SDS_4_ENABLED it is always true for this +#generation of silicon +EXTRA_CFLAGS += -DSCIC_SDS_4_ENABLED + +#temporary until atapi support ready +EXTRA_CFLAGS += -DDISABLE_ATAPI + +EXTRA_CFLAGS += -Idrivers/scsi/isci/core/ -Idrivers/scsi/isci/ +obj-$(CONFIG_SCSI_ISCI) += isci.o +isci-objs := init.o phy.o request.o sata.o \ + remote_device.o port.o timers.o deprecated.o \ + host.o task.o events.o \ + core/scic_sds_controller.o \ + core/scic_sds_remote_device.o \ + core/scic_sds_request.o \ + core/scic_sds_stp_request.o \ + core/scic_sds_stp_packet_request.o \ + core/scic_sds_stp_remote_device.o \ + core/scic_sds_port.o \ + core/scic_sds_port_configuration_agent.o \ + core/scic_sds_phy.o \ + core/scic_sds_ssp_request.o \ + core/scic_sds_remote_node_context.o \ + core/scic_sds_smp_request.o \ + core/scic_sds_smp_remote_device.o \ + core/scic_sds_remote_node_table.o \ + core/scic_sds_unsolicited_frame_control.o \ + core/sci_base_memory_descriptor_list.o \ + core/sci_base_state_machine.o \ + core/sci_util.o diff --git a/drivers/scsi/isci/core/intel_ata.h b/drivers/scsi/isci/core/intel_ata.h new file mode 100644 index 000000000000..48b297e50bd0 --- /dev/null +++ b/drivers/scsi/isci/core/intel_ata.h @@ -0,0 +1,554 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file defines all of the ATA related constants, enumerations, and types. + * Please note that this file does not necessarily contain an exhaustive + * list of all constants, commands, sub-commands, etc. + * + * + */ + +#ifndef _ATA_H_ +#define _ATA_H_ + +#include <linux/types.h> + +/** + * + * + * ATA_COMMAND_CODES These constants depict the various ATA command codes + * defined in the ATA/ATAPI specification. + */ +#define ATA_IDENTIFY_DEVICE 0xEC +#define ATA_CHECK_POWER_MODE 0xE5 +#define ATA_STANDBY 0xE2 +#define ATA_STANDBY_IMMED 0xE0 +#define ATA_IDLE_IMMED 0xE1 +#define ATA_IDLE 0xE3 +#define ATA_FLUSH_CACHE 0xE7 +#define ATA_FLUSH_CACHE_EXT 0xEA +#define ATA_READ_DMA_EXT 0x25 +#define ATA_READ_DMA 0xC8 +#define ATA_READ_SECTORS_EXT 0x24 +#define ATA_READ_SECTORS 0x20 +#define ATA_WRITE_DMA_EXT 0x35 +#define ATA_WRITE_DMA 0xCA +#define ATA_WRITE_SECTORS_EXT 0x34 +#define ATA_WRITE_SECTORS 0x30 +#define ATA_WRITE_UNCORRECTABLE 0x45 +#define ATA_READ_VERIFY_SECTORS 0x40 +#define ATA_READ_VERIFY_SECTORS_EXT 0x42 +#define ATA_READ_BUFFER 0xE4 +#define ATA_WRITE_BUFFER 0xE8 +#define ATA_EXECUTE_DEVICE_DIAG 0x90 +#define ATA_SET_FEATURES 0xEF +#define ATA_SMART 0xB0 +#define ATA_PACKET_IDENTIFY 0xA1 +#define ATA_PACKET 0xA0 +#define ATA_READ_FPDMA 0x60 +#define ATA_WRITE_FPDMA 0x61 +#define ATA_READ_LOG_EXT 0x2F +#define ATA_NOP 0x00 +#define ATA_DEVICE_RESET 0x08 +#define ATA_MEDIA_EJECT 0xED + +/** + * + * + * ATA_SMART_SUB_COMMAND_CODES These constants define the ATA SMART command + * sub-codes that can be executed. + */ +#define ATA_SMART_SUB_CMD_ENABLE 0xD8 +#define ATA_SMART_SUB_CMD_DISABLE 0xD9 +#define ATA_SMART_SUB_CMD_RETURN_STATUS 0xDA +#define ATA_SMART_SUB_CMD_READ_LOG 0xD5 + +/** + * + * + * ATA_SET_FEATURES_SUB_COMMAND_CODES These constants define the ATA SET + * FEATURES command sub-codes that can be executed. + */ +#define ATA_SET_FEATURES_SUB_CMD_ENABLE_CACHE 0x02 +#define ATA_SET_FEATURES_SUB_CMD_DISABLE_CACHE 0x82 +#define ATA_SET_FEATURES_SUB_CMD_DISABLE_READ_AHEAD 0x55 +#define ATA_SET_FEATURES_SUB_CMD_ENABLE_READ_AHEAD 0xAA +#define ATA_SET_FEATURES_SUB_CMD_SET_TRANSFER_MODE 0x3 + +/** + * + * + * ATA_READ_LOG_EXT_PAGE_CODES This is a list of log page codes available for + * use. + */ +#define ATA_LOG_PAGE_NCQ_ERROR 0x10 +#define ATA_LOG_PAGE_SMART_SELF_TEST 0x06 +#define ATA_LOG_PAGE_EXTENDED_SMART_SELF_TEST 0x07 + +/** + * + * + * ATA_LOG_PAGE_NCQ_ERROR_CONSTANTS These constants define standard values for + * use when requesting the NCQ error log page. + */ +#define ATA_LOG_PAGE_NCQ_ERROR_SECTOR 0 +#define ATA_LOG_PAGE_NCQ_ERROR_SECTOR_COUNT 1 + +/** + * + * + * ATA_STATUS_REGISTER_BITS The following are status register bit definitions + * per ATA/ATAPI-7. + */ +#define ATA_STATUS_REG_BSY_BIT 0x80 +#define ATA_STATUS_REG_DEVICE_FAULT_BIT 0x20 +#define ATA_STATUS_REG_ERROR_BIT 0x01 + +/** + * + * + * ATA_ERROR_REGISTER_BITS The following are error register bit definitions per + * ATA/ATAPI-7. + */ +#define ATA_ERROR_REG_NO_MEDIA_BIT 0x02 +#define ATA_ERROR_REG_ABORT_BIT 0x04 +#define ATA_ERROR_REG_MEDIA_CHANGE_REQUEST_BIT 0x08 +#define ATA_ERROR_REG_ID_NOT_FOUND_BIT 0x10 +#define ATA_ERROR_REG_MEDIA_CHANGE_BIT 0x20 +#define ATA_ERROR_REG_UNCORRECTABLE_BIT 0x40 +#define ATA_ERROR_REG_WRITE_PROTECTED_BIT 0x40 +#define ATA_ERROR_REG_ICRC_BIT 0x80 + +/** + * + * + * ATA_CONTROL_REGISTER_BITS The following are control register bit definitions + * per ATA/ATAPI-7 + */ +#define ATA_CONTROL_REG_INTERRUPT_ENABLE_BIT 0x02 +#define ATA_CONTROL_REG_SOFT_RESET_BIT 0x04 +#define ATA_CONTROL_REG_HIGH_ORDER_BYTE_BIT 0x80 + +/** + * + * + * ATA_DEVICE_HEAD_REGISTER_BITS The following are device/head register bit + * definitions per ATA/ATAPI-7. + */ +#define ATA_DEV_HEAD_REG_LBA_MODE_ENABLE 0x40 +#define ATA_DEV_HEAD_REG_FUA_ENABLE 0x80 + +/** + * + * + * ATA_IDENTIFY_DEVICE_FIELD_LENGTHS The following constants define the number + * of bytes contained in various fields found in the IDENTIFY DEVICE data + * structure. + */ +#define ATA_IDENTIFY_SERIAL_NUMBER_LEN 20 +#define ATA_IDENTIFY_MODEL_NUMBER_LEN 40 +#define ATA_IDENTIFY_FW_REVISION_LEN 8 +#define ATA_IDENTIFY_48_LBA_LEN 8 +#define ATA_IDENTIFY_MEDIA_SERIAL_NUMBER_LEN 30 +#define ATA_IDENTIFY_WWN_LEN 8 + +/** + * + * + * ATA_IDENTIFY_DEVICE_FIELD_MASKS The following constants define bit masks + * utilized to determine if a feature is supported/enabled or if a bit is + * simply set inside of the IDENTIFY DEVICE data structre. + */ +#define ATA_IDENTIFY_REMOVABLE_MEDIA_ENABLE 0x0080 +#define ATA_IDENTIFY_CAPABILITIES1_NORMAL_DMA_ENABLE 0x0100 +#define ATA_IDENTIFY_CAPABILITIES1_STANDBY_ENABLE 0x2000 +#define ATA_IDENTIFY_COMMAND_SET_SUPPORTED0_SMART_ENABLE 0x0001 +#define ATA_IDENTIFY_COMMAND_SET_SUPPORTED1_48BIT_ENABLE 0x0400 +#define ATA_IDENTIFY_COMMAND_SET_WWN_SUPPORT_ENABLE 0x0100 +#define ATA_IDENTIFY_COMMAND_SET_ENABLED0_SMART_ENABLE 0x0001 +#define ATA_IDENTIFY_SATA_CAPABILITIES_NCQ_ENABLE 0x0100 +#define ATA_IDENTIFY_NCQ_QUEUE_DEPTH_ENABLE 0x001F +#define ATA_IDENTIFY_SECTOR_LARGER_THEN_512_ENABLE 0x0100 +#define ATA_IDENTIFY_LOGICAL_SECTOR_PER_PHYSICAL_SECTOR_MASK 0x000F +#define ATA_IDENTIFY_LOGICAL_SECTOR_PER_PHYSICAL_SECTOR_ENABLE 0x2000 +#define ATA_IDENTIFY_WRITE_UNCORRECTABLE_SUPPORT 0x0004 +#define ATA_IDENTIFY_COMMAND_SET_SMART_SELF_TEST_SUPPORTED 0x0002 + +/** + * + * + * ATAPI_IDENTIFY_DEVICE_FIELD_MASKS These constants define the various bit + * definitions for the fields in the PACKET IDENTIFY DEVICE data structure. + */ +#define ATAPI_IDENTIFY_16BYTE_CMD_PCKT_ENABLE 0x01 + +/** + * + * + * ATA_PACKET_FEATURE_BITS These constants define the various bit definitions + * for the ATA PACKET feature register. + */ +#define ATA_PACKET_FEATURE_DMA 0x01 +#define ATA_PACKET_FEATURE_OVL 0x02 +#define ATA_PACKET_FEATURE_DMADIR 0x04 + +/** + * + * + * ATA_Device_Power_Mode_Values These constants define the power mode values + * returned by ATA_Check_Power_Mode + */ +#define ATA_STANDBY_POWER_MODE 0x00 +#define ATA_IDLE_POWER_MODE 0x80 +#define ATA_ACTIVE_POWER_MODE 0xFF + +/** + * + * + * ATA_WRITE_UNCORRECTIABLE feature field values These constants define the + * Write Uncorrectable feature values used with the SATI translation. + */ +#define ATA_WRITE_UNCORRECTABLE_PSUEDO 0x55 +#define ATA_WRITE_UNCORRECTABLE_FLAGGED 0xAA + + + +/** + * struct ATA_IDENTIFY_DEVICE - This structure depicts the ATA IDENTIFY DEVICE + * data format. + * + * + */ +struct ata_identify_device_data { + u16 general_config_bits; /* word 00 */ + u16 obsolete0; /* word 01 (num cylinders) */ + u16 vendor_specific_config_bits; /* word 02 */ + u16 obsolete1; /* word 03 (num heads) */ + u16 retired1[2]; /* words 04-05 */ + u16 obsolete2; /* word 06 (sectors / track) */ + u16 reserved_for_compact_flash1[2]; /* words 07-08 */ + u16 retired0; /* word 09 */ + u8 serial_number[ATA_IDENTIFY_SERIAL_NUMBER_LEN]; /* word 10-19 */ + u16 retired2[2]; /* words 20-21 */ + u16 obsolete4; /* word 22 */ + u8 firmware_revision[ATA_IDENTIFY_FW_REVISION_LEN]; /* words 23-26 */ + u8 model_number[ATA_IDENTIFY_MODEL_NUMBER_LEN]; /* words 27-46 */ + u16 max_sectors_per_multiple; /* word 47 */ + u16 reserved0; /* word 48 */ + u16 capabilities1; /* word 49 */ + u16 capabilities2; /* word 50 */ + u16 obsolete5[2]; /* words 51-52 */ + u16 validity_bits; /* word 53 */ + u16 obsolete6[5]; /* + * words 54-58 Used to be: + * current cylinders, + * current heads, + * current sectors/Track, + * current capacity */ + u16 current_max_sectors_per_multiple; /* word 59 */ + u8 total_num_sectors[4]; /* words 60-61 */ + u16 obsolete7; /* word 62 */ + u16 multi_word_dma_mode; /* word 63 */ + u16 pio_modes_supported; /* word 64 */ + u16 min_multiword_dma_transfer_cycle; /* word 65 */ + u16 rec_min_multiword_dma_transfer_cycle; /* word 66 */ + u16 min_pio_transfer_no_flow_ctrl; /* word 67 */ + u16 min_pio_transfer_with_flow_ctrl; /* word 68 */ + u16 reserved1[2]; /* words 69-70 */ + u16 reserved2[4]; /* words 71-74 */ + u16 queue_depth; /* word 75 */ + u16 serial_ata_capabilities; /* word 76 */ + u16 serial_ata_reserved; /* word 77 */ + u16 serial_ata_features_supported; /* word 78 */ + u16 serial_ata_features_enabled; /* word 79 */ + u16 major_version_number; /* word 80 */ + u16 minor_version_number; /* word 81 */ + u16 command_set_supported0; /* word 82 */ + u16 command_set_supported1; /* word 83 */ + u16 command_set_supported_extention; /* word 84 */ + u16 command_set_enabled0; /* word 85 */ + u16 command_set_enabled1; /* word 86 */ + u16 command_set_default; /* word 87 */ + u16 ultra_dma_mode; /* word 88 */ + u16 security_erase_completion_time; /* word 89 */ + u16 enhanced_security_erase_time; /* word 90 */ + u16 current_power_mgmt_value; /* word 91 */ + u16 master_password_revision; /* word 92 */ + u16 hardware_reset_result; /* word 93 */ + u16 current_acoustic_management_value; /* word 94 */ + u16 stream_min_request_size; /* word 95 */ + u16 stream_transfer_time; /* word 96 */ + u16 stream_access_latency; /* word 97 */ + u16 stream_performance_granularity[2]; /* words 98-99 */ + u8 max_48bit_lba[ATA_IDENTIFY_48_LBA_LEN]; /* words 100-103 */ + u16 streaming_transfer_time; /* word 104 */ + u16 reserved3; /* word 105 */ + u16 physical_logical_sector_info; /* word 106 */ + u16 acoustic_test_interseek_delay; /* word 107 */ + u8 world_wide_name[ATA_IDENTIFY_WWN_LEN]; /* words 108-111 */ + u8 reserved_for_wwn_extention[ATA_IDENTIFY_WWN_LEN]; /* words 112-115 */ + u16 reserved4; /* word 116 */ + u8 words_per_logical_sector[4]; /* words 117-118 */ + u16 command_set_supported2; /* word 119 */ + u16 reserved5[7]; /* words 120-126 */ + u16 removable_media_status; /* word 127 */ + u16 security_status; /* word 128 */ + u16 vendor_specific1[31]; /* words 129-159 */ + u16 cfa_power_mode1; /* word 160 */ + u16 reserved_for_compact_flash2[7]; /* words 161-167 */ + u16 device_nominal_form_factor; /* word 168 */ + u16 reserved_for_compact_flash3[7]; /* words 169-175 */ + u16 current_media_serial_number[ATA_IDENTIFY_MEDIA_SERIAL_NUMBER_LEN]; /* words 176-205 */ + u16 reserved6[3]; /* words 206-208 */ + u16 logical_sector_alignment; /* words 209 */ + u16 reserved7[7]; /* words 210-216 */ + u16 nominal_media_rotation_rate; /* word 217 */ + u16 reserved8[37]; /* words 218-254 */ + u16 integrity_word; /* word 255 */ + +}; + +#define ATA_IDENTIFY_DEVICE_GET_OFFSET(field_name) \ + ((unsigned long)&(((struct ata_identify_device_data *)0)->field_name)) +#define ATA_IDENTIFY_DEVICE_WCE_ENABLE 0x20 +#define ATA_IDENTIFY_DEVICE_RA_ENABLE 0x40 + +/** + * struct ATAPI_IDENTIFY_PACKET_DATA - The following structure depicts the + * ATA-ATAPI 7 version of the IDENTIFY PACKET DEVICE data structure. + * + * + */ +struct atapi_identify_packet_device { + u16 generalConfigBits; /* word 00 */ + u16 reserved0; /* word 01 (num cylinders) */ + u16 uniqueConfigBits; /* word 02 */ + u16 reserved1[7]; /* words 03 - 09 */ + u8 serialNumber[ATA_IDENTIFY_SERIAL_NUMBER_LEN]; /* word 10-19 */ + u16 reserved2[3]; /* words 20-22 */ + u8 firmwareRevision[ATA_IDENTIFY_FW_REVISION_LEN]; /* words 23-26 */ + u8 modelNumber[ATA_IDENTIFY_MODEL_NUMBER_LEN]; /* words 27-46 */ + u16 reserved4[2]; /* words 47-48 */ + u16 capabilities1; /* word 49 */ + u16 capabilities2; /* word 50 */ + u16 obsolete0[2]; /* words 51-52 */ + u16 validityBits; /* word 53 */ + u16 reserved[8]; /* words 54-61 */ + + u16 DMADIRBitRequired; /* word 62, page2 */ + u16 multiWordDmaMode; /* word 63 */ + u16 pioModesSupported; /* word 64 */ + u16 minMultiwordDmaTransferCycle; /* word 65 */ + u16 recMinMultiwordDmaTransferCycle; /* word 66 */ + u16 minPioTransferNoFlowCtrl; /* word 67 */ + u16 minPioTransferWithFlowCtrl; /* word 68 */ + u16 reserved6[2]; /* words 69-70 */ + u16 nsFromPACKETReceiptToBusRelease; /* word 71 */ + u16 nsFromSERVICEReceiptToBSYreset; /* wore 72 */ + u16 reserved7[2]; /* words 73-74 */ + u16 queueDepth; /* word 75 */ + u16 serialAtaCapabilities; /* word 76 */ + u16 serialAtaReserved; /* word 77 */ + u16 serialAtaFeaturesSupported; /* word 78 */ + u16 serialAtaFeaturesEnabled; /* word 79 */ + + u16 majorVersionNumber; /* word 80, page3 */ + u16 minorVersionNumber; /* word 81 */ + u16 commandSetSupported0; /* word 82 */ + u16 commandSetSupported1; /* word 83 */ + + u16 commandSetSupportedExtention; /* word 84, page4 */ + u16 commandSetEnabled0; /* word 85 */ + u16 commandSetEnabled1; /* word 86 */ + u16 commandSetDefault; /* word 87 */ + + u16 ultraDmaMode; /* word 88, page5 */ + u16 reserved8[4]; /* words 89 - 92 */ + + u16 hardwareResetResult; /* word 93, page6 */ + u16 currentAcousticManagementValue; /* word 94 */ + u16 reserved9[30]; /* words 95-124 */ + u16 ATAPIByteCount0Behavior; /* word 125 */ + u16 obsolete1; /* word 126 */ + u16 removableMediaStatus; /* word 127, */ + + u16 securityStatus; /* word 128, page7 */ + u16 vendorSpecific1[31]; /* words 129-159 */ + u16 reservedForCompactFlash[16]; /* words 160-175 */ + u16 reserved10[79]; /* words 176-254 */ + u16 integrityWord; /* word 255 */ +}; + +/** + * struct ata_extended_smart_self_test_log - The following structure depicts + * the ATA-8 version of the Extended SMART self test log page descriptor + * entry. + * + * + */ +union ata_descriptor_entry { + struct DESCRIPTOR_ENTRY { + u8 lba_field; + u8 status_byte; + u8 time_stamp_low; + u8 time_stamp_high; + u8 checkpoint_byte; + u8 failing_lba_low; + u8 failing_lba_mid; + u8 failing_lba_high; + u8 failing_lba_low_ext; + u8 failing_lba_mid_ext; + u8 failing_lba_high_ext; + + u8 vendor_specific1; + u8 vendor_specific2; + u8 vendor_specific3; + u8 vendor_specific4; + u8 vendor_specific5; + u8 vendor_specific6; + u8 vendor_specific7; + u8 vendor_specific8; + u8 vendor_specific9; + u8 vendor_specific10; + u8 vendor_specific11; + u8 vendor_specific12; + u8 vendor_specific13; + u8 vendor_specific14; + u8 vendor_specific15; + } DESCRIPTOR_ENTRY; + + u8 descriptor_entry[26]; + +}; + +/** + * struct ata_extended_smart_self_test_log - The following structure depicts + * the ATA-8 version of the SMART self test log page descriptor entry. + * + * + */ +union ata_smart_descriptor_entry { + struct SMART_DESCRIPTOR_ENTRY { + u8 lba_field; + u8 status_byte; + u8 time_stamp_low; + u8 time_stamp_high; + u8 checkpoint_byte; + u8 failing_lba_low; + u8 failing_lba_mid; + u8 failing_lba_high; + u8 failing_lba_low_ext; + + u8 vendor_specific1; + u8 vendor_specific2; + u8 vendor_specific3; + u8 vendor_specific4; + u8 vendor_specific5; + u8 vendor_specific6; + u8 vendor_specific7; + u8 vendor_specific8; + u8 vendor_specific9; + u8 vendor_specific10; + u8 vendor_specific11; + u8 vendor_specific12; + u8 vendor_specific13; + u8 vendor_specific14; + u8 vendor_specific15; + } SMART_DESCRIPTOR_ENTRY; + + u8 smart_descriptor_entry[24]; + +}; + +/** + * struct ata_extended_smart_self_test_log - The following structure depicts + * the ATA-8 version of the Extended SMART self test log page. + * + * + */ +struct ata_extended_smart_self_test_log { + u8 self_test_log_data_structure_revision_number; /* byte 0 */ + u8 reserved0; /* byte 1 */ + u8 self_test_descriptor_index[2]; /* byte 2-3 */ + + union ata_descriptor_entry descriptor_entrys[19]; /* bytes 4-497 */ + + u8 vendor_specific[2]; /* byte 498-499 */ + u8 reserved1[11]; /* byte 500-510 */ + u8 data_structure_checksum; /* byte 511 */ + +}; + +/** + * struct ata_extended_smart_self_test_log - The following structure depicts + * the ATA-8 version of the SMART self test log page. + * + * + */ +struct ata_smart_self_test_log { + u8 self_test_log_data_structure_revision_number[2]; /* bytes 0-1 */ + + union ata_smart_descriptor_entry descriptor_entrys[21]; /* bytes 2-505 */ + + u8 vendor_specific[2]; /* byte 506-507 */ + u8 self_test_index; /* byte 508 */ + u8 reserved1[2]; /* byte 509-510 */ + u8 data_structure_checksum; /* byte 511 */ + +}; + +#endif /* _ATA_H_ */ + diff --git a/drivers/scsi/isci/core/intel_sas.h b/drivers/scsi/isci/core/intel_sas.h new file mode 100644 index 000000000000..eb9686ea35f1 --- /dev/null +++ b/drivers/scsi/isci/core/intel_sas.h @@ -0,0 +1,948 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _INTEL_SAS_H_ +#define _INTEL_SAS_H_ + +/** + * This file contains all of the definitions relating to structures, constants, + * etc. defined by the SAS specification. + * + * + */ + +#include "intel_sata.h" +#include "intel_scsi.h" + +/** + * struct sci_sas_address - This structure depicts how a SAS address is + * represented by SCI. + * + * + */ +struct sci_sas_address { + /** + * This member contains the higher 32-bits of the SAS address. + */ + u32 high; + + /** + * This member contains the lower 32-bits of the SAS address. + */ + u32 low; + +}; + +/** + * struct sci_sas_identify_address_frame_protocols - This structure depicts the + * contents of bytes 2 and 3 in the SAS IDENTIFY ADDRESS FRAME (IAF). + * + * For specific information on each of these individual fields please reference + * the SAS specification Link layer section on address frames. + */ +struct sci_sas_identify_address_frame_protocols { + union { + struct { + u16 restricted1:1; + u16 smp_initiator:1; + u16 stp_initiator:1; + u16 ssp_initiator:1; + u16 reserved3:4; + u16 restricted2:1; + u16 smp_target:1; + u16 stp_target:1; + u16 ssp_target:1; + u16 reserved4:4; + } bits; + + u16 all; + } u; + +}; + +/** + * struct sci_sas_identify_address_frame - This structure depicts the contents + * of the SAS IDENTIFY ADDRESS FRAME (IAF). + * + * For specific information on each of these individual fields please reference + * the SAS specification Link layer section on address frames. + */ +struct sci_sas_identify_address_frame { + u16 address_frame_type:4; + u16 device_type:3; + u16 reserved1:1; + u16 reason:4; + u16 reserved2:4; + + struct sci_sas_identify_address_frame_protocols protocols; + + struct sci_sas_address device_name; + struct sci_sas_address sas_address; + + u32 phy_identifier:8; + u32 break_reply_capable:1; + u32 requested_in_zpsds:1; + u32 in_zpsds_persistent:1; + u32 reserved5:21; + + u32 reserved6[4]; + +}; + +/** + * struct sas_capabilities - This structure depicts the various SAS + * capabilities supported by the directly attached target device. For + * specific information on each of these individual fields please reference + * the SAS specification Phy layer section on speed negotiation windows. + * + * + */ +struct sas_capabilities { + union { +#if defined (SCIC_SDS_4_ENABLED) + struct { + /** + * The SAS specification indicates the start bit shall always be set to + * 1. This implementation will have the start bit set to 0 if the + * PHY CAPABILITIES were either not received or speed negotiation failed. + */ + u32 start:1; + u32 tx_ssc_type:1; + u32 reserved1:2; + u32 requested_logical_link_rate:4; + + u32 gen1_without_ssc_supported:1; + u32 gen1_with_ssc_supported:1; + u32 gen2_without_ssc_supported:1; + u32 gen2_with_ssc_supported:1; + u32 gen3_without_ssc_supported:1; + u32 gen3_with_ssc_supported:1; + u32 reserved2:17; + u32 parity:1; + } bits; +#endif /* (SCIC_SDS_4_ENABLED) */ + + u32 all; + } u; + +}; + +/** + * enum _SCI_SAS_LINK_RATE - This enumeration depicts the SAS specification + * defined link speeds. + * + * + */ +enum sci_sas_link_rate { + SCI_SAS_NO_LINK_RATE = 0, + SCI_SATA_SPINUP_HOLD = 0x3, + SCI_SAS_150_GB = 0x8, + SCI_SAS_300_GB = 0x9, + SCI_SAS_600_GB = 0xA +}; + +/** + * enum _SCI_SAS_TASK_ATTRIBUTE - This enumeration depicts the SAM/SAS + * specification defined task attribute values for a command information + * unit. + * + * + */ +enum sci_sas_task_attribute { + SCI_SAS_SIMPLE_ATTRIBUTE = 0, + SCI_SAS_HEAD_OF_QUEUE_ATTRIBUTE = 1, + SCI_SAS_ORDERED_ATTRIBUTE = 2, + SCI_SAS_ACA_ATTRIBUTE = 4, +}; + +/** + * enum _SCI_SAS_TASK_MGMT_FUNCTION - This enumeration depicts the SAM/SAS + * specification defined task management functions. + * + * This HARD_RESET function listed here is not actually defined as a task + * management function in the industry standard. + */ +enum sci_sas_task_mgmt_function { + SCI_SAS_ABORT_TASK = SCSI_TASK_REQUEST_ABORT_TASK, + SCI_SAS_ABORT_TASK_SET = SCSI_TASK_REQUEST_ABORT_TASK_SET, + SCI_SAS_CLEAR_TASK_SET = SCSI_TASK_REQUEST_CLEAR_TASK_SET, + SCI_SAS_LOGICAL_UNIT_RESET = SCSI_TASK_REQUEST_LOGICAL_UNIT_RESET, + SCI_SAS_I_T_NEXUS_RESET = SCSI_TASK_REQUEST_I_T_NEXUS_RESET, + SCI_SAS_CLEAR_ACA = SCSI_TASK_REQUEST_CLEAR_ACA, + SCI_SAS_QUERY_TASK = SCSI_TASK_REQUEST_QUERY_TASK, + SCI_SAS_QUERY_TASK_SET = SCSI_TASK_REQUEST_QUERY_TASK_SET, + SCI_SAS_QUERY_ASYNCHRONOUS_EVENT = SCSI_TASK_REQUEST_QUERY_UNIT_ATTENTION, + SCI_SAS_HARD_RESET = 0xFF +}; + + +/** + * enum _SCI_SAS_FRAME_TYPE - This enumeration depicts the SAS specification + * defined SSP frame types. + * + * + */ +enum sci_sas_frame_type { + SCI_SAS_DATA_FRAME = 0x01, + SCI_SAS_XFER_RDY_FRAME = 0x05, + SCI_SAS_COMMAND_FRAME = 0x06, + SCI_SAS_RESPONSE_FRAME = 0x07, + SCI_SAS_TASK_FRAME = 0x16 +}; + +/** + * struct sci_ssp_command_iu - This structure depicts the contents of the SSP + * COMMAND INFORMATION UNIT. For specific information on each of these + * individual fields please reference the SAS specification SSP transport + * layer section. + * + * + */ +struct sci_ssp_command_iu { + u32 lun_upper; + u32 lun_lower; + + u32 additional_cdb_length:6; + u32 reserved0:2; + u32 reserved1:8; + u32 enable_first_burst:1; + u32 task_priority:4; + u32 task_attribute:3; + u32 reserved2:8; + + u32 cdb[4]; + +}; + +/** + * struct sci_ssp_task_iu - This structure depicts the contents of the SSP TASK + * INFORMATION UNIT. For specific information on each of these individual + * fields please reference the SAS specification SSP transport layer section. + * + * + */ +struct sci_ssp_task_iu { + u32 lun_upper; + u32 lun_lower; + + u32 reserved0:8; + u32 task_function:8; + u32 reserved1:8; + u32 reserved2:8; + + u32 reserved3:16; + u32 task_tag:16; + + u32 reserved4[3]; + +}; + +#define SSP_RESPONSE_IU_MAX_DATA 64 + +#define SCI_SSP_RESPONSE_IU_DATA_PRESENT_MASK (0x03) + + +#define sci_ssp_get_sense_data_length(sense_data_length_buffer) \ + SCIC_BUILD_DWORD(sense_data_length_buffer) + +#define sci_ssp_get_response_data_length(response_data_length_buffer) \ + SCIC_BUILD_DWORD(response_data_length_buffer) + +/** + * struct sci_ssp_response_iu - This structure depicts the contents of the SSP + * RESPONSE INFORMATION UNIT. For specific information on each of these + * individual fields please reference the SAS specification SSP transport + * layer section. + * + * + */ +struct sci_ssp_response_iu { + u8 reserved0[8]; + + u8 retry_delay_timer[2]; + u8 data_present; + u8 status; + + u8 reserved1[4]; + u8 sense_data_length[4]; + u8 response_data_length[4]; + + u32 data[SSP_RESPONSE_IU_MAX_DATA]; + +}; + +/** + * enum _SCI_SAS_DATA_PRESENT_TYPE - This enumeration depicts the SAS + * specification defined SSP data present types in struct sci_ssp_response_iu. + * + * + */ +enum sci_ssp_response_iu_data_present_type { + SCI_SSP_RESPONSE_IU_NO_DATA = 0x00, + SCI_SSP_RESPONSE_IU_RESPONSE_DATA = 0x01, + SCI_SSP_RESPONSE_IU_SENSE_DATA = 0x02 +}; + +/** + * struct sci_ssp_frame_header - This structure depicts the contents of an SSP + * frame header. For specific information on the individual fields please + * reference the SAS specification transport layer SSP frame format. + * + * + */ +struct sci_ssp_frame_header { + /* Word 0 */ + u32 hashed_destination_address:24; + u32 frame_type:8; + + /* Word 1 */ + u32 hashed_source_address:24; + u32 reserved1_0:8; + + /* Word 2 */ + u32 reserved2_2:6; + u32 fill_bytes:2; + u32 reserved2_1:3; + u32 tlr_control:2; + u32 retry_data_frames:1; + u32 retransmit:1; + u32 changing_data_pointer:1; + u32 reserved2_0:16; + + /* Word 3 */ + u32 uiResv4; + + /* Word 4 */ + u16 target_port_transfer_tag; + u16 tag; + + /* Word 5 */ + u32 data_offset; + +}; + +/** + * struct smp_request_header - This structure defines the contents of an SMP + * Request header. + * + * For specific information on each of these individual fields please reference + * the SAS specification. + */ +struct smp_request_header { + u8 smp_frame_type; /* byte 0 */ + u8 function; /* byte 1 */ + u8 allocated_response_length; /* byte 2 */ + u8 request_length; /* byte 3 */ +}; + +/** + * struct smp_response_header - This structure depicts the contents of the SAS + * SMP DISCOVER RESPONSE frame. For specific information on each of these + * individual fields please reference the SAS specification Link layer + * section on address frames. + * + * + */ +struct smp_response_header { + u8 smp_frame_type; /* byte 0 */ + u8 function; /* byte 1 */ + u8 function_result; /* byte 2 */ + u8 response_length; /* byte 3 */ +}; + +/** + * struct smp_request_general - This structure defines the contents of an SMP + * Request that is comprised of the struct smp_request_header and a CRC. + * + * For specific information on each of these individual fields please reference + * the SAS specification. + */ +struct smp_request_general { + u32 crc; /* bytes 4-7 */ + +}; + +/** + * struct smp_request_phy_identifier - This structure defines the contents of + * an SMP Request that is comprised of the struct smp_request_header and a phy + * identifier. Examples: SMP_REQUEST_DISCOVER, SMP_REQUEST_REPORT_PHY_SATA. + * + * For specific information on each of these individual fields please reference + * the SAS specification. + */ +struct smp_request_phy_identifier { + u32 reserved_byte4_7; /* bytes 4-7 */ + + u32 ignore_zone_group:1; /* byte 8 */ + u32 reserved_byte8:7; + + u32 phy_identifier:8; /* byte 9 */ + u32 reserved_byte10:8; /* byte 10 */ + u32 reserved_byte11:8; /* byte 11 */ + +}; + +/** + * struct smp_request_configure_route_information - This structure defines the + * contents of an SMP Configure Route Information request. + * + * For specific information on each of these individual fields please reference + * the SAS specification. + */ +struct smp_request_configure_route_information { + u32 expected_expander_change_count:16; /* bytes 4-5 */ + u32 expander_route_index_high:8; + u32 expander_route_index:8; /* bytes 6-7 */ + + u32 reserved_byte8:8; /* bytes 8 */ + u32 phy_identifier:8; /* bytes 9 */ + u32 reserved_byte_10_11:16; /* bytes 10-11 */ + + u32 reserved_byte_12_bit_0_6:7; + u32 disable_route_entry:1; /* byte 12 */ + u32 reserved_byte_13_15:24; /* bytes 13-15 */ + + u32 routed_sas_address[2]; /* bytes 16-23 */ + u8 reserved_byte_24_39[16]; /* bytes 24-39 */ + +}; + +/** + * struct smp_request_phy_control - This structure defines the contents of an + * SMP Phy Controler request. + * + * For specific information on each of these individual fields please reference + * the SAS specification. + */ +struct smp_request_phy_control { + u16 expected_expander_change_count; /* byte 4-5 */ + + u16 reserved_byte_6_7; /* byte 6-7 */ + u8 reserved_byte_8; /* byte 8 */ + + u8 phy_identifier; /* byte 9 */ + u8 phy_operation; /* byte 10 */ + + u8 update_partial_pathway_timeout_value:1; + u8 reserved_byte_11_bit_1_7:7; /* byte 11 */ + + u8 reserved_byte_12_23[12]; /* byte 12-23 */ + + u8 attached_device_name[8]; /* byte 24-31 */ + + u8 reserved_byte_32_bit_3_0:4; /* byte 32 */ + u8 programmed_minimum_physical_link_rate:4; + + u8 reserved_byte_33_bit_3_0:4; /* byte 33 */ + u8 programmed_maximum_physical_link_rate:4; + + u16 reserved_byte_34_35; /* byte 34-35 */ + + u8 partial_pathway_timeout_value:4; + u8 reserved_byte_36_bit_4_7:4; /* byte 36 */ + + u16 reserved_byte_37_38; /* byte 37-38 */ + u8 reserved_byte_39; /* byte 39 */ + +}; + +/** + * struct smp_request_vendor_specific - This structure depicts the vendor + * specific space for SMP request. + * + * + */ + #define SMP_REQUEST_VENDOR_SPECIFIC_MAX_LENGTH 1016 +struct smp_request_vendor_specific { + u8 request_bytes[SMP_REQUEST_VENDOR_SPECIFIC_MAX_LENGTH]; +}; + +/** + * struct smp_request - This structure simply unionizes the existing request + * structures into a common request type. + * + * + */ +struct smp_request { + struct smp_request_header header; + + union { /* bytes 4-N */ + struct smp_request_general report_general; + struct smp_request_phy_identifier discover; + struct smp_request_general report_manufacturer_information; + struct smp_request_phy_identifier report_phy_sata; + struct smp_request_phy_control phy_control; + struct smp_request_phy_identifier report_phy_error_log; + struct smp_request_phy_identifier report_route_information; + struct smp_request_configure_route_information configure_route_information; + struct smp_request_vendor_specific vendor_specific_request; + } request; + +}; + + +/** + * struct smp_response_report_general - This structure depicts the SMP Report + * General for expander devices. It adheres to the SAS-2.1 specification. + * + * For specific information on each of these individual fields please reference + * the SAS specification Application layer section on SMP. + */ +struct smp_response_report_general { + u16 expander_change_count; /* byte 4-5 */ + u16 expander_route_indexes; /* byte 6-7 */ + + u32 reserved_byte8:7; /* byte 8 bit 0-6 */ + u32 long_response:1; /* byte 8 bit 7 */ + + u32 number_of_phys:8; /* byte 9 */ + + u32 configurable_route_table:1; /* byte 10 */ + u32 configuring:1; + u32 configures_others:1; + u32 open_reject_retry_supported:1; + u32 stp_continue_awt:1; + u32 self_configuring:1; + u32 zone_configuring:1; + u32 table_to_table_supported:1; + + u32 reserved_byte11:8; /* byte 11 */ + + u32 enclosure_logical_identifier_high; /* byte 12-15 */ + u32 enclosure_logical_identifier_low; /* byte 16-19 */ + + u32 reserved_byte20_23; + u32 reserved_byte24_27; + +}; + +struct smp_response_report_general_long { + struct smp_response_report_general sas1_1; + + struct { + u16 reserved1; + u16 stp_bus_inactivity_time_limit; + u16 stp_max_connect_time_limit; + u16 stp_smp_i_t_nexus_loss_time; + + u32 zoning_enabled:1; + u32 zoning_supported:1; + u32 physicaL_presence_asserted:1; + u32 zone_locked:1; + u32 reserved2:1; + u32 num_zone_groups:3; + u32 saving_zoning_enabled_supported:3; + u32 saving_zone_perms_table_supported:1; + u32 saving_zone_phy_info_supported:1; + u32 saving_zone_manager_password_supported:1; + u32 saving:1; + u32 reserved3:1; + u32 max_number_routed_sas_addresses:16; + + struct sci_sas_address active_zone_manager_sas_address; + + u16 zone_lock_inactivity_time_limit; + u16 reserved4; + + u8 reserved5; + u8 first_enclosure_connector_element_index; + u8 number_of_enclosure_connector_element_indices; + u8 reserved6; + + u32 reserved7:7; + u32 reduced_functionality:1; + u32 time_to_reduce_functionality:8; + u32 initial_time_to_reduce_functionality:8; + u8 max_reduced_functionality_time; + + u16 last_self_config_status_descriptor_index; + u16 max_number_of_stored_self_config_status_descriptors; + + u16 last_phy_event_list_descriptor_index; + u16 max_number_of_stored_phy_event_list_descriptors; + } sas2; + +}; + +/** + * struct smp_response_report_manufacturer_information - This structure depicts + * the SMP report manufacturer information for expander devices. It adheres + * to the SAS-2.1 specification. + * + * For specific information on each of these individual fields please reference + * the SAS specification Application layer section on SMP. + */ +struct smp_response_report_manufacturer_information { + u32 expander_change_count:16; /* bytes 4-5 */ + u32 reserved1:16; + + u32 sas1_1_format:1; + u32 reserved2:31; + + u8 vendor_id[8]; + u8 product_id[16]; + u8 product_revision_level[4]; + u8 component_vendor_id[8]; + u8 component_id[2]; + u8 component_revision_level; + u8 reserved3; + u8 vendor_specific[8]; + +}; + +#define SMP_RESPONSE_DISCOVER_FORMAT_1_1_SIZE 52 +#define SMP_RESPONSE_DISCOVER_FORMAT_2_SIZE 116 + +/** + * struct smp_discover_response_protocols - This structure depicts the discover + * response where the supported protocols by the remote phy are specified. + * + * For specific information on each of these individual fields please reference + * the SAS specification Link layer section on address frames. + */ +struct smp_discover_response_protocols { + union { + struct { + u16 attached_sata_host:1; + u16 attached_smp_initiator:1; + u16 attached_stp_initiator:1; + u16 attached_ssp_initiator:1; + u16 reserved3:4; + u16 attached_sata_device:1; + u16 attached_smp_target:1; + u16 attached_stp_target:1; + u16 attached_ssp_target:1; + u16 reserved4:3; + u16 attached_sata_port_selector:1; + } bits; + + u16 all; + } u; + +}; + +/** + * struct SMP_RESPONSE_DISCOVER_FORMAT - This structure defines the SMP phy + * discover response format. It handles both SAS1.1 and SAS 2 definitions. + * The unions indicate locations where the SAS specification versions differ + * from one another. + * + * + */ +struct smp_response_discover { + + union { + struct { + u8 reserved[2]; + } sas1_1; + + struct { + u16 expander_change_count; + } sas2; + + } u1; + + u8 reserved1[3]; + u8 phy_identifier; + u8 reserved2[2]; + + union { + struct { + u16 reserved1:4; + u16 attached_device_type:3; + u16 reserved2:1; + u16 negotiated_physical_link_rate:4; + u16 reserved3:4; + } sas1_1; + + struct { + u16 attached_reason:4; + u16 attached_device_type:3; + u16 reserved2:1; + u16 negotiated_logical_link_rate:4; + u16 reserved3:4; + } sas2; + + } u2; + + struct smp_discover_response_protocols protocols; + struct sci_sas_address sas_address; + struct sci_sas_address attached_sas_address; + + u8 attached_phy_identifier; + + union { + struct { + u8 reserved; + } sas1_1; + + struct { + u8 attached_break_reply_capable:1; + u8 attached_requested_inside_zpsds:1; + u8 attached_inside_zpsds_persistent:1; + u8 reserved1:5; + } sas2; + + } u3; + + u8 reserved_for_identify[6]; + + u32 hardware_min_physical_link_rate:4; + u32 programmed_min_physical_link_rate:4; + u32 hardware_max_physical_link_rate:4; + u32 programmed_max_physical_link_rate:4; + u32 phy_change_count:8; + u32 partial_pathway_timeout_value:4; + u32 reserved5:3; + u32 virtual_phy:1; + + u32 routing_attribute:4; + u32 reserved6:4; + u32 connector_type:7; + u32 reserved7:1; + u32 connector_element_index:8; + u32 connector_physical_link:8; + + u16 reserved8; + u16 vendor_specific; + + union { + struct { + /** + * In the SAS 1.1 specification this structure ends after 52 bytes. + * As a result, the contents of this field should never have a + * real value. It is undefined. + */ + u8 undefined[SMP_RESPONSE_DISCOVER_FORMAT_2_SIZE + - SMP_RESPONSE_DISCOVER_FORMAT_1_1_SIZE]; + } sas1_1; + + struct { + struct sci_sas_address attached_device_name; + + u32 zoning_enabled:1; + u32 inside_zpsds:1; + u32 zone_group_persistent:1; + u32 reserved1:1; + u32 requested_inside_zpsds:1; + u32 inside_zpsds_persistent:1; + u32 requested_inside_zpsds_changed_by_expander:1; + u32 reserved2:1; + u32 reserved_for_zoning_fields:16; + u32 zone_group:8; + + u8 self_configuration_status; + u8 self_configuration_levels_completed; + u16 reserved_for_self_config_fields; + + struct sci_sas_address self_configuration_sas_address; + + u32 programmed_phy_capabilities; + u32 current_phy_capabilities; + u32 attached_phy_capabilities; + + u32 reserved3; + + u32 reserved4:16; + u32 negotiated_physical_link_rate:4; + u32 reason:4; + u32 hardware_muxing_supported:1; + u32 negotiated_ssc:1; + u32 reserved5:6; + + u32 default_zoning_enabled:1; + u32 reserved6:1; + u32 default_zone_group_persistent:1; + u32 reserved7:1; + u32 default_requested_inside_zpsds:1; + u32 default_inside_zpsds_persistent:1; + u32 reserved8:2; + u32 reserved9:16; + u32 default_zone_group:8; + + u32 saved_zoning_enabled:1; + u32 reserved10:1; + u32 saved_zone_group_persistent:1; + u32 reserved11:1; + u32 saved_requested_inside_zpsds:1; + u32 saved_inside_zpsds_persistent:1; + u32 reserved12:18; + u32 saved_zone_group:8; + + u32 reserved14:2; + u32 shadow_zone_group_persistent:1; + u32 reserved15:1; + u32 shadow_requested_inside_zpsds:1; + u32 shadow_inside_zpsds_persistent:1; + u32 reserved16:18; + u32 shadow_zone_group:8; + + u8 device_slot_number; + u8 device_slot_group_number; + u8 device_slot_group_output_connector[6]; + } sas2; + + } u4; + +}; + +/** + * struct smp_response_report_phy_sata - This structure depicts the contents of + * the SAS SMP REPORT PHY SATA frame. For specific information on each of + * these individual fields please reference the SAS specification Link layer + * section on address frames. + * + * + */ +struct smp_response_report_phy_sata { + u32 ignored_byte_4_7; /* bytes 4-7 */ + + u32 affiliations_valid:1; + u32 affiliations_supported:1; + u32 reserved_byte11:6; /* byte 11 */ + u32 ignored_byte10:8; /* byte 10 */ + u32 phy_identifier:8; /* byte 9 */ + u32 reserved_byte_8:8; /* byte 8 */ + + u32 reserved_12_15; + u32 stp_sas_address[2]; + u8 device_to_host_fis[20]; + u32 reserved_44_47; + u32 affiliated_stp_initiator_sas_address[2]; + +}; + +struct smp_response_vendor_specific { + u8 response_bytes[SMP_REQUEST_VENDOR_SPECIFIC_MAX_LENGTH]; +}; + +union smp_response_body { + struct smp_response_report_general report_general; + struct smp_response_report_manufacturer_information report_manufacturer_information; + struct smp_response_discover discover; + struct smp_response_report_phy_sata report_phy_sata; + struct smp_response_vendor_specific vendor_specific_response; +}; + +/** + * struct smp_response - This structure simply unionizes the existing response + * structures into a common response type. + * + * + */ +struct smp_response { + struct smp_response_header header; + + union smp_response_body response; + +}; + +/* SMP Request Functions */ +#define SMP_FUNCTION_REPORT_GENERAL 0x00 +#define SMP_FUNCTION_REPORT_MANUFACTURER_INFORMATION 0x01 +#define SMP_FUNCTION_DISCOVER 0x10 +#define SMP_FUNCTION_REPORT_PHY_ERROR_LOG 0x11 +#define SMP_FUNCTION_REPORT_PHY_SATA 0x12 +#define SMP_FUNCTION_REPORT_ROUTE_INFORMATION 0X13 +#define SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION 0X90 +#define SMP_FUNCTION_PHY_CONTROL 0x91 +#define SMP_FUNCTION_PHY_TEST 0x92 + +#define SMP_FRAME_TYPE_REQUEST 0x40 +#define SMP_FRAME_TYPE_RESPONSE 0x41 + +#define PHY_OPERATION_NOP 0x00 +#define PHY_OPERATION_LINK_RESET 0x01 +#define PHY_OPERATION_HARD_RESET 0x02 +#define PHY_OPERATION_DISABLE 0x03 +#define PHY_OPERATION_CLEAR_ERROR_LOG 0x05 +#define PHY_OPERATION_CLEAR_AFFILIATION 0x06 + +#define NPLR_PHY_ENABLED_UNK_LINK_RATE 0x00 +#define NPLR_PHY_DISABLED 0x01 +#define NPLR_PHY_ENABLED_SPD_NEG_FAILED 0x02 +#define NPLR_PHY_ENABLED_SATA_HOLD 0x03 +#define NPLR_PHY_ENABLED_1_5G 0x08 +#define NPLR_PHY_ENABLED_3_0G 0x09 + +/* SMP Function Result values. */ +#define SMP_RESULT_FUNCTION_ACCEPTED 0x00 +#define SMP_RESULT_UNKNOWN_FUNCTION 0x01 +#define SMP_RESULT_FUNCTION_FAILED 0x02 +#define SMP_RESULT_INVALID_REQUEST_FRAME_LEN 0x03 +#define SMP_RESULT_INAVALID_EXPANDER_CHANGE_COUNT 0x04 +#define SMP_RESULT_BUSY 0x05 +#define SMP_RESULT_INCOMPLETE_DESCRIPTOR_LIST 0x06 +#define SMP_RESULT_PHY_DOES_NOT_EXIST 0x10 +#define SMP_RESULT_INDEX_DOES_NOT_EXIST 0x11 +#define SMP_RESULT_PHY_DOES_NOT_SUPPORT_SATA 0x12 +#define SMP_RESULT_UNKNOWN_PHY_OPERATION 0x13 +#define SMP_RESULT_UNKNOWN_PHY_TEST_FUNCTION 0x14 +#define SMP_RESULT_PHY_TEST_IN_PROGRESS 0x15 +#define SMP_RESULT_PHY_VACANT 0x16 + +/* Attached Device Types */ +#define SMP_NO_DEVICE_ATTACHED 0 +#define SMP_END_DEVICE_ONLY 1 +#define SMP_EDGE_EXPANDER_DEVICE 2 +#define SMP_FANOUT_EXPANDER_DEVICE 3 + +/* Expander phy routine attribute */ +#define DIRECT_ROUTING_ATTRIBUTE 0 +#define SUBTRACTIVE_ROUTING_ATTRIBUTE 1 +#define TABLE_ROUTING_ATTRIBUTE 2 + +#endif /* _INTEL_SAS_H_ */ + diff --git a/drivers/scsi/isci/core/intel_sat.h b/drivers/scsi/isci/core/intel_sat.h new file mode 100644 index 000000000000..c4d78ed35cf1 --- /dev/null +++ b/drivers/scsi/isci/core/intel_sat.h @@ -0,0 +1,95 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SAT_H_ +#define _SAT_H_ + +/** + * This file contains constants and constructs defined in the SCSI to ATA + * Translation (SAT) T10 standard. For more information please refer to + * www.t10.org. + * + * + */ + +/** + * + * + * SAT_PROTOCOLS These constants indicate the various protocol values that can + * be supported in a SAT translator. + */ +#define SAT_PROTOCOL_ATA_HARD_RESET 0 +#define SAT_PROTOCOL_SOFT_RESET 1 +#define SAT_PROTOCOL_NON_DATA 3 +#define SAT_PROTOCOL_PIO_DATA_IN 4 +#define SAT_PROTOCOL_PIO_DATA_OUT 5 +#define SAT_PROTOCOL_DMA 6 +#define SAT_PROTOCOL_DMA_QUEUED 7 +#define SAT_PROTOCOL_DEVICE_DIAGNOSTIC 8 +#define SAT_PROTOCOL_DEVICE_RESET 9 +#define SAT_PROTOCOL_UDMA_DATA_IN 10 +#define SAT_PROTOCOL_UDMA_DATA_OUT 11 +#define SAT_PROTOCOL_FPDMA 12 +#define SAT_PROTOCOL_RETURN_RESPONSE_INFO 15 + +#define SAT_PROTOCOL_PACKET 0x10 +#define SAT_PROTOCOL_PACKET_NON_DATA (SAT_PROTOCOL_PACKET | 0x0) +#define SAT_PROTOCOL_PACKET_DMA_DATA_IN (SAT_PROTOCOL_PACKET | 0x1) +#define SAT_PROTOCOL_PACKET_DMA_DATA_OUT (SAT_PROTOCOL_PACKET | 0x2) +#define SAT_PROTOCOL_PACKET_PIO_DATA_IN (SAT_PROTOCOL_PACKET | 0x3) +#define SAT_PROTOCOL_PACKET_PIO_DATA_OUT (SAT_PROTOCOL_PACKET | 0x4) + +#endif /* _SAT_H_ */ + diff --git a/drivers/scsi/isci/core/intel_sata.h b/drivers/scsi/isci/core/intel_sata.h new file mode 100644 index 000000000000..47390d54064d --- /dev/null +++ b/drivers/scsi/isci/core/intel_sata.h @@ -0,0 +1,280 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SATA_H_ +#define _SATA_H_ + +#include <linux/types.h> + +/** + * This file defines all of the SATA releated constants, enumerations, and + * types. Please note that this file does not necessarily contain an + * exhaustive list of all contants and commands. + * + * + */ + +/** + * + * + * SATA FIS Types These constants depict the various SATA FIS types devined in + * the serial ATA specification. + */ +#define SATA_FIS_TYPE_REGH2D 0x27 +#define SATA_FIS_TYPE_REGD2H 0x34 +#define SATA_FIS_TYPE_SETDEVBITS 0xA1 +#define SATA_FIS_TYPE_DMA_ACTIVATE 0x39 +#define SATA_FIS_TYPE_DMA_SETUP 0x41 +#define SATA_FIS_TYPE_BIST_ACTIVATE 0x58 +#define SATA_FIS_TYPE_PIO_SETUP 0x5F +#define SATA_FIS_TYPE_DATA 0x46 + +#define SATA_REGISTER_FIS_SIZE 0x20 + +/** + * struct sata_fis_header - This is the common definition for a SATA FIS Header + * word. A different header word is defined for any FIS type that does not + * use the standard header. + * + * + */ +struct sata_fis_header { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved:1; + u32 direction_flag:1; /* direction */ + u32 interrupt_flag:1; + u32 command_flag:1; /* command, auto_activate, or notification */ + u32 status:8; + u32 error:8; +}; + + +/** + * struct sata_fis_reg_h2d - This is the definition for a SATA Host to Device + * Register FIS. + * + * + */ +struct sata_fis_reg_h2d { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved0:3; + u32 command_flag:1; + u32 command:8; + u32 features:8; + u32 lba_low:8; /* word 1 */ + u32 lba_mid:8; + u32 lba_high:8; + u32 device:8; + u32 lba_low_exp:8; /* word 2 */ + u32 lba_mid_exp:8; + u32 lba_high_exp:8; + u32 features_exp:8; + u32 sector_count:8; /* word 3 */ + u32 sector_count_exp:8; + u32 reserved1:8; + u32 control:8; + u32 reserved2; /* word 4 */ +}; + +/** + * struct sata_fis_reg_d2h - SATA Device To Host FIS + * + * + */ +struct sata_fis_reg_d2h { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved0:2; + u32 irq:1; + u32 reserved1:1; + u32 status:8; + u32 error:8; + u8 lba_low; /* word 1 */ + u8 lba_mid; + u8 lba_high; + u8 device; + u8 lba_low_exp; /* word 2 */ + u8 lba_mid_exp; + u8 lba_high_exp; + u8 reserved; + u8 sector_count; /* word 3 */ + u8 sector_count_exp; + u16 reserved2; + u32 reserved3; +}; + +/** + * + * + * Status field bit definitions + */ +#define SATA_FIS_STATUS_DEVBITS_MASK (0x77) + +/** + * struct sata_fis_set_dev_bits - SATA Set Device Bits FIS + * + * + */ +struct sata_fis_set_dev_bits { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved0:2; + u32 irq:1; + u32 notification:1; + u32 status_low:4; + u32 status_high:4; + u32 error:8; + u32 s_active; /* word 1 */ +}; + +/** + * struct sata_fis_dma_activate - SATA DMA Activate FIS + * + * + */ +struct sata_fis_dma_activate { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved0:24; +}; + +/** + * + * + * The lower 5 bits in the DMA Buffer ID Low field of the DMA Setup are used to + * communicate the command tag. + */ +#define SATA_DMA_SETUP_TAG_ENABLE 0x1F + +#define SATA_DMA_SETUP_AUTO_ACT_ENABLE 0x80 + +/** + * struct sata_fis_dma_setup - SATA DMA Setup FIS + * + * + */ +struct sata_fis_dma_setup { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved_00:1; + u32 direction:1; + u32 irq:1; + u32 auto_activate:1; + u32 reserved_01:16; + u32 dma_buffer_id_low; /* word 1 */ + u32 dma_buffer_id_high; /* word 2 */ + u32 reserved0; /* word 3 */ + u32 dma_buffer_offset; /* word 4 */ + u32 dma_transfer_count; /* word 5 */ + u32 reserved1; /* word 6 */ +}; + +/** + * struct sata_fis_bist_activate - SATA BIST Activate FIS + * + * + */ +struct sata_fis_bist_activate { + u32 fis_type:8; /* word 0 */ + u32 reserved0:8; + u32 pattern_definition:8; + u32 reserved1:8; + u32 data1; /* word 1 */ + u32 data2; /* word 1 */ +}; + +/* + * SATA PIO Setup FIS + */ +struct sata_fis_pio_setup { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved_00:1; + u32 direction:1; + u32 irq:1; + u32 reserved_01:1; + u32 status:8; + u32 error:8; + u32 lba_low:8; /* word 1 */ + u32 lba_mid:8; + u32 lba_high:8; + u32 device:8; + u32 lba_low_exp:8; /* word 2 */ + u32 lba_mid_exp:8; + u32 lba_high_exp:8; + u32 reserved:8; + u32 sector_count:8; /* word 3 */ + u32 sector_count_exp:8; + u32 reserved1:8; + u32 ending_status:8; + u32 transfter_count:16; /* word 4 */ + u32 reserved3:16; +}; + +/** + * struct sata_fis_data - SATA Data FIS + * + * + */ +struct sata_fis_data { + u32 fis_type:8; /* word 0 */ + u32 pm_port:4; + u32 reserved0:24; + u8 data[4]; /* word 1 */ +}; + +#endif /* _SATA_H_ */ diff --git a/drivers/scsi/isci/core/intel_scsi.h b/drivers/scsi/isci/core/intel_scsi.h new file mode 100644 index 000000000000..1e45d3c98127 --- /dev/null +++ b/drivers/scsi/isci/core/intel_scsi.h @@ -0,0 +1,474 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file defines all of the SCSI related constants, enumerations, and + * types. Please note that this file does not necessarily contain an + * exhaustive list of all constants, commands, sub-commands, etc. + * + * + */ + +#ifndef _SCSI_H__ +#define _SCSI_H__ + + +/* + * ****************************************************************************** + * * C O N S T A N T S A N D M A C R O S + * ****************************************************************************** */ + +/** + * enum _SCSI_TASK_MGMT_REQUEST_CODES - This enumberation contains the + * constants to be used for SCSI task management request codes. SAM does + * not specify any particular values for these codes so constants used here + * are the same as those specified in SAS. + * + * + */ +enum scsi_task_mgmt_request_codes { + SCSI_TASK_REQUEST_ABORT_TASK = 0x01, + SCSI_TASK_REQUEST_ABORT_TASK_SET = 0x02, + SCSI_TASK_REQUEST_CLEAR_TASK_SET = 0x04, + SCSI_TASK_REQUEST_LOGICAL_UNIT_RESET = 0x08, + SCSI_TASK_REQUEST_I_T_NEXUS_RESET = 0x10, + SCSI_TASK_REQUEST_CLEAR_ACA = 0x40, + SCSI_TASK_REQUEST_QUERY_TASK = 0x80, + SCSI_TASK_REQUEST_QUERY_TASK_SET = 0x81, + SCSI_TASK_REQUEST_QUERY_UNIT_ATTENTION = 0x82, + +}; + +/** + * enum _SCSI_TASK_MGMT_RESPONSE_CODES - This enumeration contains all of the + * SCSI task management response codes. + * + * + */ +enum scsi_task_mgmt_response_codes { + SCSI_TASK_MGMT_FUNC_COMPLETE = 0, + SCSI_INVALID_FRAME = 2, + SCSI_TASK_MGMT_FUNC_NOT_SUPPORTED = 4, + SCSI_TASK_MGMT_FUNC_FAILED = 5, + SCSI_TASK_MGMT_FUNC_SUCCEEDED = 8, + SCSI_INVALID_LUN = 9 +}; + +/** + * enum _SCSI_SENSE_RESPONSE_CODE - this enumeration depicts the types of sense + * data responses as per SPC-3. + * + * + */ +enum scsi_sense_response_code { + SCSI_FIXED_CURRENT_RESPONSE_CODE = 0x70, + SCSI_FIXED_DEFERRED_RESPONSE_CODE = 0x71, + SCSI_DESCRIPTOR_CURRENT_RESPONSE_CODE = 0x72, + SCSI_DESCRIPTOR_DEFERRED_RESPONSE_CODE = 0x73 + +}; + +/* + * This constant represents the valid bit located in byte 0 of a FIXED + * format sense data. */ +#define SCSI_FIXED_SENSE_DATA_VALID_BIT 0x80 + +#define SCSI_FIXED_SENSE_DATA_BASE_LENGTH 18 + +/* This value is used in the DATAPRES field of the SCSI Response IU. */ +#define SCSI_RESPONSE_DATA_PRES_SENSE_DATA 0x02 + +/** + * + * + * SCSI_SENSE_KEYS These constants delineate all of the SCSI protocol sense key + * constants + */ +#define SCSI_SENSE_NO_SENSE 0x00 +#define SCSI_SENSE_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_NOT_READY 0x02 +#define SCSI_SENSE_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_DATA_PROTECT 0x07 +#define SCSI_SENSE_BLANK_CHECK 0x08 +#define SCSI_SENSE_VENDOR_SPECIFIC 0x09 +#define SCSI_SENSE_COPY_ABORTED 0x0A +#define SCSI_SENSE_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_VOLUME_OVERFLOW 0x0D +#define SCSI_SENSE_MISCOMPARE 0x0E + +/** + * + * + * SCSI_ADDITIONAL_SENSE_CODES These constants delineate all of the SCSI + * protocol additional sense code constants. + */ +#define SCSI_ASC_NO_ADDITIONAL_SENSE 0x00 +#define SCSI_ASC_INITIALIZING_COMMAND_REQUIRED 0x04 +#define SCSI_ASC_LUN_SELF_TEST_IN_PROGRESS 0x04 +#define SCSI_ASC_LUN_FORMAT_IN_PROGRESS 0x04 +#define SCSI_ASC_LUN_NOT_RESPOND_TO_SELECTION 0x05 +#define SCSI_ASC_UNRECOVERED_READ_ERROR 0x11 +#define SCSI_ASC_INVALID_COMMAND_OPERATION_CODE 0x20 +#define SCSI_ASC_LBA_OUT_OF_RANGE 0x21 +#define SCSI_ASC_INVALID_FIELD_IN_CDB 0x24 +#define SCSI_ASC_INVALID_FIELD_IN_PARM_LIST 0x26 +#define SCSI_ASC_WRITE_PROTECTED 0x27 +#define SCSI_ASC_NOT_READY_TO_READY_CHANGE 0x28 +#define SCSI_ASC_SAVING_PARMS_NOT_SUPPORTED 0x39 +#define SCSI_ASC_MEDIUM_NOT_PRESENT 0x3A +#define SCSI_ASC_INTERNAL_TARGET_FAILURE 0x44 +#define SCSI_ASC_IU_CRC_ERROR_DETECTED 0x47 +#define SCSI_ASC_MEDIUM_REMOVAL_REQUEST 0x5A +#define SCSI_ASC_COMMAND_SEQUENCE_ERROR 0x2C +#define SCSI_ASC_MEDIA_LOAD_OR_EJECT_FAILED 0x53 +#define SCSI_ASC_HARDWARE_IMPENDING_FAILURE 0x5D +#define SCSI_ASC_POWER_STATE_CHANGE 0x5E +#define SCSI_DIAGNOSTIC_FAILURE_ON_COMPONENT 0x40 +#define SCSI_ASC_ATA_DEVICE_FEATURE_NOT_ENABLED 0x67 + +/** + * + * + * SCSI_ADDITIONAL_SENSE_CODE_QUALIFIERS This enumeration contains all of the + * used SCSI protocol additional sense code qualifier constants. + */ +#define SCSI_ASCQ_NO_ADDITIONAL_SENSE 0x00 +#define SCSI_ASCQ_INVALID_FIELD_IN_CDB 0x00 +#define SCSI_ASCQ_INVALID_FIELD_IN_PARM_LIST 0x00 +#define SCSI_ASCQ_LUN_NOT_RESPOND_TO_SELECTION 0x00 +#define SCSI_ASCQ_INTERNAL_TARGET_FAILURE 0x00 +#define SCSI_ASCQ_LBA_OUT_OF_RANGE 0x00 +#define SCSI_ASCQ_MEDIUM_NOT_PRESENT 0x00 +#define SCSI_ASCQ_NOT_READY_TO_READY_CHANGE 0x00 +#define SCSI_ASCQ_WRITE_PROTECTED 0x00 +#define SCSI_ASCQ_UNRECOVERED_READ_ERROR 0x00 +#define SCSI_ASCQ_SAVING_PARMS_NOT_SUPPORTED 0x00 +#define SCSI_ASCQ_INVALID_COMMAND_OPERATION_CODE 0x00 +#define SCSI_ASCQ_MEDIUM_REMOVAL_REQUEST 0x01 +#define SCSI_ASCQ_INITIALIZING_COMMAND_REQUIRED 0x02 +#define SCSI_ASCQ_IU_CRC_ERROR_DETECTED 0x03 +#define SCSI_ASCQ_LUN_FORMAT_IN_PROGRESS 0x04 +#define SCSI_ASCQ_LUN_SELF_TEST_IN_PROGRESS 0x09 +#define SCSI_ASCQ_GENERAL_HARD_DRIVE_FAILURE 0x10 +#define SCSI_ASCQ_IDLE_CONDITION_ACTIVATE_BY_COMMAND 0x03 +#define SCSI_ASCQ_STANDBY_CONDITION_ACTIVATE_BY_COMMAND 0x04 +#define SCSI_ASCQ_POWER_STATE_CHANGE_TO_IDLE 0x42 +#define SCSI_ASCQ_POWER_STATE_CHANGE_TO_STANDBY 0x43 +#define SCSI_ASCQ_ATA_DEVICE_FEATURE_NOT_ENABLED 0x0B +#define SCSI_ASCQ_UNRECOVERED_READ_ERROR_AUTO_REALLOCATE_FAIL 0x04 + + + +/** + * + * + * SCSI_STATUS_CODES These constants define all of the used SCSI status values. + */ +#define SCSI_STATUS_GOOD 0x00 +#define SCSI_STATUS_CHECK_CONDITION 0x02 +#define SCSI_STATUS_CONDITION_MET 0x04 +#define SCSI_STATUS_BUSY 0x08 +#define SCSI_STATUS_TASKFULL 0x28 +#define SCSI_STATUS_ACA 0x30 +#define SCSI_STATUS_ABORT 0x40 + +/** + * + * + * SCSI_OPERATION_CODES These constants delineate all of the SCSI + * command/operation codes. + */ +#define SCSI_INQUIRY 0x12 +#define SCSI_READ_CAPACITY_10 0x25 +#define SCSI_SERVICE_ACTION_IN_16 0x9E +#define SCSI_TEST_UNIT_READY 0x00 +#define SCSI_START_STOP_UNIT 0x1B +#define SCSI_SYNCHRONIZE_CACHE_10 0x35 +#define SCSI_SYNCHRONIZE_CACHE_16 0x91 +#define SCSI_REQUEST_SENSE 0x03 +#define SCSI_REPORT_LUNS 0xA0 +#define SCSI_REASSIGN_BLOCKS 0x07 +#define SCSI_READ_6 0x08 +#define SCSI_READ_10 0x28 +#define SCSI_READ_12 0xA8 +#define SCSI_READ_16 0x88 +#define SCSI_WRITE_6 0x0A +#define SCSI_WRITE_10 0x2A +#define SCSI_WRITE_12 0xAA +#define SCSI_WRITE_16 0x8A +#define SCSI_VERIFY_10 0x2F +#define SCSI_VERIFY_12 0xAF +#define SCSI_VERIFY_16 0x8F +#define SCSI_SEEK_6 0x01 +#define SCSI_SEEK_10 0x02 +#define SCSI_WRITE_VERIFY 0x2E +#define SCSI_FORMAT_UNIT 0x04 +#define SCSI_READ_BUFFER 0x3C +#define SCSI_WRITE_BUFFER 0x3B +#define SCSI_SEND_DIAGNOSTIC 0x1D +#define SCSI_RECEIVE_DIAGNOSTIC 0x1C +#define SCSI_MODE_SENSE_6 0x1A +#define SCSI_MODE_SENSE_10 0x5A +#define SCSI_MODE_SELECT_6 0x15 +#define SCSI_MODE_SELECT_10 0x55 +#define SCSI_MAINTENANCE_IN 0xA3 +#define SCSI_LOG_SENSE 0x4D +#define SCSI_LOG_SELECT 0x4C +#define SCSI_RESERVE_6 0x16 +#define SCSI_RESERVE_10 0x56 +#define SCSI_RELEASE_6 0x17 +#define SCSI_RELEASE_10 0x57 +#define SCSI_ATA_PASSTHRU_12 0xA1 +#define SCSI_ATA_PASSTHRU_16 0x85 +#define SCSI_WRITE_LONG_10 0x3F +#define SCSI_WRITE_LONG_16 0x9F +#define SCSI_PERSISTENT_RESERVE_IN 0x5E +#define SCSI_PERSISTENT_RESERVE_OUT 0x5F + +/** + * + * + * SCSI_SERVICE_ACTION_IN_CODES Service action in operations. + */ +#define SCSI_SERVICE_ACTION_IN_CODES_READ_CAPACITY_16 0x10 + +#define SCSI_SERVICE_ACTION_MASK 0x1f + +/** + * + * + * SCSI_MAINTENANCE_IN_SERVICE_ACTION_CODES MAINTENANCE IN service action codes. + */ +#define SCSI_REPORT_TASK_MGMT 0x0D +#define SCSI_REPORT_OP_CODES 0x0C + +/** + * + * + * SCSI_MODE_PAGE_CONTROLS These constants delineate all of the used SCSI Mode + * Page control values. + */ +#define SCSI_MODE_SENSE_PC_CURRENT 0x0 +#define SCSI_MODE_SENSE_PC_CHANGEABLE 0x1 +#define SCSI_MODE_SENSE_PC_DEFAULT 0x2 +#define SCSI_MODE_SENSE_PC_SAVED 0x3 + +#define SCSI_MODE_SENSE_PC_SHIFT 0x06 +#define SCSI_MODE_SENSE_PAGE_CODE_ENABLE 0x3F +#define SCSI_MODE_SENSE_DBD_ENABLE 0x08 +#define SCSI_MODE_SENSE_LLBAA_ENABLE 0x10 + +/** + * + * + * SCSI_MODE_PAGE_CODES These constants delineate all of the used SCSI Mode + * Page codes. + */ +#define SCSI_MODE_PAGE_READ_WRITE_ERROR 0x01 +#define SCSI_MODE_PAGE_DISCONNECT_RECONNECT 0x02 +#define SCSI_MODE_PAGE_CACHING 0x08 +#define SCSI_MODE_PAGE_CONTROL 0x0A +#define SCSI_MODE_PAGE_PROTOCOL_SPECIFIC_PORT 0x19 +#define SCSI_MODE_PAGE_POWER_CONDITION 0x1A +#define SCSI_MODE_PAGE_INFORMATIONAL_EXCP_CONTROL 0x1C +#define SCSI_MODE_PAGE_ALL_PAGES 0x3F + +#define SCSI_MODE_SENSE_ALL_SUB_PAGES_CODE 0xFF +#define SCSI_MODE_SENSE_NO_SUB_PAGES_CODE 0x0 +#define SCSI_MODE_SENSE_PROTOCOL_PORT_NUM_SUBPAGES 0x1 +#define SCSI_MODE_PAGE_CACHE_PAGE_WCE_BIT 0x04 +#define SCSI_MODE_PAGE_CACHE_PAGE_DRA_BIT 0x20 +#define SCSI_MODE_PAGE_DEXCPT_ENABLE 0x08 +#define SCSI_MODE_SENSE_HEADER_FUA_ENABLE 0x10 +#define SCSI_MODE_PAGE_POWER_CONDITION_STANDBY 0x1 +#define SCSI_MODE_PAGE_POWER_CONDITION_IDLE 0x2 + +#define SCSI_MODE_SENSE_6_HEADER_LENGTH 4 +#define SCSI_MODE_SENSE_10_HEADER_LENGTH 8 +#define SCSI_MODE_SENSE_STD_BLOCK_DESCRIPTOR_LENGTH 8 +#define SCSI_MODE_SENSE_LLBA_BLOCK_DESCRIPTOR_LENGTH 16 + +#define SCSI_MODE_PAGE_INFORMATIONAL_EXCP_DXCPT_ENABLE 0x08 +#define SCSI_MODE_PAGE_19_SAS_ID 0x6 +#define SCSI_MODE_PAGE_19_SUB1_PAGE_NUM 0x1 +#define SCSI_MODE_PAGE_19_SUB1_PC 0x59 + +#define SCSI_MODE_HEADER_MEDIUM_TYPE_SBC 0x00 + +/* Mode Select constrains related masks value */ +#define SCSI_MODE_SELECT_PF_BIT 0x1 +#define SCSI_MODE_SELECT_PF_MASK 0x10 +#define SCSI_MODE_SELECT_MODE_PAGE_MRIE_BYTE 0x6 +#define SCSI_MODE_SELECT_MODE_PAGE_MRIE_MASK 0x0F +#define SCSI_MODE_SELECT_MODE_PAGE_SPF_MASK 0x40 +#define SCSI_MODE_SELECT_MODE_PAGE_01_AWRE_MASK 0x80 +#define SCSI_MODE_SELECT_MODE_PAGE_01_ARRE_MASK 0x40 +#define SCSI_MODE_SELECT_MODE_PAGE_01_RC_ERBITS_MASK 0x1F +#define SCSI_MODE_SELECT_MODE_PAGE_08_FSW_LBCSS_NVDIS 0xC1 +#define SCSI_MODE_SELECT_MODE_PAGE_1C_PERF_TEST 0x84 +#define SCSI_MODE_SELECT_MODE_PAGE_0A_TST_TMF_RLEC 0xF1 +#define SCSI_MODE_SELECT_MODE_PAGE_0A_MODIFIER 0xF0 +#define SCSI_MODE_SELECT_MODE_PAGE_0A_UA_SWP 0x38 +#define SCSI_MODE_SELECT_MODE_PAGE_0A_TAS_AUTO 0x47 + + +#define SCSI_CONTROL_BYTE_NACA_BIT_ENABLE 0x04 +#define SCSI_MOVE_FUA_BIT_ENABLE 0x08 +#define SCSI_READ_CAPACITY_PMI_BIT_ENABLE 0x01 +#define SCSI_READ_CAPACITY_10_DATA_LENGTH 8 +#define SCSI_READ_CAPACITY_16_DATA_LENGTH 32 + +/* Inquiry constants */ +#define SCSI_INQUIRY_EVPD_ENABLE 0x01 +#define SCSI_INQUIRY_PAGE_CODE_OFFSET 0x02 +#define SCSI_INQUIRY_SUPPORTED_PAGES_PAGE 0x00 +#define SCSI_INQUIRY_UNIT_SERIAL_NUM_PAGE 0x80 +#define SCSI_INQUIRY_DEVICE_ID_PAGE 0x83 +#define SCSI_INQUIRY_ATA_INFORMATION_PAGE 0x89 +#define SCSI_INQUIRY_BLOCK_DEVICE_PAGE 0xB1 +#define SCSI_INQUIRY_BLOCK_DEVICE_LENGTH 0x3C +#define SCSI_INQUIRY_STANDARD_ALLOCATION_LENGTH 0x24 /* 36 */ + +#define SCSI_REQUEST_SENSE_ALLOCATION_LENGTH 0xFC /* 252 */ + +/** Defines the log page codes that are use in gathing Smart data + */ +#define SCSI_LOG_PAGE_SUPPORTED_PAGES 0x00 +#define SCSI_LOG_PAGE_INFORMATION_EXCEPTION 0x2F +#define SCSI_LOG_PAGE_SELF_TEST 0x10 + +/** + * + * + * SCSI_INQUIRY_VPD The following are constants used with vital product data + * inquiry pages. Values are already shifted into the proper nibble location. + */ +#define SCSI_PIV_ENABLE 0x80 +#define SCSI_LUN_ASSOCIATION 0x00 +#define SCSI_TARGET_PORT_ASSOCIATION 0x10 + +#define SCSI_VEN_UNIQUE_IDENTIFIER_TYPE 0x00 +#define SCSI_NAA_IDENTIFIER_TYPE 0x03 + +#define SCSI_T10_IDENTIFIER_TYPE 0x01 +#define SCSI_BINARY_CODE_SET 0x01 +#define SCSI_ASCII_CODE_SET 0x02 +#define SCSI_FC_PROTOCOL_IDENTIFIER 0x00 +#define SCSI_SAS_PROTOCOL_IDENTIFIER 0x60 + +#define SCSI_VERIFY_BYTCHK_ENABLED 0x02 + +#define SCSI_SYNCHRONIZE_CACHE_IMMED_ENABLED 0x02 +/** + * + * + * SCSI_START_STOP_UNIT_POWER_CONDITION_CODES The following are SCSI Start Stop + * Unit command Power Condition codes. + */ +#define SCSI_START_STOP_UNIT_POWER_CONDITION_START_VALID 0x0 +#define SCSI_START_STOP_UNIT_POWER_CONDITION_ACTIVE 0x1 +#define SCSI_START_STOP_UNIT_POWER_CONDITION_IDLE 0x2 +#define SCSI_START_STOP_UNIT_POWER_CONDITION_STANDBY 0x3 +#define SCSI_START_STOP_UNIT_POWER_CONDITION_LU_CONTROL 0x7 +#define SCSI_START_STOP_UNIT_POWER_CONDITION_FORCE_S_CONTROL 0xB + +#define SCSI_START_STOP_UNIT_IMMED_MASK 0x1 +#define SCSI_START_STOP_UNIT_IMMED_SHIFT 0 + +#define SCSI_START_STOP_UNIT_START_BIT_MASK 0x1 +#define SCSI_START_STOP_UNIT_START_BIT_SHIFT 0 + +#define SCSI_START_STOP_UNIT_LOEJ_BIT_MASK 0x2 +#define SCSI_START_STOP_UNIT_LOEJ_BIT_SHIFT 1 + +#define SCSI_START_STOP_UNIT_NO_FLUSH_MASK 0x4 +#define SCSI_START_STOP_UNIT_NO_FLUSH_SHIFT 2 + +#define SCSI_START_STOP_UNIT_POWER_CONDITION_MODIFIER_MASK 0xF +#define SCSI_START_STOP_UNIT_POWER_CONDITION_MODIFIER_SHIFT 0 + +#define SCSI_START_STOP_UNIT_POWER_CONDITION_MASK 0xF0 +#define SCSI_START_STOP_UNIT_POWER_CONDITION_SHIFT 4 + +#define SCSI_LOG_SENSE_PC_FIELD_MASK 0xC0 +#define SCSI_LOG_SENSE_PC_FIELD_SHIFT 6 + +#define SCSI_LOG_SENSE_PAGE_CODE_FIELD_MASK 0x3F +#define SCSI_LOG_SENSE_PAGE_CODE_FIELD_SHIFT 0 + +/** + * + * + * MRIE - Method of reporting informational exceptions codes + */ +#define NO_REPORTING_INFO_EXCEPTION_CONDITION 0x0 +#define ASYNCHRONOUS_EVENT_REPORTING 0x1 +#define ESTABLISH_UNIT_ATTENTION_CONDITION 0x2 +#define CONDITIONALLY_GENERATE_RECOVERED_ERROR 0x3 +#define UNCONDITIONALLY_GENERATE_RECOVERED_ERROR 0x4 +#define GENERATE_NO_SENSE 0x5 +#define REPORT_INFO_EXCEPTION_CONDITION_ON_REQUEST 0x6 + +#define SCSI_INFORMATION_EXCEPTION_DEXCPT_BIT 0x08 + +/* Reassign Blocks masks */ +#define SCSI_REASSIGN_BLOCKS_LONGLBA_BIT 0x02 +#define SCSI_REASSIGN_BLOCKS_LONGLIST_BIT 0x01 + +#endif /* _SCSI_H_ */ + diff --git a/drivers/scsi/isci/core/sati_device.h b/drivers/scsi/isci/core/sati_device.h new file mode 100644 index 000000000000..4d1cfde12cae --- /dev/null +++ b/drivers/scsi/isci/core/sati_device.h @@ -0,0 +1,156 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SATI_DEVICE_H_ +#define _SATI_DEVICE_H_ + +/** + * This file contains all of the defintions for the SATI remote device object. + * Some translations require information to be remembered on a per device + * basis. This information is stored in the object defined in this file. + * + * + */ + +#include "sati_types.h" +#include "intel_ata.h" + +/** + * enum _SATI_DEVICE_STATE - This enumeration depicts the various states + * possible for the a translation remote device object. + * + * + */ +enum sati_device_state { + SATI_DEVICE_STATE_OPERATIONAL, + SATI_DEVICE_STATE_STOPPED, + SATI_DEVICE_STATE_STANDBY, + SATI_DEVICE_STATE_IDLE, + SATI_DEVICE_STATE_DEVICE_FAULT_OCCURRED, + SATI_DEVICE_STATE_FORMAT_UNIT_IN_PROGRESS, + SATI_DEVICE_STATE_SELF_TEST_IN_PROGRESS, + SATI_DEVICE_STATE_SEQUENCE_INCOMPLETE, + SATI_DEVICE_STATE_UNIT_ATTENTION_CONDITION + +}; + +/** + * + * + * SATI_DEVICE_CAPABILITIES These constants define the various capabilities + * that a remote device may support for which there is an impact on translation. + */ +#define SATI_DEVICE_CAP_UDMA_ENABLE 0x00000001 +#define SATI_DEVICE_CAP_NCQ_REQUESTED_ENABLE 0x00000002 +#define SATI_DEVICE_CAP_NCQ_SUPPORTED_ENABLE 0x00000004 +#define SATI_DEVICE_CAP_48BIT_ENABLE 0x00000008 +#define SATI_DEVICE_CAP_DMA_FUA_ENABLE 0x00000010 +#define SATI_DEVICE_CAP_SMART_SUPPORT 0x00000020 +#define SATI_DEVICE_CAP_REMOVABLE_MEDIA 0x00000040 +#define SATI_DEVICE_CAP_SMART_ENABLE 0x00000080 +#define SATI_DEVICE_CAP_WRITE_UNCORRECTABLE_ENABLE 0x00000100 +#define SATI_DEVICE_CAP_MULTIPLE_SECTORS_PER_PHYSCIAL_SECTOR 0x00000200 +#define SATI_DEVICE_CAP_SMART_SELF_TEST_SUPPORT 0x00000400 + + +/** + * struct sati_device - The SATI_DEVICE structure define the state of the + * remote device with respect to translation. + * + * + */ +struct sati_device { + /** + * This field simply dictates the state of the SATI device. + */ + enum sati_device_state state; + + /** + * This field indicates features supported by the remote device that + * impact translation execution. + */ + u16 capabilities; + + /** + * This field indicates the depth of the native command queue supported + * by the device. + */ + u8 ncq_depth; + + /** + * This field stores the additional sense code for a unit attention + * condition. + */ + u8 unit_attention_asc; + + /** + * This field indicates the additional sense code qualifier for a unit + * attention condition. + */ + u8 unit_attention_ascq; + +}; + +void sati_device_construct( + struct sati_device *device, + bool is_ncq_enabled, + u8 max_ncq_depth); + +void sati_device_update_capabilities( + struct sati_device *device, + struct ata_identify_device_data *identify); + +#endif /* _SATI_TRANSLATOR_SEQUENCE_H_ */ + diff --git a/drivers/scsi/isci/core/sati_translator_sequence.h b/drivers/scsi/isci/core/sati_translator_sequence.h new file mode 100644 index 000000000000..592570d50ebd --- /dev/null +++ b/drivers/scsi/isci/core/sati_translator_sequence.h @@ -0,0 +1,304 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SATI_TRANSLATOR_SEQUENCE_H_ +#define _SATI_TRANSLATOR_SEQUENCE_H_ + +/** + * This file contains all of the defintions for the SATI translator sequence. + * A translator sequence is simply a defintion for the various sequences of + * commands that occur in this translator. + * + * + */ + +#include "sati_device.h" + +/** + * enum _SATI_TRANSLATOR_SEQUENCE_TYPE - This enumeration defines the possible + * sequence types for the translator. + * + * + */ +enum sati_translator_sequence_type { + /* SCSI Primary Command (SPC) sequences. */ + SATI_SEQUENCE_REPORT_LUNS, + SATI_SEQUENCE_TEST_UNIT_READY, + SATI_SEQUENCE_INQUIRY_STANDARD, + SATI_SEQUENCE_INQUIRY_SUPPORTED_PAGES, + SATI_SEQUENCE_INQUIRY_SERIAL_NUMBER, + SATI_SEQUENCE_INQUIRY_DEVICE_ID, + SATI_SEQUENCE_INQUIRY_BLOCK_DEVICE, + SATI_SEQUENCE_MODE_SENSE_6_CACHING, + SATI_SEQUENCE_MODE_SENSE_6_INFORMATIONAL_EXCP_CONTROL, + SATI_SEQUENCE_MODE_SENSE_6_READ_WRITE_ERROR, + SATI_SEQUENCE_MODE_SENSE_6_DISCONNECT_RECONNECT, + SATI_SEQUENCE_MODE_SENSE_6_CONTROL, + SATI_SEQUENCE_MODE_SENSE_6_ALL_PAGES, + SATI_SEQUENCE_MODE_SENSE_10_CACHING, + SATI_SEQUENCE_MODE_SENSE_10_INFORMATIONAL_EXCP_CONTROL, + SATI_SEQUENCE_MODE_SENSE_10_READ_WRITE_ERROR, + SATI_SEQUENCE_MODE_SENSE_10_DISCONNECT_RECONNECT, + SATI_SEQUENCE_MODE_SENSE_10_CONTROL, + SATI_SEQUENCE_MODE_SENSE_10_ALL_PAGES, + SATI_SEQUENCE_MODE_SELECT_MODE_PAGE_CACHING, + SATI_SEQUENCE_MODE_SELECT_MODE_POWER_CONDITION, + SATI_SEQUENCE_MODE_SELECT_MODE_INFORMATION_EXCEPT_CONTROL, + + /* Log Sense Sequences */ + SATI_SEQUENCE_LOG_SENSE_SELF_TEST_LOG_PAGE, + SATI_SEQUENCE_LOG_SENSE_EXTENDED_SELF_TEST_LOG_PAGE, + SATI_SEQUENCE_LOG_SENSE_SUPPORTED_LOG_PAGE, + SATI_SEQUENCE_LOG_SENSE_INFO_EXCEPTION_LOG_PAGE, + + /* SCSI Block Command (SBC) sequences. */ + + SATI_SEQUENCE_READ_6, + SATI_SEQUENCE_READ_10, + SATI_SEQUENCE_READ_12, + SATI_SEQUENCE_READ_16, + + SATI_SEQUENCE_READ_CAPACITY_10, + SATI_SEQUENCE_READ_CAPACITY_16, + + SATI_SEQUENCE_SYNCHRONIZE_CACHE, + + SATI_SEQUENCE_VERIFY_10, + SATI_SEQUENCE_VERIFY_12, + SATI_SEQUENCE_VERIFY_16, + + SATI_SEQUENCE_WRITE_6, + SATI_SEQUENCE_WRITE_10, + SATI_SEQUENCE_WRITE_12, + SATI_SEQUENCE_WRITE_16, + + SATI_SEQUENCE_START_STOP_UNIT, + + SATI_SEQUENCE_REASSIGN_BLOCKS, + + /* SCSI Task Requests sequences */ + + SATI_SEQUENCE_LUN_RESET, + + SATI_SEQUENCE_REQUEST_SENSE_SMART_RETURN_STATUS, + SATI_SEQUENCE_REQUEST_SENSE_CHECK_POWER_MODE, + + SATI_SEQUENCE_WRITE_LONG + +}; + +#define SATI_SEQUENCE_TYPE_READ_MIN SATI_SEQUENCE_READ_6 +#define SATI_SEQUENCE_TYPE_READ_MAX SATI_SEQUENCE_READ_16 + +/** + * + * + * SATI_SEQUENCE_STATES These constants depict the various state values + * associated with a translation sequence. + */ +#define SATI_SEQUENCE_STATE_INITIAL 0 +#define SATI_SEQUENCE_STATE_TRANSLATE_DATA 1 +#define SATI_SEQUENCE_STATE_AWAIT_RESPONSE 2 +#define SATI_SEQUENCE_STATE_FINAL 3 +#define SATI_SEQUENCE_STATE_INCOMPLETE 4 + +/** + * + * + * SATI_DATA_DIRECTIONS These constants depict the various types of data + * directions for a translation sequence. Data can flow in/out (read/write) or + * no data at all. + */ +#define SATI_DATA_DIRECTION_NONE 0 +#define SATI_DATA_DIRECTION_IN 1 +#define SATI_DATA_DIRECTION_OUT 2 + +/** + * struct SATI_MODE_SELECT_PROCESSING_STATE - This structure contains all of + * the current processing states for processing mode select 6 and 10 + * commands' parameter fields. + * + * + */ +typedef struct SATI_MODE_SELECT_PROCESSING_STATE { + u8 *mode_pages; + u32 mode_page_offset; + u32 mode_pages_size; + u32 size_of_data_processed; + u32 total_ata_command_sent; + u32 ata_command_sent_for_cmp; /* cmp: current mode page */ + bool current_mode_page_processed; + +} SATI_MODE_SELECT_PROCESSING_STATE_T; + + +enum SATI_REASSIGN_BLOCKS_ATA_COMMAND_STATUS { + SATI_REASSIGN_BLOCKS_READY_TO_SEND, + SATI_REASSIGN_BLOCKS_COMMAND_FAIL, + SATI_REASSIGN_BLOCKS_COMMAND_SUCCESS, +}; + +/** + * struct sati_reassign_blocks_processing_state - This structure contains all + * of the current processing states for processing reassign block command's + * parameter fields. + * + * + */ +struct sati_reassign_blocks_processing_state { + u32 lba_offset; + u32 block_lists_size; + u8 lba_size; + u32 size_of_data_processed; + u32 ata_command_sent_for_current_lba; + bool current_lba_processed; + enum SATI_REASSIGN_BLOCKS_ATA_COMMAND_STATUS ata_command_status; + +}; + +#define SATI_ATAPI_REQUEST_SENSE_CDB_LENGTH 12 + +/** + * struct sati_atapi_data - The SATI_ATAPI_DATA structure is for sati atapi IO + * specific data. + * + * + */ +struct sati_atapi_data { + u8 request_sense_cdb[SATI_ATAPI_REQUEST_SENSE_CDB_LENGTH]; +}; + +/** + * struct sati_translator_sequence - This structure contains all of the + * translation information associated with a particular request. + * + * + */ +struct sati_translator_sequence { + /** + * This field contains the sequence type determined by the SATI. + */ + u8 type; + + /** + * This field indicates the current state for the sequence. + */ + u8 state; + + /** + * This field indicates the data direction (none, read, or write) for + * the translated request. + */ + u8 data_direction; + + /** + * This field contains the SATA/ATA protocol to be utilized during + * the IO transfer. + */ + u8 protocol; + + /** + * This field is utilized for sequences requiring data translation. + * It specifies the amount of data requested by the caller from the + * operation. It's necessary, because at times the user requests less + * data than is available. Thus, we need to avoid overrunning the + * buffer. + */ + u32 allocation_length; + + /** + * This field specifies the amount of data that will actually be + * transfered across the wire for this ATA request. + */ + u32 ata_transfer_length; + + /** + * This field specifies the amount of data bytes that have been + * set in a translation sequence. It will be incremented every time + * a data byte has been set by a sati translation. + */ + u16 number_data_bytes_set; + + /** + * This field indicates whether or not the sense response has been set + * by the translation sequence. + */ + bool is_sense_response_set; + + /** + * This field specifies the remote device context for which this + * translator sequence is destined. + */ + struct sati_device *device; + + /** + * This field is utilized to provide the translator with memory space + * required for translations that utilize multiple requests. + */ + union { + u32 translated_command; + u32 move_sector_count; + u32 scratch; + struct sati_reassign_blocks_processing_state reassign_blocks_process_state; + SATI_MODE_SELECT_PROCESSING_STATE_T process_state; + struct sati_atapi_data sati_atapi_data; + } command_specific_data; + +}; + + + +#endif /* _SATI_TRANSLATOR_SEQUENCE_H_ */ + diff --git a/drivers/scsi/isci/core/sati_types.h b/drivers/scsi/isci/core/sati_types.h new file mode 100644 index 000000000000..b6159e06c280 --- /dev/null +++ b/drivers/scsi/isci/core/sati_types.h @@ -0,0 +1,145 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SATI_TYPES_H_ +#define _SATI_TYPES_H_ + +/** + * This file contains various type definitions to be utilized with SCSI to ATA + * Translation Implementation. + * + * + */ + +/** + * enum _SATI_STATUS - This enumeration defines the possible return values from + * the SATI translation methods. + * + * + */ +enum sati_status { + /** + * This indicates that the translation was supported and occurred + * without error. + */ + SATI_SUCCESS, + + /** + * This indicates that the translation was supported, occurred without + * error, and no additional translation is necessary. This is done in + * conditions where the SCSI command doesn't require any interaction with + * the remote device. + */ + SATI_COMPLETE, + + /** + * This indicated everything SATI_COMPLETE does in addition to the response data + * not using all the memory allocated by the OS. + */ + SATI_COMPLETE_IO_DONE_EARLY, + + /** + * This indicates that translator sequence has finished some specific + * command in the sequence, but additional commands are necessary. + */ + SATI_SEQUENCE_INCOMPLETE, + + /** + * This indicates a general failure has occurred for which no further + * specification information is available. + */ + SATI_FAILURE, + + /** + * This indicates that the result of the IO request indicates a + * failure. The caller should reference the corresponding response + * data for further details. + */ + SATI_FAILURE_CHECK_RESPONSE_DATA, + + /** + * This status indicates that the supplied sequence type doesn't map + * to an existing definition. + */ + SATI_FAILURE_INVALID_SEQUENCE_TYPE, + + /** + * This status indicates that the supplied sequence state doesn't match + * the operation being requested by the user. + */ + SATI_FAILURE_INVALID_STATE + +}; + +#if (!defined(DISABLE_SATI_MODE_SENSE) \ + || !defined(DISABLE_SATI_MODE_SELECT) \ + || !defined(DISABLE_SATI_REQUEST_SENSE)) \ + +#if !defined(ENABLE_SATI_MODE_PAGES) +/** + * + * + * This macro enables the common mode page data structures and code. Currently, + * MODE SENSE, MODE SELECT, and REQUEST SENSE all make reference to this common + * code. As a result, enable the common mode page code if any of these 3 are + * being translated. + */ +#define ENABLE_SATI_MODE_PAGES +#endif /* !defined(ENABLE_SATI_MODE_PAGES) */ + +#endif /* MODE_SENSE/SELECT/REQUEST_SENSE */ + +#endif /* _SATI_TYPES_H_ */ + diff --git a/drivers/scsi/isci/core/sci_base_controller.h b/drivers/scsi/isci/core/sci_base_controller.h new file mode 100644 index 000000000000..36c53406ea5a --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_controller.h @@ -0,0 +1,306 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_CONTROLLER_H_ +#define _SCI_BASE_CONTROLLER_H_ + +#include "intel_sas.h" +#include "sci_controller_constants.h" +#include "sci_base_state.h" +#include "sci_base_memory_descriptor_list.h" +#include "sci_base_state_machine.h" +#include "sci_object.h" + +struct sci_base_memory_descriptor_list; + +/** + * enum sci_base_controller_states - This enumeration depicts all the states + * for the common controller state machine. + * + * + */ +enum sci_base_controller_states { + /** + * Simply the initial state for the base controller state machine. + */ + SCI_BASE_CONTROLLER_STATE_INITIAL = 0, + + /** + * This state indicates that the controller is reset. The memory for + * the controller is in it's initial state, but the controller requires + * initialization. + * This state is entered from the INITIAL state. + * This state is entered from the RESETTING state. + */ + SCI_BASE_CONTROLLER_STATE_RESET, + + /** + * This state is typically an action state that indicates the controller + * is in the process of initialization. In this state no new IO operations + * are permitted. + * This state is entered from the RESET state. + */ + SCI_BASE_CONTROLLER_STATE_INITIALIZING, + + /** + * This state indicates that the controller has been successfully + * initialized. In this state no new IO operations are permitted. + * This state is entered from the INITIALIZING state. + */ + SCI_BASE_CONTROLLER_STATE_INITIALIZED, + + /** + * This state indicates the the controller is in the process of becoming + * ready (i.e. starting). In this state no new IO operations are permitted. + * This state is entered from the INITIALIZED state. + */ + SCI_BASE_CONTROLLER_STATE_STARTING, + + /** + * This state indicates the controller is now ready. Thus, the user + * is able to perform IO operations on the controller. + * This state is entered from the STARTING state. + */ + SCI_BASE_CONTROLLER_STATE_READY, + + /** + * This state is typically an action state that indicates the controller + * is in the process of resetting. Thus, the user is unable to perform + * IO operations on the controller. A reset is considered destructive in + * most cases. + * This state is entered from the READY state. + * This state is entered from the FAILED state. + * This state is entered from the STOPPED state. + */ + SCI_BASE_CONTROLLER_STATE_RESETTING, + + /** + * This state indicates that the controller is in the process of stopping. + * In this state no new IO operations are permitted, but existing IO + * operations are allowed to complete. + * This state is entered from the READY state. + */ + SCI_BASE_CONTROLLER_STATE_STOPPING, + + /** + * This state indicates that the controller has successfully been stopped. + * In this state no new IO operations are permitted. + * This state is entered from the STOPPING state. + */ + SCI_BASE_CONTROLLER_STATE_STOPPED, + + /** + * This state indicates that the controller could not successfully be + * initialized. In this state no new IO operations are permitted. + * This state is entered from the INITIALIZING state. + * This state is entered from the STARTING state. + * This state is entered from the STOPPING state. + * This state is entered from the RESETTING state. + */ + SCI_BASE_CONTROLLER_STATE_FAILED, + + SCI_BASE_CONTROLLER_MAX_STATES + +}; + +/** + * struct sci_base_controller - The base controller object abstracts the fields + * common to all SCI controller objects. + * + * + */ +struct sci_base_controller { + /** + * The field specifies that the parent object for the base controller + * is the base object itself. + */ + struct sci_base_object parent; + + /** + * This field points to the memory descriptor list associated with this + * controller. The MDL indicates the memory requirements necessary for + * this controller object. + */ + struct sci_base_memory_descriptor_list mdl; + + /** + * This field contains the information for the base controller state + * machine. + */ + struct sci_base_state_machine state_machine; +}; + +/* Forward declarations */ +struct sci_base_remote_device; +struct sci_base_request; + +typedef enum sci_status +(*sci_base_controller_handler_t)(struct sci_base_controller *); + +typedef enum sci_status +(*sci_base_controller_timed_handler_t)(struct sci_base_controller *, u32); + +typedef enum sci_status +(*sci_base_controller_request_handler_t)(struct sci_base_controller *, + struct sci_base_remote_device *, + struct sci_base_request *); + +typedef enum sci_status +(*sci_base_controller_start_request_handler_t)(struct sci_base_controller *, + struct sci_base_remote_device *, + struct sci_base_request *, u16); + +/** + * struct sci_base_controller_state_handler - This structure contains all of + * the state handler methods common to base controller state machines. + * Handler methods provide the ability to change the behavior for user + * requests or transitions depending on the state the machine is in. + * + * + */ +struct sci_base_controller_state_handler { + /** + * The start_handler specifies the method invoked when a user attempts to + * start a controller. + */ + sci_base_controller_timed_handler_t start; + + /** + * The stop_handler specifies the method invoked when a user attempts to + * stop a controller. + */ + sci_base_controller_timed_handler_t stop; + + /** + * The reset_handler specifies the method invoked when a user attempts to + * reset a controller. + */ + sci_base_controller_handler_t reset; + + /** + * The initialize_handler specifies the method invoked when a user + * attempts to initialize a controller. + */ + sci_base_controller_handler_t initialize; + + /** + * The start_io_handler specifies the method invoked when a user + * attempts to start an IO request for a controller. + */ + sci_base_controller_start_request_handler_t start_io; + + /** + * The complete_io_handler specifies the method invoked when a user + * attempts to complete an IO request for a controller. + */ + sci_base_controller_request_handler_t complete_io; + + /** + * The continue_io_handler specifies the method invoked when a user + * attempts to continue an IO request for a controller. + */ + sci_base_controller_request_handler_t continue_io; + + /** + * The start_task_handler specifies the method invoked when a user + * attempts to start a task management request for a controller. + */ + sci_base_controller_start_request_handler_t start_task; + + /** + * The complete_task_handler specifies the method invoked when a user + * attempts to complete a task management request for a controller. + */ + sci_base_controller_request_handler_t complete_task; + +}; + +/** + * sci_base_controller_construct() - Construct the base controller + * @this_controller: This parameter specifies the base controller to be + * constructed. + * @state_table: This parameter specifies the table of state definitions to be + * utilized for the controller state machine. + * @mde_array: This parameter specifies the array of memory descriptor entries + * to be managed by this list. + * @mde_array_length: This parameter specifies the size of the array of entries. + * @next_mdl: This parameter specifies a subsequent MDL object to be managed by + * this MDL object. + * @oem_parameters: This parameter specifies the original equipment + * manufacturer parameters to be utilized by this controller object. + * + */ +static inline void sci_base_controller_construct( + struct sci_base_controller *scic_base, + const struct sci_base_state *state_table, + struct sci_physical_memory_descriptor *mdes, + u32 mde_count, + struct sci_base_memory_descriptor_list *next_mdl) +{ + scic_base->parent.private = NULL; + + sci_base_state_machine_construct( + &scic_base->state_machine, + &scic_base->parent, + state_table, + SCI_BASE_CONTROLLER_STATE_INITIAL + ); + + sci_base_mdl_construct(&scic_base->mdl, mdes, mde_count, next_mdl); + + sci_base_state_machine_start(&scic_base->state_machine); +} + +#endif /* _SCI_BASE_CONTROLLER_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_memory_descriptor_list.c b/drivers/scsi/isci/core/sci_base_memory_descriptor_list.c new file mode 100644 index 000000000000..86ae6a855399 --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_memory_descriptor_list.c @@ -0,0 +1,159 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the base implementation for the memory descriptor list. + * This is currently comprised of MDL iterator methods. + * + * + */ + +#include "sci_environment.h" +#include "sci_base_memory_descriptor_list.h" + +/* + * ****************************************************************************** + * * P U B L I C M E T H O D S + * ****************************************************************************** */ + +void sci_mdl_first_entry( + struct sci_base_memory_descriptor_list *base_mdl) +{ + base_mdl->next_index = 0; + + /* + * If this MDL is managing another MDL, then recursively rewind that MDL + * object as well. */ + if (base_mdl->next_mdl != SCI_INVALID_HANDLE) + sci_mdl_first_entry(base_mdl->next_mdl); +} + + +void sci_mdl_next_entry( + struct sci_base_memory_descriptor_list *base_mdl) +{ + /* + * If there is at least one more entry left in the array, then change + * the next pointer to it. */ + if (base_mdl->next_index < base_mdl->length) + base_mdl->next_index++; + else if (base_mdl->next_index == base_mdl->length) { + /* + * This MDL has exhausted it's set of entries. If this MDL is managing + * another MDL, then start iterating through that MDL. */ + if (base_mdl->next_mdl != SCI_INVALID_HANDLE) + sci_mdl_next_entry(base_mdl->next_mdl); + } +} + + +struct sci_physical_memory_descriptor *sci_mdl_get_current_entry( + struct sci_base_memory_descriptor_list *base_mdl) +{ + if (base_mdl->next_index < base_mdl->length) + return &base_mdl->mde_array[base_mdl->next_index]; + else if (base_mdl->next_index == base_mdl->length) { + /* + * This MDL has exhausted it's set of entries. If this MDL is managing + * another MDL, then return it's current entry. */ + if (base_mdl->next_mdl != SCI_INVALID_HANDLE) + return sci_mdl_get_current_entry(base_mdl->next_mdl); + } + + return NULL; +} + +/* + * ****************************************************************************** + * * P R O T E C T E D M E T H O D S + * ****************************************************************************** */ + +void sci_base_mdl_construct( + struct sci_base_memory_descriptor_list *mdl, + struct sci_physical_memory_descriptor *mde_array, + u32 mde_array_length, + struct sci_base_memory_descriptor_list *next_mdl) +{ + mdl->length = mde_array_length; + mdl->mde_array = mde_array; + mdl->next_index = 0; + mdl->next_mdl = next_mdl; +} + +/* --------------------------------------------------------------------------- */ + +bool sci_base_mde_is_valid( + struct sci_physical_memory_descriptor *mde, + u32 alignment, + u32 size, + u16 attributes) +{ + /* Only need the lower 32 bits to ensure alignment is met. */ + u32 physical_address = lower_32_bits(mde->physical_address); + + if ( + ((((unsigned long)mde->virtual_address) & (alignment - 1)) != 0) + || ((physical_address & (alignment - 1)) != 0) + || (mde->constant_memory_alignment != alignment) + || (mde->constant_memory_size != size) + || (mde->virtual_address == NULL) + || (mde->constant_memory_attributes != attributes) + ) { + return false; + } + + return true; +} + diff --git a/drivers/scsi/isci/core/sci_base_memory_descriptor_list.h b/drivers/scsi/isci/core/sci_base_memory_descriptor_list.h new file mode 100644 index 000000000000..257d6e368b4a --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_memory_descriptor_list.h @@ -0,0 +1,155 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_MEMORY_DESCRIPTOR_LIST_H_ +#define _SCI_BASE_MEMORY_DESCRIPTOR_LIST_H_ + +/** + * This file contains the protected interface structures, constants and + * interface methods for the struct sci_base_memory_descriptor_list object. + * + * + */ + + +#include "sci_types.h" +#include "sci_memory_descriptor_list.h" + + +/** + * struct sci_base_memory_descriptor_list - This structure contains all of the + * fields necessary to implement a simple stack for managing the list of + * available controller indices. + * + * + */ +struct sci_base_memory_descriptor_list { + /** + * This field indicates the length of the memory descriptor entry array. + */ + u32 length; + + /** + * This field is utilized to provide iterator pattern functionality. + * It indicates the index of the next memory descriptor in the iteration. + */ + u32 next_index; + + /** + * This field will point to the list of memory descriptors. + */ + struct sci_physical_memory_descriptor *mde_array; + + /** + * This field simply allows a user to chain memory descriptor lists + * together if desired. This field will be initialized to + * SCI_INVALID_HANDLE. + */ + struct sci_base_memory_descriptor_list *next_mdl; + +}; + +/** + * sci_base_mdl_construct() - This method is invoked to construct an memory + * descriptor list. It initializes the fields of the MDL. + * @mdl: This parameter specifies the memory descriptor list to be constructed. + * @mde_array: This parameter specifies the array of memory descriptor entries + * to be managed by this list. + * @mde_array_length: This parameter specifies the size of the array of entries. + * @next_mdl: This parameter specifies a subsequent MDL object to be managed by + * this MDL object. + * + * none. + */ +void sci_base_mdl_construct( + struct sci_base_memory_descriptor_list *mdl, + struct sci_physical_memory_descriptor *mde_array, + u32 mde_array_length, + struct sci_base_memory_descriptor_list *next_mdl); + +/** + * sci_base_mde_construct() - + * + * This macro constructs an memory descriptor entry with the given alignment + * and size + */ +#define sci_base_mde_construct(mde, alignment, size, attributes) \ + { \ + (mde)->constant_memory_alignment = (alignment); \ + (mde)->constant_memory_size = (size); \ + (mde)->constant_memory_attributes = (attributes); \ + } + +/** + * sci_base_mde_is_valid() - This method validates that the memory descriptor + * is correctly filled out by the SCI User + * @mde: This parameter is the mde entry to validate + * @alignment: This parameter specifies the expected alignment of the memory + * for the mde. + * @size: This parameter specifies the memory size expected for the mde its + * value should not have been changed by the SCI User. + * @attributes: This parameter specifies the attributes for the memory + * descriptor provided. + * + * bool This method returns an indication as to whether the supplied MDE is + * valid or not. true The MDE is valid. false The MDE is not valid. + */ +bool sci_base_mde_is_valid( + struct sci_physical_memory_descriptor *mde, + u32 alignment, + u32 size, + u16 attributes); + +#endif /* _SCI_BASE_MEMORY_DESCRIPTOR_LIST_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_phy.h b/drivers/scsi/isci/core/sci_base_phy.h new file mode 100644 index 000000000000..6c0d9bbbc95f --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_phy.h @@ -0,0 +1,205 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_PHY_H_ +#define _SCI_BASE_PHY_H_ + +/** + * This file contains all of the structures, constants, and methods common to + * all phy object definitions. + * + * + */ + +#include "sci_base_state_machine.h" + +/** + * enum sci_base_phy_states - This enumeration depicts the standard states + * common to all phy state machine implementations. + * + * + */ +enum sci_base_phy_states { + /** + * Simply the initial state for the base domain state machine. + */ + SCI_BASE_PHY_STATE_INITIAL, + + /** + * This state indicates that the phy has successfully been stopped. + * In this state no new IO operations are permitted on this phy. + * This state is entered from the INITIAL state. + * This state is entered from the STARTING state. + * This state is entered from the READY state. + * This state is entered from the RESETTING state. + */ + SCI_BASE_PHY_STATE_STOPPED, + + /** + * This state indicates that the phy is in the process of becomming + * ready. In this state no new IO operations are permitted on this phy. + * This state is entered from the STOPPED state. + * This state is entered from the READY state. + * This state is entered from the RESETTING state. + */ + SCI_BASE_PHY_STATE_STARTING, + + /** + * This state indicates the the phy is now ready. Thus, the user + * is able to perform IO operations utilizing this phy as long as it + * is currently part of a valid port. + * This state is entered from the STARTING state. + */ + SCI_BASE_PHY_STATE_READY, + + /** + * This state indicates that the phy is in the process of being reset. + * In this state no new IO operations are permitted on this phy. + * This state is entered from the READY state. + */ + SCI_BASE_PHY_STATE_RESETTING, + + /** + * Simply the final state for the base phy state machine. + */ + SCI_BASE_PHY_STATE_FINAL, + + SCI_BASE_PHY_MAX_STATES + +}; + +/** + * struct sci_base_phy - This structure defines all of the fields common to PHY + * objects. + * + * + */ +struct sci_base_phy { + /** + * This field depicts the parent object (struct sci_base_object) for the phy. + */ + struct sci_base_object parent; + + /** + * This field contains the information for the base phy state machine. + */ + struct sci_base_state_machine state_machine; +}; + +typedef enum sci_status (*SCI_BASE_PHY_HANDLER_T)( + struct sci_base_phy * + ); + +/** + * struct sci_base_phy_state_handler - This structure contains all of the state + * handler methods common to base phy state machines. Handler methods + * provide the ability to change the behavior for user requests or + * transitions depending on the state the machine is in. + * + * + */ +struct sci_base_phy_state_handler { + /** + * The start_handler specifies the method invoked when there is an + * attempt to start a phy. + */ + SCI_BASE_PHY_HANDLER_T start_handler; + + /** + * The stop_handler specifies the method invoked when there is an + * attempt to stop a phy. + */ + SCI_BASE_PHY_HANDLER_T stop_handler; + + /** + * The reset_handler specifies the method invoked when there is an + * attempt to reset a phy. + */ + SCI_BASE_PHY_HANDLER_T reset_handler; + + /** + * The destruct_handler specifies the method invoked when attempting to + * destruct a phy. + */ + SCI_BASE_PHY_HANDLER_T destruct_handler; + +}; + +/** + * sci_base_phy_construct() - Construct the base phy + * @this_phy: This parameter specifies the base phy to be constructed. + * @state_table: This parameter specifies the table of state definitions to be + * utilized for the phy state machine. + * + */ +static inline void sci_base_phy_construct( + struct sci_base_phy *base_phy, + const struct sci_base_state *state_table) +{ + base_phy->parent.private = NULL; + sci_base_state_machine_construct( + &base_phy->state_machine, + &base_phy->parent, + state_table, + SCI_BASE_PHY_STATE_INITIAL + ); + + sci_base_state_machine_start( + &base_phy->state_machine + ); +} + + +#endif /* _SCI_BASE_PHY_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_port.h b/drivers/scsi/isci/core/sci_base_port.h new file mode 100644 index 000000000000..4e2031d195f0 --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_port.h @@ -0,0 +1,203 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_PORT_H_ +#define _SCI_BASE_PORT_H_ + +#include "sci_base_state_machine.h" +#include "sci_object.h" + +/** + * enum sci_base_port_states - This enumeration depicts all the states for the + * common port state machine. + * + * + */ +enum sci_base_port_states { + /** + * This state indicates that the port has successfully been stopped. + * In this state no new IO operations are permitted. + * This state is entered from the STOPPING state. + */ + SCI_BASE_PORT_STATE_STOPPED, + + /** + * This state indicates that the port is in the process of stopping. + * In this state no new IO operations are permitted, but existing IO + * operations are allowed to complete. + * This state is entered from the READY state. + */ + SCI_BASE_PORT_STATE_STOPPING, + + /** + * This state indicates the port is now ready. Thus, the user is + * able to perform IO operations on this port. + * This state is entered from the STARTING state. + */ + SCI_BASE_PORT_STATE_READY, + + /** + * This state indicates the port is in the process of performing a hard + * reset. Thus, the user is unable to perform IO operations on this + * port. + * This state is entered from the READY state. + */ + SCI_BASE_PORT_STATE_RESETTING, + + /** + * This state indicates the port has failed a reset request. This state + * is entered when a port reset request times out. + * This state is entered from the RESETTING state. + */ + SCI_BASE_PORT_STATE_FAILED, + + SCI_BASE_PORT_MAX_STATES + +}; + +/** + * struct sci_base_port - The base port object abstracts the fields common to + * all SCI port objects. + * + * + */ +struct sci_base_port { + /** + * The field specifies that the parent object for the base controller + * is the base object itself. + */ + struct sci_base_object parent; + + /** + * This field contains the information for the base port state machine. + */ + struct sci_base_state_machine state_machine; +}; + +struct sci_base_phy; + +typedef enum sci_status (*SCI_BASE_PORT_HANDLER_T)( + struct sci_base_port * + ); + +typedef enum sci_status (*SCI_BASE_PORT_PHY_HANDLER_T)( + struct sci_base_port *, + struct sci_base_phy * + ); + +typedef enum sci_status (*SCI_BASE_PORT_RESET_HANDLER_T)( + struct sci_base_port *, + u32 timeout + ); + +/** + * struct sci_base_port_state_handler - This structure contains all of the + * state handler methods common to base port state machines. Handler + * methods provide the ability to change the behavior for user requests or + * transitions depending on the state the machine is in. + * + * + */ +struct sci_base_port_state_handler { + /** + * The start_handler specifies the method invoked when a user attempts to + * start a port. + */ + SCI_BASE_PORT_HANDLER_T start_handler; + + /** + * The stop_handler specifies the method invoked when a user attempts to + * stop a port. + */ + SCI_BASE_PORT_HANDLER_T stop_handler; + + /** + * The destruct_handler specifies the method invoked when attempting to + * destruct a port. + */ + SCI_BASE_PORT_HANDLER_T destruct_handler; + + /** + * The reset_handler specifies the method invoked when a user attempts to + * hard reset a port. + */ + SCI_BASE_PORT_RESET_HANDLER_T reset_handler; + + /** + * The add_phy_handler specifies the method invoked when a user attempts to + * add another phy into the port. + */ + SCI_BASE_PORT_PHY_HANDLER_T add_phy_handler; + + /** + * The remove_phy_handler specifies the method invoked when a user + * attempts to remove a phy from the port. + */ + SCI_BASE_PORT_PHY_HANDLER_T remove_phy_handler; + +}; + +/** + * sci_base_port_construct() - Construct the base port object + * @this_port: This parameter specifies the base port to be constructed. + * @state_table: This parameter specifies the table of state definitions to be + * utilized for the domain state machine. + * + */ +void sci_base_port_construct( + struct sci_base_port *this_port, + const struct sci_base_state *state_table); + +#endif /* _SCI_BASE_PORT_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_remote_device.h b/drivers/scsi/isci/core/sci_base_remote_device.h new file mode 100644 index 000000000000..fe6614b8ef5b --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_remote_device.h @@ -0,0 +1,277 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_REMOTE_DEVICE_H_ +#define _SCI_BASE_REMOTE_DEVICE_H_ + +/** + * This file contains all of the structures, constants, and methods common to + * all remote device object definitions. + * + * + */ + +#include "sci_base_state_machine.h" + +struct sci_base_request; + +/** + * enum sci_base_remote_device_states - This enumeration depicts all the states + * for the common remote device state machine. + * + * + */ +enum sci_base_remote_device_states { + /** + * Simply the initial state for the base remote device state machine. + */ + SCI_BASE_REMOTE_DEVICE_STATE_INITIAL, + + /** + * This state indicates that the remote device has successfully been + * stopped. In this state no new IO operations are permitted. + * This state is entered from the INITIAL state. + * This state is entered from the STOPPING state. + */ + SCI_BASE_REMOTE_DEVICE_STATE_STOPPED, + + /** + * This state indicates the the remote device is in the process of + * becoming ready (i.e. starting). In this state no new IO operations + * are permitted. + * This state is entered from the STOPPED state. + */ + SCI_BASE_REMOTE_DEVICE_STATE_STARTING, + + /** + * This state indicates the remote device is now ready. Thus, the user + * is able to perform IO operations on the remote device. + * This state is entered from the STARTING state. + */ + SCI_BASE_REMOTE_DEVICE_STATE_READY, + + /** + * This state indicates that the remote device is in the process of + * stopping. In this state no new IO operations are permitted, but + * existing IO operations are allowed to complete. + * This state is entered from the READY state. + * This state is entered from the FAILED state. + */ + SCI_BASE_REMOTE_DEVICE_STATE_STOPPING, + + /** + * This state indicates that the remote device has failed. + * In this state no new IO operations are permitted. + * This state is entered from the INITIALIZING state. + * This state is entered from the READY state. + */ + SCI_BASE_REMOTE_DEVICE_STATE_FAILED, + + /** + * This state indicates the device is being reset. + * In this state no new IO operations are permitted. + * This state is entered from the READY state. + */ + SCI_BASE_REMOTE_DEVICE_STATE_RESETTING, + + /** + * Simply the final state for the base remote device state machine. + */ + SCI_BASE_REMOTE_DEVICE_STATE_FINAL, + + SCI_BASE_REMOTE_DEVICE_MAX_STATES + +}; + +/** + * struct sci_base_remote_device - The base remote device object abstracts the + * fields common to all SCI remote device objects. + * + * + */ +struct sci_base_remote_device { + /** + * The field specifies that the parent object for the base remote + * device is the base object itself. + */ + struct sci_base_object parent; + + /** + * This field contains the information for the base remote device state + * machine. + */ + struct sci_base_state_machine state_machine; +}; + + +typedef enum sci_status (*SCI_BASE_REMOTE_DEVICE_HANDLER_T)( + struct sci_base_remote_device * + ); + +typedef enum sci_status (*SCI_BASE_REMOTE_DEVICE_REQUEST_HANDLER_T)( + struct sci_base_remote_device *, + struct sci_base_request * + ); + +typedef enum sci_status (*SCI_BASE_REMOTE_DEVICE_HIGH_PRIORITY_REQUEST_COMPLETE_HANDLER_T)( + struct sci_base_remote_device *, + struct sci_base_request *, + void *, + enum sci_io_status + ); + +/** + * struct sci_base_remote_device_state_handler - This structure contains all of + * the state handler methods common to base remote device state machines. + * Handler methods provide the ability to change the behavior for user + * requests or transitions depending on the state the machine is in. + * + * + */ +struct sci_base_remote_device_state_handler { + /** + * The start_handler specifies the method invoked when a user attempts to + * start a remote device. + */ + SCI_BASE_REMOTE_DEVICE_HANDLER_T start_handler; + + /** + * The stop_handler specifies the method invoked when a user attempts to + * stop a remote device. + */ + SCI_BASE_REMOTE_DEVICE_HANDLER_T stop_handler; + + /** + * The fail_handler specifies the method invoked when a remote device + * failure has occurred. A failure may be due to an inability to + * initialize/configure the device. + */ + SCI_BASE_REMOTE_DEVICE_HANDLER_T fail_handler; + + /** + * The destruct_handler specifies the method invoked when attempting to + * destruct a remote device. + */ + SCI_BASE_REMOTE_DEVICE_HANDLER_T destruct_handler; + + /** + * The reset handler specifies the method invloked when requesting to reset a + * remote device. + */ + SCI_BASE_REMOTE_DEVICE_HANDLER_T reset_handler; + + /** + * The reset complete handler specifies the method invloked when reporting + * that a reset has completed to the remote device. + */ + SCI_BASE_REMOTE_DEVICE_HANDLER_T reset_complete_handler; + + /** + * The start_io_handler specifies the method invoked when a user + * attempts to start an IO request for a remote device. + */ + SCI_BASE_REMOTE_DEVICE_REQUEST_HANDLER_T start_io_handler; + + /** + * The complete_io_handler specifies the method invoked when a user + * attempts to complete an IO request for a remote device. + */ + SCI_BASE_REMOTE_DEVICE_REQUEST_HANDLER_T complete_io_handler; + + /** + * The continue_io_handler specifies the method invoked when a user + * attempts to continue an IO request for a remote device. + */ + SCI_BASE_REMOTE_DEVICE_REQUEST_HANDLER_T continue_io_handler; + + /** + * The start_task_handler specifies the method invoked when a user + * attempts to start a task management request for a remote device. + */ + SCI_BASE_REMOTE_DEVICE_REQUEST_HANDLER_T start_task_handler; + + /** + * The complete_task_handler specifies the method invoked when a user + * attempts to complete a task management request for a remote device. + */ + SCI_BASE_REMOTE_DEVICE_REQUEST_HANDLER_T complete_task_handler; + +}; + +/** + * sci_base_remote_device_construct() - Construct the base remote device + * @this_remote_device: This parameter specifies the base remote device to be + * constructed. + * @state_table: This parameter specifies the table of state definitions to be + * utilized for the remote device state machine. + * + */ +static inline void sci_base_remote_device_construct( + struct sci_base_remote_device *base_dev, + const struct sci_base_state *state_table) +{ + base_dev->parent.private = NULL; + sci_base_state_machine_construct( + &base_dev->state_machine, + &base_dev->parent, + state_table, + SCI_BASE_REMOTE_DEVICE_STATE_INITIAL + ); + + sci_base_state_machine_start( + &base_dev->state_machine + ); +} +#endif /* _SCI_BASE_REMOTE_DEVICE_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_request.h b/drivers/scsi/isci/core/sci_base_request.h new file mode 100644 index 000000000000..d1b2195b228a --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_request.h @@ -0,0 +1,195 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_REQUST_H_ +#define _SCI_BASE_REQUST_H_ + +/** + * This file contains all of the constants, types, and method declarations for + * the SCI base IO and task request objects. + * + * + */ + +#include "sci_base_state_machine.h" + +/** + * enum sci_base_request_states - This enumeration depicts all the states for + * the common request state machine. + * + * + */ +enum sci_base_request_states { + /** + * Simply the initial state for the base request state machine. + */ + SCI_BASE_REQUEST_STATE_INITIAL, + + /** + * This state indicates that the request has been constructed. This state + * is entered from the INITIAL state. + */ + SCI_BASE_REQUEST_STATE_CONSTRUCTED, + + /** + * This state indicates that the request has been started. This state is + * entered from the CONSTRUCTED state. + */ + SCI_BASE_REQUEST_STATE_STARTED, + + /** + * This state indicates that the request has completed. + * This state is entered from the STARTED state. This state is entered from + * the ABORTING state. + */ + SCI_BASE_REQUEST_STATE_COMPLETED, + + /** + * This state indicates that the request is in the process of being + * terminated/aborted. + * This state is entered from the CONSTRUCTED state. + * This state is entered from the STARTED state. + */ + SCI_BASE_REQUEST_STATE_ABORTING, + + /** + * Simply the final state for the base request state machine. + */ + SCI_BASE_REQUEST_STATE_FINAL, +}; + +/** + * struct sci_base_request - The base request object abstracts the fields + * common to all SCI IO and task request objects. + * + * + */ +struct sci_base_request { + /** + * The field specifies that the parent object for the base request is the + * base object itself. + */ + struct sci_base_object parent; + + /** + * This field contains the information for the base request state machine. + */ + struct sci_base_state_machine state_machine; +}; + +typedef enum sci_status (*SCI_BASE_REQUEST_HANDLER_T)( + struct sci_base_request *this_request + ); + +/** + * struct sci_base_request_state_handler - This structure contains all of the + * state handler methods common to base IO and task request state machines. + * Handler methods provide the ability to change the behavior for user + * requests or transitions depending on the state the machine is in. + * + * + */ +struct sci_base_request_state_handler { + /** + * The start_handler specifies the method invoked when a user attempts to + * start a request. + */ + SCI_BASE_REQUEST_HANDLER_T start_handler; + + /** + * The abort_handler specifies the method invoked when a user attempts to + * abort a request. + */ + SCI_BASE_REQUEST_HANDLER_T abort_handler; + + /** + * The complete_handler specifies the method invoked when a user attempts to + * complete a request. + */ + SCI_BASE_REQUEST_HANDLER_T complete_handler; + + /** + * The destruct_handler specifies the method invoked when a user attempts to + * destruct a request. + */ + SCI_BASE_REQUEST_HANDLER_T destruct_handler; + +}; + +/** + * sci_base_request_construct() - Construct the base request. + * @this_request: This parameter specifies the base request to be constructed. + * @state_table: This parameter specifies the table of state definitions to be + * utilized for the request state machine. + * + */ +static inline void sci_base_request_construct( + struct sci_base_request *base_req, + const struct sci_base_state *my_state_table) +{ + base_req->parent.private = NULL; + sci_base_state_machine_construct( + &base_req->state_machine, + &base_req->parent, + my_state_table, + SCI_BASE_REQUEST_STATE_INITIAL + ); + + sci_base_state_machine_start( + &base_req->state_machine + ); +} + +#endif /* _SCI_BASE_REQUST_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_state.h b/drivers/scsi/isci/core/sci_base_state.h new file mode 100644 index 000000000000..d6b9c1a951b1 --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_state.h @@ -0,0 +1,90 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_STATE_H_ +#define _SCI_BASE_STATE_H_ + +#include "sci_object.h" + +typedef void (*SCI_BASE_STATE_HANDLER_T)( + void + ); + +typedef void (*SCI_STATE_TRANSITION_T)( + struct sci_base_object *base_object + ); + +/** + * struct sci_base_state - The base state object abstracts the fields common to + * all state objects defined in SCI. + * + * + */ +struct sci_base_state { + /** + * This field is a function pointer that defines the method to be + * invoked when the state is entered. + */ + SCI_STATE_TRANSITION_T enter_state; + + /** + * This field is a function pointer that defines the method to be + * invoked when the state is exited. + */ + SCI_STATE_TRANSITION_T exit_state; + +}; + +#endif /* _SCI_BASE_STATE_H_ */ diff --git a/drivers/scsi/isci/core/sci_base_state_machine.c b/drivers/scsi/isci/core/sci_base_state_machine.c new file mode 100644 index 000000000000..5b1e8da55fe6 --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_state_machine.c @@ -0,0 +1,182 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains all of the functionality common to all state machine + * object implementations. + * + * + */ + +#include "sci_base_state_machine.h" + +static void sci_state_machine_exit_state(struct sci_base_state_machine *sm) +{ + u32 state = sm->current_state_id; + SCI_STATE_TRANSITION_T exit = sm->state_table[state].exit_state; + + if (exit) + exit(sm->state_machine_owner); +} + +static void sci_state_machine_enter_state(struct sci_base_state_machine *sm) +{ + u32 state = sm->current_state_id; + SCI_STATE_TRANSITION_T enter = sm->state_table[state].enter_state; + + if (enter) + enter(sm->state_machine_owner); +} + +/* + * ****************************************************************************** + * * P R O T E C T E D M E T H O D S + * ****************************************************************************** */ + +/** + * This method will set the initial state and state table for the state + * machine. The caller should follow this request with the initialize + * request to cause the state machine to start. + * @sm: This parameter provides the state machine object to be + * constructed. + * @state_machine_owner: This parameter indicates the object that is owns the + * state machine being constructed. + * @state_table: This parameter specifies the table of state objects that is + * managed by this state machine. + * @initial_state: This parameter specifies the value of the initial state for + * this state machine. + * + */ +void sci_base_state_machine_construct(struct sci_base_state_machine *sm, + struct sci_base_object *owner, + const struct sci_base_state *state_table, + u32 initial_state) +{ + sm->state_machine_owner = owner; + sm->initial_state_id = initial_state; + sm->previous_state_id = initial_state; + sm->current_state_id = initial_state; + sm->state_table = state_table; +} + +/** + * This method will cause the state machine to enter the initial state. + * @sm: This parameter specifies the state machine that is to + * be started. + * + * sci_base_state_machine_construct() for how to set the initial state none + */ +void sci_base_state_machine_start(struct sci_base_state_machine *sm) +{ + sm->current_state_id = sm->initial_state_id; +#if defined(SCI_BASE_ENABLE_SUBJECT_NOTIFICATION) + sci_base_subject_notify(&sm->parent); +#endif + sci_state_machine_enter_state(sm); +} + +/** + * This method will cause the state machine to exit it's current state only. + * @sm: This parameter specifies the state machine that is to + * be stopped. + * + */ +void sci_base_state_machine_stop( + struct sci_base_state_machine *sm) +{ + sci_state_machine_exit_state(sm); +#if defined(SCI_BASE_ENABLE_SUBJECT_NOTIFICATION) + sci_base_subject_notify(&sm->parent); +#endif +} + +/** + * This method performs an update to the current state of the state machine. + * @sm: This parameter specifies the state machine for which + * the caller wishes to perform a state change. + * @next_state: This parameter specifies the new state for the state machine. + * + */ +void sci_base_state_machine_change_state( + struct sci_base_state_machine *sm, + u32 next_state) +{ + sci_state_machine_exit_state(sm); + + sm->previous_state_id = sm->current_state_id; + sm->current_state_id = next_state; + +#if defined(SCI_BASE_ENABLE_SUBJECT_NOTIFICATION) + /* Notify of the state change prior to entering the state. */ + sci_base_subject_notify(&sm->parent); +#endif + + sci_state_machine_enter_state(sm); +} + +/** + * This method simply returns the current state of the state machine to the + * caller. + * @sm: This parameter specifies the state machine for which to + * retrieve the current state. + * + * This method returns a u32 value indicating the current state for the + * supplied state machine. + */ +u32 sci_base_state_machine_get_state(struct sci_base_state_machine *sm) +{ + return sm->current_state_id; +} + diff --git a/drivers/scsi/isci/core/sci_base_state_machine.h b/drivers/scsi/isci/core/sci_base_state_machine.h new file mode 100644 index 000000000000..cee38bd3d127 --- /dev/null +++ b/drivers/scsi/isci/core/sci_base_state_machine.h @@ -0,0 +1,139 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_BASE_STATE_MACHINE_H_ +#define _SCI_BASE_STATE_MACHINE_H_ + +/** + * This file contains all structures, constants, or method declarations common + * to all state machines defined in SCI. + * + * + */ + + +#include "sci_base_state.h" + + +/** + * SET_STATE_HANDLER() - + * + * This macro simply provides simplified retrieval of an objects state handler. + */ +#define SET_STATE_HANDLER(object, table, state) \ + (object)->state_handlers = &(table)[(state)] + +/** + * struct sci_base_state_machine - This structure defines the fields common to + * all state machines. + * + * + */ +struct sci_base_state_machine { + /** + * This field points to the start of the state machine's state table. + */ + const struct sci_base_state *state_table; + + /** + * This field points to the object to which this state machine is + * associated. It serves as a cookie to be provided to the state + * enter/exit methods. + */ + struct sci_base_object *state_machine_owner; + + /** + * This field simply indicates the state value for the state machine's + * initial state. + */ + u32 initial_state_id; + + /** + * This field indicates the current state of the state machine. + */ + u32 current_state_id; + + /** + * This field indicates the previous state of the state machine. + */ + u32 previous_state_id; + +}; + +/* + * ****************************************************************************** + * * P R O T E C T E D M E T H O D S + * ****************************************************************************** */ + +void sci_base_state_machine_construct( + struct sci_base_state_machine *this_state_machine, + struct sci_base_object *state_machine_owner, + const struct sci_base_state *state_table, + u32 initial_state); + +void sci_base_state_machine_start( + struct sci_base_state_machine *this_state_machine); + +void sci_base_state_machine_stop( + struct sci_base_state_machine *this_state_machine); + +void sci_base_state_machine_change_state( + struct sci_base_state_machine *this_state_machine, + u32 next_state); + +u32 sci_base_state_machine_get_state( + struct sci_base_state_machine *this_state_machine); + +#endif /* _SCI_BASE_STATE_MACHINE_H_ */ diff --git a/drivers/scsi/isci/core/sci_controller.h b/drivers/scsi/isci/core/sci_controller.h new file mode 100644 index 000000000000..26c3548cbf7a --- /dev/null +++ b/drivers/scsi/isci/core/sci_controller.h @@ -0,0 +1,100 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_CONTROLLER_H_ +#define _SCI_CONTROLLER_H_ + +/** + * This file contains all of the interface methods that can be called by an SCI + * user on all SCI controller objects. + * + * + */ + + +#include "sci_types.h" + +struct sci_base_memory_descriptor_list; +struct scic_sds_controller; + +#define SCI_CONTROLLER_INVALID_IO_TAG 0xFFFF + +/** + * sci_controller_get_memory_descriptor_list_handle() - This method simply + * returns a handle for the memory descriptor list associated with the + * supplied controller. The descriptor list provides DMA safe/capable + * memory requirements for this controller. + * @controller: This parameter specifies the controller for which to retrieve + * the DMA safe memory descriptor list. + * + * The user must adhere to the alignment requirements specified in memory + * descriptor. In situations where the operating environment does not offer + * memory allocation utilities supporting alignment, then it is the + * responsibility of the user to manually align the memory buffer for SCI. + * Thus, the user may have to allocate a larger buffer to meet the alignment. + * Additionally, the user will need to remember the actual memory allocation + * addresses in order to ensure the memory can be properly freed when necessary + * to do so. This method will return a valid handle, but the MDL may not be + * accurate until after the user has invoked the associated + * sci_controller_initialize() routine. A pointer to a physical memory + * descriptor array. + */ +struct sci_base_memory_descriptor_list * + sci_controller_get_memory_descriptor_list_handle( + struct scic_sds_controller *controller); + + +#endif /* _SCI_CONTROLLER_H_ */ + diff --git a/drivers/scsi/isci/core/sci_controller_constants.h b/drivers/scsi/isci/core/sci_controller_constants.h new file mode 100644 index 000000000000..06c34c7bdd5f --- /dev/null +++ b/drivers/scsi/isci/core/sci_controller_constants.h @@ -0,0 +1,215 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_CONTROLLER_CONSTANTS_H_ +#define _SCI_CONTROLLER_CONSTANTS_H_ + +/** + * This file contains constant values that change based on the type of core or + * framework being managed. These constants are exported in order to + * provide the user with information as to the bounds (i.e. how many) of + * specific objects. + * + * + */ + + +#ifdef SCIC_SDS_4_ENABLED + +#ifndef SCI_MAX_PHYS +/** + * + * + * This constant defines the maximum number of phy objects that can be + * supported for the SCU Driver Standard (SDS) library. This is tied directly + * to silicon capabilities. + */ +#define SCI_MAX_PHYS (4) +#endif + +#ifndef SCI_MAX_PORTS +/** + * + * + * This constant defines the maximum number of port objects that can be + * supported for the SCU Driver Standard (SDS) library. This is tied directly + * to silicon capabilities. + */ +#define SCI_MAX_PORTS SCI_MAX_PHYS +#endif + +#ifndef SCI_MIN_SMP_PHYS +/** + * + * + * This constant defines the minimum number of SMP phy objects that can be + * supported for a single expander level. This was determined by using 36 + * physical phys and room for 2 virtual phys. + */ +#define SCI_MIN_SMP_PHYS (38) +#endif + +#ifndef SCI_MAX_SMP_PHYS +/** + * + * + * This constant defines the maximum number of SMP phy objects that can be + * supported for the SCU Driver Standard (SDS) library. This number can be + * increased if required. + */ +#define SCI_MAX_SMP_PHYS (384) +#endif + +#ifndef SCI_MAX_REMOTE_DEVICES +/** + * + * + * This constant defines the maximum number of remote device objects that can + * be supported for the SCU Driver Standard (SDS) library. This is tied + * directly to silicon capabilities. + */ +#define SCI_MAX_REMOTE_DEVICES (256) +#endif + +#ifndef SCI_MIN_REMOTE_DEVICES +/** + * + * + * This constant defines the minimum number of remote device objects that can + * be supported for the SCU Driver Standard (SDS) library. This # can be + * configured for minimum memory environments to any value less than + * SCI_MAX_REMOTE_DEVICES + */ +#define SCI_MIN_REMOTE_DEVICES (16) +#endif + +#ifndef SCI_MAX_IO_REQUESTS +/** + * + * + * This constant defines the maximum number of IO request objects that can be + * supported for the SCU Driver Standard (SDS) library. This is tied directly + * to silicon capabilities. + */ +#define SCI_MAX_IO_REQUESTS (256) +#endif + +#ifndef SCI_MIN_IO_REQUESTS +/** + * + * + * This constant defines the minimum number of IO request objects that can be + * supported for the SCU Driver Standard (SDS) library. This # can be + * configured for minimum memory environments to any value less than + * SCI_MAX_IO_REQUESTS. + */ +#define SCI_MIN_IO_REQUESTS (1) +#endif + +#ifndef SCI_MAX_MSIX_MESSAGES +/** + * + * + * This constant defines the maximum number of MSI-X interrupt vectors/messages + * supported for an SCU hardware controller instance. + */ +#define SCI_MAX_MSIX_MESSAGES (2) +#endif + +#ifndef SCI_MAX_SCATTER_GATHER_ELEMENTS +/** + * + * + * This constant defines the maximum number of Scatter-Gather Elements to be + * used by any SCI component. + */ +#define SCI_MAX_SCATTER_GATHER_ELEMENTS 130 +#endif + +#ifndef SCI_MIN_SCATTER_GATHER_ELEMENTS +/** + * + * + * This constant defines the minimum number of Scatter-Gather Elements to be + * used by any SCI component. + */ +#define SCI_MIN_SCATTER_GATHER_ELEMENTS 1 +#endif + +#else /* SCIC_SDS_4_ENABLED */ + +#error "SCI Core configuration left unspecified (e.g. SCIC_SDS_4_ENABLED)" + +#endif /* SCIC_SDS_4_ENABLED */ + +/** + * + * + * This constant defines the maximum number of controllers that can occur in a + * single silicon package. + */ +#define SCI_MAX_CONTROLLERS 2 + +/** + * + * + * The maximum number of supported domain objects is currently tied to the + * maximum number of support port objects. + */ +#define SCI_MAX_DOMAINS SCI_MAX_PORTS + + +#endif /* _SCI_CONTROLLER_CONSTANTS_H_ */ + diff --git a/drivers/scsi/isci/core/sci_memory_descriptor_list.h b/drivers/scsi/isci/core/sci_memory_descriptor_list.h new file mode 100644 index 000000000000..44de1c18d2c8 --- /dev/null +++ b/drivers/scsi/isci/core/sci_memory_descriptor_list.h @@ -0,0 +1,169 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_MEMORY_DESCRIPTOR_LIST_H_ +#define _SCI_MEMORY_DESCRIPTOR_LIST_H_ + +/** + * This file contains all of the basic data types utilized by an SCI user or + * implementor. + * + * + */ + + +#include "sci_types.h" + +struct sci_base_memory_descriptor_list; + +/** + * + * + * SCI_MDE_ATTRIBUTES These constants depict memory attributes for the Memory + * Descriptor Entries (MDEs) contained in the MDL. + */ +#define SCI_MDE_ATTRIBUTE_CACHEABLE 0x0001 +#define SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS 0x0002 + +/** + * struct sci_physical_memory_descriptor - This structure defines a description + * of a memory location for the SCI implementation. + * + * + */ +struct sci_physical_memory_descriptor { + /** + * This field contains the virtual address associated with this descriptor + * element. This field shall be zero when the descriptor is retrieved from + * the SCI implementation. The user shall set this field prior + * sci_controller_start() + */ + void *virtual_address; + + /** + * This field contains the physical address associated with this desciptor + * element. This field shall be zero when the descriptor is retrieved from + * the SCI implementation. The user shall set this field prior + * sci_controller_start() + */ + dma_addr_t physical_address; + + /** + * This field contains the size requirement for this memory descriptor. + * A value of zero for this field indicates the end of the descriptor + * list. The value should be treated as read only for an SCI user. + */ + u32 constant_memory_size; + + /** + * This field contains the alignment requirement for this memory + * descriptor. A value of zero for this field indicates the end of the + * descriptor list. All other values indicate the number of bytes to + * achieve the necessary alignment. The value should be treated as + * read only for an SCI user. + */ + u32 constant_memory_alignment; + + /** + * This field contains an indication regarding the desired memory + * attributes for this memory descriptor entry. + * Notes: + * - If the cacheable attribute is set, the user can allocate + * memory that is backed by cache for better performance. It + * is not required that the memory be backed by cache. + * - If the physically contiguous attribute is set, then the + * entire memory must be physically contiguous across all + * page boundaries. + */ + u16 constant_memory_attributes; + +}; + +/** + * sci_mdl_first_entry() - This method simply rewinds the MDL iterator back to + * the first memory descriptor entry in the list. + * @mdl: This parameter specifies the memory descriptor list that is to be + * rewound. + * + */ +void sci_mdl_first_entry( + struct sci_base_memory_descriptor_list *mdl); + +/** + * sci_mdl_next_entry() - This method simply updates the "current" pointer to + * the next sequential memory descriptor. + * @mdl: This parameter specifies the memory descriptor list for which to + * return the next memory descriptor entry in the list. + * + * none. + */ +void sci_mdl_next_entry( + struct sci_base_memory_descriptor_list *mdl); + +/** + * sci_mdl_get_current_entry() - This method simply returns the current memory + * descriptor entry. + * @mdl: This parameter specifies the memory descriptor list for which to + * return the current memory descriptor entry. + * + * This method returns a pointer to the current physical memory descriptor in + * the MDL. NULL This value is returned if there are no descriptors in the list. + */ +struct sci_physical_memory_descriptor *sci_mdl_get_current_entry( + struct sci_base_memory_descriptor_list *mdl); + + +#endif /* _SCI_MEMORY_DESCRIPTOR_LIST_H_ */ + diff --git a/drivers/scsi/isci/core/sci_object.h b/drivers/scsi/isci/core/sci_object.h new file mode 100644 index 000000000000..930694264f1a --- /dev/null +++ b/drivers/scsi/isci/core/sci_object.h @@ -0,0 +1,99 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_OBJECT_H_ +#define _SCI_OBJECT_H_ + +/** + * This file contains all of the method and constants associated with the SCI + * base object. The SCI base object is the class from which all other + * objects derive in the Storage Controller Interface. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" + +/** + * struct sci_base_object - all core objects must include this as their + * first member to permit the casting below + * + * TODO: unwind this assumption, convert these routines and callers to pass a struct + * sci_base_object pointer without casting, or convert 'private' to the + * expected type per-object + * + */ +struct sci_base_object { + void *private; +}; + +static inline void *sci_object_get_association(void *obj) +{ + struct sci_base_object *base = obj; + + return base->private; +} + +static inline void sci_object_set_association(void *obj, void *private) +{ + struct sci_base_object *base = obj; + + base->private = private; +} + +#endif /* _SCI_OBJECT_H_ */ + diff --git a/drivers/scsi/isci/core/sci_pool.h b/drivers/scsi/isci/core/sci_pool.h new file mode 100644 index 000000000000..c0d2ea32529b --- /dev/null +++ b/drivers/scsi/isci/core/sci_pool.h @@ -0,0 +1,199 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the interface to the pool class. This class allows two + * different two different priority tasks to insert and remove items from + * the free pool. The user of the pool is expected to evaluate the pool + * condition empty before a get operation and pool condition full before a + * put operation. Methods Provided: - sci_pool_create() - + * sci_pool_initialize() - sci_pool_empty() - sci_pool_full() - + * sci_pool_get() - sci_pool_put() + * + * + */ + +#ifndef _SCI_POOL_H_ +#define _SCI_POOL_H_ + +/** + * SCI_POOL_INCREMENT() - + * + * Private operation for the pool + */ +#define SCI_POOL_INCREMENT(this_pool, index) \ + (((index) + 1) == (this_pool).size ? 0 : (index) + 1) + +/** + * SCI_POOL_CREATE() - + * + * This creates a pool structure of pool_name. The members in the pool are of + * type with number of elements equal to size. + */ +#define SCI_POOL_CREATE(pool_name, type, pool_size) \ + struct \ + { \ + u32 size; \ + u32 get; \ + u32 put; \ + type array[(pool_size) + 1]; \ + } pool_name + + +/** + * sci_pool_empty() - + * + * This macro evaluates the pool and returns true if the pool is empty. If the + * pool is empty the user should not perform any get operation on the pool. + */ +#define sci_pool_empty(this_pool) \ + ((this_pool).get == (this_pool).put) + +/** + * sci_pool_full() - + * + * This macro evaluates the pool and returns true if the pool is full. If the + * pool is full the user should not perform any put operation. + */ +#define sci_pool_full(this_pool) \ + (SCI_POOL_INCREMENT(this_pool, (this_pool).put) == (this_pool).get) + +/** + * sci_pool_size() - + * + * This macro returns the size of the pool created. The internal size of the + * pool is actually 1 larger then necessary in order to ensure get and put + * pointers can be written simultaneously by different users. As a result, + * this macro subtracts 1 from the internal size + */ +#define sci_pool_size(this_pool) \ + ((this_pool).size - 1) + +/** + * sci_pool_count() - + * + * This macro indicates the number of elements currently contained in the pool. + */ +#define sci_pool_count(this_pool) \ + (\ + sci_pool_empty((this_pool)) \ + ? 0 \ + : (\ + sci_pool_full((this_pool)) \ + ? sci_pool_size((this_pool)) \ + : (\ + (this_pool).get > (this_pool).put \ + ? ((this_pool).size - (this_pool).get + (this_pool).put) \ + : ((this_pool).put - (this_pool).get) \ + ) \ + ) \ + ) + +/** + * sci_pool_initialize() - + * + * This macro initializes the pool to an empty condition. + */ +#define sci_pool_initialize(this_pool) \ + { \ + (this_pool).size = (sizeof((this_pool).array) / sizeof((this_pool).array[0])); \ + (this_pool).get = 0; \ + (this_pool).put = 0; \ + } + +/** + * sci_pool_get() - + * + * This macro will get the next free element from the pool. This should only be + * called if the pool is not empty. + */ +#define sci_pool_get(this_pool, my_value) \ + { \ + (my_value) = (this_pool).array[(this_pool).get]; \ + (this_pool).get = SCI_POOL_INCREMENT((this_pool), (this_pool).get); \ + } + +/** + * sci_pool_put() - + * + * This macro will put the value into the pool. This should only be called if + * the pool is not full. + */ +#define sci_pool_put(this_pool, the_value) \ + { \ + (this_pool).array[(this_pool).put] = (the_value); \ + (this_pool).put = SCI_POOL_INCREMENT((this_pool), (this_pool).put); \ + } + +/** + * sci_pool_erase() - + * + * This macro will search the pool and remove any elements in the pool matching + * the supplied value. This method can only be utilized on pools + */ +#define sci_pool_erase(this_pool, type, the_value) \ + { \ + type tmp_value; \ + u32 index; \ + u32 element_count = sci_pool_count((this_pool)); \ + \ + for (index = 0; index < element_count; index++) { \ + sci_pool_get((this_pool), tmp_value); \ + if (tmp_value != (the_value)) \ + sci_pool_put((this_pool), tmp_value); \ + } \ + } + +#endif /* _SCI_POOL_H_ */ diff --git a/drivers/scsi/isci/core/sci_status.h b/drivers/scsi/isci/core/sci_status.h new file mode 100644 index 000000000000..72b61081c28d --- /dev/null +++ b/drivers/scsi/isci/core/sci_status.h @@ -0,0 +1,409 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_STATUS_H_ +#define _SCI_STATUS_H_ + +/** + * This file contains all of the return status codes utilized across the + * various sub-components in SCI. + * + * + */ + + +/** + * enum _SCI_STATUS - This is the general return status enumeration for non-IO, + * non-task management related SCI interface methods. + * + * + */ +enum sci_status { + /** + * This member indicates successful completion. + */ + SCI_SUCCESS = 0, + + /** + * This value indicates that the calling method completed successfully, + * but that the IO may have completed before having it's start method + * invoked. This occurs during SAT translation for requests that do + * not require an IO to the target or for any other requests that may + * be completed without having to submit IO. + */ + SCI_SUCCESS_IO_COMPLETE_BEFORE_START, + + /** + * This Value indicates that the SCU hardware returned an early response + * because the io request specified more data than is returned by the + * target device (mode pages, inquiry data, etc.). The completion routine + * will handle this case to get the actual number of bytes transferred. + */ + SCI_SUCCESS_IO_DONE_EARLY, + + /** + * This member indicates that the object for which a state change is + * being requested is already in said state. + */ + SCI_WARNING_ALREADY_IN_STATE, + + /** + * This member indicates interrupt coalescence timer may cause SAS + * specification compliance issues (i.e. SMP target mode response + * frames must be returned within 1.9 milliseconds). + */ + SCI_WARNING_TIMER_CONFLICT, + + /** + * This field indicates a sequence of action is not completed yet. Mostly, + * this status is used when multiple ATA commands are needed in a SATI translation. + */ + SCI_WARNING_SEQUENCE_INCOMPLETE, + + /** + * This member indicates that there was a general failure. + */ + SCI_FAILURE, + + /** + * This member indicates that the SCI implementation is unable to complete + * an operation due to a critical flaw the prevents any further operation + * (i.e. an invalid pointer). + */ + SCI_FATAL_ERROR, + + /** + * This member indicates the calling function failed, because the state + * of the controller is in a state that prevents successful completion. + */ + SCI_FAILURE_INVALID_STATE, + + /** + * This member indicates the calling function failed, because there is + * insufficient resources/memory to complete the request. + */ + SCI_FAILURE_INSUFFICIENT_RESOURCES, + + /** + * This member indicates the calling function failed, because the + * controller object required for the operation can't be located. + */ + SCI_FAILURE_CONTROLLER_NOT_FOUND, + + /** + * This member indicates the calling function failed, because the + * discovered controller type is not supported by the library. + */ + SCI_FAILURE_UNSUPPORTED_CONTROLLER_TYPE, + + /** + * This member indicates the calling function failed, because the + * requested initialization data version isn't supported. + */ + SCI_FAILURE_UNSUPPORTED_INIT_DATA_VERSION, + + /** + * This member indicates the calling function failed, because the + * requested configuration of SAS Phys into SAS Ports is not supported. + */ + SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION, + + /** + * This member indicates the calling function failed, because the + * requested protocol is not supported by the remote device, port, + * or controller. + */ + SCI_FAILURE_UNSUPPORTED_PROTOCOL, + + /** + * This member indicates the calling function failed, because the + * requested information type is not supported by the SCI implementation. + */ + SCI_FAILURE_UNSUPPORTED_INFORMATION_TYPE, + + /** + * This member indicates the calling function failed, because the + * device already exists. + */ + SCI_FAILURE_DEVICE_EXISTS, + + /** + * This member indicates the calling function failed, because adding + * a phy to the object is not possible. + */ + SCI_FAILURE_ADDING_PHY_UNSUPPORTED, + + /** + * This member indicates the calling function failed, because the + * requested information type is not supported by the SCI implementation. + */ + SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD, + + /** + * This member indicates the calling function failed, because the SCI + * implementation does not support the supplied time limit. + */ + SCI_FAILURE_UNSUPPORTED_TIME_LIMIT, + + /** + * This member indicates the calling method failed, because the SCI + * implementation does not contain the specified Phy. + */ + SCI_FAILURE_INVALID_PHY, + + /** + * This member indicates the calling method failed, because the SCI + * implementation does not contain the specified Port. + */ + SCI_FAILURE_INVALID_PORT, + + /** + * This member indicates the calling method was partly successful + * The port was reset but not all phys in port are operational + */ + SCI_FAILURE_RESET_PORT_PARTIAL_SUCCESS, + + /** + * This member indicates that calling method failed + * The port reset did not complete because none of the phys are operational + */ + SCI_FAILURE_RESET_PORT_FAILURE, + + /** + * This member indicates the calling method failed, because the SCI + * implementation does not contain the specified remote device. + */ + SCI_FAILURE_INVALID_REMOTE_DEVICE, + + /** + * This member indicates the calling method failed, because the remote + * device is in a bad state and requires a reset. + */ + SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED, + + /** + * This member indicates the calling method failed, because the SCI + * implementation does not contain or support the specified IO tag. + */ + SCI_FAILURE_INVALID_IO_TAG, + + /** + * This member indicates that the operation failed and the user should + * check the response data associated with the IO. + */ + SCI_FAILURE_IO_RESPONSE_VALID, + + /** + * This member indicates that the operation failed, the failure is + * controller implementation specific, and the response data associated + * with the request is not valid. You can query for the controller + * specific error information via scic_controller_get_request_status() + */ + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR, + + /** + * This member indicated that the operation failed because the + * user requested this IO to be terminated. + */ + SCI_FAILURE_IO_TERMINATED, + + /** + * This member indicates that the operation failed and the associated + * request requires a SCSI abort task to be sent to the target. + */ + SCI_FAILURE_IO_REQUIRES_SCSI_ABORT, + + /** + * This member indicates that the operation failed because the supplied + * device could not be located. + */ + SCI_FAILURE_DEVICE_NOT_FOUND, + + /** + * This member indicates that the operation failed because the + * objects association is required and is not correctly set. + */ + SCI_FAILURE_INVALID_ASSOCIATION, + + /** + * This member indicates that the operation failed, because a timeout + * occurred. + */ + SCI_FAILURE_TIMEOUT, + + /** + * This member indicates that the operation failed, because the user + * specified a value that is either invalid or not supported. + */ + SCI_FAILURE_INVALID_PARAMETER_VALUE, + + /** + * This value indicates that the operation failed, because the number + * of messages (MSI-X) is not supported. + */ + SCI_FAILURE_UNSUPPORTED_MESSAGE_COUNT, + + /** + * This value indicates that the method failed due to a lack of + * available NCQ tags. + */ + SCI_FAILURE_NO_NCQ_TAG_AVAILABLE, + + /** + * This value indicates that a protocol violation has occurred on the + * link. + */ + SCI_FAILURE_PROTOCOL_VIOLATION, + + /** + * This value indicates a failure condition that retry may help to clear. + */ + SCI_FAILURE_RETRY_REQUIRED, + + /** + * This field indicates the retry limit was reached when a retry is attempted + */ + SCI_FAILURE_RETRY_LIMIT_REACHED, + + /** + * This member indicates the calling method was partly successful. + * Mostly, this status is used when a LUN_RESET issued to an expander attached + * STP device in READY NCQ substate needs to have RNC suspended/resumed + * before posting TC. + */ + SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS, + + /** + * This field indicates an illegal phy connection based on the routing attribute + * of both expander phy attached to each other. + */ + SCI_FAILURE_ILLEGAL_ROUTING_ATTRIBUTE_CONFIGURATION, + + /** + * This field indicates a CONFIG ROUTE INFO command has a response with function result + * INDEX DOES NOT EXIST, usually means exceeding max route index. + */ + SCI_FAILURE_EXCEED_MAX_ROUTE_INDEX, + + /** + * This value indicates that an unsupported PCI device ID has been + * specified. This indicates that attempts to invoke + * scic_library_allocate_controller() will fail. + */ + SCI_FAILURE_UNSUPPORTED_PCI_DEVICE_ID + +}; + +/** + * enum _SCI_IO_STATUS - This enumeration depicts all of the possible IO + * completion status values. Each value in this enumeration maps directly + * to a value in the enum sci_status enumeration. Please refer to that + * enumeration for detailed comments concerning what the status represents. + * + * Add the API to retrieve the SCU status from the core. Check to see that the + * following status are properly handled: - SCI_IO_FAILURE_UNSUPPORTED_PROTOCOL + * - SCI_IO_FAILURE_INVALID_IO_TAG + */ +enum sci_io_status { + SCI_IO_SUCCESS = SCI_SUCCESS, + SCI_IO_FAILURE = SCI_FAILURE, + SCI_IO_SUCCESS_COMPLETE_BEFORE_START = SCI_SUCCESS_IO_COMPLETE_BEFORE_START, + SCI_IO_SUCCESS_IO_DONE_EARLY = SCI_SUCCESS_IO_DONE_EARLY, + SCI_IO_FAILURE_INVALID_STATE = SCI_FAILURE_INVALID_STATE, + SCI_IO_FAILURE_INSUFFICIENT_RESOURCES = SCI_FAILURE_INSUFFICIENT_RESOURCES, + SCI_IO_FAILURE_UNSUPPORTED_PROTOCOL = SCI_FAILURE_UNSUPPORTED_PROTOCOL, + SCI_IO_FAILURE_RESPONSE_VALID = SCI_FAILURE_IO_RESPONSE_VALID, + SCI_IO_FAILURE_CONTROLLER_SPECIFIC_ERR = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR, + SCI_IO_FAILURE_TERMINATED = SCI_FAILURE_IO_TERMINATED, + SCI_IO_FAILURE_REQUIRES_SCSI_ABORT = SCI_FAILURE_IO_REQUIRES_SCSI_ABORT, + SCI_IO_FAILURE_INVALID_PARAMETER_VALUE = SCI_FAILURE_INVALID_PARAMETER_VALUE, + SCI_IO_FAILURE_NO_NCQ_TAG_AVAILABLE = SCI_FAILURE_NO_NCQ_TAG_AVAILABLE, + SCI_IO_FAILURE_PROTOCOL_VIOLATION = SCI_FAILURE_PROTOCOL_VIOLATION, + + SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED, + + SCI_IO_FAILURE_RETRY_REQUIRED = SCI_FAILURE_RETRY_REQUIRED, + SCI_IO_FAILURE_RETRY_LIMIT_REACHED = SCI_FAILURE_RETRY_LIMIT_REACHED, + SCI_IO_FAILURE_INVALID_REMOTE_DEVICE = SCI_FAILURE_INVALID_REMOTE_DEVICE +}; + +/** + * enum _SCI_TASK_STATUS - This enumeration depicts all of the possible task + * completion status values. Each value in this enumeration maps directly + * to a value in the enum sci_status enumeration. Please refer to that + * enumeration for detailed comments concerning what the status represents. + * + * Check to see that the following status are properly handled: + */ +enum sci_task_status { + SCI_TASK_SUCCESS = SCI_SUCCESS, + SCI_TASK_FAILURE = SCI_FAILURE, + SCI_TASK_FAILURE_INVALID_STATE = SCI_FAILURE_INVALID_STATE, + SCI_TASK_FAILURE_INSUFFICIENT_RESOURCES = SCI_FAILURE_INSUFFICIENT_RESOURCES, + SCI_TASK_FAILURE_UNSUPPORTED_PROTOCOL = SCI_FAILURE_UNSUPPORTED_PROTOCOL, + SCI_TASK_FAILURE_INVALID_TAG = SCI_FAILURE_INVALID_IO_TAG, + SCI_TASK_FAILURE_RESPONSE_VALID = SCI_FAILURE_IO_RESPONSE_VALID, + SCI_TASK_FAILURE_CONTROLLER_SPECIFIC_ERR = SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR, + SCI_TASK_FAILURE_TERMINATED = SCI_FAILURE_IO_TERMINATED, + SCI_TASK_FAILURE_INVALID_PARAMETER_VALUE = SCI_FAILURE_INVALID_PARAMETER_VALUE, + + SCI_TASK_FAILURE_REMOTE_DEVICE_RESET_REQUIRED = SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED, + SCI_TASK_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS = SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS + +}; + + +#endif /* _SCI_STATUS_H_ */ + diff --git a/drivers/scsi/isci/core/sci_types.h b/drivers/scsi/isci/core/sci_types.h new file mode 100644 index 000000000000..431735d1a589 --- /dev/null +++ b/drivers/scsi/isci/core/sci_types.h @@ -0,0 +1,88 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_TYPES_H_ +#define _SCI_TYPES_H_ + +#include <linux/string.h> + +#define sci_cb_make_physical_address(physical_addr, addr_upper, addr_lower) \ + ((physical_addr) = (addr_lower) | ((u64)addr_upper) << 32) + +#define SCI_INVALID_HANDLE 0x0 + +/** + * The SCI_LIBRARY_HANDLE_T will be utilized by SCI users as an opaque handle + * for the SCI Library object. + * + * SCI_LIBRARY_HANDLE_T + */ +typedef void *SCI_LIBRARY_HANDLE_T; + + +typedef enum { + SCI_IO_REQUEST_DATA_IN = 0, /* Read operation */ + SCI_IO_REQUEST_DATA_OUT, /* Write operation */ + SCI_IO_REQUEST_NO_DATA +} SCI_IO_REQUEST_DATA_DIRECTION; + + +enum sci_controller_mode { + SCI_MODE_SPEED, /* Optimized for performance */ + SCI_MODE_SIZE /* Optimized for memory use */ +}; + +#endif /* _SCI_TYPES_H_ */ + diff --git a/drivers/scsi/isci/core/sci_util.c b/drivers/scsi/isci/core/sci_util.c new file mode 100644 index 000000000000..5cdd96f29a9e --- /dev/null +++ b/drivers/scsi/isci/core/sci_util.c @@ -0,0 +1,70 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "sci_util.h" + +void scic_word_copy_with_swap( + u32 *destination, + u32 *source, + u32 word_count) +{ + while (word_count--) { + *destination = SCIC_SWAP_DWORD(*source); + + source++; + destination++; + } +} + diff --git a/drivers/scsi/isci/core/sci_util.h b/drivers/scsi/isci/core/sci_util.h new file mode 100644 index 000000000000..67e2badf8ea8 --- /dev/null +++ b/drivers/scsi/isci/core/sci_util.h @@ -0,0 +1,138 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_UTIL_H_ +#define _SCI_UTIL_H_ + +#include "sci_types.h" + +/** + * SCIC_SWAP_DWORD() - + * + * Normal byte swap macro + */ +#define SCIC_SWAP_DWORD(x) \ + (\ + (((x) >> 24) & 0x000000FF) \ + | (((x) >> 8) & 0x0000FF00) \ + | (((x) << 8) & 0x00FF0000) \ + | (((x) << 24) & 0xFF000000) \ + ) + +#define SCIC_BUILD_DWORD(char_buffer) \ + (\ + ((char_buffer)[0] << 24) \ + | ((char_buffer)[1] << 16) \ + | ((char_buffer)[2] << 8) \ + | ((char_buffer)[3]) \ + ) + +#define SCI_FIELD_OFFSET(type, field) ((unsigned long)&(((type *)0)->field)) + +/** + * sci_physical_address_add() - + * + * This macro simply performs addition on an dma_addr_t type. The + * lower u32 value is "clipped" or "wrapped" back through 0. When this occurs + * the upper 32-bits are incremented by 1. + */ +#define sci_physical_address_add(physical_address, value) \ + { \ + u32 lower = lower_32_bits((physical_address)); \ + u32 upper = upper_32_bits((physical_address)); \ + \ + if (lower + (value) < lower) \ + upper += 1; \ + \ + lower += (value); \ + sci_cb_make_physical_address(physical_address, upper, lower); \ + } + +/** + * sci_physical_address_subtract() - + * + * This macro simply performs subtraction on an dma_addr_t type. The + * lower u32 value is "clipped" or "wrapped" back through 0. When this occurs + * the upper 32-bits are decremented by 1. + */ +#define sci_physical_address_subtract(physical_address, value) \ + { \ + u32 lower = lower_32_bits((physical_address)); \ + u32 upper = upper_32_bits((physical_address)); \ + \ + if (lower - (value) > lower) \ + upper -= 1; \ + \ + lower -= (value); \ + sci_cb_make_physical_address(physical_address, upper, lower); \ + } + +/** + * scic_word_copy_with_swap() - Copy the data from source to destination and + * swap the bytes during the copy. + * @destination: This parameter specifies the destination address to which the + * data is to be copied. + * @source: This parameter specifies the source address from which data is to + * be copied. + * @word_count: This parameter specifies the number of 32-bit words to copy and + * byte swap. + * + */ +void scic_word_copy_with_swap( + u32 *destination, + u32 *source, + u32 word_count); + +#endif /* _SCI_UTIL_H_ */ diff --git a/drivers/scsi/isci/core/scic_config_parameters.h b/drivers/scsi/isci/core/scic_config_parameters.h new file mode 100644 index 000000000000..4c16a50a0658 --- /dev/null +++ b/drivers/scsi/isci/core/scic_config_parameters.h @@ -0,0 +1,347 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_USER_PARAMETERS_H_ +#define _SCIC_SDS_USER_PARAMETERS_H_ + +/** + * This file contains all of the structure definitions and interface methods + * that can be called by a SCIC user on the SCU Driver Standard + * (struct scic_sds_user_parameters) user parameter block. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" +#include "intel_sas.h" +#include "sci_controller_constants.h" + +struct scic_sds_controller; + +/** + * + * + * SCIC_SDS_PARM_PHY_SPEED These constants define the speeds utilized for a + * phy/port. + */ +#define SCIC_SDS_PARM_NO_SPEED 0 + +/** + * + * + * This value of 1 indicates generation 1 (i.e. 1.5 Gb/s). + */ +#define SCIC_SDS_PARM_GEN1_SPEED 1 + +/** + * + * + * This value of 2 indicates generation 2 (i.e. 3.0 Gb/s). + */ +#define SCIC_SDS_PARM_GEN2_SPEED 2 + +/** + * + * + * This value of 3 indicates generation 3 (i.e. 6.0 Gb/s). + */ +#define SCIC_SDS_PARM_GEN3_SPEED 3 + +/** + * + * + * For range checks, the max speed generation + */ +#define SCIC_SDS_PARM_MAX_SPEED SCIC_SDS_PARM_GEN3_SPEED + +/** + * struct scic_sds_user_parameters - This structure delineates the various user + * parameters that can be changed by the core user. + * + * + */ +struct scic_sds_user_parameters { + struct { + /** + * This field specifies the NOTIFY (ENABLE SPIN UP) primitive + * insertion frequency for this phy index. + */ + u32 notify_enable_spin_up_insertion_frequency; + + /** + * This method specifies the number of transmitted DWORDs within which + * to transmit a single ALIGN primitive. This value applies regardless + * of what type of device is attached or connection state. A value of + * 0 indicates that no ALIGN primitives will be inserted. + */ + u16 align_insertion_frequency; + + /** + * This method specifies the number of transmitted DWORDs within which + * to transmit 2 ALIGN primitives. This applies for SAS connections + * only. A minimum value of 3 is required for this field. + */ + u16 in_connection_align_insertion_frequency; + + /** + * This field indicates the maximum speed generation to be utilized + * by phys in the supplied port. + * - A value of 1 indicates generation 1 (i.e. 1.5 Gb/s). + * - A value of 2 indicates generation 2 (i.e. 3.0 Gb/s). + * - A value of 3 indicates generation 3 (i.e. 6.0 Gb/s). + */ + u8 max_speed_generation; + + } phys[SCI_MAX_PHYS]; + + /** + * This field specifies the maximum number of direct attached devices + * that can have power supplied to them simultaneously. + */ + u8 max_number_concurrent_device_spin_up; + + /** + * This field specifies the number of seconds to allow a phy to consume + * power before yielding to another phy. + * + */ + u8 phy_spin_up_delay_interval; + + /** + * These timer values specifies how long a link will remain open with no + * activity in increments of a microsecond, it can be in increments of + * 100 microseconds if the upper most bit is set. + * + */ + u16 stp_inactivity_timeout; + u16 ssp_inactivity_timeout; + + /** + * These timer values specifies how long a link will remain open in increments + * of 100 microseconds. + * + */ + u16 stp_max_occupancy_timeout; + u16 ssp_max_occupancy_timeout; + + /** + * This timer value specifies how long a link will remain open with no + * outbound traffic in increments of a microsecond. + * + */ + u8 no_outbound_task_timeout; + +}; + +/** + * This structure/union specifies the various different user parameter sets + * available. Each type is specific to a hardware controller version. + * + * union scic_user_parameters + */ +union scic_user_parameters { + /** + * This field specifies the user parameters specific to the + * Storage Controller Unit (SCU) Driver Standard (SDS) version + * 1. + */ + struct scic_sds_user_parameters sds1; + +}; + + +/** + * + * + * SCIC_SDS_OEM_PHY_MASK These constants define the valid values for phy_mask + */ + +/** + * + * + * This is the min value assignable to a port's phy mask + */ +#define SCIC_SDS_PARM_PHY_MASK_MIN 0x0 + +/** + * + * + * This is the max value assignable to a port's phy mask + */ +#define SCIC_SDS_PARM_PHY_MASK_MAX 0xF + +/** + * struct scic_sds_oem_parameters - This structure delineates the various OEM + * parameters that must be set the core user. + * + * + */ +struct scic_sds_oem_parameters { + struct { + /** + * This field indicates whether Spread Spectrum Clocking (SSC) + * should be enabled or disabled. + */ + bool do_enable_ssc; + + } controller; + + struct { + /** + * This field specifies the phys to be contained inside a port. + * The bit position in the mask specifies the index of the phy + * to be contained in the port. Multiple bits (i.e. phys) + * can be contained in a single port. + */ + u8 phy_mask; + + } ports[SCI_MAX_PORTS]; + + struct { + /** + * This field specifies the SAS address to be transmitted on + * for this phy index. + */ + struct sci_sas_address sas_address; + + } phys[SCI_MAX_PHYS]; + +}; + +/** + * This structure/union specifies the various different OEM parameter sets + * available. Each type is specific to a hardware controller version. + * + * union scic_oem_parameters + */ +union scic_oem_parameters { + /** + * This field specifies the OEM parameters specific to the + * Storage Controller Unit (SCU) Driver Standard (SDS) version + * 1. + */ + struct scic_sds_oem_parameters sds1; + +}; + +/** + * scic_user_parameters_set() - This method allows the user to attempt to + * change the user parameters utilized by the controller. + * @controller: This parameter specifies the controller on which to set the + * user parameters. + * @user_parameters: This parameter specifies the USER_PARAMETERS object + * containing the potential new values. + * + * Indicate if the update of the user parameters was successful. SCI_SUCCESS + * This value is returned if the operation succeeded. SCI_FAILURE_INVALID_STATE + * This value is returned if the attempt to change the user parameter failed, + * because changing one of the parameters is not currently allowed. + * SCI_FAILURE_INVALID_PARAMETER_VALUE This value is returned if the user + * supplied an invalid interrupt coalescence time, spin up delay interval, etc. + */ +enum sci_status scic_user_parameters_set( + struct scic_sds_controller *controller, + union scic_user_parameters *user_parameters); + +/** + * scic_user_parameters_get() - This method allows the user to retrieve the + * user parameters utilized by the controller. + * @controller: This parameter specifies the controller on which to set the + * user parameters. + * @user_parameters: This parameter specifies the USER_PARAMETERS object into + * which the framework shall save it's parameters. + * + */ +void scic_user_parameters_get( + struct scic_sds_controller *controller, + union scic_user_parameters *user_parameters); + +/** + * scic_oem_parameters_set() - This method allows the user to attempt to change + * the OEM parameters utilized by the controller. + * @controller: This parameter specifies the controller on which to set the + * user parameters. + * @oem_parameters: This parameter specifies the OEM parameters object + * containing the potential new values. + * + * Indicate if the update of the user parameters was successful. SCI_SUCCESS + * This value is returned if the operation succeeded. SCI_FAILURE_INVALID_STATE + * This value is returned if the attempt to change the user parameter failed, + * because changing one of the parameters is not currently allowed. + * SCI_FAILURE_INVALID_PARAMETER_VALUE This value is returned if the user + * supplied an unsupported value for one of the OEM parameters. + */ +enum sci_status scic_oem_parameters_set( + struct scic_sds_controller *controller, + union scic_oem_parameters *oem_parameters); + +/** + * scic_oem_parameters_get() - This method allows the user to retreive the OEM + * parameters utilized by the controller. + * @controller: This parameter specifies the controller on which to set the + * user parameters. + * @oem_parameters: This parameter specifies the OEM parameters object in which + * to write the core's OEM parameters. + * + */ +void scic_oem_parameters_get( + struct scic_sds_controller *controller, + union scic_oem_parameters *oem_parameters); + + +#endif /* _SCIC_SDS_USER_PARAMETERS_H_ */ + diff --git a/drivers/scsi/isci/core/scic_controller.h b/drivers/scsi/isci/core/scic_controller.h new file mode 100644 index 000000000000..756b14fcd9a4 --- /dev/null +++ b/drivers/scsi/isci/core/scic_controller.h @@ -0,0 +1,586 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_CONTROLLER_H_ +#define _SCIC_CONTROLLER_H_ + +/** + * This file contains all of the interface methods that can be called by an + * SCIC user on a controller object. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" +#include "sci_controller.h" +#include "scic_config_parameters.h" + +struct scic_sds_request; +struct scic_sds_phy; +struct scic_sds_port; +struct scic_sds_remote_device; + +/** + * enum _SCIC_INTERRUPT_TYPE - This enumeration depicts the various types of + * interrupts that are potentially supported by a SCI Core implementation. + * + * + */ +enum scic_interrupt_type { + SCIC_LEGACY_LINE_INTERRUPT_TYPE, + SCIC_MSIX_INTERRUPT_TYPE, + + /** + * This enumeration value indicates the use of polling. + */ + SCIC_NO_INTERRUPTS + +}; + +/** + * This method is called by the SCI user in order to have the SCI + * implementation handle the interrupt. This method performs minimal + * processing to allow for streamlined interrupt time usage. + * + * SCIC_CONTROLLER_INTERRUPT_HANDLER true: returned if there is an interrupt to + * process and it was processed. false: returned if no interrupt was processed. + */ +typedef bool (*SCIC_CONTROLLER_INTERRUPT_HANDLER)( + struct scic_sds_controller *controller + ); + +/** + * This method is called by the SCI user to process completions generated as a + * result of a previously handled interrupt. This method will result in the + * completion of IO requests and handling of other controller generated + * events. This method should be called some time after the interrupt + * handler. + * + * Most, if not all, of the user callback APIs are invoked from within this + * API. As a result, the user should be cognizent of the operating level at + * which they invoke this API. + */ +typedef void (*SCIC_CONTROLLER_COMPLETION_HANDLER)( + struct scic_sds_controller *controller + ); + +/** + * struct scic_controller_handler_methods - This structure contains an + * interrupt handler and completion handler function pointers. + * + * + */ +struct scic_controller_handler_methods { + SCIC_CONTROLLER_INTERRUPT_HANDLER interrupt_handler; + SCIC_CONTROLLER_COMPLETION_HANDLER completion_handler; + +}; + +/** + * scic_controller_construct() - This method will attempt to construct a + * controller object utilizing the supplied parameter information. + * @c: This parameter specifies the controller to be constructed. + * @scu_base: mapped base address of the scu registers + * @smu_base: mapped base address of the smu registers + * + * Indicate if the controller was successfully constructed or if it failed in + * some way. SCI_SUCCESS This value is returned if the controller was + * successfully constructed. SCI_WARNING_TIMER_CONFLICT This value is returned + * if the interrupt coalescence timer may cause SAS compliance issues for SMP + * Target mode response processing. SCI_FAILURE_UNSUPPORTED_CONTROLLER_TYPE + * This value is returned if the controller does not support the supplied type. + * SCI_FAILURE_UNSUPPORTED_INIT_DATA_VERSION This value is returned if the + * controller does not support the supplied initialization data version. + */ +enum sci_status scic_controller_construct(struct scic_sds_controller *c, + void __iomem *scu_base, + void __iomem *smu_base); + +/** + * scic_controller_enable_interrupts() - This method will enable all controller + * interrupts. + * @controller: This parameter specifies the controller for which to enable + * interrupts. + * + */ +void scic_controller_enable_interrupts( + struct scic_sds_controller *controller); + +/** + * scic_controller_disable_interrupts() - This method will disable all + * controller interrupts. + * @controller: This parameter specifies the controller for which to disable + * interrupts. + * + */ +void scic_controller_disable_interrupts( + struct scic_sds_controller *controller); + +/** + * scic_controller_get_handler_methods() - This method will return provide + * function pointers for the interrupt handler and completion handler. The + * interrupt handler is expected to be invoked at interrupt time. The + * completion handler is scheduled to run as a result of the interrupt + * handler. The completion handler performs the bulk work for processing + * silicon events. + * @interrupt_type: This parameter informs the core which type of + * interrupt/completion methods are being requested. These are the types: + * SCIC_LEGACY_LINE_INTERRUPT_TYPE, SCIC_MSIX_INTERRUPT_TYPE, + * SCIC_NO_INTERRUPTS (POLLING) + * @message_count: This parameter informs the core the number of MSI-X messages + * to be utilized. This parameter must be 0 when requesting legacy line + * based handlers. + * @handler_methods: The caller provides a pointer to a buffer of type + * struct scic_controller_handler_methods. The size depends on the combination of + * the interrupt_type and message_count input parameters: + * SCIC_LEGACY_LINE_INTERRUPT_TYPE: - size = + * sizeof(struct scic_controller_handler_methods) SCIC_MSIX_INTERRUPT_TYPE: + * sizeof(struct scic_controller_handler_methods) + * @handler_methods: SCIC fills out the caller's buffer with the appropriate + * interrupt and completion handlers based on the info provided in the + * interrupt_type and message_count input parameters. For + * SCIC_LEGACY_LINE_INTERRUPT_TYPE, the buffer receives a single + * struct scic_controller_handler_methods element regardless that the + * message_count parameter is zero. For SCIC_MSIX_INTERRUPT_TYPE, the buffer + * receives an array of elements of type struct scic_controller_handler_methods + * where the array size is equivalent to the message_count parameter. The + * array is zero-relative where entry zero corresponds to message-vector + * zero, entry one corresponds to message-vector one, and so forth. + * + * Indicate if the handler retrieval operation was successful. SCI_SUCCESS This + * value is returned if retrieval succeeded. + * SCI_FAILURE_UNSUPPORTED_MESSAGE_COUNT This value is returned if the user + * supplied an unsupported number of MSI-X messages. For legacy line interrupts + * the only valid value is 0. + */ +enum sci_status scic_controller_get_handler_methods( + enum scic_interrupt_type interrupt_type, + u16 message_count, + struct scic_controller_handler_methods *handler_methods); + +/** + * scic_controller_initialize() - This method will initialize the controller + * hardware managed by the supplied core controller object. This method + * will bring the physical controller hardware out of reset and enable the + * core to determine the capabilities of the hardware being managed. Thus, + * the core controller can determine it's exact physical (DMA capable) + * memory requirements. + * @controller: This parameter specifies the controller to be initialized. + * + * The SCI Core user must have called scic_controller_construct() on the + * supplied controller object previously. Indicate if the controller was + * successfully initialized or if it failed in some way. SCI_SUCCESS This value + * is returned if the controller hardware was successfully initialized. + */ +enum sci_status scic_controller_initialize( + struct scic_sds_controller *controller); + +/** + * scic_controller_get_suggested_start_timeout() - This method returns the + * suggested scic_controller_start() timeout amount. The user is free to + * use any timeout value, but this method provides the suggested minimum + * start timeout value. The returned value is based upon empirical + * information determined as a result of interoperability testing. + * @controller: the handle to the controller object for which to return the + * suggested start timeout. + * + * This method returns the number of milliseconds for the suggested start + * operation timeout. + */ +u32 scic_controller_get_suggested_start_timeout( + struct scic_sds_controller *controller); + +/** + * scic_controller_start() - This method will start the supplied core + * controller. This method will start the staggered spin up operation. The + * SCI User completion callback is called when the following conditions are + * met: -# the return status of this method is SCI_SUCCESS. -# after all of + * the phys have successfully started or been given the opportunity to start. + * @controller: the handle to the controller object to start. + * @timeout: This parameter specifies the number of milliseconds in which the + * start operation should complete. + * + * The SCI Core user must have filled in the physical memory descriptor + * structure via the sci_controller_get_memory_descriptor_list() method. The + * SCI Core user must have invoked the scic_controller_initialize() method + * prior to invoking this method. The controller must be in the INITIALIZED or + * STARTED state. Indicate if the controller start method succeeded or failed + * in some way. SCI_SUCCESS if the start operation succeeded. + * SCI_WARNING_ALREADY_IN_STATE if the controller is already in the STARTED + * state. SCI_FAILURE_INVALID_STATE if the controller is not either in the + * INITIALIZED or STARTED states. SCI_FAILURE_INVALID_MEMORY_DESCRIPTOR if + * there are inconsistent or invalid values in the supplied + * struct sci_physical_memory_descriptor array. + */ +enum sci_status scic_controller_start( + struct scic_sds_controller *controller, + u32 timeout); + +/** + * scic_controller_stop() - This method will stop an individual controller + * object.This method will invoke the associated user callback upon + * completion. The completion callback is called when the following + * conditions are met: -# the method return status is SCI_SUCCESS. -# the + * controller has been quiesced. This method will ensure that all IO + * requests are quiesced, phys are stopped, and all additional operation by + * the hardware is halted. + * @controller: the handle to the controller object to stop. + * @timeout: This parameter specifies the number of milliseconds in which the + * stop operation should complete. + * + * The controller must be in the STARTED or STOPPED state. Indicate if the + * controller stop method succeeded or failed in some way. SCI_SUCCESS if the + * stop operation successfully began. SCI_WARNING_ALREADY_IN_STATE if the + * controller is already in the STOPPED state. SCI_FAILURE_INVALID_STATE if the + * controller is not either in the STARTED or STOPPED states. + */ +enum sci_status scic_controller_stop( + struct scic_sds_controller *controller, + u32 timeout); + +/** + * scic_controller_reset() - This method will reset the supplied core + * controller regardless of the state of said controller. This operation is + * considered destructive. In other words, all current operations are wiped + * out. No IO completions for outstanding devices occur. Outstanding IO + * requests are not aborted or completed at the actual remote device. + * @controller: the handle to the controller object to reset. + * + * Indicate if the controller reset method succeeded or failed in some way. + * SCI_SUCCESS if the reset operation successfully started. SCI_FATAL_ERROR if + * the controller reset operation is unable to complete. + */ +enum sci_status scic_controller_reset( + struct scic_sds_controller *controller); + +/** + * scic_controller_start_io() - This method is called by the SCI user to + * send/start an IO request. If the method invocation is successful, then + * the IO request has been queued to the hardware for processing. + * @controller: the handle to the controller object for which to start an IO + * request. + * @remote_device: the handle to the remote device object for which to start an + * IO request. + * @io_request: the handle to the io request object to start. + * @io_tag: This parameter specifies a previously allocated IO tag that the + * user desires to be utilized for this request. This parameter is optional. + * The user is allowed to supply SCI_CONTROLLER_INVALID_IO_TAG as the value + * for this parameter. + * + * - IO tags are a protected resource. It is incumbent upon the SCI Core user + * to ensure that each of the methods that may allocate or free available IO + * tags are handled in a mutually exclusive manner. This method is one of said + * methods requiring proper critical code section protection (e.g. semaphore, + * spin-lock, etc.). - For SATA, the user is required to manage NCQ tags. As a + * result, it is expected the user will have set the NCQ tag field in the host + * to device register FIS prior to calling this method. There is also a + * requirement for the user to call scic_stp_io_set_ncq_tag() prior to invoking + * the scic_controller_start_io() method. scic_controller_allocate_tag() for + * more information on allocating a tag. Indicate if the controller + * successfully started the IO request. SCI_IO_SUCCESS if the IO request was + * successfully started. Determine the failure situations and return values. + */ +enum sci_io_status scic_controller_start_io( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *io_request, + u16 io_tag); + + +/** + * scic_controller_start_task() - This method is called by the SCIC user to + * send/start a framework task management request. + * @controller: the handle to the controller object for which to start the task + * management request. + * @remote_device: the handle to the remote device object for which to start + * the task management request. + * @task_request: the handle to the task request object to start. + * @io_tag: This parameter specifies a previously allocated IO tag that the + * user desires to be utilized for this request. Note this not the io_tag + * of the request being managed. It is to be utilized for the task request + * itself. This parameter is optional. The user is allowed to supply + * SCI_CONTROLLER_INVALID_IO_TAG as the value for this parameter. + * + * - IO tags are a protected resource. It is incumbent upon the SCI Core user + * to ensure that each of the methods that may allocate or free available IO + * tags are handled in a mutually exclusive manner. This method is one of said + * methods requiring proper critical code section protection (e.g. semaphore, + * spin-lock, etc.). - The user must synchronize this task with completion + * queue processing. If they are not synchronized then it is possible for the + * io requests that are being managed by the task request can complete before + * starting the task request. scic_controller_allocate_tag() for more + * information on allocating a tag. Indicate if the controller successfully + * started the IO request. SCI_TASK_SUCCESS if the task request was + * successfully started. SCI_TASK_FAILURE_REQUIRES_SCSI_ABORT This value is + * returned if there is/are task(s) outstanding that require termination or + * completion before this request can succeed. + */ +enum sci_task_status scic_controller_start_task( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *task_request, + u16 io_tag); + +/** + * scic_controller_complete_task() - This method will perform core specific + * completion operations for task management request. After this method is + * invoked, the user should consider the task request as invalid until it is + * properly reused (i.e. re-constructed). + * @controller: The handle to the controller object for which to complete the + * task management request. + * @remote_device: The handle to the remote device object for which to complete + * the task management request. + * @task_request: the handle to the task management request object to complete. + * + * Indicate if the controller successfully completed the task management + * request. SCI_SUCCESS if the completion process was successful. + */ +enum sci_status scic_controller_complete_task( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *task_request); + + +/** + * scic_controller_terminate_request() - This method is called by the SCI Core + * user to terminate an ongoing (i.e. started) core IO request. This does + * not abort the IO request at the target, but rather removes the IO request + * from the host controller. + * @controller: the handle to the controller object for which to terminate a + * request. + * @remote_device: the handle to the remote device object for which to + * terminate a request. + * @request: the handle to the io or task management request object to + * terminate. + * + * Indicate if the controller successfully began the terminate process for the + * IO request. SCI_SUCCESS if the terminate process was successfully started + * for the request. Determine the failure situations and return values. + */ +enum sci_status scic_controller_terminate_request( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *request); + +/** + * scic_controller_complete_io() - This method will perform core specific + * completion operations for an IO request. After this method is invoked, + * the user should consider the IO request as invalid until it is properly + * reused (i.e. re-constructed). + * @controller: The handle to the controller object for which to complete the + * IO request. + * @remote_device: The handle to the remote device object for which to complete + * the IO request. + * @io_request: the handle to the io request object to complete. + * + * - IO tags are a protected resource. It is incumbent upon the SCI Core user + * to ensure that each of the methods that may allocate or free available IO + * tags are handled in a mutually exclusive manner. This method is one of said + * methods requiring proper critical code section protection (e.g. semaphore, + * spin-lock, etc.). - If the IO tag for a request was allocated, by the SCI + * Core user, using the scic_controller_allocate_io_tag() method, then it is + * the responsibility of the caller to invoke the scic_controller_free_io_tag() + * method to free the tag (i.e. this method will not free the IO tag). Indicate + * if the controller successfully completed the IO request. SCI_SUCCESS if the + * completion process was successful. + */ +enum sci_status scic_controller_complete_io( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *io_request); + + +/** + * scic_controller_get_port_handle() - This method simply provides the user + * with a unique handle for a given SAS/SATA core port index. + * @controller: This parameter represents the handle to the controller object + * from which to retrieve a port (SAS or SATA) handle. + * @port_index: This parameter specifies the port index in the controller for + * which to retrieve the port handle. 0 <= port_index < maximum number of + * phys. + * @port_handle: This parameter specifies the retrieved port handle to be + * provided to the caller. + * + * Indicate if the retrieval of the port handle was successful. SCI_SUCCESS + * This value is returned if the retrieval was successful. + * SCI_FAILURE_INVALID_PORT This value is returned if the supplied port id is + * not in the supported range. + */ +enum sci_status scic_controller_get_port_handle( + struct scic_sds_controller *controller, + u8 port_index, + struct scic_sds_port **port_handle); + +/** + * scic_controller_get_phy_handle() - This method simply provides the user with + * a unique handle for a given SAS/SATA phy index/identifier. + * @controller: This parameter represents the handle to the controller object + * from which to retrieve a phy (SAS or SATA) handle. + * @phy_index: This parameter specifies the phy index in the controller for + * which to retrieve the phy handle. 0 <= phy_index < maximum number of phys. + * @phy_handle: This parameter specifies the retrieved phy handle to be + * provided to the caller. + * + * Indicate if the retrieval of the phy handle was successful. SCI_SUCCESS This + * value is returned if the retrieval was successful. SCI_FAILURE_INVALID_PHY + * This value is returned if the supplied phy id is not in the supported range. + */ +enum sci_status scic_controller_get_phy_handle( + struct scic_sds_controller *controller, + u8 phy_index, + struct scic_sds_phy **phy_handle); + +/** + * scic_controller_allocate_io_tag() - This method will allocate a tag from the + * pool of free IO tags. Direct allocation of IO tags by the SCI Core user + * is optional. The scic_controller_start_io() method will allocate an IO + * tag if this method is not utilized and the tag is not supplied to the IO + * construct routine. Direct allocation of IO tags may provide additional + * performance improvements in environments capable of supporting this usage + * model. Additionally, direct allocation of IO tags also provides + * additional flexibility to the SCI Core user. Specifically, the user may + * retain IO tags across the lives of multiple IO requests. + * @controller: the handle to the controller object for which to allocate the + * tag. + * + * IO tags are a protected resource. It is incumbent upon the SCI Core user to + * ensure that each of the methods that may allocate or free available IO tags + * are handled in a mutually exclusive manner. This method is one of said + * methods requiring proper critical code section protection (e.g. semaphore, + * spin-lock, etc.). An unsigned integer representing an available IO tag. + * SCI_CONTROLLER_INVALID_IO_TAG This value is returned if there are no + * currently available tags to be allocated. All return other values indicate a + * legitimate tag. + */ +u16 scic_controller_allocate_io_tag( + struct scic_sds_controller *controller); + +/** + * scic_controller_free_io_tag() - This method will free an IO tag to the pool + * of free IO tags. This method provides the SCI Core user more flexibility + * with regards to IO tags. The user may desire to keep an IO tag after an + * IO request has completed, because they plan on re-using the tag for a + * subsequent IO request. This method is only legal if the tag was + * allocated via scic_controller_allocate_io_tag(). + * @controller: This parameter specifies the handle to the controller object + * for which to free/return the tag. + * @io_tag: This parameter represents the tag to be freed to the pool of + * available tags. + * + * - IO tags are a protected resource. It is incumbent upon the SCI Core user + * to ensure that each of the methods that may allocate or free available IO + * tags are handled in a mutually exclusive manner. This method is one of said + * methods requiring proper critical code section protection (e.g. semaphore, + * spin-lock, etc.). - If the IO tag for a request was allocated, by the SCI + * Core user, using the scic_controller_allocate_io_tag() method, then it is + * the responsibility of the caller to invoke this method to free the tag. This + * method returns an indication of whether the tag was successfully put back + * (freed) to the pool of available tags. SCI_SUCCESS This return value + * indicates the tag was successfully placed into the pool of available IO + * tags. SCI_FAILURE_INVALID_IO_TAG This value is returned if the supplied tag + * is not a valid IO tag value. + */ +enum sci_status scic_controller_free_io_tag( + struct scic_sds_controller *controller, + u16 io_tag); + + + + +/** + * scic_controller_set_mode() - This method allows the user to configure the + * SCI core into either a performance mode or a memory savings mode. + * @controller: This parameter represents the handle to the controller object + * for which to update the operating mode. + * @mode: This parameter specifies the new mode for the controller. + * + * Indicate if the user successfully change the operating mode of the + * controller. SCI_SUCCESS The user successfully updated the mode. + */ +enum sci_status scic_controller_set_mode( + struct scic_sds_controller *controller, + enum sci_controller_mode mode); + + +/** + * scic_controller_set_interrupt_coalescence() - This method allows the user to + * configure the interrupt coalescence. + * @controller: This parameter represents the handle to the controller object + * for which its interrupt coalesce register is overridden. + * @coalesce_number: Used to control the number of entries in the Completion + * Queue before an interrupt is generated. If the number of entries exceed + * this number, an interrupt will be generated. The valid range of the input + * is [0, 256]. A setting of 0 results in coalescing being disabled. + * @coalesce_timeout: Timeout value in microseconds. The valid range of the + * input is [0, 2700000] . A setting of 0 is allowed and results in no + * interrupt coalescing timeout. + * + * Indicate if the user successfully set the interrupt coalesce parameters. + * SCI_SUCCESS The user successfully updated the interrutp coalescence. + * SCI_FAILURE_INVALID_PARAMETER_VALUE The user input value is out of range. + */ +enum sci_status scic_controller_set_interrupt_coalescence( + struct scic_sds_controller *controller, + u32 coalesce_number, + u32 coalesce_timeout); + +struct device; +struct scic_sds_controller *scic_controller_alloc(struct device *dev); + + +#endif /* _SCIC_CONTROLLER_H_ */ + diff --git a/drivers/scsi/isci/core/scic_io_request.h b/drivers/scsi/isci/core/scic_io_request.h new file mode 100644 index 000000000000..7378f335066c --- /dev/null +++ b/drivers/scsi/isci/core/scic_io_request.h @@ -0,0 +1,512 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_IO_REQUEST_H_ +#define _SCIC_IO_REQUEST_H_ + +/** + * This file contains the structures and interface methods that can be + * referenced and used by the SCI user for the SCI IO request object. + * + * Determine the failure situations and return values. + */ + + +#include "sci_types.h" +#include "sci_status.h" +#include "intel_sas.h" + +struct scic_sds_request; +struct scic_sds_remote_device; +struct scic_sds_controller; + +/** + * struct scic_io_parameters - This structure contains additional optional + * parameters for SSP IO requests. These parameters are utilized with the + * scic_io_request_construct_advanced_ssp() method. + * + * Add Block-guard/DIF, TLR + */ +struct scic_io_parameters { + /** + * This sub-structure contains SCSI specific features (for use with SSP + * IO requests). + */ + struct { + /** + * Data Integrity Format (DIF) is also known as protection information + * or block-guard. This sub-structure contains DIF specific feature + * information for SSP IO requests. + */ + struct { + void *placeholder; + } dif; + + /** + * Transport Layer Retries (TLR) is an SSP protocol specific feature. + * This sub-structure contains Transport Layer Retries (TLR) specific + * feature information for SSP IO requests. + */ + struct { + void *placeholder; + } tlr; + + } scsi; + +}; + +/** + * struct scic_passthru_request_callbacks - This structure contains the pointer + * to the callback functions for constructing the passthrough request common + * to SSP, SMP and STP. This structure must be set by the win sci layer + * before the passthrough build is called + * + * + */ +struct scic_passthru_request_callbacks { + /** + * Function pointer to get the phy identifier for passthrough request. + */ + u32 (*scic_cb_passthru_get_phy_identifier)(void *, u8 *); + /** + * Function pointer to get the port identifier for passthrough request. + */ + u32 (*scic_cb_passthru_get_port_identifier)(void *, u8 *); + /** + * Function pointer to get the connection rate for passthrough request. + */ + u32 (*scic_cb_passthru_get_connection_rate)(void *, void *); + /** + * Function pointer to get the destination sas address for passthrough request. + */ + void (*scic_cb_passthru_get_destination_sas_address)(void *, u8 **); + /** + * Function pointer to get the transfer length for passthrough request. + */ + u32 (*scic_cb_passthru_get_transfer_length)(void *); + /** + * Function pointer to get the data direction for passthrough request. + */ + u32 (*scic_cb_passthru_get_data_direction)(void *); + +}; + +/** + * struct scic_ssp_passthru_request_callbacks - This structure contains the + * pointer to the callback functions for constructing the passthrough + * request specific to SSP. This structure must be set by the win sci layer + * before the passthrough build is called + * + * + */ +struct scic_ssp_passthru_request_callbacks { + /** + * Common callbacks for all Passthru requests + */ + struct scic_passthru_request_callbacks common_callbacks; + /** + * Function pointer to get the lun for passthrough request. + */ + void (*scic_cb_ssp_passthru_get_lun)(void *, u8 **); + /** + * Function pointer to get the cdb + */ + void (*scic_cb_ssp_passthru_get_cdb)(void *, u32 *, u8 **, u32 *, u8 **); + /** + * Function pointer to get the task attribute for passthrough request. + */ + u32 (*scic_cb_ssp_passthru_get_task_attribute)(void *); +}; + +/** + * struct scic_stp_passthru_request_callbacks - This structure contains the + * pointer to the callback functions for constructing the passthrough + * request specific to STP. This structure must be set by the win sci layer + * before the passthrough build is called + * + * + */ +struct scic_stp_passthru_request_callbacks { + /** + * Common callbacks for all Passthru requests + */ + struct scic_passthru_request_callbacks common_callbacks; + /** + * Function pointer to get the protocol for passthrough request. + */ + u8 (*scic_cb_stp_passthru_get_protocol)(void *); + /** + * Function pointer to get the resgister fis + */ + void (*scic_cb_stp_passthru_get_register_fis)(void *, u8 **); + /** + * Function pointer to get the MULTIPLE_COUNT (bits 5,6,7 in Byte 1 in the SAT-specific SCSI extenstion in ATA Pass-through (0x85)) + */ + u8 (*scic_cb_stp_passthru_get_multiplecount)(void *); + /** + * Function pointer to get the EXTEND (bit 0 in Byte 1 the SAT-specific SCSI extenstion in ATA Pass-through (0x85)) + */ + u8 (*scic_cb_stp_passthru_get_extend)(void *); + /** + * Function pointer to get the CK_COND (bit 5 in Byte 2 the SAT-specific SCSI extenstion in ATA Pass-through (0x85)) + */ + u8 (*scic_cb_stp_passthru_get_ckcond)(void *); + /** + * Function pointer to get the T_DIR (bit 3 in Byte 2 the SAT-specific SCSI extenstion in ATA Pass-through (0x85)) + */ + u8 (*scic_cb_stp_passthru_get_tdir)(void *); + /** + * Function pointer to get the BYTE_BLOCK (bit 2 in Byte 2 the SAT-specific SCSI extenstion in ATA Pass-through (0x85)) + */ + u8 (*scic_cb_stp_passthru_get_byteblock)(void *); + /** + * Function pointer to get the T_LENGTH (bits 0,1 in Byte 2 the SAT-specific SCSI extenstion in ATA Pass-through (0x85)) + */ + u8 (*scic_cb_stp_passthru_get_tlength)(void *); + +}; + +/** + * struct scic_smp_passthru_request_callbacks - This structure contains the + * pointer to the callback functions for constructing the passthrough + * request specific to SMP. This structure must be set by the win sci layer + * before the passthrough build is called + * + * + */ +struct scic_smp_passthru_request_callbacks { + /** + * Common callbacks for all Passthru requests + */ + struct scic_passthru_request_callbacks common_callbacks; + + /** + * Function pointer to get the length of the smp request and its length + */ + u32 (*scic_cb_smp_passthru_get_request)(void *, u8 **); + /** + * Function pointer to get the frame type of the smp request + */ + u8 (*scic_cb_smp_passthru_get_frame_type)(void *); + /** + * Function pointer to get the function in the the smp request + */ + u8 (*scic_cb_smp_passthru_get_function)(void *); + + /** + * Function pointer to get the "allocated response length" in the the smp request + */ + u8 (*scic_cb_smp_passthru_get_allocated_response_length)(void *); + +}; + +/** + * This enumeration specifies the transport protocol utilized for the request. + * + * + */ +typedef enum { + /** + * This enumeration constant indicates that no protocol has yet been + * set. + */ + SCIC_NO_PROTOCOL, + + /** + * This enumeration constant indicates that the protocol utilized + * is the Serial Management Protocol. + */ + SCIC_SMP_PROTOCOL, + + /** + * This enumeration constant indicates that the protocol utilized + * is the Serial SCSI Protocol. + */ + SCIC_SSP_PROTOCOL, + + /** + * This enumeration constant indicates that the protocol utilized + * is the Serial-ATA Tunneling Protocol. + */ + SCIC_STP_PROTOCOL + +} SCIC_TRANSPORT_PROTOCOL; + + +/** + * scic_io_request_get_object_size() - This method simply returns the size + * required to build an SCI based IO request object. + * + * Return the size of the SCI IO request object. + */ +u32 scic_io_request_get_object_size( + void); + +/** + * scic_io_request_construct() - This method is called by the SCI user to + * construct all SCI Core IO requests. Memory initialization and + * functionality common to all IO request types is performed in this method. + * @scic_controller: the handle to the core controller object for which to + * build an IO request. + * @scic_remote_device: the handle to the core remote device object for which + * to build an IO request. + * @io_tag: This parameter specifies the IO tag to be associated with this + * request. If SCI_CONTROLLER_INVALID_IO_TAG is passed, then a copy of the + * request is built internally. The request will be copied into the actual + * controller request memory when the IO tag is allocated internally during + * the scic_controller_start_io() method. + * @user_io_request_object: This parameter specifies the user IO request to be + * utilized during IO construction. This IO pointer will become the + * associated object for the core IO request object. + * @scic_io_request_memory: This parameter specifies the memory location to be + * utilized when building the core request. + * @new_scic_io_request_handle: This parameter specifies a pointer to the + * handle the core will expect in further interactions with the core IO + * request object. + * + * The SCI core implementation will create an association between the user IO + * request object and the core IO request object. Indicate if the controller + * successfully built the IO request. SCI_SUCCESS This value is returned if the + * IO request was successfully built. + */ +enum sci_status scic_io_request_construct( + struct scic_sds_controller *scic_controller, + struct scic_sds_remote_device *scic_remote_device, + u16 io_tag, + void *user_io_request_object, + void *scic_io_request_memory, + struct scic_sds_request **new_scic_io_request_handle); + +/** + * scic_io_request_construct_basic_ssp() - This method is called by the SCI + * user to build an SSP IO request. + * @scic_io_request: This parameter specifies the handle to the io request + * object to be built. + * + * - The user must have previously called scic_io_request_construct() on the + * supplied IO request. Indicate if the controller successfully built the IO + * request. SCI_SUCCESS This value is returned if the IO request was + * successfully built. SCI_FAILURE_UNSUPPORTED_PROTOCOL This value is returned + * if the remote_device does not support the SSP protocol. + * SCI_FAILURE_INVALID_ASSOCIATION This value is returned if the user did not + * properly set the association between the SCIC IO request and the user's IO + * request. Please refer to the sci_object_set_association() routine for more + * information. + */ +enum sci_status scic_io_request_construct_basic_ssp( + struct scic_sds_request *scic_io_request); + + + + + +/** + * scic_io_request_construct_basic_sata() - This method is called by the SCI + * Core user to build an STP IO request. + * @scic_io_request: This parameter specifies the handle to the io request + * object to be built. + * + * - The user must have previously called scic_io_request_construct() on the + * supplied IO request. Indicate if the controller successfully built the IO + * request. SCI_SUCCESS This value is returned if the IO request was + * successfully built. SCI_FAILURE_UNSUPPORTED_PROTOCOL This value is returned + * if the remote_device does not support the STP protocol. + * SCI_FAILURE_INVALID_ASSOCIATION This value is returned if the user did not + * properly set the association between the SCIC IO request and the user's IO + * request. Please refer to the sci_object_set_association() routine for more + * information. + */ +enum sci_status scic_io_request_construct_basic_sata( + struct scic_sds_request *scic_io_request); + + + + +/** + * scic_io_request_construct_smp() - This method is called by the SCI user to + * build an SMP IO request. + * @scic_io_request: This parameter specifies the handle to the io request + * object to be built. + * + * - The user must have previously called scic_io_request_construct() on the + * supplied IO request. Indicate if the controller successfully built the IO + * request. SCI_SUCCESS This value is returned if the IO request was + * successfully built. SCI_FAILURE_UNSUPPORTED_PROTOCOL This value is returned + * if the remote_device does not support the SMP protocol. + * SCI_FAILURE_INVALID_ASSOCIATION This value is returned if the user did not + * properly set the association between the SCIC IO request and the user's IO + * request. Please refer to the sci_object_set_association() routine for more + * information. + */ +enum sci_status scic_io_request_construct_smp( + struct scic_sds_request *scic_io_request); + + + +/** + * scic_request_get_controller_status() - This method returns the controller + * specific IO/Task request status. These status values are unique to the + * specific controller being managed by the SCIC. + * @io_request: the handle to the IO or task management request object for + * which to retrieve the status. + * + * This method returns a value indicating the controller specific request + * status. + */ +u32 scic_request_get_controller_status( + struct scic_sds_request *io_request); + + + +/** + * scic_io_request_get_command_iu_address() - This method will return the + * address to the command information unit. + * @scic_io_request: This parameter specifies the handle to the io request + * object to be built. + * + * The address of the SSP/SMP command information unit. + */ +void *scic_io_request_get_command_iu_address( + struct scic_sds_request *scic_io_request); + +/** + * scic_io_request_get_response_iu_address() - This method will return the + * address to the response information unit. For an SSP request this buffer + * is only valid if the IO request is completed with the status + * SCI_FAILURE_IO_RESPONSE_VALID. + * @scic_io_request: This parameter specifies the handle to the io request + * object to be built. + * + * The address of the SSP/SMP response information unit. + */ +void *scic_io_request_get_response_iu_address( + struct scic_sds_request *scic_io_request); + +/** + * scic_io_request_get_io_tag() - This method will return the IO tag utilized + * by the IO request. + * @scic_io_request: This parameter specifies the handle to the io request + * object for which to return the IO tag. + * + * An unsigned integer representing the IO tag being utilized. + * SCI_CONTROLLER_INVALID_IO_TAG This value is returned if the IO does not + * currently have an IO tag allocated to it. All return other values indicate a + * legitimate tag. + */ +u16 scic_io_request_get_io_tag( + struct scic_sds_request *scic_io_request); + + +/** + * scic_stp_io_request_set_ncq_tag() - This method will assign an NCQ tag to + * the io request object. The caller of this function must make sure that + * only valid NCQ tags are assigned to the io request object. + * @scic_io_request: This parameter specifies the handle to the io request + * object to which to assign the ncq tag. + * @ncq_tag: This parameter specifies the NCQ tag to be utilized for the + * supplied core IO request. It is up to the user to make sure that this is + * a valid NCQ tag. + * + * none This function is only valid for SATA NCQ requests. + */ +void scic_stp_io_request_set_ncq_tag( + struct scic_sds_request *scic_io_request, + u16 ncq_tag); + +/** + * scic_stp_io_request_get_h2d_reg_address() - This method will return the + * address of the host to device register fis region for the io request + * object. + * @scic_io_request: This parameter specifies the handle to the io request + * object from which to get the host to device register fis buffer. + * + * The address of the host to device register fis buffer in the io request + * object. This function is only valid for SATA requests. + */ +void *scic_stp_io_request_get_h2d_reg_address( + struct scic_sds_request *scic_io_request); + +/** + * scic_stp_io_request_get_d2h_reg_address() - This method will return the + * address of the device to host register fis region for the io request + * object. + * @scic_io_request: This parameter specifies teh handle to the io request + * object from which to get the device to host register fis buffer. + * + * The address fo the device to host register fis ending the io request. This + * function is only valid for SATA requests. + */ +void *scic_stp_io_request_get_d2h_reg_address( + struct scic_sds_request *scic_io_request); + + +/** + * scic_io_request_get_number_of_bytes_transferred() - This method will return + * the number of bytes transferred from the SCU + * @scic_io_request: This parameter specifies the handle to the io request + * whose data length was not eqaul to the data length specified in the + * request. When the driver gets an early io completion status from the + * hardware, this routine should be called to get the actual number of bytes + * transferred + * + * The return is the number of bytes transferred when the data legth is not + * equal to the specified length in the io request + */ +u32 scic_io_request_get_number_of_bytes_transferred( + struct scic_sds_request *scic_io_request); + + +#endif /* _SCIC_IO_REQUEST_H_ */ + diff --git a/drivers/scsi/isci/core/scic_phy.h b/drivers/scsi/isci/core/scic_phy.h new file mode 100644 index 000000000000..25a6140fa9e4 --- /dev/null +++ b/drivers/scsi/isci/core/scic_phy.h @@ -0,0 +1,303 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_PHY_H_ +#define _SCIC_PHY_H_ + +/** + * This file contains all of the interface methods that can be called by an + * SCIC user on a phy (SAS or SATA) object. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" + +#include "intel_sata.h" +#include "intel_sas.h" + +struct scic_sds_phy; +struct scic_sds_port; + +/** + * struct scic_phy_properties - This structure defines the properties common to + * all phys that can be retrieved. + * + * + */ +struct scic_phy_properties { + /** + * This field specifies the port that currently contains the + * supplied phy. This field may be set to SCI_INVALID_HANDLE + * if the phy is not currently contained in a port. + */ + struct scic_sds_port *owning_port; + + /** + * This field specifies the link rate at which the phy is + * currently operating. + */ + enum sci_sas_link_rate negotiated_link_rate; + + /** + * This field indicates the protocols supported by the phy. + */ + struct sci_sas_identify_address_frame_protocols protocols; + + /** + * This field specifies the index of the phy in relation to other + * phys within the controller. This index is zero relative. + */ + u8 index; + +}; + +/** + * struct scic_sas_phy_properties - This structure defines the properties, + * specific to a SAS phy, that can be retrieved. + * + * + */ +struct scic_sas_phy_properties { + /** + * This field delineates the Identify Address Frame received + * from the remote end point. + */ + struct sci_sas_identify_address_frame received_iaf; + + /** + * This field delineates the Phy capabilities structure received + * from the remote end point. + */ + struct sas_capabilities received_capabilities; + +}; + +/** + * struct scic_sata_phy_properties - This structure defines the properties, + * specific to a SATA phy, that can be retrieved. + * + * + */ +struct scic_sata_phy_properties { + /** + * This field delineates the signature FIS received from the + * attached target. + */ + struct sata_fis_reg_d2h signature_fis; + + /** + * This field specifies to the user if a port selector is connected + * on the specified phy. + */ + bool is_port_selector_present; + +}; + +/** + * enum scic_phy_counter_id - This enumeration depicts the various pieces of + * optional information that can be retrieved for a specific phy. + * + * + */ +enum scic_phy_counter_id { + /** + * This PHY information field tracks the number of frames received. + */ + SCIC_PHY_COUNTER_RECEIVED_FRAME, + + /** + * This PHY information field tracks the number of frames transmitted. + */ + SCIC_PHY_COUNTER_TRANSMITTED_FRAME, + + /** + * This PHY information field tracks the number of DWORDs received. + */ + SCIC_PHY_COUNTER_RECEIVED_FRAME_WORD, + + /** + * This PHY information field tracks the number of DWORDs transmitted. + */ + SCIC_PHY_COUNTER_TRANSMITTED_FRAME_DWORD, + + /** + * This PHY information field tracks the number of times DWORD + * synchronization was lost. + */ + SCIC_PHY_COUNTER_LOSS_OF_SYNC_ERROR, + + /** + * This PHY information field tracks the number of received DWORDs with + * running disparity errors. + */ + SCIC_PHY_COUNTER_RECEIVED_DISPARITY_ERROR, + + /** + * This PHY information field tracks the number of received frames with a + * CRC error (not including short or truncated frames). + */ + SCIC_PHY_COUNTER_RECEIVED_FRAME_CRC_ERROR, + + /** + * This PHY information field tracks the number of DONE (ACK/NAK TIMEOUT) + * primitives received. + */ + SCIC_PHY_COUNTER_RECEIVED_DONE_ACK_NAK_TIMEOUT, + + /** + * This PHY information field tracks the number of DONE (ACK/NAK TIMEOUT) + * primitives transmitted. + */ + SCIC_PHY_COUNTER_TRANSMITTED_DONE_ACK_NAK_TIMEOUT, + + /** + * This PHY information field tracks the number of times the inactivity + * timer for connections on the phy has been utilized. + */ + SCIC_PHY_COUNTER_INACTIVITY_TIMER_EXPIRED, + + /** + * This PHY information field tracks the number of DONE (CREDIT TIMEOUT) + * primitives received. + */ + SCIC_PHY_COUNTER_RECEIVED_DONE_CREDIT_TIMEOUT, + + /** + * This PHY information field tracks the number of DONE (CREDIT TIMEOUT) + * primitives transmitted. + */ + SCIC_PHY_COUNTER_TRANSMITTED_DONE_CREDIT_TIMEOUT, + + /** + * This PHY information field tracks the number of CREDIT BLOCKED + * primitives received. + * @note Depending on remote device implementation, credit blocks + * may occur regularly. + */ + SCIC_PHY_COUNTER_RECEIVED_CREDIT_BLOCKED, + + /** + * This PHY information field contains the number of short frames + * received. A short frame is simply a frame smaller then what is + * allowed by either the SAS or SATA specification. + */ + SCIC_PHY_COUNTER_RECEIVED_SHORT_FRAME, + + /** + * This PHY information field contains the number of frames received after + * credit has been exhausted. + */ + SCIC_PHY_COUNTER_RECEIVED_FRAME_WITHOUT_CREDIT, + + /** + * This PHY information field contains the number of frames received after + * a DONE has been received. + */ + SCIC_PHY_COUNTER_RECEIVED_FRAME_AFTER_DONE, + + /** + * This PHY information field contains the number of times the phy + * failed to achieve DWORD synchronization during speed negotiation. + */ + SCIC_PHY_COUNTER_SN_DWORD_SYNC_ERROR +}; + + +/** + * scic_sas_phy_get_properties() - This method will enable the user to retrieve + * information specific to a SAS phy, such as: the received identify address + * frame, received phy capabilities, etc. + * @phy: this parameter specifies the phy for which to retrieve properties. + * @properties: This parameter specifies the properties structure into which to + * copy the requested information. + * + * This method returns an indication as to whether the SAS phy properties were + * successfully retrieved. SCI_SUCCESS This value is returned if the SAS + * properties are successfully retrieved. SCI_FAILURE This value is returned if + * the SAS properties are not successfully retrieved (e.g. It's not a SAS Phy). + */ +enum sci_status scic_sas_phy_get_properties( + struct scic_sds_phy *phy, + struct scic_sas_phy_properties *properties); + +/** + * scic_sata_phy_get_properties() - This method will enable the user to + * retrieve information specific to a SATA phy, such as: the received + * signature FIS, if a port selector is present, etc. + * @phy: this parameter specifies the phy for which to retrieve properties. + * @properties: This parameter specifies the properties structure into which to + * copy the requested information. + * + * This method returns an indication as to whether the SATA phy properties were + * successfully retrieved. SCI_SUCCESS This value is returned if the SATA + * properties are successfully retrieved. SCI_FAILURE This value is returned if + * the SATA properties are not successfully retrieved (e.g. It's not a SATA + * Phy). + */ +enum sci_status scic_sata_phy_get_properties( + struct scic_sds_phy *phy, + struct scic_sata_phy_properties *properties); + + + + + + + +#endif /* _SCIC_PHY_H_ */ + diff --git a/drivers/scsi/isci/core/scic_port.h b/drivers/scsi/isci/core/scic_port.h new file mode 100644 index 000000000000..34d22c04aa56 --- /dev/null +++ b/drivers/scsi/isci/core/scic_port.h @@ -0,0 +1,213 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_PORT_H_ +#define _SCIC_PORT_H_ + +/** + * This file contains all of the interface methods that can be called by an SCI + * Core user on a SAS or SATA port. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" +#include "intel_sas.h" + +struct scic_sds_port; + +enum SCIC_PORT_NOT_READY_REASON_CODE { + SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS, + SCIC_PORT_NOT_READY_HARD_RESET_REQUESTED, + SCIC_PORT_NOT_READY_INVALID_PORT_CONFIGURATION, + SCIC_PORT_NOT_READY_RECONFIGURING, + + SCIC_PORT_NOT_READY_REASON_CODE_MAX +}; + +/** + * struct scic_port_end_point_properties - This structure defines the + * properties that can be retrieved for each end-point local or remote + * (attached) port in the controller. + * + * + */ +struct scic_port_end_point_properties { + /** + * This field indicates the SAS address for the associated end + * point in the port. + */ + struct sci_sas_address sas_address; + + /** + * This field indicates the protocols supported by the associated + * end-point in the port. + */ + struct sci_sas_identify_address_frame_protocols protocols; + +}; + +/** + * struct scic_port_properties - This structure defines the properties that can + * be retrieved for each port in the controller. + * + * + */ +struct scic_port_properties { + /** + * This field specifies the logical index of the port (0 relative). + */ + u32 index; + + /** + * This field indicates the local end-point properties for port. + */ + struct scic_port_end_point_properties local; + + /** + * This field indicates the remote (attached) end-point properties + * for the port. + */ + struct scic_port_end_point_properties remote; + + /** + * This field specifies the phys contained inside the port. + */ + u32 phy_mask; + +}; + +/** + * scic_port_get_properties() - This method simply returns the properties + * regarding the port, such as: physical index, protocols, sas address, etc. + * @port: this parameter specifies the port for which to retrieve the physical + * index. + * @properties: This parameter specifies the properties structure into which to + * copy the requested information. + * + * Indicate if the user specified a valid port. SCI_SUCCESS This value is + * returned if the specified port was valid. SCI_FAILURE_INVALID_PORT This + * value is returned if the specified port is not valid. When this value is + * returned, no data is copied to the properties output parameter. + */ +enum sci_status scic_port_get_properties( + struct scic_sds_port *port, + struct scic_port_properties *properties); + + + +/** + * scic_port_start() - This method will make the port ready for operation. + * Prior to calling the start method IO operation is not possible. + * @port: This parameter specifies the port to be started. + * + * Indicate if the port was successfully started. SCI_SUCCESS This value is + * returned if the port was successfully started. SCI_WARNING_ALREADY_IN_STATE + * This value is returned if the port is in the process of starting. + * SCI_FAILURE_INVALID_PORT This value is returned if the supplied port is not + * valid. SCI_FAILURE_INVALID_STATE This value is returned if a start operation + * can't be completed due to the state of port. + */ +enum sci_status scic_port_start( + struct scic_sds_port *port); + +/** + * scic_port_stop() - This method will make the port no longer ready for + * operation. After invoking this method IO operation is not possible. + * @port: This parameter specifies the port to be stopped. + * + * Indicate if the port was successfully stopped. SCI_SUCCESS This value is + * returned if the port was successfully stopped. SCI_WARNING_ALREADY_IN_STATE + * This value is returned if the port is already stopped or in the process of + * stopping. SCI_FAILURE_INVALID_PORT This value is returned if the supplied + * port is not valid. SCI_FAILURE_INVALID_STATE This value is returned if a + * stop operation can't be completed due to the state of port. + */ +enum sci_status scic_port_stop( + struct scic_sds_port *port); + +/** + * scic_port_hard_reset() - This method will request the SCI implementation to + * perform a HARD RESET on the SAS Port. If/When the HARD RESET completes + * the SCI user will be notified via an SCI OS callback indicating a direct + * attached device was found. + * @port: a handle corresponding to the SAS port to be hard reset. + * @reset_timeout: This parameter specifies the number of milliseconds in which + * the port reset operation should complete. + * + * The SCI User callback in SCIC_USER_CALLBACKS_T will only be called once for + * each phy in the SAS Port at completion of the hard reset sequence. Return a + * status indicating whether the hard reset started successfully. SCI_SUCCESS + * This value is returned if the hard reset operation started successfully. + */ +enum sci_status scic_port_hard_reset( + struct scic_sds_port *port, + u32 reset_timeout); + +/** + * scic_port_enable_broadcast_change_notification() - This API method enables + * the broadcast change notification from underneath hardware. + * @port: The port upon which broadcast change notifications (BCN) are to be + * enabled. + * + */ +void scic_port_enable_broadcast_change_notification( + struct scic_sds_port *port); + + +#endif /* _SCIC_PORT_H_ */ + diff --git a/drivers/scsi/isci/core/scic_remote_device.h b/drivers/scsi/isci/core/scic_remote_device.h new file mode 100644 index 000000000000..e8c04592763e --- /dev/null +++ b/drivers/scsi/isci/core/scic_remote_device.h @@ -0,0 +1,295 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_REMOTE_DEVICE_H_ +#define _SCIC_REMOTE_DEVICE_H_ + +/** + * This file contains all of the interface methods that can be called by an + * SCIC user on the device object. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" +#include "intel_sas.h" + +struct scic_sds_port; +struct scic_sds_remote_device; + +/** + * + * + * + */ +enum scic_remote_device_not_ready_reason_code { + SCIC_REMOTE_DEVICE_NOT_READY_START_REQUESTED, + SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED, + SCIC_REMOTE_DEVICE_NOT_READY_SATA_REQUEST_STARTED, + SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED, + SCIC_REMOTE_DEVICE_NOT_READY_SMP_REQUEST_STARTED, + + SCIC_REMOTE_DEVICE_NOT_READY_REASON_CODE_MAX + +}; + +/** + * scic_remote_device_get_object_size() - This method simply returns the + * maximum memory space needed to store a remote device object. + * + * a positive integer value indicating the size (in bytes) of the remote device + * object. + */ +u32 scic_remote_device_get_object_size( + void); + +struct scic_sds_port; +struct scic_sds_remote_device; +/** + * scic_remote_device_construct() - This method will perform the construction + * common to all remote device objects. + * @sci_port: SAS/SATA port through which this device is accessed. + * @sci_dev: remote device to construct + * + * It isn't necessary to call scic_remote_device_destruct() for device objects + * that have only called this method for construction. Once subsequent + * construction methods have been invoked (e.g. + * scic_remote_device_da_construct()), then destruction should occur. none + */ +void scic_remote_device_construct(struct scic_sds_port *sci_port, + struct scic_sds_remote_device *sci_dev); + +/** + * scic_remote_device_da_construct() - This method will construct a + * SCIC_REMOTE_DEVICE object for a direct attached (da) device. The + * information (e.g. IAF, Signature FIS, etc.) necessary to build the device + * is known to the SCI Core since it is contained in the scic_phy object. + * @remote_device: This parameter specifies the remote device to be destructed. + * + * The user must have previously called scic_remote_device_construct() Remote + * device objects are a limited resource. As such, they must be protected. + * Thus calls to construct and destruct are mutually exclusive and + * non-reentrant. Indicate if the remote device was successfully constructed. + * SCI_SUCCESS Returned if the device was successfully constructed. + * SCI_FAILURE_DEVICE_EXISTS Returned if the device has already been + * constructed. If it's an additional phy for the target, then call + * scic_remote_device_da_add_phy(). SCI_FAILURE_UNSUPPORTED_PROTOCOL Returned + * if the supplied parameters necessitate creation of a remote device for which + * the protocol is not supported by the underlying controller hardware. + * SCI_FAILURE_INSUFFICIENT_RESOURCES This value is returned if the core + * controller associated with the supplied parameters is unable to support + * additional remote devices. + */ +enum sci_status scic_remote_device_da_construct( + struct scic_sds_remote_device *remote_device); + +/** + * scic_remote_device_ea_construct() - This method will construct an + * SCIC_REMOTE_DEVICE object for an expander attached (ea) device from an + * SMP Discover Response. + * @remote_device: This parameter specifies the remote device to be destructed. + * @discover_response: This parameter specifies the SMP Discovery Response to + * be used in device creation. + * + * The user must have previously called scic_remote_device_construct() Remote + * device objects are a limited resource. As such, they must be protected. + * Thus calls to construct and destruct are mutually exclusive and + * non-reentrant. Indicate if the remote device was successfully constructed. + * SCI_SUCCESS Returned if the device was successfully constructed. + * SCI_FAILURE_DEVICE_EXISTS Returned if the device has already been + * constructed. If it's an additional phy for the target, then call + * scic_ea_remote_device_add_phy(). SCI_FAILURE_UNSUPPORTED_PROTOCOL Returned + * if the supplied parameters necessitate creation of a remote device for which + * the protocol is not supported by the underlying controller hardware. + * SCI_FAILURE_INSUFFICIENT_RESOURCES This value is returned if the core + * controller associated with the supplied parameters is unable to support + * additional remote devices. + */ +enum sci_status scic_remote_device_ea_construct( + struct scic_sds_remote_device *remote_device, + struct smp_response_discover *discover_response); + +/** + * scic_remote_device_destruct() - This method is utilized to free up a core's + * remote device object. + * @remote_device: This parameter specifies the remote device to be destructed. + * + * Remote device objects are a limited resource. As such, they must be + * protected. Thus calls to construct and destruct are mutually exclusive and + * non-reentrant. The return value shall indicate if the device was + * successfully destructed or if some failure occurred. enum sci_status This value + * is returned if the device is successfully destructed. + * SCI_FAILURE_INVALID_REMOTE_DEVICE This value is returned if the supplied + * device isn't valid (e.g. it's already been destoryed, the handle isn't + * valid, etc.). + */ +enum sci_status scic_remote_device_destruct( + struct scic_sds_remote_device *remote_device); + + + + + +/** + * scic_remote_device_start() - This method will start the supplied remote + * device. This method enables normal IO requests to flow through to the + * remote device. + * @remote_device: This parameter specifies the device to be started. + * @timeout: This parameter specifies the number of milliseconds in which the + * start operation should complete. + * + * An indication of whether the device was successfully started. SCI_SUCCESS + * This value is returned if the device was successfully started. + * SCI_FAILURE_INVALID_PHY This value is returned if the user attempts to start + * the device when there have been no phys added to it. + */ +enum sci_status scic_remote_device_start( + struct scic_sds_remote_device *remote_device, + u32 timeout); + +/** + * scic_remote_device_stop() - This method will stop both transmission and + * reception of link activity for the supplied remote device. This method + * disables normal IO requests from flowing through to the remote device. + * @remote_device: This parameter specifies the device to be stopped. + * @timeout: This parameter specifies the number of milliseconds in which the + * stop operation should complete. + * + * An indication of whether the device was successfully stopped. SCI_SUCCESS + * This value is returned if the transmission and reception for the device was + * successfully stopped. + */ +enum sci_status scic_remote_device_stop( + struct scic_sds_remote_device *remote_device, + u32 timeout); + +/** + * scic_remote_device_reset() - This method will reset the device making it + * ready for operation. This method must be called anytime the device is + * reset either through a SMP phy control or a port hard reset request. + * @remote_device: This parameter specifies the device to be reset. + * + * This method does not actually cause the device hardware to be reset. This + * method resets the software object so that it will be operational after a + * device hardware reset completes. An indication of whether the device reset + * was accepted. SCI_SUCCESS This value is returned if the device reset is + * started. + */ +enum sci_status scic_remote_device_reset( + struct scic_sds_remote_device *remote_device); + +/** + * scic_remote_device_reset_complete() - This method informs the device object + * that the reset operation is complete and the device can resume operation + * again. + * @remote_device: This parameter specifies the device which is to be informed + * of the reset complete operation. + * + * An indication that the device is resuming operation. SCI_SUCCESS the device + * is resuming operation. + */ +enum sci_status scic_remote_device_reset_complete( + struct scic_sds_remote_device *remote_device); + + + +/** + * scic_remote_device_get_connection_rate() - This method simply returns the + * link rate at which communications to the remote device occur. + * @remote_device: This parameter specifies the device for which to get the + * connection rate. + * + * Return the link rate at which we transfer for the supplied remote device. + */ +enum sci_sas_link_rate scic_remote_device_get_connection_rate( + struct scic_sds_remote_device *remote_device); + +/** + * scic_remote_device_get_protocols() - This method will indicate which + * protocols are supported by this remote device. + * @remote_device: This parameter specifies the device for which to return the + * protocol. + * @protocols: This parameter specifies the output values, from the remote + * device object, which indicate the protocols supported by the supplied + * remote_device. + * + * The type of protocols supported by this device. The values are returned as + * part of a bit mask in order to allow for multi-protocol support. + */ +void scic_remote_device_get_protocols( + struct scic_sds_remote_device *remote_device, + struct smp_discover_response_protocols *protocols); + + +#if !defined(DISABLE_ATAPI) +/** + * scic_remote_device_is_atapi() - + * @this_device: The device whose type is to be decided. + * + * This method first decide whether a device is a stp target, then decode the + * signature fis of a DA STP device to tell whether it is a standard end disk + * or an ATAPI device. bool Indicate a device is ATAPI device or not. + */ +bool scic_remote_device_is_atapi( + struct scic_sds_remote_device *device_handle); +#else /* !defined(DISABLE_ATAPI) */ +#define scic_remote_device_is_atapi(device_handle) false +#endif /* !defined(DISABLE_ATAPI) */ + + +#endif /* _SCIC_REMOTE_DEVICE_H_ */ + diff --git a/drivers/scsi/isci/core/scic_sds_controller.c b/drivers/scsi/isci/core/scic_sds_controller.c new file mode 100644 index 000000000000..35f7796df661 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_controller.c @@ -0,0 +1,4147 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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/device.h> +#include "scic_controller.h" +#include "scic_phy.h" +#include "scic_port.h" +#include "scic_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_controller_registers.h" +#include "scic_sds_pci.h" +#include "scic_sds_phy.h" +#include "scic_sds_port_configuration_agent.h" +#include "scic_sds_port.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_user_callback.h" +#include "sci_environment.h" +#include "sci_util.h" +#include "scu_completion_codes.h" +#include "scu_constants.h" +#include "scu_event_codes.h" +#include "scu_remote_node_context.h" +#include "scu_task_context.h" +#include "scu_unsolicited_frame.h" + +#define SCU_CONTEXT_RAM_INIT_STALL_TIME 200 + +/** + * smu_dcc_get_max_ports() - + * + * This macro returns the maximum number of logical ports supported by the + * hardware. The caller passes in the value read from the device context + * capacity register and this macro will mash and shift the value appropriately. + */ +#define smu_dcc_get_max_ports(dcc_value) \ + (\ + (((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_SHIFT) + 1 \ + ) + +/** + * smu_dcc_get_max_task_context() - + * + * This macro returns the maximum number of task contexts supported by the + * hardware. The caller passes in the value read from the device context + * capacity register and this macro will mash and shift the value appropriately. + */ +#define smu_dcc_get_max_task_context(dcc_value) \ + (\ + (((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_SHIFT) + 1 \ + ) + +/** + * smu_dcc_get_max_remote_node_context() - + * + * This macro returns the maximum number of remote node contexts supported by + * the hardware. The caller passes in the value read from the device context + * capacity register and this macro will mash and shift the value appropriately. + */ +#define smu_dcc_get_max_remote_node_context(dcc_value) \ + (\ + (((dcc_value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT) + 1 \ + ) + + +static void scic_sds_controller_power_control_timer_handler( + void *controller); +#define SCIC_SDS_CONTROLLER_MIN_TIMER_COUNT 3 +#define SCIC_SDS_CONTROLLER_MAX_TIMER_COUNT 3 + +/** + * + * + * The number of milliseconds to wait for a phy to start. + */ +#define SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT 100 + +/** + * + * + * The number of milliseconds to wait while a given phy is consuming power + * before allowing another set of phys to consume power. Ultimately, this will + * be specified by OEM parameter. + */ +#define SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL 500 + +/** + * COMPLETION_QUEUE_CYCLE_BIT() - + * + * This macro will return the cycle bit of the completion queue entry + */ +#define COMPLETION_QUEUE_CYCLE_BIT(x) ((x) & 0x80000000) + +/** + * NORMALIZE_GET_POINTER() - + * + * This macro will normalize the completion queue get pointer so its value can + * be used as an index into an array + */ +#define NORMALIZE_GET_POINTER(x) \ + ((x) & SMU_COMPLETION_QUEUE_GET_POINTER_MASK) + +/** + * NORMALIZE_PUT_POINTER() - + * + * This macro will normalize the completion queue put pointer so its value can + * be used as an array inde + */ +#define NORMALIZE_PUT_POINTER(x) \ + ((x) & SMU_COMPLETION_QUEUE_PUT_POINTER_MASK) + + +/** + * NORMALIZE_GET_POINTER_CYCLE_BIT() - + * + * This macro will normalize the completion queue cycle pointer so it matches + * the completion queue cycle bit + */ +#define NORMALIZE_GET_POINTER_CYCLE_BIT(x) \ + ((SMU_CQGR_CYCLE_BIT & (x)) << (31 - SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_SHIFT)) + +/** + * NORMALIZE_EVENT_POINTER() - + * + * This macro will normalize the completion queue event entry so its value can + * be used as an index. + */ +#define NORMALIZE_EVENT_POINTER(x) \ + (\ + ((x) & SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_MASK) \ + >> SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_SHIFT \ + ) + +/** + * INCREMENT_COMPLETION_QUEUE_GET() - + * + * This macro will increment the controllers completion queue index value and + * possibly toggle the cycle bit if the completion queue index wraps back to 0. + */ +#define INCREMENT_COMPLETION_QUEUE_GET(controller, index, cycle) \ + INCREMENT_QUEUE_GET(\ + (index), \ + (cycle), \ + (controller)->completion_queue_entries, \ + SMU_CQGR_CYCLE_BIT \ + ) + +/** + * INCREMENT_EVENT_QUEUE_GET() - + * + * This macro will increment the controllers event queue index value and + * possibly toggle the event cycle bit if the event queue index wraps back to 0. + */ +#define INCREMENT_EVENT_QUEUE_GET(controller, index, cycle) \ + INCREMENT_QUEUE_GET(\ + (index), \ + (cycle), \ + (controller)->completion_event_entries, \ + SMU_CQGR_EVENT_CYCLE_BIT \ + ) + +struct sci_base_memory_descriptor_list * +sci_controller_get_memory_descriptor_list_handle(struct scic_sds_controller *scic) +{ + return &scic->parent.mdl; +} + +/* + * ****************************************************************************- + * * SCIC SDS Controller Initialization Methods + * ****************************************************************************- */ + +/** + * This timer is used to start another phy after we have given up on the + * previous phy to transition to the ready state. + * + * + */ +static void scic_sds_controller_phy_startup_timeout_handler( + void *controller) +{ + enum sci_status status; + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + this_controller->phy_startup_timer_pending = false; + + status = SCI_FAILURE; + + while (status != SCI_SUCCESS) { + status = scic_sds_controller_start_next_phy(this_controller); + } +} + +/** + * + * + * This method initializes the phy startup operations for controller start. + */ +void scic_sds_controller_initialize_phy_startup( + struct scic_sds_controller *this_controller) +{ + this_controller->phy_startup_timer = scic_cb_timer_create( + this_controller, + scic_sds_controller_phy_startup_timeout_handler, + this_controller + ); + + this_controller->next_phy_to_start = 0; + this_controller->phy_startup_timer_pending = false; +} + +/** + * + * + * This method initializes the power control operations for the controller + * object. + */ +void scic_sds_controller_initialize_power_control( + struct scic_sds_controller *this_controller) +{ + this_controller->power_control.timer = scic_cb_timer_create( + this_controller, + scic_sds_controller_power_control_timer_handler, + this_controller + ); + + memset( + this_controller->power_control.requesters, + 0, + sizeof(this_controller->power_control.requesters) + ); + + this_controller->power_control.phys_waiting = 0; +} + +/* --------------------------------------------------------------------------- */ + +#define SCU_REMOTE_NODE_CONTEXT_ALIGNMENT (32) +#define SCU_TASK_CONTEXT_ALIGNMENT (256) +#define SCU_UNSOLICITED_FRAME_ADDRESS_ALIGNMENT (64) +#define SCU_UNSOLICITED_FRAME_BUFFER_ALIGNMENT (1024) +#define SCU_UNSOLICITED_FRAME_HEADER_ALIGNMENT (64) + +/* --------------------------------------------------------------------------- */ + +/** + * This method builds the memory descriptor table for this controller. + * @this_controller: This parameter specifies the controller object for which + * to build the memory table. + * + */ +static void scic_sds_controller_build_memory_descriptor_table( + struct scic_sds_controller *this_controller) +{ + sci_base_mde_construct( + &this_controller->memory_descriptors[SCU_MDE_COMPLETION_QUEUE], + SCU_COMPLETION_RAM_ALIGNMENT, + (sizeof(u32) * this_controller->completion_queue_entries), + (SCI_MDE_ATTRIBUTE_CACHEABLE | SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS) + ); + + sci_base_mde_construct( + &this_controller->memory_descriptors[SCU_MDE_REMOTE_NODE_CONTEXT], + SCU_REMOTE_NODE_CONTEXT_ALIGNMENT, + this_controller->remote_node_entries * sizeof(union scu_remote_node_context), + SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS + ); + + sci_base_mde_construct( + &this_controller->memory_descriptors[SCU_MDE_TASK_CONTEXT], + SCU_TASK_CONTEXT_ALIGNMENT, + this_controller->task_context_entries * sizeof(struct scu_task_context), + SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS + ); + + /* + * The UF buffer address table size must be programmed to a power + * of 2. Find the first power of 2 that is equal to or greater then + * the number of unsolicited frame buffers to be utilized. */ + scic_sds_unsolicited_frame_control_set_address_table_count( + &this_controller->uf_control + ); + + sci_base_mde_construct( + &this_controller->memory_descriptors[SCU_MDE_UF_BUFFER], + SCU_UNSOLICITED_FRAME_BUFFER_ALIGNMENT, + scic_sds_unsolicited_frame_control_get_mde_size(this_controller->uf_control), + SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS + ); +} + +/** + * This method validates the driver supplied memory descriptor table. + * @this_controller: + * + * enum sci_status + */ +enum sci_status scic_sds_controller_validate_memory_descriptor_table( + struct scic_sds_controller *this_controller) +{ + bool mde_list_valid; + + mde_list_valid = sci_base_mde_is_valid( + &this_controller->memory_descriptors[SCU_MDE_COMPLETION_QUEUE], + SCU_COMPLETION_RAM_ALIGNMENT, + (sizeof(u32) * this_controller->completion_queue_entries), + (SCI_MDE_ATTRIBUTE_CACHEABLE | SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS) + ); + + if (mde_list_valid == false) + return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; + + mde_list_valid = sci_base_mde_is_valid( + &this_controller->memory_descriptors[SCU_MDE_REMOTE_NODE_CONTEXT], + SCU_REMOTE_NODE_CONTEXT_ALIGNMENT, + this_controller->remote_node_entries * sizeof(union scu_remote_node_context), + SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS + ); + + if (mde_list_valid == false) + return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; + + mde_list_valid = sci_base_mde_is_valid( + &this_controller->memory_descriptors[SCU_MDE_TASK_CONTEXT], + SCU_TASK_CONTEXT_ALIGNMENT, + this_controller->task_context_entries * sizeof(struct scu_task_context), + SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS + ); + + if (mde_list_valid == false) + return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; + + mde_list_valid = sci_base_mde_is_valid( + &this_controller->memory_descriptors[SCU_MDE_UF_BUFFER], + SCU_UNSOLICITED_FRAME_BUFFER_ALIGNMENT, + scic_sds_unsolicited_frame_control_get_mde_size(this_controller->uf_control), + SCI_MDE_ATTRIBUTE_PHYSICALLY_CONTIGUOUS + ); + + if (mde_list_valid == false) + return SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD; + + return SCI_SUCCESS; +} + +/** + * This method initializes the controller with the physical memory addresses + * that are used to communicate with the driver. + * @this_controller: + * + */ +void scic_sds_controller_ram_initialization( + struct scic_sds_controller *this_controller) +{ + struct sci_physical_memory_descriptor *mde; + + /* + * The completion queue is actually placed in cacheable memory + * Therefore it no longer comes out of memory in the MDL. */ + mde = &this_controller->memory_descriptors[SCU_MDE_COMPLETION_QUEUE]; + this_controller->completion_queue = (u32 *)mde->virtual_address; + SMU_CQBAR_WRITE(this_controller, mde->physical_address); + + /* + * Program the location of the Remote Node Context table + * into the SCU. */ + mde = &this_controller->memory_descriptors[SCU_MDE_REMOTE_NODE_CONTEXT]; + this_controller->remote_node_context_table = (union scu_remote_node_context *) + mde->virtual_address; + SMU_RNCBAR_WRITE(this_controller, mde->physical_address); + + /* Program the location of the Task Context table into the SCU. */ + mde = &this_controller->memory_descriptors[SCU_MDE_TASK_CONTEXT]; + this_controller->task_context_table = (struct scu_task_context *) + mde->virtual_address; + SMU_HTTBAR_WRITE(this_controller, mde->physical_address); + + mde = &this_controller->memory_descriptors[SCU_MDE_UF_BUFFER]; + scic_sds_unsolicited_frame_control_construct( + &this_controller->uf_control, mde, this_controller + ); + + /* + * Inform the silicon as to the location of the UF headers and + * address table. */ + SCU_UFHBAR_WRITE( + this_controller, + this_controller->uf_control.headers.physical_address); + SCU_PUFATHAR_WRITE( + this_controller, + this_controller->uf_control.address_table.physical_address); +} + +/** + * This method initializes the task context data for the controller. + * @this_controller: + * + */ +void scic_sds_controller_assign_task_entries( + struct scic_sds_controller *this_controller) +{ + u32 task_assignment; + + /* + * Assign all the TCs to function 0 + * TODO: Do we actually need to read this register to write it back? */ + task_assignment = SMU_TCA_READ(this_controller, 0); + + task_assignment = + ( + task_assignment + | (SMU_TCA_GEN_VAL(STARTING, 0)) + | (SMU_TCA_GEN_VAL(ENDING, this_controller->task_context_entries - 1)) + | (SMU_TCA_GEN_BIT(RANGE_CHECK_ENABLE)) + ); + + SMU_TCA_WRITE(this_controller, 0, task_assignment); +} + +/** + * This method initializes the hardware completion queue. + * + * + */ +void scic_sds_controller_initialize_completion_queue( + struct scic_sds_controller *this_controller) +{ + u32 index; + u32 completion_queue_control_value; + u32 completion_queue_get_value; + u32 completion_queue_put_value; + + this_controller->completion_queue_get = 0; + + completion_queue_control_value = ( + SMU_CQC_QUEUE_LIMIT_SET(this_controller->completion_queue_entries - 1) + | SMU_CQC_EVENT_LIMIT_SET(this_controller->completion_event_entries - 1) + ); + + SMU_CQC_WRITE(this_controller, completion_queue_control_value); + + /* Set the completion queue get pointer and enable the queue */ + completion_queue_get_value = ( + (SMU_CQGR_GEN_VAL(POINTER, 0)) + | (SMU_CQGR_GEN_VAL(EVENT_POINTER, 0)) + | (SMU_CQGR_GEN_BIT(ENABLE)) + | (SMU_CQGR_GEN_BIT(EVENT_ENABLE)) + ); + + SMU_CQGR_WRITE(this_controller, completion_queue_get_value); + + /* Set the completion queue put pointer */ + completion_queue_put_value = ( + (SMU_CQPR_GEN_VAL(POINTER, 0)) + | (SMU_CQPR_GEN_VAL(EVENT_POINTER, 0)) + ); + + SMU_CQPR_WRITE(this_controller, completion_queue_put_value); + + /* Initialize the cycle bit of the completion queue entries */ + for (index = 0; index < this_controller->completion_queue_entries; index++) { + /* + * If get.cycle_bit != completion_queue.cycle_bit + * its not a valid completion queue entry + * so at system start all entries are invalid */ + this_controller->completion_queue[index] = 0x80000000; + } +} + +/** + * This method initializes the hardware unsolicited frame queue. + * + * + */ +void scic_sds_controller_initialize_unsolicited_frame_queue( + struct scic_sds_controller *this_controller) +{ + u32 frame_queue_control_value; + u32 frame_queue_get_value; + u32 frame_queue_put_value; + + /* Write the queue size */ + frame_queue_control_value = + SCU_UFQC_GEN_VAL(QUEUE_SIZE, this_controller->uf_control.address_table.count); + + SCU_UFQC_WRITE(this_controller, frame_queue_control_value); + + /* Setup the get pointer for the unsolicited frame queue */ + frame_queue_get_value = ( + SCU_UFQGP_GEN_VAL(POINTER, 0) + | SCU_UFQGP_GEN_BIT(ENABLE_BIT) + ); + + SCU_UFQGP_WRITE(this_controller, frame_queue_get_value); + + /* Setup the put pointer for the unsolicited frame queue */ + frame_queue_put_value = SCU_UFQPP_GEN_VAL(POINTER, 0); + + SCU_UFQPP_WRITE(this_controller, frame_queue_put_value); +} + +/** + * This method enables the hardware port task scheduler. + * + * + */ +void scic_sds_controller_enable_port_task_scheduler( + struct scic_sds_controller *this_controller) +{ + u32 port_task_scheduler_value; + + port_task_scheduler_value = SCU_PTSGCR_READ(this_controller); + + port_task_scheduler_value |= + (SCU_PTSGCR_GEN_BIT(ETM_ENABLE) | SCU_PTSGCR_GEN_BIT(PTSG_ENABLE)); + + SCU_PTSGCR_WRITE(this_controller, port_task_scheduler_value); +} + +/* --------------------------------------------------------------------------- */ + +/** + * + * + * This macro is used to delay between writes to the AFE registers during AFE + * initialization. + */ +#define AFE_REGISTER_WRITE_DELAY 10 + +static bool is_a0(void) +{ + return isci_si_rev == ISCI_SI_REVA0; +} + +static bool is_a2(void) +{ + return isci_si_rev == ISCI_SI_REVA2; +} + +static bool is_b0(void) +{ + return isci_si_rev > ISCI_SI_REVA2; +} + +/* Initialize the AFE for this phy index. We need to read the AFE setup from + * the OEM parameters none + */ +void scic_sds_controller_afe_initialization(struct scic_sds_controller *scic) +{ + u32 afe_status; + u32 phy_id; + + /* Clear DFX Status registers */ + scu_afe_register_write(scic, afe_dfx_master_control0, 0x0081000f); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + /* Configure bias currents to normal */ + if (is_a0()) + scu_afe_register_write(scic, afe_bias_control, 0x00005500); + else + scu_afe_register_write(scic, afe_bias_control, 0x00005A00); + + + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + /* Enable PLL */ + if (is_b0()) + scu_afe_register_write(scic, afe_pll_control0, 0x80040A08); + else + scu_afe_register_write(scic, afe_pll_control0, 0x80040908); + + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + /* Wait for the PLL to lock */ + do { + afe_status = scu_afe_register_read( + scic, afe_common_block_status); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + } while ((afe_status & 0x00001000) == 0); + + if (is_b0()) { + /* Shorten SAS SNW lock time (RxLock timer value from 76 us to 50 us) */ + scu_afe_register_write(scic, afe_pmsn_master_control0, 0x7bcc96ad); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + } + + for (phy_id = 0; phy_id < SCI_MAX_PHYS; phy_id++) { + if (is_b0()) { + /* Configure transmitter SSC parameters */ + scu_afe_txreg_write(scic, phy_id, afe_tx_ssc_control, 0x00030000); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + } else { + /* + * All defaults, except the Receive Word Alignament/Comma Detect + * Enable....(0xe800) */ + scu_afe_txreg_write(scic, phy_id, afe_xcvr_control0, 0x00004512); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + scu_afe_txreg_write(scic, phy_id, afe_xcvr_control1, 0x0050100F); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + } + + /* + * Power up TX and RX out from power down (PWRDNTX and PWRDNRX) + * & increase TX int & ext bias 20%....(0xe85c) */ + if (is_a0()) + scu_afe_txreg_write(scic, phy_id, afe_channel_control, 0x000003D4); + else if (is_a2()) + scu_afe_txreg_write(scic, phy_id, afe_channel_control, 0x000003F0); + else { + /* Power down TX and RX (PWRDNTX and PWRDNRX) */ + scu_afe_txreg_write(scic, phy_id, afe_channel_control, 0x000003d7); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + /* + * Power up TX and RX out from power down (PWRDNTX and PWRDNRX) + * & increase TX int & ext bias 20%....(0xe85c) */ + scu_afe_txreg_write(scic, phy_id, afe_channel_control, 0x000003d4); + } + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + if (is_a0() || is_a2()) { + /* Enable TX equalization (0xe824) */ + scu_afe_txreg_write(scic, phy_id, afe_tx_control, 0x00040000); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + } + + /* + * RDPI=0x0(RX Power On), RXOOBDETPDNC=0x0, TPD=0x0(TX Power On), + * RDD=0x0(RX Detect Enabled) ....(0xe800) */ + scu_afe_txreg_write(scic, phy_id, afe_xcvr_control0, 0x00004100); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + /* Leave DFE/FFE on */ + if (is_a0()) + scu_afe_txreg_write(scic, phy_id, afe_rx_ssc_control0, 0x3F09983F); + else if (is_a2()) + scu_afe_txreg_write(scic, phy_id, afe_rx_ssc_control0, 0x3F11103F); + else { + scu_afe_txreg_write(scic, phy_id, afe_rx_ssc_control0, 0x3F11103F); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + /* Enable TX equalization (0xe824) */ + scu_afe_txreg_write(scic, phy_id, afe_tx_control, 0x00040000); + } + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + scu_afe_txreg_write(scic, phy_id, afe_tx_amp_control0, 0x000E7C03); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + scu_afe_txreg_write(scic, phy_id, afe_tx_amp_control1, 0x000E7C03); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + scu_afe_txreg_write(scic, phy_id, afe_tx_amp_control2, 0x000E7C03); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + + scu_afe_txreg_write(scic, phy_id, afe_tx_amp_control3, 0x000E7C03); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); + } + + /* Transfer control to the PEs */ + scu_afe_register_write(scic, afe_dfx_master_control0, 0x00010f00); + scic_cb_stall_execution(AFE_REGISTER_WRITE_DELAY); +} + +/* + * ****************************************************************************- + * * SCIC SDS Controller Internal Start/Stop Routines + * ****************************************************************************- */ + + +/** + * This method will attempt to transition into the ready state for the + * controller and indicate that the controller start operation has completed + * if all criteria are met. + * @this_controller: This parameter indicates the controller object for which + * to transition to ready. + * @status: This parameter indicates the status value to be pass into the call + * to scic_cb_controller_start_complete(). + * + * none. + */ +static void scic_sds_controller_transition_to_ready( + struct scic_sds_controller *this_controller, + enum sci_status status) +{ + if (this_controller->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_STARTING) { + /* + * We move into the ready state, because some of the phys/ports + * may be up and operational. */ + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_READY + ); + + scic_cb_controller_start_complete(this_controller, status); + } +} + +/** + * This method is the general timeout handler for the controller. It will take + * the correct timetout action based on the current controller state + */ +void scic_sds_controller_timeout_handler( + struct scic_sds_controller *scic) +{ + enum sci_base_controller_states current_state; + + current_state = sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine(scic)); + + if (current_state == SCI_BASE_CONTROLLER_STATE_STARTING) { + scic_sds_controller_transition_to_ready( + scic, SCI_FAILURE_TIMEOUT); + } else if (current_state == SCI_BASE_CONTROLLER_STATE_STOPPING) { + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(scic), + SCI_BASE_CONTROLLER_STATE_FAILED); + scic_cb_controller_stop_complete(scic, SCI_FAILURE_TIMEOUT); + } else /* / @todo Now what do we want to do in this case? */ + dev_err(scic_to_dev(scic), + "%s: Controller timer fired when controller was not " + "in a state being timed.\n", + __func__); +} + +/** + * scic_sds_controller_get_port_configuration_mode + * @this_controller: This is the controller to use to determine if we are using + * manual or automatic port configuration. + * + * SCIC_PORT_CONFIGURATION_MODE + */ +enum SCIC_PORT_CONFIGURATION_MODE scic_sds_controller_get_port_configuration_mode( + struct scic_sds_controller *this_controller) +{ + u32 index; + enum SCIC_PORT_CONFIGURATION_MODE mode; + + mode = SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE; + + for (index = 0; index < SCI_MAX_PORTS; index++) { + if (this_controller->oem_parameters.sds1.ports[index].phy_mask != 0) { + mode = SCIC_PORT_MANUAL_CONFIGURATION_MODE; + break; + } + } + + return mode; +} + +enum sci_status scic_sds_controller_stop_ports(struct scic_sds_controller *scic) +{ + u32 index; + enum sci_status port_status; + enum sci_status status = SCI_SUCCESS; + + for (index = 0; index < scic->logical_port_entries; index++) { + port_status = scic_port_stop(&scic->port_table[index]); + + if ((port_status != SCI_SUCCESS) && + (port_status != SCI_FAILURE_INVALID_STATE)) { + status = SCI_FAILURE; + + dev_warn(scic_to_dev(scic), + "%s: Controller stop operation failed to " + "stop port %d because of status %d.\n", + __func__, + scic->port_table[index].logical_port_index, + port_status); + } + } + + return status; +} + +/** + * + * + * + */ +static void scic_sds_controller_phy_timer_start( + struct scic_sds_controller *this_controller) +{ + scic_cb_timer_start( + this_controller, + this_controller->phy_startup_timer, + SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT + ); + + this_controller->phy_startup_timer_pending = true; +} + +/** + * + * + * + */ +void scic_sds_controller_phy_timer_stop( + struct scic_sds_controller *this_controller) +{ + scic_cb_timer_stop( + this_controller, + this_controller->phy_startup_timer + ); + + this_controller->phy_startup_timer_pending = false; +} + +/** + * This method is called internally by the controller object to start the next + * phy on the controller. If all the phys have been starte, then this + * method will attempt to transition the controller to the READY state and + * inform the user (scic_cb_controller_start_complete()). + * @this_controller: This parameter specifies the controller object for which + * to start the next phy. + * + * enum sci_status + */ +enum sci_status scic_sds_controller_start_next_phy( + struct scic_sds_controller *this_controller) +{ + enum sci_status status; + + status = SCI_SUCCESS; + + if (this_controller->phy_startup_timer_pending == false) { + if (this_controller->next_phy_to_start == SCI_MAX_PHYS) { + bool is_controller_start_complete = true; + struct scic_sds_phy *the_phy; + u8 index; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + the_phy = &this_controller->phy_table[index]; + + if (scic_sds_phy_get_port(the_phy) != SCI_INVALID_HANDLE) { + /** + * The controller start operation is complete if and only + * if: + * - all links have been given an opportunity to start + * - have no indication of a connected device + * - have an indication of a connected device and it has + * finished the link training process. + */ + if ( + ( + (the_phy->is_in_link_training == false) + && (the_phy->parent.state_machine.current_state_id + == SCI_BASE_PHY_STATE_INITIAL) + ) + || ( + (the_phy->is_in_link_training == false) + && (the_phy->parent.state_machine.current_state_id + == SCI_BASE_PHY_STATE_STOPPED) + ) + || ( + (the_phy->is_in_link_training == true) + && (the_phy->parent.state_machine.current_state_id + == SCI_BASE_PHY_STATE_STARTING) + ) + ) { + is_controller_start_complete = false; + break; + } + } + } + + /* + * The controller has successfully finished the start process. + * Inform the SCI Core user and transition to the READY state. */ + if (is_controller_start_complete == true) { + scic_sds_controller_transition_to_ready( + this_controller, SCI_SUCCESS + ); + scic_sds_controller_phy_timer_stop(this_controller); + } + } else { + struct scic_sds_phy *the_phy; + + the_phy = &this_controller->phy_table[this_controller->next_phy_to_start]; + + if ( + scic_sds_controller_get_port_configuration_mode(this_controller) + == SCIC_PORT_MANUAL_CONFIGURATION_MODE + ) { + if (scic_sds_phy_get_port(the_phy) == SCI_INVALID_HANDLE) { + this_controller->next_phy_to_start++; + + /* + * Caution recursion ahead be forwarned + * + * The PHY was never added to a PORT in MPC mode so start the next phy in sequence + * This phy will never go link up and will not draw power the OEM parameters either + * configured the phy incorrectly for the PORT or it was never assigned to a PORT */ + return scic_sds_controller_start_next_phy(this_controller); + } + } + + status = scic_sds_phy_start(the_phy); + + if (status == SCI_SUCCESS) { + scic_sds_controller_phy_timer_start(this_controller); + } else { + dev_warn(scic_to_dev(this_controller), + "%s: Controller stop operation failed " + "to stop phy %d because of status " + "%d.\n", + __func__, + this_controller->phy_table[this_controller->next_phy_to_start].phy_index, + status); + } + + this_controller->next_phy_to_start++; + } + } + + return status; +} + +/** + * + * @this_controller: + * + * enum sci_status + */ +enum sci_status scic_sds_controller_stop_phys( + struct scic_sds_controller *this_controller) +{ + u32 index; + enum sci_status status; + enum sci_status phy_status; + + status = SCI_SUCCESS; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + phy_status = scic_sds_phy_stop(&this_controller->phy_table[index]); + + if ( + (phy_status != SCI_SUCCESS) + && (phy_status != SCI_FAILURE_INVALID_STATE) + ) { + status = SCI_FAILURE; + + dev_warn(scic_to_dev(this_controller), + "%s: Controller stop operation failed to stop " + "phy %d because of status %d.\n", + __func__, + this_controller->phy_table[index].phy_index, phy_status); + } + } + + return status; +} + +/** + * + * @this_controller: + * + * enum sci_status + */ +enum sci_status scic_sds_controller_stop_devices( + struct scic_sds_controller *this_controller) +{ + u32 index; + enum sci_status status; + enum sci_status device_status; + + status = SCI_SUCCESS; + + for (index = 0; index < this_controller->remote_node_entries; index++) { + if (this_controller->device_table[index] != SCI_INVALID_HANDLE) { + /* / @todo What timeout value do we want to provide to this request? */ + device_status = scic_remote_device_stop(this_controller->device_table[index], 0); + + if ((device_status != SCI_SUCCESS) && + (device_status != SCI_FAILURE_INVALID_STATE)) { + dev_warn(scic_to_dev(this_controller), + "%s: Controller stop operation failed " + "to stop device 0x%p because of " + "status %d.\n", + __func__, + this_controller->device_table[index], device_status); + } + } + } + + return status; +} + +/* + * ****************************************************************************- + * * SCIC SDS Controller Power Control (Staggered Spinup) + * ****************************************************************************- */ + +/** + * + * + * This method starts the power control timer for this controller object. + */ +static void scic_sds_controller_power_control_timer_start( + struct scic_sds_controller *this_controller) +{ + scic_cb_timer_start( + this_controller, this_controller->power_control.timer, + SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL + ); + + this_controller->power_control.timer_started = true; +} + +/** + * + * + * + */ +static void scic_sds_controller_power_control_timer_handler( + void *controller) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + if (this_controller->power_control.phys_waiting == 0) { + this_controller->power_control.timer_started = false; + } else { + struct scic_sds_phy *the_phy = NULL; + u8 i; + + for (i = 0; + (i < SCI_MAX_PHYS) + && (this_controller->power_control.phys_waiting != 0); + i++) { + if (this_controller->power_control.requesters[i] != NULL) { + the_phy = this_controller->power_control.requesters[i]; + this_controller->power_control.requesters[i] = NULL; + this_controller->power_control.phys_waiting--; + break; + } + } + + /* + * It doesn't matter if the power list is empty, we need to start the + * timer in case another phy becomes ready. */ + scic_sds_controller_power_control_timer_start(this_controller); + + scic_sds_phy_consume_power_handler(the_phy); + } +} + +/** + * This method inserts the phy in the stagger spinup control queue. + * @this_controller: + * + * + */ +void scic_sds_controller_power_control_queue_insert( + struct scic_sds_controller *this_controller, + struct scic_sds_phy *the_phy) +{ + BUG_ON(the_phy == NULL); + + if ( + (this_controller->power_control.timer_started) + && (this_controller->power_control.requesters[the_phy->phy_index] == NULL) + ) { + this_controller->power_control.requesters[the_phy->phy_index] = the_phy; + this_controller->power_control.phys_waiting++; + } else { + scic_sds_controller_power_control_timer_start(this_controller); + scic_sds_phy_consume_power_handler(the_phy); + } +} + +/** + * This method removes the phy from the stagger spinup control queue. + * @this_controller: + * + * + */ +void scic_sds_controller_power_control_queue_remove( + struct scic_sds_controller *this_controller, + struct scic_sds_phy *the_phy) +{ + BUG_ON(the_phy == NULL); + + if (this_controller->power_control.requesters[the_phy->phy_index] != NULL) { + this_controller->power_control.phys_waiting--; + } + + this_controller->power_control.requesters[the_phy->phy_index] = NULL; +} + +/* + * ****************************************************************************- + * * SCIC SDS Controller Completion Routines + * ****************************************************************************- */ + +/** + * This method returns a true value if the completion queue has entries that + * can be processed + * @this_controller: + * + * bool true if the completion queue has entries to process false if the + * completion queue has no entries to process + */ +static bool scic_sds_controller_completion_queue_has_entries( + struct scic_sds_controller *this_controller) +{ + u32 get_value = this_controller->completion_queue_get; + u32 get_index = get_value & SMU_COMPLETION_QUEUE_GET_POINTER_MASK; + + if ( + NORMALIZE_GET_POINTER_CYCLE_BIT(get_value) + == COMPLETION_QUEUE_CYCLE_BIT(this_controller->completion_queue[get_index]) + ) { + return true; + } + + return false; +} + +/* --------------------------------------------------------------------------- */ + +/** + * This method processes a task completion notification. This is called from + * within the controller completion handler. + * @this_controller: + * @completion_entry: + * + */ +static void scic_sds_controller_task_completion( + struct scic_sds_controller *this_controller, + u32 completion_entry) +{ + u32 index; + struct scic_sds_request *io_request; + + index = SCU_GET_COMPLETION_INDEX(completion_entry); + io_request = this_controller->io_request_table[index]; + + /* Make sure that we really want to process this IO request */ + if ( + (io_request != SCI_INVALID_HANDLE) + && (io_request->io_tag != SCI_CONTROLLER_INVALID_IO_TAG) + && ( + scic_sds_io_tag_get_sequence(io_request->io_tag) + == this_controller->io_request_sequence[index] + ) + ) { + /* Yep this is a valid io request pass it along to the io request handler */ + scic_sds_io_request_tc_completion(io_request, completion_entry); + } +} + +/** + * This method processes an SDMA completion event. This is called from within + * the controller completion handler. + * @this_controller: + * @completion_entry: + * + */ +static void scic_sds_controller_sdma_completion( + struct scic_sds_controller *this_controller, + u32 completion_entry) +{ + u32 index; + struct scic_sds_request *io_request; + struct scic_sds_remote_device *device; + + index = SCU_GET_COMPLETION_INDEX(completion_entry); + + switch (scu_get_command_request_type(completion_entry)) { + case SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC: + case SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC: + io_request = this_controller->io_request_table[index]; + dev_warn(scic_to_dev(this_controller), + "%s: SCIC SDS Completion type SDMA %x for io request " + "%p\n", + __func__, + completion_entry, + io_request); + /* @todo For a post TC operation we need to fail the IO + * request + */ + break; + + case SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC: + case SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC: + case SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC: + device = this_controller->device_table[index]; + dev_warn(scic_to_dev(this_controller), + "%s: SCIC SDS Completion type SDMA %x for remote " + "device %p\n", + __func__, + completion_entry, + device); + /* @todo For a port RNC operation we need to fail the + * device + */ + break; + + default: + dev_warn(scic_to_dev(this_controller), + "%s: SCIC SDS Completion unknown SDMA completion " + "type %x\n", + __func__, + completion_entry); + break; + + } +} + +/** + * + * @this_controller: + * @completion_entry: + * + * This method processes an unsolicited frame message. This is called from + * within the controller completion handler. none + */ +static void scic_sds_controller_unsolicited_frame( + struct scic_sds_controller *this_controller, + u32 completion_entry) +{ + u32 index; + u32 frame_index; + + struct scu_unsolicited_frame_header *frame_header; + struct scic_sds_phy *phy; + struct scic_sds_remote_device *device; + + enum sci_status result = SCI_FAILURE; + + frame_index = SCU_GET_FRAME_INDEX(completion_entry); + + frame_header + = this_controller->uf_control.buffers.array[frame_index].header; + this_controller->uf_control.buffers.array[frame_index].state + = UNSOLICITED_FRAME_IN_USE; + + if (SCU_GET_FRAME_ERROR(completion_entry)) { + /* + * / @todo If the IAF frame or SIGNATURE FIS frame has an error will + * / this cause a problem? We expect the phy initialization will + * / fail if there is an error in the frame. */ + scic_sds_controller_release_frame(this_controller, frame_index); + return; + } + + if (frame_header->is_address_frame) { + index = SCU_GET_PROTOCOL_ENGINE_INDEX(completion_entry); + phy = &this_controller->phy_table[index]; + if (phy != NULL) { + result = scic_sds_phy_frame_handler(phy, frame_index); + } + } else { + + index = SCU_GET_COMPLETION_INDEX(completion_entry); + + if (index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { + /* + * This is a signature fis or a frame from a direct attached SATA + * device that has not yet been created. In either case forwared + * the frame to the PE and let it take care of the frame data. */ + index = SCU_GET_PROTOCOL_ENGINE_INDEX(completion_entry); + phy = &this_controller->phy_table[index]; + result = scic_sds_phy_frame_handler(phy, frame_index); + } else { + if (index < this_controller->remote_node_entries) + device = this_controller->device_table[index]; + else + device = NULL; + + if (device != NULL) + result = scic_sds_remote_device_frame_handler(device, frame_index); + else + scic_sds_controller_release_frame(this_controller, frame_index); + } + } + + if (result != SCI_SUCCESS) { + /* + * / @todo Is there any reason to report some additional error message + * / when we get this failure notifiction? */ + } +} + +/** + * This method processes an event completion entry. This is called from within + * the controller completion handler. + * @this_controller: + * @completion_entry: + * + */ +static void scic_sds_controller_event_completion( + struct scic_sds_controller *this_controller, + u32 completion_entry) +{ + u32 index; + struct scic_sds_request *io_request; + struct scic_sds_remote_device *device; + struct scic_sds_phy *phy; + + index = SCU_GET_COMPLETION_INDEX(completion_entry); + + switch (scu_get_event_type(completion_entry)) { + case SCU_EVENT_TYPE_SMU_COMMAND_ERROR: + /* / @todo The driver did something wrong and we need to fix the condtion. */ + dev_err(scic_to_dev(this_controller), + "%s: SCIC Controller 0x%p received SMU command error " + "0x%x\n", + __func__, + this_controller, + completion_entry); + break; + + case SCU_EVENT_TYPE_SMU_PCQ_ERROR: + case SCU_EVENT_TYPE_SMU_ERROR: + case SCU_EVENT_TYPE_FATAL_MEMORY_ERROR: + /* + * / @todo This is a hardware failure and its likely that we want to + * / reset the controller. */ + dev_err(scic_to_dev(this_controller), + "%s: SCIC Controller 0x%p received fatal controller " + "event 0x%x\n", + __func__, + this_controller, + completion_entry); + break; + + case SCU_EVENT_TYPE_TRANSPORT_ERROR: + io_request = this_controller->io_request_table[index]; + scic_sds_io_request_event_handler(io_request, completion_entry); + break; + + case SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT: + switch (scu_get_event_specifier(completion_entry)) { + case SCU_EVENT_SPECIFIC_SMP_RESPONSE_NO_PE: + case SCU_EVENT_SPECIFIC_TASK_TIMEOUT: + io_request = this_controller->io_request_table[index]; + if (io_request != SCI_INVALID_HANDLE) + scic_sds_io_request_event_handler(io_request, completion_entry); + else + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller 0x%p received " + "event 0x%x for io request object " + "that doesnt exist.\n", + __func__, + this_controller, + completion_entry); + + break; + + case SCU_EVENT_SPECIFIC_IT_NEXUS_TIMEOUT: + device = this_controller->device_table[index]; + if (device != SCI_INVALID_HANDLE) + scic_sds_remote_device_event_handler(device, completion_entry); + else + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller 0x%p received " + "event 0x%x for remote device object " + "that doesnt exist.\n", + __func__, + this_controller, + completion_entry); + + break; + } + break; + + case SCU_EVENT_TYPE_BROADCAST_CHANGE: + /* + * direct the broadcast change event to the phy first and then let + * the phy redirect the broadcast change to the port object */ + case SCU_EVENT_TYPE_ERR_CNT_EVENT: + /* + * direct error counter event to the phy object since that is where + * we get the event notification. This is a type 4 event. */ + case SCU_EVENT_TYPE_OSSP_EVENT: + index = SCU_GET_PROTOCOL_ENGINE_INDEX(completion_entry); + phy = &this_controller->phy_table[index]; + scic_sds_phy_event_handler(phy, completion_entry); + break; + + case SCU_EVENT_TYPE_RNC_SUSPEND_TX: + case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: + case SCU_EVENT_TYPE_RNC_OPS_MISC: + if (index < this_controller->remote_node_entries) { + device = this_controller->device_table[index]; + + if (device != NULL) + scic_sds_remote_device_event_handler(device, completion_entry); + } else + dev_err(scic_to_dev(this_controller), + "%s: SCIC Controller 0x%p received event 0x%x " + "for remote device object 0x%0x that doesnt " + "exist.\n", + __func__, + this_controller, + completion_entry, + index); + + break; + + default: + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller received unknown event code %x\n", + __func__, + completion_entry); + break; + } +} + +/** + * This method is a private routine for processing the completion queue entries. + * @this_controller: + * + */ +static void scic_sds_controller_process_completions( + struct scic_sds_controller *this_controller) +{ + u32 completion_count = 0; + u32 completion_entry; + u32 get_index; + u32 get_cycle; + u32 event_index; + u32 event_cycle; + + dev_dbg(scic_to_dev(this_controller), + "%s: completion queue begining get:0x%08x\n", + __func__, + this_controller->completion_queue_get); + + /* Get the component parts of the completion queue */ + get_index = NORMALIZE_GET_POINTER(this_controller->completion_queue_get); + get_cycle = SMU_CQGR_CYCLE_BIT & this_controller->completion_queue_get; + + event_index = NORMALIZE_EVENT_POINTER(this_controller->completion_queue_get); + event_cycle = SMU_CQGR_EVENT_CYCLE_BIT & this_controller->completion_queue_get; + + while ( + NORMALIZE_GET_POINTER_CYCLE_BIT(get_cycle) + == COMPLETION_QUEUE_CYCLE_BIT(this_controller->completion_queue[get_index]) + ) { + completion_count++; + + completion_entry = this_controller->completion_queue[get_index]; + INCREMENT_COMPLETION_QUEUE_GET(this_controller, get_index, get_cycle); + + dev_dbg(scic_to_dev(this_controller), + "%s: completion queue entry:0x%08x\n", + __func__, + completion_entry); + + switch (SCU_GET_COMPLETION_TYPE(completion_entry)) { + case SCU_COMPLETION_TYPE_TASK: + scic_sds_controller_task_completion(this_controller, completion_entry); + break; + + case SCU_COMPLETION_TYPE_SDMA: + scic_sds_controller_sdma_completion(this_controller, completion_entry); + break; + + case SCU_COMPLETION_TYPE_UFI: + scic_sds_controller_unsolicited_frame(this_controller, completion_entry); + break; + + case SCU_COMPLETION_TYPE_EVENT: + INCREMENT_EVENT_QUEUE_GET(this_controller, event_index, event_cycle); + scic_sds_controller_event_completion(this_controller, completion_entry); + break; + + case SCU_COMPLETION_TYPE_NOTIFY: + /* + * Presently we do the same thing with a notify event that we do with the + * other event codes. */ + INCREMENT_EVENT_QUEUE_GET(this_controller, event_index, event_cycle); + scic_sds_controller_event_completion(this_controller, completion_entry); + break; + + default: + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller received unknown " + "completion type %x\n", + __func__, + completion_entry); + break; + } + } + + /* Update the get register if we completed one or more entries */ + if (completion_count > 0) { + this_controller->completion_queue_get = + SMU_CQGR_GEN_BIT(ENABLE) + | SMU_CQGR_GEN_BIT(EVENT_ENABLE) + | event_cycle | SMU_CQGR_GEN_VAL(EVENT_POINTER, event_index) + | get_cycle | SMU_CQGR_GEN_VAL(POINTER, get_index); + + SMU_CQGR_WRITE(this_controller, + this_controller->completion_queue_get); + } + + dev_dbg(scic_to_dev(this_controller), + "%s: completion queue ending get:0x%08x\n", + __func__, + this_controller->completion_queue_get); + +} + +/** + * This method is a private routine for processing the completion queue entries. + * @this_controller: + * + */ +static void scic_sds_controller_transitioned_process_completions( + struct scic_sds_controller *this_controller) +{ + u32 completion_count = 0; + u32 completion_entry; + u32 get_index; + u32 get_cycle; + u32 event_index; + u32 event_cycle; + + dev_dbg(scic_to_dev(this_controller), + "%s: completion queue begining get:0x%08x\n", + __func__, + this_controller->completion_queue_get); + + /* Get the component parts of the completion queue */ + get_index = NORMALIZE_GET_POINTER(this_controller->completion_queue_get); + get_cycle = SMU_CQGR_CYCLE_BIT & this_controller->completion_queue_get; + + event_index = NORMALIZE_EVENT_POINTER(this_controller->completion_queue_get); + event_cycle = SMU_CQGR_EVENT_CYCLE_BIT & this_controller->completion_queue_get; + + while ( + NORMALIZE_GET_POINTER_CYCLE_BIT(get_cycle) + == COMPLETION_QUEUE_CYCLE_BIT( + this_controller->completion_queue[get_index]) + ) { + completion_count++; + + completion_entry = this_controller->completion_queue[get_index]; + INCREMENT_COMPLETION_QUEUE_GET(this_controller, get_index, get_cycle); + + dev_dbg(scic_to_dev(this_controller), + "%s: completion queue entry:0x%08x\n", + __func__, + completion_entry); + + switch (SCU_GET_COMPLETION_TYPE(completion_entry)) { + case SCU_COMPLETION_TYPE_TASK: + scic_sds_controller_task_completion(this_controller, completion_entry); + break; + + case SCU_COMPLETION_TYPE_NOTIFY: + case SCU_COMPLETION_TYPE_EVENT: + /* + * Presently we do the same thing with a notify event that we + * do with the other event codes. */ + INCREMENT_EVENT_QUEUE_GET(this_controller, event_index, event_cycle); + /* Fall-through */ + + case SCU_COMPLETION_TYPE_SDMA: + case SCU_COMPLETION_TYPE_UFI: + default: + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller ignoring completion type " + "%x\n", + __func__, + completion_entry); + break; + } + } + + /* Update the get register if we completed one or more entries */ + if (completion_count > 0) { + this_controller->completion_queue_get = + SMU_CQGR_GEN_BIT(ENABLE) + | SMU_CQGR_GEN_BIT(EVENT_ENABLE) + | event_cycle | SMU_CQGR_GEN_VAL(EVENT_POINTER, event_index) + | get_cycle | SMU_CQGR_GEN_VAL(POINTER, get_index); + + SMU_CQGR_WRITE(this_controller, this_controller->completion_queue_get); + } + + dev_dbg(scic_to_dev(this_controller), + "%s: completion queue ending get:0x%08x\n", + __func__, + this_controller->completion_queue_get); +} + +/* + * ****************************************************************************- + * * SCIC SDS Controller Interrupt and Completion functions + * ****************************************************************************- */ + +/** + * This method provides standard (common) processing of interrupts for polling + * and legacy based interrupts. + * @controller: + * @interrupt_status: + * + * This method returns a boolean (bool) indication as to whether an completions + * are pending to be processed. true if an interrupt is to be processed false + * if no interrupt was pending + */ +static bool scic_sds_controller_standard_interrupt_handler( + struct scic_sds_controller *this_controller, + u32 interrupt_status) +{ + bool is_completion_needed = false; + + if ((interrupt_status & SMU_ISR_QUEUE_ERROR) || + ((interrupt_status & SMU_ISR_QUEUE_SUSPEND) && + (!scic_sds_controller_completion_queue_has_entries( + this_controller)))) { + /* + * We have a fatal error on the read of the completion queue bar + * OR + * We have a fatal error there is nothing in the completion queue + * but we have a report from the hardware that the queue is full + * / @todo how do we request the a controller reset */ + is_completion_needed = true; + this_controller->encountered_fatal_error = true; + } + + if (scic_sds_controller_completion_queue_has_entries(this_controller)) { + is_completion_needed = true; + } + + return is_completion_needed; +} + +/** + * This is the method provided to handle polling for interrupts for the + * controller object. + * + * bool true if an interrupt is to be processed false if no interrupt was + * pending + */ +static bool scic_sds_controller_polling_interrupt_handler( + struct scic_sds_controller *scic) +{ + u32 interrupt_status; + + /* + * In INTERRUPT_POLLING_MODE we exit the interrupt handler if the + * hardware indicates nothing is pending. Since we are not being + * called from a real interrupt, we don't want to confuse the hardware + * by servicing the completion queue before the hardware indicates it + * is ready. We'll simply wait for another polling interval and check + * again. + */ + interrupt_status = SMU_ISR_READ(scic); + if ((interrupt_status & + (SMU_ISR_COMPLETION | + SMU_ISR_QUEUE_ERROR | + SMU_ISR_QUEUE_SUSPEND)) == 0) { + return false; + } + + return scic_sds_controller_standard_interrupt_handler( + scic, interrupt_status); +} + +/** + * This is the method provided to handle completions when interrupt polling is + * in use. + */ +static void scic_sds_controller_polling_completion_handler( + struct scic_sds_controller *scic) +{ + if (scic->encountered_fatal_error == true) { + dev_err(scic_to_dev(scic), + "%s: SCIC Controller has encountered a fatal error.\n", + __func__); + + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(scic), + SCI_BASE_CONTROLLER_STATE_FAILED); + } else if (scic_sds_controller_completion_queue_has_entries(scic)) { + if (scic->restrict_completions == false) + scic_sds_controller_process_completions(scic); + else + scic_sds_controller_transitioned_process_completions( + scic); + } + + /* + * The interrupt handler does not adjust the CQ's + * get pointer. So, SCU's INTx pin stays asserted during the + * interrupt handler even though it tries to clear the interrupt + * source. Therefore, the completion handler must ensure that the + * interrupt source is cleared. Otherwise, we get a spurious + * interrupt for which the interrupt handler will not issue a + * corresponding completion event. Also, we unmask interrupts. + */ + SMU_ISR_WRITE( + scic, + (u32)(SMU_ISR_COMPLETION | SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND) + ); +} + +/** + * This is the method provided to handle legacy interrupts for the controller + * object. + * + * bool true if an interrupt is processed false if no interrupt was processed + */ +static bool scic_sds_controller_legacy_interrupt_handler( + struct scic_sds_controller *scic) +{ + u32 interrupt_status; + bool is_completion_needed; + + interrupt_status = SMU_ISR_READ(scic); + is_completion_needed = scic_sds_controller_standard_interrupt_handler( + scic, interrupt_status); + + return is_completion_needed; +} + + +/** + * This is the method provided to handle legacy completions it is expected that + * the SCI User will call this completion handler anytime the interrupt + * handler reports that it has handled an interrupt. + */ +static void scic_sds_controller_legacy_completion_handler( + struct scic_sds_controller *scic) +{ + scic_sds_controller_polling_completion_handler(scic); + SMU_IMR_WRITE(scic, 0x00000000); +} + +/** + * This is the method provided to handle an MSIX interrupt message when there + * is just a single MSIX message being provided by the hardware. This mode + * of operation is single vector mode. + * + * bool true if an interrupt is processed false if no interrupt was processed + */ +static bool scic_sds_controller_single_vector_interrupt_handler( + struct scic_sds_controller *scic) +{ + u32 interrupt_status; + + /* + * Mask the interrupts + * There is a race in the hardware that could cause us not to be notified + * of an interrupt completion if we do not take this step. We will unmask + * the interrupts in the completion routine. */ + SMU_IMR_WRITE(scic, 0xFFFFFFFF); + + interrupt_status = SMU_ISR_READ(scic); + interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND); + + if ((interrupt_status == 0) && + scic_sds_controller_completion_queue_has_entries(scic)) { + /* + * There is at least one completion queue entry to process so we can + * return a success and ignore for now the case of an error interrupt */ + SMU_ISR_WRITE(scic, SMU_ISR_COMPLETION); + return true; + } + + if (interrupt_status != 0) { + /* + * There is an error interrupt pending so let it through and handle + * in the callback */ + return true; + } + + /* + * Clear any offending interrupts since we could not find any to handle + * and unmask them all */ + SMU_ISR_WRITE(scic, 0x00000000); + SMU_IMR_WRITE(scic, 0x00000000); + + return false; +} + +/** + * This is the method provided to handle completions for a single MSIX message. + */ +static void scic_sds_controller_single_vector_completion_handler( + struct scic_sds_controller *scic) +{ + u32 interrupt_status; + + interrupt_status = SMU_ISR_READ(scic); + interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND); + + if (interrupt_status & SMU_ISR_QUEUE_ERROR) { + dev_err(scic_to_dev(scic), + "%s: SCIC Controller has encountered a fatal error.\n", + __func__); + + /* + * We have a fatal condition and must reset the controller + * Leave the interrupt mask in place and get the controller reset */ + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(scic), + SCI_BASE_CONTROLLER_STATE_FAILED); + return; + } + + if ((interrupt_status & SMU_ISR_QUEUE_SUSPEND) && + !scic_sds_controller_completion_queue_has_entries(scic)) { + dev_err(scic_to_dev(scic), + "%s: SCIC Controller has encountered a fatal error.\n", + __func__); + + /* + * We have a fatal condtion and must reset the controller + * Leave the interrupt mask in place and get the controller reset */ + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(scic), + SCI_BASE_CONTROLLER_STATE_FAILED); + return; + } + + if (scic_sds_controller_completion_queue_has_entries(scic)) { + scic_sds_controller_process_completions(scic); + + /* + * We dont care which interrupt got us to processing the completion queu + * so clear them both. */ + SMU_ISR_WRITE( + scic, + (SMU_ISR_COMPLETION | SMU_ISR_QUEUE_SUSPEND)); + } + + SMU_IMR_WRITE(scic, 0x00000000); +} + +/** + * This is the method provided to handle a MSIX message for a normal completion. + * + * bool true if an interrupt is processed false if no interrupt was processed + */ +static bool scic_sds_controller_normal_vector_interrupt_handler( + struct scic_sds_controller *scic) +{ + if (scic_sds_controller_completion_queue_has_entries(scic)) { + return true; + } else { + /* + * we have a spurious interrupt it could be that we have already + * emptied the completion queue from a previous interrupt */ + SMU_ISR_WRITE(scic, SMU_ISR_COMPLETION); + + /* + * There is a race in the hardware that could cause us not to be notified + * of an interrupt completion if we do not take this step. We will mask + * then unmask the interrupts so if there is another interrupt pending + * the clearing of the interrupt source we get the next interrupt message. */ + SMU_IMR_WRITE(scic, 0xFF000000); + SMU_IMR_WRITE(scic, 0x00000000); + } + + return false; +} + +/** + * This is the method provided to handle the completions for a normal MSIX + * message. + */ +static void scic_sds_controller_normal_vector_completion_handler( + struct scic_sds_controller *scic) +{ + /* Empty out the completion queue */ + if (scic_sds_controller_completion_queue_has_entries(scic)) + scic_sds_controller_process_completions(scic); + + /* Clear the interrupt and enable all interrupts again */ + SMU_ISR_WRITE(scic, SMU_ISR_COMPLETION); + /* Could we write the value of SMU_ISR_COMPLETION? */ + SMU_IMR_WRITE(scic, 0xFF000000); + SMU_IMR_WRITE(scic, 0x00000000); +} + +/** + * This is the method provided to handle the error MSIX message interrupt. + * This is the normal operating mode for the hardware if MSIX is enabled. + * + * bool true if an interrupt is processed false if no interrupt was processed + */ +static bool scic_sds_controller_error_vector_interrupt_handler( + struct scic_sds_controller *scic) +{ + u32 interrupt_status; + + interrupt_status = SMU_ISR_READ(scic); + interrupt_status &= (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND); + + if (interrupt_status != 0) { + /* + * There is an error interrupt pending so let it through and handle + * in the callback */ + return true; + } + + /* + * There is a race in the hardware that could cause us not to be notified + * of an interrupt completion if we do not take this step. We will mask + * then unmask the error interrupts so if there was another interrupt + * pending we will be notified. + * Could we write the value of (SMU_ISR_QUEUE_ERROR | SMU_ISR_QUEUE_SUSPEND)? */ + SMU_IMR_WRITE(scic, 0x000000FF); + SMU_IMR_WRITE(scic, 0x00000000); + + return false; +} + +/** + * This is the method provided to handle the error completions when the + * hardware is using two MSIX messages. + */ +static void scic_sds_controller_error_vector_completion_handler( + struct scic_sds_controller *scic) +{ + u32 interrupt_status; + + interrupt_status = SMU_ISR_READ(scic); + + if ((interrupt_status & SMU_ISR_QUEUE_SUSPEND) && + scic_sds_controller_completion_queue_has_entries(scic)) { + + scic_sds_controller_process_completions(scic); + SMU_ISR_WRITE(scic, SMU_ISR_QUEUE_SUSPEND); + + } else { + dev_err(scic_to_dev(scic), + "%s: SCIC Controller reports CRC error on completion " + "ISR %x\n", + __func__, + interrupt_status); + + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(scic), + SCI_BASE_CONTROLLER_STATE_FAILED); + + return; + } + + /* + * If we dont process any completions I am not sure that we want to do this. + * We are in the middle of a hardware fault and should probably be reset. */ + SMU_IMR_WRITE(scic, 0x00000000); +} + + +/* + * ****************************************************************************- + * * SCIC SDS Controller External Methods + * ****************************************************************************- */ + +/** + * This method returns the sizeof the SCIC SDS Controller Object + */ +u32 scic_sds_controller_get_object_size(void) +{ + return sizeof(struct scic_sds_controller); +} + + +void scic_sds_controller_link_up( + struct scic_sds_controller *scic, + struct scic_sds_port *sci_port, + struct scic_sds_phy *sci_phy) +{ + scic_sds_controller_phy_handler_t link_up; + u32 state; + + state = scic->parent.state_machine.current_state_id; + link_up = scic_sds_controller_state_handler_table[state].link_up; + + if (link_up) + link_up(scic, sci_port, sci_phy); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller linkup event from phy %d in " + "unexpected state %d\n", + __func__, + sci_phy->phy_index, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + scic))); +} + + +void scic_sds_controller_link_down( + struct scic_sds_controller *scic, + struct scic_sds_port *sci_port, + struct scic_sds_phy *sci_phy) +{ + u32 state; + scic_sds_controller_phy_handler_t link_down; + + state = scic->parent.state_machine.current_state_id; + link_down = scic_sds_controller_state_handler_table[state].link_down; + + if (link_down) + link_down(scic, sci_port, sci_phy); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller linkdown event from phy %d in " + "unexpected state %d\n", + __func__, + sci_phy->phy_index, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + scic))); +} + +/** + * This method will write to the SCU PCP register the request value. The method + * is used to suspend/resume ports, devices, and phys. + * @this_controller: + * + * + */ +void scic_sds_controller_post_request( + struct scic_sds_controller *this_controller, + u32 request) +{ + dev_dbg(scic_to_dev(this_controller), + "%s: SCIC Controller 0x%p post request 0x%08x\n", + __func__, + this_controller, + request); + + SMU_PCP_WRITE(this_controller, request); +} + +/** + * This method will copy the soft copy of the task context into the physical + * memory accessible by the controller. + * @this_controller: This parameter specifies the controller for which to copy + * the task context. + * @this_request: This parameter specifies the request for which the task + * context is being copied. + * + * After this call is made the SCIC_SDS_IO_REQUEST object will always point to + * the physical memory version of the task context. Thus, all subsequent + * updates to the task context are performed in the TC table (i.e. DMAable + * memory). none + */ +void scic_sds_controller_copy_task_context( + struct scic_sds_controller *this_controller, + struct scic_sds_request *this_request) +{ + struct scu_task_context *task_context_buffer; + + task_context_buffer = scic_sds_controller_get_task_context_buffer( + this_controller, this_request->io_tag + ); + + memcpy( + task_context_buffer, + this_request->task_context_buffer, + SCI_FIELD_OFFSET(struct scu_task_context, sgl_snapshot_ac) + ); + + /* + * Now that the soft copy of the TC has been copied into the TC + * table accessible by the silicon. Thus, any further changes to + * the TC (e.g. TC termination) occur in the appropriate location. */ + this_request->task_context_buffer = task_context_buffer; +} + +/** + * This method returns the task context buffer for the given io tag. + * @this_controller: + * @io_tag: + * + * struct scu_task_context* + */ +struct scu_task_context *scic_sds_controller_get_task_context_buffer( + struct scic_sds_controller *this_controller, + u16 io_tag + ) { + u16 task_index = scic_sds_io_tag_get_index(io_tag); + + if (task_index < this_controller->task_context_entries) { + return &this_controller->task_context_table[task_index]; + } + + return NULL; +} + +/** + * This method returnst the sequence value from the io tag value + * @this_controller: + * @io_tag: + * + * u16 + */ + +/** + * This method returns the IO request associated with the tag value + * @this_controller: + * @io_tag: + * + * SCIC_SDS_IO_REQUEST_T* NULL if there is no valid IO request at the tag value + */ +struct scic_sds_request *scic_sds_controller_get_io_request_from_tag( + struct scic_sds_controller *this_controller, + u16 io_tag + ) { + u16 task_index; + u16 task_sequence; + + task_index = scic_sds_io_tag_get_index(io_tag); + + if (task_index < this_controller->task_context_entries) { + if (this_controller->io_request_table[task_index] != SCI_INVALID_HANDLE) { + task_sequence = scic_sds_io_tag_get_sequence(io_tag); + + if (task_sequence == this_controller->io_request_sequence[task_index]) { + return this_controller->io_request_table[task_index]; + } + } + } + + return SCI_INVALID_HANDLE; +} + +/** + * This method allocates remote node index and the reserves the remote node + * context space for use. This method can fail if there are no more remote + * node index available. + * @this_controller: This is the controller object which contains the set of + * free remote node ids + * @the_devce: This is the device object which is requesting the a remote node + * id + * @node_id: This is the remote node id that is assinged to the device if one + * is available + * + * enum sci_status SCI_FAILURE_OUT_OF_RESOURCES if there are no available remote + * node index available. + */ +enum sci_status scic_sds_controller_allocate_remote_node_context( + struct scic_sds_controller *this_controller, + struct scic_sds_remote_device *the_device, + u16 *node_id) +{ + u16 node_index; + u32 remote_node_count = scic_sds_remote_device_node_count(the_device); + + node_index = scic_sds_remote_node_table_allocate_remote_node( + &this_controller->available_remote_nodes, remote_node_count + ); + + if (node_index != SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { + this_controller->device_table[node_index] = the_device; + + *node_id = node_index; + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INSUFFICIENT_RESOURCES; +} + +/** + * This method frees the remote node index back to the available pool. Once + * this is done the remote node context buffer is no longer valid and can + * not be used. + * @this_controller: + * @the_device: + * @node_id: + * + */ +void scic_sds_controller_free_remote_node_context( + struct scic_sds_controller *this_controller, + struct scic_sds_remote_device *the_device, + u16 node_id) +{ + u32 remote_node_count = scic_sds_remote_device_node_count(the_device); + + if (this_controller->device_table[node_id] == the_device) { + this_controller->device_table[node_id] = SCI_INVALID_HANDLE; + + scic_sds_remote_node_table_release_remote_node_index( + &this_controller->available_remote_nodes, remote_node_count, node_id + ); + } +} + +/** + * This method returns the union scu_remote_node_context for the specified remote + * node id. + * @this_controller: + * @node_id: + * + * union scu_remote_node_context* + */ +union scu_remote_node_context *scic_sds_controller_get_remote_node_context_buffer( + struct scic_sds_controller *this_controller, + u16 node_id + ) { + if ( + (node_id < this_controller->remote_node_entries) + && (this_controller->device_table[node_id] != SCI_INVALID_HANDLE) + ) { + return &this_controller->remote_node_context_table[node_id]; + } + + return NULL; +} + +/** + * + * @resposne_buffer: This is the buffer into which the D2H register FIS will be + * constructed. + * @frame_header: This is the frame header returned by the hardware. + * @frame_buffer: This is the frame buffer returned by the hardware. + * + * This method will combind the frame header and frame buffer to create a SATA + * D2H register FIS none + */ +void scic_sds_controller_copy_sata_response( + void *response_buffer, + void *frame_header, + void *frame_buffer) +{ + memcpy( + response_buffer, + frame_header, + sizeof(u32) + ); + + memcpy( + (char *)((char *)response_buffer + sizeof(u32)), + frame_buffer, + sizeof(struct sata_fis_reg_d2h) - sizeof(u32) + ); +} + +/** + * This method releases the frame once this is done the frame is available for + * re-use by the hardware. The data contained in the frame header and frame + * buffer is no longer valid. The UF queue get pointer is only updated if UF + * control indicates this is appropriate. + * @this_controller: + * @frame_index: + * + */ +void scic_sds_controller_release_frame( + struct scic_sds_controller *this_controller, + u32 frame_index) +{ + if (scic_sds_unsolicited_frame_control_release_frame( + &this_controller->uf_control, frame_index) == true) + SCU_UFQGP_WRITE(this_controller, this_controller->uf_control.get); +} + +/** + * This method sets user parameters and OEM parameters to default values. + * Users can override these values utilizing the scic_user_parameters_set() + * and scic_oem_parameters_set() methods. + * @controller: This parameter specifies the controller for which to set the + * configuration parameters to their default values. + * + */ +static void scic_sds_controller_set_default_config_parameters( + struct scic_sds_controller *this_controller) +{ + u16 index; + + /* Default to no SSC operation. */ + this_controller->oem_parameters.sds1.controller.do_enable_ssc = false; + + /* Initialize all of the port parameter information to narrow ports. */ + for (index = 0; index < SCI_MAX_PORTS; index++) { + this_controller->oem_parameters.sds1.ports[index].phy_mask = 0; + } + + /* Initialize all of the phy parameter information. */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + /* + * Default to 3G (i.e. Gen 2) for now. User can override if + * they choose. */ + this_controller->user_parameters.sds1.phys[index].max_speed_generation = 2; + + /* + * Previous Vitesse based expanders had a arbitration issue that + * is worked around by having the upper 32-bits of SAS address + * with a value greater then the Vitesse company identifier. + * Hence, usage of 0x5FCFFFFF. */ + this_controller->oem_parameters.sds1.phys[index].sas_address.low + = 0x00000001; + this_controller->oem_parameters.sds1.phys[index].sas_address.high + = 0x5FCFFFFF; + } + + this_controller->user_parameters.sds1.stp_inactivity_timeout = 5; + this_controller->user_parameters.sds1.ssp_inactivity_timeout = 5; + this_controller->user_parameters.sds1.stp_max_occupancy_timeout = 5; + this_controller->user_parameters.sds1.ssp_max_occupancy_timeout = 20; + this_controller->user_parameters.sds1.no_outbound_task_timeout = 5; + +} + + +enum sci_status scic_controller_construct(struct scic_sds_controller *controller, + void __iomem *scu_base, + void __iomem *smu_base) +{ + u8 index; + + sci_base_controller_construct( + &controller->parent, + scic_sds_controller_state_table, + controller->memory_descriptors, + ARRAY_SIZE(controller->memory_descriptors), + NULL + ); + + controller->scu_registers = scu_base; + controller->smu_registers = smu_base; + + scic_sds_port_configuration_agent_construct(&controller->port_agent); + + /* Construct the ports for this controller */ + for (index = 0; index < SCI_MAX_PORTS; index++) + scic_sds_port_construct(&controller->port_table[index], + index, controller); + scic_sds_port_construct(&controller->port_table[index], + SCIC_SDS_DUMMY_PORT, controller); + + /* Construct the phys for this controller */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + /* Add all the PHYs to the dummy port */ + scic_sds_phy_construct( + &controller->phy_table[index], + &controller->port_table[SCI_MAX_PORTS], + index + ); + } + + controller->invalid_phy_mask = 0; + + /* Set the default maximum values */ + controller->completion_event_entries = SCU_EVENT_COUNT; + controller->completion_queue_entries = SCU_COMPLETION_QUEUE_COUNT; + controller->remote_node_entries = SCI_MAX_REMOTE_DEVICES; + controller->logical_port_entries = SCI_MAX_PORTS; + controller->task_context_entries = SCU_IO_REQUEST_COUNT; + controller->uf_control.buffers.count = SCU_UNSOLICITED_FRAME_COUNT; + controller->uf_control.address_table.count = SCU_UNSOLICITED_FRAME_COUNT; + + /* Initialize the User and OEM parameters to default values. */ + scic_sds_controller_set_default_config_parameters(controller); + + return SCI_SUCCESS; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_initialize( + struct scic_sds_controller *scic) +{ + enum sci_status status = SCI_FAILURE_INVALID_STATE; + sci_base_controller_handler_t initialize; + u32 state; + + state = scic->parent.state_machine.current_state_id; + initialize = scic_sds_controller_state_handler_table[state].base.initialize; + + if (initialize) + status = initialize(&scic->parent); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller initialize operation requested " + "in invalid state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + scic))); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +u32 scic_controller_get_suggested_start_timeout( + struct scic_sds_controller *sc) +{ + /* Validate the user supplied parameters. */ + if (sc == SCI_INVALID_HANDLE) + return 0; + + /* + * The suggested minimum timeout value for a controller start operation: + * + * Signature FIS Timeout + * + Phy Start Timeout + * + Number of Phy Spin Up Intervals + * --------------------------------- + * Number of milliseconds for the controller start operation. + * + * NOTE: The number of phy spin up intervals will be equivalent + * to the number of phys divided by the number phys allowed + * per interval - 1 (once OEM parameters are supported). + * Currently we assume only 1 phy per interval. */ + + return (SCIC_SDS_SIGNATURE_FIS_TIMEOUT + + SCIC_SDS_CONTROLLER_PHY_START_TIMEOUT + + ((SCI_MAX_PHYS - 1) * SCIC_SDS_CONTROLLER_POWER_CONTROL_INTERVAL)); +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_start( + struct scic_sds_controller *scic, + u32 timeout) +{ + enum sci_status status = SCI_FAILURE_INVALID_STATE; + sci_base_controller_timed_handler_t start; + u32 state; + + state = scic->parent.state_machine.current_state_id; + start = scic_sds_controller_state_handler_table[state].base.start; + + if (start) + status = start(&scic->parent, timeout); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller start operation requested in " + "invalid state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + scic))); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_stop( + struct scic_sds_controller *scic, + u32 timeout) +{ + enum sci_status status = SCI_FAILURE_INVALID_STATE; + sci_base_controller_timed_handler_t stop; + u32 state; + + state = scic->parent.state_machine.current_state_id; + stop = scic_sds_controller_state_handler_table[state].base.stop; + + if (stop) + status = stop(&scic->parent, timeout); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller stop operation requested in " + "invalid state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + scic))); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_reset( + struct scic_sds_controller *scic) +{ + enum sci_status status = SCI_FAILURE_INVALID_STATE; + sci_base_controller_handler_t reset; + u32 state; + + state = scic->parent.state_machine.current_state_id; + reset = scic_sds_controller_state_handler_table[state].base.reset; + + if (reset) + status = reset(&scic->parent); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller reset operation requested in " + "invalid state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + scic))); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_get_handler_methods( + enum scic_interrupt_type interrupt_type, + u16 message_count, + struct scic_controller_handler_methods *handler_methods) +{ + enum sci_status status = SCI_FAILURE_UNSUPPORTED_MESSAGE_COUNT; + + switch (interrupt_type) { + case SCIC_LEGACY_LINE_INTERRUPT_TYPE: + if (message_count == 0) { + handler_methods[0].interrupt_handler + = scic_sds_controller_legacy_interrupt_handler; + handler_methods[0].completion_handler + = scic_sds_controller_legacy_completion_handler; + + status = SCI_SUCCESS; + } + break; + + case SCIC_MSIX_INTERRUPT_TYPE: + if (message_count == 1) { + handler_methods[0].interrupt_handler + = scic_sds_controller_single_vector_interrupt_handler; + handler_methods[0].completion_handler + = scic_sds_controller_single_vector_completion_handler; + + status = SCI_SUCCESS; + } else if (message_count == 2) { + handler_methods[0].interrupt_handler + = scic_sds_controller_normal_vector_interrupt_handler; + handler_methods[0].completion_handler + = scic_sds_controller_normal_vector_completion_handler; + + handler_methods[1].interrupt_handler + = scic_sds_controller_error_vector_interrupt_handler; + handler_methods[1].completion_handler + = scic_sds_controller_error_vector_completion_handler; + + status = SCI_SUCCESS; + } + break; + + case SCIC_NO_INTERRUPTS: + if (message_count == 0) { + + handler_methods[0].interrupt_handler + = scic_sds_controller_polling_interrupt_handler; + handler_methods[0].completion_handler + = scic_sds_controller_polling_completion_handler; + + status = SCI_SUCCESS; + } + break; + + default: + status = SCI_FAILURE_INVALID_PARAMETER_VALUE; + break; + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_io_status scic_controller_start_io( + struct scic_sds_controller *scic, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *io_request, + u16 io_tag) +{ + u32 state; + sci_base_controller_start_request_handler_t start_io; + + state = scic->parent.state_machine.current_state_id; + start_io = scic_sds_controller_state_handler_table[state].base.start_io; + + return start_io(&scic->parent, + (struct sci_base_remote_device *) remote_device, + (struct sci_base_request *)io_request, io_tag); +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_terminate_request( + struct scic_sds_controller *scic, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *request) +{ + sci_base_controller_request_handler_t terminate_request; + u32 state; + + state = scic->parent.state_machine.current_state_id; + terminate_request = scic_sds_controller_state_handler_table[state].terminate_request; + + return terminate_request(&scic->parent, + (struct sci_base_remote_device *)remote_device, + (struct sci_base_request *)request); +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_complete_io( + struct scic_sds_controller *scic, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *io_request) +{ + u32 state; + sci_base_controller_request_handler_t complete_io; + + state = scic->parent.state_machine.current_state_id; + complete_io = scic_sds_controller_state_handler_table[state].base.complete_io; + + return complete_io(&scic->parent, + (struct sci_base_remote_device *)remote_device, + (struct sci_base_request *)io_request); +} + +/* --------------------------------------------------------------------------- */ + + +enum sci_task_status scic_controller_start_task( + struct scic_sds_controller *scic, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *task_request, + u16 task_tag) +{ + u32 state; + sci_base_controller_start_request_handler_t start_task; + enum sci_task_status status = SCI_TASK_FAILURE_INVALID_STATE; + + state = scic->parent.state_machine.current_state_id; + start_task = scic_sds_controller_state_handler_table[state].base.start_task; + + if (start_task) + status = start_task(&scic->parent, + (struct sci_base_remote_device *)remote_device, + (struct sci_base_request *)task_request, + task_tag); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller starting task from invalid " + "state\n", + __func__); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_complete_task( + struct scic_sds_controller *scic, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *task_request) +{ + u32 state; + sci_base_controller_request_handler_t complete_task; + enum sci_status status = SCI_FAILURE_INVALID_STATE; + + state = scic->parent.state_machine.current_state_id; + complete_task = scic_sds_controller_state_handler_table[state].base.complete_task; + + if (complete_task) + status = complete_task(&scic->parent, + (struct sci_base_remote_device *)remote_device, + (struct sci_base_request *)task_request); + else + dev_warn(scic_to_dev(scic), + "%s: SCIC Controller completing task from invalid " + "state\n", + __func__); + + return status; +} + + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_get_port_handle( + struct scic_sds_controller *scic, + u8 port_index, + struct scic_sds_port **port_handle) +{ + if (port_index < scic->logical_port_entries) { + *port_handle = &scic->port_table[port_index]; + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_PORT; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_get_phy_handle( + struct scic_sds_controller *scic, + u8 phy_index, + struct scic_sds_phy **phy_handle) +{ + if (phy_index < ARRAY_SIZE(scic->phy_table)) { + *phy_handle = &scic->phy_table[phy_index]; + + return SCI_SUCCESS; + } + + dev_err(scic_to_dev(scic), + "%s: Controller:0x%p PhyId:0x%x invalid phy index\n", + __func__, scic, phy_index); + + return SCI_FAILURE_INVALID_PHY; +} + +/* --------------------------------------------------------------------------- */ + +u16 scic_controller_allocate_io_tag( + struct scic_sds_controller *scic) +{ + u16 task_context; + u16 sequence_count; + + if (!sci_pool_empty(scic->tci_pool)) { + sci_pool_get(scic->tci_pool, task_context); + + sequence_count = scic->io_request_sequence[task_context]; + + return scic_sds_io_tag_construct(sequence_count, task_context); + } + + return SCI_CONTROLLER_INVALID_IO_TAG; +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_free_io_tag( + struct scic_sds_controller *scic, + u16 io_tag) +{ + u16 sequence; + u16 index; + + BUG_ON(io_tag == SCI_CONTROLLER_INVALID_IO_TAG); + + sequence = scic_sds_io_tag_get_sequence(io_tag); + index = scic_sds_io_tag_get_index(io_tag); + + if (!sci_pool_full(scic->tci_pool)) { + if (sequence == scic->io_request_sequence[index]) { + scic_sds_io_sequence_increment( + scic->io_request_sequence[index]); + + sci_pool_put(scic->tci_pool, index); + + return SCI_SUCCESS; + } + } + + return SCI_FAILURE_INVALID_IO_TAG; +} + +/* --------------------------------------------------------------------------- */ + +void scic_controller_enable_interrupts( + struct scic_sds_controller *scic) +{ + BUG_ON(scic->smu_registers == NULL); + SMU_IMR_WRITE(scic, 0x00000000); +} + +/* --------------------------------------------------------------------------- */ + +void scic_controller_disable_interrupts( + struct scic_sds_controller *scic) +{ + BUG_ON(scic->smu_registers == NULL); + SMU_IMR_WRITE(scic, 0xffffffff); +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_controller_set_mode( + struct scic_sds_controller *scic, + enum sci_controller_mode operating_mode) +{ + enum sci_status status = SCI_SUCCESS; + + if ((scic->parent.state_machine.current_state_id == + SCI_BASE_CONTROLLER_STATE_INITIALIZING) || + (scic->parent.state_machine.current_state_id == + SCI_BASE_CONTROLLER_STATE_INITIALIZED)) { + switch (operating_mode) { + case SCI_MODE_SPEED: + scic->remote_node_entries = SCI_MAX_REMOTE_DEVICES; + scic->task_context_entries = SCU_IO_REQUEST_COUNT; + scic->uf_control.buffers.count = + SCU_UNSOLICITED_FRAME_COUNT; + scic->completion_event_entries = SCU_EVENT_COUNT; + scic->completion_queue_entries = + SCU_COMPLETION_QUEUE_COUNT; + scic_sds_controller_build_memory_descriptor_table(scic); + break; + + case SCI_MODE_SIZE: + scic->remote_node_entries = SCI_MIN_REMOTE_DEVICES; + scic->task_context_entries = SCI_MIN_IO_REQUESTS; + scic->uf_control.buffers.count = + SCU_MIN_UNSOLICITED_FRAMES; + scic->completion_event_entries = SCU_MIN_EVENTS; + scic->completion_queue_entries = + SCU_MIN_COMPLETION_QUEUE_ENTRIES; + scic_sds_controller_build_memory_descriptor_table(scic); + break; + + default: + status = SCI_FAILURE_INVALID_PARAMETER_VALUE; + break; + } + } else + status = SCI_FAILURE_INVALID_STATE; + + return status; +} + +/** + * scic_sds_controller_reset_hardware() - + * + * This method will reset the controller hardware. + */ +void scic_sds_controller_reset_hardware( + struct scic_sds_controller *scic) +{ + /* Disable interrupts so we dont take any spurious interrupts */ + scic_controller_disable_interrupts(scic); + + /* Reset the SCU */ + SMU_SMUSRCR_WRITE(scic, 0xFFFFFFFF); + + /* Delay for 1ms to before clearing the CQP and UFQPR. */ + scic_cb_stall_execution(1000); + + /* The write to the CQGR clears the CQP */ + SMU_CQGR_WRITE(scic, 0x00000000); + + /* The write to the UFQGP clears the UFQPR */ + SCU_UFQGP_WRITE(scic, 0x00000000); +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_user_parameters_set( + struct scic_sds_controller *scic, + union scic_user_parameters *scic_parms) +{ + if ( + (scic->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_RESET) + || (scic->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_INITIALIZING) + || (scic->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_INITIALIZED) + ) { + u16 index; + + /* + * Validate the user parameters. If they are not legal, then + * return a failure. */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (! + (scic_parms->sds1.phys[index].max_speed_generation + <= SCIC_SDS_PARM_MAX_SPEED + && scic_parms->sds1.phys[index].max_speed_generation + > SCIC_SDS_PARM_NO_SPEED + ) + ) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + } + + memcpy(&scic->user_parameters, scic_parms, sizeof(*scic_parms)); + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_STATE; +} + +/* --------------------------------------------------------------------------- */ + +void scic_user_parameters_get( + struct scic_sds_controller *scic, + union scic_user_parameters *scic_parms) +{ + memcpy(scic_parms, (&scic->user_parameters), sizeof(*scic_parms)); +} + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_oem_parameters_set( + struct scic_sds_controller *scic, + union scic_oem_parameters *scic_parms) +{ + if ( + (scic->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_RESET) + || (scic->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_INITIALIZING) + || (scic->parent.state_machine.current_state_id + == SCI_BASE_CONTROLLER_STATE_INITIALIZED) + ) { + u16 index; + + /* + * Validate the oem parameters. If they are not legal, then + * return a failure. */ + for (index = 0; index < SCI_MAX_PORTS; index++) { + if (scic_parms->sds1.ports[index].phy_mask > SCIC_SDS_PARM_PHY_MASK_MAX) { + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + } + } + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if ( + scic_parms->sds1.phys[index].sas_address.high == 0 + && scic_parms->sds1.phys[index].sas_address.low == 0 + ) { + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + } + } + + memcpy(&scic->oem_parameters, scic_parms, sizeof(*scic_parms)); + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_STATE; +} + +/* --------------------------------------------------------------------------- */ + +void scic_oem_parameters_get( + struct scic_sds_controller *scic, + union scic_oem_parameters *scic_parms) +{ + memcpy(scic_parms, (&scic->oem_parameters), sizeof(*scic_parms)); +} + +/* --------------------------------------------------------------------------- */ + + +#define INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_LOWER_BOUND_NS 853 +#define INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_UPPER_BOUND_NS 1280 +#define INTERRUPT_COALESCE_TIMEOUT_MAX_US 2700000 +#define INTERRUPT_COALESCE_NUMBER_MAX 256 +#define INTERRUPT_COALESCE_TIMEOUT_ENCODE_MIN 7 +#define INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX 28 + +enum sci_status scic_controller_set_interrupt_coalescence( + struct scic_sds_controller *scic_controller, + u32 coalesce_number, + u32 coalesce_timeout) +{ + u8 timeout_encode = 0; + u32 min = 0; + u32 max = 0; + + /* Check if the input parameters fall in the range. */ + if (coalesce_number > INTERRUPT_COALESCE_NUMBER_MAX) + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + + /* + * Defined encoding for interrupt coalescing timeout: + * Value Min Max Units + * ----- --- --- ----- + * 0 - - Disabled + * 1 13.3 20.0 ns + * 2 26.7 40.0 + * 3 53.3 80.0 + * 4 106.7 160.0 + * 5 213.3 320.0 + * 6 426.7 640.0 + * 7 853.3 1280.0 + * 8 1.7 2.6 us + * 9 3.4 5.1 + * 10 6.8 10.2 + * 11 13.7 20.5 + * 12 27.3 41.0 + * 13 54.6 81.9 + * 14 109.2 163.8 + * 15 218.5 327.7 + * 16 436.9 655.4 + * 17 873.8 1310.7 + * 18 1.7 2.6 ms + * 19 3.5 5.2 + * 20 7.0 10.5 + * 21 14.0 21.0 + * 22 28.0 41.9 + * 23 55.9 83.9 + * 24 111.8 167.8 + * 25 223.7 335.5 + * 26 447.4 671.1 + * 27 894.8 1342.2 + * 28 1.8 2.7 s + * Others Undefined */ + + /* + * Use the table above to decide the encode of interrupt coalescing timeout + * value for register writing. */ + if (coalesce_timeout == 0) + timeout_encode = 0; + else{ + /* make the timeout value in unit of (10 ns). */ + coalesce_timeout = coalesce_timeout * 100; + min = INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_LOWER_BOUND_NS / 10; + max = INTERRUPT_COALESCE_TIMEOUT_BASE_RANGE_UPPER_BOUND_NS / 10; + + /* get the encode of timeout for register writing. */ + for (timeout_encode = INTERRUPT_COALESCE_TIMEOUT_ENCODE_MIN; + timeout_encode <= INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX; + timeout_encode++) { + if (min <= coalesce_timeout && max > coalesce_timeout) + break; + else if (coalesce_timeout >= max && coalesce_timeout < min * 2 + && coalesce_timeout <= INTERRUPT_COALESCE_TIMEOUT_MAX_US * 100) { + if ((coalesce_timeout - max) < (2 * min - coalesce_timeout)) + break; + else{ + timeout_encode++; + break; + } + } else { + max = max * 2; + min = min * 2; + } + } + + if (timeout_encode == INTERRUPT_COALESCE_TIMEOUT_ENCODE_MAX + 1) + /* the value is out of range. */ + return SCI_FAILURE_INVALID_PARAMETER_VALUE; + } + + SMU_ICC_WRITE( + scic_controller, + (SMU_ICC_GEN_VAL(NUMBER, coalesce_number) | + SMU_ICC_GEN_VAL(TIMER, timeout_encode)) + ); + + scic_controller->interrupt_coalesce_number = (u16)coalesce_number; + scic_controller->interrupt_coalesce_timeout = coalesce_timeout / 100; + + return SCI_SUCCESS; +} + + +struct scic_sds_controller *scic_controller_alloc(struct device *dev) +{ + return devm_kzalloc(dev, sizeof(struct scic_sds_controller), GFP_KERNEL); +} + +/* + * ***************************************************************************** + * * DEFAULT STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which, if it was used, would + * be cast to a struct scic_sds_remote_device. + * @io_request: This is the struct sci_base_request which, if it was used, would be + * cast to a SCIC_SDS_IO_REQUEST. + * @io_tag: This is the IO tag to be assigned to the IO request or + * SCI_CONTROLLER_INVALID_IO_TAG. + * + * This method is called when the struct scic_sds_controller default start io/task + * handler is in place. - Issue a warning message enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_controller_default_start_operation_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request, + u16 io_tag) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller requested to start an io/task from " + "invalid state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + this_controller))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which, if it was used, would + * be cast to a struct scic_sds_remote_device. + * @io_request: This is the struct sci_base_request which, if it was used, would be + * cast to a SCIC_SDS_IO_REQUEST. + * + * This method is called when the struct scic_sds_controller default request handler + * is in place. - Issue a warning message enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_controller_default_request_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + dev_warn(scic_to_dev(this_controller), + "%s: SCIC Controller request operation from invalid state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_controller_get_base_state_machine( + this_controller))); + + return SCI_FAILURE_INVALID_STATE; +} + +/* + * ***************************************************************************** + * * GENERAL (COMMON) STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: The struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * + * This method is called when the struct scic_sds_controller is in the ready state + * reset handler is in place. - Transition to + * SCI_BASE_CONTROLLER_STATE_RESETTING enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_controller_general_reset_handler( + struct sci_base_controller *controller) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + /* + * The reset operation is not a graceful cleanup just perform the state + * transition. */ + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_RESETTING + ); + + return SCI_SUCCESS; +} + +/* + * ***************************************************************************** + * * RESET STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: This is the struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * + * This method is the struct scic_sds_controller initialize handler for the reset + * state. - Currently this function does nothing enum sci_status SCI_FAILURE This + * function is not yet implemented and is a valid request from the reset state. + */ +static enum sci_status scic_sds_controller_reset_state_initialize_handler( + struct sci_base_controller *controller) +{ + u32 index; + enum sci_status result = SCI_SUCCESS; + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_INITIALIZING + ); + + this_controller->timeout_timer = scic_cb_timer_create( + this_controller, + (void (*)(void *))scic_sds_controller_timeout_handler, + (void (*)(void *))controller); + + scic_sds_controller_initialize_phy_startup(this_controller); + + scic_sds_controller_initialize_power_control(this_controller); + + /* + * There is nothing to do here for B0 since we do not have to + * program the AFE registers. + * / @todo The AFE settings are supposed to be correct for the B0 but + * / presently they seem to be wrong. */ + scic_sds_controller_afe_initialization(this_controller); + + if (SCI_SUCCESS == result) { + u32 status; + u32 terminate_loop; + + /* Take the hardware out of reset */ + SMU_SMUSRCR_WRITE(this_controller, 0x00000000); + + /* + * / @todo Provide meaningfull error code for hardware failure + * result = SCI_FAILURE_CONTROLLER_HARDWARE; */ + result = SCI_FAILURE; + terminate_loop = 100; + + while (terminate_loop-- && (result != SCI_SUCCESS)) { + /* Loop until the hardware reports success */ + scic_cb_stall_execution(SCU_CONTEXT_RAM_INIT_STALL_TIME); + status = SMU_SMUCSR_READ(this_controller); + + if ((status & SCU_RAM_INIT_COMPLETED) == SCU_RAM_INIT_COMPLETED) { + result = SCI_SUCCESS; + } + } + } + + if (result == SCI_SUCCESS) { + u32 max_supported_ports; + u32 max_supported_devices; + u32 max_supported_io_requests; + u32 device_context_capacity; + + /* + * Determine what are the actaul device capacities that the + * hardware will support */ + device_context_capacity = SMU_DCC_READ(this_controller); + + max_supported_ports = + smu_dcc_get_max_ports(device_context_capacity); + max_supported_devices = + smu_dcc_get_max_remote_node_context(device_context_capacity); + max_supported_io_requests = + smu_dcc_get_max_task_context(device_context_capacity); + + /* Make all PEs that are unassigned match up with the logical ports */ + for (index = 0; index < max_supported_ports; index++) { + scu_register_write( + this_controller, + this_controller->scu_registers->peg0.ptsg.protocol_engine[index], + index + ); + } + + /* Record the smaller of the two capacity values */ + this_controller->logical_port_entries = + min(max_supported_ports, this_controller->logical_port_entries); + + this_controller->task_context_entries = + min(max_supported_io_requests, this_controller->task_context_entries); + + this_controller->remote_node_entries = + min(max_supported_devices, this_controller->remote_node_entries); + + /* + * Now that we have the correct hardware reported minimum values + * build the MDL for the controller. Default to a performance + * configuration. */ + scic_controller_set_mode(this_controller, SCI_MODE_SPEED); + } + + /* Initialize hardware PCI Relaxed ordering in DMA engines */ + if (result == SCI_SUCCESS) { + u32 dma_configuration; + + /* Configure the payload DMA */ + dma_configuration = SCU_PDMACR_READ(this_controller); + dma_configuration |= SCU_PDMACR_GEN_BIT(PCI_RELAXED_ORDERING_ENABLE); + SCU_PDMACR_WRITE(this_controller, dma_configuration); + + /* Configure the control DMA */ + dma_configuration = SCU_CDMACR_READ(this_controller); + dma_configuration |= SCU_CDMACR_GEN_BIT(PCI_RELAXED_ORDERING_ENABLE); + SCU_CDMACR_WRITE(this_controller, dma_configuration); + } + + /* + * Initialize the PHYs before the PORTs because the PHY registers + * are accessed during the port initialization. */ + if (result == SCI_SUCCESS) { + /* Initialize the phys */ + for (index = 0; + (result == SCI_SUCCESS) && (index < SCI_MAX_PHYS); + index++) { + result = scic_sds_phy_initialize( + &this_controller->phy_table[index], + &this_controller->scu_registers->peg0.pe[index].ll + ); + } + } + + if (result == SCI_SUCCESS) { + /* Initialize the logical ports */ + for (index = 0; + (index < this_controller->logical_port_entries) + && (result == SCI_SUCCESS); + index++) { + result = scic_sds_port_initialize( + &this_controller->port_table[index], + &this_controller->scu_registers->peg0.pe[index].tl, + &this_controller->scu_registers->peg0.ptsg.port[index], + &this_controller->scu_registers->peg0.ptsg.protocol_engine, + &this_controller->scu_registers->peg0.viit[index] + ); + } + } + + if (SCI_SUCCESS == result) { + result = scic_sds_port_configuration_agent_initialize( + this_controller, + &this_controller->port_agent + ); + } + + /* Advance the controller state machine */ + if (result == SCI_SUCCESS) { + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_INITIALIZED + ); + } else { + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_FAILED + ); + } + + return result; +} + +/* + * ***************************************************************************** + * * INITIALIZED STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: This is the struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @timeout: This is the allowed time for the controller object to reach the + * started state. + * + * This method is the struct scic_sds_controller start handler for the initialized + * state. - Validate we have a good memory descriptor table - Initialze the + * physical memory before programming the hardware - Program the SCU hardware + * with the physical memory addresses passed in the memory descriptor table. - + * Initialzie the TCi pool - Initialize the RNi pool - Initialize the + * completion queue - Initialize the unsolicited frame data - Take the SCU port + * task scheduler out of reset - Start the first phy object. - Transition to + * SCI_BASE_CONTROLLER_STATE_STARTING. enum sci_status SCI_SUCCESS if all of the + * controller start operations complete + * SCI_FAILURE_UNSUPPORTED_INFORMATION_FIELD if one or more of the memory + * descriptor fields is invalid. + */ +static enum sci_status scic_sds_controller_initialized_state_start_handler( + struct sci_base_controller *controller, + u32 timeout) +{ + u16 index; + enum sci_status result; + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + /* Make sure that the SCI User filled in the memory descriptor table correctly */ + result = scic_sds_controller_validate_memory_descriptor_table(this_controller); + + if (result == SCI_SUCCESS) { + /* The memory descriptor list looks good so program the hardware */ + scic_sds_controller_ram_initialization(this_controller); + } + + if (SCI_SUCCESS == result) { + /* Build the TCi free pool */ + sci_pool_initialize(this_controller->tci_pool); + for (index = 0; index < this_controller->task_context_entries; index++) { + sci_pool_put(this_controller->tci_pool, index); + } + + /* Build the RNi free pool */ + scic_sds_remote_node_table_initialize( + &this_controller->available_remote_nodes, + this_controller->remote_node_entries + ); + } + + if (SCI_SUCCESS == result) { + /* + * Before anything else lets make sure we will not be interrupted + * by the hardware. */ + scic_controller_disable_interrupts(this_controller); + + /* Enable the port task scheduler */ + scic_sds_controller_enable_port_task_scheduler(this_controller); + + /* Assign all the task entries to this controller physical function */ + scic_sds_controller_assign_task_entries(this_controller); + + /* Now initialze the completion queue */ + scic_sds_controller_initialize_completion_queue(this_controller); + + /* Initialize the unsolicited frame queue for use */ + scic_sds_controller_initialize_unsolicited_frame_queue(this_controller); + } + + if (SCI_SUCCESS == result) { + scic_sds_controller_start_next_phy(this_controller); + + scic_cb_timer_start(this_controller, + this_controller->timeout_timer, + timeout); + + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_STARTING + ); + } + + return result; +} + +/* + * ***************************************************************************** + * * INITIALIZED STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: This is struct scic_sds_controller which receives the link up + * notification. + * @port: This is struct scic_sds_port with which the phy is associated. + * @phy: This is the struct scic_sds_phy which has gone link up. + * + * This method is called when the struct scic_sds_controller is in the starting state + * link up handler is called. This method will perform the following: - Stop + * the phy timer - Start the next phy - Report the link up condition to the + * port object none + */ +static void scic_sds_controller_starting_state_link_up_handler( + struct scic_sds_controller *this_controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + scic_sds_controller_phy_timer_stop(this_controller); + + this_controller->port_agent.link_up_handler( + this_controller, &this_controller->port_agent, port, phy + ); + /* scic_sds_port_link_up(port, phy); */ + + scic_sds_controller_start_next_phy(this_controller); +} + +/** + * + * @controller: This is struct scic_sds_controller which receives the link down + * notification. + * @port: This is struct scic_sds_port with which the phy is associated. + * @phy: This is the struct scic_sds_phy which has gone link down. + * + * This method is called when the struct scic_sds_controller is in the starting state + * link down handler is called. - Report the link down condition to the port + * object none + */ +static void scic_sds_controller_starting_state_link_down_handler( + struct scic_sds_controller *this_controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + this_controller->port_agent.link_down_handler( + this_controller, &this_controller->port_agent, port, phy + ); + /* scic_sds_port_link_down(port, phy); */ +} + +/* + * ***************************************************************************** + * * READY STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: The struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @timeout: The timeout for when the stop operation should report a failure. + * + * This method is called when the struct scic_sds_controller is in the ready state + * stop handler is called. - Start the timeout timer - Transition to + * SCI_BASE_CONTROLLER_STATE_STOPPING. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_controller_ready_state_stop_handler( + struct sci_base_controller *controller, + u32 timeout) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + scic_cb_timer_start(this_controller, + this_controller->timeout_timer, + timeout); + + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_STOPPING + ); + + return SCI_SUCCESS; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * @io_tag: This is the IO tag to be assigned to the IO request or + * SCI_CONTROLLER_INVALID_IO_TAG. + * + * This method is called when the struct scic_sds_controller is in the ready state and + * the start io handler is called. - Start the io request on the remote device + * - if successful - assign the io_request to the io_request_table - post the + * request to the hardware enum sci_status SCI_SUCCESS if the start io operation + * succeeds SCI_FAILURE_INSUFFICIENT_RESOURCES if the IO tag could not be + * allocated for the io request. SCI_FAILURE_INVALID_STATE if one or more + * objects are not in a valid state to accept io requests. How does the io_tag + * parameter get assigned to the io request? + */ +static enum sci_status scic_sds_controller_ready_state_start_io_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request, + u16 io_tag) +{ + enum sci_status status; + + struct scic_sds_controller *this_controller; + struct scic_sds_request *the_request; + struct scic_sds_remote_device *the_device; + + this_controller = (struct scic_sds_controller *)controller; + the_request = (struct scic_sds_request *)io_request; + the_device = (struct scic_sds_remote_device *)remote_device; + + status = scic_sds_remote_device_start_io(this_controller, the_device, the_request); + + if (status == SCI_SUCCESS) { + this_controller->io_request_table[ + scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; + + scic_sds_controller_post_request( + this_controller, + scic_sds_request_get_post_context(the_request) + ); + } + + return status; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * + * This method is called when the struct scic_sds_controller is in the ready state and + * the complete io handler is called. - Complete the io request on the remote + * device - if successful - remove the io_request to the io_request_table + * enum sci_status SCI_SUCCESS if the start io operation succeeds + * SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid state to + * accept io requests. + */ +static enum sci_status scic_sds_controller_ready_state_complete_io_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request) +{ + u16 index; + enum sci_status status; + struct scic_sds_controller *this_controller; + struct scic_sds_request *the_request; + struct scic_sds_remote_device *the_device; + + this_controller = (struct scic_sds_controller *)controller; + the_request = (struct scic_sds_request *)io_request; + the_device = (struct scic_sds_remote_device *)remote_device; + + status = scic_sds_remote_device_complete_io( + this_controller, the_device, the_request); + + if (status == SCI_SUCCESS) { + index = scic_sds_io_tag_get_index(the_request->io_tag); + this_controller->io_request_table[index] = SCI_INVALID_HANDLE; + } + + return status; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * + * This method is called when the struct scic_sds_controller is in the ready state and + * the continue io handler is called. enum sci_status + */ +static enum sci_status scic_sds_controller_ready_state_continue_io_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request) +{ + struct scic_sds_controller *this_controller; + struct scic_sds_request *the_request; + + the_request = (struct scic_sds_request *)io_request; + this_controller = (struct scic_sds_controller *)controller; + + this_controller->io_request_table[ + scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; + + scic_sds_controller_post_request( + this_controller, + scic_sds_request_get_post_context(the_request) + ); + + return SCI_SUCCESS; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * @task_tag: This is the task tag to be assigned to the task request or + * SCI_CONTROLLER_INVALID_IO_TAG. + * + * This method is called when the struct scic_sds_controller is in the ready state and + * the start task handler is called. - The remote device is requested to start + * the task request - if successful - assign the task to the io_request_table - + * post the request to the SCU hardware enum sci_status SCI_SUCCESS if the start io + * operation succeeds SCI_FAILURE_INSUFFICIENT_RESOURCES if the IO tag could + * not be allocated for the io request. SCI_FAILURE_INVALID_STATE if one or + * more objects are not in a valid state to accept io requests. How does the io + * tag get assigned in this code path? + */ +static enum sci_status scic_sds_controller_ready_state_start_task_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request, + u16 task_tag) +{ + struct scic_sds_controller *this_controller = (struct scic_sds_controller *) + controller; + struct scic_sds_request *the_request = (struct scic_sds_request *) + io_request; + struct scic_sds_remote_device *the_device = (struct scic_sds_remote_device *) + remote_device; + enum sci_status status; + + status = scic_sds_remote_device_start_task( + this_controller, the_device, the_request + ); + + if (status == SCI_SUCCESS) { + this_controller->io_request_table[ + scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; + + scic_sds_controller_post_request( + this_controller, + scic_sds_request_get_post_context(the_request) + ); + } else if (status == SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS) { + this_controller->io_request_table[ + scic_sds_io_tag_get_index(the_request->io_tag)] = the_request; + + /* + * We will let framework know this task request started successfully, + * although core is still woring on starting the request (to post tc when + * RNC is resumed.) */ + status = SCI_SUCCESS; + } + return status; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * + * This method is called when the struct scic_sds_controller is in the ready state and + * the terminate request handler is called. - call the io request terminate + * function - if successful - post the terminate request to the SCU hardware + * enum sci_status SCI_SUCCESS if the start io operation succeeds + * SCI_FAILURE_INVALID_STATE if one or more objects are not in a valid state to + * accept io requests. + */ +static enum sci_status scic_sds_controller_ready_state_terminate_request_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request) +{ + struct scic_sds_controller *this_controller = (struct scic_sds_controller *) + controller; + struct scic_sds_request *the_request = (struct scic_sds_request *) + io_request; + enum sci_status status; + + status = scic_sds_io_request_terminate(the_request); + if (status == SCI_SUCCESS) { + /* + * Utilize the original post context command and or in the POST_TC_ABORT + * request sub-type. */ + scic_sds_controller_post_request( + this_controller, + scic_sds_request_get_post_context(the_request) + | SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT + ); + } + + return status; +} + +/** + * + * @controller: This is struct scic_sds_controller which receives the link up + * notification. + * @port: This is struct scic_sds_port with which the phy is associated. + * @phy: This is the struct scic_sds_phy which has gone link up. + * + * This method is called when the struct scic_sds_controller is in the starting state + * link up handler is called. This method will perform the following: - Stop + * the phy timer - Start the next phy - Report the link up condition to the + * port object none + */ +static void scic_sds_controller_ready_state_link_up_handler( + struct scic_sds_controller *this_controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + this_controller->port_agent.link_up_handler( + this_controller, &this_controller->port_agent, port, phy + ); +} + +/** + * + * @controller: This is struct scic_sds_controller which receives the link down + * notification. + * @port: This is struct scic_sds_port with which the phy is associated. + * @phy: This is the struct scic_sds_phy which has gone link down. + * + * This method is called when the struct scic_sds_controller is in the starting state + * link down handler is called. - Report the link down condition to the port + * object none + */ +static void scic_sds_controller_ready_state_link_down_handler( + struct scic_sds_controller *this_controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + this_controller->port_agent.link_down_handler( + this_controller, &this_controller->port_agent, port, phy + ); +} + +/* + * ***************************************************************************** + * * STOPPING STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * + * This method is called when the struct scic_sds_controller is in a stopping state + * and the complete io handler is called. - This function is not yet + * implemented enum sci_status SCI_FAILURE + */ +static enum sci_status scic_sds_controller_stopping_state_complete_io_handler( + struct sci_base_controller *controller, + struct sci_base_remote_device *remote_device, + struct sci_base_request *io_request) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)controller; + + /* / @todo Implement this function */ + return SCI_FAILURE; +} + +/** + * + * @controller: This is struct sci_base_controller object which is cast into a + * struct scic_sds_controller object. + * @remote_device: This is struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device object. + * @io_request: This is the struct sci_base_request which is cast to a + * SCIC_SDS_IO_REQUEST object. + * + * This method is called when the struct scic_sds_controller is in a stopping state + * and the complete task handler is called. - This function is not yet + * implemented enum sci_status SCI_FAILURE + */ + +/* + * ***************************************************************************** + * * STOPPED STATE HANDLERS + * ***************************************************************************** */ + +/* + * ***************************************************************************** + * * FAILED STATE HANDLERS + * ***************************************************************************** */ + +const struct scic_sds_controller_state_handler scic_sds_controller_state_handler_table[] = { + [SCI_BASE_CONTROLLER_STATE_INITIAL] = { + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_RESET] = { + .base.initialize = scic_sds_controller_reset_state_initialize_handler, + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_INITIALIZING] = { + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_INITIALIZED] = { + .base.start = scic_sds_controller_initialized_state_start_handler, + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_STARTING] = { + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + .link_up = scic_sds_controller_starting_state_link_up_handler, + .link_down = scic_sds_controller_starting_state_link_down_handler + }, + [SCI_BASE_CONTROLLER_STATE_READY] = { + .base.stop = scic_sds_controller_ready_state_stop_handler, + .base.reset = scic_sds_controller_general_reset_handler, + .base.start_io = scic_sds_controller_ready_state_start_io_handler, + .base.complete_io = scic_sds_controller_ready_state_complete_io_handler, + .base.continue_io = scic_sds_controller_ready_state_continue_io_handler, + .base.start_task = scic_sds_controller_ready_state_start_task_handler, + .base.complete_task = scic_sds_controller_ready_state_complete_io_handler, + .terminate_request = scic_sds_controller_ready_state_terminate_request_handler, + .link_up = scic_sds_controller_ready_state_link_up_handler, + .link_down = scic_sds_controller_ready_state_link_down_handler + }, + [SCI_BASE_CONTROLLER_STATE_RESETTING] = { + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_STOPPING] = { + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_stopping_state_complete_io_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_STOPPED] = { + .base.reset = scic_sds_controller_general_reset_handler, + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, + [SCI_BASE_CONTROLLER_STATE_FAILED] = { + .base.reset = scic_sds_controller_general_reset_handler, + .base.start_io = scic_sds_controller_default_start_operation_handler, + .base.complete_io = scic_sds_controller_default_request_handler, + .base.continue_io = scic_sds_controller_default_request_handler, + .terminate_request = scic_sds_controller_default_request_handler, + }, +}; + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on entry + * to the SCI_BASE_CONTROLLER_STATE_INITIAL. - Set the state handlers to the + * controllers initial state. none This function should initialze the + * controller object. + */ +static void scic_sds_controller_initial_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)object; + + sci_base_state_machine_change_state( + &this_controller->parent.state_machine, SCI_BASE_CONTROLLER_STATE_RESET); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on exit + * from the SCI_BASE_CONTROLLER_STATE_STARTING. - This function stops the + * controller starting timeout timer. none + */ +static void scic_sds_controller_starting_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_controller *scic = (struct scic_sds_controller *)object; + + scic_cb_timer_stop(scic, scic->timeout_timer); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on entry + * to the SCI_BASE_CONTROLLER_STATE_READY. - Set the state handlers to the + * controllers ready state. none + */ +static void scic_sds_controller_ready_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)object; + + /* set the default interrupt coalescence number and timeout value. */ + scic_controller_set_interrupt_coalescence( + this_controller, 0x10, 250); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on exit + * from the SCI_BASE_CONTROLLER_STATE_READY. - This function does nothing. none + */ +static void scic_sds_controller_ready_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)object; + + /* disable interrupt coalescence. */ + scic_controller_set_interrupt_coalescence(this_controller, 0, 0); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on entry + * to the SCI_BASE_CONTROLLER_STATE_READY. - Set the state handlers to the + * controllers ready state. - Stop the phys on this controller - Stop the ports + * on this controller - Stop all of the remote devices on this controller none + */ +static void scic_sds_controller_stopping_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)object; + + /* Stop all of the components for this controller */ + scic_sds_controller_stop_phys(this_controller); + scic_sds_controller_stop_ports(this_controller); + scic_sds_controller_stop_devices(this_controller); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on exit + * from the SCI_BASE_CONTROLLER_STATE_STOPPING. - This function stops the + * controller stopping timeout timer. none + */ +static void scic_sds_controller_stopping_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)object; + + scic_cb_timer_stop(this_controller, this_controller->timeout_timer); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_controller + * object. + * + * This method implements the actions taken by the struct scic_sds_controller on entry + * to the SCI_BASE_CONTROLLER_STATE_RESETTING. - Set the state handlers to the + * controllers resetting state. - Write to the SCU hardware reset register to + * force a reset - Transition to the SCI_BASE_CONTROLLER_STATE_RESET none + */ +static void scic_sds_controller_resetting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_controller *this_controller; + + this_controller = (struct scic_sds_controller *)object; + + scic_sds_controller_reset_hardware(this_controller); + + sci_base_state_machine_change_state( + scic_sds_controller_get_base_state_machine(this_controller), + SCI_BASE_CONTROLLER_STATE_RESET + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_controller_state_table[] = { + [SCI_BASE_CONTROLLER_STATE_INITIAL] = { + .enter_state = scic_sds_controller_initial_state_enter, + }, + [SCI_BASE_CONTROLLER_STATE_RESET] = {}, + [SCI_BASE_CONTROLLER_STATE_INITIALIZING] = {}, + [SCI_BASE_CONTROLLER_STATE_INITIALIZED] = {}, + [SCI_BASE_CONTROLLER_STATE_STARTING] = { + .exit_state = scic_sds_controller_starting_state_exit, + }, + [SCI_BASE_CONTROLLER_STATE_READY] = { + .enter_state = scic_sds_controller_ready_state_enter, + .exit_state = scic_sds_controller_ready_state_exit, + }, + [SCI_BASE_CONTROLLER_STATE_RESETTING] = { + .enter_state = scic_sds_controller_resetting_state_enter, + }, + [SCI_BASE_CONTROLLER_STATE_STOPPING] = { + .enter_state = scic_sds_controller_stopping_state_enter, + .exit_state = scic_sds_controller_stopping_state_exit, + }, + [SCI_BASE_CONTROLLER_STATE_STOPPED] = {}, + [SCI_BASE_CONTROLLER_STATE_FAILED] = {} +}; + diff --git a/drivers/scsi/isci/core/scic_sds_controller.h b/drivers/scsi/isci/core/scic_sds_controller.h new file mode 100644 index 000000000000..afa45f9874a4 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_controller.h @@ -0,0 +1,706 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_CONTROLLER_H_ +#define _SCIC_SDS_CONTROLLER_H_ + +/** + * This file contains the structures, constants and prototypes used for the + * core controller object. + * + * + */ + +#include "sci_pool.h" +#include "sci_controller_constants.h" +#include "sci_memory_descriptor_list.h" +#include "sci_base_controller.h" +#include "scic_config_parameters.h" +#include "scic_sds_port.h" +#include "scic_sds_phy.h" +#include "scic_sds_remote_node_table.h" +#include "scu_registers.h" +#include "scu_constants.h" +#include "scu_remote_node_context.h" +#include "scu_task_context.h" +#include "scu_unsolicited_frame.h" +#include "scic_sds_unsolicited_frame_control.h" +#include "scic_sds_port_configuration_agent.h" +#include "scic_sds_pci.h" + +struct scic_sds_remote_device; +struct scic_sds_request; +struct scic_sds_controller; + + +#define SCU_COMPLETION_RAM_ALIGNMENT (64) + +/** + * enum SCIC_SDS_CONTROLLER_MEMORY_DESCRIPTORS - + * + * This enumeration depects the types of MDEs that are going to be created for + * the controller object. + */ +enum SCIC_SDS_CONTROLLER_MEMORY_DESCRIPTORS { + /** + * Completion queue MDE entry + */ + SCU_MDE_COMPLETION_QUEUE, + + /** + * Remote node context MDE entry + */ + SCU_MDE_REMOTE_NODE_CONTEXT, + + /** + * Task context MDE entry + */ + SCU_MDE_TASK_CONTEXT, + + /** + * Unsolicited frame buffer MDE entrys this is the start of the unsolicited + * frame buffer entries. + */ + SCU_MDE_UF_BUFFER, + + SCU_MAX_MDES +}; + +/** + * + * + * Allowed PORT configuration modes APC Automatic PORT configuration mode is + * defined by the OEM configuration parameters providing no PHY_MASK parameters + * for any PORT. i.e. There are no phys assigned to any of the ports at start. + * MPC Manual PORT configuration mode is defined by the OEM configuration + * parameters providing a PHY_MASK value for any PORT. It is assumed that any + * PORT with no PHY_MASK is an invalid port and not all PHYs must be assigned. + * A PORT_PHY mask that assigns just a single PHY to a port and no other PHYs + * being assigned is sufficient to declare manual PORT configuration. + */ +enum SCIC_PORT_CONFIGURATION_MODE { + SCIC_PORT_MANUAL_CONFIGURATION_MODE, + SCIC_PORT_AUTOMATIC_CONFIGURATION_MODE +}; + +/** + * struct scic_power_control - + * + * This structure defines the fields for managing power control for direct + * attached disk devices. + */ +struct scic_power_control { + /** + * This field is set when the power control timer is running and cleared when + * it is not. + */ + bool timer_started; + + /** + * This field is the handle to the driver timer object. This timer is used to + * control when the directed attached disks can consume power. + */ + void *timer; + + /** + * This field is used to keep track of how many phys are put into the + * requesters field. + */ + u8 phys_waiting; + + /** + * This field is an array of phys that we are waiting on. The phys are direct + * mapped into requesters via struct scic_sds_phy.phy_index + */ + struct scic_sds_phy *requesters[SCI_MAX_PHYS]; + +}; + +/** + * struct scic_sds_controller - + * + * This structure represents the SCU contoller object. + */ +struct scic_sds_controller { + /** + * The struct sci_base_controller is the parent object for the struct scic_sds_controller + * object. + */ + struct sci_base_controller parent; + + /** + * This field is the driver timer object handler used to time the controller + * object start and stop requests. + */ + void *timeout_timer; + + /** + * This field contains the user parameters to be utilized for this + * core controller object. + */ + union scic_user_parameters user_parameters; + + /** + * This field contains the OEM parameters to be utilized for this + * core controller object. + */ + union scic_oem_parameters oem_parameters; + + /** + * This field contains the port configuration agent for this controller. + */ + struct scic_sds_port_configuration_agent port_agent; + + /** + * This field is the array of port objects that are controlled by this + * controller object. There is one dummy port object also contained within + * this controller object. + */ + struct scic_sds_port port_table[SCI_MAX_PORTS + 1]; + + /** + * This field is the array of phy objects that are controlled by this + * controller object. + */ + struct scic_sds_phy phy_table[SCI_MAX_PHYS]; + + /** + * This field is the array of device objects that are currently constructed + * for this controller object. This table is used as a fast lookup of device + * objects that need to handle device completion notifications from the + * hardware. The table is RNi based. + */ + struct scic_sds_remote_device *device_table[SCI_MAX_REMOTE_DEVICES]; + + /** + * This field is the array of IO request objects that are currently active for + * this controller object. This table is used as a fast lookup of the io + * request object that need to handle completion queue notifications. The + * table is TCi based. + */ + struct scic_sds_request *io_request_table[SCI_MAX_IO_REQUESTS]; + + /** + * This field is the free RNi data structure + */ + struct scic_remote_node_table available_remote_nodes; + + /** + * This field is the TCi pool used to manage the task context index. + */ + SCI_POOL_CREATE(tci_pool, u16, SCI_MAX_IO_REQUESTS); + + /** + * This filed is the struct scic_power_control data used to controll when direct + * attached devices can consume power. + */ + struct scic_power_control power_control; + + /** + * This field is the array of sequence values for the IO Tag fields. Even + * though only 4 bits of the field is used for the sequence the sequence is 16 + * bits in size so the sequence can be bitwise or'd with the TCi to build the + * IO Tag value. + */ + u16 io_request_sequence[SCI_MAX_IO_REQUESTS]; + + /** + * This field in the array of sequence values for the RNi. These are used + * to control io request build to io request start operations. The sequence + * value is recorded into an io request when it is built and is checked on + * the io request start operation to make sure that there was not a device + * hot plug between the build and start operation. + */ + u8 remote_device_sequence[SCI_MAX_REMOTE_DEVICES]; + + /** + * This field is a pointer to the memory allocated by the driver for the task + * context table. This data is shared between the hardware and software. + */ + struct scu_task_context *task_context_table; + + /** + * This field is a pointer to the memory allocated by the driver for the + * remote node context table. This table is shared between the hardware and + * software. + */ + union scu_remote_node_context *remote_node_context_table; + + /** + * This field is the array of physical memory requiremets for this controller + * object. + */ + struct sci_physical_memory_descriptor memory_descriptors[SCU_MAX_MDES]; + + /** + * This field is a pointer to the completion queue. This memory is + * written to by the hardware and read by the software. + */ + u32 *completion_queue; + + /** + * This field is the software copy of the completion queue get pointer. The + * controller object writes this value to the hardware after processing the + * completion entries. + */ + u32 completion_queue_get; + + /** + * This field is the minimum of the number of hardware supported port entries + * and the software requested port entries. + */ + u32 logical_port_entries; + + /** + * This field is the minimum number of hardware supported completion queue + * entries and the software requested completion queue entries. + */ + u32 completion_queue_entries; + + /** + * This field is the minimum number of hardware supported event entries and + * the software requested event entries. + */ + u32 completion_event_entries; + + /** + * This field is the minimum number of devices supported by the hardware and + * the number of devices requested by the software. + */ + u32 remote_node_entries; + + /** + * This field is the minimum number of IO requests supported by the hardware + * and the number of IO requests requested by the software. + */ + u32 task_context_entries; + + /** + * This object contains all of the unsolicited frame specific + * data utilized by the core controller. + */ + struct scic_sds_unsolicited_frame_control uf_control; + + /** + * This field records the fact that the controller has encountered a fatal + * error and must be reset. + */ + bool encountered_fatal_error; + + /** + * This field specifies that the controller should ignore + * completion processing for non-fastpath events. This will + * cause the completions to be thrown away. + */ + bool restrict_completions; + + /* Phy Startup Data */ + /** + * This field is the driver timer handle for controller phy request startup. + * On controller start the controller will start each PHY individually in + * order of phy index. + */ + void *phy_startup_timer; + + /** + * This field is set when the phy_startup_timer is running and is cleared when + * the phy_startup_timer is stopped. + */ + bool phy_startup_timer_pending; + + /** + * This field is the index of the next phy start. It is initialized to 0 and + * increments for each phy index that is started. + */ + u32 next_phy_to_start; + + /** + * This field controlls the invalid link up notifications to the SCI_USER. If + * an invalid_link_up notification is reported a bit for the PHY index is set + * so further notifications are not made. Once the PHY object reports link up + * and is made part of a port then this bit for the PHY index is cleared. + */ + u8 invalid_phy_mask; + + /* + * This field saves the current interrupt coalescing number of the controller. + */ + u16 interrupt_coalesce_number; + + /* + * This field saves the current interrupt coalescing timeout value in microseconds. + */ + u32 interrupt_coalesce_timeout; + + /** + * This field is a pointer to the memory mapped register space for the + * struct smu_registers. + */ + struct smu_registers __iomem *smu_registers; + + /** + * This field is a pointer to the memory mapped register space for the + * struct scu_registers. + */ + struct scu_registers __iomem *scu_registers; + +}; + +typedef void (*scic_sds_controller_phy_handler_t)(struct scic_sds_controller *, + struct scic_sds_port *, + struct scic_sds_phy *); +/** + * struct scic_sds_controller_state_handler - + * + * This structure contains the SDS core specific definition for the state + * handlers. + */ +struct scic_sds_controller_state_handler { + struct sci_base_controller_state_handler base; + + sci_base_controller_request_handler_t terminate_request; + scic_sds_controller_phy_handler_t link_up; + scic_sds_controller_phy_handler_t link_down; +}; + +extern const struct scic_sds_controller_state_handler + scic_sds_controller_state_handler_table[]; +extern const struct sci_base_state scic_sds_controller_state_table[]; + +/** + * INCREMENT_QUEUE_GET() - + * + * This macro will increment the specified index to and if the index wraps to 0 + * it will toggel the cycle bit. + */ +#define INCREMENT_QUEUE_GET(index, cycle, entry_count, bit_toggle) \ + { \ + if ((index) + 1 == entry_count) { \ + (index) = 0; \ + (cycle) = (cycle) ^ (bit_toggle); \ + } else { \ + index = index + 1; \ + } \ + } + +/** + * scic_sds_controller_get_base_state_machine() - + * + * This is a helper macro that gets the base state machine for the controller + * object + */ +#define scic_sds_controller_get_base_state_machine(this_controller) \ + (&(this_controller)->parent.state_machine) + +/** + * scic_sds_controller_get_port_configuration_agent() - + * + * This is a helper macro to get the port configuration agent from the + * controller object. + */ +#define scic_sds_controller_get_port_configuration_agent(controller) \ + (&(controller)->port_agent) + +/** + * smu_register_write() - + * + * This macro writes to the smu_register for this controller + */ +#define smu_register_write(controller, reg, value) \ + scic_sds_pci_write_smu_dword((controller), &(reg), (value)) + +/** + * smu_register_read() - + * + * This macro reads the smu_register for this controller + */ +#define smu_register_read(controller, reg) \ + scic_sds_pci_read_smu_dword((controller), &(reg)) + +/** + * scu_register_write() - + * + * This mcaro writes the scu_register for this controller + */ +#define scu_register_write(controller, reg, value) \ + scic_sds_pci_write_scu_dword((controller), &(reg), (value)) + +/** + * scu_register_read() - + * + * This macro reads the scu_register for this controller + */ +#define scu_register_read(controller, reg) \ + scic_sds_pci_read_scu_dword((controller), &(reg)) + +/** + * scic_sds_controller_get_protocol_engine_group() - + * + * This macro returns the protocol engine group for this controller object. + * Presently we only support protocol engine group 0 so just return that + */ +#define scic_sds_controller_get_protocol_engine_group(controller) 0 + +/** + * scic_sds_io_tag_construct() - + * + * This macro constructs an IO tag from the sequence and index values. + */ +#define scic_sds_io_tag_construct(sequence, task_index) \ + ((sequence) << 12 | (task_index)) + +/** + * scic_sds_io_tag_get_sequence() - + * + * This macro returns the IO sequence from the IO tag value. + */ +#define scic_sds_io_tag_get_sequence(io_tag) \ + (((io_tag) & 0xF000) >> 12) + +/** + * scic_sds_io_tag_get_index() - + * + * This macro returns the TCi from the io tag value + */ +#define scic_sds_io_tag_get_index(io_tag) \ + ((io_tag) & 0x0FFF) + +/** + * scic_sds_io_sequence_increment() - + * + * This is a helper macro to increment the io sequence count. We may find in + * the future that it will be faster to store the sequence count in such a way + * as we dont perform the shift operation to build io tag values so therefore + * need a way to incrment them correctly + */ +#define scic_sds_io_sequence_increment(value) \ + ((value) = (((value) + 1) & 0x000F)) + +#define scic_sds_remote_device_node_count(device) \ + (\ + (\ + (device)->target_protocols.u.bits.attached_stp_target \ + && ((device)->is_direct_attached != true) \ + ) \ + ? SCU_STP_REMOTE_NODE_COUNT : SCU_SSP_REMOTE_NODE_COUNT \ + ) + +/** + * scic_sds_controller_set_invalid_phy() - + * + * This macro will set the bit in the invalid phy mask for this controller + * object. This is used to control messages reported for invalid link up + * notifications. + */ +#define scic_sds_controller_set_invalid_phy(controller, phy) \ + ((controller)->invalid_phy_mask |= (1 << (phy)->phy_index)) + +/** + * scic_sds_controller_clear_invalid_phy() - + * + * This macro will clear the bit in the invalid phy mask for this controller + * object. This is used to control messages reported for invalid link up + * notifications. + */ +#define scic_sds_controller_clear_invalid_phy(controller, phy) \ + ((controller)->invalid_phy_mask &= ~(1 << (phy)->phy_index)) + +/* --------------------------------------------------------------------------- */ + +u32 scic_sds_controller_get_object_size(void); + +/* --------------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------------- */ + +enum SCIC_PORT_CONFIGURATION_MODE scic_sds_controller_get_port_configuration_mode( + struct scic_sds_controller *this_controller); + +/* --------------------------------------------------------------------------- */ + +void scic_sds_controller_post_request( + struct scic_sds_controller *this_controller, + u32 request); + +/* --------------------------------------------------------------------------- */ + +void scic_sds_controller_release_frame( + struct scic_sds_controller *this_controller, + u32 frame_index); + +void scic_sds_controller_copy_sata_response( + void *response_buffer, + void *frame_header, + void *frame_buffer); + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_controller_allocate_remote_node_context( + struct scic_sds_controller *this_controller, + struct scic_sds_remote_device *the_device, + u16 *node_id); + +void scic_sds_controller_free_remote_node_context( + struct scic_sds_controller *this_controller, + struct scic_sds_remote_device *the_device, + u16 node_id); + +union scu_remote_node_context *scic_sds_controller_get_remote_node_context_buffer( + struct scic_sds_controller *this_controller, + u16 node_id); + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_request *scic_sds_controller_get_io_request_from_tag( + struct scic_sds_controller *this_controller, + u16 io_tag); + + +struct scu_task_context *scic_sds_controller_get_task_context_buffer( + struct scic_sds_controller *this_controller, + u16 io_tag); + +/* + * ***************************************************************************** + * * CORE CONTROLLER POWER CONTROL METHODS + * ***************************************************************************** */ + + +void scic_sds_controller_power_control_queue_insert( + struct scic_sds_controller *this_controller, + struct scic_sds_phy *the_phy); + +void scic_sds_controller_power_control_queue_remove( + struct scic_sds_controller *this_controller, + struct scic_sds_phy *the_phy); + +/* + * ***************************************************************************** + * * CORE CONTROLLER PHY MESSAGE PROCESSING + * ***************************************************************************** */ + +void scic_sds_controller_link_up( + struct scic_sds_controller *this_controller, + struct scic_sds_port *the_port, + struct scic_sds_phy *the_phy); + +void scic_sds_controller_link_down( + struct scic_sds_controller *this_controller, + struct scic_sds_port *the_port, + struct scic_sds_phy *the_phy); + +/* + * ***************************************************************************** + * * CORE CONTROLLER PRIVATE METHODS + * ***************************************************************************** */ + +enum sci_status scic_sds_controller_validate_memory_descriptor_table( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_ram_initialization( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_assign_task_entries( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_afe_initialization( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_enable_port_task_scheduler( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_initialize_completion_queue( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_initialize_unsolicited_frame_queue( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_phy_timer_stop( + struct scic_sds_controller *this_controller); + +enum sci_status scic_sds_controller_start_next_phy( + struct scic_sds_controller *this_controller); + +enum sci_status scic_sds_controller_stop_phys( + struct scic_sds_controller *this_controller); + +enum sci_status scic_sds_controller_stop_ports( + struct scic_sds_controller *this_controller); + +enum sci_status scic_sds_controller_stop_devices( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_copy_task_context( + struct scic_sds_controller *this_controller, + struct scic_sds_request *this_request); + +void scic_sds_controller_timeout_handler( + struct scic_sds_controller *controller); + +void scic_sds_controller_initialize_power_control( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_register_setup( + struct scic_sds_controller *this_controller); + +void scic_sds_controller_reset_hardware( + struct scic_sds_controller *this_controller); + + +void scic_sds_controller_initialize_phy_startup( + struct scic_sds_controller *this_controller); + +#endif /* _SCIC_SDS_CONTROLLER_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_controller_registers.h b/drivers/scsi/isci/core/scic_sds_controller_registers.h new file mode 100644 index 000000000000..b7bec92ee59c --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_controller_registers.h @@ -0,0 +1,463 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_CONTROLLER_REGISTERS_H_ +#define _SCIC_SDS_CONTROLLER_REGISTERS_H_ + +/** + * This file contains macros used to perform the register reads/writes to the + * SCU hardware. + * + * + */ + +#include "scu_registers.h" +#include "scic_sds_controller.h" + +/** + * scic_sds_controller_smu_register_read() - + * + * SMU_REGISTER_ACCESS_MACROS + */ +#define scic_sds_controller_smu_register_read(controller, reg) \ + smu_register_read(\ + (controller), \ + (controller)->smu_registers->reg \ + ) + +#define scic_sds_controller_smu_register_write(controller, reg, value) \ + smu_register_write(\ + (controller), \ + (controller)->smu_registers->reg, \ + (value) \ + ) + +/** + * scu_afe_register_write() - + * + * AFE_REGISTER_ACCESS_MACROS + */ +#define scu_afe_register_write(controller, reg, value) \ + scu_register_write(\ + (controller), \ + (controller)->scu_registers->afe.reg, \ + (value) \ + ) + +#define scu_afe_txreg_write(controller, phy, reg, value) \ + scu_register_write(\ + (controller), \ + (controller)->scu_registers->afe.scu_afe_xcvr[phy].reg,\ + (value) \ + ) + +#define scu_afe_register_read(controller, reg) \ + scu_register_read(\ + (controller), \ + (controller)->scu_registers->afe.reg \ + ) + +/** + * scu_controller_viit_register_write() - + * + * VIIT_REGISTER_ACCESS_MACROS + */ +#define scu_controller_viit_register_write(controller, index, reg, value) \ + scu_register_write(\ + (controller), \ + (controller)->scu_registers->peg0.viit[index].reg, \ + value \ + ) + +/* + * ***************************************************************************** + * * SMU REGISTERS + * ***************************************************************************** */ + +/** + * SMU_PCP_WRITE() - + * + * struct smu_registers + */ +#define SMU_PCP_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, post_context_port, value \ + ) + +#define SMU_TCR_READ(controller, value) \ + scic_sds_controller_smu_register_read(\ + controller, task_context_range \ + ) + +#define SMU_TCR_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, task_context_range, value \ + ) + +#define SMU_HTTBAR_WRITE(controller, address) \ + { \ + scic_sds_controller_smu_register_write(\ + controller, \ + host_task_table_lower, \ + lower_32_bits(address) \ + ); \ + scic_sds_controller_smu_register_write(\ + controller, \ + host_task_table_upper, \ + upper_32_bits(address) \ + ); \ + } + +#define SMU_CQBAR_WRITE(controller, address) \ + { \ + scic_sds_controller_smu_register_write(\ + controller, \ + completion_queue_lower, \ + lower_32_bits(address) \ + ); \ + scic_sds_controller_smu_register_write(\ + controller, \ + completion_queue_upper, \ + upper_32_bits(address) \ + ); \ + } + +#define SMU_CQGR_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, completion_queue_get, value \ + ) + +#define SMU_CQGR_READ(controller, value) \ + scic_sds_controller_smu_register_read(\ + controller, completion_queue_get \ + ) + +#define SMU_CQPR_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, completion_queue_put, value \ + ) + +#define SMU_RNCBAR_WRITE(controller, address) \ + { \ + scic_sds_controller_smu_register_write(\ + controller, \ + remote_node_context_lower, \ + lower_32_bits(address) \ + ); \ + scic_sds_controller_smu_register_write(\ + controller, \ + remote_node_context_upper, \ + upper_32_bits(address) \ + ); \ + } + +#define SMU_AMR_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, address_modifier \ + ) + +#define SMU_IMR_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, interrupt_mask \ + ) + +#define SMU_IMR_WRITE(controller, mask) \ + scic_sds_controller_smu_register_write(\ + controller, interrupt_mask, mask \ + ) + +#define SMU_ISR_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, interrupt_status \ + ) + +#define SMU_ISR_WRITE(controller, status) \ + scic_sds_controller_smu_register_write(\ + controller, interrupt_status, status \ + ) + +#define SMU_ICC_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, interrupt_coalesce_control \ + ) + +#define SMU_ICC_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, interrupt_coalesce_control, value \ + ) + +#define SMU_CQC_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, completion_queue_control, value \ + ) + +#define SMU_SMUSRCR_WRITE(controller, value) \ + scic_sds_controller_smu_register_write(\ + controller, soft_reset_control, value \ + ) + +#define SMU_TCA_WRITE(controller, index, value) \ + scic_sds_controller_smu_register_write(\ + controller, task_context_assignment[index], value \ + ) + +#define SMU_TCA_READ(controller, index) \ + scic_sds_controller_smu_register_read(\ + controller, task_context_assignment[index] \ + ) + +#define SMU_DCC_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, device_context_capacity \ + ) + +#define SMU_DFC_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, device_function_capacity \ + ) + +#define SMU_SMUCSR_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, control_status \ + ) + +#define SMU_CQPR_READ(controller) \ + scic_sds_controller_smu_register_read(\ + controller, completion_queue_put \ + ) + + +/** + * scic_sds_controller_scu_register_read() - + * + * SCU_REGISTER_ACCESS_MACROS + */ +#define scic_sds_controller_scu_register_read(controller, reg) \ + scu_register_read(\ + (controller), \ + (controller)->scu_registers->reg \ + ) + +#define scic_sds_controller_scu_register_write(controller, reg, value) \ + scu_register_write(\ + (controller), \ + (controller)->scu_registers->reg, \ + (value) \ + ) + + +/* + * **************************************************************************** + * * SCU SDMA REGISTERS + * **************************************************************************** */ + +/** + * scu_sdma_register_read() - + * + * SCU_SDMA_REGISTER_ACCESS_MACROS + */ +#define scu_sdma_register_read(controller, reg) \ + scu_register_read(\ + (controller), \ + (controller)->scu_registers->sdma.reg \ + ) + +#define scu_sdma_register_write(controller, reg, value) \ + scu_register_write(\ + (controller), \ + (controller)->scu_registers->sdma.reg, \ + (value) \ + ) + +/** + * SCU_PUFATHAR_WRITE() - + * + * struct scu_sdma_registers + */ +#define SCU_PUFATHAR_WRITE(controller, address) \ + { \ + scu_sdma_register_write(\ + controller, \ + uf_address_table_lower, \ + lower_32_bits(address) \ + ); \ + scu_sdma_register_write(\ + controller, \ + uf_address_table_upper, \ + upper_32_bits(address) \ + ); \ + } + +#define SCU_UFHBAR_WRITE(controller, address) \ + { \ + scu_sdma_register_write(\ + controller, \ + uf_header_base_address_lower, \ + lower_32_bits(address) \ + ); \ + scu_sdma_register_write(\ + controller, \ + uf_header_base_address_upper, \ + upper_32_bits(address) \ + ); \ + } + +#define SCU_UFQC_READ(controller) \ + scu_sdma_register_read(\ + controller, \ + unsolicited_frame_queue_control \ + ) + +#define SCU_UFQC_WRITE(controller, value) \ + scu_sdma_register_write(\ + controller, \ + unsolicited_frame_queue_control, \ + value \ + ) + +#define SCU_UFQPP_READ(controller) \ + scu_sdma_register_read(\ + controller, \ + unsolicited_frame_put_pointer \ + ) + +#define SCU_UFQPP_WRITE(controller, value) \ + scu_sdma_register_write(\ + controller, \ + unsolicited_frame_put_pointer, \ + value \ + ) + +#define SCU_UFQGP_WRITE(controller, value) \ + scu_sdma_register_write(\ + controller, \ + unsolicited_frame_get_pointer, \ + value \ + ) + +#define SCU_PDMACR_READ(controller) \ + scu_sdma_register_read(\ + controller, \ + pdma_configuration \ + ) + +#define SCU_PDMACR_WRITE(controller, value) \ + scu_sdma_register_write(\ + controller, \ + pdma_configuration, \ + value \ + ) + +#define SCU_CDMACR_READ(controller) \ + scu_sdma_register_read(\ + controller, \ + cdma_configuration \ + ) + +#define SCU_CDMACR_WRITE(controller, value) \ + scu_sdma_register_write(\ + controller, \ + cdma_configuration, \ + value \ + ) + +/* + * ***************************************************************************** + * * SCU Port Task Scheduler Group Registers + * ***************************************************************************** */ + +/** + * scu_ptsg_register_read() - + * + * SCU_PTSG_REGISTER_ACCESS_MACROS + */ +#define scu_ptsg_register_read(controller, reg) \ + scu_register_read(\ + (controller), \ + (controller)->scu_registers->peg0.ptsg.reg \ + ) + +#define scu_ptsg_register_write(controller, reg, value) \ + scu_register_write(\ + (controller), \ + (controller)->scu_registers->peg0.ptsg.reg, \ + (value) \ + ) + +/** + * SCU_PTSGCR_READ() - + * + * SCU_PTSG_REGISTERS + */ +#define SCU_PTSGCR_READ(controller) \ + scu_ptsg_register_read(\ + (controller), \ + control \ + ) + +#define SCU_PTSGCR_WRITE(controller, value) \ + scu_ptsg_register_write(\ + (controller), \ + control, \ + value \ + ) + +#define SCU_PTSGRTC_READ(controller) \ + scu_ptsg_register_read(\ + contoller, \ + real_time_clock \ + ) + +#endif /* _SCIC_SDS_CONTROLLER_REGISTERS_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_pci.h b/drivers/scsi/isci/core/scic_sds_pci.h new file mode 100644 index 000000000000..21326777acc6 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_pci.h @@ -0,0 +1,95 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_PCI_H_ +#define _SCIC_SDS_PCI_H_ + +/** + * This file contains the prototypes/macros utilized in writing out PCI data + * for the SCI core. + * + * + */ + +#include <asm/io.h> +#include "sci_types.h" + +struct scic_sds_controller; + +void scic_sds_pci_bar_initialization(struct scic_sds_controller *scic); + +/* for debug we separate scu and smu accesses and require a controller */ +static inline u32 scic_sds_pci_read_smu_dword(struct scic_sds_controller *scic, void __iomem *addr) +{ + return readl(addr); +} + +static inline void scic_sds_pci_write_smu_dword(struct scic_sds_controller *scic, void __iomem *addr, u32 value) +{ + writel(value, addr); +} + +static inline u32 scic_sds_pci_read_scu_dword(struct scic_sds_controller *scic, void __iomem *addr) +{ + return readl(addr); +} + +static inline void scic_sds_pci_write_scu_dword(struct scic_sds_controller *scic, void __iomem *addr, u32 value) +{ + writel(value, addr); +} + + +#endif /* _SCIC_SDS_PCI_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_phy.c b/drivers/scsi/isci/core/scic_sds_phy.c new file mode 100644 index 000000000000..7d012b571b5b --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_phy.c @@ -0,0 +1,2807 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "intel_ata.h" +#include "intel_sata.h" +#include "sci_base_state.h" +#include "sci_base_state_machine.h" +#include "scic_phy.h" +#include "scic_sds_controller.h" +#include "scic_sds_phy.h" +#include "scic_sds_phy_registers.h" +#include "scic_sds_port.h" +#include "scic_user_callback.h" +#include "sci_environment.h" +#include "sci_util.h" +#include "scu_event_codes.h" + +#define SCIC_SDS_PHY_MIN_TIMER_COUNT (SCI_MAX_PHYS) +#define SCIC_SDS_PHY_MAX_TIMER_COUNT (SCI_MAX_PHYS) + +/* Maximum arbitration wait time in micro-seconds */ +#define SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME (700) + +/* + * ***************************************************************************** + * * SCIC SDS PHY Internal Methods + * ***************************************************************************** */ + +/** + * This method will initialize the phy link layer registers + * @this_phy: + * @link_layer_registers: + * + * enum sci_status + */ +static enum sci_status scic_sds_phy_link_layer_initialization( + struct scic_sds_phy *this_phy, + struct scu_link_layer_registers *link_layer_registers) +{ + u32 phy_configuration; + struct sas_capabilities phy_capabilities; + u32 parity_check = 0; + u32 parity_count = 0; + u32 link_layer_control; + + this_phy->link_layer_registers = link_layer_registers; + + /* Set our IDENTIFY frame data */ + #define SCI_END_DEVICE 0x01 + + SCU_SAS_TIID_WRITE( + this_phy, + (SCU_SAS_TIID_GEN_BIT(SMP_INITIATOR) + | SCU_SAS_TIID_GEN_BIT(SSP_INITIATOR) + | SCU_SAS_TIID_GEN_BIT(STP_INITIATOR) + | SCU_SAS_TIID_GEN_BIT(DA_SATA_HOST) + | SCU_SAS_TIID_GEN_VAL(DEVICE_TYPE, SCI_END_DEVICE)) + ); + + /* Write the device SAS Address */ + SCU_SAS_TIDNH_WRITE(this_phy, 0xFEDCBA98); + SCU_SAS_TIDNL_WRITE(this_phy, this_phy->phy_index); + + /* Write the source SAS Address */ + SCU_SAS_TISSAH_WRITE( + this_phy, + this_phy->owning_port->owning_controller->oem_parameters.sds1.phys[ + this_phy->phy_index].sas_address.high + ); + SCU_SAS_TISSAL_WRITE( + this_phy, + this_phy->owning_port->owning_controller->oem_parameters.sds1.phys[ + this_phy->phy_index].sas_address.low + ); + + /* Clear and Set the PHY Identifier */ + SCU_SAS_TIPID_WRITE(this_phy, 0x00000000); + SCU_SAS_TIPID_WRITE(this_phy, SCU_SAS_TIPID_GEN_VALUE(ID, this_phy->phy_index)); + + /* Change the initial state of the phy configuration register */ + phy_configuration = SCU_SAS_PCFG_READ(this_phy); + + /* Hold OOB state machine in reset */ + phy_configuration |= SCU_SAS_PCFG_GEN_BIT(OOB_RESET); + SCU_SAS_PCFG_WRITE(this_phy, phy_configuration); + + /* Configure the SNW capabilities */ + phy_capabilities.u.all = 0; + phy_capabilities.u.bits.start = 1; + phy_capabilities.u.bits.gen3_without_ssc_supported = 1; + phy_capabilities.u.bits.gen2_without_ssc_supported = 1; + phy_capabilities.u.bits.gen1_without_ssc_supported = 1; + if (this_phy->owning_port->owning_controller->oem_parameters.sds1. + controller.do_enable_ssc == true) { + phy_capabilities.u.bits.gen3_with_ssc_supported = 1; + phy_capabilities.u.bits.gen2_with_ssc_supported = 1; + phy_capabilities.u.bits.gen1_with_ssc_supported = 1; + } + + /* + * The SAS specification indicates that the phy_capabilities that + * are transmitted shall have an even parity. Calculate the parity. */ + parity_check = phy_capabilities.u.all; + while (parity_check != 0) { + if (parity_check & 0x1) + parity_count++; + parity_check >>= 1; + } + + /* + * If parity indicates there are an odd number of bits set, then + * set the parity bit to 1 in the phy capabilities. */ + if ((parity_count % 2) != 0) + phy_capabilities.u.bits.parity = 1; + + SCU_SAS_PHYCAP_WRITE(this_phy, phy_capabilities.u.all); + + /* Set the enable spinup period but disable the ability to send notify enable spinup */ + SCU_SAS_ENSPINUP_WRITE(this_phy, SCU_ENSPINUP_GEN_VAL(COUNT, 0x33)); + +#if defined(CONFIG_PBG_HBA_A0) || defined(CONFIG_PBG_HBA_A2) || defined(CONFIG_PBG_HBA_BETA) + /* / @todo Provide a way to write this register correctly */ + scu_link_layer_register_write(this_phy, afe_lookup_table_control, 0x02108421); +#else + /* / @todo Provide a way to write this register correctly */ + scu_link_layer_register_write(this_phy, afe_lookup_table_control, 0x0e739ce7); +#endif + + link_layer_control = SCU_SAS_LLCTL_GEN_VAL( + NO_OUTBOUND_TASK_TIMEOUT, + (u8)this_phy->owning_port->owning_controller-> + user_parameters.sds1.no_outbound_task_timeout + ); + +/* #define COMPILED_MAX_LINK_RATE SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN1 */ +/* #define COMPILED_MAX_LINK_RATE SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN2 */ +#define COMPILED_MAX_LINK_RATE SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN3 + + if (this_phy->owning_port->owning_controller->user_parameters.sds1. + phys[this_phy->phy_index].max_speed_generation == SCIC_SDS_PARM_GEN3_SPEED) { + link_layer_control |= SCU_SAS_LLCTL_GEN_VAL( + MAX_LINK_RATE, COMPILED_MAX_LINK_RATE + ); + } else if (this_phy->owning_port->owning_controller->user_parameters.sds1. + phys[this_phy->phy_index].max_speed_generation == SCIC_SDS_PARM_GEN2_SPEED) { + link_layer_control |= SCU_SAS_LLCTL_GEN_VAL( + MAX_LINK_RATE, + min( + SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN2, + COMPILED_MAX_LINK_RATE) + ); + } else { + link_layer_control |= SCU_SAS_LLCTL_GEN_VAL( + MAX_LINK_RATE, + min( + SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN1, + COMPILED_MAX_LINK_RATE) + ); + } + + scu_link_layer_register_write( + this_phy, link_layer_control, link_layer_control + ); + + /* + * Program the max ARB time for the PHY to 700us so we inter-operate with + * the PMC expander which shuts down PHYs if the expander PHY generates too + * many breaks. This time value will guarantee that the initiator PHY will + * generate the break. */ +#if defined(CONFIG_PBG_HBA_A0) || defined(CONFIG_PBG_HBA_A2) + scu_link_layer_register_write( + this_phy, + maximum_arbitration_wait_timer_timeout, + SCIC_SDS_PHY_MAX_ARBITRATION_WAIT_TIME + ); +#endif /* defined(CONFIG_PBG_HBA_A0) || defined(CONFIG_PBG_HBA_A2) */ + + /* + * Set the link layer hang detection to 500ms (0x1F4) from its default + * value of 128ms. Max value is 511 ms. */ + scu_link_layer_register_write( + this_phy, link_layer_hang_detection_timeout, 0x1F4 + ); + + /* We can exit the initial state to the stopped state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_STOPPED + ); + + return SCI_SUCCESS; +} + +/** + * This function will handle the sata SIGNATURE FIS timeout condition. It will + * restart the starting substate machine since we dont know what has actually + * happening. + */ +static void scic_sds_phy_sata_timeout(void *phy) +{ + struct scic_sds_phy *sci_phy = phy; + + dev_dbg(sciphy_to_dev(sci_phy), + "%s: SCIC SDS Phy 0x%p did not receive signature fis before " + "timeout.\n", + __func__, + sci_phy); + + sci_base_state_machine_stop( + scic_sds_phy_get_starting_substate_machine(sci_phy)); + + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(sci_phy), + SCI_BASE_PHY_STATE_STARTING + ); +} + +/* + * ***************************************************************************** + * * SCIC SDS PHY External Methods + * ***************************************************************************** */ + +/** + * This method returns the object size for a phy object. + * + * u32 + */ + +/** + * This method returns the minimum number of timers required for a phy object. + * + * u32 + */ + +/** + * This method returns the maximum number of timers required for a phy object. + * + * u32 + */ + +#ifdef SCIC_DEBUG_ENABLED +/** + * scic_sds_phy_observe_state_change() - + * @our_observer: + * + * Debug code to record the state transitions in the phy + */ +void scic_sds_phy_observe_state_change( + struct sci_base_observer *our_observer, + struct sci_base_subject *the_subject) +{ + struct scic_sds_phy *this_phy; + struct sci_base_state_machine *the_state_machine; + + u8 transition_requestor; + u32 base_state_id; + u32 starting_substate_id; + + the_state_machine = (struct sci_base_state_machine *)the_subject; + this_phy = (struct scic_sds_phy *)the_state_machine->state_machine_owner; + + if (the_state_machine == &this_phy->parent.state_machine) { + transition_requestor = 0x01; + } else if (the_state_machine == &this_phy->starting_substate_machine) { + transition_requestor = 0x02; + } else { + transition_requestor = 0xFF; + } + + base_state_id = + sci_base_state_machine_get_state(&this_phy->parent.state_machine); + starting_substate_id = + sci_base_state_machine_get_state(&this_phy->starting_substate_machine); + + this_phy->state_record.state_transition_table[ + this_phy->state_record.index++] = ((transition_requestor << 24) + | ((u8)base_state_id << 8) + | ((u8)starting_substate_id)); + + this_phy->state_record.index = + this_phy->state_record.index & (MAX_STATE_TRANSITION_RECORD - 1); + +} +#endif /* SCIC_DEBUG_ENABLED */ + +#ifdef SCIC_DEBUG_ENABLED +/** + * scic_sds_phy_initialize_state_recording() - + * + * This method initializes the state record debug information for the phy + * object. The state machines for the phy object must be constructed before + * this function is called. + */ +void scic_sds_phy_initialize_state_recording( + struct scic_sds_phy *this_phy) +{ + this_phy->state_record.index = 0; + + sci_base_observer_initialize( + &this_phy->state_record.base_state_observer, + scic_sds_phy_observe_state_change, + &this_phy->parent.state_machine.parent + ); + + sci_base_observer_initialize( + &this_phy->state_record.starting_state_observer, + scic_sds_phy_observe_state_change, + &this_phy->starting_substate_machine.parent + ); +} +#endif /* SCIC_DEBUG_ENABLED */ + +/** + * This method will construct the struct scic_sds_phy object + * @this_phy: + * @owning_port: + * @phy_index: + * + */ +void scic_sds_phy_construct( + struct scic_sds_phy *this_phy, + struct scic_sds_port *owning_port, + u8 phy_index) +{ + /* + * Call the base constructor first + */ + sci_base_phy_construct( + &this_phy->parent, + scic_sds_phy_state_table + ); + + /* Copy the rest of the input data to our locals */ + this_phy->owning_port = owning_port; + this_phy->phy_index = phy_index; + this_phy->bcn_received_while_port_unassigned = false; + this_phy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN; + this_phy->link_layer_registers = NULL; + this_phy->max_negotiated_speed = SCI_SAS_NO_LINK_RATE; + + /* Clear out the identification buffer data */ + memset(&this_phy->phy_type, 0, sizeof(this_phy->phy_type)); + + /* Initialize the the substate machines */ + sci_base_state_machine_construct( + &this_phy->starting_substate_machine, + &this_phy->parent.parent, + scic_sds_phy_starting_substates, + SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL + ); + + #ifdef SCIC_DEBUG_ENABLED + scic_sds_phy_initialize_state_recording(this_phy); + #endif /* SCIC_DEBUG_ENABLED */ +} + +/** + * This method returns the port currently containing this phy. If the phy is + * currently contained by the dummy port, then the phy is considered to not + * be part of a port. + * @this_phy: This parameter specifies the phy for which to retrieve the + * containing port. + * + * This method returns a handle to a port that contains the supplied phy. + * SCI_INVALID_HANDLE This value is returned if the phy is not part of a real + * port (i.e. it's contained in the dummy port). !SCI_INVALID_HANDLE All other + * values indicate a handle/pointer to the port containing the phy. + */ +struct scic_sds_port *scic_sds_phy_get_port( + struct scic_sds_phy *this_phy) +{ + if (scic_sds_port_get_index(this_phy->owning_port) == SCIC_SDS_DUMMY_PORT) + return SCI_INVALID_HANDLE; + + return this_phy->owning_port; +} + +/** + * This method will assign a port to the phy object. + * @out]: this_phy This parameter specifies the phy for which to assign a port + * object. + * + * + */ +void scic_sds_phy_set_port( + struct scic_sds_phy *this_phy, + struct scic_sds_port *the_port) +{ + this_phy->owning_port = the_port; + + if (this_phy->bcn_received_while_port_unassigned) { + this_phy->bcn_received_while_port_unassigned = false; + scic_sds_port_broadcast_change_received(this_phy->owning_port, this_phy); + } +} + +/** + * This method will initialize the constructed phy + * @sci_phy: + * @link_layer_registers: + * + * enum sci_status + */ +enum sci_status scic_sds_phy_initialize( + struct scic_sds_phy *sci_phy, + struct scu_link_layer_registers *link_layer_registers) +{ + /* Create the SIGNATURE FIS Timeout timer for this phy */ + sci_phy->sata_timeout_timer = scic_cb_timer_create( + scic_sds_phy_get_controller(sci_phy), + scic_sds_phy_sata_timeout, + sci_phy + ); + + /* Perofrm the initialization of the PE hardware */ + scic_sds_phy_link_layer_initialization(sci_phy, link_layer_registers); + + /* + * There is nothing that needs to be done in this state just + * transition to the stopped state. */ + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(sci_phy), + SCI_BASE_PHY_STATE_STOPPED + ); + + return SCI_SUCCESS; +} + + +/** + * + * @this_phy: The phy object to be suspended. + * + * This function will perform the register reads/writes to suspend the SCU + * hardware protocol engine. none + */ +void scic_sds_phy_suspend( + struct scic_sds_phy *this_phy) +{ + u32 scu_sas_pcfg_value; + + scu_sas_pcfg_value = SCU_SAS_PCFG_READ(this_phy); + + scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(SUSPEND_PROTOCOL_ENGINE); + + SCU_SAS_PCFG_WRITE(this_phy, scu_sas_pcfg_value); +} + +/** + * + * @this_phy: The phy object to resume. + * + * This function will perform the register reads/writes required to resume the + * SCU hardware protocol engine. none + */ +void scic_sds_phy_resume( + struct scic_sds_phy *this_phy) +{ + u32 scu_sas_pcfg_value; + + scu_sas_pcfg_value = SCU_SAS_PCFG_READ(this_phy); + + scu_sas_pcfg_value &= ~SCU_SAS_PCFG_GEN_BIT(SUSPEND_PROTOCOL_ENGINE); + + SCU_SAS_PCFG_WRITE(this_phy, scu_sas_pcfg_value); +} + +/** + * This method returns the local sas address assigned to this phy. + * @this_phy: This parameter specifies the phy for which to retrieve the local + * SAS address. + * @sas_address: This parameter specifies the location into which to copy the + * local SAS address. + * + */ +void scic_sds_phy_get_sas_address( + struct scic_sds_phy *this_phy, + struct sci_sas_address *sas_address) +{ + sas_address->high = SCU_SAS_TISSAH_READ(this_phy); + sas_address->low = SCU_SAS_TISSAL_READ(this_phy); +} + +/** + * This method returns the remote end-point (i.e. attached) sas address + * assigned to this phy. + * @this_phy: This parameter specifies the phy for which to retrieve the remote + * end-point SAS address. + * @sas_address: This parameter specifies the location into which to copy the + * remote end-point SAS address. + * + */ +void scic_sds_phy_get_attached_sas_address( + struct scic_sds_phy *this_phy, + struct sci_sas_address *sas_address) +{ + sas_address->high + = this_phy->phy_type.sas.identify_address_frame_buffer.sas_address.high; + sas_address->low + = this_phy->phy_type.sas.identify_address_frame_buffer.sas_address.low; +} + +/** + * This method returns the supported protocols assigned to this phy + * @this_phy: + * + * + */ +void scic_sds_phy_get_protocols( + struct scic_sds_phy *this_phy, + struct sci_sas_identify_address_frame_protocols *protocols) +{ + protocols->u.all = (u16)(SCU_SAS_TIID_READ(this_phy) & 0x0000FFFF); +} + +/** + * + * @this_phy: The parameter is the phy object for which the attached phy + * protcols are to be returned. + * + * This method returns the supported protocols for the attached phy. If this + * is a SAS phy the protocols are returned from the identify address frame. If + * this is a SATA phy then protocols are made up and the target phy is an STP + * target phy. The caller will get the entire set of bits for the protocol + * value. + */ +void scic_sds_phy_get_attached_phy_protocols( + struct scic_sds_phy *this_phy, + struct sci_sas_identify_address_frame_protocols *protocols) +{ + protocols->u.all = 0; + + if (this_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + protocols->u.all = + this_phy->phy_type.sas.identify_address_frame_buffer.protocols.u.all; + } else if (this_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) { + protocols->u.bits.stp_target = 1; + } +} + +/* + * ***************************************************************************** + * * SCIC SDS PHY Handler Redirects + * ***************************************************************************** */ + +/** + * This method will attempt to start the phy object. This request is only valid + * when the phy is in the stopped state + * @this_phy: + * + * enum sci_status + */ +enum sci_status scic_sds_phy_start( + struct scic_sds_phy *this_phy) +{ + return this_phy->state_handlers->parent.start_handler(&this_phy->parent); +} + +/** + * This method will attempt to stop the phy object. + * @this_phy: + * + * enum sci_status SCI_SUCCESS if the phy is going to stop SCI_INVALID_STATE if the + * phy is not in a valid state to stop + */ +enum sci_status scic_sds_phy_stop( + struct scic_sds_phy *this_phy) +{ + return this_phy->state_handlers->parent.stop_handler(&this_phy->parent); +} + +/** + * This method will attempt to reset the phy. This request is only valid when + * the phy is in an ready state + * @this_phy: + * + * enum sci_status + */ +enum sci_status scic_sds_phy_reset( + struct scic_sds_phy *this_phy) +{ + return this_phy->state_handlers->parent.reset_handler( + &this_phy->parent + ); +} + +/** + * This method will process the event code received. + * @this_phy: + * @event_code: + * + * enum sci_status + */ +enum sci_status scic_sds_phy_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + return this_phy->state_handlers->event_handler(this_phy, event_code); +} + +/** + * This method will process the frame index received. + * @this_phy: + * @frame_index: + * + * enum sci_status + */ +enum sci_status scic_sds_phy_frame_handler( + struct scic_sds_phy *this_phy, + u32 frame_index) +{ + return this_phy->state_handlers->frame_handler(this_phy, frame_index); +} + +/** + * This method will give the phy permission to consume power + * @this_phy: + * + * enum sci_status + */ +enum sci_status scic_sds_phy_consume_power_handler( + struct scic_sds_phy *this_phy) +{ + return this_phy->state_handlers->consume_power_handler(this_phy); +} + +/* + * ***************************************************************************** + * * SCIC PHY Public Methods + * ***************************************************************************** */ + + +enum sci_status scic_sas_phy_get_properties( + struct scic_sds_phy *sci_phy, + struct scic_sas_phy_properties *properties) +{ + if (sci_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + memcpy( + &properties->received_iaf, + &sci_phy->phy_type.sas.identify_address_frame_buffer, + sizeof(struct sci_sas_identify_address_frame) + ); + + properties->received_capabilities.u.all + = SCU_SAS_RECPHYCAP_READ(sci_phy); + + return SCI_SUCCESS; + } + + return SCI_FAILURE; +} + + +enum sci_status scic_sata_phy_get_properties( + struct scic_sds_phy *sci_phy, + struct scic_sata_phy_properties *properties) +{ + if (sci_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SATA) { + memcpy( + &properties->signature_fis, + &sci_phy->phy_type.sata.signature_fis_buffer, + sizeof(struct sata_fis_reg_d2h) + ); + + /* / @todo add support for port selectors. */ + properties->is_port_selector_present = false; + + return SCI_SUCCESS; + } + + return SCI_FAILURE; +} + +/* + * ***************************************************************************** + * * SCIC SDS PHY HELPER FUNCTIONS + * ***************************************************************************** */ + + +/** + * + * @this_phy: The phy object that received SAS PHY DETECTED. + * + * This method continues the link training for the phy as if it were a SAS PHY + * instead of a SATA PHY. This is done because the completion queue had a SAS + * PHY DETECTED event when the state machine was expecting a SATA PHY event. + * none + */ +static void scic_sds_phy_start_sas_link_training( + struct scic_sds_phy *this_phy) +{ + u32 phy_control; + + phy_control = SCU_SAS_PCFG_READ(this_phy); + phy_control |= SCU_SAS_PCFG_GEN_BIT(SATA_SPINUP_HOLD); + SCU_SAS_PCFG_WRITE(this_phy, phy_control); + + sci_base_state_machine_change_state( + &this_phy->starting_substate_machine, + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN + ); + + this_phy->protocol = SCIC_SDS_PHY_PROTOCOL_SAS; +} + +/** + * + * @this_phy: The phy object that received a SATA SPINUP HOLD event + * + * This method continues the link training for the phy as if it were a SATA PHY + * instead of a SAS PHY. This is done because the completion queue had a SATA + * SPINUP HOLD event when the state machine was expecting a SAS PHY event. none + */ +static void scic_sds_phy_start_sata_link_training( + struct scic_sds_phy *this_phy) +{ + sci_base_state_machine_change_state( + &this_phy->starting_substate_machine, + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER + ); + + this_phy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA; +} + +/** + * This method performs processing common to all protocols upon completion of + * link training. + * @this_phy: This parameter specifies the phy object for which link training + * has completed. + * @max_link_rate: This parameter specifies the maximum link rate to be + * associated with this phy. + * @next_state: This parameter specifies the next state for the phy's starting + * sub-state machine. + * + */ +static void scic_sds_phy_complete_link_training( + struct scic_sds_phy *this_phy, + enum sci_sas_link_rate max_link_rate, + u32 next_state) +{ + this_phy->max_negotiated_speed = max_link_rate; + + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), next_state + ); +} + +/** + * + * @this_phy: The struct scic_sds_phy object to restart. + * + * This method restarts the struct scic_sds_phy objects base state machine in the + * starting state from any starting substate. none + */ +static void scic_sds_phy_restart_starting_state( + struct scic_sds_phy *this_phy) +{ + /* Stop the current substate machine */ + sci_base_state_machine_stop( + scic_sds_phy_get_starting_substate_machine(this_phy) + ); + + /* Re-enter the base state machine starting state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_STARTING + ); +} + +/* + * ***************************************************************************** + * * SCIC SDS PHY EVENT_HANDLERS + * ***************************************************************************** */ + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SPEED_EN. - + * decode the event - sas phy detected causes a state transition to the wait + * for speed event notification. - any other events log a warning message and + * set a failure status enum sci_status SCI_SUCCESS on any valid event notification + * SCI_FAILURE on any unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_ossp_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_SAS_PHY_DETECTED: + scic_sds_phy_start_sas_link_training(this_phy); + this_phy->is_in_link_training = true; + break; + + case SCU_EVENT_SATA_SPINUP_HOLD: + scic_sds_phy_start_sata_link_training(this_phy); + this_phy->is_in_link_training = true; + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SPEED_EN. - + * decode the event - sas phy detected returns us back to this state. - speed + * event detected causes a state transition to the wait for iaf. - identify + * timeout is an un-expected event and the state machine is restarted. - link + * failure events restart the starting state machine - any other events log a + * warning message and set a failure status enum sci_status SCI_SUCCESS on any valid + * event notification SCI_FAILURE on any unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_sas_phy_speed_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_SAS_PHY_DETECTED: + /* + * Why is this being reported again by the controller? + * We would re-enter this state so just stay here */ + break; + + case SCU_EVENT_SAS_15: + case SCU_EVENT_SAS_15_SSC: + scic_sds_phy_complete_link_training( + this_phy, SCI_SAS_150_GB, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF + ); + break; + + case SCU_EVENT_SAS_30: + case SCU_EVENT_SAS_30_SSC: + scic_sds_phy_complete_link_training( + this_phy, SCI_SAS_300_GB, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF + ); + break; + + case SCU_EVENT_SAS_60: + case SCU_EVENT_SAS_60_SSC: + scic_sds_phy_complete_link_training( + this_phy, SCI_SAS_600_GB, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF + ); + break; + + case SCU_EVENT_SATA_SPINUP_HOLD: + /* + * We were doing SAS PHY link training and received a SATA PHY event + * continue OOB/SN as if this were a SATA PHY */ + scic_sds_phy_start_sata_link_training(this_phy); + break; + + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF. - + * decode the event - sas phy detected event backs up the state machine to the + * await speed notification. - identify timeout is an un-expected event and the + * state machine is restarted. - link failure events restart the starting state + * machine - any other events log a warning message and set a failure status + * enum sci_status SCI_SUCCESS on any valid event notification SCI_FAILURE on any + * unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_iaf_uf_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_SAS_PHY_DETECTED: + /* Backup the state machine */ + scic_sds_phy_start_sas_link_training(this_phy); + break; + + case SCU_EVENT_SATA_SPINUP_HOLD: + /* + * We were doing SAS PHY link training and received a SATA PHY event + * continue OOB/SN as if this were a SATA PHY */ + scic_sds_phy_start_sata_link_training(this_phy); + break; + + case SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT: + case SCU_EVENT_LINK_FAILURE: + case SCU_EVENT_HARD_RESET_RECEIVED: + /* Start the oob/sn state machine over again */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_POWER. - + * decode the event - link failure events restart the starting state machine - + * any other events log a warning message and set a failure status enum sci_status + * SCI_SUCCESS on a link failure event SCI_FAILURE on any unexpected event + * notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_sas_power_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received unexpected " + "event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER. - + * decode the event - link failure events restart the starting state machine - + * sata spinup hold events are ignored since they are expected - any other + * events log a warning message and set a failure status enum sci_status SCI_SUCCESS + * on a link failure event SCI_FAILURE on any unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_sata_power_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + case SCU_EVENT_SATA_SPINUP_HOLD: + /* These events are received every 10ms and are expected while in this state */ + break; + + case SCU_EVENT_SAS_PHY_DETECTED: + /* + * There has been a change in the phy type before OOB/SN for the + * SATA finished start down the SAS link traning path. */ + scic_sds_phy_start_sas_link_training(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN. - + * decode the event - link failure events restart the starting state machine - + * sata spinup hold events are ignored since they are expected - sata phy + * detected event change to the wait speed event - any other events log a + * warning message and set a failure status enum sci_status SCI_SUCCESS on a link + * failure event SCI_FAILURE on any unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_sata_phy_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + case SCU_EVENT_SATA_SPINUP_HOLD: + /* + * These events might be received since we dont know how many may be in + * the completion queue while waiting for power */ + break; + + case SCU_EVENT_SATA_PHY_DETECTED: + this_phy->protocol = SCIC_SDS_PHY_PROTOCOL_SATA; + + /* We have received the SATA PHY notification change state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN + ); + break; + + case SCU_EVENT_SAS_PHY_DETECTED: + /* + * There has been a change in the phy type before OOB/SN for the + * SATA finished start down the SAS link traning path. */ + scic_sds_phy_start_sas_link_training(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN. + * - decode the event - sata phy detected returns us back to this state. - + * speed event detected causes a state transition to the wait for signature. - + * link failure events restart the starting state machine - any other events + * log a warning message and set a failure status enum sci_status SCI_SUCCESS on any + * valid event notification SCI_FAILURE on any unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_sata_speed_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_SATA_PHY_DETECTED: + /* + * The hardware reports multiple SATA PHY detected events + * ignore the extras */ + break; + + case SCU_EVENT_SATA_15: + case SCU_EVENT_SATA_15_SSC: + scic_sds_phy_complete_link_training( + this_phy, + SCI_SAS_150_GB, + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF + ); + break; + + case SCU_EVENT_SATA_30: + case SCU_EVENT_SATA_30_SSC: + scic_sds_phy_complete_link_training( + this_phy, + SCI_SAS_300_GB, + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF + ); + break; + + case SCU_EVENT_SATA_60: + case SCU_EVENT_SATA_60_SSC: + scic_sds_phy_complete_link_training( + this_phy, + SCI_SAS_600_GB, + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF + ); + break; + + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + case SCU_EVENT_SAS_PHY_DETECTED: + /* + * There has been a change in the phy type before OOB/SN for the + * SATA finished start down the SAS link traning path. */ + scic_sds_phy_start_sas_link_training(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + +/** + * + * @phy: This struct scic_sds_phy object which has received an event. + * @event_code: This is the event code which the phy object is to decode. + * + * This method is called when an event notification is received for the phy + * object when in the state SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF. - + * decode the event - sas phy detected event backs up the state machine to the + * await speed notification. - identify timeout is an un-expected event and the + * state machine is restarted. - link failure events restart the starting state + * machine - any other events log a warning message and set a failure status + * enum sci_status SCI_SUCCESS on any valid event notification SCI_FAILURE on any + * unexpected event notifation + */ +static enum sci_status scic_sds_phy_starting_substate_await_sig_fis_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + u32 result = SCI_SUCCESS; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_SATA_PHY_DETECTED: + /* Backup the state machine */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN + ); + break; + + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + scic_sds_phy_restart_starting_state(this_phy); + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected event_code %x\n", + __func__, + event_code); + + result = SCI_FAILURE; + break; + } + + return result; +} + + +/* + * ***************************************************************************** + * * SCIC SDS PHY FRAME_HANDLERS + * ***************************************************************************** */ + +/** + * + * @phy: This is struct scic_sds_phy object which is being requested to decode the + * frame data. + * @frame_index: This is the index of the unsolicited frame which was received + * for this phy. + * + * This method decodes the unsolicited frame when the struct scic_sds_phy is in the + * SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF. - Get the UF Header - If the UF + * is an IAF - Copy IAF data to local phy object IAF data buffer. - Change + * starting substate to wait power. - else - log warning message of unexpected + * unsolicted frame - release frame buffer enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_starting_substate_await_iaf_uf_frame_handler( + struct scic_sds_phy *this_phy, + u32 frame_index) +{ + enum sci_status result; + u32 *frame_words; + struct sci_sas_identify_address_frame *identify_frame; + + result = scic_sds_unsolicited_frame_control_get_header( + &(scic_sds_phy_get_controller(this_phy)->uf_control), + frame_index, + (void **)&frame_words); + + if (result != SCI_SUCCESS) { + return result; + } + + frame_words[0] = SCIC_SWAP_DWORD(frame_words[0]); + identify_frame = (struct sci_sas_identify_address_frame *)frame_words; + + if (identify_frame->address_frame_type == 0) { + /* + * Byte swap the rest of the frame so we can make + * a copy of the buffer */ + frame_words[1] = SCIC_SWAP_DWORD(frame_words[1]); + frame_words[2] = SCIC_SWAP_DWORD(frame_words[2]); + frame_words[3] = SCIC_SWAP_DWORD(frame_words[3]); + frame_words[4] = SCIC_SWAP_DWORD(frame_words[4]); + frame_words[5] = SCIC_SWAP_DWORD(frame_words[5]); + + memcpy( + &this_phy->phy_type.sas.identify_address_frame_buffer, + identify_frame, + sizeof(struct sci_sas_identify_address_frame) + ); + + if (identify_frame->protocols.u.bits.smp_target) { + /* + * We got the IAF for an expander PHY go to the final state since + * there are no power requirements for expander phys. */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL + ); + } else { + /* We got the IAF we can now go to the await spinup semaphore state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER + ); + } + + result = SCI_SUCCESS; + } else + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected frame id %x\n", + __func__, + frame_index); + + /* Regardless of the result release this frame since we are done with it */ + scic_sds_controller_release_frame( + scic_sds_phy_get_controller(this_phy), frame_index + ); + + return result; +} + +/** + * + * @phy: This is struct scic_sds_phy object which is being requested to decode the + * frame data. + * @frame_index: This is the index of the unsolicited frame which was received + * for this phy. + * + * This method decodes the unsolicited frame when the struct scic_sds_phy is in the + * SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF. - Get the UF Header - If + * the UF is an SIGNATURE FIS - Copy IAF data to local phy object SIGNATURE FIS + * data buffer. - else - log warning message of unexpected unsolicted frame - + * release frame buffer enum sci_status SCI_SUCCESS Must decode the SIGNATURE FIS + * data + */ +static enum sci_status scic_sds_phy_starting_substate_await_sig_fis_frame_handler( + struct scic_sds_phy *this_phy, + u32 frame_index) +{ + enum sci_status result; + u32 *frame_words; + struct sata_fis_header *fis_frame_header; + u32 *fis_frame_data; + + result = scic_sds_unsolicited_frame_control_get_header( + &(scic_sds_phy_get_controller(this_phy)->uf_control), + frame_index, + (void **)&frame_words); + + if (result != SCI_SUCCESS) { + return result; + } + + fis_frame_header = (struct sata_fis_header *)frame_words; + + if ( + (fis_frame_header->fis_type == SATA_FIS_TYPE_REGD2H) + && !(fis_frame_header->status & ATA_STATUS_REG_BSY_BIT) + ) { + scic_sds_unsolicited_frame_control_get_buffer( + &(scic_sds_phy_get_controller(this_phy)->uf_control), + frame_index, + (void **)&fis_frame_data + ); + + scic_sds_controller_copy_sata_response( + &this_phy->phy_type.sata.signature_fis_buffer, + frame_words, + fis_frame_data + ); + + /* We got the IAF we can now go to the await spinup semaphore state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL + ); + + result = SCI_SUCCESS; + } else + dev_warn(sciphy_to_dev(this_phy), + "%s: PHY starting substate machine received " + "unexpected frame id %x\n", + __func__, + frame_index); + + /* Regardless of the result release this frame since we are done with it */ + scic_sds_controller_release_frame( + scic_sds_phy_get_controller(this_phy), frame_index + ); + + return result; +} + +/* + * ***************************************************************************** + * * SCIC SDS PHY POWER_HANDLERS + * ***************************************************************************** */ + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method is called by the struct scic_sds_controller when the phy object is + * granted power. - The notify enable spinups are turned on for this phy object + * - The phy state machine is transitioned to the + * SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_starting_substate_await_sas_power_consume_power_handler( + struct scic_sds_phy *this_phy) +{ + u32 enable_spinup; + + enable_spinup = SCU_SAS_ENSPINUP_READ(this_phy); + enable_spinup |= SCU_ENSPINUP_GEN_BIT(ENABLE); + SCU_SAS_ENSPINUP_WRITE(this_phy, enable_spinup); + + /* Change state to the final state this substate machine has run to completion */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL + ); + + return SCI_SUCCESS; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method is called by the struct scic_sds_controller when the phy object is + * granted power. - The phy state machine is transitioned to the + * SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_starting_substate_await_sata_power_consume_power_handler( + struct scic_sds_phy *this_phy) +{ + u32 scu_sas_pcfg_value; + + /* Release the spinup hold state and reset the OOB state machine */ + scu_sas_pcfg_value = SCU_SAS_PCFG_READ(this_phy); + scu_sas_pcfg_value &= + ~(SCU_SAS_PCFG_GEN_BIT(SATA_SPINUP_HOLD) | SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE)); + scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(OOB_RESET); + SCU_SAS_PCFG_WRITE(this_phy, scu_sas_pcfg_value); + + /* Now restart the OOB operation */ + scu_sas_pcfg_value &= ~SCU_SAS_PCFG_GEN_BIT(OOB_RESET); + scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); + SCU_SAS_PCFG_WRITE(this_phy, scu_sas_pcfg_value); + + /* Change state to the final state this substate machine has run to completion */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN + ); + + return SCI_SUCCESS; +} + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_phy_state_handler +scic_sds_phy_starting_substate_handler_table[SCIC_SDS_PHY_STARTING_MAX_SUBSTATES] = +{ + /* SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_default_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_starting_substate_await_ossp_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_starting_substate_await_sas_phy_speed_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_starting_substate_await_iaf_uf_frame_handler, + scic_sds_phy_starting_substate_await_iaf_uf_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_starting_substate_await_sas_power_event_handler, + scic_sds_phy_starting_substate_await_sas_power_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER, */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_starting_substate_await_sata_power_event_handler, + scic_sds_phy_starting_substate_await_sata_power_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN, */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_starting_substate_await_sata_phy_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN, */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_starting_substate_await_sata_speed_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF, */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_starting_substate_await_sig_fis_frame_handler, + scic_sds_phy_starting_substate_await_sig_fis_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_default_event_handler, + scic_sds_phy_default_consume_power_handler + } +}; + +/** + * scic_sds_phy_set_starting_substate_handlers() - + * + * This macro sets the starting substate handlers by state_id + */ +#define scic_sds_phy_set_starting_substate_handlers(phy, state_id) \ + scic_sds_phy_set_state_handlers(\ + (phy), \ + &scic_sds_phy_starting_substate_handler_table[(state_id)] \ + ) + +/* + * **************************************************************************** + * * PHY STARTING SUBSTATE METHODS + * **************************************************************************** */ + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL. - The initial state + * handlers are put in place for the struct scic_sds_phy object. - The state is + * changed to the wait phy type event notification. none + */ +static void scic_sds_phy_starting_initial_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL); + + /* This is just an temporary state go off to the starting state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_starting_substate_machine(this_phy), + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_PHY_TYPE_EN. - Set the + * struct scic_sds_phy object state handlers for this state. none + */ +static void scic_sds_phy_starting_await_ossp_en_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SPEED_EN. - Set the + * struct scic_sds_phy object state handlers for this state. none + */ +static void scic_sds_phy_starting_await_sas_speed_en_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF. - Set the + * struct scic_sds_phy object state handlers for this state. none + */ +static void scic_sds_phy_starting_await_iaf_uf_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER. - Set the + * struct scic_sds_phy object state handlers for this state. - Add this phy object to + * the power control queue none + */ +static void scic_sds_phy_starting_await_sas_power_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER + ); + + scic_sds_controller_power_control_queue_insert( + scic_sds_phy_get_controller(this_phy), + this_phy + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on exiting + * the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER. - Remove the + * struct scic_sds_phy object from the power control queue. none + */ +static void scic_sds_phy_starting_await_sas_power_substate_exit( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_controller_power_control_queue_remove( + scic_sds_phy_get_controller(this_phy), this_phy + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER. - Set the + * struct scic_sds_phy object state handlers for this state. - Add this phy object to + * the power control queue none + */ +static void scic_sds_phy_starting_await_sata_power_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER + ); + + scic_sds_controller_power_control_queue_insert( + scic_sds_phy_get_controller(this_phy), + this_phy + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on exiting + * the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER. - Remove the + * struct scic_sds_phy object from the power control queue. none + */ +static void scic_sds_phy_starting_await_sata_power_substate_exit( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_controller_power_control_queue_remove( + scic_sds_phy_get_controller(this_phy), + this_phy + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN. - Set the + * struct scic_sds_phy object state handlers for this state. none + */ +static void scic_sds_phy_starting_await_sata_phy_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN + ); + + scic_cb_timer_start( + scic_sds_phy_get_controller(this_phy), + this_phy->sata_timeout_timer, + SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on exiting + * the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN. - stop the timer + * that was started on entry to await sata phy event notification none + */ +static void scic_sds_phy_starting_await_sata_phy_substate_exit( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_cb_timer_stop( + scic_sds_phy_get_controller(this_phy), + this_phy->sata_timeout_timer + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN. - Set the + * struct scic_sds_phy object state handlers for this state. none + */ +static void scic_sds_phy_starting_await_sata_speed_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN + ); + + scic_cb_timer_start( + scic_sds_phy_get_controller(this_phy), + this_phy->sata_timeout_timer, + SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on exiting + * the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN. - stop the timer + * that was started on entry to await sata phy event notification none + */ +static void scic_sds_phy_starting_await_sata_speed_substate_exit( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_cb_timer_stop( + scic_sds_phy_get_controller(this_phy), + this_phy->sata_timeout_timer + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF. - Set the + * struct scic_sds_phy object state handlers for this state. - Start the SIGNATURE FIS + * timeout timer none + */ +static void scic_sds_phy_starting_await_sig_fis_uf_substate_enter( + struct sci_base_object *object) +{ + bool continue_to_ready_state; + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF + ); + + continue_to_ready_state = scic_sds_port_link_detected( + this_phy->owning_port, + this_phy + ); + + if (continue_to_ready_state) { + /* + * Clear the PE suspend condition so we can actually receive SIG FIS + * The hardware will not respond to the XRDY until the PE suspend + * condition is cleared. */ + scic_sds_phy_resume(this_phy); + + scic_cb_timer_start( + scic_sds_phy_get_controller(this_phy), + this_phy->sata_timeout_timer, + SCIC_SDS_SIGNATURE_FIS_TIMEOUT + ); + } else { + this_phy->is_in_link_training = false; + } +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on exiting + * the SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF. - Stop the SIGNATURE + * FIS timeout timer. none + */ +static void scic_sds_phy_starting_await_sig_fis_uf_substate_exit( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_cb_timer_stop( + scic_sds_phy_get_controller(this_phy), + this_phy->sata_timeout_timer + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL. - Set the struct scic_sds_phy + * object state handlers for this state. - Change base state machine to the + * ready state. none + */ +static void scic_sds_phy_starting_final_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_starting_substate_handlers( + this_phy, SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL + ); + + /* + * State machine has run to completion so exit out and change + * the base state machine to the ready state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_READY); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_phy_starting_substates[] = { + [SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL] = { + .enter_state = scic_sds_phy_starting_initial_substate_enter, + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN] = { + .enter_state = scic_sds_phy_starting_await_ossp_en_substate_enter, + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN] = { + .enter_state = scic_sds_phy_starting_await_sas_speed_en_substate_enter, + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF] = { + .enter_state = scic_sds_phy_starting_await_iaf_uf_substate_enter, + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER] = { + .enter_state = scic_sds_phy_starting_await_sas_power_substate_enter, + .exit_state = scic_sds_phy_starting_await_sas_power_substate_exit, + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER] = { + .enter_state = scic_sds_phy_starting_await_sata_power_substate_enter, + .exit_state = scic_sds_phy_starting_await_sata_power_substate_exit + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN] = { + .enter_state = scic_sds_phy_starting_await_sata_phy_substate_enter, + .exit_state = scic_sds_phy_starting_await_sata_phy_substate_exit + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN] = { + .enter_state = scic_sds_phy_starting_await_sata_speed_substate_enter, + .exit_state = scic_sds_phy_starting_await_sata_speed_substate_exit + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF] = { + .enter_state = scic_sds_phy_starting_await_sig_fis_uf_substate_enter, + .exit_state = scic_sds_phy_starting_await_sig_fis_uf_substate_exit + }, + [SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL] = { + .enter_state = scic_sds_phy_starting_final_substate_enter, + } +}; + +/* + * *************************************************************************** + * * DEFAULT HANDLERS + * *************************************************************************** */ + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This is the default method for phy a start request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_start_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p requested to start from invalid " + "state %d\n", + __func__, + this_phy, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; + +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This is the default method for phy a stop request. It will report a warning + * and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_stop_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p requested to stop from invalid " + "state %d\n", + __func__, + this_phy, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This is the default method for phy a reset request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_reset_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p requested to reset from invalid state " + "%d\n", + __func__, + this_phy, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This is the default method for phy a destruct request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_destroy_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + /* / @todo Implement something for the default */ + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p requested to destroy from invalid " + "state %d\n", + __func__, + this_phy, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * @frame_index: This is the frame index that was received from the SCU + * hardware. + * + * This is the default method for a phy frame handling request. It will report + * a warning, release the frame and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_frame_handler( + struct scic_sds_phy *this_phy, + u32 frame_index) +{ + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p received unexpected frame data %d " + "while in state %d\n", + __func__, + this_phy, + frame_index, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + scic_sds_controller_release_frame( + scic_sds_phy_get_controller(this_phy), frame_index); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * @event_code: This is the event code that was received from the SCU hardware. + * + * This is the default method for a phy event handler. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p received unexpected event status %x " + "while in state %d\n", + __func__, + this_phy, + event_code, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This is the default method for a phy consume power handler. It will report + * a warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_phy_default_consume_power_handler( + struct scic_sds_phy *this_phy) +{ + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC Phy 0x%p given unexpected permission to consume " + "power while in state %d\n", + __func__, + this_phy, + sci_base_state_machine_get_state( + &this_phy->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/* + * ****************************************************************************** + * * PHY STOPPED STATE HANDLERS + * ****************************************************************************** */ + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method takes the struct scic_sds_phy from a stopped state and attempts to + * start it. - The phy state machine is transitioned to the + * SCI_BASE_PHY_STATE_STARTING. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_stopped_state_start_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_STARTING + ); + + return SCI_SUCCESS; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method takes the struct scic_sds_phy from a stopped state and destroys it. - + * This function takes no action. Shouldnt this function transition the + * struct sci_base_phy::state_machine to the SCI_BASE_PHY_STATE_FINAL? enum sci_status + * SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_stopped_state_destroy_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + /* / @todo what do we actually need to do here? */ + return SCI_SUCCESS; +} + +/* + * ****************************************************************************** + * * PHY STARTING STATE HANDLERS + * ****************************************************************************** */ + +/* All of these state handlers are mapped to the starting sub-state machine */ + +/* + * ****************************************************************************** + * * PHY READY STATE HANDLERS + * ****************************************************************************** */ + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method takes the struct scic_sds_phy from a ready state and attempts to stop + * it. - The phy state machine is transitioned to the + * SCI_BASE_PHY_STATE_STOPPED. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_ready_state_stop_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_STOPPED + ); + + return SCI_SUCCESS; +} + +/** + * + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method takes the struct scic_sds_phy from a ready state and attempts to reset + * it. - The phy state machine is transitioned to the + * SCI_BASE_PHY_STATE_STARTING. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_phy_ready_state_reset_handler( + struct sci_base_phy *phy) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)phy; + + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_RESETTING + ); + + return SCI_SUCCESS; +} + +/** + * + * @phy: This is the struct scic_sds_phy object which has received the event. + * + * This method request the struct scic_sds_phy handle the received event. The only + * event that we are interested in while in the ready state is the link failure + * event. - decoded event is a link failure - transition the struct scic_sds_phy back + * to the SCI_BASE_PHY_STATE_STARTING state. - any other event recived will + * report a warning message enum sci_status SCI_SUCCESS if the event received is a + * link failure SCI_FAILURE_INVALID_STATE for any other event received. + */ +static enum sci_status scic_sds_phy_ready_state_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + enum sci_status result = SCI_FAILURE; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_LINK_FAILURE: + /* Link failure change state back to the starting state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_STARTING + ); + + result = SCI_SUCCESS; + break; + + case SCU_EVENT_BROADCAST_CHANGE: + /* Broadcast change received. Notify the port. */ + if (scic_sds_phy_get_port(this_phy) != SCI_INVALID_HANDLE) + scic_sds_port_broadcast_change_received(this_phy->owning_port, this_phy); + else + this_phy->bcn_received_while_port_unassigned = true; + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%sP SCIC PHY 0x%p ready state machine received " + "unexpected event_code %x\n", + __func__, + this_phy, + event_code); + + result = SCI_FAILURE_INVALID_STATE; + break; + } + + return result; +} + +/* --------------------------------------------------------------------------- */ + +/** + * + * @this_phy: This is the struct scic_sds_phy object which is receiving the event. + * @event_code: This is the event code to be processed. + * + * This is the resetting state event handler. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_phy_resetting_state_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code) +{ + enum sci_status result = SCI_FAILURE; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_HARD_RESET_TRANSMITTED: + /* Link failure change state back to the starting state */ + sci_base_state_machine_change_state( + scic_sds_phy_get_base_state_machine(this_phy), + SCI_BASE_PHY_STATE_STARTING + ); + + result = SCI_SUCCESS; + break; + + default: + dev_warn(sciphy_to_dev(this_phy), + "%s: SCIC PHY 0x%p resetting state machine received " + "unexpected event_code %x\n", + __func__, + this_phy, + event_code); + + result = SCI_FAILURE_INVALID_STATE; + break; + } + + return result; +} + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_phy_state_handler +scic_sds_phy_state_handler_table[SCI_BASE_PHY_MAX_STATES] = +{ + /* SCI_BASE_PHY_STATE_INITIAL */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_default_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCI_BASE_PHY_STATE_STOPPED */ + { + { + scic_sds_phy_stopped_state_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_stopped_state_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_default_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCI_BASE_PHY_STATE_STARTING */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_default_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCI_BASE_PHY_STATE_READY */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_ready_state_stop_handler, + scic_sds_phy_ready_state_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_ready_state_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCI_BASE_PHY_STATE_RESETTING */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_resetting_state_event_handler, + scic_sds_phy_default_consume_power_handler + }, + /* SCI_BASE_PHY_STATE_FINAL */ + { + { + scic_sds_phy_default_start_handler, + scic_sds_phy_default_stop_handler, + scic_sds_phy_default_reset_handler, + scic_sds_phy_default_destroy_handler + }, + scic_sds_phy_default_frame_handler, + scic_sds_phy_default_event_handler, + scic_sds_phy_default_consume_power_handler + } +}; + +/* + * **************************************************************************** + * * PHY STATE PRIVATE METHODS + * **************************************************************************** */ + +/** + * + * @this_phy: This is the struct scic_sds_phy object to stop. + * + * This method will stop the struct scic_sds_phy object. This does not reset the + * protocol engine it just suspends it and places it in a state where it will + * not cause the end device to power up. none + */ +static void scu_link_layer_stop_protocol_engine( + struct scic_sds_phy *this_phy) +{ + u32 scu_sas_pcfg_value; + u32 enable_spinup_value; + + /* Suspend the protocol engine and place it in a sata spinup hold state */ + scu_sas_pcfg_value = SCU_SAS_PCFG_READ(this_phy); + scu_sas_pcfg_value |= ( + SCU_SAS_PCFG_GEN_BIT(OOB_RESET) + | SCU_SAS_PCFG_GEN_BIT(SUSPEND_PROTOCOL_ENGINE) + | SCU_SAS_PCFG_GEN_BIT(SATA_SPINUP_HOLD) + ); + SCU_SAS_PCFG_WRITE(this_phy, scu_sas_pcfg_value); + + /* Disable the notify enable spinup primitives */ + enable_spinup_value = SCU_SAS_ENSPINUP_READ(this_phy); + enable_spinup_value &= ~SCU_ENSPINUP_GEN_BIT(ENABLE); + SCU_SAS_ENSPINUP_WRITE(this_phy, enable_spinup_value); +} + +/** + * + * + * This method will start the OOB/SN state machine for this struct scic_sds_phy object. + */ +static void scu_link_layer_start_oob( + struct scic_sds_phy *this_phy) +{ + u32 scu_sas_pcfg_value; + + scu_sas_pcfg_value = SCU_SAS_PCFG_READ(this_phy); + scu_sas_pcfg_value |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); + scu_sas_pcfg_value &= + ~(SCU_SAS_PCFG_GEN_BIT(OOB_RESET) | SCU_SAS_PCFG_GEN_BIT(HARD_RESET)); + + SCU_SAS_PCFG_WRITE(this_phy, scu_sas_pcfg_value); +} + +/** + * + * + * This method will transmit a hard reset request on the specified phy. The SCU + * hardware requires that we reset the OOB state machine and set the hard reset + * bit in the phy configuration register. We then must start OOB over with the + * hard reset bit set. + */ +static void scu_link_layer_tx_hard_reset( + struct scic_sds_phy *this_phy) +{ + u32 phy_configuration_value; + + /* + * SAS Phys must wait for the HARD_RESET_TX event notification to transition + * to the starting state. */ + phy_configuration_value = SCU_SAS_PCFG_READ(this_phy); + phy_configuration_value |= + (SCU_SAS_PCFG_GEN_BIT(HARD_RESET) | SCU_SAS_PCFG_GEN_BIT(OOB_RESET)); + SCU_SAS_PCFG_WRITE(this_phy, phy_configuration_value); + + /* Now take the OOB state machine out of reset */ + phy_configuration_value |= SCU_SAS_PCFG_GEN_BIT(OOB_ENABLE); + phy_configuration_value &= ~SCU_SAS_PCFG_GEN_BIT(OOB_RESET); + SCU_SAS_PCFG_WRITE(this_phy, phy_configuration_value); +} + +/* + * **************************************************************************** + * * PHY BASE STATE METHODS + * **************************************************************************** */ + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCI_BASE_PHY_STATE_INITIAL. - This function sets the state + * handlers for the phy object base state machine initial state. none + */ +static void scic_sds_phy_initial_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_base_state_handlers(this_phy, SCI_BASE_PHY_STATE_STOPPED); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCI_BASE_PHY_STATE_INITIAL. - This function sets the state + * handlers for the phy object base state machine initial state. - The SCU + * hardware is requested to stop the protocol engine. none + */ +static void scic_sds_phy_stopped_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + /* / @todo We need to get to the controller to place this PE in a reset state */ + + scic_sds_phy_set_base_state_handlers(this_phy, SCI_BASE_PHY_STATE_STOPPED); + + scu_link_layer_stop_protocol_engine(this_phy); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCI_BASE_PHY_STATE_STARTING. - This function sets the state + * handlers for the phy object base state machine starting state. - The SCU + * hardware is requested to start OOB/SN on this protocl engine. - The phy + * starting substate machine is started. - If the previous state was the ready + * state then the struct scic_sds_controller is informed that the phy has gone link + * down. none + */ +static void scic_sds_phy_starting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_base_state_handlers(this_phy, SCI_BASE_PHY_STATE_STARTING); + + scu_link_layer_stop_protocol_engine(this_phy); + scu_link_layer_start_oob(this_phy); + + /* We don't know what kind of phy we are going to be just yet */ + this_phy->protocol = SCIC_SDS_PHY_PROTOCOL_UNKNOWN; + this_phy->bcn_received_while_port_unassigned = false; + + /* Change over to the starting substate machine to continue */ + sci_base_state_machine_start(&this_phy->starting_substate_machine); + + if (this_phy->parent.state_machine.previous_state_id + == SCI_BASE_PHY_STATE_READY) { + scic_sds_controller_link_down( + scic_sds_phy_get_controller(this_phy), + scic_sds_phy_get_port(this_phy), + this_phy + ); + } +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCI_BASE_PHY_STATE_READY. - This function sets the state + * handlers for the phy object base state machine ready state. - The SCU + * hardware protocol engine is resumed. - The struct scic_sds_controller is informed + * that the phy object has gone link up. none + */ +static void scic_sds_phy_ready_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_base_state_handlers(this_phy, SCI_BASE_PHY_STATE_READY); + + scic_sds_controller_link_up( + scic_sds_phy_get_controller(this_phy), + scic_sds_phy_get_port(this_phy), + this_phy + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on exiting + * the SCI_BASE_PHY_STATE_INITIAL. This function suspends the SCU hardware + * protocol engine represented by this struct scic_sds_phy object. none + */ +static void scic_sds_phy_ready_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_suspend(this_phy); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCI_BASE_PHY_STATE_RESETTING. - This function sets the state + * handlers for the phy object base state machine resetting state. none + */ +static void scic_sds_phy_resetting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_base_state_handlers(this_phy, SCI_BASE_PHY_STATE_RESETTING); + + /* + * The phy is being reset, therefore deactivate it from the port. + * In the resetting state we don't notify the user regarding + * link up and link down notifications. */ + scic_sds_port_deactivate_phy(this_phy->owning_port, this_phy, false); + + if (this_phy->protocol == SCIC_SDS_PHY_PROTOCOL_SAS) { + scu_link_layer_tx_hard_reset(this_phy); + } else { + /* + * The SCU does not need to have a descrete reset state so just go back to + * the starting state. */ + sci_base_state_machine_change_state( + &this_phy->parent.state_machine, + SCI_BASE_PHY_STATE_STARTING + ); + } +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_phy object. + * + * This method will perform the actions required by the struct scic_sds_phy on + * entering the SCI_BASE_PHY_STATE_FINAL. - This function sets the state + * handlers for the phy object base state machine final state. none + */ +static void scic_sds_phy_final_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_phy *this_phy; + + this_phy = (struct scic_sds_phy *)object; + + scic_sds_phy_set_base_state_handlers(this_phy, SCI_BASE_PHY_STATE_FINAL); + + /* Nothing to do here */ +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_phy_state_table[] = { + [SCI_BASE_PHY_STATE_INITIAL] = { + .enter_state = scic_sds_phy_initial_state_enter, + }, + [SCI_BASE_PHY_STATE_STOPPED] = { + .enter_state = scic_sds_phy_stopped_state_enter, + }, + [SCI_BASE_PHY_STATE_STARTING] = { + .enter_state = scic_sds_phy_starting_state_enter, + }, + [SCI_BASE_PHY_STATE_READY] = { + .enter_state = scic_sds_phy_ready_state_enter, + .exit_state = scic_sds_phy_ready_state_exit, + }, + [SCI_BASE_PHY_STATE_RESETTING] = { + .enter_state = scic_sds_phy_resetting_state_enter, + }, + [SCI_BASE_PHY_STATE_FINAL] = { + .enter_state = scic_sds_phy_final_state_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_phy.h b/drivers/scsi/isci/core/scic_sds_phy.h new file mode 100644 index 000000000000..d9691b30e457 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_phy.h @@ -0,0 +1,491 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_PHY_H_ +#define _SCIC_SDS_PHY_H_ + +/** + * This file contains the structures, constants and prototypes for the + * struct scic_sds_phy object. + * + * + */ + +#include "intel_sata.h" +#include "intel_sas.h" +#include "sci_base_phy.h" +#include "scu_registers.h" + +struct scic_sds_port; +/** + * + * + * This is the timeout value for the SATA phy to wait for a SIGNATURE FIS + * before restarting the starting state machine. Technically, the old parallel + * ATA specification required up to 30 seconds for a device to issue its + * signature FIS as a result of a soft reset. Now we see that devices respond + * generally within 15 seconds, but we'll use 25 for now. + */ +#define SCIC_SDS_SIGNATURE_FIS_TIMEOUT 25000 + +/** + * + * + * This is the timeout for the SATA OOB/SN because the hardware does not + * recognize a hot plug after OOB signal but before the SN signals. We need to + * make sure after a hotplug timeout if we have not received the speed event + * notification from the hardware that we restart the hardware OOB state + * machine. + */ +#define SCIC_SDS_SATA_LINK_TRAINING_TIMEOUT 250 + +/** + * enum SCIC_SDS_PHY_STARTING_SUBSTATES - + * + * + */ +enum SCIC_SDS_PHY_STARTING_SUBSTATES { + /** + * Initial state + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_INITIAL, + + /** + * Wait state for the hardware OSSP event type notification + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_OSSP_EN, + + /** + * Wait state for the PHY speed notification + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_SPEED_EN, + + /** + * Wait state for the IAF Unsolicited frame notification + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_IAF_UF, + + /** + * Wait state for the request to consume power + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SAS_POWER, + + /** + * Wait state for request to consume power + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_POWER, + + /** + * Wait state for the SATA PHY notification + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_PHY_EN, + + /** + * Wait for the SATA PHY speed notification + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SATA_SPEED_EN, + + /** + * Wait state for the SIGNATURE FIS unsolicited frame notification + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_AWAIT_SIG_FIS_UF, + + /** + * Exit state for this state machine + */ + SCIC_SDS_PHY_STARTING_SUBSTATE_FINAL, + + /** + * Maximum number of substates for the STARTING state machine + */ + SCIC_SDS_PHY_STARTING_MAX_SUBSTATES +}; + +struct scic_sds_port; +struct scic_sds_controller; + +#ifdef SCIC_DEBUG_ENABLED +#define MAX_STATE_TRANSITION_RECORD (256) + +/** + * + * + * Debug code to record the state transitions for the phy object + */ +struct scic_sds_phy_state_record { + struct sci_base_observer base_state_observer; + struct sci_base_observer starting_state_observer; + + u16 index; + + u32 state_transition_table[MAX_STATE_TRANSITION_RECORD]; + +}; +#endif /* SCIC_DEBUG_ENABLED */ + +/** + * This enumeration provides a named phy type for the state machine + * + * + */ +enum SCIC_SDS_PHY_PROTOCOL { + /** + * This is an unknown phy type since there is either nothing on the other + * end or we have not detected the phy type as yet. + */ + SCIC_SDS_PHY_PROTOCOL_UNKNOWN, + + /** + * This is a SAS PHY + */ + SCIC_SDS_PHY_PROTOCOL_SAS, + + /** + * This is a SATA PHY + */ + SCIC_SDS_PHY_PROTOCOL_SATA, + + SCIC_SDS_MAX_PHY_PROTOCOLS +}; + +/** + * struct scic_sds_phy - This structure contains or references all of the data + * necessary to represent the core phy object and SCU harware protocol + * engine. + * + * + */ +struct scic_sds_phy { + struct sci_base_phy parent; + + /** + * This field specifies the port object that owns/contains this phy. + */ + struct scic_sds_port *owning_port; + + /** + * This field indicates whether the phy supports 1.5 Gb/s, 3.0 Gb/s, + * or 6.0 Gb/s operation. + */ + enum sci_sas_link_rate max_negotiated_speed; + + /** + * This member specifies the protocol being utilized on this phy. This + * field contains a legitamite value once the PHY has link trained with + * a remote phy. + */ + enum SCIC_SDS_PHY_PROTOCOL protocol; + + /** + * This field specifies the index with which this phy is associated (0-3). + */ + u8 phy_index; + + /** + * This member indicates if this particular PHY has received a BCN while + * it had no port assignement. This BCN will be reported once the phy is + * assigned to a port. + */ + bool bcn_received_while_port_unassigned; + + /** + * This field indicates if this PHY is currently in the process of + * link training (i.e. it has started OOB, but has yet to perform + * IAF exchange/Signature FIS reception). + */ + bool is_in_link_training; + + union { + struct { + struct sci_sas_identify_address_frame identify_address_frame_buffer; + + } sas; + + struct { + struct sata_fis_reg_d2h signature_fis_buffer; + + } sata; + + } phy_type; + + /** + * This field contains a reference to the timer utilized in detecting + * when a signature FIS timeout has occurred. The signature FIS is the + * first FIS sent by an attached SATA device after OOB/SN. + */ + void *sata_timeout_timer; + + struct scic_sds_phy_state_handler *state_handlers; + + struct sci_base_state_machine starting_substate_machine; + + #ifdef SCIC_DEBUG_ENABLED + struct scic_sds_phy_state_record state_record; + #endif /* SCIC_DEBUG_ENABLED */ + + /** + * This field points to the link layer register set within the SCU. + */ + struct scu_link_layer_registers *link_layer_registers; + +}; + + +typedef enum sci_status (*SCIC_SDS_PHY_EVENT_HANDLER_T)(struct scic_sds_phy *, u32); +typedef enum sci_status (*SCIC_SDS_PHY_FRAME_HANDLER_T)(struct scic_sds_phy *, u32); +typedef enum sci_status (*SCIC_SDS_PHY_POWER_HANDLER_T)(struct scic_sds_phy *); + +/** + * struct scic_sds_phy_state_handler - + * + * + */ +struct scic_sds_phy_state_handler { + /** + * This is the struct sci_base_phy object state handlers. + */ + struct sci_base_phy_state_handler parent; + + /** + * The state handler for unsolicited frames received from the SCU hardware. + */ + SCIC_SDS_PHY_FRAME_HANDLER_T frame_handler; + + /** + * The state handler for events received from the SCU hardware. + */ + SCIC_SDS_PHY_EVENT_HANDLER_T event_handler; + + /** + * The state handler for staggered spinup. + */ + SCIC_SDS_PHY_POWER_HANDLER_T consume_power_handler; + +}; + +extern struct scic_sds_phy_state_handler scic_sds_phy_state_handler_table[]; +extern const struct sci_base_state scic_sds_phy_state_table[]; +extern const struct sci_base_state scic_sds_phy_starting_substates[]; +extern struct scic_sds_phy_state_handler + scic_sds_phy_starting_substate_handler_table[]; + + +/** + * scic_sds_phy_get_index() - + * + * This macro returns the phy index for the specified phy + */ +#define scic_sds_phy_get_index(phy) \ + ((phy)->phy_index) + +/** + * scic_sds_phy_get_controller() - This macro returns the controller for this + * phy + * + * + */ +#define scic_sds_phy_get_controller(phy) \ + (scic_sds_port_get_controller((phy)->owning_port)) + +/** + * scic_sds_phy_get_base_state_machine() - This macro returns the state machine + * for the base phy + * + * + */ +#define scic_sds_phy_get_base_state_machine(phy) \ + (&(phy)->parent.state_machine) + +/** + * scic_sds_phy_get_starting_substate_machine() - This macro returns the + * starting substate machine for this phy + * + * + */ +#define scic_sds_phy_get_starting_substate_machine(phy) \ + (&(phy)->starting_substate_machine) + +/** + * scic_sds_phy_set_state_handlers() - This macro sets the state handlers for + * this phy object + * + * + */ +#define scic_sds_phy_set_state_handlers(phy, handlers) \ + ((phy)->state_handlers = (handlers)) + +/** + * scic_sds_phy_set_base_state_handlers() - + * + * This macro set the base state handlers for the phy object. + */ +#define scic_sds_phy_set_base_state_handlers(phy, state_id) \ + scic_sds_phy_set_state_handlers(\ + (phy), \ + &scic_sds_phy_state_handler_table[(state_id)] \ + ) + +/** + * scic_sds_phy_is_ready() - + * + * This macro returns true if the current base state for this phy is + * SCI_BASE_PHY_STATE_READY + */ +#define scic_sds_phy_is_ready(phy) \ + (\ + SCI_BASE_PHY_STATE_READY \ + == sci_base_state_machine_get_state(\ + scic_sds_phy_get_base_state_machine(phy) \ + ) \ + ) + +/* --------------------------------------------------------------------------- */ + + + + +/* --------------------------------------------------------------------------- */ + +void scic_sds_phy_construct( + struct scic_sds_phy *this_phy, + struct scic_sds_port *owning_port, + u8 phy_index); + +struct scic_sds_port *scic_sds_phy_get_port( + struct scic_sds_phy *this_phy); + +void scic_sds_phy_set_port( + struct scic_sds_phy *this_phy, + struct scic_sds_port *owning_port); + +enum sci_status scic_sds_phy_initialize( + struct scic_sds_phy *this_phy, + struct scu_link_layer_registers *link_layer_registers); + +enum sci_status scic_sds_phy_start( + struct scic_sds_phy *this_phy); + +enum sci_status scic_sds_phy_stop( + struct scic_sds_phy *this_phy); + +enum sci_status scic_sds_phy_reset( + struct scic_sds_phy *this_phy); + +/* --------------------------------------------------------------------------- */ + +void scic_sds_phy_suspend( + struct scic_sds_phy *this_phy); + +void scic_sds_phy_resume( + struct scic_sds_phy *this_phy); + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_phy_event_handler( + struct scic_sds_phy *this_phy, + u32 event_code); + +enum sci_status scic_sds_phy_frame_handler( + struct scic_sds_phy *this_phy, + u32 frame_index); + +enum sci_status scic_sds_phy_consume_power_handler( + struct scic_sds_phy *this_phy); + +void scic_sds_phy_get_sas_address( + struct scic_sds_phy *this_phy, + struct sci_sas_address *sas_address); + +void scic_sds_phy_get_attached_sas_address( + struct scic_sds_phy *this_phy, + struct sci_sas_address *sas_address); + +void scic_sds_phy_get_protocols( + struct scic_sds_phy *this_phy, + struct sci_sas_identify_address_frame_protocols *protocols); + +void scic_sds_phy_get_attached_phy_protocols( + struct scic_sds_phy *this_phy, + struct sci_sas_identify_address_frame_protocols *protocols); + +/* + * ****************************************************************************- + * * SCIC SDS PHY Handler Methods + * ****************************************************************************- */ + +enum sci_status scic_sds_phy_default_start_handler( + struct sci_base_phy *phy); + +enum sci_status scic_sds_phy_default_stop_handler( + struct sci_base_phy *phy); + +enum sci_status scic_sds_phy_default_reset_handler( + struct sci_base_phy *phy); + +enum sci_status scic_sds_phy_default_destroy_handler( + struct sci_base_phy *phy); + +enum sci_status scic_sds_phy_default_frame_handler( + struct scic_sds_phy *phy, + u32 frame_index); + +enum sci_status scic_sds_phy_default_event_handler( + struct scic_sds_phy *phy, + u32 evnet_code); + +enum sci_status scic_sds_phy_default_consume_power_handler( + struct scic_sds_phy *phy); + +#endif /* _SCIC_SDS_PHY_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_phy_registers.h b/drivers/scsi/isci/core/scic_sds_phy_registers.h new file mode 100644 index 000000000000..7883819035da --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_phy_registers.h @@ -0,0 +1,187 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_PHY_REGISTERS_H_ +#define _SCIC_SDS_PHY_REGISTERS_H_ + +/** + * This file contains the macros used by the phy object to read/write to the + * SCU link layer registers. + * + * + */ + +#include "scic_sds_controller.h" + +/* + * ***************************************************************************** + * * SCU LINK LAYER REGISTER OPERATIONS + * ***************************************************************************** */ + +/** + * scu_link_layer_register_read() - + * + * THis macro requests the SCU register write for the specified link layer + * register. + */ +#define scu_link_layer_register_read(phy, reg) \ + scu_register_read(\ + scic_sds_phy_get_controller(phy), \ + (phy)->link_layer_registers->reg \ + ) + +/** + * scu_link_layer_register_write() - + * + * This macro requests the SCU register read for the specified link layer + * register. + */ +#define scu_link_layer_register_write(phy, reg, value) \ + scu_register_write(\ + scic_sds_phy_get_controller(phy), \ + (phy)->link_layer_registers->reg, \ + (value) \ + ) + +/* + * ***************************************************************************** + * * SCU LINK LAYER REGISTERS + * ***************************************************************************** */ + +/* / This macro reads from the SAS Identify Frame PHY Identifier register */ +#define SCU_SAS_TIPID_READ(phy) \ + scu_link_layer_register_read(phy, identify_frame_phy_id) + +/* / This macro writes to the SAS Identify Frame PHY Identifier register */ +#define SCU_SAS_TIPID_WRITE(phy, value) \ + scu_link_layer_register_write(phy, identify_frame_phy_id, value) + +/* / This macro reads from the SAS Identification register */ +#define SCU_SAS_TIID_READ(phy) \ + scu_link_layer_register_read(phy, transmit_identification) + +/* / This macro writes to the SAS Identification register */ +#define SCU_SAS_TIID_WRITE(phy, value) \ + scu_link_layer_register_write(phy, transmit_identification, value) + +/* / This macro reads the SAS Device Name High register */ +#define SCU_SAS_TIDNH_READ(phy) \ + scu_link_layer_register_read(phy, sas_device_name_high) + +/* / This macro writes the SAS Device Name High register */ +#define SCU_SAS_TIDNH_WRITE(phy, value) \ + scu_link_layer_register_write(phy, sas_device_name_high, value) + +/* / This macro reads the SAS Device Name Low register */ +#define SCU_SAS_TIDNL_READ(phy) \ + scu_link_layer_register_read(phy, sas_device_name_low) + +/* / This macro writes the SAS Device Name Low register */ +#define SCU_SAS_TIDNL_WRITE(phy, value) \ + scu_link_layer_register_write(phy, sas_device_name_low, value) + +/* / This macro reads the Source SAS Address High register */ +#define SCU_SAS_TISSAH_READ(phy) \ + scu_link_layer_register_read(phy, source_sas_address_high) + +/* / This macro writes the Source SAS Address High register */ +#define SCU_SAS_TISSAH_WRITE(phy, value) \ + scu_link_layer_register_write(phy, source_sas_address_high, value) + +/* / This macro reads the Source SAS Address Low register */ +#define SCU_SAS_TISSAL_READ(phy) \ + scu_link_layer_register_read(phy, source_sas_address_low) + +/* / This macro writes the Source SAS Address Low register */ +#define SCU_SAS_TISSAL_WRITE(phy, value) \ + scu_link_layer_register_write(phy, source_sas_address_low, value) + +/* / This macro reads the PHY Configuration register */ +#define SCU_SAS_PCFG_READ(phy) \ + scu_link_layer_register_read(phy, phy_configuration); + +/* / This macro writes the PHY Configuration register */ +#define SCU_SAS_PCFG_WRITE(phy, value) \ + scu_link_layer_register_write(phy, phy_configuration, value) + +/* / This macro reads the PHY Enable Spinup register */ +#define SCU_SAS_ENSPINUP_READ(phy) \ + scu_link_layer_register_read(phy, notify_enable_spinup_control) + +/* / This macro writes the PHY Enable Spinup register */ +#define SCU_SAS_ENSPINUP_WRITE(phy, value) \ + scu_link_layer_register_write(phy, notify_enable_spinup_control, value) + +/* / This macro reads the PHY Capacity register */ +#define SCU_SAS_PHYCAP_READ(phy) \ + scu_link_layer_register_read(phy, phy_capabilities) + +/* / This macro writes the PHY Capacity register */ +#define SCU_SAS_PHYCAP_WRITE(phy, value) \ + scu_link_layer_register_write(phy, phy_capabilities, value) + +/* / This macro reads the Recieved PHY Capacity register */ +#define SCU_SAS_RECPHYCAP_READ(phy) \ + scu_link_layer_register_read(phy, receive_phycap) + +/* / This macro reads the link layer control register */ +#define SCU_SAS_LLCTL_READ(phy) \ + scu_link_layer_register_read(phy, link_layer_control); + +/* / This macro writes the link layer control register */ +#define SCU_SAS_LLCTL_WRITE(phy, value) \ + scu_link_layer_register_write(phy, link_layer_control, value); + +#endif /* _SCIC_SDS_PHY_REGISTERS_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_port.c b/drivers/scsi/isci/core/scic_sds_port.c new file mode 100644 index 000000000000..1af38505840c --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_port.c @@ -0,0 +1,2757 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "intel_sas.h" +#include "sci_base_port.h" +#include "scic_controller.h" +#include "scic_phy.h" +#include "scic_port.h" +#include "scic_sds_controller.h" +#include "scic_sds_phy.h" +#include "scic_sds_phy_registers.h" +#include "scic_sds_port.h" +#include "scic_sds_port_registers.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_remote_node_context.h" +#include "scic_sds_request.h" +#include "scic_user_callback.h" +#include "sci_environment.h" + + +static void scic_sds_port_invalid_link_up( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy); +static void scic_sds_port_timeout_handler( + void *port); +#define SCIC_SDS_PORT_MIN_TIMER_COUNT (SCI_MAX_PORTS) +#define SCIC_SDS_PORT_MAX_TIMER_COUNT (SCI_MAX_PORTS) + +#define SCIC_SDS_PORT_HARD_RESET_TIMEOUT (1000) + +void sci_base_port_construct( + struct sci_base_port *base_port, + const struct sci_base_state *state_table) +{ + base_port->parent.private = NULL; + sci_base_state_machine_construct( + &base_port->state_machine, + &base_port->parent, + state_table, + SCI_BASE_PORT_STATE_STOPPED + ); + + sci_base_state_machine_start( + &base_port->state_machine + ); +} + +/** + * + * @this_port: This is the port object to which the phy is being assigned. + * @phy_index: This is the phy index that is being assigned to the port. + * + * This method will return a true value if the specified phy can be assigned to + * this port The following is a list of phys for each port that are allowed: - + * Port 0 - 3 2 1 0 - Port 1 - 1 - Port 2 - 3 2 - Port 3 - 3 This method + * doesn't preclude all configurations. It merely ensures that a phy is part + * of the allowable set of phy identifiers for that port. For example, one + * could assign phy 3 to port 0 and no other phys. Please refer to + * scic_sds_port_is_phy_mask_valid() for information regarding whether the + * phy_mask for a port can be supported. bool true if this is a valid phy + * assignment for the port false if this is not a valid phy assignment for the + * port + */ +bool scic_sds_port_is_valid_phy_assignment( + struct scic_sds_port *this_port, + u32 phy_index) +{ + /* Initialize to invalid value. */ + u32 existing_phy_index = SCI_MAX_PHYS; + u32 index; + + if ((this_port->physical_port_index == 1) && (phy_index != 1)) { + return false; + } + + if (this_port->physical_port_index == 3 && phy_index != 3) { + return false; + } + + if ( + (this_port->physical_port_index == 2) + && ((phy_index == 0) || (phy_index == 1)) + ) { + return false; + } + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if ((this_port->phy_table[index] != NULL) + && (index != phy_index)) { + existing_phy_index = index; + } + } + + /* + * Ensure that all of the phys in the port are capable of + * operating at the same maximum link rate. */ + if ( + (existing_phy_index < SCI_MAX_PHYS) + && (this_port->owning_controller->user_parameters.sds1.phys[ + phy_index].max_speed_generation != + this_port->owning_controller->user_parameters.sds1.phys[ + existing_phy_index].max_speed_generation) + ) + return false; + + return true; +} + +/** + * This method requests a list (mask) of the phys contained in the supplied SAS + * port. + * @this_port: a handle corresponding to the SAS port for which to return the + * phy mask. + * + * Return a bit mask indicating which phys are a part of this port. Each bit + * corresponds to a phy identifier (e.g. bit 0 = phy id 0). + */ +u32 scic_sds_port_get_phys(struct scic_sds_port *this_port) +{ + u32 index; + u32 mask; + + mask = 0; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (this_port->phy_table[index] != NULL) { + mask |= (1 << index); + } + } + + return mask; +} + +/** + * + * @this_port: This is the port object for which to determine if the phy mask + * can be supported. + * + * This method will return a true value if the port's phy mask can be supported + * by the SCU. The following is a list of valid PHY mask configurations for + * each port: - Port 0 - [[3 2] 1] 0 - Port 1 - [1] - Port 2 - [[3] 2] + * - Port 3 - [3] This method returns a boolean indication specifying if the + * phy mask can be supported. true if this is a valid phy assignment for the + * port false if this is not a valid phy assignment for the port + */ +bool scic_sds_port_is_phy_mask_valid( + struct scic_sds_port *this_port, + u32 phy_mask) +{ + if (this_port->physical_port_index == 0) { + if (((phy_mask & 0x0F) == 0x0F) + || ((phy_mask & 0x03) == 0x03) + || ((phy_mask & 0x01) == 0x01) + || (phy_mask == 0)) + return true; + } else if (this_port->physical_port_index == 1) { + if (((phy_mask & 0x02) == 0x02) + || (phy_mask == 0)) + return true; + } else if (this_port->physical_port_index == 2) { + if (((phy_mask & 0x0C) == 0x0C) + || ((phy_mask & 0x04) == 0x04) + || (phy_mask == 0)) + return true; + } else if (this_port->physical_port_index == 3) { + if (((phy_mask & 0x08) == 0x08) + || (phy_mask == 0)) + return true; + } + + return false; +} + +/** + * + * @this_port: This parameter specifies the port from which to return a + * connected phy. + * + * This method retrieves a currently active (i.e. connected) phy contained in + * the port. Currently, the lowest order phy that is connected is returned. + * This method returns a pointer to a SCIS_SDS_PHY object. NULL This value is + * returned if there are no currently active (i.e. connected to a remote end + * point) phys contained in the port. All other values specify a struct scic_sds_phy + * object that is active in the port. + */ +static struct scic_sds_phy *scic_sds_port_get_a_connected_phy( + struct scic_sds_port *this_port + ) { + u32 index; + struct scic_sds_phy *phy; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + /* + * Ensure that the phy is both part of the port and currently + * connected to the remote end-point. */ + phy = this_port->phy_table[index]; + if ( + (phy != NULL) + && scic_sds_port_active_phy(this_port, phy) + ) { + return phy; + } + } + + return NULL; +} + +/** + * scic_sds_port_set_phy() - + * @out]: port The port object to which the phy assignement is being made. + * @out]: phy The phy which is being assigned to the port. + * + * This method attempts to make the assignment of the phy to the port. If + * successful the phy is assigned to the ports phy table. bool true if the phy + * assignment can be made. false if the phy assignement can not be made. This + * is a functional test that only fails if the phy is currently assigned to a + * different port. + */ +enum sci_status scic_sds_port_set_phy( + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + /* + * Check to see if we can add this phy to a port + * that means that the phy is not part of a port and that the port does + * not already have a phy assinged to the phy index. */ + if ( + (port->phy_table[phy->phy_index] == SCI_INVALID_HANDLE) + && (scic_sds_phy_get_port(phy) == SCI_INVALID_HANDLE) + && scic_sds_port_is_valid_phy_assignment(port, phy->phy_index) + ) { + /* + * Phy is being added in the stopped state so we are in MPC mode + * make logical port index = physical port index */ + port->logical_port_index = port->physical_port_index; + port->phy_table[phy->phy_index] = phy; + scic_sds_phy_set_port(phy, port); + + return SCI_SUCCESS; + } + + return SCI_FAILURE; +} + +/** + * scic_sds_port_clear_phy() - + * @out]: port The port from which the phy is being cleared. + * @out]: phy The phy being cleared from the port. + * + * This method will clear the phy assigned to this port. This method fails if + * this phy is not currently assinged to this port. bool true if the phy is + * removed from the port. false if this phy is not assined to this port. + */ +enum sci_status scic_sds_port_clear_phy( + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + /* Make sure that this phy is part of this port */ + if ( + (port->phy_table[phy->phy_index] == phy) + && (scic_sds_phy_get_port(phy) == port) + ) { + /* Yep it is assigned to this port so remove it */ + scic_sds_phy_set_port( + phy, + &scic_sds_port_get_controller(port)->port_table[SCI_MAX_PORTS] + ); + + port->phy_table[phy->phy_index] = SCI_INVALID_HANDLE; + + return SCI_SUCCESS; + } + + return SCI_FAILURE; +} + +/** + * scic_sds_port_add_phy() - + * @this_port: This parameter specifies the port in which the phy will be added. + * @the_phy: This parameter is the phy which is to be added to the port. + * + * This method will add a PHY to the selected port. This method returns an + * enum sci_status. SCI_SUCCESS the phy has been added to the port. Any other status + * is failre to add the phy to the port. + */ +enum sci_status scic_sds_port_add_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + return this_port->state_handlers->parent.add_phy_handler( + &this_port->parent, &the_phy->parent); +} + + +/** + * scic_sds_port_remove_phy() - + * @this_port: This parameter specifies the port in which the phy will be added. + * @the_phy: This parameter is the phy which is to be added to the port. + * + * This method will remove the PHY from the selected PORT. This method returns + * an enum sci_status. SCI_SUCCESS the phy has been removed from the port. Any other + * status is failre to add the phy to the port. + */ +enum sci_status scic_sds_port_remove_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + return this_port->state_handlers->parent.remove_phy_handler( + &this_port->parent, &the_phy->parent); +} + +/** + * This method requests the SAS address for the supplied SAS port from the SCI + * implementation. + * @this_port: a handle corresponding to the SAS port for which to return the + * SAS address. + * @sas_address: This parameter specifies a pointer to a SAS address structure + * into which the core will copy the SAS address for the port. + * + */ +void scic_sds_port_get_sas_address( + struct scic_sds_port *this_port, + struct sci_sas_address *sas_address) +{ + u32 index; + + sas_address->high = 0; + sas_address->low = 0; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (this_port->phy_table[index] != NULL) { + scic_sds_phy_get_sas_address(this_port->phy_table[index], sas_address); + } + } +} + +/** + * This method will indicate which protocols are supported by this port. + * @this_port: a handle corresponding to the SAS port for which to return the + * supported protocols. + * @protocols: This parameter specifies a pointer to an IAF protocol field + * structure into which the core will copy the protocol values for the port. + * The values are returned as part of a bit mask in order to allow for + * multi-protocol support. + * + */ +static void scic_sds_port_get_protocols( + struct scic_sds_port *this_port, + struct sci_sas_identify_address_frame_protocols *protocols) +{ + u8 index; + + protocols->u.all = 0; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (this_port->phy_table[index] != NULL) { + scic_sds_phy_get_protocols(this_port->phy_table[index], protocols); + } + } +} + +/** + * This method requests the SAS address for the device directly attached to + * this SAS port. + * @this_port: a handle corresponding to the SAS port for which to return the + * SAS address. + * @sas_address: This parameter specifies a pointer to a SAS address structure + * into which the core will copy the SAS address for the device directly + * attached to the port. + * + */ +void scic_sds_port_get_attached_sas_address( + struct scic_sds_port *this_port, + struct sci_sas_address *sas_address) +{ + struct sci_sas_identify_address_frame_protocols protocols; + struct scic_sds_phy *phy; + + /* + * Ensure that the phy is both part of the port and currently + * connected to the remote end-point. */ + phy = scic_sds_port_get_a_connected_phy(this_port); + if (phy != NULL) { + scic_sds_phy_get_attached_phy_protocols(phy, &protocols); + + if (!protocols.u.bits.stp_target) { + scic_sds_phy_get_attached_sas_address(phy, sas_address); + } else { + scic_sds_phy_get_sas_address(phy, sas_address); + sas_address->low += phy->phy_index; + } + } else { + sas_address->high = 0; + sas_address->low = 0; + } +} + +/** + * This method will indicate which protocols are supported by this remote + * device. + * @this_port: a handle corresponding to the SAS port for which to return the + * supported protocols. + * @protocols: This parameter specifies a pointer to an IAF protocol field + * structure into which the core will copy the protocol values for the port. + * The values are returned as part of a bit mask in order to allow for + * multi-protocol support. + * + */ +void scic_sds_port_get_attached_protocols( + struct scic_sds_port *this_port, + struct sci_sas_identify_address_frame_protocols *protocols) +{ + struct scic_sds_phy *phy; + + /* + * Ensure that the phy is both part of the port and currently + * connected to the remote end-point. */ + phy = scic_sds_port_get_a_connected_phy(this_port); + if (phy != NULL) + scic_sds_phy_get_attached_phy_protocols(phy, protocols); + else + protocols->u.all = 0; +} + +/** + * This method returns the amount of memory requred for a port object. + * + * u32 + */ + +/** + * This method returns the minimum number of timers required for all port + * objects. + * + * u32 + */ + +/** + * This method returns the maximum number of timers required for all port + * objects. + * + * u32 + */ + +/** + * + * @this_port: + * @port_index: + * + * + */ +void scic_sds_port_construct( + struct scic_sds_port *this_port, + u8 port_index, + struct scic_sds_controller *owning_controller) +{ + u32 index; + + sci_base_port_construct( + &this_port->parent, + scic_sds_port_state_table + ); + + sci_base_state_machine_construct( + scic_sds_port_get_ready_substate_machine(this_port), + &this_port->parent.parent, + scic_sds_port_ready_substate_table, + SCIC_SDS_PORT_READY_SUBSTATE_WAITING + ); + + this_port->logical_port_index = SCIC_SDS_DUMMY_PORT; + this_port->physical_port_index = port_index; + this_port->active_phy_mask = 0; + + this_port->owning_controller = owning_controller; + + this_port->started_request_count = 0; + this_port->assigned_device_count = 0; + + this_port->timer_handle = SCI_INVALID_HANDLE; + + this_port->transport_layer_registers = NULL; + this_port->port_task_scheduler_registers = NULL; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + this_port->phy_table[index] = NULL; + } +} + +/** + * This method performs initialization of the supplied port. Initialization + * includes: - state machine initialization - member variable initialization + * - configuring the phy_mask + * @this_port: + * @transport_layer_registers: + * @port_task_scheduler_registers: + * @port_configuration_regsiter: + * + * enum sci_status SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This value is returned + * if the phy being added to the port + */ +enum sci_status scic_sds_port_initialize( + struct scic_sds_port *this_port, + void *transport_layer_registers, + void *port_task_scheduler_registers, + void *port_configuration_regsiter, + void *viit_registers) +{ + u32 tl_control; + + this_port->transport_layer_registers = transport_layer_registers; + this_port->port_task_scheduler_registers = port_task_scheduler_registers; + this_port->port_pe_configuration_register = port_configuration_regsiter; + this_port->viit_registers = viit_registers; + + scic_sds_port_set_direct_attached_device_id( + this_port, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ); + + /* + * Hardware team recommends that we enable the STP prefetch + * for all ports */ + tl_control = SCU_TLCR_READ(this_port); + tl_control |= SCU_TLCR_GEN_BIT(STP_WRITE_DATA_PREFETCH); + SCU_TLCR_WRITE(this_port, tl_control); + + /* + * If this is not the dummy port make the assignment of + * the timer and start the state machine */ + if (this_port->physical_port_index != SCI_MAX_PORTS) { + /* / @todo should we create the timer at create time? */ + this_port->timer_handle = scic_cb_timer_create( + scic_sds_port_get_controller(this_port), + scic_sds_port_timeout_handler, + this_port + ); + + } else { + /* + * Force the dummy port into a condition where it rejects all requests + * as its in an invalid state for any operation. + * / @todo should we set a set of specical handlers for the dummy port? */ + scic_sds_port_set_base_state_handlers( + this_port, SCI_BASE_PORT_STATE_STOPPED + ); + } + + return SCI_SUCCESS; +} + +/** + * + * @this_port: This is the struct scic_sds_port object for which has a phy that has + * gone link up. + * @the_phy: This is the struct scic_sds_phy object that has gone link up. + * @do_notify_user: This parameter specifies whether to inform the user (via + * scic_cb_port_link_up()) as to the fact that a new phy as become ready. + * + * This method is the a general link up handler for the struct scic_sds_port object. + * This function will determine if this struct scic_sds_phy can be assigned to this + * struct scic_sds_port object. If the struct scic_sds_phy object can is not a valid PHY for + * this port then the function will notify the SCIC_USER. A PHY can only be + * part of a port if it's attached SAS ADDRESS is the same as all other PHYs in + * the same port. none + */ +void scic_sds_port_general_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy, + bool do_notify_user) +{ + struct sci_sas_address port_sas_address; + struct sci_sas_address phy_sas_address; + + scic_sds_port_get_attached_sas_address(this_port, &port_sas_address); + scic_sds_phy_get_attached_sas_address(the_phy, &phy_sas_address); + + /* + * If the SAS address of the new phy matches the SAS address of + * other phys in the port OR this is the first phy in the port, + * then activate the phy and allow it to be used for operations + * in this port. */ + if ( + ( + (phy_sas_address.high == port_sas_address.high) + && (phy_sas_address.low == port_sas_address.low) + ) + || (this_port->active_phy_mask == 0) + ) { + scic_sds_port_activate_phy(this_port, the_phy, do_notify_user); + + if (this_port->parent.state_machine.current_state_id + == SCI_BASE_PORT_STATE_RESETTING) { + sci_base_state_machine_change_state( + &this_port->parent.state_machine, SCI_BASE_PORT_STATE_READY + ); + } + } else { + scic_sds_port_invalid_link_up(this_port, the_phy); + } +} + + +enum sci_status scic_port_start(struct scic_sds_port *port) +{ + return port->state_handlers->parent.start_handler(&port->parent); +} + + +enum sci_status scic_port_stop(struct scic_sds_port *port) +{ + return port->state_handlers->parent.stop_handler(&port->parent); +} + + +enum sci_status scic_port_get_properties( + struct scic_sds_port *port, + struct scic_port_properties *prop) +{ + if ((port == SCI_INVALID_HANDLE) || + (port->logical_port_index == SCIC_SDS_DUMMY_PORT)) + return SCI_FAILURE_INVALID_PORT; + + prop->index = port->logical_port_index; + prop->phy_mask = scic_sds_port_get_phys(port); + scic_sds_port_get_sas_address(port, &prop->local.sas_address); + scic_sds_port_get_protocols(port, &prop->local.protocols); + scic_sds_port_get_attached_sas_address(port, &prop->remote.sas_address); + scic_sds_port_get_attached_protocols(port, &prop->remote.protocols); + + return SCI_SUCCESS; +} + + +enum sci_status scic_port_hard_reset( + struct scic_sds_port *port, + u32 reset_timeout) +{ + return port->state_handlers->parent.reset_handler( + &port->parent, reset_timeout); +} + +/** + * + * @this_port: The port for which the direct attached device id is to be + * assigned. + * + * This method assigns the direct attached device ID for this port. + */ +void scic_sds_port_set_direct_attached_device_id( + struct scic_sds_port *this_port, + u32 device_id) +{ + u32 tl_control; + + SCU_STPTLDARNI_WRITE(this_port, device_id); + + /* + * The read should guarntee that the first write gets posted + * before the next write */ + tl_control = SCU_TLCR_READ(this_port); + tl_control |= SCU_TLCR_GEN_BIT(CLEAR_TCI_NCQ_MAPPING_TABLE); + SCU_TLCR_WRITE(this_port, tl_control); +} + + +/** + * + * @this_port: This is the port on which the phy should be enabled. + * @the_phy: This is the specific phy which to enable. + * @do_notify_user: This parameter specifies whether to inform the user (via + * scic_cb_port_link_up()) as to the fact that a new phy as become ready. + * + * This method will activate the phy in the port. Activation includes: - adding + * the phy to the port - enabling the Protocol Engine in the silicon. - + * notifying the user that the link is up. none + */ +void scic_sds_port_activate_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy, + bool do_notify_user) +{ + struct scic_sds_controller *controller; + struct sci_sas_identify_address_frame_protocols protocols; + + controller = scic_sds_port_get_controller(this_port); + scic_sds_phy_get_attached_phy_protocols(the_phy, &protocols); + + /* If this is sata port then the phy has already been resumed */ + if (!protocols.u.bits.stp_target) { + scic_sds_phy_resume(the_phy); + } + + this_port->active_phy_mask |= 1 << the_phy->phy_index; + + scic_sds_controller_clear_invalid_phy(controller, the_phy); + + if (do_notify_user == true) + scic_cb_port_link_up(this_port->owning_controller, this_port, the_phy); +} + +/** + * + * @this_port: This is the port on which the phy should be deactivated. + * @the_phy: This is the specific phy that is no longer active in the port. + * @do_notify_user: This parameter specifies whether to inform the user (via + * scic_cb_port_link_down()) as to the fact that a new phy as become ready. + * + * This method will deactivate the supplied phy in the port. none + */ +void scic_sds_port_deactivate_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy, + bool do_notify_user) +{ + this_port->active_phy_mask &= ~(1 << the_phy->phy_index); + + the_phy->max_negotiated_speed = SCI_SAS_NO_LINK_RATE; + + /* Re-assign the phy back to the LP as if it were a narrow port */ + SCU_PCSPExCR_WRITE(this_port, the_phy->phy_index, the_phy->phy_index); + + if (do_notify_user == true) + scic_cb_port_link_down(this_port->owning_controller, this_port, the_phy); +} + +/** + * + * @this_port: This is the port on which the phy should be disabled. + * @the_phy: This is the specific phy which to disabled. + * + * This method will disable the phy and report that the phy is not valid for + * this port object. None + */ +static void scic_sds_port_invalid_link_up( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + struct scic_sds_controller *controller = scic_sds_port_get_controller(this_port); + + /* + * Check to see if we have alreay reported this link as bad and if not go + * ahead and tell the SCI_USER that we have discovered an invalid link. */ + if ((controller->invalid_phy_mask & (1 << the_phy->phy_index)) == 0) { + scic_sds_controller_set_invalid_phy(controller, the_phy); + + scic_cb_port_invalid_link_up(controller, this_port, the_phy); + } +} + +/** + * This method returns false if the port only has a single phy object assigned. + * If there are no phys or more than one phy then the method will return + * true. + * @this_port: The port for which the wide port condition is to be checked. + * + * bool true Is returned if this is a wide ported port. false Is returned if + * this is a narrow port. + */ +static bool scic_sds_port_is_wide(struct scic_sds_port *this_port) +{ + u32 index; + u32 phy_count = 0; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (this_port->phy_table[index] != NULL) { + phy_count++; + } + } + + return phy_count != 1; +} + +/** + * This method is called by the PHY object when the link is detected. if the + * port wants the PHY to continue on to the link up state then the port + * layer must return true. If the port object returns false the phy object + * must halt its attempt to go link up. + * @this_port: The port associated with the phy object. + * @the_phy: The phy object that is trying to go link up. + * + * true if the phy object can continue to the link up condition. true Is + * returned if this phy can continue to the ready state. false Is returned if + * can not continue on to the ready state. This notification is in place for + * wide ports and direct attached phys. Since there are no wide ported SATA + * devices this could become an invalid port configuration. + */ +bool scic_sds_port_link_detected( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + struct sci_sas_identify_address_frame_protocols protocols; + + scic_sds_phy_get_attached_phy_protocols(the_phy, &protocols); + + if ( + (this_port->logical_port_index != SCIC_SDS_DUMMY_PORT) + && (protocols.u.bits.stp_target) + && scic_sds_port_is_wide(this_port) + ) { + scic_sds_port_invalid_link_up(this_port, the_phy); + + return false; + } + + return true; +} + +/** + * This method is the entry point for the phy to inform the port that it is now + * in a ready state + * @this_port: + * + * + */ +void scic_sds_port_link_up( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + the_phy->is_in_link_training = false; + + this_port->state_handlers->link_up_handler(this_port, the_phy); +} + +/** + * This method is the entry point for the phy to inform the port that it is no + * longer in a ready state + * @this_port: + * + * + */ +void scic_sds_port_link_down( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + this_port->state_handlers->link_down_handler(this_port, the_phy); +} + +/** + * This method is called to start an IO request on this port. + * @this_port: + * @the_device: + * @the_io_request: + * + * enum sci_status + */ +enum sci_status scic_sds_port_start_io( + struct scic_sds_port *this_port, + struct scic_sds_remote_device *the_device, + struct scic_sds_request *the_io_request) +{ + return this_port->state_handlers->start_io_handler( + this_port, the_device, the_io_request); +} + +/** + * This method is called to complete an IO request to the port. + * @this_port: + * @the_device: + * @the_io_request: + * + * enum sci_status + */ +enum sci_status scic_sds_port_complete_io( + struct scic_sds_port *this_port, + struct scic_sds_remote_device *the_device, + struct scic_sds_request *the_io_request) +{ + return this_port->state_handlers->complete_io_handler( + this_port, the_device, the_io_request); +} + +/** + * This method is provided to timeout requests for port operations. Mostly its + * for the port reset operation. + * + * + */ +static void scic_sds_port_timeout_handler(void *port) +{ + struct scic_sds_port *this_port = port; + u32 current_state; + + current_state = sci_base_state_machine_get_state( + &this_port->parent.state_machine); + + if (current_state == SCI_BASE_PORT_STATE_RESETTING) { + /* + * if the port is still in the resetting state then the timeout fired + * before the reset completed. */ + sci_base_state_machine_change_state( + &this_port->parent.state_machine, + SCI_BASE_PORT_STATE_FAILED + ); + } else if (current_state == SCI_BASE_PORT_STATE_STOPPED) { + /* + * if the port is stopped then the start request failed + * In this case stay in the stopped state. */ + dev_err(sciport_to_dev(this_port), + "%s: SCIC Port 0x%p failed to stop before tiemout.\n", + __func__, + this_port); + } else if (current_state == SCI_BASE_PORT_STATE_STOPPING) { + /* if the port is still stopping then the stop has not completed */ + scic_cb_port_stop_complete( + scic_sds_port_get_controller(this_port), + port, + SCI_FAILURE_TIMEOUT + ); + } else { + /* + * The port is in the ready state and we have a timer reporting a timeout + * this should not happen. */ + dev_err(sciport_to_dev(this_port), + "%s: SCIC Port 0x%p is processing a timeout operation " + "in state %d.\n", + __func__, + this_port, + current_state); + } +} + +/* --------------------------------------------------------------------------- */ + +#ifdef SCIC_DEBUG_ENABLED +void scic_sds_port_decrement_request_count(struct scic_sds_port *this_port) +{ + if (this_port->started_request_count == 0) + dev_warn(sciport_to_dev(this_port), + __func__, + "%s: SCIC Port object requested to decrement started " + "io count past zero.\n"); + else + this_port->started_request_count--; +} +#endif + +/** + * This function updates the hardwares VIIT entry for this port. + * + * + */ +void scic_sds_port_update_viit_entry(struct scic_sds_port *this_port) +{ + struct sci_sas_address sas_address; + + scic_sds_port_get_sas_address(this_port, &sas_address); + + scu_port_viit_register_write( + this_port, initiator_sas_address_hi, sas_address.high); + + scu_port_viit_register_write( + this_port, initiator_sas_address_lo, sas_address.low); + + /* This value get cleared just in case its not already cleared */ + scu_port_viit_register_write( + this_port, reserved, 0); + + /* We are required to update the status register last */ + scu_port_viit_register_write( + this_port, status, ( + SCU_VIIT_ENTRY_ID_VIIT + | SCU_VIIT_IPPT_INITIATOR + | ((1 << this_port->physical_port_index) << SCU_VIIT_ENTRY_LPVIE_SHIFT) + | SCU_VIIT_STATUS_ALL_VALID + ) + ); +} + +/** + * This method returns the maximum allowed speed for data transfers on this + * port. This maximum allowed speed evaluates to the maximum speed of the + * slowest phy in the port. + * @this_port: This parameter specifies the port for which to retrieve the + * maximum allowed speed. + * + * This method returns the maximum negotiated speed of the slowest phy in the + * port. + */ +enum sci_sas_link_rate scic_sds_port_get_max_allowed_speed( + struct scic_sds_port *this_port) +{ + u16 index = 0; + enum sci_sas_link_rate max_allowed_speed = SCI_SAS_600_GB; + struct scic_sds_phy *phy = NULL; + + /* + * Loop through all of the phys in this port and find the phy with the + * lowest maximum link rate. */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + phy = this_port->phy_table[index]; + if ( + (phy != NULL) + && (scic_sds_port_active_phy(this_port, phy) == true) + && (phy->max_negotiated_speed < max_allowed_speed) + ) + max_allowed_speed = phy->max_negotiated_speed; + } + + return max_allowed_speed; +} + + +/** + * This method passes the event to core user. + * @this_port: The port that a BCN happens. + * @this_phy: The phy that receives BCN. + * + */ +void scic_sds_port_broadcast_change_received( + struct scic_sds_port *this_port, + struct scic_sds_phy *this_phy) +{ + /* notify the user. */ + scic_cb_port_bc_change_primitive_received( + this_port->owning_controller, this_port, this_phy + ); +} + + +/** + * This API methhod enables the broadcast change notification from underneath + * hardware. + * @this_port: The port that a BCN had been disabled from. + * + */ +void scic_port_enable_broadcast_change_notification( + struct scic_sds_port *port) +{ + struct scic_sds_phy *phy; + u32 register_value; + u8 index; + + /* Loop through all of the phys to enable BCN. */ + for (index = 0; index < SCI_MAX_PHYS; index++) { + phy = port->phy_table[index]; + if (phy != NULL) { + register_value = SCU_SAS_LLCTL_READ(phy); + + /* clear the bit by writing 1. */ + SCU_SAS_LLCTL_WRITE(phy, register_value); + } + } +} + +/* + * **************************************************************************** + * * READY SUBSTATE HANDLERS + * **************************************************************************** */ + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This method is the general ready state stop handler for the struct scic_sds_port + * object. This function will transition the ready substate machine to its + * final state. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_ready_substate_stop_handler( + struct sci_base_port *port) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + sci_base_state_machine_change_state( + &this_port->parent.state_machine, + SCI_BASE_PORT_STATE_STOPPING + ); + + return SCI_SUCCESS; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @device: This is the struct sci_base_remote_device object which is not used in this + * function. + * @io_request: This is the struct sci_base_request object which is not used in this + * function. + * + * This method is the general ready substate complete io handler for the + * struct scic_sds_port object. This function decrments the outstanding request count + * for this port object. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_ready_substate_complete_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + scic_sds_port_decrement_request_count(this_port); + + return SCI_SUCCESS; +} + +static enum sci_status scic_sds_port_ready_substate_add_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *this_phy = (struct scic_sds_phy *)phy; + enum sci_status status; + + status = scic_sds_port_set_phy(this_port, this_phy); + + if (status == SCI_SUCCESS) { + scic_sds_port_general_link_up_handler(this_port, this_phy, true); + + this_port->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING; + + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING + ); + } + + return status; +} + + +static enum sci_status scic_sds_port_ready_substate_remove_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *this_phy = (struct scic_sds_phy *)phy; + enum sci_status status; + + status = scic_sds_port_clear_phy(this_port, this_phy); + + if (status == SCI_SUCCESS) { + scic_sds_port_deactivate_phy(this_port, this_phy, true); + + this_port->not_ready_reason = SCIC_PORT_NOT_READY_RECONFIGURING; + + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING + ); + } + + return status; +} + +/* + * **************************************************************************** + * * READY SUBSTATE WAITING HANDLERS + * **************************************************************************** */ + +/** + * + * @this_port: This is the struct scic_sds_port object that which has a phy that has + * gone link up. + * @the_phy: This is the struct scic_sds_phy object that has gone link up. + * + * This method is the ready waiting substate link up handler for the + * struct scic_sds_port object. This methos will report the link up condition for + * this port and will transition to the ready operational substate. none + */ +static void scic_sds_port_ready_waiting_substate_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + /* + * Since this is the first phy going link up for the port we can just enable + * it and continue. */ + scic_sds_port_activate_phy(this_port, the_phy, true); + + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL + ); +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @device: This is the struct sci_base_remote_device object which is not used in this + * request. + * @io_request: This is the struct sci_base_request object which is not used in this + * function. + * + * This method is the ready waiting substate start io handler for the + * struct scic_sds_port object. The port object can not accept new requests so the + * request is failed. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_port_ready_waiting_substate_start_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + return SCI_FAILURE_INVALID_STATE; +} + +/* + * **************************************************************************** + * * READY SUBSTATE OPERATIONAL HANDLERS + * **************************************************************************** */ + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @timeout: This is the timeout for the reset request to complete. + * + * This method will casue the port to reset. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_ready_operational_substate_reset_handler( + struct sci_base_port *port, + u32 timeout) +{ + enum sci_status status = SCI_FAILURE_INVALID_PHY; + u32 phy_index; + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *selected_phy = SCI_INVALID_HANDLE; + + + /* Select a phy on which we can send the hard reset request. */ + for ( + phy_index = 0; + (phy_index < SCI_MAX_PHYS) + && (selected_phy == SCI_INVALID_HANDLE); + phy_index++ + ) { + selected_phy = this_port->phy_table[phy_index]; + + if ( + (selected_phy != SCI_INVALID_HANDLE) + && !scic_sds_port_active_phy(this_port, selected_phy) + ) { + /* We found a phy but it is not ready select different phy */ + selected_phy = SCI_INVALID_HANDLE; + } + } + + /* If we have a phy then go ahead and start the reset procedure */ + if (selected_phy != SCI_INVALID_HANDLE) { + status = scic_sds_phy_reset(selected_phy); + + if (status == SCI_SUCCESS) { + scic_cb_timer_start( + scic_sds_port_get_controller(this_port), + this_port->timer_handle, + timeout + ); + + this_port->not_ready_reason = SCIC_PORT_NOT_READY_HARD_RESET_REQUESTED; + + sci_base_state_machine_change_state( + &this_port->parent.state_machine, + SCI_BASE_PORT_STATE_RESETTING + ); + } + } + + return status; +} + +/** + * scic_sds_port_ready_operational_substate_link_up_handler() - + * @this_port: This is the struct scic_sds_port object that which has a phy that has + * gone link up. + * @the_phy: This is the struct scic_sds_phy object that has gone link up. + * + * This method is the ready operational substate link up handler for the + * struct scic_sds_port object. This function notifies the SCI User that the phy has + * gone link up. none + */ +static void scic_sds_port_ready_operational_substate_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + scic_sds_port_general_link_up_handler(this_port, the_phy, true); +} + +/** + * scic_sds_port_ready_operational_substate_link_down_handler() - + * @this_port: This is the struct scic_sds_port object that which has a phy that has + * gone link down. + * @the_phy: This is the struct scic_sds_phy object that has gone link down. + * + * This method is the ready operational substate link down handler for the + * struct scic_sds_port object. This function notifies the SCI User that the phy has + * gone link down and if this is the last phy in the port the port will change + * state to the ready waiting substate. none + */ +static void scic_sds_port_ready_operational_substate_link_down_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy) +{ + scic_sds_port_deactivate_phy(this_port, the_phy, true); + + /* + * If there are no active phys left in the port, then transition + * the port to the WAITING state until such time as a phy goes + * link up. */ + if (this_port->active_phy_mask == 0) { + sci_base_state_machine_change_state( + scic_sds_port_get_ready_substate_machine(this_port), + SCIC_SDS_PORT_READY_SUBSTATE_WAITING + ); + } +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @device: This is the struct sci_base_remote_device object which is not used in this + * function. + * @io_request: This is the struct sci_base_request object which is not used in this + * function. + * + * This method is the ready operational substate start io handler for the + * struct scic_sds_port object. This function incremetns the outstanding request + * count for this port object. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_ready_operational_substate_start_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + scic_sds_port_increment_request_count(this_port); + + return SCI_SUCCESS; +} + +/* + * **************************************************************************** + * * READY SUBSTATE OPERATIONAL HANDLERS + * **************************************************************************** */ + +/** + * scic_sds_port_ready_configuring_substate_add_phy_handler() - + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port add phy request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_port_ready_configuring_substate_add_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *this_phy = (struct scic_sds_phy *)phy; + enum sci_status status; + + status = scic_sds_port_set_phy(this_port, this_phy); + + if (status == SCI_SUCCESS) { + scic_sds_port_general_link_up_handler(this_port, this_phy, true); + + /* + * Re-enter the configuring state since this may be the last phy in + * the port. */ + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING + ); + } + + return status; +} + +/** + * scic_sds_port_ready_configuring_substate_remove_phy_handler() - + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port remove phy request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_port_ready_configuring_substate_remove_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *this_phy = (struct scic_sds_phy *)phy; + enum sci_status status; + + status = scic_sds_port_clear_phy(this_port, this_phy); + + if (status == SCI_SUCCESS) { + scic_sds_port_deactivate_phy(this_port, this_phy, true); + + /* + * Re-enter the configuring state since this may be the last phy in + * the port. */ + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING + ); + } + + return status; +} + +/** + * scic_sds_port_ready_configuring_substate_complete_io_handler() - + * @port: This is the port that is being requested to complete the io request. + * @device: This is the device on which the io is completing. + * + * This method will decrement the outstanding request count for this port. If + * the request count goes to 0 then the port can be reprogrammed with its new + * phy data. + */ +static enum sci_status scic_sds_port_ready_configuring_substate_complete_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + scic_sds_port_decrement_request_count(port); + + if (port->started_request_count == 0) { + sci_base_state_machine_change_state( + &port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL + ); + } + + return SCI_SUCCESS; +} + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_port_state_handler +scic_sds_port_ready_substate_handler_table[SCIC_SDS_PORT_READY_MAX_SUBSTATES] = +{ + /* SCIC_SDS_PORT_READY_SUBSTATE_WAITING */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_ready_substate_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_ready_substate_add_phy_handler, + scic_sds_port_default_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_ready_waiting_substate_link_up_handler, + scic_sds_port_default_link_down_handler, + scic_sds_port_ready_waiting_substate_start_io_handler, + scic_sds_port_ready_substate_complete_io_handler, + }, + /* SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_ready_substate_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_ready_operational_substate_reset_handler, + scic_sds_port_ready_substate_add_phy_handler, + scic_sds_port_ready_substate_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_ready_operational_substate_link_up_handler, + scic_sds_port_ready_operational_substate_link_down_handler, + scic_sds_port_ready_operational_substate_start_io_handler, + scic_sds_port_ready_substate_complete_io_handler + }, + /* SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_ready_substate_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_ready_configuring_substate_add_phy_handler, + scic_sds_port_ready_configuring_substate_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_default_link_up_handler, + scic_sds_port_default_link_down_handler, + scic_sds_port_default_start_io_handler, + scic_sds_port_ready_configuring_substate_complete_io_handler + } +}; + + +/** + * scic_sds_port_set_ready_state_handlers() - + * + * This macro sets the port ready substate handlers. + */ +#define scic_sds_port_set_ready_state_handlers(port, state_id) \ + scic_sds_port_set_state_handlers(\ + port, &scic_sds_port_ready_substate_handler_table[(state_id)] \ + ) + +/* + * ****************************************************************************** + * * PORT STATE PRIVATE METHODS + * ****************************************************************************** */ + +/** + * + * @this_port: This is the struct scic_sds_port object to suspend. + * + * This method will susped the port task scheduler for this port object. none + */ +static void scic_sds_port_suspend_port_task_scheduler( + struct scic_sds_port *this_port) +{ + u32 pts_control_value; + u32 tl_control_value; + + pts_control_value = scu_port_task_scheduler_read(this_port, control); + tl_control_value = scu_transport_layer_read(this_port, control); + + pts_control_value |= SCU_PTSxCR_GEN_BIT(SUSPEND); + tl_control_value |= SCU_TLCR_GEN_BIT(CLEAR_TCI_NCQ_MAPPING_TABLE); + + scu_port_task_scheduler_write(this_port, control, pts_control_value); + scu_transport_layer_write(this_port, control, tl_control_value); +} + +/** + * + * @this_port: This is the struct scic_sds_port object to resume. + * + * This method will resume the port task scheduler for this port object. none + */ +static void scic_sds_port_resume_port_task_scheduler( + struct scic_sds_port *this_port) +{ + u32 pts_control_value; + + pts_control_value = scu_port_task_scheduler_read(this_port, control); + + pts_control_value &= ~SCU_PTSxCR_GEN_BIT(SUSPEND); + + scu_port_task_scheduler_write(this_port, control, pts_control_value); +} + +/* + * ****************************************************************************** + * * PORT READY SUBSTATE METHODS + * ****************************************************************************** */ + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCIC_SDS_PORT_READY_SUBSTATE_WAITING. This function checks the + * port for any ready phys. If there is at least one phy in a ready state then + * the port transitions to the ready operational substate. none + */ +static void scic_sds_port_ready_substate_waiting_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_ready_state_handlers( + this_port, SCIC_SDS_PORT_READY_SUBSTATE_WAITING + ); + + scic_sds_port_suspend_port_task_scheduler(this_port); + + this_port->not_ready_reason = SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS; + + if (this_port->active_phy_mask != 0) { + /* At least one of the phys on the port is ready */ + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL + ); + } +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function sets + * the state handlers for the port object, notifies the SCI User that the port + * is ready, and resumes port operations. none + */ +static void scic_sds_port_ready_substate_operational_enter( + struct sci_base_object *object) +{ + u32 index; + struct scic_sds_port *this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_ready_state_handlers( + this_port, SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL + ); + + scic_cb_port_ready( + scic_sds_port_get_controller(this_port), this_port + ); + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (this_port->phy_table[index] != NULL) { + scic_sds_port_write_phy_assignment( + this_port, this_port->phy_table[index] + ); + } + } + + scic_sds_port_update_viit_entry(this_port); + + scic_sds_port_resume_port_task_scheduler(this_port); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * exiting the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function reports + * the port not ready and suspends the port task scheduler. none + */ +static void scic_sds_port_ready_substate_operational_exit( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)object; + + scic_cb_port_not_ready( + scic_sds_port_get_controller(this_port), + this_port, + this_port->not_ready_reason + ); +} + +/* + * ****************************************************************************** + * * PORT READY CONFIGURING METHODS + * ****************************************************************************** */ + +/** + * scic_sds_port_ready_substate_configuring_enter() - + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * exiting the SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL. This function reports + * the port not ready and suspends the port task scheduler. none + */ +static void scic_sds_port_ready_substate_configuring_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_ready_state_handlers( + this_port, SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING + ); + + if (this_port->active_phy_mask == 0) { + scic_cb_port_not_ready( + scic_sds_port_get_controller(this_port), + this_port, + SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS + ); + + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_WAITING + ); + } else if (this_port->started_request_count == 0) { + sci_base_state_machine_change_state( + &this_port->ready_substate_machine, + SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL + ); + } +} + +static void scic_sds_port_ready_substate_configuring_exit( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)object; + + scic_sds_port_suspend_port_task_scheduler(this_port); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_port_ready_substate_table[] = { + [SCIC_SDS_PORT_READY_SUBSTATE_WAITING] = { + .enter_state = scic_sds_port_ready_substate_waiting_enter, + }, + [SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL] = { + .enter_state = scic_sds_port_ready_substate_operational_enter, + .exit_state = scic_sds_port_ready_substate_operational_exit + }, + [SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING] = { + .enter_state = scic_sds_port_ready_substate_configuring_enter, + .exit_state = scic_sds_port_ready_substate_configuring_exit + }, +}; + +/* + * *************************************************************************** + * * DEFAULT HANDLERS + * *************************************************************************** */ + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for port a start request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_port_default_start_handler( + struct sci_base_port *port) +{ + struct scic_sds_port *sci_port = (struct scic_sds_port *)port; + + dev_warn(sciport_to_dev(sci_port), + "%s: SCIC Port 0x%p requested to start while in invalid " + "state %d\n", + __func__, + port, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port stop request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_port_default_stop_handler( + struct sci_base_port *port) +{ + struct scic_sds_port *sci_port = (struct scic_sds_port *)port; + + dev_warn(sciport_to_dev(sci_port), + "%s: SCIC Port 0x%p requested to stop while in invalid " + "state %d\n", + __func__, + port, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port destruct request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_port_default_destruct_handler( + struct sci_base_port *port) +{ + struct scic_sds_port *sci_port = (struct scic_sds_port *)port; + + dev_warn(sciport_to_dev(sci_port), + "%s: SCIC Port 0x%p requested to destruct while in invalid " + "state %d\n", + __func__, + port, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @timeout: This is the timeout for the reset request to complete. + * + * This is the default method for a port reset request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_port_default_reset_handler( + struct sci_base_port *port, + u32 timeout) +{ + struct scic_sds_port *sci_port = (struct scic_sds_port *)port; + + dev_warn(sciport_to_dev(sci_port), + "%s: SCIC Port 0x%p requested to reset while in invalid " + "state %d\n", + __func__, + port, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port add phy request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_port_default_add_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *sci_port = (struct scic_sds_port *)port; + + dev_warn(sciport_to_dev(sci_port), + "%s: SCIC Port 0x%p requested to add phy 0x%p while in " + "invalid state %d\n", + __func__, + port, + phy, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port remove phy request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_port_default_remove_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *sci_port = (struct scic_sds_port *)port; + + dev_warn(sciport_to_dev(sci_port), + "%s: SCIC Port 0x%p requested to remove phy 0x%p while in " + "invalid state %d\n", + __func__, + port, + phy, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port unsolicited frame request. It will + * report a warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE Is it even + * possible to receive an unsolicited frame directed to a port object? It + * seems possible if we implementing virtual functions but until then? + */ +enum sci_status scic_sds_port_default_frame_handler( + struct scic_sds_port *port, + u32 frame_index) +{ + dev_warn(sciport_to_dev(port), + "%s: SCIC Port 0x%p requested to process frame %d while in " + "invalid state %d\n", + __func__, + port, + frame_index, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine(port))); + + scic_sds_controller_release_frame( + scic_sds_port_get_controller(port), frame_index + ); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port event request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_port_default_event_handler( + struct scic_sds_port *port, + u32 event_code) +{ + dev_warn(sciport_to_dev(port), + "%s: SCIC Port 0x%p requested to process event 0x%x while " + "in invalid state %d\n", + __func__, + port, + event_code, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine( + (struct scic_sds_port *)port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port link up notification. It will report + * a warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +void scic_sds_port_default_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy) +{ + dev_warn(sciport_to_dev(this_port), + "%s: SCIC Port 0x%p received link_up notification from phy " + "0x%p while in invalid state %d\n", + __func__, + this_port, + phy, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine(this_port))); +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port link down notification. It will + * report a warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +void scic_sds_port_default_link_down_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy) +{ + dev_warn(sciport_to_dev(this_port), + "%s: SCIC Port 0x%p received link down notification from " + "phy 0x%p while in invalid state %d\n", + __func__, + this_port, + phy, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine(this_port))); +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port start io request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_port_default_start_io_handler( + struct scic_sds_port *this_port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + dev_warn(sciport_to_dev(this_port), + "%s: SCIC Port 0x%p requested to start io request 0x%p " + "while in invalid state %d\n", + __func__, + this_port, + io_request, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine(this_port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This is the default method for a port complete io request. It will report a + * warning and exit. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_port_default_complete_io_handler( + struct scic_sds_port *this_port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + dev_warn(sciport_to_dev(this_port), + "%s: SCIC Port 0x%p requested to complete io request 0x%p " + "while in invalid state %d\n", + __func__, + this_port, + io_request, + sci_base_state_machine_get_state( + scic_sds_port_get_base_state_machine(this_port))); + + return SCI_FAILURE_INVALID_STATE; +} + +/* + * **************************************************************************** + * * GENERAL STATE HANDLERS + * **************************************************************************** */ + +/** + * + * @port: This is the struct scic_sds_port object on which the io request count will + * be decremented. + * @device: This is the struct scic_sds_remote_device object to which the io request + * is being directed. This parameter is not required to complete this + * operation. + * @io_request: This is the request that is being completed on this port + * object. This parameter is not required to complete this operation. + * + * This is a general complete io request handler for the struct scic_sds_port object. + * enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_general_complete_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + scic_sds_port_decrement_request_count(this_port); + + return SCI_SUCCESS; +} + +/* + * **************************************************************************** + * * STOPPED STATE HANDLERS + * **************************************************************************** */ + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This method takes the struct scic_sds_port from a stopped state and attempts to + * start it. To start a port it must have no assiged devices and it must have + * at least one phy assigned to it. If those conditions are met then the port + * can transition to the ready state. enum sci_status + * SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION This struct scic_sds_port object could + * not be started because the port configuration is not valid. SCI_SUCCESS the + * start request is successful and the struct scic_sds_port object has transitioned to + * the SCI_BASE_PORT_STATE_READY. + */ +static enum sci_status scic_sds_port_stopped_state_start_handler( + struct sci_base_port *port) +{ + u32 phy_mask; + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + if (this_port->assigned_device_count > 0) { + /* + * / @todo This is a start failure operation because there are still + * / devices assigned to this port. There must be no devices + * / assigned to a port on a start operation. */ + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + phy_mask = scic_sds_port_get_phys(this_port); + + /* + * There are one or more phys assigned to this port. Make sure + * the port's phy mask is in fact legal and supported by the + * silicon. */ + if (scic_sds_port_is_phy_mask_valid(this_port, phy_mask) == true) { + sci_base_state_machine_change_state( + scic_sds_port_get_base_state_machine(this_port), + SCI_BASE_PORT_STATE_READY + ); + + return SCI_SUCCESS; + } + + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This method takes the struct scic_sds_port that is in a stopped state and handles a + * stop request. This function takes no action. enum sci_status SCI_SUCCESS the + * stop request is successful as the struct scic_sds_port object is already stopped. + */ +static enum sci_status scic_sds_port_stopped_state_stop_handler( + struct sci_base_port *port) +{ + /* We are already stopped so there is nothing to do here */ + return SCI_SUCCESS; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This method takes the struct scic_sds_port that is in a stopped state and handles + * the destruct request. The stopped state is the only state in which the + * struct scic_sds_port can be destroyed. This function causes the port object to + * transition to the SCI_BASE_PORT_STATE_FINAL. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_stopped_state_destruct_handler( + struct sci_base_port *port) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + sci_base_state_machine_stop(&this_port->parent.state_machine); + + return SCI_SUCCESS; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method takes the struct scic_sds_port that is in a stopped state and handles + * the add phy request. In MPC mode the only time a phy can be added to a port + * is in the SCI_BASE_PORT_STATE_STOPPED. enum sci_status + * SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION is returned when the phy can not + * be added to the port. SCI_SUCCESS if the phy is added to the port. + */ +static enum sci_status scic_sds_port_stopped_state_add_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *this_phy = (struct scic_sds_phy *)phy; + struct sci_sas_address port_sas_address; + + /* Read the port assigned SAS Address if there is one */ + scic_sds_port_get_sas_address(this_port, &port_sas_address); + + if (port_sas_address.high != 0 && port_sas_address.low != 0) { + struct sci_sas_address phy_sas_address; + + /* + * Make sure that the PHY SAS Address matches the SAS Address + * for this port. */ + scic_sds_phy_get_sas_address(this_phy, &phy_sas_address); + + if ( + (port_sas_address.high != phy_sas_address.high) + || (port_sas_address.low != phy_sas_address.low) + ) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + } + + return scic_sds_port_set_phy(this_port, this_phy); +} + + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * @phy: This is the struct sci_base_phy object which is cast into a struct scic_sds_phy + * object. + * + * This method takes the struct scic_sds_port that is in a stopped state and handles + * the remove phy request. In MPC mode the only time a phy can be removed from + * a port is in the SCI_BASE_PORT_STATE_STOPPED. enum sci_status + * SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION is returned when the phy can not + * be added to the port. SCI_SUCCESS if the phy is added to the port. + */ +static enum sci_status scic_sds_port_stopped_state_remove_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + struct scic_sds_phy *this_phy = (struct scic_sds_phy *)phy; + + return scic_sds_port_clear_phy(this_port, this_phy); +} + +/* + * **************************************************************************** + * * READY STATE HANDLERS + * **************************************************************************** */ + +/* + * **************************************************************************** + * * RESETTING STATE HANDLERS + * **************************************************************************** */ + +/* + * **************************************************************************** + * * STOPPING STATE HANDLERS + * **************************************************************************** */ + +/** + * + * @port: This is the struct scic_sds_port object on which the io request count will + * be decremented. + * @device: This is the struct scic_sds_remote_device object to which the io request + * is being directed. This parameter is not required to complete this + * operation. + * @io_request: This is the request that is being completed on this port + * object. This parameter is not required to complete this operation. + * + * This method takes the struct scic_sds_port that is in a stopping state and handles + * the complete io request. Should the request count reach 0 then the port + * object will transition to the stopped state. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_stopping_state_complete_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + scic_sds_port_decrement_request_count(this_port); + + if (this_port->started_request_count == 0) { + sci_base_state_machine_change_state( + scic_sds_port_get_base_state_machine(this_port), + SCI_BASE_PORT_STATE_STOPPED + ); + } + + return SCI_SUCCESS; +} + +/* + * **************************************************************************** + * * RESETTING STATE HANDLERS + * **************************************************************************** */ + +/** + * + * @port: This is the port object which is being requested to stop. + * + * This method will stop a failed port. This causes a transition to the + * stopping state. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_port_reset_state_stop_handler( + struct sci_base_port *port) +{ + struct scic_sds_port *this_port = (struct scic_sds_port *)port; + + sci_base_state_machine_change_state( + &this_port->parent.state_machine, + SCI_BASE_PORT_STATE_STOPPING + ); + + return SCI_SUCCESS; +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This method will transition a failed port to its ready state. The port + * failed because a hard reset request timed out but at some time later one or + * more phys in the port became ready. enum sci_status SCI_SUCCESS + */ +static void scic_sds_port_reset_state_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy) +{ + /* + * / @todo We should make sure that the phy that has gone link up is the same + * / one on which we sent the reset. It is possible that the phy on + * / which we sent the reset is not the one that has gone link up and we + * / want to make sure that phy being reset comes back. Consider the + * / case where a reset is sent but before the hardware processes the + * / reset it get a link up on the port because of a hot plug event. + * / because of the reset request this phy will go link down almost + * / immediately. */ + + /* + * In the resetting state we don't notify the user regarding + * link up and link down notifications. */ + scic_sds_port_general_link_up_handler(this_port, phy, false); +} + +/** + * + * @port: This is the struct sci_base_port object which is cast into a struct scic_sds_port + * object. + * + * This method process link down notifications that occur during a port reset + * operation. Link downs can occur during the reset operation. enum sci_status + * SCI_SUCCESS + */ +static void scic_sds_port_reset_state_link_down_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy) +{ + /* + * In the resetting state we don't notify the user regarding + * link up and link down notifications. */ + scic_sds_port_deactivate_phy(this_port, phy, false); +} + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_port_state_handler +scic_sds_port_state_handler_table[SCI_BASE_PORT_MAX_STATES] = +{ + /* SCI_BASE_PORT_STATE_STOPPED */ + { + { + scic_sds_port_stopped_state_start_handler, + scic_sds_port_stopped_state_stop_handler, + scic_sds_port_stopped_state_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_stopped_state_add_phy_handler, + scic_sds_port_stopped_state_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_default_link_up_handler, + scic_sds_port_default_link_down_handler, + scic_sds_port_default_start_io_handler, + scic_sds_port_default_complete_io_handler + }, + /* SCI_BASE_PORT_STATE_STOPPING */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_default_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_default_add_phy_handler, + scic_sds_port_default_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_default_link_up_handler, + scic_sds_port_default_link_down_handler, + scic_sds_port_default_start_io_handler, + scic_sds_port_stopping_state_complete_io_handler + }, + /* SCI_BASE_PORT_STATE_READY */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_default_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_default_add_phy_handler, + scic_sds_port_default_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_default_link_up_handler, + scic_sds_port_default_link_down_handler, + scic_sds_port_default_start_io_handler, + scic_sds_port_general_complete_io_handler + }, + /* SCI_BASE_PORT_STATE_RESETTING */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_reset_state_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_default_add_phy_handler, + scic_sds_port_default_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_reset_state_link_up_handler, + scic_sds_port_reset_state_link_down_handler, + scic_sds_port_default_start_io_handler, + scic_sds_port_general_complete_io_handler + }, + /* SCI_BASE_PORT_STATE_FAILED */ + { + { + scic_sds_port_default_start_handler, + scic_sds_port_default_stop_handler, + scic_sds_port_default_destruct_handler, + scic_sds_port_default_reset_handler, + scic_sds_port_default_add_phy_handler, + scic_sds_port_default_remove_phy_handler + }, + scic_sds_port_default_frame_handler, + scic_sds_port_default_event_handler, + scic_sds_port_default_link_up_handler, + scic_sds_port_default_link_down_handler, + scic_sds_port_default_start_io_handler, + scic_sds_port_general_complete_io_handler + } +}; + +/* + * ****************************************************************************** + * * PORT STATE PRIVATE METHODS + * ****************************************************************************** */ + +/** + * + * @this_port: This is the port object which to suspend. + * + * This method will enable the SCU Port Task Scheduler for this port object but + * will leave the port task scheduler in a suspended state. none + */ +static void scic_sds_port_enable_port_task_scheduler( + struct scic_sds_port *this_port) +{ + u32 pts_control_value; + + pts_control_value = scu_port_task_scheduler_read(this_port, control); + + pts_control_value |= SCU_PTSxCR_GEN_BIT(ENABLE) | SCU_PTSxCR_GEN_BIT(SUSPEND); + + scu_port_task_scheduler_write(this_port, control, pts_control_value); +} + +/** + * + * @this_port: This is the port object which to resume. + * + * This method will disable the SCU port task scheduler for this port object. + * none + */ +static void scic_sds_port_disable_port_task_scheduler( + struct scic_sds_port *this_port) +{ + u32 pts_control_value; + + pts_control_value = scu_port_task_scheduler_read(this_port, control); + + pts_control_value &= ~(SCU_PTSxCR_GEN_BIT(ENABLE) + | SCU_PTSxCR_GEN_BIT(SUSPEND)); + + scu_port_task_scheduler_write(this_port, control, pts_control_value); +} + +/* + * ****************************************************************************** + * * PORT STATE METHODS + * ****************************************************************************** */ + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCI_BASE_PORT_STATE_STOPPED. This function sets the stopped + * state handlers for the struct scic_sds_port object and disables the port task + * scheduler in the hardware. none + */ +static void scic_sds_port_stopped_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_base_state_handlers( + this_port, SCI_BASE_PORT_STATE_STOPPED + ); + + if ( + SCI_BASE_PORT_STATE_STOPPING + == this_port->parent.state_machine.previous_state_id + ) { + /* + * If we enter this state becasuse of a request to stop + * the port then we want to disable the hardwares port + * task scheduler. */ + scic_sds_port_disable_port_task_scheduler(this_port); + } +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * exiting the SCI_BASE_STATE_STOPPED. This function enables the SCU hardware + * port task scheduler. none + */ +static void scic_sds_port_stopped_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + /* Enable and suspend the port task scheduler */ + scic_sds_port_enable_port_task_scheduler(this_port); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCI_BASE_PORT_STATE_READY. This function sets the ready state + * handlers for the struct scic_sds_port object, reports the port object as not ready + * and starts the ready substate machine. none + */ +static void scic_sds_port_ready_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + /* Put the ready state handlers in place though they will not be there long */ + scic_sds_port_set_base_state_handlers( + this_port, SCI_BASE_PORT_STATE_READY + ); + + if ( + SCI_BASE_PORT_STATE_RESETTING + == this_port->parent.state_machine.previous_state_id + ) { + scic_cb_port_hard_reset_complete( + scic_sds_port_get_controller(this_port), + this_port, + SCI_SUCCESS + ); + } else { + /* Notify the caller that the port is not yet ready */ + scic_cb_port_not_ready( + scic_sds_port_get_controller(this_port), + this_port, + SCIC_PORT_NOT_READY_NO_ACTIVE_PHYS + ); + } + + /* Start the ready substate machine */ + sci_base_state_machine_start( + scic_sds_port_get_ready_substate_machine(this_port) + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * exiting the SCI_BASE_STATE_READY. This function does nothing. none + */ +static void scic_sds_port_ready_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + sci_base_state_machine_stop(&this_port->ready_substate_machine); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCI_BASE_PORT_STATE_RESETTING. This function sets the resetting + * state handlers for the struct scic_sds_port object. none + */ +static void scic_sds_port_resetting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_base_state_handlers( + this_port, SCI_BASE_PORT_STATE_RESETTING + ); + + scic_sds_port_set_direct_attached_device_id( + this_port, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * exiting the SCI_BASE_STATE_RESETTING. This function does nothing. none + */ +static void scic_sds_port_resetting_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + scic_cb_timer_stop( + scic_sds_port_get_controller(this_port), + this_port->timer_handle + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCI_BASE_PORT_STATE_STOPPING. This function sets the stopping + * state handlers for the struct scic_sds_port object. none + */ +static void scic_sds_port_stopping_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_base_state_handlers( + this_port, SCI_BASE_PORT_STATE_STOPPING + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * exiting the SCI_BASE_STATE_STOPPING. This function does nothing. none + */ +static void scic_sds_port_stopping_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + scic_cb_timer_stop( + scic_sds_port_get_controller(this_port), + this_port->timer_handle + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast to a struct scic_sds_port object. + * + * This method will perform the actions required by the struct scic_sds_port on + * entering the SCI_BASE_PORT_STATE_STOPPING. This function sets the stopping + * state handlers for the struct scic_sds_port object. none + */ +static void scic_sds_port_failed_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_port *this_port; + + this_port = (struct scic_sds_port *)object; + + scic_sds_port_set_base_state_handlers( + this_port, + SCI_BASE_PORT_STATE_FAILED + ); + + scic_cb_port_hard_reset_complete( + scic_sds_port_get_controller(this_port), + this_port, + SCI_FAILURE_TIMEOUT + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_port_state_table[] = { + [SCI_BASE_PORT_STATE_STOPPED] = { + .enter_state = scic_sds_port_stopped_state_enter, + .exit_state = scic_sds_port_stopped_state_exit + }, + [SCI_BASE_PORT_STATE_STOPPING] = { + .enter_state = scic_sds_port_stopping_state_enter, + .exit_state = scic_sds_port_stopping_state_exit + }, + [SCI_BASE_PORT_STATE_READY] = { + .enter_state = scic_sds_port_ready_state_enter, + .exit_state = scic_sds_port_ready_state_exit + }, + [SCI_BASE_PORT_STATE_RESETTING] = { + .enter_state = scic_sds_port_resetting_state_enter, + .exit_state = scic_sds_port_resetting_state_exit + }, + [SCI_BASE_PORT_STATE_FAILED] = { + .enter_state = scic_sds_port_failed_state_enter, + } +}; + diff --git a/drivers/scsi/isci/core/scic_sds_port.h b/drivers/scsi/isci/core/scic_sds_port.h new file mode 100644 index 000000000000..bbb9de5228ed --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_port.h @@ -0,0 +1,514 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_PORT_H_ +#define _SCIC_SDS_PORT_H_ + +/** + * This file contains the structures, constants and prototypes for the + * struct scic_sds_port object. + * + * + */ + +#include "sci_controller_constants.h" +#include "intel_sas.h" +#include "sci_base_port.h" +#include "sci_base_phy.h" +#include "scu_registers.h" + +#define SCIC_SDS_DUMMY_PORT 0xFF + +/** + * enum SCIC_SDS_PORT_READY_SUBSTATES - + * + * This enumeration depicts all of the states for the core port ready substate + * machine. + */ +enum SCIC_SDS_PORT_READY_SUBSTATES { + /** + * The substate where the port is started and ready but has no active phys. + */ + SCIC_SDS_PORT_READY_SUBSTATE_WAITING, + + /** + * The substate where the port is started and ready and there is at least one + * phy operational. + */ + SCIC_SDS_PORT_READY_SUBSTATE_OPERATIONAL, + + /** + * The substate where the port is started and there was an add/remove phy + * event. This state is only used in Automatic Port Configuration Mode (APC) + */ + SCIC_SDS_PORT_READY_SUBSTATE_CONFIGURING, + + SCIC_SDS_PORT_READY_MAX_SUBSTATES +}; + +struct scic_sds_controller; +struct scic_sds_phy; +struct scic_sds_remote_device; +struct scic_sds_request; + +/** + * struct scic_sds_port - + * + * The core port object provides the the abstraction for an SCU port. + */ +struct scic_sds_port { + /** + * This field is the oommon base port object. + */ + struct sci_base_port parent; + + /** + * This field is the port index that is reported to the SCI USER. This allows + * the actual hardware physical port to change without the SCI USER getting a + * different answer for the get port index. + */ + u8 logical_port_index; + + /** + * This field is the port index used to program the SCU hardware. + */ + u8 physical_port_index; + + /** + * This field contains the active phy mask for the port. This mask is used in + * conjunction with the phy state to determine which phy to select for some + * port operations. + */ + u8 active_phy_mask; + + /** + * This field contains the count of the io requests started on this port + * object. It is used to control controller shutdown. + */ + u32 started_request_count; + + /** + * This field contains the number of devices assigned to this port. It is + * used to control port start requests. + */ + u32 assigned_device_count; + + /** + * This field contains the reason for the port not going ready. It is + * assigned in the state handlers and used in the state transition. + */ + u32 not_ready_reason; + + /** + * This field is the table of phys assigned to the port. + */ + struct scic_sds_phy *phy_table[SCI_MAX_PHYS]; + + /** + * This field is a pointer back to the controller that owns this port object. + */ + struct scic_sds_controller *owning_controller; + + /** + * This field contains the port start/stop timer handle. + */ + void *timer_handle; + + /** + * This field points to the current set of state handlers for this port + * object. These state handlers are assigned at each enter state of the state + * machine. + */ + struct scic_sds_port_state_handler *state_handlers; + + /** + * This field is the ready substate machine for the port. + */ + struct sci_base_state_machine ready_substate_machine; + + /* / Memory mapped hardware register space */ + /** + * This field is the pointer to the transport layer register for the SCU + * hardware. + */ + struct scu_transport_layer_registers *transport_layer_registers; + + /** + * This field is the pointer to the port task scheduler registers for the SCU + * hardware. + */ + struct scu_port_task_scheduler_registers *port_task_scheduler_registers; + + /** + * This field is identical for all port objects and points to the port task + * scheduler group PE configuration registers. It is used to assign PEs to a + * port. + */ + SCU_PORT_PE_CONFIGURATION_REGISTER_T *port_pe_configuration_register; + + /** + * This field is the VIIT register space for ths port object. + */ + struct scu_viit_entry *viit_registers; + +}; + + +typedef enum sci_status (*SCIC_SDS_PORT_EVENT_HANDLER_T)(struct scic_sds_port *, u32); + +typedef enum sci_status (*SCIC_SDS_PORT_FRAME_HANDLER_T)(struct scic_sds_port *, u32); + +typedef void (*SCIC_SDS_PORT_LINK_HANDLER_T)(struct scic_sds_port *, struct scic_sds_phy *); + +typedef enum sci_status (*SCIC_SDS_PORT_IO_REQUEST_HANDLER_T)( + struct scic_sds_port *, + struct scic_sds_remote_device *, + struct scic_sds_request *); + +struct scic_sds_port_state_handler { + struct sci_base_port_state_handler parent; + + SCIC_SDS_PORT_FRAME_HANDLER_T frame_handler; + SCIC_SDS_PORT_EVENT_HANDLER_T event_handler; + + SCIC_SDS_PORT_LINK_HANDLER_T link_up_handler; + SCIC_SDS_PORT_LINK_HANDLER_T link_down_handler; + + SCIC_SDS_PORT_IO_REQUEST_HANDLER_T start_io_handler; + SCIC_SDS_PORT_IO_REQUEST_HANDLER_T complete_io_handler; + +}; + +extern const struct sci_base_state scic_sds_port_state_table[]; +extern const struct sci_base_state scic_sds_port_ready_substate_table[]; + +extern struct scic_sds_port_state_handler scic_sds_port_state_handler_table[]; +extern struct scic_sds_port_state_handler scic_sds_port_ready_substate_handler_table[]; + +/** + * scic_sds_port_get_controller() - + * + * Helper macro to get the owning controller of this port + */ +#define scic_sds_port_get_controller(this_port) \ + ((this_port)->owning_controller) + +/** + * scic_sds_port_get_base_state_machine() - + * + * Helper macro to get the base state machine for this port + */ +#define scic_sds_port_get_base_state_machine(this_port) \ + (&(this_port)->parent.state_machine) + +/** + * scic_sds_port_set_base_state_handlers() - + * + * This macro will change the state handlers to those of the specified state id + */ +#define scic_sds_port_set_base_state_handlers(this_port, state_id) \ + scic_sds_port_set_state_handlers(\ + (this_port), &scic_sds_port_state_handler_table[(state_id)]) + +/** + * scic_sds_port_get_ready_substate_machine() - + * + * Helper macro to get the ready substate machine for this port + */ +#define scic_sds_port_get_ready_substate_machine(this_port) \ + (&(this_port)->ready_substate_machine) + +/** + * scic_sds_port_set_state_handlers() - + * + * Helper macro to set the port object state handlers + */ +#define scic_sds_port_set_state_handlers(this_port, handlers) \ + ((this_port)->state_handlers = (handlers)) + +/** + * scic_sds_port_get_index() - + * + * This macro returns the physical port index for this port object + */ +#define scic_sds_port_get_index(this_port) \ + ((this_port)->physical_port_index) + +/** + * scic_sds_port_increment_request_count() - + * + * Helper macro to increment the started request count + */ +#define scic_sds_port_increment_request_count(this_port) \ + ((this_port)->started_request_count++) + +#ifdef SCIC_DEBUG_ENABLED +/** + * scic_sds_port_decrement_request_count() - This method decrements the started + * io request count. The method will not decrment the started io request + * count below 0 and will log a debug message if this is attempted. + * + * + */ +void scic_sds_port_decrement_request_count( + struct scic_sds_port *this_port); +#else +/** + * scic_sds_port_decrement_request_count() - + * + * Helper macro to decrement the started io request count. The macro will not + * decrement the started io request count below 0. + */ +#define scic_sds_port_decrement_request_count(this_port) \ + (\ + (this_port)->started_request_count = (\ + ((this_port)->started_request_count == 0) ? \ + (this_port)->started_request_count : \ + ((this_port)->started_request_count - 1) \ + ) \ + ) +#endif + +/** + * scic_sds_port_write_phy_assignment() - + * + * Helper macro to write the phys port assignment + */ +#define scic_sds_port_write_phy_assignment(port, phy) \ + SCU_PCSPExCR_WRITE(\ + (port), \ + (phy)->phy_index, \ + (port)->physical_port_index \ + ) + +/** + * scic_sds_port_read_phy_assignment() - + * + * Helper macro to read the phys port assignment + */ +#define scic_sds_port_read_phy_assignment(port, phy) \ + SCU_PCSPExCR_READ(\ + (port), \ + (phy)->phy_index \ + ) + +#define scic_sds_port_active_phy(port, phy) \ + (((port)->active_phy_mask & (1 << (phy)->phy_index)) != 0) + +/* --------------------------------------------------------------------------- */ + + + + +/* --------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------- */ + +void scic_sds_port_construct( + struct scic_sds_port *this_port, + u8 port_index, + struct scic_sds_controller *owning_controller); + +enum sci_status scic_sds_port_initialize( + struct scic_sds_port *this_port, + void *transport_layer_registers, + void *port_task_scheduler_registers, + void *port_configuration_regsiter, + void *viit_registers); + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_port_add_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy); + +enum sci_status scic_sds_port_remove_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy); + +void scic_sds_port_set_direct_attached_device_id( + struct scic_sds_port *this_port, + u32 device_id); + +void scic_sds_port_activate_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy, + bool do_notify_user); + +void scic_sds_port_deactivate_phy( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy, + bool do_notify_user); + + + +void scic_sds_port_general_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *the_phy, + bool do_notify_user); + +bool scic_sds_port_link_detected( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy); + +void scic_sds_port_link_up( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy); + +void scic_sds_port_link_down( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy); + +/* --------------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_port_start_io( + struct scic_sds_port *this_port, + struct scic_sds_remote_device *the_device, + struct scic_sds_request *the_io_request); + +enum sci_status scic_sds_port_complete_io( + struct scic_sds_port *this_port, + struct scic_sds_remote_device *the_device, + struct scic_sds_request *the_io_request); + +/* --------------------------------------------------------------------------- */ + +void scic_sds_port_update_viit_entry( + struct scic_sds_port *this_port); + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_port_default_start_handler( + struct sci_base_port *port); + + +enum sci_status scic_sds_port_default_destruct_handler( + struct sci_base_port *port); + +enum sci_status scic_sds_port_default_reset_handler( + struct sci_base_port *port, + u32 timeout); + + +enum sci_status scic_sds_port_default_remove_phy_handler( + struct sci_base_port *port, + struct sci_base_phy *phy); + +enum sci_status scic_sds_port_default_frame_handler( + struct scic_sds_port *port, + u32 frame_index); + +enum sci_status scic_sds_port_default_event_handler( + struct scic_sds_port *port, + u32 event_code); + +void scic_sds_port_default_link_up_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy); + +void scic_sds_port_default_link_down_handler( + struct scic_sds_port *this_port, + struct scic_sds_phy *phy); + +enum sci_status scic_sds_port_default_start_io_handler( + struct scic_sds_port *port, + struct scic_sds_remote_device *device, + struct scic_sds_request *io_request); + + +enum sci_sas_link_rate scic_sds_port_get_max_allowed_speed( + struct scic_sds_port *this_port); + +void scic_sds_port_broadcast_change_received( + struct scic_sds_port *this_port, + struct scic_sds_phy *this_phy); + +bool scic_sds_port_is_valid_phy_assignment( + struct scic_sds_port *this_port, + u32 phy_index); + +bool scic_sds_port_is_phy_mask_valid( + struct scic_sds_port *this_port, + u32 phy_mask); + +u32 scic_sds_port_get_phys( + struct scic_sds_port *this_port); + +void scic_sds_port_get_sas_address( + struct scic_sds_port *this_port, + struct sci_sas_address *sas_address); + +void scic_sds_port_get_attached_sas_address( + struct scic_sds_port *this_port, + struct sci_sas_address *sas_address); + +void scic_sds_port_get_attached_protocols( + struct scic_sds_port *this_port, + struct sci_sas_identify_address_frame_protocols *protocols); + +enum sci_status scic_sds_port_set_phy( + struct scic_sds_port *port, + struct scic_sds_phy *phy); + +enum sci_status scic_sds_port_clear_phy( + struct scic_sds_port *port, + struct scic_sds_phy *phy); + + + +#endif /* _SCIC_SDS_PORT_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_port_configuration_agent.c b/drivers/scsi/isci/core/scic_sds_port_configuration_agent.c new file mode 100644 index 000000000000..37d4469162a7 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_port_configuration_agent.c @@ -0,0 +1,851 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the implementation for the public and protected methods + * for the port configuration agent. + * + * + */ + +#include "sci_environment.h" +#include "scic_controller.h" +#include "scic_sds_controller.h" +#include "scic_sds_port_configuration_agent.h" + +#define SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT (10) +#define SCIC_SDS_APC_RECONFIGURATION_TIMEOUT (10) +#define SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION (100) + +enum SCIC_SDS_APC_ACTIVITY { + SCIC_SDS_APC_SKIP_PHY, + SCIC_SDS_APC_ADD_PHY, + SCIC_SDS_APC_START_TIMER, + + SCIC_SDS_APC_ACTIVITY_MAX +}; + +/* + * ****************************************************************************** + * General port configuration agent routines + * ****************************************************************************** */ + +/** + * + * @address_one: A SAS Address to be compared. + * @address_two: A SAS Address to be compared. + * + * Compare the two SAS Address and if SAS Address One is greater than SAS + * Address Two then return > 0 else if SAS Address One is less than SAS Address + * Two return < 0 Otherwise they are the same return 0 A signed value of x > 0 + * > y where x is returned for Address One > Address Two y is returned for + * Address One < Address Two 0 is returned ofr Address One = Address Two + */ +static s32 sci_sas_address_compare( + struct sci_sas_address address_one, + struct sci_sas_address address_two) +{ + if (address_one.high > address_two.high) { + return 1; + } else if (address_one.high < address_two.high) { + return -1; + } else if (address_one.low > address_two.low) { + return 1; + } else if (address_one.low < address_two.low) { + return -1; + } + + /* The two SAS Address must be identical */ + return 0; +} + +/** + * + * @controller: The controller object used for the port search. + * @phy: The phy object to match. + * + * This routine will find a matching port for the phy. This means that the + * port and phy both have the same broadcast sas address and same received sas + * address. The port address or the SCI_INVALID_HANDLE if there is no matching + * port. port address if the port can be found to match the phy. + * SCI_INVALID_HANDLE if there is no matching port for the phy. + */ +static struct scic_sds_port *scic_sds_port_configuration_agent_find_port( + struct scic_sds_controller *controller, + struct scic_sds_phy *phy) +{ + u8 port_index; + struct scic_sds_port *port_handle; + struct sci_sas_address port_sas_address; + struct sci_sas_address port_attached_device_address; + struct sci_sas_address phy_sas_address; + struct sci_sas_address phy_attached_device_address; + + /* + * Since this phy can be a member of a wide port check to see if one or + * more phys match the sent and received SAS address as this phy in which + * case it should participate in the same port. */ + scic_sds_phy_get_sas_address(phy, &phy_sas_address); + scic_sds_phy_get_attached_sas_address(phy, &phy_attached_device_address); + + for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) { + if (scic_controller_get_port_handle(controller, port_index, &port_handle) == SCI_SUCCESS) { + struct scic_sds_port *port = (struct scic_sds_port *)port_handle; + + scic_sds_port_get_sas_address(port, &port_sas_address); + scic_sds_port_get_attached_sas_address(port, &port_attached_device_address); + + if ( + (sci_sas_address_compare(port_sas_address, phy_sas_address) == 0) + && (sci_sas_address_compare(port_attached_device_address, phy_attached_device_address) == 0) + ) { + return port; + } + } + } + + return SCI_INVALID_HANDLE; +} + +/** + * + * @controller: This is the controller object that contains the port agent + * @port_agent: This is the port configruation agent for the controller. + * + * This routine will validate the port configuration is correct for the SCU + * hardware. The SCU hardware allows for port configurations as follows. LP0 + * -> (PE0), (PE0, PE1), (PE0, PE1, PE2, PE3) LP1 -> (PE1) LP2 -> (PE2), (PE2, + * PE3) LP3 -> (PE3) enum sci_status SCI_SUCCESS the port configuration is valid for + * this port configuration agent. SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION + * the port configuration is not valid for this port configuration agent. + */ +static enum sci_status scic_sds_port_configuration_agent_validate_ports( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent) +{ + struct sci_sas_address first_address; + struct sci_sas_address second_address; + + /* + * Sanity check the max ranges for all the phys the max index + * is always equal to the port range index */ + if ( + (port_agent->phy_valid_port_range[0].max_index != 0) + || (port_agent->phy_valid_port_range[1].max_index != 1) + || (port_agent->phy_valid_port_range[2].max_index != 2) + || (port_agent->phy_valid_port_range[3].max_index != 3) + ) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + /* + * This is a request to configure a single x4 port or at least attempt + * to make all the phys into a single port */ + if ( + (port_agent->phy_valid_port_range[0].min_index == 0) + && (port_agent->phy_valid_port_range[1].min_index == 0) + && (port_agent->phy_valid_port_range[2].min_index == 0) + && (port_agent->phy_valid_port_range[3].min_index == 0) + ) { + return SCI_SUCCESS; + } + + /* + * This is a degenerate case where phy 1 and phy 2 are assigned + * to the same port this is explicitly disallowed by the hardware + * unless they are part of the same x4 port and this condition was + * already checked above. */ + if (port_agent->phy_valid_port_range[2].min_index == 1) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + /* + * PE0 and PE3 can never have the same SAS Address unless they + * are part of the same x4 wide port and we have already checked + * for this condition. */ + scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address); + scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address); + + if (sci_sas_address_compare(first_address, second_address) == 0) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + /* + * PE0 and PE1 are configured into a 2x1 ports make sure that the + * SAS Address for PE0 and PE2 are different since they can not be + * part of the same port. */ + if ( + (port_agent->phy_valid_port_range[0].min_index == 0) + && (port_agent->phy_valid_port_range[1].min_index == 1) + ) { + scic_sds_phy_get_sas_address(&controller->phy_table[0], &first_address); + scic_sds_phy_get_sas_address(&controller->phy_table[2], &second_address); + + if (sci_sas_address_compare(first_address, second_address) == 0) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + } + + /* + * PE2 and PE3 are configured into a 2x1 ports make sure that the + * SAS Address for PE1 and PE3 are different since they can not be + * part of the same port. */ + if ( + (port_agent->phy_valid_port_range[2].min_index == 2) + && (port_agent->phy_valid_port_range[3].min_index == 3) + ) { + scic_sds_phy_get_sas_address(&controller->phy_table[1], &first_address); + scic_sds_phy_get_sas_address(&controller->phy_table[3], &second_address); + + if (sci_sas_address_compare(first_address, second_address) == 0) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + } + + return SCI_SUCCESS; +} + +/* + * ****************************************************************************** + * Manual port configuration agent routines + * ****************************************************************************** */ + +/** + * + * + * This routine will verify that all of the phys in the same port are using the + * same SAS address. + */ +static enum sci_status scic_sds_mpc_agent_validate_phy_configuration( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent) +{ + u32 phy_mask; + u32 assigned_phy_mask; + struct sci_sas_address sas_address; + struct sci_sas_address phy_assigned_address; + u8 port_index; + u8 phy_index; + + assigned_phy_mask = 0; + sas_address.high = 0; + sas_address.low = 0; + + for (port_index = 0; port_index < SCI_MAX_PORTS; port_index++) { + phy_mask = controller->oem_parameters.sds1.ports[port_index].phy_mask; + + if (phy_mask != 0) { + /* + * Make sure that one or more of the phys were not already assinged to + * a different port. */ + if ((phy_mask & ~assigned_phy_mask) == 0) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + /* Find the starting phy index for this round through the loop */ + for (phy_index = 0; phy_index < SCI_MAX_PHYS; phy_index++) { + if ((1 << phy_index) & phy_mask) { + scic_sds_phy_get_sas_address( + &controller->phy_table[phy_index], &sas_address + ); + + /* + * The phy_index can be used as the starting point for the + * port range since the hardware starts all logical ports + * the same as the PE index. */ + port_agent->phy_valid_port_range[phy_index].min_index = port_index; + port_agent->phy_valid_port_range[phy_index].max_index = phy_index; + + if (phy_index != port_index) { + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + break; + } + } + + /* + * See how many additional phys are being added to this logical port. + * Note: We have not moved the current phy_index so we will actually + * compare the startting phy with itself. + * This is expected and required to add the phy to the port. */ + while (phy_index < SCI_MAX_PHYS) { + if ((1 << phy_index) & phy_mask) { + scic_sds_phy_get_sas_address( + &controller->phy_table[phy_index], &phy_assigned_address + ); + + if (sci_sas_address_compare(sas_address, phy_assigned_address) != 0) { + /* + * The phy mask specified that this phy is part of the same port + * as the starting phy and it is not so fail this configuration */ + return SCI_FAILURE_UNSUPPORTED_PORT_CONFIGURATION; + } + + port_agent->phy_valid_port_range[phy_index].min_index = port_index; + port_agent->phy_valid_port_range[phy_index].max_index = phy_index; + + scic_sds_port_add_phy( + &controller->port_table[port_index], + &controller->phy_table[phy_index] + ); + + assigned_phy_mask |= (1 << phy_index); + } + + phy_index++; + } + } + } + + return scic_sds_port_configuration_agent_validate_ports(controller, port_agent); +} + +/** + * + * + * This timer routine is used to allow the SCI User to rediscover or change + * device objects before a new series of link up notifications because a link + * down has allowed a better port configuration. + */ +static void scic_sds_mpc_agent_timeout_handler( + void *object) +{ + u8 index; + struct scic_sds_controller *controller = (struct scic_sds_controller *)object; + struct scic_sds_port_configuration_agent *port_agent = &controller->port_agent; + u16 configure_phy_mask; + + port_agent->timer_pending = false; + + /* Find the mask of phys that are reported read but as yet unconfigured into a port */ + configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask; + + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (configure_phy_mask & (1 << index)) { + port_agent->link_up_handler( + controller, + port_agent, + scic_sds_phy_get_port(&controller->phy_table[index]), + &controller->phy_table[index] + ); + } + } +} + +/** + * + * @controller: This is the controller object that receives the link up + * notification. + * @port: This is the port object associated with the phy. If the is no + * associated port this is an SCI_INVALID_HANDLE. + * @phy: This is the phy object which has gone ready. + * + * This method handles the manual port configuration link up notifications. + * Since all ports and phys are associate at initialization time we just turn + * around and notifiy the port object that there is a link up. If this PHY is + * not associated with a port there is no action taken. Is it possible to get a + * link up notification from a phy that has no assocoated port? + */ +static void scic_sds_mpc_agent_link_up( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + /* + * If the port has an invalid handle then the phy was not assigned to + * a port. This is because the phy was not given the same SAS Address + * as the other PHYs in the port. */ + if (port != SCI_INVALID_HANDLE) { + port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); + + scic_sds_port_link_up(port, phy); + + if ((port->active_phy_mask & (1 << scic_sds_phy_get_index(phy))) != 0) { + port_agent->phy_configured_mask |= (1 << scic_sds_phy_get_index(phy)); + } + } +} + +/** + * + * @controller: This is the controller object that receives the link down + * notification. + * @port: This is the port object associated with the phy. If the is no + * associated port this is an SCI_INVALID_HANDLE. The port is an invalid + * handle only if the phy was never port of this port. This happens when + * the phy is not broadcasting the same SAS address as the other phys in the + * assigned port. + * @phy: This is the phy object which has gone link down. + * + * This method handles the manual port configuration link down notifications. + * Since all ports and phys are associated at initialization time we just turn + * around and notifiy the port object of the link down event. If this PHY is + * not associated with a port there is no action taken. Is it possible to get a + * link down notification from a phy that has no assocoated port? + */ +static void scic_sds_mpc_agent_link_down( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + if (port != SCI_INVALID_HANDLE) { + /* + * If we can form a new port from the remainder of the phys then we want + * to start the timer to allow the SCI User to cleanup old devices and + * rediscover the port before rebuilding the port with the phys that + * remain in the ready state. */ + port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy)); + port_agent->phy_configured_mask &= ~(1 << scic_sds_phy_get_index(phy)); + + /* + * Check to see if there are more phys waiting to be configured into a port. + * If there are allow the SCI User to tear down this port, if necessary, and + * then reconstruc the port after the timeout. */ + if ( + (port_agent->phy_configured_mask == 0x0000) + && (port_agent->phy_ready_mask != 0x0000) + && !port_agent->timer_pending + ) { + port_agent->timer_pending = true; + + scic_cb_timer_start( + controller, + port_agent->timer, + SCIC_SDS_MPC_RECONFIGURATION_TIMEOUT + ); + } + + scic_sds_port_link_down(port, phy); + } +} + +/* + * ****************************************************************************** + * Automatic port configuration agent routines + * ****************************************************************************** */ + +/** + * + * + * This routine will verify that the phys are assigned a valid SAS address for + * automatic port configuration mode. + */ +static enum sci_status scic_sds_apc_agent_validate_phy_configuration( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent) +{ + u8 phy_index; + u8 port_index; + struct sci_sas_address sas_address; + struct sci_sas_address phy_assigned_address; + + phy_index = 0; + + while (phy_index < SCI_MAX_PHYS) { + port_index = phy_index; + + /* Get the assigned SAS Address for the first PHY on the controller. */ + scic_sds_phy_get_sas_address( + &controller->phy_table[phy_index], &sas_address + ); + + while (++phy_index < SCI_MAX_PHYS) { + scic_sds_phy_get_sas_address( + &controller->phy_table[phy_index], &phy_assigned_address + ); + + /* Verify each of the SAS address are all the same for every PHY */ + if (sci_sas_address_compare(sas_address, phy_assigned_address) == 0) { + port_agent->phy_valid_port_range[phy_index].min_index = port_index; + port_agent->phy_valid_port_range[phy_index].max_index = phy_index; + } else { + port_agent->phy_valid_port_range[phy_index].min_index = phy_index; + port_agent->phy_valid_port_range[phy_index].max_index = phy_index; + break; + } + } + } + + return scic_sds_port_configuration_agent_validate_ports(controller, port_agent); +} + +/** + * + * @controller: This is the controller that to which the port agent is assigned. + * @port_agent: This is the port agent that is requesting the timer start + * operation. + * @phy: This is the phy that has caused the timer operation to be scheduled. + * + * This routine will restart the automatic port configuration timeout timer for + * the next time period. This could be caused by either a link down event or a + * link up event where we can not yet tell to which port a phy belongs. + */ +static void scic_sds_apc_agent_start_timer( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent, + struct scic_sds_phy *phy, + u32 timeout) +{ + if (port_agent->timer_pending) { + scic_cb_timer_stop(controller, port_agent->timer); + } + + port_agent->timer_pending = true; + + scic_cb_timer_start(controller, port_agent->timer, timeout); +} + +/** + * + * @controller: This is the controller object that receives the link up + * notification. + * @phy: This is the phy object which has gone link up. + * + * This method handles the automatic port configuration for link up + * notifications. + */ +static void scic_sds_apc_agent_configure_ports( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent, + struct scic_sds_phy *phy, + bool start_timer) +{ + u8 port_index; + enum sci_status status; + struct scic_sds_port *port; + struct scic_sds_port *port_handle; + enum SCIC_SDS_APC_ACTIVITY apc_activity = SCIC_SDS_APC_SKIP_PHY; + + port = scic_sds_port_configuration_agent_find_port(controller, phy); + + if (port != SCI_INVALID_HANDLE) { + if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)) + apc_activity = SCIC_SDS_APC_ADD_PHY; + else + apc_activity = SCIC_SDS_APC_SKIP_PHY; + } else { + /* + * There is no matching Port for this PHY so lets search through the + * Ports and see if we can add the PHY to its own port or maybe start + * the timer and wait to see if a wider port can be made. + * + * Note the break when we reach the condition of the port id == phy id */ + for ( + port_index = port_agent->phy_valid_port_range[phy->phy_index].min_index; + port_index <= port_agent->phy_valid_port_range[phy->phy_index].max_index; + port_index++ + ) { + scic_controller_get_port_handle(controller, port_index, &port_handle); + + port = (struct scic_sds_port *)port_handle; + + /* First we must make sure that this PHY can be added to this Port. */ + if (scic_sds_port_is_valid_phy_assignment(port, phy->phy_index)) { + /* + * Port contains a PHY with a greater PHY ID than the current + * PHY that has gone link up. This phy can not be part of any + * port so skip it and move on. */ + if (port->active_phy_mask > (1 << phy->phy_index)) { + apc_activity = SCIC_SDS_APC_SKIP_PHY; + break; + } + + /* + * We have reached the end of our Port list and have not found + * any reason why we should not either add the PHY to the port + * or wait for more phys to become active. */ + if (port->physical_port_index == phy->phy_index) { + /* + * The Port either has no active PHYs. + * Consider that if the port had any active PHYs we would have + * or active PHYs with + * a lower PHY Id than this PHY. */ + if (apc_activity != SCIC_SDS_APC_START_TIMER) { + apc_activity = SCIC_SDS_APC_ADD_PHY; + } + + break; + } + + /* + * The current Port has no active PHYs and this PHY could be part + * of this Port. Since we dont know as yet setup to start the + * timer and see if there is a better configuration. */ + if (port->active_phy_mask == 0) { + apc_activity = SCIC_SDS_APC_START_TIMER; + } + } else if (port->active_phy_mask != 0) { + /* + * The Port has an active phy and the current Phy can not + * participate in this port so skip the PHY and see if + * there is a better configuration. */ + apc_activity = SCIC_SDS_APC_SKIP_PHY; + } + } + } + + /* + * Check to see if the start timer operations should instead map to an + * add phy operation. This is caused because we have been waiting to + * add a phy to a port but could not becuase the automatic port + * configuration engine had a choice of possible ports for the phy. + * Since we have gone through a timeout we are going to restrict the + * choice to the smallest possible port. */ + if ( + (start_timer == false) + && (apc_activity == SCIC_SDS_APC_START_TIMER) + ) { + apc_activity = SCIC_SDS_APC_ADD_PHY; + } + + switch (apc_activity) { + case SCIC_SDS_APC_ADD_PHY: + status = scic_sds_port_add_phy(port, phy); + + if (status == SCI_SUCCESS) { + port_agent->phy_configured_mask |= (1 << phy->phy_index); + } + break; + + case SCIC_SDS_APC_START_TIMER: + scic_sds_apc_agent_start_timer( + controller, port_agent, phy, SCIC_SDS_APC_WAIT_LINK_UP_NOTIFICATION + ); + break; + + case SCIC_SDS_APC_SKIP_PHY: + default: + /* do nothing the PHY can not be made part of a port at this time. */ + break; + } +} + +/** + * + * @controller: This is the controller object that receives the link up + * notification. + * @port: This is the port object associated with the phy. If the is no + * associated port this is an SCI_INVALID_HANDLE. + * @phy: This is the phy object which has gone link up. + * + * This method handles the automatic port configuration for link up + * notifications. Is it possible to get a link down notification from a phy + * that has no assocoated port? + */ +static void scic_sds_apc_agent_link_up( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + BUG_ON(port != SCI_INVALID_HANDLE); + + port_agent->phy_ready_mask |= (1 << scic_sds_phy_get_index(phy)); + + scic_sds_apc_agent_configure_ports(controller, port_agent, phy, true); +} + +/** + * + * @controller: This is the controller object that receives the link down + * notification. + * @port: This is the port object associated with the phy. If the is no + * associated port this is an SCI_INVALID_HANDLE. + * @phy: This is the phy object which has gone link down. + * + * This method handles the automatic port configuration link down + * notifications. not associated with a port there is no action taken. Is it + * possible to get a link down notification from a phy that has no assocoated + * port? + */ +static void scic_sds_apc_agent_link_down( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + port_agent->phy_ready_mask &= ~(1 << scic_sds_phy_get_index(phy)); + + if (port != SCI_INVALID_HANDLE) { + if (port_agent->phy_configured_mask & (1 << phy->phy_index)) { + enum sci_status status; + + status = scic_sds_port_remove_phy(port, phy); + + if (status == SCI_SUCCESS) { + port_agent->phy_configured_mask &= ~(1 << phy->phy_index); + } + } + } +} + +/** + * + * + * This routine will try to configure the phys into ports when the timer fires. + */ +static void scic_sds_apc_agent_timeout_handler( + void *object) +{ + u32 index; + struct scic_sds_port_configuration_agent *port_agent; + struct scic_sds_controller *controller = (struct scic_sds_controller *)object; + u16 configure_phy_mask; + + port_agent = scic_sds_controller_get_port_configuration_agent(controller); + + port_agent->timer_pending = false; + + configure_phy_mask = ~port_agent->phy_configured_mask & port_agent->phy_ready_mask; + + if (configure_phy_mask != 0x00) { + for (index = 0; index < SCI_MAX_PHYS; index++) { + if (configure_phy_mask & (1 << index)) { + scic_sds_apc_agent_configure_ports( + controller, port_agent, &controller->phy_table[index], false + ); + } + } + } +} + +/* + * ****************************************************************************** + * Public port configuration agent routines + * ****************************************************************************** */ + +/** + * + * + * This method will construct the port configuration agent for operation. This + * call is universal for both manual port configuration and automatic port + * configuration modes. + */ +void scic_sds_port_configuration_agent_construct( + struct scic_sds_port_configuration_agent *port_agent) +{ + u32 index; + + port_agent->phy_configured_mask = 0x00; + port_agent->phy_ready_mask = 0x00; + + port_agent->link_up_handler = NULL; + port_agent->link_down_handler = NULL; + + port_agent->timer_pending = false; + port_agent->timer = NULL; + + for (index = 0; index < SCI_MAX_PORTS; index++) { + port_agent->phy_valid_port_range[index].min_index = 0; + port_agent->phy_valid_port_range[index].max_index = 0; + } +} + +/** + * + * @controller: This is the controller object for which the port agent is being + * initialized. + * + * This method will construct the port configuration agent for this controller. + */ +enum sci_status scic_sds_port_configuration_agent_initialize( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent) +{ + enum sci_status status = SCI_SUCCESS; + enum SCIC_PORT_CONFIGURATION_MODE mode; + + mode = scic_sds_controller_get_port_configuration_mode(controller); + + if (mode == SCIC_PORT_MANUAL_CONFIGURATION_MODE) { + status = scic_sds_mpc_agent_validate_phy_configuration(controller, port_agent); + + port_agent->link_up_handler = scic_sds_mpc_agent_link_up; + port_agent->link_down_handler = scic_sds_mpc_agent_link_down; + + port_agent->timer = scic_cb_timer_create( + controller, + scic_sds_mpc_agent_timeout_handler, + controller + ); + } else { + status = scic_sds_apc_agent_validate_phy_configuration(controller, port_agent); + + port_agent->link_up_handler = scic_sds_apc_agent_link_up; + port_agent->link_down_handler = scic_sds_apc_agent_link_down; + + port_agent->timer = scic_cb_timer_create( + controller, + scic_sds_apc_agent_timeout_handler, + controller + ); + } + + /* Make sure we have actually gotten a timer */ + if ((status == SCI_SUCCESS) && (port_agent->timer == NULL)) { + dev_err(scic_to_dev(controller), + "%s: Controller 0x%p automatic port configuration " + "agent could not get timer.\n", + __func__, + controller); + + status = SCI_FAILURE; + } + + return status; +} diff --git a/drivers/scsi/isci/core/scic_sds_port_configuration_agent.h b/drivers/scsi/isci/core/scic_sds_port_configuration_agent.h new file mode 100644 index 000000000000..4146735ffc32 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_port_configuration_agent.h @@ -0,0 +1,108 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_PORT_CONFIGURATION_AGENT_H_ +#define _SCIC_SDS_PORT_CONFIGURATION_AGENT_H_ + +/** + * This file contains the structures, constants and prototypes used for the + * core controller automatic port configuration engine. + * + * + */ + +#include "scic_sds_port.h" +#include "scic_sds_phy.h" + +struct scic_sds_controller; +struct scic_sds_port_configuration_agent; +struct scic_sds_port; +struct scic_sds_phy; + +typedef void (*SCIC_SDS_PORT_CONFIGURATION_AGENT_PHY_HANDLER_T)( + struct scic_sds_controller *, + struct scic_sds_port_configuration_agent *, + struct scic_sds_port *, + struct scic_sds_phy * + ); + +struct SCIC_SDS_PORT_RANGE { + u8 min_index; + u8 max_index; +}; + +struct scic_sds_port_configuration_agent { + u16 phy_configured_mask; + u16 phy_ready_mask; + + struct SCIC_SDS_PORT_RANGE phy_valid_port_range[SCI_MAX_PHYS]; + + bool timer_pending; + + SCIC_SDS_PORT_CONFIGURATION_AGENT_PHY_HANDLER_T link_up_handler; + SCIC_SDS_PORT_CONFIGURATION_AGENT_PHY_HANDLER_T link_down_handler; + + void *timer; + +}; + +void scic_sds_port_configuration_agent_construct( + struct scic_sds_port_configuration_agent *port_agent); + +enum sci_status scic_sds_port_configuration_agent_initialize( + struct scic_sds_controller *controller, + struct scic_sds_port_configuration_agent *port_agent); + +#endif /* _SCIC_SDS_PORT_CONFIGURATION_AGENT_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_port_registers.h b/drivers/scsi/isci/core/scic_sds_port_registers.h new file mode 100644 index 000000000000..cf8bc073b952 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_port_registers.h @@ -0,0 +1,223 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_PORT_REGISTERS_H_ +#define _SCIC_SDS_PORT_REGISTERS_H_ + +/** + * This file contains a set of macros that assist in reading the SCU hardware + * registers. + * + * + */ + +/** + * scu_transport_layer_read() - + * + * Macro to read the transport layer register associated with this port object. + */ +#define scu_transport_layer_read(port, reg) \ + scu_register_read(\ + scic_sds_port_get_controller(port), \ + (port)->transport_layer_registers->reg \ + ) + +/** + * scu_transport_layer_write() - + * + * Macro to write the transport layer register associated with this port object. + */ +#define scu_transport_layer_write(port, reg, value) \ + scu_register_write(\ + scic_sds_port_get_controller(port), \ + (port)->transport_layer_registers->reg, \ + (value) \ + ) + +/** + * scu_port_task_scheduler_read() - + * + * Macro to read the port task scheduler register associated with this port + * object + */ +#define scu_port_task_scheduler_read(port, reg) \ + scu_register_read(\ + scic_sds_port_get_controller(port), \ + (port)->port_task_scheduler_registers->reg \ + ) + +/** + * scu_port_task_scheduler_write() - + * + * Macro to write the port task scheduler register associated with this port + * object + */ +#define scu_port_task_scheduler_write(port, reg, value) \ + scu_register_write(\ + scic_sds_port_get_controller(port), \ + (port)->port_task_scheduler_registers->reg, \ + (value) \ + ) + +#define scu_port_viit_register_write(port, reg, value) \ + scu_register_write(\ + scic_sds_port_get_controller(port), \ + (port)->viit_registers->reg, \ + (value) \ + ) + +/* + * **************************************************************************** + * * Transport Layer registers controlled by the port object + * **************************************************************************** */ + +/** + * SCU_TLCR_READ() - + * + * This macro reads the Transport layer control register + */ +#define SCU_TLCR_READ(port) \ + scu_transport_layer_read(port, control) + +/** + * SCU_TLCR_WRITE() - + * + * This macro writes the Transport layer control register + */ +#define SCU_TLCR_WRITE(port, value) \ + scu_transport_layer_write(port, control, value) + +/** + * SCU_TLADTR_READ() - + * + * This macro reads the Transport layer address translation register + */ +#define SCU_TLADTR_READ(port) \ + scu_transport_layer_read(port, address_translation) + +/** + * SCU_TLADTR_WRITE() - + * + * This macro writes the Transport layer address translation register + */ +#define SCU_TLADTR_WRITE(port) \ + scu_transport_layer_write(port, address_translation, value) + +/** + * SCU_STPTLDARNI_WRITE() - + * + * This macro writes the STP Transport Layer Direct Attached RNi register. + */ +#define SCU_STPTLDARNI_WRITE(port, index) \ + scu_transport_layer_write(port, stp_rni, index) + +/** + * SCU_STPTLDARNI_READ() - + * + * This macro reads the STP Transport Layer Direct Attached RNi register. + */ +#define SCU_STPTLDARNI_READ(port) \ + scu_transport_layer_read(port, stp_rni) + +/* + * **************************************************************************** + * * Port Task Scheduler registers controlled by the port object + * **************************************************************************** */ + +/** + * SCU_PTSxCR_READ() - + * + * Macro to read the port task scheduler control register + */ +#define SCU_PTSxCR_READ(port) \ + scu_port_task_scheduler_read(port, control) + +/** + * SCU_PTSxCR_WRITE() - + * + * Macro to write the port task scheduler control regsister + */ +#define SCU_PTSxCR_WRITE(port, value) \ + scu_port_task_scheduler_write(port, control, value) + +/* + * **************************************************************************** + * * Port PE Configuration registers + * **************************************************************************** */ + +/** + * SCU_PCSPExCR_WRITE() - + * + * Macro to write the PE Port Configuration Register + */ +#define SCU_PCSPExCR_WRITE(port, phy_id, value) \ + scu_register_write(\ + scic_sds_port_get_controller(port), \ + (port)->port_pe_configuration_register[phy_id], \ + (value) \ + ) + +/** + * SCU_PCSPExCR_READ() - + * + * Macro to read the PE Port Configuration Regsiter + */ +#define SCU_PCSPExCR_READ(port, phy_id) \ + scu_register_read(\ + scic_sds_port_get_controller(port), \ + (port)->port_pe_configuration_register[phy_id] \ + ) + +#endif /* _SCIC_SDS_PORT_REGISTERS_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_remote_device.c b/drivers/scsi/isci/core/scic_sds_remote_device.c new file mode 100644 index 000000000000..21f03bc7d797 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_remote_device.c @@ -0,0 +1,2252 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the implementation of remote device methods. + * + * + */ + +#include "intel_sas.h" +#include "sci_util.h" +#include "sci_environment.h" +#include "scic_port.h" +#include "scic_phy.h" +#include "scic_remote_device.h" +#include "scic_sds_port.h" +#include "scic_sds_phy.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_sds_controller.h" + +#define SCIC_SDS_REMOTE_DEVICE_RESET_TIMEOUT (1000) + +/* + * ***************************************************************************** + * * CORE REMOTE DEVICE PRIVATE METHODS + * ***************************************************************************** */ + +/* + * ***************************************************************************** + * * CORE REMOTE DEVICE PUBLIC METHODS + * ***************************************************************************** */ + +u32 scic_remote_device_get_object_size(void) +{ + return sizeof(struct scic_sds_remote_device) + + sizeof(struct scic_sds_remote_node_context); +} + +/* --------------------------------------------------------------------------- */ + +void scic_remote_device_construct(struct scic_sds_port *sci_port, + struct scic_sds_remote_device *sci_dev) +{ + sci_dev->owning_port = sci_port; + sci_dev->started_request_count = 0; + sci_dev->rnc = (struct scic_sds_remote_node_context *) &sci_dev[1]; + + sci_base_remote_device_construct( + &sci_dev->parent, + scic_sds_remote_device_state_table + ); + + scic_sds_remote_node_context_construct( + sci_dev, + sci_dev->rnc, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ); + + sci_object_set_association(sci_dev->rnc, sci_dev); +} + + +enum sci_status scic_remote_device_da_construct( + struct scic_sds_remote_device *sci_dev) +{ + enum sci_status status; + u16 remote_node_index; + struct sci_sas_identify_address_frame_protocols protocols; + + /* + * This information is request to determine how many remote node context + * entries will be needed to store the remote node. + */ + scic_sds_port_get_attached_protocols(sci_dev->owning_port, &protocols); + sci_dev->target_protocols.u.all = protocols.u.all; + sci_dev->is_direct_attached = true; +#if !defined(DISABLE_ATAPI) + sci_dev->is_atapi = scic_sds_remote_device_is_atapi(sci_dev); +#endif + + status = scic_sds_controller_allocate_remote_node_context( + sci_dev->owning_port->owning_controller, + sci_dev, + &remote_node_index); + + if (status == SCI_SUCCESS) { + scic_sds_remote_node_context_set_remote_node_index( + sci_dev->rnc, remote_node_index); + + scic_sds_port_get_attached_sas_address( + sci_dev->owning_port, &sci_dev->device_address); + + if (sci_dev->target_protocols.u.bits.attached_ssp_target) { + sci_dev->has_ready_substate_machine = false; + } else if (sci_dev->target_protocols.u.bits.attached_stp_target) { + sci_dev->has_ready_substate_machine = true; + + sci_base_state_machine_construct( + &sci_dev->ready_substate_machine, + &sci_dev->parent.parent, + scic_sds_stp_remote_device_ready_substate_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE); + } else if (sci_dev->target_protocols.u.bits.attached_smp_target) { + sci_dev->has_ready_substate_machine = true; + + /* add the SMP ready substate machine construction here */ + sci_base_state_machine_construct( + &sci_dev->ready_substate_machine, + &sci_dev->parent.parent, + scic_sds_smp_remote_device_ready_substate_table, + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE); + } + + sci_dev->connection_rate = scic_sds_port_get_max_allowed_speed( + sci_dev->owning_port); + + /* / @todo Should I assign the port width by reading all of the phys on the port? */ + sci_dev->device_port_width = 1; + } + + return status; +} + + +static void scic_sds_remote_device_get_info_from_smp_discover_response( + struct scic_sds_remote_device *this_device, + struct smp_response_discover *discover_response) +{ + /* decode discover_response to set sas_address to this_device. */ + this_device->device_address.high = + discover_response->attached_sas_address.high; + + this_device->device_address.low = + discover_response->attached_sas_address.low; + + this_device->target_protocols.u.all = discover_response->protocols.u.all; +} + + +enum sci_status scic_remote_device_ea_construct( + struct scic_sds_remote_device *sci_dev, + struct smp_response_discover *discover_response) +{ + enum sci_status status; + struct scic_sds_controller *the_controller; + + the_controller = scic_sds_port_get_controller(sci_dev->owning_port); + + scic_sds_remote_device_get_info_from_smp_discover_response( + sci_dev, discover_response); + + status = scic_sds_controller_allocate_remote_node_context( + the_controller, sci_dev, &sci_dev->rnc->remote_node_index); + + if (status == SCI_SUCCESS) { + if (sci_dev->target_protocols.u.bits.attached_ssp_target) { + sci_dev->has_ready_substate_machine = false; + } else if (sci_dev->target_protocols.u.bits.attached_smp_target) { + sci_dev->has_ready_substate_machine = true; + + /* add the SMP ready substate machine construction here */ + sci_base_state_machine_construct( + &sci_dev->ready_substate_machine, + &sci_dev->parent.parent, + scic_sds_smp_remote_device_ready_substate_table, + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE); + } else if (sci_dev->target_protocols.u.bits.attached_stp_target) { + sci_dev->has_ready_substate_machine = true; + + sci_base_state_machine_construct( + &sci_dev->ready_substate_machine, + &sci_dev->parent.parent, + scic_sds_stp_remote_device_ready_substate_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE); + } + + /* + * For SAS-2 the physical link rate is actually a logical link + * rate that incorporates multiplexing. The SCU doesn't + * incorporate multiplexing and for the purposes of the + * connection the logical link rate is that same as the + * physical. Furthermore, the SAS-2 and SAS-1.1 fields overlay + * one another, so this code works for both situations. */ + sci_dev->connection_rate = min_t(u16, + scic_sds_port_get_max_allowed_speed(sci_dev->owning_port), + discover_response->u2.sas1_1.negotiated_physical_link_rate + ); + + /* / @todo Should I assign the port width by reading all of the phys on the port? */ + sci_dev->device_port_width = 1; + } + + return status; +} + +enum sci_status scic_remote_device_destruct( + struct scic_sds_remote_device *sci_dev) +{ + return sci_dev->state_handlers->parent.destruct_handler(&sci_dev->parent); +} + + +enum sci_status scic_remote_device_start( + struct scic_sds_remote_device *sci_dev, + u32 timeout) +{ + return sci_dev->state_handlers->parent.start_handler(&sci_dev->parent); +} + + +enum sci_status scic_remote_device_stop( + struct scic_sds_remote_device *sci_dev, + u32 timeout) +{ + return sci_dev->state_handlers->parent.stop_handler(&sci_dev->parent); +} + + +enum sci_status scic_remote_device_reset( + struct scic_sds_remote_device *sci_dev) +{ + return sci_dev->state_handlers->parent.reset_handler(&sci_dev->parent); +} + + +enum sci_status scic_remote_device_reset_complete( + struct scic_sds_remote_device *sci_dev) +{ + return sci_dev->state_handlers->parent.reset_complete_handler(&sci_dev->parent); +} + + +enum sci_sas_link_rate scic_remote_device_get_connection_rate( + struct scic_sds_remote_device *sci_dev) +{ + return sci_dev->connection_rate; +} + + +void scic_remote_device_get_protocols( + struct scic_sds_remote_device *sci_dev, + struct smp_discover_response_protocols *pr) +{ + pr->u.all = sci_dev->target_protocols.u.all; +} + +#if !defined(DISABLE_ATAPI) +bool scic_remote_device_is_atapi(struct scic_sds_remote_device *sci_dev) +{ + return sci_dev->is_atapi; +} +#endif + + +/* + * ***************************************************************************** + * * SCU DRIVER STANDARD (SDS) REMOTE DEVICE IMPLEMENTATIONS + * ***************************************************************************** */ + +/** + * + * + * Remote device timer requirements + */ +#define SCIC_SDS_REMOTE_DEVICE_MINIMUM_TIMER_COUNT (0) +#define SCIC_SDS_REMOTE_DEVICE_MAXIMUM_TIMER_COUNT (SCI_MAX_REMOTE_DEVICES) + + +/** + * + * @this_device: The remote device for which the suspend is being requested. + * + * This method invokes the remote device suspend state handler. enum sci_status + */ +enum sci_status scic_sds_remote_device_suspend( + struct scic_sds_remote_device *this_device, + u32 suspend_type) +{ + return this_device->state_handlers->suspend_handler(this_device, suspend_type); +} + +/** + * + * @this_device: The remote device for which the resume is being requested. + * + * This method invokes the remote device resume state handler. enum sci_status + */ +enum sci_status scic_sds_remote_device_resume( + struct scic_sds_remote_device *this_device) +{ + return this_device->state_handlers->resume_handler(this_device); +} + +/** + * + * @this_device: The remote device for which the event handling is being + * requested. + * @frame_index: This is the frame index that is being processed. + * + * This method invokes the frame handler for the remote device state machine + * enum sci_status + */ +enum sci_status scic_sds_remote_device_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index) +{ + return this_device->state_handlers->frame_handler(this_device, frame_index); +} + +/** + * + * @this_device: The remote device for which the event handling is being + * requested. + * @event_code: This is the event code that is to be processed. + * + * This method invokes the remote device event handler. enum sci_status + */ +enum sci_status scic_sds_remote_device_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code) +{ + return this_device->state_handlers->event_handler(this_device, event_code); +} + +/** + * + * @controller: The controller that is starting the io request. + * @this_device: The remote device for which the start io handling is being + * requested. + * @io_request: The io request that is being started. + * + * This method invokes the remote device start io handler. enum sci_status + */ +enum sci_status scic_sds_remote_device_start_io( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *this_device, + struct scic_sds_request *io_request) +{ + return this_device->state_handlers->parent.start_io_handler( + &this_device->parent, &io_request->parent); +} + +/** + * + * @controller: The controller that is completing the io request. + * @this_device: The remote device for which the complete io handling is being + * requested. + * @io_request: The io request that is being completed. + * + * This method invokes the remote device complete io handler. enum sci_status + */ +enum sci_status scic_sds_remote_device_complete_io( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *this_device, + struct scic_sds_request *io_request) +{ + return this_device->state_handlers->parent.complete_io_handler( + &this_device->parent, &io_request->parent); +} + +/** + * + * @controller: The controller that is starting the task request. + * @this_device: The remote device for which the start task handling is being + * requested. + * @io_request: The task request that is being started. + * + * This method invokes the remote device start task handler. enum sci_status + */ +enum sci_status scic_sds_remote_device_start_task( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *this_device, + struct scic_sds_request *io_request) +{ + return this_device->state_handlers->parent.start_task_handler( + &this_device->parent, &io_request->parent); +} + +/** + * + * @controller: The controller that is completing the task request. + * @this_device: The remote device for which the complete task handling is + * being requested. + * @io_request: The task request that is being completed. + * + * This method invokes the remote device complete task handler. enum sci_status + */ + +/** + * + * @this_device: + * @request: + * + * This method takes the request and bulids an appropriate SCU context for the + * request and then requests the controller to post the request. none + */ +void scic_sds_remote_device_post_request( + struct scic_sds_remote_device *this_device, + u32 request) +{ + u32 context; + + context = scic_sds_remote_device_build_command_context(this_device, request); + + scic_sds_controller_post_request( + scic_sds_remote_device_get_controller(this_device), + context + ); +} + +#if !defined(DISABLE_ATAPI) +/** + * + * @this_device: The device to be checked. + * + * This method check the signature fis of a stp device to decide whether a + * device is atapi or not. true if a device is atapi device. False if a device + * is not atapi. + */ +bool scic_sds_remote_device_is_atapi( + struct scic_sds_remote_device *this_device) +{ + if (!this_device->target_protocols.u.bits.attached_stp_target) + return false; + else if (this_device->is_direct_attached) { + struct scic_sds_phy *phy; + struct scic_sata_phy_properties properties; + struct sata_fis_reg_d2h *signature_fis; + phy = scic_sds_port_get_a_connected_phy(this_device->owning_port); + scic_sata_phy_get_properties(phy, &properties); + + /* decode the signature fis. */ + signature_fis = &(properties.signature_fis); + + if ((signature_fis->sector_count == 0x01) + && (signature_fis->lba_low == 0x01) + && (signature_fis->lba_mid == 0x14) + && (signature_fis->lba_high == 0xEB) + && ((signature_fis->device & 0x5F) == 0x00) + ) { + /* An ATA device supporting the PACKET command set. */ + return true; + } else + return false; + } else { + /* Expander supported ATAPI device is not currently supported. */ + return false; + } +} +#endif +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the state handlers for the struct scic_sds_remote_device for the + * base state machine. + * + * + */ + +#include "sci_environment.h" +#include "scic_user_callback.h" +#include "scic_controller.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" +#include "scic_sds_request.h" +#include "scic_sds_remote_node_context.h" +#include "scu_event_codes.h" + +/* + * ***************************************************************************** + * * PROTECTED METHODS + * ***************************************************************************** */ + +/** + * + * @user_parameter: This is cast to a remote device object. + * + * This method is called once the remote node context is ready to be freed. + * The remote device can now report that its stop operation is complete. none + */ +static void scic_sds_cb_remote_device_rnc_destruct_complete( + void *user_parameter) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)user_parameter; + + BUG_ON(this_device->started_request_count != 0); + + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_STOPPED + ); +} + +/** + * + * @user_parameter: This is cast to a remote device object. + * + * This method is called once the remote node context has transisitioned to a + * ready state. This is the indication that the remote device object can also + * transition to ready. none + */ +static void scic_sds_remote_device_resume_complete_handler( + void *user_parameter) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)user_parameter; + + if ( + sci_base_state_machine_get_state(&this_device->parent.state_machine) + != SCI_BASE_REMOTE_DEVICE_STATE_READY + ) { + sci_base_state_machine_change_state( + &this_device->parent.state_machine, + SCI_BASE_REMOTE_DEVICE_STATE_READY + ); + } +} + +/** + * + * @device: This parameter specifies the device for which the request is being + * started. + * @request: This parameter specifies the request being started. + * @status: This parameter specifies the current start operation status. + * + * This method will perform the STP request start processing common to IO + * requests and task requests of all types. none + */ +void scic_sds_remote_device_start_request( + struct scic_sds_remote_device *this_device, + struct scic_sds_request *the_request, + enum sci_status status) +{ + /* We still have a fault in starting the io complete it on the port */ + if (status == SCI_SUCCESS) + scic_sds_remote_device_increment_request_count(this_device); + else{ + this_device->owning_port->state_handlers->complete_io_handler( + this_device->owning_port, this_device, the_request + ); + } +} + + +/** + * + * @request: This parameter specifies the request being continued. + * + * This method will continue to post tc for a STP request. This method usually + * serves as a callback when RNC gets resumed during a task management + * sequence. none + */ +void scic_sds_remote_device_continue_request( + struct scic_sds_remote_device *this_device) +{ + /* we need to check if this request is still valid to continue. */ + if (this_device->working_request != NULL) { + struct scic_sds_request *this_request = this_device->working_request; + struct scic_sds_controller *scic = this_request->owning_controller; + u32 state = scic->parent.state_machine.current_state_id; + sci_base_controller_request_handler_t continue_io; + + continue_io = scic_sds_controller_state_handler_table[state].base.continue_io; + continue_io(&scic->parent, &this_request->target_device->parent, + &this_request->parent); + } +} + +/** + * + * @user_parameter: This is cast to a remote device object. + * + * This method is called once the remote node context has reached a suspended + * state. The remote device can now report that its suspend operation is + * complete. none + */ + +/** + * This method will terminate all of the IO requests in the controllers IO + * request table that were targeted for this device. + * @this_device: This parameter specifies the remote device for which to + * attempt to terminate all requests. + * + * This method returns an indication as to whether all requests were + * successfully terminated. If a single request fails to be terminated, then + * this method will return the failure. + */ +static enum sci_status scic_sds_remote_device_terminate_requests( + struct scic_sds_remote_device *this_device) +{ + enum sci_status status = SCI_SUCCESS; + enum sci_status terminate_status = SCI_SUCCESS; + struct scic_sds_request *the_request; + u32 index; + u32 request_count = this_device->started_request_count; + + for (index = 0; + (index < SCI_MAX_IO_REQUESTS) && (request_count > 0); + index++) { + the_request = this_device->owning_port->owning_controller->io_request_table[index]; + + if ((the_request != NULL) && (the_request->target_device == this_device)) { + terminate_status = scic_controller_terminate_request( + this_device->owning_port->owning_controller, + this_device, + the_request + ); + + if (terminate_status != SCI_SUCCESS) + status = terminate_status; + + request_count--; + } + } + + return status; +} + +/* + * ***************************************************************************** + * * DEFAULT STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default start handler. It logs a warning and returns a + * failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_start_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to start while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default stop handler. It logs a warning and returns a + * failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_remote_device_default_stop_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to stop while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default fail handler. It logs a warning and returns a + * failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_fail_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to fail while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default destruct handler. It logs a warning and returns + * a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_destruct_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to destroy while in " + "wrong state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default reset handler. It logs a warning and returns a + * failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_reset_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to reset while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default reset complete handler. It logs a warning and + * returns a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_reset_complete_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to complete reset while " + "in wrong state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default suspend handler. It logs a warning and returns a + * failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_suspend_handler( + struct scic_sds_remote_device *this_device, + u32 suspend_type) +{ + dev_warn(scirdev_to_dev(this_device), + "%s: SCIC Remote Device 0x%p requested to suspend %d while " + "in wrong state %d\n", + __func__, + this_device, + suspend_type, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + this_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * + * This method is the default resume handler. It logs a warning and returns a + * failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_resume_handler( + struct scic_sds_remote_device *this_device) +{ + dev_warn(scirdev_to_dev(this_device), + "%s: SCIC Remote Device requested to resume while in " + "wrong state %d\n", + __func__, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + this_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @event_code: The event code that the struct scic_sds_controller wants the device + * object to process. + * + * This method is the default event handler. It will call the RNC state + * machine handler for any RNC events otherwise it will log a warning and + * returns a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_remote_device_core_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code, + bool is_ready_state) +{ + enum sci_status status; + + switch (scu_get_event_type(event_code)) { + case SCU_EVENT_TYPE_RNC_OPS_MISC: + case SCU_EVENT_TYPE_RNC_SUSPEND_TX: + case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: + status = scic_sds_remote_node_context_event_handler(this_device->rnc, event_code); + break; + case SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT: + + if (scu_get_event_code(event_code) == SCU_EVENT_IT_NEXUS_TIMEOUT) { + status = SCI_SUCCESS; + + /* Suspend the associated RNC */ + scic_sds_remote_node_context_suspend(this_device->rnc, + SCI_SOFTWARE_SUSPENSION, + NULL, NULL); + + dev_dbg(scirdev_to_dev(this_device), + "%s: device: %p event code: %x: %s\n", + __func__, this_device, event_code, + (is_ready_state) + ? "I_T_Nexus_Timeout event" + : "I_T_Nexus_Timeout event in wrong state"); + + break; + } + /* Else, fall through and treat as unhandled... */ + + default: + dev_dbg(scirdev_to_dev(this_device), + "%s: device: %p event code: %x: %s\n", + __func__, this_device, event_code, + (is_ready_state) + ? "unexpected event" + : "unexpected event in wrong state"); + status = SCI_FAILURE_INVALID_STATE; + break; + } + + return status; +} +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @event_code: The event code that the struct scic_sds_controller wants the device + * object to process. + * + * This method is the default event handler. It will call the RNC state + * machine handler for any RNC events otherwise it will log a warning and + * returns a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +static enum sci_status scic_sds_remote_device_default_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code) +{ + return scic_sds_remote_device_core_event_handler(this_device, + event_code, + false); +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @frame_index: The frame index for which the struct scic_sds_controller wants this + * device object to process. + * + * This method is the default unsolicited frame handler. It logs a warning, + * releases the frame and returns a failure. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index) +{ + dev_warn(scirdev_to_dev(this_device), + "%s: SCIC Remote Device requested to handle frame %x " + "while in wrong state %d\n", + __func__, + frame_index, + sci_base_state_machine_get_state( + &this_device->parent.state_machine)); + + /* Return the frame back to the controller */ + scic_sds_controller_release_frame( + scic_sds_remote_device_get_controller(this_device), frame_index + ); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @request: The struct sci_base_request which is then cast into a SCIC_SDS_IO_REQUEST + * to start. + * + * This method is the default start io handler. It logs a warning and returns + * a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_start_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to start io request %p " + "while in wrong state %d\n", + __func__, + request, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + (struct scic_sds_remote_device *)device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @request: The struct sci_base_request which is then cast into a SCIC_SDS_IO_REQUEST + * to complete. + * + * This method is the default complete io handler. It logs a warning and + * returns a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_complete_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to complete io_request %p " + "while in wrong state %d\n", + __func__, + request, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @request: The struct sci_base_request which is then cast into a SCIC_SDS_IO_REQUEST + * to continue. + * + * This method is the default continue io handler. It logs a warning and + * returns a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_default_continue_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + struct scic_sds_remote_device *sds_device = + (struct scic_sds_remote_device *)device; + + dev_warn(scirdev_to_dev(sds_device), + "%s: SCIC Remote Device requested to continue io request %p " + "while in wrong state %d\n", + __func__, + request, + sci_base_state_machine_get_state( + scic_sds_remote_device_get_base_state_machine( + sds_device))); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @request: The struct sci_base_request which is then cast into a SCIC_SDS_IO_REQUEST + * to complete. + * + * This method is the default complete task handler. It logs a warning and + * returns a failure. enum sci_status SCI_FAILURE_INVALID_STATE + */ + +/* + * ***************************************************************************** + * * NORMAL STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @device: The struct sci_base_remote_device which is then cast into a + * struct scic_sds_remote_device. + * @frame_index: The frame index for which the struct scic_sds_controller wants this + * device object to process. + * + * This method is a general ssp frame handler. In most cases the device object + * needs to route the unsolicited frame processing to the io request object. + * This method decodes the tag for the io request object and routes the + * unsolicited frame to that object. enum sci_status SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_remote_device_general_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index) +{ + enum sci_status result; + struct sci_ssp_frame_header *frame_header; + struct scic_sds_request *io_request; + + result = scic_sds_unsolicited_frame_control_get_header( + &(scic_sds_remote_device_get_controller(this_device)->uf_control), + frame_index, + (void **)&frame_header + ); + + if (SCI_SUCCESS == result) { + io_request = scic_sds_controller_get_io_request_from_tag( + scic_sds_remote_device_get_controller(this_device), frame_header->tag); + + if ((io_request == SCI_INVALID_HANDLE) + || (io_request->target_device != this_device)) { + /* + * We could not map this tag to a valid IO request + * Just toss the frame and continue */ + scic_sds_controller_release_frame( + scic_sds_remote_device_get_controller(this_device), frame_index + ); + } else { + /* The IO request is now in charge of releasing the frame */ + result = io_request->state_handlers->frame_handler( + io_request, frame_index); + } + } + + return result; +} + +/** + * + * @[in]: this_device This is the device object that is receiving the event. + * @[in]: event_code The event code to process. + * + * This is a common method for handling events reported to the remote device + * from the controller object. enum sci_status + */ +enum sci_status scic_sds_remote_device_general_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code) +{ + return scic_sds_remote_device_core_event_handler(this_device, + event_code, + true); +} + +/* + * ***************************************************************************** + * * STOPPED STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @device: + * + * This method takes the struct scic_sds_remote_device from a stopped state and + * attempts to start it. The RNC buffer for the device is constructed and the + * device state machine is transitioned to the + * SCIC_BASE_REMOTE_DEVICE_STATE_STARTING. enum sci_status SCI_SUCCESS if there is + * an RNC buffer available to construct the remote device. + * SCI_FAILURE_INSUFFICIENT_RESOURCES if there is no RNC buffer available in + * which to construct the remote device. + */ +static enum sci_status scic_sds_remote_device_stopped_state_start_handler( + struct sci_base_remote_device *device) +{ + enum sci_status status; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + status = scic_sds_remote_node_context_resume( + this_device->rnc, + scic_sds_remote_device_resume_complete_handler, + this_device + ); + + if (status == SCI_SUCCESS) { + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_STARTING + ); + } + + return status; +} + +/** + * + * @this_device: The struct sci_base_remote_device which is cast into a + * struct scic_sds_remote_device. + * + * This method will stop a struct scic_sds_remote_device that is already in a stopped + * state. This is not considered an error since the device is already stopped. + * enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_device_stopped_state_stop_handler( + struct sci_base_remote_device *this_device) +{ + return SCI_SUCCESS; +} + +/** + * + * @this_device: The struct sci_base_remote_device which is cast into a + * struct scic_sds_remote_device. + * + * This method will destruct a struct scic_sds_remote_device that is in a stopped + * state. This is the only state from which a destruct request will succeed. + * The RNi for this struct scic_sds_remote_device is returned to the free pool and the + * device object transitions to the SCI_BASE_REMOTE_DEVICE_STATE_FINAL. + * enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_device_stopped_state_destruct_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + scic_sds_controller_free_remote_node_context( + scic_sds_remote_device_get_controller(this_device), + this_device, + this_device->rnc->remote_node_index + ); + + scic_sds_remote_node_context_set_remote_node_index( + this_device->rnc, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ); + + scic_sds_port_set_direct_attached_device_id( + this_device->owning_port, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ); + + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_FINAL + ); + + return SCI_SUCCESS; +} + +/* + * ***************************************************************************** + * * STARTING STATE HANDLERS + * ***************************************************************************** */ + +static enum sci_status scic_sds_remote_device_starting_state_stop_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + /* + * This device has not yet started so there had better be no IO requests + */ + BUG_ON(this_device->started_request_count != 0); + + /* + * Destroy the remote node context + */ + scic_sds_remote_node_context_destruct( + this_device->rnc, + scic_sds_cb_remote_device_rnc_destruct_complete, + this_device + ); + + /* + * Transition to the stopping state and wait for the remote node to + * complete being posted and invalidated. + */ + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_STOPPING + ); + + return SCI_SUCCESS; +} + +/* + * ***************************************************************************** + * * INITIALIZING STATE HANDLERS + * ***************************************************************************** */ + +/* There is nothing to do here for SSP devices */ + +/* + * ***************************************************************************** + * * READY STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @this_device: The struct scic_sds_remote_device object to be suspended. + * + * This method is the resume handler for the struct scic_sds_remote_device object. It + * will post an RNC resume to the SCU hardware. enum sci_status SCI_SUCCESS + */ + +/** + * + * @device: The struct sci_base_remote_device object which is cast to a + * struct scic_sds_remote_device object. + * + * This method is the default stop handler for the struct scic_sds_remote_device ready + * substate machine. It will stop the current substate machine and transition + * the base state machine to SCI_BASE_REMOTE_DEVICE_STATE_STOPPING. enum sci_status + * SCI_SUCCESS + */ +enum sci_status scic_sds_remote_device_ready_state_stop_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + enum sci_status status = SCI_SUCCESS; + + /* Request the parent state machine to transition to the stopping state */ + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_STOPPING + ); + + if (this_device->started_request_count == 0) { + scic_sds_remote_node_context_destruct( + this_device->rnc, + scic_sds_cb_remote_device_rnc_destruct_complete, + this_device + ); + } else + status = scic_sds_remote_device_terminate_requests(this_device); + + return status; +} + +/** + * + * @device: The struct sci_base_remote_device object which is cast to a + * struct scic_sds_remote_device object. + * + * This is the ready state device reset handler enum sci_status + */ +enum sci_status scic_sds_remote_device_ready_state_reset_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + /* Request the parent state machine to transition to the stopping state */ + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_RESETTING + ); + + return SCI_SUCCESS; +} + +/** + * + * @device: The struct sci_base_remote_device object which is cast to a + * struct scic_sds_remote_device object. + * + * This is the default fail handler for the struct scic_sds_remote_device ready + * substate machine. It will stop the current ready substate and transition + * the remote device object to the SCI_BASE_REMOTE_DEVICE_STATE_FAILED. + * enum sci_status SCI_SUCCESS + */ + +/** + * + * @device: The struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device for which the request is to be started. + * @request: The struct sci_base_request which is cast to a SCIC_SDS_IO_REQUEST that + * is to be started. + * + * This method will attempt to start a task request for this device object. The + * remote device object will issue the start request for the task and if + * successful it will start the request for the port object then increment its + * own requet count. enum sci_status SCI_SUCCESS if the task request is started for + * this device object. SCI_FAILURE_INSUFFICIENT_RESOURCES if the io request + * object could not get the resources to start. + */ +static enum sci_status scic_sds_remote_device_ready_state_start_task_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status result; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *task_request = (struct scic_sds_request *)request; + + /* See if the port is in a state where we can start the IO request */ + result = scic_sds_port_start_io( + scic_sds_remote_device_get_port(this_device), this_device, task_request); + + if (result == SCI_SUCCESS) { + result = scic_sds_remote_node_context_start_task( + this_device->rnc, task_request + ); + + if (result == SCI_SUCCESS) { + result = scic_sds_request_start(task_request); + } + + scic_sds_remote_device_start_request(this_device, task_request, result); + } + + return result; +} + +/** + * + * @device: The struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device for which the request is to be started. + * @request: The struct sci_base_request which is cast to a SCIC_SDS_IO_REQUEST that + * is to be started. + * + * This method will attempt to start an io request for this device object. The + * remote device object will issue the start request for the io and if + * successful it will start the request for the port object then increment its + * own requet count. enum sci_status SCI_SUCCESS if the io request is started for + * this device object. SCI_FAILURE_INSUFFICIENT_RESOURCES if the io request + * object could not get the resources to start. + */ +static enum sci_status scic_sds_remote_device_ready_state_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status result; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *io_request = (struct scic_sds_request *)request; + + /* See if the port is in a state where we can start the IO request */ + result = scic_sds_port_start_io( + scic_sds_remote_device_get_port(this_device), this_device, io_request); + + if (result == SCI_SUCCESS) { + result = scic_sds_remote_node_context_start_io( + this_device->rnc, io_request + ); + + if (result == SCI_SUCCESS) { + result = scic_sds_request_start(io_request); + } + + scic_sds_remote_device_start_request(this_device, io_request, result); + } + + return result; +} + +/** + * + * @device: The struct sci_base_remote_device which is cast to a + * struct scic_sds_remote_device for which the request is to be completed. + * @request: The struct sci_base_request which is cast to a SCIC_SDS_IO_REQUEST that + * is to be completed. + * + * This method will complete the request for the remote device object. The + * method will call the completion handler for the request object and if + * successful it will complete the request on the port object then decrement + * its own started_request_count. enum sci_status + */ +static enum sci_status scic_sds_remote_device_ready_state_complete_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status result; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *the_request = (struct scic_sds_request *)request; + + result = scic_sds_request_complete(the_request); + + if (result == SCI_SUCCESS) { + /* See if the port is in a state where we can start the IO request */ + result = scic_sds_port_complete_io( + scic_sds_remote_device_get_port(this_device), this_device, the_request); + + if (result == SCI_SUCCESS) { + scic_sds_remote_device_decrement_request_count(this_device); + } + } + + return result; +} + +/* + * ***************************************************************************** + * * STOPPING STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @this_device: The struct sci_base_remote_device which is cast into a + * struct scic_sds_remote_device. + * + * This method will stop a struct scic_sds_remote_device that is already in the + * SCI_BASE_REMOTE_DEVICE_STATE_STOPPING state. This is not considered an error + * since we allow a stop request on a device that is alreay stopping or + * stopped. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_device_stopping_state_stop_handler( + struct sci_base_remote_device *device) +{ + /* + * All requests should have been terminated, but if there is an + * attempt to stop a device already in the stopping state, then + * try again to terminate. */ + return scic_sds_remote_device_terminate_requests( + (struct scic_sds_remote_device *)device); +} + + +/** + * + * @device: The device object for which the request is completing. + * @request: The task request that is being completed. + * + * This method completes requests for this struct scic_sds_remote_device while it is + * in the SCI_BASE_REMOTE_DEVICE_STATE_STOPPING state. This method calls the + * complete method for the request object and if that is successful the port + * object is called to complete the task request. Then the device object itself + * completes the task request. If struct scic_sds_remote_device started_request_count + * goes to 0 and the invalidate RNC request has completed the device object can + * transition to the SCI_BASE_REMOTE_DEVICE_STATE_STOPPED. enum sci_status + */ +static enum sci_status scic_sds_remote_device_stopping_state_complete_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status = SCI_SUCCESS; + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + status = scic_sds_request_complete(this_request); + if (status == SCI_SUCCESS) { + status = scic_sds_port_complete_io( + scic_sds_remote_device_get_port(this_device), + this_device, + this_request + ); + + if (status == SCI_SUCCESS) { + scic_sds_remote_device_decrement_request_count(this_device); + + if (scic_sds_remote_device_get_request_count(this_device) == 0) { + scic_sds_remote_node_context_destruct( + this_device->rnc, + scic_sds_cb_remote_device_rnc_destruct_complete, + this_device + ); + } + } + } + + return status; +} + +/* + * ***************************************************************************** + * * RESETTING STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @device: The struct sci_base_remote_device which is to be cast into a + * struct scic_sds_remote_device object. + * + * This method will complete the reset operation when the device is in the + * resetting state. enum sci_status + */ +static enum sci_status scic_sds_remote_device_resetting_state_reset_complete_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + sci_base_state_machine_change_state( + &this_device->parent.state_machine, + SCI_BASE_REMOTE_DEVICE_STATE_READY + ); + + return SCI_SUCCESS; +} + +/** + * + * @device: The struct sci_base_remote_device which is to be cast into a + * struct scic_sds_remote_device object. + * + * This method will stop the remote device while in the resetting state. + * enum sci_status + */ +static enum sci_status scic_sds_remote_device_resetting_state_stop_handler( + struct sci_base_remote_device *device) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + sci_base_state_machine_change_state( + &this_device->parent.state_machine, + SCI_BASE_REMOTE_DEVICE_STATE_STOPPING + ); + + return SCI_SUCCESS; +} + +/** + * + * @device: The device object for which the request is completing. + * @request: The task request that is being completed. + * + * This method completes requests for this struct scic_sds_remote_device while it is + * in the SCI_BASE_REMOTE_DEVICE_STATE_RESETTING state. This method calls the + * complete method for the request object and if that is successful the port + * object is called to complete the task request. Then the device object itself + * completes the task request. enum sci_status + */ +static enum sci_status scic_sds_remote_device_resetting_state_complete_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status = SCI_SUCCESS; + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + + status = scic_sds_request_complete(this_request); + + if (status == SCI_SUCCESS) { + status = scic_sds_port_complete_io( + scic_sds_remote_device_get_port(this_device), this_device, this_request); + + if (status == SCI_SUCCESS) { + scic_sds_remote_device_decrement_request_count(this_device); + } + } + + return status; +} + +/* + * ***************************************************************************** + * * FAILED STATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @device: The struct sci_base_remote_device which is to be cast into a + * struct scic_sds_remote_device object. + * + * This method handles the remove request for a failed struct scic_sds_remote_device + * object. The method will transition the device object to the + * SCIC_BASE_REMOTE_DEVICE_STATE_STOPPING. enum sci_status SCI_SUCCESS + */ + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_remote_device_state_handler +scic_sds_remote_device_state_handler_table[SCI_BASE_REMOTE_DEVICE_MAX_STATES] = +{ + /* SCI_BASE_REMOTE_DEVICE_STATE_INITIAL */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_default_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_default_event_handler, + scic_sds_remote_device_default_frame_handler + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_STOPPED */ + { + { + scic_sds_remote_device_stopped_state_start_handler, + scic_sds_remote_device_stopped_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_stopped_state_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_default_event_handler, + scic_sds_remote_device_default_frame_handler + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_STARTING */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_starting_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_remote_device_default_frame_handler + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_READY */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_ready_state_start_io_handler, + scic_sds_remote_device_ready_state_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_ready_state_start_task_handler, + scic_sds_remote_device_ready_state_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_remote_device_general_frame_handler, + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_STOPPING */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_stopping_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_stopping_state_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_stopping_state_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_remote_device_general_frame_handler + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_FAILED */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_default_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_default_event_handler, + scic_sds_remote_device_general_frame_handler + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_RESETTING */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_resetting_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_resetting_state_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_resetting_state_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_resetting_state_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_default_event_handler, + scic_sds_remote_device_general_frame_handler + }, + /* SCI_BASE_REMOTE_DEVICE_STATE_FINAL */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_default_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_default_event_handler, + scic_sds_remote_device_default_frame_handler + } +}; + +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the operations that are taken on the enter and exit state + * transitions for the struct sci_base_remote_device state machine. + * + * + */ + +#include "scic_remote_device.h" +#include "scic_user_callback.h" +#include "scic_sds_controller.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_INITIAL it + * immediatly transitions the remote device object to the stopped state. none + */ +static void scic_sds_remote_device_initial_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_INITIAL + ); + + /* Initial state is a transitional state to the stopped state */ + sci_base_state_machine_change_state( + scic_sds_remote_device_get_base_state_machine(this_device), + SCI_BASE_REMOTE_DEVICE_STATE_STOPPED + ); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_INITIAL it + * sets the stopped state handlers and if this state is entered from the + * SCI_BASE_REMOTE_DEVICE_STATE_STOPPING then the SCI User is informed that the + * device stop is complete. none + */ +static void scic_sds_remote_device_stopped_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_STOPPED + ); + + /* + * If we are entering from the stopping state let the SCI User know that + * the stop operation has completed. */ + if (this_device->parent.state_machine.previous_state_id + == SCI_BASE_REMOTE_DEVICE_STATE_STOPPING) { + scic_cb_remote_device_stop_complete( + scic_sds_remote_device_get_controller(this_device), + this_device, + SCI_SUCCESS + ); + } +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_STARTING it + * sets the starting state handlers, sets the device not ready, and posts the + * remote node context to the hardware. none + */ +static void scic_sds_remote_device_starting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_controller *the_controller; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + the_controller = scic_sds_remote_device_get_controller(this_device); + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_STARTING + ); + + scic_cb_remote_device_not_ready( + the_controller, + this_device, + SCIC_REMOTE_DEVICE_NOT_READY_START_REQUESTED + ); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the exit method for the SCI_BASE_REMOTE_DEVICE_STATE_STARTING it + * reports that the device start is complete. none + */ +static void scic_sds_remote_device_starting_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + /* + * / @todo Check the device object for the proper return code for this + * / callback */ + scic_cb_remote_device_start_complete( + scic_sds_remote_device_get_controller(this_device), + this_device, + SCI_SUCCESS + ); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_READY it sets + * the ready state handlers, and starts the ready substate machine. none + */ +static void scic_sds_remote_device_ready_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_controller *the_controller; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + the_controller = scic_sds_remote_device_get_controller(this_device); + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_READY + ); + + the_controller->remote_device_sequence[this_device->rnc->remote_node_index]++; + + if (this_device->has_ready_substate_machine) { + sci_base_state_machine_start(&this_device->ready_substate_machine); + } else { + scic_cb_remote_device_ready(the_controller, this_device); + } +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the exit method for the SCI_BASE_REMOTE_DEVICE_STATE_READY it does + * nothing. none + */ +static void scic_sds_remote_device_ready_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_controller *the_controller; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + the_controller = scic_sds_remote_device_get_controller(this_device); + + if (this_device->has_ready_substate_machine) { + sci_base_state_machine_stop(&this_device->ready_substate_machine); + } else { + scic_cb_remote_device_not_ready( + the_controller, + this_device, + SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED + ); + } +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_STOPPING it + * sets the stopping state handlers and posts an RNC invalidate request to the + * SCU hardware. none + */ +static void scic_sds_remote_device_stopping_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_STOPPING + ); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_FAILED it sets + * the stopping state handlers. none + */ +static void scic_sds_remote_device_failed_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_FAILED + ); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_RESETTING it + * sets the resetting state handlers. none + */ +static void scic_sds_remote_device_resetting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_RESETTING + ); + + scic_sds_remote_node_context_suspend( + this_device->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the exit method for the SCI_BASE_REMOTE_DEVICE_STATE_RESETTING it + * does nothing. none + */ +static void scic_sds_remote_device_resetting_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + scic_sds_port_set_direct_attached_device_id( + scic_sds_remote_device_get_port(this_device), + this_device->rnc->remote_node_index + ); + + scic_sds_remote_node_context_resume(this_device->rnc, NULL, NULL); +} + +/** + * + * @object: This is the struct sci_base_object that is cast into a + * struct scic_sds_remote_device. + * + * This is the enter method for the SCI_BASE_REMOTE_DEVICE_STATE_FINAL it sets + * the final state handlers. none + */ +static void scic_sds_remote_device_final_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_remote_device_state_handler_table, + SCI_BASE_REMOTE_DEVICE_STATE_FINAL + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_remote_device_state_table[] = { + [SCI_BASE_REMOTE_DEVICE_STATE_INITIAL] = { + .enter_state = scic_sds_remote_device_initial_state_enter, + }, + [SCI_BASE_REMOTE_DEVICE_STATE_STOPPED] = { + .enter_state = scic_sds_remote_device_stopped_state_enter, + }, + [SCI_BASE_REMOTE_DEVICE_STATE_STARTING] = { + .enter_state = scic_sds_remote_device_starting_state_enter, + .exit_state = scic_sds_remote_device_starting_state_exit + }, + [SCI_BASE_REMOTE_DEVICE_STATE_READY] = { + .enter_state = scic_sds_remote_device_ready_state_enter, + .exit_state = scic_sds_remote_device_ready_state_exit + }, + [SCI_BASE_REMOTE_DEVICE_STATE_STOPPING] = { + .enter_state = scic_sds_remote_device_stopping_state_enter, + }, + [SCI_BASE_REMOTE_DEVICE_STATE_FAILED] = { + .enter_state = scic_sds_remote_device_failed_state_enter, + }, + [SCI_BASE_REMOTE_DEVICE_STATE_RESETTING] = { + .enter_state = scic_sds_remote_device_resetting_state_enter, + .exit_state = scic_sds_remote_device_resetting_state_exit + }, + [SCI_BASE_REMOTE_DEVICE_STATE_FINAL] = { + .enter_state = scic_sds_remote_device_final_state_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_remote_device.h b/drivers/scsi/isci/core/scic_sds_remote_device.h new file mode 100644 index 000000000000..44185a2738ec --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_remote_device.h @@ -0,0 +1,602 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_REMOTE_DEVICE_H_ +#define _SCIC_SDS_REMOTE_DEVICE_H_ + +/** + * This file contains the structures, constants, and prototypes for the + * struct scic_sds_remote_device object. + * + * + */ + +#include "intel_sas.h" +#include "sci_base_remote_device.h" +#include "sci_base_request.h" +#include "scu_remote_node_context.h" +#include "scic_sds_remote_node_context.h" + +struct scic_sds_controller; +struct scic_sds_port; +struct scic_sds_request; +struct scic_sds_remote_device_state_handler; + +/** + * enum SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATES - + * + * This is the enumeration of the ready substates for the + * struct scic_sds_remote_device. + */ +enum SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATES { + /** + * This is the initial state for the remote device ready substate. + */ + SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_INITIAL, + + /** + * This is the ready operational substate for the remote device. This is the + * normal operational state for a remote device. + */ + SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_OPERATIONAL, + + /** + * This is the suspended state for the remote device. This is the state that + * the device is placed in when a RNC suspend is received by the SCU hardware. + */ + SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_SUSPENDED, + + /** + * This is the final state that the device is placed in before a change to the + * base state machine. + */ + SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_FINAL, + + SCIC_SDS_SSP_REMOTE_DEVICE_READY_MAX_SUBSTATES +}; + +/** + * enum SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATES - + * + * This is the enumeration for the struct scic_sds_remote_device ready substates for + * the STP remote device. + */ +enum SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATES { + /** + * This is the idle substate for the stp remote device. When there are no + * active IO for the device it is is in this state. + */ + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE, + + /** + * This is the command state for for the STP remote device. This state is + * entered when the device is processing a non-NCQ command. The device object + * will fail any new start IO requests until this command is complete. + */ + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD, + + /** + * This is the NCQ state for the STP remote device. This state is entered + * when the device is processing an NCQ reuqest. It will remain in this state + * so long as there is one or more NCQ requests being processed. + */ + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ, + + /** + * This is the NCQ error state for the STP remote device. This state is + * entered when an SDB error FIS is received by the device object while in the + * NCQ state. The device object will only accept a READ LOG command while in + * this state. + */ + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ_ERROR, + +#if !defined(DISABLE_ATAPI) + /** + * This is the ATAPI error state for the STP ATAPI remote device. This state is + * entered when ATAPI device sends error status FIS without data while the device + * object is in CMD state. A suspension event is expected in this state. The device + * object will resume right away. + */ + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_ATAPI_ERROR, +#endif + + /** + * This is the READY substate indicates the device is waiting for the RESET task + * coming to be recovered from certain hardware specific error. + */ + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_AWAIT_RESET, + + SCIC_SDS_STP_REMOTE_DEVICE_READY_MAX_SUBSTATES +}; + + +/** + * enum SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATES - + * + * This is the enumeration of the ready substates for the SMP REMOTE DEVICE. + */ + +enum SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATES { + /** + * This is the ready operational substate for the remote device. This is the + * normal operational state for a remote device. + */ + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE, + + /** + * This is the suspended state for the remote device. This is the state that + * the device is placed in when a RNC suspend is received by the SCU hardware. + */ + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD, + + SCIC_SDS_SMP_REMOTE_DEVICE_READY_MAX_SUBSTATES +}; + + + + +/** + * struct scic_sds_remote_device - This structure contains the data for an SCU + * implementation of the SCU Core device data. + * + * + */ +struct scic_sds_remote_device { + /** + * This field is the common base for all remote device objects. + */ + struct sci_base_remote_device parent; + + /** + * This field is the programmed device port width. This value is written to + * the RCN data structure to tell the SCU how many open connections this + * device can have. + */ + u32 device_port_width; + + /** + * This field is the programmed connection rate for this remote device. It is + * used to program the TC with the maximum allowed connection rate. + */ + enum sci_sas_link_rate connection_rate; + + /** + * This field contains the allowed target protocols for this remote device. + */ + struct smp_discover_response_protocols target_protocols; + + /** + * This field contains the device SAS address. + */ + struct sci_sas_address device_address; + + /** + * This filed is assinged the value of true if the device is directly attached + * to the port. + */ + bool is_direct_attached; + +#if !defined(DISABLE_ATAPI) + /** + * This filed is assinged the value of true if the device is an ATAPI device. + */ + bool is_atapi; +#endif + + /** + * This filed contains a pointer back to the port to which this device is + * assigned. + */ + struct scic_sds_port *owning_port; + + /** + * This field contains the SCU silicon remote node context specific + * information. + */ + struct scic_sds_remote_node_context *rnc; + + /** + * This field contains the stated request count for the remote device. The + * device can not reach the SCI_BASE_REMOTE_DEVICE_STATE_STOPPED until all + * requests are complete and the rnc_posted value is false. + */ + u32 started_request_count; + + /** + * This field contains a pointer to the working request object. It is only + * used only for SATA requests since the unsolicited frames we get from the + * hardware have no Tag value to look up the io request object. + */ + struct scic_sds_request *working_request; + + /** + * This field contains the reason for the remote device going not_ready. It is + * assigned in the state handlers and used in the state transition. + */ + u32 not_ready_reason; + + /** + * This field is true if this remote device has an initialzied ready substate + * machine. SSP devices do not have a ready substate machine and STP devices + * have a ready substate machine. + */ + bool has_ready_substate_machine; + + /** + * This field contains the state machine for the ready substate machine for + * this struct scic_sds_remote_device object. + */ + struct sci_base_state_machine ready_substate_machine; + + /** + * This field maintains the set of state handlers for the remote device + * object. These are changed each time the remote device enters a new state. + */ + struct scic_sds_remote_device_state_handler *state_handlers; +}; + + +typedef enum sci_status (*SCIC_SDS_REMOTE_DEVICE_HANDLER_T)( + struct scic_sds_remote_device *this_device); + +typedef enum sci_status (*SCIC_SDS_REMOTE_DEVICE_SUSPEND_HANDLER_T)( + struct scic_sds_remote_device *this_device, + u32 suspend_type); + +typedef enum sci_status (*SCIC_SDS_REMOTE_DEVICE_RESUME_HANDLER_T)( + struct scic_sds_remote_device *this_device); + +typedef enum sci_status (*SCIC_SDS_REMOTE_DEVICE_FRAME_HANDLER_T)( + struct scic_sds_remote_device *this_device, + u32 frame_index); + +typedef enum sci_status (*SCIC_SDS_REMOTE_DEVICE_EVENT_HANDLER_T)( + struct scic_sds_remote_device *this_device, + u32 event_code); + +typedef void (*SCIC_SDS_REMOTE_DEVICE_READY_NOT_READY_HANDLER_T)( + struct scic_sds_remote_device *this_device); + +/** + * struct scic_sds_remote_device_state_handler - This structure conains the + * state handlers that are needed to process requests for the SCU remote + * device objects. + * + * + */ +struct scic_sds_remote_device_state_handler { + struct sci_base_remote_device_state_handler parent; + + SCIC_SDS_REMOTE_DEVICE_SUSPEND_HANDLER_T suspend_handler; + SCIC_SDS_REMOTE_DEVICE_RESUME_HANDLER_T resume_handler; + + SCIC_SDS_REMOTE_DEVICE_EVENT_HANDLER_T event_handler; + SCIC_SDS_REMOTE_DEVICE_FRAME_HANDLER_T frame_handler; + +}; + + +extern const struct sci_base_state scic_sds_remote_device_state_table[]; +extern const struct sci_base_state scic_sds_ssp_remote_device_ready_substate_table[]; +extern const struct sci_base_state scic_sds_stp_remote_device_ready_substate_table[]; +extern const struct sci_base_state scic_sds_smp_remote_device_ready_substate_table[]; + +extern struct scic_sds_remote_device_state_handler + scic_sds_remote_device_state_handler_table[]; +extern struct scic_sds_remote_device_state_handler + scic_sds_ssp_remote_device_ready_substate_handler_table[]; +extern struct scic_sds_remote_device_state_handler + scic_sds_stp_remote_device_ready_substate_handler_table[]; +extern struct scic_sds_remote_device_state_handler + scic_sds_smp_remote_device_ready_substate_handler_table[]; + +/** + * scic_sds_remote_device_increment_request_count() - + * + * This macro incrments the request count for this device + */ +#define scic_sds_remote_device_increment_request_count(this_device) \ + ((this_device)->started_request_count++) + +/** + * scic_sds_remote_device_decrement_request_count() - + * + * This macro decrements the request count for this device. This count will + * never decrment past 0. + */ +#define scic_sds_remote_device_decrement_request_count(this_device) \ + ((this_device)->started_request_count > 0 ? \ + (this_device)->started_request_count-- : 0) + +/** + * scic_sds_remote_device_get_request_count() - + * + * This is a helper macro to return the current device request count. + */ +#define scic_sds_remote_device_get_request_count(this_device) \ + ((this_device)->started_request_count) + +/** + * scic_sds_remote_device_get_port() - + * + * This macro returns the owning port of this remote device obejct. + */ +#define scic_sds_remote_device_get_port(this_device) \ + ((this_device)->owning_port) + +/** + * scic_sds_remote_device_get_controller() - + * + * This macro returns the controller object that contains this device object + */ +#define scic_sds_remote_device_get_controller(this_device) \ + scic_sds_port_get_controller(scic_sds_remote_device_get_port(this_device)) + +/** + * scic_sds_remote_device_set_state_handlers() - + * + * This macro sets the remote device state handlers pointer and is set on entry + * to each device state. + */ +#define scic_sds_remote_device_set_state_handlers(this_device, handlers) \ + ((this_device)->state_handlers = (handlers)) + +/** + * scic_sds_remote_device_get_base_state_machine() - + * + * This macro returns the base sate machine object for the remote device. + */ +#define scic_sds_remote_device_get_base_state_machine(this_device) \ + (&(this_device)->parent.state_machine) + +/** + * scic_sds_remote_device_get_ready_substate_machine() - + * + * This macro returns the remote device ready substate machine + */ +#define scic_sds_remote_device_get_ready_substate_machine(this_device) \ + (&(this_device)->ready_substate_machine) + +/** + * scic_sds_remote_device_get_port() - + * + * This macro returns the owning port of this device + */ +#define scic_sds_remote_device_get_port(this_device) \ + ((this_device)->owning_port) + +/** + * scic_sds_remote_device_get_sequence() - + * + * This macro returns the remote device sequence value + */ +#define scic_sds_remote_device_get_sequence(this_device) \ + (\ + scic_sds_remote_device_get_controller(this_device)-> \ + remote_device_sequence[(this_device)->rnc->remote_node_index] \ + ) + +/** + * scic_sds_remote_device_get_controller_peg() - + * + * This macro returns the controllers protocol engine group + */ +#define scic_sds_remote_device_get_controller_peg(this_device) \ + (\ + scic_sds_controller_get_protocol_engine_group(\ + scic_sds_port_get_controller(\ + scic_sds_remote_device_get_port(this_device) \ + ) \ + ) \ + ) + +/** + * scic_sds_remote_device_get_port_index() - + * + * This macro returns the port index for the devices owning port + */ +#define scic_sds_remote_device_get_port_index(this_device) \ + (scic_sds_port_get_index(scic_sds_remote_device_get_port(this_device))) + +/** + * scic_sds_remote_device_get_index() - + * + * This macro returns the remote node index for this device object + */ +#define scic_sds_remote_device_get_index(this_device) \ + ((this_device)->rnc->remote_node_index) + +/** + * scic_sds_remote_device_build_command_context() - + * + * This macro builds a remote device context for the SCU post request operation + */ +#define scic_sds_remote_device_build_command_context(device, command) \ + ((command) \ + | (scic_sds_remote_device_get_controller_peg((device)) << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT) \ + | (scic_sds_remote_device_get_port_index((device)) << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT) \ + | (scic_sds_remote_device_get_index((device))) \ + ) + +/** + * scic_sds_remote_device_set_working_request() - + * + * This macro makes the working request assingment for the remote device + * object. To clear the working request use this macro with a NULL request + * object. + */ +#define scic_sds_remote_device_set_working_request(device, request) \ + ((device)->working_request = (request)) + +/* --------------------------------------------------------------------------- */ + + + +enum sci_status scic_sds_remote_device_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index); + +enum sci_status scic_sds_remote_device_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code); + +enum sci_status scic_sds_remote_device_start_io( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *this_device, + struct scic_sds_request *io_request); + +enum sci_status scic_sds_remote_device_complete_io( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *this_device, + struct scic_sds_request *io_request); + +enum sci_status scic_sds_remote_device_resume( + struct scic_sds_remote_device *this_device); + +enum sci_status scic_sds_remote_device_suspend( + struct scic_sds_remote_device *this_device, + u32 suspend_type); + +enum sci_status scic_sds_remote_device_start_task( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *this_device, + struct scic_sds_request *io_request); + +void scic_sds_remote_device_post_request( + struct scic_sds_remote_device *this_device, + u32 request); + +#if !defined(DISABLE_ATAPI) +bool scic_sds_remote_device_is_atapi( + struct scic_sds_remote_device *this_device); +#else /* !defined(DISABLE_ATAPI) */ +#define scic_sds_remote_device_is_atapi(this_device) false +#endif /* !defined(DISABLE_ATAPI) */ + +/* --------------------------------------------------------------------------- */ + +/* --------------------------------------------------------------------------- */ + +void scic_sds_remote_device_start_request( + struct scic_sds_remote_device *this_device, + struct scic_sds_request *the_request, + enum sci_status status); + +void scic_sds_remote_device_continue_request( + struct scic_sds_remote_device *this_device); + +enum sci_status scic_sds_remote_device_default_start_handler( + struct sci_base_remote_device *this_device); + + +enum sci_status scic_sds_remote_device_default_fail_handler( + struct sci_base_remote_device *this_device); + +enum sci_status scic_sds_remote_device_default_destruct_handler( + struct sci_base_remote_device *this_device); + +enum sci_status scic_sds_remote_device_default_reset_handler( + struct sci_base_remote_device *device); + +enum sci_status scic_sds_remote_device_default_reset_complete_handler( + struct sci_base_remote_device *device); + +enum sci_status scic_sds_remote_device_default_start_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request); + +enum sci_status scic_sds_remote_device_default_complete_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request); + +enum sci_status scic_sds_remote_device_default_continue_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request); + +enum sci_status scic_sds_remote_device_default_suspend_handler( + struct scic_sds_remote_device *this_device, + u32 suspend_type); + +enum sci_status scic_sds_remote_device_default_resume_handler( + struct scic_sds_remote_device *this_device); + + +enum sci_status scic_sds_remote_device_default_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index); + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_remote_device_ready_state_stop_handler( + struct sci_base_remote_device *device); + +enum sci_status scic_sds_remote_device_ready_state_reset_handler( + struct sci_base_remote_device *device); + +enum sci_status scic_sds_remote_device_general_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index); + +enum sci_status scic_sds_remote_device_general_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code); + +enum sci_status scic_sds_ssp_remote_device_ready_suspended_substate_resume_handler( + struct scic_sds_remote_device *this_device); + +/* --------------------------------------------------------------------------- */ + + +#endif /* _SCIC_SDS_REMOTE_DEVICE_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_remote_node_context.c b/drivers/scsi/isci/core/scic_sds_remote_node_context.c new file mode 100644 index 000000000000..79fe9a8cd1ed --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_remote_node_context.c @@ -0,0 +1,1244 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "sci_base_state_machine.h" +#include "scic_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_remote_node_context.h" +#include "sci_environment.h" +#include "sci_util.h" +#include "scu_event_codes.h" +#include "scu_task_context.h" + +void scic_sds_remote_node_context_construct( + struct scic_sds_remote_device *device, + struct scic_sds_remote_node_context *rnc, + u16 remote_node_index) +{ + memset(rnc, 0, sizeof(struct scic_sds_remote_node_context)); + + rnc->remote_node_index = remote_node_index; + rnc->device = device; + rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + + sci_base_state_machine_construct( + &rnc->state_machine, + &rnc->parent, + scic_sds_remote_node_context_state_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE + ); + + sci_base_state_machine_start(&rnc->state_machine); +} + +/** + * + * @this_rnc: The RNC for which the is posted request is being made. + * + * This method will return true if the RNC is not in the initial state. In all + * other states the RNC is considered active and this will return true. The + * destroy request of the state machine drives the RNC back to the initial + * state. If the state machine changes then this routine will also have to be + * changed. bool true if the state machine is not in the initial state false if + * the state machine is in the initial state + */ + +/** + * + * @this_rnc: The state of the remote node context object to check. + * + * This method will return true if the remote node context is in a READY state + * otherwise it will return false bool true if the remote node context is in + * the ready state. false if the remote node context is not in the ready state. + */ +bool scic_sds_remote_node_context_is_ready( + struct scic_sds_remote_node_context *this_rnc) +{ + u32 current_state = sci_base_state_machine_get_state(&this_rnc->state_machine); + + if (current_state == SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE) { + return true; + } + + return false; +} + +/** + * + * @this_device: The remote device to use to construct the RNC buffer. + * @rnc: The buffer into which the remote device data will be copied. + * + * This method will construct the RNC buffer for this remote device object. none + */ +void scic_sds_remote_node_context_construct_buffer( + struct scic_sds_remote_node_context *this_rnc) +{ + union scu_remote_node_context *rnc; + struct scic_sds_controller *the_controller; + + the_controller = scic_sds_remote_device_get_controller(this_rnc->device); + + rnc = scic_sds_controller_get_remote_node_context_buffer( + the_controller, this_rnc->remote_node_index); + + memset( + rnc, + 0x00, + sizeof(union scu_remote_node_context) + * scic_sds_remote_device_node_count(this_rnc->device) + ); + + rnc->ssp.remote_node_index = this_rnc->remote_node_index; + rnc->ssp.remote_node_port_width = this_rnc->device->device_port_width; + rnc->ssp.logical_port_index = + scic_sds_remote_device_get_port_index(this_rnc->device); + + rnc->ssp.remote_sas_address_hi = SCIC_SWAP_DWORD(this_rnc->device->device_address.high); + rnc->ssp.remote_sas_address_lo = SCIC_SWAP_DWORD(this_rnc->device->device_address.low); + + rnc->ssp.nexus_loss_timer_enable = true; + rnc->ssp.check_bit = false; + rnc->ssp.is_valid = false; + rnc->ssp.is_remote_node_context = true; + rnc->ssp.function_number = 0; + + rnc->ssp.arbitration_wait_time = 0; + + + if ( + this_rnc->device->target_protocols.u.bits.attached_sata_device + || this_rnc->device->target_protocols.u.bits.attached_stp_target + ) { + rnc->ssp.connection_occupancy_timeout = + the_controller->user_parameters.sds1.stp_max_occupancy_timeout; + rnc->ssp.connection_inactivity_timeout = + the_controller->user_parameters.sds1.stp_inactivity_timeout; + } else { + rnc->ssp.connection_occupancy_timeout = + the_controller->user_parameters.sds1.ssp_max_occupancy_timeout; + rnc->ssp.connection_inactivity_timeout = + the_controller->user_parameters.sds1.ssp_inactivity_timeout; + } + + rnc->ssp.initial_arbitration_wait_time = 0; + + /* Open Address Frame Parameters */ + rnc->ssp.oaf_connection_rate = this_rnc->device->connection_rate; + rnc->ssp.oaf_features = 0; + rnc->ssp.oaf_source_zone_group = 0; + rnc->ssp.oaf_more_compatibility_features = 0; +} + +/** + * + * @this_rnc: + * @the_callback: + * @callback_parameter: + * + * This method will setup the remote node context object so it will transition + * to its ready state. If the remote node context is already setup to + * transition to its final state then this function does nothing. none + */ +static void scic_sds_remote_node_context_setup_to_resume( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + if (this_rnc->destination_state != SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) { + this_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY; + this_rnc->user_callback = the_callback; + this_rnc->user_cookie = callback_parameter; + } +} + +/** + * + * @this_rnc: + * @the_callback: + * @callback_parameter: + * + * This method will setup the remote node context object so it will transistion + * to its final state. none + */ +static void scic_sds_remote_node_context_setup_to_destory( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + this_rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL; + this_rnc->user_callback = the_callback; + this_rnc->user_cookie = callback_parameter; +} + +/** + * + * @this_rnc: + * @the_callback: + * + * This method will continue to resume a remote node context. This is used in + * the states where a resume is requested while a resume is in progress. + */ +static enum sci_status scic_sds_remote_node_context_continue_to_resume_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + if (this_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) { + this_rnc->user_callback = the_callback; + this_rnc->user_cookie = callback_parameter; + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_STATE; +} + +/* --------------------------------------------------------------------------- */ + +static enum sci_status scic_sds_remote_node_context_default_destruct_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to stop while " + "in unexpected state %d\n", + __func__, + this_rnc, + sci_base_state_machine_get_state(&this_rnc->state_machine)); + + /* + * We have decided that the destruct request on the remote node context can not fail + * since it is either in the initial/destroyed state or is can be destroyed. */ + return SCI_SUCCESS; +} + +static enum sci_status scic_sds_remote_node_context_default_suspend_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 suspend_type, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to suspend " + "while in wrong state %d\n", + __func__, + this_rnc, + sci_base_state_machine_get_state(&this_rnc->state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +static enum sci_status scic_sds_remote_node_context_default_resume_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to resume " + "while in wrong state %d\n", + __func__, + this_rnc, + sci_base_state_machine_get_state(&this_rnc->state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +static enum sci_status scic_sds_remote_node_context_default_start_io_handler( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request) +{ + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to start io " + "0x%p while in wrong state %d\n", + __func__, + this_rnc, + the_request, + sci_base_state_machine_get_state(&this_rnc->state_machine)); + + return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; +} + +static enum sci_status scic_sds_remote_node_context_default_start_task_handler( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request) +{ + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to start " + "task 0x%p while in wrong state %d\n", + __func__, + this_rnc, + the_request, + sci_base_state_machine_get_state(&this_rnc->state_machine)); + + return SCI_FAILURE; +} + +static enum sci_status scic_sds_remote_node_context_default_event_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code) +{ + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to process " + "event 0x%x while in wrong state %d\n", + __func__, + this_rnc, + event_code, + sci_base_state_machine_get_state(&this_rnc->state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * + * @this_rnc: The rnc for which the task request is targeted. + * @the_request: The request which is going to be started. + * + * This method determines if the task request can be started by the SCU + * hardware. When the RNC is in the ready state any task can be started. + * enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_node_context_success_start_task_handler( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request) +{ + return SCI_SUCCESS; +} + +/** + * + * @this_rnc: + * @the_callback: + * @callback_parameter: + * + * This method handles destruct calls from the various state handlers. The + * remote node context can be requested to destroy from any state. If there was + * a user callback it is always replaced with the request to destroy user + * callback. enum sci_status + */ +static enum sci_status scic_sds_remote_node_context_general_destruct_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + scic_sds_remote_node_context_setup_to_destory( + this_rnc, the_callback, callback_parameter + ); + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE + ); + + return SCI_SUCCESS; +} + +/* --------------------------------------------------------------------------- */ + +static enum sci_status scic_sds_remote_node_context_initial_state_resume_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + if (this_rnc->remote_node_index != SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { + scic_sds_remote_node_context_setup_to_resume( + this_rnc, the_callback, callback_parameter + ); + + scic_sds_remote_node_context_construct_buffer(this_rnc); + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE + ); + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_STATE; +} + +/* --------------------------------------------------------------------------- */ + +static enum sci_status scic_sds_remote_node_context_posting_state_event_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code) +{ + enum sci_status status; + + switch (scu_get_event_code(event_code)) { + case SCU_EVENT_POST_RNC_COMPLETE: + status = SCI_SUCCESS; + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE + ); + break; + + default: + status = SCI_FAILURE; + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to " + "process unexpected event 0x%x while in posting " + "state\n", + __func__, + this_rnc, + event_code); + break; + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +static enum sci_status scic_sds_remote_node_context_invalidating_state_destruct_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + scic_sds_remote_node_context_setup_to_destory( + this_rnc, the_callback, callback_parameter + ); + + return SCI_SUCCESS; +} + +static enum sci_status scic_sds_remote_node_context_invalidating_state_event_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code) +{ + enum sci_status status; + + if (scu_get_event_code(event_code) == SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE) { + status = SCI_SUCCESS; + + if (this_rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL) { + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE + ); + } else { + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE + ); + } + } else { + switch (scu_get_event_type(event_code)) { + case SCU_EVENT_TYPE_RNC_SUSPEND_TX: + case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: + /* + * We really dont care if the hardware is going to suspend + * the device since it's being invalidated anyway */ + dev_dbg(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p was " + "suspeneded by hardware while being " + "invalidated.\n", + __func__, + this_rnc); + status = SCI_SUCCESS; + break; + + default: + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p " + "requested to process event 0x%x while " + "in state %d.\n", + __func__, + this_rnc, + event_code, + sci_base_state_machine_get_state( + &this_rnc->state_machine)); + status = SCI_FAILURE; + break; + } + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + + +static enum sci_status scic_sds_remote_node_context_resuming_state_event_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code) +{ + enum sci_status status; + + if (scu_get_event_code(event_code) == SCU_EVENT_POST_RCN_RELEASE) { + status = SCI_SUCCESS; + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE + ); + } else { + switch (scu_get_event_type(event_code)) { + case SCU_EVENT_TYPE_RNC_SUSPEND_TX: + case SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX: + /* + * We really dont care if the hardware is going to suspend + * the device since it's being resumed anyway */ + dev_dbg(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p was " + "suspeneded by hardware while being resumed.\n", + __func__, + this_rnc); + status = SCI_SUCCESS; + break; + + default: + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested " + "to process event 0x%x while in state %d.\n", + __func__, + this_rnc, + event_code, + sci_base_state_machine_get_state( + &this_rnc->state_machine)); + status = SCI_FAILURE; + break; + } + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +/** + * + * @this_rnc: The remote node context object being suspended. + * @the_callback: The callback when the suspension is complete. + * @callback_parameter: The parameter that is to be passed into the callback. + * + * This method will handle the suspend requests from the ready state. + * SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_node_context_ready_state_suspend_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 suspend_type, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + this_rnc->user_callback = the_callback; + this_rnc->user_cookie = callback_parameter; + this_rnc->suspension_code = suspend_type; + + if (suspend_type == SCI_SOFTWARE_SUSPENSION) { + scic_sds_remote_device_post_request( + this_rnc->device, + SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX + ); + } + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE + ); + + return SCI_SUCCESS; +} + +/** + * + * @this_rnc: The rnc for which the io request is targeted. + * @the_request: The request which is going to be started. + * + * This method determines if the io request can be started by the SCU hardware. + * When the RNC is in the ready state any io request can be started. enum sci_status + * SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_node_context_ready_state_start_io_handler( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request) +{ + return SCI_SUCCESS; +} + + +static enum sci_status scic_sds_remote_node_context_ready_state_event_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code) +{ + enum sci_status status; + + switch (scu_get_event_type(event_code)) { + case SCU_EVENT_TL_RNC_SUSPEND_TX: + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE + ); + + this_rnc->suspension_code = scu_get_event_specifier(event_code); + status = SCI_SUCCESS; + break; + + case SCU_EVENT_TL_RNC_SUSPEND_TX_RX: + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE + ); + + this_rnc->suspension_code = scu_get_event_specifier(event_code); + status = SCI_SUCCESS; + break; + + default: + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to " + "process event 0x%x while in state %d.\n", + __func__, + this_rnc, + event_code, + sci_base_state_machine_get_state( + &this_rnc->state_machine)); + + status = SCI_FAILURE; + break; + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +static enum sci_status scic_sds_remote_node_context_tx_suspended_state_resume_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + enum sci_status status; + struct smp_discover_response_protocols protocols; + + scic_sds_remote_node_context_setup_to_resume( + this_rnc, the_callback, callback_parameter + ); + + /* TODO: consider adding a resume action of NONE, INVALIDATE, WRITE_TLCR */ + + scic_remote_device_get_protocols(this_rnc->device, &protocols); + + if ( + (protocols.u.bits.attached_ssp_target == 1) + || (protocols.u.bits.attached_smp_target == 1) + ) { + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE + ); + + status = SCI_SUCCESS; + } else if (protocols.u.bits.attached_stp_target == 1) { + if (this_rnc->device->is_direct_attached) { + /* @todo Fix this since I am being silly in writing to the STPTLDARNI register. */ + scic_sds_port_set_direct_attached_device_id( + this_rnc->device->owning_port, + this_rnc->remote_node_index + ); + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE + ); + } else { + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE + ); + } + + status = SCI_SUCCESS; + } else { + status = SCI_FAILURE; + } + + return status; +} + +/** + * + * @this_rnc: The remote node context which is to receive the task request. + * @the_request: The task request to be transmitted to to the remote target + * device. + * + * This method will report a success or failure attempt to start a new task + * request to the hardware. Since all task requests are sent on the high + * priority queue they can be sent when the RCN is in a TX suspend state. + * enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_node_context_suspended_start_task_handler( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request) +{ + scic_sds_remote_node_context_resume(this_rnc, NULL, NULL); + + return SCI_SUCCESS; +} + +/* --------------------------------------------------------------------------- */ + +static enum sci_status scic_sds_remote_node_context_tx_rx_suspended_state_resume_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + scic_sds_remote_node_context_setup_to_resume( + this_rnc, the_callback, callback_parameter + ); + + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE + ); + + return SCI_FAILURE_INVALID_STATE; +} + +/* --------------------------------------------------------------------------- */ + +/** + * + * + * + */ +static enum sci_status scic_sds_remote_node_context_await_suspension_state_resume_handler( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter) +{ + scic_sds_remote_node_context_setup_to_resume( + this_rnc, the_callback, callback_parameter + ); + + return SCI_SUCCESS; +} + +/** + * + * @this_rnc: The remote node context which is to receive the task request. + * @the_request: The task request to be transmitted to to the remote target + * device. + * + * This method will report a success or failure attempt to start a new task + * request to the hardware. Since all task requests are sent on the high + * priority queue they can be sent when the RCN is in a TX suspend state. + * enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_remote_node_context_await_suspension_state_start_task_handler( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request) +{ + return SCI_SUCCESS; +} + +static enum sci_status scic_sds_remote_node_context_await_suspension_state_event_handler( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code) +{ + enum sci_status status; + + switch (scu_get_event_type(event_code)) { + case SCU_EVENT_TL_RNC_SUSPEND_TX: + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE + ); + + this_rnc->suspension_code = scu_get_event_specifier(event_code); + status = SCI_SUCCESS; + break; + + case SCU_EVENT_TL_RNC_SUSPEND_TX_RX: + sci_base_state_machine_change_state( + &this_rnc->state_machine, + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE + ); + + this_rnc->suspension_code = scu_get_event_specifier(event_code); + status = SCI_SUCCESS; + break; + + default: + dev_warn(scirdev_to_dev(this_rnc->device), + "%s: SCIC Remote Node Context 0x%p requested to " + "process event 0x%x while in state %d.\n", + __func__, + this_rnc, + event_code, + sci_base_state_machine_get_state( + &this_rnc->state_machine)); + + status = SCI_FAILURE; + break; + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_remote_node_context_handlers +scic_sds_remote_node_context_state_handler_table[ + SCIC_SDS_REMOTE_NODE_CONTEXT_MAX_STATES] = +{ + /* SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE */ + { + scic_sds_remote_node_context_default_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_initial_state_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_default_start_task_handler, + scic_sds_remote_node_context_default_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE */ + { + scic_sds_remote_node_context_general_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_continue_to_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_default_start_task_handler, + scic_sds_remote_node_context_posting_state_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE */ + { + scic_sds_remote_node_context_invalidating_state_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_continue_to_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_default_start_task_handler, + scic_sds_remote_node_context_invalidating_state_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE */ + { + scic_sds_remote_node_context_general_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_continue_to_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_success_start_task_handler, + scic_sds_remote_node_context_resuming_state_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE */ + { + scic_sds_remote_node_context_general_destruct_handler, + scic_sds_remote_node_context_ready_state_suspend_handler, + scic_sds_remote_node_context_default_resume_handler, + scic_sds_remote_node_context_ready_state_start_io_handler, + scic_sds_remote_node_context_success_start_task_handler, + scic_sds_remote_node_context_ready_state_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE */ + { + scic_sds_remote_node_context_general_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_tx_suspended_state_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_suspended_start_task_handler, + scic_sds_remote_node_context_default_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE */ + { + scic_sds_remote_node_context_general_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_tx_rx_suspended_state_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_suspended_start_task_handler, + scic_sds_remote_node_context_default_event_handler + }, + /* SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE */ + { + scic_sds_remote_node_context_general_destruct_handler, + scic_sds_remote_node_context_default_suspend_handler, + scic_sds_remote_node_context_await_suspension_state_resume_handler, + scic_sds_remote_node_context_default_start_io_handler, + scic_sds_remote_node_context_await_suspension_state_start_task_handler, + scic_sds_remote_node_context_await_suspension_state_event_handler + } +}; + +/* + * ***************************************************************************** + * * REMOTE NODE CONTEXT PRIVATE METHODS + * ***************************************************************************** */ + +/** + * + * + * This method just calls the user callback function and then resets the + * callback. + */ +static void scic_sds_remote_node_context_notify_user( + struct scic_sds_remote_node_context *rnc) +{ + if (rnc->user_callback != NULL) { + (*rnc->user_callback)(rnc->user_cookie); + + rnc->user_callback = NULL; + rnc->user_cookie = NULL; + } +} + +/** + * + * + * This method will continue the remote node context state machine by + * requesting to resume the remote node context state machine from its current + * state. + */ +static void scic_sds_remote_node_context_continue_state_transitions( + struct scic_sds_remote_node_context *rnc) +{ + if (rnc->destination_state == SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY) { + rnc->state_handlers->resume_handler( + rnc, rnc->user_callback, rnc->user_cookie + ); + } +} + +/** + * + * @this_rnc: The remote node context object that is to be validated. + * + * This method will mark the rnc buffer as being valid and post the request to + * the hardware. none + */ +static void scic_sds_remote_node_context_validate_context_buffer( + struct scic_sds_remote_node_context *this_rnc) +{ + union scu_remote_node_context *rnc_buffer; + + rnc_buffer = scic_sds_controller_get_remote_node_context_buffer( + scic_sds_remote_device_get_controller(this_rnc->device), + this_rnc->remote_node_index + ); + + rnc_buffer->ssp.is_valid = true; + + if ( + !this_rnc->device->is_direct_attached + && this_rnc->device->target_protocols.u.bits.attached_stp_target + ) { + scic_sds_remote_device_post_request( + this_rnc->device, + SCU_CONTEXT_COMMAND_POST_RNC_96 + ); + } else { + scic_sds_remote_device_post_request( + this_rnc->device, + SCU_CONTEXT_COMMAND_POST_RNC_32 + ); + + if (this_rnc->device->is_direct_attached) { + scic_sds_port_set_direct_attached_device_id( + this_rnc->device->owning_port, + this_rnc->remote_node_index + ); + } + } +} + +/** + * + * @this_rnc: The remote node context object that is to be invalidated. + * + * This method will update the RNC buffer and post the invalidate request. none + */ +static void scic_sds_remote_node_context_invalidate_context_buffer( + struct scic_sds_remote_node_context *this_rnc) +{ + union scu_remote_node_context *rnc_buffer; + + rnc_buffer = scic_sds_controller_get_remote_node_context_buffer( + scic_sds_remote_device_get_controller(this_rnc->device), + this_rnc->remote_node_index + ); + + rnc_buffer->ssp.is_valid = false; + + scic_sds_remote_device_post_request( + this_rnc->device, + SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE + ); + + if (this_rnc->device->is_direct_attached) { + scic_sds_port_set_direct_attached_device_id( + this_rnc->device->owning_port, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ); + } +} + +/* + * ***************************************************************************** + * * REMOTE NODE CONTEXT STATE ENTER AND EXIT METHODS + * ***************************************************************************** */ + +/** + * + * + * + */ +static void scic_sds_remote_node_context_initial_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE + ); + + /* + * Check to see if we have gotten back to the initial state because someone + * requested to destroy the remote node context object. */ + if ( + rnc->state_machine.previous_state_id + == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE + ) { + rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + + scic_sds_remote_node_context_notify_user(rnc); + } +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_posting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *this_rnc; + + this_rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + this_rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE + ); + + scic_sds_remote_node_context_validate_context_buffer(this_rnc); +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_invalidating_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE + ); + + scic_sds_remote_node_context_invalidate_context_buffer(rnc); +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_resuming_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE + ); + + scic_sds_remote_device_post_request( + rnc->device, + SCU_CONTEXT_COMMAND_POST_RNC_RESUME + ); +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_ready_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE + ); + + rnc->destination_state = SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED; + + if (rnc->user_callback != NULL) { + scic_sds_remote_node_context_notify_user(rnc); + } +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_tx_suspended_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE + ); + + scic_sds_remote_node_context_continue_state_transitions(rnc); +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_tx_rx_suspended_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE + ); + + scic_sds_remote_node_context_continue_state_transitions(rnc); +} + +/** + * + * + * + */ +static void scic_sds_remote_node_context_await_suspension_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_node_context *rnc; + + rnc = (struct scic_sds_remote_node_context *)object; + + SET_STATE_HANDLER( + rnc, + scic_sds_remote_node_context_state_handler_table, + SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_remote_node_context_state_table[] = { + [SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE] = { + .enter_state = scic_sds_remote_node_context_initial_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE] = { + .enter_state = scic_sds_remote_node_context_posting_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE] = { + .enter_state = scic_sds_remote_node_context_invalidating_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE] = { + .enter_state = scic_sds_remote_node_context_resuming_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE] = { + .enter_state = scic_sds_remote_node_context_ready_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE] = { + .enter_state = scic_sds_remote_node_context_tx_suspended_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE] = { + .enter_state = scic_sds_remote_node_context_tx_rx_suspended_state_enter, + }, + [SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE] = { + .enter_state = scic_sds_remote_node_context_await_suspension_state_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_remote_node_context.h b/drivers/scsi/isci/core/scic_sds_remote_node_context.h new file mode 100644 index 000000000000..59eacf8f400c --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_remote_node_context.h @@ -0,0 +1,342 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ +#define _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ + +/** + * This file contains the structures, constants, and prototypes associated with + * the remote node context in the silicon. It exists to model and manage + * the remote node context in the silicon. + * + * + */ + +#include "sci_types.h" +#include "sci_base_state.h" +#include "sci_base_state_machine.h" + +/* --------------------------------------------------------------------------- */ + +/** + * + * + * This constant represents an invalid remote device id, it is used to program + * the STPDARNI register so the driver knows when it has received a SIGNATURE + * FIS from the SCU. + */ +#define SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX 0x0FFF + +#define SCU_HARDWARE_SUSPENSION (0) +#define SCI_SOFTWARE_SUSPENSION (1) + +struct scic_sds_request; +struct scic_sds_remote_device; +struct scic_sds_remote_node_context; + +typedef void (*SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK)(void *); + +typedef enum sci_status (*SCIC_SDS_REMOTE_NODE_CONTEXT_OPERATION)( + struct scic_sds_remote_node_context *this_rnc, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter + ); + +typedef enum sci_status (*SCIC_SDS_REMOTE_NODE_CONTEXT_SUSPEND_OPERATION)( + struct scic_sds_remote_node_context *this_rnc, + u32 suspension_type, + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK the_callback, + void *callback_parameter + ); + +typedef enum sci_status (*SCIC_SDS_REMOTE_NODE_CONTEXT_IO_REQUEST)( + struct scic_sds_remote_node_context *this_rnc, + struct scic_sds_request *the_request + ); + +typedef enum sci_status (*SCIC_SDS_REMOTE_NODE_CONTEXT_EVENT_HANDLER)( + struct scic_sds_remote_node_context *this_rnc, + u32 event_code + ); + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_remote_node_context_handlers { + /** + * This handle is invoked to stop the RNC. The callback is invoked when after + * the hardware notification that the RNC has been invalidated. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_OPERATION destruct_handler; + + /** + * This handler is invoked when there is a request to suspend the RNC. The + * callback is invoked after the hardware notification that the remote node is + * suspended. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_SUSPEND_OPERATION suspend_handler; + + /** + * This handler is invoked when there is a request to resume the RNC. The + * callback is invoked when after the RNC has reached the ready state. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_OPERATION resume_handler; + + /** + * This handler is invoked when there is a request to start an io request + * operation. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_IO_REQUEST start_io_handler; + + /** + * This handler is invoked when there is a request to start a task request + * operation. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_IO_REQUEST start_task_handler; + + /** + * This handler is invoked where there is an RNC event that must be processed. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_EVENT_HANDLER event_handler; + +}; + +/* --------------------------------------------------------------------------- */ + +/** + * + * + * This is the enumeration of the remote node context states. + */ +enum scis_sds_remote_node_context_states { + /** + * This state is the initial state for a remote node context. On a resume + * request the remote node context will transition to the posting state. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_INITIAL_STATE, + + /** + * This is a transition state that posts the RNi to the hardware. Once the RNC + * is posted the remote node context will be made ready. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_POSTING_STATE, + + /** + * This is a transition state that will post an RNC invalidate to the + * hardware. Once the invalidate is complete the remote node context will + * transition to the posting state. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_INVALIDATING_STATE, + + /** + * This is a transition state that will post an RNC resume to the hardare. + * Once the event notification of resume complete is received the remote node + * context will transition to the ready state. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_RESUMING_STATE, + + /** + * This is the state that the remote node context must be in to accept io + * request operations. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_READY_STATE, + + /** + * This is the state that the remote node context transitions to when it gets + * a TX suspend notification from the hardware. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_SUSPENDED_STATE, + + /** + * This is the state that the remote node context transitions to when it gets + * a TX RX suspend notification from the hardware. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_TX_RX_SUSPENDED_STATE, + + /** + * This state is a wait state for the remote node context that waits for a + * suspend notification from the hardware. This state is entered when either + * there is a request to supend the remote node context or when there is a TC + * completion where the remote node will be suspended by the hardware. + */ + SCIC_SDS_REMOTE_NODE_CONTEXT_AWAIT_SUSPENSION_STATE, + + SCIC_SDS_REMOTE_NODE_CONTEXT_MAX_STATES + +}; + +/** + * + * + * This enumeration is used to define the end destination state for the remote + * node context. + */ +enum SCIC_SDS_REMOTE_NODE_CONTEXT_DESTINATION_STATE { + SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_UNSPECIFIED, + SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_READY, + SCIC_SDS_REMOTE_NODE_DESTINATION_STATE_FINAL +}; + +/** + * struct scic_sds_remote_node_context - This structure contains the data + * associated with the remote node context object. The remote node context + * (RNC) object models the the remote device information necessary to manage + * the silicon RNC. + * + * + */ +struct scic_sds_remote_node_context { + /* + * parent object + */ + struct sci_base_object parent; + + /** + * This pointer simply points to the remote device object containing + * this RNC. + * + * @todo Consider making the device pointer the associated object of the + * the parent object. + */ + struct scic_sds_remote_device *device; + + /** + * This field indicates the remote node index (RNI) associated with + * this RNC. + */ + u16 remote_node_index; + + /** + * This field is the recored suspension code or the reason for the remote node + * context suspension. + */ + u32 suspension_code; + + /** + * This field is true if the remote node context is resuming from its current + * state. This can cause an automatic resume on receiving a suspension + * notification. + */ + enum SCIC_SDS_REMOTE_NODE_CONTEXT_DESTINATION_STATE destination_state; + + /** + * This field contains the callback function that the user requested to be + * called when the requested state transition is complete. + */ + SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK user_callback; + + /** + * This field contains the parameter that is called when the user requested + * state transition is completed. + */ + void *user_cookie; + + /** + * This field contains the data for the object's state machine. + */ + struct sci_base_state_machine state_machine; + + struct scic_sds_remote_node_context_handlers *state_handlers; +}; + +/* --------------------------------------------------------------------------- */ + +extern const struct sci_base_state scic_sds_remote_node_context_state_table[]; + +extern struct scic_sds_remote_node_context_handlers + scic_sds_remote_node_context_state_handler_table[ + SCIC_SDS_REMOTE_NODE_CONTEXT_MAX_STATES]; + +/* --------------------------------------------------------------------------- */ + +void scic_sds_remote_node_context_construct( + struct scic_sds_remote_device *device, + struct scic_sds_remote_node_context *rnc, + u16 remote_node_index); + +void scic_sds_remote_node_context_construct_buffer( + struct scic_sds_remote_node_context *rnc); + + +bool scic_sds_remote_node_context_is_ready( + struct scic_sds_remote_node_context *this_rnc); + +#define scic_sds_remote_node_context_set_remote_node_index(rnc, rni) \ + ((rnc)->remote_node_index = (rni)) + +#define scic_sds_remote_node_context_get_remote_node_index(rcn) \ + ((rnc)->remote_node_index) + +#define scic_sds_remote_node_context_event_handler(rnc, event_code) \ + ((rnc)->state_handlers->event_handler(rnc, event_code)) + +#define scic_sds_remote_node_context_resume(rnc, callback, parameter) \ + ((rnc)->state_handlers->resume_handler(rnc, callback, parameter)) + +#define scic_sds_remote_node_context_suspend(rnc, suspend_type, callback, parameter) \ + ((rnc)->state_handlers->suspend_handler(rnc, suspend_type, callback, parameter)) + +#define scic_sds_remote_node_context_destruct(rnc, callback, parameter) \ + ((rnc)->state_handlers->destruct_handler(rnc, callback, parameter)) + +#define scic_sds_remote_node_context_start_io(rnc, request) \ + ((rnc)->state_handlers->start_io_handler(rnc, request)) + +#define scic_sds_remote_node_context_start_task(rnc, task) \ + ((rnc)->state_handlers->start_task_handler(rnc, task)) + +/* --------------------------------------------------------------------------- */ + +#endif /* _SCIC_SDS_REMOTE_NODE_CONTEXT_H_ */ + diff --git a/drivers/scsi/isci/core/scic_sds_remote_node_table.c b/drivers/scsi/isci/core/scic_sds_remote_node_table.c new file mode 100644 index 000000000000..77919a2cf652 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_remote_node_table.c @@ -0,0 +1,600 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the implementation of the SCIC_SDS_REMOTE_NODE_TABLE + * public, protected, and private methods. + * + * + */ +#include "sci_util.h" +#include "sci_environment.h" +#include "scic_sds_remote_node_table.h" +#include "scic_sds_remote_node_context.h" + +/** + * + * @remote_node_table: This is the remote node index table from which the + * selection will be made. + * @group_table_index: This is the index to the group table from which to + * search for an available selection. + * + * This routine will find the bit position in absolute bit terms of the next 32 + * + bit position. If there are available bits in the first u32 then it is + * just bit position. u32 This is the absolute bit position for an available + * group. + */ +static u32 scic_sds_remote_node_table_get_group_index( + struct scic_remote_node_table *remote_node_table, + u32 group_table_index) +{ + u32 dword_index; + u32 *group_table; + u32 bit_index; + + group_table = remote_node_table->remote_node_groups[group_table_index]; + + for (dword_index = 0; dword_index < remote_node_table->group_array_size; dword_index++) { + if (group_table[dword_index] != 0) { + for (bit_index = 0; bit_index < 32; bit_index++) { + if ((group_table[dword_index] & (1 << bit_index)) != 0) { + return (dword_index * 32) + bit_index; + } + } + } + } + + return SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX; +} + +/** + * + * @out]: remote_node_table This the remote node table in which to clear the + * selector. + * @set_index: This is the remote node selector in which the change will be + * made. + * @group_index: This is the bit index in the table to be modified. + * + * This method will clear the group index entry in the specified group index + * table. none + */ +static void scic_sds_remote_node_table_clear_group_index( + struct scic_remote_node_table *remote_node_table, + u32 group_table_index, + u32 group_index) +{ + u32 dword_index; + u32 bit_index; + u32 *group_table; + + BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT); + BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32)); + + dword_index = group_index / 32; + bit_index = group_index % 32; + group_table = remote_node_table->remote_node_groups[group_table_index]; + + group_table[dword_index] = group_table[dword_index] & ~(1 << bit_index); +} + +/** + * + * @out]: remote_node_table This the remote node table in which to set the + * selector. + * @group_table_index: This is the remote node selector in which the change + * will be made. + * @group_index: This is the bit position in the table to be modified. + * + * This method will set the group index bit entry in the specified gropu index + * table. none + */ +static void scic_sds_remote_node_table_set_group_index( + struct scic_remote_node_table *remote_node_table, + u32 group_table_index, + u32 group_index) +{ + u32 dword_index; + u32 bit_index; + u32 *group_table; + + BUG_ON(group_table_index >= SCU_STP_REMOTE_NODE_COUNT); + BUG_ON(group_index >= (u32)(remote_node_table->group_array_size * 32)); + + dword_index = group_index / 32; + bit_index = group_index % 32; + group_table = remote_node_table->remote_node_groups[group_table_index]; + + group_table[dword_index] = group_table[dword_index] | (1 << bit_index); +} + +/** + * + * @out]: remote_node_table This is the remote node table in which to modify + * the remote node availability. + * @remote_node_index: This is the remote node index that is being returned to + * the table. + * + * This method will set the remote to available in the remote node allocation + * table. none + */ +static void scic_sds_remote_node_table_set_node_index( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_index) +{ + u32 dword_location; + u32 dword_remainder; + u32 slot_normalized; + u32 slot_position; + + BUG_ON( + (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) + <= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT) + ); + + dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD; + dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD; + slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32); + slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT; + + remote_node_table->available_remote_nodes[dword_location] |= + 1 << (slot_normalized + slot_position); +} + +/** + * + * @out]: remote_node_table This is the remote node table from which to clear + * the available remote node bit. + * @remote_node_index: This is the remote node index which is to be cleared + * from the table. + * + * This method clears the remote node index from the table of available remote + * nodes. none + */ +static void scic_sds_remote_node_table_clear_node_index( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_index) +{ + u32 dword_location; + u32 dword_remainder; + u32 slot_position; + u32 slot_normalized; + + BUG_ON( + (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) + <= (remote_node_index / SCU_STP_REMOTE_NODE_COUNT) + ); + + dword_location = remote_node_index / SCIC_SDS_REMOTE_NODES_PER_DWORD; + dword_remainder = remote_node_index % SCIC_SDS_REMOTE_NODES_PER_DWORD; + slot_normalized = (dword_remainder / SCU_STP_REMOTE_NODE_COUNT) * sizeof(u32); + slot_position = remote_node_index % SCU_STP_REMOTE_NODE_COUNT; + + remote_node_table->available_remote_nodes[dword_location] &= + ~(1 << (slot_normalized + slot_position)); +} + +/** + * + * @out]: remote_node_table The remote node table from which the slot will be + * cleared. + * @group_index: The index for the slot that is to be cleared. + * + * This method clears the entire table slot at the specified slot index. none + */ +static void scic_sds_remote_node_table_clear_group( + struct scic_remote_node_table *remote_node_table, + u32 group_index) +{ + u32 dword_location; + u32 dword_remainder; + u32 dword_value; + + BUG_ON( + (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) + <= (group_index / SCU_STP_REMOTE_NODE_COUNT) + ); + + dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; + dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; + + dword_value = remote_node_table->available_remote_nodes[dword_location]; + dword_value &= ~(SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4)); + remote_node_table->available_remote_nodes[dword_location] = dword_value; +} + +/** + * + * @remote_node_table: + * + * THis method sets an entire remote node group in the remote node table. + */ +static void scic_sds_remote_node_table_set_group( + struct scic_remote_node_table *remote_node_table, + u32 group_index) +{ + u32 dword_location; + u32 dword_remainder; + u32 dword_value; + + BUG_ON( + (remote_node_table->available_nodes_array_size * SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD) + <= (group_index / SCU_STP_REMOTE_NODE_COUNT) + ); + + dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; + dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; + + dword_value = remote_node_table->available_remote_nodes[dword_location]; + dword_value |= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4)); + remote_node_table->available_remote_nodes[dword_location] = dword_value; +} + +/** + * + * @remote_node_table: This is the remote node table that for which the group + * value is to be returned. + * @group_index: This is the group index to use to find the group value. + * + * This method will return the group value for the specified group index. The + * bit values at the specified remote node group index. + */ +static u8 scic_sds_remote_node_table_get_group_value( + struct scic_remote_node_table *remote_node_table, + u32 group_index) +{ + u32 dword_location; + u32 dword_remainder; + u32 dword_value; + + dword_location = group_index / SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; + dword_remainder = group_index % SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD; + + dword_value = remote_node_table->available_remote_nodes[dword_location]; + dword_value &= (SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE << (dword_remainder * 4)); + dword_value = dword_value >> (dword_remainder * 4); + + return (u8)dword_value; +} + +/** + * + * @out]: remote_node_table The remote that which is to be initialized. + * @remote_node_entries: The number of entries to put in the table. + * + * This method will initialize the remote node table for use. none + */ +void scic_sds_remote_node_table_initialize( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_entries) +{ + u32 index; + + /* + * Initialize the raw data we could improve the speed by only initializing + * those entries that we are actually going to be used */ + memset( + remote_node_table->available_remote_nodes, + 0x00, + sizeof(remote_node_table->available_remote_nodes) + ); + + memset( + remote_node_table->remote_node_groups, + 0x00, + sizeof(remote_node_table->remote_node_groups) + ); + + /* Initialize the available remote node sets */ + remote_node_table->available_nodes_array_size = (u16) + (remote_node_entries / SCIC_SDS_REMOTE_NODES_PER_DWORD) + + ((remote_node_entries % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0); + + + /* Initialize each full DWORD to a FULL SET of remote nodes */ + for (index = 0; index < remote_node_entries; index++) { + scic_sds_remote_node_table_set_node_index(remote_node_table, index); + } + + remote_node_table->group_array_size = (u16) + (remote_node_entries / (SCU_STP_REMOTE_NODE_COUNT * 32)) + + ((remote_node_entries % (SCU_STP_REMOTE_NODE_COUNT * 32)) != 0); + + for (index = 0; index < (remote_node_entries / SCU_STP_REMOTE_NODE_COUNT); index++) { + /* + * These are all guaranteed to be full slot values so fill them in the + * available sets of 3 remote nodes */ + scic_sds_remote_node_table_set_group_index(remote_node_table, 2, index); + } + + /* Now fill in any remainders that we may find */ + if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 2) { + scic_sds_remote_node_table_set_group_index(remote_node_table, 1, index); + } else if ((remote_node_entries % SCU_STP_REMOTE_NODE_COUNT) == 1) { + scic_sds_remote_node_table_set_group_index(remote_node_table, 0, index); + } +} + +/** + * + * @out]: remote_node_table The remote node table from which to allocate a + * remote node. + * @table_index: The group index that is to be used for the search. + * + * This method will allocate a single RNi from the remote node table. The + * table index will determine from which remote node group table to search. + * This search may fail and another group node table can be specified. The + * function is designed to allow a serach of the available single remote node + * group up to the triple remote node group. If an entry is found in the + * specified table the remote node is removed and the remote node groups are + * updated. The RNi value or an invalid remote node context if an RNi can not + * be found. + */ +static u16 scic_sds_remote_node_table_allocate_single_remote_node( + struct scic_remote_node_table *remote_node_table, + u32 group_table_index) +{ + u8 index; + u8 group_value; + u32 group_index; + u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX; + + group_index = scic_sds_remote_node_table_get_group_index( + remote_node_table, group_table_index); + + /* We could not find an available slot in the table selector 0 */ + if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) { + group_value = scic_sds_remote_node_table_get_group_value( + remote_node_table, group_index); + + for (index = 0; index < SCU_STP_REMOTE_NODE_COUNT; index++) { + if (((1 << index) & group_value) != 0) { + /* We have selected a bit now clear it */ + remote_node_index = (u16)(group_index * SCU_STP_REMOTE_NODE_COUNT + + index); + + scic_sds_remote_node_table_clear_group_index( + remote_node_table, group_table_index, group_index + ); + + scic_sds_remote_node_table_clear_node_index( + remote_node_table, remote_node_index + ); + + if (group_table_index > 0) { + scic_sds_remote_node_table_set_group_index( + remote_node_table, group_table_index - 1, group_index + ); + } + + break; + } + } + } + + return remote_node_index; +} + +/** + * + * @remote_node_table: This is the remote node table from which to allocate the + * remote node entries. + * @group_table_index: THis is the group table index which must equal two (2) + * for this operation. + * + * This method will allocate three consecutive remote node context entries. If + * there are no remaining triple entries the function will return a failure. + * The remote node index that represents three consecutive remote node entries + * or an invalid remote node context if none can be found. + */ +static u16 scic_sds_remote_node_table_allocate_triple_remote_node( + struct scic_remote_node_table *remote_node_table, + u32 group_table_index) +{ + u32 group_index; + u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX; + + group_index = scic_sds_remote_node_table_get_group_index( + remote_node_table, group_table_index); + + if (group_index != SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX) { + remote_node_index = (u16)group_index * SCU_STP_REMOTE_NODE_COUNT; + + scic_sds_remote_node_table_clear_group_index( + remote_node_table, group_table_index, group_index + ); + + scic_sds_remote_node_table_clear_group( + remote_node_table, group_index + ); + } + + return remote_node_index; +} + +/** + * + * @remote_node_table: This is the remote node table from which the remote node + * allocation is to take place. + * @remote_node_count: This is ther remote node count which is one of + * SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3). + * + * This method will allocate a remote node that mataches the remote node count + * specified by the caller. Valid values for remote node count is + * SCU_SSP_REMOTE_NODE_COUNT(1) or SCU_STP_REMOTE_NODE_COUNT(3). u16 This is + * the remote node index that is returned or an invalid remote node context. + */ +u16 scic_sds_remote_node_table_allocate_remote_node( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_count) +{ + u16 remote_node_index = SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX; + + if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) { + remote_node_index = + scic_sds_remote_node_table_allocate_single_remote_node( + remote_node_table, 0); + + if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { + remote_node_index = + scic_sds_remote_node_table_allocate_single_remote_node( + remote_node_table, 1); + } + + if (remote_node_index == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX) { + remote_node_index = + scic_sds_remote_node_table_allocate_single_remote_node( + remote_node_table, 2); + } + } else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) { + remote_node_index = + scic_sds_remote_node_table_allocate_triple_remote_node( + remote_node_table, 2); + } + + return remote_node_index; +} + +/** + * + * @remote_node_table: + * + * This method will free a single remote node index back to the remote node + * table. This routine will update the remote node groups + */ +static void scic_sds_remote_node_table_release_single_remote_node( + struct scic_remote_node_table *remote_node_table, + u16 remote_node_index) +{ + u32 group_index; + u8 group_value; + + group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT; + + group_value = scic_sds_remote_node_table_get_group_value(remote_node_table, group_index); + + /* + * Assert that we are not trying to add an entry to a slot that is already + * full. */ + BUG_ON(group_value == SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE); + + if (group_value == 0x00) { + /* + * There are no entries in this slot so it must be added to the single + * slot table. */ + scic_sds_remote_node_table_set_group_index(remote_node_table, 0, group_index); + } else if ((group_value & (group_value - 1)) == 0) { + /* + * There is only one entry in this slot so it must be moved from the + * single slot table to the dual slot table */ + scic_sds_remote_node_table_clear_group_index(remote_node_table, 0, group_index); + scic_sds_remote_node_table_set_group_index(remote_node_table, 1, group_index); + } else { + /* + * There are two entries in the slot so it must be moved from the dual + * slot table to the tripple slot table. */ + scic_sds_remote_node_table_clear_group_index(remote_node_table, 1, group_index); + scic_sds_remote_node_table_set_group_index(remote_node_table, 2, group_index); + } + + scic_sds_remote_node_table_set_node_index(remote_node_table, remote_node_index); +} + +/** + * + * @remote_node_table: This is the remote node table to which the remote node + * index is to be freed. + * + * This method will release a group of three consecutive remote nodes back to + * the free remote nodes. + */ +static void scic_sds_remote_node_table_release_triple_remote_node( + struct scic_remote_node_table *remote_node_table, + u16 remote_node_index) +{ + u32 group_index; + + group_index = remote_node_index / SCU_STP_REMOTE_NODE_COUNT; + + scic_sds_remote_node_table_set_group_index( + remote_node_table, 2, group_index + ); + + scic_sds_remote_node_table_set_group(remote_node_table, group_index); +} + +/** + * + * @remote_node_table: The remote node table to which the remote node index is + * to be freed. + * @remote_node_count: This is the count of consecutive remote nodes that are + * to be freed. + * + * This method will release the remote node index back into the remote node + * table free pool. + */ +void scic_sds_remote_node_table_release_remote_node_index( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_count, + u16 remote_node_index) +{ + if (remote_node_count == SCU_SSP_REMOTE_NODE_COUNT) { + scic_sds_remote_node_table_release_single_remote_node( + remote_node_table, remote_node_index); + } else if (remote_node_count == SCU_STP_REMOTE_NODE_COUNT) { + scic_sds_remote_node_table_release_triple_remote_node( + remote_node_table, remote_node_index); + } +} + diff --git a/drivers/scsi/isci/core/scic_sds_remote_node_table.h b/drivers/scsi/isci/core/scic_sds_remote_node_table.h new file mode 100644 index 000000000000..6ee5fbab25dd --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_remote_node_table.h @@ -0,0 +1,196 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_REMOTE_NODE_TABLE_H_ +#define _SCIC_SDS_REMOTE_NODE_TABLE_H_ + +/** + * This file contains the structures, constants and prototypes used for the + * remote node table. + * + * + */ + +#include "sci_types.h" +#include "sci_controller_constants.h" + +/** + * + * + * Remote node sets are sets of remote node index in the remtoe node table The + * SCU hardware requires that STP remote node entries take three consecutive + * remote node index so the table is arranged in sets of three. The bits are + * used as 0111 0111 to make a byte and the bits define the set of three remote + * nodes to use as a sequence. + */ +#define SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE 2 + +/** + * + * + * Since the remote node table is organized as DWORDS take the remote node sets + * in bytes and represent them in DWORDs. The lowest ordered bits are the ones + * used in case full DWORD is not being used. i.e. 0000 0000 0000 0000 0111 + * 0111 0111 0111 // if only a single WORD is in use in the DWORD. + */ +#define SCIC_SDS_REMOTE_NODE_SETS_PER_DWORD \ + (sizeof(u32) * SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE) +/** + * + * + * This is a count of the numeber of remote nodes that can be represented in a + * byte + */ +#define SCIC_SDS_REMOTE_NODES_PER_BYTE \ + (SCU_STP_REMOTE_NODE_COUNT * SCIC_SDS_REMOTE_NODE_SETS_PER_BYTE) + +/** + * + * + * This is a count of the number of remote nodes that can be represented in a + * DWROD + */ +#define SCIC_SDS_REMOTE_NODES_PER_DWORD \ + (sizeof(u32) * SCIC_SDS_REMOTE_NODES_PER_BYTE) + +/** + * + * + * This is the number of bits in a remote node group + */ +#define SCIC_SDS_REMOTE_NODES_BITS_PER_GROUP 4 + +#define SCIC_SDS_REMOTE_NODE_TABLE_INVALID_INDEX (0xFFFFFFFF) +#define SCIC_SDS_REMOTE_NODE_TABLE_FULL_SLOT_VALUE (0x07) +#define SCIC_SDS_REMOTE_NODE_TABLE_EMPTY_SLOT_VALUE (0x00) + +/** + * + * + * Expander attached sata remote node count + */ +#define SCU_STP_REMOTE_NODE_COUNT 3 + +/** + * + * + * Expander or direct attached ssp remote node count + */ +#define SCU_SSP_REMOTE_NODE_COUNT 1 + +/** + * + * + * Direct attached STP remote node count + */ +#define SCU_SATA_REMOTE_NODE_COUNT 1 + +/** + * struct scic_remote_node_table - + * + * + */ +struct scic_remote_node_table { + /** + * This field contains the array size in dwords + */ + u16 available_nodes_array_size; + + /** + * This field contains the array size of the + */ + u16 group_array_size; + + /** + * This field is the array of available remote node entries in bits. + * Because of the way STP remote node data is allocated on the SCU hardware + * the remote nodes must occupy three consecutive remote node context + * entries. For ease of allocation and de-allocation we have broken the + * sets of three into a single nibble. When the STP RNi is allocated all + * of the bits in the nibble are cleared. This math results in a table size + * of MAX_REMOTE_NODES / CONSECUTIVE RNi ENTRIES for STP / 2 entries per byte. + */ + u32 available_remote_nodes[ + (SCI_MAX_REMOTE_DEVICES / SCIC_SDS_REMOTE_NODES_PER_DWORD) + + ((SCI_MAX_REMOTE_DEVICES % SCIC_SDS_REMOTE_NODES_PER_DWORD) != 0)]; + + /** + * This field is the nibble selector for the above table. There are three + * possible selectors each for fast lookup when trying to find one, two or + * three remote node entries. + */ + u32 remote_node_groups[ + SCU_STP_REMOTE_NODE_COUNT][ + (SCI_MAX_REMOTE_DEVICES / (32 * SCU_STP_REMOTE_NODE_COUNT)) + + ((SCI_MAX_REMOTE_DEVICES % (32 * SCU_STP_REMOTE_NODE_COUNT)) != 0)]; + +}; + +/* --------------------------------------------------------------------------- */ + +void scic_sds_remote_node_table_initialize( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_entries); + +u16 scic_sds_remote_node_table_allocate_remote_node( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_count); + +void scic_sds_remote_node_table_release_remote_node_index( + struct scic_remote_node_table *remote_node_table, + u32 remote_node_count, + u16 remote_node_index); + +#endif /* _SCIC_SDS_REMOTE_NODE_TABLE_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_request.c b/drivers/scsi/isci/core/scic_sds_request.c new file mode 100644 index 000000000000..c696d246ea5a --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_request.c @@ -0,0 +1,2179 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "intel_sas.h" +#include "intel_sata.h" +#include "intel_sat.h" +#include "sci_base_request.h" +#include "scic_controller.h" +#include "scic_io_request.h" +#include "scic_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_controller_registers.h" +#include "scic_sds_pci.h" +#include "scic_sds_port.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_sds_smp_request.h" +#include "scic_sds_stp_request.h" +#include "scic_sds_unsolicited_frame_control.h" +#include "scic_user_callback.h" +#include "sci_environment.h" +#include "sci_types.h" +#include "sci_util.h" +#include "scu_completion_codes.h" +#include "scu_constants.h" +#include "scu_task_context.h" + +#if !defined(DISABLE_ATAPI) +#include "scic_sds_stp_packet_request.h" +#endif + +/* + * **************************************************************************** + * * SCIC SDS IO REQUEST CONSTANTS + * **************************************************************************** */ + +/** + * + * + * We have no timer requirements for IO requests right now + */ +#define SCIC_SDS_IO_REQUEST_MINIMUM_TIMER_COUNT (0) +#define SCIC_SDS_IO_REQUEST_MAXIMUM_TIMER_COUNT (0) + +/* + * **************************************************************************** + * * SCIC SDS IO REQUEST MACROS + * **************************************************************************** */ + +/** + * scic_sds_request_get_user_request() - + * + * This is a helper macro to return the os handle for this request object. + */ +#define scic_sds_request_get_user_request(request) \ + ((request)->user_request) + + +/** + * scic_ssp_io_request_get_object_size() - + * + * This macro returns the sizeof memory required to store the an SSP IO + * request. This does not include the size of the SGL or SCU Task Context + * memory. + */ +#define scic_ssp_io_request_get_object_size() \ + (\ + sizeof(struct sci_ssp_command_iu) \ + + sizeof(struct sci_ssp_response_iu) \ + ) + +/** + * scic_sds_ssp_request_get_command_buffer() - + * + * This macro returns the address of the ssp command buffer in the io request + * memory + */ +#define scic_sds_ssp_request_get_command_buffer(memory) \ + ((struct sci_ssp_command_iu *)(\ + ((char *)(memory)) + sizeof(struct scic_sds_request) \ + )) + +/** + * scic_sds_ssp_request_get_response_buffer() - + * + * This macro returns the address of the ssp response buffer in the io request + * memory + */ +#define scic_sds_ssp_request_get_response_buffer(memory) \ + ((struct sci_ssp_response_iu *)(\ + ((char *)(scic_sds_ssp_request_get_command_buffer(memory))) \ + + sizeof(struct sci_ssp_command_iu) \ + )) + +/** + * scic_sds_ssp_request_get_task_context_buffer() - + * + * This macro returns the address of the task context buffer in the io request + * memory + */ +#define scic_sds_ssp_request_get_task_context_buffer(memory) \ + ((struct scu_task_context *)(\ + ((char *)(scic_sds_ssp_request_get_response_buffer(memory))) \ + + sizeof(struct sci_ssp_response_iu) \ + )) + +/** + * scic_sds_ssp_request_get_sgl_element_buffer() - + * + * This macro returns the address of the sgl elment pairs in the io request + * memory buffer + */ +#define scic_sds_ssp_request_get_sgl_element_buffer(memory) \ + ((struct scu_sgl_element_pair *)(\ + ((char *)(scic_sds_ssp_request_get_task_context_buffer(memory))) \ + + sizeof(struct scu_task_context) \ + )) + + +/** + * scic_ssp_task_request_get_object_size() - + * + * This macro returns the sizeof of memory required to store an SSP Task + * request. This does not include the size of the SCU Task Context memory. + */ +#define scic_ssp_task_request_get_object_size() \ + (\ + sizeof(struct sci_ssp_task_iu) \ + + sizeof(struct sci_ssp_response_iu) \ + ) + +/** + * scic_sds_ssp_task_request_get_command_buffer() - + * + * This macro returns the address of the ssp command buffer in the task request + * memory. Yes its the same as the above macro except for the name. + */ +#define scic_sds_ssp_task_request_get_command_buffer(memory) \ + ((struct sci_ssp_task_iu *)(\ + ((char *)(memory)) + sizeof(struct scic_sds_request) \ + )) + +/** + * scic_sds_ssp_task_request_get_response_buffer() - + * + * This macro returns the address of the ssp response buffer in the task + * request memory. + */ +#define scic_sds_ssp_task_request_get_response_buffer(memory) \ + ((struct sci_ssp_response_iu *)(\ + ((char *)(scic_sds_ssp_task_request_get_command_buffer(memory))) \ + + sizeof(struct sci_ssp_task_iu) \ + )) + +/** + * scic_sds_ssp_task_request_get_task_context_buffer() - + * + * This macro returs the task context buffer for the SSP task request. + */ +#define scic_sds_ssp_task_request_get_task_context_buffer(memory) \ + ((struct scu_task_context *)(\ + ((char *)(scic_sds_ssp_task_request_get_response_buffer(memory))) \ + + sizeof(struct sci_ssp_response_iu) \ + )) + + + +/* + * **************************************************************************** + * * SCIC SDS IO REQUEST PRIVATE METHODS + * **************************************************************************** */ + +/** + * + * + * This method returns the size required to store an SSP IO request object. u32 + */ +static u32 scic_sds_ssp_request_get_object_size(void) +{ + return sizeof(struct scic_sds_request) + + scic_ssp_io_request_get_object_size() + + sizeof(struct scu_task_context) + + CACHE_LINE_SIZE + + sizeof(struct scu_sgl_element_pair) * SCU_MAX_SGL_ELEMENT_PAIRS; +} + +/** + * This method returns the sgl element pair for the specificed sgl_pair index. + * @this_request: This parameter specifies the IO request for which to retrieve + * the Scatter-Gather List element pair. + * @sgl_pair_index: This parameter specifies the index into the SGL element + * pair to be retrieved. + * + * This method returns a pointer to an struct scu_sgl_element_pair. + */ +static struct scu_sgl_element_pair *scic_sds_request_get_sgl_element_pair( + struct scic_sds_request *this_request, + u32 sgl_pair_index + ) { + struct scu_task_context *task_context; + + task_context = (struct scu_task_context *)this_request->task_context_buffer; + + if (sgl_pair_index == 0) { + return &task_context->sgl_pair_ab; + } else if (sgl_pair_index == 1) { + return &task_context->sgl_pair_cd; + } + + return &this_request->sgl_element_pair_buffer[sgl_pair_index - 2]; +} + +/** + * This function will build the SGL list for an IO request. + * @this_request: This parameter specifies the IO request for which to build + * the Scatter-Gather List. + * + */ +void scic_sds_request_build_sgl( + struct scic_sds_request *this_request) +{ + void *os_sge; + void *os_handle; + dma_addr_t physical_address; + u32 sgl_pair_index = 0; + struct scu_sgl_element_pair *scu_sgl_list = NULL; + struct scu_sgl_element_pair *previous_pair = NULL; + + os_handle = scic_sds_request_get_user_request(this_request); + scic_cb_io_request_get_next_sge(os_handle, NULL, &os_sge); + + while (os_sge != NULL) { + scu_sgl_list = + scic_sds_request_get_sgl_element_pair(this_request, sgl_pair_index); + + SCU_SGL_COPY(os_handle, scu_sgl_list->A, os_sge); + + scic_cb_io_request_get_next_sge(os_handle, os_sge, &os_sge); + + if (os_sge != NULL) { + SCU_SGL_COPY(os_handle, scu_sgl_list->B, os_sge); + + scic_cb_io_request_get_next_sge(os_handle, os_sge, &os_sge); + } else { + SCU_SGL_ZERO(scu_sgl_list->B); + } + + if (previous_pair != NULL) { + scic_cb_io_request_get_physical_address( + scic_sds_request_get_controller(this_request), + this_request, + scu_sgl_list, + &physical_address + ); + + previous_pair->next_pair_upper = + upper_32_bits(physical_address); + previous_pair->next_pair_lower = + lower_32_bits(physical_address); + } + + previous_pair = scu_sgl_list; + sgl_pair_index++; + } + + if (scu_sgl_list != NULL) { + scu_sgl_list->next_pair_upper = 0; + scu_sgl_list->next_pair_lower = 0; + } +} + +/** + * This method initializes common portions of the io request object. This + * includes construction of the struct sci_base_request parent. + * @the_controller: This parameter specifies the controller for which the + * request is being constructed. + * @the_target: This parameter specifies the remote device for which the + * request is being constructed. + * @io_tag: This parameter specifies the IO tag to be utilized for this + * request. This parameter can be set to SCI_CONTROLLER_INVALID_IO_TAG. + * @user_io_request_object: This parameter specifies the user request object + * for which the request is being constructed. + * @this_request: This parameter specifies the request being constructed. + * + */ +static void scic_sds_general_request_construct( + struct scic_sds_controller *the_controller, + struct scic_sds_remote_device *the_target, + u16 io_tag, + void *user_io_request_object, + struct scic_sds_request *this_request) +{ + sci_base_request_construct( + &this_request->parent, + scic_sds_request_state_table + ); + + this_request->io_tag = io_tag; + this_request->user_request = user_io_request_object; + this_request->owning_controller = the_controller; + this_request->target_device = the_target; + this_request->has_started_substate_machine = false; + this_request->protocol = SCIC_NO_PROTOCOL; + this_request->saved_rx_frame_index = SCU_INVALID_FRAME_INDEX; + this_request->device_sequence = scic_sds_remote_device_get_sequence(the_target); + + this_request->sci_status = SCI_SUCCESS; + this_request->scu_status = 0; + this_request->post_context = 0xFFFFFFFF; + + this_request->is_task_management_request = false; + + if (io_tag == SCI_CONTROLLER_INVALID_IO_TAG) { + this_request->was_tag_assigned_by_user = false; + this_request->task_context_buffer = NULL; + } else { + this_request->was_tag_assigned_by_user = true; + + this_request->task_context_buffer = + scic_sds_controller_get_task_context_buffer( + this_request->owning_controller, io_tag); + } +} + +/** + * This method build the remainder of the IO request object. + * @this_request: This parameter specifies the request object being constructed. + * + * The scic_sds_general_request_construct() must be called before this call is + * valid. none + */ +static void scic_sds_ssp_io_request_assign_buffers( + struct scic_sds_request *this_request) +{ + this_request->command_buffer = + scic_sds_ssp_request_get_command_buffer(this_request); + this_request->response_buffer = + scic_sds_ssp_request_get_response_buffer(this_request); + this_request->sgl_element_pair_buffer = + scic_sds_ssp_request_get_sgl_element_buffer(this_request); + this_request->sgl_element_pair_buffer = + scic_sds_request_align_sgl_element_buffer(this_request->sgl_element_pair_buffer); + + if (this_request->was_tag_assigned_by_user == false) { + this_request->task_context_buffer = + scic_sds_ssp_request_get_task_context_buffer(this_request); + this_request->task_context_buffer = + scic_sds_request_align_task_context_buffer(this_request->task_context_buffer); + } +} + +/** + * This method constructs the SSP Command IU data for this io request object. + * @this_request: This parameter specifies the request object for which the SSP + * command information unit is being built. + * + */ +static void scic_sds_io_request_build_ssp_command_iu( + struct scic_sds_request *this_request) +{ + struct sci_ssp_command_iu *command_frame; + void *os_handle; + u32 cdb_length; + u32 *cdb_buffer; + + command_frame = + (struct sci_ssp_command_iu *)this_request->command_buffer; + + os_handle = scic_sds_request_get_user_request(this_request); + + command_frame->lun_upper = 0; + command_frame->lun_lower = scic_cb_ssp_io_request_get_lun(os_handle); + + ((u32 *)command_frame)[2] = 0; + + cdb_length = scic_cb_ssp_io_request_get_cdb_length(os_handle); + cdb_buffer = (u32 *)scic_cb_ssp_io_request_get_cdb_address(os_handle); + + if (cdb_length > 16) { + command_frame->additional_cdb_length = cdb_length - 16; + } + + /* / @todo Is it ok to leave junk at the end of the cdb buffer? */ + scic_word_copy_with_swap( + (u32 *)(&command_frame->cdb), + (u32 *)(cdb_buffer), + (cdb_length + 3) / sizeof(u32) + ); + + command_frame->enable_first_burst = 0; + command_frame->task_priority = + scic_cb_ssp_io_request_get_command_priority(os_handle); + command_frame->task_attribute = + scic_cb_ssp_io_request_get_task_attribute(os_handle); +} + + +/** + * This method constructs the SSP Task IU data for this io request object. + * @this_request: + * + */ +static void scic_sds_task_request_build_ssp_task_iu( + struct scic_sds_request *this_request) +{ + struct sci_ssp_task_iu *command_frame; + void *os_handle; + + command_frame = + (struct sci_ssp_task_iu *)this_request->command_buffer; + + os_handle = scic_sds_request_get_user_request(this_request); + + command_frame->lun_upper = 0; + command_frame->lun_lower = scic_cb_ssp_task_request_get_lun(os_handle); + + ((u32 *)command_frame)[2] = 0; + + command_frame->task_function = + scic_cb_ssp_task_request_get_function(os_handle); + command_frame->task_tag = + scic_cb_ssp_task_request_get_io_tag_to_manage(os_handle); +} + + +/** + * This method is will fill in the SCU Task Context for any type of SSP request. + * @this_request: + * @task_context: + * + */ +static void scu_ssp_reqeust_construct_task_context( + struct scic_sds_request *this_request, + struct scu_task_context *task_context) +{ + dma_addr_t physical_address; + struct scic_sds_controller *owning_controller; + struct scic_sds_remote_device *target_device; + struct scic_sds_port *target_port; + + owning_controller = scic_sds_request_get_controller(this_request); + target_device = scic_sds_request_get_device(this_request); + target_port = scic_sds_request_get_port(this_request); + + /* Fill in the TC with the its required data */ + task_context->abort = 0; + task_context->priority = 0; + task_context->initiator_request = 1; + task_context->connection_rate = + scic_remote_device_get_connection_rate(target_device); + task_context->protocol_engine_index = + scic_sds_controller_get_protocol_engine_group(owning_controller); + task_context->logical_port_index = + scic_sds_port_get_index(target_port); + task_context->protocol_type = SCU_TASK_CONTEXT_PROTOCOL_SSP; + task_context->valid = SCU_TASK_CONTEXT_VALID; + task_context->context_type = SCU_TASK_CONTEXT_TYPE; + + task_context->remote_node_index = + scic_sds_remote_device_get_index(this_request->target_device); + task_context->command_code = 0; + + task_context->link_layer_control = 0; + task_context->do_not_dma_ssp_good_response = 1; + task_context->strict_ordering = 0; + task_context->control_frame = 0; + task_context->timeout_enable = 0; + task_context->block_guard_enable = 0; + + task_context->address_modifier = 0; + + /* task_context->type.ssp.tag = this_request->io_tag; */ + task_context->task_phase = 0x01; + + if (this_request->was_tag_assigned_by_user) { + /* Build the task context now since we have already read the data */ + this_request->post_context = ( + SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC + | ( + scic_sds_controller_get_protocol_engine_group(owning_controller) + << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT + ) + | ( + scic_sds_port_get_index(target_port) + << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT + ) + | scic_sds_io_tag_get_index(this_request->io_tag) + ); + } else { + /* Build the task context now since we have already read the data */ + this_request->post_context = ( + SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC + | ( + scic_sds_controller_get_protocol_engine_group(owning_controller) + << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT + ) + | ( + scic_sds_port_get_index(target_port) + << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT + ) + /* This is not assigned because we have to wait until we get a TCi */ + ); + } + + /* Copy the physical address for the command buffer to the SCU Task Context */ + scic_cb_io_request_get_physical_address( + scic_sds_request_get_controller(this_request), + this_request, + this_request->command_buffer, + &physical_address + ); + + task_context->command_iu_upper = + upper_32_bits(physical_address); + task_context->command_iu_lower = + lower_32_bits(physical_address); + + /* Copy the physical address for the response buffer to the SCU Task Context */ + scic_cb_io_request_get_physical_address( + scic_sds_request_get_controller(this_request), + this_request, + this_request->response_buffer, + &physical_address + ); + + task_context->response_iu_upper = + upper_32_bits(physical_address); + task_context->response_iu_lower = + lower_32_bits(physical_address); +} + +/** + * This method is will fill in the SCU Task Context for a SSP IO request. + * @this_request: + * + */ +static void scu_ssp_io_request_construct_task_context( + struct scic_sds_request *this_request, + SCI_IO_REQUEST_DATA_DIRECTION data_direction, + u32 transfer_length_bytes) +{ + struct scu_task_context *task_context; + + task_context = scic_sds_request_get_task_context(this_request); + + scu_ssp_reqeust_construct_task_context(this_request, task_context); + + task_context->ssp_command_iu_length = sizeof(struct sci_ssp_command_iu) / sizeof(u32); + task_context->type.ssp.frame_type = SCI_SAS_COMMAND_FRAME; + + switch (data_direction) { + case SCI_IO_REQUEST_DATA_IN: + case SCI_IO_REQUEST_NO_DATA: + task_context->task_type = SCU_TASK_TYPE_IOREAD; + break; + case SCI_IO_REQUEST_DATA_OUT: + task_context->task_type = SCU_TASK_TYPE_IOWRITE; + break; + } + + task_context->transfer_length_bytes = transfer_length_bytes; + + if (task_context->transfer_length_bytes > 0) { + scic_sds_request_build_sgl(this_request); + } +} + + +/** + * This method will fill in the remainder of the io request object for SSP Task + * requests. + * @this_request: + * + */ +static void scic_sds_ssp_task_request_assign_buffers( + struct scic_sds_request *this_request) +{ + /* Assign all of the buffer pointers */ + this_request->command_buffer = + scic_sds_ssp_task_request_get_command_buffer(this_request); + this_request->response_buffer = + scic_sds_ssp_task_request_get_response_buffer(this_request); + this_request->sgl_element_pair_buffer = NULL; + + if (this_request->was_tag_assigned_by_user == false) { + this_request->task_context_buffer = + scic_sds_ssp_task_request_get_task_context_buffer(this_request); + this_request->task_context_buffer = + scic_sds_request_align_task_context_buffer(this_request->task_context_buffer); + } +} + +/** + * This method will fill in the SCU Task Context for a SSP Task request. The + * following important settings are utilized: -# priority == + * SCU_TASK_PRIORITY_HIGH. This ensures that the task request is issued + * ahead of other task destined for the same Remote Node. -# task_type == + * SCU_TASK_TYPE_IOREAD. This simply indicates that a normal request type + * (i.e. non-raw frame) is being utilized to perform task management. -# + * control_frame == 1. This ensures that the proper endianess is set so + * that the bytes are transmitted in the right order for a task frame. + * @this_request: This parameter specifies the task request object being + * constructed. + * + */ +static void scu_ssp_task_request_construct_task_context( + struct scic_sds_request *this_request) +{ + struct scu_task_context *task_context; + + task_context = scic_sds_request_get_task_context(this_request); + + scu_ssp_reqeust_construct_task_context(this_request, task_context); + + task_context->control_frame = 1; + task_context->priority = SCU_TASK_PRIORITY_HIGH; + task_context->task_type = SCU_TASK_TYPE_RAW_FRAME; + task_context->transfer_length_bytes = 0; + task_context->type.ssp.frame_type = SCI_SAS_TASK_FRAME; + task_context->ssp_command_iu_length = sizeof(struct sci_ssp_task_iu) / sizeof(u32); +} + + +/** + * This method constructs the SSP Command IU data for this ssp passthrough + * comand request object. + * @this_request: This parameter specifies the request object for which the SSP + * command information unit is being built. + * + * enum sci_status, returns invalid parameter is cdb > 16 + */ + + +/** + * This method constructs the SATA request object. + * @this_request: + * @sat_protocol: + * @transfer_length: + * @data_direction: + * @copy_rx_frame: + * + * enum sci_status + */ +static enum sci_status scic_io_request_construct_sata( + struct scic_sds_request *this_request, + u8 sat_protocol, + u32 transfer_length, + SCI_IO_REQUEST_DATA_DIRECTION data_direction, + bool copy_rx_frame) +{ + enum sci_status status = SCI_SUCCESS; + + switch (sat_protocol) { + case SAT_PROTOCOL_PIO_DATA_IN: + case SAT_PROTOCOL_PIO_DATA_OUT: + status = scic_sds_stp_pio_request_construct(this_request, sat_protocol, copy_rx_frame); + break; + + case SAT_PROTOCOL_UDMA_DATA_IN: + case SAT_PROTOCOL_UDMA_DATA_OUT: + status = scic_sds_stp_udma_request_construct(this_request, transfer_length, data_direction); + break; + + case SAT_PROTOCOL_ATA_HARD_RESET: + case SAT_PROTOCOL_SOFT_RESET: + status = scic_sds_stp_soft_reset_request_construct(this_request); + break; + + case SAT_PROTOCOL_NON_DATA: + status = scic_sds_stp_non_data_request_construct(this_request); + break; + + case SAT_PROTOCOL_FPDMA: + status = scic_sds_stp_ncq_request_construct(this_request, transfer_length, data_direction); + break; + +#if !defined(DISABLE_ATAPI) + case SAT_PROTOCOL_PACKET_NON_DATA: + case SAT_PROTOCOL_PACKET_DMA_DATA_IN: + case SAT_PROTOCOL_PACKET_DMA_DATA_OUT: + case SAT_PROTOCOL_PACKET_PIO_DATA_IN: + case SAT_PROTOCOL_PACKET_PIO_DATA_OUT: + status = scic_sds_stp_packet_request_construct(this_request); + break; +#endif + + case SAT_PROTOCOL_DMA_QUEUED: + case SAT_PROTOCOL_DMA: + case SAT_PROTOCOL_DEVICE_DIAGNOSTIC: + case SAT_PROTOCOL_DEVICE_RESET: + case SAT_PROTOCOL_RETURN_RESPONSE_INFO: + default: + dev_err(scic_to_dev(this_request->owning_controller), + "%s: SCIC IO Request 0x%p received un-handled " + "SAT Protocl %d.\n", + __func__, this_request, sat_protocol); + + status = SCI_FAILURE; + break; + } + + return status; +} + +/* + * **************************************************************************** + * * SCIC Interface Implementation + * **************************************************************************** */ + + + + +/* --------------------------------------------------------------------------- */ + +u32 scic_io_request_get_object_size(void) +{ + u32 ssp_request_size; + u32 stp_request_size; + u32 smp_request_size; + + ssp_request_size = scic_sds_ssp_request_get_object_size(); + stp_request_size = scic_sds_stp_request_get_object_size(); + smp_request_size = scic_sds_smp_request_get_object_size(); + + return max(ssp_request_size, max(stp_request_size, smp_request_size)); +} + +/* --------------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------------- */ + + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_io_request_construct( + struct scic_sds_controller *scic_controller, + struct scic_sds_remote_device *scic_remote_device, + u16 io_tag, + void *user_io_request_object, + void *scic_io_request_memory, + struct scic_sds_request **new_scic_io_request_handle) +{ + enum sci_status status = SCI_SUCCESS; + struct scic_sds_request *this_request; + struct smp_discover_response_protocols device_protocol; + + this_request = (struct scic_sds_request *)scic_io_request_memory; + + /* Build the common part of the request */ + scic_sds_general_request_construct( + (struct scic_sds_controller *)scic_controller, + (struct scic_sds_remote_device *)scic_remote_device, + io_tag, + user_io_request_object, + this_request + ); + + if ( + scic_sds_remote_device_get_index((struct scic_sds_remote_device *)scic_remote_device) + == SCIC_SDS_REMOTE_NODE_CONTEXT_INVALID_INDEX + ) { + return SCI_FAILURE_INVALID_REMOTE_DEVICE; + } + + scic_remote_device_get_protocols(scic_remote_device, &device_protocol); + + if (device_protocol.u.bits.attached_ssp_target) { + scic_sds_ssp_io_request_assign_buffers(this_request); + } else if (device_protocol.u.bits.attached_stp_target) { + scic_sds_stp_request_assign_buffers(this_request); + memset(this_request->command_buffer, 0, sizeof(struct sata_fis_reg_h2d)); + } else if (device_protocol.u.bits.attached_smp_target) { + scic_sds_smp_request_assign_buffers(this_request); + memset(this_request->command_buffer, 0, sizeof(struct smp_request)); + } else { + status = SCI_FAILURE_UNSUPPORTED_PROTOCOL; + } + + if (status == SCI_SUCCESS) { + memset( + this_request->task_context_buffer, + 0, + SCI_FIELD_OFFSET(struct scu_task_context, sgl_pair_ab) + ); + *new_scic_io_request_handle = scic_io_request_memory; + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + + +enum sci_status scic_task_request_construct( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + u16 io_tag, + void *user_io_request_object, + void *scic_task_request_memory, + struct scic_sds_request **new_scic_task_request_handle) +{ + enum sci_status status = SCI_SUCCESS; + struct scic_sds_request *this_request = (struct scic_sds_request *) + scic_task_request_memory; + struct smp_discover_response_protocols device_protocol; + + /* Build the common part of the request */ + scic_sds_general_request_construct( + (struct scic_sds_controller *)controller, + (struct scic_sds_remote_device *)remote_device, + io_tag, + user_io_request_object, + this_request + ); + + scic_remote_device_get_protocols(remote_device, &device_protocol); + + if (device_protocol.u.bits.attached_ssp_target) { + scic_sds_ssp_task_request_assign_buffers(this_request); + + this_request->has_started_substate_machine = true; + + /* Construct the started sub-state machine. */ + sci_base_state_machine_construct( + &this_request->started_substate_machine, + &this_request->parent.parent, + scic_sds_io_request_started_task_mgmt_substate_table, + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_COMPLETION + ); + } else if (device_protocol.u.bits.attached_stp_target) { + scic_sds_stp_request_assign_buffers(this_request); + } else { + status = SCI_FAILURE_UNSUPPORTED_PROTOCOL; + } + + if (status == SCI_SUCCESS) { + this_request->is_task_management_request = true; + memset(this_request->task_context_buffer, 0x00, sizeof(struct scu_task_context)); + *new_scic_task_request_handle = scic_task_request_memory; + } + + return status; +} + + +enum sci_status scic_io_request_construct_basic_ssp( + struct scic_sds_request *sci_req) +{ + void *os_handle; + + sci_req->protocol = SCIC_SSP_PROTOCOL; + + os_handle = scic_sds_request_get_user_request(sci_req); + + scu_ssp_io_request_construct_task_context( + sci_req, + scic_cb_io_request_get_data_direction(os_handle), + scic_cb_io_request_get_transfer_length(os_handle) + ); + + + scic_sds_io_request_build_ssp_command_iu(sci_req); + + sci_base_state_machine_change_state( + &sci_req->parent.state_machine, + SCI_BASE_REQUEST_STATE_CONSTRUCTED + ); + + return SCI_SUCCESS; +} + + +enum sci_status scic_task_request_construct_ssp( + struct scic_sds_request *sci_req) +{ + /* Construct the SSP Task SCU Task Context */ + scu_ssp_task_request_construct_task_context(sci_req); + + /* Fill in the SSP Task IU */ + scic_sds_task_request_build_ssp_task_iu(sci_req); + + sci_base_state_machine_change_state( + &sci_req->parent.state_machine, + SCI_BASE_REQUEST_STATE_CONSTRUCTED + ); + + return SCI_SUCCESS; +} + + +enum sci_status scic_io_request_construct_basic_sata( + struct scic_sds_request *sci_req) +{ + enum sci_status status; + struct scic_sds_stp_request *this_stp_request; + u8 sat_protocol; + u32 transfer_length; + SCI_IO_REQUEST_DATA_DIRECTION data_direction; + bool copy_rx_frame = false; + + this_stp_request = (struct scic_sds_stp_request *)sci_req; + + sci_req->protocol = SCIC_STP_PROTOCOL; + + transfer_length = + scic_cb_io_request_get_transfer_length(sci_req->user_request); + data_direction = + scic_cb_io_request_get_data_direction(sci_req->user_request); + + sat_protocol = scic_cb_request_get_sat_protocol(sci_req->user_request); + copy_rx_frame = scic_cb_io_request_do_copy_rx_frames(this_stp_request->parent.user_request); + + status = scic_io_request_construct_sata( + sci_req, + sat_protocol, + transfer_length, + data_direction, + copy_rx_frame + ); + + if (status == SCI_SUCCESS) + sci_base_state_machine_change_state( + &sci_req->parent.state_machine, + SCI_BASE_REQUEST_STATE_CONSTRUCTED + ); + + return status; +} + + +enum sci_status scic_task_request_construct_sata( + struct scic_sds_request *sci_req) +{ + enum sci_status status; + u8 sat_protocol = scic_cb_request_get_sat_protocol(sci_req->user_request); + + switch (sat_protocol) { + case SAT_PROTOCOL_ATA_HARD_RESET: + case SAT_PROTOCOL_SOFT_RESET: + status = scic_sds_stp_soft_reset_request_construct(sci_req); + break; + + default: + dev_err(scic_to_dev(sci_req->owning_controller), + "%s: SCIC IO Request 0x%p received un-handled SAT " + "Protocl %d.\n", + __func__, + sci_req, + sat_protocol); + + status = SCI_FAILURE; + break; + } + + if (status == SCI_SUCCESS) + sci_base_state_machine_change_state( + &sci_req->parent.state_machine, + SCI_BASE_REQUEST_STATE_CONSTRUCTED + ); + + return status; +} + + +u16 scic_io_request_get_io_tag( + struct scic_sds_request *sci_req) +{ + return sci_req->io_tag; +} + + +u32 scic_request_get_controller_status( + struct scic_sds_request *sci_req) +{ + return sci_req->scu_status; +} + + +void *scic_io_request_get_command_iu_address( + struct scic_sds_request *sci_req) +{ + return sci_req->command_buffer; +} + + +void *scic_io_request_get_response_iu_address( + struct scic_sds_request *sci_req) +{ + return sci_req->response_buffer; +} + + +#define SCU_TASK_CONTEXT_SRAM 0x200000 +u32 scic_io_request_get_number_of_bytes_transferred( + struct scic_sds_request *scic_sds_request) +{ + u32 ret_val = 0; + + if (SMU_AMR_READ(scic_sds_request->owning_controller) == 0) { + /* + * get the bytes of data from the Address == BAR1 + 20002Ch + (256*TCi) where + * BAR1 is the scu_registers + * 0x20002C = 0x200000 + 0x2c + * = start of task context SRAM + offset of (type.ssp.data_offset) + * TCi is the io_tag of struct scic_sds_request */ + ret_val = scic_sds_pci_read_scu_dword( + scic_sds_request->owning_controller, + ( + (u8 *)scic_sds_request->owning_controller->scu_registers + + (SCU_TASK_CONTEXT_SRAM + SCI_FIELD_OFFSET(struct scu_task_context, type.ssp.data_offset)) + + ((sizeof(struct scu_task_context)) * scic_sds_io_tag_get_index(scic_sds_request->io_tag)) + ) + ); + } + + return ret_val; +} + + +/* + * **************************************************************************** + * * SCIC SDS Interface Implementation + * **************************************************************************** */ + +/** + * + * @this_request: The SCIC_SDS_IO_REQUEST_T object for which the start + * operation is to be executed. + * + * This method invokes the base state start request handler for the + * SCIC_SDS_IO_REQUEST_T object. enum sci_status + */ +enum sci_status scic_sds_request_start( + struct scic_sds_request *this_request) +{ + if ( + this_request->device_sequence + == scic_sds_remote_device_get_sequence(this_request->target_device) + ) { + return this_request->state_handlers->parent.start_handler( + &this_request->parent + ); + } + + return SCI_FAILURE; +} + +/** + * + * @this_request: The SCIC_SDS_IO_REQUEST_T object for which the start + * operation is to be executed. + * + * This method invokes the base state terminate request handber for the + * SCIC_SDS_IO_REQUEST_T object. enum sci_status + */ +enum sci_status scic_sds_io_request_terminate( + struct scic_sds_request *this_request) +{ + return this_request->state_handlers->parent.abort_handler( + &this_request->parent); +} + +/** + * + * @this_request: The SCIC_SDS_IO_REQUEST_T object for which the start + * operation is to be executed. + * + * This method invokes the base state request completion handler for the + * SCIC_SDS_IO_REQUEST_T object. enum sci_status + */ +enum sci_status scic_sds_io_request_complete( + struct scic_sds_request *this_request) +{ + return this_request->state_handlers->parent.complete_handler( + &this_request->parent); +} + +/** + * + * @this_request: The SCIC_SDS_IO_REQUEST_T object for which the start + * operation is to be executed. + * @event_code: The event code returned by the hardware for the task reqeust. + * + * This method invokes the core state handler for the SCIC_SDS_IO_REQUEST_T + * object. enum sci_status + */ +enum sci_status scic_sds_io_request_event_handler( + struct scic_sds_request *this_request, + u32 event_code) +{ + return this_request->state_handlers->event_handler(this_request, event_code); +} + +/** + * + * @this_request: The SCIC_SDS_IO_REQUEST_T object for which the start + * operation is to be executed. + * @frame_index: The frame index returned by the hardware for the reqeust + * object. + * + * This method invokes the core state frame handler for the + * SCIC_SDS_IO_REQUEST_T object. enum sci_status + */ +enum sci_status scic_sds_io_request_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + return this_request->state_handlers->frame_handler(this_request, frame_index); +} + +/** + * + * @this_request: The SCIC_SDS_IO_REQUEST_T object for which the task start + * operation is to be executed. + * + * This method invokes the core state task complete handler for the + * SCIC_SDS_IO_REQUEST_T object. enum sci_status + */ + +/* + * **************************************************************************** + * * SCIC SDS PROTECTED METHODS + * **************************************************************************** */ + +/** + * This method copies response data for requests returning response data + * instead of sense data. + * @this_request: This parameter specifies the request object for which to copy + * the response data. + * + */ +void scic_sds_io_request_copy_response( + struct scic_sds_request *this_request) +{ + void *response_buffer; + u32 user_response_length; + u32 core_response_length; + struct sci_ssp_response_iu *ssp_response; + + ssp_response = (struct sci_ssp_response_iu *)this_request->response_buffer; + + response_buffer = scic_cb_ssp_task_request_get_response_data_address( + this_request->user_request + ); + + user_response_length = scic_cb_ssp_task_request_get_response_data_length( + this_request->user_request + ); + + core_response_length = sci_ssp_get_response_data_length( + ssp_response->response_data_length + ); + + user_response_length = min(user_response_length, core_response_length); + + memcpy(response_buffer, ssp_response->data, user_response_length); +} + +/* + * ***************************************************************************** + * * DEFAULT STATE HANDLERS + * ***************************************************************************** */ + +/** + * scic_sds_request_default_start_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method is the default action to take when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_start() request. The default action is + * to log a warning and return a failure status. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_request_default_start_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *scic_request = + (struct scic_sds_request *)request; + + dev_warn(scic_to_dev(scic_request->owning_controller), + "%s: SCIC IO Request requested to start while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + &((struct scic_sds_request *)request)->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +static enum sci_status scic_sds_request_default_abort_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *scic_request = + (struct scic_sds_request *)request; + + dev_warn(scic_to_dev(scic_request->owning_controller), + "%s: SCIC IO Request requested to abort while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + &((struct scic_sds_request *)request)->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * scic_sds_request_default_complete_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method is the default action to take when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_complete() request. The default action + * is to log a warning and return a failure status. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_request_default_complete_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *scic_request = + (struct scic_sds_request *)request; + + dev_warn(scic_to_dev(scic_request->owning_controller), + "%s: SCIC IO Request requested to complete while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + &((struct scic_sds_request *)request)->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * scic_sds_request_default_destruct_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method is the default action to take when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_complete() request. The default action + * is to log a warning and return a failure status. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_request_default_destruct_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *scic_request = + (struct scic_sds_request *)request; + + dev_warn(scic_to_dev(scic_request->owning_controller), + "%s: SCIC IO Request requested to destroy while in wrong " + "state %d\n", + __func__, + sci_base_state_machine_get_state( + &((struct scic_sds_request *)request)->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * scic_sds_request_default_tc_completion_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method is the default action to take when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_task_request_complete() request. The default + * action is to log a warning and return a failure status. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_request_default_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + dev_warn(scic_to_dev(this_request->owning_controller), + "%s: SCIC IO Request given task completion notification %x " + "while in wrong state %d\n", + __func__, + completion_code, + sci_base_state_machine_get_state( + &this_request->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; + +} + +/** + * scic_sds_request_default_event_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method is the default action to take when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_event_handler() request. The default + * action is to log a warning and return a failure status. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_request_default_event_handler( + struct scic_sds_request *this_request, + u32 event_code) +{ + dev_warn(scic_to_dev(this_request->owning_controller), + "%s: SCIC IO Request given event code notification %x while " + "in wrong state %d\n", + __func__, + event_code, + sci_base_state_machine_get_state( + &this_request->parent.state_machine)); + + return SCI_FAILURE_INVALID_STATE; +} + +/** + * scic_sds_request_default_frame_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method is the default action to take when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_event_handler() request. The default + * action is to log a warning and return a failure status. enum sci_status + * SCI_FAILURE_INVALID_STATE + */ +enum sci_status scic_sds_request_default_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + dev_warn(scic_to_dev(this_request->owning_controller), + "%s: SCIC IO Request given unexpected frame %x while in " + "state %d\n", + __func__, + frame_index, + sci_base_state_machine_get_state( + &this_request->parent.state_machine)); + + scic_sds_controller_release_frame( + this_request->owning_controller, frame_index); + + return SCI_FAILURE_INVALID_STATE; +} + +/* + * ***************************************************************************** + * * CONSTRUCTED STATE HANDLERS + * ***************************************************************************** */ + +/** + * scic_sds_request_constructed_state_start_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action taken when a constructed + * SCIC_SDS_IO_REQUEST_T object receives a scic_sds_request_start() request. + * This method will, if necessary, allocate a TCi for the io request object and + * then will, if necessary, copy the constructed TC data into the actual TC + * buffer. If everything is successful the post context field is updated with + * the TCi so the controller can post the request to the hardware. enum sci_status + * SCI_SUCCESS SCI_FAILURE_INSUFFICIENT_RESOURCES + */ +static enum sci_status scic_sds_request_constructed_state_start_handler( + struct sci_base_request *request) +{ + struct scu_task_context *task_context; + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + if (this_request->io_tag == SCI_CONTROLLER_INVALID_IO_TAG) { + this_request->io_tag = + scic_controller_allocate_io_tag(this_request->owning_controller); + } + + /* Record the IO Tag in the request */ + if (this_request->io_tag != SCI_CONTROLLER_INVALID_IO_TAG) { + task_context = this_request->task_context_buffer; + + task_context->task_index = scic_sds_io_tag_get_index(this_request->io_tag); + + switch (task_context->protocol_type) { + case SCU_TASK_CONTEXT_PROTOCOL_SMP: + case SCU_TASK_CONTEXT_PROTOCOL_SSP: + /* SSP/SMP Frame */ + task_context->type.ssp.tag = this_request->io_tag; + task_context->type.ssp.target_port_transfer_tag = 0xFFFF; + break; + + case SCU_TASK_CONTEXT_PROTOCOL_STP: + /* + * STP/SATA Frame + * task_context->type.stp.ncq_tag = this_request->ncq_tag; */ + break; + + case SCU_TASK_CONTEXT_PROTOCOL_NONE: + /* / @todo When do we set no protocol type? */ + break; + + default: + /* This should never happen since we build the IO requests */ + break; + } + + /* + * Check to see if we need to copy the task context buffer + * or have been building into the task context buffer */ + if (this_request->was_tag_assigned_by_user == false) { + scic_sds_controller_copy_task_context( + this_request->owning_controller, this_request + ); + } + + /* Add to the post_context the io tag value */ + this_request->post_context |= scic_sds_io_tag_get_index(this_request->io_tag); + + /* Everything is good go ahead and change state */ + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_STARTED + ); + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INSUFFICIENT_RESOURCES; +} + +/** + * scic_sds_request_constructed_state_abort_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_terminate() request. Since the request + * has not yet been posted to the hardware the request transitions to the + * completed state. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_request_constructed_state_abort_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + /* + * This request has been terminated by the user make sure that the correct + * status code is returned */ + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_TASK_ABORT, + SCI_FAILURE_IO_TERMINATED + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + return SCI_SUCCESS; +} + +/* + * ***************************************************************************** + * * STARTED STATE HANDLERS + * ***************************************************************************** */ + +/** + * scic_sds_request_started_state_abort_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_terminate() request. Since the request + * has been posted to the hardware the io request state is changed to the + * aborting state. enum sci_status SCI_SUCCESS + */ +enum sci_status scic_sds_request_started_state_abort_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + if (this_request->has_started_substate_machine) { + sci_base_state_machine_stop(&this_request->started_substate_machine); + } + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_ABORTING + ); + + return SCI_SUCCESS; +} + +/** + * scic_sds_request_started_state_tc_completion_handler() - This method process + * TC (task context) completions for normal IO request (i.e. Task/Abort + * Completions of type 0). This method will update the + * SCIC_SDS_IO_REQUEST_T::status field. + * @this_request: This parameter specifies the request for which a completion + * occurred. + * @completion_code: This parameter specifies the completion code received from + * the SCU. + * + */ +enum sci_status scic_sds_request_started_state_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + u8 data_present; + struct sci_ssp_response_iu *response_buffer; + + /** + * @todo Any SDMA return code of other than 0 is bad + * decode 0x003C0000 to determine SDMA status + */ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + break; + + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_EARLY_RESP): + { + /* + * There are times when the SCU hardware will return an early response + * because the io request specified more data than is returned by the + * target device (mode pages, inquiry data, etc.). We must check the + * response stats to see if this is truly a failed request or a good + * request that just got completed early. */ + struct sci_ssp_response_iu *response = (struct sci_ssp_response_iu *) + this_request->response_buffer; + scic_word_copy_with_swap( + this_request->response_buffer, + this_request->response_buffer, + sizeof(struct sci_ssp_response_iu) / sizeof(u32) + ); + + if (response->status == 0) { + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS_IO_DONE_EARLY + ); + } else { + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + } + } + break; + + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CHECK_RESPONSE): + scic_word_copy_with_swap( + this_request->response_buffer, + this_request->response_buffer, + sizeof(struct sci_ssp_response_iu) / sizeof(u32) + ); + + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + break; + + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_RESP_LEN_ERR): + /* + * / @todo With TASK_DONE_RESP_LEN_ERR is the response frame guaranteed + * / to be received before this completion status is posted? */ + response_buffer = + (struct sci_ssp_response_iu *)this_request->response_buffer; + data_present = + response_buffer->data_present & SCI_SSP_RESPONSE_IU_DATA_PRESENT_MASK; + + if ((data_present == 0x01) || (data_present == 0x02)) { + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + } else { + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + } + break; + + /* only stp device gets suspended. */ + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_ACK_NAK_TO): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_PERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_NAK_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_DATA_LEN_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_ABORT_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_XR_WD_LEN): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_RESP): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_SDBFIS): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_REG_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SDB_ERR): + if (this_request->protocol == SCIC_STP_PROTOCOL) { + scic_sds_request_set_status( + this_request, + SCU_GET_COMPLETION_TL_STATUS(completion_code) >> SCU_COMPLETION_TL_STATUS_SHIFT, + SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED + ); + } else { + scic_sds_request_set_status( + this_request, + SCU_GET_COMPLETION_TL_STATUS(completion_code) >> SCU_COMPLETION_TL_STATUS_SHIFT, + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + } + break; + + /* both stp/ssp device gets suspended */ + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LF_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_WRONG_DESTINATION): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_BAD_DESTINATION): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_ZONE_VIOLATION): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED): + scic_sds_request_set_status( + this_request, + SCU_GET_COMPLETION_TL_STATUS(completion_code) >> SCU_COMPLETION_TL_STATUS_SHIFT, + SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED + ); + break; + + /* neither ssp nor stp gets suspended. */ + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_NAK_CMD_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_XR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_XR_IU_LEN_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SDMA_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_OFFSET_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_EXCESS_DATA): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_RESP_TO_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_UFI_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_FRM_TYPE_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_LL_RX_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_DATA): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_OPEN_FAIL): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_VIIT_ENTRY_NV): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_IIT_ENTRY_NV): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_RNCNV_OUTBOUND): + default: + scic_sds_request_set_status( + this_request, + SCU_GET_COMPLETION_TL_STATUS(completion_code) >> SCU_COMPLETION_TL_STATUS_SHIFT, + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + break; + } + + /** + * @todo This is probably wrong for ACK/NAK timeout conditions + */ + + /* In all cases we will treat this as the completion of the IO request. */ + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + return SCI_SUCCESS; +} + +/** + * scic_sds_request_started_state_frame_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * @frame_index: This is the index of the unsolicited frame to be processed. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_frame_handler() request. This method + * first determines the frame type received. If this is a response frame then + * the response data is copied to the io request response buffer for processing + * at completion time. If the frame type is not a response buffer an error is + * logged. enum sci_status SCI_SUCCESS SCI_FAILURE_INVALID_PARAMETER_VALUE + */ +static enum sci_status scic_sds_request_started_state_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + enum sci_status status; + struct sci_ssp_frame_header *frame_header; + + /* / @todo If this is a response frame we must record that we received it */ + status = scic_sds_unsolicited_frame_control_get_header( + &(scic_sds_request_get_controller(this_request)->uf_control), + frame_index, + (void **)&frame_header + ); + + if (frame_header->frame_type == SCI_SAS_RESPONSE_FRAME) { + struct sci_ssp_response_iu *response_buffer; + + status = scic_sds_unsolicited_frame_control_get_buffer( + &(scic_sds_request_get_controller(this_request)->uf_control), + frame_index, + (void **)&response_buffer + ); + + scic_word_copy_with_swap( + this_request->response_buffer, + (u32 *)response_buffer, + sizeof(struct sci_ssp_response_iu) + ); + + response_buffer = (struct sci_ssp_response_iu *)this_request->response_buffer; + + if ((response_buffer->data_present == 0x01) || + (response_buffer->data_present == 0x02)) { + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + } else + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + } else + /* This was not a response frame why did it get forwarded? */ + dev_err(scic_to_dev(this_request->owning_controller), + "%s: SCIC IO Request 0x%p received unexpected " + "frame %d type 0x%02x\n", + __func__, + this_request, + frame_index, + frame_header->frame_type); + + /* + * In any case we are done with this frame buffer return it to the + * controller */ + scic_sds_controller_release_frame( + this_request->owning_controller, frame_index + ); + + return SCI_SUCCESS; +} + +/* + * ***************************************************************************** + * * COMPLETED STATE HANDLERS + * ***************************************************************************** */ + + +/** + * scic_sds_request_completed_state_complete_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_complete() request. This method frees up + * any io request resources that have been allocated and transitions the + * request to its final state. Consider stopping the state machine instead of + * transitioning to the final state? enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_request_completed_state_complete_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + if (this_request->was_tag_assigned_by_user != true) { + scic_controller_free_io_tag( + this_request->owning_controller, this_request->io_tag + ); + } + + if (this_request->saved_rx_frame_index != SCU_INVALID_FRAME_INDEX) { + scic_sds_controller_release_frame( + this_request->owning_controller, this_request->saved_rx_frame_index); + } + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_FINAL + ); + + return SCI_SUCCESS; +} + +/* + * ***************************************************************************** + * * ABORTING STATE HANDLERS + * ***************************************************************************** */ + +/** + * scic_sds_request_aborting_state_abort_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_terminate() request. This method is the + * io request aborting state abort handlers. On receipt of a multiple + * terminate requests the io request will transition to the completed state. + * This should not happen in normal operation. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_request_aborting_state_abort_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + return SCI_SUCCESS; +} + +/** + * scic_sds_request_aborting_state_tc_completion_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_task_completion() request. This method + * decodes the completion type waiting for the abort task complete + * notification. When the abort task complete is received the io request + * transitions to the completed state. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_request_aborting_state_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case (SCU_TASK_DONE_GOOD << SCU_COMPLETION_TL_STATUS_SHIFT): + case (SCU_TASK_DONE_TASK_ABORT << SCU_COMPLETION_TL_STATUS_SHIFT): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_TASK_ABORT, SCI_FAILURE_IO_TERMINATED + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + + default: + /* + * Unless we get some strange error wait for the task abort to complete + * TODO: Should there be a state change for this completion? */ + break; + } + + return SCI_SUCCESS; +} + +/** + * scic_sds_request_aborting_state_frame_handler() - + * @request: This is the struct sci_base_request object that is cast to the + * SCIC_SDS_IO_REQUEST_T object for which the start operation is requested. + * + * This method implements the action to be taken when an SCIC_SDS_IO_REQUEST_T + * object receives a scic_sds_request_frame_handler() request. This method + * discards the unsolicited frame since we are waiting for the abort task + * completion. enum sci_status SCI_SUCCESS + */ +static enum sci_status scic_sds_request_aborting_state_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + /* TODO: Is it even possible to get an unsolicited frame in the aborting state? */ + + scic_sds_controller_release_frame( + this_request->owning_controller, frame_index); + + return SCI_SUCCESS; +} + +/* --------------------------------------------------------------------------- */ + +const struct scic_sds_io_request_state_handler scic_sds_request_state_handler_table[] = { + [SCI_BASE_REQUEST_STATE_INITIAL] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_default_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCI_BASE_REQUEST_STATE_CONSTRUCTED] = { + .parent.start_handler = scic_sds_request_constructed_state_start_handler, + .parent.abort_handler = scic_sds_request_constructed_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCI_BASE_REQUEST_STATE_STARTED] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_started_state_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_started_state_frame_handler, + }, + [SCI_BASE_REQUEST_STATE_COMPLETED] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_default_abort_handler, + .parent.complete_handler = scic_sds_request_completed_state_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCI_BASE_REQUEST_STATE_ABORTING] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_aborting_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_aborting_state_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_aborting_state_frame_handler, + }, + [SCI_BASE_REQUEST_STATE_FINAL] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_default_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, +}; + +/** + * scic_sds_request_initial_state_enter() - + * @object: This parameter specifies the base object for which the state + * transition is occurring. + * + * This method implements the actions taken when entering the + * SCI_BASE_REQUEST_STATE_INITIAL state. This state is entered when the initial + * base request is constructed. Entry into the initial state sets all handlers + * for the io request object to their default handlers. none + */ +static void scic_sds_request_initial_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_request_state_handler_table, + SCI_BASE_REQUEST_STATE_INITIAL + ); +} + +/** + * scic_sds_request_constructed_state_enter() - + * @object: The io request object that is to enter the constructed state. + * + * This method implements the actions taken when entering the + * SCI_BASE_REQUEST_STATE_CONSTRUCTED state. The method sets the state handlers + * for the the constructed state. none + */ +static void scic_sds_request_constructed_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_request_state_handler_table, + SCI_BASE_REQUEST_STATE_CONSTRUCTED + ); +} + +/** + * scic_sds_request_started_state_enter() - + * @object: This parameter specifies the base object for which the state + * transition is occuring. This is cast into a SCIC_SDS_IO_REQUEST object. + * + * This method implements the actions taken when entering the + * SCI_BASE_REQUEST_STATE_STARTED state. If the io request object type is a + * SCSI Task request we must enter the started substate machine. none + */ +static void scic_sds_request_started_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_request_state_handler_table, + SCI_BASE_REQUEST_STATE_STARTED + ); + + /* + * Most of the request state machines have a started substate machine so + * start its execution on the entry to the started state. */ + if (this_request->has_started_substate_machine == true) + sci_base_state_machine_start(&this_request->started_substate_machine); +} + +/** + * scic_sds_request_started_state_exit() - + * @object: This parameter specifies the base object for which the state + * transition is occuring. This object is cast into a SCIC_SDS_IO_REQUEST + * object. + * + * This method implements the actions taken when exiting the + * SCI_BASE_REQUEST_STATE_STARTED state. For task requests the action will be + * to stop the started substate machine. none + */ +static void scic_sds_request_started_state_exit( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + if (this_request->has_started_substate_machine == true) + sci_base_state_machine_stop(&this_request->started_substate_machine); +} + +/** + * scic_sds_request_completed_state_enter() - + * @object: This parameter specifies the base object for which the state + * transition is occuring. This object is cast into a SCIC_SDS_IO_REQUEST + * object. + * + * This method implements the actions taken when entering the + * SCI_BASE_REQUEST_STATE_COMPLETED state. This state is entered when the + * SCIC_SDS_IO_REQUEST has completed. The method will decode the request + * completion status and convert it to an enum sci_status to return in the + * completion callback function. none + */ +static void scic_sds_request_completed_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_request_state_handler_table, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + /* Tell the SCI_USER that the IO request is complete */ + if (this_request->is_task_management_request == false) { + scic_cb_io_request_complete( + scic_sds_request_get_controller(this_request), + scic_sds_request_get_device(this_request), + this_request, + this_request->sci_status + ); + } else { + scic_cb_task_request_complete( + scic_sds_request_get_controller(this_request), + scic_sds_request_get_device(this_request), + this_request, + this_request->sci_status + ); + } +} + +/** + * scic_sds_request_aborting_state_enter() - + * @object: This parameter specifies the base object for which the state + * transition is occuring. This object is cast into a SCIC_SDS_IO_REQUEST + * object. + * + * This method implements the actions taken when entering the + * SCI_BASE_REQUEST_STATE_ABORTING state. none + */ +static void scic_sds_request_aborting_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + /* Setting the abort bit in the Task Context is required by the silicon. */ + this_request->task_context_buffer->abort = 1; + + SET_STATE_HANDLER( + this_request, + scic_sds_request_state_handler_table, + SCI_BASE_REQUEST_STATE_ABORTING + ); +} + +/** + * scic_sds_request_final_state_enter() - + * @object: This parameter specifies the base object for which the state + * transition is occuring. This is cast into a SCIC_SDS_IO_REQUEST object. + * + * This method implements the actions taken when entering the + * SCI_BASE_REQUEST_STATE_FINAL state. The only action required is to put the + * state handlers in place. none + */ +static void scic_sds_request_final_state_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_request_state_handler_table, + SCI_BASE_REQUEST_STATE_FINAL + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_request_state_table[] = { + [SCI_BASE_REQUEST_STATE_INITIAL] = { + .enter_state = scic_sds_request_initial_state_enter, + }, + [SCI_BASE_REQUEST_STATE_CONSTRUCTED] = { + .enter_state = scic_sds_request_constructed_state_enter, + }, + [SCI_BASE_REQUEST_STATE_STARTED] = { + .enter_state = scic_sds_request_started_state_enter, + .exit_state = scic_sds_request_started_state_exit + }, + [SCI_BASE_REQUEST_STATE_COMPLETED] = { + .enter_state = scic_sds_request_completed_state_enter, + }, + [SCI_BASE_REQUEST_STATE_ABORTING] = { + .enter_state = scic_sds_request_aborting_state_enter, + }, + [SCI_BASE_REQUEST_STATE_FINAL] = { + .enter_state = scic_sds_request_final_state_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_request.h b/drivers/scsi/isci/core/scic_sds_request.h new file mode 100644 index 000000000000..0691a7552a2a --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_request.h @@ -0,0 +1,484 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_IO_REQUEST_H_ +#define _SCIC_SDS_IO_REQUEST_H_ + +/** + * This file contains the structures, constants and prototypes for the + * SCIC_SDS_IO_REQUEST object. + * + * + */ + +#include "scic_io_request.h" + +#include "sci_base_request.h" +#include "scu_task_context.h" +#include "intel_sas.h" + +struct scic_sds_controller; +struct scic_sds_remote_device; +struct scic_sds_io_request_state_handler; + +/** + * enum _SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATES - This enumeration + * depicts all of the substates for a task management request to be + * performed in the STARTED super-state. + * + * + */ +enum scic_sds_raw_request_started_task_mgmt_substates { + /** + * The AWAIT_TC_COMPLETION sub-state indicates that the started raw + * task management request is waiting for the transmission of the + * initial frame (i.e. command, task, etc.). + */ + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_COMPLETION, + + /** + * This sub-state indicates that the started task management request + * is waiting for the reception of an unsolicited frame + * (i.e. response IU). + */ + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE, +}; + + +/** + * enum _SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATES - This enumeration depicts all + * of the substates for a SMP request to be performed in the STARTED + * super-state. + * + * + */ +enum scic_sds_smp_request_started_substates { + /** + * This sub-state indicates that the started task management request + * is waiting for the reception of an unsolicited frame + * (i.e. response IU). + */ + SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE, + + /** + * The AWAIT_TC_COMPLETION sub-state indicates that the started SMP request is + * waiting for the transmission of the initial frame (i.e. command, task, etc.). + */ + SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION, +}; + +/** + * struct SCIC_SDS_IO_REQUEST - This structure contains or references all of + * the data necessary to process a task management or normal IO request. + * + * + */ +struct scic_sds_request { + /** + * This field indictes the parent object of the request. + */ + struct sci_base_request parent; + + void *user_request; + + /** + * This field simply points to the controller to which this IO request + * is associated. + */ + struct scic_sds_controller *owning_controller; + + /** + * This field simply points to the remote device to which this IO request + * is associated. + */ + struct scic_sds_remote_device *target_device; + + /** + * This field is utilized to determine if the SCI user is managing + * the IO tag for this request or if the core is managing it. + */ + bool was_tag_assigned_by_user; + + /** + * This field indicates the IO tag for this request. The IO tag is + * comprised of the task_index and a sequence count. The sequence count + * is utilized to help identify tasks from one life to another. + */ + u16 io_tag; + + /** + * This field specifies the protocol being utilized for this + * IO request. + */ + SCIC_TRANSPORT_PROTOCOL protocol; + + /** + * This field indicates the completion status taken from the SCUs + * completion code. It indicates the completion result for the SCU hardware. + */ + u32 scu_status; + + /** + * This field indicates the completion status returned to the SCI user. It + * indicates the users view of the io request completion. + */ + u32 sci_status; + + /** + * This field contains the value to be utilized when posting (e.g. Post_TC, + * Post_TC_Abort) this request to the silicon. + */ + u32 post_context; + + void *command_buffer; + void *response_buffer; + struct scu_task_context *task_context_buffer; + struct scu_sgl_element_pair *sgl_element_pair_buffer; + + /** + * This field indicates if this request is a task management request or + * normal IO request. + */ + bool is_task_management_request; + + /** + * This field indicates that this request contains an initialized started + * substate machine. + */ + bool has_started_substate_machine; + + /** + * This field is a pointer to the stored rx frame data. It is used in STP + * internal requests and SMP response frames. If this field is non-NULL the + * saved frame must be released on IO request completion. + * + * @todo In the future do we want to keep a list of RX frame buffers? + */ + u32 saved_rx_frame_index; + + /** + * This field specifies the data necessary to manage the sub-state + * machine executed while in the SCI_BASE_REQUEST_STATE_STARTED state. + */ + struct sci_base_state_machine started_substate_machine; + + /** + * This field specifies the current state handlers in place for this + * IO Request object. This field is updated each time the request + * changes state. + */ + const struct scic_sds_io_request_state_handler *state_handlers; + + /** + * This field in the recorded device sequence for the io request. This is + * recorded during the build operation and is compared in the start + * operation. If the sequence is different then there was a change of + * devices from the build to start operations. + */ + u8 device_sequence; + +}; + + +typedef enum sci_status +(*scic_sds_io_request_frame_handler_t)(struct scic_sds_request *req, u32 frame); + +typedef enum sci_status +(*scic_sds_io_request_event_handler_t)(struct scic_sds_request *req, u32 event); + +typedef enum sci_status +(*scic_sds_io_request_task_completion_handler_t)(struct scic_sds_request *req, u32 completion_code); + +/** + * struct scic_sds_io_request_state_handler - This is the SDS core definition + * of the state handlers. + * + * + */ +struct scic_sds_io_request_state_handler { + struct sci_base_request_state_handler parent; + + scic_sds_io_request_task_completion_handler_t tc_completion_handler; + scic_sds_io_request_event_handler_t event_handler; + scic_sds_io_request_frame_handler_t frame_handler; + +}; + +extern const struct sci_base_state scic_sds_request_state_table[]; +extern const struct scic_sds_io_request_state_handler scic_sds_request_state_handler_table[]; + +extern const struct sci_base_state scic_sds_io_request_started_task_mgmt_substate_table[]; +extern const struct scic_sds_io_request_state_handler scic_sds_ssp_task_request_started_substate_handler_table[]; + +extern const struct sci_base_state scic_sds_smp_request_started_substate_table[]; +extern const struct scic_sds_io_request_state_handler scic_sds_smp_request_started_substate_handler_table[]; + +/** + * + * + * This macro returns the maximum number of SGL element paris that we will + * support in a single IO request. + */ +#define SCU_MAX_SGL_ELEMENT_PAIRS ((SCU_IO_REQUEST_SGE_COUNT + 1) / 2) + +/** + * scic_sds_request_get_controller() - + * + * This macro will return the controller for this io request object + */ +#define scic_sds_request_get_controller(this_request) \ + ((this_request)->owning_controller) + +/** + * scic_sds_request_get_device() - + * + * This macro will return the device for this io request object + */ +#define scic_sds_request_get_device(this_request) \ + ((this_request)->target_device) + +/** + * scic_sds_request_get_port() - + * + * This macro will return the port for this io request object + */ +#define scic_sds_request_get_port(this_request) \ + scic_sds_remote_device_get_port(scic_sds_request_get_device(this_request)) + +/** + * scic_sds_request_get_post_context() - + * + * This macro returns the constructed post context result for the io request. + */ +#define scic_sds_request_get_post_context(this_request) \ + ((this_request)->post_context) + +/** + * scic_sds_request_get_task_context() - + * + * This is a helper macro to return the os handle for this request object. + */ +#define scic_sds_request_get_task_context(request) \ + ((request)->task_context_buffer) + +#define CACHE_LINE_SIZE (64) +#define scic_sds_request_align_task_context_buffer(address) \ + ((struct scu_task_context *)(\ + (((unsigned long)(address)) + (CACHE_LINE_SIZE - 1)) \ + & ~(CACHE_LINE_SIZE - 1) \ + )) + +/** + * scic_sds_request_align_sgl_element_buffer() - + * + * This macro will align the memory address so that it is correct for the SCU + * hardware to DMA the SGL element pairs. + */ +#define scic_sds_request_align_sgl_element_buffer(address) \ + ((struct scu_sgl_element_pair *)(\ + ((char *)(address)) \ + + (\ + ((~(unsigned long)(address)) + 1) \ + & (sizeof(struct scu_sgl_element_pair) - 1) \ + ) \ + )) + +/** + * scic_sds_request_set_status() - + * + * This macro will set the scu hardware status and sci request completion + * status for an io request. + */ +#define scic_sds_request_set_status(request, scu_status_code, sci_status_code) \ + { \ + (request)->scu_status = (scu_status_code); \ + (request)->sci_status = (sci_status_code); \ + } + +#define scic_sds_request_complete(a_request) \ + ((a_request)->state_handlers->parent.complete_handler(&(a_request)->parent)) + + + + +/** + * scic_sds_io_request_tc_completion() - + * + * This macro invokes the core state task completion handler for the + * SCIC_SDS_IO_REQUEST_T object. + */ +#define scic_sds_io_request_tc_completion(this_request, completion_code) \ + { \ + if (this_request->parent.state_machine.current_state_id \ + == SCI_BASE_REQUEST_STATE_STARTED \ + && this_request->has_started_substate_machine \ + == false) \ + scic_sds_request_started_state_tc_completion_handler(this_request, completion_code); \ + else \ + this_request->state_handlers->tc_completion_handler(this_request, completion_code); \ + } + +/** + * SCU_SGL_ZERO() - + * + * This macro zeros the hardware SGL element data + */ +#define SCU_SGL_ZERO(scu_sge) \ + { \ + (scu_sge).length = 0; \ + (scu_sge).address_lower = 0; \ + (scu_sge).address_upper = 0; \ + (scu_sge).address_modifier = 0; \ + } + +/** + * SCU_SGL_COPY() - + * + * This macro copys the SGL Element data from the host os to the hardware SGL + * elment data + */ +#define SCU_SGL_COPY(os_handle, scu_sge, os_sge) \ + { \ + (scu_sge).length = \ + scic_cb_sge_get_length_field(os_handle, os_sge); \ + (scu_sge).address_upper = \ + upper_32_bits(scic_cb_sge_get_address_field(os_handle, os_sge)); \ + (scu_sge).address_lower = \ + lower_32_bits(scic_cb_sge_get_address_field(os_handle, os_sge)); \ + (scu_sge).address_modifier = 0; \ + } + +/* + * ***************************************************************************** + * * CORE REQUEST PROTOTYPES + * ***************************************************************************** */ + +void scic_sds_request_build_sgl( + struct scic_sds_request *this_request); + + + +void scic_sds_stp_request_assign_buffers( + struct scic_sds_request *this_request); + +void scic_sds_smp_request_assign_buffers( + struct scic_sds_request *this_request); + +/* --------------------------------------------------------------------------- */ + +enum sci_status scic_sds_request_start( + struct scic_sds_request *this_request); + +enum sci_status scic_sds_io_request_terminate( + struct scic_sds_request *this_request); + +enum sci_status scic_sds_io_request_complete( + struct scic_sds_request *this_request); + +void scic_sds_io_request_copy_response( + struct scic_sds_request *this_request); + +enum sci_status scic_sds_io_request_event_handler( + struct scic_sds_request *this_request, + u32 event_code); + +enum sci_status scic_sds_io_request_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index); + + +enum sci_status scic_sds_task_request_terminate( + struct scic_sds_request *this_request); + +/* + * ***************************************************************************** + * * DEFAULT STATE HANDLERS + * ***************************************************************************** */ + +enum sci_status scic_sds_request_default_start_handler( + struct sci_base_request *this_request); + + +enum sci_status scic_sds_request_default_complete_handler( + struct sci_base_request *this_request); + +enum sci_status scic_sds_request_default_destruct_handler( + struct sci_base_request *this_request); + +enum sci_status scic_sds_request_default_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code); + +enum sci_status scic_sds_request_default_event_handler( + struct scic_sds_request *this_request, + u32 event_code); + +enum sci_status scic_sds_request_default_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index); + +/* + * ***************************************************************************** + * * STARTED STATE HANDLERS + * ***************************************************************************** */ + +enum sci_status scic_sds_request_started_state_abort_handler( + struct sci_base_request *this_request); + +enum sci_status scic_sds_request_started_state_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code); + +#endif /* _SCIC_SDS_IO_REQUEST_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_smp_remote_device.c b/drivers/scsi/isci/core/scic_sds_smp_remote_device.c new file mode 100644 index 000000000000..7cf78d3212cc --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_smp_remote_device.c @@ -0,0 +1,410 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains This file contains the ready substate handlers for a SMP + * device. + * + * + */ + +#include "sci_environment.h" +#include "scic_user_callback.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" +#include "scic_sds_request.h" +#include "scu_event_codes.h" +#include "scu_task_context.h" + + +/* + * ***************************************************************************** + * * SMP REMOTE DEVICE READY IDLE SUBSTATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @[in]: device The device the io is sent to. + * @[in]: request The io to start. + * + * This method will handle the start io operation for a SMP device that is in + * the idle state. enum sci_status + */ +static enum sci_status scic_sds_smp_remote_device_ready_idle_substate_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *io_request = (struct scic_sds_request *)request; + + /* Will the port allow the io request to start? */ + status = this_device->owning_port->state_handlers->start_io_handler( + this_device->owning_port, + this_device, + io_request + ); + + if (status == SCI_SUCCESS) { + status = + scic_sds_remote_node_context_start_io(this_device->rnc, io_request); + + if (status == SCI_SUCCESS) { + status = scic_sds_request_start(io_request); + } + + if (status == SCI_SUCCESS) { + this_device->working_request = io_request; + + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD + ); + } + + scic_sds_remote_device_start_request(this_device, io_request, status); + } + + return status; +} + + +/* + * ****************************************************************************** + * * SMP REMOTE DEVICE READY SUBSTATE CMD HANDLERS + * ****************************************************************************** */ +/** + * + * @device: This is the device object that is receiving the IO. + * @request: The io to start. + * + * This device is already handling a command it can not accept new commands + * until this one is complete. enum sci_status + */ +static enum sci_status scic_sds_smp_remote_device_ready_cmd_substate_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + return SCI_FAILURE_INVALID_STATE; +} + + +/** + * this is the complete_io_handler for smp device at ready cmd substate. + * @device: This is the device object that is receiving the IO. + * @request: The io to start. + * + * enum sci_status + */ +static enum sci_status scic_sds_smp_remote_device_ready_cmd_substate_complete_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status; + struct scic_sds_remote_device *this_device; + struct scic_sds_request *the_request; + + this_device = (struct scic_sds_remote_device *)device; + the_request = (struct scic_sds_request *)request; + + status = scic_sds_io_request_complete(the_request); + + if (status == SCI_SUCCESS) { + status = scic_sds_port_complete_io( + this_device->owning_port, this_device, the_request); + + if (status == SCI_SUCCESS) { + scic_sds_remote_device_decrement_request_count(this_device); + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE + ); + } else + dev_err(scirdev_to_dev(this_device), + "%s: SCIC SDS Remote Device 0x%p io request " + "0x%p could not be completd on the port 0x%p " + "failed with status %d.\n", + __func__, + this_device, + the_request, + this_device->owning_port, + status); + } + + return status; +} + +/** + * This is frame handler for smp device ready cmd substate. + * @this_device: This is the device object that is receiving the frame. + * @frame_index: The index for the frame received. + * + * enum sci_status + */ +static enum sci_status scic_sds_smp_remote_device_ready_cmd_substate_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index) +{ + enum sci_status status; + + /* + * / The device does not process any UF received from the hardware while + * / in this state. All unsolicited frames are forwarded to the io request + * / object. */ + status = scic_sds_io_request_frame_handler( + this_device->working_request, + frame_index + ); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_remote_device_state_handler +scic_sds_smp_remote_device_ready_substate_handler_table[ + SCIC_SDS_SMP_REMOTE_DEVICE_READY_MAX_SUBSTATES] = +{ + /* SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_smp_remote_device_ready_idle_substate_start_io_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_remote_device_default_frame_handler + }, + /* SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_default_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_smp_remote_device_ready_cmd_substate_start_io_handler, + scic_sds_smp_remote_device_ready_cmd_substate_complete_io_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_smp_remote_device_ready_cmd_substate_frame_handler + } +}; +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the enter and exit functions for the + * struct scic_sds_remote_device ready substate machine. + * + * + */ + +#include "scic_remote_device.h" +#include "scic_user_callback.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" +#include "sci_util.h" +#include "sci_environment.h" + +/** + * + * @object: This is the struct sci_base_object which is cast into a + * struct scic_sds_remote_device. + * + * This is the SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE enter method. + * This method sets the ready cmd substate handlers and reports the device as + * ready. none + */ +static void scic_sds_smp_remote_device_ready_idle_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + SET_STATE_HANDLER( + this_device, + scic_sds_smp_remote_device_ready_substate_handler_table, + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE + ); + + scic_cb_remote_device_ready( + scic_sds_remote_device_get_controller(this_device), this_device); +} + +/** + * + * @object: This is the struct sci_base_object which is cast into a + * struct scic_sds_remote_device. + * + * This is the SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD enter method. This + * method sets the remote device objects ready cmd substate handlers, and + * notify core user that the device is not ready. none + */ +static void scic_sds_smp_remote_device_ready_cmd_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + BUG_ON(this_device->working_request == NULL); + + SET_STATE_HANDLER( + this_device, + scic_sds_smp_remote_device_ready_substate_handler_table, + SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD + ); + + scic_cb_remote_device_not_ready( + scic_sds_remote_device_get_controller(this_device), + this_device, + SCIC_REMOTE_DEVICE_NOT_READY_SMP_REQUEST_STARTED + ); +} + +/** + * + * @object: This is the struct sci_base_object which is cast into a + * struct scic_sds_remote_device. + * + * This is the SCIC_SDS_SSP_REMOTE_DEVICE_READY_SUBSTATE_CMD exit method. none + */ +static void scic_sds_smp_remote_device_ready_cmd_substate_exit( + struct sci_base_object *object) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)object; + + this_device->working_request = NULL; +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_smp_remote_device_ready_substate_table[] = { + [SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_IDLE] = { + .enter_state = scic_sds_smp_remote_device_ready_idle_substate_enter, + }, + [SCIC_SDS_SMP_REMOTE_DEVICE_READY_SUBSTATE_CMD] = { + .enter_state = scic_sds_smp_remote_device_ready_cmd_substate_enter, + .exit_state = scic_sds_smp_remote_device_ready_cmd_substate_exit, + }, +}; diff --git a/drivers/scsi/isci/core/scic_sds_smp_request.c b/drivers/scsi/isci/core/scic_sds_smp_request.c new file mode 100644 index 000000000000..949d23e7452a --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_smp_request.c @@ -0,0 +1,669 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "intel_sas.h" +#include "sci_base_state_machine.h" +#include "scic_controller.h" +#include "scic_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_sds_smp_request.h" +#include "sci_environment.h" +#include "sci_util.h" +#include "scu_completion_codes.h" +#include "scu_task_context.h" + +static void scu_smp_request_construct_task_context( + struct scic_sds_request *this_request, + struct smp_request *smp_request); + +/** + * + * + * This method return the memory space required for STP PIO requests. u32 + */ +u32 scic_sds_smp_request_get_object_size(void) +{ + return sizeof(struct scic_sds_request) + + sizeof(struct smp_request) + + sizeof(struct smp_response) + + sizeof(struct scu_task_context); +} + +/** + * scic_sds_smp_request_get_command_buffer() - + * + * This macro returns the address of the smp command buffer in the smp request + * memory. No need to cast to SMP request type. + */ +#define scic_sds_smp_request_get_command_buffer(memory) \ + (((char *)(memory)) + sizeof(struct scic_sds_request)) + +/** + * scic_sds_smp_request_get_response_buffer() - + * + * This macro returns the address of the smp response buffer in the smp request + * memory. + */ +#define scic_sds_smp_request_get_response_buffer(memory) \ + (((char *)(scic_sds_smp_request_get_command_buffer(memory))) \ + + sizeof(struct smp_request)) + +/** + * scic_sds_smp_request_get_task_context_buffer() - + * + * This macro returs the task context buffer for the SMP request. + */ +#define scic_sds_smp_request_get_task_context_buffer(memory) \ + ((struct scu_task_context *)(\ + ((char *)(scic_sds_smp_request_get_response_buffer(memory))) \ + + sizeof(struct smp_response) \ + )) + + + +/** + * This method build the remainder of the IO request object. + * @this_request: This parameter specifies the request object being constructed. + * + * The scic_sds_general_request_construct() must be called before this call is + * valid. none + */ + +void scic_sds_smp_request_assign_buffers( + struct scic_sds_request *this_request) +{ + /* Assign all of the buffer pointers */ + this_request->command_buffer = + scic_sds_smp_request_get_command_buffer(this_request); + this_request->response_buffer = + scic_sds_smp_request_get_response_buffer(this_request); + this_request->sgl_element_pair_buffer = NULL; + + if (this_request->was_tag_assigned_by_user == false) { + this_request->task_context_buffer = + scic_sds_smp_request_get_task_context_buffer(this_request); + this_request->task_context_buffer = + scic_sds_request_align_task_context_buffer(this_request->task_context_buffer); + } + +} +/** + * This method is called by the SCI user to build an SMP IO request. + * + * - The user must have previously called scic_io_request_construct() on the + * supplied IO request. Indicate if the controller successfully built the IO + * request. SCI_SUCCESS This value is returned if the IO request was + * successfully built. SCI_FAILURE_UNSUPPORTED_PROTOCOL This value is returned + * if the remote_device does not support the SMP protocol. + * SCI_FAILURE_INVALID_ASSOCIATION This value is returned if the user did not + * properly set the association between the SCIC IO request and the user's IO + * request. Please refer to the sci_object_set_association() routine for more + * information. + */ +enum sci_status scic_io_request_construct_smp( + struct scic_sds_request *sci_req) +{ + struct smp_request *smp_req = kmalloc(sizeof(*smp_req), GFP_KERNEL); + + if (!smp_req) + return SCI_FAILURE_INSUFFICIENT_RESOURCES; + + sci_req->protocol = SCIC_SMP_PROTOCOL; + sci_req->has_started_substate_machine = true; + + /* Construct the started sub-state machine. */ + sci_base_state_machine_construct( + &sci_req->started_substate_machine, + &sci_req->parent.parent, + scic_sds_smp_request_started_substate_table, + SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE + ); + + /* Construct the SMP SCU Task Context */ + memcpy(smp_req, sci_req->command_buffer, sizeof(*smp_req)); + + /* + * Look at the SMP requests' header fields; for certain SAS 1.x SMP + * functions under SAS 2.0, a zero request length really indicates + * a non-zero default length. */ + if (smp_req->header.request_length == 0) { + switch (smp_req->header.function) { + case SMP_FUNCTION_DISCOVER: + case SMP_FUNCTION_REPORT_PHY_ERROR_LOG: + case SMP_FUNCTION_REPORT_PHY_SATA: + case SMP_FUNCTION_REPORT_ROUTE_INFORMATION: + smp_req->header.request_length = 2; + break; + case SMP_FUNCTION_CONFIGURE_ROUTE_INFORMATION: + case SMP_FUNCTION_PHY_CONTROL: + case SMP_FUNCTION_PHY_TEST: + smp_req->header.request_length = 9; + break; + /* Default - zero is a valid default for 2.0. */ + } + } + + scu_smp_request_construct_task_context(sci_req, smp_req); + + sci_base_state_machine_change_state( + &sci_req->parent.state_machine, + SCI_BASE_REQUEST_STATE_CONSTRUCTED + ); + + kfree(smp_req); + + return SCI_SUCCESS; +} + +/** + * This method is called by the SCI user to build an SMP pass-through IO + * request. + * @scic_smp_request: This parameter specifies the handle to the io request + * object to be built. + * @passthru_cb: This parameter specifies the pointer to the callback structure + * that contains the function pointers + * + * - The user must have previously called scic_io_request_construct() on the + * supplied IO request. Indicate if the controller successfully built the IO + * request. + */ + +/** + * This method will fill in the SCU Task Context for a SMP request. The + * following important settings are utilized: -# task_type == + * SCU_TASK_TYPE_SMP. This simply indicates that a normal request type + * (i.e. non-raw frame) is being utilized to perform task management. -# + * control_frame == 1. This ensures that the proper endianess is set so + * that the bytes are transmitted in the right order for a smp request frame. + * @this_request: This parameter specifies the smp request object being + * constructed. + * + */ +static void scu_smp_request_construct_task_context( + struct scic_sds_request *this_request, + struct smp_request *smp_request) +{ + dma_addr_t physical_address; + struct scic_sds_controller *owning_controller; + struct scic_sds_remote_device *target_device; + struct scic_sds_port *target_port; + struct scu_task_context *task_context; + + /* byte swap the smp request. */ + scic_word_copy_with_swap( + this_request->command_buffer, + (u32 *)smp_request, + sizeof(struct smp_request) / sizeof(u32) + ); + + task_context = scic_sds_request_get_task_context(this_request); + + owning_controller = scic_sds_request_get_controller(this_request); + target_device = scic_sds_request_get_device(this_request); + target_port = scic_sds_request_get_port(this_request); + + /* + * Fill in the TC with the its required data + * 00h */ + task_context->priority = 0; + task_context->initiator_request = 1; + task_context->connection_rate = + scic_remote_device_get_connection_rate(target_device); + task_context->protocol_engine_index = + scic_sds_controller_get_protocol_engine_group(owning_controller); + task_context->logical_port_index = + scic_sds_port_get_index(target_port); + task_context->protocol_type = SCU_TASK_CONTEXT_PROTOCOL_SMP; + task_context->abort = 0; + task_context->valid = SCU_TASK_CONTEXT_VALID; + task_context->context_type = SCU_TASK_CONTEXT_TYPE; + + /* 04h */ + task_context->remote_node_index = this_request->target_device->rnc->remote_node_index; + task_context->command_code = 0; + task_context->task_type = SCU_TASK_TYPE_SMP_REQUEST; + + /* 08h */ + task_context->link_layer_control = 0; + task_context->do_not_dma_ssp_good_response = 1; + task_context->strict_ordering = 0; + task_context->control_frame = 1; + task_context->timeout_enable = 0; + task_context->block_guard_enable = 0; + + /* 0ch */ + task_context->address_modifier = 0; + + /* 10h */ + task_context->ssp_command_iu_length = smp_request->header.request_length; + + /* 14h */ + task_context->transfer_length_bytes = 0; + + /* + * 18h ~ 30h, protocol specific + * since commandIU has been build by framework at this point, we just + * copy the frist DWord from command IU to this location. */ + memcpy((void *)(&task_context->type.smp), this_request->command_buffer, sizeof(u32)); + + /* + * 40h + * "For SMP you could program it to zero. We would prefer that way so that + * done code will be consistent." - Venki */ + task_context->task_phase = 0; + + if (this_request->was_tag_assigned_by_user) { + /* Build the task context now since we have already read the data */ + this_request->post_context = ( + SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC + | ( + scic_sds_controller_get_protocol_engine_group(owning_controller) + << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT + ) + | ( + scic_sds_port_get_index(target_port) + << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT + ) + | scic_sds_io_tag_get_index(this_request->io_tag) + ); + } else { + /* Build the task context now since we have already read the data */ + this_request->post_context = ( + SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC + | ( + scic_sds_controller_get_protocol_engine_group(owning_controller) + << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT + ) + | ( + scic_sds_port_get_index(target_port) + << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT + ) + /* This is not assigned because we have to wait until we get a TCi */ + ); + } + + /* + * Copy the physical address for the command buffer to the SCU Task Context + * command buffer should not contain command header. */ + scic_cb_io_request_get_physical_address( + scic_sds_request_get_controller(this_request), + this_request, + ((char *)(this_request->command_buffer) + sizeof(u32)), + &physical_address + ); + + task_context->command_iu_upper = + upper_32_bits(physical_address); + task_context->command_iu_lower = + lower_32_bits(physical_address); + + + /* SMP response comes as UF, so no need to set response IU address. */ + task_context->response_iu_upper = 0; + task_context->response_iu_lower = 0; +} + +/** + * This method processes an unsolicited frame while the SMP request is waiting + * for a response frame. It will copy the response data, release the + * unsolicited frame, and transition the request to the + * SCI_BASE_REQUEST_STATE_COMPLETED state. + * @this_request: This parameter specifies the request for which the + * unsolicited frame was received. + * @frame_index: This parameter indicates the unsolicited frame index that + * should contain the response. + * + * This method returns an indication of whether the response frame was handled + * successfully or not. SCI_SUCCESS Currently this value is always returned and + * indicates successful processing of the TC response. + */ +static enum sci_status scic_sds_smp_request_await_response_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + enum sci_status status; + void *frame_header; + struct smp_response_header *this_frame_header; + u8 *user_smp_buffer = this_request->response_buffer; + + status = scic_sds_unsolicited_frame_control_get_header( + &(scic_sds_request_get_controller(this_request)->uf_control), + frame_index, + &frame_header + ); + + /* byte swap the header. */ + scic_word_copy_with_swap( + (u32 *)user_smp_buffer, + frame_header, + sizeof(struct smp_response_header) / sizeof(u32) + ); + this_frame_header = (struct smp_response_header *)user_smp_buffer; + + if (this_frame_header->smp_frame_type == SMP_FRAME_TYPE_RESPONSE) { + void *smp_response_buffer; + + status = scic_sds_unsolicited_frame_control_get_buffer( + &(scic_sds_request_get_controller(this_request)->uf_control), + frame_index, + &smp_response_buffer + ); + + scic_word_copy_with_swap( + (u32 *)(user_smp_buffer + sizeof(struct smp_response_header)), + smp_response_buffer, + sizeof(union smp_response_body) / sizeof(u32) + ); + if (this_frame_header->function == SMP_FUNCTION_DISCOVER) { + struct smp_response *this_smp_response; + + this_smp_response = (struct smp_response *)user_smp_buffer; + + /* + * Some expanders only report an attached SATA device, and + * not an STP target. Since the core depends on the STP + * target attribute to correctly build I/O, set the bit now + * if necessary. */ + if (this_smp_response->response.discover.protocols.u.bits.attached_sata_device + && !this_smp_response->response.discover.protocols.u.bits.attached_stp_target) { + this_smp_response->response.discover.protocols.u.bits.attached_stp_target = 1; + + dev_dbg(scic_to_dev(this_request->owning_controller), + "%s: scic_sds_smp_request_await_response_frame_handler(0x%p) Found SATA dev, setting STP bit.\n", + __func__, this_request); + } + } + + /* + * Don't need to copy to user space. User instead will refer to + * core request's response buffer. */ + + /* + * copy the smp response to framework smp request's response buffer. + * scic_sds_smp_request_copy_response(this_request); */ + + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION + ); + } else { + /* This was not a response frame why did it get forwarded? */ + dev_err(scic_to_dev(this_request->owning_controller), + "%s: SCIC SMP Request 0x%p received unexpected frame " + "%d type 0x%02x\n", + __func__, + this_request, + frame_index, + this_frame_header->smp_frame_type); + + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_SMP_FRM_TYPE_ERR, + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + } + + scic_sds_controller_release_frame( + this_request->owning_controller, frame_index + ); + + return SCI_SUCCESS; +} + + +/** + * This method processes an abnormal TC completion while the SMP request is + * waiting for a response frame. It decides what happened to the IO based + * on TC completion status. + * @this_request: This parameter specifies the request for which the TC + * completion was received. + * @completion_code: This parameter indicates the completion status information + * for the TC. + * + * Indicate if the tc completion handler was successful. SCI_SUCCESS currently + * this method always returns success. + */ +static enum sci_status scic_sds_smp_request_await_response_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + /* + * In the AWAIT RESPONSE state, any TC completion is unexpected. + * but if the TC has success status, we complete the IO anyway. */ + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_RESP_TO_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_UFI_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_FRM_TYPE_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_SMP_LL_RX_ERR): + /* + * These status has been seen in a specific LSI expander, which sometimes + * is not able to send smp response within 2 ms. This causes our hardware + * break the connection and set TC completion with one of these SMP_XXX_XX_ERR + * status. For these type of error, we ask scic user to retry the request. */ + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_SMP_RESP_TO_ERR, SCI_FAILURE_RETRY_REQUIRED + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return SCI_SUCCESS; +} + + +/** + * This method processes the completions transport layer (TL) status to + * determine if the SMP request was sent successfully. If the SMP request + * was sent successfully, then the state for the SMP request transits to + * waiting for a response frame. + * @this_request: This parameter specifies the request for which the TC + * completion was received. + * @completion_code: This parameter indicates the completion status information + * for the TC. + * + * Indicate if the tc completion handler was successful. SCI_SUCCESS currently + * this method always returns success. + */ +static enum sci_status scic_sds_smp_request_await_tc_completion_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return SCI_SUCCESS; +} + + +const struct scic_sds_io_request_state_handler scic_sds_smp_request_started_substate_handler_table[] = { + [SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_smp_request_await_response_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_smp_request_await_response_frame_handler, + }, + [SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_smp_request_await_tc_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + } +}; + +/** + * This method performs the actions required when entering the + * SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_RESPONSE sub-state. This + * includes setting the IO request state handlers for this sub-state. + * @object: This parameter specifies the request object for which the sub-state + * change is occuring. + * + * none. + */ +static void scic_sds_smp_request_started_await_response_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_smp_request_started_substate_handler_table, + SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE + ); +} + +/** + * This method performs the actions required when entering the + * SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION sub-state. + * This includes setting the SMP request state handlers for this sub-state. + * @object: This parameter specifies the request object for which the sub-state + * change is occuring. + * + * none. + */ +static void scic_sds_smp_request_started_await_tc_completion_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_smp_request_started_substate_handler_table, + SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION + ); +} + +const struct sci_base_state scic_sds_smp_request_started_substate_table[] = { + [SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_RESPONSE] = { + .enter_state = scic_sds_smp_request_started_await_response_substate_enter, + }, + [SCIC_SDS_SMP_REQUEST_STARTED_SUBSTATE_AWAIT_TC_COMPLETION] = { + .enter_state = scic_sds_smp_request_started_await_tc_completion_substate_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_smp_request.h b/drivers/scsi/isci/core/scic_sds_smp_request.h new file mode 100644 index 000000000000..b7c5b83b1989 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_smp_request.h @@ -0,0 +1,70 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ +#ifndef _SCIC_SDS_SMP_REQUEST_T_ +#define _SCIC_SDS_SMP_REQUEST_T_ + +#include "intel_sas.h" +#include "sci_types.h" +#include "scic_sds_request.h" + + +u32 scic_sds_smp_request_get_object_size(void); + + +void scic_sds_smp_request_copy_response( + struct scic_sds_request *this_request); + +#endif /* _SCIC_SDS_SMP_REQUEST_T_ */ + diff --git a/drivers/scsi/isci/core/scic_sds_ssp_request.c b/drivers/scsi/isci/core/scic_sds_ssp_request.c new file mode 100644 index 000000000000..0d6441c5ce00 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_ssp_request.c @@ -0,0 +1,340 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the task management substate handlers for the + * SCIC_SDS_IO_REQUEST object. + * + * + */ + +#include "intel_sas.h" +#include "sci_environment.h" +#include "scic_sds_request.h" +#include "scic_controller.h" +#include "scic_sds_controller.h" +#include "scu_completion_codes.h" +#include "scu_task_context.h" + +/** + * This method processes the completions transport layer (TL) status to + * determine if the RAW task management frame was sent successfully. If the + * raw frame was sent successfully, then the state for the task request + * transitions to waiting for a response frame. + * @this_request: This parameter specifies the request for which the TC + * completion was received. + * @completion_code: This parameter indicates the completion status information + * for the TC. + * + * Indicate if the tc completion handler was successful. SCI_SUCCESS currently + * this method always returns success. + */ +static enum sci_status scic_sds_ssp_task_request_await_tc_completion_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE + ); + break; + + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_ACK_NAK_TO): + /* + * Currently, the decision is to simply allow the task request to + * timeout if the task IU wasn't received successfully. + * There is a potential for receiving multiple task responses if we + * decide to send the task IU again. */ + dev_warn(scic_to_dev(this_request->owning_controller), + "%s: TaskRequest:0x%p CompletionCode:%x - " + "ACK/NAK timeout\n", + __func__, + this_request, + completion_code); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return SCI_SUCCESS; +} + +/** + * This method is responsible for processing a terminate/abort request for this + * TC while the request is waiting for the task management response + * unsolicited frame. + * @this_request: This parameter specifies the request for which the + * termination was requested. + * + * This method returns an indication as to whether the abort request was + * successfully handled. need to update to ensure the received UF doesn't cause + * damage to subsequent requests (i.e. put the extended tag in a holding + * pattern for this particular device). + */ +static enum sci_status scic_sds_ssp_task_request_await_tc_response_abort_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_ABORTING + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + return SCI_SUCCESS; +} + +/** + * This method processes an unsolicited frame while the task mgmt request is + * waiting for a response frame. It will copy the response data, release + * the unsolicited frame, and transition the request to the + * SCI_BASE_REQUEST_STATE_COMPLETED state. + * @this_request: This parameter specifies the request for which the + * unsolicited frame was received. + * @frame_index: This parameter indicates the unsolicited frame index that + * should contain the response. + * + * This method returns an indication of whether the TC response frame was + * handled successfully or not. SCI_SUCCESS Currently this value is always + * returned and indicates successful processing of the TC response. Should + * probably update to check frame type and make sure it is a response frame. + */ +static enum sci_status scic_sds_ssp_task_request_await_tc_response_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + scic_sds_io_request_copy_response(this_request); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + scic_sds_controller_release_frame( + this_request->owning_controller, frame_index + ); + + return SCI_SUCCESS; +} + +const struct scic_sds_io_request_state_handler scic_sds_ssp_task_request_started_substate_handler_table[] = { + [SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_COMPLETION] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_ssp_task_request_await_tc_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_ssp_task_request_await_tc_response_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_ssp_task_request_await_tc_response_frame_handler, + } +}; + +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the enter/exit methods associated with each of the task + * management raw request states. For more information on the task + * management request state machine please refer to scic_sds_io_request.h + * + * + */ + +#include "scic_sds_request.h" +#include "sci_base_state_machine.h" + +/** + * This method performs the actions required when entering the + * SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_COMPLETION + * sub-state. This includes setting the IO request state handlers for this + * sub-state. + * @object: This parameter specifies the request object for which the sub-state + * change is occuring. + * + * none. + */ +static void scic_sds_io_request_started_task_mgmt_await_tc_completion_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_ssp_task_request_started_substate_handler_table, + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_COMPLETION + ); +} + +/** + * This method performs the actions required when entering the + * SCIC_SDS_IO_REQUEST_STARTED_SUBSTATE_AWAIT_TC_RESPONSE sub-state. This + * includes setting the IO request state handlers for this sub-state. + * @object: This parameter specifies the request object for which the sub-state + * change is occuring. + * + * none. + */ +static void scic_sds_io_request_started_task_mgmt_await_task_response_substate_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_ssp_task_request_started_substate_handler_table, + SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE + ); +} + +const struct sci_base_state scic_sds_io_request_started_task_mgmt_substate_table[] = { + [SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_COMPLETION] = { + .enter_state = scic_sds_io_request_started_task_mgmt_await_tc_completion_substate_enter, + }, + [SCIC_SDS_IO_REQUEST_STARTED_TASK_MGMT_SUBSTATE_AWAIT_TC_RESPONSE] = { + .enter_state = scic_sds_io_request_started_task_mgmt_await_task_response_substate_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_stp_packet_request.c b/drivers/scsi/isci/core/scic_sds_stp_packet_request.c new file mode 100644 index 000000000000..f52a8e32b48f --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_stp_packet_request.c @@ -0,0 +1,838 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ +#if !defined(DISABLE_ATAPI) + +#include "intel_ata.h" +#include "intel_sas.h" +#include "intel_sata.h" +#include "intel_sat.h" +#include "sati_translator_sequence.h" +#include "sci_base_state.h" +#include "scic_controller.h" +#include "scic_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_sds_stp_packet_request.h" +#include "scic_user_callback.h" +#include "sci_util.h" +#include "scu_completion_codes.h" +#include "scu_task_context.h" + + +/** + * This method will fill in the SCU Task Context for a PACKET fis. And + * construct the request STARTED sub-state machine for Packet Protocol IO. + * @this_request: This parameter specifies the stp packet request object being + * constructed. + * + */ +enum sci_status scic_sds_stp_packet_request_construct( + struct scic_sds_request *this_request) +{ + struct sata_fis_reg_h2d *h2d_fis = + scic_stp_io_request_get_h2d_reg_address( + this_request + ); + + /* + * Work around, we currently only support PACKET DMA protocol, so we + * need to make change to Packet Fis features field. */ + h2d_fis->features = h2d_fis->features | ATA_PACKET_FEATURE_DMA; + + scic_sds_stp_non_ncq_request_construct(this_request); + + /* Build the Packet Fis task context structure */ + scu_stp_raw_request_construct_task_context( + (struct scic_sds_stp_request *)this_request, + this_request->task_context_buffer + ); + + sci_base_state_machine_construct( + &this_request->started_substate_machine, + &this_request->parent.parent, + scic_sds_stp_packet_request_started_substate_table, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_TC_COMPLETION_SUBSTATE + ); + + return SCI_SUCCESS; +} + + +/** + * This method will fill in the SCU Task Context for a Packet request command + * phase in PACKET DMA DATA (IN/OUT) type. The following important settings + * are utilized: -# task_type == SCU_TASK_TYPE_PACKET_DMA. This simply + * indicates that a normal request type (i.e. non-raw frame) is being + * utilized to perform task management. -# control_frame == 1. This ensures + * that the proper endianess is set so that the bytes are transmitted in the + * right order for a smp request frame. + * @this_request: This parameter specifies the smp request object being + * constructed. + * @task_context: The task_context to be reconstruct for packet request command + * phase. + * + */ +void scu_stp_packet_request_command_phase_construct_task_context( + struct scic_sds_request *this_request, + struct scu_task_context *task_context) +{ + void *atapi_cdb; + u32 atapi_cdb_length; + struct scic_sds_stp_request *stp_request = (struct scic_sds_stp_request *)this_request; + + /* + * reference: SSTL 1.13.4.2 + * task_type, sata_direction */ + if (scic_cb_io_request_get_data_direction(this_request->user_request) + == SCI_IO_REQUEST_DATA_OUT) { + task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_OUT; + task_context->sata_direction = 0; + } else { /* todo: for NO_DATA command, we need to send out raw frame. */ + task_context->task_type = SCU_TASK_TYPE_PACKET_DMA_IN; + task_context->sata_direction = 1; + } + + /* sata header */ + memset(&(task_context->type.stp), 0, sizeof(struct STP_TASK_CONTEXT)); + task_context->type.stp.fis_type = SATA_FIS_TYPE_DATA; + + /* + * Copy in the command IU with CDB so that the commandIU address doesn't + * change. */ + memset(this_request->command_buffer, 0, sizeof(struct sata_fis_reg_h2d)); + + atapi_cdb = + scic_cb_stp_packet_io_request_get_cdb_address(this_request->user_request); + + atapi_cdb_length = + scic_cb_stp_packet_io_request_get_cdb_length(this_request->user_request); + + memcpy(((u8 *)this_request->command_buffer + sizeof(u32)), atapi_cdb, atapi_cdb_length); + + atapi_cdb_length = + max(atapi_cdb_length, stp_request->type.packet.device_preferred_cdb_length); + + task_context->ssp_command_iu_length = + ((atapi_cdb_length % 4) == 0) ? + (atapi_cdb_length / 4) : ((atapi_cdb_length / 4) + 1); + + /* task phase is set to TX_CMD */ + task_context->task_phase = 0x1; + + /* retry counter */ + task_context->stp_retry_count = 0; + + if (scic_cb_request_is_initial_construction(this_request->user_request)) { + /* data transfer size. */ + task_context->transfer_length_bytes = + scic_cb_io_request_get_transfer_length(this_request->user_request); + + /* setup sgl */ + scic_sds_request_build_sgl(this_request); + } else { + /* data transfer size, need to be 4 bytes aligned. */ + task_context->transfer_length_bytes = (SCSI_FIXED_SENSE_DATA_BASE_LENGTH + 2); + + scic_sds_stp_packet_internal_request_sense_build_sgl(this_request); + } +} + +/** + * This method will fill in the SCU Task Context for a DATA fis containing CDB + * in Raw Frame type. The TC for previous Packet fis was already there, we + * only need to change the H2D fis content. + * @this_request: This parameter specifies the smp request object being + * constructed. + * @task_context: The task_context to be reconstruct for packet request command + * phase. + * + */ +void scu_stp_packet_request_command_phase_reconstruct_raw_frame_task_context( + struct scic_sds_request *this_request, + struct scu_task_context *task_context) +{ + void *atapi_cdb = + scic_cb_stp_packet_io_request_get_cdb_address(this_request->user_request); + + u32 atapi_cdb_length = + scic_cb_stp_packet_io_request_get_cdb_length(this_request->user_request); + + memset(this_request->command_buffer, 0, sizeof(struct sata_fis_reg_h2d)); + memcpy(((u8 *)this_request->command_buffer + sizeof(u32)), atapi_cdb, atapi_cdb_length); + + memset(&(task_context->type.stp), 0, sizeof(struct STP_TASK_CONTEXT)); + task_context->type.stp.fis_type = SATA_FIS_TYPE_DATA; + + /* + * Note the data send out has to be 4 bytes aligned. Or else out hardware will + * patch non-zero bytes and cause the target device unhappy. */ + task_context->transfer_length_bytes = 12; +} + + +/* + * *@brief This methods decode the D2H status FIS and retrieve the sense data, + * then pass the sense data to user request. + * + ***@param[in] this_request The request receive D2H status FIS. + ***@param[in] status_fis The D2H status fis to be processed. + * + */ +enum sci_status scic_sds_stp_packet_request_process_status_fis( + struct scic_sds_request *this_request, + struct sata_fis_reg_d2h *status_fis) +{ + enum sci_status status = SCI_SUCCESS; + + /* TODO: Process the error status fis, retrieve sense data. */ + if (status_fis->status & ATA_STATUS_REG_ERROR_BIT) + status = SCI_FAILURE_IO_RESPONSE_VALID; + + return status; +} + +/* + * *@brief This methods builds sgl for internal REQUEST SENSE stp packet + * command using this request response buffer, only one sge is + * needed. + * + ***@param[in] this_request The request receive request sense data. + * + */ +void scic_sds_stp_packet_internal_request_sense_build_sgl( + struct scic_sds_request *this_request) +{ + void *sge; + struct scu_sgl_element_pair *scu_sgl_list = NULL; + struct scu_task_context *task_context; + dma_addr_t physical_address; + + struct sci_ssp_response_iu *rsp_iu = + (struct sci_ssp_response_iu *)this_request->response_buffer; + + sge = (void *)&rsp_iu->data[0]; + + task_context = (struct scu_task_context *)this_request->task_context_buffer; + scu_sgl_list = &task_context->sgl_pair_ab; + + scic_cb_io_request_get_physical_address( + scic_sds_request_get_controller(this_request), + this_request, + ((char *)sge), + &physical_address + ); + + scu_sgl_list->A.address_upper = sci_cb_physical_address_upper(physical_address); + scu_sgl_list->A.address_lower = sci_cb_physical_address_lower(physical_address); + scu_sgl_list->A.length = task_context->transfer_length_bytes; + scu_sgl_list->A.address_modifier = 0; + + SCU_SGL_ZERO(scu_sgl_list->B); +} + +/** + * This method processes the completions transport layer (TL) status to + * determine if the Packet FIS was sent successfully. If the Packet FIS was + * sent successfully, then the state for the Packet request transits to + * waiting for a PIO SETUP frame. + * @this_request: This parameter specifies the request for which the TC + * completion was received. + * @completion_code: This parameter indicates the completion status information + * for the TC. + * + * Indicate if the tc completion handler was successful. SCI_SUCCESS currently + * this method always returns success. + */ +enum sci_status scic_sds_stp_packet_request_packet_phase_await_tc_completion_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + enum sci_status status = SCI_SUCCESS; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_PIO_SETUP_SUBSTATE + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return status; +} + + +/** + * This method processes an unsolicited frame while the Packet request is + * waiting for a PIO SETUP FIS. It will release the unsolicited frame, and + * transition the request to the COMMAND_PHASE_AWAIT_TC_COMPLETION_SUBSTATE + * state. + * @this_request: This parameter specifies the request for which the + * unsolicited frame was received. + * @frame_index: This parameter indicates the unsolicited frame index that + * should contain the response. + * + * This method returns an indication of whether the pio setup frame was handled + * successfully or not. SCI_SUCCESS Currently this value is always returned and + * indicates successful processing of the TC response. + */ +enum sci_status scic_sds_stp_packet_request_packet_phase_await_pio_setup_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + u32 *frame_buffer; + struct scic_sds_stp_request *this_request; + + this_request = (struct scic_sds_stp_request *)request; + + status = scic_sds_unsolicited_frame_control_get_header( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + BUG_ON(frame_header->fis_type != SATA_FIS_TYPE_PIO_SETUP); + + /* + * Get from the frame buffer the PIO Setup Data, although we don't need + * any info from this pio setup fis. */ + scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + /* + * Get the data from the PIO Setup + * The SCU Hardware returns first word in the frame_header and the rest + * of the data is in the frame buffer so we need to back up one dword */ + this_request->type.packet.device_preferred_cdb_length = + (u16)((struct sata_fis_pio_setup *)(&frame_buffer[-1]))->transfter_count; + + /* Frame has been decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, frame_index + ); + + sci_base_state_machine_change_state( + &this_request->parent.started_substate_machine, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_TC_COMPLETION_SUBSTATE + ); + } else + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC IO Request 0x%p could not get frame header " + "for frame index %d, status %x\n", + __func__, this_request, frame_index, status); + + return status; +} + + +/** + * This method processes the completions transport layer (TL) status to + * determine if the PACKET command data FIS was sent successfully. If + * successfully, then the state for the packet request transits to COMPLETE + * state. If not successfuly, the request transits to + * COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE. + * @this_request: This parameter specifies the request for which the TC + * completion was received. + * @completion_code: This parameter indicates the completion status information + * for the TC. + * + * Indicate if the tc completion handler was successful. SCI_SUCCESS currently + * this method always returns success. + */ +enum sci_status scic_sds_stp_packet_request_command_phase_await_tc_completion_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + enum sci_status status = SCI_SUCCESS; + u8 sat_packet_protocol = + scic_cb_request_get_sat_protocol(this_request->user_request); + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case (SCU_TASK_DONE_GOOD << SCU_COMPLETION_TL_STATUS_SHIFT): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + if (sat_packet_protocol == SAT_PROTOCOL_PACKET_DMA_DATA_IN + || sat_packet_protocol == SAT_PROTOCOL_PACKET_DMA_DATA_OUT + ) + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + else + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE + ); + break; + + case (SCU_TASK_DONE_UNEXP_FIS << SCU_COMPLETION_TL_STATUS_SHIFT): + if (scic_io_request_get_number_of_bytes_transferred(this_request) < + scic_cb_io_request_get_transfer_length(this_request->user_request)) { + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS_IO_DONE_EARLY + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + status = this_request->sci_status; + } + break; + + case (SCU_TASK_DONE_EXCESS_DATA << SCU_COMPLETION_TL_STATUS_SHIFT): + /* In this case, there is no UF coming after. compelte the IO now. */ + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + break; + + default: + if (this_request->sci_status != SCI_SUCCESS) { /* The io status was set already. This means an UF for the status + * fis was received already. + */ + + /* + * A device suspension event is expected, we need to have the device + * coming out of suspension, then complete the IO. */ + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMPLETION_DELAY_SUBSTATE + ); + + /* change the device state to ATAPI_ERROR. */ + sci_base_state_machine_change_state( + &this_request->target_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_ATAPI_ERROR + ); + + status = this_request->sci_status; + } else { /* If receiving any non-sucess TC status, no UF received yet, then an UF for + * the status fis is coming after. + */ + scic_sds_request_set_status( + this_request, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE + ); + } + break; + } + + return status; +} + + +/** + * This method processes an unsolicited frame. + * @this_request: This parameter specifies the request for which the + * unsolicited frame was received. + * @frame_index: This parameter indicates the unsolicited frame index that + * should contain the response. + * + * This method returns an indication of whether the UF frame was handled + * successfully or not. SCI_SUCCESS Currently this value is always returned and + * indicates successful processing of the TC response. + */ +enum sci_status scic_sds_stp_packet_request_command_phase_common_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + u32 *frame_buffer; + struct scic_sds_stp_request *this_request; + + this_request = (struct scic_sds_stp_request *)request; + + status = scic_sds_unsolicited_frame_control_get_header( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + BUG_ON(frame_header->fis_type != SATA_FIS_TYPE_REGD2H); + + /* + * Get from the frame buffer the PIO Setup Data, although we don't need + * any info from this pio setup fis. */ + scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + scic_sds_controller_copy_sata_response( + &this_request->d2h_reg_fis, (u32 *)frame_header, frame_buffer + ); + + /* Frame has been decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, frame_index + ); + } + + return status; +} + +/** + * This method processes an unsolicited frame while the packet request is + * expecting TC completion. It will process the FIS and construct sense data. + * @this_request: This parameter specifies the request for which the + * unsolicited frame was received. + * @frame_index: This parameter indicates the unsolicited frame index that + * should contain the response. + * + * This method returns an indication of whether the UF frame was handled + * successfully or not. SCI_SUCCESS Currently this value is always returned and + * indicates successful processing of the TC response. + */ +enum sci_status scic_sds_stp_packet_request_command_phase_await_tc_completion_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + struct scic_sds_stp_request *this_request = (struct scic_sds_stp_request *)request; + + enum sci_status status = + scic_sds_stp_packet_request_command_phase_common_frame_handler( + request, frame_index); + + if (status == SCI_SUCCESS) { + /* The command has completed with error status from target device. */ + status = scic_sds_stp_packet_request_process_status_fis( + request, &this_request->d2h_reg_fis); + + if (status != SCI_SUCCESS) { + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_CHECK_RESPONSE, + status + ); + } else + scic_sds_request_set_status( + &this_request->parent, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + } + + return status; +} + + +/** + * This method processes an unsolicited frame while the packet request is + * expecting TC completion. It will process the FIS and construct sense data. + * @this_request: This parameter specifies the request for which the + * unsolicited frame was received. + * @frame_index: This parameter indicates the unsolicited frame index that + * should contain the response. + * + * This method returns an indication of whether the UF frame was handled + * successfully or not. SCI_SUCCESS Currently this value is always returned and + * indicates successful processing of the TC response. + */ +enum sci_status scic_sds_stp_packet_request_command_phase_await_d2h_fis_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status = + scic_sds_stp_packet_request_command_phase_common_frame_handler( + request, frame_index); + + struct scic_sds_stp_request *this_request = (struct scic_sds_stp_request *)request; + + if (status == SCI_SUCCESS) { + /* The command has completed with error status from target device. */ + status = scic_sds_stp_packet_request_process_status_fis( + request, &this_request->d2h_reg_fis); + + if (status != SCI_SUCCESS) { + scic_sds_request_set_status( + request, + SCU_TASK_DONE_CHECK_RESPONSE, + status + ); + } else + scic_sds_request_set_status( + request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + /* + * Always complete the NON_DATA command right away, no need to delay completion + * even an error status fis came from target device. */ + sci_base_state_machine_change_state( + &request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + } + + return status; +} + +enum sci_status scic_sds_stp_packet_request_started_completion_delay_complete_handler( + struct sci_base_request *request) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + return this_request->sci_status; +} + +/* --------------------------------------------------------------------------- */ + +const struct scic_sds_io_request_state_handler scic_sds_stp_packet_request_started_substate_handler_table[] = { + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_TC_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler + .tc_completion_handler = scic_sds_stp_packet_request_packet_phase_await_tc_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_PIO_SETUP_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_packet_request_packet_phase_await_pio_setup_frame_handler + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_TC_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler + .tc_completion_handler = scic_sds_stp_packet_request_command_phase_await_tc_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_packet_request_command_phase_await_tc_completion_frame_handler + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_packet_request_command_phase_await_d2h_fis_frame_handler + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMPLETION_DELAY_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_stp_packet_request_started_completion_delay_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler + }, +}; + +void scic_sds_stp_packet_request_started_packet_phase_await_tc_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_packet_request_started_substate_handler_table, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_TC_COMPLETION_SUBSTATE + ); + + scic_sds_remote_device_set_working_request( + this_request->target_device, this_request + ); +} + +void scic_sds_stp_packet_request_started_packet_phase_await_pio_setup_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_packet_request_started_substate_handler_table, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_PIO_SETUP_SUBSTATE + ); +} + +void scic_sds_stp_packet_request_started_command_phase_await_tc_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + u8 sat_packet_protocol = + scic_cb_request_get_sat_protocol(this_request->user_request); + + struct scu_task_context *task_context; + enum sci_status status; + + /* + * Recycle the TC and reconstruct it for sending out data fis containing + * CDB. */ + task_context = scic_sds_controller_get_task_context_buffer( + this_request->owning_controller, this_request->io_tag); + + if (sat_packet_protocol == SAT_PROTOCOL_PACKET_NON_DATA) + scu_stp_packet_request_command_phase_reconstruct_raw_frame_task_context( + this_request, task_context); + else + scu_stp_packet_request_command_phase_construct_task_context( + this_request, task_context); + + /* send the new TC out. */ + status = this_request->owning_controller->state_handlers->parent.continue_io_handler( + &this_request->owning_controller->parent, + &this_request->target_device->parent, + &this_request->parent + ); + + if (status == SCI_SUCCESS) + SET_STATE_HANDLER( + this_request, + scic_sds_stp_packet_request_started_substate_handler_table, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_TC_COMPLETION_SUBSTATE + ); +} + +void scic_sds_stp_packet_request_started_command_phase_await_d2h_fis_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_packet_request_started_substate_handler_table, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE + ); +} + +void scic_sds_stp_packet_request_started_completion_delay_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_packet_request_started_substate_handler_table, + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMPLETION_DELAY_SUBSTATE + ); +} + + +/* --------------------------------------------------------------------------- */ +const struct sci_base_state scic_sds_stp_packet_request_started_substate_table[] = { + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_TC_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_packet_request_started_packet_phase_await_tc_completion_enter, + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_PIO_SETUP_SUBSTATE] = { + .enter_state = scic_sds_stp_packet_request_started_packet_phase_await_pio_setup_enter, + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_TC_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_packet_request_started_command_phase_await_tc_completion_enter, + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE] = { + .enter_state = scic_sds_stp_packet_request_started_command_phase_await_d2h_fis_enter, + }, + [SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMPLETION_DELAY_SUBSTATE] = { + .enter_state scic_sds_stp_packet_request_started_completion_delay_enter, + } +}; + +#endif /* !defined(DISABLE_ATAPI) */ diff --git a/drivers/scsi/isci/core/scic_sds_stp_packet_request.h b/drivers/scsi/isci/core/scic_sds_stp_packet_request.h new file mode 100644 index 000000000000..fc18b3f6a13f --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_stp_packet_request.h @@ -0,0 +1,154 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ +#ifndef _SCIC_SDS_STP_PACKET_REQUEST_H_ +#define _SCIC_SDS_STP_PACKET_REQUEST_H_ + +#include "intel_sas.h" +#include "sci_types.h" +#include "scic_sds_stp_request.h" + +/** + * This file contains the structures and constants for PACKET protocol requests. + * + * + */ + + +/** + * + * + * This is the enumeration of the SATA PIO DATA IN started substate machine. + */ +enum _SCIC_SDS_STP_PACKET_REQUEST_STARTED_SUBSTATES { + /** + * While in this state the IO request object is waiting for the TC completion + * notification for the H2D Register FIS + */ + SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_TC_COMPLETION_SUBSTATE, + + /** + * While in this state the IO request object is waiting for either a PIO Setup. + */ + SCIC_SDS_STP_PACKET_REQUEST_STARTED_PACKET_PHASE_AWAIT_PIO_SETUP_SUBSTATE, + + /** + * While in this state the IO request object is waiting for TC completion for + * the Packet DMA DATA fis or Raw Frame. + */ + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_TC_COMPLETION_SUBSTATE, + + /** + * The non-data IO transit to this state in this state after receiving TC + * completion. While in this state IO request object is waiting for D2H status + * frame as UF. + */ + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMMAND_PHASE_AWAIT_D2H_FIS_SUBSTATE, + + /** + * The IO transit to this state in this state if the previous TC completion status + * is not success and the atapi device is suspended due to target device failed the IO. + * While in this state IO request object is waiting for device coming out of the + * suspension state then complete the IO. + */ + SCIC_SDS_STP_PACKET_REQUEST_STARTED_COMPLETION_DELAY_SUBSTATE, +}; + + + +#if !defined(DISABLE_ATAPI) +extern const struct sci_base_state scic_sds_stp_packet_request_started_substate_table[]; +extern const struct scic_sds_io_request_state_handler scic_sds_stp_packet_request_started_substate_handler_table[]; +#endif /* !defined(DISABLE_ATAPI) */ + +#if !defined(DISABLE_ATAPI) +enum sci_status scic_sds_stp_packet_request_construct( + struct scic_sds_request *this_request); +#else /* !defined(DISABLE_ATAPI) */ +#define scic_sds_stp_packet_request_construct(request) SCI_FAILURE +#endif /* !defined(DISABLE_ATAPI) */ + +#if !defined(DISABLE_ATAPI) +void scu_stp_packet_request_command_phase_construct_task_context( + struct scic_sds_request *this_request, + struct scu_task_context *task_context); +#else /* !defined(DISABLE_ATAPI) */ +#define scu_stp_packet_request_command_phase_construct_task_context(reqeust, tc) +#endif /* !defined(DISABLE_ATAPI) */ + +#if !defined(DISABLE_ATAPI) +void scu_stp_packet_request_command_phase_reconstruct_raw_frame_task_context( + struct scic_sds_request *this_request, + struct scu_task_context *task_context); +#else /* !defined(DISABLE_ATAPI) */ +#define scu_stp_packet_request_command_phase_reconstruct_raw_frame_task_context(reqeust, tc) +#endif /* !defined(DISABLE_ATAPI) */ + +#if !defined(DISABLE_ATAPI) +enum sci_status scic_sds_stp_packet_request_process_status_fis( + struct scic_sds_request *this_request, + struct sata_fis_reg_d2h *status_fis); +#else /* !defined(DISABLE_ATAPI) */ +#define scic_sds_stp_packet_request_process_status_fis(reqeust, fis) SCI_FAILURE +#endif /* !defined(DISABLE_ATAPI) */ + +#if !defined(DISABLE_ATAPI) +void scic_sds_stp_packet_internal_request_sense_build_sgl( + struct scic_sds_request *this_request); +#else /* !defined(DISABLE_ATAPI) */ +#define scic_sds_stp_packet_internal_request_sense_build_sgl(request) +#endif /* !defined(DISABLE_ATAPI) */ + +#endif /* _SCIC_SDS_STP_PACKET_REQUEST_H_ */ + diff --git a/drivers/scsi/isci/core/scic_sds_stp_pio_request.h b/drivers/scsi/isci/core/scic_sds_stp_pio_request.h new file mode 100644 index 000000000000..64bf40a6e1d2 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_stp_pio_request.h @@ -0,0 +1,116 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_SATA_PIO_REQUEST_H_ +#define _SCIC_SDS_SATA_PIO_REQUEST_H_ + +#include "sci_base_state.h" +#include "scic_sds_request.h" +#include "scu_task_context.h" + +/** + * This file contains the structures and constants for SATA PIO requests. + * + * + */ + + +/** + * + * + * This is the enumeration of the SATA PIO DATA IN started substate machine. + */ +enum _SCIC_SDS_STP_REQUEST_STARTED_PIO_SUBSTATES { + /** + * While in this state the IO request object is waiting for the TC completion + * notification for the H2D Register FIS + */ + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_H2D_COMPLETION_SUBSTATE, + + /** + * While in this state the IO request object is waiting for either a PIO Setup + * FIS or a D2H register FIS. The type of frame received is based on the + * result of the prior frame and line conditions. + */ + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE, + + /** + * While in this state the IO request object is waiting for a DATA frame from + * the device. + */ + SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_IN_AWAIT_DATA_SUBSTATE, + + /** + * While in this state the IO request object is waiting to transmit the next data + * frame to the device. + */ + SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_OUT_TRANSMIT_DATA_SUBSTATE, +}; + + +/* --------------------------------------------------------------------------- */ + +extern const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_pio_substate_handler_table[]; + +extern const struct sci_base_state scic_sds_stp_request_started_pio_substate_table[]; + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_stp_request; + +struct scu_sgl_element *scic_sds_stp_request_pio_get_next_sgl( + struct scic_sds_stp_request *this_request); + +#endif /* _SCIC_SDS_SATA_PIO_REQUEST_H_ */ diff --git a/drivers/scsi/isci/core/scic_sds_stp_remote_device.c b/drivers/scsi/isci/core/scic_sds_stp_remote_device.c new file mode 100644 index 000000000000..abe8f331a15c --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_stp_remote_device.c @@ -0,0 +1,975 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the ready substate handlers for an STP device. + * + * + */ + +#include "intel_sat.h" +#include "intel_ata.h" +#include "intel_sata.h" +#include "sci_environment.h" +#include "scic_remote_device.h" +#include "scic_user_callback.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scu_event_codes.h" + +/** + * This method will perform the STP request completion processing common to IO + * requests and task requests of all types + * @device: This parameter specifies the device for which the request is being + * completed. + * @request: This parameter specifies the request being completed. + * + * This method returns an indication as to whether the request processing + * completed successfully. + */ +static enum sci_status scic_sds_stp_remote_device_complete_request( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *the_request = (struct scic_sds_request *)request; + enum sci_status status; + + status = scic_sds_io_request_complete(the_request); + + if (status == SCI_SUCCESS) { + status = scic_sds_port_complete_io( + this_device->owning_port, this_device, the_request + ); + + if (status == SCI_SUCCESS) { + scic_sds_remote_device_decrement_request_count(this_device); + if (the_request->sci_status == SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) { + /* + * This request causes hardware error, device needs to be Lun Reset. + * So here we force the state machine to IDLE state so the rest IOs + * can reach RNC state handler, these IOs will be completed by RNC with + * status of "DEVICE_RESET_REQUIRED", instead of "INVALID STATE". */ + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_AWAIT_RESET + ); + } else if (scic_sds_remote_device_get_request_count(this_device) == 0) { + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE + ); + } + } + } + + if (status != SCI_SUCCESS) + dev_err(scirdev_to_dev(this_device), + "%s: Port:0x%p Device:0x%p Request:0x%p Status:0x%x " + "could not complete\n", + __func__, + this_device->owning_port, + this_device, + the_request, + status); + + return status; +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY COMMON SUBSTATE HANDLERS + * ***************************************************************************** */ + +/** + * This is the READY NCQ substate handler to start task management request. In + * this routine, we suspend and resume the RNC. + * @device: The target device a task management request towards to. + * @request: The task request. + * + * enum sci_status Always return SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS status to + * let controller_start_task_handler know that the controller can't post TC for + * task request yet, instead, when RNC gets resumed, a controller_continue_task + * callback will be called. + */ +static enum sci_status scic_sds_stp_remote_device_ready_substate_start_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *this_request = (struct scic_sds_request *)request; + + /* Will the port allow the io request to start? */ + status = this_device->owning_port->state_handlers->start_io_handler( + this_device->owning_port, + this_device, + this_request + ); + + if (SCI_SUCCESS == status) { + status = + scic_sds_remote_node_context_start_task(this_device->rnc, this_request); + + if (SCI_SUCCESS == status) { + status = this_request->state_handlers->parent.start_handler(request); + } + + if (status == SCI_SUCCESS) { + /* + * / @note If the remote device state is not IDLE this will replace + * / the request that probably resulted in the task management + * / request. */ + this_device->working_request = this_request; + + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD + ); + + /* + * The remote node context must cleanup the TCi to NCQ mapping table. + * The only way to do this correctly is to either write to the TLCR + * register or to invalidate and repost the RNC. In either case the + * remote node context state machine will take the correct action when + * the remote node context is suspended and later resumed. */ + scic_sds_remote_node_context_suspend( + this_device->rnc, SCI_SOFTWARE_SUSPENSION, NULL, NULL); + + scic_sds_remote_node_context_resume( + this_device->rnc, + (SCICS_SDS_REMOTE_NODE_CONTEXT_CALLBACK) + scic_sds_remote_device_continue_request, + this_device); + } + + scic_sds_remote_device_start_request(this_device, this_request, status); + + /* + * We need to let the controller start request handler know that it can't + * post TC yet. We will provide a callback function to post TC when RNC gets + * resumed. */ + return SCI_FAILURE_RESET_DEVICE_PARTIAL_SUCCESS; + } + + return status; +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY IDLE SUBSTATE HANDLERS + * ***************************************************************************** */ + +/** + * This method will handle the start io operation for a sata device that is in + * the command idle state. - Evalute the type of IO request to be started - + * If its an NCQ request change to NCQ substate - If its any other command + * change to the CMD substate + * @device: + * @request: + * + * If this is a softreset we may want to have a different substate. enum sci_status + */ +static enum sci_status scic_sds_stp_remote_device_ready_idle_substate_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *io_request = (struct scic_sds_request *)request; + + + /* Will the port allow the io request to start? */ + status = this_device->owning_port->state_handlers->start_io_handler( + this_device->owning_port, + this_device, + io_request + ); + + if (status == SCI_SUCCESS) { + status = + scic_sds_remote_node_context_start_io(this_device->rnc, io_request); + + if (status == SCI_SUCCESS) { + status = io_request->state_handlers->parent.start_handler(request); + } + + if (status == SCI_SUCCESS) { + if ( + scic_cb_request_get_sat_protocol(io_request->user_request) + == SAT_PROTOCOL_FPDMA + ) { + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ + ); + } else { + this_device->working_request = io_request; + + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD + ); + } + } + + scic_sds_remote_device_start_request(this_device, io_request, status); + } + + return status; +} + + +/** + * + * @[in]: device The device received event. + * @[in]: event_code The event code. + * + * This method will handle the event for a sata device that is in the idle + * state. We pick up suspension events to handle specifically to this state. We + * resume the RNC right away. enum sci_status + */ +static enum sci_status scic_sds_stp_remote_device_ready_idle_substate_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code) +{ + enum sci_status status; + + status = scic_sds_remote_device_general_event_handler(this_device, event_code); + + if (status == SCI_SUCCESS) { + if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX + || scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) { + status = scic_sds_remote_node_context_resume( + this_device->rnc, NULL, NULL); + } + } + + return status; +} + + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY NCQ SUBSTATE HANDLERS + * ***************************************************************************** */ + +static enum sci_status scic_sds_stp_remote_device_ready_ncq_substate_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + enum sci_status status; + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *io_request = (struct scic_sds_request *)request; + + if ( + scic_cb_request_get_sat_protocol(io_request->user_request) + == SAT_PROTOCOL_FPDMA + ) { + status = this_device->owning_port->state_handlers->start_io_handler( + this_device->owning_port, + this_device, + io_request + ); + + if (status == SCI_SUCCESS) { + status = scic_sds_remote_node_context_start_io(this_device->rnc, io_request); + + if (status == SCI_SUCCESS) { + status = io_request->state_handlers->parent.start_handler(request); + } + + scic_sds_remote_device_start_request(this_device, io_request, status); + } + } else { + status = SCI_FAILURE_INVALID_STATE; + } + + return status; +} + + +/** + * This method will handle events received while the STP device is in the ready + * command substate. + * @this_device: This is the device object that is receiving the event. + * @event_code: The event code to process. + * + * enum sci_status + */ + +static enum sci_status scic_sds_stp_remote_device_ready_ncq_substate_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + + status = scic_sds_unsolicited_frame_control_get_header( + &(scic_sds_remote_device_get_controller(this_device)->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + if ( + (frame_header->fis_type == SATA_FIS_TYPE_SETDEVBITS) + && (frame_header->status & ATA_STATUS_REG_ERROR_BIT) + ) { + this_device->not_ready_reason = + SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED; + + sci_base_state_machine_change_state( + &this_device->ready_substate_machine, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ_ERROR + ); + } else { + status = SCI_FAILURE; + } + + scic_sds_controller_release_frame( + scic_sds_remote_device_get_controller(this_device), frame_index + ); + } + + return status; +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY CMD SUBSTATE HANDLERS + * ***************************************************************************** */ + +/** + * This device is already handling a command it can not accept new commands + * until this one is complete. + * @device: + * @request: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_remote_device_ready_cmd_substate_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + return SCI_FAILURE_INVALID_STATE; +} + +static enum sci_status scic_sds_stp_remote_device_ready_cmd_substate_suspend_handler( + struct scic_sds_remote_device *this_device, + u32 suspend_type) +{ + enum sci_status status; + + status = scic_sds_remote_node_context_suspend( + this_device->rnc, suspend_type, NULL, NULL + ); + + return status; +} + +static enum sci_status scic_sds_stp_remote_device_ready_cmd_substate_frame_handler( + struct scic_sds_remote_device *this_device, + u32 frame_index) +{ + enum sci_status status; + + /* + * / The device doe not process any UF received from the hardware while + * / in this state. All unsolicited frames are forwarded to the io request + * / object. */ + status = scic_sds_io_request_frame_handler( + this_device->working_request, + frame_index + ); + + return status; +} + + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY NCQ SUBSTATE HANDLERS + * ***************************************************************************** */ + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY NCQ ERROR SUBSTATE HANDLERS + * ***************************************************************************** */ + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY AWAIT RESET SUBSTATE HANDLERS + * ***************************************************************************** */ +static enum sci_status scic_sds_stp_remote_device_ready_await_reset_substate_start_io_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + return SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED; +} + + + +/** + * This method will perform the STP request (both io or task) completion + * processing for await reset state. + * @device: This parameter specifies the device for which the request is being + * completed. + * @request: This parameter specifies the request being completed. + * + * This method returns an indication as to whether the request processing + * completed successfully. + */ +static enum sci_status scic_sds_stp_remote_device_ready_await_reset_substate_complete_request_handler( + struct sci_base_remote_device *device, + struct sci_base_request *request) +{ + struct scic_sds_remote_device *this_device = (struct scic_sds_remote_device *)device; + struct scic_sds_request *the_request = (struct scic_sds_request *)request; + enum sci_status status; + + status = scic_sds_io_request_complete(the_request); + + if (status == SCI_SUCCESS) { + status = scic_sds_port_complete_io( + this_device->owning_port, this_device, the_request + ); + + if (status == SCI_SUCCESS) + scic_sds_remote_device_decrement_request_count(this_device); + } + + if (status != SCI_SUCCESS) + dev_err(scirdev_to_dev(this_device), + "%s: Port:0x%p Device:0x%p Request:0x%p Status:0x%x " + "could not complete\n", + __func__, + this_device->owning_port, + this_device, + the_request, + status); + + return status; +} + +#if !defined(DISABLE_ATAPI) +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY ATAPI ERROR SUBSTATE HANDLERS + * ***************************************************************************** */ + +/** + * + * @[in]: device The device received event. + * @[in]: event_code The event code. + * + * This method will handle the event for a ATAPI device that is in the ATAPI + * ERROR state. We pick up suspension events to handle specifically to this + * state. We resume the RNC right away. We then complete the outstanding IO to + * this device. enum sci_status + */ +enum sci_status scic_sds_stp_remote_device_ready_atapi_error_substate_event_handler( + struct scic_sds_remote_device *this_device, + u32 event_code) +{ + enum sci_status status; + + status = scic_sds_remote_device_general_event_handler(this_device, event_code); + + if (status == SCI_SUCCESS) { + if (scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX + || scu_get_event_type(event_code) == SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX) { + status = scic_sds_remote_node_context_resume( + this_device->rnc, + this_device->working_request->state_handlers->parent.complete_handler, + (void *)this_device->working_request + ); + } + } + + return status; +} +#endif /* !defined(DISABLE_ATAPI) */ + +/* --------------------------------------------------------------------------- */ + +struct scic_sds_remote_device_state_handler +scic_sds_stp_remote_device_ready_substate_handler_table[ + SCIC_SDS_STP_REMOTE_DEVICE_READY_MAX_SUBSTATES] = +{ + /* SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_stp_remote_device_ready_idle_substate_start_io_handler, + scic_sds_remote_device_default_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_stp_remote_device_ready_substate_start_request_handler, + scic_sds_remote_device_default_complete_request_handler + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_stp_remote_device_ready_idle_substate_event_handler, + scic_sds_remote_device_default_frame_handler + }, + /* SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_stp_remote_device_ready_cmd_substate_start_io_handler, + scic_sds_stp_remote_device_complete_request, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_stp_remote_device_ready_substate_start_request_handler, + scic_sds_stp_remote_device_complete_request, + }, + scic_sds_stp_remote_device_ready_cmd_substate_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_stp_remote_device_ready_cmd_substate_frame_handler + }, + /* SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_stp_remote_device_ready_ncq_substate_start_io_handler, + scic_sds_stp_remote_device_complete_request, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_stp_remote_device_ready_substate_start_request_handler, + scic_sds_stp_remote_device_complete_request + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_stp_remote_device_ready_ncq_substate_frame_handler + }, + /* SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ_ERROR */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_stp_remote_device_complete_request, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_stp_remote_device_ready_substate_start_request_handler, + scic_sds_stp_remote_device_complete_request + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_remote_device_general_frame_handler + }, +#if !defined(DISABLE_ATAPI) + /* SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_ATAPI_ERROR */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_remote_device_default_start_request_handler, + scic_sds_stp_remote_device_complete_request, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_stp_remote_device_ready_substate_start_request_handler, + scic_sds_stp_remote_device_complete_request + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_stp_remote_device_ready_atapi_error_substate_event_handler, + scic_sds_remote_device_general_frame_handler + }, +#endif + /* SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_AWAIT_RESET */ + { + { + scic_sds_remote_device_default_start_handler, + scic_sds_remote_device_ready_state_stop_handler, + scic_sds_remote_device_default_fail_handler, + scic_sds_remote_device_default_destruct_handler, + scic_sds_remote_device_ready_state_reset_handler, + scic_sds_remote_device_default_reset_complete_handler, + scic_sds_stp_remote_device_ready_await_reset_substate_start_io_handler, + scic_sds_stp_remote_device_ready_await_reset_substate_complete_request_handler, + scic_sds_remote_device_default_continue_request_handler, + scic_sds_stp_remote_device_ready_substate_start_request_handler, + scic_sds_stp_remote_device_complete_request + }, + scic_sds_remote_device_default_suspend_handler, + scic_sds_remote_device_default_resume_handler, + scic_sds_remote_device_general_event_handler, + scic_sds_remote_device_general_frame_handler + } +}; + +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "sci_base_state.h" +#include "scic_remote_device.h" +#include "scic_user_callback.h" +#include "scic_sds_controller.h" +#include "scic_sds_port.h" +#include "scic_sds_remote_device.h" +#include "sci_util.h" +#include "sci_environment.h" + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY SUBSTATE PRIVATE METHODS + * ***************************************************************************** */ + +static void scic_sds_stp_remote_device_ready_idle_substate_resume_complete_handler( + void *user_cookie) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)user_cookie; + + /* + * For NCQ operation we do not issue a + * scic_cb_remote_device_not_ready(). As a result, avoid sending + * the ready notification. */ + if (this_device->ready_substate_machine.previous_state_id + != SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ) { + scic_cb_remote_device_ready( + scic_sds_remote_device_get_controller(this_device), this_device + ); + } +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY IDLE SUBSTATE + * ***************************************************************************** */ + +/** + * + * @device: This is the SCI base object which is cast into a + * struct scic_sds_remote_device object. + * + */ +static void scic_sds_stp_remote_device_ready_idle_substate_enter( + struct sci_base_object *device) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)device; + + SET_STATE_HANDLER( + this_device, + scic_sds_stp_remote_device_ready_substate_handler_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE + ); + + this_device->working_request = NULL; + + if (scic_sds_remote_node_context_is_ready(this_device->rnc)) { + /* + * Since the RNC is ready, it's alright to finish completion + * processing (e.g. signal the remote device is ready). */ + scic_sds_stp_remote_device_ready_idle_substate_resume_complete_handler( + this_device + ); + } else { + scic_sds_remote_node_context_resume( + this_device->rnc, + scic_sds_stp_remote_device_ready_idle_substate_resume_complete_handler, + this_device + ); + } +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY CMD SUBSTATE + * ***************************************************************************** */ + +/** + * + * @device: This is the SCI base object which is cast into a + * struct scic_sds_remote_device object. + * + */ +static void scic_sds_stp_remote_device_ready_cmd_substate_enter( + struct sci_base_object *device) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)device; + + BUG_ON(this_device->working_request == NULL); + + SET_STATE_HANDLER( + this_device, + scic_sds_stp_remote_device_ready_substate_handler_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD + ); + + scic_cb_remote_device_not_ready( + scic_sds_remote_device_get_controller(this_device), + this_device, + SCIC_REMOTE_DEVICE_NOT_READY_SATA_REQUEST_STARTED + ); +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY NCQ SUBSTATE + * ***************************************************************************** */ + +/** + * + * @device: This is the SCI base object which is cast into a + * struct scic_sds_remote_device object. + * + */ +static void scic_sds_stp_remote_device_ready_ncq_substate_enter( + struct sci_base_object *device) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)device; + + SET_STATE_HANDLER( + this_device, + scic_sds_stp_remote_device_ready_substate_handler_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ + ); +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY NCQ ERROR SUBSTATE + * ***************************************************************************** */ + +/** + * + * @device: This is the SCI base object which is cast into a + * struct scic_sds_remote_device object. + * + */ +static void scic_sds_stp_remote_device_ready_ncq_error_substate_enter( + struct sci_base_object *device) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)device; + + SET_STATE_HANDLER( + this_device, + scic_sds_stp_remote_device_ready_substate_handler_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ_ERROR + ); + + if (this_device->not_ready_reason == + SCIC_REMOTE_DEVICE_NOT_READY_SATA_SDB_ERROR_FIS_RECEIVED) { + scic_cb_remote_device_not_ready( + scic_sds_remote_device_get_controller(this_device), + this_device, + this_device->not_ready_reason + ); + } +} + +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY AWAIT RESET SUBSTATE + * ***************************************************************************** */ + +/** + * The enter routine to READY AWAIT RESET substate. + * @device: This is the SCI base object which is cast into a + * struct scic_sds_remote_device object. + * + */ +static void scic_sds_stp_remote_device_ready_await_reset_substate_enter( + struct sci_base_object *device) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)device; + + SET_STATE_HANDLER( + this_device, + scic_sds_stp_remote_device_ready_substate_handler_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_AWAIT_RESET + ); +} + +#if !defined(DISABLE_ATAPI) +/* + * ***************************************************************************** + * * STP REMOTE DEVICE READY ATAPI ERROR SUBSTATE + * ***************************************************************************** */ + +/** + * The enter routine to READY ATAPI ERROR substate. + * @device: This is the SCI base object which is cast into a + * struct scic_sds_remote_device object. + * + */ +void scic_sds_stp_remote_device_ready_atapi_error_substate_enter( + struct sci_base_object *device) +{ + struct scic_sds_remote_device *this_device; + + this_device = (struct scic_sds_remote_device *)device; + + SET_STATE_HANDLER( + this_device, + scic_sds_stp_remote_device_ready_substate_handler_table, + SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_ATAPI_ERROR + ); +} +#endif /* !defined(DISABLE_ATAPI) */ + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_stp_remote_device_ready_substate_table[] = { + [SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_IDLE] = { + .enter_state = scic_sds_stp_remote_device_ready_idle_substate_enter, + }, + [SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_CMD] = { + .enter_state = scic_sds_stp_remote_device_ready_cmd_substate_enter, + }, + [SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ] = { + .enter_state = scic_sds_stp_remote_device_ready_ncq_substate_enter, + }, + [SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_NCQ_ERROR] = { + .enter_state = scic_sds_stp_remote_device_ready_ncq_error_substate_enter, + }, +#if !defined(DISABLE_ATAPI) + [SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_ATAPI_ERROR] = { + .enter_state = scic_sds_stp_remote_device_ready_atapi_error_substate_enter, + }, +#endif + [SCIC_SDS_STP_REMOTE_DEVICE_READY_SUBSTATE_AWAIT_RESET] = { + .enter_state = scic_sds_stp_remote_device_ready_await_reset_substate_enter, + }, +}; diff --git a/drivers/scsi/isci/core/scic_sds_stp_request.c b/drivers/scsi/isci/core/scic_sds_stp_request.c new file mode 100644 index 000000000000..c14f6f10edb1 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_stp_request.c @@ -0,0 +1,2004 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "intel_ata.h" +#include "intel_sata.h" +#include "intel_sat.h" +#include "sci_base_state.h" +#include "sci_base_state_machine.h" +#include "scic_io_request.h" +#include "scic_remote_device.h" +#include "scic_sds_controller.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_request.h" +#include "scic_sds_stp_pio_request.h" +#include "scic_sds_stp_request.h" +#include "scic_sds_unsolicited_frame_control.h" +#include "scic_user_callback.h" +#include "sci_environment.h" +#include "sci_types.h" +#include "sci_util.h" +#include "scu_completion_codes.h" +#include "scu_event_codes.h" +#include "scu_task_context.h" + +/** + * scic_sds_stp_request_get_h2d_reg_buffer() - + * + * This macro returns the address of the stp h2d reg fis buffer in the io + * request memory + */ +#define scic_sds_stp_request_get_h2d_reg_buffer(memory) \ + ((struct sata_fis_reg_h2d *)(\ + ((char *)(memory)) + sizeof(struct scic_sds_stp_request) \ + )) + +/** + * scic_sds_stp_request_get_response_buffer() - + * + * This macro returns the address of the ssp response iu buffer in the io + * request memory + */ +#define scic_sds_stp_request_get_response_buffer(memory) \ + ((struct sata_fis_reg_d2h *)(\ + ((char *)(scic_sds_stp_request_get_h2d_reg_buffer(memory))) \ + + sizeof(struct sata_fis_reg_h2d) \ + )) + +/** + * scic_sds_stp_request_get_task_context_buffer() - + * + * This macro returns the address of the task context buffer in the io request + * memory + */ +#define scic_sds_stp_request_get_task_context_buffer(memory) \ + ((struct scu_task_context *)(\ + ((char *)(scic_sds_stp_request_get_response_buffer(memory))) \ + + sizeof(struct sci_ssp_response_iu) \ + )) + +/** + * scic_sds_stp_request_get_sgl_element_buffer() - + * + * This macro returns the address of the sgl elment pairs in the io request + * memory buffer + */ +#define scic_sds_stp_request_get_sgl_element_buffer(memory) \ + ((struct scu_sgl_element_pair *)(\ + ((char *)(scic_sds_stp_request_get_task_context_buffer(memory))) \ + + sizeof(struct scu_task_context) \ + )) + +/** + * + * + * This method return the memory space required for STP PIO requests. u32 + */ +u32 scic_sds_stp_request_get_object_size(void) +{ + return sizeof(struct scic_sds_stp_request) + + sizeof(struct sata_fis_reg_h2d) + + sizeof(struct sata_fis_reg_d2h) + + sizeof(struct scu_task_context) + + sizeof(struct scu_sgl_element_pair) * SCU_MAX_SGL_ELEMENT_PAIRS; +} + +/** + * + * + * + */ +void scic_sds_stp_request_assign_buffers( + struct scic_sds_request *request) +{ + struct scic_sds_stp_request *this_request = (struct scic_sds_stp_request *)request; + + this_request->parent.command_buffer = + scic_sds_stp_request_get_h2d_reg_buffer(this_request); + this_request->parent.response_buffer = + scic_sds_stp_request_get_response_buffer(this_request); + this_request->parent.sgl_element_pair_buffer = + scic_sds_stp_request_get_sgl_element_buffer(this_request); + this_request->parent.sgl_element_pair_buffer = + scic_sds_request_align_sgl_element_buffer(this_request->parent.sgl_element_pair_buffer); + + if (this_request->parent.was_tag_assigned_by_user == false) { + this_request->parent.task_context_buffer = + scic_sds_stp_request_get_task_context_buffer(this_request); + this_request->parent.task_context_buffer = + scic_sds_request_align_task_context_buffer(this_request->parent.task_context_buffer); + } +} + +/** + * This method is will fill in the SCU Task Context for any type of SATA + * request. This is called from the various SATA constructors. + * @this_request: The general IO request object which is to be used in + * constructing the SCU task context. + * @task_context: The buffer pointer for the SCU task context which is being + * constructed. + * + * The general io request construction is complete. The buffer assignment for + * the command buffer is complete. none Revisit task context construction to + * determine what is common for SSP/SMP/STP task context structures. + */ +static void scu_sata_reqeust_construct_task_context( + struct scic_sds_request *this_request, + struct scu_task_context *task_context) +{ + dma_addr_t physical_address; + struct scic_sds_controller *owning_controller; + struct scic_sds_remote_device *target_device; + struct scic_sds_port *target_port; + + owning_controller = scic_sds_request_get_controller(this_request); + target_device = scic_sds_request_get_device(this_request); + target_port = scic_sds_request_get_port(this_request); + + /* Fill in the TC with the its required data */ + task_context->abort = 0; + task_context->priority = SCU_TASK_PRIORITY_NORMAL; + task_context->initiator_request = 1; + task_context->connection_rate = + scic_remote_device_get_connection_rate(target_device); + task_context->protocol_engine_index = + scic_sds_controller_get_protocol_engine_group(owning_controller); + task_context->logical_port_index = + scic_sds_port_get_index(target_port); + task_context->protocol_type = SCU_TASK_CONTEXT_PROTOCOL_STP; + task_context->valid = SCU_TASK_CONTEXT_VALID; + task_context->context_type = SCU_TASK_CONTEXT_TYPE; + + task_context->remote_node_index = + scic_sds_remote_device_get_index(this_request->target_device); + task_context->command_code = 0; + + task_context->link_layer_control = 0; + task_context->do_not_dma_ssp_good_response = 1; + task_context->strict_ordering = 0; + task_context->control_frame = 0; + task_context->timeout_enable = 0; + task_context->block_guard_enable = 0; + + task_context->address_modifier = 0; + task_context->task_phase = 0x01; + + task_context->ssp_command_iu_length = + (sizeof(struct sata_fis_reg_h2d) - sizeof(u32)) / sizeof(u32); + + /* Set the first word of the H2D REG FIS */ + task_context->type.words[0] = *(u32 *)this_request->command_buffer; + + if (this_request->was_tag_assigned_by_user) { + /* Build the task context now since we have already read the data */ + this_request->post_context = ( + SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC + | ( + scic_sds_controller_get_protocol_engine_group(owning_controller) + << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT + ) + | ( + scic_sds_port_get_index(target_port) + << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT + ) + | scic_sds_io_tag_get_index(this_request->io_tag) + ); + } else { + /* Build the task context now since we have already read the data */ + this_request->post_context = ( + SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC + | ( + scic_sds_controller_get_protocol_engine_group(owning_controller) + << SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT + ) + | ( + scic_sds_port_get_index(target_port) + << SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT + ) + /* This is not assigned because we have to wait until we get a TCi */ + ); + } + + /* + * Copy the physical address for the command buffer to the SCU Task Context + * We must offset the command buffer by 4 bytes because the first 4 bytes are + * transfered in the body of the TC */ + scic_cb_io_request_get_physical_address( + scic_sds_request_get_controller(this_request), + this_request, + ((char *)this_request->command_buffer) + sizeof(u32), + &physical_address + ); + + task_context->command_iu_upper = + upper_32_bits(physical_address); + task_context->command_iu_lower = + lower_32_bits(physical_address); + + /* SATA Requests do not have a response buffer */ + task_context->response_iu_upper = 0; + task_context->response_iu_lower = 0; +} + +/** + * + * @this_request: + * + * This method will perform any general sata request construction. What part of + * SATA IO request construction is general? none + */ +void scic_sds_stp_non_ncq_request_construct( + struct scic_sds_request *this_request) +{ + this_request->has_started_substate_machine = true; +} + +/** + * + * @this_request: This parameter specifies the request to be constructed as an + * optimized request. + * @optimized_task_type: This parameter specifies whether the request is to be + * an UDMA request or a NCQ request. - A value of 0 indicates UDMA. - A + * value of 1 indicates NCQ. + * + * This method will perform request construction common to all types of STP + * requests that are optimized by the silicon (i.e. UDMA, NCQ). This method + * returns an indication as to whether the construction was successful. + */ +static void scic_sds_stp_optimized_request_construct( + struct scic_sds_request *this_request, + u8 optimized_task_type, + u32 transfer_length, + SCI_IO_REQUEST_DATA_DIRECTION data_direction) +{ + struct scu_task_context *task_context = this_request->task_context_buffer; + + /* Build the STP task context structure */ + scu_sata_reqeust_construct_task_context(this_request, task_context); + + /* Copy over the SGL elements */ + scic_sds_request_build_sgl(this_request); + + /* Copy over the number of bytes to be transfered */ + task_context->transfer_length_bytes = transfer_length; + + if (data_direction == SCI_IO_REQUEST_DATA_OUT) { + /* + * The difference between the DMA IN and DMA OUT request task type + * values are consistent with the difference between FPDMA READ + * and FPDMA WRITE values. Add the supplied task type parameter + * to this difference to set the task type properly for this + * DATA OUT (WRITE) case. */ + task_context->task_type = optimized_task_type + (SCU_TASK_TYPE_DMA_OUT + - SCU_TASK_TYPE_DMA_IN); + } else { + /* + * For the DATA IN (READ) case, simply save the supplied + * optimized task type. */ + task_context->task_type = optimized_task_type; + } +} + +/** + * + * @this_request: This parameter specifies the request to be constructed. + * + * This method will construct the STP UDMA request and its associated TC data. + * This method returns an indication as to whether the construction was + * successful. SCI_SUCCESS Currently this method always returns this value. + */ +enum sci_status scic_sds_stp_udma_request_construct( + struct scic_sds_request *this_request, + u32 transfer_length, + SCI_IO_REQUEST_DATA_DIRECTION data_direction) +{ + scic_sds_stp_non_ncq_request_construct(this_request); + + scic_sds_stp_optimized_request_construct( + this_request, + SCU_TASK_TYPE_DMA_IN, + transfer_length, + data_direction + ); + + sci_base_state_machine_construct( + &this_request->started_substate_machine, + &this_request->parent.parent, + scic_sds_stp_request_started_udma_substate_table, + SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_TC_COMPLETION_SUBSTATE + ); + + return SCI_SUCCESS; +} + +/** + * + * @this_request: This parameter specifies the request to be constructed. + * + * This method will construct the STP UDMA request and its associated TC data. + * This method returns an indication as to whether the construction was + * successful. SCI_SUCCESS Currently this method always returns this value. + */ +enum sci_status scic_sds_stp_ncq_request_construct( + struct scic_sds_request *this_request, + u32 transfer_length, + SCI_IO_REQUEST_DATA_DIRECTION data_direction) +{ + scic_sds_stp_optimized_request_construct( + this_request, + SCU_TASK_TYPE_FPDMAQ_READ, + transfer_length, + data_direction + ); + return SCI_SUCCESS; +} + +/** + * + * @this_request: This parameter specifies the STP request object for which to + * construct a RAW command frame task context. + * @task_context: This parameter specifies the SCU specific task context buffer + * to construct. + * + * This method performs the operations common to all SATA/STP requests + * utilizing the raw frame method. none + */ +void scu_stp_raw_request_construct_task_context( + struct scic_sds_stp_request *this_request, + struct scu_task_context *task_context) +{ + scu_sata_reqeust_construct_task_context(&this_request->parent, task_context); + + task_context->control_frame = 0; + task_context->priority = SCU_TASK_PRIORITY_NORMAL; + task_context->task_type = SCU_TASK_TYPE_SATA_RAW_FRAME; + task_context->type.stp.fis_type = SATA_FIS_TYPE_REGH2D; + task_context->transfer_length_bytes = sizeof(struct sata_fis_reg_h2d) - sizeof(u32); +} + +/** + * + * @this_request: This parameter specifies the core request object to + * construction into an STP/SATA non-data request. + * + * This method will construct the STP Non-data request and its associated TC + * data. A non-data request essentially behaves like a 0 length read request + * in the SCU. This method currently always returns SCI_SUCCESS + */ +enum sci_status scic_sds_stp_non_data_request_construct( + struct scic_sds_request *this_request) +{ + scic_sds_stp_non_ncq_request_construct(this_request); + + /* Build the STP task context structure */ + scu_stp_raw_request_construct_task_context( + (struct scic_sds_stp_request *)this_request, + this_request->task_context_buffer + ); + + sci_base_state_machine_construct( + &this_request->started_substate_machine, + &this_request->parent.parent, + scic_sds_stp_request_started_non_data_substate_table, + SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_H2D_COMPLETION_SUBSTATE + ); + + return SCI_SUCCESS; +} + + +enum sci_status scic_sds_stp_soft_reset_request_construct( + struct scic_sds_request *this_request) +{ + scic_sds_stp_non_ncq_request_construct(this_request); + + /* Build the STP task context structure */ + scu_stp_raw_request_construct_task_context( + (struct scic_sds_stp_request *)this_request, + this_request->task_context_buffer + ); + + sci_base_state_machine_construct( + &this_request->started_substate_machine, + &this_request->parent.parent, + scic_sds_stp_request_started_soft_reset_substate_table, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_ASSERTED_COMPLETION_SUBSTATE + ); + + return SCI_SUCCESS; +} + + +void scic_stp_io_request_set_ncq_tag( + struct scic_sds_request *req, + u16 ncq_tag) +{ + /** + * @note This could be made to return an error to the user if the user + * attempts to set the NCQ tag in the wrong state. + */ + req->task_context_buffer->type.stp.ncq_tag = ncq_tag; +} + + +void *scic_stp_io_request_get_h2d_reg_address( + struct scic_sds_request *req) +{ + return req->command_buffer; +} + + +void *scic_stp_io_request_get_d2h_reg_address( + struct scic_sds_request *req) +{ + return &((struct scic_sds_stp_request *)req)->d2h_reg_fis; +} + +/** + * + * @this_request: + * + * Get the next SGL element from the request. - Check on which SGL element pair + * we are working - if working on SLG pair element A - advance to element B - + * else - check to see if there are more SGL element pairs for this IO request + * - if there are more SGL element pairs - advance to the next pair and return + * element A struct scu_sgl_element* + */ +struct scu_sgl_element *scic_sds_stp_request_pio_get_next_sgl( + struct scic_sds_stp_request *this_request + ) { + struct scu_sgl_element *current_sgl; + + if (this_request->type.pio.request_current.sgl_set == SCU_SGL_ELEMENT_PAIR_A) { + if ( + (this_request->type.pio.request_current.sgl_pair->B.address_lower == 0) + && (this_request->type.pio.request_current.sgl_pair->B.address_upper == 0) + ) { + current_sgl = NULL; + } else { + this_request->type.pio.request_current.sgl_set = SCU_SGL_ELEMENT_PAIR_B; + current_sgl = &(this_request->type.pio.request_current.sgl_pair->B); + } + } else { + if ( + (this_request->type.pio.request_current.sgl_pair->next_pair_lower == 0) + && (this_request->type.pio.request_current.sgl_pair->next_pair_upper == 0) + ) { + current_sgl = NULL; + } else { + dma_addr_t physical_address; + + sci_cb_make_physical_address( + physical_address, + this_request->type.pio.request_current.sgl_pair->next_pair_upper, + this_request->type.pio.request_current.sgl_pair->next_pair_lower + ); + + this_request->type.pio.request_current.sgl_pair = + (struct scu_sgl_element_pair *)scic_cb_get_virtual_address( + this_request->parent.owning_controller, + physical_address + ); + + this_request->type.pio.request_current.sgl_set = SCU_SGL_ELEMENT_PAIR_A; + + current_sgl = &(this_request->type.pio.request_current.sgl_pair->A); + } + } + + return current_sgl; +} + +/** + * + * @scic_io_request: The core request object which is cast to a SATA PIO + * request object. + * + * This method will construct the SATA PIO request. This method returns an + * indication as to whether the construction was successful. SCI_SUCCESS + * Currently this method always returns this value. + */ +enum sci_status scic_sds_stp_pio_request_construct( + struct scic_sds_request *scic_io_request, + u8 sat_protocol, + bool copy_rx_frame) +{ + struct scic_sds_stp_request *this_request; + + this_request = (struct scic_sds_stp_request *)scic_io_request; + + scic_sds_stp_non_ncq_request_construct(&this_request->parent); + + scu_stp_raw_request_construct_task_context( + this_request, this_request->parent.task_context_buffer + ); + + this_request->type.pio.current_transfer_bytes = 0; + this_request->type.pio.ending_error = 0; + this_request->type.pio.ending_status = 0; + + this_request->type.pio.request_current.sgl_offset = 0; + this_request->type.pio.request_current.sgl_set = SCU_SGL_ELEMENT_PAIR_A; + this_request->type.pio.sat_protocol = sat_protocol; + + if (copy_rx_frame) { + scic_sds_request_build_sgl(&this_request->parent); + /* + * Since the IO request copy of the TC contains the same data as + * the actual TC this pointer is vaild for either. */ + this_request->type.pio.request_current.sgl_pair = + &this_request->parent.task_context_buffer->sgl_pair_ab; + } else { + /* The user does not want the data copied to the SGL buffer location */ + this_request->type.pio.request_current.sgl_pair = NULL; + } + + sci_base_state_machine_construct( + &this_request->parent.started_substate_machine, + &this_request->parent.parent.parent, + scic_sds_stp_request_started_pio_substate_table, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_H2D_COMPLETION_SUBSTATE + ); + + return SCI_SUCCESS; +} + +/** + * + * @this_request: + * @completion_code: + * + * This method processes a TC completion. The expected TC completion is for + * the transmission of the H2D register FIS containing the SATA/STP non-data + * request. This method always successfully processes the TC completion. + * SCI_SUCCESS This value is always returned. + */ +static enum sci_status scic_sds_stp_request_non_data_await_h2d_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_D2H_SUBSTATE + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return SCI_SUCCESS; +} + +/** + * + * @request: This parameter specifies the request for which a frame has been + * received. + * @frame_index: This parameter specifies the index of the frame that has been + * received. + * + * This method processes frames received from the target while waiting for a + * device to host register FIS. If a non-register FIS is received during this + * time, it is treated as a protocol violation from an IO perspective. Indicate + * if the received frame was processed successfully. + */ +static enum sci_status scic_sds_stp_request_non_data_await_d2h_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + u32 *frame_buffer; + struct scic_sds_stp_request *this_request = (struct scic_sds_stp_request *)request; + + status = scic_sds_unsolicited_frame_control_get_header( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + switch (frame_header->fis_type) { + case SATA_FIS_TYPE_REGD2H: + scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + scic_sds_controller_copy_sata_response( + &this_request->d2h_reg_fis, (u32 *)frame_header, frame_buffer + ); + + /* The command has completed with error */ + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + break; + + default: + dev_warn(scic_to_dev(request->owning_controller), + "%s: IO Request:0x%p Frame Id:%d protocol " + "violation occurred\n", + __func__, this_request, frame_index); + + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_UNEXP_FIS, + SCI_FAILURE_PROTOCOL_VIOLATION + ); + break; + } + + sci_base_state_machine_change_state( + &this_request->parent.parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + /* Frame has been decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, frame_index + ); + } else + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC IO Request 0x%p could not get frame header " + "for frame index %d, status %x\n", + __func__, this_request, frame_index, status); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_non_data_substate_handler_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_H2D_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_stp_request_non_data_await_h2d_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_D2H_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_request_non_data_await_d2h_frame_handler, + } +}; + +static void scic_sds_stp_request_started_non_data_await_h2d_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_non_data_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_H2D_COMPLETION_SUBSTATE + ); + + scic_sds_remote_device_set_working_request( + this_request->target_device, this_request + ); +} + +static void scic_sds_stp_request_started_non_data_await_d2h_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_non_data_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_D2H_SUBSTATE + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_stp_request_started_non_data_substate_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_H2D_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_non_data_await_h2d_completion_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_D2H_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_non_data_await_d2h_enter, + }, +}; + +#define SCU_MAX_FRAME_BUFFER_SIZE 0x400 /* 1K is the maximum SCU frame data payload */ + +/** + * + * @this_request: + * @length: + * + * This function will transmit DATA_FIS from (current sgl + offset) for input + * parameter length. current sgl and offset is alreay stored in the IO request + * enum sci_status + */ + +static enum sci_status scic_sds_stp_request_pio_data_out_trasmit_data_frame( + struct scic_sds_request *this_request, + u32 length) +{ + struct scic_sds_stp_request *this_sds_stp_request = (struct scic_sds_stp_request *)this_request; + sci_base_controller_request_handler_t continue_io; + struct scu_sgl_element *current_sgl; + struct scic_sds_controller *scic; + u32 state; + + /* + * Recycle the TC and reconstruct it for sending out DATA FIS containing + * for the data from current_sgl+offset for the input length */ + struct scu_task_context *task_context = scic_sds_controller_get_task_context_buffer( + this_request->owning_controller, + this_request->io_tag + ); + + if (this_sds_stp_request->type.pio.request_current.sgl_set == SCU_SGL_ELEMENT_PAIR_A) + current_sgl = &(this_sds_stp_request->type.pio.request_current.sgl_pair->A); + else + current_sgl = &(this_sds_stp_request->type.pio.request_current.sgl_pair->B); + + /* update the TC */ + task_context->command_iu_upper = current_sgl->address_upper; + task_context->command_iu_lower = current_sgl->address_lower; + task_context->transfer_length_bytes = length; + task_context->type.stp.fis_type = SATA_FIS_TYPE_DATA; + + /* send the new TC out. */ + scic = this_request->owning_controller; + state = scic->parent.state_machine.current_state_id; + continue_io = scic_sds_controller_state_handler_table[state].base.continue_io; + return continue_io(&scic->parent, &this_request->target_device->parent, + &this_request->parent); +} + +/** + * + * @this_request: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_data_out_transmit_data( + struct scic_sds_request *this_sds_request) +{ + + struct scu_sgl_element *current_sgl; + u32 sgl_offset; + u32 remaining_bytes_in_current_sgl = 0; + enum sci_status status = SCI_SUCCESS; + + struct scic_sds_stp_request *this_sds_stp_request = (struct scic_sds_stp_request *)this_sds_request; + + sgl_offset = this_sds_stp_request->type.pio.request_current.sgl_offset; + + if (this_sds_stp_request->type.pio.request_current.sgl_set == SCU_SGL_ELEMENT_PAIR_A) { + current_sgl = &(this_sds_stp_request->type.pio.request_current.sgl_pair->A); + remaining_bytes_in_current_sgl = this_sds_stp_request->type.pio.request_current.sgl_pair->A.length - sgl_offset; + } else { + current_sgl = &(this_sds_stp_request->type.pio.request_current.sgl_pair->B); + remaining_bytes_in_current_sgl = this_sds_stp_request->type.pio.request_current.sgl_pair->B.length - sgl_offset; + } + + + if (this_sds_stp_request->type.pio.pio_transfer_bytes > 0) { + if (this_sds_stp_request->type.pio.pio_transfer_bytes >= remaining_bytes_in_current_sgl) { + /* recycle the TC and send the H2D Data FIS from (current sgl + sgl_offset) and length = remaining_bytes_in_current_sgl */ + status = scic_sds_stp_request_pio_data_out_trasmit_data_frame(this_sds_request, remaining_bytes_in_current_sgl); + if (status == SCI_SUCCESS) { + this_sds_stp_request->type.pio.pio_transfer_bytes -= remaining_bytes_in_current_sgl; + + /* update the current sgl, sgl_offset and save for future */ + current_sgl = scic_sds_stp_request_pio_get_next_sgl(this_sds_stp_request); + sgl_offset = 0; + } + } else if (this_sds_stp_request->type.pio.pio_transfer_bytes < remaining_bytes_in_current_sgl) { + /* recycle the TC and send the H2D Data FIS from (current sgl + sgl_offset) and length = type.pio.pio_transfer_bytes */ + scic_sds_stp_request_pio_data_out_trasmit_data_frame(this_sds_request, this_sds_stp_request->type.pio.pio_transfer_bytes); + + if (status == SCI_SUCCESS) { + /* Sgl offset will be adjusted and saved for future */ + sgl_offset += this_sds_stp_request->type.pio.pio_transfer_bytes; + current_sgl->address_lower += this_sds_stp_request->type.pio.pio_transfer_bytes; + this_sds_stp_request->type.pio.pio_transfer_bytes = 0; + } + } + } + + if (status == SCI_SUCCESS) { + this_sds_stp_request->type.pio.request_current.sgl_offset = sgl_offset; + } + + return status; +} + +/** + * + * @this_request: The request that is used for the SGL processing. + * @data_buffer: The buffer of data to be copied. + * @length: The length of the data transfer. + * + * Copy the data from the buffer for the length specified to the IO reqeust SGL + * specified data region. enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_data_in_copy_data_buffer( + struct scic_sds_stp_request *this_request, + u8 *data_buffer, + u32 length) +{ + enum sci_status status; + struct scu_sgl_element *current_sgl; + u32 sgl_offset; + u32 data_offset; + u8 *source_address; + u8 *destination_address; + u32 copy_length; + + /* Initial setup to get the current working SGL and the offset within the buffer */ + current_sgl = + (this_request->type.pio.request_current.sgl_set == SCU_SGL_ELEMENT_PAIR_A) ? + &(this_request->type.pio.request_current.sgl_pair->A) : + &(this_request->type.pio.request_current.sgl_pair->B); + + sgl_offset = this_request->type.pio.request_current.sgl_offset; + + source_address = data_buffer; + data_offset = 0; + + status = SCI_SUCCESS; + + /* While we are still doing Ok and there is more data to transfer */ + while ( + (length > 0) + && (status == SCI_SUCCESS) + ) { + if (current_sgl->length == sgl_offset) { + /* This SGL has been exauhasted so we need to get the next SGL */ + current_sgl = scic_sds_stp_request_pio_get_next_sgl(this_request); + + if (current_sgl == NULL) + status = SCI_FAILURE; + else + sgl_offset = 0; + } else { + dma_addr_t physical_address; + + sci_cb_make_physical_address( + physical_address, + current_sgl->address_upper, + current_sgl->address_lower + ); + + destination_address = (u8 *)scic_cb_get_virtual_address( + this_request->parent.owning_controller, + physical_address + ); + + source_address += data_offset; + destination_address += sgl_offset; + + copy_length = min(length, current_sgl->length - sgl_offset); + + memcpy(destination_address, source_address, copy_length); + + length -= copy_length; + sgl_offset += copy_length; + data_offset += copy_length; + } + } + + this_request->type.pio.request_current.sgl_offset = sgl_offset; + + return status; +} + +/** + * + * @this_request: The PIO DATA IN request that is to receive the data. + * @data_buffer: The buffer to copy from. + * + * Copy the data buffer to the io request data region. enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_data_in_copy_data( + struct scic_sds_stp_request *this_request, + u8 *data_buffer) +{ + enum sci_status status; + + /* + * If there is less than 1K remaining in the transfer request + * copy just the data for the transfer */ + if (this_request->type.pio.pio_transfer_bytes < SCU_MAX_FRAME_BUFFER_SIZE) { + status = scic_sds_stp_request_pio_data_in_copy_data_buffer( + this_request, data_buffer, this_request->type.pio.pio_transfer_bytes); + + if (status == SCI_SUCCESS) + this_request->type.pio.pio_transfer_bytes = 0; + } else { + /* We are transfering the whole frame so copy */ + status = scic_sds_stp_request_pio_data_in_copy_data_buffer( + this_request, data_buffer, SCU_MAX_FRAME_BUFFER_SIZE); + + if (status == SCI_SUCCESS) + this_request->type.pio.pio_transfer_bytes -= SCU_MAX_FRAME_BUFFER_SIZE; + } + + return status; +} + +/** + * + * @this_request: + * @completion_code: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_await_h2d_completion_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + enum sci_status status = SCI_SUCCESS; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return status; +} + +/** + * + * @this_request: + * @frame_index: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_await_frame_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + u32 *frame_buffer; + struct scic_sds_stp_request *this_request; + + this_request = (struct scic_sds_stp_request *)request; + + status = scic_sds_unsolicited_frame_control_get_header( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + switch (frame_header->fis_type) { + case SATA_FIS_TYPE_PIO_SETUP: + /* Get from the frame buffer the PIO Setup Data */ + scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + /* + * Get the data from the PIO Setup + * The SCU Hardware returns first word in the frame_header and the rest + * of the data is in the frame buffer so we need to back up one dword */ + this_request->type.pio.pio_transfer_bytes = + (u16)((struct sata_fis_pio_setup *)(&frame_buffer[-1]))->transfter_count; + this_request->type.pio.ending_status = + (u8)((struct sata_fis_pio_setup *)(&frame_buffer[-1]))->ending_status; + + scic_sds_controller_copy_sata_response( + &this_request->d2h_reg_fis, (u32 *)frame_header, frame_buffer + ); + + this_request->d2h_reg_fis.status = + this_request->type.pio.ending_status; + + /* The next state is dependent on whether the request was PIO Data-in or Data out */ + if (this_request->type.pio.sat_protocol == SAT_PROTOCOL_PIO_DATA_IN) { + sci_base_state_machine_change_state( + &this_request->parent.started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_IN_AWAIT_DATA_SUBSTATE + ); + } else if (this_request->type.pio.sat_protocol == SAT_PROTOCOL_PIO_DATA_OUT) { + /* Transmit data */ + status = scic_sds_stp_request_pio_data_out_transmit_data(request); + if (status == SCI_SUCCESS) { + sci_base_state_machine_change_state( + &this_request->parent.started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_OUT_TRANSMIT_DATA_SUBSTATE + ); + } + } + break; + + case SATA_FIS_TYPE_SETDEVBITS: + sci_base_state_machine_change_state( + &this_request->parent.started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE + ); + break; + + case SATA_FIS_TYPE_REGD2H: + if ((frame_header->status & ATA_STATUS_REG_BSY_BIT) == 0) { + scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + scic_sds_controller_copy_sata_response( + &this_request->d2h_reg_fis, (u32 *)frame_header, frame_buffer); + + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + + sci_base_state_machine_change_state( + &this_request->parent.parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + } else { + /* + * Now why is the drive sending a D2H Register FIS when it is still busy? + * Do nothing since we are still in the right state. */ + dev_dbg(scic_to_dev(request->owning_controller), + "%s: SCIC PIO Request 0x%p received " + "D2H Register FIS with BSY status " + "0x%x\n", + __func__, + this_request, + frame_header->status); + } + break; + + default: + break; + } + + /* Frame is decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, + frame_index + ); + } else + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC IO Request 0x%p could not get frame header " + "for frame index %d, status %x\n", + __func__, this_request, frame_index, status); + + return status; +} + +/** + * + * @this_request: + * @frame_index: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_data_in_await_data_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + struct sata_fis_data *frame_buffer; + struct scic_sds_stp_request *this_request; + + this_request = (struct scic_sds_stp_request *)request; + + status = scic_sds_unsolicited_frame_control_get_header( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + if (frame_header->fis_type == SATA_FIS_TYPE_DATA) { + if (this_request->type.pio.request_current.sgl_pair == NULL) { + this_request->parent.saved_rx_frame_index = frame_index; + this_request->type.pio.pio_transfer_bytes = 0; + } else { + status = scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + status = scic_sds_stp_request_pio_data_in_copy_data(this_request, (u8 *)frame_buffer); + + /* Frame is decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, + frame_index + ); + } + + /* + * Check for the end of the transfer, are there more bytes remaining + * for this data transfer */ + if ( + (status == SCI_SUCCESS) + && (this_request->type.pio.pio_transfer_bytes == 0) + ) { + if ((this_request->type.pio.ending_status & ATA_STATUS_REG_BSY_BIT) == 0) { + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + + sci_base_state_machine_change_state( + &this_request->parent.parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + } else { + sci_base_state_machine_change_state( + &this_request->parent.started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE + ); + } + } + } else { + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC PIO Request 0x%p received frame %d " + "with fis type 0x%02x when expecting a data " + "fis.\n", + __func__, + this_request, + frame_index, + frame_header->fis_type); + + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_GOOD, + SCI_FAILURE_IO_REQUIRES_SCSI_ABORT + ); + + sci_base_state_machine_change_state( + &this_request->parent.parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + /* Frame is decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, + frame_index + ); + } + } else + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC IO Request 0x%p could not get frame header " + "for frame index %d, status %x\n", + __func__, this_request, frame_index, status); + + return status; +} + + +/** + * + * @this_request: + * @completion_code: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_pio_data_out_await_data_transmit_completion_tc_completion_handler( + + struct scic_sds_request *this_request, + u32 completion_code) +{ + enum sci_status status = SCI_SUCCESS; + bool all_frames_transferred = false; + + struct scic_sds_stp_request *this_scic_sds_stp_request = (struct scic_sds_stp_request *)this_request; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + /* Transmit data */ + if (this_scic_sds_stp_request->type.pio.pio_transfer_bytes != 0) { + status = scic_sds_stp_request_pio_data_out_transmit_data(this_request); + if (status == SCI_SUCCESS) { + if (this_scic_sds_stp_request->type.pio.pio_transfer_bytes == 0) + all_frames_transferred = true; + } + } else if (this_scic_sds_stp_request->type.pio.pio_transfer_bytes == 0) { + /* + * this will happen if the all data is written at the + * first time after the pio setup fis is received + */ + all_frames_transferred = true; + } + + /* all data transferred. */ + if (all_frames_transferred) { + /* + * Change the state to SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_IN_AWAIT_FRAME_SUBSTATE + * and wait for PIO_SETUP fis / or D2H REg fis. */ + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE + ); + } + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return status; +} + +/** + * + * @request: This is the request which is receiving the event. + * @event_code: This is the event code that the request on which the request is + * expected to take action. + * + * This method will handle any link layer events while waiting for the data + * frame. enum sci_status SCI_SUCCESS SCI_FAILURE + */ +static enum sci_status scic_sds_stp_request_pio_data_in_await_data_event_handler( + struct scic_sds_request *request, + u32 event_code) +{ + enum sci_status status; + + switch (scu_get_event_specifier(event_code)) { + case SCU_TASK_DONE_CRC_ERR << SCU_EVENT_SPECIFIC_CODE_SHIFT: + /* + * We are waiting for data and the SCU has R_ERR the data frame. + * Go back to waiting for the D2H Register FIS */ + sci_base_state_machine_change_state( + &request->started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE + ); + + status = SCI_SUCCESS; + break; + + default: + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC PIO Request 0x%p received unexpected " + "event 0x%08x\n", + __func__, request, event_code); + + /* / @todo Should we fail the PIO request when we get an unexpected event? */ + status = SCI_FAILURE; + break; + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_pio_substate_handler_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_H2D_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_stp_request_pio_await_h2d_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler + }, + [SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_request_pio_await_frame_frame_handler + }, + [SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_IN_AWAIT_DATA_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_stp_request_pio_data_in_await_data_event_handler, + .frame_handler = scic_sds_stp_request_pio_data_in_await_data_frame_handler + }, + [SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_OUT_TRANSMIT_DATA_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_stp_request_pio_data_out_await_data_transmit_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + } +}; + +static void scic_sds_stp_request_started_pio_await_h2d_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_pio_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_H2D_COMPLETION_SUBSTATE + ); + + scic_sds_remote_device_set_working_request( + this_request->target_device, this_request); +} + +static void scic_sds_stp_request_started_pio_await_frame_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_pio_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE + ); +} + +static void scic_sds_stp_request_started_pio_data_in_await_data_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_pio_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_IN_AWAIT_DATA_SUBSTATE + ); +} + +static void scic_sds_stp_request_started_pio_data_out_transmit_data_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_pio_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_OUT_TRANSMIT_DATA_SUBSTATE + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_stp_request_started_pio_substate_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_H2D_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_pio_await_h2d_completion_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_PIO_AWAIT_FRAME_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_pio_await_frame_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_IN_AWAIT_DATA_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_pio_data_in_await_data_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_PIO_DATA_OUT_TRANSMIT_DATA_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_pio_data_out_transmit_data_enter, + } +}; + +static void scic_sds_stp_request_udma_complete_request( + struct scic_sds_request *this_request, + u32 scu_status, + enum sci_status sci_status) +{ + scic_sds_request_set_status( + this_request, scu_status, sci_status + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); +} + +/** + * + * @this_request: + * @frame_index: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_udma_general_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + u32 *frame_buffer; + + status = scic_sds_unsolicited_frame_control_get_header( + &this_request->owning_controller->uf_control, + frame_index, + (void **)&frame_header + ); + + if ( + (status == SCI_SUCCESS) + && (frame_header->fis_type == SATA_FIS_TYPE_REGD2H) + ) { + scic_sds_unsolicited_frame_control_get_buffer( + &this_request->owning_controller->uf_control, + frame_index, + (void **)&frame_buffer + ); + + scic_sds_controller_copy_sata_response( + &((struct scic_sds_stp_request *)this_request)->d2h_reg_fis, + (u32 *)frame_header, + frame_buffer + ); + } + + scic_sds_controller_release_frame( + this_request->owning_controller, frame_index); + + return status; +} + +/** + * This method process TC completions while in the state where we are waiting + * for TC completions. + * @this_request: + * @completion_code: + * + * enum sci_status + */ +static enum sci_status scic_sds_stp_request_udma_await_tc_completion_tc_completion_handler( + struct scic_sds_request *request, + u32 completion_code) +{ + enum sci_status status = SCI_SUCCESS; + struct scic_sds_stp_request *this_request = (struct scic_sds_stp_request *)request; + + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_stp_request_udma_complete_request( + &this_request->parent, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + break; + + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_UNEXP_FIS): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_REG_ERR): + /* + * We must check ther response buffer to see if the D2H Register FIS was + * received before we got the TC completion. */ + if (this_request->d2h_reg_fis.fis_type == SATA_FIS_TYPE_REGD2H) { + scic_sds_remote_device_suspend( + this_request->parent.target_device, + SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)) + ); + + scic_sds_stp_request_udma_complete_request( + &this_request->parent, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + } else { + /* + * If we have an error completion status for the TC then we can expect a + * D2H register FIS from the device so we must change state to wait for it */ + sci_base_state_machine_change_state( + &this_request->parent.started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_D2H_REG_FIS_SUBSTATE + ); + } + break; + + /* + * / @todo Check to see if any of these completion status need to wait for + * / the device to host register fis. */ + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_INV_FIS_LEN): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_MAX_PLD_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_LL_R_ERR): + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_CRC_ERR): + scic_sds_remote_device_suspend( + this_request->parent.target_device, + SCU_EVENT_SPECIFIC(SCU_NORMALIZE_COMPLETION_STATUS(completion_code)) + ); + /* Fall through to the default case */ + default: + /* All other completion status cause the IO to be complete. */ + scic_sds_stp_request_udma_complete_request( + &this_request->parent, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + break; + } + + return status; +} + +static enum sci_status scic_sds_stp_request_udma_await_d2h_reg_fis_frame_handler( + struct scic_sds_request *this_request, + u32 frame_index) +{ + enum sci_status status; + + /* Use the general frame handler to copy the resposne data */ + status = scic_sds_stp_request_udma_general_frame_handler(this_request, frame_index); + + if (status == SCI_SUCCESS) { + scic_sds_stp_request_udma_complete_request( + this_request, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + } + + return status; +} + +/* --------------------------------------------------------------------------- */ + +const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_udma_substate_handler_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_TC_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_stp_request_udma_await_tc_completion_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_request_udma_general_frame_handler, + }, + [SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_D2H_REG_FIS_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_request_udma_await_d2h_reg_fis_frame_handler, + }, +}; + +static void scic_sds_stp_request_started_udma_await_tc_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_udma_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_TC_COMPLETION_SUBSTATE + ); +} + +/** + * + * + * This state is entered when there is an TC completion failure. The hardware + * received an unexpected condition while processing the IO request and now + * will UF the D2H register FIS to complete the IO. + */ +static void scic_sds_stp_request_started_udma_await_d2h_reg_fis_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_udma_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_D2H_REG_FIS_SUBSTATE + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_stp_request_started_udma_substate_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_TC_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_udma_await_tc_completion_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_D2H_REG_FIS_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_udma_await_d2h_reg_fis_enter, + }, +}; + +/** + * + * @this_request: + * @completion_code: + * + * This method processes a TC completion. The expected TC completion is for + * the transmission of the H2D register FIS containing the SATA/STP non-data + * request. This method always successfully processes the TC completion. + * SCI_SUCCESS This value is always returned. + */ +static enum sci_status scic_sds_stp_request_soft_reset_await_h2d_asserted_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_DIAGNOSTIC_COMPLETION_SUBSTATE + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return SCI_SUCCESS; +} + +/** + * + * @this_request: + * @completion_code: + * + * This method processes a TC completion. The expected TC completion is for + * the transmission of the H2D register FIS containing the SATA/STP non-data + * request. This method always successfully processes the TC completion. + * SCI_SUCCESS This value is always returned. + */ +static enum sci_status scic_sds_stp_request_soft_reset_await_h2d_diagnostic_tc_completion_handler( + struct scic_sds_request *this_request, + u32 completion_code) +{ + switch (SCU_GET_COMPLETION_TL_STATUS(completion_code)) { + case SCU_MAKE_COMPLETION_STATUS(SCU_TASK_DONE_GOOD): + scic_sds_request_set_status( + this_request, SCU_TASK_DONE_GOOD, SCI_SUCCESS + ); + + sci_base_state_machine_change_state( + &this_request->started_substate_machine, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_D2H_RESPONSE_FRAME_SUBSTATE + ); + break; + + default: + /* + * All other completion status cause the IO to be complete. If a NAK + * was received, then it is up to the user to retry the request. */ + scic_sds_request_set_status( + this_request, + SCU_NORMALIZE_COMPLETION_STATUS(completion_code), + SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR + ); + + sci_base_state_machine_change_state( + &this_request->parent.state_machine, SCI_BASE_REQUEST_STATE_COMPLETED + ); + break; + } + + return SCI_SUCCESS; +} + +/** + * + * @request: This parameter specifies the request for which a frame has been + * received. + * @frame_index: This parameter specifies the index of the frame that has been + * received. + * + * This method processes frames received from the target while waiting for a + * device to host register FIS. If a non-register FIS is received during this + * time, it is treated as a protocol violation from an IO perspective. Indicate + * if the received frame was processed successfully. + */ +static enum sci_status scic_sds_stp_request_soft_reset_await_d2h_frame_handler( + struct scic_sds_request *request, + u32 frame_index) +{ + enum sci_status status; + struct sata_fis_header *frame_header; + u32 *frame_buffer; + struct scic_sds_stp_request *this_request = (struct scic_sds_stp_request *)request; + + status = scic_sds_unsolicited_frame_control_get_header( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_header + ); + + if (status == SCI_SUCCESS) { + switch (frame_header->fis_type) { + case SATA_FIS_TYPE_REGD2H: + scic_sds_unsolicited_frame_control_get_buffer( + &(this_request->parent.owning_controller->uf_control), + frame_index, + (void **)&frame_buffer + ); + + scic_sds_controller_copy_sata_response( + &this_request->d2h_reg_fis, (u32 *)frame_header, frame_buffer + ); + + /* The command has completed with error */ + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_CHECK_RESPONSE, + SCI_FAILURE_IO_RESPONSE_VALID + ); + break; + + default: + dev_warn(scic_to_dev(request->owning_controller), + "%s: IO Request:0x%p Frame Id:%d protocol " + "violation occurred\n", + __func__, + this_request, + frame_index); + + scic_sds_request_set_status( + &this_request->parent, + SCU_TASK_DONE_UNEXP_FIS, + SCI_FAILURE_PROTOCOL_VIOLATION + ); + break; + } + + sci_base_state_machine_change_state( + &this_request->parent.parent.state_machine, + SCI_BASE_REQUEST_STATE_COMPLETED + ); + + /* Frame has been decoded return it to the controller */ + scic_sds_controller_release_frame( + this_request->parent.owning_controller, frame_index + ); + } else + dev_err(scic_to_dev(request->owning_controller), + "%s: SCIC IO Request 0x%p could not get frame header " + "for frame index %d, status %x\n", + __func__, this_request, frame_index, status); + + return status; +} + +/* --------------------------------------------------------------------------- */ + +const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_soft_reset_substate_handler_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_ASSERTED_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_stp_request_soft_reset_await_h2d_asserted_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_DIAGNOSTIC_COMPLETION_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_stp_request_soft_reset_await_h2d_diagnostic_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_request_default_frame_handler, + }, + [SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_D2H_RESPONSE_FRAME_SUBSTATE] = { + .parent.start_handler = scic_sds_request_default_start_handler, + .parent.abort_handler = scic_sds_request_started_state_abort_handler, + .parent.complete_handler = scic_sds_request_default_complete_handler, + .parent.destruct_handler = scic_sds_request_default_destruct_handler, + .tc_completion_handler = scic_sds_request_default_tc_completion_handler, + .event_handler = scic_sds_request_default_event_handler, + .frame_handler = scic_sds_stp_request_soft_reset_await_d2h_frame_handler, + }, +}; + +static void scic_sds_stp_request_started_soft_reset_await_h2d_asserted_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_soft_reset_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_ASSERTED_COMPLETION_SUBSTATE + ); + + scic_sds_remote_device_set_working_request( + this_request->target_device, this_request + ); +} + +static void scic_sds_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + sci_base_controller_request_handler_t continue_io; + struct scu_task_context *task_context; + struct sata_fis_reg_h2d *h2d_fis; + struct scic_sds_controller *scic; + enum sci_status status; + u32 state; + + /* Clear the SRST bit */ + h2d_fis = scic_stp_io_request_get_h2d_reg_address(this_request); + h2d_fis->control = 0; + + /* Clear the TC control bit */ + task_context = scic_sds_controller_get_task_context_buffer( + this_request->owning_controller, this_request->io_tag); + task_context->control_frame = 0; + + scic = this_request->owning_controller; + state = scic->parent.state_machine.current_state_id; + continue_io = scic_sds_controller_state_handler_table[state].base.continue_io; + + status = continue_io(&scic->parent, &this_request->target_device->parent, + &this_request->parent); + + if (status == SCI_SUCCESS) { + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_soft_reset_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_DIAGNOSTIC_COMPLETION_SUBSTATE + ); + } +} + +static void scic_sds_stp_request_started_soft_reset_await_d2h_response_enter( + struct sci_base_object *object) +{ + struct scic_sds_request *this_request = (struct scic_sds_request *)object; + + SET_STATE_HANDLER( + this_request, + scic_sds_stp_request_started_soft_reset_substate_handler_table, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_D2H_RESPONSE_FRAME_SUBSTATE + ); +} + +/* --------------------------------------------------------------------------- */ + +const struct sci_base_state scic_sds_stp_request_started_soft_reset_substate_table[] = { + [SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_ASSERTED_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_soft_reset_await_h2d_asserted_completion_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_DIAGNOSTIC_COMPLETION_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_soft_reset_await_h2d_diagnostic_completion_enter, + }, + [SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_D2H_RESPONSE_FRAME_SUBSTATE] = { + .enter_state = scic_sds_stp_request_started_soft_reset_await_d2h_response_enter, + }, +}; + diff --git a/drivers/scsi/isci/core/scic_sds_stp_request.h b/drivers/scsi/isci/core/scic_sds_stp_request.h new file mode 100644 index 000000000000..5578d2baf7ca --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_stp_request.h @@ -0,0 +1,221 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_SDS_STP_REQUEST_T_ +#define _SCIC_SDS_STP_REQUEST_T_ + +#include "intel_sata.h" +#include "sci_types.h" +#include "scic_sds_request.h" + +/** + * This structure represents the additional information that is required to + * handle SATA PIO requests. + * + * + */ +struct scic_sds_stp_request { + struct scic_sds_request parent; + + struct sata_fis_reg_d2h d2h_reg_fis; + + union { + u32 ncq; + + u32 udma; + + struct { + /** + * Total transfer for the entire PIO request recorded at request constuction + * time. + * + * @todo Should we just decrement this value for each byte of data transitted + * or received to elemenate the current_transfer_bytes field? + */ + u32 total_transfer_bytes; + + /** + * Total number of bytes received/transmitted in data frames since the start + * of the IO request. At the end of the IO request this should equal the + * total_transfer_bytes. + */ + u32 current_transfer_bytes; + + /** + * The number of bytes requested in the in the PIO setup. + */ + u32 pio_transfer_bytes; + + /** + * PIO Setup ending status value to tell us if we need to wait for another FIS + * or if the transfer is complete. On the receipt of a D2H FIS this will be + * the status field of that FIS. + */ + u8 ending_status; + + /** + * On receipt of a D2H FIS this will be the ending error field if the + * ending_status has the SATA_STATUS_ERR bit set. + */ + u8 ending_error; + + /** + * Protocol Type. This is filled in by core during IO Request construction type. + */ + u8 sat_protocol; + + struct { + struct scu_sgl_element_pair *sgl_pair; + u8 sgl_set; + u32 sgl_offset; + } request_current; + } pio; + + struct { + /** + * The number of bytes requested in the PIO setup before CDB data frame. + */ + u32 device_preferred_cdb_length; + } packet; + } type; + +}; + +/** + * enum SCIC_SDS_STP_REQUEST_STARTED_UDMA_SUBSTATES - This enumeration depicts + * the various sub-states associated with a SATA/STP UDMA protocol operation. + * + * + */ +enum SCIC_SDS_STP_REQUEST_STARTED_UDMA_SUBSTATES { + SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_TC_COMPLETION_SUBSTATE, + SCIC_SDS_STP_REQUEST_STARTED_UDMA_AWAIT_D2H_REG_FIS_SUBSTATE, +}; + +/** + * enum SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_SUBSTATES - This enumeration + * depicts the various sub-states associated with a SATA/STP non-data + * protocol operation. + * + * + */ +enum SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_SUBSTATES { + SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_H2D_COMPLETION_SUBSTATE, + SCIC_SDS_STP_REQUEST_STARTED_NON_DATA_AWAIT_D2H_SUBSTATE, +}; + +/** + * enum SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_SUBSTATES - THis enumeration + * depicts the various sub-states associated with a SATA/STP soft reset + * operation. + * + * + */ +enum SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_SUBSTATES { + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_ASSERTED_COMPLETION_SUBSTATE, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_H2D_DIAGNOSTIC_COMPLETION_SUBSTATE, + SCIC_SDS_STP_REQUEST_STARTED_SOFT_RESET_AWAIT_D2H_RESPONSE_FRAME_SUBSTATE, +}; + +extern const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_udma_substate_handler_table[]; + +extern const struct sci_base_state scic_sds_stp_request_started_udma_substate_table[]; + +extern const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_non_data_substate_handler_table[]; + +extern const struct sci_base_state scic_sds_stp_request_started_non_data_substate_table[]; + +extern const struct scic_sds_io_request_state_handler scic_sds_stp_request_started_soft_reset_substate_handler_table[]; + +extern const struct sci_base_state scic_sds_stp_request_started_soft_reset_substate_table[]; + +/* --------------------------------------------------------------------------- */ + +u32 scic_sds_stp_request_get_object_size(void); + + +void scic_sds_stp_non_ncq_request_construct( + struct scic_sds_request *this_request); + +enum sci_status scic_sds_stp_pio_request_construct( + struct scic_sds_request *scic_io_request, + u8 sat_protocol, + bool copy_rx_frame); + +enum sci_status scic_sds_stp_pio_request_construct_pass_through( + struct scic_sds_request *scic_io_request, + struct scic_stp_passthru_request_callbacks *passthru_cb); + +enum sci_status scic_sds_stp_udma_request_construct( + struct scic_sds_request *this_request, + u32 transfer_length, + SCI_IO_REQUEST_DATA_DIRECTION data_direction); + +enum sci_status scic_sds_stp_non_data_request_construct( + struct scic_sds_request *this_request); + +enum sci_status scic_sds_stp_soft_reset_request_construct( + struct scic_sds_request *this_request); + +enum sci_status scic_sds_stp_ncq_request_construct( + struct scic_sds_request *this_request, + u32 transfer_length, + SCI_IO_REQUEST_DATA_DIRECTION data_direction); + +void scu_stp_raw_request_construct_task_context( + struct scic_sds_stp_request *this_request, + struct scu_task_context *task_context); + +#endif /* _SCIC_SDS_STP_REQUEST_T_ */ diff --git a/drivers/scsi/isci/core/scic_sds_unsolicited_frame_control.c b/drivers/scsi/isci/core/scic_sds_unsolicited_frame_control.c new file mode 100644 index 000000000000..7ca2f1709f6d --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_unsolicited_frame_control.c @@ -0,0 +1,379 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the implementation of the + * struct scic_sds_unsolicited_frame_control object and it's public, protected, and + * private methods. + * + * + */ + +#include "scic_sds_unsolicited_frame_control.h" +#include "scu_registers.h" +#include "scic_sds_controller.h" +#include "scic_user_callback.h" +#include "sci_util.h" +#include "sci_environment.h" + +/** + * The UF buffer address table size must be programmed to a power of 2. Find + * the first power of 2 that is equal to or greater then the number of + * unsolicited frame buffers to be utilized. + * @uf_control: This parameter specifies the UF control object for which to + * update the address table count. + * + */ +void scic_sds_unsolicited_frame_control_set_address_table_count( + struct scic_sds_unsolicited_frame_control *uf_control) +{ + uf_control->address_table.count = SCU_MIN_UF_TABLE_ENTRIES; + while ( + (uf_control->address_table.count < uf_control->buffers.count) + && (uf_control->address_table.count < SCU_ABSOLUTE_MAX_UNSOLICITED_FRAMES) + ) { + uf_control->address_table.count <<= 1; + } +} + +/** + * This method will program the unsolicited frames (UFs) into the UF address + * table and construct the UF frame structure being modeled in the core. It + * will handle the case where some of the UFs are not being used and thus + * should have entries programmed to zero in the address table. + * @uf_control: This parameter specifies the unsolicted frame control object + * for which to construct the unsolicited frames objects. + * @uf_buffer_phys_address: This parameter specifies the physical address for + * the first unsolicited frame buffer. + * @uf_buffer_virt_address: This parameter specifies the virtual address for + * the first unsolicited frame buffer. + * @unused_uf_header_entries: This parameter specifies the number of unused UF + * headers. This value can be non-zero when there are a non-power of 2 + * number of unsolicited frames being supported. + * @used_uf_header_entries: This parameter specifies the number of actually + * utilized UF headers. + * + */ +static void scic_sds_unsolicited_frame_control_construct_frames( + struct scic_sds_unsolicited_frame_control *uf_control, + dma_addr_t uf_buffer_phys_address, + unsigned long uf_buffer_virt_address, + u32 unused_uf_header_entries, + u32 used_uf_header_entries) +{ + u32 index; + struct scic_sds_unsolicited_frame *uf; + + /* + * Program the unused buffers into the UF address table and the + * controller's array of UFs. */ + for (index = 0; index < unused_uf_header_entries; index++) { + uf = &uf_control->buffers.array[index]; + + sci_cb_make_physical_address( + uf_control->address_table.array[index], 0, 0 + ); + uf->buffer = NULL; + uf->header = &uf_control->headers.array[index]; + uf->state = UNSOLICITED_FRAME_EMPTY; + } + + /* + * Program the actual used UF buffers into the UF address table and + * the controller's array of UFs. */ + for (index = unused_uf_header_entries; + index < unused_uf_header_entries + used_uf_header_entries; + index++) { + uf = &uf_control->buffers.array[index]; + + uf_control->address_table.array[index] = uf_buffer_phys_address; + + uf->buffer = (void *)uf_buffer_virt_address; + uf->header = &uf_control->headers.array[index]; + uf->state = UNSOLICITED_FRAME_EMPTY; + + /* + * Increment the address of the physical and virtual memory pointers + * Everything is aligned on 1k boundary with an increment of 1k */ + uf_buffer_virt_address += SCU_UNSOLICITED_FRAME_BUFFER_SIZE; + sci_physical_address_add( + uf_buffer_phys_address, SCU_UNSOLICITED_FRAME_BUFFER_SIZE + ); + } +} + +/** + * This method constructs the various members of the unsolicted frame control + * object (buffers, headers, address, table, etc). + * @uf_control: This parameter specifies the unsolicited frame control object + * to construct. + * @mde: This parameter specifies the memory descriptor from which to derive + * all of the address information needed to get the unsolicited frame + * functionality working. + * @controller: This parameter specifies the controller object associated with + * the uf_control being constructed. + * + */ +void scic_sds_unsolicited_frame_control_construct( + struct scic_sds_unsolicited_frame_control *uf_control, + struct sci_physical_memory_descriptor *mde, + struct scic_sds_controller *controller) +{ + u32 unused_uf_header_entries; + u32 used_uf_header_entries; + u32 used_uf_buffer_bytes; + u32 unused_uf_header_bytes; + u32 used_uf_header_bytes; + dma_addr_t uf_buffer_phys_address; + + /* + * Prepare all of the memory sizes for the UF headers, UF address + * table, and UF buffers themselves. */ + used_uf_buffer_bytes = uf_control->buffers.count + * SCU_UNSOLICITED_FRAME_BUFFER_SIZE; + unused_uf_header_entries = uf_control->address_table.count + - uf_control->buffers.count; + used_uf_header_entries = uf_control->buffers.count; + unused_uf_header_bytes = unused_uf_header_entries + * sizeof(struct scu_unsolicited_frame_header); + used_uf_header_bytes = used_uf_header_entries + * sizeof(struct scu_unsolicited_frame_header); + + /* + * The Unsolicited Frame buffers are set at the start of the UF + * memory descriptor entry. The headers and address table will be + * placed after the buffers. */ + uf_buffer_phys_address = mde->physical_address; + + /* + * Program the location of the UF header table into the SCU. + * Notes: + * - The address must align on a 64-byte boundary. Guaranteed to be + * on 64-byte boundary already 1KB boundary for unsolicited frames. + * - Program unused header entries to overlap with the last + * unsolicited frame. The silicon will never DMA to these unused + * headers, since we program the UF address table pointers to + * NULL. */ + uf_control->headers.physical_address = uf_buffer_phys_address; + sci_physical_address_add( + uf_control->headers.physical_address, used_uf_buffer_bytes); + sci_physical_address_subtract( + uf_control->headers.physical_address, unused_uf_header_bytes); + uf_control->headers.array + = (struct scu_unsolicited_frame_header *) + scic_cb_get_virtual_address( + controller, uf_control->headers.physical_address + ); + + /* + * Program the location of the UF address table into the SCU. + * Notes: + * - The address must align on a 64-bit boundary. Guaranteed to be on 64 + * byte boundary already due to above programming headers being on a + * 64-bit boundary and headers are on a 64-bytes in size. */ + uf_control->address_table.physical_address = uf_buffer_phys_address; + sci_physical_address_add( + uf_control->address_table.physical_address, used_uf_buffer_bytes); + sci_physical_address_add( + uf_control->address_table.physical_address, used_uf_header_bytes); + uf_control->address_table.array + = (dma_addr_t *) + scic_cb_get_virtual_address( + controller, uf_control->address_table.physical_address + ); + + uf_control->get = 0; + + /* + * UF buffer requirements are: + * - The last entry in the UF queue is not NULL. + * - There is a power of 2 number of entries (NULL or not-NULL) + * programmed into the queue. + * - Aligned on a 1KB boundary. */ + + /* + * If the user provided less then the maximum amount of memory, + * then be sure that we programm the first entries in the UF + * address table to NULL. */ + scic_sds_unsolicited_frame_control_construct_frames( + uf_control, + uf_buffer_phys_address, + (unsigned long)mde->virtual_address, + unused_uf_header_entries, + used_uf_header_entries + ); +} + +/** + * This method returns the frame header for the specified frame index. + * @uf_control: + * @frame_index: + * @frame_header: + * + * enum sci_status + */ +enum sci_status scic_sds_unsolicited_frame_control_get_header( + struct scic_sds_unsolicited_frame_control *uf_control, + u32 frame_index, + void **frame_header) +{ + if (frame_index < uf_control->address_table.count) { + /* + * Skip the first word in the frame since this is a controll word used + * by the hardware. */ + *frame_header = &uf_control->buffers.array[frame_index].header->data; + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_PARAMETER_VALUE; +} + +/** + * This method returns the frame buffer for the specified frame index. + * @uf_control: + * @frame_index: + * @frame_buffer: + * + * enum sci_status + */ +enum sci_status scic_sds_unsolicited_frame_control_get_buffer( + struct scic_sds_unsolicited_frame_control *uf_control, + u32 frame_index, + void **frame_buffer) +{ + if (frame_index < uf_control->address_table.count) { + *frame_buffer = uf_control->buffers.array[frame_index].buffer; + + return SCI_SUCCESS; + } + + return SCI_FAILURE_INVALID_PARAMETER_VALUE; +} + +/** + * This method releases the frame once this is done the frame is available for + * re-use by the hardware. The data contained in the frame header and frame + * buffer is no longer valid. + * @uf_control: This parameter specifies the UF control object + * @frame_index: This parameter specifies the frame index to attempt to release. + * + * This method returns an indication to the caller as to whether the + * unsolicited frame get pointer should be updated. true This value indicates + * the unsolicited frame get pointer should be updated (i.e. write + * SCU_UFQGP_WRITE). false This value indicates the get pointer should not be + * updated. + */ +bool scic_sds_unsolicited_frame_control_release_frame( + struct scic_sds_unsolicited_frame_control *uf_control, + u32 frame_index) +{ + u32 frame_get; + u32 frame_cycle; + + frame_get = uf_control->get & (uf_control->address_table.count - 1); + frame_cycle = uf_control->get & uf_control->address_table.count; + + /* + * In the event there are NULL entries in the UF table, we need to + * advance the get pointer in order to find out if this frame should + * be released (i.e. update the get pointer). */ + while (((lower_32_bits(uf_control->address_table.array[frame_get]) + == 0) && + (upper_32_bits(uf_control->address_table.array[frame_get]) + == 0)) && + (frame_get < uf_control->address_table.count)) + frame_get++; + + /* + * The table has a NULL entry as it's last element. This is + * illegal. */ + BUG_ON(frame_get >= uf_control->address_table.count); + + if (frame_index < uf_control->address_table.count) { + uf_control->buffers.array[frame_index].state = UNSOLICITED_FRAME_RELEASED; + + /* + * The frame index is equal to the current get pointer so we + * can now free up all of the frame entries that */ + if (frame_get == frame_index) { + while ( + uf_control->buffers.array[frame_get].state + == UNSOLICITED_FRAME_RELEASED + ) { + uf_control->buffers.array[frame_get].state = UNSOLICITED_FRAME_EMPTY; + + INCREMENT_QUEUE_GET( + frame_get, + frame_cycle, + uf_control->address_table.count - 1, + uf_control->address_table.count + ); + } + + uf_control->get = + (SCU_UFQGP_GEN_BIT(ENABLE_BIT) | frame_cycle | frame_get); + + return true; + } else { + /* + * Frames remain in use until we advance the get pointer + * so there is nothing we can do here */ + } + } + + return false; +} + diff --git a/drivers/scsi/isci/core/scic_sds_unsolicited_frame_control.h b/drivers/scsi/isci/core/scic_sds_unsolicited_frame_control.h new file mode 100644 index 000000000000..49db83faac37 --- /dev/null +++ b/drivers/scsi/isci/core/scic_sds_unsolicited_frame_control.h @@ -0,0 +1,286 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains all of the unsolicited frame related management for the + * address table, the headers, and actual payload buffers. + * + * + */ + +#ifndef _SCIC_SDS_UNSOLICITED_FRAME_CONTROL_H_ +#define _SCIC_SDS_UNSOLICITED_FRAME_CONTROL_H_ + +#include "scu_unsolicited_frame.h" +#include "sci_memory_descriptor_list.h" +#include "scu_constants.h" +#include "sci_status.h" + +/** + * enum UNSOLICITED_FRAME_STATE - + * + * This enumeration represents the current unsolicited frame state. The + * controller object can not updtate the hardware unsolicited frame put pointer + * unless it has already processed the priror unsolicited frames. + */ +enum UNSOLICITED_FRAME_STATE { + /** + * This state is when the frame is empty and not in use. It is + * different from the released state in that the hardware could DMA + * data to this frame buffer. + */ + UNSOLICITED_FRAME_EMPTY, + + /** + * This state is set when the frame buffer is in use by by some + * object in the system. + */ + UNSOLICITED_FRAME_IN_USE, + + /** + * This state is set when the frame is returned to the free pool + * but one or more frames prior to this one are still in use. + * Once all of the frame before this one are freed it will go to + * the empty state. + */ + UNSOLICITED_FRAME_RELEASED, + + UNSOLICITED_FRAME_MAX_STATES +}; + +/** + * struct scic_sds_unsolicited_frame - + * + * This is the unsolicited frame data structure it acts as the container for + * the current frame state, frame header and frame buffer. + */ +struct scic_sds_unsolicited_frame { + /** + * This field contains the current frame state + */ + enum UNSOLICITED_FRAME_STATE state; + + /** + * This field points to the frame header data. + */ + struct scu_unsolicited_frame_header *header; + + /** + * This field points to the frame buffer data. + */ + void *buffer; + +}; + +/** + * struct scic_sds_uf_header_array - + * + * This structure contains all of the unsolicited frame header information. + */ +struct scic_sds_uf_header_array { + /** + * This field is represents a virtual pointer to the start + * address of the UF address table. The table contains + * 64-bit pointers as required by the hardware. + */ + struct scu_unsolicited_frame_header *array; + + /** + * This field specifies the physical address location for the UF + * buffer array. + */ + dma_addr_t physical_address; + +}; + +/* + * Determine the size of the unsolicited frame array including + * unused buffers. */ +#if SCU_UNSOLICITED_FRAME_COUNT <= SCU_MIN_UF_TABLE_ENTRIES +#define SCU_UNSOLICITED_FRAME_CONTROL_ARRAY_SIZE SCU_MIN_UF_TABLE_ENTRIES +#else +#define SCU_UNSOLICITED_FRAME_CONTROL_ARRAY_SIZE SCU_MAX_UNSOLICITED_FRAMES +#endif /* SCU_UNSOLICITED_FRAME_COUNT <= SCU_MIN_UF_TABLE_ENTRIES */ + +/** + * struct scic_sds_uf_buffer_array - + * + * This structure contains all of the unsolicited frame buffer (actual payload) + * information. + */ +struct scic_sds_uf_buffer_array { + /** + * This field is the minimum number of unsolicited frames supported by the + * hardware and the number of unsolicited frames requested by the software. + */ + u32 count; + + /** + * This field is the SCIC_UNSOLICITED_FRAME data its used to manage + * the data for the unsolicited frame requests. It also represents + * the virtual address location that corresponds to the + * physical_address field. + */ + struct scic_sds_unsolicited_frame array[SCU_UNSOLICITED_FRAME_CONTROL_ARRAY_SIZE]; + + /** + * This field specifies the physical address location for the UF + * buffer array. + */ + dma_addr_t physical_address; + +}; + +/** + * struct scic_sds_uf_address_table_array - + * + * This object maintains all of the unsolicited frame address table specific + * data. The address table is a collection of 64-bit pointers that point to + * 1KB buffers into which the silicon will DMA unsolicited frames. + */ +struct scic_sds_uf_address_table_array { + /** + * This field specifies the actual programmed size of the + * unsolicited frame buffer address table. The size of the table + * can be larger than the actual number of UF buffers, but it must + * be a power of 2 and the last entry in the table is not allowed + * to be NULL. + */ + u32 count; + + /** + * This field represents a virtual pointer that refers to the + * starting address of the UF address table. + * 64-bit pointers are required by the hardware. + */ + dma_addr_t *array; + + /** + * This field specifies the physical address location for the UF + * address table. + */ + dma_addr_t physical_address; + +}; + +/** + * struct scic_sds_unsolicited_frame_control - + * + * This object contains all of the data necessary to handle unsolicited frames. + */ +struct scic_sds_unsolicited_frame_control { + /** + * This field is the software copy of the unsolicited frame queue + * get pointer. The controller object writes this value to the + * hardware to let the hardware put more unsolicited frame entries. + */ + u32 get; + + /** + * This field contains all of the unsolicited frame header + * specific fields. + */ + struct scic_sds_uf_header_array headers; + + /** + * This field contains all of the unsolicited frame buffer + * specific fields. + */ + struct scic_sds_uf_buffer_array buffers; + + /** + * This field contains all of the unsolicited frame address table + * specific fields. + */ + struct scic_sds_uf_address_table_array address_table; + +}; + +void scic_sds_unsolicited_frame_control_set_address_table_count( + struct scic_sds_unsolicited_frame_control *uf_control); + +struct scic_sds_controller; +void scic_sds_unsolicited_frame_control_construct( + struct scic_sds_unsolicited_frame_control *uf_control, + struct sci_physical_memory_descriptor *mde, + struct scic_sds_controller *this_controller); + +enum sci_status scic_sds_unsolicited_frame_control_get_header( + struct scic_sds_unsolicited_frame_control *uf_control, + u32 frame_index, + void **frame_header); + +enum sci_status scic_sds_unsolicited_frame_control_get_buffer( + struct scic_sds_unsolicited_frame_control *uf_control, + u32 frame_index, + void **frame_buffer); + +bool scic_sds_unsolicited_frame_control_release_frame( + struct scic_sds_unsolicited_frame_control *uf_control, + u32 frame_index); + +/** + * scic_sds_unsolicited_frame_control_get_mde_size() - + * + * This macro simply calculates the size of the memory descriptor entry that + * relates to unsolicited frames and the surrounding silicon memory required to + * utilize it. + */ +#define scic_sds_unsolicited_frame_control_get_mde_size(uf_control) \ + (((uf_control).buffers.count * SCU_UNSOLICITED_FRAME_BUFFER_SIZE) \ + + ((uf_control).address_table.count * sizeof(dma_addr_t)) \ + + ((uf_control).buffers.count * sizeof(struct scu_unsolicited_frame_header))) + +#endif /* _SCIC_SDS_UNSOLICITED_FRAME_CONTROL_H_ */ diff --git a/drivers/scsi/isci/core/scic_task_request.h b/drivers/scsi/isci/core/scic_task_request.h new file mode 100644 index 000000000000..ef76cb6bdbea --- /dev/null +++ b/drivers/scsi/isci/core/scic_task_request.h @@ -0,0 +1,148 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_TASK_REQUEST_H_ +#define _SCIC_TASK_REQUEST_H_ + +/** + * This file contains the structures and interface methods that can be + * referenced and used by the SCI user for to utilize task management + * requests. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" + +struct scic_sds_request; +struct scic_sds_remote_device; +struct scic_sds_controller; + + +/** + * scic_task_request_construct() - This method is called by the SCI user to + * construct all SCI Core task management requests, regardless of protocol. + * Memory initialization and functionality common to all task request types + * is performed in this method. + * @scic_controller: the handle to the core controller object for which to + * build the task managmement request. + * @scic_remote_device: the handle to the core remote device object for which + * to build the task management request. passed, then a copy of the request + * is built internally. The request will be copied into the actual + * controller request memory when the task is allocated internally during + * the scic_controller_start_task() method. + * @io_tag: This parameter specifies the IO tag to be associated with this + * request. If SCI_CONTROLLER_INVALID_IO_TAG is passed, then a copy of the + * request is built internally. The request will be copied into the actual + * controller request memory when the IO tag is allocated internally during + * the scic_controller_start_io() method. + * @user_task_request_object: This parameter specifies the user task request to + * be utilized during construction. This task pointer will become the + * associated object for the core task request object. + * @scic_task_request_memory: This parameter specifies the memory location to + * be utilized when building the core request. + * @new_scic_task_request_handle: This parameter specifies a pointer to the + * handle the core will expect in further interactions with the core task + * request object. + * + * The SCI core implementation will create an association between the user task + * request object and the core task request object. Indicate if the controller + * successfully built the task request. SCI_SUCCESS This value is returned if + * the task request was successfully built. + */ +enum sci_status scic_task_request_construct( + struct scic_sds_controller *scic_controller, + struct scic_sds_remote_device *scic_remote_device, + u16 io_tag, + void *user_task_request_object, + void *scic_task_request_memory, + struct scic_sds_request **new_scic_task_request_handle); + +/** + * scic_task_request_construct_ssp() - This method is called by the SCI user to + * construct all SCI Core SSP task management requests. Memory + * initialization and functionality common to all task request types is + * performed in this method. + * @scic_task_request: This parameter specifies the handle to the core task + * request object for which to construct a SATA specific task management + * request. + * + * Indicate if the controller successfully built the task request. SCI_SUCCESS + * This value is returned if the task request was successfully built. + */ +enum sci_status scic_task_request_construct_ssp( + struct scic_sds_request *scic_task_request); + +/** + * scic_task_request_construct_sata() - This method is called by the SCI user + * to construct all SCI Core SATA task management requests. Memory + * initialization and functionality common to all task request types is + * performed in this method. + * @scic_task_request_handle: This parameter specifies the handle to the core + * task request object for which to construct a SATA specific task + * management request. + * + * Indicate if the controller successfully built the task request. SCI_SUCCESS + * This value is returned if the task request was successfully built. + */ +enum sci_status scic_task_request_construct_sata( + struct scic_sds_request *scic_task_request_handle); + + + +#endif /* _SCIC_TASK_REQUEST_H_ */ + diff --git a/drivers/scsi/isci/core/scic_user_callback.h b/drivers/scsi/isci/core/scic_user_callback.h new file mode 100644 index 000000000000..6eca5a96649e --- /dev/null +++ b/drivers/scsi/isci/core/scic_user_callback.h @@ -0,0 +1,740 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCIC_USER_CALLBACK_H_ +#define _SCIC_USER_CALLBACK_H_ + +/** + * This file contains all of the interface methods/macros that must be + * implemented by an SCI Core user. + * + * + */ + + +#include "sci_types.h" +#include "sci_status.h" + +struct scic_sds_request; +struct scic_sds_phy; +struct scic_sds_port; +struct scic_sds_remote_device; +struct scic_sds_controller; + +/** + * scic_cb_timer_create() - This callback method asks the user to create a + * timer and provide a handle for this timer for use in further timer + * interactions. + * @controller: This parameter specifies the controller with which this timer + * is to be associated. + * @timer_callback: This parameter specifies the callback method to be invoked + * whenever the timer expires. + * @cookie: This parameter specifies a piece of information that the user must + * retain. This cookie is to be supplied by the user anytime a timeout + * occurs for the created timer. + * + * The "timer_callback" method should be executed in a mutually exlusive manner + * from the controller completion handler handler (refer to + * scic_controller_get_handler_methods()). This method returns a handle to a + * timer object created by the user. The handle will be utilized for all + * further interactions relating to this timer. + */ +void *scic_cb_timer_create( + struct scic_sds_controller *controller, + void (*timer_callback)(void *), + void *cookie); + + +/** + * scic_cb_timer_start() - This callback method asks the user to start the + * supplied timer. + * @controller: This parameter specifies the controller with which this timer + * is to associated. + * @timer: This parameter specifies the timer to be started. + * @milliseconds: This parameter specifies the number of milliseconds for which + * to stall. The operating system driver is allowed to round this value up + * where necessary. + * + * All timers in the system started by the SCI Core are one shot timers. + * Therefore, the SCI user should make sure that it removes the timer from it's + * list when a timer actually fires. Additionally, SCI Core user's should be + * able to handle calls from the SCI Core to stop a timer that may already be + * stopped. none + */ +void scic_cb_timer_start( + struct scic_sds_controller *controller, + void *timer, + u32 milliseconds); + +/** + * scic_cb_timer_stop() - This callback method asks the user to stop the + * supplied timer. + * @controller: This parameter specifies the controller with which this timer + * is to associated. + * @timer: This parameter specifies the timer to be stopped. + * + */ +void scic_cb_timer_stop( + struct scic_sds_controller *controller, + void *timer); + +/** + * scic_cb_stall_execution() - This method is called when the core requires the + * OS driver to stall execution. This method is utilized during + * initialization or non-performance paths only. + * @microseconds: This parameter specifies the number of microseconds for which + * to stall. The operating system driver is allowed to round this value up + * where necessary. + * + * none. + */ +void scic_cb_stall_execution( + u32 microseconds); + +/** + * scic_cb_controller_start_complete() - This user callback will inform the + * user that the controller has finished the start process. + * @controller: This parameter specifies the controller that was started. + * @completion_status: This parameter specifies the results of the start + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_controller_start_complete( + struct scic_sds_controller *controller, + enum sci_status completion_status); + +/** + * scic_cb_controller_stop_complete() - This user callback will inform the user + * that the controller has finished the stop process. + * @controller: This parameter specifies the controller that was stopped. + * @completion_status: This parameter specifies the results of the stop + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_controller_stop_complete( + struct scic_sds_controller *controller, + enum sci_status completion_status); + +/** + * scic_cb_io_request_complete() - This user callback will inform the user that + * an IO request has completed. + * @controller: This parameter specifies the controller on which the IO is + * completing. + * @remote_device: This parameter specifies the remote device on which this IO + * request is completing. + * @io_request: This parameter specifies the IO request that has completed. + * @completion_status: This parameter specifies the results of the IO request + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_io_request_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *io_request, + enum sci_io_status completion_status); + +/** + * scic_cb_task_request_complete() - This user callback will inform the user + * that a task management request completed. + * @controller: This parameter specifies the controller on which the task + * management request is completing. + * @remote_device: This parameter specifies the remote device on which this + * task management request is completing. + * @task_request: This parameter specifies the task management request that has + * completed. + * @completion_status: This parameter specifies the results of the IO request + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_task_request_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *task_request, + enum sci_task_status completion_status); + +#ifndef SCI_GET_PHYSICAL_ADDRESS_OPTIMIZATION_ENABLED +/** + * scic_cb_io_request_get_physical_address() - This callback method asks the + * user to provide the physical address for the supplied virtual address + * when building an io request object. + * @controller: This parameter is the core controller object handle. + * @io_request: This parameter is the io request object handle for which the + * physical address is being requested. + * @virtual_address: This paramter is the virtual address which is to be + * returned as a physical address. + * @physical_address: The physical address for the supplied virtual address. + * + * None. + */ +void scic_cb_io_request_get_physical_address( + struct scic_sds_controller *controller, + struct scic_sds_request *io_request, + void *virtual_address, + dma_addr_t *physical_address); +#endif /* SCI_GET_PHYSICAL_ADDRESS_OPTIMIZATION_ENABLED */ + +/** + * scic_cb_io_request_get_transfer_length() - This callback method asks the + * user to provide the number of bytes to be transfered as part of this + * request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the number of payload data bytes to be transfered for + * this IO request. + */ +u32 scic_cb_io_request_get_transfer_length( + void *scic_user_io_request); + +/** + * scic_cb_io_request_get_data_direction() - This callback method asks the user + * to provide the data direction for this request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the value of SCI_IO_REQUEST_DATA_OUT or + * SCI_IO_REQUEST_DATA_IN, or SCI_IO_REQUEST_NO_DATA. + */ +SCI_IO_REQUEST_DATA_DIRECTION scic_cb_io_request_get_data_direction( + void *scic_user_io_request); + +#ifndef SCI_SGL_OPTIMIZATION_ENABLED +/** + * scic_cb_io_request_get_next_sge() - This callback method asks the user to + * provide the address to where the next Scatter-Gather Element is located. + * Details regarding usage: - Regarding the first SGE: the user should + * initialize an index, or a pointer, prior to construction of the request + * that will reference the very first scatter-gather element. This is + * important since this method is called for every scatter-gather element, + * including the first element. - Regarding the last SGE: the user should + * return NULL from this method when this method is called and the SGL has + * exhausted all elements. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * @current_sge_address: This parameter specifies the address for the current + * SGE (i.e. the one that has just processed). + * @next_sge: An address specifying the location for the next scatter gather + * element to be processed. + * + * None + */ +void scic_cb_io_request_get_next_sge( + void *scic_user_io_request, + void *current_sge_address, + void **next_sge); +#endif /* SCI_SGL_OPTIMIZATION_ENABLED */ + +/** + * scic_cb_sge_get_address_field() - This callback method asks the user to + * provide the contents of the "address" field in the Scatter-Gather Element. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * @sge_address: This parameter specifies the address for the SGE from which to + * retrieve the address field. + * + * A physical address specifying the contents of the SGE's address field. + */ +dma_addr_t scic_cb_sge_get_address_field( + void *scic_user_io_request, + void *sge_address); + +/** + * scic_cb_sge_get_length_field() - This callback method asks the user to + * provide the contents of the "length" field in the Scatter-Gather Element. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * @sge_address: This parameter specifies the address for the SGE from which to + * retrieve the address field. + * + * This method returns the length field specified inside the SGE referenced by + * the sge_address parameter. + */ +u32 scic_cb_sge_get_length_field( + void *scic_user_io_request, + void *sge_address); + +/** + * scic_cb_ssp_io_request_get_cdb_address() - This callback method asks the + * user to provide the address for the command descriptor block (CDB) + * associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the virtual address of the CDB. + */ +void *scic_cb_ssp_io_request_get_cdb_address( + void *scic_user_io_request); + +/** + * scic_cb_ssp_io_request_get_cdb_length() - This callback method asks the user + * to provide the length of the command descriptor block (CDB) associated + * with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the length of the CDB. + */ +u32 scic_cb_ssp_io_request_get_cdb_length( + void *scic_user_io_request); + +/** + * scic_cb_ssp_io_request_get_lun() - This callback method asks the user to + * provide the Logical Unit (LUN) associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * The contents of the value returned from this callback are defined by the + * protocol standard (e.g. T10 SAS specification). Please refer to the + * transport command information unit description in the associated standard. + * This method returns the LUN associated with this request. This should be u64? + */ +u32 scic_cb_ssp_io_request_get_lun( + void *scic_user_io_request); + +/** + * scic_cb_ssp_io_request_get_task_attribute() - This callback method asks the + * user to provide the task attribute associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * The contents of the value returned from this callback are defined by the + * protocol standard (e.g. T10 SAS specification). Please refer to the + * transport command information unit description in the associated standard. + * This method returns the task attribute associated with this IO request. + */ +u32 scic_cb_ssp_io_request_get_task_attribute( + void *scic_user_io_request); + +/** + * scic_cb_ssp_io_request_get_command_priority() - This callback method asks + * the user to provide the command priority associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * The contents of the value returned from this callback are defined by the + * protocol standard (e.g. T10 SAS specification). Please refer to the + * transport command information unit description in the associated standard. + * This method returns the command priority associated with this IO request. + */ +u32 scic_cb_ssp_io_request_get_command_priority( + void *scic_user_io_request); + +/** + * scic_cb_io_request_do_copy_rx_frames() - This callback method asks the user + * if the received RX frame data is to be copied to the SGL or should be + * stored by the SCI core to be retrieved later with the + * scic_io_request_get_rx_frame(). + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns true if the SCI core should copy the received frame data + * to the SGL location or false if the SCI user wants to retrieve the frame + * data at a later time. + */ +bool scic_cb_io_request_do_copy_rx_frames( + void *scic_user_io_request); + +/** + * scic_cb_request_get_sat_protocol() - This callback method asks the user to + * return the SAT protocol definition for this IO request. This method is + * only called by the SCI core if the request type constructed is SATA. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns one of the sat.h defined protocols for the given io + * request. + */ +u8 scic_cb_request_get_sat_protocol( + void *scic_user_io_request); + + +/** + * scic_cb_ssp_task_request_get_lun() - This method returns the Logical Unit to + * be utilized for this task management request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * The contents of the value returned from this callback are defined by the + * protocol standard (e.g. T10 SAS specification). Please refer to the + * transport task information unit description in the associated standard. This + * method returns the LUN associated with this request. This should be u64? + */ +u32 scic_cb_ssp_task_request_get_lun( + void *scic_user_task_request); + +/** + * scic_cb_ssp_task_request_get_function() - This method returns the task + * management function to be utilized for this task request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * The contents of the value returned from this callback are defined by the + * protocol standard (e.g. T10 SAS specification). Please refer to the + * transport task information unit description in the associated standard. This + * method returns an unsigned byte representing the task management function to + * be performed. + */ +u8 scic_cb_ssp_task_request_get_function( + void *scic_user_task_request); + +/** + * scic_cb_ssp_task_request_get_io_tag_to_manage() - This method returns the + * task management IO tag to be managed. Depending upon the task management + * function the value returned from this method may be ignored. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns an unsigned 16-bit word depicting the IO tag to be + * managed. + */ +u16 scic_cb_ssp_task_request_get_io_tag_to_manage( + void *scic_user_task_request); + +/** + * scic_cb_ssp_task_request_get_response_data_address() - This callback method + * asks the user to provide the virtual address of the response data buffer + * for the supplied IO request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the virtual address for the response data buffer + * associated with this IO request. + */ +void *scic_cb_ssp_task_request_get_response_data_address( + void *scic_user_task_request); + +/** + * scic_cb_ssp_task_request_get_response_data_length() - This callback method + * asks the user to provide the length of the response data buffer for the + * supplied IO request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the length of the response buffer data associated with + * this IO request. + */ +u32 scic_cb_ssp_task_request_get_response_data_length( + void *scic_user_task_request); + +/** + * scic_cb_pci_get_bar() - In this method the user must return the base address + * register (BAR) value for the supplied base address register number. + * @controller: The controller for which to retrieve the bar number. + * @bar_number: This parameter depicts the BAR index/number to be read. + * + * Return a pointer value indicating the contents of the BAR. NULL indicates an + * invalid BAR index/number was specified. All other values indicate a valid + * VIRTUAL address from the BAR. + */ +void *scic_cb_pci_get_bar( + struct scic_sds_controller *controller, + u16 bar_number); + +/** + * scic_cb_get_virtual_address() - This callback method asks the user to + * provide the virtual address for the supplied physical address. + * @controller: This parameter is the core controller object handle. + * @physical_address: This parameter is the physical address which is to be + * returned as a virtual address. + * + * The method returns the virtual address for the supplied physical address. + */ +void *scic_cb_get_virtual_address( + struct scic_sds_controller *controller, + dma_addr_t physical_address); + +/** + * scic_cb_port_stop_complete() - This method informs the user when a stop + * operation on the port has completed. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @completion_status: This parameter specifies the status for the operation + * being completed. + * + */ +void scic_cb_port_stop_complete( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + enum sci_status completion_status); + +/** + * scic_cb_port_hard_reset_complete() - This method informs the user when a + * hard reset on the port has completed. This hard reset could have been + * initiated by the user or by the remote port. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @completion_status: This parameter specifies the status for the operation + * being completed. + * + */ +void scic_cb_port_hard_reset_complete( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + enum sci_status completion_status); + +/** + * scic_cb_port_ready() - This method informs the user that the port is now in + * a ready state and can be utilized to issue IOs. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * + */ +void scic_cb_port_ready( + struct scic_sds_controller *controller, + struct scic_sds_port *port); + +/** + * scic_cb_port_not_ready() - This method informs the user that the port is now + * not in a ready (i.e. busy) state and can't be utilized to issue IOs. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @reason_code: This parameter specifies the reason for the port not ready + * callback. + * + */ +void scic_cb_port_not_ready( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + u32 reason_code); + +/** + * scic_cb_port_invalid_link_up() - This method informs the SCI Core user that + * a phy/link became ready, but the phy is not allowed in the port. In some + * situations the underlying hardware only allows for certain phy to port + * mappings. If these mappings are violated, then this API is invoked. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @phy: This parameter specifies the phy that came ready, but the phy can't be + * a valid member of the port. + * + */ +void scic_cb_port_invalid_link_up( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy); + +/** + * scic_cb_port_bc_change_primitive_received() - This callback method informs + * the user that a broadcast change primitive was received. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. For instances where the phy on which the primitive was + * received is not part of a port, this parameter will be + * SCI_INVALID_HANDLE_T. + * @phy: This parameter specifies the phy on which the primitive was received. + * + */ +void scic_cb_port_bc_change_primitive_received( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy); + + + + +/** + * scic_cb_port_link_up() - This callback method informs the user that a phy + * has become operational and is capable of communicating with the remote + * end point. + * @controller: This parameter represents the controller associated with the + * phy. + * @port: This parameter specifies the port object for which the user callback + * is being invoked. There may be conditions where this parameter can be + * SCI_INVALID_HANDLE + * @phy: This parameter specifies the phy object for which the user callback is + * being invoked. + * + */ +void scic_cb_port_link_up( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy); + +/** + * scic_cb_port_link_down() - This callback method informs the user that a phy + * is no longer operational and is not capable of communicating with the + * remote end point. + * @controller: This parameter represents the controller associated with the + * phy. + * @port: This parameter specifies the port object for which the user callback + * is being invoked. There may be conditions where this parameter can be + * SCI_INVALID_HANDLE + * @phy: This parameter specifies the phy object for which the user callback is + * being invoked. + * + */ +void scic_cb_port_link_down( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy); + +/** + * scic_cb_remote_device_start_complete() - This user callback method will + * inform the user that a start operation has completed. + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the completion callback. + * @completion_status: This parameter specifies the completion status for the + * operation. + * + */ +void scic_cb_remote_device_start_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + enum sci_status completion_status); + +/** + * scic_cb_remote_device_stop_complete() - This user callback method will + * inform the user that a stop operation has completed. + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the completion callback. + * @completion_status: This parameter specifies the completion status for the + * operation. + * + */ +void scic_cb_remote_device_stop_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + enum sci_status completion_status); + +/** + * scic_cb_remote_device_ready() - This user callback method will inform the + * user that a remote device is now capable of handling IO requests. + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the callback. + * + */ +void scic_cb_remote_device_ready( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device); + +/** + * scic_cb_remote_device_not_ready() - This user callback method will inform + * the user that a remote device is no longer capable of handling IO + * requests (until a ready callback is invoked). + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the callback. + * @reason_code: This paramete specifies the reason the remote device is not + * ready. + * + */ +void scic_cb_remote_device_not_ready( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + u32 reason_code); + +#if !defined(DISABLE_ATAPI) +/** + * scic_cb_stp_packet_io_request_get_cdb_address() - This user callback gets + * from stp packet io's user request the CDB address. + * @scic_user_io_request: + * + * The cdb adress. + */ +void *scic_cb_stp_packet_io_request_get_cdb_address( + void *scic_user_io_request); + +/** + * scic_cb_stp_packet_io_request_get_cdb_length() - This user callback gets + * from stp packet io's user request the CDB length. + * @scic_user_io_request: + * + * The cdb length. + */ +u32 scic_cb_stp_packet_io_request_get_cdb_length( + void *scic_user_io_request); +#else /* !defined(DISABLE_ATAPI) */ +#define scic_cb_stp_packet_io_request_get_cdb_address(scic_user_io_request) NULL +#define scic_cb_stp_packet_io_request_get_cdb_length(scic_user_io_request) 0 +#endif /* !defined(DISABLE_ATAPI) */ + + +#endif /* _SCIC_USER_CALLBACK_H_ */ + diff --git a/drivers/scsi/isci/core/scu_completion_codes.h b/drivers/scsi/isci/core/scu_completion_codes.h new file mode 100644 index 000000000000..17ee4c8b9122 --- /dev/null +++ b/drivers/scsi/isci/core/scu_completion_codes.h @@ -0,0 +1,280 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCU_COMPLETION_CODES_HEADER_ +#define _SCU_COMPLETION_CODES_HEADER_ + +/** + * This file contains the constants and macros for the SCU hardware completion + * codes. + * + * + */ + +#define SCU_COMPLETION_TYPE_SHIFT 28 +#define SCU_COMPLETION_TYPE_MASK 0x70000000 + +/** + * SCU_COMPLETION_TYPE() - + * + * This macro constructs an SCU completion type + */ +#define SCU_COMPLETION_TYPE(type) \ + ((u32)(type) << SCU_COMPLETION_TYPE_SHIFT) + +/** + * SCU_COMPLETION_TYPE() - + * + * These macros contain the SCU completion types SCU_COMPLETION_TYPE + */ +#define SCU_COMPLETION_TYPE_TASK SCU_COMPLETION_TYPE(0) +#define SCU_COMPLETION_TYPE_SDMA SCU_COMPLETION_TYPE(1) +#define SCU_COMPLETION_TYPE_UFI SCU_COMPLETION_TYPE(2) +#define SCU_COMPLETION_TYPE_EVENT SCU_COMPLETION_TYPE(3) +#define SCU_COMPLETION_TYPE_NOTIFY SCU_COMPLETION_TYPE(4) + +/** + * + * + * These constants provide the shift and mask values for the various parts of + * an SCU completion code. + */ +#define SCU_COMPLETION_STATUS_MASK 0x0FFC0000 +#define SCU_COMPLETION_TL_STATUS_MASK 0x0FC00000 +#define SCU_COMPLETION_TL_STATUS_SHIFT 22 +#define SCU_COMPLETION_SDMA_STATUS_MASK 0x003C0000 +#define SCU_COMPLETION_PEG_MASK 0x00010000 +#define SCU_COMPLETION_PORT_MASK 0x00007000 +#define SCU_COMPLETION_PE_MASK SCU_COMPLETION_PORT_MASK +#define SCU_COMPLETION_PE_SHIFT 12 +#define SCU_COMPLETION_INDEX_MASK 0x00000FFF + +/** + * SCU_GET_COMPLETION_TYPE() - + * + * This macro returns the SCU completion type. + */ +#define SCU_GET_COMPLETION_TYPE(completion_code) \ + ((completion_code) & SCU_COMPLETION_TYPE_MASK) + +/** + * SCU_GET_COMPLETION_STATUS() - + * + * This macro returns the SCU completion status. + */ +#define SCU_GET_COMPLETION_STATUS(completion_code) \ + ((completion_code) & SCU_COMPLETION_STATUS_MASK) + +/** + * SCU_GET_COMPLETION_TL_STATUS() - + * + * This macro returns the transport layer completion status. + */ +#define SCU_GET_COMPLETION_TL_STATUS(completion_code) \ + ((completion_code) & SCU_COMPLETION_TL_STATUS_MASK) + +/** + * SCU_MAKE_COMPLETION_STATUS() - + * + * This macro takes a completion code and performs the shift and mask + * operations to turn it into a completion code that can be compared to a + * SCU_GET_COMPLETION_TL_STATUS. + */ +#define SCU_MAKE_COMPLETION_STATUS(completion_code) \ + ((u32)(completion_code) << SCU_COMPLETION_TL_STATUS_SHIFT) + +/** + * SCU_NORMALIZE_COMPLETION_STATUS() - + * + * This macro takes a SCU_GET_COMPLETION_TL_STATUS and normalizes it for a + * return code. + */ +#define SCU_NORMALIZE_COMPLETION_STATUS(completion_code) \ + (\ + ((completion_code) & SCU_COMPLETION_TL_STATUS_MASK) \ + >> SCU_COMPLETION_TL_STATUS_SHIFT \ + ) + +/** + * SCU_GET_COMPLETION_SDMA_STATUS() - + * + * This macro returns the SDMA completion status. + */ +#define SCU_GET_COMPLETION_SDMA_STATUS(completion_code) \ + ((completion_code) & SCU_COMPLETION_SDMA_STATUS_MASK) + +/** + * SCU_GET_COMPLETION_PEG() - + * + * This macro returns the Protocol Engine Group from the completion code. + */ +#define SCU_GET_COMPLETION_PEG(completion_code) \ + ((completion_code) & SCU_COMPLETION_PEG_MASK) + +/** + * SCU_GET_COMPLETION_PORT() - + * + * This macro reuturns the logical port index from the completion code. + */ +#define SCU_GET_COMPLETION_PORT(completion_code) \ + ((completion_code) & SCU_COMPLETION_PORT_MASK) + +/** + * SCU_GET_PROTOCOL_ENGINE_INDEX() - + * + * This macro returns the PE index from the completion code. + */ +#define SCU_GET_PROTOCOL_ENGINE_INDEX(completion_code) \ + (((completion_code) & SCU_COMPLETION_PE_MASK) >> SCU_COMPLETION_PE_SHIFT) + +/** + * SCU_GET_COMPLETION_INDEX() - + * + * This macro returns the index of the completion which is either a TCi or an + * RNi depending on the completion type. + */ +#define SCU_GET_COMPLETION_INDEX(completion_code) \ + ((completion_code) & SCU_COMPLETION_INDEX_MASK) + +#define SCU_UNSOLICITED_FRAME_MASK 0x0FFF0000 +#define SCU_UNSOLICITED_FRAME_SHIFT 16 + +/** + * SCU_GET_FRAME_INDEX() - + * + * This macro returns a normalized frame index from an unsolicited frame + * completion. + */ +#define SCU_GET_FRAME_INDEX(completion_code) \ + (\ + ((completion_code) & SCU_UNSOLICITED_FRAME_MASK) \ + >> SCU_UNSOLICITED_FRAME_SHIFT \ + ) + +#define SCU_UNSOLICITED_FRAME_ERROR_MASK 0x00008000 + +/** + * SCU_GET_FRAME_ERROR() - + * + * This macro returns a zero (0) value if there is no frame error otherwise it + * returns non-zero (!0). + */ +#define SCU_GET_FRAME_ERROR(completion_code) \ + ((completion_code) & SCU_UNSOLICITED_FRAME_ERROR_MASK) + +/** + * + * + * These constants represent normalized completion codes which must be shifted + * 18 bits to match it with the hardware completion code. In a 16-bit compiler, + * immediate constants are 16-bit values (the size of an int). If we shift + * those by 18 bits, we completely lose the value. To ensure the value is a + * 32-bit value like we want, each immediate value must be cast to a u32. + */ +#define SCU_TASK_DONE_GOOD ((u32)0x00) +#define SCU_TASK_DONE_CRC_ERR ((u32)0x14) +#define SCU_TASK_DONE_CHECK_RESPONSE ((u32)0x14) +#define SCU_TASK_DONE_GEN_RESPONSE ((u32)0x15) +#define SCU_TASK_DONE_NAK_CMD_ERR ((u32)0x16) +#define SCU_TASK_DONE_LL_R_ERR ((u32)0x17) +#define SCU_TASK_DONE_ACK_NAK_TO ((u32)0x17) +#define SCU_TASK_DONE_LL_PERR ((u32)0x18) +#define SCU_TASK_DONE_LL_SY_TERM ((u32)0x19) +#define SCU_TASK_DONE_NAK_ERR ((u32)0x19) +#define SCU_TASK_DONE_LL_LF_TERM ((u32)0x1A) +#define SCU_TASK_DONE_DATA_LEN_ERR ((u32)0x1A) +#define SCU_TASK_DONE_LL_CL_TERM ((u32)0x1B) +#define SCU_TASK_DONE_LL_ABORT_ERR ((u32)0x1B) +#define SCU_TASK_DONE_SEQ_INV_TYPE ((u32)0x1C) +#define SCU_TASK_DONE_UNEXP_XR ((u32)0x1C) +#define SCU_TASK_DONE_INV_FIS_TYPE ((u32)0x1D) +#define SCU_TASK_DONE_XR_IU_LEN_ERR ((u32)0x1D) +#define SCU_TASK_DONE_INV_FIS_LEN ((u32)0x1E) +#define SCU_TASK_DONE_XR_WD_LEN ((u32)0x1E) +#define SCU_TASK_DONE_SDMA_ERR ((u32)0x1F) +#define SCU_TASK_DONE_OFFSET_ERR ((u32)0x20) +#define SCU_TASK_DONE_MAX_PLD_ERR ((u32)0x21) +#define SCU_TASK_DONE_EXCESS_DATA ((u32)0x22) +#define SCU_TASK_DONE_LF_ERR ((u32)0x23) +#define SCU_TASK_DONE_UNEXP_FIS ((u32)0x24) +#define SCU_TASK_DONE_UNEXP_RESP ((u32)0x24) +#define SCU_TASK_DONE_EARLY_RESP ((u32)0x25) +#define SCU_TASK_DONE_SMP_RESP_TO_ERR ((u32)0x26) +#define SCU_TASK_DONE_DMASETUP_DIRERR ((u32)0x27) +#define SCU_TASK_DONE_SMP_UFI_ERR ((u32)0x27) +#define SCU_TASK_DONE_XFERCNT_ERR ((u32)0x28) +#define SCU_TASK_DONE_SMP_FRM_TYPE_ERR ((u32)0x28) +#define SCU_TASK_DONE_SMP_LL_RX_ERR ((u32)0x29) +#define SCU_TASK_DONE_RESP_LEN_ERR ((u32)0x2A) +#define SCU_TASK_DONE_UNEXP_DATA ((u32)0x2B) +#define SCU_TASK_DONE_OPEN_FAIL ((u32)0x2C) +#define SCU_TASK_DONE_UNEXP_SDBFIS ((u32)0x2D) +#define SCU_TASK_DONE_REG_ERR ((u32)0x2E) +#define SCU_TASK_DONE_SDB_ERR ((u32)0x2F) +#define SCU_TASK_DONE_TASK_ABORT ((u32)0x30) +#define SCU_TASK_OPEN_REJECT_WRONG_DESTINATION ((u32)0x34) +#define SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1 ((u32)0x35) +#define SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2 ((u32)0x36) +#define SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3 ((u32)0x37) +#define SCU_TASK_OPEN_REJECT_BAD_DESTINATION ((u32)0x38) +#define SCU_TASK_OPEN_REJECT_ZONE_VIOLATION ((u32)0x39) +#define SCU_TASK_DONE_VIIT_ENTRY_NV ((u32)0x3A) +#define SCU_TASK_DONE_IIT_ENTRY_NV ((u32)0x3B) +#define SCU_TASK_DONE_RNCNV_OUTBOUND ((u32)0x3C) +#define SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY ((u32)0x3D) +#define SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED ((u32)0x3E) +#define SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED ((u32)0x3F) + +#endif /* _SCU_COMPLETION_CODES_HEADER_ */ diff --git a/drivers/scsi/isci/core/scu_constants.h b/drivers/scsi/isci/core/scu_constants.h new file mode 100644 index 000000000000..a99d1103ad3b --- /dev/null +++ b/drivers/scsi/isci/core/scu_constants.h @@ -0,0 +1,151 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCU_CONSTANTS_H_ +#define _SCU_CONSTANTS_H_ + +/** + * This file contains the SCU hardware constants. + * + * + */ + +#include "sci_controller_constants.h" + +/** + * + * + * 2 indicates the maximum number of UFs that can occur for a given IO request. + * The hardware handles reception of additional unsolicited frames while all + * UFs are in use, by holding off the transmitting device. This number could + * be theoretically reduced to 1, but 2 provides for more reliable operation. + * During SATA PIO operation, it is possible under some conditions for there to + * be 3 separate FISes received, back to back to back (PIO Setup, Data, D2H + * Register). It is unlikely to have all 3 pending all at once without some of + * them already being processed. + */ +#define SCU_MIN_UNSOLICITED_FRAMES (1) +#define SCU_MIN_CRITICAL_NOTIFICATIONS (24) +#define SCU_MIN_EVENTS (4) +#define SCU_MIN_COMPLETION_QUEUE_SCRATCH (2) +#define SCU_MIN_COMPLETION_QUEUE_ENTRIES (SCU_MIN_CRITICAL_NOTIFICATIONS \ + + SCU_MIN_EVENTS \ + + SCU_MIN_UNSOLICITED_FRAMES \ + + SCI_MIN_IO_REQUESTS \ + + SCU_MIN_COMPLETION_QUEUE_SCRATCH) + +#define SCU_MAX_CRITICAL_NOTIFICATIONS (384) +#define SCU_MAX_EVENTS (128) +#define SCU_MAX_UNSOLICITED_FRAMES (128) +#define SCU_MAX_COMPLETION_QUEUE_SCRATCH (128) +#define SCU_MAX_COMPLETION_QUEUE_ENTRIES (SCU_MAX_CRITICAL_NOTIFICATIONS \ + + SCU_MAX_EVENTS \ + + SCU_MAX_UNSOLICITED_FRAMES \ + + SCI_MAX_IO_REQUESTS \ + + SCU_MAX_COMPLETION_QUEUE_SCRATCH) + +#if !defined(ENABLE_MINIMUM_MEMORY_MODE) +#define SCU_UNSOLICITED_FRAME_COUNT SCU_MAX_UNSOLICITED_FRAMES +#define SCU_CRITICAL_NOTIFICATION_COUNT SCU_MAX_CRITICAL_NOTIFICATIONS +#define SCU_EVENT_COUNT SCU_MAX_EVENTS +#define SCU_COMPLETION_QUEUE_SCRATCH SCU_MAX_COMPLETION_QUEUE_SCRATCH +#define SCU_IO_REQUEST_COUNT SCI_MAX_IO_REQUESTS +#define SCU_IO_REQUEST_SGE_COUNT SCI_MAX_SCATTER_GATHER_ELEMENTS +#define SCU_COMPLETION_QUEUE_COUNT SCU_MAX_COMPLETION_QUEUE_ENTRIES +#else +#define SCU_UNSOLICITED_FRAME_COUNT SCU_MIN_UNSOLICITED_FRAMES +#define SCU_CRITICAL_NOTIFICATION_COUNT SCU_MIN_CRITICAL_NOTIFICATIONS +#define SCU_EVENT_COUNT SCU_MIN_EVENTS +#define SCU_COMPLETION_QUEUE_SCRATCH SCU_MIN_COMPLETION_QUEUE_SCRATCH +#define SCU_IO_REQUEST_COUNT SCI_MIN_IO_REQUESTS +#define SCU_IO_REQUEST_SGE_COUNT SCI_MIN_SCATTER_GATHER_ELEMENTS +#define SCU_COMPLETION_QUEUE_COUNT SCU_MIN_COMPLETION_QUEUE_ENTRIES +#endif /* !defined(ENABLE_MINIMUM_MEMORY_OPERATION) */ + +/** + * + * + * The SCU_COMPLETION_QUEUE_COUNT constant indicates the size of the completion + * queue into which the hardware DMAs 32-bit quantas (completion entries). + */ + +/** + * + * + * This queue must be programmed to a power of 2 size (e.g. 32, 64, 1024, etc.). + */ +#if (SCU_COMPLETION_QUEUE_COUNT != 16) && \ + (SCU_COMPLETION_QUEUE_COUNT != 32) && \ + (SCU_COMPLETION_QUEUE_COUNT != 64) && \ + (SCU_COMPLETION_QUEUE_COUNT != 128) && \ + (SCU_COMPLETION_QUEUE_COUNT != 256) && \ + (SCU_COMPLETION_QUEUE_COUNT != 512) && \ + (SCU_COMPLETION_QUEUE_COUNT != 1024) +#error "SCU_COMPLETION_QUEUE_COUNT must be set to a power of 2." +#endif + +#if SCU_MIN_UNSOLICITED_FRAMES > SCU_MAX_UNSOLICITED_FRAMES +#error "Invalid configuration of unsolicited frame constants" +#endif /* SCU_MIN_UNSOLICITED_FRAMES > SCU_MAX_UNSOLICITED_FRAMES */ + +#define SCU_MIN_UF_TABLE_ENTRIES (8) +#define SCU_ABSOLUTE_MAX_UNSOLICITED_FRAMES (4096) +#define SCU_UNSOLICITED_FRAME_BUFFER_SIZE (1024) +#define SCU_INVALID_FRAME_INDEX (0xFFFF) + +#define SCU_IO_REQUEST_MAX_SGE_SIZE (0x00FFFFFF) +#define SCU_IO_REQUEST_MAX_TRANSFER_LENGTH (0x00FFFFFF) + +#endif /* _SCU_CONSTANTS_H_ */ diff --git a/drivers/scsi/isci/core/scu_event_codes.h b/drivers/scsi/isci/core/scu_event_codes.h new file mode 100644 index 000000000000..36a945ad5722 --- /dev/null +++ b/drivers/scsi/isci/core/scu_event_codes.h @@ -0,0 +1,336 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef __SCU_EVENT_CODES_HEADER__ +#define __SCU_EVENT_CODES_HEADER__ + +/** + * This file contains the constants and macros for the SCU event codes. + * + * + */ + +#define SCU_EVENT_TYPE_CODE_SHIFT 24 +#define SCU_EVENT_TYPE_CODE_MASK 0x0F000000 + +#define SCU_EVENT_SPECIFIC_CODE_SHIFT 18 +#define SCU_EVENT_SPECIFIC_CODE_MASK 0x00FC0000 + +#define SCU_EVENT_CODE_MASK \ + (SCU_EVENT_TYPE_CODE_MASK | SCU_EVENT_SPECIFIC_CODE_MASK) + +/** + * SCU_EVENT_TYPE() - + * + * This macro constructs an SCU event type from the type value. + */ +#define SCU_EVENT_TYPE(type) \ + ((u32)(type) << SCU_EVENT_TYPE_CODE_SHIFT) + +/** + * SCU_EVENT_SPECIFIC() - + * + * This macro constructs an SCU event specifier from the code value. + */ +#define SCU_EVENT_SPECIFIC(code) \ + ((u32)(code) << SCU_EVENT_SPECIFIC_CODE_SHIFT) + +/** + * SCU_EVENT_MESSAGE() - + * + * This macro constructs a combines an SCU event type and SCU event specifier + * from the type and code values. + */ +#define SCU_EVENT_MESSAGE(type, code) \ + ((type) | SCU_EVENT_SPECIFIC(code)) + +/** + * SCU_EVENT_TYPE() - + * + * SCU_EVENT_TYPES + */ +#define SCU_EVENT_TYPE_SMU_COMMAND_ERROR SCU_EVENT_TYPE(0x08) +#define SCU_EVENT_TYPE_SMU_PCQ_ERROR SCU_EVENT_TYPE(0x09) +#define SCU_EVENT_TYPE_SMU_ERROR SCU_EVENT_TYPE(0x00) +#define SCU_EVENT_TYPE_TRANSPORT_ERROR SCU_EVENT_TYPE(0x01) +#define SCU_EVENT_TYPE_BROADCAST_CHANGE SCU_EVENT_TYPE(0x02) +#define SCU_EVENT_TYPE_OSSP_EVENT SCU_EVENT_TYPE(0x03) +#define SCU_EVENT_TYPE_FATAL_MEMORY_ERROR SCU_EVENT_TYPE(0x0F) +#define SCU_EVENT_TYPE_RNC_SUSPEND_TX SCU_EVENT_TYPE(0x04) +#define SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX SCU_EVENT_TYPE(0x05) +#define SCU_EVENT_TYPE_RNC_OPS_MISC SCU_EVENT_TYPE(0x06) +#define SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT SCU_EVENT_TYPE(0x07) +#define SCU_EVENT_TYPE_ERR_CNT_EVENT SCU_EVENT_TYPE(0x0A) + +/** + * + * + * SCU_EVENT_SPECIFIERS + */ +#define SCU_EVENT_SPECIFIER_DRIVER_SUSPEND 0x20 +#define SCU_EVENT_SPECIFIER_RNC_RELEASE 0x00 + +/** + * + * + * SMU_COMMAND_EVENTS + */ +#define SCU_EVENT_INVALID_CONTEXT_COMMAND \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_COMMAND_ERROR, 0x00) + +/** + * + * + * SMU_PCQ_EVENTS + */ +#define SCU_EVENT_UNCORRECTABLE_PCQ_ERROR \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_PCQ_ERROR, 0x00) + +/** + * + * + * SMU_EVENTS + */ +#define SCU_EVENT_UNCORRECTABLE_REGISTER_WRITE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x02) +#define SCU_EVENT_UNCORRECTABLE_REGISTER_READ \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x03) +#define SCU_EVENT_PCIE_INTERFACE_ERROR \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x04) +#define SCU_EVENT_FUNCTION_LEVEL_RESET \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_SMU_ERROR, 0x05) + +/** + * + * + * TRANSPORT_LEVEL_ERRORS + */ +#define SCU_EVENT_ACK_NAK_TIMEOUT_ERROR \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_TRANSPORT_ERROR, 0x00) + +/** + * + * + * BROADCAST_CHANGE_EVENTS + */ +#define SCU_EVENT_BROADCAST_CHANGE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x01) +#define SCU_EVENT_BROADCAST_RESERVED0 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x02) +#define SCU_EVENT_BROADCAST_RESERVED1 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x03) +#define SCU_EVENT_BROADCAST_SES \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x04) +#define SCU_EVENT_BROADCAST_EXPANDER \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x05) +#define SCU_EVENT_BROADCAST_AEN \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x06) +#define SCU_EVENT_BROADCAST_RESERVED3 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x07) +#define SCU_EVENT_BROADCAST_RESERVED4 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x08) +#define SCU_EVENT_PE_SUSPENDED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_BROADCAST_CHANGE, 0x09) + +/** + * + * + * OSSP_EVENTS + */ +#define SCU_EVENT_PORT_SELECTOR_DETECTED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x10) +#define SCU_EVENT_SENT_PORT_SELECTION \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x11) +#define SCU_EVENT_HARD_RESET_TRANSMITTED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x12) +#define SCU_EVENT_HARD_RESET_RECEIVED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x13) +#define SCU_EVENT_RECEIVED_IDENTIFY_TIMEOUT \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x15) +#define SCU_EVENT_LINK_FAILURE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x16) +#define SCU_EVENT_SATA_SPINUP_HOLD \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x17) +#define SCU_EVENT_SAS_15_SSC \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x18) +#define SCU_EVENT_SAS_15 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x19) +#define SCU_EVENT_SAS_30_SSC \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1A) +#define SCU_EVENT_SAS_30 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1B) +#define SCU_EVENT_SAS_60_SSC \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1C) +#define SCU_EVENT_SAS_60 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1D) +#define SCU_EVENT_SATA_15_SSC \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1E) +#define SCU_EVENT_SATA_15 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x1F) +#define SCU_EVENT_SATA_30_SSC \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x20) +#define SCU_EVENT_SATA_30 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x21) +#define SCU_EVENT_SATA_60_SSC \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x22) +#define SCU_EVENT_SATA_60 \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x23) +#define SCU_EVENT_SAS_PHY_DETECTED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x24) +#define SCU_EVENT_SATA_PHY_DETECTED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_OSSP_EVENT, 0x25) + +/** + * + * + * FATAL_INTERNAL_MEMORY_ERROR_EVENTS + */ +#define SCU_EVENT_TSC_RNSC_UNCORRECTABLE_ERROR \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x00) +#define SCU_EVENT_TC_RNC_UNCORRECTABLE_ERROR \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x01) +#define SCU_EVENT_ZPT_UNCORRECTABLE_ERROR \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_FATAL_MEMORY_ERROR, 0x02) + +/** + * + * + * REMOTE_NODE_SUSPEND_EVENTS + */ +#define SCU_EVENT_TL_RNC_SUSPEND_TX \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX, 0x00) +#define SCU_EVENT_TL_RNC_SUSPEND_TX_RX \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX, 0x00) +#define SCU_EVENT_DRIVER_POST_RNC_SUSPEND_TX \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX, 0x20) +#define SCU_EVENT_DRIVER_POST_RNC_SUSPEND_TX_RX \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_SUSPEND_TX_RX, 0x20) + +/** + * + * + * REMOTE_NODE_MISC_EVENTS + */ +#define SCU_EVENT_POST_RCN_RELEASE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, SCU_EVENT_SPECIFIER_RNC_RELEASE) +#define SCU_EVENT_POST_IT_NEXUS_LOSS_TIMER_ENABLE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x01) +#define SCU_EVENT_POST_IT_NEXUS_LOSS_TIMER_DISABLE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x02) +#define SCU_EVENT_POST_RNC_COMPLETE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x03) +#define SCU_EVENT_POST_RNC_INVALIDATE_COMPLETE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_RNC_OPS_MISC, 0x04) + +/** + * + * + * ERROR_COUNT_EVENT + */ +#define SCU_EVENT_RX_CREDIT_BLOCKED_RECEIVED \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x00) +#define SCU_EVENT_TX_DONE_CREDIT_TIMEOUT \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x01) +#define SCU_EVENT_RX_DONE_CREDIT_TIMEOUT \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_ERR_CNT_EVENT, 0x02) + +/** + * scu_get_event_type() - + * + * This macro returns the SCU event type from the event code. + */ +#define scu_get_event_type(event_code) \ + ((event_code) & SCU_EVENT_TYPE_CODE_MASK) + +/** + * scu_get_event_specifier() - + * + * This macro returns the SCU event specifier from the event code. + */ +#define scu_get_event_specifier(event_code) \ + ((event_code) & SCU_EVENT_SPECIFIC_CODE_MASK) + +/** + * scu_get_event_code() - + * + * This macro returns the combined SCU event type and SCU event specifier from + * the event code. + */ +#define scu_get_event_code(event_code) \ + ((event_code) & SCU_EVENT_CODE_MASK) + + +/** + * + * + * PTS_SCHEDULE_EVENT + */ +#define SCU_EVENT_SMP_RESPONSE_NO_PE \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x00) +#define SCU_EVENT_SPECIFIC_SMP_RESPONSE_NO_PE \ + scu_get_event_specifier(SCU_EVENT_SMP_RESPONSE_NO_PE) + +#define SCU_EVENT_TASK_TIMEOUT \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x01) +#define SCU_EVENT_SPECIFIC_TASK_TIMEOUT \ + scu_get_event_specifier(SCU_EVENT_TASK_TIMEOUT) + +#define SCU_EVENT_IT_NEXUS_TIMEOUT \ + SCU_EVENT_MESSAGE(SCU_EVENT_TYPE_PTX_SCHEDULE_EVENT, 0x02) +#define SCU_EVENT_SPECIFIC_IT_NEXUS_TIMEOUT \ + scu_get_event_specifier(SCU_EVENT_IT_NEXUS_TIMEOUT) + + +#endif /* __SCU_EVENT_CODES_HEADER__ */ diff --git a/drivers/scsi/isci/core/scu_registers.h b/drivers/scsi/isci/core/scu_registers.h new file mode 100644 index 000000000000..175d2b98bdf2 --- /dev/null +++ b/drivers/scsi/isci/core/scu_registers.h @@ -0,0 +1,1824 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCU_REGISTERS_H_ +#define _SCU_REGISTERS_H_ + +/** + * This file contains the constants and structures for the SCU memory mapped + * registers. + * + * + */ + +#include "sci_types.h" +#include "scu_viit_data.h" + + +/* Generate a value for an SCU register */ +#define SCU_GEN_VALUE(name, value) \ + (((value) << name ## _SHIFT) & (name ## _MASK)) + +/* + * Generate a bit value for an SCU register + * Make sure that the register MASK is just a single bit */ +#define SCU_GEN_BIT(name) \ + SCU_GEN_VALUE(name, ((u32)1)) + +#define SCU_SET_BIT(name, reg_value) \ + ((reg_value) | SCU_GEN_BIT(name)) + +#define SCU_CLEAR_BIT(name, reg_value) \ + ((reg_value)$ ~(SCU_GEN_BIT(name))) + +/* + * ***************************************************************************** + * Unions for bitfield definitions of SCU Registers + * SMU Post Context Port + * ***************************************************************************** */ +#define SMU_POST_CONTEXT_PORT_CONTEXT_INDEX_SHIFT (0) +#define SMU_POST_CONTEXT_PORT_CONTEXT_INDEX_MASK (0x00000FFF) +#define SMU_POST_CONTEXT_PORT_LOGICAL_PORT_INDEX_SHIFT (12) +#define SMU_POST_CONTEXT_PORT_LOGICAL_PORT_INDEX_MASK (0x0000F000) +#define SMU_POST_CONTEXT_PORT_PROTOCOL_ENGINE_SHIFT (16) +#define SMU_POST_CONTEXT_PORT_PROTOCOL_ENGINE_MASK (0x00030000) +#define SMU_POST_CONTEXT_PORT_COMMAND_CONTEXT_SHIFT (18) +#define SMU_POST_CONTEXT_PORT_COMMAND_CONTEXT_MASK (0x00FC0000) +#define SMU_POST_CONTEXT_PORT_RESERVED_MASK (0xFF000000) + +#define SMU_PCP_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_POST_CONTEXT_PORT_ ## name, value) + +/* ***************************************************************************** */ +#define SMU_INTERRUPT_STATUS_COMPLETION_SHIFT (31) +#define SMU_INTERRUPT_STATUS_COMPLETION_MASK (0x80000000) +#define SMU_INTERRUPT_STATUS_QUEUE_SUSPEND_SHIFT (1) +#define SMU_INTERRUPT_STATUS_QUEUE_SUSPEND_MASK (0x00000002) +#define SMU_INTERRUPT_STATUS_QUEUE_ERROR_SHIFT (0) +#define SMU_INTERRUPT_STATUS_QUEUE_ERROR_MASK (0x00000001) +#define SMU_INTERRUPT_STATUS_RESERVED_MASK (0x7FFFFFFC) + +#define SMU_ISR_GEN_BIT(name) \ + SCU_GEN_BIT(SMU_INTERRUPT_STATUS_ ## name) + +#define SMU_ISR_QUEUE_ERROR SMU_ISR_GEN_BIT(QUEUE_ERROR) +#define SMU_ISR_QUEUE_SUSPEND SMU_ISR_GEN_BIT(QUEUE_SUSPEND) +#define SMU_ISR_COMPLETION SMU_ISR_GEN_BIT(COMPLETION) + +/* ***************************************************************************** */ +#define SMU_INTERRUPT_MASK_COMPLETION_SHIFT (31) +#define SMU_INTERRUPT_MASK_COMPLETION_MASK (0x80000000) +#define SMU_INTERRUPT_MASK_QUEUE_SUSPEND_SHIFT (1) +#define SMU_INTERRUPT_MASK_QUEUE_SUSPEND_MASK (0x00000002) +#define SMU_INTERRUPT_MASK_QUEUE_ERROR_SHIFT (0) +#define SMU_INTERRUPT_MASK_QUEUE_ERROR_MASK (0x00000001) +#define SMU_INTERRUPT_MASK_RESERVED_MASK (0x7FFFFFFC) + +#define SMU_IMR_GEN_BIT(name) \ + SCU_GEN_BIT(SMU_INTERRUPT_MASK_ ## name) + +#define SMU_IMR_QUEUE_ERROR SMU_IMR_GEN_BIT(QUEUE_ERROR) +#define SMU_IMR_QUEUE_SUSPEND SMU_IMR_GEN_BIT(QUEUE_SUSPEND) +#define SMU_IMR_COMPLETION SMU_IMR_GEN_BIT(COMPLETION) + +/* ***************************************************************************** */ +#define SMU_INTERRUPT_COALESCING_CONTROL_TIMER_SHIFT (0) +#define SMU_INTERRUPT_COALESCING_CONTROL_TIMER_MASK (0x0000001F) +#define SMU_INTERRUPT_COALESCING_CONTROL_NUMBER_SHIFT (8) +#define SMU_INTERRUPT_COALESCING_CONTROL_NUMBER_MASK (0x0000FF00) +#define SMU_INTERRUPT_COALESCING_CONTROL_RESERVED_MASK (0xFFFF00E0) + +#define SMU_ICC_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_INTERRUPT_COALESCING_CONTROL_ ## name, value) + +/* ***************************************************************************** */ +#define SMU_TASK_CONTEXT_RANGE_START_SHIFT (0) +#define SMU_TASK_CONTEXT_RANGE_START_MASK (0x00000FFF) +#define SMU_TASK_CONTEXT_RANGE_ENDING_SHIFT (16) +#define SMU_TASK_CONTEXT_RANGE_ENDING_MASK (0x0FFF0000) +#define SMU_TASK_CONTEXT_RANGE_ENABLE_SHIFT (31) +#define SMU_TASK_CONTEXT_RANGE_ENABLE_MASK (0x80000000) +#define SMU_TASK_CONTEXT_RANGE_RESERVED_MASK (0x7000F000) + +#define SMU_TCR_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_TASK_CONTEXT_RANGE_ ## name, value) + +#define SMU_TCR_GEN_BIT(name, value) \ + SCU_GEN_BIT(SMU_TASK_CONTEXT_RANGE_ ## name) + +/* ***************************************************************************** */ + +#define SMU_COMPLETION_QUEUE_PUT_POINTER_SHIFT (0) +#define SMU_COMPLETION_QUEUE_PUT_POINTER_MASK (0x00003FFF) +#define SMU_COMPLETION_QUEUE_PUT_CYCLE_BIT_SHIFT (15) +#define SMU_COMPLETION_QUEUE_PUT_CYCLE_BIT_MASK (0x00008000) +#define SMU_COMPLETION_QUEUE_PUT_EVENT_POINTER_SHIFT (16) +#define SMU_COMPLETION_QUEUE_PUT_EVENT_POINTER_MASK (0x03FF0000) +#define SMU_COMPLETION_QUEUE_PUT_EVENT_CYCLE_BIT_SHIFT (26) +#define SMU_COMPLETION_QUEUE_PUT_EVENT_CYCLE_BIT_MASK (0x04000000) +#define SMU_COMPLETION_QUEUE_PUT_RESERVED_MASK (0xF8004000) + +#define SMU_CQPR_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_COMPLETION_QUEUE_PUT_ ## name, value) + +#define SMU_CQPR_GEN_BIT(name) \ + SCU_GEN_BIT(SMU_COMPLETION_QUEUE_PUT_ ## name) + +/* ***************************************************************************** */ + +#define SMU_COMPLETION_QUEUE_GET_POINTER_SHIFT (0) +#define SMU_COMPLETION_QUEUE_GET_POINTER_MASK (0x00003FFF) +#define SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_SHIFT (15) +#define SMU_COMPLETION_QUEUE_GET_CYCLE_BIT_MASK (0x00008000) +#define SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_SHIFT (16) +#define SMU_COMPLETION_QUEUE_GET_EVENT_POINTER_MASK (0x03FF0000) +#define SMU_COMPLETION_QUEUE_GET_EVENT_CYCLE_BIT_SHIFT (26) +#define SMU_COMPLETION_QUEUE_GET_EVENT_CYCLE_BIT_MASK (0x04000000) +#define SMU_COMPLETION_QUEUE_GET_ENABLE_SHIFT (30) +#define SMU_COMPLETION_QUEUE_GET_ENABLE_MASK (0x40000000) +#define SMU_COMPLETION_QUEUE_GET_EVENT_ENABLE_SHIFT (31) +#define SMU_COMPLETION_QUEUE_GET_EVENT_ENABLE_MASK (0x80000000) +#define SMU_COMPLETION_QUEUE_GET_RESERVED_MASK (0x38004000) + +#define SMU_CQGR_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_COMPLETION_QUEUE_GET_ ## name, value) + +#define SMU_CQGR_GEN_BIT(name) \ + SCU_GEN_BIT(SMU_COMPLETION_QUEUE_GET_ ## name) + +#define SMU_CQGR_CYCLE_BIT \ + SMU_CQGR_GEN_BIT(CYCLE_BIT) + +#define SMU_CQGR_EVENT_CYCLE_BIT \ + SMU_CQGR_GEN_BIT(EVENT_CYCLE_BIT) + +#define SMU_CQGR_GET_POINTER_SET(value) \ + SMU_CQGR_GEN_VAL(POINTER, value) + + +/* ***************************************************************************** */ +#define SMU_COMPLETION_QUEUE_CONTROL_QUEUE_LIMIT_SHIFT (0) +#define SMU_COMPLETION_QUEUE_CONTROL_QUEUE_LIMIT_MASK (0x00003FFF) +#define SMU_COMPLETION_QUEUE_CONTROL_EVENT_LIMIT_SHIFT (16) +#define SMU_COMPLETION_QUEUE_CONTROL_EVENT_LIMIT_MASK (0x03FF0000) +#define SMU_COMPLETION_QUEUE_CONTROL_RESERVED_MASK (0xFC00C000) + +#define SMU_CQC_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_COMPLETION_QUEUE_CONTROL_ ## name, value) + +#define SMU_CQC_QUEUE_LIMIT_SET(value) \ + SMU_CQC_GEN_VAL(QUEUE_LIMIT, value) + +#define SMU_CQC_EVENT_LIMIT_SET(value) \ + SMU_CQC_GEN_VAL(EVENT_LIMIT, value) + + +/* ***************************************************************************** */ +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_SHIFT (0) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_MASK (0x00000FFF) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_SHIFT (12) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_MASK (0x00007000) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT (15) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_MASK (0x07FF8000) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_PEG_SHIFT (27) +#define SMU_DEVICE_CONTEXT_CAPACITY_MAX_PEG_MASK (0x08000000) +#define SMU_DEVICE_CONTEXT_CAPACITY_RESERVED_MASK (0xF0000000) + +#define SMU_DCC_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_DEVICE_CONTEXT_CAPACITY_ ## name, value) + +#define SMU_DCC_GET_MAX_PEG(value) \ + (\ + ((value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_PEG_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_SHIFT \ + ) + +#define SMU_DCC_GET_MAX_LP(value) \ + (\ + ((value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_LP_SHIFT \ + ) + +#define SMU_DCC_GET_MAX_TC(value) \ + (\ + ((value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_TC_SHIFT \ + ) + +#define SMU_DCC_GET_MAX_RNC(value) \ + (\ + ((value) & SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_MASK) \ + >> SMU_DEVICE_CONTEXT_CAPACITY_MAX_RNC_SHIFT \ + ) + +/* -------------------------------------------------------------------------- */ + +#define SMU_CONTROL_STATUS_TASK_CONTEXT_RANGE_ENABLE_SHIFT (0) +#define SMU_CONTROL_STATUS_TASK_CONTEXT_RANGE_ENABLE_MASK (0x00000001) +#define SMU_CONTROL_STATUS_COMPLETION_BYTE_SWAP_ENABLE_SHIFT (1) +#define SMU_CONTROL_STATUS_COMPLETION_BYTE_SWAP_ENABLE_MASK (0x00000002) +#define SMU_CONTROL_STATUS_CONTEXT_RAM_INIT_COMPLETED_SHIFT (16) +#define SMU_CONTROL_STATUS_CONTEXT_RAM_INIT_COMPLETED_MASK (0x00010000) +#define SMU_CONTROL_STATUS_SCHEDULER_RAM_INIT_COMPLETED_SHIFT (17) +#define SMU_CONTROL_STATUS_SCHEDULER_RAM_INIT_COMPLETED_MASK (0x00020000) +#define SMU_CONTROL_STATUS_RESERVED_MASK (0xFFFCFFFC) + +#define SMU_SMUCSR_GEN_BIT(name) \ + SCU_GEN_BIT(SMU_CONTROL_STATUS_ ## name) + +#define SMU_SMUCSR_SCHEDULER_RAM_INIT_COMPLETED \ + (SMU_SMUCSR_GEN_BIT(SCHEDULER_RAM_INIT_COMPLETED)) + +#define SMU_SMUCSR_CONTEXT_RAM_INIT_COMPLETED \ + (SMU_SMUCSR_GEN_BIT(CONTEXT_RAM_INIT_COMPLETED)) + +#define SCU_RAM_INIT_COMPLETED \ + (\ + SMU_SMUCSR_CONTEXT_RAM_INIT_COMPLETED \ + | SMU_SMUCSR_SCHEDULER_RAM_INIT_COMPLETED \ + ) + +/* -------------------------------------------------------------------------- */ + +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE0_SHIFT (0) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE0_MASK (0x00000001) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE1_SHIFT (1) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE1_MASK (0x00000002) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE2_SHIFT (2) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE2_MASK (0x00000004) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE3_SHIFT (3) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_PE3_MASK (0x00000008) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE0_SHIFT (8) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE0_MASK (0x00000100) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE1_SHIFT (9) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE1_MASK (0x00000200) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE2_SHIFT (10) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE2_MASK (0x00000400) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE3_SHIFT (11) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_PE3_MASK (0x00000800) + +#define SMU_RESET_PROTOCOL_ENGINE(peg, pe) \ + ((1 << (pe)) << ((peg) * 8)) + +#define SMU_RESET_PEG_PROTOCOL_ENGINES(peg) \ + (\ + SMU_RESET_PROTOCOL_ENGINE(peg, 0) \ + | SMU_RESET_PROTOCOL_ENGINE(peg, 1) \ + | SMU_RESET_PROTOCOL_ENGINE(peg, 2) \ + | SMU_RESET_PROTOCOL_ENGINE(peg, 3) \ + ) + +#define SMU_RESET_ALL_PROTOCOL_ENGINES() \ + (\ + SMU_RESET_PEG_PROTOCOL_ENGINES(0) \ + | SMU_RESET_PEG_PROTOCOL_ENGINES(1) \ + ) + +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG0_LP0_SHIFT (16) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG0_LP0_MASK (0x00010000) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG0_LP2_SHIFT (17) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG0_LP2_MASK (0x00020000) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG1_LP0_SHIFT (18) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG1_LP0_MASK (0x00040000) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG1_LP2_SHIFT (19) +#define SMU_SOFTRESET_CONTROL_RESET_WIDE_PORT_PEG1_LP2_MASK (0x00080000) + +#define SMU_RESET_WIDE_PORT_QUEUE(peg, wide_port) \ + ((1 << ((wide_port) / 2)) << ((peg) * 2) << 16) + +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_SHIFT (20) +#define SMU_SOFTRESET_CONTROL_RESET_PEG0_MASK (0x00100000) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_SHIFT (21) +#define SMU_SOFTRESET_CONTROL_RESET_PEG1_MASK (0x00200000) +#define SMU_SOFTRESET_CONTROL_RESET_SCU_SHIFT (22) +#define SMU_SOFTRESET_CONTROL_RESET_SCU_MASK (0x00400000) + +/* + * It seems to make sense that if you are going to reset the protocol + * engine group that you would also reset all of the protocol engines */ +#define SMU_RESET_PROTOCOL_ENGINE_GROUP(peg) \ + (\ + (1 << ((peg) + 20)) \ + | SMU_RESET_WIDE_PORT_QUEUE(peg, 0) \ + | SMU_RESET_WIDE_PORT_QUEUE(peg, 1) \ + | SMU_RESET_PEG_PROTOCOL_ENGINES(peg) \ + ) + +#define SMU_RESET_ALL_PROTOCOL_ENGINE_GROUPS() \ + (\ + SMU_RESET_PROTOCOL_ENGINE_GROUP(0) \ + | SMU_RESET_PROTOCOL_ENGINE_GROUP(1) \ + ) + +#define SMU_RESET_SCU() (0xFFFFFFFF) + + + +/* ***************************************************************************** */ +#define SMU_TASK_CONTEXT_ASSIGNMENT_STARTING_SHIFT (0) +#define SMU_TASK_CONTEXT_ASSIGNMENT_STARTING_MASK (0x00000FFF) +#define SMU_TASK_CONTEXT_ASSIGNMENT_ENDING_SHIFT (16) +#define SMU_TASK_CONTEXT_ASSIGNMENT_ENDING_MASK (0x0FFF0000) +#define SMU_TASK_CONTEXT_ASSIGNMENT_RANGE_CHECK_ENABLE_SHIFT (31) +#define SMU_TASK_CONTEXT_ASSIGNMENT_RANGE_CHECK_ENABLE_MASK (0x80000000) +#define SMU_TASK_CONTEXT_ASSIGNMENT_RESERVED_MASK (0x7000F000) + +#define SMU_TCA_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SMU_TASK_CONTEXT_ASSIGNMENT_ ## name, value) + +#define SMU_TCA_GEN_BIT(name) \ + SCU_GEN_BIT(SMU_TASK_CONTEXT_ASSIGNMENT_ ## name) + +/* ***************************************************************************** */ +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_CONTROL_QUEUE_SIZE_SHIFT (0) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_CONTROL_QUEUE_SIZE_MASK (0x00000FFF) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_CONTROL_RESERVED_MASK (0xFFFFF000) + +#define SCU_UFQC_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SDMA_UNSOLICITED_FRAME_QUEUE_CONTROL_ ## name, value) + +#define SCU_UFQC_QUEUE_SIZE_SET(value) \ + SCU_UFQC_GEN_VAL(QUEUE_SIZE, value) + +/* ***************************************************************************** */ +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_POINTER_SHIFT (0) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_POINTER_MASK (0x00000FFF) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_CYCLE_BIT_SHIFT (12) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_CYCLE_BIT_MASK (0x00001000) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_RESERVED_MASK (0xFFFFE000) + +#define SCU_UFQPP_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_ ## name, value) + +#define SCU_UFQPP_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SDMA_UNSOLICITED_FRAME_QUEUE_PUT_ ## name) + +/* + * ***************************************************************************** + * * SDMA Registers + * ***************************************************************************** */ +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_POINTER_SHIFT (0) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_POINTER_MASK (0x00000FFF) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_CYCLE_BIT_SHIFT (12) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_CYCLE_BIT_MASK (12) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_ENABLE_BIT_SHIFT (31) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_ENABLE_BIT_MASK (0x80000000) +#define SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_RESERVED_MASK (0x7FFFE000) + +#define SCU_UFQGP_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_ ## name, value) + +#define SCU_UFQGP_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SDMA_UNSOLICITED_FRAME_QUEUE_GET_ ## name) + +#define SCU_UFQGP_CYCLE_BIT(value) \ + SCU_UFQGP_GEN_BIT(CYCLE_BIT, value) + +#define SCU_UFQGP_GET_POINTER(value) \ + SCU_UFQGP_GEN_VALUE(POINTER, value) + +#define SCU_UFQGP_ENABLE(value) \ + (SCU_UFQGP_GEN_BIT(ENABLE) | value) + +#define SCU_UFQGP_DISABLE(value) \ + (~SCU_UFQGP_GEN_BIT(ENABLE) & value) + +#define SCU_UFQGP_VALUE(bit, value) \ + (SCU_UFQGP_CYCLE_BIT(bit) | SCU_UFQGP_GET_POINTER(value)) + +/* ***************************************************************************** */ +#define SCU_PDMA_CONFIGURATION_ADDRESS_MODIFIER_SHIFT (0) +#define SCU_PDMA_CONFIGURATION_ADDRESS_MODIFIER_MASK (0x0000FFFF) +#define SCU_PDMA_CONFIGURATION_PCI_RELAXED_ORDERING_ENABLE_SHIFT (16) +#define SCU_PDMA_CONFIGURATION_PCI_RELAXED_ORDERING_ENABLE_MASK (0x00010000) +#define SCU_PDMA_CONFIGURATION_PCI_NO_SNOOP_ENABLE_SHIFT (17) +#define SCU_PDMA_CONFIGURATION_PCI_NO_SNOOP_ENABLE_MASK (0x00020000) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_BYTE_SWAP_SHIFT (18) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_BYTE_SWAP_MASK (0x00040000) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_XPI_SGL_FETCH_SHIFT (19) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_XPI_SGL_FETCH_MASK (0x00080000) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_XPI_RX_HEADER_RAM_WRITE_SHIFT (20) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_XPI_RX_HEADER_RAM_WRITE_MASK (0x00100000) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_XPI_UF_ADDRESS_FETCH_SHIFT (21) +#define SCU_PDMA_CONFIGURATION_BIG_ENDIAN_CONTROL_XPI_UF_ADDRESS_FETCH_MASK (0x00200000) +#define SCU_PDMA_CONFIGURATION_ADDRESS_MODIFIER_SELECT_SHIFT (22) +#define SCU_PDMA_CONFIGURATION_ADDRESS_MODIFIER_SELECT_MASK (0x00400000) +#define SCU_PDMA_CONFIGURATION_RESERVED_MASK (0xFF800000) + +#define SCU_PDMACR_GEN_VALUE(name, value) \ + SCU_GEN_VALUE(SCU_PDMA_CONFIGURATION_ ## name, value) + +#define SCU_PDMACR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_PDMA_CONFIGURATION_ ## name) + +#define SCU_PDMACR_BE_GEN_BIT(name) \ + SCU_PCMACR_GEN_BIT(BIG_ENDIAN_CONTROL_ ## name) + +/* ***************************************************************************** */ +#define SCU_CDMA_CONFIGURATION_PCI_RELAXED_ORDERING_ENABLE_SHIFT (8) +#define SCU_CDMA_CONFIGURATION_PCI_RELAXED_ORDERING_ENABLE_MASK (0x00000100) + +#define SCU_CDMACR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_CDMA_CONFIGURATION_ ## name) + +/* + * ***************************************************************************** + * * SCU Link Layer Registers + * ***************************************************************************** */ +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_TIMEOUT_SHIFT (0) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_TIMEOUT_MASK (0x000000FF) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_LOCK_TIME_SHIFT (8) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_LOCK_TIME_MASK (0x0000FF00) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_RATE_CHANGE_DELAY_SHIFT (16) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_RATE_CHANGE_DELAY_MASK (0x00FF0000) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_DWORD_SYNC_TIMEOUT_SHIFT (24) +#define SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_DWORD_SYNC_TIMEOUT_MASK (0xFF000000) +#define SCU_LINK_LAYER_SPEED_NECGOIATION_TIMER_VALUES_REQUIRED_MASK (0x00000000) +#define SCU_LINK_LAYER_SPEED_NECGOIATION_TIMER_VALUES_DEFAULT_MASK (0x7D00676F) +#define SCU_LINK_LAYER_SPEED_NECGOIATION_TIMER_VALUES_RESERVED_MASK (0x00FF0000) + +#define SCU_SAS_SPDTOV_GEN_VALUE(name, value) \ + SCU_GEN_VALUE(SCU_LINK_LAYER_SPEED_NEGOTIATION_TIMER_VALUES_ ## name, value) + + +#define SCU_LINK_STATUS_DWORD_SYNC_AQUIRED_SHIFT (2) +#define SCU_LINK_STATUS_DWORD_SYNC_AQUIRED_MASK (0x00000004) +#define SCU_LINK_STATUS_TRANSMIT_PORT_SELECTION_DONE_SHIFT (4) +#define SCU_LINK_STATUS_TRANSMIT_PORT_SELECTION_DONE_MASK (0x00000010) +#define SCU_LINK_STATUS_RECEIVER_CREDIT_EXHAUSTED_SHIFT (5) +#define SCU_LINK_STATUS_RECEIVER_CREDIT_EXHAUSTED_MASK (0x00000020) +#define SCU_LINK_STATUS_RESERVED_MASK (0xFFFFFFCD) + +#define SCU_SAS_LLSTA_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_LINK_STATUS_ ## name) + + +/* TODO: Where is the SATA_PSELTOV register? */ + +/* + * ***************************************************************************** + * * SCU SAS Maximum Arbitration Wait Time Timeout Register + * ***************************************************************************** */ +#define SCU_SAS_MAX_ARBITRATION_WAIT_TIME_TIMEOUT_VALUE_SHIFT (0) +#define SCU_SAS_MAX_ARBITRATION_WAIT_TIME_TIMEOUT_VALUE_MASK (0x00007FFF) +#define SCU_SAS_MAX_ARBITRATION_WAIT_TIME_TIMEOUT_SCALE_SHIFT (15) +#define SCU_SAS_MAX_ARBITRATION_WAIT_TIME_TIMEOUT_SCALE_MASK (0x00008000) + +#define SCU_SAS_MAWTTOV_GEN_VALUE(name, value) \ + SCU_GEN_VALUE(SCU_SAS_MAX_ARBITRATION_WAIT_TIME_TIMEOUT_ ## name, value) + +#define SCU_SAS_MAWTTOV_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SAS_MAX_ARBITRATION_WAIT_TIME_TIMEOUT_ ## name) + + +/* + * TODO: Where is the SAS_LNKTOV regsiter? + * TODO: Where is the SAS_PHYTOV register? */ + +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SMP_TARGET_SHIFT (1) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SMP_TARGET_MASK (0x00000002) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_STP_TARGET_SHIFT (2) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_STP_TARGET_MASK (0x00000004) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SSP_TARGET_SHIFT (3) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SSP_TARGET_MASK (0x00000008) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_DA_SATA_HOST_SHIFT (8) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_DA_SATA_HOST_MASK (0x00000100) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SMP_INITIATOR_SHIFT (9) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SMP_INITIATOR_MASK (0x00000200) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_STP_INITIATOR_SHIFT (10) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_STP_INITIATOR_MASK (0x00000400) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SSP_INITIATOR_SHIFT (11) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_SSP_INITIATOR_MASK (0x00000800) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_REASON_CODE_SHIFT (16) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_REASON_CODE_MASK (0x000F0000) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_ADDRESS_FRAME_TYPE_SHIFT (24) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_ADDRESS_FRAME_TYPE_MASK (0x0F000000) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_DEVICE_TYPE_SHIFT (28) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_DEVICE_TYPE_MASK (0x70000000) +#define SCU_SAS_TRANSMIT_IDENTIFICATION_RESERVED_MASK (0x80F0F1F1) + +#define SCU_SAS_TIID_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SAS_TRANSMIT_IDENTIFICATION_ ## name, value) + +#define SCU_SAS_TIID_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SAS_TRANSMIT_IDENTIFICATION_ ## name) + +/* SAS Identify Frame PHY Identifier Register */ +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_BREAK_REPLY_CAPABLE_SHIFT (16) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_BREAK_REPLY_CAPABLE_MASK (0x00010000) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_REQUESTED_INSIDE_ZPSDS_SHIFT (17) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_REQUESTED_INSIDE_ZPSDS_MASK (0x00020000) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_INSIDE_ZPSDS_PERSISTENT_SHIFT (18) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_INSIDE_ZPSDS_PERSISTENT_MASK (0x00040000) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_ID_SHIFT (24) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_ID_MASK (0xFF000000) +#define SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_RESERVED_MASK (0x00F800FF) + +#define SCU_SAS_TIPID_GEN_VALUE(name, value) \ + SCU_GEN_VALUE(SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_ ## name, value) + +#define SCU_SAS_TIPID_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_LINK_LAYER_IDENTIFY_FRAME_PHY_IDENTIFIER_ ## name) + + +#define SCU_SAS_PHY_CONFIGURATION_TX_PARITY_CHECK_SHIFT (4) +#define SCU_SAS_PHY_CONFIGURATION_TX_PARITY_CHECK_MASK (0x00000010) +#define SCU_SAS_PHY_CONFIGURATION_TX_BAD_CRC_SHIFT (6) +#define SCU_SAS_PHY_CONFIGURATION_TX_BAD_CRC_MASK (0x00000040) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_SCRAMBLER_SHIFT (7) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_SCRAMBLER_MASK (0x00000080) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_DESCRAMBLER_SHIFT (8) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_DESCRAMBLER_MASK (0x00000100) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_CREDIT_INSERTION_SHIFT (9) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_CREDIT_INSERTION_MASK (0x00000200) +#define SCU_SAS_PHY_CONFIGURATION_SUSPEND_PROTOCOL_ENGINE_SHIFT (11) +#define SCU_SAS_PHY_CONFIGURATION_SUSPEND_PROTOCOL_ENGINE_MASK (0x00000800) +#define SCU_SAS_PHY_CONFIGURATION_SATA_SPINUP_HOLD_SHIFT (12) +#define SCU_SAS_PHY_CONFIGURATION_SATA_SPINUP_HOLD_MASK (0x00001000) +#define SCU_SAS_PHY_CONFIGURATION_TRANSMIT_PORT_SELECTION_SIGNAL_SHIFT (13) +#define SCU_SAS_PHY_CONFIGURATION_TRANSMIT_PORT_SELECTION_SIGNAL_MASK (0x00002000) +#define SCU_SAS_PHY_CONFIGURATION_HARD_RESET_SHIFT (14) +#define SCU_SAS_PHY_CONFIGURATION_HARD_RESET_MASK (0x00004000) +#define SCU_SAS_PHY_CONFIGURATION_OOB_ENABLE_SHIFT (15) +#define SCU_SAS_PHY_CONFIGURATION_OOB_ENABLE_MASK (0x00008000) +#define SCU_SAS_PHY_CONFIGURATION_ENABLE_FRAME_TX_INSERT_ALIGN_SHIFT (23) +#define SCU_SAS_PHY_CONFIGURATION_ENABLE_FRAME_TX_INSERT_ALIGN_MASK (0x00800000) +#define SCU_SAS_PHY_CONFIGURATION_FORWARD_IDENTIFY_FRAME_SHIFT (27) +#define SCU_SAS_PHY_CONFIGURATION_FORWARD_IDENTIFY_FRAME_MASK (0x08000000) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_BYTE_TRANSPOSE_STP_FRAME_SHIFT (28) +#define SCU_SAS_PHY_CONFIGURATION_DISABLE_BYTE_TRANSPOSE_STP_FRAME_MASK (0x10000000) +#define SCU_SAS_PHY_CONFIGURATION_OOB_RESET_SHIFT (29) +#define SCU_SAS_PHY_CONFIGURATION_OOB_RESET_MASK (0x20000000) +#define SCU_SAS_PHY_CONFIGURATION_THREE_IAF_ENABLE_SHIFT (30) +#define SCU_SAS_PHY_CONFIGURATION_THREE_IAF_ENABLE_MASK (0x40000000) +#define SCU_SAS_PHY_CONFIGURATION_OOB_ALIGN0_ENABLE_SHIFT (31) +#define SCU_SAS_PHY_CONFIGURATION_OOB_ALIGN0_ENABLE_MASK (0x80000000) +#define SCU_SAS_PHY_CONFIGURATION_REQUIRED_MASK (0x0100000F) +#define SCU_SAS_PHY_CONFIGURATION_DEFAULT_MASK (0x4180100F) +#define SCU_SAS_PHY_CONFIGURATION_RESERVED_MASK (0x00000000) + +#define SCU_SAS_PCFG_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SAS_PHY_CONFIGURATION_ ## name) + + +#define SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_COUNT_SHIFT (0) +#define SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_COUNT_MASK (0x0003FFFF) +#define SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_ENABLE_SHIFT (31) +#define SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_ENABLE_MASK (0x80000000) +#define SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_RESERVED_MASK (0x7FFC0000) + +#define SCU_ENSPINUP_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_ ## name, value) + +#define SCU_ENSPINUP_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_LINK_LAYER_ENABLE_SPINUP_CONTROL_ ## name) + + +#define SCU_LINK_LAYER_PHY_CAPABILITIES_TXSSCTYPE_SHIFT (1) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_TXSSCTYPE_MASK (0x00000002) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_RLLRATE_SHIFT (4) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_RLLRATE_MASK (0x000000F0) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SWO15GBPS_SHIFT (8) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SWO15GBPS_MASK (0x00000100) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SW15GBPS_SHIFT (9) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SW15GBPS_MASK (0x00000201) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SWO30GBPS_SHIFT (10) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SWO30GBPS_MASK (0x00000401) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SW30GBPS_SHIFT (11) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SW30GBPS_MASK (0x00000801) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SWO60GBPS_SHIFT (12) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SWO60GBPS_MASK (0x00001001) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SW60GBPS_SHIFT (13) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_SW60GBPS_MASK (0x00002001) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_EVEN_PARITY_SHIFT (31) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_EVEN_PARITY_MASK (0x80000000) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_DEFAULT_MASK (0x00003F01) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_REQUIRED_MASK (0x00000001) +#define SCU_LINK_LAYER_PHY_CAPABILITIES_RESERVED_MASK (0x7FFFC00D) + +#define SCU_SAS_PHYCAP_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_LINK_LAYER_PHY_CAPABILITIES_ ## name, value) + +#define SCU_SAS_PHYCAP_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_LINK_LAYER_PHY_CAPABILITIES_ ## name) + + +#define SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_VIRTUAL_EXPANDER_PHY_ZONE_GROUP_SHIFT (0) +#define SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_VIRTUAL_EXPANDER_PHY_ZONE_GROUP_MASK (0x000000FF) +#define SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_INSIDE_SOURCE_ZONE_GROUP_SHIFT (31) +#define SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_INSIDE_SOURCE_ZONE_GROUP_MASK (0x80000000) +#define SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_RESERVED_MASK (0x7FFFFF00) + +#define SCU_PSZGCR_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_ ## name, value) + +#define SCU_PSZGCR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_LINK_LAYER_PHY_SOURCE_ZONE_GROUP_CONTROL_ ## name) + +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE0_LOCKED_SHIFT (1) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE0_LOCKED_MASK (0x00000002) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE0_UPDATING_SHIFT (2) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE0_UPDATING_MASK (0x00000004) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE1_LOCKED_SHIFT (4) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE1_LOCKED_MASK (0x00000010) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE1_UPDATING_SHIFT (5) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZONE1_UPDATING_MASK (0x00000020) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE0_SHIFT (16) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE0_MASK (0x00030000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE0_SHIFT (19) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE0_MASK (0x00080000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE1_SHIFT (20) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE1_MASK (0x00300000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE1_SHIFT (23) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE1_MASK (0x00800000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE2_SHIFT (24) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE2_MASK (0x03000000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE2_SHIFT (27) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE2_MASK (0x08000000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE3_SHIFT (28) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ZPT_ASSOCIATION_PE3_MASK (0x30000000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE3_SHIFT (31) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_AIP_ENABLE_PE3_MASK (0x80000000) +#define SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_RESERVED_MASK (0x4444FFC9) + +#define SCU_PEG_SCUVZECR_GEN_VAL(name, val) \ + SCU_GEN_VALUE(SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ ## name, val) + +#define SCU_PEG_SCUVZECR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_PROTOCOL_ENGINE_GROUP_VIRTUAL_ZONING_EXPANDER_CONTROL_ ## name) + + +/* + * ***************************************************************************** + * * Port Task Scheduler registers shift and mask values + * ***************************************************************************** */ +#define SCU_PTSG_CONTROL_IT_NEXUS_TIMEOUT_SHIFT (0) +#define SCU_PTSG_CONTROL_IT_NEXUS_TIMEOUT_MASK (0x0000FFFF) +#define SCU_PTSG_CONTROL_TASK_TIMEOUT_SHIFT (16) +#define SCU_PTSG_CONTROL_TASK_TIMEOUT_MASK (0x00FF0000) +#define SCU_PTSG_CONTROL_PTSG_ENABLE_SHIFT (24) +#define SCU_PTSG_CONTROL_PTSG_ENABLE_MASK (0x01000000) +#define SCU_PTSG_CONTROL_ETM_ENABLE_SHIFT (25) +#define SCU_PTSG_CONTROL_ETM_ENABLE_MASK (0x02000000) +#define SCU_PTSG_CONTROL_DEFAULT_MASK (0x00020002) +#define SCU_PTSG_CONTROL_REQUIRED_MASK (0x00000000) +#define SCU_PTSG_CONTROL_RESERVED_MASK (0xFC000000) + +#define SCU_PTSGCR_GEN_VAL(name, val) \ + SCU_GEN_VALUE(SCU_PTSG_CONTROL_ ## name, val) + +#define SCU_PTSGCR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_PTSG_CONTROL_ ## name) + + +/* ***************************************************************************** */ +#define SCU_PTSG_REAL_TIME_CLOCK_SHIFT (0) +#define SCU_PTSG_REAL_TIME_CLOCK_MASK (0x0000FFFF) +#define SCU_PTSG_REAL_TIME_CLOCK_RESERVED_MASK (0xFFFF0000) + +#define SCU_RTCR_GEN_VAL(name, val) \ + SCU_GEN_VALUE(SCU_PTSG_ ## name, val) + + +#define SCU_PTSG_REAL_TIME_CLOCK_CONTROL_PRESCALER_VALUE_SHIFT (0) +#define SCU_PTSG_REAL_TIME_CLOCK_CONTROL_PRESCALER_VALUE_MASK (0x00FFFFFF) +#define SCU_PTSG_REAL_TIME_CLOCK_CONTROL_RESERVED_MASK (0xFF000000) + +#define SCU_RTCCR_GEN_VAL(name, val) \ + SCU_GEN_VALUE(SCU_PTSG_REAL_TIME_CLOCK_CONTROL_ ## name, val) + + +#define SCU_PTSG_PORT_TASK_SCHEDULER_CONTROL_SUSPEND_SHIFT (0) +#define SCU_PTSG_PORT_TASK_SCHEDULER_CONTROL_SUSPEND_MASK (0x00000001) +#define SCU_PTSG_PORT_TASK_SCHEDULER_CONTROL_ENABLE_SHIFT (1) +#define SCU_PTSG_PORT_TASK_SCHEDULER_CONTROL_ENABLE_MASK (0x00000002) +#define SCU_PTSG_PORT_TASK_SCHEDULER_CONTROL_RESERVED_MASK (0xFFFFFFFC) + +#define SCU_PTSxCR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_PTSG_PORT_TASK_SCHEDULER_CONTROL_ ## name) + + +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_NEXT_RN_VALID_SHIFT (0) +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_NEXT_RN_VALID_MASK (0x00000001) +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_ACTIVE_RNSC_LIST_VALID_SHIFT (1) +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_ACTIVE_RNSC_LIST_VALID_MASK (0x00000002) +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_PTS_SUSPENDED_SHIFT (2) +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_PTS_SUSPENDED_MASK (0x00000004) +#define SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_RESERVED_MASK (0xFFFFFFF8) + +#define SCU_PTSxSR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_PTSG_PORT_TASK_SCHEDULER_STATUS_ ## name) + + +/* + * ***************************************************************************** + * * SGPIO Register shift and mask values + * ***************************************************************************** */ +#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_SHIFT (0) +#define SCU_SGPIO_CONTROL_SGPIO_ENABLE_MASK (0x00000001) +#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_SHIFT (1) +#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_CLOCK_SELECT_MASK (0x00000002) +#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_SHIFT (2) +#define SCU_SGPIO_CONTROL_SGPIO_SERIAL_SHIFT_WIDTH_SELECT_MASK (0x00000004) +#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_SHIFT (15) +#define SCU_SGPIO_CONTROL_SGPIO_TEST_BIT_MASK (0x00008000) +#define SCU_SGPIO_CONTROL_SGPIO_RESERVED_MASK (0xFFFF7FF8) + +#define SCU_SGICRx_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SGPIO_CONTROL_SGPIO_ ## name) + +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_SHIFT (0) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R0_MASK (0x0000000F) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_SHIFT (4) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R1_MASK (0x000000F0) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_SHIFT (8) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R2_MASK (0x00000F00) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_SHIFT (12) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_R3_MASK (0x0000F000) +#define SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_RESERVED_MASK (0xFFFF0000) + +#define SCU_SGPBRx_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SGPIO_PROGRAMMABLE_BLINK_REGISTER_ ## name, value) + +#define SCU_SGPIO_START_DRIVE_LOWER_R0_SHIFT (0) +#define SCU_SGPIO_START_DRIVE_LOWER_R0_MASK (0x00000003) +#define SCU_SGPIO_START_DRIVE_LOWER_R1_SHIFT (4) +#define SCU_SGPIO_START_DRIVE_LOWER_R1_MASK (0x00000030) +#define SCU_SGPIO_START_DRIVE_LOWER_R2_SHIFT (8) +#define SCU_SGPIO_START_DRIVE_LOWER_R2_MASK (0x00000300) +#define SCU_SGPIO_START_DRIVE_LOWER_R3_SHIFT (12) +#define SCU_SGPIO_START_DRIVE_LOWER_R3_MASK (0x00003000) +#define SCU_SGPIO_START_DRIVE_LOWER_RESERVED_MASK (0xFFFF8888) + +#define SCU_SGSDLRx_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value) + +#define SCU_SGPIO_START_DRIVE_UPPER_R0_SHIFT (0) +#define SCU_SGPIO_START_DRIVE_UPPER_R0_MASK (0x00000003) +#define SCU_SGPIO_START_DRIVE_UPPER_R1_SHIFT (4) +#define SCU_SGPIO_START_DRIVE_UPPER_R1_MASK (0x00000030) +#define SCU_SGPIO_START_DRIVE_UPPER_R2_SHIFT (8) +#define SCU_SGPIO_START_DRIVE_UPPER_R2_MASK (0x00000300) +#define SCU_SGPIO_START_DRIVE_UPPER_R3_SHIFT (12) +#define SCU_SGPIO_START_DRIVE_UPPER_R3_MASK (0x00003000) +#define SCU_SGPIO_START_DRIVE_UPPER_RESERVED_MASK (0xFFFF8888) + +#define SCU_SGSDURx_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SGPIO_START_DRIVE_LOWER_ ## name, value) + +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_SHIFT (0) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D0_MASK (0x00000003) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_SHIFT (4) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D1_MASK (0x00000030) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_SHIFT (8) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D2_MASK (0x00000300) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_SHIFT (12) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_D3_MASK (0x00003000) +#define SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_RESERVED_MASK (0xFFFF8888) + +#define SCU_SGSIDLRx_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value) + +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_SHIFT (0) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D0_MASK (0x00000003) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_SHIFT (4) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D1_MASK (0x00000030) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_SHIFT (8) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D2_MASK (0x00000300) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_SHIFT (12) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_D3_MASK (0x00003000) +#define SCU_SGPIO_SERIAL_INPUT_DATA_UPPER_RESERVED_MASK (0xFFFF8888) + +#define SCU_SGSIDURx_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SGPIO_SERIAL_INPUT_DATA_LOWER_ ## name, value) + +#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_SHIFT (0) +#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_MASK (0x0000000F) +#define SCU_SGPIO_VENDOR_SPECIFIC_CODE_RESERVED_MASK (0xFFFFFFF0) + +#define SCU_SGVSCR_GEN_VAL(value) \ + SCU_GEN_VALUE(SCU_SGPIO_VENDOR_SPECIFIC_CODE ## name, value) + +#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_SHIFT (0) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA0_MASK (0x00000003) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_SHIFT (2) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA0_MASK (0x00000004) +#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_SHIFT (3) +#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA0_MASK (0x00000008) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_SHIFT (4) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA1_MASK (0x00000030) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_SHIFT (6) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA1_MASK (0x00000040) +#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_SHIFT (7) +#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA1_MASK (0x00000080) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_SHIFT (8) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INPUT_DATA2_MASK (0x00000300) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_SHIFT (10) +#define SCU_SGPIO_OUPUT_DATA_SELECT_INVERT_INPUT_DATA2_MASK (0x00000400) +#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_SHIFT (11) +#define SCU_SGPIO_OUPUT_DATA_SELECT_JOG_ENABLE_DATA2_MASK (0x00000800) +#define SCU_SGPIO_OUPUT_DATA_SELECT_RESERVED_MASK (0xFFFFF000) + +#define SCU_SGODSR_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name, value) + +#define SCU_SGODSR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SGPIO_OUPUT_DATA_SELECT_ ## name) + +/* + * ***************************************************************************** + * * SMU Registers + * ***************************************************************************** */ + +/* + * ---------------------------------------------------------------------------- + * SMU Registers + * These registers are based off of BAR0 + * + * To calculate the offset for other functions use + * BAR0 + FN# * SystemPageSize * 2 + * + * The TCA is only accessable from FN#0 (Physical Function) and each + * is programmed by (BAR0 + SCU_SMU_TCA_OFFSET + (FN# * 0x04)) or + * TCA0 for FN#0 is at BAR0 + 0x0400 + * TCA1 for FN#1 is at BAR0 + 0x0404 + * etc. + * ---------------------------------------------------------------------------- + * Accessable to all FN#s */ +#define SCU_SMU_PCP_OFFSET 0x0000 +#define SCU_SMU_AMR_OFFSET 0x0004 +#define SCU_SMU_ISR_OFFSET 0x0010 +#define SCU_SMU_IMR_OFFSET 0x0014 +#define SCU_SMU_ICC_OFFSET 0x0018 +#define SCU_SMU_HTTLBAR_OFFSET 0x0020 +#define SCU_SMU_HTTUBAR_OFFSET 0x0024 +#define SCU_SMU_TCR_OFFSET 0x0028 +#define SCU_SMU_CQLBAR_OFFSET 0x0030 +#define SCU_SMU_CQUBAR_OFFSET 0x0034 +#define SCU_SMU_CQPR_OFFSET 0x0040 +#define SCU_SMU_CQGR_OFFSET 0x0044 +#define SCU_SMU_CQC_OFFSET 0x0048 +/* Accessable to FN#0 only */ +#define SCU_SMU_RNCLBAR_OFFSET 0x0080 +#define SCU_SMU_RNCUBAR_OFFSET 0x0084 +#define SCU_SMU_DCC_OFFSET 0x0090 +#define SCU_SMU_DFC_OFFSET 0x0094 +#define SCU_SMU_SMUCSR_OFFSET 0x0098 +#define SCU_SMU_SCUSRCR_OFFSET 0x009C +#define SCU_SMU_SMAW_OFFSET 0x00A0 +#define SCU_SMU_SMDW_OFFSET 0x00A4 +/* Accessable to FN#0 only */ +#define SCU_SMU_TCA_OFFSET 0x0400 +/* Accessable to all FN#s */ +#define SCU_SMU_MT_MLAR0_OFFSET 0x2000 +#define SCU_SMU_MT_MUAR0_OFFSET 0x2004 +#define SCU_SMU_MT_MDR0_OFFSET 0x2008 +#define SCU_SMU_MT_VCR0_OFFSET 0x200C +#define SCU_SMU_MT_MLAR1_OFFSET 0x2010 +#define SCU_SMU_MT_MUAR1_OFFSET 0x2014 +#define SCU_SMU_MT_MDR1_OFFSET 0x2018 +#define SCU_SMU_MT_VCR1_OFFSET 0x201C +#define SCU_SMU_MPBA_OFFSET 0x3000 + +/** + * struct smu_registers - These are the SMU registers + * + * + */ +struct smu_registers { +/* 0x0000 PCP */ + u32 post_context_port; +/* 0x0004 AMR */ + u32 address_modifier; + u32 reserved_08; + u32 reserved_0C; +/* 0x0010 ISR */ + u32 interrupt_status; +/* 0x0014 IMR */ + u32 interrupt_mask; +/* 0x0018 ICC */ + u32 interrupt_coalesce_control; + u32 reserved_1C; +/* 0x0020 HTTLBAR */ + u32 host_task_table_lower; +/* 0x0024 HTTUBAR */ + u32 host_task_table_upper; +/* 0x0028 TCR */ + u32 task_context_range; + u32 reserved_2C; +/* 0x0030 CQLBAR */ + u32 completion_queue_lower; +/* 0x0034 CQUBAR */ + u32 completion_queue_upper; + u32 reserved_38; + u32 reserved_3C; +/* 0x0040 CQPR */ + u32 completion_queue_put; +/* 0x0044 CQGR */ + u32 completion_queue_get; +/* 0x0048 CQC */ + u32 completion_queue_control; + u32 reserved_4C; + u32 reserved_5x[4]; + u32 reserved_6x[4]; + u32 reserved_7x[4]; +/* + * Accessable to FN#0 only + * 0x0080 RNCLBAR */ + u32 remote_node_context_lower; +/* 0x0084 RNCUBAR */ + u32 remote_node_context_upper; + u32 reserved_88; + u32 reserved_8C; +/* 0x0090 DCC */ + u32 device_context_capacity; +/* 0x0094 DFC */ + u32 device_function_capacity; +/* 0x0098 SMUCSR */ + u32 control_status; +/* 0x009C SCUSRCR */ + u32 soft_reset_control; +/* 0x00A0 SMAW */ + u32 mmr_address_window; +/* 0x00A4 SMDW */ + u32 mmr_data_window; + u32 reserved_A8; + u32 reserved_AC; +/* A whole bunch of reserved space */ + u32 reserved_Bx[4]; + u32 reserved_Cx[4]; + u32 reserved_Dx[4]; + u32 reserved_Ex[4]; + u32 reserved_Fx[4]; + u32 reserved_1xx[64]; + u32 reserved_2xx[64]; + u32 reserved_3xx[64]; +/* + * Accessable to FN#0 only + * 0x0400 TCA */ + u32 task_context_assignment[256]; +/* MSI-X registers not included */ +}; + +/* + * ***************************************************************************** + * SDMA Registers + * ***************************************************************************** */ +#define SCU_SDMA_BASE 0x6000 +#define SCU_SDMA_PUFATLHAR_OFFSET 0x0000 +#define SCU_SDMA_PUFATUHAR_OFFSET 0x0004 +#define SCU_SDMA_UFLHBAR_OFFSET 0x0008 +#define SCU_SDMA_UFUHBAR_OFFSET 0x000C +#define SCU_SDMA_UFQC_OFFSET 0x0010 +#define SCU_SDMA_UFQPP_OFFSET 0x0014 +#define SCU_SDMA_UFQGP_OFFSET 0x0018 +#define SCU_SDMA_PDMACR_OFFSET 0x001C +#define SCU_SDMA_CDMACR_OFFSET 0x0080 + +/** + * struct scu_sdma_registers - These are the SCU SDMA Registers + * + * + */ +struct scu_sdma_registers { +/* 0x0000 PUFATLHAR */ + u32 uf_address_table_lower; +/* 0x0004 PUFATUHAR */ + u32 uf_address_table_upper; +/* 0x0008 UFLHBAR */ + u32 uf_header_base_address_lower; +/* 0x000C UFUHBAR */ + u32 uf_header_base_address_upper; +/* 0x0010 UFQC */ + u32 unsolicited_frame_queue_control; +/* 0x0014 UFQPP */ + u32 unsolicited_frame_put_pointer; +/* 0x0018 UFQGP */ + u32 unsolicited_frame_get_pointer; +/* 0x001C PDMACR */ + u32 pdma_configuration; +/* Reserved until offset 0x80 */ + u32 reserved_0020_007C[0x18]; +/* 0x0080 CDMACR */ + u32 cdma_configuration; +/* Remainder SDMA register space */ + u32 reserved_0084_0400[0xDF]; + +}; + +/* + * ***************************************************************************** + * * SCU Link Registers + * ***************************************************************************** */ +#define SCU_PEG0_OFFSET 0x0000 +#define SCU_PEG1_OFFSET 0x8000 + +#define SCU_TL0_OFFSET 0x0000 +#define SCU_TL1_OFFSET 0x0400 +#define SCU_TL2_OFFSET 0x0800 +#define SCU_TL3_OFFSET 0x0C00 + +#define SCU_LL_OFFSET 0x0080 +#define SCU_LL0_OFFSET (SCU_TL0_OFFSET + SCU_LL_OFFSET) +#define SCU_LL1_OFFSET (SCU_TL1_OFFSET + SCU_LL_OFFSET) +#define SCU_LL2_OFFSET (SCU_TL2_OFFSET + SCU_LL_OFFSET) +#define SCU_LL3_OFFSET (SCU_TL3_OFFSET + SCU_LL_OFFSET) + +/* Transport Layer Offsets (PEG + TL) */ +#define SCU_TLCR_OFFSET 0x0000 +#define SCU_TLADTR_OFFSET 0x0004 +#define SCU_TLTTMR_OFFSET 0x0008 +#define SCU_TLEECR0_OFFSET 0x000C +#define SCU_STPTLDARNI_OFFSET 0x0010 + + +#define SCU_TLCR_HASH_SAS_CHECKING_ENABLE_SHIFT (0) +#define SCU_TLCR_HASH_SAS_CHECKING_ENABLE_MASK (0x00000001) +#define SCU_TLCR_CLEAR_TCI_NCQ_MAPPING_TABLE_SHIFT (1) +#define SCU_TLCR_CLEAR_TCI_NCQ_MAPPING_TABLE_MASK (0x00000002) +#define SCU_TLCR_STP_WRITE_DATA_PREFETCH_SHIFT (3) +#define SCU_TLCR_STP_WRITE_DATA_PREFETCH_MASK (0x00000008) +#define SCU_TLCR_CMD_NAK_STATUS_CODE_SHIFT (4) +#define SCU_TLCR_CMD_NAK_STATUS_CODE_MASK (0x00000010) +#define SCU_TLCR_RESERVED_MASK (0xFFFFFFEB) + +#define SCU_TLCR_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_TLCR_ ## name) + +/** + * struct scu_transport_layer_registers - These are the SCU Transport Layer + * registers + * + * + */ +struct scu_transport_layer_registers { + /* 0x0000 TLCR */ + u32 control; + /* 0x0004 TLADTR */ + u32 arbitration_delay_timer; + /* 0x0008 TLTTMR */ + u32 timer_test_mode; + /* 0x000C reserved */ + u32 reserved_0C; + /* 0x0010 STPTLDARNI */ + u32 stp_rni; + /* 0x0014 TLFEWPORCTRL */ + u32 tlfe_wpo_read_control; + /* 0x0018 TLFEWPORDATA */ + u32 tlfe_wpo_read_data; + /* 0x001C RXTLSSCSR1 */ + u32 rxtl_single_step_control_status_1; + /* 0x0020 RXTLSSCSR2 */ + u32 rxtl_single_step_control_status_2; + /* 0x0024 AWTRDDCR */ + u32 tlfe_awt_retry_delay_debug_control; + /* Remainder of TL memory space */ + u32 reserved_0028_007F[0x16]; + +}; + +/* Protocol Engine Group Registers */ +#define SCU_SCUVZECRx_OFFSET 0x1080 + +/* Link Layer Offsets (PEG + TL + LL) */ +#define SCU_SAS_SPDTOV_OFFSET 0x0000 +#define SCU_SAS_LLSTA_OFFSET 0x0004 +#define SCU_SATA_PSELTOV_OFFSET 0x0008 +#define SCU_SAS_TIMETOV_OFFSET 0x0010 +#define SCU_SAS_LOSTOT_OFFSET 0x0014 +#define SCU_SAS_LNKTOV_OFFSET 0x0018 +#define SCU_SAS_PHYTOV_OFFSET 0x001C +#define SCU_SAS_AFERCNT_OFFSET 0x0020 +#define SCU_SAS_WERCNT_OFFSET 0x0024 +#define SCU_SAS_TIID_OFFSET 0x0028 +#define SCU_SAS_TIDNH_OFFSET 0x002C +#define SCU_SAS_TIDNL_OFFSET 0x0030 +#define SCU_SAS_TISSAH_OFFSET 0x0034 +#define SCU_SAS_TISSAL_OFFSET 0x0038 +#define SCU_SAS_TIPID_OFFSET 0x003C +#define SCU_SAS_TIRES2_OFFSET 0x0040 +#define SCU_SAS_ADRSTA_OFFSET 0x0044 +#define SCU_SAS_MAWTTOV_OFFSET 0x0048 +#define SCU_SAS_FRPLDFIL_OFFSET 0x0054 +#define SCU_SAS_RFCNT_OFFSET 0x0060 +#define SCU_SAS_TFCNT_OFFSET 0x0064 +#define SCU_SAS_RFDCNT_OFFSET 0x0068 +#define SCU_SAS_TFDCNT_OFFSET 0x006C +#define SCU_SAS_LERCNT_OFFSET 0x0070 +#define SCU_SAS_RDISERRCNT_OFFSET 0x0074 +#define SCU_SAS_CRERCNT_OFFSET 0x0078 +#define SCU_STPCTL_OFFSET 0x007C +#define SCU_SAS_PCFG_OFFSET 0x0080 +#define SCU_SAS_CLKSM_OFFSET 0x0084 +#define SCU_SAS_TXCOMWAKE_OFFSET 0x0088 +#define SCU_SAS_TXCOMINIT_OFFSET 0x008C +#define SCU_SAS_TXCOMSAS_OFFSET 0x0090 +#define SCU_SAS_COMINIT_OFFSET 0x0094 +#define SCU_SAS_COMWAKE_OFFSET 0x0098 +#define SCU_SAS_COMSAS_OFFSET 0x009C +#define SCU_SAS_SFERCNT_OFFSET 0x00A0 +#define SCU_SAS_CDFERCNT_OFFSET 0x00A4 +#define SCU_SAS_DNFERCNT_OFFSET 0x00A8 +#define SCU_SAS_PRSTERCNT_OFFSET 0x00AC +#define SCU_SAS_CNTCTL_OFFSET 0x00B0 +#define SCU_SAS_SSPTOV_OFFSET 0x00B4 +#define SCU_FTCTL_OFFSET 0x00B8 +#define SCU_FRCTL_OFFSET 0x00BC +#define SCU_FTWMRK_OFFSET 0x00C0 +#define SCU_ENSPINUP_OFFSET 0x00C4 +#define SCU_SAS_TRNTOV_OFFSET 0x00C8 +#define SCU_SAS_PHYCAP_OFFSET 0x00CC +#define SCU_SAS_PHYCTL_OFFSET 0x00D0 +#define SCU_SAS_LLCTL_OFFSET 0x00D8 +#define SCU_AFE_XCVRCR_OFFSET 0x00DC +#define SCU_AFE_LUTCR_OFFSET 0x00E0 + +#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_SHIFT (0) +#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_MASK (0x00000003) +#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN1 (0) +#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN2 (1) +#define SCU_SAS_LINK_LAYER_CONTROL_MAX_LINK_RATE_GEN3 (2) +#define SCU_SAS_LINK_LAYER_CONTROL_BROADCAST_PRIMITIVE_SHIFT (2) +#define SCU_SAS_LINK_LAYER_CONTROL_BROADCAST_PRIMITIVE_MASK (0x000003FC) +#define SCU_SAS_LINK_LAYER_CONTROL_CLOSE_NO_ACTIVE_TASK_DISABLE_SHIFT (16) +#define SCU_SAS_LINK_LAYER_CONTROL_CLOSE_NO_ACTIVE_TASK_DISABLE_MASK (0x00010000) +#define SCU_SAS_LINK_LAYER_CONTROL_CLOSE_NO_OUTBOUND_TASK_DISABLE_SHIFT (17) +#define SCU_SAS_LINK_LAYER_CONTROL_CLOSE_NO_OUTBOUND_TASK_DISABLE_MASK (0x00020000) +#define SCU_SAS_LINK_LAYER_CONTROL_NO_OUTBOUND_TASK_TIMEOUT_SHIFT (24) +#define SCU_SAS_LINK_LAYER_CONTROL_NO_OUTBOUND_TASK_TIMEOUT_MASK (0xFF000000) +#define SCU_SAS_LINK_LAYER_CONTROL_RESERVED (0x00FCFC00) + +#define SCU_SAS_LLCTL_GEN_VAL(name, value) \ + SCU_GEN_VALUE(SCU_SAS_LINK_LAYER_CONTROL_ ## name, value) + +#define SCU_SAS_LLCTL_GEN_BIT(name) \ + SCU_GEN_BIT(SCU_SAS_LINK_LAYER_CONTROL_ ## name) + + +/* #define SCU_FRXHECR_DCNT_OFFSET 0x00B0 */ +#define SCU_PSZGCR_OFFSET 0x00E4 +#define SCU_SAS_RECPHYCAP_OFFSET 0x00E8 +/* #define SCU_TX_LUTSEL_OFFSET 0x00B8 */ + +#define SCU_SAS_PTxC_OFFSET 0x00D4 /* Same offset as SAS_TCTSTM */ + +/** + * struct scu_link_layer_registers - SCU Link Layer Registers + * + * + */ +struct scu_link_layer_registers { +/* 0x0000 SAS_SPDTOV */ + u32 speed_negotiation_timers; +/* 0x0004 SAS_LLSTA */ + u32 link_layer_status; +/* 0x0008 SATA_PSELTOV */ + u32 port_selector_timeout; + u32 reserved0C; +/* 0x0010 SAS_TIMETOV */ + u32 timeout_unit_value; +/* 0x0014 SAS_RCDTOV */ + u32 rcd_timeout; +/* 0x0018 SAS_LNKTOV */ + u32 link_timer_timeouts; +/* 0x001C SAS_PHYTOV */ + u32 sas_phy_timeouts; +/* 0x0020 SAS_AFERCNT */ + u32 received_address_frame_error_counter; +/* 0x0024 SAS_WERCNT */ + u32 invalid_dword_counter; +/* 0x0028 SAS_TIID */ + u32 transmit_identification; +/* 0x002C SAS_TIDNH */ + u32 sas_device_name_high; +/* 0x0030 SAS_TIDNL */ + u32 sas_device_name_low; +/* 0x0034 SAS_TISSAH */ + u32 source_sas_address_high; +/* 0x0038 SAS_TISSAL */ + u32 source_sas_address_low; +/* 0x003C SAS_TIPID */ + u32 identify_frame_phy_id; +/* 0x0040 SAS_TIRES2 */ + u32 identify_frame_reserved; +/* 0x0044 SAS_ADRSTA */ + u32 received_address_frame; +/* 0x0048 SAS_MAWTTOV */ + u32 maximum_arbitration_wait_timer_timeout; +/* 0x004C SAS_PTxC */ + u32 transmit_primitive; +/* 0x0050 SAS_RORES */ + u32 error_counter_event_notification_control; +/* 0x0054 SAS_FRPLDFIL */ + u32 frxq_payload_fill_threshold; +/* 0x0058 SAS_LLHANG_TOT */ + u32 link_layer_hang_detection_timeout; + u32 reserved_5C; +/* 0x0060 SAS_RFCNT */ + u32 received_frame_count; +/* 0x0064 SAS_TFCNT */ + u32 transmit_frame_count; +/* 0x0068 SAS_RFDCNT */ + u32 received_dword_count; +/* 0x006C SAS_TFDCNT */ + u32 transmit_dword_count; +/* 0x0070 SAS_LERCNT */ + u32 loss_of_sync_error_count; +/* 0x0074 SAS_RDISERRCNT */ + u32 running_disparity_error_count; +/* 0x0078 SAS_CRERCNT */ + u32 received_frame_crc_error_count; +/* 0x007C STPCTL */ + u32 stp_control; +/* 0x0080 SAS_PCFG */ + u32 phy_configuration; +/* 0x0084 SAS_CLKSM */ + u32 clock_skew_management; +/* 0x0088 SAS_TXCOMWAKE */ + u32 transmit_comwake_signal; +/* 0x008C SAS_TXCOMINIT */ + u32 transmit_cominit_signal; +/* 0x0090 SAS_TXCOMSAS */ + u32 transmit_comsas_signal; +/* 0x0094 SAS_COMINIT */ + u32 cominit_control; +/* 0x0098 SAS_COMWAKE */ + u32 comwake_control; +/* 0x009C SAS_COMSAS */ + u32 comsas_control; +/* 0x00A0 SAS_SFERCNT */ + u32 received_short_frame_count; +/* 0x00A4 SAS_CDFERCNT */ + u32 received_frame_without_credit_count; +/* 0x00A8 SAS_DNFERCNT */ + u32 received_frame_after_done_count; +/* 0x00AC SAS_PRSTERCNT */ + u32 phy_reset_problem_count; +/* 0x00B0 SAS_CNTCTL */ + u32 counter_control; +/* 0x00B4 SAS_SSPTOV */ + u32 ssp_timer_timeout_values; +/* 0x00B8 FTCTL */ + u32 ftx_control; +/* 0x00BC FRCTL */ + u32 frx_control; +/* 0x00C0 FTWMRK */ + u32 ftx_watermark; +/* 0x00C4 ENSPINUP */ + u32 notify_enable_spinup_control; +/* 0x00C8 SAS_TRNTOV */ + u32 sas_training_sequence_timer_values; +/* 0x00CC SAS_PHYCAP */ + u32 phy_capabilities; +/* 0x00D0 SAS_PHYCTL */ + u32 phy_control; + u32 reserved_d4; +/* 0x00D8 LLCTL */ + u32 link_layer_control; +/* 0x00DC AFE_XCVRCR */ + u32 afe_xcvr_control; +/* 0x00E0 AFE_LUTCR */ + u32 afe_lookup_table_control; +/* 0x00E4 PSZGCR */ + u32 phy_source_zone_group_control; +/* 0x00E8 SAS_RECPHYCAP */ + u32 receive_phycap; + u32 reserved_ec; +/* 0x00F0 SNAFERXRSTCTL */ + u32 speed_negotiation_afe_rx_reset_control; +/* 0x00F4 SAS_SSIPMCTL */ + u32 power_management_control; +/* 0x00F8 SAS_PSPREQ_PRIM */ + u32 sas_pm_partial_request_primitive; +/* 0x00FC SAS_PSSREQ_PRIM */ + u32 sas_pm_slumber_request_primitive; +/* 0x0100 SAS_PPSACK_PRIM */ + u32 sas_pm_ack_primitive_register; +/* 0x0104 SAS_PSNAK_PRIM */ + u32 sas_pm_nak_primitive_register; +/* 0x0108 SAS_SSIPMTOV */ + u32 sas_primitive_timeout; + u32 reserved_10c; +/* 0x0110 - 0x011C PLAPRDCTRLxREG */ + u32 pla_product_control[4]; +/* 0x0120 PLAPRDSUMREG */ + u32 pla_product_sum; +/* 0x0124 PLACONTROLREG */ + u32 pla_control; +/* Remainder of memory space 896 bytes */ + u32 reserved_0128_037f[0x96]; + +}; + +/* + * 0x00D4 // Same offset as SAS_TCTSTM SAS_PTxC + * u32 primitive_transmit_control; */ + +/* + * ---------------------------------------------------------------------------- + * SGPIO + * ---------------------------------------------------------------------------- */ +#define SCU_SGPIO_OFFSET 0x1400 + +/* #define SCU_SGPIO_OFFSET 0x6000 // later moves to 0x1400 see HSD 652625 */ +#define SCU_SGPIO_SGICR_OFFSET 0x0000 +#define SCU_SGPIO_SGPBR_OFFSET 0x0004 +#define SCU_SGPIO_SGSDLR_OFFSET 0x0008 +#define SCU_SGPIO_SGSDUR_OFFSET 0x000C +#define SCU_SGPIO_SGSIDLR_OFFSET 0x0010 +#define SCU_SGPIO_SGSIDUR_OFFSET 0x0014 +#define SCU_SGPIO_SGVSCR_OFFSET 0x0018 +/* Address from 0x0820 to 0x083C */ +#define SCU_SGPIO_SGODSR_OFFSET 0x0020 + +/** + * struct scu_sgpio_registers - SCU SGPIO Registers + * + * + */ +struct scu_sgpio_registers { +/* 0x0000 SGPIO_SGICR */ + u32 interface_control; +/* 0x0004 SGPIO_SGPBR */ + u32 blink_rate; +/* 0x0008 SGPIO_SGSDLR */ + u32 start_drive_lower; +/* 0x000C SGPIO_SGSDUR */ + u32 start_drive_upper; +/* 0x0010 SGPIO_SGSIDLR */ + u32 serial_input_lower; +/* 0x0014 SGPIO_SGSIDUR */ + u32 serial_input_upper; +/* 0x0018 SGPIO_SGVSCR */ + u32 vendor_specific_code; +/* 0x0020 SGPIO_SGODSR */ + u32 ouput_data_select[8]; +/* Remainder of memory space 256 bytes */ + u32 reserved_1444_14ff[0x31]; + +}; + +/* + * ***************************************************************************** + * * Defines for VIIT entry offsets + * * Access additional entries by SCU_VIIT_BASE + index * 0x10 + * ***************************************************************************** */ +#define SCU_VIIT_BASE 0x1c00 + +struct SCU_VIIT_REGISTERS { + u32 registers[256]; +}; + +/* + * ***************************************************************************** + * * SCU PORT TASK SCHEDULER REGISTERS + * ***************************************************************************** */ + +#define SCU_PTSG_BASE 0x1000 + +#define SCU_PTSG_PTSGCR_OFFSET 0x0000 +#define SCU_PTSG_RTCR_OFFSET 0x0004 +#define SCU_PTSG_RTCCR_OFFSET 0x0008 +#define SCU_PTSG_PTS0CR_OFFSET 0x0010 +#define SCU_PTSG_PTS0SR_OFFSET 0x0014 +#define SCU_PTSG_PTS1CR_OFFSET 0x0018 +#define SCU_PTSG_PTS1SR_OFFSET 0x001C +#define SCU_PTSG_PTS2CR_OFFSET 0x0020 +#define SCU_PTSG_PTS2SR_OFFSET 0x0024 +#define SCU_PTSG_PTS3CR_OFFSET 0x0028 +#define SCU_PTSG_PTS3SR_OFFSET 0x002C +#define SCU_PTSG_PCSPE0CR_OFFSET 0x0030 +#define SCU_PTSG_PCSPE1CR_OFFSET 0x0034 +#define SCU_PTSG_PCSPE2CR_OFFSET 0x0038 +#define SCU_PTSG_PCSPE3CR_OFFSET 0x003C +#define SCU_PTSG_ETMTSCCR_OFFSET 0x0040 +#define SCU_PTSG_ETMRNSCCR_OFFSET 0x0044 + +/** + * struct scu_port_task_scheduler_registers - These are the control/stats pairs + * for each Port Task Scheduler. + * + * + */ +struct scu_port_task_scheduler_registers { + u32 control; + u32 status; +}; + +typedef u32 SCU_PORT_PE_CONFIGURATION_REGISTER_T; + +/** + * struct scu_port_task_scheduler_group_registers - These are the PORT Task + * Scheduler registers + * + * + */ +struct scu_port_task_scheduler_group_registers { +/* 0x0000 PTSGCR */ + u32 control; +/* 0x0004 RTCR */ + u32 real_time_clock; +/* 0x0008 RTCCR */ + u32 real_time_clock_control; +/* 0x000C */ + u32 reserved_0C; +/* + * 0x0010 PTS0CR + * 0x0014 PTS0SR + * 0x0018 PTS1CR + * 0x001C PTS1SR + * 0x0020 PTS2CR + * 0x0024 PTS2SR + * 0x0028 PTS3CR + * 0x002C PTS3SR */ + struct scu_port_task_scheduler_registers port[4]; +/* + * 0x0030 PCSPE0CR + * 0x0034 PCSPE1CR + * 0x0038 PCSPE2CR + * 0x003C PCSPE3CR */ + SCU_PORT_PE_CONFIGURATION_REGISTER_T protocol_engine[4]; +/* 0x0040 ETMTSCCR */ + u32 tc_scanning_interval_control; +/* 0x0044 ETMRNSCCR */ + u32 rnc_scanning_interval_control; +/* Remainder of memory space 128 bytes */ + u32 reserved_1048_107f[0x0E]; + +}; + +#define SCU_PTSG_SCUVZECR_OFFSET 0x003C + +/* + * ***************************************************************************** + * * AFE REGISTERS + * ***************************************************************************** */ +#define SCU_AFE_MMR_BASE 0xE000 + +/* + * AFE 0 is at offset 0x0800 + * AFE 1 is at offset 0x0900 + * AFE 2 is at offset 0x0a00 + * AFE 3 is at offset 0x0b00 */ +struct scu_afe_transceiver { + /* 0x0000 AFE_XCVR_CTRL0 */ + u32 afe_xcvr_control0; + /* 0x0004 AFE_XCVR_CTRL1 */ + u32 afe_xcvr_control1; + /* 0x0008 */ + u32 reserved_0008; + /* 0x000c afe_dfx_rx_control0 */ + u32 afe_dfx_rx_control0; + /* 0x0010 AFE_DFX_RX_CTRL1 */ + u32 afe_dfx_rx_control1; + /* 0x0014 */ + u32 reserved_0014; + /* 0x0018 AFE_DFX_RX_STS0 */ + u32 afe_dfx_rx_status0; + /* 0x001c AFE_DFX_RX_STS1 */ + u32 afe_dfx_rx_status1; + /* 0x0020 */ + u32 reserved_0020; + /* 0x0024 AFE_TX_CTRL */ + u32 afe_tx_control; + /* 0x0028 AFE_TX_AMP_CTRL0 */ + u32 afe_tx_amp_control0; + /* 0x002c AFE_TX_AMP_CTRL1 */ + u32 afe_tx_amp_control1; + /* 0x0030 AFE_TX_AMP_CTRL2 */ + u32 afe_tx_amp_control2; + /* 0x0034 AFE_TX_AMP_CTRL3 */ + u32 afe_tx_amp_control3; + /* 0x0038 afe_tx_ssc_control */ + u32 afe_tx_ssc_control; + /* 0x003c */ + u32 reserved_003c; + /* 0x0040 AFE_RX_SSC_CTRL0 */ + u32 afe_rx_ssc_control0; + /* 0x0044 AFE_RX_SSC_CTRL1 */ + u32 afe_rx_ssc_control1; + /* 0x0048 AFE_RX_SSC_CTRL2 */ + u32 afe_rx_ssc_control2; + /* 0x004c AFE_RX_EQ_STS0 */ + u32 afe_rx_eq_status0; + /* 0x0050 AFE_RX_EQ_STS1 */ + u32 afe_rx_eq_status1; + /* 0x0054 AFE_RX_CDR_STS */ + u32 afe_rx_cdr_status; + /* 0x0058 */ + u32 reserved_0058; + /* 0x005c AFE_CHAN_CTRL */ + u32 afe_channel_control; + /* 0x0060-0x006c */ + u32 reserved_0060_006c[0x04]; + /* 0x0070 AFE_XCVR_EC_STS0 */ + u32 afe_xcvr_error_capture_status0; + /* 0x0074 AFE_XCVR_EC_STS1 */ + u32 afe_xcvr_error_capture_status1; + /* 0x0078 AFE_XCVR_EC_STS2 */ + u32 afe_xcvr_error_capture_status2; + /* 0x007c afe_xcvr_ec_status3 */ + u32 afe_xcvr_error_capture_status3; + /* 0x0080 AFE_XCVR_EC_STS4 */ + u32 afe_xcvr_error_capture_status4; + /* 0x0084 AFE_XCVR_EC_STS5 */ + u32 afe_xcvr_error_capture_status5; + /* 0x0088-0x00fc */ + u32 reserved_008c_00fc[0x1e]; +}; + +/** + * struct scu_afe_registers - AFE Regsiters + * + * + */ +/* Uaoa AFE registers */ +struct scu_afe_registers { + /* 0Xe000 AFE_BIAS_CTRL */ + u32 afe_bias_control; + u32 reserved_0004; + /* 0x0008 AFE_PLL_CTRL0 */ + u32 afe_pll_control0; + /* 0x000c AFE_PLL_CTRL1 */ + u32 afe_pll_control1; + /* 0x0010 AFE_PLL_CTRL2 */ + u32 afe_pll_control2; + /* 0x0014 AFE_CB_STS */ + u32 afe_common_block_status; + /* 0x0018-0x007c */ + u32 reserved_18_7c[0x1a]; + /* 0x0080 AFE_PMSN_MCTRL0 */ + u32 afe_pmsn_master_control0; + /* 0x0084 AFE_PMSN_MCTRL1 */ + u32 afe_pmsn_master_control1; + /* 0x0088 AFE_PMSN_MCTRL2 */ + u32 afe_pmsn_master_control2; + /* 0x008C-0x00fc */ + u32 reserved_008c_00fc[0x1D]; + /* 0x0100 AFE_DFX_MST_CTRL0 */ + u32 afe_dfx_master_control0; + /* 0x0104 AFE_DFX_MST_CTRL1 */ + u32 afe_dfx_master_control1; + /* 0x0108 AFE_DFX_DCL_CTRL */ + u32 afe_dfx_dcl_control; + /* 0x010c AFE_DFX_DMON_CTRL */ + u32 afe_dfx_digital_monitor_control; + /* 0x0110 AFE_DFX_AMONP_CTRL */ + u32 afe_dfx_analog_p_monitor_control; + /* 0x0114 AFE_DFX_AMONN_CTRL */ + u32 afe_dfx_analog_n_monitor_control; + /* 0x0118 AFE_DFX_NTL_STS */ + u32 afe_dfx_ntl_status; + /* 0x011c AFE_DFX_FIFO_STS0 */ + u32 afe_dfx_fifo_status0; + /* 0x0120 AFE_DFX_FIFO_STS1 */ + u32 afe_dfx_fifo_status1; + /* 0x0124 AFE_DFX_MPAT_CTRL */ + u32 afe_dfx_master_pattern_control; + /* 0x0128 AFE_DFX_P0_CTRL */ + u32 afe_dfx_p0_control; + /* 0x012c-0x01a8 AFE_DFX_P0_DRx */ + u32 afe_dfx_p0_data[32]; + /* 0x01ac */ + u32 reserved_01ac; + /* 0x01b0-0x020c AFE_DFX_P0_IRx */ + u32 afe_dfx_p0_instruction[24]; + /* 0x0210 */ + u32 reserved_0210; + /* 0x0214 AFE_DFX_P1_CTRL */ + u32 afe_dfx_p1_control; + /* 0x0218-0x245 AFE_DFX_P1_DRx */ + u32 afe_dfx_p1_data[16]; + /* 0x0258-0x029c */ + u32 reserved_0258_029c[0x12]; + /* 0x02a0-0x02bc AFE_DFX_P1_IRx */ + u32 afe_dfx_p1_instruction[8]; + /* 0x02c0-0x2fc */ + u32 reserved_02c0_02fc[0x10]; + /* 0x0300 AFE_DFX_TX_PMSN_CTRL */ + u32 afe_dfx_tx_pmsn_control; + /* 0x0304 AFE_DFX_RX_PMSN_CTRL */ + u32 afe_dfx_rx_pmsn_control; + u32 reserved_0308; + /* 0x030c AFE_DFX_NOA_CTRL0 */ + u32 afe_dfx_noa_control0; + /* 0x0310 AFE_DFX_NOA_CTRL1 */ + u32 afe_dfx_noa_control1; + /* 0x0314 AFE_DFX_NOA_CTRL2 */ + u32 afe_dfx_noa_control2; + /* 0x0318 AFE_DFX_NOA_CTRL3 */ + u32 afe_dfx_noa_control3; + /* 0x031c AFE_DFX_NOA_CTRL4 */ + u32 afe_dfx_noa_control4; + /* 0x0320 AFE_DFX_NOA_CTRL5 */ + u32 afe_dfx_noa_control5; + /* 0x0324 AFE_DFX_NOA_CTRL6 */ + u32 afe_dfx_noa_control6; + /* 0x0328 AFE_DFX_NOA_CTRL7 */ + u32 afe_dfx_noa_control7; + /* 0x032c-0x07fc */ + u32 reserved_032c_07fc[0x135]; + + /* 0x0800-0x0bfc */ + struct scu_afe_transceiver scu_afe_xcvr[4]; + + /* 0x0c00-0x0ffc */ + u32 reserved_0c00_0ffc[0x0100]; +}; + +struct SCU_PROTOCOL_ENGINE_GROUP_REGISTERS { + u32 table[0xE0]; +}; + + +struct SCU_VIIT_IIT { + u32 table[256]; +}; + +/** + * Placeholder for the ZONE Partition Table information ZONING will not be + * included in the 1.1 release. + * + * + */ +struct SCU_ZONE_PARTITION_TABLE { + u32 table[2048]; +}; + +/** + * Placeholder for the CRAM register since I am not sure if we need to + * read/write to these registers as yet. + * + * + */ +struct SCU_COMPLETION_RAM { + u32 ram[128]; +}; + +/** + * Placeholder for the FBRAM registers since I am not sure if we need to + * read/write to these registers as yet. + * + * + */ +struct SCU_FRAME_BUFFER_RAM { + u32 ram[128]; +}; + +#define SCU_SCRATCH_RAM_SIZE_IN_DWORDS 256 + +/** + * Placeholder for the scratch RAM registers. + * + * + */ +struct SCU_SCRATCH_RAM { + u32 ram[SCU_SCRATCH_RAM_SIZE_IN_DWORDS]; +}; + +/** + * Placeholder since I am not yet sure what these registers are here for. + * + * + */ +struct NOA_PROTOCOL_ENGINE_PARTITION { + u32 reserved[64]; +}; + +/** + * Placeholder since I am not yet sure what these registers are here for. + * + * + */ +struct NOA_HUB_PARTITION { + u32 reserved[64]; +}; + +/** + * Placeholder since I am not yet sure what these registers are here for. + * + * + */ +struct NOA_HOST_INTERFACE_PARTITION { + u32 reserved[64]; +}; + +/** + * struct TRANSPORT_LINK_LAYER_PAIR - The SCU Hardware pairs up the TL + * registers with the LL registers so we must place them adjcent to make the + * array of registers in the PEG. + * + * + */ +struct TRANSPORT_LINK_LAYER_PAIR { + struct scu_transport_layer_registers tl; + struct scu_link_layer_registers ll; +}; + +/** + * struct SCU_PEG_REGISTERS - SCU Protocol Engine Memory mapped register space. + * These registers are unique to each protocol engine group. There can be + * at most two PEG for a single SCU part. + * + * + */ +struct SCU_PEG_REGISTERS { + struct TRANSPORT_LINK_LAYER_PAIR pe[4]; + struct scu_port_task_scheduler_group_registers ptsg; + struct SCU_PROTOCOL_ENGINE_GROUP_REGISTERS peg; + struct scu_sgpio_registers sgpio; + u32 reserved_01500_1BFF[0x1C0]; + struct scu_viit_entry viit[64]; + struct SCU_ZONE_PARTITION_TABLE zpt0; + struct SCU_ZONE_PARTITION_TABLE zpt1; +}; + +/** + * struct scu_registers - SCU regsiters including both PEG registers if we turn + * on that compile option. All of these registers are in the memory mapped + * space returned from BAR1. + * + * + */ +struct scu_registers { + /* 0x0000 - PEG 0 */ + struct SCU_PEG_REGISTERS peg0; + + /* 0x6000 - SDMA and Miscellaneous */ + struct scu_sdma_registers sdma; + struct SCU_COMPLETION_RAM cram; + struct SCU_FRAME_BUFFER_RAM fbram; + u32 reserved_6800_69FF[0x80]; + struct NOA_PROTOCOL_ENGINE_PARTITION noa_pe; + struct NOA_HUB_PARTITION noa_hub; + struct NOA_HOST_INTERFACE_PARTITION noa_if; + u32 reserved_6d00_7fff[0x4c0]; + + /* 0x8000 - PEG 1 */ + struct SCU_PEG_REGISTERS peg1; + + /* 0xE000 - AFE Registers */ + struct scu_afe_registers afe; + + /* 0xF000 - reserved */ + u32 reserved_f000_211fff[0x80c00]; + + /* 0x212000 - scratch RAM */ + struct SCU_SCRATCH_RAM scratch_ram; + +}; + + +#endif /* _SCU_REGISTERS_HEADER_ */ diff --git a/drivers/scsi/isci/core/scu_remote_node_context.h b/drivers/scsi/isci/core/scu_remote_node_context.h new file mode 100644 index 000000000000..8006f2ee2d1d --- /dev/null +++ b/drivers/scsi/isci/core/scu_remote_node_context.h @@ -0,0 +1,230 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef __SCU_REMOTE_NODE_CONTEXT_HEADER__ +#define __SCU_REMOTE_NODE_CONTEXT_HEADER__ + +/** + * This file contains the structures and constatns used by the SCU hardware to + * describe a remote node context. + * + * + */ +#include "sci_types.h" + +/** + * struct ssp_remote_node_context - This structure contains the SCU hardware + * definition for an SSP remote node. + * + * + */ +struct ssp_remote_node_context { + /* WORD 0 */ + + /** + * This field is the remote node index assigned for this remote node. All + * remote nodes must have a unique remote node index. The value of the remote + * node index can not exceed the maximum number of remote nodes reported in + * the SCU device context capacity register. + */ + u32 remote_node_index:12; + u32 reserved0_1:4; + + /** + * This field tells the SCU hardware how many simultaneous connections that + * this remote node will support. + */ + u32 remote_node_port_width:4; + + /** + * This field tells the SCU hardware which logical port to associate with this + * remote node. + */ + u32 logical_port_index:3; + u32 reserved0_2:5; + + /** + * This field will enable the I_T nexus loss timer for this remote node. + */ + u32 nexus_loss_timer_enable:1; + + /** + * This field is the for driver debug only and is not used. + */ + u32 check_bit:1; + + /** + * This field must be set to true when the hardware DMAs the remote node + * context to the hardware SRAM. When the remote node is being invalidated + * this field must be set to false. + */ + u32 is_valid:1; + + /** + * This field must be set to true. + */ + u32 is_remote_node_context:1; + + /* WORD 1 - 2 */ + + /** + * This is the low word of the remote device SAS Address + */ + u32 remote_sas_address_lo; + + /** + * This field is the high word of the remote device SAS Address + */ + u32 remote_sas_address_hi; + + /* WORD 3 */ + /** + * This field reprensets the function number assigned to this remote device. + * This value must match the virtual function number that is being used to + * communicate to the device. + */ + u32 function_number:8; + u32 reserved3_1:8; + + /** + * This field provides the driver a way to cheat on the arbitration wait time + * for this remote node. + */ + u32 arbitration_wait_time:16; + + /* WORD 4 */ + /** + * This field tells the SCU hardware how long this device may occupy the + * connection before it must be closed. + */ + u32 connection_occupancy_timeout:16; + + /** + * This field tells the SCU hardware how long to maintain a connection when + * there are no frames being transmitted on the link. + */ + u32 connection_inactivity_timeout:16; + + /* WORD 5 */ + /** + * This field allows the driver to cheat on the arbitration wait time for this + * remote node. + */ + u32 initial_arbitration_wait_time:16; + + /** + * This field is tells the hardware what to program for the connection rate in + * the open address frame. See the SAS spec for valid values. + */ + u32 oaf_connection_rate:4; + + /** + * This field tells the SCU hardware what to program for the features in the + * open address frame. See the SAS spec for valid values. + */ + u32 oaf_features:4; + + /** + * This field tells the SCU hardware what to use for the source zone group in + * the open address frame. See the SAS spec for more details on zoning. + */ + u32 oaf_source_zone_group:8; + + /* WORD 6 */ + /** + * This field tells the SCU hardware what to use as the more capibilities in + * the open address frame. See the SAS Spec for details. + */ + u32 oaf_more_compatibility_features; + + /* WORD 7 */ + u32 reserved7; + +}; + +/** + * struct stp_remote_node_context - This structure contains the SCU hardware + * definition for a STP remote node. + * + * STP Targets are not yet supported so this definition is a placeholder until + * we do support them. + */ +struct stp_remote_node_context { + /** + * Placeholder data for the STP remote node. + */ + u32 data[8]; + +}; + +/** + * This union combines the SAS and SATA remote node definitions. + * + * union scu_remote_node_context + */ +union scu_remote_node_context { + /** + * SSP Remote Node + */ + struct ssp_remote_node_context ssp; + + /** + * STP Remote Node + */ + struct stp_remote_node_context stp; + +}; + +#endif /* __SCU_REMOTE_NODE_CONTEXT_HEADER__ */ diff --git a/drivers/scsi/isci/core/scu_task_context.h b/drivers/scsi/isci/core/scu_task_context.h new file mode 100644 index 000000000000..d08c51bb2262 --- /dev/null +++ b/drivers/scsi/isci/core/scu_task_context.h @@ -0,0 +1,943 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCU_TASK_CONTEXT_H_ +#define _SCU_TASK_CONTEXT_H_ + +/** + * This file contains the structures and constants for the SCU hardware task + * context. + * + * + */ + +#include "sci_types.h" + +/** + * enum SCU_SSP_TASK_TYPE - This enumberation defines the various SSP task + * types the SCU hardware will accept. The definition for the various task + * types the SCU hardware will accept can be found in the DS specification. + * + * + */ +typedef enum { + SCU_TASK_TYPE_IOREAD, /* /< IO READ direction or no direction */ + SCU_TASK_TYPE_IOWRITE, /* /< IO Write direction */ + SCU_TASK_TYPE_SMP_REQUEST, /* /< SMP Request type */ + SCU_TASK_TYPE_RESPONSE, /* /< Driver generated response frame (targt mode) */ + SCU_TASK_TYPE_RAW_FRAME, /* /< Raw frame request type */ + SCU_TASK_TYPE_PRIMITIVE /* /< Request for a primitive to be transmitted */ +} SCU_SSP_TASK_TYPE; + +/** + * enum SCU_SATA_TASK_TYPE - This enumeration defines the various SATA task + * types the SCU hardware will accept. The definition for the various task + * types the SCU hardware will accept can be found in the DS specification. + * + * + */ +typedef enum { + SCU_TASK_TYPE_DMA_IN, /* /< Read request */ + SCU_TASK_TYPE_FPDMAQ_READ, /* /< NCQ read request */ + SCU_TASK_TYPE_PACKET_DMA_IN, /* /< Packet read request */ + SCU_TASK_TYPE_SATA_RAW_FRAME, /* /< Raw frame request */ + RESERVED_4, + RESERVED_5, + RESERVED_6, + RESERVED_7, + SCU_TASK_TYPE_DMA_OUT, /* /< Write request */ + SCU_TASK_TYPE_FPDMAQ_WRITE, /* /< NCQ write Request */ + SCU_TASK_TYPE_PACKET_DMA_OUT /* /< Packet write request */ +} SCU_SATA_TASK_TYPE; + + +/** + * + * + * SCU_CONTEXT_TYPE + */ +#define SCU_TASK_CONTEXT_TYPE 0 +#define SCU_RNC_CONTEXT_TYPE 1 + +/** + * + * + * SCU_TASK_CONTEXT_VALIDITY + */ +#define SCU_TASK_CONTEXT_INVALID 0 +#define SCU_TASK_CONTEXT_VALID 1 + +/** + * + * + * SCU_COMMAND_CODE + */ +#define SCU_COMMAND_CODE_INITIATOR_NEW_TASK 0 +#define SCU_COMMAND_CODE_ACTIVE_TASK 1 +#define SCU_COMMAND_CODE_PRIMITIVE_SEQ_TASK 2 +#define SCU_COMMAND_CODE_TARGET_RAW_FRAMES 3 + +/** + * + * + * SCU_TASK_PRIORITY + */ +/** + * + * + * This priority is used when there is no priority request for this request. + */ +#define SCU_TASK_PRIORITY_NORMAL 0 + +/** + * + * + * This priority indicates that the task should be scheduled to the head of the + * queue. The task will NOT be executed if the TX is suspended for the remote + * node. + */ +#define SCU_TASK_PRIORITY_HEAD_OF_Q 1 + +/** + * + * + * This priority indicates that the task will be executed before all + * SCU_TASK_PRIORITY_NORMAL and SCU_TASK_PRIORITY_HEAD_OF_Q tasks. The task + * WILL be executed if the TX is suspended for the remote node. + */ +#define SCU_TASK_PRIORITY_HIGH 2 + +/** + * + * + * This task priority is reserved and should not be used. + */ +#define SCU_TASK_PRIORITY_RESERVED 3 + +#define SCU_TASK_INITIATOR_MODE 1 +#define SCU_TASK_TARGET_MODE 0 + +#define SCU_TASK_REGULAR 0 +#define SCU_TASK_ABORTED 1 + +/* direction bit defintion */ +/** + * + * + * SATA_DIRECTION + */ +#define SCU_SATA_WRITE_DATA_DIRECTION 0 +#define SCU_SATA_READ_DATA_DIRECTION 1 + +/** + * + * + * SCU_COMMAND_CONTEXT_MACROS These macros provide the mask and shift + * operations to construct the various SCU commands + */ +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_SHIFT 21 +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK 0x00E00000 +#define scu_get_command_request_type(x) \ + ((x) & SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK) + +#define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_SHIFT 18 +#define SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK 0x001C0000 +#define scu_get_command_request_subtype(x) \ + ((x) & SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK) + +#define SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK \ + (\ + SCU_CONTEXT_COMMAND_REQUEST_TYPE_MASK \ + | SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_MASK \ + ) +#define scu_get_command_request_full_type(x) \ + ((x) & SCU_CONTEXT_COMMAND_REQUEST_FULLTYPE_MASK) + +#define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_SHIFT 16 +#define SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK 0x00010000 +#define scu_get_command_protocl_engine_group(x) \ + ((x) & SCU_CONTEXT_COMMAND_PROTOCOL_ENGINE_GROUP_MASK) + +#define SCU_CONTEXT_COMMAND_LOGICAL_PORT_SHIFT 12 +#define SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK 0x00007000 +#define scu_get_command_reqeust_logical_port(x) \ + ((x) & SCU_CONTEXT_COMMAND_LOGICAL_PORT_MASK) + + +#define MAKE_SCU_CONTEXT_COMMAND_TYPE(type) \ + ((u32)(type) << SCU_CONTEXT_COMMAND_REQUEST_TYPE_SHIFT) + +/** + * MAKE_SCU_CONTEXT_COMMAND_TYPE() - + * + * SCU_COMMAND_TYPES These constants provide the grouping of the different SCU + * command types. + */ +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC MAKE_SCU_CONTEXT_COMMAND_TYPE(0) +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC MAKE_SCU_CONTEXT_COMMAND_TYPE(1) +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC MAKE_SCU_CONTEXT_COMMAND_TYPE(2) +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC MAKE_SCU_CONTEXT_COMMAND_TYPE(3) +#define SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC MAKE_SCU_CONTEXT_COMMAND_TYPE(6) + +#define MAKE_SCU_CONTEXT_COMMAND_REQUEST(type, command) \ + ((type) | ((command) << SCU_CONTEXT_COMMAND_REQUEST_SUBTYPE_SHIFT)) + +/** + * + * + * SCU_REQUEST_TYPES These constants are the various request types that can be + * posted to the SCU hardware. + */ +#define SCU_CONTEXT_COMMAND_REQUST_POST_TC \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC, 0)) + +#define SCU_CONTEXT_COMMAND_REQUEST_POST_TC_ABORT \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_TC, 1)) + +#define SCU_CONTEXT_COMMAND_REQUST_DUMP_TC \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_TC, 0)) + +#define SCU_CONTEXT_COMMAND_POST_RNC_32 \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC, 0)) + +#define SCU_CONTEXT_COMMAND_POST_RNC_96 \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC, 1)) + +#define SCU_CONTEXT_COMMAND_POST_RNC_INVALIDATE \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_POST_RNC, 2)) + +#define SCU_CONTEXT_COMMAND_DUMP_RNC_32 \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC, 0)) + +#define SCU_CONTEXT_COMMAND_DUMP_RNC_96 \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_DUMP_RNC, 1)) + +#define SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 0)) + +#define SCU_CONTEXT_COMMAND_POST_RNC_SUSPEND_TX_RX \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 1)) + +#define SCU_CONTEXT_COMMAND_POST_RNC_RESUME \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 2)) + +#define SCU_CONTEXT_IT_NEXUS_LOSS_TIMER_ENABLE \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 3)) + +#define SCU_CONTEXT_IT_NEXUS_LOSS_TIMER_DISABLE \ + (MAKE_SCU_CONTEXT_COMMAND_REQUEST(SCU_CONTEXT_COMMAND_REQUEST_TYPE_OTHER_RNC, 4)) + +/** + * + * + * SCU_TASK_CONTEXT_PROTOCOL SCU Task context protocol types this is uesd to + * program the SCU Task context protocol field in word 0x00. + */ +#define SCU_TASK_CONTEXT_PROTOCOL_SMP 0x00 +#define SCU_TASK_CONTEXT_PROTOCOL_SSP 0x01 +#define SCU_TASK_CONTEXT_PROTOCOL_STP 0x02 +#define SCU_TASK_CONTEXT_PROTOCOL_NONE 0x07 + +/** + * struct SSP_TASK_CONTEXT - This is the SCU hardware definition for an SSP + * request. + * + * + */ +struct SSP_TASK_CONTEXT { + /* OFFSET 0x18 */ + u32 reserved00:24; + u32 frame_type:8; + + /* OFFSET 0x1C */ + u32 reserved01; + + /* OFFSET 0x20 */ + u32 fill_bytes:2; + u32 reserved02:6; + u32 changing_data_pointer:1; + u32 retransmit:1; + u32 retry_data_frame:1; + u32 tlr_control:2; + u32 reserved03:19; + + /* OFFSET 0x24 */ + u32 uiRsvd4; + + /* OFFSET 0x28 */ + u32 target_port_transfer_tag:16; + u32 tag:16; + + /* OFFSET 0x2C */ + u32 data_offset; +}; + +/** + * struct STP_TASK_CONTEXT - This is the SCU hardware definition for an STP + * request. + * + * + */ +struct STP_TASK_CONTEXT { + /* OFFSET 0x18 */ + u32 fis_type:8; + u32 pm_port:4; + u32 reserved0:3; + u32 control:1; + u32 command:8; + u32 features:8; + + /* OFFSET 0x1C */ + u32 reserved1; + + /* OFFSET 0x20 */ + u32 reserved2; + + /* OFFSET 0x24 */ + u32 reserved3; + + /* OFFSET 0x28 */ + u32 ncq_tag:5; + u32 reserved4:27; + + /* OFFSET 0x2C */ + u32 data_offset; /* TODO: What is this used for? */ +}; + +/** + * struct SMP_TASK_CONTEXT - This is the SCU hardware definition for an SMP + * request. + * + * + */ +struct SMP_TASK_CONTEXT { + /* OFFSET 0x18 */ + u32 response_length:8; + u32 function_result:8; + u32 function:8; + u32 frame_type:8; + + /* OFFSET 0x1C */ + u32 smp_response_ufi:12; + u32 reserved1:20; + + /* OFFSET 0x20 */ + u32 reserved2; + + /* OFFSET 0x24 */ + u32 reserved3; + + /* OFFSET 0x28 */ + u32 reserved4; + + /* OFFSET 0x2C */ + u32 reserved5; +}; + +/** + * struct PRIMITIVE_TASK_CONTEXT - This is the SCU hardware definition used + * when the driver wants to send a primitive on the link. + * + * + */ +struct PRIMITIVE_TASK_CONTEXT { + /* OFFSET 0x18 */ + /** + * This field is the control word and it must be 0. + */ + u32 control; /* /< must be set to 0 */ + + /* OFFSET 0x1C */ + /** + * This field specifies the primitive that is to be transmitted. + */ + u32 sequence; + + /* OFFSET 0x20 */ + u32 reserved0; + + /* OFFSET 0x24 */ + u32 reserved1; + + /* OFFSET 0x28 */ + u32 reserved2; + + /* OFFSET 0x2C */ + u32 reserved3; +}; + +/** + * The union of the protocols that can be selected in the SCU task context + * field. + * + * PROTOCOL_CONTEXT + */ +union PROTOCOL_CONTEXT { + struct SSP_TASK_CONTEXT ssp; + struct STP_TASK_CONTEXT stp; + struct SMP_TASK_CONTEXT smp; + struct PRIMITIVE_TASK_CONTEXT primitive; + u32 words[6]; +}; + +/** + * struct scu_sgl_element - This structure represents a single SCU defined SGL + * element. SCU SGLs contain a 64 bit address with the maximum data transfer + * being 24 bits in size. The SGL can not cross a 4GB boundary. + * + * struct scu_sgl_element + */ +struct scu_sgl_element { + /** + * This field is the upper 32 bits of the 64 bit physical address. + */ + u32 address_upper; + + /** + * This field is the lower 32 bits of the 64 bit physical address. + */ + u32 address_lower; + + /** + * This field is the number of bytes to transfer. + */ + u32 length:24; + + /** + * This field is the address modifier to be used when a virtual function is + * requesting a data transfer. + */ + u32 address_modifier:8; + +}; + +#define SCU_SGL_ELEMENT_PAIR_A 0 +#define SCU_SGL_ELEMENT_PAIR_B 1 + +/** + * struct scu_sgl_element_pair - This structure is the SCU hardware definition + * of a pair of SGL elements. The SCU hardware always works on SGL pairs. + * They are refered to in the DS specification as SGL A and SGL B. Each SGL + * pair is followed by the address of the next pair. + * + * + */ +struct scu_sgl_element_pair { + /* OFFSET 0x60-0x68 */ + /** + * This field is the SGL element A of the SGL pair. + */ + struct scu_sgl_element A; + + /* OFFSET 0x6C-0x74 */ + /** + * This field is the SGL element B of the SGL pair. + */ + struct scu_sgl_element B; + + /* OFFSET 0x78-0x7C */ + /** + * This field is the upper 32 bits of the 64 bit address to the next SGL + * element pair. + */ + u32 next_pair_upper; + + /** + * This field is the lower 32 bits of the 64 bit address to the next SGL + * element pair. + */ + u32 next_pair_lower; + +}; + +/** + * struct TRANSPORT_SNAPSHOT - This structure is the SCU hardware scratch area + * for the task context. This is set to 0 by the driver but can be read by + * issuing a dump TC request to the SCU. + * + * + */ +struct TRANSPORT_SNAPSHOT { + /* OFFSET 0x48 */ + u32 xfer_rdy_write_data_length; + + /* OFFSET 0x4C */ + u32 data_offset; + + /* OFFSET 0x50 */ + u32 data_transfer_size:24; + u32 reserved_50_0:8; + + /* OFFSET 0x54 */ + u32 next_initiator_write_data_offset; + + /* OFFSET 0x58 */ + u32 next_initiator_write_data_xfer_size:24; + u32 reserved_58_0:8; +}; + +/** + * struct scu_task_context - This structure defines the contents of the SCU + * silicon task context. It lays out all of the fields according to the + * expected order and location for the Storage Controller unit. + * + * + */ +struct scu_task_context { + /* OFFSET 0x00 ------ */ + /** + * This field must be encoded to one of the valid SCU task priority values + * - SCU_TASK_PRIORITY_NORMAL + * - SCU_TASK_PRIORITY_HEAD_OF_Q + * - SCU_TASK_PRIORITY_HIGH + */ + u32 priority:2; + + /** + * This field must be set to true if this is an initiator generated request. + * Until target mode is supported all task requests are initiator requests. + */ + u32 initiator_request:1; + + /** + * This field must be set to one of the valid connection rates valid values + * are 0x8, 0x9, and 0xA. + */ + u32 connection_rate:4; + + /** + * This field muse be programed when generating an SMP response since the SMP + * connection remains open until the SMP response is generated. + */ + u32 protocol_engine_index:3; + + /** + * This field must contain the logical port for the task request. + */ + u32 logical_port_index:3; + + /** + * This field must be set to one of the SCU_TASK_CONTEXT_PROTOCOL values + * - SCU_TASK_CONTEXT_PROTOCOL_SMP + * - SCU_TASK_CONTEXT_PROTOCOL_SSP + * - SCU_TASK_CONTEXT_PROTOCOL_STP + * - SCU_TASK_CONTEXT_PROTOCOL_NONE + */ + u32 protocol_type:3; + + /** + * This filed must be set to the TCi allocated for this task + */ + u32 task_index:12; + + /** + * This field is reserved and must be set to 0x00 + */ + u32 reserved_00_0:1; + + /** + * For a normal task request this must be set to 0. If this is an abort of + * this task request it must be set to 1. + */ + u32 abort:1; + + /** + * This field must be set to true for the SCU hardware to process the task. + */ + u32 valid:1; + + /** + * This field must be set to SCU_TASK_CONTEXT_TYPE + */ + u32 context_type:1; + + /* OFFSET 0x04 */ + /** + * This field contains the RNi that is the target of this request. + */ + u32 remote_node_index:12; + + /** + * This field is programmed if this is a mirrored request, which we are not + * using, in which case it is the RNi for the mirrored target. + */ + u32 mirrored_node_index:12; + + /** + * This field is programmed with the direction of the SATA reqeust + * - SCU_SATA_WRITE_DATA_DIRECTION + * - SCU_SATA_READ_DATA_DIRECTION + */ + u32 sata_direction:1; + + /** + * This field is programmsed with one of the following SCU_COMMAND_CODE + * - SCU_COMMAND_CODE_INITIATOR_NEW_TASK + * - SCU_COMMAND_CODE_ACTIVE_TASK + * - SCU_COMMAND_CODE_PRIMITIVE_SEQ_TASK + * - SCU_COMMAND_CODE_TARGET_RAW_FRAMES + */ + u32 command_code:2; + + /** + * This field is set to true if the remote node should be suspended. + * This bit is only valid for SSP & SMP target devices. + */ + u32 suspend_node:1; + + /** + * This field is programmed with one of the following command type codes + * + * For SAS requests use the SCU_SSP_TASK_TYPE + * - SCU_TASK_TYPE_IOREAD + * - SCU_TASK_TYPE_IOWRITE + * - SCU_TASK_TYPE_SMP_REQUEST + * - SCU_TASK_TYPE_RESPONSE + * - SCU_TASK_TYPE_RAW_FRAME + * - SCU_TASK_TYPE_PRIMITIVE + * + * For SATA requests use the SCU_SATA_TASK_TYPE + * - SCU_TASK_TYPE_DMA_IN + * - SCU_TASK_TYPE_FPDMAQ_READ + * - SCU_TASK_TYPE_PACKET_DMA_IN + * - SCU_TASK_TYPE_SATA_RAW_FRAME + * - SCU_TASK_TYPE_DMA_OUT + * - SCU_TASK_TYPE_FPDMAQ_WRITE + * - SCU_TASK_TYPE_PACKET_DMA_OUT + */ + u32 task_type:4; + + /* OFFSET 0x08 */ + /** + * This field is reserved and the must be set to 0x00 + */ + u32 link_layer_control:8; /* presently all reserved */ + + /** + * This field is set to true when TLR is to be enabled + */ + u32 ssp_tlr_enable:1; + + /** + * This is field specifies if the SCU DMAs a response frame to host + * memory for good response frames when operating in target mode. + */ + u32 dma_ssp_target_good_response:1; + + /** + * This field indicates if the SCU should DMA the response frame to + * host memory. + */ + u32 do_not_dma_ssp_good_response:1; + + /** + * This field is set to true when strict ordering is to be enabled + */ + u32 strict_ordering:1; + + /** + * This field indicates the type of endianess to be utilized for the + * frame. command, task, and response frames utilized control_frame + * set to 1. + */ + u32 control_frame:1; + + /** + * This field is reserved and the driver should set to 0x00 + */ + u32 tl_control_reserved:3; + + /** + * This field is set to true when the SCU hardware task timeout control is to + * be enabled + */ + u32 timeout_enable:1; + + /** + * This field is reserved and the driver should set it to 0x00 + */ + u32 pts_control_reserved:7; + + /** + * This field should be set to true when block guard is to be enabled + */ + u32 block_guard_enable:1; + + /** + * This field is reserved and the driver should set to 0x00 + */ + u32 sdma_control_reserved:7; + + /* OFFSET 0x0C */ + /** + * This field is the address modifier for this io request it should be + * programmed with the virtual function that is making the request. + */ + u32 address_modifier:16; + + /** + * @todo What we support mirrored SMP response frame? + */ + u32 mirrored_protocol_engine:3; /* mirrored protocol Engine Index */ + + /** + * If this is a mirrored request the logical port index for the mirrored RNi + * must be programmed. + */ + u32 mirrored_logical_port:4; /* mirrored local port index */ + + /** + * This field is reserved and the driver must set it to 0x00 + */ + u32 reserved_0C_0:8; + + /** + * This field must be set to true if the mirrored request processing is to be + * enabled. + */ + u32 mirror_request_enable:1; /* Mirrored request Enable */ + + /* OFFSET 0x10 */ + /** + * This field is the command iu length in dwords + */ + u32 ssp_command_iu_length:8; + + /** + * This is the target TLR enable bit it must be set to 0 when creatning the + * task context. + */ + u32 xfer_ready_tlr_enable:1; + + /** + * This field is reserved and the driver must set it to 0x00 + */ + u32 reserved_10_0:7; + + /** + * This is the maximum burst size that the SCU hardware will send in one + * connection its value is (N x 512) and N must be a multiple of 2. If the + * value is 0x00 then maximum burst size is disabled. + */ + u32 ssp_max_burst_size:16; + + /* OFFSET 0x14 */ + /** + * This filed is set to the number of bytes to be transfered in the request. + */ + u32 transfer_length_bytes:24; /* In terms of bytes */ + + /** + * This field is reserved and the driver should set it to 0x00 + */ + u32 reserved_14_0:8; + + /* OFFSET 0x18-0x2C */ + /** + * This union provides for the protocol specif part of the SCU Task Context. + */ + union PROTOCOL_CONTEXT type; + + /* OFFSET 0x30-0x34 */ + /** + * This field is the upper 32 bits of the 64 bit physical address of the + * command iu buffer + */ + u32 command_iu_upper; + + /** + * This field is the lower 32 bits of the 64 bit physical address of the + * command iu buffer + */ + u32 command_iu_lower; + + /* OFFSET 0x38-0x3C */ + /** + * This field is the upper 32 bits of the 64 bit physical address of the + * response iu buffer + */ + u32 response_iu_upper; + + /** + * This field is the lower 32 bits of the 64 bit physical address of the + * response iu buffer + */ + u32 response_iu_lower; + + /* OFFSET 0x40 */ + /** + * This field is set to the task phase of the SCU hardware. The driver must + * set this to 0x01 + */ + u32 task_phase:8; + + /** + * This field is set to the transport layer task status. The driver must set + * this to 0x00 + */ + u32 task_status:8; + + /** + * This field is used during initiator write TLR + */ + u32 previous_extended_tag:4; + + /** + * This field is set the maximum number of retries for a STP non-data FIS + */ + u32 stp_retry_count:2; + + /** + * This field is reserved and the driver must set it to 0x00 + */ + u32 reserved_40_1:2; + + /** + * This field is used by the SCU TL to determine when to take a snapshot when + * tranmitting read data frames. + * - 0x00 The entire IO + * - 0x01 32k + * - 0x02 64k + * - 0x04 128k + * - 0x08 256k + */ + u32 ssp_tlr_threshold:4; + + /** + * This field is reserved and the driver must set it to 0x00 + */ + u32 reserved_40_2:4; + + /* OFFSET 0x44 */ + u32 write_data_length; /* read only set to 0 */ + + /* OFFSET 0x48-0x58 */ + struct TRANSPORT_SNAPSHOT snapshot; /* read only set to 0 */ + + /* OFFSET 0x5C */ + u32 block_protection_enable:1; + u32 block_size:2; + u32 block_protection_function:2; + u32 reserved_5C_0:9; + u32 active_sgl_element:2; /* read only set to 0 */ + u32 sgl_exhausted:1; /* read only set to 0 */ + u32 payload_data_transfer_error:4; /* read only set to 0 */ + u32 frame_buffer_offset:11; /* read only set to 0 */ + + /* OFFSET 0x60-0x7C */ + /** + * This field is the first SGL element pair found in the TC data structure. + */ + struct scu_sgl_element_pair sgl_pair_ab; + /* OFFSET 0x80-0x9C */ + /** + * This field is the second SGL element pair found in the TC data structure. + */ + struct scu_sgl_element_pair sgl_pair_cd; + + /* OFFSET 0xA0-BC */ + struct scu_sgl_element_pair sgl_snapshot_ac; + + /* OFFSET 0xC0 */ + u32 active_sgl_element_pair; /* read only set to 0 */ + + /* OFFSET 0xC4-0xCC */ + u32 reserved_C4_CC[3]; + + /* OFFSET 0xD0 */ + u32 intermediate_crc_value:16; + u32 initial_crc_seed:16; + + /* OFFSET 0xD4 */ + u32 application_tag_for_verify:16; + u32 application_tag_for_generate:16; + + /* OFFSET 0xD8 */ + u32 reference_tag_seed_for_verify_function; + + /* OFFSET 0xDC */ + u32 reserved_DC; + + /* OFFSET 0xE0 */ + u32 reserved_E0_0:16; + u32 application_tag_mask_for_generate:16; + + /* OFFSET 0xE4 */ + u32 block_protection_control:16; + u32 application_tag_mask_for_verify:16; + + /* OFFSET 0xE8 */ + u32 block_protection_error:8; + u32 reserved_E8_0:24; + + /* OFFSET 0xEC */ + u32 reference_tag_seed_for_verify; + + /* OFFSET 0xF0 */ + u32 intermediate_crc_valid_snapshot:16; + u32 reserved_F0_0:16; + + /* OFFSET 0xF4 */ + u32 reference_tag_seed_for_verify_function_snapshot; + + /* OFFSET 0xF8 */ + u32 snapshot_of_reserved_dword_DC_of_tc; + + /* OFFSET 0xFC */ + u32 reference_tag_seed_for_generate_function_snapshot; + +}; + +#endif /* _SCU_TASK_CONTEXT_H_ */ diff --git a/drivers/scsi/isci/core/scu_unsolicited_frame.h b/drivers/scsi/isci/core/scu_unsolicited_frame.h new file mode 100644 index 000000000000..590ea02745fd --- /dev/null +++ b/drivers/scsi/isci/core/scu_unsolicited_frame.h @@ -0,0 +1,117 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This field defines the SCU format of an unsolicited frame (UF). A UF is a + * frame received by the SCU for which there is no known corresponding task + * context (TC). + * + * + */ + +#ifndef _SCU_UNSOLICITED_FRAME_H_ +#define _SCU_UNSOLICITED_FRAME_H_ + +#include "sci_types.h" + +/** + * + * + * This constant defines the number of DWORDS found the unsolicited frame + * header data member. + */ +#define SCU_UNSOLICITED_FRAME_HEADER_DATA_DWORDS 15 + +/** + * struct scu_unsolicited_frame_header - + * + * This structure delineates the format of an unsolicited frame header. The + * first DWORD are UF attributes defined by the silicon architecture. The data + * depicts actual header information received on the link. + */ +struct scu_unsolicited_frame_header { + /** + * This field indicates if there is an Initiator Index Table entry with + * which this header is associated. + */ + u32 iit_exists:1; + + /** + * This field simply indicates the protocol type (i.e. SSP, STP, SMP). + */ + u32 protocol_type:3; + + /** + * This field indicates if the frame is an address frame (IAF or OAF) + * or if it is a information unit frame. + */ + u32 is_address_frame:1; + + /** + * This field simply indicates the connection rate at which the frame + * was received. + */ + u32 connection_rate:4; + + u32 reserved:23; + + /** + * This field represents the actual header data received on the link. + */ + u32 data[SCU_UNSOLICITED_FRAME_HEADER_DATA_DWORDS]; + +}; + +#endif /* _SCU_UNSOLICITED_FRAME_H_ */ diff --git a/drivers/scsi/isci/core/scu_viit_data.h b/drivers/scsi/isci/core/scu_viit_data.h new file mode 100644 index 000000000000..4601d1962ca3 --- /dev/null +++ b/drivers/scsi/isci/core/scu_viit_data.h @@ -0,0 +1,179 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCU_VIIT_DATA_HEADER_ +#define _SCU_VIIT_DATA_HEADER_ + +/** + * This file contains the constants and structures for the SCU hardware VIIT + * table entries. + * + * + */ + +#include "sci_types.h" + +#define SCU_VIIT_ENTRY_ID_MASK (0xC0000000) +#define SCU_VIIT_ENTRY_ID_SHIFT (30) + +#define SCU_VIIT_ENTRY_FUNCTION_MASK (0x0FF00000) +#define SCU_VIIT_ENTRY_FUNCTION_SHIFT (20) + +#define SCU_VIIT_ENTRY_IPPTMODE_MASK (0x0001F800) +#define SCU_VIIT_ENTRY_IPPTMODE_SHIFT (12) + +#define SCU_VIIT_ENTRY_LPVIE_MASK (0x00000F00) +#define SCU_VIIT_ENTRY_LPVIE_SHIFT (8) + +#define SCU_VIIT_ENTRY_STATUS_MASK (0x000000FF) +#define SCU_VIIT_ENTRY_STATUS_SHIFT (0) + +#define SCU_VIIT_ENTRY_ID_INVALID (0 << SCU_VIIT_ENTRY_ID_SHIFT) +#define SCU_VIIT_ENTRY_ID_VIIT (1 << SCU_VIIT_ENTRY_ID_SHIFT) +#define SCU_VIIT_ENTRY_ID_IIT (2 << SCU_VIIT_ENTRY_ID_SHIFT) +#define SCU_VIIT_ENTRY_ID_VIRT_EXP (3 << SCU_VIIT_ENTRY_ID_SHIFT) + +#define SCU_VIIT_IPPT_SSP_INITIATOR (0x01 << SCU_VIIT_ENTRY_IPPTMODE_SHIFT) +#define SCU_VIIT_IPPT_SMP_INITIATOR (0x02 << SCU_VIIT_ENTRY_IPPTMODE_SHIFT) +#define SCU_VIIT_IPPT_STP_INITIATOR (0x04 << SCU_VIIT_ENTRY_IPPTMODE_SHIFT) +#define SCU_VIIT_IPPT_INITIATOR \ + (\ + SCU_VIIT_IPPT_SSP_INITIATOR \ + | SCU_VIIT_IPPT_SMP_INITIATOR \ + | SCU_VIIT_IPPT_STP_INITIATOR \ + ) + +#define SCU_VIIT_STATUS_RNC_VALID (0x01 << SCU_VIIT_ENTRY_STATUS_SHIFT) +#define SCU_VIIT_STATUS_ADDRESS_VALID (0x02 << SCU_VIIT_ENTRY_STATUS_SHIFT) +#define SCU_VIIT_STATUS_RNI_VALID (0x04 << SCU_VIIT_ENTRY_STATUS_SHIFT) +#define SCU_VIIT_STATUS_ALL_VALID \ + (\ + SCU_VIIT_STATUS_RNC_VALID \ + | SCU_VIIT_STATUS_ADDRESS_VALID \ + | SCU_VIIT_STATUS_RNI_VALID \ + ) + +#define SCU_VIIT_IPPT_SMP_TARGET (0x10 << SCU_VIIT_ENTRY_IPPTMODE_SHIFT) + +/** + * struct scu_viit_entry - This is the SCU Virtual Initiator Table Entry + * + * + */ +struct scu_viit_entry { + /** + * This must be encoded as to the type of initiator that is being constructed + * for this port. + */ + u32 status; + + /** + * Virtual initiator high SAS Address + */ + u32 initiator_sas_address_hi; + + /** + * Virtual initiator low SAS Address + */ + u32 initiator_sas_address_lo; + + /** + * This must be 0 + */ + u32 reserved; + +}; + + +/* IIT Status Defines */ +#define SCU_IIT_ENTRY_ID_MASK (0xC0000000) +#define SCU_IIT_ENTRY_ID_SHIFT (30) + +#define SCU_IIT_ENTRY_STATUS_UPDATE_MASK (0x20000000) +#define SCU_IIT_ENTRY_STATUS_UPDATE_SHIFT (29) + +#define SCU_IIT_ENTRY_LPI_MASK (0x00000F00) +#define SCU_IIT_ENTRY_LPI_SHIFT (8) + +#define SCU_IIT_ENTRY_STATUS_MASK (0x000000FF) +#define SCU_IIT_ENTRY_STATUS_SHIFT (0) + +/* IIT Remote Initiator Defines */ +#define SCU_IIT_ENTRY_REMOTE_TAG_MASK (0x0000FFFF) +#define SCU_IIT_ENTRY_REMOTE_TAG_SHIFT (0) + +#define SCU_IIT_ENTRY_REMOTE_RNC_MASK (0x0FFF0000) +#define SCU_IIT_ENTRY_REMOTE_RNC_SHIFT (16) + +#define SCU_IIT_ENTRY_ID_INVALID (0 << SCU_IIT_ENTRY_ID_SHIFT) +#define SCU_IIT_ENTRY_ID_VIIT (1 << SCU_IIT_ENTRY_ID_SHIFT) +#define SCU_IIT_ENTRY_ID_IIT (2 << SCU_IIT_ENTRY_ID_SHIFT) +#define SCU_IIT_ENTRY_ID_VIRT_EXP (3 << SCU_IIT_ENTRY_ID_SHIFT) + +/** + * struct scu_iit_entry - This will be implemented later when we support + * virtual functions + * + * + */ +struct scu_iit_entry { + u32 status; + u32 remote_initiator_sas_address_hi; + u32 remote_initiator_sas_address_lo; + u32 remote_initiator; + +}; + +#endif /* _SCU_VIIT_DATA_HEADER_ */ diff --git a/drivers/scsi/isci/deprecated.c b/drivers/scsi/isci/deprecated.c new file mode 100644 index 000000000000..847e6874e1a8 --- /dev/null +++ b/drivers/scsi/isci/deprecated.c @@ -0,0 +1,491 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + + +/** + * This file contains isci module object implementation. + * + * + */ + +#include "isci.h" +#include "request.h" +#include "sata.h" +#include "task.h" + + +/** + * scic_cb_stall_execution() - This method is called when the core requires the + * OS driver to stall execution. This method is utilized during + * initialization or non-performance paths only. + * @microseconds: This parameter specifies the number of microseconds for which + * to stall. The operating system driver is allowed to round this value up + * where necessary. + * + * none. + */ +void scic_cb_stall_execution( + u32 microseconds) +{ + udelay(microseconds); +} + + +/** + * scic_cb_io_request_get_physical_address() - This callback method asks the + * user to provide the physical address for the supplied virtual address + * when building an io request object. + * @controller: This parameter is the core controller object handle. + * @io_request: This parameter is the io request object handle for which the + * physical address is being requested. + * + * + */ +void scic_cb_io_request_get_physical_address( + struct scic_sds_controller *controller, + struct scic_sds_request *io_request, + void *virtual_address, + dma_addr_t *physical_address) +{ + struct isci_request *request = + (struct isci_request *)sci_object_get_association(io_request); + + char *requested_address = (char *)virtual_address; + char *base_address = (char *)request; + + BUG_ON(requested_address < base_address); + BUG_ON((requested_address - base_address) >= + request->request_alloc_size); + + *physical_address = request->request_daddr + + (requested_address - base_address); +} + +/** + * scic_cb_io_request_get_transfer_length() - This callback method asks the + * user to provide the number of bytes to be transfered as part of this + * request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the number of payload data bytes to be transfered for + * this IO request. + */ +u32 scic_cb_io_request_get_transfer_length( + void *scic_user_io_request) +{ + return isci_request_io_request_get_transfer_length( + scic_user_io_request + ); +} + + +/** + * scic_cb_io_request_get_data_direction() - This callback method asks the user + * to provide the data direction for this request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the value of SCI_IO_REQUEST_DATA_OUT or + * SCI_IO_REQUEST_DATA_IN, or SCI_IO_REQUEST_NO_DATA. + */ +SCI_IO_REQUEST_DATA_DIRECTION scic_cb_io_request_get_data_direction( + void *scic_user_io_request) +{ + return isci_request_io_request_get_data_direction( + scic_user_io_request + ); +} + + +/** + * scic_cb_io_request_get_next_sge() - This callback method asks the user to + * provide the address to where the next Scatter-Gather Element is located. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * @current_sge_address: This parameter specifies the address for the current + * SGE (i.e. the one that has just processed). + * + * An address specifying the location for the next scatter gather element to be + * processed. + */ +void scic_cb_io_request_get_next_sge( + void *scic_user_io_request, + void *current_sge_address, + void **next_sge) +{ + *next_sge = isci_request_io_request_get_next_sge( + scic_user_io_request, + current_sge_address + ); +} + +/** + * scic_cb_sge_get_address_field() - This callback method asks the user to + * provide the contents of the "address" field in the Scatter-Gather Element. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * @sge_address: This parameter specifies the address for the SGE from which to + * retrieve the address field. + * + * A physical address specifying the contents of the SGE's address field. + */ +dma_addr_t scic_cb_sge_get_address_field( + void *scic_user_io_request, + void *sge_address) +{ + return isci_request_sge_get_address_field( + scic_user_io_request, + sge_address + ); +} + +/** + * scic_cb_sge_get_length_field() - This callback method asks the user to + * provide the contents of the "length" field in the Scatter-Gather Element. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * @sge_address: This parameter specifies the address for the SGE from which to + * retrieve the address field. + * + * This method returns the length field specified inside the SGE referenced by + * the sge_address parameter. + */ +u32 scic_cb_sge_get_length_field( + void *scic_user_io_request, + void *sge_address) +{ + return isci_request_sge_get_length_field( + scic_user_io_request, + sge_address + ); +} + +/** + * scic_cb_ssp_io_request_get_cdb_address() - This callback method asks the + * user to provide the address for the command descriptor block (CDB) + * associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the virtual address of the CDB. + */ +void *scic_cb_ssp_io_request_get_cdb_address( + void *scic_user_io_request) +{ + return isci_request_ssp_io_request_get_cdb_address( + scic_user_io_request + ); +} + +/** + * scic_cb_ssp_io_request_get_cdb_length() - This callback method asks the user + * to provide the length of the command descriptor block (CDB) associated + * with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the length of the CDB. + */ +u32 scic_cb_ssp_io_request_get_cdb_length( + void *scic_user_io_request) +{ + return isci_request_ssp_io_request_get_cdb_length( + scic_user_io_request + ); +} + +/** + * scic_cb_ssp_io_request_get_lun() - This callback method asks the user to + * provide the Logical Unit (LUN) associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the LUN associated with this request. This should be u64? + */ +u32 scic_cb_ssp_io_request_get_lun( + void *scic_user_io_request) +{ + return isci_request_ssp_io_request_get_lun(scic_user_io_request); +} + +/** + * scic_cb_ssp_io_request_get_task_attribute() - This callback method asks the + * user to provide the task attribute associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the task attribute associated with this IO request. + */ +u32 scic_cb_ssp_io_request_get_task_attribute( + void *scic_user_io_request) +{ + return isci_request_ssp_io_request_get_task_attribute( + scic_user_io_request + ); +} + +/** + * scic_cb_ssp_io_request_get_command_priority() - This callback method asks + * the user to provide the command priority associated with this IO request. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the command priority associated with this IO request. + */ +u32 scic_cb_ssp_io_request_get_command_priority( + void *scic_user_io_request) +{ + return isci_request_ssp_io_request_get_command_priority( + scic_user_io_request + ); +} + +/** + * scic_cb_ssp_task_request_get_lun() - This method returns the Logical Unit to + * be utilized for this task management request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the LUN associated with this request. This should be u64? + */ +u32 scic_cb_ssp_task_request_get_lun( + void *scic_user_task_request) +{ + return isci_task_ssp_request_get_lun( + (struct isci_request *)scic_user_task_request + ); +} + +/** + * scic_cb_ssp_task_request_get_function() - This method returns the task + * management function to be utilized for this task request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns an unsigned byte representing the task management + * function to be performed. + */ +u8 scic_cb_ssp_task_request_get_function( + void *scic_user_task_request) +{ + return isci_task_ssp_request_get_function( + (struct isci_request *)scic_user_task_request + ); +} + +/** + * scic_cb_ssp_task_request_get_io_tag_to_manage() - This method returns the + * task management IO tag to be managed. Depending upon the task management + * function the value returned from this method may be ignored. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns an unsigned 16-bit word depicting the IO tag to be + * managed. + */ +u16 scic_cb_ssp_task_request_get_io_tag_to_manage( + void *scic_user_task_request) +{ + return isci_task_ssp_request_get_io_tag_to_manage( + (struct isci_request *)scic_user_task_request + ); +} + +/** + * scic_cb_ssp_task_request_get_response_data_address() - This callback method + * asks the user to provide the virtual address of the response data buffer + * for the supplied IO request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the virtual address for the response data buffer + * associated with this IO request. + */ +void *scic_cb_ssp_task_request_get_response_data_address( + void *scic_user_task_request) +{ + return isci_task_ssp_request_get_response_data_address( + (struct isci_request *)scic_user_task_request + ); +} + +/** + * scic_cb_ssp_task_request_get_response_data_length() - This callback method + * asks the user to provide the length of the response data buffer for the + * supplied IO request. + * @scic_user_task_request: This parameter points to the user's task request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns the length of the response buffer data associated with + * this IO request. + */ +u32 scic_cb_ssp_task_request_get_response_data_length( + void *scic_user_task_request) +{ + return isci_task_ssp_request_get_response_data_length( + (struct isci_request *)scic_user_task_request + ); +} + +#if !defined(DISABLE_ATAPI) +/** + * scic_cb_stp_packet_io_request_get_cdb_address() - This user callback asks + * the user to provide stp packet io's the CDB address. + * @scic_user_io_request: + * + * The packet IO's cdb adress. + */ +void *scic_cb_stp_packet_io_request_get_cdb_address( + void *scic_user_io_request) +{ + return isci_request_stp_packet_io_request_get_cdb_address( + scic_user_io_request + ); +} + + +/** + * scic_cb_stp_packet_io_request_get_cdb_length() - This user callback asks the + * user to provide stp packet io's the CDB length. + * @scic_user_io_request: + * + * The packet IO's cdb length. + */ +u32 scic_cb_stp_packet_io_request_get_cdb_length( + void *scic_user_io_request) +{ + return isci_request_stp_packet_io_request_get_cdb_length( + scic_user_io_request + ); +} +#endif /* #if !defined(DISABLE_ATAPI) */ + + +/** + * scic_cb_io_request_do_copy_rx_frames() - This callback method asks the user + * if the received RX frame data is to be copied to the SGL or should be + * stored by the SCI core to be retrieved later with the + * scic_io_request_get_rx_frame(). + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns true if the SCI core should copy the received frame data + * to the SGL location or false if the SCI user wants to retrieve the frame + * data at a later time. + */ +bool scic_cb_io_request_do_copy_rx_frames( + void *scic_user_io_request) +{ + struct sas_task *task + = isci_request_access_task( + (struct isci_request *)scic_user_io_request + ); + + return (task->data_dir == DMA_NONE) ? false : true; +} + +/** + * scic_cb_get_virtual_address() - This callback method asks the user to + * provide the virtual address for the supplied physical address. + * @controller: This parameter is the core controller object handle. + * @physical_address: This parameter is the physical address which is to be + * returned as a virtual address. + * + * The method returns the virtual address for the supplied physical address. + */ +void *scic_cb_get_virtual_address( + struct scic_sds_controller *controller, + dma_addr_t physical_address) +{ + void *virt_addr = (void *)phys_to_virt(physical_address); + + return virt_addr; +} + +/** + * scic_cb_request_get_sat_protocol() - This callback method asks the user to + * return the SAT protocol definition for this IO request. This method is + * only called by the SCI core if the request type constructed is SATA. + * @scic_user_io_request: This parameter points to the user's IO request + * object. It is a cookie that allows the user to provide the necessary + * information for this callback. + * + * This method returns one of the sat.h defined protocols for the given io + * request. + */ +u8 scic_cb_request_get_sat_protocol( + void *scic_user_io_request) +{ + return isci_sata_get_sat_protocol( + (struct isci_request *)scic_user_io_request + ); +} diff --git a/drivers/scsi/isci/events.c b/drivers/scsi/isci/events.c new file mode 100644 index 000000000000..75f9cd551278 --- /dev/null +++ b/drivers/scsi/isci/events.c @@ -0,0 +1,619 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + + +/** + * This file contains isci module object implementation. + * + * + */ + +#include "isci.h" +#include "request.h" +#include "sata.h" +#include "task.h" + +/** + * scic_cb_timer_create() - This callback method asks the user to create a + * timer and provide a handle for this timer for use in further timer + * interactions. The appropriate isci timer object function is called to + * create a timer object. + * @timer_callback: This parameter specifies the callback method to be invoked + * whenever the timer expires. + * @controller: This parameter specifies the controller with which this timer + * is to be associated. + * @cookie: This parameter specifies a piece of information that the user must + * retain. This cookie is to be supplied by the user anytime a timeout + * occurs for the created timer. + * + * This method returns a handle to a timer object created by the user. The + * handle will be utilized for all further interactions relating to this timer. + */ +void *scic_cb_timer_create( + struct scic_sds_controller *controller, + void (*timer_callback)(void *), + void *cookie) +{ + struct isci_host *isci_host; + struct isci_timer *timer = NULL; + + isci_host = (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host = %p", + __func__, isci_host); + + timer = isci_timer_create(&isci_host->timer_list_struct, + isci_host, + cookie, + timer_callback); + + dev_dbg(&isci_host->pdev->dev, "%s: timer = %p\n", __func__, timer); + + return (void *)timer; +} + + +/** + * scic_cb_timer_start() - This callback method asks the user to start the + * supplied timer. The appropriate isci timer object function is called to + * start the timer. + * @controller: This parameter specifies the controller with which this timer + * is to associated. + * @timer: This parameter specifies the timer to be started. + * @milliseconds: This parameter specifies the number of milliseconds for which + * to stall. The operating system driver is allowed to round this value up + * where necessary. + * + */ +void scic_cb_timer_start( + struct scic_sds_controller *controller, + void *timer, + u32 milliseconds) +{ + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host = %p, timer = %p, milliseconds = %d\n", + __func__, isci_host, timer, milliseconds); + + isci_timer_start((struct isci_timer *)timer, milliseconds); + +} + +/** + * scic_cb_timer_stop() - This callback method asks the user to stop the + * supplied timer. The appropriate isci timer object function is called to + * stop the timer. + * @controller: This parameter specifies the controller with which this timer + * is to associated. + * @timer: This parameter specifies the timer to be stopped. + * + */ +void scic_cb_timer_stop( + struct scic_sds_controller *controller, + void *timer) +{ + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host = %p, timer = %p\n", + __func__, isci_host, timer); + + isci_timer_stop((struct isci_timer *)timer); +} + +/** + * scic_cb_controller_start_complete() - This user callback will inform the + * user that the controller has finished the start process. The associated + * isci host adapter's start_complete function is called. + * @controller: This parameter specifies the controller that was started. + * @completion_status: This parameter specifies the results of the start + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_controller_start_complete( + struct scic_sds_controller *controller, + enum sci_status completion_status) +{ + struct isci_host *isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host = %p\n", __func__, isci_host); + + isci_host_start_complete(isci_host, completion_status); +} + +/** + * scic_cb_controller_stop_complete() - This user callback will inform the user + * that the controller has finished the stop process. The associated isci + * host adapter's start_complete function is called. + * @controller: This parameter specifies the controller that was stopped. + * @completion_status: This parameter specifies the results of the stop + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_controller_stop_complete( + struct scic_sds_controller *controller, + enum sci_status completion_status) +{ + struct isci_host *isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: status = 0x%x\n", __func__, completion_status); + isci_host_stop_complete(isci_host, completion_status); +} + +/** + * scic_cb_io_request_complete() - This user callback will inform the user that + * an IO request has completed. + * @controller: This parameter specifies the controller on which the IO is + * completing. + * @remote_device: This parameter specifies the remote device on which this IO + * request is completing. + * @io_request: This parameter specifies the IO request that has completed. + * @completion_status: This parameter specifies the results of the IO request + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_io_request_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *scic_io_request, + enum sci_io_status completion_status) +{ + struct isci_request *request; + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + request = + (struct isci_request *)sci_object_get_association( + scic_io_request + ); + + isci_request_io_request_complete(isci_host, + request, + completion_status); +} + +/** + * scic_cb_task_request_complete() - This user callback will inform the user + * that a task management request completed. + * @controller: This parameter specifies the controller on which the task + * management request is completing. + * @remote_device: This parameter specifies the remote device on which this + * task management request is completing. + * @task_request: This parameter specifies the task management request that has + * completed. + * @completion_status: This parameter specifies the results of the IO request + * operation. SCI_SUCCESS indicates successful completion. + * + */ +void scic_cb_task_request_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + struct scic_sds_request *scic_task_request, + enum sci_task_status completion_status) +{ + struct isci_request *request; + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + request = + (struct isci_request *)sci_object_get_association( + scic_task_request); + + isci_task_request_complete(isci_host, request, completion_status); +} + +/** + * scic_cb_port_stop_complete() - This method informs the user when a stop + * operation on the port has completed. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @completion_status: This parameter specifies the status for the operation + * being completed. + * + */ +void scic_cb_port_stop_complete( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + enum sci_status completion_status) +{ + pr_warn("%s:************************************************\n", + __func__); +} + +/** + * scic_cb_port_hard_reset_complete() - This method informs the user when a + * hard reset on the port has completed. This hard reset could have been + * initiated by the user or by the remote port. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @completion_status: This parameter specifies the status for the operation + * being completed. + * + */ +void scic_cb_port_hard_reset_complete( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + enum sci_status completion_status) +{ + struct isci_port *isci_port + = (struct isci_port *)sci_object_get_association(port); + + isci_port_hard_reset_complete(isci_port, completion_status); +} + +/** + * scic_cb_port_ready() - This method informs the user that the port is now in + * a ready state and can be utilized to issue IOs. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * + */ +void scic_cb_port_ready( + struct scic_sds_controller *controller, + struct scic_sds_port *port) +{ + struct isci_port *isci_port; + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + isci_port = + (struct isci_port *)sci_object_get_association(port); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", __func__, isci_port); + + isci_port_ready(isci_host, isci_port); +} + +/** + * scic_cb_port_not_ready() - This method informs the user that the port is now + * not in a ready (i.e. busy) state and can't be utilized to issue IOs. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * + */ +void scic_cb_port_not_ready( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + u32 reason_code) +{ + struct isci_port *isci_port; + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + isci_port = + (struct isci_port *)sci_object_get_association(port); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", __func__, isci_port); + + isci_port_not_ready(isci_host, isci_port); +} + +/** + * scic_cb_port_invalid_link_up() - This method informs the SCI Core user that + * a phy/link became ready, but the phy is not allowed in the port. In some + * situations the underlying hardware only allows for certain phy to port + * mappings. If these mappings are violated, then this API is invoked. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. + * @phy: This parameter specifies the phy that came ready, but the phy can't be + * a valid member of the port. + * + */ +void scic_cb_port_invalid_link_up( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + pr_warn("%s:************************************************\n", + __func__); +} + +/** + * scic_cb_port_bc_change_primitive_received() - This callback method informs + * the user that a broadcast change primitive was received. + * @controller: This parameter represents the controller which contains the + * port. + * @port: This parameter specifies the SCI port object for which the callback + * is being invoked. For instances where the phy on which the primitive was + * received is not part of a port, this parameter will be + * SCI_INVALID_HANDLE_T. + * @phy: This parameter specifies the phy on which the primitive was received. + * + */ +void scic_cb_port_bc_change_primitive_received( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: port = %p, phy = %p\n", __func__, port, phy); + isci_port_bc_change_received(isci_host, port, phy); +} + + + + +/** + * scic_cb_port_link_up() - This callback method informs the user that a phy + * has become operational and is capable of communicating with the remote + * end point. + * @controller: This parameter represents the controller associated with the + * phy. + * @port: This parameter specifies the port object for which the user callback + * is being invoked. There may be conditions where this parameter can be + * SCI_INVALID_HANDLE + * @phy: This parameter specifies the phy object for which the user callback is + * being invoked. + * + * none. + */ +void scic_cb_port_link_up( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: phy = %p\n", __func__, phy); + + isci_port_link_up(isci_host, port, phy); +} + +/** + * scic_cb_port_link_down() - This callback method informs the user that a phy + * is no longer operational and is not capable of communicating with the + * remote end point. + * @controller: This parameter represents the controller associated with the + * phy. + * @port: This parameter specifies the port object for which the user callback + * is being invoked. There may be conditions where this parameter can be + * SCI_INVALID_HANDLE + * @phy: This parameter specifies the phy object for which the user callback is + * being invoked. + * + * none. + */ +void scic_cb_port_link_down( + struct scic_sds_controller *controller, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + struct isci_host *isci_host; + struct isci_phy *isci_phy; + struct isci_port *isci_port; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + isci_phy = + (struct isci_phy *)sci_object_get_association(phy); + + isci_port = + (struct isci_port *)sci_object_get_association(port); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", __func__, isci_port); + + isci_port_link_down(isci_host, isci_phy, isci_port); +} + +/** + * scic_cb_remote_device_start_complete() - This user callback method will + * inform the user that a start operation has completed. + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the completion callback. + * @completion_status: This parameter specifies the completion status for the + * operation. + * + */ +void scic_cb_remote_device_start_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + enum sci_status completion_status) +{ + struct isci_host *isci_host; + struct isci_remote_device *isci_device; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + isci_device = + (struct isci_remote_device *)sci_object_get_association( + remote_device + ); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + isci_remote_device_start_complete( + isci_host, isci_device, completion_status); + +} + +/** + * scic_cb_remote_device_stop_complete() - This user callback method will + * inform the user that a stop operation has completed. + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the completion callback. + * @completion_status: This parameter specifies the completion status for the + * operation. + * + */ +void scic_cb_remote_device_stop_complete( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + enum sci_status completion_status) +{ + struct isci_host *isci_host; + struct isci_remote_device *isci_device; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + isci_device = + (struct isci_remote_device *)sci_object_get_association( + remote_device + ); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + isci_remote_device_stop_complete( + isci_host, isci_device, completion_status); + +} + +/** + * scic_cb_remote_device_ready() - This user callback method will inform the + * user that a remote device is now capable of handling IO requests. + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the callback. + * + */ +void scic_cb_remote_device_ready( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device) +{ + struct isci_remote_device *isci_device = + (struct isci_remote_device *) + sci_object_get_association(remote_device); + + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + isci_remote_device_ready(isci_device); +} + +/** + * scic_cb_remote_device_not_ready() - This user callback method will inform + * the user that a remote device is no longer capable of handling IO + * requests (until a ready callback is invoked). + * @controller: This parameter specifies the core controller associated with + * the completion callback. + * @remote_device: This parameter specifies the remote device associated with + * the callback. + * @reason_code: This parameter specifies the reason for the remote device + * going to a not ready state. + * + */ +void scic_cb_remote_device_not_ready( + struct scic_sds_controller *controller, + struct scic_sds_remote_device *remote_device, + u32 reason_code) +{ + struct isci_remote_device *isci_device = + (struct isci_remote_device *) + sci_object_get_association(remote_device); + + struct isci_host *isci_host; + + isci_host = + (struct isci_host *)sci_object_get_association(controller); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p, reason_code = %x\n", + __func__, isci_device, reason_code); + + isci_remote_device_not_ready(isci_device, reason_code); +} + + diff --git a/drivers/scsi/isci/firmware/Makefile b/drivers/scsi/isci/firmware/Makefile new file mode 100644 index 000000000000..5f54461cabc5 --- /dev/null +++ b/drivers/scsi/isci/firmware/Makefile @@ -0,0 +1,19 @@ +# Makefile for create_fw +# +CC=gcc +CFLAGS=-c -Wall -O2 -g +LDFLAGS= +SOURCES=create_fw.c +OBJECTS=$(SOURCES:.cpp=.o) +EXECUTABLE=create_fw + +all: $(SOURCES) $(EXECUTABLE) + +$(EXECUTABLE): $(OBJECTS) + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -O $@ + +clean: + rm -f *.o $(EXECUTABLE) diff --git a/drivers/scsi/isci/firmware/README b/drivers/scsi/isci/firmware/README new file mode 100644 index 000000000000..cf7e4286e896 --- /dev/null +++ b/drivers/scsi/isci/firmware/README @@ -0,0 +1,36 @@ +This defines the temporary binary blow we are to pass to the SCU +driver to emulate the binary firmware that we will eventually be +able to access via NVRAM on the SCU controller. + +The current size of the binary blob is expected to be 149 bytes or larger + +Header Types: +0x1: Phy Masks +0x2: Phy Gens +0x3: SAS Addrs +0xff: End of Data + +ID string - u8[12]: "#SCU MAGIC#\0" +Version - u8: 1 +SubVersion - u8: 0 + +Header Type - u8: 0x1 +Size - u8: 8 +Phy Mask - u32[8] + +Header Type - u8: 0x2 +Size - u8: 8 +Phy Gen - u32[8] + +Header Type - u8: 0x3 +Size - u8: 8 +Sas Addr - u64[8] + +Header Type - u8: 0xf + + +============================================================================== + +Place isci_firmware.bin in /lib/firmware +Be sure to recreate the initramfs image to include the firmware. + diff --git a/drivers/scsi/isci/firmware/create_fw.c b/drivers/scsi/isci/firmware/create_fw.c new file mode 100644 index 000000000000..442caac9675d --- /dev/null +++ b/drivers/scsi/isci/firmware/create_fw.c @@ -0,0 +1,177 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> + +char blob_name[] = "isci_firmware.bin"; +char id[] = "#SCU MAGIC#"; +unsigned char version = 1; +unsigned char sub_version = 0; + + +/* + * For all defined arrays: + * elements 0-3 are for SCU0, ports 0-3 + * elements 4-7 are for SCU1, ports 0-3 + * + * valid configurations for one SCU are: + * P0 P1 P2 P3 + * ---------------- + * 0xF,0x0,0x0,0x0 # 1 x4 port + * 0x3,0x0,0x4,0x8 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are each x1 + * # ports + * 0x1,0x2,0xC,0x0 # Phys 0 and 1 are each x1 ports, phy 2 and phy 3 are a x2 + * # port + * 0x3,0x0,0xC,0x0 # Phys 0 and 1 are a x2 port, phy 2 and phy 3 are a x2 port + * 0x1,0x2,0x4,0x8 # Each phy is a x1 port (this is the default configuration) + * + * if there is a port/phy on which you do not wish to override the default + * values, use the value assigned to UNINIT_PARAM (255). + */ +unsigned int phy_mask[] = { 1, 2, 4, 8, 1, 2, 4, 8 }; + + +/* denotes SAS generation. i.e. 3: SAS Gen 3 6G */ +unsigned int phy_gen[] = { 3, 3, 3, 3, 3, 3, 3, 3 }; + +/* + * if there is a port/phy on which you do not wish to override the default + * values, use the value "0000000000000000". SAS address of zero's is + * considered invalid and will not be used. + */ +unsigned long long sas_addr[] = { 0x5FCFFFFFF0000000ULL, + 0x5FCFFFFFF1000000ULL, + 0x5FCFFFFFF2000000ULL, + 0x5FCFFFFFF3000000ULL, + 0x5FCFFFFFF4000000ULL, + 0x5FCFFFFFF5000000ULL, + 0x5FCFFFFFF6000000ULL, + 0x5FCFFFFFF7000000ULL }; + +int write_blob(void) +{ + FILE *fd; + int err; + + fd = fopen(blob_name, "w+"); + if (!fd) { + perror("Open file for write failed"); + return -EIO; + } + + /* write id */ + err = fwrite((void *)id, sizeof(char), strlen(id)+1, fd); + if (err == 0) { + perror("write id failed"); + return err; + } + + /* write version */ + err = fwrite((void *)&version, sizeof(version), 1, fd); + if (err == 0) { + perror("write version failed"); + return err; + } + + /* write sub version */ + err = fwrite((void *)&sub_version, sizeof(sub_version), 1, fd); + if (err == 0) { + perror("write subversion failed"); + return err; + } + + /* write phy mask header */ + err = fputc(0x1, fd); + if (err == EOF) { + perror("write phy mask header failed"); + return -EIO; + } + + /* write size */ + err = fputc(8, fd); + if (err == EOF) { + perror("write phy mask size failed"); + return -EIO; + } + + /* write phy masks */ + err = fwrite((void *)phy_mask, 1, sizeof(phy_mask), fd); + if (err == 0) { + perror("write phy_mask failed"); + return err; + } + + /* write phy gen header */ + err = fputc(0x2, fd); + if (err == EOF) { + perror("write phy gen header failed"); + return -EIO; + } + + /* write size */ + err = fputc(8, fd); + if (err == EOF) { + perror("write phy gen size failed"); + return -EIO; + } + + /* write phy_gen */ + err = fwrite((void *)phy_gen, + 1, + sizeof(phy_gen), + fd); + if (err == 0) { + perror("write phy_gen failed"); + return err; + } + + /* write phy gen header */ + err = fputc(0x3, fd); + if (err == EOF) { + perror("write sas addr header failed"); + return -EIO; + } + + /* write size */ + err = fputc(8, fd); + if (err == EOF) { + perror("write sas addr size failed"); + return -EIO; + } + + /* write sas_addr */ + err = fwrite((void *)sas_addr, + 1, + sizeof(sas_addr), + fd); + if (err == 0) { + perror("write sas_addr failed"); + return err; + } + + /* write end header */ + err = fputc(0xff, fd); + if (err == EOF) { + perror("write end header failed"); + return -EIO; + } + + fclose(fd); + + return 0; +} + +int main(void) +{ + int err; + + err = write_blob(); + if (err < 0) + return err; + + return 0; +} diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c new file mode 100644 index 000000000000..6f16f4d6c82b --- /dev/null +++ b/drivers/scsi/isci/host.c @@ -0,0 +1,781 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "isci.h" +#include "scic_io_request.h" +#include "scic_remote_device.h" +#include "scic_port.h" + +#include "port.h" +#include "request.h" +#include "host.h" + +/** + * isci_isr() - This function is the interrupt service routine for the + * controller. It schedules the tasklet and returns. + * @vec: This parameter specifies the interrupt vector. + * @data: This parameter specifies the ISCI host object. + * + * IRQ_HANDLED if out interrupt otherwise, IRQ_NONE + */ +irqreturn_t isci_isr(int vec, void *data) +{ + struct isci_host *isci_host + = (struct isci_host *)data; + struct scic_controller_handler_methods *handlers + = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; + irqreturn_t ret = IRQ_NONE; + + if (isci_host_get_state(isci_host) != isci_starting + && handlers->interrupt_handler) { + + if (handlers->interrupt_handler(isci_host->core_controller)) { + if (isci_host_get_state(isci_host) != isci_stopped) { + tasklet_schedule( + &isci_host->completion_tasklet); + } else + dev_dbg(&isci_host->pdev->dev, + "%s: controller stopped\n", + __func__); + ret = IRQ_HANDLED; + } + } else + dev_warn(&isci_host->pdev->dev, + "%s: get_handler_methods failed, " + "isci_host->status = 0x%x\n", + __func__, + isci_host_get_state(isci_host)); + + return ret; +} + +irqreturn_t isci_legacy_isr(int vec, void *data) +{ + struct pci_dev *pdev = data; + struct isci_host *isci_host; + struct scic_controller_handler_methods *handlers; + irqreturn_t ret = IRQ_NONE; + + /* + * Since this is a legacy interrupt, either or both + * controllers could have triggered it. Thus, we have to call + * the legacy interrupt handler for all controllers on the + * PCI function. + */ + for_each_isci_host(isci_host, pdev) { + handlers = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; + + if (isci_host_get_state(isci_host) != isci_starting + && handlers->interrupt_handler) { + + if (handlers->interrupt_handler(isci_host->core_controller)) { + if (isci_host_get_state(isci_host) != isci_stopped) { + tasklet_schedule( + &isci_host->completion_tasklet); + } else + dev_dbg(&isci_host->pdev->dev, + "%s: controller stopped\n", + __func__); + ret = IRQ_HANDLED; + } + } else + dev_warn(&isci_host->pdev->dev, + "%s: get_handler_methods failed, " + "isci_host->status = 0x%x\n", + __func__, + isci_host_get_state(isci_host)); + } + return ret; +} + + +/** + * isci_host_start_complete() - This function is called by the core library, + * through the ISCI Module, to indicate controller start status. + * @isci_host: This parameter specifies the ISCI host object + * @completion_status: This parameter specifies the completion status from the + * core library. + * + */ +void isci_host_start_complete( + struct isci_host *isci_host, + enum sci_status completion_status) +{ + if (completion_status == SCI_SUCCESS) { + dev_dbg(&isci_host->pdev->dev, + "%s: completion_status: SCI_SUCCESS\n", __func__); + isci_host_change_state(isci_host, isci_ready); + complete_all(&isci_host->start_complete); + } else + dev_err(&isci_host->pdev->dev, + "controller start failed with " + "completion_status = 0x%x;", + completion_status); + +} + + + +/** + * isci_host_scan_finished() - This function is one of the SCSI Host Template + * functions. The SCSI midlayer calls this function during a target scan, + * approx. once every 10 millisecs. + * @shost: This parameter specifies the SCSI host being scanned + * @time: This parameter specifies the number of ticks since the scan started. + * + * scan status, zero indicates the SCSI midlayer should continue to poll, + * otherwise assume controller is ready. + */ +int isci_host_scan_finished( + struct Scsi_Host *shost, + unsigned long time) +{ + struct isci_host *isci_host + = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); + + struct scic_controller_handler_methods *handlers + = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; + + if (handlers->interrupt_handler == NULL) { + dev_err(&isci_host->pdev->dev, + "%s: scic_controller_get_handler_methods failed\n", + __func__); + return 1; + } + + /** + * check interrupt_handler's status and call completion_handler if true, + * link_up events should be coming from the scu core lib, as phy's come + * online. for each link_up from the core, call + * get_received_identify_address_frame, copy the frame into the + * sas_phy object and call libsas notify_port_event(PORTE_BYTES_DMAED). + * continue to return zero from thee scan_finished routine until + * the scic_cb_controller_start_complete() call comes from the core. + **/ + if (handlers->interrupt_handler(isci_host->core_controller)) + handlers->completion_handler(isci_host->core_controller); + + if (isci_starting == isci_host_get_state(isci_host) + && time < (HZ * 10)) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host->status = %d, time = %ld\n", + __func__, isci_host_get_state(isci_host), time); + return 0; + } + + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host->status = %d, time = %ld\n", + __func__, isci_host_get_state(isci_host), time); + + scic_controller_enable_interrupts(isci_host->core_controller); + + return 1; + +} + + +/** + * isci_host_scan_start() - This function is one of the SCSI Host Template + * function, called by the SCSI mid layer berfore a target scan begins. The + * core library controller start routine is called from here. + * @shost: This parameter specifies the SCSI host to be scanned + * + */ +void isci_host_scan_start(struct Scsi_Host *shost) +{ + struct isci_host *isci_host; + + isci_host = isci_host_from_sas_ha(SHOST_TO_SAS_HA(shost)); + isci_host_change_state(isci_host, isci_starting); + + scic_controller_disable_interrupts(isci_host->core_controller); + init_completion(&isci_host->start_complete); + scic_controller_start( + isci_host->core_controller, + scic_controller_get_suggested_start_timeout( + isci_host->core_controller) + ); +} + +void isci_host_stop_complete( + struct isci_host *isci_host, + enum sci_status completion_status) +{ + isci_host_change_state(isci_host, isci_stopped); + scic_controller_disable_interrupts( + isci_host->core_controller + ); + complete(&isci_host->stop_complete); +} + +static struct coherent_memory_info *isci_host_alloc_mdl_struct( + struct isci_host *isci_host, + u32 size) +{ + struct coherent_memory_info *mdl_struct; + void *uncached_address = NULL; + + + mdl_struct = devm_kzalloc(&isci_host->pdev->dev, + sizeof(*mdl_struct), + GFP_KERNEL); + if (!mdl_struct) + return NULL; + + INIT_LIST_HEAD(&mdl_struct->node); + + uncached_address = dmam_alloc_coherent(&isci_host->pdev->dev, + size, + &mdl_struct->dma_handle, + GFP_KERNEL); + if (!uncached_address) + return NULL; + + /* memset the whole memory area. */ + memset((char *)uncached_address, 0, size); + mdl_struct->vaddr = uncached_address; + mdl_struct->size = (size_t)size; + + return mdl_struct; +} + +static void isci_host_build_mde( + struct sci_physical_memory_descriptor *mde_struct, + struct coherent_memory_info *mdl_struct) +{ + unsigned long address = 0; + dma_addr_t dma_addr = 0; + + address = (unsigned long)mdl_struct->vaddr; + dma_addr = mdl_struct->dma_handle; + + /* to satisfy the alignment. */ + if ((address % mde_struct->constant_memory_alignment) != 0) { + int align_offset + = (mde_struct->constant_memory_alignment + - (address % mde_struct->constant_memory_alignment)); + address += align_offset; + dma_addr += align_offset; + } + + mde_struct->virtual_address = (void *)address; + mde_struct->physical_address = dma_addr; + mdl_struct->mde = mde_struct; +} + +static int isci_host_mdl_allocate_coherent( + struct isci_host *isci_host) +{ + struct sci_physical_memory_descriptor *current_mde; + struct coherent_memory_info *mdl_struct; + u32 size = 0; + + struct sci_base_memory_descriptor_list *mdl_handle + = sci_controller_get_memory_descriptor_list_handle( + isci_host->core_controller); + + sci_mdl_first_entry(mdl_handle); + + current_mde = sci_mdl_get_current_entry(mdl_handle); + + while (current_mde != NULL) { + + size = (current_mde->constant_memory_size + + current_mde->constant_memory_alignment); + + mdl_struct = isci_host_alloc_mdl_struct(isci_host, size); + if (!mdl_struct) + return -ENOMEM; + + list_add_tail(&mdl_struct->node, &isci_host->mdl_struct_list); + + isci_host_build_mde(current_mde, mdl_struct); + + sci_mdl_next_entry(mdl_handle); + current_mde = sci_mdl_get_current_entry(mdl_handle); + } + + return 0; +} + + +/** + * isci_host_completion_routine() - This function is the delayed service + * routine that calls the sci core library's completion handler. It's + * scheduled as a tasklet from the interrupt service routine when interrupts + * in use, or set as the timeout function in polled mode. + * @data: This parameter specifies the ISCI host object + * + */ +static void isci_host_completion_routine(unsigned long data) +{ + struct isci_host *isci_host = (struct isci_host *)data; + struct scic_controller_handler_methods *handlers + = &isci_host->scic_irq_handlers[SCI_MSIX_NORMAL_VECTOR]; + struct list_head completed_request_list; + struct list_head aborted_request_list; + struct list_head *current_position; + struct list_head *next_position; + struct isci_request *request; + struct isci_request *next_request; + struct sas_task *task; + + INIT_LIST_HEAD(&completed_request_list); + INIT_LIST_HEAD(&aborted_request_list); + + spin_lock_irq(&isci_host->scic_lock); + + if (handlers->completion_handler) { + handlers->completion_handler( + isci_host->core_controller + ); + } + /* Take the lists of completed I/Os from the host. */ + list_splice_init(&isci_host->requests_to_complete, + &completed_request_list); + + list_splice_init(&isci_host->requests_to_abort, + &aborted_request_list); + + spin_unlock_irq(&isci_host->scic_lock); + + /* Process any completions in the lists. */ + list_for_each_safe(current_position, next_position, + &completed_request_list) { + + request = list_entry(current_position, struct isci_request, + completed_node); + task = isci_request_access_task(request); + + /* Normal notification (task_done) */ + dev_dbg(&isci_host->pdev->dev, + "%s: Normal - request/task = %p/%p\n", + __func__, + request, + task); + + task->task_done(task); + task->lldd_task = NULL; + + /* Free the request object. */ + isci_request_free(isci_host, request); + } + list_for_each_entry_safe(request, next_request, &aborted_request_list, + completed_node) { + + task = isci_request_access_task(request); + + /* Use sas_task_abort */ + dev_warn(&isci_host->pdev->dev, + "%s: Error - request/task = %p/%p\n", + __func__, + request, + task); + + /* Put the task into the abort path. */ + sas_task_abort(task); + } + +} + +void isci_host_deinit( + struct isci_host *isci_host) +{ + int i; + + isci_host_change_state(isci_host, isci_stopping); + for (i = 0; i < SCI_MAX_PORTS; i++) { + struct isci_port *port = &isci_host->isci_ports[i]; + struct isci_remote_device *device, *tmpdev; + list_for_each_entry_safe(device, tmpdev, + &port->remote_dev_list, node) { + isci_remote_device_change_state(device, isci_stopping); + isci_remote_device_stop(device); + } + } + + /* stop the comtroller and wait for completion. */ + init_completion(&isci_host->stop_complete); + scic_controller_stop( + isci_host->core_controller, + SCIC_CONTROLLER_STOP_TIMEOUT + ); + wait_for_completion(&isci_host->stop_complete); + /* next, reset the controller. */ + scic_controller_reset(isci_host->core_controller); +} + +static int isci_verify_firmware(const struct firmware *fw, + struct isci_firmware *isci_fw) +{ + const u8 *tmp; + + if (fw->size < ISCI_FIRMWARE_MIN_SIZE) + return -EINVAL; + + tmp = fw->data; + + /* 12th char should be the NULL terminate for the ID string */ + if (tmp[11] != '\0') + return -EINVAL; + + if (strncmp("#SCU MAGIC#", tmp, 11) != 0) + return -EINVAL; + + isci_fw->id = tmp; + isci_fw->version = fw->data[ISCI_FW_VER_OFS]; + isci_fw->subversion = fw->data[ISCI_FW_SUBVER_OFS]; + + tmp = fw->data + ISCI_FW_DATA_OFS; + + while (*tmp != ISCI_FW_HDR_EOF) { + switch (*tmp) { + case ISCI_FW_HDR_PHYMASK: + tmp++; + isci_fw->phy_masks_size = *tmp; + tmp++; + isci_fw->phy_masks = (const u32 *)tmp; + tmp += sizeof(u32) * isci_fw->phy_masks_size; + break; + + case ISCI_FW_HDR_PHYGEN: + tmp++; + isci_fw->phy_gens_size = *tmp; + tmp++; + isci_fw->phy_gens = (const u32 *)tmp; + tmp += sizeof(u32) * isci_fw->phy_gens_size; + break; + + case ISCI_FW_HDR_SASADDR: + tmp++; + isci_fw->sas_addrs_size = *tmp; + tmp++; + isci_fw->sas_addrs = (const u64 *)tmp; + tmp += sizeof(u64) * isci_fw->sas_addrs_size; + break; + + default: + pr_err("bad field in firmware binary blob\n"); + return -EINVAL; + } + } + + pr_info("isci firmware v%u.%u loaded.\n", + isci_fw->version, isci_fw->subversion); + + return SCI_SUCCESS; +} + +static void __iomem *scu_base(struct isci_host *isci_host) +{ + struct pci_dev *pdev = isci_host->pdev; + int id = isci_host->id; + + return pcim_iomap_table(pdev)[SCI_SCU_BAR * 2] + SCI_SCU_BAR_SIZE * id; +} + +static void __iomem *smu_base(struct isci_host *isci_host) +{ + struct pci_dev *pdev = isci_host->pdev; + int id = isci_host->id; + + return pcim_iomap_table(pdev)[SCI_SMU_BAR * 2] + SCI_SMU_BAR_SIZE * id; +} + +#define SCI_MAX_TIMER_COUNT 25 + +int isci_host_init(struct isci_host *isci_host) +{ + int err = 0; + int index = 0; + enum sci_status status; + struct scic_sds_controller *controller; + struct scic_sds_port *scic_port; + struct scic_controller_handler_methods *handlers + = &isci_host->scic_irq_handlers[0]; + union scic_oem_parameters scic_oem_params; + union scic_user_parameters scic_user_params; + const struct firmware *fw = NULL; + struct isci_firmware *isci_fw = NULL; + + INIT_LIST_HEAD(&isci_host->timer_list_struct.timers); + isci_timer_list_construct( + &isci_host->timer_list_struct, + SCI_MAX_TIMER_COUNT + ); + + controller = scic_controller_alloc(&isci_host->pdev->dev); + + if (!controller) { + err = -ENOMEM; + dev_err(&isci_host->pdev->dev, "%s: failed (%d)\n", __func__, err); + goto out; + } + + isci_host->core_controller = controller; + spin_lock_init(&isci_host->state_lock); + spin_lock_init(&isci_host->scic_lock); + spin_lock_init(&isci_host->queue_lock); + + isci_host_change_state(isci_host, isci_starting); + isci_host->can_queue = ISCI_CAN_QUEUE_VAL; + + status = scic_controller_construct(controller, scu_base(isci_host), + smu_base(isci_host)); + + if (status != SCI_SUCCESS) { + dev_err(&isci_host->pdev->dev, + "%s: scic_controller_construct failed - status = %x\n", + __func__, + status); + err = -ENODEV; + goto out; + } + + isci_host->sas_ha.dev = &isci_host->pdev->dev; + isci_host->sas_ha.lldd_ha = isci_host; + + /*----------- SCIC controller Initialization Stuff ------------------ + * set association host adapter struct in core controller. + */ + sci_object_set_association(isci_host->core_controller, + (void *)isci_host + ); + + /* grab initial values stored in the controller object for OEM and USER + * parameters */ + scic_oem_parameters_get(controller, &scic_oem_params); + scic_user_parameters_get(controller, &scic_user_params); + + isci_fw = devm_kzalloc(&isci_host->pdev->dev, + sizeof(struct isci_firmware), + GFP_KERNEL); + if (!isci_fw) { + dev_warn(&isci_host->pdev->dev, + "allocating firmware struct failed\n"); + dev_warn(&isci_host->pdev->dev, + "Default OEM configuration being used:" + " 4 narrow ports, and default SAS Addresses\n"); + goto set_default_params; + } + + status = request_firmware(&fw, ISCI_FW_NAME, &isci_host->pdev->dev); + if (status) { + dev_warn(&isci_host->pdev->dev, + "Loading firmware failed, using default values\n"); + dev_warn(&isci_host->pdev->dev, + "Default OEM configuration being used:" + " 4 narrow ports, and default SAS Addresses\n"); + goto set_default_params; + } + else { + status = isci_verify_firmware(fw, isci_fw); + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "firmware verification failed\n"); + dev_warn(&isci_host->pdev->dev, + "Default OEM configuration being used:" + " 4 narrow ports, and default SAS " + "Addresses\n"); + goto set_default_params; + } + + /* grab any OEM and USER parameters specified at module load */ + status = isci_parse_oem_parameters(&scic_oem_params, + isci_host->id, isci_fw); + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "parsing firmware oem parameters failed\n"); + err = -EINVAL; + goto out; + } + + status = isci_parse_user_parameters(&scic_user_params, + isci_host->id, isci_fw); + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: isci_parse_user_parameters" + " failed\n", __func__); + err = -EINVAL; + goto out; + } + } + + set_default_params: + + status = scic_oem_parameters_set(isci_host->core_controller, + &scic_oem_params + ); + + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: scic_oem_parameters_set failed\n", + __func__); + err = -ENODEV; + goto out; + } + + + status = scic_user_parameters_set(isci_host->core_controller, + &scic_user_params + ); + + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: scic_user_parameters_set failed\n", + __func__); + err = -ENODEV; + goto out; + } + + status = scic_controller_initialize(isci_host->core_controller); + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: scic_controller_initialize failed -" + " status = 0x%x\n", + __func__, status); + err = -ENODEV; + goto out; + } + + /* @todo: use both MSI-X interrupts, and don't do indirect + * calls to the handlers just register direct calls + */ + if (isci_host->pdev->msix_enabled) { + status = scic_controller_get_handler_methods( + SCIC_MSIX_INTERRUPT_TYPE, + SCI_MSIX_DOUBLE_VECTOR, + handlers + ); + } else { + status = scic_controller_get_handler_methods( + SCIC_LEGACY_LINE_INTERRUPT_TYPE, + 0, + handlers + ); + } + + if (status != SCI_SUCCESS) { + handlers->interrupt_handler = NULL; + handlers->completion_handler = NULL; + dev_err(&isci_host->pdev->dev, + "%s: scic_controller_get_handler_methods failed\n", + __func__); + } + + tasklet_init(&isci_host->completion_tasklet, + isci_host_completion_routine, + (unsigned long)isci_host + ); + + INIT_LIST_HEAD(&(isci_host->mdl_struct_list)); + + INIT_LIST_HEAD(&isci_host->requests_to_complete); + INIT_LIST_HEAD(&isci_host->requests_to_abort); + + /* populate mdl with dma memory. scu_mdl_allocate_coherent() */ + err = isci_host_mdl_allocate_coherent(isci_host); + + if (err) + goto err_out; + + /* + * keep the pool alloc size around, will use it for a bounds checking + * when trying to convert virtual addresses to physical addresses + */ + isci_host->dma_pool_alloc_size = sizeof(struct isci_request) + + scic_io_request_get_object_size(); + isci_host->dma_pool = dmam_pool_create(DRV_NAME, &isci_host->pdev->dev, + isci_host->dma_pool_alloc_size, + SLAB_HWCACHE_ALIGN, 0); + + if (!isci_host->dma_pool) { + err = -ENOMEM; + goto req_obj_err_out; + } + + for (index = 0; index < SCI_MAX_PORTS; index++) { + isci_port_init(&isci_host->isci_ports[index], + isci_host, index); + } + + for (index = 0; index < SCI_MAX_PHYS; index++) + isci_phy_init(&isci_host->phys[index], isci_host, index); + + /* Why are we doing this? Is this even necessary? */ + memcpy(&isci_host->sas_addr[0], &isci_host->phys[0].sas_addr[0], + SAS_ADDR_SIZE); + + /* Start the ports */ + for (index = 0; index < SCI_MAX_PORTS; index++) { + + scic_controller_get_port_handle(controller, index, &scic_port); + scic_port_start(scic_port); + } + + goto out; + +/* SPB_Debug: destroy request object cache */ + req_obj_err_out: +/* SPB_Debug: destroy remote object cache */ + err_out: +/* SPB_Debug: undo controller init, construct and alloc, remove from parent + * controller list. */ + out: + if (fw) + release_firmware(fw); + return err; +} diff --git a/drivers/scsi/isci/host.h b/drivers/scsi/isci/host.h new file mode 100644 index 000000000000..3530076d6107 --- /dev/null +++ b/drivers/scsi/isci/host.h @@ -0,0 +1,283 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the isci_module initialization routines. + * + * host.h + */ + + + +#if !defined(_SCI_HOST_H_) +#define _SCI_HOST_H_ + +#include "phy.h" +/*#include "task.h"*/ +#include "timers.h" +#include "remote_device.h" +#include "scic_user_callback.h" + +#define DRV_NAME "isci" +#define SCI_PCI_BAR_COUNT 2 +#define SCI_NUM_MSI_X_INT 2 +#define SCI_SMU_BAR 0 +#define SCI_SMU_BAR_SIZE (16*1024) +#define SCI_SCU_BAR 1 +#define SCI_SCU_BAR_SIZE (4*1024*1024) +#define SCI_IO_SPACE_BAR0 2 +#define SCI_IO_SPACE_BAR1 3 +#define SCI_MSIX_NORMAL_VECTOR 0 +#define SCI_MSIX_ERROR_VECTOR 1 +#define SCI_MSIX_SINGLE_VECTOR 1 +#define SCI_MSIX_DOUBLE_VECTOR 2 +#define ISCI_CAN_QUEUE_VAL 250 /* < SCI_MAX_IO_REQUESTS ? */ +#define SCIC_CONTROLLER_STOP_TIMEOUT 5000 + +struct coherent_memory_info { + struct list_head node; + dma_addr_t dma_handle; + void *vaddr; + size_t size; + struct sci_physical_memory_descriptor *mde; +}; + +struct isci_host { + struct scic_sds_controller *core_controller; + struct scic_controller_handler_methods scic_irq_handlers[SCI_NUM_MSI_X_INT]; + union scic_oem_parameters oem_parameters; + + int id; /* unique within a given pci device */ + struct isci_timer_list timer_list_struct; + void *core_ctrl_memory; + struct dma_pool *dma_pool; + unsigned int dma_pool_alloc_size; + struct isci_phy phys[SCI_MAX_PHYS]; + + /* isci_ports and sas_ports are implicitly parallel to the + * ports maintained by the core + */ + struct isci_port isci_ports[SCI_MAX_PORTS]; + struct asd_sas_port sas_ports[SCI_MAX_PORTS]; + struct sas_ha_struct sas_ha; + + int can_queue; + spinlock_t queue_lock; + spinlock_t state_lock; + + struct pci_dev *pdev; + u8 sas_addr[SAS_ADDR_SIZE]; + + enum isci_status status; + struct Scsi_Host *shost; + struct tasklet_struct completion_tasklet; + struct list_head mdl_struct_list; + struct list_head requests_to_complete; + struct list_head requests_to_abort; + struct completion stop_complete; + struct completion start_complete; + spinlock_t scic_lock; + struct isci_host *next; +}; + + +/** + * struct isci_pci_info - This class represents the pci function containing the + * controllers. Depending on PCI SKU, there could be up to 2 controllers in + * the PCI function. + */ +#define SCI_MAX_MSIX_INT (SCI_NUM_MSI_X_INT*SCI_MAX_CONTROLLERS) + +struct isci_pci_info { + struct msix_entry msix_entries[SCI_MAX_MSIX_INT]; + int core_lib_array_index; + SCI_LIBRARY_HANDLE_T core_lib_handle; + struct isci_host *hosts; +}; + +static inline struct isci_pci_info *to_pci_info(struct pci_dev *pdev) +{ + return pci_get_drvdata(pdev); +} + +#define for_each_isci_host(isci_host, pdev) \ + for (isci_host = to_pci_info(pdev)->hosts;\ + isci_host; isci_host = isci_host->next) + +static inline +enum isci_status isci_host_get_state( + struct isci_host *isci_host) +{ + return isci_host->status; +} + + +static inline void isci_host_change_state( + struct isci_host *isci_host, + enum isci_status status) +{ + unsigned long flags; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host = %p, state = 0x%x", + __func__, + isci_host, + status); + spin_lock_irqsave(&isci_host->state_lock, flags); + isci_host->status = status; + spin_unlock_irqrestore(&isci_host->state_lock, flags); + +} + +static inline int isci_host_can_queue( + struct isci_host *isci_host, + int num) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&isci_host->queue_lock, flags); + if ((isci_host->can_queue - num) < 0) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host->can_queue = %d\n", + __func__, + isci_host->can_queue); + ret = -SAS_QUEUE_FULL; + + } else + isci_host->can_queue -= num; + + spin_unlock_irqrestore(&isci_host->queue_lock, flags); + + return ret; +} + +static inline void isci_host_can_dequeue( + struct isci_host *isci_host, + int num) +{ + unsigned long flags; + + spin_lock_irqsave(&isci_host->queue_lock, flags); + isci_host->can_queue += num; + spin_unlock_irqrestore(&isci_host->queue_lock, flags); +} + +/** + * isci_host_from_sas_ha() - This accessor retrieves the isci_host object + * reference from the Linux sas_ha_struct reference. + * @ha_struct,: This parameter points to the Linux sas_ha_struct object + * + * A reference to the associated isci_host structure. + */ +#define isci_host_from_sas_ha(ha_struct) \ + ((struct isci_host *)(ha_struct)->lldd_ha) + +/** + * isci_host_scan_finished() - + * + * This function is one of the SCSI Host Template functions. The SCSI midlayer + * calls this function during a target scan, approx. once every 10 millisecs. + */ +int isci_host_scan_finished( + struct Scsi_Host *, + unsigned long); + + +/** + * isci_host_scan_start() - + * + * This function is one of the SCSI Host Template function, called by the SCSI + * mid layer berfore a target scan begins. The core library controller start + * routine is called from here. + */ +void isci_host_scan_start( + struct Scsi_Host *); + +/** + * isci_host_start_complete() - + * + * This function is called by the core library, through the ISCI Module, to + * indicate controller start status. + */ +void isci_host_start_complete( + struct isci_host *, + enum sci_status); + +void isci_host_stop_complete( + struct isci_host *isci_host, + enum sci_status completion_status); + +int isci_host_init(struct isci_host *); + +void isci_host_init_controller_names( + struct isci_host *isci_host, + unsigned int controller_idx); + +void isci_host_deinit( + struct isci_host *); + +void isci_host_port_link_up( + struct isci_host *, + struct scic_sds_port *, + struct scic_sds_phy *); +int isci_host_dev_found(struct domain_device *); + +void isci_host_remote_device_start_complete( + struct isci_host *, + struct isci_remote_device *, + enum sci_status); + +#endif /* !defined(_SCI_HOST_H_) */ diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c new file mode 100644 index 000000000000..07b072f3004d --- /dev/null +++ b/drivers/scsi/isci/init.c @@ -0,0 +1,613 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <asm/string.h> +#include "isci.h" +#include "task.h" +#include "sci_controller_constants.h" +#include "scic_remote_device.h" +#include "sci_environment.h" + +static struct scsi_transport_template *isci_transport_template; +struct kmem_cache *isci_kmem_cache; + +static DEFINE_PCI_DEVICE_TABLE(isci_id_table) = { + { PCI_VDEVICE(INTEL, 0x1D61),}, + { PCI_VDEVICE(INTEL, 0x1D63),}, + { PCI_VDEVICE(INTEL, 0x1D65),}, + { PCI_VDEVICE(INTEL, 0x1D67),}, + { PCI_VDEVICE(INTEL, 0x1D69),}, + { PCI_VDEVICE(INTEL, 0x1D6B),}, + { PCI_VDEVICE(INTEL, 0x1D60),}, + { PCI_VDEVICE(INTEL, 0x1D62),}, + { PCI_VDEVICE(INTEL, 0x1D64),}, + { PCI_VDEVICE(INTEL, 0x1D66),}, + { PCI_VDEVICE(INTEL, 0x1D68),}, + { PCI_VDEVICE(INTEL, 0x1D6A),}, + {} +}; + +static int __devinit isci_pci_probe( + struct pci_dev *pdev, + const struct pci_device_id *device_id_p); + +static void __devexit isci_pci_remove(struct pci_dev *pdev); + +MODULE_DEVICE_TABLE(pci, isci_id_table); + +static struct pci_driver isci_pci_driver = { + .name = DRV_NAME, + .id_table = isci_id_table, + .probe = isci_pci_probe, + .remove = __devexit_p(isci_pci_remove), +}; + +/* linux isci specific settings */ +int loglevel = 3; +module_param(loglevel, int, S_IRUGO | S_IWUSR); + +#if defined(CONFIG_PBG_HBA_A0) +int isci_si_rev = ISCI_SI_REVA0; +#elif defined(CONFIG_PBG_HBA_A2) +int isci_si_rev = ISCI_SI_REVA2; +#else +int isci_si_rev = ISCI_SI_REVB0; +#endif +module_param(isci_si_rev, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(isci_si_rev, "override default si rev (0: A0 1: A2 2: B0)"); + +static struct scsi_host_template isci_sht = { + + .module = THIS_MODULE, + .name = DRV_NAME, + .queuecommand = sas_queuecommand, + .target_alloc = sas_target_alloc, + .slave_configure = sas_slave_configure, + .slave_destroy = sas_slave_destroy, + .scan_finished = isci_host_scan_finished, + .scan_start = isci_host_scan_start, + .change_queue_depth = sas_change_queue_depth, + .change_queue_type = sas_change_queue_type, + .bios_param = sas_bios_param, + .can_queue = ISCI_CAN_QUEUE_VAL, + .cmd_per_lun = 1, + .this_id = -1, + .sg_tablesize = SG_ALL, + .max_sectors = SCSI_DEFAULT_MAX_SECTORS, + .use_clustering = ENABLE_CLUSTERING, + .eh_device_reset_handler = sas_eh_device_reset_handler, + .eh_bus_reset_handler = isci_bus_reset_handler, + .slave_alloc = sas_slave_alloc, + .target_destroy = sas_target_destroy, + .ioctl = sas_ioctl, +}; + +static struct sas_domain_function_template isci_transport_ops = { + + /* The class calls these to notify the LLDD of an event. */ + .lldd_port_formed = isci_port_formed, + .lldd_port_deformed = isci_port_deformed, + + /* The class calls these when a device is found or gone. */ + .lldd_dev_found = isci_remote_device_found, + .lldd_dev_gone = isci_remote_device_gone, + + .lldd_execute_task = isci_task_execute_task, + /* Task Management Functions. Must be called from process context. */ + .lldd_abort_task = isci_task_abort_task, + .lldd_abort_task_set = isci_task_abort_task_set, + .lldd_clear_aca = isci_task_clear_aca, + .lldd_clear_task_set = isci_task_clear_task_set, + .lldd_I_T_nexus_reset = isci_task_I_T_nexus_reset, + .lldd_lu_reset = isci_task_lu_reset, + .lldd_query_task = isci_task_query_task, + + /* Port and Adapter management */ + .lldd_clear_nexus_port = isci_task_clear_nexus_port, + .lldd_clear_nexus_ha = isci_task_clear_nexus_ha, + + /* Phy management */ + .lldd_control_phy = isci_phy_control, +}; + + +/****************************************************************************** +* P R O T E C T E D M E T H O D S +******************************************************************************/ + + + +/** + * isci_register_sas_ha() - This method initializes various lldd + * specific members of the sas_ha struct and calls the libsas + * sas_register_ha() function. + * @isci_host: This parameter specifies the lldd specific wrapper for the + * libsas sas_ha struct. + * + * This method returns an error code indicating sucess or failure. The user + * should check for possible memory allocation error return otherwise, a zero + * indicates success. + */ +static int isci_register_sas_ha(struct isci_host *isci_host) +{ + int i; + struct sas_ha_struct *sas_ha = &(isci_host->sas_ha); + struct asd_sas_phy **sas_phys; + struct asd_sas_port **sas_ports; + + sas_phys = devm_kzalloc(&isci_host->pdev->dev, + SCI_MAX_PHYS * sizeof(void *), + GFP_KERNEL); + if (!sas_phys) + return -ENOMEM; + + sas_ports = devm_kzalloc(&isci_host->pdev->dev, + SCI_MAX_PORTS * sizeof(void *), + GFP_KERNEL); + if (!sas_ports) + return -ENOMEM; + + /*----------------- Libsas Initialization Stuff---------------------- + * Set various fields in the sas_ha struct: + */ + + sas_ha->sas_ha_name = DRV_NAME; + sas_ha->lldd_module = THIS_MODULE; + sas_ha->sas_addr = &(isci_host->sas_addr[0]); + + /* set the array of phy and port structs. */ + for (i = 0; i < SCI_MAX_PHYS; i++) { + sas_phys[i] = &(isci_host->phys[i].sas_phy); + sas_ports[i] = &(isci_host->sas_ports[i]); + } + + sas_ha->sas_phy = sas_phys; + sas_ha->sas_port = sas_ports; + sas_ha->num_phys = SCI_MAX_PHYS; + + sas_ha->lldd_queue_size = ISCI_CAN_QUEUE_VAL; + sas_ha->lldd_max_execute_num = 1; + sas_ha->strict_wide_ports = 1; + + sas_register_ha(sas_ha); + + return 0; +} + +static void isci_unregister_sas_ha(struct isci_host *isci_host) +{ + if (!isci_host) + return; + + sas_unregister_ha(&(isci_host->sas_ha)); + + sas_remove_host(isci_host->shost); + scsi_remove_host(isci_host->shost); + scsi_host_put(isci_host->shost); +} + +static int __devinit isci_pci_init(struct pci_dev *pdev) +{ + int err, bar_num, bar_mask; + void __iomem * const *iomap; + + err = pcim_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, + "failed enable PCI device %s!\n", + pci_name(pdev)); + return err; + } + + for (bar_num = 0; bar_num < SCI_PCI_BAR_COUNT; bar_num++) + bar_mask |= 1 << (bar_num * 2); + + err = pcim_iomap_regions(pdev, bar_mask, DRV_NAME); + if (err) + return err; + + iomap = pcim_iomap_table(pdev); + if (!iomap) + return -ENOMEM; + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) + return err; + } + + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); + if (err) { + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (err) + return err; + } + + return 0; +} + +static struct isci_host *isci_host_by_id(struct pci_dev *pdev, int id) +{ + struct isci_host *h; + + for_each_isci_host(h, pdev) + if (h->id == id) + return h; + return NULL; +} + +static int num_controllers(struct pci_dev *pdev) +{ + /* bar size alone can tell us if we are running with a dual controller + * part, no need to trust revision ids that might be under broken firmware + * control + */ + resource_size_t scu_bar_size = pci_resource_len(pdev, SCI_SCU_BAR*2); + resource_size_t smu_bar_size = pci_resource_len(pdev, SCI_SMU_BAR*2); + + if (scu_bar_size >= SCI_SCU_BAR_SIZE*SCI_MAX_CONTROLLERS && + smu_bar_size >= SCI_SMU_BAR_SIZE*SCI_MAX_CONTROLLERS) + return SCI_MAX_CONTROLLERS; + else + return 1; +} + +static int isci_setup_interrupts(struct pci_dev *pdev) +{ + int err, i, num_msix; + struct isci_pci_info *pci_info = to_pci_info(pdev); + + /* + * Determine the number of vectors associated with this + * PCI function. + */ + num_msix = num_controllers(pdev) * SCI_NUM_MSI_X_INT; + + for (i = 0; i < num_msix; i++) + pci_info->msix_entries[i].entry = i; + + err = pci_enable_msix(pdev, pci_info->msix_entries, num_msix); + if (err) + goto intx; + + for (i = 0; i < num_msix; i++) { + int id = i / SCI_NUM_MSI_X_INT; + struct msix_entry *msix = &pci_info->msix_entries[i]; + struct isci_host *isci_host = isci_host_by_id(pdev, id); + + BUG_ON(!isci_host); + + /* @todo: need to handle error case. */ + err = devm_request_irq(&pdev->dev, msix->vector, isci_isr, 0, + DRV_NAME"-msix", isci_host); + if (!err) + continue; + + dev_info(&pdev->dev, "msix setup failed falling back to intx\n"); + while (i--) { + id = i / SCI_NUM_MSI_X_INT; + isci_host = isci_host_by_id(pdev, id); + msix = &pci_info->msix_entries[i]; + devm_free_irq(&pdev->dev, msix->vector, isci_host); + } + pci_disable_msix(pdev); + goto intx; + } + + return 0; + + intx: + err = devm_request_irq(&pdev->dev, pdev->irq, isci_legacy_isr, + IRQF_SHARED, DRV_NAME"-intx", pdev); + + return err; +} + +/** + * isci_parse_oem_parameters() - This method will take OEM parameters + * from the module init parameters and copy them to oem_params. This will + * only copy values that are not set to the module parameter default values + * @oem_parameters: This parameter specifies the controller default OEM + * parameters. It is expected that this has been initialized to the default + * parameters for the controller + * + * + */ +enum sci_status isci_parse_oem_parameters(union scic_oem_parameters *oem_params, + int scu_index, + struct isci_firmware *fw) +{ + int i; + + /* check for valid inputs */ + if (!(scu_index >= 0 + && scu_index < SCI_MAX_CONTROLLERS + && oem_params != NULL)) { + return SCI_FAILURE; + } + + for (i = 0; i < SCI_MAX_PHYS; i++) { + int array_idx = i + (SCI_MAX_PHYS * scu_index); + u64 sas_addr = fw->sas_addrs[array_idx]; + + if (sas_addr != 0) { + oem_params->sds1.phys[i].sas_address.low = + (u32)(sas_addr & 0xffffffff); + oem_params->sds1.phys[i].sas_address.high = + (u32)((sas_addr >> 32) & 0xffffffff); + } + } + + for (i = 0; i < SCI_MAX_PORTS; i++) { + int array_idx = i + (SCI_MAX_PORTS * scu_index); + u32 pmask = fw->phy_masks[array_idx]; + + oem_params->sds1.ports[i].phy_mask = pmask; + } + + return SCI_SUCCESS; +} + +/** + * isci_parse_user_parameters() - This method will take user parameters + * from the module init parameters and copy them to user_params. This will + * only copy values that are not set to the module parameter default values + * @user_parameters: This parameter specifies the controller default user + * parameters. It is expected that this has been initialized to the default + * parameters for the controller + * + * + */ +enum sci_status isci_parse_user_parameters( + union scic_user_parameters *user_params, + int scu_index, + struct isci_firmware *fw) +{ + int i; + + if (!(scu_index >= 0 + && scu_index < SCI_MAX_CONTROLLERS + && user_params != NULL)) { + return SCI_FAILURE; + } + + for (i = 0; i < SCI_MAX_PORTS; i++) { + int array_idx = i + (SCI_MAX_PORTS * scu_index); + u32 gen = fw->phy_gens[array_idx]; + + user_params->sds1.phys[i].max_speed_generation = gen; + + } + + return SCI_SUCCESS; +} + +static struct isci_host *isci_host_alloc(struct pci_dev *pdev, int id) +{ + struct isci_host *isci_host; + struct Scsi_Host *shost; + int err; + + isci_host = devm_kzalloc(&pdev->dev, sizeof(*isci_host), GFP_KERNEL); + if (!isci_host) + return NULL; + + isci_host->pdev = pdev; + isci_host->id = id; + + shost = scsi_host_alloc(&isci_sht, sizeof(void *)); + if (!shost) + return NULL; + isci_host->shost = shost; + + err = isci_host_init(isci_host); + if (err) + goto err_shost; + + SHOST_TO_SAS_HA(shost) = &isci_host->sas_ha; + isci_host->sas_ha.core.shost = shost; + shost->transportt = isci_transport_template; + + shost->max_id = ~0; + shost->max_lun = ~0; + shost->max_cmd_len = MAX_COMMAND_SIZE; + + err = scsi_add_host(shost, &pdev->dev); + if (err) + goto err_shost; + + err = isci_register_sas_ha(isci_host); + if (err) + goto err_shost_remove; + + return isci_host; + + err_shost_remove: + scsi_remove_host(shost); + err_shost: + scsi_host_put(shost); + + return NULL; +} + +static void check_si_rev(struct pci_dev *pdev) +{ + if (num_controllers(pdev) > 1) + isci_si_rev = ISCI_SI_REVB0; + else { + switch (pdev->revision) { + case 0: + case 1: + /* if the id is ambiguous don't update isci_si_rev */ + break; + case 3: + isci_si_rev = ISCI_SI_REVA2; + break; + default: + case 4: + isci_si_rev = ISCI_SI_REVB0; + break; + } + } + + dev_info(&pdev->dev, "driver configured for %s silicon (rev: %d)\n", + isci_si_rev == ISCI_SI_REVA0 ? "A0" : + isci_si_rev == ISCI_SI_REVA2 ? "A2" : "B0", pdev->revision); + +} + +static int __devinit isci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct isci_pci_info *pci_info; + int err, i; + struct isci_host *isci_host; + + check_si_rev(pdev); + + pci_info = devm_kzalloc(&pdev->dev, sizeof(*pci_info), GFP_KERNEL); + if (!pci_info) + return -ENOMEM; + pci_set_drvdata(pdev, pci_info); + + err = isci_pci_init(pdev); + if (err) + return err; + + for (i = 0; i < num_controllers(pdev); i++) { + struct isci_host *h = isci_host_alloc(pdev, i); + + if (!h) { + err = -ENOMEM; + goto err_host_alloc; + } + + h->next = pci_info->hosts; + pci_info->hosts = h; + } + + err = isci_setup_interrupts(pdev); + if (err) + goto err_host_alloc; + + for_each_isci_host(isci_host, pdev) + scsi_scan_host(isci_host->shost); + + return 0; + + err_host_alloc: + for_each_isci_host(isci_host, pdev) + isci_unregister_sas_ha(isci_host); + return err; +} + +static void __devexit isci_pci_remove(struct pci_dev *pdev) +{ + struct isci_host *isci_host; + + for_each_isci_host(isci_host, pdev) { + isci_unregister_sas_ha(isci_host); + isci_host_deinit(isci_host); + scic_controller_disable_interrupts(isci_host->core_controller); + } +} + +static __init int isci_init(void) +{ + int err = -ENOMEM; + + pr_info("%s: Intel(R) C600 SAS Controller Driver\n", DRV_NAME); + + isci_kmem_cache = kmem_cache_create(DRV_NAME, + sizeof(struct isci_remote_device) + + scic_remote_device_get_object_size(), + 0, 0, NULL); + if (!isci_kmem_cache) + return err; + + isci_transport_template = sas_domain_attach_transport(&isci_transport_ops); + if (!isci_transport_template) + goto err_kmem; + + err = pci_register_driver(&isci_pci_driver); + if (err) + goto err_sas; + + return 0; + + err_sas: + sas_release_transport(isci_transport_template); + err_kmem: + kmem_cache_destroy(isci_kmem_cache); + + return err; +} + +static __exit void isci_exit(void) +{ + pci_unregister_driver(&isci_pci_driver); + sas_release_transport(isci_transport_template); + kmem_cache_destroy(isci_kmem_cache); +} + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_FIRMWARE(ISCI_FW_NAME); +module_init(isci_init); +module_exit(isci_exit); diff --git a/drivers/scsi/isci/isci.h b/drivers/scsi/isci/isci.h new file mode 100644 index 000000000000..7d984f408f24 --- /dev/null +++ b/drivers/scsi/isci/isci.h @@ -0,0 +1,138 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the isci_module object definition. + * + * isci.h + */ + +#if !defined(_SCI_MODULE_H_) +#define _SCI_MODULE_H_ + +/** + * This file contains the SCI low level driver interface to the SCI and Libsas + * Libraries. + * + * isci.h + */ + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/firmware.h> +#include <linux/bug.h> +#include <scsi/libsas.h> +#include <scsi/scsi.h> + +#include "sci_types.h" +#include "sci_base_controller.h" +#include "scic_controller.h" +#include "host.h" +#include "timers.h" +#include "sci_status.h" + +extern int loglevel; +extern struct kmem_cache *isci_kmem_cache; + +#define ISCI_FW_NAME "isci/isci_firmware.bin" + +#define ISCI_FIRMWARE_MIN_SIZE 149 + +#define ISCI_FW_IDSIZE 12 +#define ISCI_FW_VER_OFS ISCI_FW_IDSIZE +#define ISCI_FW_SUBVER_OFS ISCI_FW_VER_OFS + 1 +#define ISCI_FW_DATA_OFS ISCI_FW_SUBVER_OFS + 1 + +#define ISCI_FW_HDR_PHYMASK 0x1 +#define ISCI_FW_HDR_PHYGEN 0x2 +#define ISCI_FW_HDR_SASADDR 0x3 +#define ISCI_FW_HDR_EOF 0xff + +struct isci_firmware { + const u8 *id; + u8 version; + u8 subversion; + const u32 *phy_masks; + u8 phy_masks_size; + const u32 *phy_gens; + u8 phy_gens_size; + const u64 *sas_addrs; + u8 sas_addrs_size; +}; + +irqreturn_t isci_isr(int vec, void *data); +irqreturn_t isci_legacy_isr(int vec, void *data); + +enum sci_status isci_parse_oem_parameters( + union scic_oem_parameters *oem_params, + int scu_index, + struct isci_firmware *fw); + +enum sci_status isci_parse_user_parameters( + union scic_user_parameters *user_params, + int scu_index, + struct isci_firmware *fw); + +#ifdef ISCI_SLAVE_ALLOC +extern int ISCI_SLAVE_ALLOC(struct scsi_device *scsi_dev); +#endif /* ISCI_SLAVE_ALLOC */ + +#ifdef ISCI_SLAVE_DESTROY +extern void ISCI_SLAVE_DESTROY(struct scsi_device *scsi_dev); +#endif /* ISCI_SLAVE_DESTROY */ +#endif /* !defined(_SCI_MODULE_H_) */ diff --git a/drivers/scsi/isci/phy.c b/drivers/scsi/isci/phy.c new file mode 100644 index 000000000000..fbda570d25e1 --- /dev/null +++ b/drivers/scsi/isci/phy.c @@ -0,0 +1,179 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "isci.h" +#include "phy.h" +#include "scic_port.h" +#include "scic_config_parameters.h" + + +/** + * isci_phy_init() - This function is called by the probe function to + * initialize the phy objects. This func assumes that the isci_port objects + * associated with the SCU have been initialized. + * @isci_phy: This parameter specifies the isci_phy object to initialize + * @isci_host: This parameter specifies the parent SCU host object for this + * isci_phy + * @index: This parameter specifies which SCU phy associates with this + * isci_phy. Generally, SCU phy 0 relates isci_phy 0, etc. + * + */ +void isci_phy_init( + struct isci_phy *phy, + struct isci_host *isci_host, + int index) +{ + struct scic_sds_controller *controller = isci_host->core_controller; + struct scic_sds_phy *scic_phy; + union scic_oem_parameters oem_parameters; + enum sci_status status = SCI_SUCCESS; + + /*--------------- SCU_Phy Initialization Stuff -----------------------*/ + + status = scic_controller_get_phy_handle(controller, index, &scic_phy); + if (status == SCI_SUCCESS) { + sci_object_set_association(scic_phy, (void *)phy); + phy->sci_phy_handle = scic_phy; + } else + dev_err(&isci_host->pdev->dev, + "failed scic_controller_get_phy_handle\n"); + + scic_oem_parameters_get(controller, &oem_parameters); + + phy->sas_addr[0] = oem_parameters.sds1.phys[index].sas_address.low + & 0xFF; + phy->sas_addr[1] = (oem_parameters.sds1.phys[index].sas_address.low + >> 8) & 0xFF; + phy->sas_addr[2] = (oem_parameters.sds1.phys[index].sas_address.low + >> 16) & 0xFF; + phy->sas_addr[3] = (oem_parameters.sds1.phys[index].sas_address.low + >> 24) & 0xFF; + phy->sas_addr[4] = oem_parameters.sds1.phys[index].sas_address.high + & 0xFF; + phy->sas_addr[5] = (oem_parameters.sds1.phys[index].sas_address.high + >> 8) & 0xFF; + phy->sas_addr[6] = (oem_parameters.sds1.phys[index].sas_address.high + >> 16) & 0xFF; + phy->sas_addr[7] = (oem_parameters.sds1.phys[index].sas_address.high + >> 24) & 0xFF; + + phy->isci_port = NULL; + phy->sas_phy.enabled = 0; + phy->sas_phy.id = index; + phy->sas_phy.sas_addr = &phy->sas_addr[0]; + phy->sas_phy.frame_rcvd = (u8 *)&phy->frame_rcvd; + phy->sas_phy.ha = &isci_host->sas_ha; + phy->sas_phy.lldd_phy = phy; + phy->sas_phy.enabled = 1; + phy->sas_phy.class = SAS; + phy->sas_phy.iproto = SAS_PROTOCOL_ALL; + phy->sas_phy.tproto = 0; + phy->sas_phy.type = PHY_TYPE_PHYSICAL; + phy->sas_phy.role = PHY_ROLE_INITIATOR; + phy->sas_phy.oob_mode = OOB_NOT_CONNECTED; + phy->sas_phy.linkrate = SAS_LINK_RATE_UNKNOWN; + memset((u8 *)&phy->frame_rcvd, 0, sizeof(phy->frame_rcvd)); +} + + +/** + * isci_phy_control() - This function is one of the SAS Domain Template + * functions. This is a phy management function. + * @phy: This parameter specifies the sphy being controlled. + * @func: This parameter specifies the phy control function being invoked. + * @buf: This parameter is specific to the phy function being invoked. + * + * status, zero indicates success. + */ +int isci_phy_control( + struct asd_sas_phy *phy, + enum phy_func func, + void *buf) +{ + int ret = TMF_RESP_FUNC_COMPLETE; + struct isci_phy *isci_phy_ptr = (struct isci_phy *)phy->lldd_phy; + struct isci_port *isci_port_ptr = NULL; + + if (isci_phy_ptr != NULL) + isci_port_ptr = isci_phy_ptr->isci_port; + + if ((isci_phy_ptr == NULL) || (isci_port_ptr == NULL)) { + pr_err("%s: asd_sas_phy %p: lldd_phy %p or " + "isci_port %p == NULL!\n", + __func__, phy, isci_phy_ptr, isci_port_ptr); + return TMF_RESP_FUNC_FAILED; + } + + pr_debug("%s: phy %p; func %d; buf %p; isci phy %p, port %p\n", + __func__, phy, func, buf, isci_phy_ptr, isci_port_ptr); + + switch (func) { + case PHY_FUNC_HARD_RESET: + case PHY_FUNC_LINK_RESET: + + /* Perform the port reset. */ + ret = isci_port_perform_hard_reset(isci_port_ptr, isci_phy_ptr); + + break; + + case PHY_FUNC_DISABLE: + default: + pr_debug("%s: phy %p; func %d NOT IMPLEMENTED!\n", + __func__, phy, func); + ret = TMF_RESP_FUNC_FAILED; + break; + } + return ret; +} diff --git a/drivers/scsi/isci/phy.h b/drivers/scsi/isci/phy.h new file mode 100644 index 000000000000..44b727f1c455 --- /dev/null +++ b/drivers/scsi/isci/phy.h @@ -0,0 +1,104 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + + +#if !defined(_ISCI_PHY_H_) +#define _ISCI_PHY_H_ + +#include "port.h" +#include "host.h" +#include <scsi/libsas.h> + + +/** + * struct isci_phy - This class implements the ISCI specific representation of + * the phy object. + * + * + */ + +struct isci_phy { + + struct scic_sds_phy *sci_phy_handle; + + struct asd_sas_phy sas_phy; + struct sas_identify_frame *frame; + struct isci_port *isci_port; + u8 sas_addr[SAS_ADDR_SIZE]; + + union { + + u8 aif[sizeof(struct sci_sas_identify_address_frame)]; + u8 fis[sizeof(struct sata_fis_reg_d2h)]; + + } frame_rcvd; +}; + +#define to_isci_phy(p) \ + container_of(p, struct isci_phy, sas_phy); + +struct isci_host; + +void isci_phy_init( + struct isci_phy *phy, + struct isci_host *isci_host, + int index); + +int isci_phy_control( + struct asd_sas_phy *phy, + enum phy_func func, + void *buf); + +#endif /* !defined(_ISCI_PHY_H_) */ diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c new file mode 100644 index 000000000000..2343f6556428 --- /dev/null +++ b/drivers/scsi/isci/port.c @@ -0,0 +1,484 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the isci port implementation. + * + * + */ + + +#include <linux/workqueue.h> +#include "isci.h" +#include "scic_io_request.h" +#include "scic_remote_device.h" +#include "scic_phy.h" +#include "scic_sds_phy.h" +#include "scic_port.h" +#include "port.h" +#include "request.h" + +static void isci_port_change_state( + struct isci_port *isci_port, + enum isci_status status); + + + +/** + * isci_port_init() - This function initializes the given isci_port object. + * @isci_port: This parameter specifies the port object to be initialized. + * @isci_host: This parameter specifies parent controller object for the port. + * @index: This parameter specifies which SCU port the isci_port associates + * with. Generally, SCU port 0 relates to isci_port 0, etc. + * + */ +void isci_port_init( + struct isci_port *isci_port, + struct isci_host *isci_host, + int index) +{ + struct scic_sds_port *scic_port; + struct scic_sds_controller *controller = isci_host->core_controller; + + INIT_LIST_HEAD(&isci_port->remote_dev_list); + INIT_LIST_HEAD(&isci_port->domain_dev_list); + spin_lock_init(&isci_port->remote_device_lock); + spin_lock_init(&isci_port->state_lock); + init_completion(&isci_port->start_complete); + isci_port->isci_host = isci_host; + isci_port_change_state(isci_port, isci_freed); + + (void)scic_controller_get_port_handle(controller, index, &scic_port); + sci_object_set_association(scic_port, isci_port); + isci_port->sci_port_handle = scic_port; +} + + +/** + * isci_port_get_state() - This function gets the status of the port object. + * @isci_port: This parameter points to the isci_port object + * + * status of the object as a isci_status enum. + */ +enum isci_status isci_port_get_state( + struct isci_port *isci_port) +{ + return isci_port->status; +} + +static void isci_port_change_state( + struct isci_port *isci_port, + enum isci_status status) +{ + unsigned long flags; + + dev_dbg(&isci_port->isci_host->pdev->dev, + "%s: isci_port = %p, state = 0x%x\n", + __func__, isci_port, status); + + spin_lock_irqsave(&isci_port->state_lock, flags); + isci_port->status = status; + spin_unlock_irqrestore(&isci_port->state_lock, flags); +} + +void isci_port_bc_change_received( + struct isci_host *isci_host, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + struct isci_phy *isci_phy = + (struct isci_phy *)sci_object_get_association(phy); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_phy = %p, sas_phy = %p\n", + __func__, + isci_phy, + &isci_phy->sas_phy); + + isci_host->sas_ha.notify_port_event( + &isci_phy->sas_phy, + PORTE_BROADCAST_RCVD + ); + + scic_port_enable_broadcast_change_notification(port); +} + +/** + * isci_port_link_up() - This function is called by the sci core when a link + * becomes active. the identify address frame is retrieved from the core and + * a notify port event is sent to libsas. + * @isci_host: This parameter specifies the isci host object. + * @port: This parameter specifies the sci port with the active link. + * @phy: This parameter specifies the sci phy with the active link. + * + */ +void isci_port_link_up( + struct isci_host *isci_host, + struct scic_sds_port *port, + struct scic_sds_phy *phy) +{ + unsigned long flags; + struct scic_port_properties properties; + struct isci_phy *isci_phy + = (struct isci_phy *)sci_object_get_association(phy); + struct isci_port *isci_port + = (struct isci_port *)sci_object_get_association(port); + enum sci_status call_status; + unsigned long success = true; + + BUG_ON(isci_phy->isci_port != NULL); + isci_phy->isci_port = isci_port; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", + __func__, isci_port); + + spin_lock_irqsave(&isci_phy->sas_phy.frame_rcvd_lock, flags); + + isci_port_change_state(isci_phy->isci_port, isci_starting); + + scic_port_get_properties(port, &properties); + + if (properties.remote.protocols.u.bits.stp_target) { + + struct scic_sata_phy_properties sata_phy_properties; + + isci_phy->sas_phy.oob_mode = SATA_OOB_MODE; + + /* Get a copy of the signature fis for libsas */ + call_status = scic_sata_phy_get_properties(phy, + &sata_phy_properties); + + /* + * XXX I am concerned about this "assert". shouldn't we + * handle the return appropriately? + */ + BUG_ON(call_status != SCI_SUCCESS); + + memcpy(isci_phy->frame_rcvd.fis, + &sata_phy_properties.signature_fis, + sizeof(struct sata_fis_reg_d2h)); + + isci_phy->sas_phy.frame_rcvd_size = sizeof(struct sata_fis_reg_d2h); + + /* + * For direct-attached SATA devices, the SCI core will + * automagically assign a SAS address to the end device + * for the purpose of creating a port. This SAS address + * will not be the same as assigned to the PHY and needs + * to be obtained from struct scic_port_properties properties. + */ + + BUG_ON(((size_t)SAS_ADDR_SIZE / 2) + != sizeof(properties.remote.sas_address.low)); + + memcpy(&isci_phy->sas_phy.attached_sas_addr[0], + &properties.remote.sas_address.low, + SAS_ADDR_SIZE / 2); + + memcpy(&isci_phy->sas_phy.attached_sas_addr[4], + &properties.remote.sas_address.high, + SAS_ADDR_SIZE / 2); + + } else if (properties.remote.protocols.u.bits.ssp_target || + properties.remote.protocols.u.bits.smp_target) { + + struct scic_sas_phy_properties sas_phy_properties; + + isci_phy->sas_phy.oob_mode = SAS_OOB_MODE; + + /* Get a copy of the identify address frame for libsas */ + call_status = scic_sas_phy_get_properties(phy, + &sas_phy_properties); + + BUG_ON(call_status != SCI_SUCCESS); + + memcpy(isci_phy->frame_rcvd.aif, + &(sas_phy_properties.received_iaf), + sizeof(struct sci_sas_identify_address_frame)); + + isci_phy->sas_phy.frame_rcvd_size + = sizeof(struct sci_sas_identify_address_frame); + + /* Copy the attached SAS address from the IAF */ + memcpy(isci_phy->sas_phy.attached_sas_addr, + ((struct sas_identify_frame *) + (&isci_phy->frame_rcvd.aif))->sas_addr, + SAS_ADDR_SIZE); + + } else { + dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__); + success = false; + } + + spin_unlock_irqrestore(&isci_phy->sas_phy.frame_rcvd_lock, flags); + + /* Notify libsas that we have an address frame, if indeed + * we've found an SSP, SMP, or STP target */ + if (success) + isci_host->sas_ha.notify_port_event(&isci_phy->sas_phy, + PORTE_BYTES_DMAED); +} + + +/** + * isci_port_link_down() - This function is called by the sci core when a link + * becomes inactive. + * @isci_host: This parameter specifies the isci host object. + * @phy: This parameter specifies the isci phy with the active link. + * @port: This parameter specifies the isci port with the active link. + * + */ +void isci_port_link_down( + struct isci_host *isci_host, + struct isci_phy *isci_phy, + struct isci_port *isci_port) +{ + struct isci_remote_device *isci_device; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", __func__, isci_port); + + if (isci_port) { + + /* check to see if this is the last phy on this port. */ + if (isci_phy->sas_phy.port + && isci_phy->sas_phy.port->num_phys == 1) { + + /* change the state for all devices on this port. + * The next task sent to this device will be returned + * as SAS_TASK_UNDELIVERED, and the scsi mid layer + * will remove the target + */ + list_for_each_entry(isci_device, + &isci_port->remote_dev_list, + node) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", + __func__, isci_device); + isci_remote_device_change_state(isci_device, + isci_stopping); + } + } + isci_port_change_state(isci_port, isci_stopping); + } + + /* Notify libsas of the borken link, this will trigger calls to our + * isci_port_deformed and isci_dev_gone functions. + */ + sas_phy_disconnected(&isci_phy->sas_phy); + isci_host->sas_ha.notify_phy_event(&isci_phy->sas_phy, + PHYE_LOSS_OF_SIGNAL); + + isci_phy->isci_port = NULL; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p - Done\n", __func__, isci_port); +} + + +/** + * isci_port_deformed() - This function is called by libsas when a port becomes + * inactive. + * @phy: This parameter specifies the libsas phy with the inactive port. + * + */ +void isci_port_deformed( + struct asd_sas_phy *phy) +{ + pr_debug("%s: sas_phy = %p\n", __func__, phy); +} + +/** + * isci_port_formed() - This function is called by libsas when a port becomes + * active. + * @phy: This parameter specifies the libsas phy with the active port. + * + */ +void isci_port_formed( + struct asd_sas_phy *phy) +{ + pr_debug("%s: sas_phy = %p, sas_port = %p\n", __func__, phy, phy->port); +} + +/** + * isci_port_ready() - This function is called by the sci core when a link + * becomes ready. + * @isci_host: This parameter specifies the isci host object. + * @port: This parameter specifies the sci port with the active link. + * + */ +void isci_port_ready( + struct isci_host *isci_host, + struct isci_port *isci_port) +{ + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", __func__, isci_port); + + complete_all(&isci_port->start_complete); + isci_port_change_state(isci_port, isci_ready); + return; +} + +/** + * isci_port_not_ready() - This function is called by the sci core when a link + * is not ready. All remote devices on this link will be removed if they are + * in the stopping state. + * @isci_host: This parameter specifies the isci host object. + * @port: This parameter specifies the sci port with the active link. + * + */ +void isci_port_not_ready( + struct isci_host *isci_host, + struct isci_port *isci_port) +{ + dev_dbg(&isci_host->pdev->dev, + "%s: isci_port = %p\n", __func__, isci_port); +} + +/** + * isci_port_hard_reset_complete() - This function is called by the sci core + * when the hard reset complete notification has been received. + * @port: This parameter specifies the sci port with the active link. + * @completion_status: This parameter specifies the core status for the reset + * process. + * + */ +void isci_port_hard_reset_complete( + struct isci_port *isci_port, + enum sci_status completion_status) +{ + dev_dbg(&isci_port->isci_host->pdev->dev, + "%s: isci_port = %p, completion_status=%x\n", + __func__, isci_port, completion_status); + + /* Save the status of the hard reset from the port. */ + isci_port->hard_reset_status = completion_status; + + complete_all(&isci_port->hard_reset_complete); +} +/** + * isci_port_perform_hard_reset() - This function is one of the SAS Domain + * Template functions. This is a phy management function. + * @isci_port: + * @isci_phy: + * + * status, TMF_RESP_FUNC_COMPLETE indicates success. + */ +int isci_port_perform_hard_reset( + struct isci_port *isci_port, + struct isci_phy *isci_phy) +{ + enum sci_status status; + int ret = TMF_RESP_FUNC_COMPLETE; + unsigned long flags; + + + dev_dbg(&isci_port->isci_host->pdev->dev, + "%s: isci_port = %p\n", + __func__, isci_port); + + BUG_ON(isci_port == NULL); + + init_completion(&isci_port->hard_reset_complete); + + spin_lock_irqsave(&isci_port->isci_host->scic_lock, flags); + + #define ISCI_PORT_RESET_TIMEOUT SCIC_SDS_SIGNATURE_FIS_TIMEOUT + status = scic_port_hard_reset(isci_port->sci_port_handle, + ISCI_PORT_RESET_TIMEOUT); + + spin_unlock_irqrestore(&isci_port->isci_host->scic_lock, flags); + + if (status == SCI_SUCCESS) { + wait_for_completion(&isci_port->hard_reset_complete); + + dev_dbg(&isci_port->isci_host->pdev->dev, + "%s: isci_port = %p; hard reset completion\n", + __func__, isci_port); + + if (isci_port->hard_reset_status != SCI_SUCCESS) + ret = TMF_RESP_FUNC_FAILED; + } else { + ret = TMF_RESP_FUNC_FAILED; + + dev_err(&isci_port->isci_host->pdev->dev, + "%s: isci_port = %p; scic_port_hard_reset call" + " failed 0x%x\n", + __func__, isci_port, status); + + } + + /* If the hard reset for the port has failed, consider this + * the same as link failures on all phys in the port. + */ + if (ret != TMF_RESP_FUNC_COMPLETE) { + BUG_ON(isci_port->isci_host == NULL); + + dev_err(&isci_port->isci_host->pdev->dev, + "%s: isci_port = %p; hard reset failed " + "(0x%x) - sending link down to libsas for phy %p\n", + __func__, + isci_port, + isci_port->hard_reset_status, + isci_phy); + + isci_port_link_down(isci_port->isci_host, + isci_phy, + isci_port); + } + + return ret; +} diff --git a/drivers/scsi/isci/port.h b/drivers/scsi/isci/port.h new file mode 100644 index 000000000000..b01b0c63e53f --- /dev/null +++ b/drivers/scsi/isci/port.h @@ -0,0 +1,153 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +/** + * This file contains the isci_port object definition. + * + * port.h + */ + +#if !defined(_ISCI_PORT_H_) +#define _ISCI_PORT_H_ + +struct isci_phy; +struct isci_host; + + +enum isci_status { + isci_freed = 0x00, + isci_starting = 0x01, + isci_ready = 0x02, + isci_ready_for_io = 0x03, + isci_stopping = 0x04, + isci_stopped = 0x05, + isci_host_quiesce = 0x06 +}; + +/** + * struct isci_port - This class represents the port object used to internally + * represent libsas port objects. It also keeps a list of remote device + * objects. + * + * + */ +struct isci_port { + + struct scic_sds_port *sci_port_handle; + + enum isci_status status; + struct isci_host *isci_host; + struct asd_sas_port sas_port; + struct list_head remote_dev_list; + spinlock_t remote_device_lock; + spinlock_t state_lock; + struct list_head domain_dev_list; + struct completion start_complete; + struct completion hard_reset_complete; + enum sci_status hard_reset_status; +}; + +#define to_isci_port(p) \ + container_of(p, struct isci_port, sas_port); + +enum isci_status isci_port_get_state( + struct isci_port *isci_port); + + + +void isci_port_formed( + struct asd_sas_phy *); + +void isci_port_deformed( + struct asd_sas_phy *); + +void isci_port_bc_change_received( + struct isci_host *isci_host, + struct scic_sds_port *port, + struct scic_sds_phy *phy); + +void isci_port_link_up( + struct isci_host *isci_host, + struct scic_sds_port *port, + struct scic_sds_phy *phy); + +void isci_port_link_down( + struct isci_host *isci_host, + struct isci_phy *isci_phy, + struct isci_port *port); + +void isci_port_ready( + struct isci_host *isci_host, + struct isci_port *isci_port); + +void isci_port_not_ready( + struct isci_host *isci_host, + struct isci_port *port); + +void isci_port_init( + struct isci_port *port, + struct isci_host *host, + int index); + +void isci_port_hard_reset_complete( + struct isci_port *isci_port, + enum sci_status completion_status); + +int isci_port_perform_hard_reset( + struct isci_port *isci_port_ptr, + struct isci_phy *isci_phy_ptr); + +#endif /* !defined(_ISCI_PORT_H_) */ + diff --git a/drivers/scsi/isci/remote_device.c b/drivers/scsi/isci/remote_device.c new file mode 100644 index 000000000000..dbf3c82f6195 --- /dev/null +++ b/drivers/scsi/isci/remote_device.c @@ -0,0 +1,698 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "isci.h" +#include "scic_io_request.h" +#include "scic_remote_device.h" +#include "scic_phy.h" +#include "scic_port.h" +#include "port.h" +#include "remote_device.h" +#include "request.h" +#include "task.h" + + + +/** + * isci_remote_device_deconstruct() - This function frees an isci_remote_device. + * @isci_host: This parameter specifies the isci host object. + * @isci_device: This parameter specifies the remote device to be freed. + * + */ +static void isci_remote_device_deconstruct( + struct isci_host *isci_host, + struct isci_remote_device *isci_device) +{ + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + /* There should not be any outstanding io's. All paths to + * here should go through isci_remote_device_nuke_requests. + * If we hit this condition, we will need a way to complete + * io requests in process */ + while (!list_empty(&isci_device->reqs_in_process)) { + + dev_err(&isci_host->pdev->dev, + "%s: ** request list not empty! **\n", __func__); + BUG(); + } + + /* Remove all related references to this device and free + * the cache object. + */ + scic_remote_device_destruct(isci_device->sci_device_handle); + isci_device->domain_dev->lldd_dev = NULL; + list_del(&isci_device->node); + kmem_cache_free(isci_kmem_cache, isci_device); +} + + +/** + * isci_remote_device_construct() - This function calls the scic remote device + * construct and start functions, it waits on the remote device start + * completion. + * @port: This parameter specifies the isci port with the remote device. + * @isci_device: This parameter specifies the isci remote device + * + * status from the scic calls, the caller to this function should clean up + * resources as appropriate. + */ +static enum sci_status isci_remote_device_construct( + struct isci_port *port, + struct isci_remote_device *isci_device) +{ + enum sci_status status = SCI_SUCCESS; + + /* let the core do it's common constuction. */ + scic_remote_device_construct(port->sci_port_handle, + isci_device->sci_device_handle); + + /* let the core do it's device specific constuction. */ + if (isci_device->domain_dev->parent && + (isci_device->domain_dev->parent->dev_type == EDGE_DEV)) { + int i; + + /* struct smp_response_discover discover_response; */ + struct discover_resp discover_response; + struct domain_device *parent = + isci_device->domain_dev->parent; + + struct expander_device *parent_ex = &parent->ex_dev; + + for (i = 0; i < parent_ex->num_phys; i++) { + + struct ex_phy *phy = &parent_ex->ex_phy[i]; + + if ((phy->phy_state == PHY_VACANT) || + (phy->phy_state == PHY_NOT_PRESENT)) + continue; + + if (SAS_ADDR(phy->attached_sas_addr) + == SAS_ADDR(isci_device->domain_dev->sas_addr)) { + + discover_response.attached_dev_type + = phy->attached_dev_type; + discover_response.linkrate + = phy->linkrate; + discover_response.attached_sata_host + = phy->attached_sata_host; + discover_response.attached_sata_dev + = phy->attached_sata_dev; + discover_response.attached_sata_ps + = phy->attached_sata_ps; + discover_response.iproto + = phy->attached_iproto >> 1; + discover_response.tproto + = phy->attached_tproto >> 1; + memcpy( + discover_response.attached_sas_addr, + phy->attached_sas_addr, + SAS_ADDR_SIZE + ); + discover_response.attached_phy_id + = phy->attached_phy_id; + discover_response.change_count + = phy->phy_change_count; + discover_response.routing_attr + = phy->routing_attr; + discover_response.hmin_linkrate + = phy->phy->minimum_linkrate_hw; + discover_response.hmax_linkrate + = phy->phy->maximum_linkrate_hw; + discover_response.pmin_linkrate + = phy->phy->minimum_linkrate; + discover_response.pmax_linkrate + = phy->phy->maximum_linkrate; + } + } + + + dev_dbg(&port->isci_host->pdev->dev, + "%s: parent->dev_type = EDGE_DEV\n", + __func__); + + status = scic_remote_device_ea_construct( + isci_device->sci_device_handle, + (struct smp_response_discover *)&discover_response + ); + + } else + status = scic_remote_device_da_construct( + isci_device->sci_device_handle + ); + + + if (status != SCI_SUCCESS) { + dev_dbg(&port->isci_host->pdev->dev, + "%s: scic_remote_device_da_construct failed - " + "isci_device = %p\n", + __func__, + isci_device); + + return status; + } + + sci_object_set_association( + isci_device->sci_device_handle, + isci_device + ); + + BUG_ON(port->isci_host == NULL); + + /* start the device. */ + status = scic_remote_device_start( + isci_device->sci_device_handle, + ISCI_REMOTE_DEVICE_START_TIMEOUT + ); + + if (status != SCI_SUCCESS) { + dev_warn(&port->isci_host->pdev->dev, + "%s: scic_remote_device_start failed\n", + __func__); + return status; + } + + return status; +} + + +/** + * isci_remote_device_nuke_requests() - This function terminates all requests + * for a given remote device. + * @isci_device: This parameter specifies the remote device + * + */ +void isci_remote_device_nuke_requests( + struct isci_remote_device *isci_device) +{ + DECLARE_COMPLETION_ONSTACK(aborted_task_completion); + struct isci_host *isci_host; + + isci_host = isci_device->isci_port->isci_host; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + /* Cleanup all requests pending for this device. */ + isci_terminate_pending_requests(isci_host, isci_device, terminating); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p, done\n", __func__, isci_device); +} + + + +/** + * This function builds the isci_remote_device when a libsas dev_found message + * is received. + * @isci_host: This parameter specifies the isci host object. + * @port: This parameter specifies the isci_port conected to this device. + * + * pointer to new isci_remote_device. + */ +static struct isci_remote_device * +isci_remote_device_alloc(struct isci_host *isci_host, struct isci_port *port) +{ + struct isci_remote_device *isci_device; + struct scic_sds_remote_device *sci_dev; + + isci_device = kmem_cache_zalloc(isci_kmem_cache, GFP_KERNEL); + + if (!isci_device) { + dev_warn(&isci_host->pdev->dev, "%s: failed\n", __func__); + return NULL; + } + + sci_dev = (struct scic_sds_remote_device *) &isci_device[1]; + isci_device->sci_device_handle = sci_dev; + INIT_LIST_HEAD(&isci_device->reqs_in_process); + INIT_LIST_HEAD(&isci_device->node); + isci_device->host_quiesce = false; + + spin_lock_init(&isci_device->state_lock); + spin_lock_init(&isci_device->host_quiesce_lock); + isci_remote_device_change_state(isci_device, isci_freed); + + return isci_device; + +} +/** + * isci_device_set_host_quiesce_lock_state() - This function sets the host I/O + * quiesce lock state for the remote_device object. + * @isci_device,: This parameter points to the isci_remote_device object + * @isci_device: This parameter specifies the new quiesce state. + * + */ +void isci_device_set_host_quiesce_lock_state( + struct isci_remote_device *isci_device, + bool lock_state) +{ + unsigned long flags; + + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device=%p, lock_state=%d\n", + __func__, isci_device, lock_state); + + spin_lock_irqsave(&isci_device->host_quiesce_lock, flags); + isci_device->host_quiesce = lock_state; + spin_unlock_irqrestore(&isci_device->host_quiesce_lock, flags); +} + +/** + * isci_remote_device_ready() - This function is called by the scic when the + * remote device is ready. We mark the isci device as ready and signal the + * waiting proccess. + * @isci_host: This parameter specifies the isci host object. + * @isci_device: This parameter specifies the remote device + * + */ +void isci_remote_device_ready(struct isci_remote_device *isci_device) +{ + unsigned long flags; + + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + /* device ready is actually a "ready for io" state. */ + if ((isci_starting == isci_remote_device_get_state(isci_device)) || + (isci_ready == isci_remote_device_get_state(isci_device))) { + spin_lock_irqsave(&isci_device->isci_port->remote_device_lock, + flags); + isci_remote_device_change_state(isci_device, isci_ready_for_io); + if (isci_device->completion) + complete(isci_device->completion); + spin_unlock_irqrestore( + &isci_device->isci_port->remote_device_lock, + flags); + } + +} + +/** + * isci_remote_device_not_ready() - This function is called by the scic when + * the remote device is not ready. We mark the isci device as ready (not + * "ready_for_io") and signal the waiting proccess. + * @isci_host: This parameter specifies the isci host object. + * @isci_device: This parameter specifies the remote device + * + */ +void isci_remote_device_not_ready( + struct isci_remote_device *isci_device, + u32 reason_code) +{ + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + if (reason_code == SCIC_REMOTE_DEVICE_NOT_READY_STOP_REQUESTED) + isci_remote_device_change_state(isci_device, isci_stopping); + else + /* device ready is actually a "not ready for io" state. */ + isci_remote_device_change_state(isci_device, isci_ready); +} + +/** + * isci_remote_device_stop_complete() - This function is called by the scic + * when the remote device stop has completed. We mark the isci device as not + * ready and remove the isci remote device. + * @isci_host: This parameter specifies the isci host object. + * @isci_device: This parameter specifies the remote device. + * @status: This parameter specifies status of the completion. + * + */ +void isci_remote_device_stop_complete( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + enum sci_status status) +{ + struct completion *completion = isci_device->completion; + + dev_dbg(&isci_host->pdev->dev, + "%s: complete isci_device = %p, status = 0x%x\n", + __func__, + isci_device, + status); + + isci_remote_device_change_state(isci_device, isci_stopped); + + /* after stop, we can tear down resources. */ + isci_remote_device_deconstruct(isci_host, isci_device); + + /* notify interested parties. */ + if (completion) + complete(completion); +} + +/** + * isci_remote_device_start_complete() - This function is called by the scic + * when the remote device start has completed + * @isci_host: This parameter specifies the isci host object. + * @isci_device: This parameter specifies the remote device. + * @status: This parameter specifies status of the completion. + * + */ +void isci_remote_device_start_complete( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + enum sci_status status) +{ + + +} + + +/** + * isci_remote_device_stop() - This function is called internally to stop the + * remote device. + * @isci_host: This parameter specifies the isci host object. + * @isci_device: This parameter specifies the remote device. + * + * The status of the scic request to stop. + */ +enum sci_status isci_remote_device_stop( + struct isci_remote_device *isci_device) +{ + enum sci_status status; + unsigned long flags; + + DECLARE_COMPLETION_ONSTACK(completion); + + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + isci_remote_device_change_state(isci_device, isci_stopping); + + /* We need comfirmation that stop completed. */ + isci_device->completion = &completion; + + BUG_ON(isci_device->isci_port == NULL); + BUG_ON(isci_device->isci_port->isci_host == NULL); + + spin_lock_irqsave(&isci_device->isci_port->isci_host->scic_lock, flags); + + status = scic_remote_device_stop( + isci_device->sci_device_handle, + 50 + ); + + spin_unlock_irqrestore(&isci_device->isci_port->isci_host->scic_lock, flags); + + /* Wait for the stop complete callback. */ + if (status == SCI_SUCCESS) + wait_for_completion(&completion); + + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p - after completion wait\n", + __func__, isci_device); + + isci_device->completion = NULL; + return status; +} + +/** + * isci_remote_device_gone() - This function is called by libsas when a domain + * device is removed. + * @domain_device: This parameter specifies the libsas domain device. + * + */ +void isci_remote_device_gone( + struct domain_device *domain_dev) +{ + struct isci_remote_device *isci_device = isci_dev_from_domain_dev( + domain_dev); + + dev_err(&isci_device->isci_port->isci_host->pdev->dev, + "%s: domain_device = %p, isci_device = %p, isci_port = %p\n", + __func__, domain_dev, isci_device, isci_device->isci_port); + + if (isci_device != NULL) + isci_remote_device_stop(isci_device); +} + + +/** + * isci_remote_device_found() - This function is called by libsas when a remote + * device is discovered. A remote device object is created and started. the + * function then sleeps until the sci core device started message is + * received. + * @domain_device: This parameter specifies the libsas domain device. + * + * status, zero indicates success. + */ +int isci_remote_device_found(struct domain_device *domain_dev) +{ + unsigned long flags; + struct isci_host *isci_host; + struct isci_port *isci_port; + struct isci_phy *isci_phy; + struct asd_sas_port *sas_port; + struct asd_sas_phy *sas_phy; + struct isci_remote_device *isci_device; + enum sci_status status; + DECLARE_COMPLETION_ONSTACK(completion); + + isci_host = isci_host_from_sas_ha(domain_dev->port->ha); + + dev_dbg(&isci_host->pdev->dev, + "%s: domain_device = %p\n", __func__, domain_dev); + + sas_port = domain_dev->port; + sas_phy = list_first_entry(&sas_port->phy_list, struct asd_sas_phy, + port_phy_el); + isci_phy = to_isci_phy(sas_phy); + isci_port = isci_phy->isci_port; + + /* we are being called for a device on this port, + * so it has to come up eventually + */ + wait_for_completion(&isci_port->start_complete); + + if ((isci_stopping == isci_port_get_state(isci_port)) || + (isci_stopped == isci_port_get_state(isci_port))) + return -ENODEV; + + isci_device = isci_remote_device_alloc(isci_host, isci_port); + + INIT_LIST_HEAD(&isci_device->node); + domain_dev->lldd_dev = isci_device; + isci_device->domain_dev = domain_dev; + isci_device->isci_port = isci_port; + isci_remote_device_change_state(isci_device, isci_starting); + + + spin_lock_irqsave(&isci_port->remote_device_lock, flags); + list_add_tail(&isci_device->node, &isci_port->remote_dev_list); + + /* for the device ready event. */ + isci_device->completion = &completion; + + status = isci_remote_device_construct(isci_port, isci_device); + + spin_unlock_irqrestore(&isci_port->remote_device_lock, flags); + + /* wait for the device ready callback. */ + wait_for_completion(isci_device->completion); + isci_device->completion = NULL; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", + __func__, isci_device); + + if (status != SCI_SUCCESS) { + + spin_lock_irqsave(&isci_port->remote_device_lock, flags); + isci_remote_device_deconstruct( + isci_host, + isci_device + ); + spin_unlock_irqrestore(&isci_port->remote_device_lock, flags); + return -ENODEV; + } + + wait_for_completion(&isci_host->start_complete); + + return 0; +} +/** + * isci_device_is_reset_pending() - This function will check if there is any + * pending reset condition on the device. + * @request: This parameter is the isci_device object. + * + * true if there is a reset pending for the device. + */ +bool isci_device_is_reset_pending( + struct isci_host *isci_host, + struct isci_remote_device *isci_device) +{ + struct isci_request *isci_request; + struct isci_request *tmp_req; + bool reset_is_pending = false; + unsigned long flags; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* Check for reset on all pending requests. */ + list_for_each_entry_safe(isci_request, tmp_req, + &isci_device->reqs_in_process, dev_node) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p request = %p\n", + __func__, isci_device, isci_request); + + if (isci_request->ttype == io_task) { + + unsigned long flags; + struct sas_task *task = isci_request_access_task( + isci_request); + + spin_lock_irqsave(&task->task_state_lock, flags); + if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) + reset_is_pending = true; + spin_unlock_irqrestore(&task->task_state_lock, flags); + } + } + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p reset_is_pending = %d\n", + __func__, isci_device, reset_is_pending); + + return reset_is_pending; +} + +/** + * isci_device_clear_reset_pending() - This function will clear if any pending + * reset condition flags on the device. + * @request: This parameter is the isci_device object. + * + * true if there is a reset pending for the device. + */ +void isci_device_clear_reset_pending(struct isci_remote_device *isci_device) +{ + struct isci_request *isci_request; + struct isci_request *tmp_req; + struct isci_host *isci_host = NULL; + unsigned long flags = 0; + + /* FIXME more port gone confusion, and this time it makes the + * locking "fun" + */ + if (isci_device->isci_port != NULL) + isci_host = isci_device->isci_port->isci_host; + + /* + * FIXME when the isci_host gets sorted out + * use dev_dbg() + */ + pr_debug("%s: isci_device=%p, isci_host=%p\n", + __func__, isci_device, isci_host); + + if (isci_host != NULL) + spin_lock_irqsave(&isci_host->scic_lock, flags); + else + pr_err("%s: isci_device %p; isci_host == NULL!\n", + __func__, isci_device); + + /* Clear reset pending on all pending requests. */ + list_for_each_entry_safe(isci_request, tmp_req, + &isci_device->reqs_in_process, dev_node) { + /* + * FIXME when the conditional spinlock is gone + * change to dev_dbg() + */ + pr_debug("%s: isci_device = %p request = %p\n", + __func__, isci_device, isci_request); + + if (isci_request->ttype == io_task) { + + unsigned long flags2; + struct sas_task *task = isci_request_access_task( + isci_request); + + spin_lock_irqsave(&task->task_state_lock, flags2); + task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, flags2); + } + } + + if (isci_host != NULL) + spin_unlock_irqrestore(&isci_host->scic_lock, flags); +} + +/** + * isci_remote_device_change_state() - This function gets the status of the + * remote_device object. + * @isci_device: This parameter points to the isci_remote_device object + * + * status of the object as a isci_status enum. + */ +void isci_remote_device_change_state( + struct isci_remote_device *isci_device, + enum isci_status status) +{ + unsigned long flags; + + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p, state = 0x%x", + __func__, + isci_device, + status); + + spin_lock_irqsave(&isci_device->state_lock, flags); + isci_device->status = status; + spin_unlock_irqrestore(&isci_device->state_lock, flags); +} diff --git a/drivers/scsi/isci/remote_device.h b/drivers/scsi/isci/remote_device.h new file mode 100644 index 000000000000..a208f81785eb --- /dev/null +++ b/drivers/scsi/isci/remote_device.h @@ -0,0 +1,154 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#if !defined(_ISCI_REMOTE_DEVICE_H_) +#define _ISCI_REMOTE_DEVICE_H_ +#include "scic_user_callback.h" + +struct isci_host; +struct scic_sds_remote_device; + +struct isci_remote_device { + struct scic_sds_remote_device *sci_device_handle; + enum isci_status status; + struct isci_port *isci_port; + struct domain_device *domain_dev; + struct completion *completion; + struct list_head node; + struct list_head reqs_in_process; + struct work_struct stop_work; + spinlock_t state_lock; + spinlock_t host_quiesce_lock; + bool host_quiesce; +}; + +#define to_isci_remote_device(p) \ + container_of(p, struct isci_remote_device, sci_remote_device); + +#define ISCI_REMOTE_DEVICE_START_TIMEOUT 5000 + + +/** + * This function gets the status of the remote_device object. + * @isci_device: This parameter points to the isci_remote_device object + * + * status of the object as a isci_status enum. + */ +static inline +enum isci_status isci_remote_device_get_state( + struct isci_remote_device *isci_device) +{ + return (isci_device->host_quiesce) + ? isci_host_quiesce + : isci_device->status; +} + + +/** + * isci_dev_from_domain_dev() - This accessor retrieves the remote_device + * object reference from the Linux domain_device reference. + * @domdev,: This parameter points to the Linux domain_device object . + * + * A reference to the associated isci remote device. + */ +#define isci_dev_from_domain_dev(domdev) \ + ((struct isci_remote_device *)(domdev)->lldd_dev) + +void isci_remote_device_start_complete( + struct isci_host *, + struct isci_remote_device *, + enum sci_status); + +void isci_remote_device_stop_complete( + struct isci_host *, + struct isci_remote_device *, + enum sci_status); + +enum sci_status isci_remote_device_stop( + struct isci_remote_device *isci_device); + +void isci_remote_device_nuke_requests( + struct isci_remote_device *isci_device); + +void isci_remote_device_ready( + struct isci_remote_device *); + +void isci_remote_device_not_ready( + struct isci_remote_device *, + u32); + +void isci_remote_device_gone( + struct domain_device *domain_dev); + +int isci_remote_device_found( + struct domain_device *domain_dev); + +bool isci_device_is_reset_pending( + struct isci_host *isci_host, + struct isci_remote_device *isci_device); + +void isci_device_clear_reset_pending( + struct isci_remote_device *isci_device); + +void isci_device_set_host_quiesce_lock_state( + struct isci_remote_device *isci_device, + bool lock_state); + +void isci_remote_device_change_state( + struct isci_remote_device *isci_device, + enum isci_status status); + +#endif /* !defined(_ISCI_REMOTE_DEVICE_H_) */ + diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c new file mode 100644 index 000000000000..e564121b6726 --- /dev/null +++ b/drivers/scsi/isci/request.c @@ -0,0 +1,1472 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "isci.h" +#include "scic_remote_device.h" +#include "scic_io_request.h" +#include "scic_task_request.h" +#include "scic_port.h" +#include "task.h" +#include "request.h" +#include "sata.h" +#include "scu_completion_codes.h" + + +static enum sci_status isci_request_ssp_request_construct( + struct isci_request *request) +{ + enum sci_status status; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p\n", + __func__, + request); + status = scic_io_request_construct_basic_ssp( + request->sci_request_handle + ); + return status; +} + +static enum sci_status isci_request_stp_request_construct( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + enum sci_status status; + struct host_to_dev_fis *register_fis; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p\n", + __func__, + request); + + /* Get the host_to_dev_fis from the core and copy + * the fis from the task into it. + */ + register_fis = isci_sata_task_to_fis_copy(task); + + status = scic_io_request_construct_basic_sata( + request->sci_request_handle + ); + + /* Set the ncq tag in the fis, from the queue + * command in the task. + */ + if (isci_sata_is_task_ncq(task)) { + + isci_sata_set_ncq_tag( + register_fis, + task + ); + } + + return status; +} + +/** + * isci_smp_request_build() - This function builds the smp request object. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter points to the isci_request object allocated in the + * request construct function. + * @sci_device: This parameter is the handle for the sci core's remote device + * object that is the destination for this request. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static enum sci_status isci_smp_request_build( + struct isci_request *request) +{ + enum sci_status status = SCI_FAILURE; + struct sas_task *task = isci_request_access_task(request); + + void *command_iu_address = + scic_io_request_get_command_iu_address( + request->sci_request_handle + ); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p\n", + __func__, + request); + dev_dbg(&request->isci_host->pdev->dev, + "%s: smp_req len = %d\n", + __func__, + task->smp_task.smp_req.length); + + /* copy the smp_command to the address; */ + sg_copy_to_buffer(&task->smp_task.smp_req, 1, + (char *)command_iu_address, + sizeof(struct smp_request) + ); + + status = scic_io_request_construct_smp(request->sci_request_handle); + if (status != SCI_SUCCESS) + dev_warn(&request->isci_host->pdev->dev, + "%s: scic_io_request_construct_smp failed with " + "status = %d\n", + __func__, + status); + + return status; +} + +/** + * isci_io_request_build() - This function builds the io request object. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter points to the isci_request object allocated in the + * request construct function. + * @sci_device: This parameter is the handle for the sci core's remote device + * object that is the destination for this request. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static enum sci_status isci_io_request_build( + struct isci_host *isci_host, + struct isci_request *request, + struct isci_remote_device *isci_device) +{ + struct smp_discover_response_protocols dev_protocols; + enum sci_status status = SCI_SUCCESS; + struct sas_task *task = isci_request_access_task(request); + struct scic_sds_remote_device *sci_device = + isci_device->sci_device_handle; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = 0x%p; request = %p, " + "num_scatter = %d\n", + __func__, + isci_device, + request, + task->num_scatter); + + /* map the sgl addresses, if present. + * libata does the mapping for sata devices + * before we get the request. + */ + if (task->num_scatter && + !sas_protocol_ata(task->task_proto) && + !(SAS_PROTOCOL_SMP & task->task_proto)) { + + request->num_sg_entries = dma_map_sg( + &isci_host->pdev->dev, + task->scatter, + task->num_scatter, + task->data_dir + ); + + if (request->num_sg_entries == 0) + return SCI_FAILURE_INSUFFICIENT_RESOURCES; + } + + /* build the common request object. For now, + * we will let the core allocate the IO tag. + */ + status = scic_io_request_construct( + isci_host->core_controller, + sci_device, + SCI_CONTROLLER_INVALID_IO_TAG, + request, + request->sci_request_mem_ptr, + (struct scic_sds_request **)&request->sci_request_handle + ); + + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: failed request construct\n", + __func__); + return SCI_FAILURE; + } + + sci_object_set_association(request->sci_request_handle, request); + + /* Determine protocol and call the appropriate basic constructor */ + scic_remote_device_get_protocols(sci_device, &dev_protocols); + if (dev_protocols.u.bits.attached_ssp_target) + status = isci_request_ssp_request_construct(request); + else if (dev_protocols.u.bits.attached_stp_target) + status = isci_request_stp_request_construct(request); + else if (dev_protocols.u.bits.attached_smp_target) + status = isci_smp_request_build(request); + else { + dev_warn(&isci_host->pdev->dev, + "%s: unknown protocol\n", __func__); + return SCI_FAILURE; + } + + return SCI_SUCCESS; +} + + +/** + * isci_request_alloc_core() - This function gets the request object from the + * isci_host dma cache. + * @isci_host: This parameter specifies the ISCI host object + * @isci_request: This parameter will contain the pointer to the new + * isci_request object. + * @isci_device: This parameter is the pointer to the isci remote device object + * that is the destination for this request. + * @gfp_flags: This parameter specifies the os allocation flags. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static int isci_request_alloc_core( + struct isci_host *isci_host, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags) +{ + int ret = 0; + dma_addr_t handle; + struct isci_request *request; + + + /* get pointer to dma memory. This actually points + * to both the isci_remote_device object and the + * sci object. The isci object is at the beginning + * of the memory allocated here. + */ + request = dma_pool_alloc(isci_host->dma_pool, gfp_flags, &handle); + if (!request) { + dev_warn(&isci_host->pdev->dev, + "%s: dma_pool_alloc returned NULL\n", __func__); + return -ENOMEM; + } + + /* initialize the request object. */ + spin_lock_init(&request->state_lock); + isci_request_change_state(request, allocated); + request->sci_request_mem_ptr = ((u8 *)request) + + sizeof(struct isci_request); + request->request_daddr = handle; + request->isci_host = isci_host; + request->isci_device = isci_device; + request->io_request_completion = NULL; + + request->request_alloc_size = isci_host->dma_pool_alloc_size; + request->num_sg_entries = 0; + + request->complete_in_target = false; + + INIT_LIST_HEAD(&request->completed_node); + INIT_LIST_HEAD(&request->dev_node); + + *isci_request = request; + + return ret; +} + +static int isci_request_alloc_io( + struct isci_host *isci_host, + struct sas_task *task, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags) +{ + int retval = isci_request_alloc_core(isci_host, isci_request, + isci_device, gfp_flags); + + if (!retval) { + (*isci_request)->ttype_ptr.io_task_ptr = task; + (*isci_request)->ttype = io_task; + + task->lldd_task = *isci_request; + } + return retval; +} + +/** + * isci_request_alloc_tmf() - This function gets the request object from the + * isci_host dma cache and initializes the relevant fields as a sas_task. + * @isci_host: This parameter specifies the ISCI host object + * @sas_task: This parameter is the task struct from the upper layer driver. + * @isci_request: This parameter will contain the pointer to the new + * isci_request object. + * @isci_device: This parameter is the pointer to the isci remote device object + * that is the destination for this request. + * @gfp_flags: This parameter specifies the os allocation flags. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +int isci_request_alloc_tmf( + struct isci_host *isci_host, + struct isci_tmf *isci_tmf, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags) +{ + int retval = isci_request_alloc_core(isci_host, isci_request, + isci_device, gfp_flags); + + if (!retval) { + + (*isci_request)->ttype_ptr.tmf_task_ptr = isci_tmf; + (*isci_request)->ttype = tmf_task; + } + return retval; +} + +/** + * isci_request_signal_device_reset() - This function will set the "device + * needs target reset" flag in the given sas_tasks' task_state_flags, and + * then cause the task to be added into the SCSI error handler queue which + * will eventually be escalated to a target reset. + * + * + */ +static void isci_request_signal_device_reset( + struct isci_request *isci_request) +{ + unsigned long flags; + struct sas_task *task = isci_request_access_task(isci_request); + + dev_dbg(&isci_request->isci_host->pdev->dev, + "%s: request=%p, task=%p\n", __func__, isci_request, task); + + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, flags); + + /* Cause this task to be scheduled in the SCSI error handler + * thread. + */ + sas_task_abort(task); +} + +/** + * isci_request_execute() - This function allocates the isci_request object, + * all fills in some common fields. + * @isci_host: This parameter specifies the ISCI host object + * @sas_task: This parameter is the task struct from the upper layer driver. + * @isci_request: This parameter will contain the pointer to the new + * isci_request object. + * @gfp_flags: This parameter specifies the os allocation flags. + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +int isci_request_execute( + struct isci_host *isci_host, + struct sas_task *task, + struct isci_request **isci_request, + gfp_t gfp_flags) +{ + int ret = 0; + struct scic_sds_remote_device *sci_device; + enum sci_status status = SCI_FAILURE_UNSUPPORTED_PROTOCOL; + struct isci_remote_device *isci_device; + struct isci_request *request; + unsigned long flags; + + isci_device = isci_dev_from_domain_dev(task->dev); + sci_device = isci_device->sci_device_handle; + + /* do common allocation and init of request object. */ + ret = isci_request_alloc_io( + isci_host, + task, + &request, + isci_device, + gfp_flags + ); + + if (ret) + goto out; + + status = isci_io_request_build(isci_host, request, isci_device); + if (status == SCI_SUCCESS) { + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* send the request, let the core assign the IO TAG. */ + status = scic_controller_start_io( + isci_host->core_controller, + sci_device, + request->sci_request_handle, + SCI_CONTROLLER_INVALID_IO_TAG + ); + + if (status == SCI_SUCCESS || + status == SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) { + + /* Either I/O started OK, or the core has signaled that + * the device needs a target reset. + * + * In either case, hold onto the I/O for later. + * + * Update it's status and add it to the list in the + * remote device object. + */ + isci_request_change_state(request, started); + list_add(&request->dev_node, + &isci_device->reqs_in_process); + + if (status == + SCI_FAILURE_REMOTE_DEVICE_RESET_REQUIRED) { + /* Signal libsas that we need the SCSI error + * handler thread to work on this I/O and that + * we want a device reset. + */ + isci_request_signal_device_reset(request); + + /* Change the status, since we are holding + * the I/O until it is managed by the SCSI + * error handler. + */ + status = SCI_SUCCESS; + } + } else + dev_warn(&isci_host->pdev->dev, + "%s: failed request start\n", + __func__); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + } else + dev_warn(&isci_host->pdev->dev, + "%s: request_construct failed - status = 0x%x\n", + __func__, + status); + + out: + if (status != SCI_SUCCESS) { + + /* release dma memory on failure. */ + isci_request_free(isci_host, request); + request = NULL; + ret = SCI_FAILURE; + } + + *isci_request = request; + return ret; +} + + +/** + * isci_request_process_response_iu() - This function sets the status and + * response iu, in the task struct, from the request object for the upper + * layer driver. + * @sas_task: This parameter is the task struct from the upper layer driver. + * @resp_iu: This parameter points to the response iu of the completed request. + * @dev: This parameter specifies the linux device struct. + * + * none. + */ +static void isci_request_process_response_iu( + struct sas_task *task, + struct ssp_response_iu *resp_iu, + struct device *dev) +{ + dev_dbg(dev, + "%s: resp_iu = %p " + "resp_iu->status = 0x%x,\nresp_iu->datapres = %d " + "resp_iu->response_data_len = %x, " + "resp_iu->sense_data_len = %x\nrepsonse data: ", + __func__, + resp_iu, + resp_iu->status, + resp_iu->datapres, + resp_iu->response_data_len, + resp_iu->sense_data_len); + + task->task_status.stat = resp_iu->status; + + /* libsas updates the task status fields based on the response iu. */ + sas_ssp_task_response(dev, task, resp_iu); +} + +/** + * isci_request_set_open_reject_status() - This function prepares the I/O + * completion for OPEN_REJECT conditions. + * @request: This parameter is the completed isci_request object. + * @response_ptr: This parameter specifies the service response for the I/O. + * @status_ptr: This parameter specifies the exec status for the I/O. + * @complete_to_host_ptr: This parameter specifies the action to be taken by + * the LLDD with respect to completing this request or forcing an abort + * condition on the I/O. + * @open_rej_reason: This parameter specifies the encoded reason for the + * abandon-class reject. + * + * none. + */ +static void isci_request_set_open_reject_status( + struct isci_request *request, + struct sas_task *task, + enum service_response *response_ptr, + enum exec_status *status_ptr, + enum isci_completion_selection *complete_to_host_ptr, + enum sas_open_rej_reason open_rej_reason) +{ + /* Task in the target is done. */ + request->complete_in_target = true; + *response_ptr = SAS_TASK_UNDELIVERED; + *status_ptr = SAS_OPEN_REJECT; + *complete_to_host_ptr = isci_perform_normal_io_completion; + task->task_status.open_rej_reason = open_rej_reason; +} + +/** + * isci_request_handle_controller_specific_errors() - This function decodes + * controller-specific I/O completion error conditions. + * @request: This parameter is the completed isci_request object. + * @response_ptr: This parameter specifies the service response for the I/O. + * @status_ptr: This parameter specifies the exec status for the I/O. + * @complete_to_host_ptr: This parameter specifies the action to be taken by + * the LLDD with respect to completing this request or forcing an abort + * condition on the I/O. + * + * none. + */ +static void isci_request_handle_controller_specific_errors( + struct isci_remote_device *isci_device, + struct isci_request *request, + struct sas_task *task, + enum service_response *response_ptr, + enum exec_status *status_ptr, + enum isci_completion_selection *complete_to_host_ptr) +{ + unsigned int cstatus; + + cstatus = scic_request_get_controller_status( + request->sci_request_handle + ); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: %p SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR " + "- controller status = 0x%x\n", + __func__, request, cstatus); + + /* Decode the controller-specific errors; most + * important is to recognize those conditions in which + * the target may still have a task outstanding that + * must be aborted. + * + * Note that there are SCU completion codes being + * named in the decode below for which SCIC has already + * done work to handle them in a way other than as + * a controller-specific completion code; these are left + * in the decode below for completeness sake. + */ + switch (cstatus) { + case SCU_TASK_DONE_DMASETUP_DIRERR: + /* Also SCU_TASK_DONE_SMP_FRM_TYPE_ERR: */ + case SCU_TASK_DONE_XFERCNT_ERR: + /* Also SCU_TASK_DONE_SMP_UFI_ERR: */ + if (task->task_proto == SAS_PROTOCOL_SMP) { + /* SCU_TASK_DONE_SMP_UFI_ERR == Task Done. */ + *response_ptr = SAS_TASK_COMPLETE; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + *status_ptr = SAS_DEVICE_UNKNOWN; + else + *status_ptr = SAS_ABORTED_TASK; + + request->complete_in_target = true; + + *complete_to_host_ptr = + isci_perform_normal_io_completion; + } else { + /* Task in the target is not done. */ + *response_ptr = SAS_TASK_UNDELIVERED; + + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + *status_ptr = SAS_DEVICE_UNKNOWN; + else + *status_ptr = SAM_STAT_TASK_ABORTED; + + request->complete_in_target = false; + + *complete_to_host_ptr = + isci_perform_error_io_completion; + } + + break; + + case SCU_TASK_DONE_CRC_ERR: + case SCU_TASK_DONE_NAK_CMD_ERR: + case SCU_TASK_DONE_EXCESS_DATA: + case SCU_TASK_DONE_UNEXP_FIS: + /* Also SCU_TASK_DONE_UNEXP_RESP: */ + case SCU_TASK_DONE_VIIT_ENTRY_NV: /* TODO - conditions? */ + case SCU_TASK_DONE_IIT_ENTRY_NV: /* TODO - conditions? */ + case SCU_TASK_DONE_RNCNV_OUTBOUND: /* TODO - conditions? */ + /* These are conditions in which the target + * has completed the task, so that no cleanup + * is necessary. + */ + *response_ptr = SAS_TASK_COMPLETE; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + *status_ptr = SAS_DEVICE_UNKNOWN; + else + *status_ptr = SAS_ABORTED_TASK; + + request->complete_in_target = true; + + *complete_to_host_ptr = isci_perform_normal_io_completion; + break; + + + /* Note that the only open reject completion codes seen here will be + * abandon-class codes; all others are automatically retried in the SCU. + */ + case SCU_TASK_OPEN_REJECT_WRONG_DESTINATION: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_WRONG_DEST); + break; + + case SCU_TASK_OPEN_REJECT_ZONE_VIOLATION: + + /* Note - the return of AB0 will change when + * libsas implements detection of zone violations. + */ + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB0); + break; + + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_1: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB1); + break; + + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_2: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB2); + break; + + case SCU_TASK_OPEN_REJECT_RESERVED_ABANDON_3: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_RESV_AB3); + break; + + case SCU_TASK_OPEN_REJECT_BAD_DESTINATION: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_BAD_DEST); + break; + + case SCU_TASK_OPEN_REJECT_STP_RESOURCES_BUSY: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_STP_NORES); + break; + + case SCU_TASK_OPEN_REJECT_PROTOCOL_NOT_SUPPORTED: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_EPROTO); + break; + + case SCU_TASK_OPEN_REJECT_CONNECTION_RATE_NOT_SUPPORTED: + + isci_request_set_open_reject_status( + request, task, response_ptr, status_ptr, + complete_to_host_ptr, SAS_OREJ_CONN_RATE); + break; + + case SCU_TASK_DONE_LL_R_ERR: + /* Also SCU_TASK_DONE_ACK_NAK_TO: */ + case SCU_TASK_DONE_LL_PERR: + case SCU_TASK_DONE_LL_SY_TERM: + /* Also SCU_TASK_DONE_NAK_ERR:*/ + case SCU_TASK_DONE_LL_LF_TERM: + /* Also SCU_TASK_DONE_DATA_LEN_ERR: */ + case SCU_TASK_DONE_LL_ABORT_ERR: + case SCU_TASK_DONE_SEQ_INV_TYPE: + /* Also SCU_TASK_DONE_UNEXP_XR: */ + case SCU_TASK_DONE_XR_IU_LEN_ERR: + case SCU_TASK_DONE_INV_FIS_LEN: + /* Also SCU_TASK_DONE_XR_WD_LEN: */ + case SCU_TASK_DONE_SDMA_ERR: + case SCU_TASK_DONE_OFFSET_ERR: + case SCU_TASK_DONE_MAX_PLD_ERR: + case SCU_TASK_DONE_LF_ERR: + case SCU_TASK_DONE_SMP_RESP_TO_ERR: /* Escalate to dev reset? */ + case SCU_TASK_DONE_SMP_LL_RX_ERR: + case SCU_TASK_DONE_UNEXP_DATA: + case SCU_TASK_DONE_UNEXP_SDBFIS: + case SCU_TASK_DONE_REG_ERR: + case SCU_TASK_DONE_SDB_ERR: + case SCU_TASK_DONE_TASK_ABORT: + default: + /* Task in the target is not done. */ + *response_ptr = SAS_TASK_UNDELIVERED; + *status_ptr = SAM_STAT_TASK_ABORTED; + request->complete_in_target = false; + + *complete_to_host_ptr = isci_perform_error_io_completion; + break; + } +} + +/** + * isci_task_save_for_upper_layer_completion() - This function saves the + * request for later completion to the upper layer driver. + * @host: This parameter is a pointer to the host on which the the request + * should be queued (either as an error or success). + * @request: This parameter is the completed request. + * @response: This parameter is the response code for the completed task. + * @status: This parameter is the status code for the completed task. + * + * none. + */ +static void isci_task_save_for_upper_layer_completion( + struct isci_host *host, + struct isci_request *request, + enum service_response response, + enum exec_status status, + enum isci_completion_selection task_notification_selection) +{ + struct sas_task *task = isci_request_access_task(request); + + isci_task_set_completion_status(task, response, status, + task_notification_selection); + + + /* Tasks aborted specifically by a call to the lldd_abort_task + * function should not be completed to the host in the regular path. + */ + switch (task_notification_selection) { + + case isci_perform_normal_io_completion: + + /* Normal notification (task_done) */ + dev_dbg(&host->pdev->dev, + "%s: Normal - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + /* Add to the completed list. */ + list_add(&request->completed_node, + &host->requests_to_complete); + break; + + case isci_perform_aborted_io_completion: + /* + * No notification because this request is already + * in the abort path. + */ + dev_warn(&host->pdev->dev, + "%s: Aborted - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + break; + + case isci_perform_error_io_completion: + /* Use sas_task_abort */ + dev_warn(&host->pdev->dev, + "%s: Error - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + /* Add to the aborted list. */ + list_add(&request->completed_node, + &host->requests_to_abort); + break; + + default: + dev_warn(&host->pdev->dev, + "%s: Unknown - task = %p, response=%d, status=%d\n", + __func__, + task, + response, + status); + + /* Add to the aborted list. */ + list_add(&request->completed_node, + &host->requests_to_abort); + break; + } +} + +/** + * isci_request_io_request_complete() - This function is called by the sci core + * when an io request completes. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter is the completed isci_request object. + * @completion_status: This parameter specifies the completion status from the + * sci core. + * + * none. + */ +void isci_request_io_request_complete( + struct isci_host *isci_host, + struct isci_request *request, + enum sci_io_status completion_status) +{ + struct sas_task *task = isci_request_access_task(request); + struct ssp_response_iu *resp_iu; + void *resp_buf; + unsigned long task_flags; + unsigned long state_flags; + struct completion *io_request_completion; + struct isci_remote_device *isci_device = request->isci_device; + enum service_response response = SAS_TASK_UNDELIVERED; + enum exec_status status = SAS_ABORTED_TASK; + enum isci_request_status request_status; + enum isci_completion_selection complete_to_host + = isci_perform_normal_io_completion; + + dev_dbg(&isci_host->pdev->dev, + "%s: request = %p, task = %p,\n" + "task->data_dir = %d completion_status = 0x%x\n", + __func__, + request, + task, + task->data_dir, + completion_status); + + spin_lock_irqsave(&request->state_lock, state_flags); + request_status = isci_request_get_state(request); + spin_unlock_irqrestore(&request->state_lock, state_flags); + + /* Decode the request status. Note that if the request has been + * aborted by a task management function, we don't care + * what the status is. + */ + switch (request_status) { + + case aborted: + /* "aborted" indicates that the request was aborted by a task + * management function, since once a task management request is + * perfomed by the device, the request only completes because + * of the subsequent driver terminate. + * + * Aborted also means an external thread is explicitly managing + * this request, so that we do not complete it up the stack. + * + * The target is still there (since the TMF was successful). + */ + request->complete_in_target = true; + response = SAS_TASK_COMPLETE; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) + || (isci_device->status == isci_stopped) + ) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_aborted_io_completion; + /* This was an aborted request. */ + break; + + case aborting: + /* aborting means that the task management function tried and + * failed to abort the request. We need to note the request + * as SAS_TASK_UNDELIVERED, so that the scsi mid layer marks the + * target as down. + * + * Aborting also means an external thread is explicitly managing + * this request, so that we do not complete it up the stack. + */ + request->complete_in_target = true; + response = SAS_TASK_UNDELIVERED; + + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + /* The device has been /is being stopped. Note that + * we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_PHY_DOWN; + + complete_to_host = isci_perform_aborted_io_completion; + + /* This was an aborted request. */ + break; + + case terminating: + + /* This was an terminated request. This happens when + * the I/O is being terminated because of an action on + * the device (reset, tear down, etc.), and the I/O needs + * to be completed up the stack. + */ + request->complete_in_target = true; + response = SAS_TASK_UNDELIVERED; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_normal_io_completion; + + /* This was a terminated request. */ + break; + + default: + + /* This is an active request being completed from the core. */ + switch (completion_status) { + + case SCI_IO_FAILURE_RESPONSE_VALID: + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID (%p/%p)\n", + __func__, + request, + task); + + if (sas_protocol_ata(task->task_proto)) { + resp_buf + = scic_stp_io_request_get_d2h_reg_address( + request->sci_request_handle + ); + isci_request_process_stp_response(task, + resp_buf + ); + + } else if (SAS_PROTOCOL_SSP == task->task_proto) { + + /* crack the iu response buffer. */ + resp_iu + = scic_io_request_get_response_iu_address( + request->sci_request_handle + ); + + isci_request_process_response_iu(task, resp_iu, + &isci_host->pdev->dev + ); + + } else if (SAS_PROTOCOL_SMP == task->task_proto) { + + dev_err(&isci_host->pdev->dev, + "%s: SCI_IO_FAILURE_RESPONSE_VALID: " + "SAS_PROTOCOL_SMP protocol\n", + __func__); + + } else + dev_err(&isci_host->pdev->dev, + "%s: unknown protocol\n", __func__); + + /* use the task status set in the task struct by the + * isci_request_process_response_iu call. + */ + request->complete_in_target = true; + response = task->task_status.resp; + status = task->task_status.stat; + break; + + case SCI_IO_SUCCESS: + case SCI_IO_SUCCESS_IO_DONE_EARLY: + + response = SAS_TASK_COMPLETE; + status = SAM_STAT_GOOD; + request->complete_in_target = true; + + if (task->task_proto == SAS_PROTOCOL_SMP) { + + u8 *command_iu_address + = scic_io_request_get_command_iu_address( + request->sci_request_handle + ); + + dev_dbg(&isci_host->pdev->dev, + "%s: SMP protocol completion\n", + __func__); + + sg_copy_from_buffer( + &task->smp_task.smp_resp, 1, + command_iu_address + + sizeof(struct smp_request), + sizeof(struct smp_resp) + ); + } else if (completion_status + == SCI_IO_SUCCESS_IO_DONE_EARLY) { + + /* This was an SSP / STP / SATA transfer. + * There is a possibility that less data than + * the maximum was transferred. + */ + u32 transferred_length + = scic_io_request_get_number_of_bytes_transferred( + request->sci_request_handle); + + task->task_status.residual + = task->total_xfer_len - transferred_length; + + /* If there were residual bytes, call this an + * underrun. + */ + if (task->task_status.residual != 0) + status = SAS_DATA_UNDERRUN; + + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_SUCCESS_IO_DONE_EARLY %d\n", + __func__, + status); + + } else + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_SUCCESS\n", + __func__); + + break; + + case SCI_IO_FAILURE_TERMINATED: + dev_dbg(&isci_host->pdev->dev, + "%s: SCI_IO_FAILURE_TERMINATED (%p/%p)\n", + __func__, + request, + task); + + /* The request was terminated explicitly. No handling + * is needed in the SCSI error handler path. + */ + request->complete_in_target = true; + response = SAS_TASK_UNDELIVERED; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_normal_io_completion; + break; + + case SCI_FAILURE_CONTROLLER_SPECIFIC_IO_ERR: + + isci_request_handle_controller_specific_errors( + isci_device, request, task, &response, &status, + &complete_to_host); + + break; + + case SCI_IO_FAILURE_REMOTE_DEVICE_RESET_REQUIRED: + /* This is a special case, in that the I/O completion + * is telling us that the device needs a reset. + * In order for the device reset condition to be + * noticed, the I/O has to be handled in the error + * handler. Set the reset flag and cause the + * SCSI error thread to be scheduled. + */ + spin_lock_irqsave(&task->task_state_lock, task_flags); + task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; + spin_unlock_irqrestore(&task->task_state_lock, task_flags); + + complete_to_host = isci_perform_error_io_completion; + request->complete_in_target = false; + break; + + default: + /* Catch any otherwise unhandled error codes here. */ + dev_warn(&isci_host->pdev->dev, + "%s: invalid completion code: 0x%x - " + "isci_request = %p\n", + __func__, completion_status, request); + + response = SAS_TASK_UNDELIVERED; + + /* See if the device has been/is being stopped. Note + * that we ignore the quiesce state, since we are + * concerned about the actual device state. + */ + if ((isci_device->status == isci_stopping) || + (isci_device->status == isci_stopped)) + status = SAS_DEVICE_UNKNOWN; + else + status = SAS_ABORTED_TASK; + + complete_to_host = isci_perform_error_io_completion; + request->complete_in_target = false; + break; + } + break; + } + + isci_request_unmap_sgl(request, isci_host->pdev); + + /* Put the completed request on the correct list */ + isci_task_save_for_upper_layer_completion(isci_host, request, response, + status, complete_to_host + ); + + /* complete the io request to the core. */ + scic_controller_complete_io( + isci_host->core_controller, + isci_device->sci_device_handle, + request->sci_request_handle + ); + /* NULL the request handle so it cannot be completed or + * terminated again, and to cause any calls into abort + * task to recognize the already completed case. + */ + request->sci_request_handle = NULL; + + /* Only remove the request from the remote device list + * of pending requests if we have not requested error + * handling on this request. + */ + if (complete_to_host != isci_perform_error_io_completion) + list_del_init(&request->dev_node); + + + /* Save possible completion ptr. */ + io_request_completion = request->io_request_completion; + + if (io_request_completion) { + + /* This is inherantly a regular I/O request, + * since we are currently in the regular + * I/O completion callback function. + * Signal whoever is waiting that this + * request is complete. + */ + complete(io_request_completion); + } + + isci_host_can_dequeue(isci_host, 1); +} + +/** + * isci_request_io_request_get_transfer_length() - This function is called by + * the sci core to retrieve the transfer length for a given request. + * @request: This parameter is the isci_request object. + * + * length of transfer for specified request. + */ +u32 isci_request_io_request_get_transfer_length(struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: total_xfer_len: %d\n", + __func__, + task->total_xfer_len); + return task->total_xfer_len; +} + + +/** + * isci_request_io_request_get_data_direction() - This function is called by + * the sci core to retrieve the data direction for a given request. + * @request: This parameter is the isci_request object. + * + * data direction for specified request. + */ +SCI_IO_REQUEST_DATA_DIRECTION isci_request_io_request_get_data_direction( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + SCI_IO_REQUEST_DATA_DIRECTION ret; + + switch (task->data_dir) { + + case DMA_FROM_DEVICE: + ret = SCI_IO_REQUEST_DATA_IN; + dev_dbg(&request->isci_host->pdev->dev, + "%s: request=%p, FROM_DEVICE\n", + __func__, + request); + break; + + case DMA_TO_DEVICE: + ret = SCI_IO_REQUEST_DATA_OUT; + dev_dbg(&request->isci_host->pdev->dev, + "%s: request=%p, TO_DEVICE\n", + __func__, + request); + break; + + case DMA_BIDIRECTIONAL: + case DMA_NONE: + default: + ret = SCI_IO_REQUEST_NO_DATA; + dev_dbg(&request->isci_host->pdev->dev, + "%s: request=%p, unhandled direction case, " + "data_dir=%d\n", + __func__, + request, + task->data_dir); + break; + + } + return ret; +} + +/** + * isci_request_sge_get_address_field() - This function is called by the sci + * core to retrieve the address field contents for a given sge. + * @request: This parameter is the isci_request object. + * @sge_address: This parameter is the sge. + * + * physical address in the specified sge. + */ +dma_addr_t isci_request_sge_get_address_field( + struct isci_request *request, + void *sge_address) +{ + struct sas_task *task = isci_request_access_task(request); + dma_addr_t ret; + struct isci_host *isci_host = isci_host_from_sas_ha( + task->dev->port->ha); + + dev_dbg(&isci_host->pdev->dev, + "%s: request = %p, sge_address = %p\n", + __func__, + request, + sge_address); + + if (task->data_dir == PCI_DMA_NONE) + return 0; + + /* the case where num_scatter == 0 is special, in that + * task->scatter is the actual buffer address, not an sgl. + * so a map single is required here. + */ + if ((task->num_scatter == 0) && + !sas_protocol_ata(task->task_proto)) { + ret = dma_map_single( + &isci_host->pdev->dev, + task->scatter, + task->total_xfer_len, + task->data_dir + ); + request->zero_scatter_daddr = ret; + } else + ret = sg_dma_address(((struct scatterlist *)sge_address)); + + dev_dbg(&isci_host->pdev->dev, + "%s: bus address = %lx\n", + __func__, + (unsigned long)ret); + + return ret; +} + + +/** + * isci_request_sge_get_length_field() - This function is called by the sci + * core to retrieve the length field contents for a given sge. + * @request: This parameter is the isci_request object. + * @sge_address: This parameter is the sge. + * + * length field value in the specified sge. + */ +u32 isci_request_sge_get_length_field( + struct isci_request *request, + void *sge_address) +{ + struct sas_task *task = isci_request_access_task(request); + int ret; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p, sge_address = %p\n", + __func__, + request, + sge_address); + + if (task->data_dir == PCI_DMA_NONE) + return 0; + + /* the case where num_scatter == 0 is special, in that + * task->scatter is the actual buffer address, not an sgl. + * so we return total_xfer_len here. + */ + if (task->num_scatter == 0) + ret = task->total_xfer_len; + else + ret = sg_dma_len((struct scatterlist *)sge_address); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: len = %d\n", + __func__, + ret); + + return ret; +} + + +/** + * isci_request_ssp_io_request_get_cdb_address() - This function is called by + * the sci core to retrieve the cdb address for a given request. + * @request: This parameter is the isci_request object. + * + * cdb address for specified request. + */ +void *isci_request_ssp_io_request_get_cdb_address( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.cdb = %p\n", + __func__, + task->ssp_task.cdb); + return task->ssp_task.cdb; +} + + +/** + * isci_request_ssp_io_request_get_cdb_length() - This function is called by + * the sci core to retrieve the cdb length for a given request. + * @request: This parameter is the isci_request object. + * + * cdb length for specified request. + */ +u32 isci_request_ssp_io_request_get_cdb_length( + struct isci_request *request) +{ + return 16; +} + + +/** + * isci_request_ssp_io_request_get_lun() - This function is called by the sci + * core to retrieve the lun for a given request. + * @request: This parameter is the isci_request object. + * + * lun for specified request. + */ +u32 isci_request_ssp_io_request_get_lun( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + +#ifdef DEBUG + int i; + + for (i = 0; i < 8; i++) + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.LUN[%d] = %x\n", + __func__, i, request->task->ssp_task.LUN[i]); + +#endif + + return task->ssp_task.LUN[0]; +} + + +/** + * isci_request_ssp_io_request_get_task_attribute() - This function is called + * by the sci core to retrieve the task attribute for a given request. + * @request: This parameter is the isci_request object. + * + * task attribute for specified request. + */ +u32 isci_request_ssp_io_request_get_task_attribute( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.task_attr = %x\n", + __func__, + task->ssp_task.task_attr); + + return task->ssp_task.task_attr; +} + + +/** + * isci_request_ssp_io_request_get_command_priority() - This function is called + * by the sci core to retrieve the command priority for a given request. + * @request: This parameter is the isci_request object. + * + * command priority for specified request. + */ +u32 isci_request_ssp_io_request_get_command_priority( + struct isci_request *request) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request->task->ssp_task.task_prio = %x\n", + __func__, + task->ssp_task.task_prio); + + return task->ssp_task.task_prio; +} diff --git a/drivers/scsi/isci/request.h b/drivers/scsi/isci/request.h new file mode 100644 index 000000000000..5079d4a7c41b --- /dev/null +++ b/drivers/scsi/isci/request.h @@ -0,0 +1,429 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#if !defined(_ISCI_REQUEST_H_) +#define _ISCI_REQUEST_H_ + +#include "isci.h" + +/** + * struct isci_request_status - This enum defines the possible states of an I/O + * request. + * + * + */ +enum isci_request_status { + unallocated = 0x00, + allocated = 0x01, + started = 0x02, + completed = 0x03, + aborting = 0x04, + aborted = 0x05, + terminating = 0x06 +}; + +enum task_type { + io_task = 0, + tmf_task = 1 +}; + +/** + * struct isci_request - This class represents the request object used to track + * IO, smp and TMF request internal. It wraps the SCIC request object. + * + * + */ +struct isci_request { + + struct scic_sds_request *sci_request_handle; + + enum isci_request_status status; + enum task_type ttype; + unsigned short io_tag; + bool complete_in_target; + + union ttype_ptr_union { + struct sas_task *io_task_ptr; /* When ttype==io_task */ + struct isci_tmf *tmf_task_ptr; /* When ttype==tmf_task */ + } ttype_ptr; + struct isci_host *isci_host; + struct isci_remote_device *isci_device; + /* For use in the requests_to_{complete|abort} lists: */ + struct list_head completed_node; + /* For use in the reqs_in_process list: */ + struct list_head dev_node; + void *sci_request_mem_ptr; + spinlock_t state_lock; + dma_addr_t request_daddr; + dma_addr_t zero_scatter_daddr; + + unsigned int num_sg_entries; /* returned by pci_alloc_sg */ + unsigned int request_alloc_size; /* size of block from dma_pool_alloc */ + + /** Note: "io_request_completion" is completed in two different ways + * depending on whether this is a TMF or regular request. + * - TMF requests are completed in the thread that started them; + * - regular requests are completed in the request completion callback + * function. + * This difference in operation allows the aborter of a TMF request + * to be sure that once the TMF request completes, the I/O that the + * TMF was aborting is guaranteed to have completed. + */ + struct completion *io_request_completion; +}; + +/** + * This function gets the status of the request object. + * @request: This parameter points to the isci_request object + * + * status of the object as a isci_request_status enum. + */ +static inline +enum isci_request_status isci_request_get_state( + struct isci_request *isci_request) +{ + BUG_ON(isci_request == NULL); + + /*probably a bad sign... */ + if (isci_request->status == unallocated) + dev_warn(&isci_request->isci_host->pdev->dev, + "%s: isci_request->status == unallocated\n", + __func__); + + return isci_request->status; +} + + +/** + * isci_request_change_state() - This function sets the status of the request + * object. + * @request: This parameter points to the isci_request object + * @status: This Parameter is the new status of the object + * + */ +static inline enum isci_request_status isci_request_change_state( + struct isci_request *isci_request, + enum isci_request_status status) +{ + enum isci_request_status old_state; + unsigned long flags; + + dev_dbg(&isci_request->isci_host->pdev->dev, + "%s: isci_request = %p, state = 0x%x\n", + __func__, + isci_request, + status); + + BUG_ON(isci_request == NULL); + + spin_lock_irqsave(&isci_request->state_lock, flags); + old_state = isci_request->status; + isci_request->status = status; + spin_unlock_irqrestore(&isci_request->state_lock, flags); + + return old_state; +} + +/** + * isci_request_change_started_to_newstate() - This function sets the status of + * the request object. + * @request: This parameter points to the isci_request object + * @status: This Parameter is the new status of the object + * + * state previous to any change. + */ +static inline enum isci_request_status isci_request_change_started_to_newstate( + struct isci_request *isci_request, + struct completion *completion_ptr, + enum isci_request_status newstate) +{ + enum isci_request_status old_state; + unsigned long flags; + + BUG_ON(isci_request == NULL); + + spin_lock_irqsave(&isci_request->state_lock, flags); + + old_state = isci_request->status; + + if (old_state == started) { + BUG_ON(isci_request->io_request_completion != NULL); + + isci_request->io_request_completion = completion_ptr; + isci_request->status = newstate; + } + spin_unlock_irqrestore(&isci_request->state_lock, flags); + + dev_dbg(&isci_request->isci_host->pdev->dev, + "%s: isci_request = %p, old_state = 0x%x\n", + __func__, + isci_request, + old_state); + + return old_state; +} + +/** + * isci_request_change_started_to_aborted() - This function sets the status of + * the request object. + * @request: This parameter points to the isci_request object + * @completion_ptr: This parameter is saved as the kernel completion structure + * signalled when the old request completes. + * + * state previous to any change. + */ +static inline enum isci_request_status isci_request_change_started_to_aborted( + struct isci_request *isci_request, + struct completion *completion_ptr) +{ + return isci_request_change_started_to_newstate( + isci_request, completion_ptr, aborted + ); +} +/** + * isci_request_free() - This function frees the request object. + * @isci_host: This parameter specifies the ISCI host object + * @isci_request: This parameter points to the isci_request object + * + */ +static inline void isci_request_free( + struct isci_host *isci_host, + struct isci_request *isci_request) +{ + BUG_ON(isci_request == NULL); + + /* release the dma memory if we fail. */ + dma_pool_free(isci_host->dma_pool, isci_request, + isci_request->request_daddr); +} + + +/* #define ISCI_REQUEST_VALIDATE_ACCESS + */ + +#ifdef ISCI_REQUEST_VALIDATE_ACCESS + +static inline +struct sas_task *isci_request_access_task(struct isci_request *isci_request) +{ + BUG_ON(isci_request->ttype != io_task); + return isci_request->ttype_ptr.io_task_ptr; +} + +static inline +struct isci_tmf *isci_request_access_tmf(struct isci_request *isci_request) +{ + BUG_ON(isci_request->ttype != tmf_task); + return isci_request->ttype_ptr.tmf_task_ptr; +} + +#else /* not ISCI_REQUEST_VALIDATE_ACCESS */ + +#define isci_request_access_task(RequestPtr) \ + ((RequestPtr)->ttype_ptr.io_task_ptr) + +#define isci_request_access_tmf(RequestPtr) \ + ((RequestPtr)->ttype_ptr.tmf_task_ptr) + +#endif /* not ISCI_REQUEST_VALIDATE_ACCESS */ + + +int isci_request_alloc_tmf( + struct isci_host *isci_host, + struct isci_tmf *isci_tmf, + struct isci_request **isci_request, + struct isci_remote_device *isci_device, + gfp_t gfp_flags); + + +int isci_request_execute( + struct isci_host *isci_host, + struct sas_task *task, + struct isci_request **request, + gfp_t gfp_flags); + +/** + * isci_request_unmap_sgl() - This function unmaps the DMA address of a given + * sgl + * @request: This parameter points to the isci_request object + * @*pdev: This Parameter is the pci_device struct for the controller + * + */ +static inline void isci_request_unmap_sgl( + struct isci_request *request, + struct pci_dev *pdev) +{ + struct sas_task *task = isci_request_access_task(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p, task = %p,\n" + "task->data_dir = %d, is_sata = %d\n ", + __func__, + request, + task, + task->data_dir, + sas_protocol_ata(task->task_proto)); + + if ((task->data_dir != PCI_DMA_NONE) && + !sas_protocol_ata(task->task_proto)) { + if (task->num_scatter == 0) + /* 0 indicates a single dma address */ + dma_unmap_single( + &pdev->dev, + request->zero_scatter_daddr, + task->total_xfer_len, + task->data_dir + ); + + else /* unmap the sgl dma addresses */ + dma_unmap_sg( + &pdev->dev, + task->scatter, + request->num_sg_entries, + task->data_dir + ); + } +} + + +void isci_request_io_request_complete( + struct isci_host *isci_host, + struct isci_request *request, + enum sci_io_status completion_status); + +u32 isci_request_io_request_get_transfer_length( + struct isci_request *request); + +SCI_IO_REQUEST_DATA_DIRECTION isci_request_io_request_get_data_direction( + struct isci_request *request); + +/** + * isci_request_io_request_get_next_sge() - This function is called by the sci + * core to retrieve the next sge for a given request. + * @request: This parameter is the isci_request object. + * @current_sge_address: This parameter is the last sge retrieved by the sci + * core for this request. + * + * pointer to the next sge for specified request. + */ +static inline void *isci_request_io_request_get_next_sge( + struct isci_request *request, + void *current_sge_address) +{ + struct sas_task *task = isci_request_access_task(request); + void *ret = NULL; + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p, " + "current_sge_address = %p, " + "num_scatter = %d\n", + __func__, + request, + current_sge_address, + task->num_scatter); + + if (!current_sge_address) /* First time through.. */ + ret = task->scatter; /* always task->scatter */ + else if (task->num_scatter == 0) /* Next element, if num_scatter == 0 */ + ret = NULL; /* there is only one element. */ + else + ret = sg_next(current_sge_address); /* sg_next returns NULL + * for the last element + */ + + dev_dbg(&request->isci_host->pdev->dev, + "%s: next sge address = %p\n", + __func__, + ret); + + return ret; +} + +dma_addr_t isci_request_sge_get_address_field( + struct isci_request *request, + void *sge_address); + +u32 isci_request_sge_get_length_field( + struct isci_request *request, + void *sge_address); + +void *isci_request_ssp_io_request_get_cdb_address( + struct isci_request *request); + +u32 isci_request_ssp_io_request_get_cdb_length( + struct isci_request *request); + +u32 isci_request_ssp_io_request_get_lun( + struct isci_request *request); + +u32 isci_request_ssp_io_request_get_task_attribute( + struct isci_request *request); + +u32 isci_request_ssp_io_request_get_command_priority( + struct isci_request *request); + + + + + +void isci_terminate_pending_requests( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + enum isci_request_status new_request_state); + + + + +#endif /* !defined(_ISCI_REQUEST_H_) */ diff --git a/drivers/scsi/isci/sata.c b/drivers/scsi/isci/sata.c new file mode 100644 index 000000000000..19b0eea93ca7 --- /dev/null +++ b/drivers/scsi/isci/sata.c @@ -0,0 +1,356 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "isci.h" +#include "scic_remote_device.h" +#include "scic_sds_remote_device.h" +#include "scic_io_request.h" +#include "scic_task_request.h" +#include "task.h" +#include "request.h" +#include "sata.h" +#include "intel_sat.h" +#include "intel_ata.h" + +static u8 isci_sata_get_management_task_protocol(struct isci_tmf *tmf); + + +/** + * isci_sata_task_to_fis_copy() - This function gets the host_to_dev_fis from + * the core and copies the fis from the task into it. + * @task: This parameter is a pointer to the task struct from libsas. + * + * pointer to the host_to_dev_fis from the core request object. + */ +struct host_to_dev_fis *isci_sata_task_to_fis_copy(struct sas_task *task) +{ + struct isci_request *request = task->lldd_task; + struct host_to_dev_fis *register_fis = + scic_stp_io_request_get_h2d_reg_address( + request->sci_request_handle + ); + + memcpy( + (u8 *)register_fis, + (u8 *)&task->ata_task.fis, + sizeof(struct host_to_dev_fis) + ); + + if (!task->ata_task.device_control_reg_update) + register_fis->flags |= 0x80; + + register_fis->flags &= 0xF0; + + return register_fis; +} + +/** + * isci_sata_is_task_ncq() - This function determines if the given stp task is + * a ncq request. + * @task: This parameter is a pointer to the task struct from libsas. + * + * true if the task is ncq + */ +bool isci_sata_is_task_ncq(struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + + bool ret = (qc && + (qc->tf.command == ATA_CMD_FPDMA_WRITE || + qc->tf.command == ATA_CMD_FPDMA_READ)); + + return ret; +} + +/** + * isci_sata_set_ncq_tag() - This function sets the ncq tag field in the + * host_to_dev_fis equal to the tag in the queue command in the task. + * @task: This parameter is a pointer to the task struct from libsas. + * @register_fis: This parameter is a pointer to the host_to_dev_fis from the + * core request object. + * + */ +void isci_sata_set_ncq_tag( + struct host_to_dev_fis *register_fis, + struct sas_task *task) +{ + struct ata_queued_cmd *qc = task->uldd_task; + struct isci_request *request = task->lldd_task; + + register_fis->sector_count = qc->tag << 3; + scic_stp_io_request_set_ncq_tag(request->sci_request_handle, qc->tag); +} + +/** + * isci_request_process_stp_response() - This function sets the status and + * response, in the task struct, from the request object for the upper layer + * driver. + * @sas_task: This parameter is the task struct from the upper layer driver. + * @response_buffer: This parameter points to the response of the completed + * request. + * + * none. + */ +void isci_request_process_stp_response( + struct sas_task *task, + void *response_buffer) +{ + struct sata_fis_reg_d2h *d2h_reg_fis = (struct sata_fis_reg_d2h *)response_buffer; + struct task_status_struct *ts = &task->task_status; + struct ata_task_resp *resp = (void *)&ts->buf[0]; + + resp->frame_len = le16_to_cpu(*(__le16 *)(response_buffer + 6)); + memcpy(&resp->ending_fis[0], response_buffer + 16, 24); + ts->buf_valid_size = sizeof(*resp); + + /** + * If the device fault bit is set in the status register, then + * set the sense data and return. + */ + if (d2h_reg_fis->status & ATA_STATUS_REG_DEVICE_FAULT_BIT) + ts->stat = SAS_PROTO_RESPONSE; + else + ts->stat = SAM_STAT_GOOD; + + ts->resp = SAS_TASK_COMPLETE; +} + +/** + * isci_sata_get_sat_protocol() - retrieve the sat protocol for the request + * @isci_request: ata request + * + * Note: temporary implementation until expert mode removes the callback + * + */ +u8 isci_sata_get_sat_protocol(struct isci_request *isci_request) +{ + struct sas_task *task; + struct domain_device *dev; + + dev_dbg(&isci_request->isci_host->pdev->dev, + "%s: isci_request = %p, ttype = %d\n", + __func__, isci_request, isci_request->ttype); + + if (tmf_task == isci_request->ttype) { + struct isci_tmf *tmf = isci_request_access_tmf(isci_request); + + return isci_sata_get_management_task_protocol(tmf); + } + + task = isci_request_access_task(isci_request); + dev = task->dev; + + if (!sas_protocol_ata(task->task_proto)) { + WARN(1, "unhandled task protocol\n"); + return SAT_PROTOCOL_NON_DATA; + } + + if (task->data_dir == DMA_NONE) + return SAT_PROTOCOL_NON_DATA; + + /* the "_IN" protocol types are equivalent to their "_OUT" + * analogs as far as the core is concerned + */ + if (dev->sata_dev.command_set == ATAPI_COMMAND_SET) { + if (task->ata_task.dma_xfer) + return SAT_PROTOCOL_PACKET_DMA_DATA_IN; + else + return SAT_PROTOCOL_PACKET_PIO_DATA_IN; + } + + if (task->ata_task.use_ncq) + return SAT_PROTOCOL_FPDMA; + + if (task->ata_task.dma_xfer) + return SAT_PROTOCOL_UDMA_DATA_IN; + else + return SAT_PROTOCOL_PIO_DATA_IN; +} + +static u8 isci_sata_get_management_task_protocol( + struct isci_tmf *tmf) +{ + u8 ret = 0; + + pr_warn("tmf = %p, func = %d\n", tmf, tmf->tmf_code); + + if ((tmf->tmf_code == isci_tmf_sata_srst_high) || + (tmf->tmf_code == isci_tmf_sata_srst_low)) { + pr_warn("%s: tmf->tmf_code == TMF_LU_RESET\n", __func__); + ret = SAT_PROTOCOL_SOFT_RESET; + } + + return ret; +} + +enum sci_status isci_sata_management_task_request_build( + struct isci_request *isci_request) +{ + struct isci_tmf *isci_tmf; + enum sci_status status; + + if (tmf_task != isci_request->ttype) + return SCI_FAILURE; + + isci_tmf = isci_request_access_tmf(isci_request); + + switch (isci_tmf->tmf_code) { + + case isci_tmf_sata_srst_high: + case isci_tmf_sata_srst_low: + { + struct host_to_dev_fis *register_fis = + scic_stp_io_request_get_h2d_reg_address( + isci_request->sci_request_handle + ); + + memset(register_fis, 0, sizeof(*register_fis)); + + register_fis->fis_type = 0x27; + register_fis->flags &= ~0x80; + register_fis->flags &= 0xF0; + if (isci_tmf->tmf_code == isci_tmf_sata_srst_high) + register_fis->control |= ATA_SRST; + else + register_fis->control &= ~ATA_SRST; + break; + } + /* other management commnd go here... */ + default: + return SCI_FAILURE; + } + + /* core builds the protocol specific request + * based on the h2d fis. + */ + status = scic_task_request_construct_sata( + isci_request->sci_request_handle + ); + + return status; +} + +/** + * isci_task_send_lu_reset_sata() - This function is called by of the SAS + * Domain Template functions. This is one of the Task Management functoins + * called by libsas, to reset the given SAS lun. Note the assumption that + * while this call is executing, no I/O will be sent by the host to the + * device. + * @lun: This parameter specifies the lun to be reset. + * + * status, zero indicates success. + */ +int isci_task_send_lu_reset_sata( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + u8 *lun) +{ + struct isci_tmf tmf; + int ret = TMF_RESP_FUNC_FAILED; + unsigned long flags; + + /* Send the initial SRST to the target */ + #define ISCI_SRST_TIMEOUT_MS 20 /* 20 ms timeout. */ + isci_task_build_tmf(&tmf, isci_device, isci_tmf_sata_srst_high, + NULL, NULL + ); + + ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_SRST_TIMEOUT_MS); + + if (ret != TMF_RESP_FUNC_COMPLETE) { + dev_warn(&isci_host->pdev->dev, + "%s: Assert SRST failed (%p) = %x", + __func__, + isci_device, + ret); + + /* Return the failure so that the LUN reset is escalated + * to a target reset. + */ + goto out; + } + + /* Leave SRST high for a bit. */ + #define ISCI_SRST_ASSERT_DELAY 100 /* usecs */ + scic_cb_stall_execution(ISCI_SRST_ASSERT_DELAY); + + /* Deassert SRST. */ + isci_task_build_tmf(&tmf, isci_device, isci_tmf_sata_srst_low, + NULL, NULL + ); + ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_SRST_TIMEOUT_MS); + + if (ret == TMF_RESP_FUNC_COMPLETE) + dev_dbg(&isci_host->pdev->dev, + "%s: SATA LUN reset passed (%p)\n", + __func__, + isci_device); + else + dev_warn(&isci_host->pdev->dev, + "%s: Deassert SRST failed (%p)=%x\n", + __func__, + isci_device, + ret); + + out: + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* Resume the device. */ + scic_sds_remote_device_resume(isci_device->sci_device_handle); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + return ret; +} diff --git a/drivers/scsi/isci/sata.h b/drivers/scsi/isci/sata.h new file mode 100644 index 000000000000..b6ba25b52d2c --- /dev/null +++ b/drivers/scsi/isci/sata.h @@ -0,0 +1,83 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "intel_sat.h" + + + +struct host_to_dev_fis *isci_sata_task_to_fis_copy( + struct sas_task *task); + +bool isci_sata_is_task_ncq( + struct sas_task *task); + +void isci_sata_set_ncq_tag( + struct host_to_dev_fis *register_fis, + struct sas_task *task); + +void isci_request_process_stp_response( + struct sas_task *task, + void *response_buffer); + +u8 isci_sata_get_sat_protocol( + struct isci_request *isci_request); + +enum sci_status isci_sata_management_task_request_build( + struct isci_request *isci_request); + +int isci_task_send_lu_reset_sata( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + u8 *lun); diff --git a/drivers/scsi/isci/sci_environment.h b/drivers/scsi/isci/sci_environment.h new file mode 100644 index 000000000000..e1020ee6c38e --- /dev/null +++ b/drivers/scsi/isci/sci_environment.h @@ -0,0 +1,112 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#ifndef _SCI_ENVIRONMENT_H_ +#define _SCI_ENVIRONMENT_H_ + +#include "isci.h" + +struct scic_sds_controller; +struct scic_sds_phy; +struct scic_sds_port; +struct scic_sds_remote_device; + +static inline struct device *scic_to_dev(struct scic_sds_controller *scic) +{ + struct isci_host *isci_host = sci_object_get_association(scic); + + return &isci_host->pdev->dev; +} + +static inline struct device *sciphy_to_dev(struct scic_sds_phy *sci_phy) +{ + struct isci_phy *iphy = sci_object_get_association(sci_phy); + + if (!iphy || !iphy->isci_port || !iphy->isci_port->isci_host) + return NULL; + + return &iphy->isci_port->isci_host->pdev->dev; +} + +static inline struct device *sciport_to_dev(struct scic_sds_port *sci_port) +{ + struct isci_port *iport = sci_object_get_association(sci_port); + + if (!iport || !iport->isci_host) + return NULL; + + return &iport->isci_host->pdev->dev; +} + +static inline struct device *scirdev_to_dev(struct scic_sds_remote_device *sci_dev) +{ + struct isci_remote_device *idev = sci_object_get_association(sci_dev); + + if (!idev || !idev->isci_port || !idev->isci_port->isci_host) + return NULL; + + return &idev->isci_port->isci_host->pdev->dev; +} + +enum { + ISCI_SI_REVA0, + ISCI_SI_REVA2, + ISCI_SI_REVB0, +}; + +extern int isci_si_rev; + + +#endif diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c new file mode 100644 index 000000000000..5e6f55863407 --- /dev/null +++ b/drivers/scsi/isci/task.c @@ -0,0 +1,1691 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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/completion.h> +#include "scic_task_request.h" +#include "scic_remote_device.h" +#include "scic_io_request.h" +#include "scic_sds_remote_device.h" +#include "scic_sds_remote_node_context.h" +#include "isci.h" +#include "request.h" +#include "sata.h" +#include "task.h" + + +/** + * isci_task_execute_task() - This function is one of the SAS Domain Template + * functions. This function is called by libsas to send a task down to + * hardware. + * @task: This parameter specifies the SAS task to send. + * @num: This parameter specifies the number of tasks to queue. + * @gfp_flags: This parameter specifies the context of this call. + * + * status, zero indicates success. + */ +int isci_task_execute_task(struct sas_task *task, int num, gfp_t gfp_flags) +{ + struct isci_host *isci_host; + struct isci_request *request = NULL; + struct isci_remote_device *device; + unsigned long flags; + unsigned long quiesce_flags = 0; + int ret; + enum sci_status status; + + + dev_dbg(task->dev->port->ha->dev, "%s: num=%d\n", __func__, num); + + if (task->task_state_flags & SAS_TASK_STATE_ABORTED) { + + isci_task_complete_for_upper_layer( + task, + SAS_TASK_UNDELIVERED, + SAM_STAT_TASK_ABORTED, + isci_perform_normal_io_completion + ); + + return 0; /* The I/O was accepted (and failed). */ + } + if ((task->dev == NULL) || (task->dev->port == NULL)) { + + /* Indicate SAS_TASK_UNDELIVERED, so that the scsi midlayer + * removes the target. + */ + isci_task_complete_for_upper_layer( + task, + SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN, + isci_perform_normal_io_completion + ); + return 0; /* The I/O was accepted (and failed). */ + } + isci_host = isci_host_from_sas_ha(task->dev->port->ha); + + /* Check if we have room for more tasks */ + ret = isci_host_can_queue(isci_host, num); + + if (ret) { + dev_warn(task->dev->port->ha->dev, "%s: queue full\n", __func__); + return ret; + } + + do { + dev_dbg(task->dev->port->ha->dev, + "task = %p, num = %d; dev = %p; cmd = %p\n", + task, num, task->dev, task->uldd_task); + + if ((task->dev == NULL) || (task->dev->port == NULL)) { + dev_warn(task->dev->port->ha->dev, + "%s: task %p's port or dev == NULL!\n", + __func__, task); + + /* Indicate SAS_TASK_UNDELIVERED, so that the scsi + * midlayer removes the target. + */ + isci_task_complete_for_upper_layer( + task, + SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN, + isci_perform_normal_io_completion + ); + /* We don't have a valid host reference, so we + * can't control the host queueing condition. + */ + continue; + } + + device = isci_dev_from_domain_dev(task->dev); + + isci_host = isci_host_from_sas_ha(task->dev->port->ha); + + /* check if the controller hasn't started or if the device + * is ready but not accepting IO. + */ + if (device) { + + spin_lock_irqsave(&device->host_quiesce_lock, + quiesce_flags); + } + /* From this point onward, any process that needs to guarantee + * that there is no kernel I/O being started will have to wait + * for the quiesce spinlock. + */ + + if (isci_host_get_state(isci_host) == isci_starting || + (device && ((isci_remote_device_get_state(device) == isci_ready) || + (isci_remote_device_get_state(device) == isci_host_quiesce)))) { + + /* Forces a retry from scsi mid layer. */ + dev_warn(task->dev->port->ha->dev, + "%s: task %p: isci_host->status = %d, " + "device = %p\n", + __func__, + task, + isci_host_get_state(isci_host), + device); + + if (device) + dev_dbg(task->dev->port->ha->dev, + "%s: device->status = 0x%x\n", + __func__, + isci_remote_device_get_state(device)); + + /* Indicate QUEUE_FULL so that the scsi midlayer + * retries. + */ + isci_task_complete_for_upper_layer( + task, + SAS_TASK_COMPLETE, + SAS_QUEUE_FULL, + isci_perform_normal_io_completion + ); + isci_host_can_dequeue(isci_host, 1); + } + /* the device is going down... */ + else if (!device || (isci_ready_for_io != isci_remote_device_get_state(device))) { + + dev_dbg(task->dev->port->ha->dev, + "%s: task %p: isci_host->status = %d, " + "device = %p\n", + __func__, + task, + isci_host_get_state(isci_host), + device); + + if (device) + dev_dbg(task->dev->port->ha->dev, + "%s: device->status = 0x%x\n", + __func__, + isci_remote_device_get_state(device)); + + /* Indicate SAS_TASK_UNDELIVERED, so that the scsi + * midlayer removes the target. + */ + isci_task_complete_for_upper_layer( + task, + SAS_TASK_UNDELIVERED, + SAS_DEVICE_UNKNOWN, + isci_perform_normal_io_completion + ); + isci_host_can_dequeue(isci_host, 1); + + } else { + /* build and send the request. */ + status = isci_request_execute(isci_host, task, &request, + gfp_flags); + + if (status == SCI_SUCCESS) { + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_AT_INITIATOR; + spin_unlock_irqrestore(&task->task_state_lock, flags); + } else { + /* Indicate QUEUE_FULL so that the scsi + * midlayer retries. if the request + * failed for remote device reasons, + * it gets returned as + * SAS_TASK_UNDELIVERED next time + * through. + */ + isci_task_complete_for_upper_layer( + task, + SAS_TASK_COMPLETE, + SAS_QUEUE_FULL, + isci_perform_normal_io_completion + ); + isci_host_can_dequeue(isci_host, 1); + } + } + if (device) { + spin_unlock_irqrestore(&device->host_quiesce_lock, + quiesce_flags + ); + } + task = list_entry(task->list.next, struct sas_task, list); + } while (--num > 0); + return 0; +} + + + +/** + * isci_task_request_build() - This function builds the task request object. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter points to the isci_request object allocated in the + * request construct function. + * @tmf: This parameter is the task management struct to be built + * + * SCI_SUCCESS on successfull completion, or specific failure code. + */ +static enum sci_status isci_task_request_build( + struct isci_host *isci_host, + struct isci_request **isci_request, + struct isci_tmf *isci_tmf) +{ + struct scic_sds_remote_device *sci_device; + enum sci_status status = SCI_FAILURE; + struct isci_request *request; + struct isci_remote_device *isci_device; +/* struct sci_sas_identify_address_frame_protocols dev_protocols; */ + struct smp_discover_response_protocols dev_protocols; + + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_tmf = %p\n", __func__, isci_tmf); + + isci_device = isci_tmf->device; + sci_device = isci_device->sci_device_handle; + + /* do common allocation and init of request object. */ + status = isci_request_alloc_tmf( + isci_host, + isci_tmf, + &request, + isci_device, + GFP_ATOMIC + ); + + if (status != SCI_SUCCESS) + goto out; + + /* let the core do it's construct. */ + status = scic_task_request_construct( + isci_host->core_controller, + sci_device, + SCI_CONTROLLER_INVALID_IO_TAG, + request, + request->sci_request_mem_ptr, + &request->sci_request_handle + ); + + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: scic_task_request_construct failed - " + "status = 0x%x\n", + __func__, + status); + goto errout; + } + + sci_object_set_association( + request->sci_request_handle, + request + ); + + scic_remote_device_get_protocols( + sci_device, + &dev_protocols + ); + + /* let the core do it's protocol + * specific construction. + */ + if (dev_protocols.u.bits.attached_ssp_target) { + + isci_tmf->proto = SAS_PROTOCOL_SSP; + status = scic_task_request_construct_ssp( + request->sci_request_handle + ); + if (status != SCI_SUCCESS) + goto errout; + } + + if (dev_protocols.u.bits.attached_stp_target) { + + isci_tmf->proto = SAS_PROTOCOL_SATA; + status = isci_sata_management_task_request_build(request); + + if (status != SCI_SUCCESS) + goto errout; + } + + goto out; + + errout: + + /* release the dma memory if we fail. */ + isci_request_free(isci_host, request); + request = NULL; + + out: + *isci_request = request; + return status; +} + +/** + * isci_tmf_timeout_cb() - This function is called as a kernel callback when + * the timeout period for the TMF has expired. + * + * + */ +static void isci_tmf_timeout_cb(void *tmf_request_arg) +{ + struct isci_request *request = (struct isci_request *)tmf_request_arg; + struct isci_tmf *tmf = isci_request_access_tmf(request); + enum sci_status status; + + BUG_ON(request->ttype != tmf_task); + + /* This task management request has timed-out. Terminate the request + * so that the request eventually completes to the requestor in the + * request completion callback path. + */ + /* Note - the timer callback function itself has provided spinlock + * exclusion from the start and completion paths. No need to take + * the request->isci_host->scic_lock here. + */ + + if (tmf->timeout_timer != NULL) { + /* Call the users callback, if any. */ + if (tmf->cb_state_func != NULL) + tmf->cb_state_func(isci_tmf_timed_out, tmf, + tmf->cb_data); + + /* Terminate the TMF transmit request. */ + status = scic_controller_terminate_request( + request->isci_host->core_controller, + request->isci_device->sci_device_handle, + request->sci_request_handle + ); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: tmf_request = %p; tmf = %p; status = %d\n", + __func__, request, tmf, status); + } else + dev_dbg(&request->isci_host->pdev->dev, + "%s: timer already canceled! " + "tmf_request = %p; tmf = %p\n", + __func__, request, tmf); + + /* No need to unlock since the caller to this callback is doing it for + * us. + * request->isci_host->scic_lock + */ +} + +/** + * isci_task_execute_tmf() - This function builds and sends a task request, + * then waits for the completion. + * @isci_host: This parameter specifies the ISCI host object + * @tmf: This parameter is the pointer to the task management structure for + * this request. + * @timeout_ms: This parameter specifies the timeout period for the task + * management request. + * + * TMF_RESP_FUNC_COMPLETE on successful completion of the TMF (this includes + * error conditions reported in the IU status), or TMF_RESP_FUNC_FAILED. + */ +int isci_task_execute_tmf( + struct isci_host *isci_host, + struct isci_tmf *tmf, + unsigned long timeout_ms) +{ + DECLARE_COMPLETION_ONSTACK(completion); + enum sci_status status = SCI_FAILURE; + struct scic_sds_remote_device *sci_device; + struct isci_remote_device *isci_device = tmf->device; + struct isci_request *request; + int ret = TMF_RESP_FUNC_FAILED; + unsigned long flags; + + /* sanity check, return TMF_RESP_FUNC_FAILED + * if the device is not there and ready. + */ + if (!isci_device || + ((isci_ready_for_io != isci_remote_device_get_state(isci_device)) && + (isci_host_quiesce != isci_remote_device_get_state(isci_device)))) { + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p not ready (%d)\n", + __func__, + isci_device, + isci_remote_device_get_state(isci_device)); + return TMF_RESP_FUNC_FAILED; + } else + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p\n", + __func__, isci_device); + + sci_device = isci_device->sci_device_handle; + + /* Assign the pointer to the TMF's completion kernel wait structure. */ + tmf->complete = &completion; + + isci_task_request_build( + isci_host, + &request, + tmf + ); + + if (!request) { + dev_warn(&isci_host->pdev->dev, + "%s: isci_task_request_build failed\n", + __func__); + return TMF_RESP_FUNC_FAILED; + } + + /* Allocate the TMF timeout timer. */ + tmf->timeout_timer = isci_timer_create( + &isci_host->timer_list_struct, + isci_host, + request, + isci_tmf_timeout_cb + ); + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* Start the timer. */ + if (tmf->timeout_timer) + isci_timer_start(tmf->timeout_timer, timeout_ms); + else + dev_warn(&isci_host->pdev->dev, + "%s: isci_timer_create failed!!!!\n", + __func__); + + /* start the TMF io. */ + status = scic_controller_start_task( + isci_host->core_controller, + sci_device, + request->sci_request_handle, + SCI_CONTROLLER_INVALID_IO_TAG + ); + + if (status != SCI_SUCCESS) { + dev_warn(&isci_host->pdev->dev, + "%s: start_io failed - status = 0x%x, request = %p\n", + __func__, + status, + request); + goto cleanup_request; + } + + /* Call the users callback, if any. */ + if (tmf->cb_state_func != NULL) + tmf->cb_state_func(isci_tmf_started, tmf, tmf->cb_data); + + /* Change the state of the TMF-bearing request to "started". */ + isci_request_change_state(request, started); + + /* add the request to the remote device request list. */ + list_add(&request->dev_node, &isci_device->reqs_in_process); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + /* Wait for the TMF to complete, or a timeout. */ + wait_for_completion(&completion); + + isci_print_tmf(tmf); + + if (tmf->status == SCI_SUCCESS) + ret = TMF_RESP_FUNC_COMPLETE; + else if (tmf->status == SCI_FAILURE_IO_RESPONSE_VALID) { + dev_dbg(&isci_host->pdev->dev, + "%s: tmf.status == " + "SCI_FAILURE_IO_RESPONSE_VALID\n", + __func__); + ret = TMF_RESP_FUNC_COMPLETE; + } + /* Else - leave the default "failed" status alone. */ + + dev_dbg(&isci_host->pdev->dev, + "%s: completed request = %p\n", + __func__, + request); + + if (request->io_request_completion != NULL) { + + /* The fact that this is non-NULL for a TMF request + * means there is a thread waiting for this TMF to + * finish. + */ + complete(request->io_request_completion); + } + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + cleanup_request: + + /* Clean up the timer if needed. */ + if (tmf->timeout_timer) { + isci_timer_stop(tmf->timeout_timer); + isci_timer_free(&isci_host->timer_list_struct, + tmf->timeout_timer); + tmf->timeout_timer = NULL; + } + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + isci_request_free(isci_host, request); + + return ret; +} + +void isci_task_build_tmf( + struct isci_tmf *tmf, + struct isci_remote_device *isci_device, + enum isci_tmf_function_codes code, + void (*tmf_sent_cb)(enum isci_tmf_cb_state, + struct isci_tmf *, + void *), + void *cb_data) +{ + dev_dbg(&isci_device->isci_port->isci_host->pdev->dev, + "%s: isci_device = %p\n", __func__, isci_device); + + memset(tmf, 0, sizeof(*tmf)); + + tmf->device = isci_device; + tmf->tmf_code = code; + tmf->timeout_timer = NULL; + tmf->cb_state_func = tmf_sent_cb; + tmf->cb_data = cb_data; +} + +static struct isci_request *isci_task_get_request_from_task( + struct sas_task *task, + struct isci_host **isci_host, + struct isci_remote_device **isci_device) +{ + + struct isci_request *request = NULL; + unsigned long flags; + + spin_lock_irqsave(&task->task_state_lock, flags); + + request = task->lldd_task; + + /* If task is already done, the request isn't valid */ + if (!(task->task_state_flags & SAS_TASK_STATE_DONE) && + (task->task_state_flags & SAS_TASK_AT_INITIATOR) && + (request != NULL)) { + + if (isci_host != NULL) + *isci_host = request->isci_host; + + if (isci_device != NULL) + *isci_device = request->isci_device; + } + + spin_unlock_irqrestore(&task->task_state_lock, flags); + + return request; +} + +/** + * isci_task_validate_request_to_abort() - This function checks the given I/O + * against the "started" state. If the request is still "started", it's + * state is changed to aborted. NOTE: isci_host->scic_lock MUST BE HELD + * BEFORE CALLING THIS FUNCTION. + * @isci_request: This parameter specifies the request object to control. + * @isci_host: This parameter specifies the ISCI host object + * @isci_device: This is the device to which the request is pending. + * @aborted_io_completion: This is a completion structure that will be added to + * the request in case it is changed to aborting; this completion is + * triggered when the request is fully completed. + * + * Either "started" on successful change of the task status to "aborted", or + * "unallocated" if the task cannot be controlled. + */ +static enum isci_request_status isci_task_validate_request_to_abort( + struct isci_request *isci_request, + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + struct completion *aborted_io_completion) +{ + enum isci_request_status old_state = unallocated; + + /* Only abort the task if it's in the + * device's request_in_process list + */ + if (isci_request && !list_empty(&isci_request->dev_node)) { + old_state = isci_request_change_started_to_aborted( + isci_request, aborted_io_completion); + + /* Only abort requests in the started state. */ + if (old_state != started) + old_state = unallocated; + } + + return old_state; +} + +static void isci_request_cleanup_completed_loiterer( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + struct isci_request *isci_request) +{ + struct sas_task *task = isci_request_access_task(isci_request); + unsigned long flags; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device=%p, request=%p, task=%p\n", + __func__, isci_device, isci_request, + isci_request->ttype_ptr.io_task_ptr); + + spin_lock_irqsave(&isci_host->scic_lock, flags); + list_del_init(&isci_request->dev_node); + if (task != NULL) + task->lldd_task = NULL; + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + isci_request_free(isci_host, isci_request); +} +/** + * isci_terminate_request_core() - This function will terminate the given + * request, and wait for it to complete. This function must only be called + * from a thread that can wait. Note that the request is terminated and + * completed (back to the host, if started there). + * @isci_host: This SCU. + * @isci_device: The target. + * @isci_request: The I/O request to be terminated. + * + * + */ +static void isci_terminate_request_core( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + struct isci_request *isci_request, + struct completion *request_completion) +{ + enum sci_status status = SCI_SUCCESS; + bool was_terminated = false; + bool needs_cleanup_handling = false; + enum isci_request_status request_status; + unsigned long flags; + + dev_dbg(&isci_host->pdev->dev, + "%s: device = %p; request = %p\n", + __func__, isci_device, isci_request); + + /* Peek at the current status of the request. This will tell + * us if there was special handling on the request such that it + * needs to be detached and freed here. + */ + spin_lock_irqsave(&isci_request->state_lock, flags); + request_status = isci_request_get_state(isci_request); + + /* TMFs are in their own thread */ + if ((isci_request->ttype == io_task) && + ((request_status == aborted) || + (request_status == aborting) || + (request_status == terminating))) + /* The completion routine won't free a request in + * the aborted/aborting/terminating state, so we do + * it here. + */ + needs_cleanup_handling = true; + + spin_unlock_irqrestore(&isci_request->state_lock, flags); + + spin_lock_irqsave(&isci_host->scic_lock, flags); + /* Make sure the request wasn't just sitting around signalling + * device condition (if the request handle is NULL, then the + * request completed but needed additional handling here). + */ + if (isci_request->sci_request_handle != NULL) { + was_terminated = true; + status = scic_controller_terminate_request( + isci_host->core_controller, + isci_device->sci_device_handle, + isci_request->sci_request_handle + ); + } + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + /* + * The only time the request to terminate will + * fail is when the io request is completed and + * being aborted. + */ + if (status != SCI_SUCCESS) + dev_err(&isci_host->pdev->dev, + "%s: scic_controller_terminate_request" + " returned = 0x%x\n", + __func__, + status); + else { + if (was_terminated) { + dev_dbg(&isci_host->pdev->dev, + "%s: before completion wait (%p)\n", + __func__, + request_completion); + + /* Wait here for the request to complete. */ + wait_for_completion(request_completion); + + dev_dbg(&isci_host->pdev->dev, + "%s: after completion wait (%p)\n", + __func__, + request_completion); + } + + if (needs_cleanup_handling) + isci_request_cleanup_completed_loiterer( + isci_host, isci_device, isci_request + ); + } +} +static void isci_terminate_request( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + struct isci_request *isci_request, + enum isci_request_status new_request_state) +{ + enum isci_request_status old_state; + + DECLARE_COMPLETION_ONSTACK(request_completion); + unsigned long flags; + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* Change state to "new_request_state" if it is currently "started" */ + old_state = isci_request_change_started_to_newstate( + isci_request, + &request_completion, + new_request_state + ); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + if (old_state == started) + /* This request was not already being aborted. If it had been, + * then the aborting I/O (ie. the TMF request) would not be in + * the aborting state, and thus would be terminated here. Note + * that since the TMF completion's call to the kernel function + * "complete()" does not happen until the pending I/O request + * terminate fully completes, we do not have to implement a + * special wait here for already aborting requests - the + * termination of the TMF request will force the request + * to finish it's already started terminate. + */ + isci_terminate_request_core(isci_host, isci_device, + isci_request, &request_completion); +} + +/** + * isci_terminate_pending_requests() - This function will change the all of the + * requests on the given device's state to "aborting", will terminate the + * requests, and wait for them to complete. This function must only be + * called from a thread that can wait. Note that the requests are all + * terminated and completed (back to the host, if started there). + * @isci_host: This parameter specifies SCU. + * @isci_device: This parameter specifies the target. + * + * + */ +void isci_terminate_pending_requests( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + enum isci_request_status new_request_state) +{ + struct isci_request *isci_request; + struct sas_task *task; + bool done = false; + unsigned long flags; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p (new request state = %d)\n", + __func__, isci_device, new_request_state); + + #define ISCI_TERMINATE_SHOW_PENDING_REQUESTS + #ifdef ISCI_TERMINATE_SHOW_PENDING_REQUESTS + { + struct isci_request *request; + + /* Only abort the task if it's in the + * device's request_in_process list + */ + list_for_each_entry(request, + &isci_device->reqs_in_process, + dev_node) + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p; request is on " + "reqs_in_process list: %p\n", + __func__, isci_device, request); + } + #endif /* ISCI_TERMINATE_SHOW_PENDING_REQUESTS */ + + /* Clean up all pending requests. */ + do { + spin_lock_irqsave(&isci_host->scic_lock, flags); + + if (list_empty(&isci_device->reqs_in_process)) { + + done = true; + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device = %p; done.\n", + __func__, isci_device); + } else { + /* The list was not empty - grab the first request. */ + isci_request = list_first_entry( + &isci_device->reqs_in_process, + struct isci_request, dev_node + ); + /* Note that we are not expecting to have to control + * the target to abort the request. + */ + isci_request->complete_in_target = true; + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + /* Get the libsas task reference. */ + task = isci_request_access_task(isci_request); + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_device=%p request=%p; task=%p\n", + __func__, isci_device, isci_request, task); + + /* Mark all still pending I/O with the selected next + * state. + */ + isci_terminate_request(isci_host, isci_device, + isci_request, new_request_state + ); + + /* Set the 'done' state on the task. */ + if (task) + isci_task_all_done(task); + } + } while (!done); +} + +/** + * isci_task_send_lu_reset_sas() - This function is called by of the SAS Domain + * Template functions. + * @lun: This parameter specifies the lun to be reset. + * + * status, zero indicates success. + */ +static int isci_task_send_lu_reset_sas( + struct isci_host *isci_host, + struct isci_remote_device *isci_device, + u8 *lun) +{ + struct isci_tmf tmf; + int ret = TMF_RESP_FUNC_FAILED; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_host = %p, isci_device = %p\n", + __func__, isci_host, isci_device); + /* Send the LUN reset to the target. By the time the call returns, + * the TMF has fully exected in the target (in which case the return + * value is "TMF_RESP_FUNC_COMPLETE", or the request timed-out (or + * was otherwise unable to be executed ("TMF_RESP_FUNC_FAILED"). + */ + isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_lun_reset, NULL, + NULL); + + #define ISCI_LU_RESET_TIMEOUT_MS 2000 /* 2 second timeout. */ + ret = isci_task_execute_tmf(isci_host, &tmf, ISCI_LU_RESET_TIMEOUT_MS); + + if (ret == TMF_RESP_FUNC_COMPLETE) + dev_dbg(&isci_host->pdev->dev, + "%s: %p: TMF_LU_RESET passed\n", + __func__, isci_device); + else + dev_dbg(&isci_host->pdev->dev, + "%s: %p: TMF_LU_RESET failed (%x)\n", + __func__, isci_device, ret); + + return ret; +} + +/** + * isci_task_lu_reset() - This function is one of the SAS Domain Template + * functions. This is one of the Task Management functoins called by libsas, + * to reset the given lun. Note the assumption that while this call is + * executing, no I/O will be sent by the host to the device. + * @lun: This parameter specifies the lun to be reset. + * + * status, zero indicates success. + */ +int isci_task_lu_reset( + struct domain_device *domain_device, + u8 *lun) +{ + struct isci_host *isci_host = NULL; + struct isci_remote_device *isci_device = NULL; + int ret; + bool device_stopping = false; + + if (domain_device == NULL) { + pr_warn("%s: domain_device == NULL\n", __func__); + return TMF_RESP_FUNC_FAILED; + } + + isci_device = isci_dev_from_domain_dev(domain_device); + + if (domain_device->port != NULL) + isci_host = isci_host_from_sas_ha(domain_device->port->ha); + + pr_debug("%s: domain_device=%p, isci_host=%p; isci_device=%p\n", + __func__, domain_device, isci_host, isci_device); + + if (isci_device != NULL) + device_stopping = (isci_device->status == isci_stopping) + || (isci_device->status == isci_stopped); + + /* If there is a device reset pending on any request in the + * device's list, fail this LUN reset request in order to + * escalate to the device reset. + */ + if ((isci_device == NULL) || + (isci_host == NULL) || + ((isci_host != NULL) && + (isci_device != NULL) && + (device_stopping || + (isci_device_is_reset_pending(isci_host, isci_device))))) { + dev_warn(&isci_host->pdev->dev, + "%s: No dev (%p), no host (%p), or " + "RESET PENDING: domain_device=%p\n", + __func__, isci_device, isci_host, domain_device); + return TMF_RESP_FUNC_FAILED; + } + + /* Stop I/O to the remote device. */ + isci_device_set_host_quiesce_lock_state(isci_device, true); + + /* Send the task management part of the reset. */ + if (sas_protocol_ata(domain_device->tproto)) { + ret = isci_task_send_lu_reset_sata( + isci_host, isci_device, lun + ); + } else + ret = isci_task_send_lu_reset_sas(isci_host, isci_device, lun); + + /* If the LUN reset worked, all the I/O can now be terminated. */ + if (ret == TMF_RESP_FUNC_COMPLETE) + /* Terminate all I/O now. */ + isci_terminate_pending_requests(isci_host, + isci_device, + terminating); + + /* Resume I/O to the remote device. */ + isci_device_set_host_quiesce_lock_state(isci_device, false); + + return ret; +} + + +/* int (*lldd_clear_nexus_port)(struct asd_sas_port *); */ +int isci_task_clear_nexus_port(struct asd_sas_port *port) +{ + return TMF_RESP_FUNC_FAILED; +} + + + +int isci_task_clear_nexus_ha(struct sas_ha_struct *ha) +{ + return TMF_RESP_FUNC_FAILED; +} + +int isci_task_I_T_nexus_reset(struct domain_device *dev) +{ + return TMF_RESP_FUNC_FAILED; +} + + +/* Task Management Functions. Must be called from process context. */ + +/** + * isci_abort_task_process_cb() - This is a helper function for the abort task + * TMF command. It manages the request state with respect to the successful + * transmission / completion of the abort task request. + * @cb_state: This parameter specifies when this function was called - after + * the TMF request has been started and after it has timed-out. + * @tmf: This parameter specifies the TMF in progress. + * + * + */ +static void isci_abort_task_process_cb( + enum isci_tmf_cb_state cb_state, + struct isci_tmf *tmf, + void *cb_data) +{ + struct isci_request *old_request; + + old_request = (struct isci_request *)cb_data; + + dev_dbg(&old_request->isci_host->pdev->dev, + "%s: tmf=%p, old_request=%p\n", + __func__, tmf, old_request); + + switch (cb_state) { + + case isci_tmf_started: + /* The TMF has been started. Nothing to do here, since the + * request state was already set to "aborted" by the abort + * task function. + */ + BUG_ON(old_request->status != aborted); + break; + + case isci_tmf_timed_out: + + /* Set the task's state to "aborting", since the abort task + * function thread set it to "aborted" (above) in anticipation + * of the task management request working correctly. Since the + * timeout has now fired, the TMF request failed. We set the + * state such that the request completion will indicate the + * device is no longer present. + */ + isci_request_change_state(old_request, aborting); + break; + + default: + dev_err(&old_request->isci_host->pdev->dev, + "%s: Bad cb_state (%d): tmf=%p, old_request=%p\n", + __func__, cb_state, tmf, old_request); + break; + } +} + +/** + * isci_task_abort_task() - This function is one of the SAS Domain Template + * functions. This function is called by libsas to abort a specified task. + * @task: This parameter specifies the SAS task to abort. + * + * status, zero indicates success. + */ +int isci_task_abort_task(struct sas_task *task) +{ + DECLARE_COMPLETION_ONSTACK(aborted_io_completion); + struct isci_request *old_request = NULL; + struct isci_remote_device *isci_device = NULL; + struct isci_host *isci_host = NULL; + struct isci_tmf tmf; + int ret = TMF_RESP_FUNC_FAILED; + unsigned long flags; + bool any_dev_reset, device_stopping; + + /* Get the isci_request reference from the task. Note that + * this check does not depend on the pending request list + * in the device, because tasks driving resets may land here + * after completion in the core. + */ + old_request = isci_task_get_request_from_task(task, &isci_host, + &isci_device); + + dev_dbg(&isci_host->pdev->dev, + "%s: task = %p\n", __func__, task); + + /* Check if the device has been / is currently being removed. + * If so, no task management will be done, and the I/O will + * be terminated. + */ + device_stopping = (isci_device->status == isci_stopping) + || (isci_device->status == isci_stopped); + +#ifdef NOMORE + /* This abort task function is the first stop of the libsas error + * handler thread. Since libsas is executing in a thread with a + * referernce to the "task" parameter, that task cannot be completed + * directly back to the upper layers. In order to make sure that + * the task is managed correctly if this abort task fails, set the + * "SAS_TASK_STATE_ABORTED" bit now such that completions up the + * stack will be intercepted and only allowed to happen in the + * libsas SCSI error handler thread. + */ + spin_lock_irqsave(&task->task_state_lock, flags); + task->task_state_flags |= SAS_TASK_STATE_ABORTED; + spin_unlock_irqrestore(&task->task_state_lock, flags); +#endif /* NOMORE */ + + /* This version of the driver will fail abort requests for + * SATA/STP. Failing the abort request this way will cause the + * SCSI error handler thread to escalate to LUN reset + */ + if (sas_protocol_ata(task->task_proto) && !device_stopping) { + dev_warn(&isci_host->pdev->dev, + " task %p is for a STP/SATA device;" + " returning TMF_RESP_FUNC_FAILED\n" + " to cause a LUN reset...\n", task); + return TMF_RESP_FUNC_FAILED; + } + + dev_dbg(&isci_host->pdev->dev, + "%s: old_request == %p\n", __func__, old_request); + + spin_lock_irqsave(&task->task_state_lock, flags); + + /* Don't do resets to stopping devices. */ + if (device_stopping) + task->task_state_flags &= ~SAS_TASK_NEED_DEV_RESET; + + /* See if there is a pending device reset for this device. */ + any_dev_reset = task->task_state_flags & SAS_TASK_NEED_DEV_RESET; + + spin_unlock_irqrestore(&task->task_state_lock, flags); + + if ((isci_device != NULL) && !device_stopping) + any_dev_reset = any_dev_reset + || isci_device_is_reset_pending(isci_host, + isci_device + ); + + /* If the extraction of the request reference from the task + * failed, then the request has been completed (or if there is a + * pending reset then this abort request function must be failed + * in order to escalate to the target reset). + */ + if ((old_request == NULL) || + ((old_request != NULL) && + (old_request->sci_request_handle == NULL) && + (old_request->complete_in_target)) || + any_dev_reset) { + + spin_lock_irqsave(&task->task_state_lock, flags); + + /* If the device reset task flag is set, fail the task + * management request. Otherwise, the original request + * has completed. + */ + if (any_dev_reset) { + + /* Turn off the task's DONE to make sure this + * task is escalated to a target reset. + */ + task->task_state_flags &= ~SAS_TASK_STATE_DONE; + + /* Fail the task management request in order to + * escalate to the target reset. + */ + ret = TMF_RESP_FUNC_FAILED; + + dev_dbg(&isci_host->pdev->dev, + "%s: Failing task abort in order to " + "escalate to target reset because\n" + "SAS_TASK_NEED_DEV_RESET is set for " + "task %p on dev %p\n", + __func__, task, isci_device); + + } else { + ret = TMF_RESP_FUNC_COMPLETE; + + dev_dbg(&isci_host->pdev->dev, + "%s: abort task not needed for %p\n", + __func__, task); + + /* The request has already completed and there + * is nothing to do here other than to set the task + * done bit, and indicate that the task abort function + * was sucessful. + */ + isci_set_task_doneflags(task); + + /* Set the abort bit to make sure that libsas sticks the + * task in the completed task queue. + */ +/* task->task_state_flags |= SAS_TASK_STATE_ABORTED; */ + + /* Check for the situation where the request was + * left around on the device list but the + * request already completed. + */ + if (old_request && !old_request->sci_request_handle) { + + isci_request_cleanup_completed_loiterer( + isci_host, isci_device, old_request + ); + } + } + spin_unlock_irqrestore(&task->task_state_lock, flags); + + return ret; + } + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + /* Sanity check the request status, and set the I/O kernel completion + * struct that will be triggered when the request completes. + */ + if (isci_task_validate_request_to_abort( + old_request, + isci_host, + isci_device, + &aborted_io_completion) + == unallocated) { + dev_dbg(&isci_host->pdev->dev, + "%s: old_request not valid for device = %p\n", + __func__, + isci_device); + old_request = NULL; + } + + if (!old_request) { + + /* There is no isci_request attached to the sas_task. + * It must have been completed and detached. + */ + dev_dbg(&isci_host->pdev->dev, + "%s: old_request == NULL\n", + __func__); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + /* Set the state on the task. */ + isci_task_all_done(task); + + return TMF_RESP_FUNC_COMPLETE; + } + if (task->task_proto == SAS_PROTOCOL_SMP || device_stopping) { + + if (device_stopping) + dev_dbg(&isci_host->pdev->dev, + "%s: device is stopping, thus no TMF\n", + __func__); + else + dev_dbg(&isci_host->pdev->dev, + "%s: request is SMP, thus no TMF\n", + __func__); + + old_request->complete_in_target = true; + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + /* Set the state on the task. */ + isci_task_all_done(task); + + ret = TMF_RESP_FUNC_COMPLETE; + + /* Stopping and SMP devices are not sent a TMF, and are not + * reset, but the outstanding I/O request is terminated here. + * + * Clean up the request on our side, and wait for the aborted + * I/O to complete. + */ + isci_terminate_request_core(isci_host, isci_device, old_request, + &aborted_io_completion); + } else { + /* Fill in the tmf stucture */ + isci_task_build_tmf(&tmf, isci_device, isci_tmf_ssp_task_abort, + isci_abort_task_process_cb, old_request); + + tmf.io_tag = scic_io_request_get_io_tag( + old_request->sci_request_handle + ); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + #define ISCI_ABORT_TASK_TIMEOUT_MS 500 /* half second timeout. */ + ret = isci_task_execute_tmf(isci_host, &tmf, + ISCI_ABORT_TASK_TIMEOUT_MS); + + if (ret == TMF_RESP_FUNC_COMPLETE) { + old_request->complete_in_target = true; + + /* Clean up the request on our side, and wait for the aborted I/O to + * complete. + */ + isci_terminate_request_core(isci_host, isci_device, old_request, + &aborted_io_completion); + + /* Set the state on the task. */ + isci_task_all_done(task); + } else + dev_err(&isci_host->pdev->dev, + "%s: isci_task_send_tmf failed\n", + __func__); + } + + return ret; +} + +/** + * isci_task_abort_task_set() - This function is one of the SAS Domain Template + * functions. This is one of the Task Management functoins called by libsas, + * to abort all task for the given lun. + * @d_device: This parameter specifies the domain device associated with this + * request. + * @lun: This parameter specifies the lun associated with this request. + * + * status, zero indicates success. + */ +int isci_task_abort_task_set( + struct domain_device *d_device, + u8 *lun) +{ + return TMF_RESP_FUNC_FAILED; +} + + +/** + * isci_task_clear_aca() - This function is one of the SAS Domain Template + * functions. This is one of the Task Management functoins called by libsas. + * @d_device: This parameter specifies the domain device associated with this + * request. + * @lun: This parameter specifies the lun associated with this request. + * + * status, zero indicates success. + */ +int isci_task_clear_aca( + struct domain_device *d_device, + u8 *lun) +{ + return TMF_RESP_FUNC_FAILED; +} + + + +/** + * isci_task_clear_task_set() - This function is one of the SAS Domain Template + * functions. This is one of the Task Management functoins called by libsas. + * @d_device: This parameter specifies the domain device associated with this + * request. + * @lun: This parameter specifies the lun associated with this request. + * + * status, zero indicates success. + */ +int isci_task_clear_task_set( + struct domain_device *d_device, + u8 *lun) +{ + return TMF_RESP_FUNC_FAILED; +} + + +/** + * isci_task_query_task() - This function is implemented to cause libsas to + * correctly escalate the failed abort to a LUN or target reset (this is + * because sas_scsi_find_task libsas function does not correctly interpret + * all return codes from the abort task call). When TMF_RESP_FUNC_SUCC is + * returned, libsas turns this into a LUN reset; when FUNC_FAILED is + * returned, libsas will turn this into a target reset + * @task: This parameter specifies the sas task being queried. + * @lun: This parameter specifies the lun associated with this request. + * + * status, zero indicates success. + */ +int isci_task_query_task( + struct sas_task *task) +{ + /* See if there is a pending device reset for this device. */ + if (task->task_state_flags & SAS_TASK_NEED_DEV_RESET) + return TMF_RESP_FUNC_FAILED; + else + return TMF_RESP_FUNC_SUCC; +} + +/** + * isci_task_request_complete() - This function is called by the sci core when + * an task request completes. + * @isci_host: This parameter specifies the ISCI host object + * @request: This parameter is the completed isci_request object. + * @completion_status: This parameter specifies the completion status from the + * sci core. + * + * none. + */ +void isci_task_request_complete( + struct isci_host *isci_host, + struct isci_request *request, + enum sci_task_status completion_status) +{ + struct isci_remote_device *isci_device = request->isci_device; + enum isci_request_status old_state; + struct isci_tmf *tmf = isci_request_access_tmf(request); + struct completion *tmf_complete; + + dev_dbg(&isci_host->pdev->dev, + "%s: request = %p, status=%d\n", + __func__, request, completion_status); + + old_state = isci_request_change_state(request, completed); + + tmf->status = completion_status; + request->complete_in_target = true; + + if (SAS_PROTOCOL_SSP == tmf->proto) { + + memcpy(&tmf->resp.resp_iu, + scic_io_request_get_response_iu_address( + request->sci_request_handle + ), + sizeof(struct sci_ssp_response_iu)); + + } else if (SAS_PROTOCOL_SATA == tmf->proto) { + + memcpy(&tmf->resp.d2h_fis, + scic_stp_io_request_get_d2h_reg_address( + request->sci_request_handle + ), + sizeof(struct sata_fis_reg_d2h) + ); + } + + /* Manage the timer if it is still running. */ + if (tmf->timeout_timer) { + + isci_timer_stop(tmf->timeout_timer); + isci_timer_free(&isci_host->timer_list_struct, + tmf->timeout_timer); + tmf->timeout_timer = NULL; + } + + /* PRINT_TMF( ((struct isci_tmf *)request->task)); */ + tmf_complete = tmf->complete; + + scic_controller_complete_task( + isci_host->core_controller, + isci_device->sci_device_handle, + request->sci_request_handle + ); + /* NULL the request handle to make sure it cannot be terminated + * or completed again. + */ + request->sci_request_handle = NULL; + + isci_request_change_state(request, unallocated); + list_del_init(&request->dev_node); + + /* The task management part completes last. */ + complete(tmf_complete); +} + + +/** + * isci_task_ssp_request_get_lun() - This function is called by the sci core to + * retrieve the lun for a given task request. + * @request: This parameter is the isci_request object. + * + * lun for specified task request. + */ +u32 isci_task_ssp_request_get_lun(struct isci_request *request) +{ + struct isci_tmf *isci_tmf = isci_request_access_tmf(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: lun = %d\n", __func__, isci_tmf->lun[0]); +/* @todo: build lun from array of bytes to 32 bit */ + return isci_tmf->lun[0]; +} + +/** + * isci_task_ssp_request_get_function() - This function is called by the sci + * core to retrieve the function for a given task request. + * @request: This parameter is the isci_request object. + * + * function code for specified task request. + */ +u8 isci_task_ssp_request_get_function(struct isci_request *request) +{ + struct isci_tmf *isci_tmf = isci_request_access_tmf(request); + + dev_dbg(&request->isci_host->pdev->dev, + "%s: func = %d\n", __func__, isci_tmf->tmf_code); + + return isci_tmf->tmf_code; +} + +/** + * isci_task_ssp_request_get_io_tag_to_manage() - This function is called by + * the sci core to retrieve the io tag for a given task request. + * @request: This parameter is the isci_request object. + * + * io tag for specified task request. + */ +u16 isci_task_ssp_request_get_io_tag_to_manage(struct isci_request *request) +{ + u16 io_tag = SCI_CONTROLLER_INVALID_IO_TAG; + + if (tmf_task == request->ttype) { + struct isci_tmf *tmf = isci_request_access_tmf(request); + io_tag = tmf->io_tag; + } + + dev_dbg(&request->isci_host->pdev->dev, + "%s: request = %p, io_tag = %d\n", + __func__, request, io_tag); + + return io_tag; +} + +/** + * isci_task_ssp_request_get_response_data_address() - This function is called + * by the sci core to retrieve the response data address for a given task + * request. + * @request: This parameter is the isci_request object. + * + * response data address for specified task request. + */ +void *isci_task_ssp_request_get_response_data_address( + struct isci_request *request) +{ + struct isci_tmf *isci_tmf = isci_request_access_tmf(request); + + return &isci_tmf->resp.resp_iu; +} + +/** + * isci_task_ssp_request_get_response_data_length() - This function is called + * by the sci core to retrieve the response data length for a given task + * request. + * @request: This parameter is the isci_request object. + * + * response data length for specified task request. + */ +u32 isci_task_ssp_request_get_response_data_length( + struct isci_request *request) +{ + struct isci_tmf *isci_tmf = isci_request_access_tmf(request); + + return sizeof(isci_tmf->resp.resp_iu); +} + +/** + * isci_bus_reset_handler() - This function performs a target reset of the + * device referenced by "cmd'. This function is exported through the + * "struct scsi_host_template" structure such that it is called when an I/O + * recovery process has escalated to a target reset. Note that this function + * is called from the scsi error handler event thread, so may block on calls. + * @scsi_cmd: This parameter specifies the target to be reset. + * + * SUCCESS if the reset process was successful, else FAILED. + */ +int isci_bus_reset_handler(struct scsi_cmnd *cmd) +{ + unsigned long flags = 0; + struct isci_host *isci_host = NULL; + enum sci_status status; + int base_status; + struct isci_remote_device *isci_dev + = isci_dev_from_domain_dev( + sdev_to_domain_dev(cmd->device)); + + dev_dbg(&cmd->device->sdev_gendev, + "%s: cmd %p, isci_dev %p\n", + __func__, cmd, isci_dev); + + if (!isci_dev) { + dev_warn(&cmd->device->sdev_gendev, + "%s: isci_dev is GONE!\n", + __func__); + + return TMF_RESP_FUNC_COMPLETE; /* Nothing to reset. */ + } + + if (isci_dev->isci_port != NULL) + isci_host = isci_dev->isci_port->isci_host; + + if (isci_host != NULL) + spin_lock_irqsave(&isci_host->scic_lock, flags); + + status = scic_remote_device_reset(isci_dev->sci_device_handle); + if (status != SCI_SUCCESS) { + + if (isci_host != NULL) + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + scmd_printk(KERN_WARNING, cmd, + "%s: scic_remote_device_reset(%p) returned %d!\n", + __func__, isci_dev, status); + + return TMF_RESP_FUNC_FAILED; + } + if (isci_host != NULL) + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + /* Stop I/O to the remote device. */ + isci_device_set_host_quiesce_lock_state(isci_dev, true); + + /* Make sure all pending requests are able to be fully terminated. */ + isci_device_clear_reset_pending(isci_dev); + + /* Terminate in-progress I/O now. */ + isci_remote_device_nuke_requests(isci_dev); + + /* Call into the libsas default handler (which calls sas_phy_reset). */ + base_status = sas_eh_bus_reset_handler(cmd); + + if (base_status != SUCCESS) { + + /* There can be cases where the resets to individual devices + * behind an expander will fail because of an unplug of the + * expander itself. + */ + scmd_printk(KERN_WARNING, cmd, + "%s: sas_eh_bus_reset_handler(%p) returned %d!\n", + __func__, cmd, base_status); + } + + /* WHAT TO DO HERE IF sas_phy_reset FAILS? */ + + if (isci_host != NULL) + spin_lock_irqsave(&isci_host->scic_lock, flags); + status + = scic_remote_device_reset_complete(isci_dev->sci_device_handle); + + if (isci_host != NULL) + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + if (status != SCI_SUCCESS) { + scmd_printk(KERN_WARNING, cmd, + "%s: scic_remote_device_reset_complete(%p) " + "returned %d!\n", + __func__, isci_dev, status); + } + /* WHAT TO DO HERE IF scic_remote_device_reset_complete FAILS? */ + + dev_dbg(&cmd->device->sdev_gendev, + "%s: cmd %p, isci_dev %p complete.\n", + __func__, cmd, isci_dev); + + /* Resume I/O to the remote device. */ + isci_device_set_host_quiesce_lock_state(isci_dev, false); + + return TMF_RESP_FUNC_COMPLETE; +} diff --git a/drivers/scsi/isci/task.h b/drivers/scsi/isci/task.h new file mode 100644 index 000000000000..ced6a8be1b31 --- /dev/null +++ b/drivers/scsi/isci/task.h @@ -0,0 +1,368 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#if !defined(_ISCI_TASK_H_) +#define _ISCI_TASK_H_ + +struct isci_request; +struct isci_host; + +/** + * enum isci_tmf_cb_state - This enum defines the possible states in which the + * TMF callback function is invoked during the TMF execution process. + * + * + */ +enum isci_tmf_cb_state { + + isci_tmf_init_state = 0, + isci_tmf_started, + isci_tmf_timed_out +}; + +/** + * enum isci_tmf_function_codes - This enum defines the possible preparations + * of task management requests. + * + * + */ +enum isci_tmf_function_codes { + + isci_tmf_func_none = 0, + isci_tmf_ssp_task_abort = TMF_ABORT_TASK, + isci_tmf_ssp_lun_reset = TMF_LU_RESET, + isci_tmf_sata_srst_high = TMF_LU_RESET + 0x100, /* Non SCSI */ + isci_tmf_sata_srst_low = TMF_LU_RESET + 0x101 /* Non SCSI */ +}; +/** + * struct isci_tmf - This class represents the task management object which + * acts as an interface to libsas for processing task management requests + * + * + */ +struct isci_tmf { + + struct completion *complete; + enum sas_protocol proto; + union { + struct sci_ssp_response_iu resp_iu; + struct dev_to_host_fis d2h_fis; + } resp; + unsigned char lun[8]; + u16 io_tag; + struct isci_remote_device *device; + enum isci_tmf_function_codes tmf_code; + int status; + + struct isci_timer *timeout_timer; + + /* The optional callback function allows the user process to + * track the TMF transmit / timeout conditions. + */ + void (*cb_state_func)( + enum isci_tmf_cb_state, + struct isci_tmf *, void *); + void *cb_data; + +}; + +static inline void isci_print_tmf( + struct isci_tmf *tmf) +{ + if (SAS_PROTOCOL_SATA == tmf->proto) + dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev, + "%s: status = %x\n" + "tmf->resp.d2h_fis.status = %x\n" + "tmf->resp.d2h_fis.error = %x\n", + __func__, + tmf->status, + tmf->resp.d2h_fis.status, + tmf->resp.d2h_fis.error); + else + dev_dbg(&tmf->device->isci_port->isci_host->pdev->dev, + "%s: status = %x\n" + "tmf->resp.resp_iu.data_present = %x\n" + "tmf->resp.resp_iu.status = %x\n" + "tmf->resp.resp_iu.data_length = %x\n" + "tmf->resp.resp_iu.data[0] = %x\n" + "tmf->resp.resp_iu.data[1] = %x\n" + "tmf->resp.resp_iu.data[2] = %x\n" + "tmf->resp.resp_iu.data[3] = %x\n", + __func__, + tmf->status, + tmf->resp.resp_iu.data_present, + tmf->resp.resp_iu.status, + (tmf->resp.resp_iu.response_data_length[0] << 24) + + (tmf->resp.resp_iu.response_data_length[1] << 16) + + (tmf->resp.resp_iu.response_data_length[2] << 8) + + tmf->resp.resp_iu.response_data_length[3], + tmf->resp.resp_iu.data[0], + tmf->resp.resp_iu.data[1], + tmf->resp.resp_iu.data[2], + tmf->resp.resp_iu.data[3]); +} + + +int isci_task_execute_task( + struct sas_task *task, + int num, + gfp_t gfp_flags); + +int isci_task_abort_task( + struct sas_task *task); + +int isci_task_abort_task_set( + struct domain_device *d_device, + u8 *lun); + +int isci_task_clear_aca( + struct domain_device *d_device, + u8 *lun); + +int isci_task_clear_task_set( + struct domain_device *d_device, + u8 *lun); + +int isci_task_query_task( + struct sas_task *task); + +int isci_task_lu_reset( + struct domain_device *d_device, + u8 *lun); + +int isci_task_clear_nexus_port( + struct asd_sas_port *port); + +int isci_task_clear_nexus_ha( + struct sas_ha_struct *ha); + +int isci_task_I_T_nexus_reset( + struct domain_device *d_device); + +void isci_task_request_complete( + struct isci_host *isci_host, + struct isci_request *request, + enum sci_task_status completion_status); + +u16 isci_task_ssp_request_get_io_tag_to_manage( + struct isci_request *request); + +u8 isci_task_ssp_request_get_function( + struct isci_request *request); + +u32 isci_task_ssp_request_get_lun( + struct isci_request *request); + +void *isci_task_ssp_request_get_response_data_address( + struct isci_request *request); + +u32 isci_task_ssp_request_get_response_data_length( + struct isci_request *request); + +int isci_queuecommand( + struct scsi_cmnd *scsi_cmd, + void (*donefunc)(struct scsi_cmnd *)); + +int isci_bus_reset_handler(struct scsi_cmnd *cmd); + +void isci_task_build_tmf( + struct isci_tmf *tmf, + struct isci_remote_device *isci_device, + enum isci_tmf_function_codes code, + void (*tmf_sent_cb)( + enum isci_tmf_cb_state, + struct isci_tmf *, void *), + void *cb_data); + +int isci_task_execute_tmf( + struct isci_host *isci_host, + struct isci_tmf *tmf, + unsigned long timeout_ms); + +/** + * enum isci_completion_selection - This enum defines the possible actions to + * take with respect to a given request's notification back to libsas. + * + * + */ +enum isci_completion_selection { + + isci_perform_normal_io_completion, /* Normal notify (task_done) */ + isci_perform_aborted_io_completion, /* No notification. */ + isci_perform_error_io_completion /* Use sas_task_abort */ +}; + +static inline void isci_set_task_doneflags( + struct sas_task *task) +{ + /* Since no futher action will be taken on this task, + * make sure to mark it complete from the lldd perspective. + */ + task->task_state_flags |= SAS_TASK_STATE_DONE; + task->task_state_flags &= ~SAS_TASK_AT_INITIATOR; + task->task_state_flags &= ~SAS_TASK_STATE_PENDING; +} +/** + * isci_task_all_done() - This function clears the task bits to indicate the + * LLDD is done with the task. + * + * + */ +static inline void isci_task_all_done( + struct sas_task *task) +{ + unsigned long flags; + + /* Since no futher action will be taken on this task, + * make sure to mark it complete from the lldd perspective. + */ + spin_lock_irqsave(&task->task_state_lock, flags); + isci_set_task_doneflags(task); + spin_unlock_irqrestore(&task->task_state_lock, flags); +} + +/** + * isci_task_set_completion_status() - This function sets the completion status + * for the request. + * @task: This parameter is the completed request. + * @response: This parameter is the response code for the completed task. + * @status: This parameter is the status code for the completed task. + * + * none. + */ +static inline void isci_task_set_completion_status( + struct sas_task *task, + enum service_response response, + enum exec_status status, + enum isci_completion_selection task_notification_selection) +{ + unsigned long flags; + + spin_lock_irqsave(&task->task_state_lock, flags); + + task->task_status.resp = response; + task->task_status.stat = status; + + /* Don't set DONE (or clear AT_INITIATOR) for any task going into the + * error path, because the EH interprets that as a handled error condition. + * Also don't take action if there is a reset pending. + */ + if ((task_notification_selection != isci_perform_error_io_completion) + && !(task->task_state_flags & SAS_TASK_NEED_DEV_RESET)) + isci_set_task_doneflags(task); + + spin_unlock_irqrestore(&task->task_state_lock, flags); +} +/** + * isci_task_complete_for_upper_layer() - This function completes the request + * to the upper layer driver. + * @host: This parameter is a pointer to the host on which the the request + * should be queued (either as an error or success). + * @request: This parameter is the completed request. + * @response: This parameter is the response code for the completed task. + * @status: This parameter is the status code for the completed task. + * + * none. + */ +static inline void isci_task_complete_for_upper_layer( + struct sas_task *task, + enum service_response response, + enum exec_status status, + enum isci_completion_selection task_notification_selection) +{ + isci_task_set_completion_status(task, response, status, + task_notification_selection); + + + /* Tasks aborted specifically by a call to the lldd_abort_task + * function should not be completed to the host in the regular path. + */ + switch (task_notification_selection) { + case isci_perform_normal_io_completion: + /* Normal notification (task_done) */ + dev_dbg(task->dev->port->ha->dev, + "%s: Normal - task = %p, response=%d, status=%d\n", + __func__, task, response, status); + task->task_done(task); + task->lldd_task = NULL; + break; + + case isci_perform_aborted_io_completion: + /* No notification because this request is already in the + * abort path. + */ + dev_warn(task->dev->port->ha->dev, + "%s: Aborted - task = %p, response=%d, status=%d\n", + __func__, task, response, status); + break; + + case isci_perform_error_io_completion: + /* Use sas_task_abort */ + dev_warn(task->dev->port->ha->dev, + "%s: Error - task = %p, response=%d, status=%d\n", + __func__, task, response, status); + sas_task_abort(task); + break; + + default: + dev_warn(task->dev->port->ha->dev, + "%s: isci task notification default case!", + __func__); + sas_task_abort(task); + break; + } +} + +#endif /* !defined(_SCI_TASK_H_) */ diff --git a/drivers/scsi/isci/timers.c b/drivers/scsi/isci/timers.c new file mode 100644 index 000000000000..ca723089ee88 --- /dev/null +++ b/drivers/scsi/isci/timers.c @@ -0,0 +1,319 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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 "isci.h" +#include "timers.h" + + +/** + * isci_timer_list_construct() - This method contrucst the SCI Timer List + * object used by the SCI Module class. The construction process involves + * creating isci_timer objects and adding them to the SCI Timer List + * object's list member. The number of isci_timer objects is determined by + * the timer_list_size parameter. + * @isci_timer_list: This parameter points to the SCI Timer List object being + * constructed. The calling routine is responsible for allocating the memory + * for isci_timer_list and initializing the timer list_head member of + * isci_timer_list. + * @timer_list_size: This parameter specifies the number of isci_timer objects + * contained by the SCI Timer List. which this timer is to be associated. + * + * This method returns an error code indicating sucess or failure. The user + * should check for possible memory allocation error return otherwise, a zero + * indicates success. + */ +int isci_timer_list_construct( + struct isci_timer_list *isci_timer_list, + int timer_list_size) +{ + struct isci_timer *isci_timer; + int i; + int err = 0; + + + for (i = 0; i < timer_list_size; i++) { + + isci_timer = kzalloc(sizeof(*isci_timer), GFP_KERNEL); + + if (!isci_timer) { + + err = -ENOMEM; + break; + } + isci_timer->used = 0; + isci_timer->stopped = 1; + isci_timer->parent = isci_timer_list; + list_add(&isci_timer->node, &isci_timer_list->timers); + } + + return 0; + +} + + +/** + * isci_timer_list_destroy() - This method destroys the SCI Timer List object + * used by the SCI Module class. The destruction process involves freeing + * memory allocated for isci_timer objects on the SCI Timer List object's + * timers list_head member. If any isci_timer objects are mark as "in use", + * they are not freed and the function returns an error code of -EBUSY. + * @isci_timer_list: This parameter points to the SCI Timer List object being + * destroyed. + * + * This method returns an error code indicating sucess or failure. The user + * should check for possible -EBUSY error return, in the event of one or more + * isci_timers still "in use", otherwise, a zero indicates success. + */ +int isci_timer_list_destroy( + struct isci_timer_list *isci_timer_list) +{ + struct isci_timer *timer, *tmp; + + list_for_each_entry_safe(timer, tmp, &isci_timer_list->timers, node) { + isci_timer_free(isci_timer_list, timer); + list_del(&timer->node); + kfree(timer); + } + return 0; +} + + + +static void isci_timer_restart(struct isci_timer *isci_timer) +{ + struct timer_list *timer = + &isci_timer->timer; + unsigned long timeout; + + dev_dbg(&isci_timer->isci_host->pdev->dev, + "%s: isci_timer = %p\n", __func__, isci_timer); + + isci_timer->restart = 0; + isci_timer->stopped = 0; + timeout = isci_timer->timeout_value; + timeout = (timeout * HZ) / 1000; + timeout = timeout ? timeout : 1; + mod_timer(timer, jiffies + timeout); +} + +/** + * This method pulls an isci_timer object off of the list for the SCI Timer + * List object specified, marks the isci_timer as "in use" and initializes + * it with user callback function and cookie data. The timer is not start at + * this time, just reserved for the user. + * @isci_timer_list: This parameter points to the SCI Timer List from which the + * timer is reserved. + * @cookie: This parameter specifies a piece of information that the user must + * retain. This cookie is to be supplied by the user anytime a timeout + * occurs for the created timer. + * @timer_callback: This parameter specifies the callback method to be invoked + * whenever the timer expires. + * + * This method returns a pointer to an isci_timer object reserved from the SCI + * Timer List. The pointer will be utilized for all further interactions + * relating to this timer. + */ + +static void timer_function(unsigned long data) +{ + + struct isci_timer *timer = (struct isci_timer *)data; + struct isci_host *isci_host = timer->isci_host; + unsigned long flags; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_timer = %p\n", __func__, timer); + + if (isci_stopped == isci_host_get_state(isci_host)) { + timer->stopped = 1; + return; + } + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + if (!timer->stopped) { + timer->stopped = 1; + timer->timer_callback(timer->cookie); + + if (timer->restart) + isci_timer_restart(timer); + } + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); +} + + +struct isci_timer *isci_timer_create( + struct isci_timer_list *isci_timer_list, + struct isci_host *isci_host, + void *cookie, + void (*timer_callback)(void *)) +{ + + struct timer_list *timer; + struct isci_timer *isci_timer; + struct list_head *timer_list = + &isci_timer_list->timers; + unsigned long flags; + + spin_lock_irqsave(&isci_host->scic_lock, flags); + + if (list_empty(timer_list)) { + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + return NULL; + } + + isci_timer = list_entry(timer_list->next, struct isci_timer, node); + + if (isci_timer->used) { + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + return NULL; + } + isci_timer->used = 1; + isci_timer->stopped = 1; + list_move_tail(&isci_timer->node, timer_list); + + spin_unlock_irqrestore(&isci_host->scic_lock, flags); + + timer = &isci_timer->timer; + timer->data = (unsigned long)isci_timer; + timer->function = timer_function; + isci_timer->cookie = cookie; + isci_timer->timer_callback = timer_callback; + isci_timer->isci_host = isci_host; + + dev_dbg(&isci_host->pdev->dev, + "%s: isci_timer = %p\n", __func__, isci_timer); + + return isci_timer; +} + +/** + * isci_timer_free() - This method frees the isci_timer, marking it "free to + * use", then places its back at the head of the timers list for the SCI + * Timer List object specified. + * @isci_timer_list: This parameter points to the SCI Timer List from which the + * timer is reserved. + * @isci_timer: This parameter specifies the timer to be freed. + * + */ +void isci_timer_free( + struct isci_timer_list *isci_timer_list, + struct isci_timer *isci_timer) +{ + struct list_head *timer_list = &isci_timer_list->timers; + + dev_dbg(&isci_timer->isci_host->pdev->dev, + "%s: isci_timer = %p\n", __func__, isci_timer); + + if (list_empty(timer_list)) + return; + + isci_timer->used = 0; + list_move(&isci_timer->node, timer_list); + + if (!isci_timer->stopped) { + del_timer(&isci_timer->timer); + isci_timer->stopped = 1; + } +} + +/** + * isci_timer_start() - This method starts the specified isci_timer, with the + * specified timeout value. + * @isci_timer: This parameter specifies the timer to be started. + * @timeout: This parameter specifies the timeout, in milliseconds, after which + * the associated callback function will be called. + * + */ +void isci_timer_start( + struct isci_timer *isci_timer, + unsigned long timeout) +{ + struct timer_list *timer = &isci_timer->timer; + + dev_dbg(&isci_timer->isci_host->pdev->dev, + "%s: isci_timer = %p\n", __func__, isci_timer); + + isci_timer->timeout_value = timeout; + init_timer(timer); + timeout = (timeout * HZ) / 1000; + timeout = timeout ? timeout : 1; + + timer->expires = jiffies + timeout; + timer->data = (unsigned long)isci_timer; + timer->function = timer_function; + isci_timer->stopped = 0; + isci_timer->restart = 0; + add_timer(timer); +} + +/** + * isci_timer_stop() - This method stops the supplied isci_timer. + * @isci_timer: This parameter specifies the isci_timer to be stopped. + * + */ +void isci_timer_stop(struct isci_timer *isci_timer) +{ + dev_dbg(&isci_timer->isci_host->pdev->dev, + "%s: isci_timer = %p\n", __func__, isci_timer); + + if (isci_timer->stopped) + return; + + isci_timer->stopped = 1; + + del_timer(&isci_timer->timer); +} diff --git a/drivers/scsi/isci/timers.h b/drivers/scsi/isci/timers.h new file mode 100644 index 000000000000..ca5c2159a1c2 --- /dev/null +++ b/drivers/scsi/isci/timers.h @@ -0,0 +1,126 @@ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License 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. + * + * 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. + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * BSD LICENSE + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "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 COPYRIGHT + * OWNER OR CONTRIBUTORS 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. + */ + +#if !defined(_SCI_TIMER_H_) +#define _SCI_TIMER_H_ + +#include <linux/timer.h> +#include <linux/types.h> + +/*************************************************** + * isci_timer + *************************************************** + */ +/** + * struct isci_timer_list - This class is the container for all isci_timer + * objects + * + * + */ +struct isci_timer_list { + + struct list_head timers; +}; + +/** + * struct isci_timer - This class represents the timer object used by SCIC. It + * wraps the Linux timer_list object. + * + * + */ +struct isci_timer { + int used; + int stopped; + int restart; + int timeout_value; + void *cookie; + void (*timer_callback)(void *); + struct list_head node; + struct timer_list timer; + struct isci_timer_list *parent; + struct isci_host *isci_host; +}; + +#define to_isci_timer(t) \ + container_of(t, struct isci_timer, timer); + +int isci_timer_list_construct( + struct isci_timer_list *isci_timer_list, + int timer_list_size); + + +int isci_timer_list_destroy( + struct isci_timer_list *isci_timer_list); + +struct isci_timer *isci_timer_create( + struct isci_timer_list *isci_timer_list, + struct isci_host *isci_host, + void *cookie, + void (*timer_callback)(void *)); + +void isci_timer_free( + struct isci_timer_list *isci_timer_list, + struct isci_timer *isci_timer); + +void isci_timer_start( + struct isci_timer *isci_timer, + unsigned long timeout); + +void isci_timer_stop( + struct isci_timer *isci_timer); + + +#endif /* !defined (_SCI_TIMER_H_) */ + |