Zephyr UFS Driver Implementation
This page provides details on the implementation of the UFS Zephyr driver.
Zephyr - Boot up Process (Arm Cortex-R5 processor)
Zephyr Project - Directory Structure
Top-level directory structure of a zephyrproject/ tree.
Zephyr - Subsystem Layer (Generic Driver Stack)
Zephyr generic subsystem layer.
Zephyr - Driver Layer (Vendor Specific Drivers)
Zephyr vendor-independent driver layer.
Zephyr - UFS Driver (Proposed Directory Structure)
Zephyr - UFS Driver Device Tree
Ufs Node
ufs0: ufs@f10b0000 { compatible = "amd,versal2-ufshc"; status = "disabled"; reg = <0xf10b0000 0x10000>, <0xf1060000 0x2000>, <0xf1250000 0x100>, <0xf1260000 0x100>; interrupts = <GIC_SPI 234 IRQ_TYPE_LEVEL IRQ_DEFAULT_PRIORITY>; clocks = <&ufs_core_clk>; clock-names = "core_clk"; };
amd,versal2-ufshc.yaml
# Copyright (c) 2024 Advanced Micro Devices, Inc. # SPDX-License-Identifier: Apache-2.0 description: | Amd Versal Gen2 UFS Host Controller. Example usage: ufs@f10b0000 { compatible = "amd,versal2-ufs"; reg = <0xf10b0000 0x1000>; interrupts = <GIC_SPI 234 IRQ_TYPE_LEVEL_HIGH>; clocks = <&ufs_core_clk>; clock-names = "core_clk"; freq-table-hz = <0 0>; resets = <&scmi_reset 4>, <&scmi_reset 35>; reset-names = "ufshc-rst", "ufsphy-rst"; }; compatible: "amd,versal2-ufshc" include: [ufs-common.yaml, reset-device.yaml] properties: clocks: required: true description: The list of clocks used by the UFS Host Controller. clock-names: required: true description: core_clk - Clock for the core logic interrupts: required: true description: Interrupts used by the UFS Host Controller. reg: required: true description: | - Host Controller Base address - IOU(Input-Output Unit) SLCR(System-Level Control Register) - EFUSE Cache register - UFS CRP(Clock reset Power) register resets: description: Reset control for both UFS Host Controller and PHY. reset-names: description: | - "ufshc-rst" - Reset signal for the UFS Host Controller - "ufsphy-rst" - Reset signal for the UFS PHY
Ufs Disk Node
ufs_disk0 { compatible = "zephyr,ufs-disk"; lun = <0>; disk-name = "UFS"; status = "okay"; };
Zephyr - UFS Driver Kconfig
CONFIG_AMD_VERSAL2_UFSHC — AMD Versal2 UFS Driver
CONFIG_UFSHC - UFS Driver
CONFIG_UFS_STACK - UFS Subsystem Stack
CONFIG_SCSI - SCSI Subsystem Stack
CONFIG_DYNAMIC_INTERRUPTS - Runtime interrupt configure
CONFIG_EVENTS - Posting ISR Events
CONFIG_HEAP_MEM_POOL_SIZE - For Heap Memory Allocation
CONFIG_CACHE_MANAGEMENT - For cached related operations
SCSI Layer
<subsys><scsi><src>
/* SCSI subsys file */ /** * struct scsi_cmd - information about a SCSI command to be processed * @cmd: Command descriptor block (CDB) * @lun: Target LUN (Logical Unit Number) * @cmdlen: Length of the command * @datalen: Total length of data to be transferred * @pdata: Pointer to data to be transferred * @dma_dir: Direction of data transfer (e.g., read/write) */ struct scsi_cmd { uint8_t cmd[MAX_CDB_SIZE]; uint8_t lun; uint8_t cmdlen; uint64_t datalen; uint8_t *pdata; uint8_t dma_dir; } __packed; /** * struct scsi_host_info - Information about a SCSI host controller * @sdevices: List of connected SCSI devices * @ops: Pointer to the SCSI operations structure * @hostdata: Pointer to host-specific data * @parent: Parent device associated with the host */ struct scsi_host_info { sys_slist_t sdevices; const struct scsi_ops *ops; void *hostdata; struct device *parent; }; /** * struct scsi_device - Information about a SCSI device * @node: Node in the list of SCSI devices * @host: Pointer to the associated SCSI host controller * @lun: LUN ID for the device * @sector_size: Size of sectors in bytes * @capacity: Total capacity of the device in blocks */ struct scsi_device { sys_snode_t node; struct scsi_host_info *host; uint8_t lun; uint32_t sector_size; uint32_t capacity; }; /** * struct scsi_ops - Operations for managing SCSI commands * * @exec: Function to execute a SCSI command */ struct scsi_ops { /** * exec() - Execute a SCSI command * * @sdev: SCSI device handle * @cmd: SCSI command to execute * * @return: 0 on success, negative value on error */ int32_t (*exec)(struct scsi_device *sdev, struct scsi_cmd *cmd); }; /** * struct sg_io_req - Structure to represent a SCSI Generic (SG) I/O request * @protocol: SCSI protocol type * @subprotocol: Sub-protocol type (0 for SCSI command, 1 for SCSI transport) * @request: Pointer to the SCSI CDB or transport layer request * @request_len: Length of the request in bytes * @response: Pointer to the response buffer * @max_response_len: Maximum length of the response buffer * @dxfer_dir: Data transfer direction * @dxfer_len: Length of the data to transfer * @dxferp: Pointer to the data to be transferred */ struct sg_io_req { uint32_t protocol; uint32_t subprotocol; void *request; uint32_t request_len; void *response; uint32_t max_response_len; int32_t dxfer_dir; uint32_t dxfer_len; void *dxferp; }; /* Public APIs exposed from SCSI layer */ /** * scsi_ioctl - Perform an IOCTL operation on a SCSI device * * @sdev: Pointer to the SCSI device * @cmd: IOCTL command * @arg: Arguments for the IOCTL command * * @return: 0 on success, negative value on error */ int32_t scsi_ioctl(struct scsi_device *sdev, int32_t cmd, void *arg); /** * scsi_host_alloc - Allocate memory for a SCSI host controller * * @sops: Pointer to the SCSI operations structure * * @return: Pointer to the allocated scsi_host_info structure, or NULL on failure */ struct scsi_host_info *scsi_host_alloc(const struct scsi_ops *sops);
UFS Subsys Layer
To support UFS Bare-metal driver fnctionality in Zephyr OS
Host Controller Initialization
UIC Cmd, UTP Transfer Requests UPIU
<subsys><ufs><src>
/* UFS Subsys file */ /** * struct ufs_host_controller - UFS host controller private structure * @dev: Pointer to the driver device handle. * @mmio_base: Base address for the UFSHCI memory-mapped I/O registers. * @is_initialized: Flag indicating if the host controller has been initialized. * @irq: IRQ number for the UFS host controller. * @is_cache_coherent: Flag indicating whether the UFS controller supports cache coherency. * @ucdl_base_addr: Pointer to the UFS Command Descriptor base address. * @utrdl_base_addr: Pointer to the UTP Transfer Request Descriptor base address. * @xfer_req_depth: Maximum depth of the Transfer Request Queue supported by the controller. * @outstanding_xfer_reqs: Bitfield representing outstanding transfer requests. * @irq_event: Event object used to signal interrupt handling. * @ufs_lock: Mutex to synchronize access to UFS driver resources. * @dev_info: Structure holding information about the UFS device. * @host: Pointer to the SCSI host structure associated with the UFS controller. */ struct ufs_host_controller { struct device *dev; mem_addr_t mmio_base; bool is_initialized; uint32_t irq; uint8_t is_cache_coherent; struct ufshc_xfer_cmd_desc *ucdl_base_addr; struct ufshc_xfer_req_desc *utrdl_base_addr; uint32_t xfer_req_depth; uint32_t outstanding_xfer_reqs; struct k_event irq_event; struct k_mutex ufs_lock; struct ufs_dev_info dev_info; struct scsi_host_info *host; }; /** * struct ufs_qry_ioctl_req - UFS IOCTL request * @ioctl_id: IOCTL identifier for the request * @attr: UFS attribute query data * @flag: UFS flag query data * @desc: UFS descriptor query data * * This structure holds data for different types of UFS query IOCTL requests, * including attribute, flag, and descriptor queries. */ struct ufs_qry_ioctl_req { uint32_t ioctl_id; union { struct ufs_qry_ioctl_attr attr; struct ufs_qry_ioctl_flag flag; struct ufs_qry_ioctl_desc desc; }; }; /** * struct ufs_sg_req - UFS SCSI Generic (SG) request * @msgcode: Message code for the request * @req_qry_ioctl: Pointer to the UFS query IOCTL request data * * This structure is used for representing SCSI Generic requests, * querying UFS attributes, flags, or descriptors. */ struct ufs_sg_req { int32_t msgcode; struct ufs_qry_ioctl_req *req_qry_ioctl; }; /* Public APIs exposed from UFS Layer */ /** * ufs_init - Initialize the UFS host controller * @ufshc_dev: Pointer to the device structure for the UFS host controller * @ufshc: Pointer to the UFS host controller structure that will be initialized * * Return: 0 on success, negative error code on failure */ int32_t ufs_init(const struct device *ufshc_dev, struct ufs_host_controller ufshc); / * ufs_sg_request - Handle a SCSI Generic (SG) request for UFS * @ufshc: Pointer to the UFS host controller structure * @arg: Argument for the request, which could be a UFS query or other data * * Return: 0 on success, negative error code on failure */ int32_t ufs_sg_request(struct ufs_host_controller *ufshc, void *arg);
UFS Driver Layer
Vendor Specific Driver API Implementation
Device Tree Instantiation
driver.h
/* Driver APIs which are hooked for UFS host controller */ /** * @brief UFS Host Controller (UFSHC) Driver API interface * * This structure defines the set of functions that must be implemented by the * UFS Host Controller driver for PHY initialization and link startup notifications. */ __subsystem struct ufshc_driver_api { /** * @brief PHY initialization for the UFS Host Controller. * * This function is called to initialize the PHY layer of the UFS Host Controller. * * @param dev The device pointer to the UFS Host Controller device. * * @return 0 on success, negative errno value on failure. * -ETIMEDOUT: command timed out during execution. * -ENOTSUP: host controller does not support this operation. * -EIO: I/O error. */ int32_t (*phy_initialization)(const struct device *dev); /** * @brief Link startup notification for UFS Host Controller. * * This function notifies the host controller about the link startup status. * * @param dev The device pointer to the UFS Host Controller device. * @param status The status of the link startup, either PRE or POST. * * @return 0 on success, negative errno value on failure. * -ETIMEDOUT: command timed out during execution. * -ENOTSUP: host controller does not support this operation. * -EIO: I/O error. */ int32_t (*link_startup_notify)(const struct device *dev, uint8_t status); };
driver.src
#define DT_DRV_COMPAT amd_versal2_ufshc /** * struct ufshc_versal2_config - Configuration for the Versal2 UFS Host Controller. * @mmio_base: Base address for the UFS controller memory-mapped I/O. * @core_clk_rate: UFS core clock rate in Hz. * @irq_id: IRQ line for the UFS controller interrupt. * @reg_iou_slcr: IOU SLCR register address for UFS configuration. * @reg_efuse_cache: eFuse cache register address. * @reg_ufs_crp: UFS CRP register address. */ struct ufshc_versal2_config { mem_addr_t mmio_base; uint32_t core_clk_rate; uint32_t irq_id; mem_addr_t reg_iou_slcr; mem_addr_t reg_efuse_cache; mem_addr_t reg_ufs_crp; }; /** * struct ufshc_versal2_data - Runtime data for the Versal2 UFS Host Controller. * @ufshc: UFS host controller structure. * @rx_att_comp_val_l0: Receive AFE compensation value for lane 0. * @rx_att_comp_val_l1: Receive AFE compensation value for lane 1. * @rx_ctle_comp_val_l0: Receive CTLE compensation value for lane 0. * @rx_ctle_comp_val_l1: Receive CTLE compensation value for lane 1. */ struct ufshc_versal2_data { struct ufs_host_controller ufshc; uint32_t rx_att_comp_val_l0; uint32_t rx_att_comp_val_l1; uint32_t rx_ctle_comp_val_l0; uint32_t rx_ctle_comp_val_l1; }; /* Driver APIs. to register */ static const struct ufshc_driver_api ufshc_versal2_api = { .phy_initialization = ufshc_versal2_phy_init, .link_startup_notify = ufshc_versal2_link_startup_notify, }; /** * UFSHC_VERSAL2_INIT - Macro to initialize a UFS host controller instance for Versal2 * @n: The instance index of the UFS controller * * This macro initializes the configuration, data structures, and device * instance for a UFS host controller instance. Creates a device entry for the * UFS host controller in Zephyr device model. */ #define UFSHC_VERSAL2_INIT(n) \ \ static const struct ufshc_versal2_config ufshc_versal2_config_##n = { \ .mmio_base = DT_INST_REG_ADDR_BY_IDX(n, 0), \ .core_clk_rate = DT_PROP(DT_INST_PHANDLE_BY_NAME(n, clocks, core_clk), \ clock_frequency), \ .irq_id = DT_INST_IRQN(n), \ .reg_iou_slcr = DT_INST_REG_ADDR_BY_IDX(n, 1), \ .reg_efuse_cache = DT_INST_REG_ADDR_BY_IDX(n, 2), \ .reg_ufs_crp = DT_INST_REG_ADDR_BY_IDX(n, 3), \ }; \ \ static struct ufshc_versal2_data ufshc_versal2_data_##n = { \ }; \ \ DEVICE_DT_INST_DEFINE(n, &ufshc_versal2_init, NULL, \ &ufshc_versal2_data_##n, &ufshc_versal2_config_##n, \ POST_KERNEL, CONFIG_UFSHC_INIT_PRIORITY, \ &ufshc_versal2_api); DT_INST_FOREACH_STATUS_OKAY(UFSHC_VERSAL2_INIT)
Zephyr - UFS Driver - Filesystem Example - Flow
Zephyr - UFS Driver - Raw API Example - Flow
Zephyr - UFS Driver - Initialization Flow
Zephyr - UFS Driver - RAW UFS requests