UFS (Universal Flash Storage) — MIPI M-PHY/UniPro & SCSI Protocol

UFS(Universal Flash Storage)는 JEDEC JESD220 표준으로 정의된 고성능 플래시 스토리지 인터페이스입니다. 플래그십 스마트폰, 태블릿, 자동차 인포테인먼트, 임베디드 시스템에서 핵심 저장 장치로 사용되며, MIPI M-PHY 물리 계층과 UniPro 전송 프로토콜 위에 SCSI 명령 체계를 운용합니다. 이 문서에서는 UFS 아키텍처, UFSHCI(UFS Host Controller Interface), ufshcd 호스트 드라이버, MCQ(Multi-Circular Queue), Write Booster, HPB(Host Performance Booster), 인라인 암호화(ICE), RPMB, 전원 관리(Power Management), 디버깅(Debugging), 성능 튜닝까지 Linux 커널의 UFS 서브시스템(drivers/ufs/) 전체를 심층 분석합니다.

전제 조건: 블록 I/O 문서와 SCSI 서브시스템 문서를 먼저 읽으세요. UFS는 SCSI 프로토콜을 사용하므로 SCSI mid-layer 이해가 필수적입니다. 디바이스 트리(Device Tree) 지식이 있으면 플랫폼 바인딩 섹션을 더 쉽게 이해할 수 있습니다.
일상 비유: UFS는 전용 고속도로에 비유할 수 있습니다. eMMC가 반이중(half-duplex) 통신으로 한 번에 한 방향만 데이터를 전송하는 “왕복 1차선 지방도로”라면, UFS는 전이중(full-duplex) 직렬 링크로 양방향 동시 전송이 가능한 “전용 고속도로”입니다. 또한 SCSI 명령 큐잉을 지원하여 여러 I/O 요청을 동시에 처리할 수 있으므로, 마치 고속도로에 여러 차선이 있어 다수의 차량이 동시에 달리는 것과 같습니다.

핵심 요약

UFS(Universal Flash Storage)는 JEDEC JESD220 표준에 의해 정의된 고성능 임베디드 스토리지 인터페이스입니다. MIPI M-PHY 물리 계층(최대 2레인, 각 레인 당 최대 23.2Gbps)과 UniPro 전송 프로토콜을 기반으로 하며, 상위 응용 계층에서 SCSI 명령 체계를 사용합니다. 전이중(full-duplex) 직렬 통신으로 eMMC 대비 수 배 높은 대역폭(Bandwidth)과 낮은 지연(Latency) 시간을 제공합니다. Linux 커널에서는 drivers/ufs/ 디렉터리의 ufshcd 드라이버가 UFS 호스트 컨트롤러를 관리합니다.
  • ufs_hba — UFS 호스트 컨트롤러 하나를 대표하는 최상위 구조체(Struct). UFSHCI 레지스터(Register), UTRL/UTMRL, 클럭, 전원 상태를 관리합니다.
  • ufshcd_lrb — UFS Local Reference Block. 개별 I/O 요청의 SCSI 명령, UPIU, UTRD를 연결하는 중간 구조체입니다.
  • utp_transfer_req_desc — UTP Transfer Request Descriptor(UTRD). UFSHCI 하드웨어가 DMA로 읽어가는 전송 요청 디스크립터입니다.
  • scsi_device — UFS LU(Logical Unit) 하나에 대응하는 SCSI 디바이스. UFS는 최대 32개의 일반 LU와 4개의 Well-Known LU를 지원합니다.
  • /dev/sda — UFS는 SCSI 계층을 사용하므로 블록 디바이스가 /dev/sda, /dev/sdb 등으로 나타납니다.
UFS vs eMMC vs NVMe vs SATA 비교
항목UFSeMMCNVMeSATA
인터페이스직렬 MIPI M-PHY (1~2 레인)병렬 8-bit MMC직렬 PCIe (x1~x4)직렬 SATA (1 레인)
최대 속도4.6 GB/s (UFS 4.0, 2L HS-G5)400 MB/s (HS400)~14 GB/s (PCIe 5.0 x4)600 MB/s (SATA III)
프로토콜SCSI (full-duplex)MMC (half-duplex)NVMe (full-duplex)ATA/AHCI (half-duplex)
큐 깊이32 (SDB) / MCQ 가변32 (CMDQ)최대 6553532 (NCQ)
폼 팩터BGA (11.5x13mm)BGA (11.5x13mm)M.2 / U.2 / AIC2.5" / M.2
디바이스 노드/dev/sda/dev/mmcblk0/dev/nvme0n1/dev/sda
전형적 용도플래그십 스마트폰, 자동차IoT, 미드레인지 폰PC, 서버데스크톱, NAS
전이중 통신지원미지원지원미지원
JEDEC UFS 스펙 버전 변천
스펙 버전발표 연도최대 Gear최대 속도 (2L)주요 특징
UFS 1.0 (JESD220)2011PWM-G1~3 MB/s초기 규격, MIPI M-PHY + UniPro + SCSI 기반 아키텍처 정의
UFS 1.1 (JESD220A)2012HS-G1300 MB/sHS(High Speed) Gear 도입, Boot LU 지원
UFS 2.0 (JESD220B)2013HS-G21.2 GB/sHS-G2, 2레인 지원, RPMB(Replay Protected Memory Block)
UFS 2.1 (JESD220C)2016HS-G21.2 GB/s보안 강화, DeepSleep, 인라인 암호화 옵션
UFS 3.0 (JESD220D)2018HS-G42.9 GB/sHS-G4, Write Booster, HPB(Host Performance Booster)
UFS 3.1 (JESD220E)2020HS-G42.9 GB/sWrite Booster 필수화, HPB 표준화, DeepSleep 개선
UFS 4.0 (JESD220F)2022HS-G54.6 GB/sHS-G5 (11.6Gbps/lane), MCQ(Multi-Circular Queue), 향상된 RPMB
UFS 4.12024HS-G54.6 GB/s향상된 전원 관리, Performance Throttling Notification
/dev/sdX 네이밍: UFS 디바이스는 SCSI 레이어를 사용하므로 /dev/sda, /dev/sdb 등으로 나타납니다. 각 UFS Logical Unit(LU)이 별도의 SCSI 디바이스로 등록되며, LU 0이 sda, LU 1이 sdb가 됩니다. Well-Known LU(Boot, RPMB, Device)는 BSG(Block SCSI Generic) 노드를 통해 접근합니다. ufs-bsg 디바이스(/dev/bsg/ufs-bsg0)로 UFS 고유 Query 명령을 전송할 수 있습니다.
/* UFS 핵심 구조체 관계 — drivers/ufs/core/ufshcd.c */
struct ufs_hba {
    struct Scsi_Host *host;          /* SCSI 호스트 어댑터 */
    void __iomem *mmio_base;         /* UFSHCI 레지스터 매핑 */
    struct utp_transfer_req_desc *utrdl_base_addr; /* UTRD 배열 */
    struct utp_task_req_desc *utmrdl_base_addr;    /* UTMRD 배열 */
    struct ufshcd_lrb *lrb;          /* LRB 배열 (태그별) */
    const struct ufs_hba_variant_ops *vops; /* 벤더 콜백 */
    struct ufs_dev_info dev_info;    /* 디바이스 정보 */
    enum ufs_pm_level rpm_lvl;       /* 런타임 PM 레벨 */
    enum ufs_pm_level spm_lvl;       /* 시스템 PM 레벨 */
    u32 capabilities;                /* 호스트 역량 비트맵 */
    unsigned int nutrs;              /* UTP Transfer Request 슬롯 수 */
    unsigned int nutmrs;             /* UTP Task Management 슬롯 수 */
    /* ... */
};
# UFS 디바이스 확인
$ lsscsi
[0:0:0:49488] disk    SAMSUNG  KLUDG4U1EA-B0C1  0200  /dev/sda
[0:0:0:49456] disk    SAMSUNG  KLUDG4U1EA-B0C1  0200  /dev/sdb

# UFS sysfs 정보 확인
$ cat /sys/bus/platform/drivers/ufshcd-qcom/*/device_descriptor/manufacturer_name
SAMSUNG

# UFS 디바이스 속성 확인
$ cat /sys/class/scsi_device/0:0:0:49488/device/vendor
SAMSUNG

단계별 이해: UFS I/O 처리 흐름

UFS I/O는 Linux 블록 레이어에서 시작하여 SCSI mid-layer, ufshcd 드라이버, UFSHCI 하드웨어, M-PHY/UniPro 링크를 거쳐 UFS 디바이스에 도달합니다. 아래 5단계로 전체 흐름을 요약합니다.
  1. 블록 레이어 → SCSI mid-layer → ufshcd_queuecommand
    사용자 공간(User Space)의 read()/write() 호출이 VFS, 파일시스템(Filesystem)을 거쳐 블록 레이어의 struct request로 변환됩니다. blk-mq가 이를 SCSI mid-layer에 전달하면, scsi_queue_rq()struct scsi_cmnd를 생성하고 UFS 호스트 드라이버의 ufshcd_queuecommand()을 호출합니다.
    /* SCSI mid-layer → UFS 호스트 드라이버 진입점 */
    static int ufshcd_queuecommand(struct Scsi_Host *host,
                                    struct scsi_cmnd *cmd)
    {
        struct ufshcd_lrb *lrbp;
        int tag = scsi_cmd_to_rq(cmd)->tag;
    
        lrbp = &hba->lrb[tag];
        lrbp->cmd = cmd;
        lrbp->task_tag = tag;
        lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);
    
        ufshcd_compose_devman_upiu(hba, lrbp);  /* UPIU 구성 */
        ufshcd_send_command(hba, tag);            /* doorbell ring */
        return 0;
    }
  2. UPIU(UFS Protocol Information Unit) 및 UTRD 구성
    ufshcd_compose_devman_upiu()는 SCSI CDB(Command Descriptor Block)를 UPIU 형식으로 변환합니다. Command UPIU(32바이트 헤더 + CDB)를 UCD(UTP Command Descriptor) 내에 배치하고, 데이터 전송이 필요한 경우 PRDT(Physical Region Descriptor Table)를 scatter-gather 리스트로부터 구성합니다. UTRD(UTP Transfer Request Descriptor)는 UCD 주소, 데이터 방향, 응답 UPIU 오프셋(Offset) 등을 담습니다.
  3. UFSHCI DMA 매핑(Mapping) 및 Doorbell 링
    UTRD와 UCD가 DMA 접근 가능한 메모리에 준비되면, ufshcd_send_command()가 UFSHCI의 UTRLDBR(UTP Transfer Request List DoorBell Register)에 해당 태그 비트를 설정합니다. 호스트 컨트롤러 하드웨어는 이를 감지하여 DMA로 UTRD를 읽어가고, UCD 내의 Command UPIU를 추출합니다.
    /* Doorbell 링 — UFSHCI에 전송 시작 알림 */
    void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
    {
        ufshcd_writel(hba, 1 << task_tag, REG_UTP_TRANSFER_REQ_DOOR_BELL);
        /* 하드웨어가 UTRD[task_tag]를 DMA로 fetch */
    }
  4. M-PHY/UniPro 전송 및 디바이스 처리
    UFSHCI 하드웨어는 Command UPIU를 UniPro 프레임으로 캡슐화(Encapsulation)하고 M-PHY 물리 계층을 통해 UFS 디바이스에 전송합니다. 디바이스는 UPIU를 수신하여 SCSI 명령을 해석하고, NAND 플래시에 대한 읽기/쓰기를 수행합니다. 쓰기 요청의 경우, 디바이스는 먼저 RTT(Ready To Transfer) UPIU를 보내 호스트에 데이터 전송을 요청합니다.
  5. 응답 UPIU 수신 및 SCSI 완료
    디바이스가 명령 처리를 완료하면 Response UPIU를 호스트에 전송합니다. UFSHCI가 이를 수신하여 UCD의 Response UPIU 영역에 기록하고, 인터럽트(Interrupt)를 발생시킵니다. ufshcd_intr()ufshcd_transfer_req_compl()가 호출되어 완료된 태그의 Response UPIU 상태를 확인하고, scsi_done()으로 SCSI 레이어에 완료를 알립니다.
    /* 인터럽트 핸들러 — 전송 완료 처리 */
    static irqreturn_t ufshcd_intr(int irq, void *__hba)
    {
        struct ufs_hba *hba = __hba;
        u32 intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);
    
        if (intr_status & UTP_TRANSFER_REQ_COMPL)
            ufshcd_transfer_req_compl(hba);
    
        if (intr_status & UTP_TASK_REQ_COMPL)
            ufshcd_tmc_handler(hba);
    
        return IRQ_HANDLED;
    }
SDB vs MCQ: 전통적 UFS(UFS 3.1 이하)는 SDB(Single DoorBell) 방식으로 하나의 UTRLDBR을 통해 최대 32개 슬롯을 관리합니다. UFS 4.0의 MCQ(Multi-Circular Queue)는 여러 개의 Submission/Completion Queue를 두어 NVMe와 유사한 다중 큐 아키텍처를 지원합니다. 이에 대해서는 후반부 MCQ 섹션에서 상세히 다룹니다.

UFS 아키텍처 개요

UFS 아키텍처는 크게 응용 계층(Application Layer), 전송 계층(UFS Transport Protocol, UTP), 인터커넥트 계층(UniPro), 물리 계층(MIPI M-PHY)의 4단계 프로토콜 스택으로 구성됩니다. 호스트 측과 디바이스 측이 각각 이 스택을 구현하며, M-PHY 직렬 링크를 통해 전이중 통신합니다.
UFS Host (SoC) UFS Device Application Layer SCSI Command Set (READ/WRITE/UNMAP) UFS Command Set (Query, NOP, Task Mgmt) Application Layer SCSI Target + FTL + NAND Controller LU Management, Descriptor/Attribute/Flag UFS Transport Protocol (UTP) UPIU (UFS Protocol Information Unit) UTRD / UCD / PRDT 관리 UFS Transport Protocol (UTP) UPIU Decode / Response Generation Command Queuing, Task Management UniPro (MIPI) L4: Transport (CPort, T_PDU) L3: Network / L2: Data Link (AFC) L1.5: PHY Adapter (PA_GEAR, PA_PWR) UniPro (MIPI) L4: Transport (CPort, T_PDU) L3: Network / L2: Data Link (AFC) L1.5: PHY Adapter MIPI M-PHY (L1) HS-Gear 1~5 / PWM-Gear 1~7 / TX&RX Lane MIPI M-PHY (L1) HS-Gear 1~5 / PWM-Gear 1~7 / TX&RX Lane TX Lane RX Lane UFSHCI (Host Controller Interface) MMIO Registers, UTRL, UTMRL, DMA Engine NAND Flash Array 3D TLC/QLC NAND, FTL, ECC, Wear Leveling Full-Duplex Serial Link: TX/RX 동시 전송 가능 (최대 2레인)

UFS의 프로토콜 스택은 네트워크 OSI 모델과 유사한 계층 구조를 갖습니다. 최상위 응용 계층에서 SCSI 명령을 생성하고, UTP 계층이 이를 UPIU로 캡슐화하며, UniPro 계층이 전송을 관리하고, M-PHY 물리 계층이 실제 직렬 데이터를 전송합니다.

UFS 프로토콜 스택 각 계층의 역할
계층스펙주요 역할핵심 개념
Application (SAP)JESD220SCSI 명령 처리, UFS 명령(Query, NOP, Task Mgmt)LU, Descriptor, Attribute, Flag
UTP (Transport)JESD220UPIU 생성/파싱, 커맨드 큐잉UPIU Header, CDB, Data Segment, PRDT
UniPro L4 (Transport)MIPI UniProCPort 멀티플렉싱, 세그먼트 전송CPort, T_PDU, Connection ID
UniPro L3 (Network)MIPI UniProDevice ID 기반 라우팅(Routing)DeviceID, N_PDU
UniPro L2 (Data Link)MIPI UniPro흐름 제어(Flow Control), 오류 복구AFC(Adaptive Flow Control), CRC, TC(Traffic Class)
UniPro L1.5 (PHY Adapter)MIPI UniPro레인 병합, 기어/모드 설정PA_GEAR, PA_ActiveTxDataLanes, PA_PWRMode
M-PHY L1 (Physical)MIPI M-PHY직렬 데이터 전송, 클럭 복원HS-Gear, PWM-Gear, 8b10b/10b12b 인코딩

Linux 커널의 UFS 관련 소스 코드는 drivers/ufs/ 디렉터리에 집중되어 있습니다. 커널 6.x 기준으로 core, host 서브디렉터리로 구분됩니다.

# UFS 커널 소스 구조 (Linux 6.x)
drivers/ufs/
├── core/
│   ├── ufshcd.c            # UFS 호스트 컨트롤러 드라이버 코어 (메인)
│   ├── ufshcd-crypto.c     # 인라인 암호화 (ICE) 지원
│   ├── ufshcd-priv.h       # 내부 선언
│   ├── ufs-sysfs.c         # sysfs 인터페이스
│   ├── ufs-debugfs.c       # debugfs 인터페이스
│   ├── ufs-mcq.c           # Multi-Circular Queue 지원
│   ├── ufs-hwmon.c         # 하드웨어 모니터 (온도)
│   └── ufs_bsg.c           # BSG(Block SCSI Generic) 인터페이스
├── host/
│   ├── ufs-qcom.c          # Qualcomm UFS PHY/호스트 드라이버
│   ├── ufs-exynos.c        # Samsung Exynos UFS 드라이버
│   ├── ufs-mediatek.c      # MediaTek UFS 드라이버
│   ├── ufs-hisi.c          # HiSilicon UFS 드라이버
│   ├── ufs-renesas.c       # Renesas UFS 드라이버
│   ├── ufshcd-pci.c        # PCI 기반 UFS 호스트 (Intel)
│   ├── ufshcd-pltfrm.c     # 플랫폼 디바이스 공통 코드
│   ├── ufs-qcom.h          # Qualcomm 전용 헤더
│   └── tc-dwc-g210.c       # Synopsys DWC UFS PHY
└── Kconfig / Makefile
/* UFS 관련 주요 헤더 파일 */
#include <ufs/ufshcd.h>       /* struct ufs_hba, ufshcd_* API */
#include <ufs/ufshci.h>       /* UFSHCI 레지스터, UTRD/UTMRD 정의 */
#include <ufs/ufs.h>          /* UFS 스펙 상수, UPIU 정의 */
#include <ufs/unipro.h>       /* UniPro DME 속성 정의 */

/* UFS 버전 매크로 */
#define UFS_SPEC_VER_20   0x0200
#define UFS_SPEC_VER_21   0x0210
#define UFS_SPEC_VER_30   0x0300
#define UFS_SPEC_VER_31   0x0310
#define UFS_SPEC_VER_40   0x0400
UFS 버전 역사 요약: UFS 1.0(2011)은 기초 아키텍처를 정의했지만 실제 제품화는 UFS 2.0(2013)부터 시작되었습니다. UFS 2.1(2016)은 보안을 강화했고, UFS 3.0(2018)은 HS-G4로 대역폭을 크게 늘렸습니다. UFS 3.1(2020)은 Write Booster와 HPB를 표준화했으며, UFS 4.0(2022)은 HS-G5와 MCQ를 도입하여 성능과 확장성을 한층 강화했습니다.

MIPI M-PHY / UniPro 물리·전송 계층

UFS의 물리적 연결은 MIPI Alliance가 표준화한 M-PHY(물리 계층)와 UniPro(Unified Protocol, 전송 계층)를 사용합니다. M-PHY는 SerDes(Serializer/Deserializer) 기반의 고속 직렬 인터페이스로, 각 방향(TX/RX)에 차동(differential) 신호 쌍을 사용합니다. UniPro는 그 위에 계층화된 프로토콜로, 흐름 제어, 오류 복구, 레인 관리 등을 담당합니다.
UFS Host (SoC M-PHY TX/RX) UFS Device (Device M-PHY TX/RX) TX Lane 0 RX Lane 0 DIF_P (Differential Pair +) DIF_N (Differential Pair -) RX Lane 0 TX Lane 0 TX Lane 1 (opt) RX Lane 1 (opt) RX Lane 1 (opt) TX Lane 1 (opt) M-PHY HS-Gear 속도 (per Lane) G1 1.46 Gbps G2 2.92 Gbps G3 5.83 Gbps G4 11.6 Gbps G5 23.2 Gbps Rate A (Series A): G1~G3 기본 Rate B (Series B): G4~G5 고속 2-Lane HS-G5: 2 x 23.2 Gbps = 46.4 Gbps (양방향 합산 92.8 Gbps)
M-PHY HS-Gear 속도 사양
HS-GearRate Series속도 (per Lane)유효 대역폭 (per Lane)2-Lane 유효 대역폭UFS 버전
HS-G1A1.4584 Gbps~150 MB/s~300 MB/sUFS 1.1+
HS-G2A2.9168 Gbps~300 MB/s~600 MB/sUFS 2.0+
HS-G3A5.8336 Gbps~600 MB/s~1.2 GB/sUFS 2.1+
HS-G4B11.6672 Gbps~1.2 GB/s~2.4 GB/sUFS 3.0+
HS-G5B23.3344 Gbps~2.3 GB/s~4.6 GB/sUFS 4.0+
M-PHY PWM-Gear 속도 사양 (저전력 모드)
PWM-Gear속도 (per Lane)용도
PWM-G19 Mbps링크 초기화 시 기본 모드
PWM-G218 Mbps저전력 데이터 전송
PWM-G336 Mbps저전력 데이터 전송
PWM-G472 Mbps저전력 데이터 전송
PWM-G5144 Mbps저전력 유지 통신
PWM-G6288 Mbps저전력 유지 통신
PWM-G7576 Mbps저전력 유지 통신

UniPro는 M-PHY 위에 4개 서브 계층(L1.5, L2, L3, L4)을 쌓아 신뢰성 있는 데이터 전송을 보장합니다.

/* UniPro DME 속성 설정 — ufshcd_dme_set() */
int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
                         u8 attr_set, u32 mib_val, u8 peer)
{
    struct uic_command uic_cmd = {0};

    uic_cmd.command = peer ?
        UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET;
    uic_cmd.argument1 = attr_sel;   /* MIB 속성 ID + GenSelector */
    uic_cmd.argument2 = UIC_ARG_ATTR_TYPE(attr_set);
    uic_cmd.argument3 = mib_val;    /* 설정할 값 */

    return ufshcd_send_uic_cmd(hba, &uic_cmd);
}

/* 사용 예: HS-G4, Rate B, 2 Lane 설정 */
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSGEAR), 4);     /* HS-Gear 4 */
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSRATESERIRES), 2); /* Rate B */
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), 2); /* 2 Lane TX */
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), 2); /* 2 Lane RX */
ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODE),
    FAST_MODE << 4 | FAST_MODE);   /* TX/RX 모두 FAST 모드 */
/* UniPro 주요 DME 속성 상수 (include/ufs/unipro.h) */
#define PA_ACTIVETXDATALANES    0x1560
#define PA_ACTIVERXDATALANES    0x1580
#define PA_TXGEAR               0x1568
#define PA_RXGEAR               0x1583
#define PA_HSSERIES             0x156A
#define PA_PWRMODE              0x1571
#define PA_MAXRXHSGEAR          0x1587
#define PA_LOCALVERINFO         0x15A0
#define PA_GRANULARITY          0x15AA
#define PA_PACPREQTIMEOUT       0x1590
#define PA_PACPREQEOBTIMEOUT    0x1591
#define DME_LOCALFC_PROTECTION_TIMEOUT_ATTR  0x1590

/* Power Mode 값 */
#define FAST_MODE       1
#define SLOW_MODE       2
#define FASTAUTO_MODE   4
#define SLOWAUTO_MODE   5
#define UNCHANGED       7
HS-Gear 전환 시퀀스: ufshcd_config_pwr_mode()는 먼저 디바이스의 PA_MAXRXHSGEAR를 읽어 지원 가능한 최대 Gear를 확인한 후, 호스트와 디바이스 양쪽의 Gear/Rate/Lane 수를 일치시켜 DME_SET(PA_PWRMODE) UIC 명령으로 전환합니다. 벤더별 vops->pwr_change_notify() 콜백(Callback)을 통해 플랫폼 특화 PHY 설정을 적용합니다. 전환 실패 시 SLOW_MODE 폴백을 시도합니다.

UPIU (UFS Protocol Information Unit) 프로토콜

UPIU는 UFS 호스트와 디바이스 간에 주고받는 모든 정보의 기본 단위입니다. 32바이트 고정 헤더와 가변 길이 데이터 세그먼트로 구성되며, SCSI 명령, 데이터 전송, 응답, Query 요청/응답, NOP, Task Management 등 모든 UFS 통신이 UPIU 형식으로 이루어집니다.
UPIU Header Layout (32 Bytes) Byte 0-3 Byte 4-7 Byte 8-11 Byte 12-15 Transaction Type [7:0] Flags LUN [7:0] Task Tag [7:0] Cmd Set Type / IID Query Func / TMF Response Status EHS Length Device Info Data Segment Len [15:0] DWORD 3~7: CDB (16 bytes) / Query Parameters Command UPIU: SCSI CDB 16바이트 / Query: TSF (Transaction Specific Fields) EHS (Extra Header Segment) — optional, UFS 4.0+ Data Segment (variable length, max 65535 bytes) UPIU Transaction Types Host → Device (Requests) 0x01 NOP OUT 0x06 COMMAND 0x02 DATA OUT 0x04 TASK MANAGEMENT REQ 0x16 QUERY REQUEST Device → Host (Responses) 0x20 NOP IN 0x21 RESPONSE 0x22 DATA IN 0x31 READY TO TRANSFER 0x36 QUERY RESPONSE 0x3F REJECT (any direction) 0x24 TASK MANAGEMENT RESP Command UPIU: 32B Header + 16B CDB = 48 Bytes (최소) + Data Segment Query UPIU: 32B Header + TSF(Transaction Specific Fields) + Data Segment Response UPIU Status Codes 0x00: GOOD 0x02: CHECK_CONDITION 0x04: BUSY 0x08: RESERVATION_CONFLICT 0x28: TASK_ABORTED
UPIU Transaction Types
코드이름방향설명
0x01NOP OUTHost → Device링크 활성 확인용. 디바이스는 NOP IN으로 응답
0x20NOP INDevice → HostNOP OUT에 대한 응답
0x06COMMANDHost → DeviceSCSI 명령 전송 (CDB 16바이트 포함)
0x22DATA INDevice → Host읽기 데이터 전송 (디바이스가 호스트에 전달)
0x02DATA OUTHost → Device쓰기 데이터 전송 (호스트가 디바이스에 전달)
0x21RESPONSEDevice → HostCOMMAND에 대한 SCSI 상태 응답
0x31READY TO TRANSFERDevice → Host쓰기 시 디바이스가 데이터 수신 준비 완료 통보
0x16QUERY REQUESTHost → DeviceDescriptor/Attribute/Flag 읽기/쓰기 요청
0x36QUERY RESPONSEDevice → HostQuery 요청에 대한 응답
0x04TASK MANAGEMENT REQHost → DeviceAbort Task, Query Task, Logical Unit Reset 등
0x24TASK MANAGEMENT RESPDevice → HostTask Management 요청에 대한 응답
0x3FREJECT양방향잘못된 UPIU 수신 시 거부 응답
UFS에서 사용하는 주요 SCSI 명령
SCSI 명령Opcode용도비고
READ(10)0x28데이터 읽기 (32-bit LBA)일반 읽기 I/O
WRITE(10)0x2A데이터 쓰기 (32-bit LBA)일반 쓰기 I/O
READ(16)0x88데이터 읽기 (64-bit LBA)HPB 읽기에도 사용
WRITE(16)0x8A데이터 쓰기 (64-bit LBA)대용량 LU 지원
SYNCHRONIZE_CACHE(10)0x35디바이스 캐시(Cache) 플러시(Flush)fsync() 시 발행
START_STOP_UNIT0x1B전원 상태 전환PowerDown, Sleep, Active 전환
UNMAP0x42TRIM (논리 블록 해제 통보)discard 지원
SECURITY PROTOCOL IN0xA2RPMB 읽기보안 프로토콜 데이터 수신
SECURITY PROTOCOL OUT0xB5RPMB 쓰기보안 프로토콜 데이터 전송
INQUIRY0x12디바이스 정보 조회Vendor, Product, Revision
TEST UNIT READY0x00디바이스 준비 상태 확인정상 여부 확인
REPORT LUNS0xA0사용 가능한 LUN 목록 조회LU 탐지 시
/* UPIU 헤더 구조체 — include/ufs/ufs.h */
struct utp_upiu_header {
    __be32 dword_0;  /* Transaction Type | Flags | LUN | Task Tag */
    __be32 dword_1;  /* Cmd Set Type/IID | Query Func | Response | Status */
    __be32 dword_2;  /* EHS Length | Device Info | Data Segment Length */
    __be32 dword_3;  /* Reserved (or Query specific) */
};

/* Transaction Type 추출 매크로 */
#define UPIU_HEADER_TRANSACTION_TYPE(dword0) \
    (((dword0) >> 24) & 0xFF)

/* Command UPIU 구조체 */
struct utp_upiu_cmd {
    struct utp_upiu_header header;
    __be32 exp_data_transfer_len;  /* 예상 데이터 전송 길이 */
    u8     cdb[UFS_CDB_SIZE];     /* SCSI CDB (16바이트) */
};

/* Query UPIU 구조체 */
struct utp_upiu_query {
    struct utp_upiu_header header;
    u8     opcode;     /* Query 종류: Read/Write Descriptor/Attribute/Flag */
    u8     idn;        /* Descriptor/Attribute/Flag IDN */
    u8     index;      /* 인덱스 */
    u8     selector;   /* 선택자 */
    __be16 reserved_osf;
    __be16 length;     /* 데이터 길이 */
    __be32 value;      /* Attribute 값 또는 Flag 값 */
    __be32 reserved[2];
};
/* Command UPIU 구성 — ufshcd_compose_devman_upiu() */
static void ufshcd_prepare_utp_scsi_cmd_upiu(
    struct ufshcd_lrb *lrbp, u8 upiu_flags)
{
    struct utp_upiu_req *ucd_req_ptr = lrbp->ucd_req_ptr;
    struct scsi_cmnd *cmd = lrbp->cmd;

    /* UPIU 헤더 설정 */
    ucd_req_ptr->header.dword_0 = cpu_to_be32(
        UPIU_TRANSACTION_COMMAND << 24 |   /* Transaction Type */
        upiu_flags << 16 |                  /* Flags (R/W/None) */
        lrbp->lun << 8 |                   /* LUN */
        lrbp->task_tag);                    /* Task Tag */

    ucd_req_ptr->header.dword_1 = cpu_to_be32(
        UPIU_COMMAND_SET_TYPE_SCSI << 24);  /* SCSI Command Set */

    /* 예상 데이터 전송 길이 */
    ucd_req_ptr->sc.exp_data_transfer_len =
        cpu_to_be32(scsi_bufflen(cmd));

    /* SCSI CDB 복사 */
    memcpy(ucd_req_ptr->sc.cdb, cmd->cmnd, min_t(u8, cmd->cmd_len, UFS_CDB_SIZE));
}
RTT(Ready To Transfer) 흐름: 쓰기 명령의 경우, 호스트가 Command UPIU를 보낸 후 즉시 데이터를 전송하지 않습니다. 디바이스가 RTT UPIU를 보내 “데이터를 보내도 좋다”고 알려주면, 그때 호스트가 DATA OUT UPIU로 데이터를 전송합니다. 이 메커니즘은 디바이스 내부 버퍼(Buffer) 관리를 위한 것으로, eMMC의 busy 시그널(Signal)링과 유사한 역할을 합니다. 읽기 명령은 RTT 없이 디바이스가 바로 DATA IN UPIU를 보냅니다.

UFS 디바이스 아키텍처

UFS 디바이스 내부는 Logical Unit(LU) 기반으로 구성됩니다. 일반 목적의 LU(최대 32개)와 특수 목적의 Well-Known LU(Boot, RPMB, Device)가 있으며, 각 LU는 독립적인 블록 디바이스로 호스트에 노출됩니다. 디바이스의 설정과 상태는 Descriptor, Attribute, Flag 체계를 통해 관리됩니다.
UFS Device Internal Architecture M-PHY / UniPro Interface (Device Side) UTP Engine — UPIU Decode / Response / Command Queue (32 slots) LU Manager — Logical Unit Routing & Descriptor/Attribute/Flag Engine General Purpose Logical Units (최대 32개) LU 0 /dev/sda LU 1 /dev/sdb LU 2 /dev/sdc ... LU 31 /dev/sd? 각 LU는 독립 SCSI 디바이스 — LBA 주소 공간, 블록 크기, 프로비저닝 타입 별도 Well-Known LUs W-LU BOOT LUN=0xB0 W-LU RPMB LUN=0xC4 W-LU Device LUN=0xD0 W-LU Boot 1 LUN=0xB1 Flash Translation Layer (FTL) — L2P Mapping, GC, Wear Leveling, ECC Write Buffer(SLC Cache) | HPB L2P Cache | Write Booster Buffer 3D NAND Flash Array TLC/QLC NAND | Multi-Plane | Multi-Die | Multi-Channel
Well-Known Logical Unit 종류
Well-Known LUW-LUN 값용도접근 방법
REPORT LUNS0x81사용 가능한 LUN 목록 보고SCSI REPORT LUNS 명령
UFS Device0xD0디바이스 전체 관리 (Power, Reset)START_STOP_UNIT 명령
Boot LU 00xB0부트 파티션 0 (부트로더(Bootloader) 저장)READ/WRITE 명령
Boot LU 10xB1부트 파티션 1 (부트로더 백업)READ/WRITE 명령
RPMB0xC4Replay Protected Memory BlockSECURITY PROTOCOL IN/OUT

UFS 디바이스의 설정과 상태는 Descriptor, Attribute, Flag 세 가지 체계로 관리됩니다. 모두 Query UPIU를 통해 읽기/쓰기가 가능합니다.

UFS 주요 Descriptor 종류
Descriptor IDN이름크기주요 필드
0x00Device Descriptor~64BbNumberLU, bUFSFeaturesSupport, wManufacturerID, bBootEnable, bDeviceClass
0x01Configuration Descriptor~144BbBootEnable, bDescrAccessEn, bInitPowerMode, 각 LU 설정 배열
0x02Unit Descriptor~45BbLUEnable, bBootLunID, bLUWriteProtect, qLogicalBlockCount, dEraseBlockSize
0x04Interconnect Descriptor~6BUniPro/M-PHY 버전 정보
0x05String Descriptor가변제조사명, 제품명, 시리얼 번호 (UTF-16)
0x07Geometry Descriptor~72B총 NAND 용량, 최대 LU 수, 세그먼트 크기, 얼로케이션 단위
0x08Power Parameters Descriptor~98B각 기어/모드별 전력 소비량
0x09Device Health Descriptor~45BbPreEOLInfo, bDeviceLifeTimeEstA/B (수명 잔량 추정)
/* Device Descriptor 읽기 — Query UPIU 사용 */
static int ufshcd_read_device_desc(struct ufs_hba *hba, u8 *buf, u32 size)
{
    return ufshcd_read_desc_param(hba,
        QUERY_DESC_IDN_DEVICE,  /* IDN = 0x00 */
        0,                       /* Index */
        0,                       /* Offset */
        buf, size);
}

/* Device Descriptor 주요 필드 오프셋 */
enum device_desc_param {
    DEVICE_DESC_PARAM_LEN           = 0x00,
    DEVICE_DESC_PARAM_TYPE          = 0x01,
    DEVICE_DESC_PARAM_DEVICE_TYPE   = 0x02,
    DEVICE_DESC_PARAM_DEVICE_CLASS  = 0x03,
    DEVICE_DESC_PARAM_DEVICE_SUB_CLASS = 0x04,
    DEVICE_DESC_PARAM_PRTCL         = 0x05,
    DEVICE_DESC_PARAM_NUM_LU        = 0x06,
    DEVICE_DESC_PARAM_NUM_WLU       = 0x07,
    DEVICE_DESC_PARAM_BOOT_ENBL     = 0x08,
    DEVICE_DESC_PARAM_MANF_ID       = 0x18, /* 제조사 ID (2바이트) */
    DEVICE_DESC_PARAM_SPEC_VER      = 0x10, /* UFS 스펙 버전 */
    DEVICE_DESC_PARAM_UFS_FEAT      = 0x1F, /* 지원 기능 비트맵 */
    DEVICE_DESC_PARAM_HPB_VER       = 0x40, /* HPB 버전 */
    DEVICE_DESC_PARAM_WB_TYPE       = 0x45, /* Write Booster 타입 */
};
/* Attribute 읽기/쓰기 예시 */
/* bCurrentPowerMode 읽기 */
u32 power_mode;
ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
    QUERY_ATTR_IDN_POWER_MODE, 0, 0, &power_mode);

/* bActiveICCLevel 쓰기 — 전류 소비 레벨 설정 */
u32 icc_level = 0x0F; /* 최대 레벨 */
ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
    QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, &icc_level);

/* Flag 설정 예시 — fDeviceInit 플래그로 디바이스 초기화 트리거 */
ufshcd_query_flag(hba, UPIU_QUERY_OPCODE_SET_FLAG,
    QUERY_FLAG_IDN_FDEVICEINIT, 0, NULL);
Descriptor vs Attribute vs Flag 차이: Descriptor는 다수 필드를 포함하는 구조화된 데이터 블록으로, 디바이스의 정적/반정적 설정을 담습니다. Attribute는 단일 32비트 값으로, 실시간(Real-time) 상태나 동적 설정(전원 모드, ICC 레벨 등)을 나타냅니다. Flag는 단일 비트(boolean) 값으로, 디바이스 초기화, 퍼지 활성화 등 on/off 스위치 역할을 합니다. 셋 모두 Query UPIU를 통해 호스트에서 읽고/쓸 수 있습니다.
ufs-utils 도구: 사용자 공간에서 ufs-utils 패키지의 ufs-tool로 Descriptor, Attribute, Flag를 직접 조회할 수 있습니다. ufs-tool desc -t 0 -p /dev/bsg/ufs-bsg0으로 Device Descriptor를 읽고, ufs-tool attr -t 2 -p /dev/bsg/ufs-bsg0으로 bCurrentPowerMode를 확인할 수 있습니다.

UFSHCI (UFS Host Controller Interface) 레지스터 아키텍처

UFSHCI는 JEDEC 표준(JESD223)에 의해 정의된 UFS 호스트 컨트롤러의 하드웨어 인터페이스입니다. SoC에 통합된 UFS 호스트 컨트롤러가 이 레지스터 맵을 구현하며, ufshcd 드라이버가 MMIO를 통해 이 레지스터들을 제어합니다. 핵심은 UTRL(UTP Transfer Request List), UTMRL(UTP Task Management Request List), 그리고 인터럽트/상태 레지스터입니다.
UFSHCI MMIO Registers CAP (0x00) VER (0x08) HCE (0x34) HCS (0x30) IS (0x20) IE (0x24) UTRLBA / UTRLBAU (0x50/0x54) — Transfer Request List Base UTRLDBR (0x58) — Transfer Request DoorBell UTRLCLR (0x5C) — Transfer Request List Clear UTMRLBA / UTMRLBAU (0x70/0x74) — Task Mgmt Base UTMRLDBR (0x78) — Task Mgmt DoorBell UICCMD (0x90) UCMDARG1-3 (0x94+) MCQ Config (0x300+) — UFS 4.0+ Multi-Circular Queue Vendor Specific Registers (0xC0~) DMA Memory Layout UTRL — UTP Transfer Request List (DMA 주소: UTRLBA/UTRLBAU가 가리킴) UTRD[0] UTRD[1] UTRD[2] ... 최대 32 슬롯 (SDB) — 각 UTRD = 32 bytes UTRD → UCD(Command Descriptor) DMA 포인터 포함 UCD — UTP Command Descriptor (각 UTRD가 가리키는 DMA 메모리) Command UPIU (32B+) Response UPIU (32B+) PRDT (Physical Region Descriptor Table) Scatter-Gather: {base_addr, size, flags} x N entries UTMRL — UTP Task Management Request List 최대 8 슬롯 — Abort Task, LU Reset, Query Task 각 UTMRD = Task Mgmt Request UPIU + Response UPIU UFSHCI v3.0+: UTRL max 32 slots / UTMRL max 8 slots / UFS 4.0 MCQ: variable SQ/CQ
UFSHCI 주요 레지스터
오프셋이름접근설명
0x00CAP (Capabilities)RO호스트 역량: NUTRS(슬롯 수), NUTMRS, 64-bit 지원, 오토 히버네이트, MCQ 지원 등
0x08VER (Version)ROUFSHCI 스펙 버전 (예: 0x0300 = v3.0)
0x20IS (Interrupt Status)RW1C인터럽트 상태: UTP Transfer/Task 완료, UIC 명령 완료, 오류 등
0x24IE (Interrupt Enable)RW인터럽트 활성화 마스크
0x30HCS (Host Controller Status)RO컨트롤러 상태: UCRDY, UTRLRDY, UTMRLRDY, DP(Device Present)
0x34HCE (Host Controller Enable)RW컨트롤러 활성화/비활성화 (비트 0: HCE)
0x50UTRLBARWUTRL 베이스 주소 (하위 32비트)
0x54UTRLBAURWUTRL 베이스 주소 (상위 32비트, 64-bit 주소용)
0x58UTRLDBRRWTransfer Request Doorbell: 비트 N 세팅 → UTRD[N] 전송 시작
0x5CUTRLCLRRWTransfer Request Clear: 비트 N 세팅 → UTRD[N] 취소
0x60UTRLRSRRWTransfer Request List Run-Stop Register
0x70UTMRLBARWUTMRL 베이스 주소 (하위 32비트)
0x74UTMRLBAURWUTMRL 베이스 주소 (상위 32비트)
0x78UTMRLDBRRWTask Management Request Doorbell
0x90UICCMDRWUIC Command 레지스터 (DME_GET/SET, DME_PEER_GET/SET 등)
0x94UCMDARG1RWUIC 명령 인자 1 (MIB Attribute ID)
0x98UCMDARG2RWUIC 명령 인자 2 (Attribute Set Type)
0x9CUCMDARG3RWUIC 명령 인자 3 (값)

UTRD(UTP Transfer Request Descriptor)는 UFSHCI 하드웨어가 DMA로 읽어가는 32바이트 구조체로, UCD(UTP Command Descriptor)의 DMA 주소와 데이터 전송 방향을 담습니다.

/* UTRD 구조체 — include/ufs/ufshci.h */
struct utp_transfer_req_desc {
    /* DWord 0-1: Command Descriptor 관련 */
    struct {
        __le32 dword_0;  /* CT(Command Type) | DD(Data Direction) | I(Interrupt) | OCS */
        __le32 dword_1;  /* Reserved */
    } header;

    /* DWord 2-3: UCD Base Address (Command UPIU 주소) */
    __le32 command_desc_base_addr_lo;  /* UCD 하위 32비트 주소 */
    __le32 command_desc_base_addr_hi;  /* UCD 상위 32비트 주소 */

    /* DWord 4-5: Response UPIU offset/length + PRDT offset/length */
    __le16 response_upiu_length;  /* Response UPIU 길이 (DWord 단위) */
    __le16 response_upiu_offset;  /* UCD 내 Response UPIU 오프셋 */
    __le16 prd_table_length;      /* PRDT 엔트리 수 */
    __le16 prd_table_offset;      /* UCD 내 PRDT 오프셋 */

    /* DWord 6-7: Reserved */
    __le32 reserved[2];
};

/* OCS (Overall Command Status) 값 */
enum utp_ocs {
    OCS_SUCCESS                  = 0x00,
    OCS_INVALID_CMD_TABLE_ATTR   = 0x01,
    OCS_INVALID_PRDT_ATTR        = 0x02,
    OCS_MISMATCH_DATA_BUF_SIZE   = 0x03,
    OCS_MISMATCH_RESP_UPIU_SIZE  = 0x04,
    OCS_PEER_COMM_FAILURE        = 0x05,
    OCS_ABORTED                  = 0x06,
    OCS_FATAL_ERROR              = 0x07,
    OCS_DEVICE_FATAL_ERROR       = 0x08,
    OCS_INVALID_CRYPTO_CONFIG    = 0x09,
    OCS_GENERAL_CRYPTO_ERROR     = 0x0A,
    OCS_INVALID_COMMAND_STATUS   = 0x0F,
};
/* LRB(Local Reference Block) 구조체 — 각 태그의 런타임 정보 */
struct ufshcd_lrb {
    struct utp_transfer_req_desc *utr_descriptor_ptr; /* UTRD 포인터 */
    struct utp_upiu_req *ucd_req_ptr;  /* UCD 내 Command UPIU 포인터 */
    struct utp_upiu_rsp *ucd_rsp_ptr;  /* UCD 내 Response UPIU 포인터 */
    struct ufshcd_sg_entry *ucd_prdt_ptr; /* PRDT 포인터 */

    struct scsi_cmnd *cmd;          /* 연결된 SCSI 명령 */
    u8 *sense_buffer;               /* Sense Data 버퍼 */
    unsigned int sense_bufflen;

    int scsi_status;
    int command_type;               /* SCSI / DEV_MANAGE / NOP */
    int task_tag;                   /* 슬롯 번호 (0~31) */
    u8 lun;                         /* 대상 LUN */
    bool req_abort_skip;            /* abort skip 플래그 */
    int crypto_key_slot;            /* 인라인 암호화 키 슬롯 (-1: 비활성) */
};
PRDT(Physical Region Descriptor Table) 엔트리: 각 PRDT 엔트리는 DMA scatter-gather 세그먼트 하나를 나타내며, 베이스 주소(64비트)와 데이터 바이트 수를 담습니다. UFSHCI 스펙은 PRDT 엔트리의 최소 정렬을 4바이트, 최소 크기를 4바이트로 규정합니다. struct ufshcd_sg_entry는 각 엔트리가 16바이트이며, 인라인 암호화(ICE) 활성 시 추가 필드가 포함됩니다. hba->sg_entry_size로 실제 크기를 확인할 수 있습니다.
OCS 확인: 전송 완료 시 ufshcd_transfer_req_compl()은 먼저 UTRD의 OCS(Overall Command Status)를 확인합니다. OCS가 OCS_SUCCESS이면 Response UPIU의 SCSI Status를 파싱하고, 그렇지 않으면 OCS 자체가 에러 원인입니다. OCS_PEER_COMM_FAILURE는 M-PHY/UniPro 링크 에러를, OCS_FATAL_ERROR는 UFSHCI 하드웨어 치명적 오류를 나타냅니다.

ufshcd 호스트 컨트롤러 드라이버

ufshcd(UFS Host Controller Driver)는 Linux 커널의 UFS 핵심 드라이버로, drivers/ufs/core/ufshcd.c에 구현되어 있습니다. SCSI 호스트 어댑터로 등록되며, 벤더별 플랫폼 드라이버(ufs-qcom, ufs-exynos 등)는 ufs_hba_variant_ops(vops) 콜백을 통해 하드웨어 특화 동작을 제공합니다.
Block Layer (blk-mq) → SCSI Mid-Layer (scsi_queue_rq) ufshcd Core (drivers/ufs/core/ufshcd.c) struct ufs_hba Scsi_Host | mmio_base | utrdl | lrb[] | vops dev_info | clk_list | vreg | pm_lvl | caps Scsi_Host scsi_host_template ufshcd_queuecommand() SCSI CDB → UPIU → doorbell ufshcd_send_command() UTRLDBR write → HW DMA ufshcd_transfer_req_compl() OCS check → scsi_done() ufshcd_init() / _async_scan() ufshcd_err_handler() ufshcd_uic_cmd(DME_SET/GET) vops Callbacks init, setup_clocks pwr_change_notify hce_enable_notify Clock/Regulator core_clk, ref_clk, pclk vcc, vccq, vccq2 regulators Runtime PM hibernate / resume rpm_lvl / spm_lvl sysfs / debugfs / bsg ufs-sysfs.c, ufs-debugfs.c ufs_bsg.c (Query UPIU) UFSHCI Hardware (MMIO Registers + DMA) UTRL, UTMRL, Interrupt, UIC Command, MCQ Config ufs-qcom.c Qualcomm Snapdragon ufs-exynos.c Samsung Exynos ufs-mediatek.c MediaTek Dimensity ufshcd-pci.c Intel / PCI-based Platform drivers register vops callbacks → ufshcd_pltfrm_init() → ufshcd_init()

ufshcd의 핵심은 struct ufs_hba입니다. 이 구조체는 UFS 호스트 컨트롤러의 모든 상태를 관리하며, SCSI 호스트(Scsi_Host), UFSHCI 레지스터 매핑(mmio_base), UTRD/LRB 배열, 벤더 콜백(vops), 클럭/레귤레이터, 전원 관리 상태 등을 포함합니다.

/* ufs_hba 주요 필드 상세 — include/ufs/ufshcd.h */
struct ufs_hba {
    /* === SCSI 호스트 === */
    struct Scsi_Host *host;
    struct device *dev;

    /* === UFSHCI 레지스터 === */
    void __iomem *mmio_base;         /* MMIO 베이스 주소 */
    u32 ahit;                        /* 오토 히버네이트 타이머 값 */

    /* === UTRL / UTMRL DMA 메모리 === */
    struct utp_transfer_req_desc *utrdl_base_addr;  /* UTRD 배열 */
    dma_addr_t utrdl_dma_addr;                      /* UTRD DMA 주소 */
    struct utp_task_req_desc *utmrdl_base_addr;     /* UTMRD 배열 */
    dma_addr_t utmrdl_dma_addr;

    /* === UCD(Command Descriptor) DMA 메모리 === */
    struct utp_transfer_cmd_desc *ucdl_base_addr;   /* UCD 배열 */
    dma_addr_t ucdl_dma_addr;

    /* === LRB (Local Reference Block) 배열 === */
    struct ufshcd_lrb *lrb;          /* 태그별 런타임 정보 */
    unsigned long lrb_in_use;        /* 사용 중인 태그 비트맵 */

    /* === 벤더 콜백 === */
    const struct ufs_hba_variant_ops *vops;
    struct ufs_hba_variant *priv;    /* 벤더 전용 데이터 */

    /* === 역량 및 슬롯 수 === */
    u32 capabilities;                /* UFSHCI CAP 레지스터 값 */
    unsigned int nutrs;              /* Transfer Request 슬롯 수 (1~32) */
    unsigned int nutmrs;             /* Task Mgmt 슬롯 수 (1~8) */

    /* === 클럭 및 전원 === */
    struct list_head clk_list_head;  /* 클럭 목록 */
    struct ufs_vreg_info vreg_info;  /* vcc, vccq, vccq2 레귤레이터 */

    /* === 전원 관리 === */
    enum ufs_pm_level rpm_lvl;       /* 런타임 PM 레벨 */
    enum ufs_pm_level spm_lvl;       /* 시스템 PM 레벨 */
    int pm_op_in_progress;

    /* === 디바이스 정보 === */
    struct ufs_dev_info dev_info;    /* LU 수, WB 지원, HPB 지원 등 */
    u16 dev_quirks;                  /* 디바이스 quirk 비트맵 */

    /* === 에러 처리 === */
    struct work_struct eh_work;      /* 에러 핸들러 워크큐 */
    u32 errors;                      /* 마지막 에러 상태 */
    u32 uic_error;                   /* UIC 에러 상태 */

    /* === MCQ (UFS 4.0+) === */
    bool mcq_enabled;
    unsigned int nr_hw_queues;       /* MCQ 하드웨어 큐 수 */
    struct ufs_hw_queue *uhq;        /* MCQ 큐 배열 */
};

벤더별 플랫폼 드라이버는 ufs_hba_variant_ops(vops) 구조체를 통해 하드웨어 특화 초기화, PHY 설정, 전력 모드 전환 등의 콜백을 제공합니다.

/* 벤더 콜백 구조체 — include/ufs/ufshcd.h */
struct ufs_hba_variant_ops {
    const char *name;

    /* 초기화/해제 */
    int  (*init)(struct ufs_hba *);
    void (*exit)(struct ufs_hba *);

    /* 클럭 제어 */
    int  (*setup_clocks)(struct ufs_hba *, bool, enum ufs_notify_change_status);

    /* HCE(Host Controller Enable) 전후 알림 */
    int  (*hce_enable_notify)(struct ufs_hba *, enum ufs_notify_change_status);

    /* 링크 스타트업 전후 알림 */
    int  (*link_startup_notify)(struct ufs_hba *, enum ufs_notify_change_status);

    /* 전력 모드 변경 전후 알림 (Gear/Lane/Rate 변경) */
    int  (*pwr_change_notify)(struct ufs_hba *,
            enum ufs_notify_change_status,
            struct ufs_pa_layer_attr *desired,
            struct ufs_pa_layer_attr *final);

    /* 런타임 suspend/resume */
    int  (*suspend)(struct ufs_hba *, enum ufs_pm_op, enum ufs_notify_change_status);
    int  (*resume)(struct ufs_hba *, enum ufs_pm_op);

    /* PHY 특화 설정 */
    int  (*phy_initialization)(struct ufs_hba *);

    /* 디바이스 리셋 GPIO 제어 */
    int  (*device_reset)(struct ufs_hba *);

    /* MCQ 관련 (UFS 4.0+) */
    int  (*mcq_config_resource)(struct ufs_hba *);
    int  (*get_hba_mac)(struct ufs_hba *);      /* 최대 활성 명령 수 */
    int  (*op_runtime_config)(struct ufs_hba *); /* MCQ 런타임 설정 */

    /* 프로그램 키 (인라인 암호화) */
    int  (*program_key)(struct ufs_hba *,
            const union ufs_crypto_cfg_entry *, int);
};

/* 벤더 드라이버 등록 예시 (Qualcomm) */
static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
    .name                   = "qcom",
    .init                   = ufs_qcom_init,
    .exit                   = ufs_qcom_exit,
    .setup_clocks           = ufs_qcom_setup_clocks,
    .hce_enable_notify      = ufs_qcom_hce_enable_notify,
    .link_startup_notify    = ufs_qcom_link_startup_notify,
    .pwr_change_notify      = ufs_qcom_pwr_change_notify,
    .suspend                = ufs_qcom_suspend,
    .resume                 = ufs_qcom_resume,
    .device_reset           = ufs_qcom_device_reset,
    .config_scaling_param   = ufs_qcom_config_scaling_param,
    .program_key            = ufs_qcom_ice_program_key,
    .mcq_config_resource    = ufs_qcom_mcq_config_resource,
    .get_hba_mac            = ufs_qcom_get_hba_mac,
    .op_runtime_config      = ufs_qcom_op_runtime_config,
};

ufshcd 등록 및 초기화 흐름

플랫폼 드라이버가 ufshcd_pltfrm_init()을 호출하면 다음 순서로 초기화가 진행됩니다:

  1. ufshcd_alloc_host()Scsi_Hostufs_hba 할당, SCSI 호스트 템플릿 설정
  2. ufshcd_init() — UTRL/UTMRL/UCD DMA 메모리 할당, LRB 배열 초기화, 인터럽트 등록, 워크큐 생성
  3. ufshcd_async_scan() (비동기) — HCE 활성화 → 링크 스타트업 → NOP OUT 확인 → Device Descriptor 읽기 → Gear 전환 → SCSI 호스트 스캔
/* 초기화 핵심 흐름 */
int ufshcd_pltfrm_init(struct platform_device *pdev,
                        const struct ufs_hba_variant_ops *vops)
{
    struct ufs_hba *hba;
    void __iomem *mmio_base;
    int irq, err;

    mmio_base = devm_platform_ioremap_resource(pdev, 0);
    irq = platform_get_irq(pdev, 0);

    err = ufshcd_alloc_host(&pdev->dev, &hba);
    hba->vops = vops;

    err = ufshcd_init(hba, mmio_base, irq);
    /* → 내부에서 ufshcd_async_scan() 스케줄링 */

    platform_set_drvdata(pdev, hba);
    return err;
}

/* ufshcd_async_scan() 주요 단계 */
static void ufshcd_async_scan(void *data, async_cookie_t cookie)
{
    struct ufs_hba *hba = data;

    /* 1. HCE 활성화: HCE 레지스터에 1 기록 */
    ufshcd_hba_enable(hba);

    /* 2. 링크 스타트업: UniPro 링크 초기화 (PWM-G1에서 시작) */
    ufshcd_link_startup(hba);

    /* 3. NOP OUT/IN 교환으로 링크 상태 확인 */
    ufshcd_verify_dev_init(hba);

    /* 4. Device Descriptor 읽기 → 디바이스 정보 파싱 */
    ufshcd_probe_hba(hba, false);
    /* → bNumberLU, bUFSFeaturesSupport, wManufacturerID 등 */

    /* 5. 최고 HS-Gear로 전환 */
    ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info);
    /* → vops->pwr_change_notify() → DME_SET(PA_PWRMODE) */

    /* 6. SCSI 호스트 스캔: LU 탐지 → /dev/sdX 생성 */
    scsi_scan_host(hba->host);
}

I/O 경로 상세

일반 I/O 경로의 핵심 함수 호출 순서를 추적합니다.

/* ===== I/O 발행 경로 ===== */

/* 1. SCSI mid-layer가 ufshcd_queuecommand() 호출 */
static int ufshcd_queuecommand(struct Scsi_Host *host,
                                struct scsi_cmnd *cmd)
{
    struct ufs_hba *hba = shost_priv(host);
    int tag = scsi_cmd_to_rq(cmd)->tag;
    struct ufshcd_lrb *lrbp = &hba->lrb[tag];

    /* 히버네이트 상태면 resume 먼저 */
    ufshcd_hold(hba);

    /* LRB 설정 */
    lrbp->cmd = cmd;
    lrbp->task_tag = tag;
    lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);

    /* UTRD 기본 필드 설정 */
    ufshcd_prepare_lrbp_crypto(cmd, lrbp);  /* 인라인 암호화 */
    lrbp->req_abort_skip = false;

    /* UPIU + PRDT 구성 */
    ufshcd_comp_scsi_upiu(hba, lrbp);
    /* → Command UPIU 헤더, CDB, PRDT scatter-gather 설정 */

    /* 2. Doorbell 링으로 HW에 전송 */
    ufshcd_send_command(hba, tag);
    return 0;
}

/* ===== I/O 완료 경로 ===== */

/* 3. 인터럽트 핸들러 */
static irqreturn_t ufshcd_intr(int irq, void *__hba)
{
    u32 intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS);

    /* Transfer Request 완료 인터럽트 */
    if (intr_status & UTP_TRANSFER_REQ_COMPL) {
        u32 completed = ~ufshcd_readl(hba, REG_UTP_TRANSFER_REQ_DOOR_BELL)
                        & hba->outstanding_reqs;
        ufshcd_transfer_req_compl(hba, completed);
    }

    /* 에러 인터럽트 */
    if (intr_status & UFSHCD_ERROR_MASK)
        ufshcd_check_errors(hba, intr_status);

    return IRQ_HANDLED;
}

/* 4. 개별 요청 완료 처리 */
static void ufshcd_transfer_req_compl(struct ufs_hba *hba,
                                       unsigned long completed_reqs)
{
    int tag;

    for_each_set_bit(tag, &completed_reqs, hba->nutrs) {
        struct ufshcd_lrb *lrbp = &hba->lrb[tag];
        struct scsi_cmnd *cmd = lrbp->cmd;

        /* UTRD의 OCS(Overall Command Status) 확인 */
        int ocs = le32_to_cpu(lrbp->utr_descriptor_ptr->header.dword_0)
                  & OCS_MASK;

        if (ocs == OCS_SUCCESS) {
            /* Response UPIU에서 SCSI Status 추출 */
            int result = ufshcd_transfer_rsp_status(hba, lrbp);
            cmd->result = result;
        } else {
            /* OCS 에러 → 에러 핸들러로 위임 */
            cmd->result = DID_ERROR << 16;
        }

        scsi_done(cmd);   /* SCSI 레이어에 완료 통보 */
        hba->outstanding_reqs &= ~(1UL << tag);
    }
}
ufshcd 주요 함수 참조
함수명파일역할
ufshcd_alloc_host()ufshcd.cScsi_Host + ufs_hba 할당
ufshcd_init()ufshcd.cDMA 메모리, IRQ, 워크큐 초기화
ufshcd_async_scan()ufshcd.c비동기 HCE→링크→NOP→스캔 시퀀스
ufshcd_hba_enable()ufshcd.cHCE 레지스터 활성화, 컨트롤러 리셋
ufshcd_link_startup()ufshcd.cUniPro 링크 초기화 (DME_LINKSTARTUP)
ufshcd_config_pwr_mode()ufshcd.cHS-Gear/Rate/Lane 전환
ufshcd_queuecommand()ufshcd.cSCSI 명령 → UPIU → doorbell
ufshcd_send_command()ufshcd.cUTRLDBR doorbell 링
ufshcd_intr()ufshcd.c인터럽트 핸들러 (IS 레지스터 처리)
ufshcd_transfer_req_compl()ufshcd.c전송 완료: OCS 확인 → scsi_done()
ufshcd_err_handler()ufshcd.c에러 복구: abort → reset → full reset
ufshcd_send_uic_cmd()ufshcd.cUIC 명령 전송 (DME_SET/GET 등)
ufshcd_query_attr()ufshcd.cQuery UPIU로 Attribute 읽기/쓰기
ufshcd_query_flag()ufshcd.cQuery UPIU로 Flag 설정/해제/읽기
ufshcd_read_desc_param()ufshcd.cQuery UPIU로 Descriptor 읽기
SCSI 호스트 템플릿: ufshcdscsi_host_template을 통해 SCSI mid-layer에 자신을 등록합니다. .queuecommand = ufshcd_queuecommand, .eh_abort_handler = ufshcd_abort, .eh_device_reset_handler = ufshcd_eh_device_reset_handler, .eh_host_reset_handler = ufshcd_eh_host_reset_handler 등이 핵심 콜백입니다. .cmd_per_lun은 기본 32이고, .can_queuehba->nutrs 값을 사용합니다.
에러 복구 레벨: ufshcd_err_handler()는 3단계 에러 복구를 수행합니다: (1) Abort — 개별 태그의 UTRLCLR로 명령 취소, (2) LU Reset — Task Management UPIU로 특정 LU 리셋, (3) Host Reset — HCE 비활성화/재활성화로 전체 UFSHCI 리셋 후 링크 재초기화. 각 단계 실패 시 다음 단계로 에스컬레이션됩니다. UIC 에러(M-PHY/UniPro 링크 오류)의 경우 즉시 Host Reset으로 진행할 수 있습니다.
/* 에러 핸들러 개요 */
static void ufshcd_err_handler(struct work_struct *work)
{
    struct ufs_hba *hba = container_of(work, struct ufs_hba, eh_work);

    /* 1단계: 개별 명령 Abort */
    for_each_set_bit(tag, &hba->outstanding_reqs, hba->nutrs) {
        err = ufshcd_try_to_abort_task(hba, tag);
        if (err) goto reset_lu;
    }
    return;

reset_lu:
    /* 2단계: LU 리셋 (Task Management UPIU) */
    err = ufshcd_eh_device_reset_handler(/* ... */);
    if (err) goto reset_host;
    return;

reset_host:
    /* 3단계: 호스트 전체 리셋 */
    ufshcd_reset_and_restore(hba);
    /* → HCE disable → HCE enable → link startup → restore */
}
# ufshcd 디바이스 트리 바인딩 예시 (Qualcomm SM8550)
ufs_mem_hc: ufshc@1d84000 {
    compatible = "qcom,sm8550-ufshc", "qcom,ufshc", "jedec,ufs-2.0";
    reg = <0x01d84000 0x3000>,       /* UFSHCI MMIO */
          <0x01d90000 0x8000>;        /* UFS PHY */
    reg-names = "std", "ice";

    interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;

    clocks = <&gcc GCC_UFS_PHY_AXI_CLK>,
             <&gcc GCC_UFS_PHY_ICE_CORE_CLK>,
             <&gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>,
             <&rpmhcc RPMH_CXO_CLK>;
    clock-names = "core_clk", "ice_core_clk",
                  "unipro_core_clk", "ref_clk";

    freq-table-hz = <75000000 300000000>,   /* core_clk */
                    <75000000 300000000>,   /* ice_core_clk */
                    <75000000 300000000>,   /* unipro_core_clk */
                    <75000000 75000000>;    /* ref_clk */

    vcc-supply = <&vreg_l17b_2p5>;
    vccq-supply = <&vreg_l6b_1p2>;
    vcc-max-microamp = <800000>;
    vccq-max-microamp = <900000>;

    power-domains = <&gcc GCC_UFS_PHY_GDSC>;

    resets = <&gcc GCC_UFS_PHY_BCR>;
    reset-names = "rst";

    phys = <&ufs_mem_phy>;
    phy-names = "ufsphy";

    lanes-per-direction = <2>;  /* 2-Lane 구성 */

    status = "okay";
};
클럭 스케일링(Clock Scaling): ufshcd는 devfreq 프레임워크와 연동하여 런타임에 UFS 클럭 주파수를 동적으로 조절합니다. I/O 부하가 낮을 때 클럭을 낮추어 전력을 절약하고, 부하가 높아지면 최대 클럭으로 올립니다. ufshcd_devfreq_scale()가 기어 변경까지 포함하여 처리하며, vops->setup_clocks() 콜백으로 벤더별 클럭 게이팅/언게이팅을 수행합니다. sysfs의 /sys/class/devfreq/*/에서 현재 주파수와 통계를 확인할 수 있습니다.

커널 소스 분석 — drivers/ufs/ 구조

UFS 호스트 컨트롤러 드라이버의 핵심은 drivers/ufs/ 디렉터리에 위치합니다. Linux 6.x 기준으로 이 디렉터리는 코어 드라이버, 플랫폼별 글루(glue) 드라이버, 그리고 보조 기능 모듈로 구성됩니다. 전체 구조를 파악하면 UFS 스택의 동작 원리를 소스 레벨에서 이해할 수 있습니다.

소스 디렉터리 개요: Linux 커널 6.x에서 UFS 관련 소스는 크게 두 곳에 분산되어 있습니다. 드라이버 구현은 drivers/ufs/(이전에는 drivers/scsi/ufs/)에, 헤더 정의는 include/ufs/에 위치합니다. 커널 6.0부터 SCSI 디렉터리에서 독립적인 서브시스템으로 분리되었으며, drivers/ufs/core/drivers/ufs/host/로 세분화됩니다.

소스 파일 맵

경로파일명역할대략적 규모
drivers/ufs/core/ufshcd.cUFS Host Controller Driver 코어 — 초기화, I/O 처리, 에러 핸들링, 전원 관리~10,000줄
drivers/ufs/core/ufshcd-priv.h코어 내부 private 헤더~200줄
drivers/ufs/core/ufs-sysfs.csysfs 인터페이스 — 디바이스 속성 노출, 런타임 설정~1,000줄
drivers/ufs/core/ufs-mcq.cMulti-Circular Queue(MCQ) 지원 — UFS 4.0+~800줄
drivers/ufs/core/ufshcd-crypto.c인라인 암호화 — blk-crypto 프레임워크 연동~300줄
drivers/ufs/core/ufs-bsg.cBSG(Block SCSI Generic) — 사용자 공간 UFS 커맨드 통로~400줄
drivers/ufs/core/ufs-fault-inject.cFault injection 프레임워크 연동~100줄
drivers/ufs/core/ufs-debugfs.cdebugfs 인터페이스 — 런타임 디버깅~200줄
include/ufs/ufshci.hUFSHCI 레지스터 정의, UTRD/UTMRD 구조체~600줄
include/ufs/ufshcd.hufs_hba, ufshcd_lrb 등 핵심 구조체~1,200줄
include/ufs/ufs.hUFS 프로토콜 상수, Descriptor/Flag/Attribute 정의~700줄
include/ufs/ufs_quirks.h벤더별 quirk 플래그 정의~100줄
include/ufs/unipro.hUniPro 레이어 상수 (PA, DL, NL, TL 속성)~300줄

핵심 함수 호출 체인

UFS 드라이버의 전체 생명주기를 이해하려면 주요 함수들의 호출 관계를 파악해야 합니다. 아래는 드라이버 초기화부터 I/O 처리까지의 핵심 콜 체인입니다.

/* === 드라이버 초기화 콜 체인 === */
ufshcd_pltfrm_init()            /* 플랫폼 드라이버 진입점 */
  └─ ufshcd_alloc_host()        /* Scsi_Host + ufs_hba 할당 */
  └─ ufshcd_init()              /* 코어 초기화 */
       ├─ ufshcd_hba_init()     /* HBA 하드웨어 초기화 */
       ├─ ufshcd_host_reset_and_restore()
       ├─ scsi_add_host()       /* SCSI 미드 레이어 등록 */
       └─ async_schedule(ufshcd_async_scan, hba)
            └─ ufshcd_probe_hba()   /* 디바이스 탐색 및 설정 */
                 ├─ ufshcd_link_startup()    /* UniPro 링크 수립 */
                 ├─ ufshcd_verify_dev_init()
                 ├─ ufshcd_complete_dev_init()
                 ├─ ufshcd_device_params_init()
                 ├─ ufs_get_device_desc()   /* Device Descriptor 읽기 */
                 ├─ ufshcd_tune_unipro_params()
                 └─ scsi_scan_host()   /* LU 스캔 → /dev/sdX 생성 */

ufshcd_probe_hba() 상세 흐름

ufshcd_probe_hba()는 UFS 디바이스 탐색과 초기 설정의 핵심입니다. 이 함수는 비동기 스캔 콜백으로 호출되며, 링크 수립부터 LU(Logical Unit) 등록까지 전 과정을 담당합니다.

/* drivers/ufs/core/ufshcd.c — ufshcd_probe_hba() 핵심 흐름 */
static int ufshcd_probe_hba(struct ufs_hba *hba, bool init_dev_params)
{
    int ret;

    /* 1단계: UniPro 링크 수립 */
    ret = ufshcd_link_startup(hba);
    if (ret)
        goto out;

    /* 2단계: 디바이스 초기화 확인 */
    ret = ufshcd_verify_dev_init(hba);
    if (ret)
        goto out;

    /* 3단계: UFS 디바이스 파라미터 초기화 */
    if (init_dev_params) {
        ret = ufshcd_device_params_init(hba);
        if (ret)
            goto out;
    }

    /* 4단계: UniPro 파라미터 튜닝 (Gear, Lane, Rate) */
    ufshcd_tune_unipro_params(hba);

    /* 5단계: UFS Power Mode 변경 (HS-G4 등) */
    ret = ufshcd_config_pwr_mode(hba, &hba->max_pwr_info.info);
    if (ret)
        goto out;

    /* 6단계: Write Booster 초기화 */
    ufshcd_wb_config(hba);

    /* 7단계: SCSI 호스트 스캔 → /dev/sdX 생성 */
    if (hba->scsi_host_added)
        scsi_scan_host(hba->host);

    ufshcd_set_ufs_dev_active(hba);
out:
    return ret;
}

에러 핸들링: ufshcd_err_handler()

UFS 에러 핸들링은 전용 워크 큐(eh_work)를 통해 비동기적으로 처리됩니다. 인터럽트 컨텍스트에서 에러가 감지되면 워크를 스케줄하고, 별도 스레드(Thread)에서 복구를 수행합니다. 이 구조는 에러 복구 중에도 시스템이 응답성을 유지할 수 있게 합니다.

/* 에러 핸들러 워크 — ufshcd_err_handler() 주요 단계 */
static void ufshcd_err_handler(struct work_struct *work)
{
    struct ufs_hba *hba = container_of(work, struct ufs_hba, eh_work);

    /* 1. 미완료 태스크 정리 */
    ufshcd_clear_pending_tasks(hba);

    /* 2. 에러 유형별 분기 */
    if (hba->saved_err & UIC_ERROR) {
        /* UniPro 에러 → 링크 복구 시도 */
        ufshcd_update_evt_hist(hba, UFS_EVT_LINK_STARTUP_FAIL, 0);
        ret = ufshcd_uic_hibern8_exit(hba);
    }

    if (hba->saved_err & UFSHCD_UIC_DL_TCx_REPLAY_ERROR) {
        /* Data Link 에러 → 호스트 리셋 */
        ret = ufshcd_host_reset_and_restore(hba);
    }

    if (hba->saved_err & INT_FATAL_ERRORS) {
        /* Fatal 에러 → 전체 리셋 */
        ret = ufshcd_reset_and_restore(hba);
    }

    /* 3. 복구 완료 후 I/O 재개 */
    ufshcd_recover_pm_error(hba);
    ufshcd_release(hba);
}
디버깅 팁: UFS 에러 핸들링 과정을 추적하려면 tracepoint를 활용하세요. trace_ufshcd_command, trace_ufshcd_uic_command, trace_ufshcd_upiu 등의 tracepoint가 정의되어 있으며, echo 1 > /sys/kernel/debug/tracing/events/ufs/enable로 활성화할 수 있습니다.

주요 구조체 관계

구조체정의 위치역할주요 필드
struct ufs_hbainclude/ufs/ufshcd.hHBA 인스턴스 — 드라이버 전체 상태 관리host, mmio_base, utrdl_base_addr, lrb, vops
struct ufshcd_lrbinclude/ufs/ufshcd.hLocal Reference Block — 개별 I/O 요청 추적utr_descriptor_ptr, ucd_req_ptr, cmd, task_tag
struct utp_transfer_req_descinclude/ufs/ufshci.hUTP Transfer Request Descriptor (UTRD)header, command_desc_base_addr, prd_table_*
struct ufs_dev_infoinclude/ufs/ufshcd.hUFS 디바이스 정보 캐시wmanufacturerid, model, wspecversion
struct ufs_hba_variant_opsinclude/ufs/ufshcd.h플랫폼별 콜백 (vops)init, setup_clocks, hce_enable_notify, link_startup_notify

플랫폼별 드라이버 — Platform-Specific Glue Drivers

UFS 코어 드라이버(ufshcd.c)는 하드웨어 독립적인 로직을 제공하고, 각 SoC 벤더는 ufs_hba_variant_ops(통칭 vops) 콜백을 구현하여 플랫폼 고유의 PHY 초기화, 클럭 설정, 암호화 엔진 등을 처리합니다. 이 분리 구조 덕분에 새 플랫폼 지원을 추가할 때 코어 드라이버 수정 없이 글루 드라이버만 작성하면 됩니다.

vops 패턴: UFS 플랫폼 드라이버의 핵심은 struct ufs_hba_variant_ops입니다. 이 구조체의 각 함수 포인터가 초기화, 링크 수립, 전원 관리, 암호화 등의 시점별 콜백을 정의합니다. 코어 드라이버는 적절한 시점에 ufshcd_vops_xxx() 래퍼 매크로(Macro)를 통해 이 콜백들을 호출합니다.

플랫폼 드라이버 비교

드라이버파일compatible주요 특징PHY 유형
Qualcommufs-qcom.cqcom,ufshcICE(Inline Crypto Engine), QMP PHY, testbus 디버깅QMP UFS PHY
Samsung Exynosufs-exynos.csamsung,exynos7-ufsFMP(Flash Memory Protector), TXHSLV/RXHSLV 캘리브레이션Exynos UFS PHY
MediaTekufs-mediatek.cmediatek,mt8183-ufshciVA09 레귤레이터, 전용 crypto IP, MPHY 캘리브레이션MediaTek MPHY
HiSiliconufs-hisi.chisilicon,hi3670-ufsKirin SoC용, PHY 초기화 시퀀스HiSilicon MPHY
Renesasufs-renesas.crenesas,r8a779f0-ufsR-Car Gen4용, 자체 PHY setupRenesas MPHY
Intel PCIufshcd-pci.cPCI VID/DID 매칭PCI 기반 UFS, 서버/데스크탑용내장 PHY

vops 콜백 구현 예시

/* Qualcomm UFS vops — ufs-qcom.c */
static const struct ufs_hba_variant_ops ufs_hba_qcom_vops = {
    .name                   = "qcom",
    .init                   = ufs_qcom_init,
    .exit                   = ufs_qcom_exit,
    .set_dma_mask           = ufs_qcom_set_dma_mask,
    .hce_enable_notify      = ufs_qcom_hce_enable_notify,
    .link_startup_notify    = ufs_qcom_link_startup_notify,
    .pwr_change_notify      = ufs_qcom_pwr_change_notify,
    .apply_dev_quirks       = ufs_qcom_apply_dev_quirks,
    .suspend                = ufs_qcom_suspend,
    .resume                 = ufs_qcom_resume,
    .dbg_register_dump      = ufs_qcom_dump_dbg_regs,
    .device_reset           = ufs_qcom_device_reset,
    .config_scaling_param   = ufs_qcom_config_scaling_param,
    .program_key            = ufs_qcom_ice_program_key,  /* ICE 암호화 */
    .event_notify           = ufs_qcom_event_notify,
    .mcq_config_resource    = ufs_qcom_mcq_config_resource,
};

/* Samsung Exynos UFS vops — ufs-exynos.c */
static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = {
    .name                   = "exynos",
    .init                   = exynos_ufs_init,
    .exit                   = exynos_ufs_exit,
    .hce_enable_notify      = exynos_ufs_hce_enable_notify,
    .link_startup_notify    = exynos_ufs_link_startup_notify,
    .pwr_change_notify      = exynos_ufs_pwr_change_notify,
    .suspend                = exynos_ufs_suspend,
    .resume                 = exynos_ufs_resume,
    .program_key            = exynos_ufs_program_key,    /* FMP 암호화 */
};

새 플랫폼 드라이버 작성 스켈레톤

/* drivers/ufs/host/ufs-myplatform.c — 새 플랫폼 드라이버 스켈레톤 */
#include <ufs/ufshcd.h>
#include <ufs/ufshcd-pltfrm.h>
#include <ufs/unipro.h>

struct ufs_myplatform_host {
    struct ufs_hba *hba;
    void __iomem *phy_base;
    struct clk *ref_clk;
    struct regulator *vcc_supply;
    /* 플랫폼 고유 필드 */
};

static int ufs_myplatform_init(struct ufs_hba *hba)
{
    struct ufs_myplatform_host *host;
    struct device *dev = hba->dev;

    host = devm_kzalloc(dev, sizeof(*host), GFP_KERNEL);
    if (!host)
        return -ENOMEM;

    host->hba = hba;
    ufshcd_set_variant(hba, host);

    /* PHY 초기화 */
    host->phy_base = devm_platform_ioremap_resource_byname(
        to_platform_device(dev), "phy");
    if (IS_ERR(host->phy_base))
        return PTR_ERR(host->phy_base);

    /* 클럭, 레귤레이터 설정 */
    host->ref_clk = devm_clk_get(dev, "ref_clk");
    clk_prepare_enable(host->ref_clk);

    /* UFS Host Controller 특화 초기화 */
    hba->caps |= UFSHCD_CAP_CLK_GATING |
                  UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;

    return 0;
}

static void ufs_myplatform_exit(struct ufs_hba *hba)
{
    struct ufs_myplatform_host *host = ufshcd_get_variant(hba);
    clk_disable_unprepare(host->ref_clk);
}

static int ufs_myplatform_link_startup_notify(
    struct ufs_hba *hba, enum ufs_notify_change_status status)
{
    if (status == PRE_CHANGE) {
        /* UniPro 파라미터 사전 설정 */
        ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);
    }
    return 0;
}

static const struct ufs_hba_variant_ops ufs_myplatform_vops = {
    .name                = "myplatform",
    .init                = ufs_myplatform_init,
    .exit                = ufs_myplatform_exit,
    .link_startup_notify = ufs_myplatform_link_startup_notify,
};

static const struct of_device_id ufs_myplatform_of_match[] = {
    { .compatible = "myvendor,my-ufshc", .data = &ufs_myplatform_vops },
    { },
};
MODULE_DEVICE_TABLE(of, ufs_myplatform_of_match);

static int ufs_myplatform_probe(struct platform_device *pdev)
{
    const struct ufs_hba_variant_ops *vops;

    vops = of_device_get_match_data(&pdev->dev);
    return ufshcd_pltfrm_init(pdev, vops);
}

static void ufs_myplatform_remove(struct platform_device *pdev)
{
    ufshcd_pltfrm_remove(pdev);
}

static struct platform_driver ufs_myplatform_driver = {
    .probe  = ufs_myplatform_probe,
    .remove = ufs_myplatform_remove,
    .driver = {
        .name = "ufs-myplatform",
        .of_match_table = ufs_myplatform_of_match,
        .pm = &ufshcd_of_pm_ops,
    },
};
module_platform_driver(ufs_myplatform_driver);
Qualcomm ICE (Inline Crypto Engine): Qualcomm SoC에서는 UFS 컨트롤러에 ICE가 통합되어 있어 하드웨어 수준의 인라인 암호화를 지원합니다. ufs_qcom_ice_program_key()blk_crypto_profile의 키슬롯에 AES-256-XTS 키를 프로그래밍합니다. ICE는 CPU 개입 없이 데이터 경로에서 직접 암복호화를 수행하므로 성능 오버헤드(Overhead)가 거의 없습니다.

Kconfig 설정

# UFS 플랫폼 드라이버 Kconfig 예시
CONFIG_SCSI_UFS_QCOM=m          # Qualcomm UFS 호스트
CONFIG_SCSI_UFS_EXYNOS=m        # Samsung Exynos UFS 호스트
CONFIG_SCSI_UFS_MEDIATEK=m      # MediaTek UFS 호스트
CONFIG_SCSI_UFS_HISI=m          # HiSilicon UFS 호스트
CONFIG_SCSI_UFS_RENESAS=m       # Renesas UFS 호스트
CONFIG_SCSI_UFSHCD_PCI=m        # PCI 기반 UFS 호스트
CONFIG_SCSI_UFS_DWC_TC_PCI=m    # Synopsys DesignWare PCI

제조사별 칩셋 심층 분석 (Vendor-Specific Deep Dive)

UFS 코어 드라이버(ufshcd.c)는 벤더 중립적이지만, 실제 SoC에서는 PHY 캘리브레이션, 인라인 암호화 엔진, 전원 도메인, 디버깅 인프라 등에서 제조사마다 상당히 다른 구현을 갖습니다. 이 섹션에서는 주요 SoC 벤더의 UFS 호스트 드라이버가 커널에서 어떻게 구현되어 있는지, 각 벤더의 고유 하드웨어 IP와 그에 대응하는 커널 코드를 심층 분석합니다.

SoC 벤더별 UFS 호스트 아키텍처 비교 ufshcd.c (공통 코어) Qualcomm (ufs-qcom) ICE v3 암호화 엔진 QMP UFS PHY Testbus 디버깅 MCQ 리소스 매핑 DevFreq 스케일링 ESI (Event Specific IRQ) drivers/ufs/host/ufs-qcom.c Samsung (ufs-exynos) FMP 암호화 엔진 Exynos UFS PHY (PMA) TXHSLV/RXHSLV 캘리브 EOM (Eye Opening Monitor) 다중 Gear 테이블 VS_DEBUGCLOCKENABLE drivers/ufs/host/ufs-exynos.c MediaTek (ufs-mediatek) 전용 Crypto IP MediaTek MPHY VA09 레귤레이터 다단계 클럭 게이팅 MCQ Interrupt 매핑 VCC/VCCQ 시퀀싱 drivers/ufs/host/ufs-mediatek.c HiSi / Renesas Kirin PHY 다단계 초기화 Hi3660/Hi3670 Quirks R-Car Gen4 PHY 자동차 등급 온도 범위 최소 vops 구현 PMA 레지스터 직접 제어 ufs-hisi.c / ufs-renesas.c MIPI M-PHY / UniPro → UFS Device (NAND Flash) 공통 패턴: 모든 벤더가 ufs_hba_variant_ops (vops) 콜백으로 플랫폼 고유 동작을 주입 차이점: PHY IP, 암호화 엔진(ICE vs FMP vs 전용IP), 디버깅 인프라, 전원 도메인 관리가 벤더마다 상이 ※ Synopsys DesignWare UFS IP를 사용하는 SoC(일부 HiSilicon, 기타)는 ufshcd-dwc.c 공통 헬퍼를 활용

Qualcomm — ICE, QMP PHY, Testbus

Qualcomm의 UFS 호스트 드라이버(ufs-qcom.c)는 커널 UFS 플랫폼 드라이버 중 가장 기능이 풍부하며, Snapdragon SoC의 주요 스토리지 경로를 담당합니다. ICE(Inline Crypto Engine), QMP PHY, Testbus 디버깅, MCQ 리소스 매핑, DevFreq 기반 성능 스케일링 등 다양한 하드웨어 IP를 커널에서 직접 제어합니다.

ICE (Inline Crypto Engine) 상세

ICE는 UFS 컨트롤러와 M-PHY 사이의 데이터 경로에 위치하여, CPU 개입 없이 읽기/쓰기 데이터를 실시간으로 암복호화합니다. Android의 파일 기반 암호화(FBE, File-Based Encryption)에서 핵심적인 역할을 수행하며, AES-256-XTS 모드를 하드웨어에서 직접 처리하여 소프트웨어 암호화 대비 성능 오버헤드가 거의 없습니다.

ICE 버전별 특성 비교
ICE 버전키슬롯 수지원 알고리즘대표 SoC커널 지원
ICE v132AES-128/256-XTSSDM845Legacy (비표준 API)
ICE v232AES-256-XTSSM8150, SM7150blk-crypto + qcom_ice
ICE v332AES-256-XTS, AES-256-CTSSM8250+, SM8550+blk-crypto + qcom_ice + wrapped keys
/* ICE 키 프로그래밍 흐름 — ufs-qcom.c + qcom-ice.c */

/*
 * 1단계: blk-crypto 프레임워크가 키슬롯 할당 요청
 *    → ufshcd_program_key() → vops->program_key()
 */
static int ufs_qcom_ice_program_key(struct ufs_hba *hba,
                                     const union ufs_crypto_cfg_entry *cfg,
                                     int slot)
{
    struct ufs_qcom_host *host = ufshcd_get_variant(hba);
    union ufs_crypto_cap_entry cap;
    union {
        u8 bytes[AES_256_XTS_KEY_SIZE];
        u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
    } key;

    /* crypto capability 확인 */
    cap = hba->crypto_cap_array[cfg->crypto_cap_idx];

    /* 키 데이터를 ICE 레지스터 형식으로 변환 */
    memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE);

    /* qcom_ice 헬퍼를 통해 ICE 레지스터에 키 프로그래밍 */
    return qcom_ice_program_key(host->ice, cfg->crypto_cap_idx,
                                 QCOM_ICE_CRYPTO_KEY_SIZE_256,
                                 key.bytes, cfg->data_unit_size,
                                 slot);
}

/*
 * 2단계: ICE 레지스터에 키 쓰기 — qcom-ice.c
 *
 * ICE는 각 키슬롯에 대해 다음 레지스터를 프로그래밍:
 *   QCOM_ICE_REG_CRYPTOCFG      — 알고리즘, 키 크기, DUN(Data Unit Number) 크기
 *   QCOM_ICE_LUT_KEYS_CRYPTOCFG — 키 데이터 (256비트 × 2 = XTS 모드)
 *
 * 키 프로그래밍 순서:
 *   1. CRYPTOCFG.ENABLE = 0 (슬롯 비활성화)
 *   2. 키 데이터 레지스터에 32바이트씩 쓰기
 *   3. CRYPTOCFG = algorithm | key_size | dusize | ENABLE
 */

/*
 * 3단계: I/O 경로에서 ICE 자동 적용
 *   → UTRD의 crypto_enable 비트 설정
 *   → ICE가 DMA 전송 중 실시간 암복호화 수행
 *
 * ufshcd_prepare_req_desc_hdr()에서:
 *   dword_0 |= UTP_REQ_DESC_CRYPTO_ENABLE_CMD;
 *   dword_0 |= (u32)lrbp->crypto_key_slot << 24;
 *   req_desc->header.dword_1 = lower_32_bits(lrbp->data_unit_num);
 *   req_desc->header.dword_3 = upper_32_bits(lrbp->data_unit_num);
 */
Wrapped Keys (ICE v3+): ICE v3부터 지원되는 wrapped key 기능은 실제 암호화 키가 소프트웨어에 노출되지 않도록 합니다. 키는 하드웨어 보안 모듈(TrustZone)에서 래핑(wrapping)된 상태로 ICE에 프로그래밍되며, ICE 내부의 하드웨어 키 언래퍼(unwrapper)가 실시간으로 언래핑하여 사용합니다. 이를 통해 커널 메모리 덤프나 cold boot 공격에서도 실제 암호화 키를 보호할 수 있습니다. CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK=n으로 설정하면 소프트웨어 폴백(fallback)이 비활성화되어 반드시 ICE 하드웨어를 통해서만 암호화가 수행됩니다.

QMP UFS PHY

Qualcomm SoC는 QMP(Qualcomm Multi-Protocol) PHY IP를 사용하여 MIPI M-PHY 호환 직렬 링크를 구현합니다. QMP PHY는 USB, PCIe, UFS 등 여러 프로토콜을 지원하는 범용 SerDes(Serializer/Deserializer) IP이며, UFS 모드에서는 HS-Gear 4/5, Rate-A/B, 1~2 Lane을 지원합니다.

/* QMP UFS PHY 초기화 시퀀스 — 주요 흐름 */

/*
 * PHY 초기화는 link_startup_notify(PRE_CHANGE) 시점에 수행됩니다.
 * 핵심은 SoC별 캘리브레이션 테이블을 PHY 레지스터에 프로그래밍하는 것입니다.
 */
static int ufs_qcom_link_startup_notify(struct ufs_hba *hba,
                                         enum ufs_notify_change_status status)
{
    struct ufs_qcom_host *host = ufshcd_get_variant(hba);

    switch (status) {
    case PRE_CHANGE:
        /* UniPro 로컬 파라미터 설정 */
        ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);

        /* PHY 전원 On + 캘리브레이션 */
        if (host->phy)
            phy_power_on(host->phy);  /* → qmp_ufs_power_on() */
        break;

    case POST_CHANGE:
        /* 링크 수립 후 Gear/Rate 협상 결과 확인 */
        ufs_qcom_link_startup_post_change(hba);
        break;
    }
    return 0;
}

/*
 * QMP PHY 캘리브레이션 테이블 구조 (SoC별로 다름):
 *
 * static const struct qmp_phy_init_tbl sm8550_ufsphy_serdes[] = {
 *     QMP_PHY_INIT_CFG(QSERDES_UFS_V6_COM_SYSCLK_EN_SEL, 0xd9),
 *     QMP_PHY_INIT_CFG(QSERDES_UFS_V6_COM_CMN_CONFIG, 0x06),
 *     QMP_PHY_INIT_CFG(QSERDES_UFS_V6_COM_VCO_TUNE_MAP, 0x02),
 *     ... // SoC별 수십~수백 개의 레지스터 값
 * };
 *
 * 각 테이블은 TX(송신), RX(수신), PCS(Protocol Convergence Sublayer),
 * SERDES(PLL/CDR) 영역별로 분리되어 있습니다.
 */

Testbus 디버깅 인프라

Qualcomm UFS 컨트롤러는 내부 상태를 외부에서 관찰할 수 있는 Testbus 메커니즘을 제공합니다. 에러 핸들링 시 ufs_qcom_dump_dbg_regs()가 자동 호출되어 컨트롤러 내부 상태를 커널 로그에 덤프합니다.

/* Testbus 디버그 레지스터 덤프 — ufs-qcom.c */
static void ufs_qcom_dump_dbg_regs(struct ufs_hba *hba)
{
    struct ufs_qcom_host *host = ufshcd_get_variant(hba);

    /* 1. UFS 호스트 컨트롤러 레지스터 덤프 */
    ufshcd_dump_regs(hba, REG_UFS_SYS1CLK_1US, 16 * 4,
                     "HCI Vendor Specific Regs ");

    /* 2. Testbus 셀렉터별 상태 읽기 */
    ufs_qcom_testbus_read(hba);

    /*
     * Testbus 셀렉터 예시:
     *   TESTBUS_CFG = 0x   → UTP Transfer Request 상태
     *   TESTBUS_CFG = 0x05 → UTP Task Management 상태
     *   TESTBUS_CFG = 0x06 → UTRLCNR (완료 알림 레지스터) 상태
     *   TESTBUS_CFG = 0x07 → UTMRLCNR 상태
     *   TESTBUS_CFG = 0x10 → UFS-PHY 어댑터 레이어 상태
     *   TESTBUS_CFG = 0x11 → UniPro 상태
     *
     * 에러 분석 시 testbus 값의 비트 패턴으로
     * 요청이 어느 단계에서 멈췄는지 판별할 수 있습니다.
     */

    /* 3. PHY 상태 덤프 (선택) */
    if (host->dbg_print_en & UFS_QCOM_DBG_PRINT_PHY_EN)
        ufs_qcom_phy_dbg_register_dump(host->phy);
}

/*
 * sysfs를 통한 testbus 제어:
 *   echo  > /sys/kernel/debug/ufshcd0/testbus
 *   cat /sys/kernel/debug/ufshcd0/testbus
 */

MCQ 리소스 매핑

UFS 4.0의 MCQ(Multi-Circular Queue) 모드에서 Qualcomm SoC는 SQ(Submission Queue)와 CQ(Completion Queue)의 도어벨(doorbell) 레지스터를 별도의 MMIO 영역에 매핑합니다. ufs_qcom_mcq_config_resource()는 디바이스 트리에서 이 영역의 물리 주소를 읽어와 ioremap합니다.

/* Qualcomm MCQ 리소스 설정 — ufs-qcom.c */
static int ufs_qcom_mcq_config_resource(struct ufs_hba *hba)
{
    struct ufs_qcom_host *host = ufshcd_get_variant(hba);
    struct platform_device *pdev = to_platform_device(hba->dev);
    struct resource *res;

    /* MCQ SQ/CQ 도어벨 전용 MMIO 영역 */
    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mcq");
    if (res) {
        host->mcq_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(host->mcq_base))
            return PTR_ERR(host->mcq_base);
    }

    /*
     * 각 SQ/CQ 쌍은 고유한 도어벨 레지스터 주소를 가짐:
     *   SQ[n] Tail Doorbell = mcq_base + 0x0 + (n * stride)
     *   CQ[n] Head Doorbell = mcq_base + 0x4 + (n * stride)
     *
     * 이 분리 매핑 덕분에 서로 다른 CPU 코어가
     * 각자의 큐에 독립적으로 접근할 수 있어 lock contention이 감소합니다.
     */

    return 0;
}

/*
 * DevFreq 성능 스케일링 — ufs_qcom_config_scaling_param()
 *
 * Qualcomm SoC는 UFS 컨트롤러의 동작 주파수를 DevFreq 프레임워크로 제어합니다.
 * I/O 부하가 낮을 때 Gear를 낮추고, 높을 때 최대 Gear로 올려
 * 전력-성능 균형을 자동으로 조절합니다.
 *
 * static void ufs_qcom_config_scaling_param(struct ufs_hba *hba,
 *                                   struct devfreq_dev_profile *p,
 *                                   struct devfreq_simple_ondemand_data *d)
 * {
 *     p->polling_ms = 60;       // 60ms마다 부하 샘플링
 *     d->upthreshold = 70;      // 70% 이상 → Gear Up
 *     d->downdifferential = 5;  // 65% 이하 → Gear Down
 * }
 */

Samsung Exynos — FMP, EOM, PHY 캘리브레이션

Samsung Exynos SoC의 UFS 드라이버(ufs-exynos.c)는 FMP(Flash Memory Protector)라는 자체 인라인 암호화 엔진, Exynos UFS PHY의 PMA(Physical Medium Attachment) 레지스터 직접 제어, EOM(Eye Opening Monitor)을 통한 신호 품질 분석 등 고유한 기능을 갖습니다.

FMP (Flash Memory Protector)

FMP는 Qualcomm의 ICE와 유사한 역할을 수행하지만, 구현 방식이 다릅니다. ICE가 별도의 레지스터 블록을 통해 키를 관리하는 반면, FMP는 PRDT(Physical Region Descriptor Table)의 각 엔트리에 암호화 정보를 직접 포함시킵니다.

ICE vs FMP 인라인 암호화 비교
특성Qualcomm ICESamsung FMP
키 저장 위치ICE 전용 레지스터 (키슬롯)PRDT 엔트리 내 임베딩
키 프로그래밍사전에 키슬롯에 프로그래밍매 I/O마다 PRDT에 포함
키슬롯 수 제한32개 (고정)제한 없음 (PRDT 단위)
Wrapped Key지원 (v3+)미지원 (업스트림 기준)
알고리즘AES-256-XTS, AES-256-CTSAES-256-XTS
DUN 크기64비트64비트
커널 통합blk-crypto + qcom_iceblk-crypto + exynos-fmp
/* FMP 키 프로그래밍 — PRDT 기반 방식 */

/*
 * FMP는 PRDT의 각 세그먼트에 암호화 파라미터를 직접 삽입합니다.
 * 표준 PRDT 엔트리(16바이트)가 FMP 확장 필드(추가 16바이트)를 포함하여
 * 총 32바이트로 확장됩니다.
 *
 * struct fmp_sg_entry {
 *     // 표준 PRDT 필드 (16바이트)
 *     __le32  des0;     // base address lower
 *     __le32  des1;     // base address upper
 *     __le32  des2;     // reserved
 *     __le32  des3;     // size (+ ODD/EDD 비트)
 *
 *     // FMP 확장 필드 (16바이트) — 암호화 설정
 *     __le32  file_iv0; // IV (Initialization Vector) [31:0]
 *     __le32  file_iv1; // IV [63:32]
 *     __le32  file_iv2; // IV [95:64]
 *     __le32  file_iv3; // IV [127:96]
 * };
 *
 * FMP 활성화: des3의 FAS(File Algorithm Select) 비트를 설정
 *   FAS = 0b00 : 암호화 비활성화
 *   FAS = 0b10 : AES-256-XTS 파일 암호화
 */

/* exynos_ufs_program_key() — vops->program_key 콜백 */
static int exynos_ufs_program_key(struct ufs_hba *hba,
                                   const union ufs_crypto_cfg_entry *cfg,
                                   int slot)
{
    /*
     * FMP는 키슬롯 개념이 아닌 PRDT 단위로 동작하므로,
     * 이 함수는 blk-crypto 프레임워크의 키슬롯 인터페이스와
     * FMP의 PRDT 기반 방식을 연결하는 어댑터 역할을 합니다.
     *
     * 실제 키 삽입은 ufshcd_prepare_req_desc_hdr() 시점에
     * PRDT를 구성하면서 이루어집니다.
     */
    return 0;
}

Exynos UFS PHY — PMA 캘리브레이션

Exynos UFS PHY는 PMA(Physical Medium Attachment) 레지스터를 통해 TX/RX 신호 특성을 세밀하게 조절합니다. TXHSLV(TX High-Speed Level)와 RXHSLV(RX High-Speed Level) 값은 PCB 트레이스 길이, 임피던스, 보드 레이아웃에 따라 SoC별로 달라지며, 캘리브레이션이 부정확하면 링크 수립 실패나 CRC 에러가 발생합니다.

/* Exynos UFS PHY 캘리브레이션 — ufs-exynos.c */

/*
 * link_startup_notify(PRE_CHANGE)에서 PHY 파라미터를 설정합니다.
 * 각 SoC(Exynos 990, 2100, 2200 등)별로 최적 값이 다릅니다.
 */
static int exynos_ufs_pre_link(struct exynos_ufs *ufs)
{
    struct ufs_hba *hba = ufs->hba;

    /* UniPro 파라미터 설정 */
    ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);

    /* TX 캘리브레이션 */
    for_each_ufs_tx_lane(ufs, i) {
        ufshcd_dme_set(hba,
            UIC_ARG_MIB_SEL(TX_HIBERN8TIME_CAPABILITY, i), 0x01);
        ufshcd_dme_set(hba,
            UIC_ARG_MIB_SEL(TX_HS_EQUALIZER_SETTING, i),
            ufs->drv_data->tx_eq_val);  /* SoC별 이퀄라이저 값 */
    }

    /* RX 캘리브레이션 */
    for_each_ufs_rx_lane(ufs, i) {
        ufshcd_dme_set(hba,
            UIC_ARG_MIB_SEL(RX_HS_G1_SYNC_LENGTH_CAPABILITY, i), 0x4F);
        ufshcd_dme_set(hba,
            UIC_ARG_MIB_SEL(RX_HS_G2_SYNC_LENGTH_CAPABILITY, i), 0x4F);
        ufshcd_dme_set(hba,
            UIC_ARG_MIB_SEL(RX_HS_G3_SYNC_LENGTH_CAPABILITY, i), 0x4F);
    }

    /* Vendor-Specific 파라미터 (M-PHY 표준 외 확장) */
    exynos_ufs_config_phy(ufs);

    return 0;
}

/*
 * post_link에서 Gear/Rate 변경 후 추가 캘리브레이션 수행:
 * - HS-Series B (Rate-B)에서는 추가 SYNC 길이 조정 필요
 * - Gear 4에서는 RX 이퀄라이저 부스트 값 증가
 */

EOM (Eye Opening Monitor)

EOM은 Exynos UFS PHY에 내장된 신호 품질 측정 기능으로, 수신 데이터의 아이 다이어그램(Eye Diagram)을 소프트웨어에서 분석할 수 있게 합니다. PHY 캘리브레이션의 적절성을 검증하거나, 보드 설계의 시그널 인테그리티 문제를 진단할 때 사용됩니다.

/* EOM (Eye Opening Monitor) — debugfs를 통한 아이 다이어그램 측정 */

/*
 * 측정 원리:
 *   1. PHY RX 샘플링 포인트를 시간(phase)·전압(voltage) 축으로 이동
 *   2. 각 좌표에서 비트 에러율(BER) 측정
 *   3. BER < 임계값인 영역 = "Eye Opening" (개방 영역)
 *   4. 개방 영역이 클수록 신호 품질이 양호
 *
 * debugfs 인터페이스:
 *   /sys/kernel/debug/ufshcd0/eom
 *
 * 출력 예시 (phase × voltage 격자의 BER 맵):
 *   phase[-32..+32], voltage[-32..+32]
 *   중앙 영역의 BER = 0 → Eye Open
 *   가장자리 BER > 0 → Eye Closing (jitter/noise 영향)
 *
 * 실무 활용:
 *   - 보드 브링업 시 Eye가 충분히 열려 있는지 확인
 *   - HS-Gear 4/5에서 Eye가 닫히면 TX 이퀄라이저 또는 RX DFE 조정
 *   - 온도/전압 변동에 따른 마진 확인
 */

/*
 * Samsung 자체 VS(Vendor-Specific) 레지스터를 통한 EOM 제어:
 *   VS_DEBUGCLOCKENABLE — 디버그 클럭 활성화
 *   VS_SAVECONFIGTIME    — 캘리브레이션 저장 시간
 *   VS_DEBUGCOUNTENABLE  — 에러 카운터 활성화
 */

MediaTek — VA09, MPHY, 클럭 게이팅

MediaTek의 UFS 드라이버(ufs-mediatek.c)는 MT8183, MT8195, Dimensity 시리즈 등의 SoC에서 사용됩니다. VA09 레귤레이터 관리, 자체 MPHY IP, 다단계 클럭 게이팅 등 전력 효율에 초점을 맞춘 구현이 특징입니다.

VA09 레귤레이터 및 전원 시퀀싱

/* MediaTek UFS 전원 관리 — ufs-mediatek.c */

/*
 * MediaTek SoC는 UFS PHY에 VA09(0.9V 아날로그 전원)를 별도 레귤레이터로 공급합니다.
 * 이 전원은 M-PHY 아날로그 프론트엔드에 필수이며,
 * 전원 On/Off 순서가 정확하지 않으면 PHY가 불안정해집니다.
 */
static int ufs_mtk_mphy_power_on(struct ufs_hba *hba, bool on)
{
    struct ufs_mtk_host *host = ufshcd_get_variant(hba);
    int ret = 0;

    if (on) {
        /* 전원 On 순서: VA09 → MPHY → Reference Clock */
        ret = regulator_enable(host->reg_va09);
        if (ret)
            return ret;

        /* PHY 안정화 대기 */
        usleep_range(200, 210);

        /* MPHY 활성화 */
        ufs_mtk_setup_ref_clk(hba, true);

        /* MPHY 캘리브레이션 트리거 */
        ufs_mtk_mphy_ctrl(host, true);
    } else {
        /* 전원 Off 순서: MPHY → Reference Clock → VA09 (역순) */
        ufs_mtk_mphy_ctrl(host, false);
        ufs_mtk_setup_ref_clk(hba, false);
        regulator_disable(host->reg_va09);
    }

    return ret;
}

/*
 * MediaTek 전원 도메인 구조:
 *
 *   VCC (3.3V/1.8V) ──→ UFS 디바이스 (NAND 전원)
 *   VCCQ (1.2V)     ──→ UFS 디바이스 (I/O 전원)
 *   VCCQ2 (1.8V)    ──→ UFS 디바이스 (보조 I/O)
 *   VA09 (0.9V)     ──→ MPHY 아날로그 프론트엔드 (SoC 내부)
 *   VCC_HCI          ──→ UFS 호스트 컨트롤러 (SoC 내부)
 *
 * Suspend 시 VA09를 끄면 ~5mW 추가 절전 효과가 있으나,
 * Resume 시 PHY 재캘리브레이션이 필요하여 ~2ms 지연이 발생합니다.
 */

다단계 클럭 게이팅

/* MediaTek 클럭 관리 — 세분화된 클럭 게이팅 */

/*
 * MediaTek SoC는 UFS 관련 클럭을 5개 이상으로 세분화하여
 * 각 클럭을 독립적으로 게이팅할 수 있습니다.
 *
 * 클럭 구성 예시 (MT8195):
 *   1. ufs_sel       — UFS 코어 클럭 소스 선택 (200MHz/400MHz)
 *   2. ufs_hclk      — AHB 버스 클럭 (호스트 레지스터 접근)
 *   3. ufs_unipro    — UniPro 계층 클럭
 *   4. ufs_mp_sap    — MPHY 서브-어댑터 프로토콜 클럭
 *   5. ufs_encrypt   — 암호화 엔진 클럭
 *
 * 유휴 상태에서는 1~5 모두 게이팅 → 최소 전력 소비
 * Hibernate 상태에서는 2~5만 게이팅 → 빠른 복귀
 * 활성 상태에서는 5만 조건부 게이팅 → 암호화 미사용 시 절전
 */

static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on,
                                 enum ufs_notify_change_status status)
{
    struct ufs_mtk_host *host = ufshcd_get_variant(hba);

    if (status == PRE_CHANGE && !on) {
        /* 클럭 Off 전: 진행 중인 트랜잭션 완료 대기 */
        ufs_mtk_wait_idle_state(hba, 5);

        /* Reference 클럭 비활성화 */
        ufs_mtk_setup_ref_clk(hba, false);
    } else if (status == POST_CHANGE && on) {
        /* 클럭 On 후: Reference 클럭 활성화 + PHY 안정화 */
        ufs_mtk_setup_ref_clk(hba, true);
    }

    return 0;
}

MediaTek 전용 Crypto IP

MediaTek SoC는 UFS 인라인 암호화를 위해 자체 설계한 crypto IP를 사용합니다. Qualcomm ICE와 달리 암호화 엔진이 UFS 컨트롤러 내부에 더 긴밀하게 통합되어 있으며, UFSHCI 표준의 crypto_cap 레지스터를 통해 능력(capability)을 보고합니다.

/* MediaTek Crypto 초기화 — ufs-mediatek.c */
static int ufs_mtk_init_crypto(struct ufs_hba *hba)
{
    struct ufs_mtk_host *host = ufshcd_get_variant(hba);

    /* Crypto 엔진 클럭 활성화 */
    if (host->clk_encrypt) {
        clk_prepare_enable(host->clk_encrypt);
    }

    /*
     * UFSHCI 표준 crypto 인터페이스 사용:
     *   hba->crypto_capabilities   — 지원 알고리즘/키 크기
     *   hba->crypto_cap_array      — 각 capability의 세부 사항
     *
     * MediaTek은 표준 UFSHCI crypto 레지스터를 통해
     * 키 프로그래밍을 수행하므로 별도의 vops->program_key가
     * 불필요한 경우도 있습니다.
     *
     * 단, SoC에 따라 crypto 엔진 리셋 순서에 quirk가 있어
     * suspend/resume 시 특별한 처리가 필요합니다:
     *   - Resume 후 crypto 엔진 리셋 + 키 재프로그래밍
     *   - hba->caps |= UFSHCD_CAP_CRYPTO를 설정하여 코어 드라이버에 알림
     */

    return 0;
}

HiSilicon Kirin — 다단계 PHY 초기화

HiSilicon(화웨이 반도체 부문)의 UFS 드라이버(ufs-hisi.c)는 Kirin 960/970/980/990 SoC를 지원합니다. 다른 벤더 드라이버에 비해 상대적으로 단순하지만, PHY 초기화 시퀀스가 매우 길고 SoC 리비전별로 세밀한 차이가 있는 것이 특징입니다.

/* HiSilicon UFS PHY 초기화 — ufs-hisi.c */

/*
 * Kirin SoC의 PHY 초기화는 3단계로 수행됩니다:
 *   1단계: SYSCTRL 레지스터를 통한 전원/리셋 제어
 *   2단계: PHY 레지스터 직접 프로그래밍 (수십 개의 레지스터)
 *   3단계: PLL 락(Lock) 확인 및 캘리브레이션 완료 대기
 */
static int ufs_hisi_link_startup_pre_change(struct ufs_hba *hba)
{
    struct ufs_hisi_host *host = ufshcd_get_variant(hba);
    int ret;

    /* SYSCTRL: UFS 리셋 해제 */
    writel(BIT_UFS_PSW_MTCMOS_EN,
           host->ufs_sys_ctrl + UFS_SYS_PSW_POWER_CTRL);
    udelay(10);

    /* PHY 레지스터 프로그래밍 (SoC별) */
    if (host->caps & UFS_HISI_CAP_PHY10nm) {
        /* Hi3670/Kirin 980: 10nm PHY */
        ufs_hisi_phy_init_10nm(host);
    } else {
        /* Hi3660/Kirin 960: 이전 세대 PHY */
        ufs_hisi_phy_init(host);
    }

    /* UniPro 파라미터 */
    ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0);

    /*
     * Hi3660 특유 Quirk:
     * - Hibernate Exit 후 첫 번째 커맨드가 간헐적으로 타임아웃
     * - 워크어라운드: H8 Exit 후 100μs 추가 대기
     *
     * Hi3670 특유 Quirk:
     * - HS-Gear 3 Rate-B에서 RX 에러 발생 가능
     * - 워크어라운드: RX_HS_G3_SYNC_LENGTH를 표준보다 길게 설정
     */

    return 0;
}

/*
 * HiSilicon은 PHY를 별도 드라이버로 분리하지 않고
 * UFS 호스트 드라이버 내부에서 직접 PHY 레지스터를 제어합니다.
 * (다른 벤더는 대부분 drivers/phy/ 아래 별도 PHY 드라이버를 사용)
 *
 * 이 때문에 코드가 하드웨어에 강하게 결합되어 있으며,
 * SoC 리비전 추가 시 if/else 분기가 늘어나는 구조적 한계가 있습니다.
 */

Renesas R-Car — 자동차 등급 UFS

Renesas의 UFS 드라이버(ufs-renesas.c)는 R-Car Gen4(V4H) SoC를 대상으로 하며, 자동차 인포테인먼트와 ADAS(Advanced Driver-Assistance Systems) 시스템에서 사용됩니다. 자동차 환경의 넓은 온도 범위(-40°C~+125°C)와 높은 신뢰성 요구사항에 대응하는 것이 핵심입니다.

/* Renesas R-Car UFS — ufs-renesas.c */

/*
 * R-Car Gen4 UFS의 특징:
 *   1. 자동차 등급 온도 범위 대응: -40°C ~ +125°C
 *   2. 자체 설계 MPHY (Synopsys DesignWare 기반이 아님)
 *   3. 최소한의 vops 구현 — 핵심 기능만 포함
 */

static int ufs_renesas_init(struct ufs_hba *hba)
{
    struct ufs_renesas_priv *priv;

    priv = devm_kzalloc(hba->dev, sizeof(*priv), GFP_KERNEL);
    if (!priv)
        return -ENOMEM;

    ufshcd_set_variant(hba, priv);

    /*
     * R-Car Gen4는 Auto-Hibernate를 기본 활성화합니다.
     * 자동차 환경에서는 빈번한 Sleep/Wake 전이가 발생하므로
     * H8 타이머를 짧게(1ms) 설정하여 전력 소비를 최소화합니다.
     */
    hba->ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, 1) |
                FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, 3); /* 1ms */

    return 0;
}

static int ufs_renesas_pwr_change_notify(
    struct ufs_hba *hba,
    enum ufs_notify_change_status status,
    struct ufs_pa_layer_attr *dev_max_params,
    struct ufs_pa_layer_attr *dev_req_params)
{
    if (status == PRE_CHANGE) {
        /*
         * PHY 캘리브레이션 파라미터를 Gear에 맞게 조정
         *
         * 자동차 온도 범위에서는 고온에서 PHY 마진이 줄어들므로
         * HS-Gear 3 이상에서 TX/RX 이퀄라이저를 보수적으로 설정
         *
         * 레지스터 값은 Renesas 데이터시트에 의한 권장값:
         * - TX_AMPLITUDE: 고온 보상 +10%
         * - RX_THRESHOLD: 노이즈 마진 확보
         */
        ufs_renesas_set_phy_params(hba, dev_req_params);
    }

    return 0;
}

/*
 * 자동차 등급 UFS 디바이스의 특성:
 *   - eUFS(Embedded UFS) 2.1/3.1 사양 준수
 *   - AEC-Q100 인증 (자동차 전자부품 신뢰성)
 *   - 넓은 동작 온도 범위에서 성능 일관성 보장
 *   - RPMB를 통한 보안 저장 (차량 보안 키, OTA 검증)
 *   - Write Protect로 펌웨어 영역 보호
 */

Intel / Synopsys — PCI 기반 UFS

서버, 데스크탑, 개발 보드 환경에서는 UFS 컨트롤러가 PCI 버스에 연결됩니다. ufshcd-pci.c는 PCI 기반 UFS 호스트를 지원하며, Synopsys DesignWare UFS IP를 사용하는 SoC도 ufshcd-dwc.c 헬퍼를 통해 지원됩니다.

/* PCI 기반 UFS — ufshcd-pci.c */

/*
 * PCI UFS는 디바이스 트리 대신 PCI VID/DID로 디바이스를 식별합니다.
 * 플랫폼(SoC) 드라이버와 달리 PHY 초기화, 레귤레이터, 클럭 관리가
 * 불필요하거나 PCI 서브시스템이 담당합니다.
 */
static const struct pci_device_id ufshcd_pci_tbl[] = {
    { PCI_VENDOR_ID_INTEL, 0x9DFA,  /* Intel Ice Lake */
      PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&ufs_intel_cnl_hba_vops },
    { PCI_VENDOR_ID_INTEL, 0x4B41,  /* Intel Elkhart Lake */
      PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&ufs_intel_ehl_hba_vops },
    { PCI_VENDOR_ID_INTEL, 0x0,     /* Intel LKF */
      PCI_ANY_ID, PCI_ANY_ID, 0, 0, (kernel_ulong_t)&ufs_intel_lkf_hba_vops },
    { }  /* terminate list */
};

/*
 * Intel UFS vops는 최소한의 구현:
 *   - init: LTR(Latency Tolerance Reporting) 설정
 *   - resume: PCI D0 복귀 후 레지스터 복원
 *   - link_startup: Intel PHY 고유 파라미터 설정
 *
 * Synopsys DesignWare UFS TC(Test Chip):
 *   - FPGA/ASIC 검증용 UFS 호스트 컨트롤러
 *   - ufshcd-dwc.c + tc-dwc-g210 시리즈 드라이버
 *   - 40비트 어드레싱 지원 (DMA_BIT_MASK(40))
 */

디바이스 Quirks — ufs_fixups[] 테이블

UFS 디바이스(NAND 측) 제조사마다 표준 사양과 미묘하게 다른 동작을 보이는 경우가 있습니다. Linux 커널은 ufs_fixups[] 테이블을 통해 이러한 디바이스별 워크어라운드(quirk)를 관리합니다. 디바이스의 Manufacturer IDModel 문자열을 매칭하여 적절한 quirk 플래그를 적용합니다.

ufs_fixups[] Quirk 매칭 흐름 Device Descriptor iManufacturerName iProductName + wManufacturerID 읽기 ufshcd_fixup_dev_quirks() ufs_fixups[] 테이블 순회 MID + Model 매칭 적용 hba->dev_quirks |= flags 동작 분기에서 quirk 플래그 확인 워크어라운드 로직 활성화 주요 Quirk 플래그: UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS UFS_DEVICE_QUIRK_PA_TACTIVATE UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM UFS_DEVICE_QUIRK_DELAY_AFTER_LPM UFS_DEVICE_QUIRK_NO_LINK_OFF UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES UFS_DEVICE_NO_VCCQ UFS_DEVICE_NO_FASTAUTO ※ 각 quirk는 특정 디바이스의 비표준 동작을 보정합니다. 예: DL NAC 에러 후 추가 복구 절차, Hibernate 진입 전 지연 삽입 등 ※ 커널 v6.x 기준 약 15개 이상의 디바이스 quirk가 정의되어 있으며, 벤더별 vops->apply_dev_quirks()로 추가 quirk도 적용 가능
/* ufs_fixups[] — 디바이스별 워크어라운드 테이블 (ufshcd.c) */
static const struct ufs_dev_quirk ufs_fixups[] = {
    /*
     * Samsung UFS 디바이스
     * - 초기 모델에서 DL(Data Link) NAC 에러 복구 문제
     * - PA_TACTIVATE 타이밍 조정 필요
     */
    { .wmanufacturerid = UFS_VENDOR_SAMSUNG,
      .model = UFS_ANY_MODEL,
      .quirk = UFS_DEVICE_QUIRK_RECOVERY_FROM_DL_NAC_ERRORS |
               UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME |
               UFS_DEVICE_QUIRK_PA_TACTIVATE },

    /*
     * SK hynix UFS 디바이스
     * - Hibernate 진입 전 지연 필요 (NAND 내부 GC 완료 대기)
     * - PA_TACTIVATE 표준보다 긴 값 필요
     */
    { .wmanufacturerid = UFS_VENDOR_SKHYNIX,
      .model = UFS_ANY_MODEL,
      .quirk = UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME |
               UFS_DEVICE_QUIRK_PA_TACTIVATE },

    /*
     * Kioxia (구 Toshiba) UFS 디바이스
     * - PA_TACTIVATE 타이밍 조정
     * - 일부 모델에서 Hibernate 후 링크 재수립 지연
     */
    { .wmanufacturerid = UFS_VENDOR_TOSHIBA,
      .model = UFS_ANY_MODEL,
      .quirk = UFS_DEVICE_QUIRK_HOST_PA_SAVECONFIGTIME |
               UFS_DEVICE_QUIRK_PA_TACTIVATE |
               UFS_DEVICE_QUIRK_DELAY_AFTER_LPM },

    /*
     * Micron UFS 디바이스
     * - 특정 모델에서 확장 기능(Extended Features) 보고 방식이 다름
     */
    { .wmanufacturerid = UFS_VENDOR_MICRON,
      .model = UFS_ANY_MODEL,
      .quirk = UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM },

    { } /* sentinel */
};

/*
 * Quirk 적용 시점: ufshcd_probe_hba() → ufshcd_fixup_dev_quirks()
 *
 * 매칭 알고리즘:
 *   1. ufs_fixups[] 순회
 *   2. wmanufacturerid 비교 (UFS_ANY_VENDOR는 전체 매칭)
 *   3. model 문자열 비교 (UFS_ANY_MODEL은 전체 매칭)
 *   4. 매칭되면 hba->dev_quirks |= fixup->quirk
 *
 * 벤더 추가 quirk: vops->apply_dev_quirks()로 플랫폼별 추가 적용 가능
 */
static void ufshcd_fixup_dev_quirks(struct ufs_hba *hba,
                                     const struct ufs_dev_quirk *fixups)
{
    const struct ufs_dev_quirk *f;
    struct ufs_dev_info *dev_info = &hba->dev_info;

    if (!fixups)
        return;

    for (f = fixups; f->quirk; f++) {
        if ((f->wmanufacturerid == dev_info->wmanufacturerid ||
             f->wmanufacturerid == UFS_ANY_VENDOR) &&
            ((f->model == UFS_ANY_MODEL) ||
             !strcmp(f->model, dev_info->model)))
            hba->dev_quirks |= f->quirk;
    }
}

주요 Quirk 상세 설명

UFS Device Quirk 플래그 상세
Quirk 플래그영향받는 벤더문제 현상워크어라운드
RECOVERY_FROM_DL_NAC_ERRORS Samsung Data Link 계층 NAC(Negative Acknowledge) 에러 후 링크가 불안정해짐 NAC 에러 감지 시 추가 복구 시퀀스 수행 (링크 재수립)
PA_TACTIVATE Samsung, SK hynix, Kioxia PA_TACTIVATE(활성화 시간) 기본값이 디바이스 요구사항보다 짧음 디바이스가 보고하는 peer PA_TACTIVATE 값으로 재설정
HOST_PA_SAVECONFIGTIME Samsung, SK hynix, Kioxia 전원 모드 변경 시 Config 저장 시간이 부족하여 Gear 전환 실패 PA_SAVECONFIGTIME을 디바이스 요구에 맞게 증가
DELAY_BEFORE_LPM Micron LPM(Low Power Mode) 진입 전 NAND 내부 작업 미완료 Hibernate 진입 전 추가 지연 삽입 (수 ms)
DELAY_AFTER_LPM Kioxia LPM 탈출 후 디바이스 응답 지연 Hibernate Exit 후 추가 대기 시간 삽입
NO_LINK_OFF 일부 디바이스 Link Off(전원 차단) 후 재수립 실패 Suspend 시 Link Off 대신 Hibernate 유지
NO_VCCQ 특정 보드 설계 VCCQ 레귤레이터가 물리적으로 없거나 항상 On VCCQ 전원 제어 스킵
Quirk 디버깅 팁: 새 UFS 디바이스를 브링업할 때 링크 에러, Gear 전환 실패, Hibernate 실패가 발생하면 dmesg | grep -i "quirk\|fixup"로 적용된 quirk를 확인하세요. quirk가 적용되지 않은 새 디바이스라면 ufs_fixups[]에 엔트리를 추가하거나, vops->apply_dev_quirks()에서 플랫폼별 quirk를 적용해야 할 수 있습니다. cat /sys/bus/platform/devices/*/string_descriptors/manufacturer_namecat /sys/bus/platform/devices/*/string_descriptors/product_name으로 디바이스 식별 정보를 확인할 수 있습니다.

UFS 디바이스 제조사별 NAND 특성

UFS 디바이스의 성능과 수명은 내부 NAND 플래시의 특성에 크게 좌우됩니다. 각 디바이스 제조사는 자체 NAND 기술을 기반으로 Write Booster, HPB, 가비지 컬렉션(GC) 등의 FTL(Flash Translation Layer) 전략을 다르게 구현하며, 이러한 차이가 실제 워크로드에서의 성능과 수명에 영향을 미칩니다.

UFS 디바이스 제조사별 NAND 특성 비교 (UFS 4.0 기준)
특성SamsungSK hynixKioxia / WDMicron
NAND 기술 V-NAND 8세대 (236단) 238단 4D NAND BiCS8 (218단) 232단 TLC NAND
최대 순차 읽기 ~4,300 MB/s ~4,200 MB/s ~3,500 MB/s ~4,000 MB/s
최대 순차 쓰기 ~2,800 MB/s ~2,800 MB/s ~2,500 MB/s ~2,600 MB/s
Write Booster 최대 양호 (큰 SLC 버퍼) 양호 양호 양호
HPB 지원 HPB 2.0 (Active Region 동적 관리) HPB 2.0 HPB 1.0/2.0 HPB 2.0
내구성 (TBW) 높음 (V-NAND 최적화) 높음 보통~높음 보통~높음
Manufacturer ID 0x1CE (UFS_VENDOR_SAMSUNG) 0x1AD (UFS_VENDOR_SKHYNIX) 0x198 (UFS_VENDOR_TOSHIBA) 0x12C (UFS_VENDOR_MICRON)
Linux Quirks DL NAC 복구, PA_TACTIVATE PA_TACTIVATE, SAVECONFIGTIME PA_TACTIVATE, LPM 후 지연 LPM 전 지연

Write Booster 동작 차이

Write Booster는 TLC/QLC NAND의 일부 영역을 SLC 모드로 사용하여 쓰기 속도를 가속하는 기능입니다. 제조사마다 SLC 버퍼 크기, 플러싱 정책, 잔여 용량에 따른 동작이 다릅니다.

/* Write Booster 제조사별 동작 차이 */

/*
 * 공통 동작 원리:
 *   1. 호스트가 Write Booster 활성화 → bWriteBoosterBufferFlushEn = 1
 *   2. 쓰기 데이터가 SLC 버퍼에 먼저 기록 (고속)
 *   3. 유휴 시간에 SLC → TLC 플러싱 (저속, 백그라운드)
 *   4. SLC 버퍼가 가득 차면 직접 TLC에 기록 (저속)
 *
 * Samsung 특성:
 *   - 상대적으로 큰 SLC 버퍼 (전체 용량의 ~3~5%)
 *   - 어그레시브한 플러싱 정책 (유휴 1초 이내 시작)
 *   - bAvailableWriteBoosterBufferSize로 잔여 SLC 버퍼 조회 가능
 *   - 온도가 높으면 (>45°C) 플러싱 우선도 증가
 *
 * SK hynix 특성:
 *   - 적응형 버퍼 관리 (사용 패턴에 따라 SLC 영역 크기 조절)
 *   - 디바이스 수명에 따라 SLC 영역 축소 (수명 보호)
 *
 * Kioxia/WD 특성:
 *   - 보수적 플러싱 (유휴 시간이 길어야 플러싱 시작)
 *   - 높은 온도에서 WB 비활성화 가능
 *
 * Micron 특성:
 *   - WB 모드 전환 시 약간의 지연 (DELAY_BEFORE_LPM quirk 관련)
 *   - 예측 기반 플러싱 (연속 쓰기 패턴 감지 시 사전 플러싱)
 */

/*
 * Write Booster 상태 확인 (공통):
 *   cat /sys/bus/platform/devices/*/attributes/wb_on
 *   cat /sys/bus/platform/devices/*/attributes/wb_buf_flush_status
 *   cat /sys/bus/platform/devices/*/attributes/wb_avail_buf
 *   cat /sys/bus/platform/devices/*/attributes/wb_buf_lifetime_est
 */

HPB 구현 차이

HPB(Host Performance Booster)는 UFS 디바이스의 L2P(Logical-to-Physical) 매핑 테이블 중 자주 접근하는 영역을 호스트 메모리(DRAM)에 캐싱하여 랜덤 읽기 성능을 개선합니다. HPB 1.0은 디바이스가 능동적으로 매핑을 추천하고, HPB 2.0은 호스트가 주도적으로 관리합니다.

/* HPB 제조사별 차이 — drivers/ufs/core/ufshpb.c */

/*
 * HPB 2.0 공통 동작:
 *   1. 호스트가 READ BUFFER(HPB_READ) 커맨드로 L2P 엔트리 요청
 *   2. 디바이스가 L2P 데이터를 응답
 *   3. 호스트가 DRAM에 캐싱
 *   4. 이후 읽기 시 HPB_READ 커맨드에 물리 주소를 직접 포함
 *   5. 디바이스가 L2P 테이블 조회를 건너뛰어 지연 시간 감소
 *
 * Samsung HPB 특성:
 *   - Active Region 수: 최대 64개 (디바이스 모델에 따라 다름)
 *   - Active Region당 Subregion: 최대 8개
 *   - L2P 캐시 적중 시 ~30% 랜덤 읽기 지연 개선
 *   - 디바이스 내부에서도 L2P 캐시 유지 (호스트 캐시와 이중)
 *
 * SK hynix HPB 특성:
 *   - 좀 더 공격적인 Inactive/Active 전환
 *   - 써멀 스로틀링 시 HPB 동작 축소
 *
 * HPB 활성화 확인:
 *   cat /sys/bus/platform/devices/*/device_descriptor/hpb_version
 *   cat /sys/bus/platform/devices/*/geometry_descriptor/hpb_region_size
 *   cat /sys/bus/platform/devices/*/geometry_descriptor/hpb_number_lu
 */

/*
 * HPB sysfs 제어:
 *   echo 1 > /sys/class/scsi_device/0:0:0:0/device/hpb_param/requeue_timeout_ms
 *   cat /sys/class/scsi_device/0:0:0:0/device/hpb_stats/hit_cnt
 *   cat /sys/class/scsi_device/0:0:0:0/device/hpb_stats/miss_cnt
 *   cat /sys/class/scsi_device/0:0:0:0/device/hpb_stats/rb_noti_cnt
 */

Logical Unit 배치 전략

UFS 디바이스는 최대 32개의 LU(Logical Unit)를 지원하며, 각 LU는 독립적인 속성(용량, 메모리 타입, Write Protect 등)을 가질 수 있습니다. 제조사마다 기본 LU 배치가 다르며, 출하 시 UFS Provisioning으로 구성합니다.

일반적인 LU 배치 구성
LU 번호용도메모리 타입크기 (예시)비고
LU 0부트 파티션 AEnhanced (SLC)4~32 MB부트로더, 보안 부팅
LU 1부트 파티션 BEnhanced (SLC)4~32 MBA/B 부팅 지원
LU 2메인 사용자 데이터Normal (TLC/QLC)전체 용량의 대부분/data, /system
LU 3RPMBEnhanced (SLC)16~32 MB보안 스토리지
Well-Known LU장치 관리BOOT, RPMB, Device
# LU 구성 확인
lsscsi -g
# [0:0:0:0]  disk  SAMSUNG  KLUEG4UHDB-B0D1  0400  /dev/sda  /dev/sg0
# [0:0:0:1]  disk  SAMSUNG  KLUEG4UHDB-B0D1  0400  /dev/sdb  /dev/sg1
# [0:0:0:2]  disk  SAMSUNG  KLUEG4UHDB-B0D1  0400  /dev/sdc  /dev/sg2
# [0:0:0:49152] ...  SAMSUNG  KLUEG4UHDB-B0D1       /dev/sg3  ← RPMB W-LU

# 각 LU 속성 확인 (ufs-utils)
ufs-utils desc -a -p /dev/bsg/ufs-bsg0 -t unit -i 0
# bLUEnable: 0x01
# bBootLunID: 0x01
# bLUWriteProtect: 0x00
# bMemoryType: 0x03 (Enhanced 1 = SLC)
# qPhyMemResourceCount: 0x00001000

# Provisioning (LU 재구성) — 주의: 데이터 완전 삭제
ufs-utils desc -w -p /dev/bsg/ufs-bsg0 -t conf -i 0
# bConfigDescrLock이 0x00인 경우에만 가능 (공장 출하 상태)
벤더 식별 방법: UFS 디바이스의 제조사를 코드에서 확인하려면 Device Descriptor의 wManufacturerID 필드를 읽으면 됩니다. 커널은 include/ufs/ufs.h에 주요 벤더 ID를 정의합니다: UFS_VENDOR_SAMSUNG = 0x1CE, UFS_VENDOR_SKHYNIX = 0x1AD, UFS_VENDOR_TOSHIBA = 0x198 (Kioxia), UFS_VENDOR_MICRON = 0x12C. cat /sys/bus/platform/devices/*/device_descriptor/manufacturer_id로 확인하거나, ufs-utils desc -a -p /dev/bsg/ufs-bsg0 -t device로 전체 Device Descriptor를 조회할 수 있습니다.

SCSI 통합 및 블록 레이어 연동

UFS는 SCSI 프로토콜 위에 구축되어 있으므로, Linux 커널에서 UFS 디바이스는 SCSI 서브시스템을 통해 관리됩니다. 애플리케이션의 I/O 요청이 VFS → Block Layer(blk-mq) → SCSI mid-layer → UFS HCD → UFSHCI → M-PHY를 거쳐 UFS 디바이스에 도달하는 전체 경로를 이해하는 것이 UFS 성능 최적화와 디버깅의 핵심입니다.

UFS I/O 경로 — Application → UFS Device Application (read/write) VFS (Virtual File System) Block Layer (blk-mq) I/O 스케줄링, request 병합, hw queue 분배 SCSI Mid-Layer scsi_cmnd 생성, CDB 구성, 에러 복구 UFS Host Controller Driver (ufshcd) UPIU 패킹, UTRD/PRDT 구성, Doorbell Ring UFSHCI (HW Registers) UTRLDBR Doorbell, Transfer/Task Mgmt Ring M-PHY / UniPro UFS Device (NAND Flash) struct request struct scsi_cmnd UPIU + UTRD Crypto 처리

scsi_host_template 정의

UFS 드라이버는 struct scsi_host_template을 통해 SCSI 미드 레이어에 등록됩니다. 이 템플릿은 I/O 처리(queuecommand)와 에러 복구(eh_*_handler) 콜백을 정의합니다.

/* drivers/ufs/core/ufshcd.c — SCSI host template */
static const struct scsi_host_template ufshcd_driver_template = {
    .module                 = THIS_MODULE,
    .name                   = UFSHCD,
    .proc_name              = UFSHCD,
    .map_queues             = ufshcd_map_queues,
    .queuecommand           = ufshcd_queuecommand,
    .mq_poll                = ufshcd_poll,
    .slave_alloc            = ufshcd_slave_alloc,
    .slave_configure        = ufshcd_slave_configure,
    .slave_destroy          = ufshcd_slave_destroy,
    .change_queue_depth     = ufshcd_change_queue_depth,
    .eh_abort_handler       = ufshcd_abort,
    .eh_device_reset_handler = ufshcd_eh_device_reset_handler,
    .eh_host_reset_handler  = ufshcd_eh_host_reset_handler,
    .this_id                = -1,
    .sg_tablesize           = SG_ALL,
    .cmd_per_lun            = UFSHCD_CMD_PER_LUN,
    .can_queue              = UFSHCD_CAN_QUEUE,
    .max_segment_size       = PRDT_DATA_BYTE_COUNT_MAX,
    .max_host_blocked       = 1,
    .track_queue_depth      = 1,
    .skip_settle_delay      = 1,
    .sdev_groups            = ufshcd_driver_groups,
};

queuecommand — I/O 요청 처리

/* ufshcd_queuecommand() — blk-mq → SCSI → UFS 진입점 */
static int ufshcd_queuecommand(struct Scsi_Host *host,
                               struct scsi_cmnd *cmd)
{
    struct ufs_hba *hba = shost_priv(host);
    int tag = scsi_cmd_to_rq(cmd)->tag;
    struct ufshcd_lrb *lrbp = &hba->lrb[tag];

    /* 1. LRB(Local Reference Block) 설정 */
    lrbp->cmd = cmd;
    lrbp->task_tag = tag;
    lrbp->lun = ufshcd_scsi_to_upiu_lun(cmd->device->lun);

    /* 2. UPIU 구성 (Command UPIU 패킹) */
    ufshcd_compose_devman_upiu(hba, lrbp);  /* 또는 ufshcd_compose_scsi_cmd() */

    /* 3. UTRD(Transfer Request Descriptor) 설정 */
    ufshcd_prepare_utp_scsi_cmd_upiu(lrbp, cmd->sc_data_direction);

    /* 4. Crypto 설정 (인라인 암호화 사용 시) */
    ufshcd_prepare_req_desc_hdr(lrbp, &lrbp->tr_type, UTP_CMD_TYPE_SCSI);

    /* 5. Doorbell Ring — HW에 전송 요청 */
    ufshcd_send_command(hba, tag, hba->nutrs);

    return 0;
}

/dev/sdX 디바이스 생성 흐름

단계함수/동작설명
1scsi_scan_host()SCSI 호스트에 연결된 LU 스캔 시작
2REPORT LUNS 커맨드UFS 디바이스에 존재하는 LU 목록 조회
3INQUIRY 커맨드각 LU의 디바이스 유형, 벤더 정보 확인
4scsi_add_device()struct scsi_device 생성 및 등록
5sd_probe()SCSI 디스크 드라이버가 LU 인식, gendisk 생성
6device_add_disk()/dev/sdX 블록 디바이스 노드 생성

Well-Known LU (W-LU)

UFS 스펙은 일반 데이터 LU 외에 특수 목적의 Well-Known LU를 정의합니다. 이 W-LU들은 SCSI generic(/dev/sgX) 디바이스로 접근 가능하며, 디바이스 관리, RPMB 접근, 부트 LU 등의 기능을 제공합니다.

W-LUN ID이름용도접근 방식
0xD0REPORT LUNS W-LULU 목록 보고SCSI REPORT LUNS
0x50UFS Device W-LU디바이스 관리 (Descriptor/Flag/Attribute R/W)/dev/bsg/ufs-bsg0
0xC4RPMB W-LUReplay Protected Memory Block 접근/dev/sgX + SECURITY PROTOCOL
0xB0Boot W-LU부트 이미지 저장 (Boot LU A)/dev/sdX
blk-mq 큐 매핑: UFS는 blk-mq의 hardware queue를 UFS Transfer Request slot에 매핑합니다. 기존 SDB(Single Doorbell) 모드에서는 하나의 HW 큐만 사용하지만, MCQ(Multi-Circular Queue) 모드에서는 per-CPU 큐를 사용하여 병렬성을 극대화합니다. ufshcd_map_queues()가 이 매핑을 담당하며, MCQ 모드에서는 default, read, poll 3종의 큐 타입을 지원합니다.

MCQ — Multi-Circular Queue (UFS 4.0+)

MCQ(Multi-Circular Queue)는 UFS 4.0 스펙에서 도입된 새로운 큐 모델로, 기존의 Single Doorbell(SDB) 방식을 대체합니다. SDB에서는 모든 I/O 요청이 하나의 UTRL(UTP Transfer Request List)을 공유하고 단일 Doorbell 레지스터로 전송을 트리거했지만, MCQ는 NVMe와 유사한 Submission Queue(SQ)/Completion Queue(CQ) 쌍을 여러 개 생성하여 CPU별 독립적인 I/O 처리를 가능하게 합니다.

MCQ (Multi-Circular Queue) 아키텍처 CPU 0 CPU 1 CPU 2 CPU 3 SQ 0 (default) SQE[0]..SQE[N-1] SQ 1 (default) SQ 2 (read) SQ 3 (poll) UFS Host Controller (UFSHCI 4.0+) SQ Tail Doorbell Reg CQ Head Doorbell Reg MCQ Config Registers CQ 0 CQE[0]..CQE[N-1] CQ 1 CQ 2 CQ 3 Legacy SDB (Single Doorbell) UTRL (단일 Transfer Request List) UTRLDBR (단일 Doorbell Register) 모든 CPU가 하나의 doorbell 경쟁 Lock contention + Interrupt storm 최대 32 슬롯 MCQ (Multi-Circular Queue) SQ/CQ 쌍 × N (per-CPU 분배) 개별 SQ Tail / CQ Head Doorbell CPU별 독립 큐 → Lock-free 전송 CQ별 독립 인터럽트 → 분산 처리 큐당 최대 256 엔트리
MCQ의 핵심 이점: MCQ는 NVMe의 SQ/CQ 모델을 UFS에 도입한 것입니다. 기존 SDB 모드에서는 모든 CPU가 단일 UTRLDBR(Doorbell Register)에 접근해야 했으므로 spin lock 경쟁이 심했습니다. MCQ는 per-CPU SQ/CQ 쌍을 제공하여 lock contention을 제거하고, CQ별 인터럽트로 인터럽트 처리도 분산합니다. 결과적으로 랜덤 4K IOPS가 SDB 대비 40~60% 향상됩니다.

MCQ 레지스터 셋

레지스터오프셋설명
MCQCAP0x00 (MCQ Base)MCQ Capability — 지원 SQ/CQ 최대 수
SQATTRSQ Base + 0x00Submission Queue 속성 (크기, 타입)
SQLBASQ Base + 0x04SQ Base Address (Lower 32-bit)
SQUBASQ Base + 0x08SQ Base Address (Upper 32-bit)
SQDAOSQ Base + 0x0CSQ Doorbell Address Offset
SQISAOSQ Base + 0x10SQ Interrupt Status Address Offset
CQATTRCQ Base + 0x00Completion Queue 속성 (크기, 인터럽트 벡터)
CQLBACQ Base + 0x04CQ Base Address (Lower 32-bit)
CQUBACQ Base + 0x08CQ Base Address (Upper 32-bit)
CQDAOCQ Base + 0x0CCQ Doorbell Address Offset

MCQ 초기화 및 큐 생성

/* drivers/ufs/core/ufs-mcq.c — MCQ 초기화 핵심 */
int ufshcd_mcq_init(struct ufs_hba *hba)
{
    int ret, i;
    int num_queues;

    /* 1. MCQ 지원 여부 확인 */
    if (!(hba->mcq_capabilities & MASK_MCQ_SUPPORT))
        return -EOPNOTSUPP;

    /* 2. 큐 수 결정 (online CPU 수 기반) */
    num_queues = min_t(int, num_online_cpus(),
                       hba->mcq_capabilities & MCQ_MAX_QUEUES_MASK);

    /* 3. SQ/CQ 메모리 할당 (DMA coherent) */
    for (i = 0; i < num_queues; i++) {
        ret = ufshcd_mcq_sq_alloc(hba, i);
        if (ret)
            goto err;
        ret = ufshcd_mcq_cq_alloc(hba, i);
        if (ret)
            goto err;
    }

    /* 4. MCQ 레지스터 설정 */
    ufshcd_mcq_config_nr_queues(hba);

    /* 5. SQ/CQ 활성화 */
    for (i = 0; i < num_queues; i++) {
        ufshcd_mcq_sq_start(hba, &hba->mcq_sq[i]);
        ufshcd_mcq_cq_start(hba, &hba->mcq_cq[i]);
    }

    return 0;
err:
    ufshcd_mcq_release(hba);
    return ret;
}

SQ/CQ 시작/정지 제어

/* SQ 시작 — Tail Doorbell 초기화 */
void ufshcd_mcq_sq_start(struct ufs_hba *hba, struct ufs_hw_queue *hwq)
{
    u32 val;

    /* SQATTR: 큐 크기, 활성화 비트 설정 */
    val = FIELD_PREP(SQ_SIZE_MASK, hwq->max_entries - 1) | SQ_EN;
    writel(val, hba->mmio_base + hwq->sq_attr_offset);

    /* SQ Base Address 설정 */
    writel(lower_32_bits(hwq->sq_dma), hba->mmio_base + hwq->sq_lba_offset);
    writel(upper_32_bits(hwq->sq_dma), hba->mmio_base + hwq->sq_uba_offset);

    /* Tail Pointer 초기화 */
    hwq->sq_tail = 0;
    writel(0, hwq->sq_tail_db);
}

/* SQ 정지 — 에러 복구 또는 shutdown 시 */
void ufshcd_mcq_sq_stop(struct ufs_hba *hba, struct ufs_hw_queue *hwq)
{
    u32 val;

    val = readl(hba->mmio_base + hwq->sq_attr_offset);
    val &= ~SQ_EN;  /* 비활성화 */
    writel(val, hba->mmio_base + hwq->sq_attr_offset);

    /* SQ가 완전히 정지할 때까지 대기 */
    ufshcd_mcq_poll_sq_stopped(hba, hwq);
}
큐 매핑 전략: MCQ 모드에서 blk-mq의 3종 큐 타입이 SQ에 매핑됩니다. default 큐는 일반 write 요청에, read 큐는 read 요청에, poll 큐는 IRQ 없이 폴링(Polling) 방식으로 완료를 확인하는 저지연 I/O에 사용됩니다. ufshcd_map_queues()에서 blk_mq_map_queues()를 호출하여 CPU → SQ 매핑을 설정합니다.

Write Booster — SLC 캐시 기반 쓰기 가속

Write Booster(WB)는 UFS 3.1 스펙에서 도입된 쓰기 성능 향상 기능입니다. UFS 디바이스 내부의 SLC(Single Level Cell) 모드 영역을 캐시로 활용하여 TLC/QLC NAND의 느린 쓰기 속도를 보상합니다. 호스트 드라이버는 sysfs를 통해 WB를 활성화/비활성화하고, 버퍼 플러시를 제어할 수 있습니다.

Write Booster 원리: TLC NAND는 셀당 3비트를 저장하여 용량은 크지만 쓰기가 느립니다. WB는 일부 영역을 SLC 모드(셀당 1비트)로 운영하여 높은 쓰기 속도를 제공하고, 유휴 시간에 SLC 캐시 데이터를 TLC 영역으로 플러시(flush)합니다. 이는 스마트폰에서 앱 설치, 사진 촬영 등의 버스(Bus)트 쓰기 시나리오에서 큰 성능 이점을 제공합니다.

Dedicated Buffer vs Shared Buffer

모드설명장점단점
Dedicated BufferWB 전용으로 예약된 SLC 영역안정적인 WB 용량 보장, 사용자 데이터 영역과 독립전체 용량 감소 (WB 영역만큼)
Shared Buffer사용자 데이터 영역의 일부를 동적으로 SLC 모드로 전환전용 영역 불필요, 유연한 용량 활용남은 용량에 따라 WB 가용 크기 변동

Write Booster sysfs 인터페이스

sysfs 속성경로R/W설명
wb_on/sys/bus/platform/drivers/ufshcd/*/wb_onR/WWrite Booster 활성화/비활성화 (1/0)
wb_buf_flush_en동일 경로R/WWB 버퍼 플러시 활성화 (1: 즉시 플러시)
wb_avail_buf동일 경로R현재 사용 가능한 WB 버퍼 크기 (%)
wb_cur_buf동일 경로R현재 사용 중인 WB 버퍼 크기
wb_buf_life_time_est동일 경로RWB 버퍼 수명 추정 (0x00~0x0B)

WB 활성화/비활성화 코드

/* drivers/ufs/core/ufshcd.c — Write Booster 토글 */
int ufshcd_wb_toggle(struct ufs_hba *hba, bool enable)
{
    enum attr_idn attr;
    int ret;

    if (!ufshcd_is_wb_allowed(hba))
        return 0;

    /* fWriteBoosterEn 플래그 설정/해제 */
    if (enable)
        attr = QUERY_ATTR_IDN_WB_EN;
    else
        attr = QUERY_ATTR_IDN_WB_EN;

    ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
                                  attr, 0, 0, &enable);
    if (ret) {
        dev_err(hba->dev, "%s: Write Booster %s failed: %d\n",
                __func__, enable ? "enable" : "disable", ret);
        return ret;
    }

    hba->dev_info.wb_enabled = enable;
    dev_info(hba->dev, "Write Booster %s\n",
             enable ? "enabled" : "disabled");

    return 0;
}

/* WB 버퍼 플러시 트리거 */
static int ufshcd_wb_buf_flush_enable(struct ufs_hba *hba)
{
    int ret;
    u32 val = 1;

    /* fWriteBoosterBufferFlushEn 플래그 설정 */
    ret = ufshcd_query_attr_retry(hba, UPIU_QUERY_OPCODE_WRITE_ATTR,
                                  QUERY_ATTR_IDN_WB_BUFF_FLUSH_EN,
                                  0, 0, &val);
    return ret;
}

sysfs 읽기/쓰기 구현

/* drivers/ufs/core/ufs-sysfs.c — wb_on sysfs */
static ssize_t wb_on_show(struct device *dev,
                          struct device_attribute *attr, char *buf)
{
    struct ufs_hba *hba = dev_get_drvdata(dev);
    return sysfs_emit(buf, "%d\n", hba->dev_info.wb_enabled);
}

static ssize_t wb_on_store(struct device *dev,
                           struct device_attribute *attr,
                           const char *buf, size_t count)
{
    struct ufs_hba *hba = dev_get_drvdata(dev);
    bool enable;
    int ret;

    if (kstrtobool(buf, &enable))
        return -EINVAL;

    /* Runtime PM 활성 상태에서만 설정 가능 */
    ufshcd_rpm_get_sync(hba);
    ret = ufshcd_wb_toggle(hba, enable);
    ufshcd_rpm_put_sync(hba);

    return ret ? ret : count;
}
static DEVICE_ATTR_RW(wb_on);
WB 버퍼 수명 주의: SLC 모드의 NAND 셀도 P/E(Program/Erase) 사이클 제한이 있습니다. WB 버퍼는 사용자 데이터 영역보다 빈번하게 쓰기/소거가 반복되므로 수명이 더 빨리 소진됩니다. wb_buf_life_time_est 값이 0x0B(수명 소진)에 도달하면 WB 기능이 자동 비활성화됩니다. 불필요하게 WB를 항상 켜두기보다는 버스트 쓰기 시에만 활성화하는 전략이 권장됩니다.

성능 영향 분석

# Write Booster ON/OFF 성능 비교 (fio 예시)
# WB OFF 상태에서 순차 쓰기 측정
echo 0 > /sys/bus/platform/drivers/ufshcd/*/wb_on
fio --name=seq_write --ioengine=libaio --direct=1 --bs=128k \
    --rw=write --numjobs=1 --size=1G --filename=/dev/sda

# WB ON 상태에서 동일 측정
echo 1 > /sys/bus/platform/drivers/ufshcd/*/wb_on
fio --name=seq_write --ioengine=libaio --direct=1 --bs=128k \
    --rw=write --numjobs=1 --size=1G --filename=/dev/sda

# WB 버퍼 상태 모니터링
cat /sys/bus/platform/drivers/ufshcd/*/wb_avail_buf
cat /sys/bus/platform/drivers/ufshcd/*/wb_cur_buf
cat /sys/bus/platform/drivers/ufshcd/*/wb_buf_life_time_est

HPB — Host Performance Booster

HPB(Host Performance Booster)는 UFS 디바이스의 L2P(Logical to Physical) 매핑 테이블 일부를 호스트 메모리(DRAM)에 캐시하여 읽기 성능을 향상시키는 기능입니다. 일반적으로 UFS 디바이스는 읽기 요청 시 내부 SRAM에 캐시된 L2P 테이블을 참조하는데, 테이블 미스가 발생하면 NAND에서 L2P를 읽어야 하므로 수백 마이크로초의 지연이 추가됩니다. HPB는 호스트가 L2P 정보를 미리 보유하여 이 NAND 접근을 건너뛸 수 있게 합니다.

HPB (Host Performance Booster) 동작 원리 Host (Linux Kernel) Host DRAM — L2P Cache (HPB) Region 0 | Region 1 | ... | Region N 각 Region = 여러 Subregion (4KB L2P 엔트리) ufshpb 드라이버 ufshpb_prep() → HPB READ(16) 구성 HPB READ(16) Command CDB + HPB Entry (PPN: Physical Page Number) UFS Device UFS Controller (FTL) L2P Table (SRAM 캐시 + NAND 저장) NAND Flash (L2P Miss → 추가 지연) L2P 테이블이 SRAM에 없으면 NAND 접근 필요 NAND Data Pages HPB Entry 유효 시 → NAND L2P 조회 생략! 1. L2P 조회 2. HPB READ(16) + PPN 3. L2P 조회 생략! 4. 직접 데이터 읽기 5. 데이터 응답 HPB 없음 ~200μs (L2P miss) HPB 적용 ~80μs (L2P skip)

Region과 Subregion 개념

HPB는 UFS 디바이스의 논리적 주소 공간(Address Space)을 RegionSubregion으로 나누어 관리합니다. Region은 L2P 캐싱의 기본 관리 단위이고, Subregion은 실제 L2P 엔트리를 전송하는 최소 단위입니다. 호스트는 디바이스로부터 HPB 업데이트 알림을 받아 L2P 캐시를 갱신합니다.

개념크기설명
Region일반적으로 512MBL2P 캐시 관리의 단위, 활성화/비활성화 대상
Subregion일반적으로 4MBL2P 엔트리 전송(READ BUFFER) 단위, Region 내 128개
HPB Entry8바이트PPN(Physical Page Number) 4B + Reserved 4B
Active Region디바이스별 상이동시에 호스트 캐시에 유지할 수 있는 Region 수
Pinned Region디바이스별 상이항상 활성 상태를 유지하는 Region (빈번 접근 영역)

HPB sysfs 인터페이스

sysfs 속성R/W설명
hpb_modeR/WHPB 동작 모드 (0: OFF, 1: host-initiated, 2: device-initiated)
hpb_srgn_mem_sizeRSubregion 하나의 L2P 캐시 메모리 크기
hpb_hit_countRHPB 히트 횟수 (성능 모니터링)
hpb_miss_countRHPB 미스 횟수
hpb_active_countR현재 활성화된 Region/Subregion 수

HPB READ(16) 커맨드 구성

/* drivers/ufs/core/ufshpb.c — HPB READ(16) 커맨드 준비 */
void ufshpb_prep(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
{
    struct ufshpb_lu *hpb_lu;
    struct ufshpb_region *rgn;
    struct ufshpb_subregion *srgn;
    u64 ppn;
    unsigned long lpn;
    int transfer_len;

    if (!ufshpb_is_enabled(hba))
        return;

    hpb_lu = ufshpb_get_lu(hba, lrbp->lun);
    if (!hpb_lu)
        return;

    /* 1. LBA → LPN(Logical Page Number) 변환 */
    lpn = ufshpb_get_lpn(lrbp);
    transfer_len = ufshpb_get_len(lrbp);

    /* 2. Region/Subregion 인덱스 계산 */
    rgn = ufshpb_get_region(hpb_lu, lpn);
    srgn = ufshpb_get_subregion(rgn, lpn);

    /* 3. L2P 캐시에서 PPN 조회 */
    if (!ufshpb_get_ppn(srgn, lpn, &ppn)) {
        /* HPB 미스 — 일반 READ(10) 사용 */
        hpb_lu->stats.miss_cnt++;
        return;
    }

    /* 4. HPB READ(16) CDB 구성 */
    hpb_lu->stats.hit_cnt++;
    ufshpb_set_hpb_read_to_upiu(lrbp, lpn, ppn, transfer_len);
}

/* HPB READ(16) CDB 설정 */
static void ufshpb_set_hpb_read_to_upiu(
    struct ufshcd_lrb *lrbp, u64 lpn, u64 ppn, int len)
{
    unsigned char *cdb = lrbp->cmd->cmnd;

    /* Opcode: 0x94 (HPB READ) */
    cdb[0] = HPB_READ_16;
    /* LBA (Big-Endian) */
    put_unaligned_be64(lpn, &cdb[2]);
    /* Transfer Length */
    put_unaligned_be32(len, &cdb[10]);
    /* HPB Entry (PPN) → UPIU에 별도 필드로 전달 */
    lrbp->hpb_entry = ppn;
}

HPB 버전 차이

/* HPB 1.0 (Device-initiated) vs HPB 2.0 (Host-initiated) */

/*
 * HPB 1.0: 디바이스가 주도적으로 Region 활성화/비활성화 결정
 *   - DEV_HPB_RECOMMENDATION UPIU: 디바이스 → 호스트 (Region 추천)
 *   - 호스트는 추천에 따라 READ BUFFER로 L2P 다운로드
 */

/*
 * HPB 2.0: 호스트가 Region 활성화/비활성화를 결정
 *   - 호스트가 접근 패턴 분석 → 자주 접근하는 Region 활성화
 *   - 호스트 주도로 READ BUFFER 발행하여 L2P 캐시
 *   - 더 높은 히트율, 호스트 메모리 관리 최적화 가능
 */
enum ufshpb_mode {
    HPB_MODE_OFF            = 0,
    HPB_MODE_HOST_CONTROL   = 1,  /* HPB 2.0 */
    HPB_MODE_DEVICE_CONTROL = 2,  /* HPB 1.0 */
};
HPB 효과: HPB는 랜덤 읽기 성능에서 가장 큰 효과를 발휘합니다. 특히 데이터베이스(SQLite), 앱 로딩 등 L2P 테이블 미스가 빈번한 워크로드에서 읽기 지연을 40~60% 단축할 수 있습니다. 다만 호스트 DRAM 사용량이 증가하므로 (활성 Region당 수 MB), 메모리 제약이 있는 시스템에서는 활성 Region 수를 제한해야 합니다.

인라인 암호화 — Inline Encryption Framework

UFS 인라인 암호화(Inline Encryption)는 호스트 컨트롤러에 내장된 암호화 엔진(Crypto Engine 또는 ICE)을 활용하여 스토리지 I/O 경로에서 직접 데이터를 암복호화하는 기능입니다. CPU가 소프트웨어적으로 암복호화를 수행하는 대신, 하드웨어가 DMA 전송 과정에서 투명하게 처리하므로 성능 오버헤드가 거의 없습니다. Linux 커널의 blk-crypto 프레임워크와 통합되어 fscrypt, dm-crypt 등에서 활용됩니다.

UFS 인라인 암호화 (Inline Encryption) 데이터 흐름 Block I/O Request + bio_crypt_ctx (key, DUN) ufshcd (UFS HCD) UTRD에 Crypto 필드 삽입 UTRD CCI (Crypto Config Index) DUN (Data Unit Number) Crypto Engine (ICE / FMP) AES-256-XTS 암복호화 Keyslot[CCI]에서 키 로드 → DUN으로 IV 생성 Keyslot Table Slot 0: Key_A (AES-256-XTS) Slot 1: Key_B ... CCI로 키 선택 암호화된 데이터 M-PHY / UniPro Link UFS NAND Flash 암호화된 데이터 저장 (at-rest encryption) fscrypt / dm-crypt blk_crypto_profile을 통해 키 프로그래밍 blk_crypto_profile 키슬롯 관리, 알고리즘 capability CCI = Crypto Configuration Index | DUN = Data Unit Number | ICE = Inline Crypto Engine | FMP = Flash Memory Protector
인라인 암호화 vs 소프트웨어 암호화: 소프트웨어 암호화(dm-crypt fallback)는 CPU에서 데이터를 암복호화하므로 추가 메모리 복사와 CPU 사이클이 필요합니다. 인라인 암호화는 DMA 경로에 암호화 엔진이 삽입되어 데이터가 통과하면서 투명하게 처리됩니다. 결과적으로 순차 읽기/쓰기 성능이 소프트웨어 방식 대비 20~50% 향상되며, CPU 사용률도 현저히 낮습니다.

지원 알고리즘

알고리즘키 크기블록 크기UFS 스펙 지원비고
AES-256-XTS512비트 (2×256)4096바이트필수Android FBE(File-Based Encryption) 기본
AES-128-XTS256비트 (2×128)4096바이트선택일부 플랫폼에서 지원
AES-256-ECB256비트-선택파일명 암호화용
💡

blk-crypto 프레임워크 전체 구조: blk-crypto 키 관리, ICE keyslot 할당, 소프트웨어 폴백 메커니즘, fscrypt 연동, dm-crypt passthrough 등 인라인 암호화의 전체 스택 아키텍처는 암호화 하드웨어 가속 — 스토리지 인라인 암호화에서 상세히 다룹니다.

blk-crypto 프레임워크 연동

/* drivers/ufs/core/ufshcd-crypto.c — crypto profile 초기화 */
int ufshcd_hba_init_crypto_capabilities(struct ufs_hba *hba)
{
    struct blk_crypto_profile *profile = &hba->crypto_profile;
    int cap_idx, err;
    unsigned int num_keyslots;

    /* 1. UFSHCI Crypto Capability 레지스터 읽기 */
    hba->crypto_capabilities.reg_val =
        cpu_to_le32(ufshcd_readl(hba, REG_UFS_CCAP));

    num_keyslots = hba->crypto_capabilities.config_count + 1;

    /* 2. blk_crypto_profile 초기화 */
    err = devm_blk_crypto_profile_init(hba->dev, profile, num_keyslots);
    if (err)
        return err;

    profile->ll_ops = ufshcd_crypto_ops;
    profile->max_dun_bytes_supported = 8;  /* 64-bit DUN */

    /* 3. 지원 알고리즘 등록 */
    for (cap_idx = 0; cap_idx < hba->crypto_capabilities.num_crypto_cap; cap_idx++) {
        enum blk_crypto_mode_num blk_mode;
        union ufs_crypto_cap_entry cap;

        cap = hba->crypto_cap_array[cap_idx];

        switch (cap.algorithm_id) {
        case UFS_CRYPTO_ALG_AES_XTS:
            blk_mode = BLK_ENCRYPTION_MODE_AES_256_XTS;
            break;
        default:
            continue;
        }

        profile->modes_supported[blk_mode] |=
            cap.sdus_mask * 512;  /* 지원 데이터 유닛 크기 */
    }

    return 0;
}

UTRD Crypto 필드 설정

/* UTRD에 Crypto 정보 삽입 */
static void ufshcd_prepare_req_desc_hdr_crypto(
    struct ufshcd_lrb *lrbp, struct utp_transfer_req_desc *req_desc)
{
    struct bio_crypt_ctx *bc;
    struct request *rq = scsi_cmd_to_rq(lrbp->cmd);

    if (!rq || !rq->crypt_ctx)
        return;

    bc = rq->crypt_ctx;

    /* CCI (Crypto Configuration Index) — 키슬롯 번호 */
    req_desc->header.crypto_config_index =
        cpu_to_le16(bc->bc_keyslot);

    /* CE (Crypto Enable) 비트 설정 */
    req_desc->header.cci |= UTP_REQ_DESC_CRYPTO_ENABLE;

    /* DUN (Data Unit Number) — IV 생성에 사용 */
    req_desc->header.dunu = cpu_to_le32(
        upper_32_bits(bc->bc_dun[0]));
    req_desc->header.dunl = cpu_to_le32(
        lower_32_bits(bc->bc_dun[0]));
}

플랫폼별 Crypto 구현

/* Qualcomm ICE — ufs-qcom.c */
static int ufs_qcom_ice_program_key(struct ufs_hba *hba,
                                     const union ufs_crypto_cfg_entry *cfg,
                                     int slot)
{
    struct ufs_qcom_host *host = ufshcd_get_variant(hba);
    union {
        u8 bytes[AES_256_XTS_KEY_SIZE];
        u32 words[AES_256_XTS_KEY_SIZE / sizeof(u32)];
    } key;

    /* ICE 키 레지스터에 AES-256-XTS 키 프로그래밍 */
    memcpy(key.bytes, cfg->crypto_key, AES_256_XTS_KEY_SIZE);

    qcom_ice_program_key(host->ice, QCOM_ICE_CRYPTO_ALG_AES_XTS,
                         QCOM_ICE_CRYPTO_KEY_SIZE_256,
                         key.words, cfg->data_unit_size, slot);

    memzero_explicit(&key, sizeof(key));
    return 0;
}

/* Samsung FMP — ufs-exynos.c */
static int exynos_ufs_program_key(struct ufs_hba *hba,
                                   const union ufs_crypto_cfg_entry *cfg,
                                   int slot)
{
    /* FMP(Flash Memory Protector)는 UTRD의 PRDT 엔트리 내에
     * 암호화 키와 IV를 직접 삽입하는 방식을 사용 */
    struct exynos_ufs *ufs = ufshcd_get_variant(hba);
    return exynos_fmp_program_key(ufs->fmp, cfg, slot);
}
fscrypt 연동: Android의 FBE(File-Based Encryption)는 fscrypt를 사용하며, fscrypt는 blk-crypto를 통해 UFS 인라인 암호화를 활용합니다. 파일별 고유 키가 키슬롯에 프로그래밍되고, 파일의 논리적 블록 번호가 DUN으로 사용됩니다. 이를 통해 파일 단위의 세밀한 암호화가 하드웨어 수준에서 이루어집니다. CONFIG_FS_ENCRYPTION_INLINE_CRYPT=y 설정이 필요합니다.

RPMB — Replay Protected Memory Block

RPMB(Replay Protected Memory Block)는 UFS 디바이스 내의 보안 스토리지 영역으로, HMAC-SHA256 인증과 단조 증가 Write Counter를 통해 데이터의 무결성(Integrity)과 재생 공격(Replay Attack) 방지를 보장합니다. RPMB는 Well-Known LU(W-LUN 0xC4)로 접근하며, 보안 부팅(Secure Boot) 인증 정보, 롤백(Rollback) 방지 카운터, 디바이스 키 저장 등에 사용됩니다.

RPMB의 핵심 보안 모델: RPMB는 세 가지 보안 메커니즘을 결합합니다. (1) HMAC-SHA256 인증: 모든 쓰기/읽기 요청에 256비트 인증 키로 서명하여 위변조를 방지합니다. (2) Write Counter: 단조 증가하는 32비트 카운터로 재생 공격을 탐지합니다. (3) Nonce: 읽기 요청에 랜덤 nonce를 포함하여 응답의 신선도(freshness)를 보장합니다.

RPMB 프레임 구조

오프셋크기필드설명
0x0000196바이트Stuff bytes패딩 (0x00)
0x00C432바이트Key / MACHMAC-SHA256 인증값 또는 키 등록 시 키
0x00E4256바이트Data읽기/쓰기 데이터 (256B 단위)
0x01E416바이트Nonce호스트가 생성한 난수 (읽기 요청 시)
0x01F44바이트Write Counter단조 증가 카운터 (Big-Endian)
0x01F82바이트AddressRPMB 블록 주소 (256B 블록 단위)
0x01FA2바이트Block Count전송할 블록 수
0x01FC2바이트Result응답 결과 코드 (0x0000=성공)
0x01FE2바이트Request / Response Type요청/응답 타입 식별자

RPMB 요청/응답 타입

타입 값이름설명
0x0001Authentication Key WriteRPMB 인증 키 프로그래밍 (최초 1회만)
0x0002Read Write Counter현재 Write Counter 값 조회
0x0003Authenticated Data Write인증된 데이터 쓰기
0x0004Authenticated Data Read인증된 데이터 읽기
0x0005Result Read이전 쓰기 작업의 결과 확인
0x0100Authentication Key Write Response키 쓰기 응답
0x0200Read Write Counter Response카운터 읽기 응답
0x0300Authenticated Data Write Response데이터 쓰기 응답
0x0400Authenticated Data Read Response데이터 읽기 응답

RPMB SCSI 커맨드

/* RPMB 접근은 SECURITY PROTOCOL IN/OUT SCSI 커맨드를 사용 */

/* SECURITY PROTOCOL OUT — RPMB 요청 전송 */
/*
 * CDB (12 bytes):
 * [0] = 0xB5 (SECURITY PROTOCOL OUT)
 * [1] = 0xEC (Security Protocol: UFS)
 * [2-3] = 0x0001 (Security Protocol Specific: RPMB)
 * [4] = 0x00 (INC_512 = 0)
 * [5] = 0x00
 * [6-9] = Transfer Length
 * [10-11] = 0x00
 */

/* SECURITY PROTOCOL IN — RPMB 응답 수신 */
/*
 * CDB (12 bytes):
 * [0] = 0xA2 (SECURITY PROTOCOL IN)
 * [1] = 0xEC (Security Protocol: UFS)
 * [2-3] = 0x0001 (Security Protocol Specific: RPMB)
 * [4] = 0x00 (INC_512 = 0)
 * [5] = 0x00
 * [6-9] = Allocation Length
 * [10-11] = 0x00
 */

/* RPMB 인증 쓰기 시퀀스 */
static int ufs_rpmb_authenticated_write(struct ufs_hba *hba,
                                         u8 *data, u16 addr,
                                         u16 block_count, u8 *key)
{
    struct rpmb_frame frame;
    u8 mac[32];
    int ret;

    memset(&frame, 0, sizeof(frame));

    /* 1. Write Counter 읽기 */
    frame.req_resp_type = cpu_to_be16(0x0002);  /* Read Counter */
    ret = ufs_rpmb_security_out(hba, &frame, sizeof(frame));
    ret = ufs_rpmb_security_in(hba, &frame, sizeof(frame));

    /* 2. 쓰기 프레임 구성 */
    frame.req_resp_type = cpu_to_be16(0x0003);  /* Auth Write */
    frame.write_counter = frame.write_counter;   /* 응답에서 받은 값 */
    frame.address = cpu_to_be16(addr);
    frame.block_count = cpu_to_be16(block_count);
    memcpy(frame.data, data, block_count * 256);

    /* 3. HMAC-SHA256 계산 */
    hmac_sha256(key, 32, (u8 *)&frame.data,
                284,  /* data + nonce + counter + addr + count + type */
                mac);
    memcpy(frame.key_mac, mac, 32);

    /* 4. SECURITY PROTOCOL OUT 전송 */
    ret = ufs_rpmb_security_out(hba, &frame, sizeof(frame));

    /* 5. Result 확인 (SECURITY PROTOCOL IN) */
    frame.req_resp_type = cpu_to_be16(0x0005);  /* Result Read */
    ret = ufs_rpmb_security_out(hba, &frame, sizeof(frame));
    ret = ufs_rpmb_security_in(hba, &frame, sizeof(frame));

    if (be16_to_cpu(frame.result) != 0x0000)
        return -EIO;

    return 0;
}

ufs_bsg 인터페이스

/* drivers/ufs/core/ufs-bsg.c — BSG를 통한 RPMB 접근 */

/*
 * 사용자 공간에서 /dev/bsg/ufs-bsg0 또는 /dev/0:0:0:49476을 통해
 * RPMB 커맨드를 전송할 수 있습니다.
 *
 * W-LUN 0xC4는 SCSI LUN 49476 (0xC140)으로 매핑됩니다.
 * (UFS W-LUN → SCSI LUN 변환 규칙 적용)
 */

/* BSG RPMB 요청 처리 */
static int ufs_bsg_request(struct bsg_job *job)
{
    struct ufs_hba *hba = shost_priv(dev_to_shost(job->dev->parent));
    struct ufs_bsg_request *bsg_request = job->request;
    struct ufs_bsg_reply *bsg_reply = job->reply;
    int ret;

    switch (bsg_request->msgcode) {
    case UPIU_TRANSACTION_QUERY_REQ:
        ret = ufshcd_exec_raw_upiu_cmd(hba, ...);
        break;
    case UPIU_TRANSACTION_NOP_OUT:
        ret = ufshcd_exec_raw_upiu_cmd(hba, ...);
        break;
    default:
        ret = -ENOTSUPP;
    }

    bsg_reply->result = ret;
    job->reply_len = sizeof(*bsg_reply);
    bsg_job_done(job, ret, 0);
    return 0;
}

UFS RPMB v2 vs v3

특성RPMB v2 (UFS 2.x)RPMB v3 (UFS 3.x+)
최대 크기16MB64MB
블록 크기256바이트4KB (선택적)
Multi-block Write미지원 (1블록씩)지원 (최대 64블록)
인증 알고리즘HMAC-SHA256HMAC-SHA256
Write Counter32비트32비트
Atomic Write미보장Power-fail safe 보장

사용자 공간에서 RPMB 접근

# ufs-utils를 사용한 RPMB 접근 예시

# 1. RPMB 키 프로그래밍 (주의: 최초 1회만 가능, 변경 불가!)
ufs-utils rpmb write-key /dev/bsg/ufs-bsg0 --key-file rpmb_key.bin

# 2. Write Counter 읽기
ufs-utils rpmb read-counter /dev/bsg/ufs-bsg0 --key-file rpmb_key.bin

# 3. 데이터 쓰기
ufs-utils rpmb write /dev/bsg/ufs-bsg0 --key-file rpmb_key.bin \
    --address 0 --data-file data.bin

# 4. 데이터 읽기
ufs-utils rpmb read /dev/bsg/ufs-bsg0 --key-file rpmb_key.bin \
    --address 0 --block-count 1 --out-file output.bin

# 5. RPMB 상태 확인
cat /sys/class/scsi_device/0:0:0:49476/device/model
RPMB 키 프로그래밍은 비가역적입니다: RPMB 인증 키는 한 번 프로그래밍되면 변경할 수 없습니다. 키를 분실하면 해당 RPMB 영역에 더 이상 쓰기가 불가능합니다. Android 기기에서는 TEE(Trusted Execution Environment)가 키를 관리하며, 일반적으로 공장 출하 시 프로그래밍됩니다. RPMB는 보안 부팅의 롤백 방지(anti-rollback), Keymaster/Gatekeeper의 키 저장, Verified Boot 상태 보존 등에 핵심적으로 사용됩니다.

전원 관리 (Power Management)

UFS 전원 관리는 모바일 디바이스의 배터리 수명에 직접적인 영향을 미치는 핵심 요소입니다. UFS 호스트 컨트롤러(ufshcd)는 Linux 커널의 Runtime PM 프레임워크와 통합되어, 유휴 시 자동으로 링크 상태를 전환하고 클럭을 게이팅하여 전력 소비를 최소화합니다.

UFS 전원 상태 전이 (Power State Transitions) Active Link Up, Clocks On Idle (Clock Gated) Link Up, Clocks Off Hibernate (H8) Link Hibern8, Clocks Off Sleep Device Sleep, VCCQ Off PowerDown VCC/VCCQ Off, Link Down SSU PowerDown (Emergency) gate_delay 만료 I/O 요청 auto_hibern8 또는 rpm_suspend hibern8_exit system_suspend (sleep) system_resume system_suspend (off) full re-init Active (전력 최대) Clock Gated (중간) Hibernate (저전력) Sleep (초저전력) PowerDown (전원 차단)

전원 모드 비교

UFS 전원 모드 특성
전원 모드링크 상태클럭VCCVCCQ/VCCQ2복귀 지연전력 소비
ActiveUp (LS-HS)ONONON최대
Clock GatedUpOFF (gated)ONON~10 µs중간
Hibernate (H8)Hibern8OFFONON~100 µs낮음
Sleep (DeepSleep)Off / H8OFFONOFF~1 ms매우 낮음
PowerDownOffOFFOFFOFF~100 ms (재초기화)~0

클럭 게이팅 (Clock Gating)

UFS 호스트 드라이버는 일정 시간 유휴 상태(Idle State)가 지속되면 자동으로 클럭을 차단합니다. ufshcd_gate_clk_work()gate_delay(기본 150ms)가 경과한 후 실행되어, 호스트 컨트롤러와 PHY 클럭을 순차적으로 게이팅합니다.

/* drivers/ufs/core/ufshcd.c — 클럭 게이팅 워크 */
static void ufshcd_gate_work(struct work_struct *work)
{
    struct ufs_hba *hba = container_of(work, struct ufs_hba,
                                       clk_gating.gate_work);
    unsigned long flags;

    spin_lock_irqsave(hba->host->host_lock, flags);

    /* 아직 활성 요청이 있으면 게이팅 취소 */
    if (hba->clk_gating.active_reqs ||
        hba->clk_gating.state == CLKS_ON) {
        spin_unlock_irqrestore(hba->host->host_lock, flags);
        return;
    }

    /* 클럭 상태 전이: REQ_CLKS_OFF → CLKS_OFF */
    hba->clk_gating.state = CLKS_OFF;
    spin_unlock_irqrestore(hba->host->host_lock, flags);

    /* 실제 클럭 비활성화 */
    ufshcd_setup_clocks(hba, false);
}

/* 클럭 언게이팅: I/O 요청 시 자동 호출 */
static void ufshcd_ungate_work(struct work_struct *work)
{
    struct ufs_hba *hba = container_of(work, struct ufs_hba,
                                       clk_gating.ungate_work);

    cancel_delayed_work_sync(&hba->clk_gating.gate_work);

    /* 클럭 재활성화 */
    ufshcd_setup_clocks(hba, true);

    /* Hibern8 상태였으면 exit 수행 */
    if (ufshcd_is_link_hibern8(hba))
        ufshcd_uic_hibern8_exit(hba);
}

Hibernate (H8) 진입/탈출

Hibernate(H8)는 UniPro 링크를 저전력 상태로 전환하는 UFS 고유 기능입니다. Auto-Hibernate8 기능을 사용하면 하드웨어가 유휴 타이머(Timer) 만료 시 자동으로 H8에 진입하고, 새로운 UTRD/UTMRD 발행 시 자동으로 탈출합니다.

/* drivers/ufs/core/ufshcd.c — Hibernate8 진입 */
int ufshcd_uic_hibern8_enter(struct ufs_hba *hba)
{
    struct uic_command uic_cmd = {0};
    int ret;

    uic_cmd.command = UIC_CMD_DME_HIBER_ENTER;
    ret = ufshcd_uic_pwr_ctrl(hba, &uic_cmd);
    if (ret) {
        dev_err(hba->dev, "hibern8 enter failed: %d\n", ret);
        /* 에러 복구 시도 */
        ufshcd_update_evt_hist(hba, UFS_EVT_HIB_ERR, ret);
        return ret;
    }
    hba->ufs_stats.hibern8_enter_cnt++;
    return 0;
}

/* Auto-Hibernate8 설정 (UFSHCI 3.0+) */
void ufshcd_auto_hibern8_enable(struct ufs_hba *hba)
{
    u32 ahit;

    if (!ufshcd_is_auto_hibern8_supported(hba))
        return;

    /* 타이머 값 설정: scale(10us/100us/1ms/10ms) + timer_val */
    ahit = FIELD_PREP(UFSHCI_AHIBERN8_TIMER_MASK, hba->ahit) |
           FIELD_PREP(UFSHCI_AHIBERN8_SCALE_MASK, UFSHCI_AHIBERN8_1MS);

    ufshcd_writel(hba, ahit, REG_AUTO_HIBERNATE_IDLE_TIMER);
}

Runtime PM 통합

UFS 드라이버는 Linux Runtime PM과 완전히 통합됩니다. rpm_lvlspm_lvl sysfs 속성으로 Runtime Suspend와 System Suspend 시의 전원 레벨을 독립적으로 제어할 수 있습니다.

/* drivers/ufs/core/ufshcd.c — System Suspend/Resume */
int ufshcd_system_suspend(struct device *dev)
{
    struct ufs_hba *hba = dev_get_drvdata(dev);
    int ret;

    /* 이미 Runtime Suspended 상태면 추가 작업 불필요 */
    if (pm_runtime_suspended(hba->dev))
        return 0;

    ret = ufshcd_suspend(hba, UFS_SYSTEM_PM);
    if (ret)
        dev_err(hba->dev, "system suspend failed: %d\n", ret);
    return ret;
}

int ufshcd_system_resume(struct device *dev)
{
    struct ufs_hba *hba = dev_get_drvdata(dev);

    if (pm_runtime_suspended(hba->dev))
        return 0;

    return ufshcd_resume(hba, UFS_SYSTEM_PM);
}

/* 내부 suspend 함수 — rpm_lvl/spm_lvl에 따라 동작 결정 */
static int ufshcd_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op)
{
    enum ufs_pm_level pm_lvl;
    enum ufs_dev_pwr_mode req_dev_pwr_mode;
    enum uic_link_state req_link_state;

    pm_lvl = (pm_op == UFS_RUNTIME_PM) ?
             hba->rpm_lvl : hba->spm_lvl;

    req_dev_pwr_mode = ufs_get_pm_lvl_to_dev_pwr_mode(pm_lvl);
    req_link_state = ufs_get_pm_lvl_to_link_pwr_state(pm_lvl);

    /* BKOPS flush → SSU(PowerMode) → Hibern8/Off → Clock gate */
    if (req_dev_pwr_mode != hba->curr_dev_pwr_mode)
        ufshcd_set_dev_pwr_mode(hba, req_dev_pwr_mode);

    if (req_link_state == UIC_LINK_HIBERN8_STATE)
        ufshcd_uic_hibern8_enter(hba);

    ufshcd_setup_clocks(hba, false);
    return 0;
}

Gear Scaling (동적 기어 변경)

Gear scaling은 부하에 따라 UFS 링크 속도(Gear)를 동적으로 조절하는 기능입니다. 높은 I/O 부하 시 HS-G4로 올리고, 유휴 시 HS-G1으로 내려 전력을 절감합니다. devfreq 프레임워크와 통합되어 자동으로 동작합니다.

# Gear scaling 상태 확인
cat /sys/bus/platform/devices/*/clkscale_enable

# Gear scaling 비활성화 (디버깅/벤치마크 시)
echo 0 > /sys/bus/platform/devices/1d84000.ufshc/clkscale_enable

# 현재 링크 속도 확인
cat /sys/kernel/debug/ufshcd0/show_hba
# 출력 예: HS Gear: 4, Lanes: 2, Rate: B (HS-G4B 2-Lane)
전원 관리와 배터리 수명: 적절한 UFS 전원 관리는 모바일 디바이스 배터리 수명에 결정적입니다. Auto-Hibernate8 타이머를 너무 길게 설정하면 전력 낭비가, 너무 짧게 설정하면 H8 진입/탈출 오버헤드로 인한 지연이 발생합니다. 일반적으로 5~15ms가 적절한 타이머 값으로 권장됩니다. rpm_lvl=5 (dev:PowerDown, link:Off)는 전력 절감이 크지만 복귀 시간이 길어 모바일에서는 rpm_lvl=3 (dev:Sleep, link:Hibern8)을 주로 사용합니다.

디바이스 건강 모니터링 (Device Health Monitoring)

UFS 디바이스는 내장 건강 지표를 제공하여 수명 예측과 사전 장애 대응을 가능하게 합니다. bDeviceLifeTimeEstA/BbPreEOLInfo 속성은 디바이스 디스크립터를 통해 조회하며, 임계치를 초과하면 Exception Event를 통해 호스트에 알립니다.

수명 추정치 (Device Life Time Estimation)

UFS 디바이스는 두 가지 수명 추정 속성을 제공합니다. bDeviceLifeTimeEstA는 SLC 영역(Boot/RPMB 포함)의 수명을, bDeviceLifeTimeEstB는 MLC/TLC 사용자 데이터 영역의 수명을 나타냅니다.

bDeviceLifeTimeEstA/B 값 해석
수명 사용률의미권장 대응
0x00정보 없음디바이스가 수명 정보를 지원하지 않음
0x010~10%정상 — 수명 충분모니터링 유지
0x0210~20%정상모니터링 유지
0x0320~30%정상모니터링 유지
0x0430~40%정상모니터링 유지
0x0540~50%중간 수명교체 계획 수립
0x0650~60%중간 수명교체 계획 수립
0x0760~70%주의교체 일정 확정
0x0870~80%주의교체 준비
0x0980~90%경고즉시 교체 준비
0x0A90~100%위험즉시 교체 필요
0x0B100% 초과수명 초과즉시 데이터 백업 및 교체

Pre-EOL 정보 (bPreEOLInfo)

bPreEOLInfo는 디바이스의 예비 블록 상태를 세 단계로 간략화한 지표입니다. 이 값이 0x02(Warning) 이상이면 BKOPS 빈도 증가, 쓰기 앰플리피케이션 악화, 갑작스러운 성능 저하가 발생할 수 있습니다.

bPreEOLInfo 값 및 대응
상태설명대응
0x00미정의정보 제공하지 않음
0x01Normal예비 블록 충분정상 운용
0x02Warning예비 블록 소진 경고 (80% 이상 사용)교체 일정 수립, BKOPS 모니터링 강화
0x03Urgent예비 블록 거의 소진 (90% 이상 사용)즉시 백업 및 교체 실행

Exception Events

UFS 디바이스는 긴급 상황 시 Exception Event를 호스트에 통보합니다. 호스트 드라이버(ufshcd)는 이 이벤트를 인터럽트로 수신하고 적절한 대응 동작을 수행합니다.

/* drivers/ufs/core/ufshcd.c — Exception Event 처리 */
static void ufshcd_exception_event_handler(struct work_struct *work)
{
    struct ufs_hba *hba = container_of(work, struct ufs_hba,
                                       eeh_work);
    u32 status = 0;

    /* Exception Event Status 읽기 */
    ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
                      QUERY_ATTR_IDN_EE_STATUS, 0, 0, &status);

    /* Urgent BKOPS 필요 */
    if (status & MASK_EE_URGENT_BKOPS)
        ufshcd_bkops_exception_event_handler(hba);

    /* 동적 용량 부족 */
    if (status & MASK_EE_URGENT_TEMP)
        dev_warn(hba->dev, "UFS: device temperature warning!\n");

    /* Write Booster 버퍼 부족 */
    if (status & MASK_EE_WRITEBOOSTER_EVENT)
        ufshcd_wb_exception_event_handler(hba);
}

/* BKOPS (Background Operations) 관리 */
static int ufshcd_bkops_ctrl(struct ufs_hba *hba,
                             enum bkops_status status)
{
    int err;
    u32 curr_status = 0;

    err = ufshcd_query_attr(hba, UPIU_QUERY_OPCODE_READ_ATTR,
                            QUERY_ATTR_IDN_BKOPS_STATUS,
                            0, 0, &curr_status);
    if (err)
        return err;

    /*
     * BKOPS Status:
     *   0x00 = No operations needed
     *   0x01 = Non-critical (수행 권장)
     *   0x02 = Performance impacted (즉시 수행 필요)
     *   0x03 = Critical (즉시 수행, 커맨드 처리 불가 수준)
     */
    if (curr_status >= BKOPS_STATUS_PERF_IMPACT) {
        err = ufshcd_enable_auto_bkops(hba);
        dev_info(hba->dev, "BKOPS critical: status=%d, enabled auto\n",
                 curr_status);
    }
    return err;
}

건강 정보 읽기

# sysfs를 통한 건강 정보 읽기
cat /sys/bus/platform/devices/*/health_descriptor/life_time_estimation_a
cat /sys/bus/platform/devices/*/health_descriptor/life_time_estimation_b
cat /sys/bus/platform/devices/*/health_descriptor/eol_info

# ufs-utils를 사용한 건강 정보 조회
ufs-utils desc -a -p /dev/bsg/ufs-bsg0 -t health
# 출력 예:
#   bLength: 0x25
#   bDescriptorIDN: 0x09
#   bPreEOLInfo: 0x01
#   bDeviceLifeTimeEstA: 0x02
#   bDeviceLifeTimeEstB: 0x01

# Query UPIU로 직접 읽기 (bPreEOLInfo)
ufs-utils attr -a -p /dev/bsg/ufs-bsg0 -t 2 -i 0x02
# bPreEOLInfo at Health Descriptor offset 0x02

모니터링 스크립트

#!/bin/bash
# UFS 건강 모니터링 스크립트
# 주기적으로 실행하여 디바이스 상태를 추적

UFS_SYSFS="/sys/bus/platform/devices/1d84000.ufshc"
LOG="/var/log/ufs_health.log"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

# 건강 디스크립터 읽기
LIFE_A=$(cat "${UFS_SYSFS}/health_descriptor/life_time_estimation_a" 2>/dev/null)
LIFE_B=$(cat "${UFS_SYSFS}/health_descriptor/life_time_estimation_b" 2>/dev/null)
EOL=$(cat "${UFS_SYSFS}/health_descriptor/eol_info" 2>/dev/null)

echo "[${TIMESTAMP}] LifeA=${LIFE_A} LifeB=${LIFE_B} EOL=${EOL}" >> "${LOG}"

# 경고 조건 확인
if [ "${EOL}" = "0x02" ] || [ "${EOL}" = "0x03" ]; then
    echo "[${TIMESTAMP}] WARNING: PreEOL=${EOL} — 디바이스 교체 필요!" >> "${LOG}"
    # 알림 전송 (선택)
    logger -p user.crit "UFS PreEOL Warning: ${EOL}"
fi

if [ "${LIFE_A}" = "0x0A" ] || [ "${LIFE_A}" = "0x0B" ] ||
   [ "${LIFE_B}" = "0x0A" ] || [ "${LIFE_B}" = "0x0B" ]; then
    echo "[${TIMESTAMP}] CRITICAL: Lifetime exceeded 90%!" >> "${LOG}"
    logger -p user.crit "UFS Lifetime Critical: A=${LIFE_A} B=${LIFE_B}"
fi
PreEOLInfo 경고를 무시하면 안 됩니다: bPreEOLInfo0x02(Warning) 또는 0x03(Urgent)인 상태에서 계속 사용하면, 예비 블록 완전 소진 시 갑작스러운 읽기 전용(Read-Only) 전환 또는 디바이스 응답 불가가 발생할 수 있습니다. 특히 임베디드/자동차 시스템에서는 정기적 건강 모니터링과 사전 교체 정책이 필수입니다. 프로덕션 환경에서는 bDeviceLifeTimeEstA/B ≥ 0x08 시점에 교체 알람을 설정하는 것을 권장합니다.

디바이스 트리 바인딩 (Device Tree Bindings)

UFS 호스트 컨트롤러의 디바이스 트리 바인딩은 하드웨어 자원(레지스터, 인터럽트, 클럭, 레귤레이터, PHY)과 플랫폼별 설정을 선언합니다. 정확한 DT 설정은 UFS 초기화 성공과 최적 성능의 전제 조건입니다.

UFS 디바이스 트리 노드 구조 ufshc@1d84000 compatible = "qcom,sm8550-ufshc" reg, interrupts, freq-table-hz Clocks core_clk, bus_clk, ref_clk, tx/rx_symbol Regulators vcc, vccq, vccq2 vdd-hba-supply UFS PHY phys = <ufs_phy> M-PHY (qcom,sm8550) Reset resets = <gcc ...> reset-names ufs_phy: phy@1d87000 compatible = "qcom,sm8550-qmp-ufs-phy" reg, clocks, vdda-phy-supply, vdda-pll-supply #phy-cells = <0> 주요 DT 속성 reg = <0x1d84000 0x3000>; interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>; freq-table-hz = <75000000 300000000>, ... vcc-supply = <&vreg_l7b_2p96>; vccq-supply = <&vreg_l9b_1p2>; vccq2-supply = <&vreg_s4a_1p8>; vcc-max-microamp = <800000>;

표준 DT 속성

UFS 호스트 컨트롤러 DT 속성
속성타입필수설명예시 값
compatiblestringO플랫폼별 호환 문자열"qcom,sm8550-ufshc"
regu32 arrayOMMIO 레지스터 베이스 및 크기<0x1d84000 0x3000>
interruptsu32 arrayO인터럽트 라인<GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>
clocksphandle arrayO클럭 소스 참조<&gcc GCC_UFS_PHY_AXI_CLK>, ...
clock-namesstring arrayO클럭 이름"core_clk", "bus_aggr_clk", ...
freq-table-hzu32 arrayO클럭 주파수 범위 (min max 쌍)<75000000 300000000>
vcc-supplyphandleOVCC 레귤레이터 (2.7~3.6V)<&vreg_l7b_2p96>
vccq-supplyphandle선택VCCQ 레귤레이터 (1.14~1.26V)<&vreg_l9b_1p2>
vccq2-supplyphandle선택VCCQ2 레귤레이터 (1.7~1.95V)<&vreg_s4a_1p8>
vcc-max-microampu32선택VCC 최대 전류<800000>
physphandleOM-PHY 참조<&ufs_mem_phy>
phy-namesstringOPHY 이름"ufsphy"
resetsphandle선택리셋 컨트롤러 참조<&gcc GCC_UFS_PHY_BCR>
reset-namesstring선택리셋 이름"rst"

Qualcomm 플랫폼 예시 (SM8550)

/* Qualcomm SM8550 UFS 호스트 컨트롤러 DT 노드 */
ufshc@1d84000 {
    compatible = "qcom,sm8550-ufshc", "qcom,ufshc", "jedec,ufs-2.0";
    reg = <0x1d84000 0x3000>;
    interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>;

    /* 클럭 설정 */
    clocks = <&gcc GCC_UFS_PHY_AXI_CLK>,
             <&gcc GCC_AGGRE_UFS_PHY_AXI_CLK>,
             <&gcc GCC_UFS_PHY_AHB_CLK>,
             <&gcc GCC_UFS_PHY_UNIPRO_CORE_CLK>,
             <&gcc GCC_UFS_PHY_TX_SYMBOL_0_CLK>,
             <&gcc GCC_UFS_PHY_RX_SYMBOL_0_CLK>,
             <&gcc GCC_UFS_PHY_RX_SYMBOL_1_CLK>;
    clock-names = "core_clk",
                  "bus_aggr_clk",
                  "iface_clk",
                  "core_clk_unipro",
                  "tx_lane0_sync_clk",
                  "rx_lane0_sync_clk",
                  "rx_lane1_sync_clk";
    freq-table-hz = <75000000 300000000>,   /* core_clk */
                    <0 0>,                   /* bus_aggr_clk */
                    <0 0>,                   /* iface_clk */
                    <75000000 300000000>,    /* core_clk_unipro */
                    <0 0>,                   /* tx_lane0_sync */
                    <0 0>,                   /* rx_lane0_sync */
                    <0 0>;                   /* rx_lane1_sync */

    /* 레귤레이터 */
    vcc-supply = <&vreg_l7b_2p96>;
    vccq-supply = <&vreg_l9b_1p2>;
    vccq2-supply = <&vreg_s4a_1p8>;
    vcc-max-microamp = <800000>;
    vccq-max-microamp = <900000>;
    vccq2-max-microamp = <800000>;

    /* PHY 참조 */
    phys = <&ufs_mem_phy>;
    phy-names = "ufsphy";

    /* 리셋 */
    resets = <&gcc GCC_UFS_PHY_BCR>;
    reset-names = "rst";

    /* 플랫폼별 속성 */
    qcom,ice = <&ice>;

    status = "okay";

    /* Auto-Hibernate8 타이머 (ms) */
    /* 런타임에 sysfs로도 변경 가능 */
};

Samsung Exynos 플랫폼 예시

/* Samsung Exynos 2200 UFS 호스트 컨트롤러 */
ufs: ufshc@13100000 {
    compatible = "samsung,exynos2200-ufs";
    reg = <0x13100000 0x100>,   /* HCI 레지스터 */
          <0x13180000 0x8000>,  /* UNIPRO 레지스터 */
          <0x131A0000 0x2000>;  /* UFS protector 레지스터 */
    reg-names = "hci", "unipro", "ufsp";
    interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;

    clocks = <&cmu_hsi2 CLK_GOUT_HSI2_UFS_EMBD_ACLK>,
             <&cmu_hsi2 CLK_GOUT_HSI2_UFS_EMBD_UNIPRO>;
    clock-names = "core_clk", "core_clk_unipro";
    freq-table-hz = <0 267000000>, <0 267000000>;

    vcc-supply = <&ldo15_reg>;
    vccq-supply = <&ldo16_reg>;
    vccq2-supply = <&ldo17_reg>;

    phys = <&ufs_phy>;
    phy-names = "ufsphy";
    samsung,sysreg-phandle = <&sysreg_hsi2>;

    status = "okay";
};

MediaTek 플랫폼 예시

/* MediaTek Dimensity 9200 (MT6985) UFS 호스트 컨트롤러 */
ufshci: ufshci@11270000 {
    compatible = "mediatek,mt6985-ufshci", "mediatek,ufshci";
    reg = <0x11270000 0x2C00>,   /* HCI */
          <0x11280000 0x2000>;   /* MPHY */
    reg-names = "hci", "mphy";
    interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;

    clocks = <&topckgen CLK_TOP_UFS_SEL>,
             <&infracfg CLK_INFRA_UFS_AXI>,
             <&infracfg CLK_INFRA_UFS_AHB>,
             <&infracfg CLK_INFRA_UNIPRO_SYS>,
             <&infracfg CLK_INFRA_UNIPRO_TICK>;
    clock-names = "ufs_sel", "ufs_axi", "ufs_ahb",
                  "unipro_sys", "unipro_tick";
    freq-table-hz = <0 416000000>, <0 0>, <0 0>,
                    <0 416000000>, <0 0>;

    vcc-supply = <&mt6373_vemc>;
    vccq-supply = <&mt6373_vufs>;
    vccq2-supply = <&mt6373_vufs18>;

    phys = <&ufsphy>;
    phy-names = "ufsphy";

    mediatek,top-gpio = <&pio>;

    status = "okay";
};
DT 바인딩 검증: 커널 소스의 Documentation/devicetree/bindings/ufs/ 디렉터리에서 각 벤더별 YAML 스키마를 확인할 수 있습니다. dt-schema 도구로 DT 노드의 유효성을 빌드 시 자동 검증합니다: make dt_binding_check DT_SCHEMA_FILES=ufs/. freq-table-hz 속성의 쌍 수가 clock-names의 항목 수와 반드시 일치해야 합니다.

sysfs 인터페이스

UFS 드라이버는 /sys/ 아래에 다양한 속성을 노출하여 런타임 모니터링과 튜닝을 지원합니다. 재컴파일 없이 전원 정책, Write Booster, Auto-Hibernate8 등의 동작을 변경할 수 있어 개발 및 디버깅에 필수적인 인터페이스입니다.

주요 sysfs 속성

UFS sysfs 속성 정리
경로 (상대)속성R/W설명기본값 예시
rpm_lvlRuntime PM 전원 레벨
RWRuntime Suspend 시 전원 레벨 (0~5)5
spm_lvlSystem PM 전원 레벨
RWSystem Suspend 시 전원 레벨 (0~5)5
auto_hibern8RWAuto-Hibernate8 타이머 (ms), 0=비활성10
wb_onRWWrite Booster 활성화 (0/1)1
wb_buf_flush_enRWWB 버퍼 플러시 활성화0
wb_flush_thresholdRWWB 플러시 시작 임계값 (%)40
clkscale_enableRWGear scaling 활성화 (0/1)1
clkgate_enableRWClock gating 활성화 (0/1)1
clkgate_delay_msRWClock gating 지연 (ms)150
rtc_update_msRWRTC 동기화 주기 (ms)10000
urgent_bkopsROUrgent BKOPS 발생 횟수0

전원 레벨 상세

rpm_lvl / spm_lvl 값 매핑
레벨디바이스 전원링크 상태적합 시나리오
0ActiveActive테스트/벤치마크 (절전 비활성)
1ActiveHibern8낮은 지연, 링크만 절전
2SleepActive디바이스만 절전 (드문 설정)
3SleepHibern8모바일 기본값 (균형)
4PowerDownHibern8높은 절전, 중간 복귀 시간
5PowerDownOff최대 절전, 긴 복귀 시간

sysfs 읽기/쓰기 예시

# UFS sysfs 베이스 경로 확인
UFS_PATH=$(find /sys/bus/platform/devices -maxdepth 1 -name "*ufshc*" | head -1)
echo "UFS sysfs path: ${UFS_PATH}"

# 현재 전원 레벨 확인
cat ${UFS_PATH}/rpm_lvl
cat ${UFS_PATH}/spm_lvl

# Runtime PM 레벨을 Sleep+Hibern8으로 변경
echo 3 > ${UFS_PATH}/rpm_lvl

# Auto-Hibernate8 타이머 설정 (10ms)
echo 10 > ${UFS_PATH}/auto_hibern8

# Write Booster 활성화/비활성화
echo 1 > ${UFS_PATH}/wb_on
echo 0 > ${UFS_PATH}/wb_on

# Clock gating 지연 조정 (200ms)
echo 200 > ${UFS_PATH}/clkgate_delay_ms

# Gear scaling 비활성화 (벤치마크 시)
echo 0 > ${UFS_PATH}/clkscale_enable

# 건강 정보 조회
ls ${UFS_PATH}/health_descriptor/
cat ${UFS_PATH}/health_descriptor/life_time_estimation_a
cat ${UFS_PATH}/health_descriptor/eol_info

# Unit descriptor 정보 (LUN별)
ls ${UFS_PATH}/unit_descriptor/
cat ${UFS_PATH}/unit_descriptor/unit0/boot_lun_id

ufs-utils 도구

ufs-utils는 UFS 디바이스 관리를 위한 커맨드라인 유틸리티로, /dev/bsg/ufs-bsg0 디바이스 노드를 통해 Query UPIU를 직접 전송합니다.

# ufs-utils 설치 (소스 빌드)
git clone https://github.com/westerndigitalcorporation/ufs-utils.git
cd ufs-utils && make && sudo make install

# 디바이스 디스크립터 읽기
sudo ufs-utils desc -a -p /dev/bsg/ufs-bsg0 -t device
# 출력 예:
#   bLength: 0x59
#   bDescriptorIDN: 0x00
#   bDevice: 0x00
#   bDeviceClass: 0x00
#   bDeviceSubClass: 0x01
#   bNumberLU: 0x01
#   wSpecVersion: 0x0310 (UFS 3.1)

# 설정 디스크립터 읽기
sudo ufs-utils desc -a -p /dev/bsg/ufs-bsg0 -t config

# Attribute 읽기 (bCurrentPowerMode)
sudo ufs-utils attr -a -p /dev/bsg/ufs-bsg0 -t 2

# Flag 읽기 (fDeviceInit, fPermanentWPEn 등)
sudo ufs-utils flag -a -p /dev/bsg/ufs-bsg0

# UFS 디바이스 정보 요약
sudo ufs-utils desc -a -p /dev/bsg/ufs-bsg0 -t device | grep -E "wSpec|bNumber|wManufacturer"
재컴파일 없는 런타임 튜닝: sysfs 인터페이스를 활용하면 커널 재컴파일이나 재부팅 없이 UFS 동작을 실시간으로 조정할 수 있습니다. 벤치마크 전에 clkscale_enable=0, auto_hibern8=0으로 설정하면 전원 상태 전이로 인한 측정 오차를 제거할 수 있습니다. 프로덕션에서는 rpm_lvl을 3(Sleep+H8)으로, auto_hibern8을 5~15ms로 설정하는 것이 일반적입니다.

디버깅 및 문제 해결 (Debugging & Troubleshooting)

UFS 디버깅은 에러 핸들러의 동작 이해, 트레이스포인트 활용, debugfs 분석의 세 축으로 이루어집니다. ufshcd_err_handler는 OCS 에러, UIC 에러, SCSI 에러를 분류하고 단계적 복구를 수행합니다.

UFS 에러 핸들링 흐름 (ufshcd_err_handler) UFS Error 감지 (ISR) ufshcd_err_handler() OCS Error Overall Command Status UIC Error UniPro/M-PHY 링크 에러 SCSI Error 디바이스 응답 에러 Host Error 시스템 버스/레지스터 커맨드 재시도 Link Recovery Device Reset (LU) Host Reset (Full) 에스컬레이션: 재시도 실패 → 링크 복구 → 디바이스 리셋 → 호스트 리셋 복구 성공 복구 실패 → scsi_host_reset / 시스템 패닉

OCS 에러 코드

Overall Command Status (OCS) 에러 코드
OCS 값이름원인대응
0x00SUCCESS정상 완료
0x01INVALID_CMD_TABLE_ATTRUTRD 설정 오류드라이버 버그 확인
0x02INVALID_PRDT_ATTRPRDT 엔트리 오류DMA 매핑 확인
0x03MISMATCH_DATA_BUF_SIZE데이터 크기 불일치Transfer 크기 확인
0x04MISMATCH_RESP_UPIU_SIZEResponse UPIU 크기 불일치Response 버퍼 확인
0x05PEER_COMM_FAILUREUniPro 통신 실패링크 복구 시도
0x06ABORTED호스트에 의한 중단타임아웃 or 에러 복구 중
0x07FATAL_ERROR치명적 하드웨어 에러호스트 리셋 필요
0x08DEVICE_FATAL_ERROR디바이스 치명적 에러디바이스 리셋 + 호스트 리셋
0x0FINVALID_OCS_VALUEUFS HCI SW 에러드라이버 상태 확인

UIC 에러 유형

UIC 에러 분류
에러 유형레지스터원인증상
PA (Physical Adapter)UIC_ERROR_CODE_PAM-PHY 시그널 불안정, 전압/클럭 문제링크 드롭, H8 진입/탈출 실패
DL (Data Link)UIC_ERROR_CODE_DLCRC 에러, NAC 과다, TC 크레딧 소진프레임 재전송 증가, 성능 저하
NL (Network Layer)UIC_ERROR_CODE_NLUniPro 라우팅 에러통신 장애 (드문 경우)
TL (Transport Layer)UIC_ERROR_CODE_TLE2E FC 크레딧 에러, UPIU 형식 에러커맨드 타임아웃
DMEUIC_ERROR_CODE_DMEUIC 커맨드 실행 실패Gear 변경/H8 실패

트레이스포인트 활용

# UFS 트레이스포인트 목록 확인
ls /sys/kernel/debug/tracing/events/ufs/
# ufshcd_command, ufshcd_uic_command, ufshcd_clk_gating,
# ufshcd_clk_scaling, ufshcd_auto_bkops_state, ...

# 커맨드 트레이스 활성화
echo 1 > /sys/kernel/debug/tracing/events/ufs/ufshcd_command/enable

# UIC 커맨드 트레이스 활성화
echo 1 > /sys/kernel/debug/tracing/events/ufs/ufshcd_uic_command/enable

# 트레이스 버퍼 읽기
cat /sys/kernel/debug/tracing/trace_pipe | head -50
# 출력 예:
# ufshcd_command: 0.0.0: send tag: 0, DB: 0x1, size: 4096,
#   IS: 0, LBA: 0x1234, opcode: 0x28 (READ_10), group_id: 0x0
# ufshcd_command: 0.0.0: complete tag: 0, DB: 0x0, size: 4096,
#   IS: 0, scsi_status: 0x0 (GOOD), OCS: 0x0 (SUCCESS)

# ftrace로 UFS I/O 지연 분석
echo function_graph > /sys/kernel/debug/tracing/current_tracer
echo ufshcd_queuecommand > /sys/kernel/debug/tracing/set_graph_function
echo 1 > /sys/kernel/debug/tracing/tracing_on
# ... I/O 수행 ...
echo 0 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace

debugfs 엔트리

# debugfs 마운트 확인
mount | grep debugfs
# debugfs on /sys/kernel/debug type debugfs

# UFS debugfs 엔트리 확인
ls /sys/kernel/debug/ufshcd0/
# dump_device_desc  dump_health_desc  dump_string_desc
# err_stats         host_regs         req_stats
# show_hba          stats

# HBA 상태 전체 덤프
cat /sys/kernel/debug/ufshcd0/show_hba
# 출력 예:
#   UFS Host state: OPERATIONAL
#   lrb in use: 0x0
#   Outstanding reqs: 0x0
#   HS Gear: 4, Lanes: 2, Rate: B
#   Dev Power Mode: ACTIVE
#   Link State: UP

# 에러 통계 확인
cat /sys/kernel/debug/ufshcd0/err_stats
# UIC Errors (PA): 0
# UIC Errors (DL): 0
# UIC Errors (NL): 0
# UIC Errors (TL): 0
# UIC Errors (DME): 0
# SCSI cmd errors: 0
# Host reset count: 0

# 호스트 레지스터 덤프 (하드웨어 상태 분석)
cat /sys/kernel/debug/ufshcd0/host_regs

Kconfig 디버깅 옵션

# UFS 관련 Kconfig 옵션 확인
grep -E "CONFIG_SCSI_UFS|CONFIG_TRACING" /boot/config-$(uname -r) | sort
# CONFIG_SCSI_UFSHCD=y           # UFS 호스트 컨트롤러 드라이버
# CONFIG_SCSI_UFSHCD_PCI=m       # PCI 기반 UFS (인텔 등)
# CONFIG_SCSI_UFSHCD_PLATFORM=y  # 플랫폼 기반 UFS (모바일)
# CONFIG_SCSI_UFS_BSG=y          # BSG 노드 (/dev/bsg/ufs-bsg0)
# CONFIG_SCSI_UFS_CRYPTO=y       # 인라인 암호화 지원
# CONFIG_SCSI_UFS_HPB=y          # Host Performance Booster
# CONFIG_SCSI_UFS_FAULT_INJECTION=n  # 장애 주입 테스트
# CONFIG_TRACING=y               # ftrace 프레임워크

# dmesg에서 UFS 초기화 로그 분석
dmesg | grep -i "ufshcd\|ufs" | head -30
# [    1.234] ufshcd-qcom 1d84000.ufshc: ufshcd_pltfrm_init() start
# [    1.240] ufshcd-qcom 1d84000.ufshc: UFS Host Controller version: 3.0
# [    1.251] ufshcd-qcom 1d84000.ufshc: Linked as scsi host0
# [    1.312] ufshcd-qcom 1d84000.ufshc: HS-G4B (2-lane) connected
# [    1.315] ufshcd-qcom 1d84000.ufshc: UFS device is SAMSUNG KLUEG8UHDB
# [    1.320] scsi 0:0:0:0: Direct-Access UFS 3.1 PQ: 0 ANSI: 6
에러 복구 에스컬레이션 순서: UFS 에러 핸들러는 다음 순서로 복구를 시도합니다: (1) 커맨드 재시도(2) 링크 복구 (H8 exit → re-link) → (3) 디바이스 리셋 (Task Management UPIU) → (4) 호스트 전체 리셋 (ufshcd_host_reset_and_restore). 호스트 리셋까지 실패하면 SCSI 에러가 상위 블록 레이어로 전파됩니다. /sys/kernel/debug/ufshcd0/err_stats에서 누적 에러 카운트를 정기적으로 확인하면 잠재적 하드웨어 문제를 조기에 발견할 수 있습니다.

성능 튜닝 (Performance Tuning)

UFS 성능 최적화는 하드웨어 계층(Gear/Lane), 컨트롤러 계층(MCQ), 디바이스 기능(WB/HPB), 블록 레이어, 파일시스템에 이르기까지 다층적으로 접근해야 합니다. 각 계층의 튜닝 포인트를 이해하면 단일 스토리지에서도 2~5배의 성능 차이를 만들 수 있습니다.

UFS 성능 최적화 계층 (Bottom-Up) PHY 계층: Gear / Lane 최적화 HS-G4B 2-Lane = 2×2.9 GB/s (Max), Gear scaling 비활성 (벤치마크) 컨트롤러: MCQ (Multi-Circular Queue) SQ/CQ per CPU → UTRD lock contention 제거, nr_hw_queues 튜닝 디바이스 기능: Write Booster (WB) SLC 캐시로 burst write 가속 (2~4x), wb_on=1, flush 임계값 조정 디바이스 기능: HPB (Host Performance Booster) L2P 맵 호스트 캐시 → random read 지연 감소 (0.3ms → 0.1ms) 블록 레이어 튜닝 I/O scheduler (none/mq-deadline), nr_requests, read_ahead_kb, rq_affinity 파일시스템 튜닝 F2FS (flash-friendly), ext4 (discard, journal_async_commit), IO priority 성능 영향 ↑ L6 L5 L4 L3 L2 L1 ※ 하위 계층 튜닝이 상위 계층보다 근본적인 성능 영향을 줌

튜닝 파라미터 정리

UFS 성능 튜닝 파라미터 및 권장값
계층파라미터기본값권장값 (성능 우선)효과
PHYHS GearAuto (G1~G4)G4 (고정)최대 대역폭
PHYLanes22대역폭 2배
PHYRate (A/B)BBRate-B가 더 높은 대역폭
ControllerMCQ 큐 수nr_cpusnr_cpus (또는 8)병렬 I/O 처리
Controllerauto_hibern810 ms0 (벤치마크)H8 전이 오버헤드 제거
WBwb_on11burst write 가속
WBwb_flush_threshold40%60% (대용량 쓰기)flush 빈도 감소
HPBHPB 활성화ONONrandom read 가속
Blockschedulermq-deadlinenone (NVMe형)스케줄러(Scheduler) 오버헤드 제거
Blocknr_requests256256~512큐 깊이 증가
Blockread_ahead_kb128256~512순차 읽기 프리페치
Blockrq_affinity12완료 CPU 고정
FSF2FS discardononTRIM/UNMAP 전달

Gear/Lane 최적화

# 현재 Gear/Lane 상태 확인
cat /sys/kernel/debug/ufshcd0/show_hba | grep -E "Gear|Lane|Rate"
# HS Gear: 4, Lanes: 2, Rate: B

# Gear scaling 비활성화 (최대 성능 고정)
echo 0 > /sys/bus/platform/devices/1d84000.ufshc/clkscale_enable

# Power mode 변경 테스트 (개발 환경)
# 주의: 잘못된 Gear 설정은 링크 드롭을 유발
echo "HS-G4" > /sys/kernel/debug/ufshcd0/power_mode
# Rate-B, 2-Lane이 기본 최대 설정

블록 레이어 튜닝

# UFS 블록 디바이스 확인
DISK="sda"  # UFS는 SCSI 서브시스템 → /dev/sdX

# I/O 스케줄러 변경 (MCQ 사용 시 none 권장)
echo none > /sys/block/${DISK}/queue/scheduler
cat /sys/block/${DISK}/queue/scheduler
# [none] mq-deadline kyber bfq

# 큐 깊이 조정
echo 512 > /sys/block/${DISK}/queue/nr_requests

# Read-ahead 증가 (순차 읽기 워크로드)
echo 512 > /sys/block/${DISK}/queue/read_ahead_kb

# RQ affinity 설정 (완료 인터럽트 → 요청 CPU)
echo 2 > /sys/block/${DISK}/queue/rq_affinity

# Write Booster 활성화 확인 및 플러시 임계값 설정
echo 1 > /sys/bus/platform/devices/1d84000.ufshc/wb_on
echo 60 > /sys/bus/platform/devices/1d84000.ufshc/wb_flush_threshold

fio 벤치마크 설정

# === UFS Sequential Read 벤치마크 ===
cat > /tmp/ufs_seq_read.fio << 'FIOEOF'
[global]
ioengine=io_uring
direct=1
bs=128k
iodepth=32
numjobs=1
runtime=30
time_based
group_reporting

[seq-read]
filename=/dev/sda
rw=read
FIOEOF

sudo fio /tmp/ufs_seq_read.fio

# === UFS Random Read 벤치마크 (HPB 효과 측정) ===
cat > /tmp/ufs_rand_read.fio << 'FIOEOF'
[global]
ioengine=io_uring
direct=1
bs=4k
iodepth=32
numjobs=4
runtime=30
time_based
group_reporting

[rand-read]
filename=/dev/sda
rw=randread
FIOEOF

sudo fio /tmp/ufs_rand_read.fio

# === UFS Random Write 벤치마크 (WB 효과 측정) ===
cat > /tmp/ufs_rand_write.fio << 'FIOEOF'
[global]
ioengine=io_uring
direct=1
bs=4k
iodepth=16
numjobs=4
runtime=30
time_based
group_reporting

[rand-write]
filename=/dev/sda
rw=randwrite
FIOEOF

sudo fio /tmp/ufs_rand_write.fio

튜닝 전후 비교

UFS 4.0 (HS-G4B 2-Lane) 튜닝 전후 성능 비교 (참고 수치)
워크로드튜닝 전 (기본)튜닝 후개선율핵심 설정
순차 읽기 (128KB)~2,000 MB/s~2,800 MB/s+40%Gear 고정, read_ahead=512
순차 쓰기 (128KB)~1,400 MB/s~1,800 MB/s+28%WB + scheduler=none
랜덤 읽기 (4KB)~60K IOPS~120K IOPS+100%HPB + MCQ + rq_affinity=2
랜덤 쓰기 (4KB)~40K IOPS~80K IOPS+100%WB + MCQ + nr_requests=512
혼합 R/W (7:3, 4KB)~35K IOPS~70K IOPS+100%전체 최적화 적용
벤치마크 환경 설정 체크리스트: 정확한 UFS 성능 측정을 위해 다음을 확인하세요: (1) auto_hibern8=0 — H8 전이 오버헤드 제거, (2) clkscale_enable=0 — Gear 고정, (3) scheduler=none — I/O 스케줄러 오버헤드 제거, (4) direct=1 — 페이지 캐시(Page Cache) 바이패스, (5) thermal throttling 비활성화 확인. 프로덕션 환경에서는 전력-성능 균형을 고려하여 Gear scaling과 Auto-H8을 활성 상태로 유지하되, read_ahead_kbnr_requests 최적화는 상시 적용할 수 있습니다.

UFS 서브시스템을 더 깊이 이해하거나 관련 기술을 학습할 때 참고할 문서를 정리합니다.

관련 내부 문서
페이지(Page)링크관련성
eMMC/MMC 서브시스템eMMC/MMCUFS의 전세대 기술, MMC 프로토콜 비교 학습에 필수
SCSI 서브시스템SCSIUFS는 SCSI 명령 집합을 사용하며, SCSI 미들레이어를 통해 동작
NVMeNVMePCIe 기반 플래시 스토리지, MCQ 아키텍처 비교
SATA/AHCISATA/AHCI또 다른 SCSI 기반 스토리지 서브시스템 비교
블록 I/O 레이어블록 I/OUFS 위의 블록 레이어, blk-mq, I/O 스케줄러
디바이스 트리디바이스 트리UFS DT 바인딩, PHY/클럭/레귤레이터 설정
DMADMAPRDT(Physical Region Descriptor Table), scatter-gather, DMA 매핑
Android 커널AndroidAndroid 환경에서의 UFS 최적화, 보안 부팅, ICE
F2FS 파일시스템F2FSUFS에 최적화된 플래시 전용 파일시스템, TRIM/discard 연계
커널 암호화 (Crypto)Crypto커널 Crypto API, blk-crypto, ICE/FMP 인라인 암호화 프레임워크
클럭 프레임워크Common Clock FrameworkUFS 호스트 클럭 관리, clk_prepare_enable/clk_gating, MediaTek 다단계 클럭
레귤레이터 프레임워크Regulator FrameworkVCC/VCCQ/VA09 레귤레이터 제어, 전원 시퀀싱, MediaTek VA09
전원 관리전원 관리Runtime PM, System Suspend, Auto-Hibernate, DevFreq 스케일링
IOMMUIOMMUUFS DMA 매핑의 IOMMU 변환, ARM SMMU 설정
TrustZone / OP-TEETrustZone / OP-TEEICE Wrapped Key, RPMB 보안 저장소, Secure World 연동
ThermalThermalUFS 디바이스 온도 모니터링, 써멀 스로틀링에 의한 성능 제한

표준 사양서

커널 문서 및 소스 코드

기술 문서 및 분석 기사

UFS 서브시스템을 소스 코드 레벨에서 이해하려면 다음 순서로 읽는 것을 권장합니다.

1단계: 헤더 및 프로토콜 정의

  1. include/ufs/ufs.h — UFS 프로토콜 상수, UPIU 구조체, 디스크립터 IDN, 전원 모드, 벤더 ID 정의(UFS_VENDOR_SAMSUNG 등), 디바이스 quirk 플래그
  2. include/ufs/ufshci.h — UFSHCI 레지스터 오프셋, UTRD/UTMRD 구조체, OCS 코드 정의
  3. include/ufs/ufshcd.hstruct ufs_hba, struct ufs_hba_variant_ops(vops) 정의, quirk 플래그 적용 매크로

2단계: 코어 드라이버

  1. drivers/ufs/core/ufshcd.c — UFS 호스트 컨트롤러 드라이버 코어 (초기화, I/O 경로, 에러 핸들링, PM, ufs_fixups[] 테이블)
  2. drivers/ufs/core/ufs-mcq.c — Multi-Circular Queue 구현 (SQ/CQ 관리, 큐 매핑)
  3. drivers/ufs/core/ufs-sysfs.c — sysfs 속성 정의 및 읽기/쓰기 핸들러
  4. drivers/ufs/core/ufshcd-crypto.c — 인라인 암호화(ICE) 키 프로그래밍 및 crypto 프로파일
  5. drivers/ufs/core/ufshpb.c — HPB 코어 구현 (L2P 캐시 관리, Active/Inactive Region 전환)

3단계: 플랫폼별 드라이버 (벤더 심층 분석 대응)

  1. drivers/ufs/host/ufs-qcom.cQualcomm: ICE 키 프로그래밍, QMP PHY 연동, Testbus 덤프, MCQ 리소스 매핑, DevFreq 스케일링
  2. drivers/ufs/host/ufs-exynos.cSamsung Exynos: FMP(PRDT 기반 암호화), PMA PHY 캘리브레이션, TXHSLV/RXHSLV 조정, EOM
  3. drivers/ufs/host/ufs-mediatek.cMediaTek: VA09 레귤레이터 시퀀싱, 다단계 클럭 게이팅, 전용 Crypto IP 초기화
  4. drivers/ufs/host/ufs-hisi.cHiSilicon: Kirin SoC PHY 다단계 초기화, Hi3660/Hi3670 리비전별 분기
  5. drivers/ufs/host/ufs-renesas.cRenesas: R-Car Gen4 자동차 등급 PHY, Auto-Hibernate 정책
  6. drivers/ufs/host/ufshcd-pci.cIntel/PCI: PCI 기반 UFS, LTR 설정, VID/DID 매칭

4단계: 관련 서브시스템

  1. drivers/soc/qcom/ice.c — Qualcomm ICE 공통 드라이버 (키슬롯 레지스터 접근, Wrapped Key 처리)
  2. drivers/phy/qualcomm/phy-qcom-qmp-ufs.c — QMP UFS PHY 드라이버 (SoC별 캘리브레이션 테이블)
  3. drivers/phy/samsung/phy-exynos-ufs.c — Exynos UFS PHY 드라이버 (PMA 레지스터, EOM 구현)

핵심 I/O 경로 (ufshcd_queuecommand()에서 시작):

  1. SCSI 미들레이어가 scsi_cmnd를 전달 → ufshcd_queuecommand()
  2. UPIU 구성
    • ufshcd_compose_devman_upiu() (Query/NOP)
    • ufshcd_compose_scsi_cmd() (SCSI Command)
  3. UTRD 설정 + DMA 매핑
    • ufshcd_prepare_utp_scsi_cmd_upiu()
    • ufshcd_map_sg() (scatter-gather → PRDT)
  4. Doorbell 레지스터 쓰기(SDB) 또는 SQ Tail 갱신(MCQ) → ufshcd_send_command()
  5. 인터럽트 → 완료 처리
    • ufshcd_transfer_req_compl()
    • ufshcd_compl_one_cqe() (MCQ)
# 소스 코드 탐색 팁
# 1. I/O 경로 시작점
grep -n "ufshcd_queuecommand" drivers/ufs/core/ufshcd.c | head -5

# 2. 에러 핸들러 진입점
grep -n "ufshcd_err_handler" drivers/ufs/core/ufshcd.c | head -5

# 3. 전원 관리 진입점
grep -n "ufshcd_suspend\|ufshcd_resume" drivers/ufs/core/ufshcd.c | head -10

# 4. MCQ 관련 함수
grep -n "ufshcd_mcq" drivers/ufs/core/ufs-mcq.c | head -10

# 5. 플랫폼 드라이버 ops 정의 (벤더별)
grep -n "ufs_hba_qcom_vops" drivers/ufs/host/ufs-qcom.c
grep -n "ufs_hba_exynos_ops" drivers/ufs/host/ufs-exynos.c
grep -n "ufs_hba_mtk_vops" drivers/ufs/host/ufs-mediatek.c

# 6. 디바이스 Quirks 테이블 — 벤더별 워크어라운드 확인
grep -n "ufs_fixups\[\]" drivers/ufs/core/ufshcd.c

# 7. ICE 키 프로그래밍 흐름
grep -n "program_key" drivers/ufs/host/ufs-qcom.c
grep -n "qcom_ice_program_key" drivers/soc/qcom/ice.c

# 8. FMP PRDT 확장 구조
grep -n "fmp\|FMP" drivers/ufs/host/ufs-exynos.c

# 9. 벤더 ID 및 quirk 플래그 정의
grep -n "UFS_VENDOR_\|UFS_DEVICE_QUIRK_" include/ufs/ufs.h
학습 시작점: UFS를 처음 접한다면 drivers/ufs/core/ufshcd.cufshcd_queuecommand() 함수부터 읽으세요. SCSI 커맨드가 UFS UPIU로 변환되어 하드웨어에 전달되는 전체 I/O 경로를 하나의 함수 호출 체인에서 따라갈 수 있습니다. 이후 ufshcd_err_handler()로 에러 복구 흐름을, ufshcd_suspend()/ufshcd_resume()으로 전원 관리 흐름을 학습하면 UFS 드라이버의 전체 구조를 파악할 수 있습니다. 벤더별 구현을 비교하려면 ufs-qcom.cufs-exynos.c의 vops 구조체를 나란히 놓고, 같은 콜백(예: program_key)이 ICE와 FMP에서 어떻게 다르게 구현되는지 비교하는 것이 효과적입니다.