xref: /openbmc/qemu/hw/ufs/ufs.c (revision 80a37b03)
1bc4e68d3SJeuk Kim /*
2bc4e68d3SJeuk Kim  * QEMU Universal Flash Storage (UFS) Controller
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 
112a8b36a4SJeuk Kim /**
122a8b36a4SJeuk Kim  * Reference Specs: https://www.jedec.org/, 3.1
132a8b36a4SJeuk Kim  *
142a8b36a4SJeuk Kim  * Usage
152a8b36a4SJeuk Kim  * -----
162a8b36a4SJeuk Kim  *
172a8b36a4SJeuk Kim  * Add options:
182a8b36a4SJeuk Kim  *      -drive file=<file>,if=none,id=<drive_id>
192a8b36a4SJeuk Kim  *      -device ufs,serial=<serial>,id=<bus_name>, \
202a8b36a4SJeuk Kim  *              nutrs=<N[optional]>,nutmrs=<N[optional]>
212a8b36a4SJeuk Kim  *      -device ufs-lu,drive=<drive_id>,bus=<bus_name>
222a8b36a4SJeuk Kim  */
232a8b36a4SJeuk Kim 
24bc4e68d3SJeuk Kim #include "qemu/osdep.h"
25bc4e68d3SJeuk Kim #include "qapi/error.h"
26bc4e68d3SJeuk Kim #include "migration/vmstate.h"
27096434feSJeuk Kim #include "scsi/constants.h"
28bc4e68d3SJeuk Kim #include "trace.h"
29bc4e68d3SJeuk Kim #include "ufs.h"
30bc4e68d3SJeuk Kim 
31bc4e68d3SJeuk Kim /* The QEMU-UFS device follows spec version 3.1 */
32329f1662SJeuk Kim #define UFS_SPEC_VER 0x0310
33bc4e68d3SJeuk Kim #define UFS_MAX_NUTRS 32
34bc4e68d3SJeuk Kim #define UFS_MAX_NUTMRS 8
35bc4e68d3SJeuk Kim 
ufs_addr_read(UfsHc * u,hwaddr addr,void * buf,int size)36329f1662SJeuk Kim static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size)
37329f1662SJeuk Kim {
38329f1662SJeuk Kim     hwaddr hi = addr + size - 1;
39329f1662SJeuk Kim 
40329f1662SJeuk Kim     if (hi < addr) {
41329f1662SJeuk Kim         return MEMTX_DECODE_ERROR;
42329f1662SJeuk Kim     }
43329f1662SJeuk Kim 
44329f1662SJeuk Kim     if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) {
45329f1662SJeuk Kim         return MEMTX_DECODE_ERROR;
46329f1662SJeuk Kim     }
47329f1662SJeuk Kim 
48329f1662SJeuk Kim     return pci_dma_read(PCI_DEVICE(u), addr, buf, size);
49329f1662SJeuk Kim }
50329f1662SJeuk Kim 
ufs_addr_write(UfsHc * u,hwaddr addr,const void * buf,int size)51329f1662SJeuk Kim static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf,
52329f1662SJeuk Kim                                   int size)
53329f1662SJeuk Kim {
54329f1662SJeuk Kim     hwaddr hi = addr + size - 1;
55329f1662SJeuk Kim     if (hi < addr) {
56329f1662SJeuk Kim         return MEMTX_DECODE_ERROR;
57329f1662SJeuk Kim     }
58329f1662SJeuk Kim 
59329f1662SJeuk Kim     if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) {
60329f1662SJeuk Kim         return MEMTX_DECODE_ERROR;
61329f1662SJeuk Kim     }
62329f1662SJeuk Kim 
63329f1662SJeuk Kim     return pci_dma_write(PCI_DEVICE(u), addr, buf, size);
64329f1662SJeuk Kim }
65329f1662SJeuk Kim 
ufs_get_utrd_addr(UfsHc * u,uint32_t slot)66329f1662SJeuk Kim static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot)
67329f1662SJeuk Kim {
68329f1662SJeuk Kim     hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba;
69329f1662SJeuk Kim     hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc);
70329f1662SJeuk Kim 
71329f1662SJeuk Kim     return utrd_addr;
72329f1662SJeuk Kim }
73329f1662SJeuk Kim 
ufs_get_req_upiu_base_addr(const UtpTransferReqDesc * utrd)74329f1662SJeuk Kim static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd)
75329f1662SJeuk Kim {
76329f1662SJeuk Kim     uint32_t cmd_desc_base_addr_lo =
77329f1662SJeuk Kim         le32_to_cpu(utrd->command_desc_base_addr_lo);
78329f1662SJeuk Kim     uint32_t cmd_desc_base_addr_hi =
79329f1662SJeuk Kim         le32_to_cpu(utrd->command_desc_base_addr_hi);
80329f1662SJeuk Kim 
81329f1662SJeuk Kim     return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo;
82329f1662SJeuk Kim }
83329f1662SJeuk Kim 
ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc * utrd)84329f1662SJeuk Kim static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd)
85329f1662SJeuk Kim {
86329f1662SJeuk Kim     hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd);
87329f1662SJeuk Kim     uint32_t rsp_upiu_byte_off =
88329f1662SJeuk Kim         le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t);
89329f1662SJeuk Kim     return req_upiu_base_addr + rsp_upiu_byte_off;
90329f1662SJeuk Kim }
91329f1662SJeuk Kim 
ufs_dma_read_utrd(UfsRequest * req)92329f1662SJeuk Kim static MemTxResult ufs_dma_read_utrd(UfsRequest *req)
93329f1662SJeuk Kim {
94329f1662SJeuk Kim     UfsHc *u = req->hc;
95329f1662SJeuk Kim     hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
96329f1662SJeuk Kim     MemTxResult ret;
97329f1662SJeuk Kim 
98329f1662SJeuk Kim     ret = ufs_addr_read(u, utrd_addr, &req->utrd, sizeof(req->utrd));
99329f1662SJeuk Kim     if (ret) {
100329f1662SJeuk Kim         trace_ufs_err_dma_read_utrd(req->slot, utrd_addr);
101329f1662SJeuk Kim     }
102329f1662SJeuk Kim     return ret;
103329f1662SJeuk Kim }
104329f1662SJeuk Kim 
ufs_dma_read_req_upiu(UfsRequest * req)105329f1662SJeuk Kim static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req)
106329f1662SJeuk Kim {
107329f1662SJeuk Kim     UfsHc *u = req->hc;
108329f1662SJeuk Kim     hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd);
109329f1662SJeuk Kim     UtpUpiuReq *req_upiu = &req->req_upiu;
110329f1662SJeuk Kim     uint32_t copy_size;
111329f1662SJeuk Kim     uint16_t data_segment_length;
112329f1662SJeuk Kim     MemTxResult ret;
113329f1662SJeuk Kim 
114329f1662SJeuk Kim     /*
115329f1662SJeuk Kim      * To know the size of the req_upiu, we need to read the
116329f1662SJeuk Kim      * data_segment_length in the header first.
117329f1662SJeuk Kim      */
118329f1662SJeuk Kim     ret = ufs_addr_read(u, req_upiu_base_addr, &req_upiu->header,
119329f1662SJeuk Kim                         sizeof(UtpUpiuHeader));
120329f1662SJeuk Kim     if (ret) {
121329f1662SJeuk Kim         trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
122329f1662SJeuk Kim         return ret;
123329f1662SJeuk Kim     }
124329f1662SJeuk Kim     data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length);
125329f1662SJeuk Kim 
126329f1662SJeuk Kim     copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
127329f1662SJeuk Kim                 data_segment_length;
128329f1662SJeuk Kim 
129329f1662SJeuk Kim     ret = ufs_addr_read(u, req_upiu_base_addr, &req->req_upiu, copy_size);
130329f1662SJeuk Kim     if (ret) {
131329f1662SJeuk Kim         trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
132329f1662SJeuk Kim     }
133329f1662SJeuk Kim     return ret;
134329f1662SJeuk Kim }
135329f1662SJeuk Kim 
ufs_dma_read_prdt(UfsRequest * req)136329f1662SJeuk Kim static MemTxResult ufs_dma_read_prdt(UfsRequest *req)
137329f1662SJeuk Kim {
138329f1662SJeuk Kim     UfsHc *u = req->hc;
139329f1662SJeuk Kim     uint16_t prdt_len = le16_to_cpu(req->utrd.prd_table_length);
140329f1662SJeuk Kim     uint16_t prdt_byte_off =
141329f1662SJeuk Kim         le16_to_cpu(req->utrd.prd_table_offset) * sizeof(uint32_t);
142329f1662SJeuk Kim     uint32_t prdt_size = prdt_len * sizeof(UfshcdSgEntry);
143329f1662SJeuk Kim     g_autofree UfshcdSgEntry *prd_entries = NULL;
144329f1662SJeuk Kim     hwaddr req_upiu_base_addr, prdt_base_addr;
145329f1662SJeuk Kim     int err;
146329f1662SJeuk Kim 
147329f1662SJeuk Kim     assert(!req->sg);
148329f1662SJeuk Kim 
149329f1662SJeuk Kim     if (prdt_size == 0) {
150329f1662SJeuk Kim         return MEMTX_OK;
151329f1662SJeuk Kim     }
152329f1662SJeuk Kim     prd_entries = g_new(UfshcdSgEntry, prdt_size);
153329f1662SJeuk Kim 
154329f1662SJeuk Kim     req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd);
155329f1662SJeuk Kim     prdt_base_addr = req_upiu_base_addr + prdt_byte_off;
156329f1662SJeuk Kim 
157329f1662SJeuk Kim     err = ufs_addr_read(u, prdt_base_addr, prd_entries, prdt_size);
158329f1662SJeuk Kim     if (err) {
159329f1662SJeuk Kim         trace_ufs_err_dma_read_prdt(req->slot, prdt_base_addr);
160329f1662SJeuk Kim         return err;
161329f1662SJeuk Kim     }
162329f1662SJeuk Kim 
163329f1662SJeuk Kim     req->sg = g_malloc0(sizeof(QEMUSGList));
164329f1662SJeuk Kim     pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
165096434feSJeuk Kim     req->data_len = 0;
166329f1662SJeuk Kim 
167329f1662SJeuk Kim     for (uint16_t i = 0; i < prdt_len; ++i) {
168329f1662SJeuk Kim         hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr);
169329f1662SJeuk Kim         uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1;
170329f1662SJeuk Kim         qemu_sglist_add(req->sg, data_dma_addr, data_byte_count);
171096434feSJeuk Kim         req->data_len += data_byte_count;
172329f1662SJeuk Kim     }
173329f1662SJeuk Kim     return MEMTX_OK;
174329f1662SJeuk Kim }
175329f1662SJeuk Kim 
ufs_dma_read_upiu(UfsRequest * req)176329f1662SJeuk Kim static MemTxResult ufs_dma_read_upiu(UfsRequest *req)
177329f1662SJeuk Kim {
178329f1662SJeuk Kim     MemTxResult ret;
179329f1662SJeuk Kim 
180329f1662SJeuk Kim     ret = ufs_dma_read_utrd(req);
181329f1662SJeuk Kim     if (ret) {
182329f1662SJeuk Kim         return ret;
183329f1662SJeuk Kim     }
184329f1662SJeuk Kim 
185329f1662SJeuk Kim     ret = ufs_dma_read_req_upiu(req);
186329f1662SJeuk Kim     if (ret) {
187329f1662SJeuk Kim         return ret;
188329f1662SJeuk Kim     }
189329f1662SJeuk Kim 
190329f1662SJeuk Kim     ret = ufs_dma_read_prdt(req);
191329f1662SJeuk Kim     if (ret) {
192329f1662SJeuk Kim         return ret;
193329f1662SJeuk Kim     }
194329f1662SJeuk Kim 
195329f1662SJeuk Kim     return 0;
196329f1662SJeuk Kim }
197329f1662SJeuk Kim 
ufs_dma_write_utrd(UfsRequest * req)198329f1662SJeuk Kim static MemTxResult ufs_dma_write_utrd(UfsRequest *req)
199329f1662SJeuk Kim {
200329f1662SJeuk Kim     UfsHc *u = req->hc;
201329f1662SJeuk Kim     hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
202329f1662SJeuk Kim     MemTxResult ret;
203329f1662SJeuk Kim 
204329f1662SJeuk Kim     ret = ufs_addr_write(u, utrd_addr, &req->utrd, sizeof(req->utrd));
205329f1662SJeuk Kim     if (ret) {
206329f1662SJeuk Kim         trace_ufs_err_dma_write_utrd(req->slot, utrd_addr);
207329f1662SJeuk Kim     }
208329f1662SJeuk Kim     return ret;
209329f1662SJeuk Kim }
210329f1662SJeuk Kim 
ufs_dma_write_rsp_upiu(UfsRequest * req)211329f1662SJeuk Kim static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req)
212329f1662SJeuk Kim {
213329f1662SJeuk Kim     UfsHc *u = req->hc;
214329f1662SJeuk Kim     hwaddr rsp_upiu_base_addr = ufs_get_rsp_upiu_base_addr(&req->utrd);
215329f1662SJeuk Kim     uint32_t rsp_upiu_byte_len =
216329f1662SJeuk Kim         le16_to_cpu(req->utrd.response_upiu_length) * sizeof(uint32_t);
217329f1662SJeuk Kim     uint16_t data_segment_length =
218329f1662SJeuk Kim         be16_to_cpu(req->rsp_upiu.header.data_segment_length);
219329f1662SJeuk Kim     uint32_t copy_size = sizeof(UtpUpiuHeader) +
220329f1662SJeuk Kim                          UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
221329f1662SJeuk Kim                          data_segment_length;
222329f1662SJeuk Kim     MemTxResult ret;
223329f1662SJeuk Kim 
224329f1662SJeuk Kim     if (copy_size > rsp_upiu_byte_len) {
225329f1662SJeuk Kim         copy_size = rsp_upiu_byte_len;
226329f1662SJeuk Kim     }
227329f1662SJeuk Kim 
228329f1662SJeuk Kim     ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size);
229329f1662SJeuk Kim     if (ret) {
230329f1662SJeuk Kim         trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr);
231329f1662SJeuk Kim     }
232329f1662SJeuk Kim     return ret;
233329f1662SJeuk Kim }
234329f1662SJeuk Kim 
ufs_dma_write_upiu(UfsRequest * req)235329f1662SJeuk Kim static MemTxResult ufs_dma_write_upiu(UfsRequest *req)
236329f1662SJeuk Kim {
237329f1662SJeuk Kim     MemTxResult ret;
238329f1662SJeuk Kim 
239329f1662SJeuk Kim     ret = ufs_dma_write_rsp_upiu(req);
240329f1662SJeuk Kim     if (ret) {
241329f1662SJeuk Kim         return ret;
242329f1662SJeuk Kim     }
243329f1662SJeuk Kim 
244329f1662SJeuk Kim     return ufs_dma_write_utrd(req);
245329f1662SJeuk Kim }
246329f1662SJeuk Kim 
ufs_irq_check(UfsHc * u)247bc4e68d3SJeuk Kim static void ufs_irq_check(UfsHc *u)
248bc4e68d3SJeuk Kim {
249bc4e68d3SJeuk Kim     PCIDevice *pci = PCI_DEVICE(u);
250bc4e68d3SJeuk Kim 
251bc4e68d3SJeuk Kim     if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) {
252bc4e68d3SJeuk Kim         trace_ufs_irq_raise();
253bc4e68d3SJeuk Kim         pci_irq_assert(pci);
254bc4e68d3SJeuk Kim     } else {
255bc4e68d3SJeuk Kim         trace_ufs_irq_lower();
256bc4e68d3SJeuk Kim         pci_irq_deassert(pci);
257bc4e68d3SJeuk Kim     }
258bc4e68d3SJeuk Kim }
259bc4e68d3SJeuk Kim 
ufs_process_db(UfsHc * u,uint32_t val)260329f1662SJeuk Kim static void ufs_process_db(UfsHc *u, uint32_t val)
261329f1662SJeuk Kim {
26297970daeSJeuk Kim     DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS);
263329f1662SJeuk Kim     uint32_t slot;
264329f1662SJeuk Kim     uint32_t nutrs = u->params.nutrs;
265329f1662SJeuk Kim     UfsRequest *req;
266329f1662SJeuk Kim 
267329f1662SJeuk Kim     val &= ~u->reg.utrldbr;
268329f1662SJeuk Kim     if (!val) {
269329f1662SJeuk Kim         return;
270329f1662SJeuk Kim     }
271329f1662SJeuk Kim 
27297970daeSJeuk Kim     doorbell[0] = val;
27397970daeSJeuk Kim     slot = find_first_bit(doorbell, nutrs);
274329f1662SJeuk Kim 
275329f1662SJeuk Kim     while (slot < nutrs) {
276329f1662SJeuk Kim         req = &u->req_list[slot];
277329f1662SJeuk Kim         if (req->state == UFS_REQUEST_ERROR) {
278329f1662SJeuk Kim             trace_ufs_err_utrl_slot_error(req->slot);
279329f1662SJeuk Kim             return;
280329f1662SJeuk Kim         }
281329f1662SJeuk Kim 
282329f1662SJeuk Kim         if (req->state != UFS_REQUEST_IDLE) {
283329f1662SJeuk Kim             trace_ufs_err_utrl_slot_busy(req->slot);
284329f1662SJeuk Kim             return;
285329f1662SJeuk Kim         }
286329f1662SJeuk Kim 
287329f1662SJeuk Kim         trace_ufs_process_db(slot);
288329f1662SJeuk Kim         req->state = UFS_REQUEST_READY;
28997970daeSJeuk Kim         slot = find_next_bit(doorbell, nutrs, slot + 1);
290329f1662SJeuk Kim     }
291329f1662SJeuk Kim 
292329f1662SJeuk Kim     qemu_bh_schedule(u->doorbell_bh);
293329f1662SJeuk Kim }
294329f1662SJeuk Kim 
ufs_process_uiccmd(UfsHc * u,uint32_t val)295bc4e68d3SJeuk Kim static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
296bc4e68d3SJeuk Kim {
297bc4e68d3SJeuk Kim     trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2,
298bc4e68d3SJeuk Kim                              u->reg.ucmdarg3);
299bc4e68d3SJeuk Kim     /*
300bc4e68d3SJeuk Kim      * Only the essential uic commands for running drivers on Linux and Windows
301bc4e68d3SJeuk Kim      * are implemented.
302bc4e68d3SJeuk Kim      */
303bc4e68d3SJeuk Kim     switch (val) {
304bc4e68d3SJeuk Kim     case UFS_UIC_CMD_DME_LINK_STARTUP:
305bc4e68d3SJeuk Kim         u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, DP, 1);
306bc4e68d3SJeuk Kim         u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTRLRDY, 1);
307bc4e68d3SJeuk Kim         u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1);
308bc4e68d3SJeuk Kim         u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
309bc4e68d3SJeuk Kim         break;
310bc4e68d3SJeuk Kim     /* TODO: Revisit it when Power Management is implemented */
311bc4e68d3SJeuk Kim     case UFS_UIC_CMD_DME_HIBER_ENTER:
312bc4e68d3SJeuk Kim         u->reg.is = FIELD_DP32(u->reg.is, IS, UHES, 1);
313bc4e68d3SJeuk Kim         u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL);
314bc4e68d3SJeuk Kim         u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
315bc4e68d3SJeuk Kim         break;
316bc4e68d3SJeuk Kim     case UFS_UIC_CMD_DME_HIBER_EXIT:
317bc4e68d3SJeuk Kim         u->reg.is = FIELD_DP32(u->reg.is, IS, UHXS, 1);
318bc4e68d3SJeuk Kim         u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL);
319bc4e68d3SJeuk Kim         u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
320bc4e68d3SJeuk Kim         break;
321bc4e68d3SJeuk Kim     default:
322bc4e68d3SJeuk Kim         u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_FAILURE;
323bc4e68d3SJeuk Kim     }
324bc4e68d3SJeuk Kim 
325bc4e68d3SJeuk Kim     u->reg.is = FIELD_DP32(u->reg.is, IS, UCCS, 1);
326bc4e68d3SJeuk Kim 
327bc4e68d3SJeuk Kim     ufs_irq_check(u);
328bc4e68d3SJeuk Kim }
329bc4e68d3SJeuk Kim 
ufs_write_reg(UfsHc * u,hwaddr offset,uint32_t data,unsigned size)330bc4e68d3SJeuk Kim static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
331bc4e68d3SJeuk Kim {
332bc4e68d3SJeuk Kim     switch (offset) {
333bc4e68d3SJeuk Kim     case A_IS:
334bc4e68d3SJeuk Kim         u->reg.is &= ~data;
335bc4e68d3SJeuk Kim         ufs_irq_check(u);
336bc4e68d3SJeuk Kim         break;
337bc4e68d3SJeuk Kim     case A_IE:
338bc4e68d3SJeuk Kim         u->reg.ie = data;
339bc4e68d3SJeuk Kim         ufs_irq_check(u);
340bc4e68d3SJeuk Kim         break;
341bc4e68d3SJeuk Kim     case A_HCE:
342bc4e68d3SJeuk Kim         if (!FIELD_EX32(u->reg.hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) {
343bc4e68d3SJeuk Kim             u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UCRDY, 1);
344bc4e68d3SJeuk Kim             u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 1);
345bc4e68d3SJeuk Kim         } else if (FIELD_EX32(u->reg.hce, HCE, HCE) &&
346bc4e68d3SJeuk Kim                    !FIELD_EX32(data, HCE, HCE)) {
347bc4e68d3SJeuk Kim             u->reg.hcs = 0;
348bc4e68d3SJeuk Kim             u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 0);
349bc4e68d3SJeuk Kim         }
350bc4e68d3SJeuk Kim         break;
351bc4e68d3SJeuk Kim     case A_UTRLBA:
352bc4e68d3SJeuk Kim         u->reg.utrlba = data & R_UTRLBA_UTRLBA_MASK;
353bc4e68d3SJeuk Kim         break;
354bc4e68d3SJeuk Kim     case A_UTRLBAU:
355bc4e68d3SJeuk Kim         u->reg.utrlbau = data;
356bc4e68d3SJeuk Kim         break;
357bc4e68d3SJeuk Kim     case A_UTRLDBR:
358329f1662SJeuk Kim         ufs_process_db(u, data);
359329f1662SJeuk Kim         u->reg.utrldbr |= data;
360bc4e68d3SJeuk Kim         break;
361bc4e68d3SJeuk Kim     case A_UTRLRSR:
362bc4e68d3SJeuk Kim         u->reg.utrlrsr = data;
363bc4e68d3SJeuk Kim         break;
364bc4e68d3SJeuk Kim     case A_UTRLCNR:
365bc4e68d3SJeuk Kim         u->reg.utrlcnr &= ~data;
366bc4e68d3SJeuk Kim         break;
367bc4e68d3SJeuk Kim     case A_UTMRLBA:
368bc4e68d3SJeuk Kim         u->reg.utmrlba = data & R_UTMRLBA_UTMRLBA_MASK;
369bc4e68d3SJeuk Kim         break;
370bc4e68d3SJeuk Kim     case A_UTMRLBAU:
371bc4e68d3SJeuk Kim         u->reg.utmrlbau = data;
372bc4e68d3SJeuk Kim         break;
373bc4e68d3SJeuk Kim     case A_UICCMD:
374bc4e68d3SJeuk Kim         ufs_process_uiccmd(u, data);
375bc4e68d3SJeuk Kim         break;
376bc4e68d3SJeuk Kim     case A_UCMDARG1:
377bc4e68d3SJeuk Kim         u->reg.ucmdarg1 = data;
378bc4e68d3SJeuk Kim         break;
379bc4e68d3SJeuk Kim     case A_UCMDARG2:
380bc4e68d3SJeuk Kim         u->reg.ucmdarg2 = data;
381bc4e68d3SJeuk Kim         break;
382bc4e68d3SJeuk Kim     case A_UCMDARG3:
383bc4e68d3SJeuk Kim         u->reg.ucmdarg3 = data;
384bc4e68d3SJeuk Kim         break;
385bc4e68d3SJeuk Kim     case A_UTRLCLR:
386bc4e68d3SJeuk Kim     case A_UTMRLDBR:
387bc4e68d3SJeuk Kim     case A_UTMRLCLR:
388bc4e68d3SJeuk Kim     case A_UTMRLRSR:
389bc4e68d3SJeuk Kim         trace_ufs_err_unsupport_register_offset(offset);
390bc4e68d3SJeuk Kim         break;
391bc4e68d3SJeuk Kim     default:
392bc4e68d3SJeuk Kim         trace_ufs_err_invalid_register_offset(offset);
393bc4e68d3SJeuk Kim         break;
394bc4e68d3SJeuk Kim     }
395bc4e68d3SJeuk Kim }
396bc4e68d3SJeuk Kim 
ufs_mmio_read(void * opaque,hwaddr addr,unsigned size)397bc4e68d3SJeuk Kim static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
398bc4e68d3SJeuk Kim {
399bc4e68d3SJeuk Kim     UfsHc *u = (UfsHc *)opaque;
400bc4e68d3SJeuk Kim     uint8_t *ptr = (uint8_t *)&u->reg;
401bc4e68d3SJeuk Kim     uint64_t value;
402bc4e68d3SJeuk Kim 
403bc4e68d3SJeuk Kim     if (addr > sizeof(u->reg) - size) {
404bc4e68d3SJeuk Kim         trace_ufs_err_invalid_register_offset(addr);
405bc4e68d3SJeuk Kim         return 0;
406bc4e68d3SJeuk Kim     }
407bc4e68d3SJeuk Kim 
408bc4e68d3SJeuk Kim     value = *(uint32_t *)(ptr + addr);
409bc4e68d3SJeuk Kim     trace_ufs_mmio_read(addr, value, size);
410bc4e68d3SJeuk Kim     return value;
411bc4e68d3SJeuk Kim }
412bc4e68d3SJeuk Kim 
ufs_mmio_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)413bc4e68d3SJeuk Kim static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
414bc4e68d3SJeuk Kim                            unsigned size)
415bc4e68d3SJeuk Kim {
416bc4e68d3SJeuk Kim     UfsHc *u = (UfsHc *)opaque;
417bc4e68d3SJeuk Kim 
418bc4e68d3SJeuk Kim     if (addr > sizeof(u->reg) - size) {
419bc4e68d3SJeuk Kim         trace_ufs_err_invalid_register_offset(addr);
420bc4e68d3SJeuk Kim         return;
421bc4e68d3SJeuk Kim     }
422bc4e68d3SJeuk Kim 
423bc4e68d3SJeuk Kim     trace_ufs_mmio_write(addr, data, size);
424bc4e68d3SJeuk Kim     ufs_write_reg(u, addr, data, size);
425bc4e68d3SJeuk Kim }
426bc4e68d3SJeuk Kim 
427bc4e68d3SJeuk Kim static const MemoryRegionOps ufs_mmio_ops = {
428bc4e68d3SJeuk Kim     .read = ufs_mmio_read,
429bc4e68d3SJeuk Kim     .write = ufs_mmio_write,
430bc4e68d3SJeuk Kim     .endianness = DEVICE_LITTLE_ENDIAN,
431bc4e68d3SJeuk Kim     .impl = {
432bc4e68d3SJeuk Kim         .min_access_size = 4,
433bc4e68d3SJeuk Kim         .max_access_size = 4,
434bc4e68d3SJeuk Kim     },
435bc4e68d3SJeuk Kim };
436bc4e68d3SJeuk Kim 
4372a8b36a4SJeuk Kim 
ufs_build_upiu_header(UfsRequest * req,uint8_t trans_type,uint8_t flags,uint8_t response,uint8_t scsi_status,uint16_t data_segment_length)438096434feSJeuk Kim void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
439096434feSJeuk Kim                            uint8_t response, uint8_t scsi_status,
440329f1662SJeuk Kim                            uint16_t data_segment_length)
441329f1662SJeuk Kim {
442329f1662SJeuk Kim     memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader));
443329f1662SJeuk Kim     req->rsp_upiu.header.trans_type = trans_type;
444329f1662SJeuk Kim     req->rsp_upiu.header.flags = flags;
445329f1662SJeuk Kim     req->rsp_upiu.header.response = response;
446329f1662SJeuk Kim     req->rsp_upiu.header.scsi_status = scsi_status;
447329f1662SJeuk Kim     req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
448329f1662SJeuk Kim }
449329f1662SJeuk Kim 
ufs_exec_scsi_cmd(UfsRequest * req)4502a8b36a4SJeuk Kim static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req)
4512a8b36a4SJeuk Kim {
4522a8b36a4SJeuk Kim     UfsHc *u = req->hc;
4532a8b36a4SJeuk Kim     uint8_t lun = req->req_upiu.header.lun;
454096434feSJeuk Kim 
455096434feSJeuk Kim     UfsLu *lu = NULL;
4562a8b36a4SJeuk Kim 
4572a8b36a4SJeuk Kim     trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]);
4582a8b36a4SJeuk Kim 
459096434feSJeuk Kim     if (!is_wlun(lun) && (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) {
4602a8b36a4SJeuk Kim         trace_ufs_err_scsi_cmd_invalid_lun(lun);
4612a8b36a4SJeuk Kim         return UFS_REQUEST_FAIL;
4622a8b36a4SJeuk Kim     }
4632a8b36a4SJeuk Kim 
4642a8b36a4SJeuk Kim     switch (lun) {
4652a8b36a4SJeuk Kim     case UFS_UPIU_REPORT_LUNS_WLUN:
466096434feSJeuk Kim         lu = &u->report_wlu;
4672a8b36a4SJeuk Kim         break;
4682a8b36a4SJeuk Kim     case UFS_UPIU_UFS_DEVICE_WLUN:
469096434feSJeuk Kim         lu = &u->dev_wlu;
4702a8b36a4SJeuk Kim         break;
4712a8b36a4SJeuk Kim     case UFS_UPIU_BOOT_WLUN:
472096434feSJeuk Kim         lu = &u->boot_wlu;
4732a8b36a4SJeuk Kim         break;
4742a8b36a4SJeuk Kim     case UFS_UPIU_RPMB_WLUN:
475096434feSJeuk Kim         lu = &u->rpmb_wlu;
4762a8b36a4SJeuk Kim         break;
4772a8b36a4SJeuk Kim     default:
478096434feSJeuk Kim         lu = u->lus[lun];
4792a8b36a4SJeuk Kim     }
4802a8b36a4SJeuk Kim 
481096434feSJeuk Kim     return lu->scsi_op(lu, req);
4822a8b36a4SJeuk Kim }
4832a8b36a4SJeuk Kim 
ufs_exec_nop_cmd(UfsRequest * req)484329f1662SJeuk Kim static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req)
485329f1662SJeuk Kim {
486329f1662SJeuk Kim     trace_ufs_exec_nop_cmd(req->slot);
487329f1662SJeuk Kim     ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_NOP_IN, 0, 0, 0, 0);
488329f1662SJeuk Kim     return UFS_REQUEST_SUCCESS;
489329f1662SJeuk Kim }
490329f1662SJeuk Kim 
491329f1662SJeuk Kim /*
492329f1662SJeuk Kim  * This defines the permission of flags based on their IDN. There are some
493329f1662SJeuk Kim  * things that are declared read-only, which is inconsistent with the ufs spec,
494329f1662SJeuk Kim  * because we want to return an error for features that are not yet supported.
495329f1662SJeuk Kim  */
496329f1662SJeuk Kim static const int flag_permission[UFS_QUERY_FLAG_IDN_COUNT] = {
497329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_FDEVICEINIT] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET,
498329f1662SJeuk Kim     /* Write protection is not supported */
499329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_PERMANENT_WPE] = UFS_QUERY_FLAG_READ,
500329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_PWR_ON_WPE] = UFS_QUERY_FLAG_READ,
501329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_BKOPS_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET |
502329f1662SJeuk Kim                                     UFS_QUERY_FLAG_CLEAR |
503329f1662SJeuk Kim                                     UFS_QUERY_FLAG_TOGGLE,
504329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE] =
505329f1662SJeuk Kim         UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | UFS_QUERY_FLAG_CLEAR |
506329f1662SJeuk Kim         UFS_QUERY_FLAG_TOGGLE,
507329f1662SJeuk Kim     /* Purge Operation is not supported */
508329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_PURGE_ENABLE] = UFS_QUERY_FLAG_NONE,
509329f1662SJeuk Kim     /* Refresh Operation is not supported */
510329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_REFRESH_ENABLE] = UFS_QUERY_FLAG_NONE,
511329f1662SJeuk Kim     /* Physical Resource Removal is not supported */
512329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL] = UFS_QUERY_FLAG_READ,
513329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_BUSY_RTC] = UFS_QUERY_FLAG_READ,
514329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE] = UFS_QUERY_FLAG_READ,
515329f1662SJeuk Kim     /* Write Booster is not supported */
516329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ,
517329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] = UFS_QUERY_FLAG_READ,
518329f1662SJeuk Kim     [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8] = UFS_QUERY_FLAG_READ,
519329f1662SJeuk Kim };
520329f1662SJeuk Kim 
ufs_flag_check_idn_valid(uint8_t idn,int op)521329f1662SJeuk Kim static inline QueryRespCode ufs_flag_check_idn_valid(uint8_t idn, int op)
522329f1662SJeuk Kim {
523329f1662SJeuk Kim     if (idn >= UFS_QUERY_FLAG_IDN_COUNT) {
524329f1662SJeuk Kim         return UFS_QUERY_RESULT_INVALID_IDN;
525329f1662SJeuk Kim     }
526329f1662SJeuk Kim 
527329f1662SJeuk Kim     if (!(flag_permission[idn] & op)) {
528329f1662SJeuk Kim         if (op == UFS_QUERY_FLAG_READ) {
529329f1662SJeuk Kim             trace_ufs_err_query_flag_not_readable(idn);
530329f1662SJeuk Kim             return UFS_QUERY_RESULT_NOT_READABLE;
531329f1662SJeuk Kim         }
532329f1662SJeuk Kim         trace_ufs_err_query_flag_not_writable(idn);
533329f1662SJeuk Kim         return UFS_QUERY_RESULT_NOT_WRITEABLE;
534329f1662SJeuk Kim     }
535329f1662SJeuk Kim 
536329f1662SJeuk Kim     return UFS_QUERY_RESULT_SUCCESS;
537329f1662SJeuk Kim }
538329f1662SJeuk Kim 
539329f1662SJeuk Kim static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = {
540329f1662SJeuk Kim     /* booting is not supported */
541329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_BOOT_LU_EN] = UFS_QUERY_ATTR_READ,
542329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_POWER_MODE] = UFS_QUERY_ATTR_READ,
543329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL] =
544329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
545329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_OOO_DATA_EN] = UFS_QUERY_ATTR_READ,
546329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_BKOPS_STATUS] = UFS_QUERY_ATTR_READ,
547329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_PURGE_STATUS] = UFS_QUERY_ATTR_READ,
548329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_MAX_DATA_IN] =
549329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
550329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_MAX_DATA_OUT] =
551329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
552329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED] = UFS_QUERY_ATTR_READ,
553329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_REF_CLK_FREQ] =
554329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
555329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK] = UFS_QUERY_ATTR_READ,
556329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT] =
557329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
558329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_EE_CONTROL] =
559329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
560329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_EE_STATUS] = UFS_QUERY_ATTR_READ,
561329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_SECONDS_PASSED] = UFS_QUERY_ATTR_WRITE,
562329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_CNTX_CONF] = UFS_QUERY_ATTR_READ,
563329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_FFU_STATUS] = UFS_QUERY_ATTR_READ,
564329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_PSA_STATE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
565329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE] =
566329f1662SJeuk Kim         UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
567329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME] = UFS_QUERY_ATTR_READ,
568329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP] = UFS_QUERY_ATTR_READ,
569329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
570329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
571329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_THROTTLING_STATUS] = UFS_QUERY_ATTR_READ,
572329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS] = UFS_QUERY_ATTR_READ,
573329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
574329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST] = UFS_QUERY_ATTR_READ,
575329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
576329f1662SJeuk Kim     /* refresh operation is not supported */
577329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_REFRESH_STATUS] = UFS_QUERY_ATTR_READ,
578329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_REFRESH_FREQ] = UFS_QUERY_ATTR_READ,
579329f1662SJeuk Kim     [UFS_QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
580329f1662SJeuk Kim };
581329f1662SJeuk Kim 
ufs_attr_check_idn_valid(uint8_t idn,int op)582329f1662SJeuk Kim static inline QueryRespCode ufs_attr_check_idn_valid(uint8_t idn, int op)
583329f1662SJeuk Kim {
584329f1662SJeuk Kim     if (idn >= UFS_QUERY_ATTR_IDN_COUNT) {
585329f1662SJeuk Kim         return UFS_QUERY_RESULT_INVALID_IDN;
586329f1662SJeuk Kim     }
587329f1662SJeuk Kim 
588329f1662SJeuk Kim     if (!(attr_permission[idn] & op)) {
589329f1662SJeuk Kim         if (op == UFS_QUERY_ATTR_READ) {
590329f1662SJeuk Kim             trace_ufs_err_query_attr_not_readable(idn);
591329f1662SJeuk Kim             return UFS_QUERY_RESULT_NOT_READABLE;
592329f1662SJeuk Kim         }
593329f1662SJeuk Kim         trace_ufs_err_query_attr_not_writable(idn);
594329f1662SJeuk Kim         return UFS_QUERY_RESULT_NOT_WRITEABLE;
595329f1662SJeuk Kim     }
596329f1662SJeuk Kim 
597329f1662SJeuk Kim     return UFS_QUERY_RESULT_SUCCESS;
598329f1662SJeuk Kim }
599329f1662SJeuk Kim 
ufs_exec_query_flag(UfsRequest * req,int op)600329f1662SJeuk Kim static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op)
601329f1662SJeuk Kim {
602329f1662SJeuk Kim     UfsHc *u = req->hc;
603329f1662SJeuk Kim     uint8_t idn = req->req_upiu.qr.idn;
604329f1662SJeuk Kim     uint32_t value;
605329f1662SJeuk Kim     QueryRespCode ret;
606329f1662SJeuk Kim 
607329f1662SJeuk Kim     ret = ufs_flag_check_idn_valid(idn, op);
608329f1662SJeuk Kim     if (ret) {
609329f1662SJeuk Kim         return ret;
610329f1662SJeuk Kim     }
611329f1662SJeuk Kim 
612329f1662SJeuk Kim     if (idn == UFS_QUERY_FLAG_IDN_FDEVICEINIT) {
613329f1662SJeuk Kim         value = 0;
614329f1662SJeuk Kim     } else if (op == UFS_QUERY_FLAG_READ) {
615329f1662SJeuk Kim         value = *(((uint8_t *)&u->flags) + idn);
616329f1662SJeuk Kim     } else if (op == UFS_QUERY_FLAG_SET) {
617329f1662SJeuk Kim         value = 1;
618329f1662SJeuk Kim     } else if (op == UFS_QUERY_FLAG_CLEAR) {
619329f1662SJeuk Kim         value = 0;
620329f1662SJeuk Kim     } else if (op == UFS_QUERY_FLAG_TOGGLE) {
621329f1662SJeuk Kim         value = *(((uint8_t *)&u->flags) + idn);
622329f1662SJeuk Kim         value = !value;
623329f1662SJeuk Kim     } else {
624329f1662SJeuk Kim         trace_ufs_err_query_invalid_opcode(op);
625329f1662SJeuk Kim         return UFS_QUERY_RESULT_INVALID_OPCODE;
626329f1662SJeuk Kim     }
627329f1662SJeuk Kim 
628329f1662SJeuk Kim     *(((uint8_t *)&u->flags) + idn) = value;
629329f1662SJeuk Kim     req->rsp_upiu.qr.value = cpu_to_be32(value);
630329f1662SJeuk Kim     return UFS_QUERY_RESULT_SUCCESS;
631329f1662SJeuk Kim }
632329f1662SJeuk Kim 
ufs_read_attr_value(UfsHc * u,uint8_t idn)633329f1662SJeuk Kim static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
634329f1662SJeuk Kim {
635329f1662SJeuk Kim     switch (idn) {
636329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_BOOT_LU_EN:
637329f1662SJeuk Kim         return u->attributes.boot_lun_en;
638329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_POWER_MODE:
639329f1662SJeuk Kim         return u->attributes.current_power_mode;
640329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
641329f1662SJeuk Kim         return u->attributes.active_icc_level;
642329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_OOO_DATA_EN:
643329f1662SJeuk Kim         return u->attributes.out_of_order_data_en;
644329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_BKOPS_STATUS:
645329f1662SJeuk Kim         return u->attributes.background_op_status;
646329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_PURGE_STATUS:
647329f1662SJeuk Kim         return u->attributes.purge_status;
648329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_MAX_DATA_IN:
649329f1662SJeuk Kim         return u->attributes.max_data_in_size;
650329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_MAX_DATA_OUT:
651329f1662SJeuk Kim         return u->attributes.max_data_out_size;
652329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED:
653329f1662SJeuk Kim         return be32_to_cpu(u->attributes.dyn_cap_needed);
654329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_REF_CLK_FREQ:
655329f1662SJeuk Kim         return u->attributes.ref_clk_freq;
656329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK:
657329f1662SJeuk Kim         return u->attributes.config_descr_lock;
658329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
659329f1662SJeuk Kim         return u->attributes.max_num_of_rtt;
660329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_EE_CONTROL:
661329f1662SJeuk Kim         return be16_to_cpu(u->attributes.exception_event_control);
662329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_EE_STATUS:
663329f1662SJeuk Kim         return be16_to_cpu(u->attributes.exception_event_status);
664329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_SECONDS_PASSED:
665329f1662SJeuk Kim         return be32_to_cpu(u->attributes.seconds_passed);
666329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_CNTX_CONF:
667329f1662SJeuk Kim         return be16_to_cpu(u->attributes.context_conf);
668329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_FFU_STATUS:
669329f1662SJeuk Kim         return u->attributes.device_ffu_status;
670329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_PSA_STATE:
671329f1662SJeuk Kim         return be32_to_cpu(u->attributes.psa_state);
672329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE:
673329f1662SJeuk Kim         return be32_to_cpu(u->attributes.psa_data_size);
674329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME:
675329f1662SJeuk Kim         return u->attributes.ref_clk_gating_wait_time;
676329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP:
677329f1662SJeuk Kim         return u->attributes.device_case_rough_temperaure;
678329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND:
679329f1662SJeuk Kim         return u->attributes.device_too_high_temp_boundary;
680329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND:
681329f1662SJeuk Kim         return u->attributes.device_too_low_temp_boundary;
682329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_THROTTLING_STATUS:
683329f1662SJeuk Kim         return u->attributes.throttling_status;
684329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS:
685329f1662SJeuk Kim         return u->attributes.wb_buffer_flush_status;
686329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE:
687329f1662SJeuk Kim         return u->attributes.available_wb_buffer_size;
688329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST:
689329f1662SJeuk Kim         return u->attributes.wb_buffer_life_time_est;
690329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE:
691329f1662SJeuk Kim         return be32_to_cpu(u->attributes.current_wb_buffer_size);
692329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_REFRESH_STATUS:
693329f1662SJeuk Kim         return u->attributes.refresh_status;
694329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_REFRESH_FREQ:
695329f1662SJeuk Kim         return u->attributes.refresh_freq;
696329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_REFRESH_UNIT:
697329f1662SJeuk Kim         return u->attributes.refresh_unit;
698329f1662SJeuk Kim     }
699329f1662SJeuk Kim     return 0;
700329f1662SJeuk Kim }
701329f1662SJeuk Kim 
ufs_write_attr_value(UfsHc * u,uint8_t idn,uint32_t value)702329f1662SJeuk Kim static void ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
703329f1662SJeuk Kim {
704329f1662SJeuk Kim     switch (idn) {
705329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
706329f1662SJeuk Kim         u->attributes.active_icc_level = value;
707329f1662SJeuk Kim         break;
708329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_MAX_DATA_IN:
709329f1662SJeuk Kim         u->attributes.max_data_in_size = value;
710329f1662SJeuk Kim         break;
711329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_MAX_DATA_OUT:
712329f1662SJeuk Kim         u->attributes.max_data_out_size = value;
713329f1662SJeuk Kim         break;
714329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_REF_CLK_FREQ:
715329f1662SJeuk Kim         u->attributes.ref_clk_freq = value;
716329f1662SJeuk Kim         break;
717329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
718329f1662SJeuk Kim         u->attributes.max_num_of_rtt = value;
719329f1662SJeuk Kim         break;
720329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_EE_CONTROL:
721329f1662SJeuk Kim         u->attributes.exception_event_control = cpu_to_be16(value);
722329f1662SJeuk Kim         break;
723329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_SECONDS_PASSED:
724329f1662SJeuk Kim         u->attributes.seconds_passed = cpu_to_be32(value);
725329f1662SJeuk Kim         break;
726329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_PSA_STATE:
727329f1662SJeuk Kim         u->attributes.psa_state = value;
728329f1662SJeuk Kim         break;
729329f1662SJeuk Kim     case UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE:
730329f1662SJeuk Kim         u->attributes.psa_data_size = cpu_to_be32(value);
731329f1662SJeuk Kim         break;
732329f1662SJeuk Kim     }
733329f1662SJeuk Kim }
734329f1662SJeuk Kim 
ufs_exec_query_attr(UfsRequest * req,int op)735329f1662SJeuk Kim static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op)
736329f1662SJeuk Kim {
737329f1662SJeuk Kim     UfsHc *u = req->hc;
738329f1662SJeuk Kim     uint8_t idn = req->req_upiu.qr.idn;
739329f1662SJeuk Kim     uint32_t value;
740329f1662SJeuk Kim     QueryRespCode ret;
741329f1662SJeuk Kim 
742329f1662SJeuk Kim     ret = ufs_attr_check_idn_valid(idn, op);
743329f1662SJeuk Kim     if (ret) {
744329f1662SJeuk Kim         return ret;
745329f1662SJeuk Kim     }
746329f1662SJeuk Kim 
747329f1662SJeuk Kim     if (op == UFS_QUERY_ATTR_READ) {
748329f1662SJeuk Kim         value = ufs_read_attr_value(u, idn);
749329f1662SJeuk Kim     } else {
750329f1662SJeuk Kim         value = be32_to_cpu(req->req_upiu.qr.value);
751329f1662SJeuk Kim         ufs_write_attr_value(u, idn, value);
752329f1662SJeuk Kim     }
753329f1662SJeuk Kim 
754329f1662SJeuk Kim     req->rsp_upiu.qr.value = cpu_to_be32(value);
755329f1662SJeuk Kim     return UFS_QUERY_RESULT_SUCCESS;
756329f1662SJeuk Kim }
757329f1662SJeuk Kim 
758329f1662SJeuk Kim static const RpmbUnitDescriptor rpmb_unit_desc = {
759329f1662SJeuk Kim     .length = sizeof(RpmbUnitDescriptor),
760329f1662SJeuk Kim     .descriptor_idn = 2,
761329f1662SJeuk Kim     .unit_index = UFS_UPIU_RPMB_WLUN,
762329f1662SJeuk Kim     .lu_enable = 0,
763329f1662SJeuk Kim };
764329f1662SJeuk Kim 
ufs_read_unit_desc(UfsRequest * req)765329f1662SJeuk Kim static QueryRespCode ufs_read_unit_desc(UfsRequest *req)
766329f1662SJeuk Kim {
7672a8b36a4SJeuk Kim     UfsHc *u = req->hc;
768329f1662SJeuk Kim     uint8_t lun = req->req_upiu.qr.index;
769329f1662SJeuk Kim 
7702a8b36a4SJeuk Kim     if (lun != UFS_UPIU_RPMB_WLUN &&
77197970daeSJeuk Kim         (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) {
772329f1662SJeuk Kim         trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun);
773329f1662SJeuk Kim         return UFS_QUERY_RESULT_INVALID_INDEX;
774329f1662SJeuk Kim     }
775329f1662SJeuk Kim 
776329f1662SJeuk Kim     if (lun == UFS_UPIU_RPMB_WLUN) {
777329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &rpmb_unit_desc, rpmb_unit_desc.length);
778329f1662SJeuk Kim     } else {
7792a8b36a4SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &u->lus[lun]->unit_desc,
7802a8b36a4SJeuk Kim                sizeof(u->lus[lun]->unit_desc));
781329f1662SJeuk Kim     }
782329f1662SJeuk Kim 
783329f1662SJeuk Kim     return UFS_QUERY_RESULT_SUCCESS;
784329f1662SJeuk Kim }
785329f1662SJeuk Kim 
manufacturer_str_desc(void)786329f1662SJeuk Kim static inline StringDescriptor manufacturer_str_desc(void)
787329f1662SJeuk Kim {
788329f1662SJeuk Kim     StringDescriptor desc = {
789329f1662SJeuk Kim         .length = 0x12,
790329f1662SJeuk Kim         .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
791329f1662SJeuk Kim     };
792329f1662SJeuk Kim     desc.UC[0] = cpu_to_be16('R');
793329f1662SJeuk Kim     desc.UC[1] = cpu_to_be16('E');
794329f1662SJeuk Kim     desc.UC[2] = cpu_to_be16('D');
795329f1662SJeuk Kim     desc.UC[3] = cpu_to_be16('H');
796329f1662SJeuk Kim     desc.UC[4] = cpu_to_be16('A');
797329f1662SJeuk Kim     desc.UC[5] = cpu_to_be16('T');
798329f1662SJeuk Kim     return desc;
799329f1662SJeuk Kim }
800329f1662SJeuk Kim 
product_name_str_desc(void)801329f1662SJeuk Kim static inline StringDescriptor product_name_str_desc(void)
802329f1662SJeuk Kim {
803329f1662SJeuk Kim     StringDescriptor desc = {
804329f1662SJeuk Kim         .length = 0x22,
805329f1662SJeuk Kim         .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
806329f1662SJeuk Kim     };
807329f1662SJeuk Kim     desc.UC[0] = cpu_to_be16('Q');
808329f1662SJeuk Kim     desc.UC[1] = cpu_to_be16('E');
809329f1662SJeuk Kim     desc.UC[2] = cpu_to_be16('M');
810329f1662SJeuk Kim     desc.UC[3] = cpu_to_be16('U');
811329f1662SJeuk Kim     desc.UC[4] = cpu_to_be16(' ');
812329f1662SJeuk Kim     desc.UC[5] = cpu_to_be16('U');
813329f1662SJeuk Kim     desc.UC[6] = cpu_to_be16('F');
814329f1662SJeuk Kim     desc.UC[7] = cpu_to_be16('S');
815329f1662SJeuk Kim     return desc;
816329f1662SJeuk Kim }
817329f1662SJeuk Kim 
product_rev_level_str_desc(void)818329f1662SJeuk Kim static inline StringDescriptor product_rev_level_str_desc(void)
819329f1662SJeuk Kim {
820329f1662SJeuk Kim     StringDescriptor desc = {
821329f1662SJeuk Kim         .length = 0x0a,
822329f1662SJeuk Kim         .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
823329f1662SJeuk Kim     };
824329f1662SJeuk Kim     desc.UC[0] = cpu_to_be16('0');
825329f1662SJeuk Kim     desc.UC[1] = cpu_to_be16('0');
826329f1662SJeuk Kim     desc.UC[2] = cpu_to_be16('0');
827329f1662SJeuk Kim     desc.UC[3] = cpu_to_be16('1');
828329f1662SJeuk Kim     return desc;
829329f1662SJeuk Kim }
830329f1662SJeuk Kim 
831329f1662SJeuk Kim static const StringDescriptor null_str_desc = {
832329f1662SJeuk Kim     .length = 0x02,
833329f1662SJeuk Kim     .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
834329f1662SJeuk Kim };
835329f1662SJeuk Kim 
ufs_read_string_desc(UfsRequest * req)836329f1662SJeuk Kim static QueryRespCode ufs_read_string_desc(UfsRequest *req)
837329f1662SJeuk Kim {
838329f1662SJeuk Kim     UfsHc *u = req->hc;
839329f1662SJeuk Kim     uint8_t index = req->req_upiu.qr.index;
840329f1662SJeuk Kim     StringDescriptor desc;
841329f1662SJeuk Kim 
842329f1662SJeuk Kim     if (index == u->device_desc.manufacturer_name) {
843329f1662SJeuk Kim         desc = manufacturer_str_desc();
844329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
845329f1662SJeuk Kim     } else if (index == u->device_desc.product_name) {
846329f1662SJeuk Kim         desc = product_name_str_desc();
847329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
848329f1662SJeuk Kim     } else if (index == u->device_desc.serial_number) {
849329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
850329f1662SJeuk Kim     } else if (index == u->device_desc.oem_id) {
851329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
852329f1662SJeuk Kim     } else if (index == u->device_desc.product_revision_level) {
853329f1662SJeuk Kim         desc = product_rev_level_str_desc();
854329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
855329f1662SJeuk Kim     } else {
856329f1662SJeuk Kim         trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, index);
857329f1662SJeuk Kim         return UFS_QUERY_RESULT_INVALID_INDEX;
858329f1662SJeuk Kim     }
859329f1662SJeuk Kim     return UFS_QUERY_RESULT_SUCCESS;
860329f1662SJeuk Kim }
861329f1662SJeuk Kim 
interconnect_desc(void)862329f1662SJeuk Kim static inline InterconnectDescriptor interconnect_desc(void)
863329f1662SJeuk Kim {
864329f1662SJeuk Kim     InterconnectDescriptor desc = {
865329f1662SJeuk Kim         .length = sizeof(InterconnectDescriptor),
866329f1662SJeuk Kim         .descriptor_idn = UFS_QUERY_DESC_IDN_INTERCONNECT,
867329f1662SJeuk Kim     };
868329f1662SJeuk Kim     desc.bcd_unipro_version = cpu_to_be16(0x180);
869329f1662SJeuk Kim     desc.bcd_mphy_version = cpu_to_be16(0x410);
870329f1662SJeuk Kim     return desc;
871329f1662SJeuk Kim }
872329f1662SJeuk Kim 
ufs_read_desc(UfsRequest * req)873329f1662SJeuk Kim static QueryRespCode ufs_read_desc(UfsRequest *req)
874329f1662SJeuk Kim {
875329f1662SJeuk Kim     UfsHc *u = req->hc;
876329f1662SJeuk Kim     QueryRespCode status;
877329f1662SJeuk Kim     uint8_t idn = req->req_upiu.qr.idn;
878329f1662SJeuk Kim     uint16_t length = be16_to_cpu(req->req_upiu.qr.length);
879329f1662SJeuk Kim     InterconnectDescriptor desc;
880329f1662SJeuk Kim 
881329f1662SJeuk Kim     switch (idn) {
882329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_DEVICE:
883329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &u->device_desc, sizeof(u->device_desc));
884329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
885329f1662SJeuk Kim         break;
886329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_UNIT:
887329f1662SJeuk Kim         status = ufs_read_unit_desc(req);
888329f1662SJeuk Kim         break;
889329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_GEOMETRY:
890329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &u->geometry_desc,
891329f1662SJeuk Kim                sizeof(u->geometry_desc));
892329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
893329f1662SJeuk Kim         break;
894329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_INTERCONNECT: {
895329f1662SJeuk Kim         desc = interconnect_desc();
896329f1662SJeuk Kim         memcpy(&req->rsp_upiu.qr.data, &desc, sizeof(InterconnectDescriptor));
897329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
898329f1662SJeuk Kim         break;
899329f1662SJeuk Kim     }
900329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_STRING:
901329f1662SJeuk Kim         status = ufs_read_string_desc(req);
902329f1662SJeuk Kim         break;
903329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_POWER:
904329f1662SJeuk Kim         /* mocking of power descriptor is not supported */
905329f1662SJeuk Kim         memset(&req->rsp_upiu.qr.data, 0, sizeof(PowerParametersDescriptor));
906329f1662SJeuk Kim         req->rsp_upiu.qr.data[0] = sizeof(PowerParametersDescriptor);
907329f1662SJeuk Kim         req->rsp_upiu.qr.data[1] = UFS_QUERY_DESC_IDN_POWER;
908329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
909329f1662SJeuk Kim         break;
910329f1662SJeuk Kim     case UFS_QUERY_DESC_IDN_HEALTH:
911329f1662SJeuk Kim         /* mocking of health descriptor is not supported */
912329f1662SJeuk Kim         memset(&req->rsp_upiu.qr.data, 0, sizeof(DeviceHealthDescriptor));
913329f1662SJeuk Kim         req->rsp_upiu.qr.data[0] = sizeof(DeviceHealthDescriptor);
914329f1662SJeuk Kim         req->rsp_upiu.qr.data[1] = UFS_QUERY_DESC_IDN_HEALTH;
915329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
916329f1662SJeuk Kim         break;
917329f1662SJeuk Kim     default:
918329f1662SJeuk Kim         length = 0;
919329f1662SJeuk Kim         trace_ufs_err_query_invalid_idn(req->req_upiu.qr.opcode, idn);
920329f1662SJeuk Kim         status = UFS_QUERY_RESULT_INVALID_IDN;
921329f1662SJeuk Kim     }
922329f1662SJeuk Kim 
923329f1662SJeuk Kim     if (length > req->rsp_upiu.qr.data[0]) {
924329f1662SJeuk Kim         length = req->rsp_upiu.qr.data[0];
925329f1662SJeuk Kim     }
926329f1662SJeuk Kim     req->rsp_upiu.qr.opcode = req->req_upiu.qr.opcode;
927329f1662SJeuk Kim     req->rsp_upiu.qr.idn = req->req_upiu.qr.idn;
928329f1662SJeuk Kim     req->rsp_upiu.qr.index = req->req_upiu.qr.index;
929329f1662SJeuk Kim     req->rsp_upiu.qr.selector = req->req_upiu.qr.selector;
930329f1662SJeuk Kim     req->rsp_upiu.qr.length = cpu_to_be16(length);
931329f1662SJeuk Kim 
932329f1662SJeuk Kim     return status;
933329f1662SJeuk Kim }
934329f1662SJeuk Kim 
ufs_exec_query_read(UfsRequest * req)935329f1662SJeuk Kim static QueryRespCode ufs_exec_query_read(UfsRequest *req)
936329f1662SJeuk Kim {
937329f1662SJeuk Kim     QueryRespCode status;
938329f1662SJeuk Kim     switch (req->req_upiu.qr.opcode) {
939329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_NOP:
940329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
941329f1662SJeuk Kim         break;
942329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_READ_DESC:
943329f1662SJeuk Kim         status = ufs_read_desc(req);
944329f1662SJeuk Kim         break;
945329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_READ_ATTR:
946329f1662SJeuk Kim         status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_READ);
947329f1662SJeuk Kim         break;
948329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_READ_FLAG:
949329f1662SJeuk Kim         status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_READ);
950329f1662SJeuk Kim         break;
951329f1662SJeuk Kim     default:
952329f1662SJeuk Kim         trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
953329f1662SJeuk Kim         status = UFS_QUERY_RESULT_INVALID_OPCODE;
954329f1662SJeuk Kim         break;
955329f1662SJeuk Kim     }
956329f1662SJeuk Kim 
957329f1662SJeuk Kim     return status;
958329f1662SJeuk Kim }
959329f1662SJeuk Kim 
ufs_exec_query_write(UfsRequest * req)960329f1662SJeuk Kim static QueryRespCode ufs_exec_query_write(UfsRequest *req)
961329f1662SJeuk Kim {
962329f1662SJeuk Kim     QueryRespCode status;
963329f1662SJeuk Kim     switch (req->req_upiu.qr.opcode) {
964329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_NOP:
965329f1662SJeuk Kim         status = UFS_QUERY_RESULT_SUCCESS;
966329f1662SJeuk Kim         break;
967329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_WRITE_DESC:
968329f1662SJeuk Kim         /* write descriptor is not supported */
969329f1662SJeuk Kim         status = UFS_QUERY_RESULT_NOT_WRITEABLE;
970329f1662SJeuk Kim         break;
971329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_WRITE_ATTR:
972329f1662SJeuk Kim         status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_WRITE);
973329f1662SJeuk Kim         break;
974329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_SET_FLAG:
975329f1662SJeuk Kim         status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_SET);
976329f1662SJeuk Kim         break;
977329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG:
978329f1662SJeuk Kim         status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_CLEAR);
979329f1662SJeuk Kim         break;
980329f1662SJeuk Kim     case UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG:
981329f1662SJeuk Kim         status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_TOGGLE);
982329f1662SJeuk Kim         break;
983329f1662SJeuk Kim     default:
984329f1662SJeuk Kim         trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
985329f1662SJeuk Kim         status = UFS_QUERY_RESULT_INVALID_OPCODE;
986329f1662SJeuk Kim         break;
987329f1662SJeuk Kim     }
988329f1662SJeuk Kim 
989329f1662SJeuk Kim     return status;
990329f1662SJeuk Kim }
991329f1662SJeuk Kim 
ufs_exec_query_cmd(UfsRequest * req)992329f1662SJeuk Kim static UfsReqResult ufs_exec_query_cmd(UfsRequest *req)
993329f1662SJeuk Kim {
994329f1662SJeuk Kim     uint8_t query_func = req->req_upiu.header.query_func;
995329f1662SJeuk Kim     uint16_t data_segment_length;
996329f1662SJeuk Kim     QueryRespCode status;
997329f1662SJeuk Kim 
998329f1662SJeuk Kim     trace_ufs_exec_query_cmd(req->slot, req->req_upiu.qr.opcode);
999329f1662SJeuk Kim     if (query_func == UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST) {
1000329f1662SJeuk Kim         status = ufs_exec_query_read(req);
1001329f1662SJeuk Kim     } else if (query_func == UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST) {
1002329f1662SJeuk Kim         status = ufs_exec_query_write(req);
1003329f1662SJeuk Kim     } else {
1004329f1662SJeuk Kim         status = UFS_QUERY_RESULT_GENERAL_FAILURE;
1005329f1662SJeuk Kim     }
1006329f1662SJeuk Kim 
1007329f1662SJeuk Kim     data_segment_length = be16_to_cpu(req->rsp_upiu.qr.length);
1008329f1662SJeuk Kim     ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_QUERY_RSP, 0, status, 0,
1009329f1662SJeuk Kim                           data_segment_length);
1010329f1662SJeuk Kim 
1011329f1662SJeuk Kim     if (status != UFS_QUERY_RESULT_SUCCESS) {
1012329f1662SJeuk Kim         return UFS_REQUEST_FAIL;
1013329f1662SJeuk Kim     }
1014329f1662SJeuk Kim     return UFS_REQUEST_SUCCESS;
1015329f1662SJeuk Kim }
1016329f1662SJeuk Kim 
ufs_exec_req(UfsRequest * req)1017329f1662SJeuk Kim static void ufs_exec_req(UfsRequest *req)
1018329f1662SJeuk Kim {
1019329f1662SJeuk Kim     UfsReqResult req_result;
1020329f1662SJeuk Kim 
1021329f1662SJeuk Kim     if (ufs_dma_read_upiu(req)) {
1022329f1662SJeuk Kim         return;
1023329f1662SJeuk Kim     }
1024329f1662SJeuk Kim 
1025329f1662SJeuk Kim     switch (req->req_upiu.header.trans_type) {
1026329f1662SJeuk Kim     case UFS_UPIU_TRANSACTION_NOP_OUT:
1027329f1662SJeuk Kim         req_result = ufs_exec_nop_cmd(req);
1028329f1662SJeuk Kim         break;
1029329f1662SJeuk Kim     case UFS_UPIU_TRANSACTION_COMMAND:
10302a8b36a4SJeuk Kim         req_result = ufs_exec_scsi_cmd(req);
1031329f1662SJeuk Kim         break;
1032329f1662SJeuk Kim     case UFS_UPIU_TRANSACTION_QUERY_REQ:
1033329f1662SJeuk Kim         req_result = ufs_exec_query_cmd(req);
1034329f1662SJeuk Kim         break;
1035329f1662SJeuk Kim     default:
1036329f1662SJeuk Kim         trace_ufs_err_invalid_trans_code(req->slot,
1037329f1662SJeuk Kim                                          req->req_upiu.header.trans_type);
1038329f1662SJeuk Kim         req_result = UFS_REQUEST_FAIL;
1039329f1662SJeuk Kim     }
1040329f1662SJeuk Kim 
10412a8b36a4SJeuk Kim     /*
10422a8b36a4SJeuk Kim      * The ufs_complete_req for scsi commands is handled by the
10432a8b36a4SJeuk Kim      * ufs_scsi_command_complete() callback function. Therefore, to avoid
10442a8b36a4SJeuk Kim      * duplicate processing, ufs_complete_req() is not called for scsi commands.
10452a8b36a4SJeuk Kim      */
10462a8b36a4SJeuk Kim     if (req_result != UFS_REQUEST_NO_COMPLETE) {
1047329f1662SJeuk Kim         ufs_complete_req(req, req_result);
1048329f1662SJeuk Kim     }
10492a8b36a4SJeuk Kim }
1050329f1662SJeuk Kim 
ufs_process_req(void * opaque)1051329f1662SJeuk Kim static void ufs_process_req(void *opaque)
1052329f1662SJeuk Kim {
1053329f1662SJeuk Kim     UfsHc *u = opaque;
1054329f1662SJeuk Kim     UfsRequest *req;
1055329f1662SJeuk Kim     int slot;
1056329f1662SJeuk Kim 
1057329f1662SJeuk Kim     for (slot = 0; slot < u->params.nutrs; slot++) {
1058329f1662SJeuk Kim         req = &u->req_list[slot];
1059329f1662SJeuk Kim 
1060329f1662SJeuk Kim         if (req->state != UFS_REQUEST_READY) {
1061329f1662SJeuk Kim             continue;
1062329f1662SJeuk Kim         }
1063329f1662SJeuk Kim         trace_ufs_process_req(slot);
1064329f1662SJeuk Kim         req->state = UFS_REQUEST_RUNNING;
1065329f1662SJeuk Kim 
1066329f1662SJeuk Kim         ufs_exec_req(req);
1067329f1662SJeuk Kim     }
1068329f1662SJeuk Kim }
1069329f1662SJeuk Kim 
ufs_complete_req(UfsRequest * req,UfsReqResult req_result)1070096434feSJeuk Kim void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
1071329f1662SJeuk Kim {
1072329f1662SJeuk Kim     UfsHc *u = req->hc;
1073329f1662SJeuk Kim     assert(req->state == UFS_REQUEST_RUNNING);
1074329f1662SJeuk Kim 
1075329f1662SJeuk Kim     if (req_result == UFS_REQUEST_SUCCESS) {
1076329f1662SJeuk Kim         req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_SUCCESS);
1077329f1662SJeuk Kim     } else {
1078329f1662SJeuk Kim         req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_CMD_TABLE_ATTR);
1079329f1662SJeuk Kim     }
1080329f1662SJeuk Kim 
1081329f1662SJeuk Kim     trace_ufs_complete_req(req->slot);
1082329f1662SJeuk Kim     req->state = UFS_REQUEST_COMPLETE;
1083329f1662SJeuk Kim     qemu_bh_schedule(u->complete_bh);
1084329f1662SJeuk Kim }
1085329f1662SJeuk Kim 
ufs_clear_req(UfsRequest * req)1086329f1662SJeuk Kim static void ufs_clear_req(UfsRequest *req)
1087329f1662SJeuk Kim {
1088329f1662SJeuk Kim     if (req->sg != NULL) {
1089329f1662SJeuk Kim         qemu_sglist_destroy(req->sg);
1090329f1662SJeuk Kim         g_free(req->sg);
1091329f1662SJeuk Kim         req->sg = NULL;
1092096434feSJeuk Kim         req->data_len = 0;
1093329f1662SJeuk Kim     }
1094329f1662SJeuk Kim 
1095329f1662SJeuk Kim     memset(&req->utrd, 0, sizeof(req->utrd));
1096329f1662SJeuk Kim     memset(&req->req_upiu, 0, sizeof(req->req_upiu));
1097329f1662SJeuk Kim     memset(&req->rsp_upiu, 0, sizeof(req->rsp_upiu));
1098329f1662SJeuk Kim }
1099329f1662SJeuk Kim 
ufs_sendback_req(void * opaque)1100329f1662SJeuk Kim static void ufs_sendback_req(void *opaque)
1101329f1662SJeuk Kim {
1102329f1662SJeuk Kim     UfsHc *u = opaque;
1103329f1662SJeuk Kim     UfsRequest *req;
1104329f1662SJeuk Kim     int slot;
1105329f1662SJeuk Kim 
1106329f1662SJeuk Kim     for (slot = 0; slot < u->params.nutrs; slot++) {
1107329f1662SJeuk Kim         req = &u->req_list[slot];
1108329f1662SJeuk Kim 
1109329f1662SJeuk Kim         if (req->state != UFS_REQUEST_COMPLETE) {
1110329f1662SJeuk Kim             continue;
1111329f1662SJeuk Kim         }
1112329f1662SJeuk Kim 
1113329f1662SJeuk Kim         if (ufs_dma_write_upiu(req)) {
1114329f1662SJeuk Kim             req->state = UFS_REQUEST_ERROR;
1115329f1662SJeuk Kim             continue;
1116329f1662SJeuk Kim         }
1117329f1662SJeuk Kim 
1118329f1662SJeuk Kim         /*
1119329f1662SJeuk Kim          * TODO: UTP Transfer Request Interrupt Aggregation Control is not yet
1120329f1662SJeuk Kim          * supported
1121329f1662SJeuk Kim          */
1122329f1662SJeuk Kim         if (le32_to_cpu(req->utrd.header.dword_2) != UFS_OCS_SUCCESS ||
1123329f1662SJeuk Kim             le32_to_cpu(req->utrd.header.dword_0) & UFS_UTP_REQ_DESC_INT_CMD) {
1124329f1662SJeuk Kim             u->reg.is = FIELD_DP32(u->reg.is, IS, UTRCS, 1);
1125329f1662SJeuk Kim         }
1126329f1662SJeuk Kim 
1127329f1662SJeuk Kim         u->reg.utrldbr &= ~(1 << slot);
1128329f1662SJeuk Kim         u->reg.utrlcnr |= (1 << slot);
1129329f1662SJeuk Kim 
1130329f1662SJeuk Kim         trace_ufs_sendback_req(req->slot);
1131329f1662SJeuk Kim 
1132329f1662SJeuk Kim         ufs_clear_req(req);
1133329f1662SJeuk Kim         req->state = UFS_REQUEST_IDLE;
1134329f1662SJeuk Kim     }
1135329f1662SJeuk Kim 
1136329f1662SJeuk Kim     ufs_irq_check(u);
1137329f1662SJeuk Kim }
1138329f1662SJeuk Kim 
ufs_check_constraints(UfsHc * u,Error ** errp)1139bc4e68d3SJeuk Kim static bool ufs_check_constraints(UfsHc *u, Error **errp)
1140bc4e68d3SJeuk Kim {
1141bc4e68d3SJeuk Kim     if (u->params.nutrs > UFS_MAX_NUTRS) {
1142bc4e68d3SJeuk Kim         error_setg(errp, "nutrs must be less than or equal to %d",
1143bc4e68d3SJeuk Kim                    UFS_MAX_NUTRS);
1144bc4e68d3SJeuk Kim         return false;
1145bc4e68d3SJeuk Kim     }
1146bc4e68d3SJeuk Kim 
1147bc4e68d3SJeuk Kim     if (u->params.nutmrs > UFS_MAX_NUTMRS) {
1148bc4e68d3SJeuk Kim         error_setg(errp, "nutmrs must be less than or equal to %d",
1149bc4e68d3SJeuk Kim                    UFS_MAX_NUTMRS);
1150bc4e68d3SJeuk Kim         return false;
1151bc4e68d3SJeuk Kim     }
1152bc4e68d3SJeuk Kim 
1153bc4e68d3SJeuk Kim     return true;
1154bc4e68d3SJeuk Kim }
1155bc4e68d3SJeuk Kim 
ufs_init_pci(UfsHc * u,PCIDevice * pci_dev)1156bc4e68d3SJeuk Kim static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
1157bc4e68d3SJeuk Kim {
1158bc4e68d3SJeuk Kim     uint8_t *pci_conf = pci_dev->config;
1159bc4e68d3SJeuk Kim 
1160bc4e68d3SJeuk Kim     pci_conf[PCI_INTERRUPT_PIN] = 1;
1161bc4e68d3SJeuk Kim     pci_config_set_prog_interface(pci_conf, 0x1);
1162bc4e68d3SJeuk Kim 
1163bc4e68d3SJeuk Kim     memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
1164bc4e68d3SJeuk Kim                           u->reg_size);
1165bc4e68d3SJeuk Kim     pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
1166bc4e68d3SJeuk Kim     u->irq = pci_allocate_irq(pci_dev);
1167bc4e68d3SJeuk Kim }
1168bc4e68d3SJeuk Kim 
ufs_init_state(UfsHc * u)1169329f1662SJeuk Kim static void ufs_init_state(UfsHc *u)
1170329f1662SJeuk Kim {
1171329f1662SJeuk Kim     u->req_list = g_new0(UfsRequest, u->params.nutrs);
1172329f1662SJeuk Kim 
1173329f1662SJeuk Kim     for (int i = 0; i < u->params.nutrs; i++) {
1174329f1662SJeuk Kim         u->req_list[i].hc = u;
1175329f1662SJeuk Kim         u->req_list[i].slot = i;
1176329f1662SJeuk Kim         u->req_list[i].sg = NULL;
1177329f1662SJeuk Kim         u->req_list[i].state = UFS_REQUEST_IDLE;
1178329f1662SJeuk Kim     }
1179329f1662SJeuk Kim 
1180329f1662SJeuk Kim     u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u,
1181329f1662SJeuk Kim                                          &DEVICE(u)->mem_reentrancy_guard);
1182329f1662SJeuk Kim     u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u,
1183329f1662SJeuk Kim                                          &DEVICE(u)->mem_reentrancy_guard);
1184329f1662SJeuk Kim }
1185329f1662SJeuk Kim 
ufs_init_hc(UfsHc * u)1186bc4e68d3SJeuk Kim static void ufs_init_hc(UfsHc *u)
1187bc4e68d3SJeuk Kim {
1188bc4e68d3SJeuk Kim     uint32_t cap = 0;
1189bc4e68d3SJeuk Kim 
1190bc4e68d3SJeuk Kim     u->reg_size = pow2ceil(sizeof(UfsReg));
1191bc4e68d3SJeuk Kim 
1192bc4e68d3SJeuk Kim     memset(&u->reg, 0, sizeof(u->reg));
1193bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
1194bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, RTT, 2);
1195bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
1196bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, AUTOH8, 0);
1197bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, 64AS, 1);
1198bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, OODDS, 0);
1199bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
1200bc4e68d3SJeuk Kim     cap = FIELD_DP32(cap, CAP, CS, 0);
1201bc4e68d3SJeuk Kim     u->reg.cap = cap;
1202bc4e68d3SJeuk Kim     u->reg.ver = UFS_SPEC_VER;
1203329f1662SJeuk Kim 
1204329f1662SJeuk Kim     memset(&u->device_desc, 0, sizeof(DeviceDescriptor));
1205329f1662SJeuk Kim     u->device_desc.length = sizeof(DeviceDescriptor);
1206329f1662SJeuk Kim     u->device_desc.descriptor_idn = UFS_QUERY_DESC_IDN_DEVICE;
1207329f1662SJeuk Kim     u->device_desc.device_sub_class = 0x01;
1208329f1662SJeuk Kim     u->device_desc.number_lu = 0x00;
1209329f1662SJeuk Kim     u->device_desc.number_wlu = 0x04;
1210329f1662SJeuk Kim     /* TODO: Revisit it when Power Management is implemented */
1211329f1662SJeuk Kim     u->device_desc.init_power_mode = 0x01; /* Active Mode */
1212329f1662SJeuk Kim     u->device_desc.high_priority_lun = 0x7F; /* Same Priority */
1213329f1662SJeuk Kim     u->device_desc.spec_version = cpu_to_be16(UFS_SPEC_VER);
1214329f1662SJeuk Kim     u->device_desc.manufacturer_name = 0x00;
1215329f1662SJeuk Kim     u->device_desc.product_name = 0x01;
1216329f1662SJeuk Kim     u->device_desc.serial_number = 0x02;
1217329f1662SJeuk Kim     u->device_desc.oem_id = 0x03;
1218329f1662SJeuk Kim     u->device_desc.ud_0_base_offset = 0x16;
1219329f1662SJeuk Kim     u->device_desc.ud_config_p_length = 0x1A;
1220329f1662SJeuk Kim     u->device_desc.device_rtt_cap = 0x02;
1221329f1662SJeuk Kim     u->device_desc.queue_depth = u->params.nutrs;
1222329f1662SJeuk Kim     u->device_desc.product_revision_level = 0x04;
1223329f1662SJeuk Kim 
1224329f1662SJeuk Kim     memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
1225329f1662SJeuk Kim     u->geometry_desc.length = sizeof(GeometryDescriptor);
1226329f1662SJeuk Kim     u->geometry_desc.descriptor_idn = UFS_QUERY_DESC_IDN_GEOMETRY;
1227329f1662SJeuk Kim     u->geometry_desc.max_number_lu = (UFS_MAX_LUS == 32) ? 0x1 : 0x0;
1228329f1662SJeuk Kim     u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4KB */
1229329f1662SJeuk Kim     u->geometry_desc.allocation_unit_size = 0x1; /* 4KB */
1230329f1662SJeuk Kim     u->geometry_desc.min_addr_block_size = 0x8; /* 4KB */
1231329f1662SJeuk Kim     u->geometry_desc.max_in_buffer_size = 0x8;
1232329f1662SJeuk Kim     u->geometry_desc.max_out_buffer_size = 0x8;
1233329f1662SJeuk Kim     u->geometry_desc.rpmb_read_write_size = 0x40;
1234329f1662SJeuk Kim     u->geometry_desc.data_ordering =
1235329f1662SJeuk Kim         0x0; /* out-of-order data transfer is not supported */
1236329f1662SJeuk Kim     u->geometry_desc.max_context_id_number = 0x5;
1237329f1662SJeuk Kim     u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001);
1238329f1662SJeuk Kim 
1239329f1662SJeuk Kim     memset(&u->attributes, 0, sizeof(u->attributes));
1240329f1662SJeuk Kim     u->attributes.max_data_in_size = 0x08;
1241329f1662SJeuk Kim     u->attributes.max_data_out_size = 0x08;
1242329f1662SJeuk Kim     u->attributes.ref_clk_freq = 0x01; /* 26 MHz */
1243329f1662SJeuk Kim     /* configure descriptor is not supported */
1244329f1662SJeuk Kim     u->attributes.config_descr_lock = 0x01;
1245329f1662SJeuk Kim     u->attributes.max_num_of_rtt = 0x02;
1246329f1662SJeuk Kim 
1247329f1662SJeuk Kim     memset(&u->flags, 0, sizeof(u->flags));
1248329f1662SJeuk Kim     u->flags.permanently_disable_fw_update = 1;
1249bc4e68d3SJeuk Kim }
1250bc4e68d3SJeuk Kim 
ufs_realize(PCIDevice * pci_dev,Error ** errp)1251bc4e68d3SJeuk Kim static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1252bc4e68d3SJeuk Kim {
1253bc4e68d3SJeuk Kim     UfsHc *u = UFS(pci_dev);
1254bc4e68d3SJeuk Kim 
1255bc4e68d3SJeuk Kim     if (!ufs_check_constraints(u, errp)) {
1256bc4e68d3SJeuk Kim         return;
1257bc4e68d3SJeuk Kim     }
1258bc4e68d3SJeuk Kim 
12592a8b36a4SJeuk Kim     qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev,
12602a8b36a4SJeuk Kim               u->parent_obj.qdev.id);
12612a8b36a4SJeuk Kim 
1262329f1662SJeuk Kim     ufs_init_state(u);
1263bc4e68d3SJeuk Kim     ufs_init_hc(u);
1264bc4e68d3SJeuk Kim     ufs_init_pci(u, pci_dev);
12652a8b36a4SJeuk Kim 
1266096434feSJeuk Kim     ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN);
1267096434feSJeuk Kim     ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN);
1268096434feSJeuk Kim     ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN);
1269096434feSJeuk Kim     ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN);
1270bc4e68d3SJeuk Kim }
1271bc4e68d3SJeuk Kim 
ufs_exit(PCIDevice * pci_dev)1272329f1662SJeuk Kim static void ufs_exit(PCIDevice *pci_dev)
1273329f1662SJeuk Kim {
1274329f1662SJeuk Kim     UfsHc *u = UFS(pci_dev);
1275329f1662SJeuk Kim 
1276329f1662SJeuk Kim     qemu_bh_delete(u->doorbell_bh);
1277329f1662SJeuk Kim     qemu_bh_delete(u->complete_bh);
1278329f1662SJeuk Kim 
1279329f1662SJeuk Kim     for (int i = 0; i < u->params.nutrs; i++) {
1280329f1662SJeuk Kim         ufs_clear_req(&u->req_list[i]);
1281329f1662SJeuk Kim     }
1282329f1662SJeuk Kim     g_free(u->req_list);
1283329f1662SJeuk Kim }
1284329f1662SJeuk Kim 
1285bc4e68d3SJeuk Kim static Property ufs_props[] = {
1286bc4e68d3SJeuk Kim     DEFINE_PROP_STRING("serial", UfsHc, params.serial),
1287bc4e68d3SJeuk Kim     DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
1288bc4e68d3SJeuk Kim     DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
1289bc4e68d3SJeuk Kim     DEFINE_PROP_END_OF_LIST(),
1290bc4e68d3SJeuk Kim };
1291bc4e68d3SJeuk Kim 
1292bc4e68d3SJeuk Kim static const VMStateDescription ufs_vmstate = {
1293bc4e68d3SJeuk Kim     .name = "ufs",
1294bc4e68d3SJeuk Kim     .unmigratable = 1,
1295bc4e68d3SJeuk Kim };
1296bc4e68d3SJeuk Kim 
ufs_class_init(ObjectClass * oc,void * data)1297bc4e68d3SJeuk Kim static void ufs_class_init(ObjectClass *oc, void *data)
1298bc4e68d3SJeuk Kim {
1299bc4e68d3SJeuk Kim     DeviceClass *dc = DEVICE_CLASS(oc);
1300bc4e68d3SJeuk Kim     PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
1301bc4e68d3SJeuk Kim 
1302bc4e68d3SJeuk Kim     pc->realize = ufs_realize;
1303329f1662SJeuk Kim     pc->exit = ufs_exit;
1304bc4e68d3SJeuk Kim     pc->vendor_id = PCI_VENDOR_ID_REDHAT;
1305bc4e68d3SJeuk Kim     pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
1306bc4e68d3SJeuk Kim     pc->class_id = PCI_CLASS_STORAGE_UFS;
1307bc4e68d3SJeuk Kim 
1308bc4e68d3SJeuk Kim     set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1309bc4e68d3SJeuk Kim     dc->desc = "Universal Flash Storage";
1310bc4e68d3SJeuk Kim     device_class_set_props(dc, ufs_props);
1311bc4e68d3SJeuk Kim     dc->vmsd = &ufs_vmstate;
1312bc4e68d3SJeuk Kim }
1313bc4e68d3SJeuk Kim 
ufs_bus_check_address(BusState * qbus,DeviceState * qdev,Error ** errp)13142a8b36a4SJeuk Kim static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev,
13152a8b36a4SJeuk Kim                                   Error **errp)
13162a8b36a4SJeuk Kim {
1317096434feSJeuk Kim     if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) != 0) {
1318096434feSJeuk Kim         error_setg(errp, "%s cannot be connected to ufs-bus",
1319096434feSJeuk Kim                    object_get_typename(OBJECT(qdev)));
13202a8b36a4SJeuk Kim         return false;
13212a8b36a4SJeuk Kim     }
13222a8b36a4SJeuk Kim 
13232a8b36a4SJeuk Kim     return true;
13242a8b36a4SJeuk Kim }
13252a8b36a4SJeuk Kim 
ufs_bus_get_dev_path(DeviceState * dev)1326*80a37b03SAkinobu Mita static char *ufs_bus_get_dev_path(DeviceState *dev)
1327*80a37b03SAkinobu Mita {
1328*80a37b03SAkinobu Mita     BusState *bus = qdev_get_parent_bus(dev);
1329*80a37b03SAkinobu Mita 
1330*80a37b03SAkinobu Mita     return qdev_get_dev_path(bus->parent);
1331*80a37b03SAkinobu Mita }
1332*80a37b03SAkinobu Mita 
ufs_bus_class_init(ObjectClass * class,void * data)13332a8b36a4SJeuk Kim static void ufs_bus_class_init(ObjectClass *class, void *data)
13342a8b36a4SJeuk Kim {
13352a8b36a4SJeuk Kim     BusClass *bc = BUS_CLASS(class);
1336*80a37b03SAkinobu Mita     bc->get_dev_path = ufs_bus_get_dev_path;
13372a8b36a4SJeuk Kim     bc->check_address = ufs_bus_check_address;
13382a8b36a4SJeuk Kim }
13392a8b36a4SJeuk Kim 
1340bc4e68d3SJeuk Kim static const TypeInfo ufs_info = {
1341bc4e68d3SJeuk Kim     .name = TYPE_UFS,
1342bc4e68d3SJeuk Kim     .parent = TYPE_PCI_DEVICE,
1343bc4e68d3SJeuk Kim     .class_init = ufs_class_init,
1344bc4e68d3SJeuk Kim     .instance_size = sizeof(UfsHc),
1345bc4e68d3SJeuk Kim     .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
1346bc4e68d3SJeuk Kim };
1347bc4e68d3SJeuk Kim 
13482a8b36a4SJeuk Kim static const TypeInfo ufs_bus_info = {
13492a8b36a4SJeuk Kim     .name = TYPE_UFS_BUS,
1350096434feSJeuk Kim     .parent = TYPE_BUS,
13512a8b36a4SJeuk Kim     .class_init = ufs_bus_class_init,
13522a8b36a4SJeuk Kim     .class_size = sizeof(UfsBusClass),
13532a8b36a4SJeuk Kim     .instance_size = sizeof(UfsBus),
13542a8b36a4SJeuk Kim };
13552a8b36a4SJeuk Kim 
ufs_register_types(void)1356bc4e68d3SJeuk Kim static void ufs_register_types(void)
1357bc4e68d3SJeuk Kim {
1358bc4e68d3SJeuk Kim     type_register_static(&ufs_info);
13592a8b36a4SJeuk Kim     type_register_static(&ufs_bus_info);
1360bc4e68d3SJeuk Kim }
1361bc4e68d3SJeuk Kim 
1362bc4e68d3SJeuk Kim type_init(ufs_register_types)
1363