1bc4e68d3SJeuk Kim /*
2bc4e68d3SJeuk Kim * QEMU UFS
3bc4e68d3SJeuk Kim *
4bc4e68d3SJeuk Kim * Copyright (c) 2023 Samsung Electronics Co., Ltd. All rights reserved.
5bc4e68d3SJeuk Kim *
6bc4e68d3SJeuk Kim * Written by Jeuk Kim <jeuk20.kim@samsung.com>
7bc4e68d3SJeuk Kim *
8bc4e68d3SJeuk Kim * SPDX-License-Identifier: GPL-2.0-or-later
9bc4e68d3SJeuk Kim */
10bc4e68d3SJeuk Kim
11bc4e68d3SJeuk Kim #ifndef HW_UFS_UFS_H
12bc4e68d3SJeuk Kim #define HW_UFS_UFS_H
13bc4e68d3SJeuk Kim
14bc4e68d3SJeuk Kim #include "hw/pci/pci_device.h"
15bc4e68d3SJeuk Kim #include "hw/scsi/scsi.h"
16bc4e68d3SJeuk Kim #include "block/ufs.h"
17bc4e68d3SJeuk Kim
18bc4e68d3SJeuk Kim #define UFS_MAX_LUS 32
195c079578SMinwoo Im #define UFS_MAX_MCQ_QNUM 32
20096434feSJeuk Kim #define UFS_BLOCK_SIZE_SHIFT 12
21096434feSJeuk Kim #define UFS_BLOCK_SIZE (1 << UFS_BLOCK_SIZE_SHIFT)
22bc4e68d3SJeuk Kim
232a8b36a4SJeuk Kim typedef struct UfsBusClass {
242a8b36a4SJeuk Kim BusClass parent_class;
252a8b36a4SJeuk Kim bool (*parent_check_address)(BusState *bus, DeviceState *dev, Error **errp);
262a8b36a4SJeuk Kim } UfsBusClass;
272a8b36a4SJeuk Kim
282a8b36a4SJeuk Kim typedef struct UfsBus {
29096434feSJeuk Kim BusState parent_bus;
302a8b36a4SJeuk Kim } UfsBus;
312a8b36a4SJeuk Kim
322a8b36a4SJeuk Kim #define TYPE_UFS_BUS "ufs-bus"
332a8b36a4SJeuk Kim DECLARE_OBJ_CHECKERS(UfsBus, UfsBusClass, UFS_BUS, TYPE_UFS_BUS)
342a8b36a4SJeuk Kim
35329f1662SJeuk Kim typedef enum UfsRequestState {
36329f1662SJeuk Kim UFS_REQUEST_IDLE = 0,
37329f1662SJeuk Kim UFS_REQUEST_READY = 1,
38329f1662SJeuk Kim UFS_REQUEST_RUNNING = 2,
39329f1662SJeuk Kim UFS_REQUEST_COMPLETE = 3,
40329f1662SJeuk Kim UFS_REQUEST_ERROR = 4,
41329f1662SJeuk Kim } UfsRequestState;
42329f1662SJeuk Kim
43329f1662SJeuk Kim typedef enum UfsReqResult {
44329f1662SJeuk Kim UFS_REQUEST_SUCCESS = 0,
45329f1662SJeuk Kim UFS_REQUEST_FAIL = 1,
462a8b36a4SJeuk Kim UFS_REQUEST_NO_COMPLETE = 2,
47329f1662SJeuk Kim } UfsReqResult;
48329f1662SJeuk Kim
495c079578SMinwoo Im #define UFS_INVALID_SLOT (-1)
50329f1662SJeuk Kim typedef struct UfsRequest {
51329f1662SJeuk Kim struct UfsHc *hc;
52329f1662SJeuk Kim UfsRequestState state;
535c079578SMinwoo Im int slot; /* -1 when it's a MCQ request */
54329f1662SJeuk Kim
55329f1662SJeuk Kim UtpTransferReqDesc utrd;
56329f1662SJeuk Kim UtpUpiuReq req_upiu;
57329f1662SJeuk Kim UtpUpiuRsp rsp_upiu;
58329f1662SJeuk Kim
59329f1662SJeuk Kim /* for scsi command */
60329f1662SJeuk Kim QEMUSGList *sg;
61096434feSJeuk Kim uint32_t data_len;
625c079578SMinwoo Im
635c079578SMinwoo Im /* for MCQ */
645c079578SMinwoo Im struct UfsSq *sq;
655c079578SMinwoo Im struct UfsCqEntry cqe;
665c079578SMinwoo Im QTAILQ_ENTRY(UfsRequest) entry;
67329f1662SJeuk Kim } UfsRequest;
68329f1662SJeuk Kim
ufs_mcq_req(UfsRequest * req)695c079578SMinwoo Im static inline bool ufs_mcq_req(UfsRequest *req)
705c079578SMinwoo Im {
715c079578SMinwoo Im return req->sq != NULL;
725c079578SMinwoo Im }
735c079578SMinwoo Im
74096434feSJeuk Kim struct UfsLu;
75096434feSJeuk Kim typedef UfsReqResult (*UfsScsiOp)(struct UfsLu *, UfsRequest *);
76096434feSJeuk Kim
772a8b36a4SJeuk Kim typedef struct UfsLu {
78096434feSJeuk Kim DeviceState qdev;
792a8b36a4SJeuk Kim uint8_t lun;
802a8b36a4SJeuk Kim UnitDescriptor unit_desc;
81096434feSJeuk Kim SCSIBus bus;
82096434feSJeuk Kim SCSIDevice *scsi_dev;
83096434feSJeuk Kim BlockConf conf;
84096434feSJeuk Kim UfsScsiOp scsi_op;
852a8b36a4SJeuk Kim } UfsLu;
862a8b36a4SJeuk Kim
87bc4e68d3SJeuk Kim typedef struct UfsParams {
88bc4e68d3SJeuk Kim char *serial;
89bc4e68d3SJeuk Kim uint8_t nutrs; /* Number of UTP Transfer Request Slots */
90bc4e68d3SJeuk Kim uint8_t nutmrs; /* Number of UTP Task Management Request Slots */
915c079578SMinwoo Im bool mcq; /* Multiple Command Queue support */
925c079578SMinwoo Im uint8_t mcq_qcfgptr; /* MCQ Queue Configuration Pointer in MCQCAP */
935c079578SMinwoo Im uint8_t mcq_maxq; /* MCQ Maximum number of Queues */
94bc4e68d3SJeuk Kim } UfsParams;
95bc4e68d3SJeuk Kim
965c079578SMinwoo Im /*
975c079578SMinwoo Im * MCQ Properties
985c079578SMinwoo Im */
995c079578SMinwoo Im typedef struct UfsSq {
1005c079578SMinwoo Im struct UfsHc *u;
1015c079578SMinwoo Im uint8_t sqid;
1025c079578SMinwoo Im struct UfsCq *cq;
1035c079578SMinwoo Im uint64_t addr;
1045c079578SMinwoo Im uint16_t size; /* A number of entries (qdepth) */
1055c079578SMinwoo Im
1065c079578SMinwoo Im QEMUBH *bh; /* Bottom half to process requests in async */
1075c079578SMinwoo Im UfsRequest *req;
1085c079578SMinwoo Im QTAILQ_HEAD(, UfsRequest) req_list; /* Free request list */
1095c079578SMinwoo Im } UfsSq;
1105c079578SMinwoo Im
1115c079578SMinwoo Im typedef struct UfsCq {
1125c079578SMinwoo Im struct UfsHc *u;
1135c079578SMinwoo Im uint8_t cqid;
1145c079578SMinwoo Im uint64_t addr;
1155c079578SMinwoo Im uint16_t size; /* A number of entries (qdepth) */
1165c079578SMinwoo Im
1175c079578SMinwoo Im QEMUBH *bh;
1185c079578SMinwoo Im QTAILQ_HEAD(, UfsRequest) req_list;
1195c079578SMinwoo Im } UfsCq;
1205c079578SMinwoo Im
121bc4e68d3SJeuk Kim typedef struct UfsHc {
122bc4e68d3SJeuk Kim PCIDevice parent_obj;
1232a8b36a4SJeuk Kim UfsBus bus;
124bc4e68d3SJeuk Kim MemoryRegion iomem;
125bc4e68d3SJeuk Kim UfsReg reg;
1265c079578SMinwoo Im UfsMcqReg mcq_reg[UFS_MAX_MCQ_QNUM];
1275c079578SMinwoo Im UfsMcqOpReg mcq_op_reg[UFS_MAX_MCQ_QNUM];
128bc4e68d3SJeuk Kim UfsParams params;
129bc4e68d3SJeuk Kim uint32_t reg_size;
130329f1662SJeuk Kim UfsRequest *req_list;
131329f1662SJeuk Kim
1322a8b36a4SJeuk Kim UfsLu *lus[UFS_MAX_LUS];
133096434feSJeuk Kim UfsLu report_wlu;
134096434feSJeuk Kim UfsLu dev_wlu;
135096434feSJeuk Kim UfsLu boot_wlu;
136096434feSJeuk Kim UfsLu rpmb_wlu;
137329f1662SJeuk Kim DeviceDescriptor device_desc;
138329f1662SJeuk Kim GeometryDescriptor geometry_desc;
139329f1662SJeuk Kim Attributes attributes;
140329f1662SJeuk Kim Flags flags;
141bc4e68d3SJeuk Kim
142bc4e68d3SJeuk Kim qemu_irq irq;
143bc4e68d3SJeuk Kim QEMUBH *doorbell_bh;
144bc4e68d3SJeuk Kim QEMUBH *complete_bh;
1455c079578SMinwoo Im
1465c079578SMinwoo Im /* MCQ properties */
1475c079578SMinwoo Im UfsSq *sq[UFS_MAX_MCQ_QNUM];
1485c079578SMinwoo Im UfsCq *cq[UFS_MAX_MCQ_QNUM];
149bc4e68d3SJeuk Kim } UfsHc;
150bc4e68d3SJeuk Kim
ufs_mcq_sq_tail(UfsHc * u,uint32_t qid)1515c079578SMinwoo Im static inline uint32_t ufs_mcq_sq_tail(UfsHc *u, uint32_t qid)
1525c079578SMinwoo Im {
1535c079578SMinwoo Im return u->mcq_op_reg[qid].sq.tp;
1545c079578SMinwoo Im }
1555c079578SMinwoo Im
ufs_mcq_update_sq_tail(UfsHc * u,uint32_t qid,uint32_t db)1565c079578SMinwoo Im static inline void ufs_mcq_update_sq_tail(UfsHc *u, uint32_t qid, uint32_t db)
1575c079578SMinwoo Im {
1585c079578SMinwoo Im u->mcq_op_reg[qid].sq.tp = db;
1595c079578SMinwoo Im }
1605c079578SMinwoo Im
ufs_mcq_sq_head(UfsHc * u,uint32_t qid)1615c079578SMinwoo Im static inline uint32_t ufs_mcq_sq_head(UfsHc *u, uint32_t qid)
1625c079578SMinwoo Im {
1635c079578SMinwoo Im return u->mcq_op_reg[qid].sq.hp;
1645c079578SMinwoo Im }
1655c079578SMinwoo Im
ufs_mcq_update_sq_head(UfsHc * u,uint32_t qid,uint32_t db)1665c079578SMinwoo Im static inline void ufs_mcq_update_sq_head(UfsHc *u, uint32_t qid, uint32_t db)
1675c079578SMinwoo Im {
1685c079578SMinwoo Im u->mcq_op_reg[qid].sq.hp = db;
1695c079578SMinwoo Im }
1705c079578SMinwoo Im
ufs_mcq_sq_empty(UfsHc * u,uint32_t qid)1715c079578SMinwoo Im static inline bool ufs_mcq_sq_empty(UfsHc *u, uint32_t qid)
1725c079578SMinwoo Im {
1735c079578SMinwoo Im return ufs_mcq_sq_tail(u, qid) == ufs_mcq_sq_head(u, qid);
1745c079578SMinwoo Im }
1755c079578SMinwoo Im
ufs_mcq_cq_tail(UfsHc * u,uint32_t qid)1765c079578SMinwoo Im static inline uint32_t ufs_mcq_cq_tail(UfsHc *u, uint32_t qid)
1775c079578SMinwoo Im {
1785c079578SMinwoo Im return u->mcq_op_reg[qid].cq.tp;
1795c079578SMinwoo Im }
1805c079578SMinwoo Im
ufs_mcq_update_cq_tail(UfsHc * u,uint32_t qid,uint32_t db)1815c079578SMinwoo Im static inline void ufs_mcq_update_cq_tail(UfsHc *u, uint32_t qid, uint32_t db)
1825c079578SMinwoo Im {
1835c079578SMinwoo Im u->mcq_op_reg[qid].cq.tp = db;
1845c079578SMinwoo Im }
1855c079578SMinwoo Im
ufs_mcq_cq_head(UfsHc * u,uint32_t qid)1865c079578SMinwoo Im static inline uint32_t ufs_mcq_cq_head(UfsHc *u, uint32_t qid)
1875c079578SMinwoo Im {
1885c079578SMinwoo Im return u->mcq_op_reg[qid].cq.hp;
1895c079578SMinwoo Im }
1905c079578SMinwoo Im
ufs_mcq_update_cq_head(UfsHc * u,uint32_t qid,uint32_t db)1915c079578SMinwoo Im static inline void ufs_mcq_update_cq_head(UfsHc *u, uint32_t qid, uint32_t db)
1925c079578SMinwoo Im {
1935c079578SMinwoo Im u->mcq_op_reg[qid].cq.hp = db;
1945c079578SMinwoo Im }
1955c079578SMinwoo Im
ufs_mcq_cq_empty(UfsHc * u,uint32_t qid)1965c079578SMinwoo Im static inline bool ufs_mcq_cq_empty(UfsHc *u, uint32_t qid)
1975c079578SMinwoo Im {
1985c079578SMinwoo Im return ufs_mcq_cq_tail(u, qid) == ufs_mcq_cq_head(u, qid);
1995c079578SMinwoo Im }
2005c079578SMinwoo Im
201bc4e68d3SJeuk Kim #define TYPE_UFS "ufs"
202bc4e68d3SJeuk Kim #define UFS(obj) OBJECT_CHECK(UfsHc, (obj), TYPE_UFS)
203bc4e68d3SJeuk Kim
2042a8b36a4SJeuk Kim #define TYPE_UFS_LU "ufs-lu"
2052a8b36a4SJeuk Kim #define UFSLU(obj) OBJECT_CHECK(UfsLu, (obj), TYPE_UFS_LU)
2062a8b36a4SJeuk Kim
207329f1662SJeuk Kim typedef enum UfsQueryFlagPerm {
208329f1662SJeuk Kim UFS_QUERY_FLAG_NONE = 0x0,
209329f1662SJeuk Kim UFS_QUERY_FLAG_READ = 0x1,
210329f1662SJeuk Kim UFS_QUERY_FLAG_SET = 0x2,
211329f1662SJeuk Kim UFS_QUERY_FLAG_CLEAR = 0x4,
212329f1662SJeuk Kim UFS_QUERY_FLAG_TOGGLE = 0x8,
213329f1662SJeuk Kim } UfsQueryFlagPerm;
214329f1662SJeuk Kim
215329f1662SJeuk Kim typedef enum UfsQueryAttrPerm {
216329f1662SJeuk Kim UFS_QUERY_ATTR_NONE = 0x0,
217329f1662SJeuk Kim UFS_QUERY_ATTR_READ = 0x1,
218329f1662SJeuk Kim UFS_QUERY_ATTR_WRITE = 0x2,
219329f1662SJeuk Kim } UfsQueryAttrPerm;
220329f1662SJeuk Kim
is_wlun(uint8_t lun)2212a8b36a4SJeuk Kim static inline bool is_wlun(uint8_t lun)
2222a8b36a4SJeuk Kim {
2232a8b36a4SJeuk Kim return (lun == UFS_UPIU_REPORT_LUNS_WLUN ||
2242a8b36a4SJeuk Kim lun == UFS_UPIU_UFS_DEVICE_WLUN || lun == UFS_UPIU_BOOT_WLUN ||
2252a8b36a4SJeuk Kim lun == UFS_UPIU_RPMB_WLUN);
2262a8b36a4SJeuk Kim }
2272a8b36a4SJeuk Kim
228096434feSJeuk Kim void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
229096434feSJeuk Kim uint8_t response, uint8_t scsi_status,
230096434feSJeuk Kim uint16_t data_segment_length);
231*de2cc407SKyoungrul Kim void ufs_build_query_response(UfsRequest *req);
232096434feSJeuk Kim void ufs_complete_req(UfsRequest *req, UfsReqResult req_result);
233096434feSJeuk Kim void ufs_init_wlu(UfsLu *wlu, uint8_t wlun);
234bc4e68d3SJeuk Kim #endif /* HW_UFS_UFS_H */
235