Date: Sun, 15 Jun 2025 06:09:39 GMT From: Warner Losh <imp@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: 1349a733cf28 - main - ufshci: Introduce the ufshci(4) driver Message-ID: <202506150609.55F69dn3021406@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by imp: URL: https://cgit.FreeBSD.org/src/commit/?id=1349a733cf2828e0040cabef89eeadc3ff00c40b commit 1349a733cf2828e0040cabef89eeadc3ff00c40b Author: Jaeyoon Choi <j_yoon.choi@samsung.com> AuthorDate: 2025-06-13 18:33:01 +0000 Commit: Warner Losh <imp@FreeBSD.org> CommitDate: 2025-06-15 06:08:56 +0000 ufshci: Introduce the ufshci(4) driver This commit adds a storage driver that supports the Universal Flash Storage Host Controller Interface (UFSHCI) on FreeBSD. Universal Flash Storage (UFS) is a flash-based mobile storage device that replaces eMMC, aiming for high performance with low power. The UFS Host Controller Interface (UFSHCI) is the host side controller and connects UFS device to a system bus, such as PCIe. The code targets the latest standards: - UFS 4.1: https://www.jedec.org/standards-documents/docs/jesd220g - UFSHCI 4.1: https://www.jedec.org/standards-documents/docs/jesd223f The ufshci(4) driver implements controller/device initialization, interrupt, single-doorbell(SDB) queue based IO requests. Support for multi-queue (MCQ) IO requests is planned for a later commit. Implemented features: - PCIe bus support - legacy(INTx) Interrupt Handling - UIC command support - UTP Transfer Request (UTR) support - UTP Task Management Request (UTMR) support - single doorbell queue (SDB) with multiple queue depth - SCSI command set support - sysctl Work in progress: - multi-Circular Queue (per-CPU IO queues) - MSI-X interrupt Support - write booster - write Protect - Host Performance Booster (HPB) - interrupt aggregation - ARM based system bus support - ufs-utils port Tests were performed on QEMU and an Intel-based laptop. Since QEMU has an emulated UFS device, I tested on QEMU. How to test on QEMU: 1. Run QEMU $ qemu-system-x86_64 ... -device ufs -drive file=blk1g.bin,format=raw,if=none,id=luimg -device ufs-lu,drive=luimg,lun=0 2. Loading/unloading the ufshci module on QEMU $ kldload /usr/obj/usr/src/amd64.amd64/sys/modules/ufshci/ufshci.ko $ kldunload ufshci Testing on real hardware: - Samsung Galaxy Book S (Intel Lakefield) with UFS 3.0 - Lenovo duet 3 11ian8 (Intel N100) with UFS 2.1 Sponsored by: Samsung Electronics Reviewed by: imp Differential Revision: https://reviews.freebsd.org/D50370 --- sbin/camcontrol/camcontrol.c | 13 + sys/cam/cam_ccb.h | 20 + sys/cam/scsi/scsi_xpt.c | 1 + sys/dev/ufshci/ufshci.c | 76 +++ sys/dev/ufshci/ufshci.h | 939 ++++++++++++++++++++++++++++++++++++++ sys/dev/ufshci/ufshci_ctrlr.c | 503 ++++++++++++++++++++ sys/dev/ufshci/ufshci_ctrlr_cmd.c | 53 +++ sys/dev/ufshci/ufshci_dev.c | 428 +++++++++++++++++ sys/dev/ufshci/ufshci_pci.c | 260 +++++++++++ sys/dev/ufshci/ufshci_private.h | 508 +++++++++++++++++++++ sys/dev/ufshci/ufshci_reg.h | 469 +++++++++++++++++++ sys/dev/ufshci/ufshci_req_queue.c | 490 ++++++++++++++++++++ sys/dev/ufshci/ufshci_req_sdb.c | 427 +++++++++++++++++ sys/dev/ufshci/ufshci_sim.c | 372 +++++++++++++++ sys/dev/ufshci/ufshci_sysctl.c | 233 ++++++++++ sys/dev/ufshci/ufshci_uic_cmd.c | 224 +++++++++ sys/modules/ufshci/Makefile | 22 + 17 files changed, 5038 insertions(+) diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index a2e65055fcaa..19684c044ef5 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -5400,6 +5400,19 @@ cts_print(struct cam_device *device, struct ccb_trans_settings *cts) nvmf_transport_type(nvmf->trtype)); } } + if (cts->transport == XPORT_UFSHCI) { + struct ccb_trans_settings_ufshci *ufshci = + &cts->xport_specific.ufshci; + + if (ufshci->valid & CTS_UFSHCI_VALID_MODE) { + fprintf(stdout, "%sHigh Speed Gear: %d (%d max)\n", + pathstr, ufshci->hs_gear, ufshci->max_hs_gear); + fprintf(stdout, "%sUnipro TX lanes: %d (%d max)\n", pathstr, + ufshci->tx_lanes, ufshci->max_tx_lanes); + fprintf(stdout, "%sUnipro RX lanes: %d (%d max)\n", pathstr, + ufshci->rx_lanes, ufshci->max_rx_lanes); + } + } if (cts->protocol == PROTO_ATA) { struct ccb_trans_settings_ata *ata= &cts->proto_specific.ata; diff --git a/sys/cam/cam_ccb.h b/sys/cam/cam_ccb.h index 15e136e8a072..da98b98ba7d1 100644 --- a/sys/cam/cam_ccb.h +++ b/sys/cam/cam_ccb.h @@ -298,6 +298,7 @@ typedef enum { XPORT_NVME, /* NVMe over PCIe */ XPORT_MMCSD, /* MMC, SD, SDIO card */ XPORT_NVMF, /* NVMe over Fabrics */ + XPORT_UFSHCI, /* Universal Flash Storage Host Interface */ } cam_xport; #define XPORT_IS_NVME(t) ((t) == XPORT_NVME || (t) == XPORT_NVMF) @@ -1065,6 +1066,24 @@ struct ccb_trans_settings_nvmf uint8_t trtype; }; +struct ccb_trans_settings_ufshci +{ + u_int valid; /* Which fields to honor */ + /* + * Ensure the validity of the information for the Unipro link + * (GEAR, SPEED, LANE) + */ +#define CTS_UFSHCI_VALID_LINK 0x01 + uint32_t speed; + uint8_t hs_gear; /* High Speed Gear (G1, G2, G3...) */ + uint8_t tx_lanes; + uint8_t rx_lanes; + uint8_t max_hs_gear; /* Maximum HS Gear */ + uint8_t max_tx_lanes; + uint8_t max_rx_lanes; +}; + + #include <cam/mmc/mmc_bus.h> struct ccb_trans_settings_mmc { struct mmc_ios ios; @@ -1138,6 +1157,7 @@ struct ccb_trans_settings { struct ccb_trans_settings_sata sata; struct ccb_trans_settings_nvme nvme; struct ccb_trans_settings_nvmf nvmf; + struct ccb_trans_settings_ufshci ufshci; } xport_specific; }; diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c index 2bb59cb2d92b..439dd2050a95 100644 --- a/sys/cam/scsi/scsi_xpt.c +++ b/sys/cam/scsi/scsi_xpt.c @@ -618,6 +618,7 @@ SCSI_XPT_XPORT(usb, USB); SCSI_XPT_XPORT(iscsi, ISCSI); SCSI_XPT_XPORT(srp, SRP); SCSI_XPT_XPORT(ppb, PPB); +SCSI_XPT_XPORT(ufshci, UFSHCI); #undef SCSI_XPORT_XPORT diff --git a/sys/dev/ufshci/ufshci.c b/sys/dev/ufshci/ufshci.c new file mode 100644 index 000000000000..84a9629e74b0 --- /dev/null +++ b/sys/dev/ufshci/ufshci.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 2025, Samsung Electronics Co., Ltd. + * Written by Jaeyoon Choi + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/module.h> + +#include "ufshci_private.h" + +MALLOC_DEFINE(M_UFSHCI, "ufshci", "ufshci(4) memory allocations"); + +int +ufshci_attach(device_t dev) +{ + struct ufshci_controller *ctrlr = device_get_softc(dev); + int status; + + status = ufshci_ctrlr_construct(ctrlr, dev); + if (status != 0) { + ufshci_ctrlr_destruct(ctrlr, dev); + return (status); + } + + ctrlr->config_hook.ich_func = ufshci_ctrlr_start_config_hook; + ctrlr->config_hook.ich_arg = ctrlr; + + if (config_intrhook_establish(&ctrlr->config_hook) != 0) + return (ENOMEM); + + return (0); +} + +int +ufshci_detach(device_t dev) +{ + struct ufshci_controller *ctrlr = device_get_softc(dev); + + config_intrhook_drain(&ctrlr->config_hook); + + ufshci_ctrlr_destruct(ctrlr, dev); + + return (0); +} + +void +ufshci_completion_poll_cb(void *arg, const struct ufshci_completion *cpl, + bool error) +{ + struct ufshci_completion_poll_status *status = arg; + + /* + * Copy status into the argument passed by the caller, so that the + * caller can check the status to determine if the the request passed + * or failed. + */ + memcpy(&status->cpl.response_upiu, &cpl->response_upiu, cpl->size); + status->error = error; + atomic_store_rel_int(&status->done, 1); +} + +static int +ufshci_modevent(module_t mod __unused, int type __unused, void *argp __unused) +{ + return (0); +} + +static moduledata_t ufshci_mod = { "ufshci", ufshci_modevent, 0 }; + +DECLARE_MODULE(ufshci, ufshci_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(ufshci, 1); +MODULE_DEPEND(ufshci, cam, 1, 1, 1); diff --git a/sys/dev/ufshci/ufshci.h b/sys/dev/ufshci/ufshci.h new file mode 100644 index 000000000000..9f0faaadeb57 --- /dev/null +++ b/sys/dev/ufshci/ufshci.h @@ -0,0 +1,939 @@ +/*- + * Copyright (c) 2025, Samsung Electronics Co., Ltd. + * Written by Jaeyoon Choi + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#ifndef __UFSHCI_H__ +#define __UFSHCI_H__ + +#include <sys/param.h> +#include <sys/endian.h> + +/* + * 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_overall_command_status { + UFSHCI_OCS_SUCCESS = 0x0, + UFSHCI_OCS_INVALID_COMMAND_TABLE_ATTRIBUTES = 0x01, + UFSHCI_OCS_INVALID_PRDT_ATTRIBUTES = 0x02, + UFSHCI_OCS_MISMATCH_DATA_BUFFER_SIZE = 0x03, + UFSHCI_OCS_MISMATCH_RESPONSE_UPIU_SIZE = 0x04, + UFSHCI_OCS_COMMUNICATION_FAILURE_WITHIN_UIC_LAYERS = 0x05, + UFSHCI_OCS_ABORTED = 0x06, + UFSHCI_OCS_HOST_CONTROLLER_FATAL_ERROR = 0x07, + UFSHCI_OCS_DEVICE_FATAL_ERROR = 0x08, + UFSHCI_OCS_INVALID_CRYPTO_CONFIGURATION = 0x09, + UFSHCI_OCS_GENERAL_CRYPTO_ERROR = 0x0A, + UFSHCI_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 + +/* 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; + 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"); + +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"); + +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"); + +/* 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_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 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"); + +/* + * 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; *** 4268 LINES SKIPPED ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202506150609.55F69dn3021406>