From b6034d44a848450691e6d9aecb15638fe5a4890c Mon Sep 17 00:00:00 2001 From: Raz Ben Yehuda Date: Thu, 28 May 2026 15:06:56 +0300 Subject: [PATCH 1/4] ufs: add UFS descriptor/configuration utility support Add a UFS tool implementation for querying, printing, and updating UFS descriptors and configuration parameters. Features include: * Query and print device, geometry, unit, config, and power descriptors * Display LUN configuration and calculated capacities * Support updating LUN, config, and device descriptor fields * Add WriteBooster-related configuration handling * Add flag query support for common UFS runtime flags * Add commit and clear-all configuration helpers * Improve descriptor decoding and formatted debug output The tool is intended for U-Boot/UFS debugging, provisioning, and configuration workflows. Signed-off-by: Raz Ben Yehuda --- drivers/ufs/ufs_tool.c | 1251 ++++++++++++++++++++++++++++++++++++++++ drivers/ufs/ufs_tool.h | 394 +++++++++++++ 2 files changed, 1645 insertions(+) create mode 100644 drivers/ufs/ufs_tool.c create mode 100644 drivers/ufs/ufs_tool.h diff --git a/drivers/ufs/ufs_tool.c b/drivers/ufs/ufs_tool.c new file mode 100644 index 00000000000..1f5ce85beed --- /dev/null +++ b/drivers/ufs/ufs_tool.c @@ -0,0 +1,1251 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ufs.h" +#include "ufs_tool.h" +#include + +#define UFS_DESC_SIZE (unsigned char)0xff + +static int ufshcd_query_desc_idn_geometry(struct ufs_hba *, + struct ufs_geometry_descriptor *decs); +static int ufshcd_query_desc_idn_config(struct ufs_hba *hba, + struct ufs_config_descriptor *cfg); +static int ufshcd_query_desc_idn_device(struct ufs_hba *hba, + struct ufs_device_descriptor *desc); +static int ufshcd_query_desc_idn_unit(struct ufs_hba *hba, int lun, + struct unit_descriptor *desc); + +static void +ufshcd_print_idn_geometry_desc(struct ufs_geometry_descriptor *desc); +static void ufshcd_print_device_descriptor(struct ufs_device_descriptor *desc); +static void ufshcd_print_unit_descriptor(struct unit_descriptor *desc); +static void +ufshcd_print_idn_desc_config(struct ufs_config_descriptor *user_cfg, + struct ufs_geometry_descriptor *geometry, + u32 b_ud0_base_offset, u32 b_u_dconfig_p_length); + +static int +ufshcd_query_desc_idn_power(struct ufs_hba *hba, int lun, + struct ufs_power_parameters_descriptor *desc); + +static void +print_ufs_power_parameters(struct ufs_power_parameters_descriptor *desc); + +static int ufshcd_query_user_flag(struct ufs_hba *hba, const char *flag_name); +static int ufshcd_write_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, + int desc_index, unsigned char param_offset, + unsigned char *param_write_buf, + unsigned char param_size); + +static struct ufs_config_descriptor g_cfg_desc; + +struct ufs_hba *ufstool_probe_dev(int index) +{ + struct udevice *dev; + struct ufs_hba *hba = NULL; + + if (!uclass_get_device(UCLASS_UFS, index, &dev)) + hba = dev_get_uclass_priv(dev); + return hba; +} + +int ufshcd_query_desc_idn_device(struct ufs_hba *hba, + struct ufs_device_descriptor *desc) +{ + int ret; + int param_offset = 0; + int desc_index = 0; /* LUNS 0-7 controlled by index 0 */ + + memset(desc, 0x00, sizeof(*desc)); + ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_DEVICE, desc_index, + param_offset, (void *)desc, + UFS_DESC_SIZE); + return ret; +} + +void ufshcd_print_device_descriptor(struct ufs_device_descriptor *descriptor) +{ + u32 val = descriptor->num_shared_write_booster_buffer_allocunits; + + printf("--- UFS Device Descriptor (IDN: 00h %ld) ---\n", + sizeof(struct ufs_device_descriptor)); + + printf(" Name | Value | Description\n"); + printf("-----------------------------------------------------------\n"); + /* Descriptor Header (Offset 00h – 01h) */ + printf("Length | 0x%x\n", descriptor->length); + printf("DescriptorIdn | 0x%x\n", descriptor->descriptor_idn); + + /* Device Identification & Core configuration (Offset 02h – 0Dh) */ + printf("Device | 0x%x\n", descriptor->device); + printf("DeviceClass | 0x%x\n", descriptor->device_class); + printf("DeviceSubClass | 0x%x\n", descriptor->device_sub_class); + printf("protocol | 0x%x\n", descriptor->protocol); + printf("NumberLu | %d\n", descriptor->number_lu); + printf("NumberWLu | %d\n", descriptor->number_wlu); + printf("BootEnable | 0x%x\n", descriptor->boot_enable); + printf("DescrAccessEn | 0x%x\n", descriptor->descr_access_en); + printf("InitPowerMode | 0x%x\n", descriptor->init_power_mode); + printf("highPriorityLun | 0x%x\n", + descriptor->high_priority_lun); + printf("SecureRemovalType | 0x%x\n", + descriptor->secure_removal_type); + printf("SecurityLu | 0x%x\n", descriptor->security_lu); + + /* power Management, Version & Pointers (Offset 0Eh – 2Ah) */ + printf("b_background_ops_term_lat | 0x%x\n", + descriptor->background_ops_term_lat); + printf("b_init_active_icc_level | 0x%x\n", + descriptor->init_active_icc_level); + printf("SpecVersion | 0x%x\n", + __builtin_bswap16(descriptor->spec_version)); + printf("ManufactureDate | 0x%x\n", descriptor->manufacture_date); + printf("ManufacturerName | 0x%x\n", descriptor->manufacturer_name); + printf("ProductName | 0x%x\n", descriptor->product_name); + printf("SerialNumber | 0x%x\n", descriptor->serial_number); + printf("OEMId | 0x%x\n", descriptor->oem_id); + printf("ManufacturerId | 0x%x\n", descriptor->manufacturer_id); + printf("Ud0BaseOffset | %d\n", descriptor->ud0_base_offset); + printf("DConfigLength | %d\n", + descriptor->config_length); + printf("DeviceRttCap | 0x%x\n", descriptor->device_rtt_cap); + printf("PeriodicRtcUpdate | 0x%x\n", + descriptor->periodic_rtc_update); + printf("UfsFeaturesSupport | 0x%x\n", + descriptor->ufs_features_support); + printf("FfuTimeout | %d\n", descriptor->ffu_timeout); + printf("QueueDepth | %d\n", descriptor->queue_depth); + printf("DeviceVersion | 0x%x\n", descriptor->device_version); + printf("NumSecureWpArea | 0x%x\n", + descriptor->num_secure_wp_area); + printf("PsaMaxDataSize | 0x%x\n", + descriptor->psa_max_data_size); + printf("PsaStateTimeout | 0x%x\n", + descriptor->psa_state_timeout); + printf("ProductRevisionLevel | 0x%x\n", + descriptor->product_revision_level); + + /* Extended Features (Offset 4Fh – 58h) */ + printf("ExtendedUfsFeaturesSupport "); + printf("| 0x%x |(includes WriteBooster/Throttling)\n", + be32_to_cpu(descriptor->extended_ufs_features_support)); + + printf("WriteBoosterBufferPreserveUserSpaceEn"); + printf("| 0x%x | Preserve User Space mode for WriteBooster Buffer\n", + descriptor->write_booster_buffer_preserve_user_space_en); + + printf("write booster buffer type"); + printf("| 0x%x | WriteBooster Buffer Type", + descriptor->write_booster_buffer_type); + puts("(00h: LU dedicated, 01h: shared)\n"); + + printf("NumSharedWriteBooster_buffer_allocunits |"); + printf("%d | Shared WriteBooster Buf size (in Allocation units)\n", + be32_to_cpu(val)); +} + +int ufshcd_query_desc_idn_geometry(struct ufs_hba *hba, + struct ufs_geometry_descriptor *desc) +{ + int param_offset = 0; + int desc_index = 0; + + memset(desc, 0xc, sizeof(*desc)); + return ufshcd_read_desc_param(hba, QUERY_DESC_IDN_GEOMETRY, desc_index, + param_offset, (void *)desc, + QUERY_DESC_GEOMETRY_DEF_SIZE); +} + +void ufshcd_print_idn_geometry_desc(struct ufs_geometry_descriptor *desc) +{ + u64 cap = be64_to_cpu(desc->total_raw_device_capacity); + + cap = ((cap << 9) >> 30); + + printf("--- UFS geometry Descriptor (IDN: 07h) ---\n"); + printf(" Name | Value | Description\n"); + printf("-------------------------------------------------------\n"); + + /* Header and Core Capacity (Offset 00h - 0Ch) */ + printf("Length | 0x%x | Size of descriptor\n", desc->length); + printf("DescriptorIdn | 0x%x | geometry Descriptor ID\n", + desc->descriptor_idn); + printf("MediaTechnology | 0x%x | Reserved (00h)\n", + desc->media_technology); + + printf("TotalRawDeviceCapacity | %lld GB\n", cap); + + printf("MaxNumberLu | %d | Max LUs (00h: 8, 01h: 32)\n", + desc->max_number_lu); + + /* Segment and Allocation unit constraints (Offset 0Dh - 12h) */ + printf("SegmentSize | %d | Segment size (512B units)\n", + be32_to_cpu(desc->segment_size)); + + printf("AllocationunitSize | %d | Alloc unit size (in segments)\n", + desc->allocationunit_size); + + printf("MinAddrBlockSize | %d | Min addressable block size\n", + desc->min_addr_block_size); + + /* Performance and Buffer parameters (Offset 13h - 17h) */ + printf("OptimalReadBlockSize"); + printf("| %d | Optimal read size (512B units)\n", + desc->optimal_read_block_size); + + printf("OptimalWriteBlockSize "); + printf("| 0x%x | Optimal write size (512B units)\n", + desc->optimal_write_block_size); + + printf("MaxInBufferSize | 0x%x | Max Data-In buffer (512B units)\n", + desc->max_in_buffer_size); + + printf("MaxOutBufSize | 0x%x | Max Data-Out buffer (512B)\n", + desc->max_out_buffer_size); + + printf("bRPMBReadWriteSize | 0x%x | Max RPMB frames per command\n", + desc->rpmb_read_write_size); + + /* Feature Support Bitmaps (Offset 18h - 1Fh) */ + printf("DynamicCapacityResourcePolicy| 0x%x |", + desc->dynamic_capacity_resource_policy); + + printf("00h: per LU, 01h: per Memory Type\n"); + printf("DataOrdering | 0x%x | 01h: Out-of-order supported\n", + desc->data_ordering); + printf("MaxContexIdNumber | 0x%x | Max supported contexts (min 5)\n", + desc->max_contex_id_number); + + printf("SysDataTagUnitSize | 0x%x | System data tag granularity\n", + desc->sys_data_tagunit_size); + + printf("SysDataTagResSize | 0x%x | Max area for tagged system data\n", + desc->sys_data_tag_res_size); + + printf("SupportedSecRTypes | 0x%x | Supported secure removal types\n", + desc->supported_sec_r_types); + + printf("SupportedMemoryTypes | 0x%x | Bitmap of memory types\n", + be16_to_cpu(desc->supported_memory_types)); + /* + * Memory Type Specific Capacities and Adjustment Factors + */ + printf("SystemCodeMaxNAlloc | 0x%x | Max Alloc units\n", + desc->system_code_max_n_alloc_u); + + printf("SystemCodeCapAdjFac | 0x%x | cap_adj_factor: System Code\n", + desc->system_code_cap_adj_fac); + + printf("NonPersistMaxNAlloc | 0x%x | Max Alloc units: Non-Persist\n", + desc->non_persist_max_n_alloc_u); + + printf("NonPersistCapAdjFac | 0x%x | cap_adj_factor: Non-Persist\n", + desc->non_persist_cap_adj_fac); + + printf("Enhanced1MaxNAlloc | %d | Max Alloc units: Enhanced 1\n", + be32_to_cpu(desc->enhanced1_max_n_alloc_u)); + + printf("Enhanced1CapAdjFac | %d | cap_adj_factor: Enhanced 1\n", + be16_to_cpu(desc->enhanced1_cap_adj_fac)); + + printf("Enhanced2MaxNAlloc_u | %d | Max Alloc units: Enhanced 2\n", + be32_to_cpu(desc->enhanced2_max_n_alloc_u)); + + printf("Enhanced2CapAdjFac | %d | cap_adj_factor: Enhanced 2\n", + be16_to_cpu(desc->enhanced2_cap_adj_fac)); + printf("Enhanced3MaxNAlloc | %d | Max Alloc units: Enhanced 3\n", + be32_to_cpu(desc->enhanced3_max_n_alloc_u)); + + printf("Enhanced3CapAdjFac | %d | cap_adj_factor: Enhanced 3\n", + be32_to_cpu(desc->enhanced3_cap_adj_fac)); + + printf("Enhanced4MaxNAlloc | %d | Max Alloc units: Enhanced 4\n", + be32_to_cpu(desc->enhanced4_max_n_alloc_u)); + + printf("Enhanced4CapAdjFac | %d | cap_adj_factor: Enhanced 4\n", + be16_to_cpu(desc->enhanced4_cap_adj_fac)); + + /* Performance and WriteBooster (Offset 44h - 56h) */ + printf("OptimalLogicalBlockSize | %d | Optimal Blk Size per type\n", + be32_to_cpu(desc->optimal_logical_block_size)); + + printf("WriteBoosterBufferMaxNAllocunits"); + printf("| %d | Max tot WB buf size\n", + be32_to_cpu(desc->write_booster_buffer_max_alloc_units)); + + printf("DeviceMaxWriteBoosterLus "); + printf("| %d | Max LUs supporting WB (MDV:1)\n", + desc->device_max_write_booster_lus); + + printf("WriteBoosterBufferCapAdjFac"); + printf("| %d | WB Capacity Adjustment Factor\n", + desc->write_booster_buffer_capadjfac); + + printf("SupportedwbBufferUserSpaceReductionType"); + printf(" | 0x%x | Reduction/Preserve mode support\n", + desc->supported_write_booster_buffer_userspace_reduction_types); + + printf("SupportedWriteBoosterBufferTypes"); + printf("| 0x%x | 00h: LU based, 01h: Shared\n", + desc->supported_write_booster_buffer_types); +} + +void ufshcd_print_config_descriptor(const struct config_descriptor *cd) +{ + if (!cd) + return; + + printf("Config Descriptor:\n"); + + printf("Length : 0x0%x\n", cd->length); + printf("DescriptorIdn : 0x0%x\n", + cd->descriptor_idn); + printf("ConfDescContinue : 0x0%x\n", + cd->conf_desc_continue); + + printf("BootEnable : 0x0%x\n", + cd->boot_enable); + printf("DescrAccess_en : 0x0%x\n", + cd->descr_access_en); + printf("InitPowerMode : 0x0%x\n", + cd->initpower_mode); + printf("HighPriorityLun : 0x0%x\n", + cd->high_priority_lun); + printf("SecureRemovalType : 0x0%x\n", + cd->secure_removal_type); + printf("InitActiveIccLevel : 0x0%x\n", + cd->init_active_icc_level); + printf("PeriodicRtcUpdate : 0x0%x\n", + cd->periodic_rtc_update); + printf("Reserved_HPB : 0x0%x\n", + cd->reserved_HPB); + + printf("RpmbRegionEnable : 0x0%x\n", + cd->rpmb_region_enable); + printf("RpmbRegion1Size : 0x0%x\n", + cd->rpmb_region1_size); + printf("RpmbRegion2Size : 0x0%x\n", + cd->rpmb_region2_size); + printf("RpmbRegion3Size : 0x0%x\n", + cd->rpmb_region3_size); + + printf("WriteBoosterBufferPreserveUserSpaceEN : 0x0%x\n", + cd->write_booster_buffer_preserve_user_space_en); + printf("WriteBoosterBufferType : 0x0%x\n", + cd->write_booster_buffer_type); + printf("NumSharedWriteBoosterBufferAllocUnits : 0x0%x\n", + cd->num_shared_write_booster_buffer_allocunits); +} + +int ufshcd_query_desc_idn_config(struct ufs_hba *hba, + struct ufs_config_descriptor *user_cfg) +{ + int ret; + int param_offset = 0; + int desc_index = 0; + + ret = + ufshcd_read_desc_param(hba, QUERY_DESC_IDN_CONFIGURATION, + desc_index, param_offset, (void *)user_cfg, + QUERY_DESC_CONFIGURATION_DEF_SIZE); + if (ret) + printf("Failed to read config\n"); + return ret; +} + +void ufshcd_print_unit_param_descriptor(struct ufs_unit_configurable_parameters + *unit) +{ + /* Header segment (Offset 00h - 03h) */ + printf("LuEnable | 0x%x | Enabled, 00h: Disabled\n", + unit->lu_enable); + printf("BootLunId | 0x%x | Boot LU A, 02h: Boot LU B\n", + unit->boot_lun_id); + printf("LuWriteProtect | 0x%x | power-on, 02h: Permanent\n", + unit->lu_write_protect); + printf("MemoryType| 0x%x | Normal, 01h: System Code\n", + unit->memory_type); + /* Allocation and Sizing (Offset 04h - 0Ah) */ + printf("NumAllocunits | %d | Number of Allocation units for LU\n", + be32_to_cpu(unit->num_allocunits)); + printf("DataReliability | 0x%x | power failure protection\n", + unit->data_reliability); + printf("LogicalBlockSize | 0x%x | Block size exponent (Min: 0Ch)\n", + unit->logical_block_size); + printf("ProvisioningType | 0x%x | Full, 02h/03h: Thin\n", + unit->provisioning_type); + + /* Context and HPB (Offset 0Bh - 15h) */ + puts("ContextCapabilities"); + printf(" | 0x%x | LU Context support/multipliers\n", + unit->context_capabilities); + /* printf("Reserved | -- | Reserved\n"); */ + /* printf("Reserved (HPB) | -- | Reserved for HPB Extension\n"); */ + /* WriteBooster (Offset 16h) */ + printf("LuNumWriteBoosterBufferAllocunits"); + printf("| 0x%x | WB Buffer size (LUs 0-7 only)\n", + unit->lu_num_write_booster_buffer_allocunits); +} + +void ufshcd_clearall_luns(struct ufs_config_descriptor *user_cfg, + u32 base_offset, u32 config_length) + +{ + int i; + int off; + u8 *config = (u8 *)user_cfg; + struct ufs_unit_configurable_parameters *unit; + + for (i = 0; i < 8; i++) { + off = base_offset + (i * config_length); + + unit = (struct ufs_unit_configurable_parameters *)&config[off]; + + unit->num_allocunits = cpu_to_be32(0); + unit->memory_type = 0; + unit->lu_enable = 0; + unit->boot_lun_id = 0; + unit->logical_block_size = 0xC; + unit->data_reliability = 0; + unit->provisioning_type = 0; + unit->context_capabilities = cpu_to_be16(0); + unit->lu_num_write_booster_buffer_allocunits = cpu_to_be32(0); + } +} + +static float calc_lun_capacity(struct ufs_unit_configurable_parameters *unit, + struct ufs_geometry_descriptor *geometry) +{ + u16 cap_adj_fac = 1; + float cap_adj_facfl = 1; + u64 lun_capacity; + + switch (unit->memory_type) { + case 0: /* Normal */ + cap_adj_fac = 1; + break; + case 1: + cap_adj_fac = + be16_to_cpu(geometry->system_code_cap_adj_fac); + break; + case 2: + cap_adj_fac = + be16_to_cpu(geometry->non_persist_cap_adj_fac); + break; + case 3: + cap_adj_fac = + be16_to_cpu(geometry->enhanced1_cap_adj_fac); + break; + case 4: + cap_adj_fac = + be16_to_cpu(geometry->enhanced2_cap_adj_fac); + break; + case 5: + cap_adj_fac = + be16_to_cpu(geometry->enhanced3_cap_adj_fac); + break; + case 6: + cap_adj_fac = + be16_to_cpu(geometry->enhanced4_cap_adj_fac); + break; + default: + printf("ilegal memory type\n"); + } + + if (unit->memory_type) { + /* See geometry Descriptor/cap_adj_fac in Jedec. */ + cap_adj_facfl = (float)(cap_adj_fac / 256.00); + } + + lun_capacity = 512 * (u64)be32_to_cpu(unit->num_allocunits) * + geometry->allocationunit_size * + (u64)be32_to_cpu(geometry->segment_size); + + lun_capacity /= cap_adj_facfl; + u32 mb = (u32)(lun_capacity >> 20); + return mb; +} + +void ufs_tool_print_luns(void) +{ + struct ufs_geometry_descriptor geometry; + struct ufs_config_descriptor config_desc; + struct ufs_device_descriptor device_desc; + struct ufs_hba *hba; + int i; + + hba = ufstool_probe_dev(0); + if (!hba) { + printf("ufs_tool: failed to get hba\n"); + return; + } + if (ufshcd_query_desc_idn_config(hba, &config_desc)) { + printf("Failed to read configuration\n"); + return; + } + + if (ufshcd_query_desc_idn_device(hba, &device_desc)) { + printf("Failed to read 0h\n"); + return; + } + + if (ufshcd_query_desc_idn_geometry(hba, &geometry)) { + printf("Failed to read geometry\n"); + return; + } + + u8 *config = (u8 *)&config_desc; + /* Header */ + for (i = 0; i < 8; i++) { + const int COL_WIDTH = 14; + int off; + char buf[32]; + struct ufs_unit_configurable_parameters *unit; + + off = device_desc.ud0_base_offset + + (i * device_desc.config_length); + + unit = (struct ufs_unit_configurable_parameters *)&config[off]; + + if (unit->lu_enable != 1) + continue; + + snprintf(buf, sizeof(buf), "LUN %d", i); + printf("| %-*s", COL_WIDTH - 2, buf); + } + printf("|\n"); + /* Sizes */ + for (i = 0; i < 8; i++) { + int off; + s64 mb; + char size_str[32]; + const int COL_WIDTH = 14; + struct ufs_unit_configurable_parameters *unit; + + off = device_desc.ud0_base_offset + + (i * device_desc.config_length); + + unit = (struct ufs_unit_configurable_parameters *)&config[off]; + + if (unit->lu_enable != 1) + continue; + + mb = calc_lun_capacity(unit, &geometry); + + snprintf(size_str, sizeof(size_str), "%lld MB", mb); + + printf("| %-*s", COL_WIDTH - 2, size_str); + } + printf("|\n"); +} + +static void ufshcd_print_idn_desc_config(struct ufs_config_descriptor *user_cfg, + struct ufs_geometry_descriptor *geometry, + u32 base_offset, u32 config_ength) +{ + int i; + int off; + struct ufs_unit_configurable_parameters *unit; + u8 *config = (u8 *)user_cfg; + u32 mb; + + ufshcd_print_config_descriptor(&user_cfg->cfg_desc); + for (i = 0; i < 8; i++) { + off = base_offset + (i * config_ength); + + unit = (struct ufs_unit_configurable_parameters *)&config[off]; + mb = calc_lun_capacity(unit, geometry); + printf("-------------------------------------------------\n"); + printf("LUN %d Capacity %d MB\n", i, mb); + printf("Name | Value | Description\n"); + printf("-------------------------------------------------\n"); + ufshcd_print_unit_param_descriptor(unit); + } +} + +/** + * Decodes and prints a single 16-bit power Parameter Element. + * Format based on Table 7.13. + */ +void print_power_element(struct ufs_power_parameter_element desc) +{ + static const char * const units[] = { "n_a", "u_a", "m_a", "A" }; + u16 element = desc.upower_value; + + /* Bits [15:14] define the unit */ + u8 unit_code = (element >> 14) & 0x03; + + /* Bits [9:0] define the maximum current value */ + u16 value = element & 0x03FF; + + printf("%d %s", value, units[unit_code]); +} + +/** + * Print function for UFS power Parameters Descriptor (IDN: 08h). + * References: Table 14.16, Table 7.12, and Table 7.13. + */ +void print_ufs_power_parameters(struct ufs_power_parameters_descriptor *desc) +{ + printf("--- UFS power Parameters Descriptor (IDN: 08h) ---\n"); + printf("Descriptor Length: 0x%x (%u bytes)\n", desc->length, desc->length); + printf("Descriptor IDN: 0x%x\n\n", desc->descriptor_idn); + + printf("Level | VCC Current | VCCQ Current | VCCQ2 Current\n"); + printf("--------------------------------------------------\n"); + + for (int i = 0; i < 5; i++) { + printf(" %x | ", i); + /* Accessing the levels for each supply (VCC, VCCQ, VCCQ2) */ + /* Values represent worst-case maximum peak current */ + print_power_element(desc->active_icc_levels_vcc[i]); + printf(" | "); + print_power_element(desc->active_icc_levels_vccq[i]); + printf(" | "); + print_power_element(desc->active_icc_levels_vccq2[i]); + printf("\n"); + } +} + +int ufshcd_query_desc_idn_power(struct ufs_hba *hba, int lun, + struct ufs_power_parameters_descriptor + *out_desc) +{ + int ret; + unsigned char Space[QUERY_DESC_POWER_DEF_SIZE]; + + memset(out_desc, 0xFF, sizeof(*out_desc)); + + ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_POWER, lun, 0, Space, + QUERY_DESC_POWER_DEF_SIZE); + + if (ret) { + puts("Err:--- UFS power Descriptor"); + return ret; + } + memcpy(out_desc, Space, sizeof(*out_desc)); + return ret; +} + +int ufshcd_query_desc_idn_unit(struct ufs_hba *hba, int lun, + struct unit_descriptor *out_desc) +{ + int ret; + unsigned char unit_space[QUERY_DESC_UNIT_DEF_SIZE]; + struct unit_descriptor *desc = (struct unit_descriptor *)&unit_space; + + memset(desc, 0xFF, sizeof(*desc)); + ret = ufshcd_read_desc_param(hba, QUERY_DESC_IDN_UNIT, lun, 0, + (void *)desc, QUERY_DESC_UNIT_DEF_SIZE); + if (ret) { + puts("Err:--- UFS unit Descriptor"); + return ret; + } + memcpy(out_desc, desc, sizeof(*out_desc)); + return ret; +} + +void ufshcd_print_unit_descriptor(struct unit_descriptor *desc) +{ + printf("--- UFS unit Descriptor (IDN: 02h) ---\n"); + printf(" Name | Value | Description\n"); + printf("---------------------------------------------\n"); + + /* Descriptor Header */ + printf("Length | %d | Size of descriptor\n", desc->length); + printf("DescriptorIdn | 0x%x | unit Descriptor ID\n", + desc->descriptor_idn); + printf("BunitIndex | 0x%x | Logical unit Index\n", + desc->bunit_index); + + /* configuration Parameters */ + printf("luEnable | 0x%x | LU Enabled (01h) or Disabled (00h)\n", + desc->lu_enable); + printf("BootLunId | 0x%x| Boot LU ID (01h: A, 02h: B\n", + desc->boot_lun_id); + printf("luWriteProtect | 0x%x | WP (01h: power-on, 02h: Perm)\n", + desc->lu_write_protect); + printf("luQueueDepth | 0x%x | Queue depth (0 if shared)\n", + desc->lu_queue_depth); + printf("psaSensitive | 0x%x | Soldering sensitivity indicator\n", + desc->psa_sensitive); + printf("MemoryType | 0x%x | Memory type (00h: Normal\n", + desc->memory_type); + printf("DataReliability | 0x%x | power failure protection enabled\n", + desc->data_reliability); + printf("LogicalBlockSize| 0x%x | Block size exponent (2^n)\n", + desc->logical_block_size); + /* Capacity and geometry */ + printf("LogicalBlockCount | %d %d | Total addressable blocks\n", + be32_to_cpu(upper_32_bits(desc->logical_block_count)), + be32_to_cpu(lower_32_bits(desc->logical_block_count))); + printf("EraseBlockSize | 0x%x | Optimal erase granularity\n", + desc->erase_block_size); + printf("ProvisioningType | 0x%x | 00h: Full, 02h/03h: Thin\n", + desc->provisioning_type); + printf("phyMemResourceCount| %d %d Available physical resources\n", + lower_32_bits(desc->phy_mem_resource_count), + upper_32_bits(desc->phy_mem_resource_count)); + /* Performance and Extended Features */ + printf("ContextCapabilities | 0x%x | Context ID & LU Multiplier\n", + desc->context_capabilities); + printf("bLargeunitGranularity_M1|"); + printf("0x%x | Large unit granularity minus one", + desc->largeunit_granularity_M1); + puts("Reserved | Reserved for extension standards"); + puts("luNumWriteBoosterBufferAllocunits"); + printf(" | 0x%x | WB Buffer size in Alloc units\n", + desc->lu_num_write_booster_buffer_allocunits); +} + +#define str_eq !strcmp + +int set_param_field(struct ufs_unit_configurable_parameters *ud, + const char *name, u32 value) +{ + if (str_eq(name, "boot_lun_id")) + ud->boot_lun_id = (u8)value; + else if (str_eq(name, "lu_enable")) + ud->lu_enable = (u8)value; + else if (str_eq(name, "lu_write_protect")) + ud->lu_write_protect = (u8)value; + else if (str_eq(name, "memory_type")) + ud->memory_type = (u8)value; + else if (str_eq(name, "data_reliability")) + ud->data_reliability = (u8)value; + else if (str_eq(name, "num_allocunits")) + ud->num_allocunits = cpu_to_be32(value); + else if (str_eq(name, "provisioning_type")) + ud->provisioning_type = (u8)value; + else if (str_eq(name, "context_capabilities")) + ud->context_capabilities = (u16)value; + else if (str_eq(name, "lu_num_write_booster_buffer_allocunits")) + ud->lu_num_write_booster_buffer_allocunits = (u32)value; + else + return -1; + return 0; +} + +int ufshcd_update_lun(struct ufs_hba *hba, int lun, + const char *field, u32 val) +{ + int off; + int ret; + u8 *config; + struct ufs_unit_configurable_parameters *unit; + struct ufs_device_descriptor dev_desc; + + // Populate descriptor + if (g_cfg_desc.cfg_desc.length == 0 && + ufshcd_query_desc_idn_config(hba, &g_cfg_desc)) + return -EINVAL; + + if (ufshcd_query_desc_idn_device(hba, &dev_desc)) { + printf("Failed to read IDN 0h\n"); + return -EINVAL; + } + + config = (__u8 *)&g_cfg_desc; + off = dev_desc.ud0_base_offset + (lun * dev_desc.config_length); + + unit = (struct ufs_unit_configurable_parameters *)&config[off]; + + unit->logical_block_size = 0xC; + // Always enable the Lun + unit->lu_enable = 1; + // Enable Lun configuration + //g_cfg_desc.cfg_desc.write_booster_buffer_type = 0; + + // Now change the required value + if (set_param_field(unit, field, val)) { + printf("ilegal value to set %s\n", field); + return -EINVAL; + } + ufshcd_print_unit_param_descriptor(unit); + return ret; +} + +static int ufshcd_write_desc_param(struct ufs_hba *hba, enum desc_idn desc_id, + int desc_index, unsigned char param_offset, + unsigned char *param_write_buf, + unsigned char param_size) +{ + int ret; + int buff_len; + int selector = 0; + + if (desc_id >= QUERY_DESC_IDN_MAX || !param_size) + return -EINVAL; + + buff_len = param_size; + ret = ufshcd_query_descriptor_retry(hba, UPIU_QUERY_OPCODE_WRITE_DESC, + desc_id, desc_index, selector, + param_write_buf, &buff_len); + if (!ret) + return 0; + printf("Err: desc_id %d desc_index %d, param_offset %d,buflen %d,ret=%d\n", + (unsigned int)desc_id, desc_index, + (unsigned int)param_offset, buff_len, ret); + return ret; +} + +enum flag_idn ufshcd_get_flag(const char *name) +{ + if (str_eq(name, "deviceinit")) + return QUERY_FLAG_IDN_FDEVICEINIT; + + if (str_eq(name, "permanent_wpe")) + return QUERY_FLAG_IDN_PERMANENT_WPE; + + if (str_eq(name, "pwr_on_wpe")) + return QUERY_FLAG_IDN_PWR_ON_WPE; + + if (str_eq(name, "bkops_en")) + return QUERY_FLAG_IDN_BKOPS_EN; + + if (str_eq(name, "life_span_mode")) + return QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE; + + if (str_eq(name, "purge_enable")) + return QUERY_FLAG_IDN_PURGE_ENABLE; + + if (str_eq(name, "fphy_resource_removal")) + return QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL; + + if (str_eq(name, "busy_rtc")) + return QUERY_FLAG_IDN_BUSY_RTC; + + if (str_eq(name, "permanently_disable_fw_update")) + return QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE; + + if (str_eq(name, "write_booster_en")) + return QUERY_FLAG_IDN_F_WRITE_BOOSTER_EN; + + if (str_eq(name, "wb_buf_flush_en")) + return QUERY_FLAG_IDN_F_WB_BUF_FLUSH_EN; + + if (str_eq(name, "wb_buf_flush_h8")) + return QUERY_FLAG_IDN_F_WB_BUF_FLUSH_HIBERN8; + return -1; +} + +int ufshcd_query_user_flag(struct ufs_hba *hba, const char *flag_name) +{ + enum flag_idn idn; + bool flag_res; + int ret; + + if (!hba) { + printf("invalid hba\n"); + return -EINVAL; + } + + idn = ufshcd_get_flag(flag_name); + if ((int)idn < 0) { + printf("%s invalid\n", __func__); + return -EINVAL; + } + ret = + ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_READ_FLAG, idn, &flag_res); + if (!ret) { + printf("%s = %x\n", flag_name, flag_res); + return 0; + } + printf("%s idn query failed\n", flag_name); + return ret; +} + +#define CMD_HELP \ + "query:\n" \ + "\tDevice desc (0h)\n" \ + "\tGeometry desc(7h)\n" \ + "\tPower desc (8h)\n" \ + "\tUnit desc(2h)\n" \ + "\tConfig desc(1h)\n" \ + "****************************\n" \ + "\n\tset_lun [LUN] (parm)\n" \ + "\t\tlu_enable\n" \ + "\t\tboot_lun_id\n" \ + "\t\tlu_write_protect\n" \ + "\t\tmemory_type\n" \ + "\t\tdata_reliability\n" \ + "\t\tnum_allocunits\n" \ + "\t\tlogical_block_size\n" \ + "\t\tprovisioning_type\n" \ + "\t\tcontext_capabilities\n" \ + "\t\tlu_num_write_booster_buffer_allocunits\n\n" \ + "****************************\n" \ + "\n\tset_dev_desc [parm] [value]\n" \ + "\t\tdescriptor_idn\n" \ + "\t\tdevice\n" \ + "\t\tdevice_class\n" \ + "\t\tdevice_sub_class\n" \ + "\t\tnumber_lu\n" \ + "\t\tboot_enable\n" \ + "\t\tdescr_access_en\n" \ + "\t\tinit_power_mode\n" \ + "\t\thigh_priority_lun\n" \ + "\t\tsecure_removal_type\n" \ + "\t\tinitactive_icc_level\n" \ + "\t\tqueue_depth\n" \ + "\t\twrite_booster_buffer_preserve_user_space_en\n" \ + "\t\twrite_booster_buffer_type\n" \ + "\t\tspec_version\n" \ + "\t\tmanufacturer_id\n" \ + "\t\tperiodic_rtc_update\n" \ + "\t\tdevice_version\n" \ + "\t\tpsa_max_data_size\n" \ + "\t\textended_ufs_features_support\n" \ + "\t\tnum_shared_write_booster_buffer_allocunits\n" \ + "\t\twrite_booster_buffer_preserve_user_space_en\n" \ + "****************************\n" \ + "\n\tset_cfg_desc [parm] [value]\n" \ + "\t\tlength\n" \ + "\t\tdescriptor_idn\n" \ + "\t\tconf_desc_continue\n" \ + "\t\tboot_enable\n" \ + "\t\tdescr_access_en\n" \ + "\t\tinitpower_mode\n" \ + "\t\thigh_priority_lun\n" \ + "\t\tsecure_removal_type\n" \ + "\t\tinit_active_icc_level\n" \ + "\t\tperiodic_rtc_update\n" \ + "\t\treserved_HPB\n" \ + "\t\trpmb_region_enable\n" \ + "\t\trpmb_region1_size\n" \ + "\t\trpmb_region2_size\n" \ + "\t\trpmb_region3_size\n" \ + "\t\twrite_booster_buffer_preserve_user_space_en\n" \ + "\t\twrite_booster_buffer_type\n" \ + "\t\tnum_shared_write_booster_buffer_allocunits\n" \ + "****************************\n" \ + "\n\tflag [flag name]\n" \ + "\t\tdeviceinit\n" \ + "\t\tpermanent_wpe\n" \ + "\t\tpwr_on_wpe\n" \ + "\t\tbkops_en\n" \ + "\t\tlife_span_mode\n" \ + "\t\tpurge_enable\n" \ + "\t\tfphy_resource_removal\n" \ + "\t\tbusy_rtc\n" \ + "\t\tpermanently_disable_fw_update\n" \ + "\t\twrite_booster_en\n" \ + "\t\twb_buf_flush_en\n" \ + "\t\twb_buf_flush_h8\n" \ + "****************************\n" \ + "\texamples :\n" \ + "\n\nto write lun please\n" \ + "\tufs query 0 1\n" \ + "\tfix the luns, disable lu_enable\n" \ + "\tuse ufs set [LUN] num_allocunits=xxx to set size\n" \ + "\tufs set_cfg_desc num_shared_write_booster_buffer_allocunits 100000\n" \ + "\tufs set_cfg_desc write_booster_buffer_type 1\n" \ + "\tufs commit\n" + +void ufs_tool_help(void) +{ + puts(CMD_HELP); +} + +void idn_read(struct ufs_hba *hba, int idn, int lun) +{ + struct ufs_device_descriptor device_desc; + struct unit_descriptor unit_desc; + struct ufs_geometry_descriptor geometry; + struct ufs_power_parameters_descriptor power_desc; + + switch (idn) { + case QUERY_DESC_IDN_DEVICE: /* 0x0 */ + if (!ufshcd_query_desc_idn_device(hba, &device_desc)) + ufshcd_print_device_descriptor(&device_desc); + break; + + case QUERY_DESC_IDN_GEOMETRY: /* 0x7 */ + if (!ufshcd_query_desc_idn_geometry(hba, &geometry)) { + ufshcd_print_idn_geometry_desc(&geometry); + return; + } + printf("Failed to read geometry\n"); + break; + + case QUERY_DESC_IDN_UNIT: /* 0x2 */ + unit_desc.bunit_index = lun; + if (!ufshcd_query_desc_idn_unit(hba, lun, &unit_desc)) + ufshcd_print_unit_descriptor(&unit_desc); + break; + + case QUERY_DESC_IDN_POWER: + if (!ufshcd_query_desc_idn_power(hba, lun, &power_desc)) + print_ufs_power_parameters(&power_desc); + break; + + case QUERY_DESC_IDN_CONFIGURATION: /* 0x1 */ + if (ufshcd_query_desc_idn_device(hba, &device_desc)) { + printf("Failed to read IDN 0h\n"); + return; + } + + if (ufshcd_query_desc_idn_geometry(hba, &geometry)) { + printf("Failed to read geometry\n"); + return; + } + + ufshcd_query_desc_idn_config(hba, &g_cfg_desc); + ufshcd_print_idn_desc_config(&g_cfg_desc, &geometry, + device_desc.ud0_base_offset, + device_desc.config_length); + break; + + default: + printf("Ilegal IDN %d\n", idn); + ufs_tool_help(); + } +} + +static int set_config_desc_field(struct config_descriptor *desc, + const char *fieldname, u32 value) +{ + /* --- Descriptor Header & Flow Control --- */ + if (!strcmp(fieldname, "length")) + desc->length = (u8)value; + else if (!strcmp(fieldname, "descriptor_idn")) + desc->descriptor_idn = (u8)value; + else if (!strcmp(fieldname, "conf_desc_continue")) + desc->conf_desc_continue = (u8)value; + /* --- Device Descriptor Configurable Parameters --- */ + else if (!strcmp(fieldname, "boot_enable")) + desc->boot_enable = (u8)value; + else if (!strcmp(fieldname, "descr_access_en")) + desc->descr_access_en = (u8)value; + else if (!strcmp(fieldname, "initpower_mode")) + desc->initpower_mode = (u8)value; + else if (!strcmp(fieldname, "high_priority_lun")) + desc->high_priority_lun = (u8)value; + else if (!strcmp(fieldname, "secure_removal_type")) + desc->secure_removal_type = (u8)value; + else if (!strcmp(fieldname, "init_active_icc_level")) + desc->init_active_icc_level = (u8)value; + else if (!strcmp(fieldname, "periodic_rtc_update")) + desc->periodic_rtc_update = (u16)value; + else if (!strcmp(fieldname, "reserved_HPB")) + desc->reserved_HPB = (u8)value; + /* --- RPMB Configuration Parameters --- */ + else if (!strcmp(fieldname, "rpmb_region_enable")) + desc->rpmb_region_enable = (u8)value; + else if (!strcmp(fieldname, "rpmb_region1_size")) + desc->rpmb_region1_size = (u8)value; + else if (!strcmp(fieldname, "rpmb_region2_size")) + desc->rpmb_region2_size = (u8)value; + else if (!strcmp(fieldname, "rpmb_region3_size")) + desc->rpmb_region3_size = (u8)value; + else if (!strcmp(fieldname, + "write_booster_buffer_preserve_user_space_en")) + desc->write_booster_buffer_preserve_user_space_en = (u8)value; + else if (!strcmp(fieldname, "write_booster_buffer_type")) + desc->write_booster_buffer_type = (u8)value; + else if (!strcmp(fieldname, + "num_shared_write_booster_buffer_allocunits")) + desc->num_shared_write_booster_buffer_allocunits = + cpu_to_be32(value); + else + return -1; + return 0; +} + +static int set_device_desc_field(struct ufs_device_descriptor *dev_desc, + const char *name, u32 value) +{ + if (!strcmp(name, "descriptor_idn")) + dev_desc->descriptor_idn = (u8)value; + else if (!strcmp(name, "device")) + dev_desc->device = (u8)value; + else if (!strcmp(name, "device_class")) + dev_desc->device_class = (u8)value; + else if (!strcmp(name, "device_sub_class")) + dev_desc->device_sub_class = (u8)value; + else if (!strcmp(name, "number_lu")) + dev_desc->number_lu = (u8)value; + else if (!strcmp(name, "boot_enable")) + dev_desc->boot_enable = (u8)value; + else if (!strcmp(name, "descr_access_en")) + dev_desc->descr_access_en = (u8)value; + else if (!strcmp(name, "bInitPowerMode")) + dev_desc->init_power_mode = (u8)value; + else if (!strcmp(name, "high_priority_lun")) + dev_desc->high_priority_lun = (u8)value; + else if (!strcmp(name, "secure_removal_type")) + dev_desc->secure_removal_type = (u8)value; + else if (!strcmp(name, "initactive_icc_level")) + dev_desc->init_active_icc_level = (u8)value; + else if (!strcmp(name, "queue_depth")) + dev_desc->queue_depth = (u8)value; + else if (!strcmp(name, "write_booster_buffer_preserve_user_space_en")) + dev_desc->write_booster_buffer_preserve_user_space_en = (u8)value; + else if (!strcmp(name, "write_booster_buffer_type")) + dev_desc->write_booster_buffer_type = (u8)value; + else if (!strcmp(name, "spec_version")) + dev_desc->spec_version = cpu_to_be16(value); + else if (!strcmp(name, "manufacturer_id")) + dev_desc->manufacturer_id = cpu_to_be16(value); + else if (!strcmp(name, "periodic_rtc_update")) + dev_desc->periodic_rtc_update = cpu_to_be16(value); + else if (!strcmp(name, "device_version")) + dev_desc->device_version = cpu_to_be16(value); + else if (!strcmp(name, "psa_max_data_size")) + dev_desc->psa_max_data_size = cpu_to_be32(value); + else if (!strcmp(name, "extended_ufs_features_support")) + dev_desc->extended_ufs_features_support = cpu_to_be32(value); + else if (!strcmp(name, "num_shared_write_booster_buffer_allocunits")) + dev_desc->num_shared_write_booster_buffer_allocunits = be32_to_cpu(value); + else + return -1; + return 0; +} + +int ufs_tool_set_config_desc(const char *name, u32 value) +{ + struct ufs_hba *hba = ufstool_probe_dev(0); + + if (g_cfg_desc.cfg_desc.length == 0) { + if (ufshcd_query_desc_idn_config(hba, &g_cfg_desc)) { + printf("Failed to read device descriptor\n"); + return -EINVAL; + } + } + + if (set_config_desc_field(&g_cfg_desc.cfg_desc, name, value)) { + printf("Failed to set parameters %s descriptor\n", name); + return -EINVAL; + } + ufshcd_print_config_descriptor(&g_cfg_desc.cfg_desc); + return 0; +} + +int ufs_tool_set_device_desc(const char *name, u32 value) +{ + struct ufs_device_descriptor device_desc; + struct ufs_hba *hba = ufstool_probe_dev(0); + int ret; + + if (ufshcd_query_desc_idn_device(hba, &device_desc)) { + printf("Failed to read device descriptor\n"); + return -EINVAL; + } + + if (set_device_desc_field(&device_desc, name, value)) { + printf("Failed to set parameters %s descriptor\n", name); + return -EINVAL; + } + + ret = ufshcd_write_desc_param(hba, QUERY_DESC_IDN_DEVICE, + 0, 0, (void *)&device_desc, + UFS_DESC_SIZE); + if (ret) { + printf("Failed to write %s\n", name); + return -EINVAL; + } + return ret; +} + +int ufs_tool_commit(void) +{ + int ret; + u8 *config; + struct ufs_hba *hba = ufstool_probe_dev(0); + + config = (__u8 *)&g_cfg_desc; + ufshcd_cache_flush(config, QUERY_DESC_UNIT_DEF_SIZE); + ret = ufshcd_write_desc_param(hba, + QUERY_DESC_IDN_CONFIGURATION, + 0, 0, config, + g_cfg_desc.cfg_desc.length); + + if (ret) + printf("Failed to write Configuration\n"); + return ret; +} + +int ufs_tool_clearall(void) +{ + int ret; + u8 *config; + struct ufs_device_descriptor device_desc; + struct ufs_hba *hba = ufstool_probe_dev(0); + + // Populate descriptor + if (g_cfg_desc.cfg_desc.length == 0 && + ufshcd_query_desc_idn_config(hba, &g_cfg_desc)) { + return -EINVAL; + } + config = (__u8 *)&g_cfg_desc; + + ufshcd_cache_flush(config, QUERY_DESC_UNIT_DEF_SIZE); + if (ufshcd_query_desc_idn_device(hba, &device_desc)) { + printf("Failed to read IDN 0h\n"); + return -1; + } + + ufshcd_clearall_luns(&g_cfg_desc, + device_desc.ud0_base_offset, + device_desc.config_length); + + ret = ufshcd_write_desc_param(hba, + QUERY_DESC_IDN_CONFIGURATION, + 0, 0, config, + g_cfg_desc.cfg_desc.length); + + if (ret) + printf("Failed to Clear Configuration\n"); + return ret; +} + +int ufs_tool_set_lun(int lun, const char *parm, u32 val) +{ + struct ufs_hba *hba = ufstool_probe_dev(0); + + return ufshcd_update_lun(hba, lun, parm, val); +} + +int ufs_tool_query_flag(const char *flag) +{ + struct ufs_hba *hba = ufstool_probe_dev(0); + + return ufshcd_query_user_flag(hba, flag); +} + +int ufs_tool_query(int lun, int idn) +{ + struct ufs_hba *hba; + + hba = ufstool_probe_dev(0); + if (!hba) { + printf("ufs_tool: failed to get hba\n"); + return -1; + } + + if (lun >= 0 && idn >= 0) { + idn_read(hba, idn, lun); + return 0; + } + + ufs_tool_help(); + return -1; +} diff --git a/drivers/ufs/ufs_tool.h b/drivers/ufs/ufs_tool.h new file mode 100644 index 00000000000..7ba852ceed0 --- /dev/null +++ b/drivers/ufs/ufs_tool.h @@ -0,0 +1,394 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#ifndef __UFS2_H +#define __UFS2_H +#include + +/* + * In a QUERY REQUEST UPIU, the Device Descriptor + * is addressed setting: DESCRIPTOR IDN = 00h + */ +struct ufs_device_descriptor { + u8 length; /* Size of this descriptor */ + u8 descriptor_idn; /* Descriptor Type Identifier (00h) */ + u8 device; /* Device type (00h: Device) */ + u8 device_class; + /* UFS Device Class (00h: Mass Storage) */ + u8 device_sub_class; + /* UFS Mass Storage Subclass (e.g., 00h: Embedded Bootable) */ + u8 protocol; + /* Protocol supported by UFS Device (00h: SCSI) */ + u8 number_lu; + /* Number of Logical units (non-well known) */ + u8 number_wlu; + /* Number of Well known Logical units (MDV = 04h) */ + u8 boot_enable; /* Boot Enable (01h: enabled) */ + u8 descr_access_en; + /* Descriptor Access Enable during partial boot initialization */ + + /* --- power Management & Priority (Offset 0x0A) --- */ + u8 init_power_mode; + /* Initial power Mode after device initi (00h: UFS-Sleep, 01h: */ + /* Active) [4, 5] */ + u8 high_priority_lun; + /* High Priority Logical unit (7Fh if all LUs have same priority) */ + u8 secure_removal_type; + /* Secure Removal Type (00h: erase, 01h: overwrite/erase, etc.) */ + u8 security_lu; + /* Support for security LU (01h: RPMB) */ + u8 background_ops_term_lat; + /* Background Operations Termination Latency (in units of 10 ms) */ + u8 init_active_icc_level; + /* Initial Active ICC Level after power on/reset (00h to 0Fh) */ + + /* --- Version, Naming, & configuration Pointers (Offset 0x10) --- */ + u16 spec_version; + /* Specification version in BCD format (e.g., 3.1 = 0310h) */ + u16 manufacture_date; /* Manufacturing Date in BCD format */ + u8 manufacturer_name; + /* Index to the Manufacturer Name string descriptor */ + u8 product_name; + /* Index to the Product Name string descriptor */ + u8 serial_number; + /* Index to the Serial Number string descriptor */ + u8 oem_id; + /* Index to the OEM ID string descriptor */ + u16 manufacturer_id; + /* Manufacturer ID code and Bank Index (JEP106 definition) */ + u8 ud0_base_offset; + /* Offset of unit Descriptor 0 configurable parameters within the */ + /* configuration Descriptor */ + u8 config_length; + /* Total size of the configurable unit Descriptor parameters */ + u8 device_rtt_cap; + /* Maximum number of outstanding Ready To Transfer (RTT) UPIUs */ + /* supported (min 2) */ + u16 periodic_rtc_update; + /* Frequency and method of Real-Time Clock update */ + + /* --- Feature Support & Device Status (Offset 0x1F) --- */ + u8 ufs_features_support; + /* Bitmap indicating supported UFS features (FFU, PSA, etc.) */ + u8 ffu_timeout; + /* Field Firmware Update Timeout (in seconds) */ + u8 queue_depth; + /* Queue Depth (0: per-LU queueing; 1-255: shared queue depth) */ + u16 device_version; /* Device Version */ + u8 num_secure_wp_area; + /* Total number of Secure Write Protect */ + /* Areas supported (max 32) */ + u32 psa_max_data_size; + /* PSA Maximum Data Size (in units of 4 Kbyte) */ + u8 psa_state_timeout; + /* PSA State Timeout (max command timeout for b_psa_state change) */ + u8 product_revision_level; + /* Index to the Product Revision Level string descriptor */ + u8 reserved_2bh[18]; /* reserved */ + u8 reserved_30h[19]; + /* Reserved for Unified Memory Extension standard */ + u8 reserved_40h[20]; + /* Reserved for Host Performance Booster (HPB) Extension Standard */ + u8 reserved_43h[21]; /* Reserved */ + + /* --- Extended Feature Support (Offset 0x4F) --- */ + u32 extended_ufs_features_support; + /* Extended UFS Features Support (including WriteBooster, */ + /* offset 53h Performance Throttling) */ + u8 write_booster_buffer_preserve_user_space_en; + /* offset 54h Preserve User Space mode for WriteBooster Buffer */ + u8 write_booster_buffer_type; + /* offset 55h WriteBooster Buffer Type + * (0h:dedicated buffer,01h: shared buffer) + */ + u32 num_shared_write_booster_buffer_allocunits; + /* WriteBooster Buffer size for shared configuration */ + /* (in Allocation units) */ +} __packed; + +struct unit_descriptor { + u8 length; /* Size of this descriptor (Default: 2Dh) */ + u8 descriptor_idn; /* unit Descriptor Type Identifier (02h) */ + u8 bunit_index; /* The index of the Logical unit */ + + /* --- configuration Parameters --- */ + u8 lu_enable; /* 01h: LU enabled, 00h: LU disabled */ + u8 boot_lun_id; + /* 01h: Boot LU A, 02h: Boot LU B, 00h: Not bootable */ + u8 lu_write_protect; + /* 01h: power-on WP, 02h: Permanent WP, 00h: None */ + u8 lu_queue_depth; + /* LU-specific queue depth (0 if shared queuing is used) */ + u8 psa_sensitive; + /* 01h: Sensitive to soldering, 00h: Not sensitive */ + u8 memory_type; + /* Memory type (e.g., 00h: Normal, 01h: System Code) */ + u8 data_reliability; + /* 01h: Data protected against power failure */ + u8 logical_block_size; + /* Logical Block Size exponent (e.g., 0Ch = 4 Kbyte) */ + + /* --- Capacity and geometry --- */ + u64 logical_block_count; + /* Total number of addressable logical blocks in the LU */ + u32 erase_block_size; + /* Optimal granularity for erase/discard operations */ + u8 provisioning_type; + /* 00h: Full, 02h: Thin (TPRZ=0), 03h: Thin (TPRZ=1) */ + u64 phy_mem_resource_count; + /* Total physical memory resources available to the LU */ + + /* --- Performance and Extended Features --- */ + u16 context_capabilities; + /* Bits [3:0]: MaxContextID; Bits [6:4]: Large unit Multiplier */ + u8 largeunit_granularity_M1; + /* Large unit Granularity = 1MB * (value + 1) */ + u8 reserved[11]; /* Reserved for HPB Extension Standard */ + u32 lu_num_write_booster_buffer_allocunits; + /* WriteBooster Buffer size in Allocation units */ +} __packed; + +/* + * + * UFS Geometry Descriptor (IDN 07h) + * Total Size: 87 bytes (0x57) + */ +struct ufs_geometry_descriptor { + /* --- Descriptor Header (Offset 0x00) --- */ + u8 length; /* Size of this descriptor (57h) */ + u8 descriptor_idn; /* geometry Descriptor ID (07h) */ + u8 media_technology; /* Reserved (00h) */ + u8 reserved_03h; /* Reserved (00h) */ + + /* --- Capacity and Device geometry (Offset 0x04) --- */ + u64 total_raw_device_capacity; /* Total memory available (units of 512B) */ + u8 max_number_lu; /* Max LUs: 00h (8 LUs) or 01h (32 LUs) */ + u32 segment_size; /* Segment size (units of 512B) */ + u8 allocationunit_size; /* Allocation unit Size (number of segments) */ + u8 min_addr_block_size; + /* Min addressable block size (units of 512B, min 08h) */ + u8 optimal_read_block_size; /* Optimal read granularity (units of 512B) */ + u8 optimal_write_block_size; /* Optimal write granularity (units of 512B) */ + u8 max_in_buffer_size; /* Max data-in buffer size (units of 512B) */ + u8 max_out_buffer_size; /* Max data-out buffer size (units of 512B) */ + u8 rpmb_read_write_size; /* Max RPMB frames (256B) per command */ + u8 dynamic_capacity_resource_policy; /* 00h: per LU, 01h: per memory type */ + u8 data_ordering; /* 01h: Out-of-order data transfer supported */ + u8 max_contex_id_number; /* Maximum supported contexts (min 5) */ + u8 sys_data_tagunit_size; /* Granularity for System Data Tagging */ + u8 sys_data_tag_res_size; /* Max storage area for system data tagging */ + u8 supported_sec_r_types; /* Bitmap of supported Secure Removal Types */ + u16 supported_memory_types; + /* Bitmap of supported memory types (Normal, Enhanced, etc.) */ + + /* --- Memory Type Capacity and Factors (Offset 0x20) --- */ + u32 system_code_max_n_alloc_u; + /* Max Allocation units for System Code memory */ + u16 system_code_cap_adj_fac; + /* Capacity Adjustment Factor for System Code */ + u32 non_persist_max_n_alloc_u; + /* Max Allocation units for Non-Persistent memory */ + u16 non_persist_cap_adj_fac; + /* Capacity Adjustment Factor for Non-Persistent */ + u32 enhanced1_max_n_alloc_u; + /* Max Allocation units for Enhanced memory */ + /* Capacity Adjustment Factor for Enhanced */ + u16 enhanced1_cap_adj_fac; + u32 enhanced2_max_n_alloc_u; + /* Max Allocation units for Enhanced memory */ + /* Capacity Adjustment Factor for Enhanced */ + u16 enhanced2_cap_adj_fac; + u32 enhanced3_max_n_alloc_u; + /* Max Allocation units for Enhanced memory */ + /* Capacity Adjustment Factor for Enhanced */ + u16 enhanced3_cap_adj_fac; + u32 enhanced4_max_n_alloc_u; + /* Max Allocation units for Enhanced memory */ + /* Capacity Adjustment Factor for Enhanced */ + u16 enhanced4_cap_adj_fac; + /* --- Performance Optimization (Offset 0x44) --- */ + /* Optimal Logical Block Size per memory type */ + u32 optimal_logical_block_size; + u8 reserved_hpb[4]; // 48h: Reserved for HPB Extension + u16 reserved2; // 4Dh: Reserved + /* WriteBooster Parameters */ + /* 4Fh: Max total WriteBooster Buffer size */ + u32 write_booster_buffer_max_alloc_units; + /* 53h: Max LUs supporting WriteBooster (Valid: 1) */ + u8 device_max_write_booster_lus; + /* 54h: LBA space reduction factor (e.g., 3 for TLC) */ + u8 write_booster_buffer_capadjfac; + /* 55h: User space vs Preserve types */ + u8 supported_write_booster_buffer_userspace_reduction_types; + /* 56h: LU based vs Shared buffer type */ + u8 supported_write_booster_buffer_types; +} __packed; + +/** + * UFS configuration Descriptor - Header and Device configurable Parameters + * Based on Table 14.10 (INDEX = 00h) + */ +struct config_descriptor { + /* --- Descriptor Header --- */ + u8 length; + /* 00h: Size of this descriptor (MDV: E6h) */ + u8 descriptor_idn; + /* 01h: configuration Descriptor Type ID (01h) */ + + /* --- configuration Flow Control --- */ + u8 conf_desc_continue; + /* 02h: 00h = Last descriptor (apply config); */ + /* 01h = More descriptors follow */ + + /* --- Device Descriptor configurable Parameters --- */ + u8 boot_enable; + /* 03h: Enables boot feature (01h: enabled) */ + u8 descr_access_en; + /* 04h: Enables Device Descriptor access after partial init */ + u8 initpower_mode; + /* 05h: Initial power Mode after reset (01h: Active) */ + u8 high_priority_lun; + /* 06h: LUN of the high priority logical unit */ + u8 secure_removal_type; + /* 07h: configures secure removal (e.g., 00h: erase) */ + u8 init_active_icc_level; + /* 08h: Initial ICC level after reset (00h-0Fh) */ + u16 periodic_rtc_update; + /* 09h: Frequency/method of Real-Time Clock update */ + u8 reserved_HPB; + /* 0Bh: Reserved for Host Performance Booster */ + + /* --- RPMB configuration Parameters --- */ + u8 rpmb_region_enable; + /* 0Ch: Bitmask to enable RPMB regions 1-3 */ + u8 rpmb_region1_size; + /* 0Dh: Size of RPMB region 1 in 128KB units */ + u8 rpmb_region2_size; + /* 0Eh: Size of RPMB region 2 in 128KB units */ + u8 rpmb_region3_size; + /* 0Fh: Size of RPMB region 3 in 128KB units */ + + /* --- WriteBooster configuration Parameters --- */ + u8 write_booster_buffer_preserve_user_space_en; + /* 10h: 01h = Preserve user space */ + u8 write_booster_buffer_type; + /* 11h: 00h = Dedicated LU; 01h = Shared */ + u32 num_shared_write_booster_buffer_allocunits; + /* 12h: Shared buffer size in Alloc units */ +} __packed; + +/** + * UFS unit Descriptor configurable Parameters + * Found within the configuration Descriptor (IDN: 01h) + * Based on Table 14.12 + */ +struct ufs_unit_configurable_parameters { + u8 lu_enable; /* 01h: Enabled, 00h: Disabled */ + + u8 boot_lun_id; /* 0 Not Bootable,1-A or 2-B */ + + /* 01h: Boot LU A, 02h: Boot LU B, 00h: Not bootable */ + u8 lu_write_protect; + /* 00h: None, 01h: power-on WP, 02h: Permanent WP */ + u8 memory_type; + /* Memory type (00h: Normal, 01h: System Code) */ + + /* Number of allocation units assigned to the LU. */ + u32 num_allocunits; + + u8 data_reliability; /* 01h: Protected against power failure */ + + /* Block size exponent (e.g., 0Ch = 4 KB) */ + u8 logical_block_size; + + /* 00h: Full, 02h: Thin (TPRZ=0), 03h: Thin (TPRZ=1) */ + u8 provisioning_type; + + u16 context_capabilities; /* LU Context support and multipliers */ + + u8 reserved_0dh[6]; /* Reserved */ + + u8 reserved_hpb[7]; + /* Reserved for Host Performance Booster Extension */ + + u32 lu_num_write_booster_buffer_allocunits; + /* WriteBooster Buffer size in Allocation units. */ +} __packed; + +struct ufs_config_descriptor { + struct config_descriptor cfg_desc; + /* + * NOTE: For INDEX = 00h, configurable parameters + * for unit Descriptors 0 to 7 + */ + /* follow this header at the offset defined + * by b_ud0_base_offset + */ + unsigned char space[QUERY_DESC_CONFIGURATION_DEF_SIZE - + sizeof(struct config_descriptor)]; +} __packed; + +/** + * UFS power Parameter Element Format + * Based on Table 7.13 + */ +struct ufs_power_parameter_element { + /* Bits [15:14]: unit (00b:n_a, 01b:u_a, 10b:m_a, 11b:A) */ + /* Bits [13:10]: Reserved (0000b) */ + /* Bits [9:0]: Value (Maximum current expected) */ + u16 upower_value; +}; + +/** + * UFS power Parameters Descriptor (IDN: 08h) + * Based on Table 14.16 + */ +struct ufs_power_parameters_descriptor { + /* Offset 00h */ + u8 length; /* Size of descriptor (62h) */ + u8 descriptor_idn; /* Descriptor Type ID (08h) */ + + /* Offset 02h: Maximum current for VCC (16 levels) */ + struct ufs_power_parameter_element active_icc_levels_vcc[5]; + /* Offset 22h: Maximum current for VCCQ (16 levels) */ + struct ufs_power_parameter_element active_icc_levels_vccq[5]; + /* Offset 42h: Maximum current for VCCQ2 (16 levels) */ + struct ufs_power_parameter_element active_icc_levels_vccq2[5]; +} __packed; + +/** + * UFS Temperature-related Attributes + * These are device-level attributes (Type 'D') retrieved via Query Requests. + * All temperature values follow the formula: Temperature (ºC) = Value – 80. + */ +struct ufs_temp_parameters_descriptor { + /** + * IDN 18h: bDeviceCaseRoughTemperature [1] + * Represents the rough package case surface temperature. + * 0: Unknown Temperature. + * 1 ~ 250: Calculated as (Value - 80) in Celsius (-79ºC ~ 170ºC). + */ + u8 device_case_rough_temperature; + /** + * IDN 19h: bDeviceTooHighTempBoundary + * High temperature boundary that triggers + * the TOO_HIGH_TEMP exception event. + * 100 ~ 250: Calculated as (Value - 80) in Celsius (20ºC ~ 170ºC). + */ + u8 device_too_high_temp_boundary; + /* + * IDN 1Ah: bDeviceTooLowTempBoundary + * Low temperature boundary that triggers the TOO_LOW_TEMP + * exception event. + * 1 ~ 80: Calculated as (Value - 80) in Celsius (-79ºC ~ 0ºC). + */ + u8 device_too_low_temp_boundary; + /** + * IDN 1Bh: bThrottlingStatus + * Indicates if the device is limiting performance due + * to specific conditions. + * bit 0: Set to 1 if performance is throttled due to Temperature. + * bits [7:1]: Reserved. + */ + u8 throttling_status; +} __packed__; + +#endif -- 2.43.0