Skip site navigation (1)Skip section navigation (2)
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>