/*- * Copyright (c) 2025, Samsung Electronics Co., Ltd. * Written by Jaeyoon Choi * * SPDX-License-Identifier: BSD-2-Clause */ #ifndef __UFSHCI_H__ #define __UFSHCI_H__ #include #include /* * Note: This driver currently assumes a little-endian architecture. * Big-endian support is not yet implemented. */ /* MIPI UniPro spec 2.0, section 5.8.1 "PHY Adapter Common Attributes" */ #define PA_AvailTxDataLanes 0x1520 #define PA_AvailRxDataLanes 0x1540 /* * MIPI UniPro spec 2.0, section 5.8.2 "PHY Adapter M-PHY-Specific * Attributes" */ #define PA_ConnectedTxDataLanes 0x1561 #define PA_ConnectedRxDataLanes 0x1581 #define PA_MaxRxHSGear 0x1587 #define PA_Granularity 0x15AA #define PA_TActivate 0x15A8 #define PA_RemoteVerInfo 0x15A0 #define PA_LocalVerInfo 0x15A9 /* UFSHCI spec 4.1, section 7.4 "UIC Power Mode Change" */ #define PA_ActiveTxDataLanes 0x1560 #define PA_ActiveRxDataLanes 0x1580 #define PA_TxGear 0x1568 #define PA_RxGear 0x1583 #define PA_TxTermination 0x1569 #define PA_RxTermination 0x1584 #define PA_HSSeries 0x156A #define PA_PWRModeUserData0 0x15B0 #define PA_PWRModeUserData1 0x15B1 #define PA_PWRModeUserData2 0x15B2 #define PA_PWRModeUserData3 0x15B3 #define PA_PWRModeUserData4 0x15B4 #define PA_PWRModeUserData5 0x15B5 #define PA_TxHsAdaptType 0x15D4 #define PA_PWRMode 0x1571 #define DME_LocalFC0ProtectionTimeOutVal 0xD041 #define DME_LocalTC0ReplayTimeOutVal 0xD042 #define DME_LocalAFC0ReqTimeOutVal 0xD043 /* Currently, UFS uses TC0 only. */ #define DL_FC0ProtectionTimeOutVal_Default 8191 #define DL_TC0ReplayTimeOutVal_Default 65535 #define DL_AFC0ReqTimeOutVal_Default 32767 /* UFS Spec 4.1, section 6.4 "Reference Clock" */ enum ufshci_attribute_reference_clock { UFSHCI_REF_CLK_19_2MHz = 0x0, UFSHCI_REF_CLK_26MHz = 0x1, UFSHCI_REF_CLK_38_4MHz = 0x2, UFSHCI_REF_CLK_OBSOLETE = 0x3, }; /* UFS spec 4.1, section 9 "UFS UIC Layer: MIPI Unipro" */ enum ufshci_uic_cmd_opcode { /* Configuration */ UFSHCI_DME_GET = 0x01, UFSHCI_DME_SET = 0x02, UFSHCI_DME_PEER_GET = 0x03, UFSHCI_DME_PEER_SET = 0x04, /* Controll */ UFSHCI_DME_POWER_ON = 0x10, UFSHCI_DME_POWER_OFF = 0x11, UFSHCI_DME_ENABLE = 0x12, UFSHCI_DME_RESET = 0x14, UFSHCI_DME_ENDPOINT_RESET = 0x15, UFSHCI_DME_LINK_STARTUP = 0x16, UFSHCI_DME_HIBERNATE_ENTER = 0x17, UFSHCI_DME_HIBERNATE_EXIT = 0x18, UFSHCI_DME_TEST_MODE = 0x1a, }; /* UFSHCI spec 4.1, section 5.6.3 "Offset 98h: UICCMDARG2 – UIC Command * Argument" */ enum ufshci_uic_cmd_attr_set_type { UFSHCI_ATTR_SET_TYPE_NORMAL = 0, /* volatile value */ UFSHCI_ATTR_SET_TYPE_STATIC = 1, /* non-volatile reset value */ }; struct ufshci_uic_cmd { uint8_t opcode; uint32_t argument1; uint32_t argument2; uint32_t argument3; }; /* UFS spec 4.1, section 10.5 "UPIU Transactions" */ enum transaction_code { UFSHCI_UPIU_TRANSACTION_CODE_NOP_OUT = 0x00, UFSHCI_UPIU_TRANSACTION_CODE_COMMAND = 0x01, UFSHCI_UPIU_TRANSACTION_CODE_DATA_OUT = 0x02, UFSHCI_UPIU_TRANSACTION_CODE_TASK_MANAGEMENT_REQUEST = 0x04, UFSHCI_UPIU_TRANSACTION_CODE_QUERY_REQUEST = 0x16, UFSHCI_UPIU_TRANSACTION_CODE_NOP_IN = 0x20, UFSHCI_UPIU_TRANSACTION_CODE_RESPONSE = 0x21, UFSHCI_UPIU_TRANSACTION_CODE_DATA_IN = 0x22, UFSHCI_UPIU_TRANSACTION_CODE_TASK_MANAGEMENT_RESPONSE = 0x24, UFSHCI_UPIU_TRANSACTION_CODE_READY_TO_TRANSFER = 0x31, UFSHCI_UPIU_TRANSACTION_CODE_QUERY_RESPONSE = 0x36, UFSHCI_UPIU_TRANSACTION_CODE_REJECT_UPIU = 0x3f, }; enum overall_command_status { UFSHCI_DESC_SUCCESS = 0x0, UFSHCI_DESC_INVALID_COMMAND_TABLE_ATTRIBUTES = 0x01, UFSHCI_DESC_INVALID_PRDT_ATTRIBUTES = 0x02, UFSHCI_DESC_MISMATCH_DATA_BUFFER_SIZE = 0x03, UFSHCI_DESC_MISMATCH_RESPONSE_UPIU_SIZE = 0x04, UFSHCI_DESC_COMMUNICATION_FAILURE_WITHIN_UIC_LAYERS = 0x05, UFSHCI_DESC_ABORTED = 0x06, UFSHCI_DESC_HOST_CONTROLLER_FATAL_ERROR = 0x07, UFSHCI_DESC_DEVICEFATALERROR = 0x08, UFSHCI_DESC_INVALID_CRYPTO_CONFIGURATION = 0x09, UFSHCI_DESC_GENERAL_CRYPTO_ERROR = 0x0A, UFSHCI_DESC_INVALID = 0x0F, }; enum response_code { UFSHCI_RESPONSE_CODE_TARGET_SUCCESS = 0x00, UFSHCI_RESPONSE_CODE_TARGET_FAILURE = 0x01, UFSHCI_RESPONSE_CODE_PARAMETER_NOTREADABLE = 0xF6, UFSHCI_RESPONSE_CODE_PARAMETER_NOTWRITEABLE = 0xF7, UFSHCI_RESPONSE_CODE_PARAMETER_ALREADYWRITTEN = 0xF8, UFSHCI_RESPONSE_CODE_INVALID_LENGTH = 0xF9, UFSHCI_RESPONSE_CODE_INVALID_VALUE = 0xFA, UFSHCI_RESPONSE_CODE_INVALID_SELECTOR = 0xFB, UFSHCI_RESPONSE_CODE_INVALID_INDEX = 0xFC, UFSHCI_RESPONSE_CODE_INVALID_IDN = 0xFD, UFSHCI_RESPONSE_CODE_INVALID_OPCODE = 0xFE, UFSHCI_RESPONSE_CODE_GENERAL_FAILURE = 0xFF, }; /* UFSHCI spec 4.1, section 6.1.1 "UTP Transfer Request Descriptor" */ enum ufshci_command_type { UFSHCI_COMMAND_TYPE_UFS_STORAGE = 0x01, UFSHCI_COMMAND_TYPE_NULLIFIED_UTRD = 0x0F, }; enum ufshci_data_direction { UFSHCI_DATA_DIRECTION_NO_DATA_TRANSFER = 0x00, UFSHCI_DATA_DIRECTION_FROM_SYS_TO_TGT = 0x01, UFSHCI_DATA_DIRECTION_FROM_TGT_TO_SYS = 0x10, UFSHCI_DATA_DIRECTION_RESERVED = 0b11, }; enum ufshci_utr_overall_command_status { UFSHCI_UTR_OCS_SUCCESS = 0x0, UFSHCI_UTR_OCS_INVALID_COMMAND_TABLE_ATTRIBUTES = 0x01, UFSHCI_UTR_OCS_INVALID_PRDT_ATTRIBUTES = 0x02, UFSHCI_UTR_OCS_MISMATCH_DATA_BUFFER_SIZE = 0x03, UFSHCI_UTR_OCS_MISMATCH_RESPONSE_UPIU_SIZE = 0x04, UFSHCI_UTR_OCS_COMMUNICATION_FAILURE_WITHIN_UIC_LAYERS = 0x05, UFSHCI_UTR_OCS_ABORTED = 0x06, UFSHCI_UTR_OCS_HOST_CONTROLLER_FATAL_ERROR = 0x07, UFSHCI_UTR_OCS_DEVICE_FATAL_ERROR = 0x08, UFSHCI_UTR_OCS_INVALID_CRYPTO_CONFIGURATION = 0x09, UFSHCI_UTR_OCS_GENERAL_CRYPTO_ERROR = 0x0A, UFSHCI_UTR_OCS_INVALID = 0xF, }; struct ufshci_utp_xfer_req_desc { /* dword 0 */ uint32_t cci : 8; /* [7:0] */ uint32_t total_ehs_length : 8; /* [15:8] */ uint32_t reserved0 : 7; /* [22:16] */ uint32_t ce : 1; /* [23] */ uint32_t interrupt : 1; /* [24] */ uint32_t data_direction : 2; /* [26:25] */ uint32_t reserved1 : 1; /* [27] */ uint32_t command_type : 4; /* [31:28] */ /* dword 1 */ uint32_t data_unit_number_lower; /* [31:0] */ /* dword 2 */ uint8_t overall_command_status; /* [7:0] */ uint8_t common_data_size; /* [15:8] */ uint16_t last_data_byte_count; /* [31:16] */ /* dword 3 */ uint32_t data_unit_number_upper; /* [31:0] */ /* dword 4 */ uint32_t utp_command_descriptor_base_address; /* [31:0] */ /* dword 5 */ uint32_t utp_command_descriptor_base_address_upper; /* [31:0] */ /* dword 6 */ uint16_t response_upiu_length; /* [15:0] */ uint16_t response_upiu_offset; /* [31:16] */ /* dword 7 */ uint16_t prdt_length; /* [15:0] */ uint16_t prdt_offset; /* [31:16] */ } __packed __aligned(8); _Static_assert(sizeof(struct ufshci_utp_xfer_req_desc) == 32, "ufshci_utp_xfer_req_desc must be 32 bytes"); /* * According to the UFSHCI specification, the size of the UTP command * descriptor is as follows. The size of the transfer request is not limited, * a transfer response can be as long as 65535 * dwords, and a PRDT can be as * long as 65565 * PRDT entry size(16 bytes). However, for ease of use, this * UFSHCI Driver imposes the following limits. The size of the transfer * request and the transfer response is 1024 bytes or less. The PRDT region * limits the number of scatter gathers to 256 + 1, using a total of 4096 + * 16 bytes. Therefore, only 8KB size is allocated for the UTP command * descriptor. */ #define UFSHCI_UTP_COMMAND_DESCRIPTOR_SIZE 8192 #define UFSHCI_UTP_XFER_REQ_SIZE 512 #define UFSHCI_UTP_XFER_RESP_SIZE 512 /* * To reduce the size of the UTP Command Descriptor(8KB), we must use only * 256 + 1 PRDT entries. The reason for adding the 1 is that if the data is * not aligned, one additional PRDT_ENTRY is used. */ #define UFSHCI_MAX_PRDT_ENTRY_COUNT (256 + 1) /* UFSHCI spec 4.1, section 6.1.2 "UTP Command Descriptor" */ struct ufshci_prdt_entry { /* dword 0 */ uint32_t data_base_address; /* [31:0] */ /* dword 1 */ uint32_t data_base_address_upper; /* [31:0] */ /* dword 2 */ uint32_t reserved; /* [31:0] */ /* dword 3 */ uint32_t data_byte_count; /* [17:0] Maximum byte * count is 256KB */ } __packed __aligned(8); _Static_assert(sizeof(struct ufshci_prdt_entry) == 16, "ufshci_prdt_entry must be 16 bytes"); struct ufshci_utp_cmd_desc { uint8_t command_upiu[UFSHCI_UTP_XFER_REQ_SIZE]; uint8_t response_upiu[UFSHCI_UTP_XFER_RESP_SIZE]; uint8_t prd_table[sizeof(struct ufshci_prdt_entry) * UFSHCI_MAX_PRDT_ENTRY_COUNT]; uint8_t padding[3072 - sizeof(struct ufshci_prdt_entry)]; } __packed __aligned(128); _Static_assert(sizeof(struct ufshci_utp_cmd_desc) == UFSHCI_UTP_COMMAND_DESCRIPTOR_SIZE, "ufshci_utp_cmd_desc must be 8192 bytes"); #define UFSHCI_UTP_TASK_MGMT_REQ_SIZE 32 #define UFSHCI_UTP_TASK_MGMT_RESP_SIZE 32 enum ufshci_utmr_overall_command_status { UFSHCI_UTMR_OCS_SUCCESS = 0x0, UFSHCI_UTMR_OCS_INVALID_TASK_MANAGEMENT_FUNCTION_ATTRIBUTES = 0x01, UFSHCI_UTMR_OCS_MISMATCH_TASK_MANAGEMENT_REQUEST_SIZE = 0x02, UFSHCI_UTMR_OCS_MISMATCH_TASK_MANAGEMENT_RESPONSE_SIZE = 0x03, UFSHCI_UTMR_OCS_PEER_COMMUNICATION_FAILURE = 0x04, UFSHCI_UTMR_OCS_ABORTED = 0x05, UFSHCI_UTMR_OCS_FATAL_ERROR = 0x06, UFSHCI_UTMR_OCS_DEVICE_FATAL_ERROR = 0x07, UFSHCI_UTMR_OCS_INVALID = 0xF, }; /* UFSHCI spec 4.1, section 6.3.1 "UTP Task Management Request Descriptor" */ struct ufshci_utp_task_mgmt_req_desc { /* dword 0 */ uint32_t reserved0 : 24; /* [23:0] */ uint32_t interrupt : 1; /* [24] */ uint32_t reserved1 : 7; /* [31:25] */ /* dword 1 */ uint32_t reserved2; /* [31:0] */ /* dword 2 */ uint8_t overall_command_status; /* [7:0] */ uint8_t reserved3; /* [15:8] */ uint16_t reserved4; /* [31:16] */ /* dword 3 */ uint32_t reserved5; /* [31:0] */ /* dword 4-11 */ uint8_t request_upiu[UFSHCI_UTP_TASK_MGMT_REQ_SIZE]; /* dword 12-19 */ uint8_t response_upiu[UFSHCI_UTP_TASK_MGMT_RESP_SIZE]; } __packed __aligned(8); _Static_assert(sizeof(struct ufshci_utp_task_mgmt_req_desc) == 80, "ufshci_utp_task_mgmt_req_desc must be 80 bytes"); /* UFS spec 4.1, section 10.6.2 "Basic Header Format" */ struct ufshci_upiu_header { /* dword 0 */ union { struct { uint8_t trans_code : 6; /* [5:0] */ uint8_t dd : 1; /* [6] */ uint8_t hd : 1; /* [7] */ }; uint8_t trans_type; }; union { struct { uint8_t task_attribute : 2; /* [1:0] */ uint8_t cp : 1; /* [2] */ uint8_t retransmit_indicator : 1; /* [3] */ #define UFSHCI_OPERATIONAL_FLAG_W 0x2 #define UFSHCI_OPERATIONAL_FLAG_R 0x4 uint8_t operational_flags : 4; /* [7:4] */ }; uint8_t flags; }; uint8_t lun; #define UFSHCI_UPIU_UNIT_NUMBER_ID_MASK 0x7f #define UFSHCI_UPIU_WLUN_ID_MASK 0x80 uint8_t task_tag; /* dword 1 */ #define UFSHCI_COMMAND_SET_TYPE_SCSI 0 uint8_t cmd_set_type : 4; /* [3:0] */ uint8_t iid : 4; /* [7:4] */ uint8_t ext_iid_or_function; uint8_t response; uint8_t ext_iid_or_status; /* dword 2 */ uint8_t ehs_length; uint8_t device_infomation; uint16_t data_segment_length; /* (Big-endian) */ } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_upiu_header) == 12, "ufshci_upiu_header must be 12 bytes"); #define UFSHCI_MAX_UPIU_SIZE 512 #define UFSHCI_UPIU_ALIGNMENT 8 /* UPIU requires 64-bit alignment. */ struct ufshci_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3-127 */ uint8_t reserved[UFSHCI_MAX_UPIU_SIZE - sizeof(struct ufshci_upiu_header)]; } __packed __aligned(8); _Static_assert(sizeof(struct ufshci_upiu) == 512, "ufshci_upiu must be 512 bytes"); /* UFS Spec 4.1, section 10.7.1 "COMMAND UPIU" */ struct ufshci_cmd_command_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3 */ uint32_t expected_data_transfer_length; /* (Big-endian) */ /* dword 4-7 */ uint8_t cdb[16]; } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_cmd_command_upiu) == 32, "bad size for ufshci_cmd_command_upiu"); _Static_assert(sizeof(struct ufshci_cmd_command_upiu) <= UFSHCI_UTP_XFER_REQ_SIZE, "bad size for ufshci_cmd_command_upiu"); _Static_assert(sizeof(struct ufshci_cmd_command_upiu) % UFSHCI_UPIU_ALIGNMENT == 0, "UPIU requires 64-bit alignment"); /* UFS Spec 4.1, section 10.7.2 "RESPONSE UPIU" */ struct ufshci_cmd_response_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3 */ uint32_t residual_transfer_count; /* (Big-endian) */ /* dword 4-7 */ uint8_t reserved[16]; /* Sense Data */ uint16_t sense_data_len; /* (Big-endian) */ uint8_t sense_data[18]; /* Add padding to align the kUpiuAlignment. */ uint8_t padding[4]; } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_cmd_response_upiu) == 56, "bad size for ufshci_cmd_response_upiu"); _Static_assert(sizeof(struct ufshci_cmd_response_upiu) <= UFSHCI_UTP_XFER_RESP_SIZE, "bad size for ufshci_cmd_response_upiu"); _Static_assert(sizeof(struct ufshci_cmd_response_upiu) % UFSHCI_UPIU_ALIGNMENT == 0, "UPIU requires 64-bit alignment"); enum task_management_function { UFSHCI_TASK_MGMT_FUNCTION_ABORT_TASK = 0x01, UFSHCI_TASK_MGMT_FUNCTION_ABORT_TASK_SET = 0x02, UFSHCI_TASK_MGMT_FUNCTION_CLEAR_TASK_SET = 0x04, UFSHCI_TASK_MGMT_FUNCTION_LOGICAL_UNIT_RESET = 0x08, UFSHCI_TASK_MGMT_FUNCTION_QUERY_TASK = 0x80, UFSHCI_TASK_MGMT_FUNCTION_QUERY_TASKSET = 0x81, }; /* UFS Spec 4.1, section 10.7.6 "TASK MANAGEMENT REQUEST UPIU" */ struct ufshci_task_mgmt_request_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3 */ uint32_t input_param1; /* (Big-endian) */ /* dword 4 */ uint32_t input_param2; /* (Big-endian) */ /* dword 5 */ uint32_t input_param3; /* (Big-endian) */ /* dword 6-7 */ uint8_t reserved[8]; } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_task_mgmt_request_upiu) == 32, "bad size for ufshci_task_mgmt_request_upiu"); _Static_assert(sizeof(struct ufshci_task_mgmt_request_upiu) <= UFSHCI_UTP_XFER_RESP_SIZE, "bad size for ufshci_task_mgmt_request_upiu"); _Static_assert(sizeof(struct ufshci_task_mgmt_request_upiu) % UFSHCI_UPIU_ALIGNMENT == 0, "UPIU requires 64-bit alignment"); enum task_management_service_response { UFSHCI_TASK_MGMT_SERVICE_RESPONSE_FUNCTION_COMPLETE = 0x00, UFSHCI_TASK_MGMT_SERVICE_RESPONSE_FUNCTION_NOT_SUPPORTED = 0x04, UFSHCI_TASK_MGMT_SERVICE_RESPONSE_FUNCTION_FAILED = 0x05, UFSHCI_TASK_MGMT_SERVICE_RESPONSE_FUNCTION_SUCCEEDED = 0x08, UFSHCI_TASK_MGMT_SERVICE_RESPONSE_INCORRECT_LUN = 0x09, }; /* UFS Spec 4.1, section 10.7.7 "TASK MANAGEMENT RESPONSE UPIU" */ struct ufshci_task_mgmt_response_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3 */ uint32_t output_param1; /* (Big-endian) */ /* dword 4 */ uint32_t output_param2; /* (Big-endian) */ /* dword 5-7 */ uint8_t reserved[12]; } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_task_mgmt_response_upiu) == 32, "bad size for ufshci_task_mgmt_response_upiu"); _Static_assert(sizeof(struct ufshci_task_mgmt_response_upiu) <= UFSHCI_UTP_XFER_RESP_SIZE, "bad size for ufshci_task_mgmt_response_upiu"); _Static_assert(sizeof(struct ufshci_task_mgmt_response_upiu) % UFSHCI_UPIU_ALIGNMENT == 0, "UPIU requires 64-bit alignment"); /* UFS Spec 4.1, section 10.7.8 "QUERY REQUEST UPIU" */ enum ufshci_query_function { UFSHCI_QUERY_FUNC_STANDARD_READ_REQUEST = 0x01, UFSHCI_QUERY_FUNC_STANDARD_WRITE_REQUEST = 0x81, }; enum ufshci_query_opcode { UFSHCI_QUERY_OPCODE_NOP = 0, UFSHCI_QUERY_OPCODE_READ_DESCRIPTOR, UFSHCI_QUERY_OPCODE_WRITE_DESCRIPTOR, UFSHCI_QUERY_OPCODE_READ_ATTRIBUTE, UFSHCI_QUERY_OPCODE_WRITE_ATTRIBUTE, UFSHCI_QUERY_OPCODE_READ_FLAG, UFSHCI_QUERY_OPCODE_SET_FLAG, UFSHCI_QUERY_OPCODE_CLEAR_FLAG, UFSHCI_QUERY_OPCODE_TOGGLE_FLAG, }; struct ufshci_query_param { enum ufshci_query_function function; enum ufshci_query_opcode opcode; uint8_t type; uint8_t index; uint8_t selector; uint64_t value; size_t desc_size; }; struct ufshci_query_request_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3 */ uint8_t opcode; uint8_t idn; uint8_t index; uint8_t selector; /* dword 4-5 */ union { /* The Write Attribute opcode uses 64 - bit value. */ uint64_t value_64; /* (Big-endian) */ struct { uint8_t reserved1[2]; uint16_t length; /* (Big-endian) */ uint32_t value_32; /* (Big-endian) */ }; } __packed __aligned(4); /* dword 6 */ uint32_t reserved2; /* dword 7 */ uint32_t reserved3; uint8_t command_data[256]; } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_query_request_upiu) == 288, "bad size for ufshci_query_request_upiu"); _Static_assert(sizeof(struct ufshci_query_request_upiu) <= UFSHCI_UTP_XFER_REQ_SIZE, "bad size for ufshci_query_request_upiu"); _Static_assert(sizeof(struct ufshci_query_request_upiu) % UFSHCI_UPIU_ALIGNMENT == 0, "UPIU requires 64-bit alignment"); /* UFS Spec 4.1, section 10.7.9 "QUERY RESPONSE UPIU" */ enum ufshci_query_response_code { UFSHCI_QUERY_RESP_CODE_SUCCESS = 0x00, UFSHCI_QUERY_RESP_CODE_PARAMETER_NOT_READABLE = 0xf6, UFSHCI_QUERY_RESP_CODE_PARAMETER_NOT_WRITEABLE = 0xf7, UFSHCI_QUERY_RESP_CODE_PARAMETER_ALREADY_WRITTEN = 0xf8, UFSHCI_QUERY_RESP_CODE_INVALID_LENGTH = 0xf9, UFSHCI_QUERY_RESP_CODE_INVALID_VALUE = 0xfa, UFSHCI_QUERY_RESP_CODE_INVALID_SELECTOR = 0xfb, UFSHCI_QUERY_RESP_CODE_INVALID_INDEX = 0xfc, UFSHCI_QUERY_RESP_CODE_INVALID_IDN = 0xfd, UFSHCI_QUERY_RESP_CODE_INVALID_OPCODE = 0xfe, UFSHCI_QUERY_RESP_CODE_GENERAL_FAILURE = 0xff, }; struct ufshci_query_response_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3 */ uint8_t opcode; uint8_t idn; uint8_t index; uint8_t selector; /* dword 4-5 */ union { /* The Read / Write Attribute opcodes use 64 - bit value. */ uint64_t value_64; /* (Big-endian) */ struct { uint8_t reserved1[2]; uint16_t length; /* (Big-endian) */ union { uint32_t value_32; /* (Big-endian) */ struct { uint8_t reserved2[3]; uint8_t flag_value; }; }; }; } __packed __aligned(4); /* dword 6 */ uint8_t reserved3[4]; /* dword 7 */ uint8_t reserved4[4]; uint8_t command_data[256]; } __packed __aligned(4); _Static_assert(sizeof(struct ufshci_query_response_upiu) == 288, "bad size for ufshci_query_response_upiu"); _Static_assert(sizeof(struct ufshci_query_response_upiu) <= UFSHCI_UTP_XFER_RESP_SIZE, "bad size for ufshci_query_response_upiu"); _Static_assert(sizeof(struct ufshci_query_response_upiu) % UFSHCI_UPIU_ALIGNMENT == 0, "UPIU requires 64-bit alignment"); /* UFS 4.1, section 10.7.11 "NOP OUT UPIU" */ struct ufshci_nop_out_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3-7 */ uint8_t reserved[20]; } __packed __aligned(8); _Static_assert(sizeof(struct ufshci_nop_out_upiu) == 32, "ufshci_upiu_nop_out must be 32 bytes"); /* UFS 4.1, section 10.7.12 "NOP IN UPIU" */ struct ufshci_nop_in_upiu { /* dword 0-2 */ struct ufshci_upiu_header header; /* dword 3-7 */ uint8_t reserved[20]; } __packed __aligned(8); _Static_assert(sizeof(struct ufshci_nop_in_upiu) == 32, "ufshci_upiu_nop_in must be 32 bytes"); union ufshci_reponse_upiu { struct ufshci_upiu_header header; struct ufshci_cmd_response_upiu cmd_response_upiu; struct ufshci_query_response_upiu query_response_upiu; struct ufshci_task_mgmt_response_upiu task_mgmt_response_upiu; struct ufshci_nop_in_upiu nop_in_upiu; }; struct ufshci_completion { union ufshci_reponse_upiu response_upiu; size_t size; }; typedef void (*ufshci_cb_fn_t)(void *, const struct ufshci_completion *, bool); /* UFS 4.1, section 10.8.5 "Well Known Logical Unit Defined in UFS" */ enum ufshci_well_known_luns { UFSHCI_WLUN_REPORT_LUNS = 0x81, UFSHCI_WLUN_BOOT = 0xb0, UFSHCI_WLUN_RPMB = 0xc4, UFSHCI_WLUN_UFS_DEVICE = 0xd0, }; /* * UFS Spec 4.1, section 14.1 "UFS Descriptors" * All descriptors use big-endian byte ordering. */ enum ufshci_descriptor_type { UFSHCI_DESC_TYPE_DEVICE = 0x00, UFSHCI_DESC_TYPE_CONFIGURATION = 0x01, UFSHCI_DESC_TYPE_UNIT = 0x02, UFSHCI_DESC_TYPE_INTERCONNECT = 0x04, UFSHCI_DESC_TYPE_STRING = 0x05, UFSHCI_DESC_TYPE_GEOMETRY = 0X07, UFSHCI_DESC_TYPE_POWER = 0x08, UFSHCI_DESC_TYPE_DEVICE_HEALTH = 0x09, UFSHCI_DESC_TYPE_FBO_EXTENSION_SPECIFICATION = 0x0a, }; /* * UFS Spec 4.1, section 14.1.5.2 "Device Descriptor" * DeviceDescriptor use big-endian byte ordering. */ struct ufshci_device_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t bDevice; uint8_t bDeviceClass; uint8_t bDeviceSubClass; uint8_t bProtocol; uint8_t bNumberLU; uint8_t bNumberWLU; uint8_t bBootEnable; uint8_t bDescrAccessEn; uint8_t bInitPowerMode; uint8_t bHighPriorityLUN; uint8_t bSecureRemovalType; uint8_t bSecurityLU; uint8_t bBackgroundOpsTermLat; uint8_t bInitActiveICCLevel; /* 0x10 */ uint16_t wSpecVersion; uint16_t wManufactureDate; uint8_t iManufacturerName; uint8_t iProductName; uint8_t iSerialNumber; uint8_t iOemID; uint16_t wManufacturerID; uint8_t bUD0BaseOffset; uint8_t bUDConfigPLength; uint8_t bDeviceRTTCap; uint16_t wPeriodicRTCUpdate; uint8_t bUfsFeaturesSupport; /* 0x20 */ uint8_t bFFUTimeout; uint8_t bQueueDepth; uint16_t wDeviceVersion; uint8_t bNumSecureWPArea; uint32_t dPSAMaxDataSize; uint8_t bPSAStateTimeout; uint8_t iProductRevisionLevel; uint8_t Reserved[5]; /* 0x2a */ /* 0x30 */ uint8_t ReservedUME[16]; /* 0x40 */ uint8_t ReservedHpb[3]; uint8_t Reserved2[12]; uint32_t dExtendedUfsFeaturesSupport; uint8_t bWriteBoosterBufferPreserveUserSpaceEn; uint8_t bWriteBoosterBufferType; uint32_t dNumSharedWriteBoosterBufferAllocUnits; } __packed; _Static_assert(sizeof(struct ufshci_device_descriptor) == 89, "bad size for ufshci_device_descriptor"); /* Defines the bit field of dExtendedUfsFeaturesSupport. */ enum ufshci_desc_wb_ext_ufs_feature { UFSHCI_DESC_EXT_UFS_FEATURE_FFU = (1 << 0), UFSHCI_DESC_EXT_UFS_FEATURE_PSA = (1 << 1), UFSHCI_DESC_EXT_UFS_FEATURE_DEV_LIFE_SPAN = (1 << 2), UFSHCI_DESC_EXT_UFS_FEATURE_REFRESH_OP = (1 << 3), UFSHCI_DESC_EXT_UFS_FEATURE_TOO_HIGH_TEMP = (1 << 4), UFSHCI_DESC_EXT_UFS_FEATURE_TOO_LOW_TEMP = (1 << 5), UFSHCI_DESC_EXT_UFS_FEATURE_EXT_TEMP = (1 << 6), UFSHCI_DESC_EXT_UFS_FEATURE_HPB_SUPPORT = (1 << 7), UFSHCI_DESC_EXT_UFS_FEATURE_WRITE_BOOSTER = (1 << 8), UFSHCI_DESC_EXT_UFS_FEATURE_PERF_THROTTLING = (1 << 9), UFSHCI_DESC_EXT_UFS_FEATURE_ADVANCED_RPMB = (1 << 10), UFSHCI_DESC_EXT_UFS_FEATURE_ZONED_UFS_EXTENSION = (1 << 11), UFSHCI_DESC_EXT_UFS_FEATURE_DEV_LEVEL_EXCEPTION = (1 << 12), UFSHCI_DESC_EXT_UFS_FEATURE_HID = (1 << 13), UFSHCI_DESC_EXT_UFS_FEATURE_BARRIER = (1 << 14), UFSHCI_DESC_EXT_UFS_FEATURE_CLEAR_ERROR_HISTORY = (1 << 15), UFSHCI_DESC_EXT_UFS_FEATURE_EXT_IID = (1 << 16), UFSHCI_DESC_EXT_UFS_FEATURE_FBO = (1 << 17), UFSHCI_DESC_EXT_UFS_FEATURE_FAST_RECOVERY_MODE = (1 << 18), UFSHCI_DESC_EXT_UFS_FEATURE_RPMB_VENDOR_CMD = (1 << 19), }; /* Defines the bit field of bWriteBoosterBufferType. */ enum ufshci_desc_wb_buffer_type { UFSHCI_DESC_WB_BUF_TYPE_LU_DEDICATED = 0x00, UFSHCI_DESC_WB_BUF_TYPE_SINGLE_SHARED = 0x01, }; /* Defines the bit field of bWriteBoosterBufferPreserveUserSpaceEn. */ enum ufshci_desc_user_space_config { UFSHCI_DESC_WB_BUF_USER_SPACE_REDUCTION = 0x00, UFSHCI_DESC_WB_BUF_PRESERVE_USER_SPACE = 0x01, }; /* * UFS Spec 4.1, section 14.1.5.3 "Configuration Descriptor" * ConfigurationDescriptor use big-endian byte ordering. */ struct ufshci_unit_descriptor_configurable_parameters { uint8_t bLUEnable; uint8_t bBootLunID; uint8_t bLUWriteProtect; uint8_t bMemoryType; uint32_t dNumAllocUnits; uint8_t bDataReliability; uint8_t bLogicalBlockSize; uint8_t bProvisioningType; uint16_t wContextCapabilities; union { struct { uint8_t Reserved[3]; uint8_t ReservedHpb[6]; } __packed; uint16_t wZoneBufferAllocUnits; }; uint32_t dLUNumWriteBoosterBufferAllocUnits; } __packed; _Static_assert(sizeof(struct ufshci_unit_descriptor_configurable_parameters) == 27, "bad size for ufshci_unit_descriptor_configurable_parameters"); #define UFSHCI_CONFIGURATION_DESCEIPTOR_LU_NUM 8 struct ufshci_configuration_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t bConfDescContinue; uint8_t bBootEnable; uint8_t bDescrAccessEn; uint8_t bInitPowerMode; uint8_t bHighPriorityLUN; uint8_t bSecureRemovalType; uint8_t bInitActiveICCLevel; uint16_t wPeriodicRTCUpdate; uint8_t Reserved; uint8_t bRPMBRegionEnable; uint8_t bRPMBRegion1Size; uint8_t bRPMBRegion2Size; uint8_t bRPMBRegion3Size; uint8_t bWriteBoosterBufferPreserveUserSpaceEn; uint8_t bWriteBoosterBufferType; uint32_t dNumSharedWriteBoosterBufferAllocUnits; /* 0x16 */ struct ufshci_unit_descriptor_configurable_parameters unit_config_params[UFSHCI_CONFIGURATION_DESCEIPTOR_LU_NUM]; } __packed; _Static_assert(sizeof(struct ufshci_configuration_descriptor) == (22 + 27 * 8), "bad size for ufshci_configuration_descriptor"); /* * UFS Spec 4.1, section 14.1.5.4 "Geometry Descriptor" * GeometryDescriptor use big-endian byte ordering. */ struct ufshci_geometry_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t bMediaTechnology; uint8_t Reserved; uint64_t qTotalRawDeviceCapacity; uint8_t bMaxNumberLU; uint32_t dSegmentSize; /* 0x11 */ uint8_t bAllocationUnitSize; uint8_t bMinAddrBlockSize; uint8_t bOptimalReadBlockSize; uint8_t bOptimalWriteBlockSize; uint8_t bMaxInBufferSize; uint8_t bMaxOutBufferSize; uint8_t bRPMB_ReadWriteSize; uint8_t bDynamicCapacityResourcePolicy; uint8_t bDataOrdering; uint8_t bMaxContexIDNumber; uint8_t bSysDataTagUnitSize; uint8_t bSysDataTagResSize; uint8_t bSupportedSecRTypes; uint16_t wSupportedMemoryTypes; /* 0x20 */ uint32_t dSystemCodeMaxNAllocU; uint16_t wSystemCodeCapAdjFac; uint32_t dNonPersistMaxNAllocU; uint16_t wNonPersistCapAdjFac; uint32_t dEnhanced1MaxNAllocU; /* 0x30 */ uint16_t wEnhanced1CapAdjFac; uint32_t dEnhanced2MaxNAllocU; uint16_t wEnhanced2CapAdjFac; uint32_t dEnhanced3MaxNAllocU; uint16_t wEnhanced3CapAdjFac; uint32_t dEnhanced4MaxNAllocU; /* 0x42 */ uint16_t wEnhanced4CapAdjFac; uint32_t dOptimalLogicalBlockSize; uint8_t ReservedHpb[5]; uint8_t Reserved2[2]; uint32_t dWriteBoosterBufferMaxNAllocUnits; uint8_t bDeviceMaxWriteBoosterLUs; uint8_t bWriteBoosterBufferCapAdjFac; uint8_t bSupportedWriteBoosterBufferUserSpaceReductionTypes; uint8_t bSupportedWriteBoosterBufferTypes; } __packed; _Static_assert(sizeof(struct ufshci_geometry_descriptor) == 87, "bad size for ufshci_geometry_descriptor"); /* * UFS Spec 4.1, section 14.1.5.5 "Unit Descriptor" * UnitDescriptor use big-endian byte ordering. */ struct ufshci_unit_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t bUnitIndex; uint8_t bLUEnable; uint8_t bBootLunID; uint8_t bLUWriteProtect; uint8_t bLUQueueDepth; uint8_t bPSASensitive; uint8_t bMemoryType; uint8_t bDataReliability; uint8_t bLogicalBlockSize; uint64_t qLogicalBlockCount; /* 0x13 */ uint32_t dEraseBlockSize; uint8_t bProvisioningType; uint64_t qPhyMemResourceCount; /* 0x20 */ uint16_t wContextCapabilities; uint8_t bLargeUnitGranularity_M1; uint8_t ReservedHpb[6]; uint32_t dLUNumWriteBoosterBufferAllocUnits; } __packed; _Static_assert(sizeof(struct ufshci_unit_descriptor) == 45, "bad size for ufshci_unit_descriptor"); enum LUWriteProtect { kNoWriteProtect = 0x00, kPowerOnWriteProtect = 0x01, kPermanentWriteProtect = 0x02, }; /* * UFS Spec 4.1, section 14.1.5.6 "RPMB Unit Descriptor" * RpmbUnitDescriptor use big-endian byte ordering. */ struct ufshci_rpmb_unit_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t bUnitIndex; uint8_t bLUEnable; uint8_t bBootLunID; uint8_t bLUWriteProtect; uint8_t bLUQueueDepth; uint8_t bPSASensitive; uint8_t bMemoryType; uint8_t Reserved; uint8_t bLogicalBlockSize; uint64_t qLogicalBlockCount; /* 0x13 */ uint32_t dEraseBlockSize; uint8_t bProvisioningType; uint64_t qPhyMemResourceCount; /* 0x20 */ uint8_t Reserved1[3]; } __packed; _Static_assert(sizeof(struct ufshci_rpmb_unit_descriptor) == 35, "bad size for RpmbUnitDescriptor"); /* * UFS Spec 4.1, section 14.1.5.7 "Power Parameters Descriptor" * PowerParametersDescriptor use big-endian byte ordering. */ struct ufshci_power_parameters_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint16_t wActiveICCLevelsVCC[16]; uint16_t wActiveICCLevelsVCCQ[16]; uint16_t wActiveICCLevelsVCCQ2[16]; } __packed; _Static_assert(sizeof(struct ufshci_power_parameters_descriptor) == 98, "bad size for PowerParametersDescriptor"); /* * UFS Spec 4.1, section 14.1.5.8 "Interconnect Descriptor" * InterconnectDescriptor use big-endian byte ordering. */ struct ufshci_interconnect_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint16_t bcdUniproVersion; uint16_t bcdMphyVersion; } __packed; _Static_assert(sizeof(struct ufshci_interconnect_descriptor) == 6, "bad size for InterconnectDescriptor"); /* * UFS Spec 4.1, section 14.1.5.9-13 "String Descriptor" * StringDescriptor use big-endian byte ordering. */ struct ufshci_string_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint16_t UC[126]; } __packed; _Static_assert(sizeof(struct ufshci_string_descriptor) == 254, "bad size for StringDescriptor"); /* * UFS Spec 4.1, section 14.1.5.14 "Device Health Descriptor" * DeviceHealthDescriptor use big-endian byte ordering. */ struct ufshci_device_healthd_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t bPreEOLInfo; uint8_t bDeviceLifeTimeEstA; uint8_t bDeviceLifeTimeEstB; uint8_t VendorPropInfo[32]; uint32_t dRefreshTotalCount; uint32_t dRefreshProgress; } __packed; _Static_assert(sizeof(struct ufshci_device_healthd_descriptor) == 45, "bad size for DeviceHealthDescriptor"); /* * UFS Spec 4.1, section 14.1.5.15 "Vendor Specific Descriptor" * VendorSpecificDescriptor use big-endian byte ordering. */ struct ufshci_vendor_specific_descriptor { uint8_t bLength; uint8_t bDescriptorIDN; uint8_t DATA[254]; } __packed; _Static_assert(sizeof(struct ufshci_vendor_specific_descriptor) == 256, "bad size for VendorSpecificDescriptor"); /* UFS Spec 4.1, section 14.2 "Flags" */ enum ufshci_flags { UFSHCI_FLAG_F_RESERVED = 0x00, UFSHCI_FLAG_F_DEVICE_INIT = 0x01, UFSHCI_FLAG_F_PERMANENT_WP_EN = 0x02, UFSHCI_FLAS_F_POWER_ON_WP_EN = 0x03, UFSHCI_FLAG_F_BACKGROUND_OPS_EN = 0x04, UFSHCI_FLAG_F_DEVICE_LIFE_SPAN_MODE_EN = 0x05, UFSHCI_FLAG_F_PURGE_ENABLE = 0x06, UFSHCI_FLAG_F_REFRESH_ENABLE = 0x07, UFSHCI_FLAG_F_PHY_RESOURCE_REMOVAL = 0x08, UFSHCI_FLAG_F_BUSY_RTC = 0x09, UFSHCI_FLAG_F_PERMANENTLY_DISABLE_FW_UPDATE = 0x0b, UFSHCI_FLAG_F_WRITE_BOOSTER_EN = 0x0e, UFSHCI_FLAG_F_WB_BUFFER_FLUSH_EN = 0x0f, UFSHCI_FLAG_F_WB_BUFFER_FLUSH_DURING_HIBERNATE = 0x10, UFSHCI_FLAG_F_UNPIN_EN = 0x13, }; /* UFS Spec 4.1, section 14.3 "Attributes" */ enum ufshci_attributes { UFSHCI_ATTR_B_BOOT_LUN_EN = 0x00, UFSHCI_ATTR_B_CURRENT_POWER_MODE = 0x02, UFSHCI_ATTR_B_ACTIVE_ICC_LEVEL = 0x03, UFSHCI_ATTR_B_OUT_OF_ORDER_DATA_EN = 0x04, UFSHCI_ATTR_B_BACKGROUND_OP_STATUS = 0x05, UFSHCI_ATTR_B_PURGE_STATUS = 0x06, UFSHCI_ATTR_B_MAX_DATA_IN_SIZE = 0x07, UFSHCI_ATTR_B_MAX_DATA_OUT_SIZE = 0x08, UFSHCI_ATTR_D_DYN_CAP_NEEDED = 0x09, UFSHCI_ATTR_B_REF_CLK_FREQ = 0x0a, UFSHCI_ATTR_B_CONFIG_DESCR_LOCK = 0x0b, UFSHCI_ATTR_B_MAX_NUM_OF_RTT = 0x0c, UFSHCI_ATTR_W_EXCEPTION_EVENT_CONTROL = 0x0d, UFSHCI_ATTR_W_EXCEPTION_EVENT_STATUS = 0x0e, UFSHCI_ATTR_D_SECONDS_PASSED = 0x0f, UFSHCI_ATTR_W_CONTEXT_CONF = 0x10, UFSHCI_ATTR_B_DEVICE_FFU_STATUS = 0x14, UFSHCI_ATTR_B_PSA_STATE = 0x15, UFSHCI_ATTR_D_PSA_DATA_SIZE = 0x16, UFSHCI_ATTR_B_REF_CLK_GATING_WAIT_TIME = 0x17, UFSHCI_ATTR_B_DEVICE_CASE_ROUGH_TEMPERAURE = 0x18, UFSHCI_ATTR_B_DEVICE_TOO_HIGH_TEMP_BOUNDARY = 0x19, UFSHCI_ATTR_B_DEVICE_TOO_LOW_TEMP_BOUNDARY = 0x1a, UFSHCI_ATTR_B_THROTTLING_STATUS = 0x1b, UFSHCI_ATTR_B_WB_BUFFER_FLUSH_STATUS = 0x1c, UFSHCI_ATTR_B_AVAILABLE_WB_BUFFER_SIZE = 0x1d, UFSHCI_ATTR_B_WB_BUFFER_LIFE_TIME_EST = 0x1e, UFSHCI_ATTR_D_CURRENT_WB_BUFFER_SIZE = 0x1f, UFSHCI_ATTR_B_REFRESH_STATUS = 0x2c, UFSHCI_ATTR_B_REFRESH_FREQ = 0x2d, UFSHCI_ATTR_B_REFRESH_UNIT = 0x2e, UFSHCI_ATTR_B_REFRESH_METHOD = 0x2f, }; /* bAvailableWriteBoosterBufferSize codes (UFS WriteBooster abailable buffer * left %) */ enum ufshci_wb_available_buffer_Size { UFSHCI_ATTR_WB_AVAILABLE_0 = 0x00, /* 0% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_10 = 0x01, /* 10% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_20 = 0x02, /* 20% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_30 = 0x03, /* 30% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_40 = 0x04, /* 40% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_50 = 0x05, /* 50% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_60 = 0x06, /* 60% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_70 = 0x07, /* 70% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_80 = 0x08, /* 80% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_90 = 0x09, /* 90% buffer remains */ UFSHCI_ATTR_WB_AVAILABLE_100 = 0x0A, /* 100% buffer remains */ }; /* bWriteBoosterBufferLifeTimeEst codes (UFS WriteBooster buffer life %) */ enum ufshci_wb_lifetime { UFSHCI_ATTR_WB_LIFE_DISABLED = 0x00, /* Info not available */ UFSHCI_ATTR_WB_LIFE_0_10 = 0x01, /* 0%–10% used */ UFSHCI_ATTR_WB_LIFE_10_20 = 0x02, /* 10%–20% used */ UFSHCI_ATTR_WB_LIFE_20_30 = 0x03, /* 20%–30% used */ UFSHCI_ATTR_WB_LIFE_30_40 = 0x04, /* 30%–40% used */ UFSHCI_ATTR_WB_LIFE_40_50 = 0x05, /* 40%–50% used */ UFSHCI_ATTR_WB_LIFE_50_60 = 0x06, /* 50%–60% used */ UFSHCI_ATTR_WB_LIFE_60_70 = 0x07, /* 60%–70% used */ UFSHCI_ATTR_WB_LIFE_70_80 = 0x08, /* 70%–80% used */ UFSHCI_ATTR_WB_LIFE_80_90 = 0x09, /* 80%–90% used */ UFSHCI_ATTR_WB_LIFE_90_100 = 0x0A, /* 90%–100% used */ UFSHCI_ATTR_WB_LIFE_EXCEEDED = 0x0B, /* Exceeded estimated life (treat as WB disabled) */ }; #endif /* __UFSHCI_H__ */