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 /**
125c079578SMinwoo Im * Reference Specs: https://www.jedec.org/, 4.0
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
315c079578SMinwoo Im /* The QEMU-UFS device follows spec version 4.0 */
325c079578SMinwoo Im #define UFS_SPEC_VER 0x0400
33bc4e68d3SJeuk Kim #define UFS_MAX_NUTRS 32
34bc4e68d3SJeuk Kim #define UFS_MAX_NUTMRS 8
355c079578SMinwoo Im #define UFS_MCQ_QCFGPTR 2
365c079578SMinwoo Im
375c079578SMinwoo Im static void ufs_exec_req(UfsRequest *req);
385c079578SMinwoo Im static void ufs_clear_req(UfsRequest *req);
395c079578SMinwoo Im
ufs_mcq_reg_addr(UfsHc * u,int qid)405c079578SMinwoo Im static inline uint64_t ufs_mcq_reg_addr(UfsHc *u, int qid)
415c079578SMinwoo Im {
425c079578SMinwoo Im /* Submission Queue MCQ Registers offset (400h) */
435c079578SMinwoo Im return (UFS_MCQ_QCFGPTR * 0x200) + qid * 0x40;
445c079578SMinwoo Im }
455c079578SMinwoo Im
ufs_mcq_op_reg_addr(UfsHc * u,int qid)465c079578SMinwoo Im static inline uint64_t ufs_mcq_op_reg_addr(UfsHc *u, int qid)
475c079578SMinwoo Im {
485c079578SMinwoo Im /* MCQ Operation & Runtime Registers offset (1000h) */
495c079578SMinwoo Im return UFS_MCQ_OPR_START + qid * 48;
505c079578SMinwoo Im }
515c079578SMinwoo Im
ufs_reg_size(UfsHc * u)525c079578SMinwoo Im static inline uint64_t ufs_reg_size(UfsHc *u)
535c079578SMinwoo Im {
545c079578SMinwoo Im /* Total UFS HCI Register size in bytes */
555c079578SMinwoo Im return ufs_mcq_op_reg_addr(u, 0) + sizeof(u->mcq_op_reg);
565c079578SMinwoo Im }
575c079578SMinwoo Im
ufs_is_mcq_reg(UfsHc * u,uint64_t addr,unsigned size)58e12b11f6SMinwoo Im static inline bool ufs_is_mcq_reg(UfsHc *u, uint64_t addr, unsigned size)
595c079578SMinwoo Im {
6050475f15SJeuk Kim uint64_t mcq_reg_addr;
6150475f15SJeuk Kim
6250475f15SJeuk Kim if (!u->params.mcq) {
6350475f15SJeuk Kim return false;
6450475f15SJeuk Kim }
6550475f15SJeuk Kim
6650475f15SJeuk Kim mcq_reg_addr = ufs_mcq_reg_addr(u, 0);
67e12b11f6SMinwoo Im return (addr >= mcq_reg_addr &&
68e12b11f6SMinwoo Im addr + size <= mcq_reg_addr + sizeof(u->mcq_reg));
695c079578SMinwoo Im }
705c079578SMinwoo Im
ufs_is_mcq_op_reg(UfsHc * u,uint64_t addr,unsigned size)71e12b11f6SMinwoo Im static inline bool ufs_is_mcq_op_reg(UfsHc *u, uint64_t addr, unsigned size)
725c079578SMinwoo Im {
7350475f15SJeuk Kim uint64_t mcq_op_reg_addr;
7450475f15SJeuk Kim
7550475f15SJeuk Kim if (!u->params.mcq) {
7650475f15SJeuk Kim return false;
7750475f15SJeuk Kim }
7850475f15SJeuk Kim
7950475f15SJeuk Kim mcq_op_reg_addr = ufs_mcq_op_reg_addr(u, 0);
805c079578SMinwoo Im return (addr >= mcq_op_reg_addr &&
81e12b11f6SMinwoo Im addr + size <= mcq_op_reg_addr + sizeof(u->mcq_op_reg));
825c079578SMinwoo Im }
83bc4e68d3SJeuk Kim
ufs_addr_read(UfsHc * u,hwaddr addr,void * buf,int size)84329f1662SJeuk Kim static MemTxResult ufs_addr_read(UfsHc *u, hwaddr addr, void *buf, int size)
85329f1662SJeuk Kim {
86329f1662SJeuk Kim hwaddr hi = addr + size - 1;
87329f1662SJeuk Kim
88329f1662SJeuk Kim if (hi < addr) {
89329f1662SJeuk Kim return MEMTX_DECODE_ERROR;
90329f1662SJeuk Kim }
91329f1662SJeuk Kim
92329f1662SJeuk Kim if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) {
93329f1662SJeuk Kim return MEMTX_DECODE_ERROR;
94329f1662SJeuk Kim }
95329f1662SJeuk Kim
96329f1662SJeuk Kim return pci_dma_read(PCI_DEVICE(u), addr, buf, size);
97329f1662SJeuk Kim }
98329f1662SJeuk Kim
ufs_addr_write(UfsHc * u,hwaddr addr,const void * buf,int size)99329f1662SJeuk Kim static MemTxResult ufs_addr_write(UfsHc *u, hwaddr addr, const void *buf,
100329f1662SJeuk Kim int size)
101329f1662SJeuk Kim {
102329f1662SJeuk Kim hwaddr hi = addr + size - 1;
103329f1662SJeuk Kim if (hi < addr) {
104329f1662SJeuk Kim return MEMTX_DECODE_ERROR;
105329f1662SJeuk Kim }
106329f1662SJeuk Kim
107329f1662SJeuk Kim if (!FIELD_EX32(u->reg.cap, CAP, 64AS) && (hi >> 32)) {
108329f1662SJeuk Kim return MEMTX_DECODE_ERROR;
109329f1662SJeuk Kim }
110329f1662SJeuk Kim
111329f1662SJeuk Kim return pci_dma_write(PCI_DEVICE(u), addr, buf, size);
112329f1662SJeuk Kim }
113329f1662SJeuk Kim
ufs_get_utrd_addr(UfsHc * u,uint32_t slot)114329f1662SJeuk Kim static inline hwaddr ufs_get_utrd_addr(UfsHc *u, uint32_t slot)
115329f1662SJeuk Kim {
116329f1662SJeuk Kim hwaddr utrl_base_addr = (((hwaddr)u->reg.utrlbau) << 32) + u->reg.utrlba;
117329f1662SJeuk Kim hwaddr utrd_addr = utrl_base_addr + slot * sizeof(UtpTransferReqDesc);
118329f1662SJeuk Kim
119329f1662SJeuk Kim return utrd_addr;
120329f1662SJeuk Kim }
121329f1662SJeuk Kim
ufs_get_req_upiu_base_addr(const UtpTransferReqDesc * utrd)122329f1662SJeuk Kim static inline hwaddr ufs_get_req_upiu_base_addr(const UtpTransferReqDesc *utrd)
123329f1662SJeuk Kim {
124329f1662SJeuk Kim uint32_t cmd_desc_base_addr_lo =
125329f1662SJeuk Kim le32_to_cpu(utrd->command_desc_base_addr_lo);
126329f1662SJeuk Kim uint32_t cmd_desc_base_addr_hi =
127329f1662SJeuk Kim le32_to_cpu(utrd->command_desc_base_addr_hi);
128329f1662SJeuk Kim
129329f1662SJeuk Kim return (((hwaddr)cmd_desc_base_addr_hi) << 32) + cmd_desc_base_addr_lo;
130329f1662SJeuk Kim }
131329f1662SJeuk Kim
ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc * utrd)132329f1662SJeuk Kim static inline hwaddr ufs_get_rsp_upiu_base_addr(const UtpTransferReqDesc *utrd)
133329f1662SJeuk Kim {
134329f1662SJeuk Kim hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(utrd);
135329f1662SJeuk Kim uint32_t rsp_upiu_byte_off =
136329f1662SJeuk Kim le16_to_cpu(utrd->response_upiu_offset) * sizeof(uint32_t);
137329f1662SJeuk Kim return req_upiu_base_addr + rsp_upiu_byte_off;
138329f1662SJeuk Kim }
139329f1662SJeuk Kim
ufs_dma_read_utrd(UfsRequest * req)140329f1662SJeuk Kim static MemTxResult ufs_dma_read_utrd(UfsRequest *req)
141329f1662SJeuk Kim {
142329f1662SJeuk Kim UfsHc *u = req->hc;
143329f1662SJeuk Kim hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
144329f1662SJeuk Kim MemTxResult ret;
145329f1662SJeuk Kim
146329f1662SJeuk Kim ret = ufs_addr_read(u, utrd_addr, &req->utrd, sizeof(req->utrd));
147329f1662SJeuk Kim if (ret) {
148329f1662SJeuk Kim trace_ufs_err_dma_read_utrd(req->slot, utrd_addr);
149329f1662SJeuk Kim }
150329f1662SJeuk Kim return ret;
151329f1662SJeuk Kim }
152329f1662SJeuk Kim
ufs_dma_read_req_upiu(UfsRequest * req)153329f1662SJeuk Kim static MemTxResult ufs_dma_read_req_upiu(UfsRequest *req)
154329f1662SJeuk Kim {
155329f1662SJeuk Kim UfsHc *u = req->hc;
156329f1662SJeuk Kim hwaddr req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd);
157329f1662SJeuk Kim UtpUpiuReq *req_upiu = &req->req_upiu;
158329f1662SJeuk Kim uint32_t copy_size;
159329f1662SJeuk Kim uint16_t data_segment_length;
160329f1662SJeuk Kim MemTxResult ret;
161329f1662SJeuk Kim
162329f1662SJeuk Kim /*
163329f1662SJeuk Kim * To know the size of the req_upiu, we need to read the
164329f1662SJeuk Kim * data_segment_length in the header first.
165329f1662SJeuk Kim */
166329f1662SJeuk Kim ret = ufs_addr_read(u, req_upiu_base_addr, &req_upiu->header,
167329f1662SJeuk Kim sizeof(UtpUpiuHeader));
168329f1662SJeuk Kim if (ret) {
169329f1662SJeuk Kim trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
170329f1662SJeuk Kim return ret;
171329f1662SJeuk Kim }
172329f1662SJeuk Kim data_segment_length = be16_to_cpu(req_upiu->header.data_segment_length);
173329f1662SJeuk Kim
174329f1662SJeuk Kim copy_size = sizeof(UtpUpiuHeader) + UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
175329f1662SJeuk Kim data_segment_length;
176329f1662SJeuk Kim
177f2c8aeb1SJeuk Kim if (copy_size > sizeof(req->req_upiu)) {
178f2c8aeb1SJeuk Kim copy_size = sizeof(req->req_upiu);
179f2c8aeb1SJeuk Kim }
180f2c8aeb1SJeuk Kim
181329f1662SJeuk Kim ret = ufs_addr_read(u, req_upiu_base_addr, &req->req_upiu, copy_size);
182329f1662SJeuk Kim if (ret) {
183329f1662SJeuk Kim trace_ufs_err_dma_read_req_upiu(req->slot, req_upiu_base_addr);
184329f1662SJeuk Kim }
185329f1662SJeuk Kim return ret;
186329f1662SJeuk Kim }
187329f1662SJeuk Kim
ufs_dma_read_prdt(UfsRequest * req)188329f1662SJeuk Kim static MemTxResult ufs_dma_read_prdt(UfsRequest *req)
189329f1662SJeuk Kim {
190329f1662SJeuk Kim UfsHc *u = req->hc;
191329f1662SJeuk Kim uint16_t prdt_len = le16_to_cpu(req->utrd.prd_table_length);
192329f1662SJeuk Kim uint16_t prdt_byte_off =
193329f1662SJeuk Kim le16_to_cpu(req->utrd.prd_table_offset) * sizeof(uint32_t);
194329f1662SJeuk Kim uint32_t prdt_size = prdt_len * sizeof(UfshcdSgEntry);
195329f1662SJeuk Kim g_autofree UfshcdSgEntry *prd_entries = NULL;
196329f1662SJeuk Kim hwaddr req_upiu_base_addr, prdt_base_addr;
197329f1662SJeuk Kim int err;
198329f1662SJeuk Kim
199329f1662SJeuk Kim assert(!req->sg);
200329f1662SJeuk Kim
201329f1662SJeuk Kim if (prdt_size == 0) {
202329f1662SJeuk Kim return MEMTX_OK;
203329f1662SJeuk Kim }
204329f1662SJeuk Kim prd_entries = g_new(UfshcdSgEntry, prdt_size);
205329f1662SJeuk Kim
206329f1662SJeuk Kim req_upiu_base_addr = ufs_get_req_upiu_base_addr(&req->utrd);
207329f1662SJeuk Kim prdt_base_addr = req_upiu_base_addr + prdt_byte_off;
208329f1662SJeuk Kim
209329f1662SJeuk Kim err = ufs_addr_read(u, prdt_base_addr, prd_entries, prdt_size);
210329f1662SJeuk Kim if (err) {
211329f1662SJeuk Kim trace_ufs_err_dma_read_prdt(req->slot, prdt_base_addr);
212329f1662SJeuk Kim return err;
213329f1662SJeuk Kim }
214329f1662SJeuk Kim
215329f1662SJeuk Kim req->sg = g_malloc0(sizeof(QEMUSGList));
216329f1662SJeuk Kim pci_dma_sglist_init(req->sg, PCI_DEVICE(u), prdt_len);
217096434feSJeuk Kim req->data_len = 0;
218329f1662SJeuk Kim
219329f1662SJeuk Kim for (uint16_t i = 0; i < prdt_len; ++i) {
220329f1662SJeuk Kim hwaddr data_dma_addr = le64_to_cpu(prd_entries[i].addr);
221329f1662SJeuk Kim uint32_t data_byte_count = le32_to_cpu(prd_entries[i].size) + 1;
222329f1662SJeuk Kim qemu_sglist_add(req->sg, data_dma_addr, data_byte_count);
223096434feSJeuk Kim req->data_len += data_byte_count;
224329f1662SJeuk Kim }
225329f1662SJeuk Kim return MEMTX_OK;
226329f1662SJeuk Kim }
227329f1662SJeuk Kim
ufs_dma_read_upiu(UfsRequest * req)228329f1662SJeuk Kim static MemTxResult ufs_dma_read_upiu(UfsRequest *req)
229329f1662SJeuk Kim {
230329f1662SJeuk Kim MemTxResult ret;
231329f1662SJeuk Kim
2325c079578SMinwoo Im /*
2335c079578SMinwoo Im * In case of MCQ, UTRD has already been read from a SQ, so skip it.
2345c079578SMinwoo Im */
2355c079578SMinwoo Im if (!ufs_mcq_req(req)) {
236329f1662SJeuk Kim ret = ufs_dma_read_utrd(req);
237329f1662SJeuk Kim if (ret) {
238329f1662SJeuk Kim return ret;
239329f1662SJeuk Kim }
2405c079578SMinwoo Im }
241329f1662SJeuk Kim
242329f1662SJeuk Kim ret = ufs_dma_read_req_upiu(req);
243329f1662SJeuk Kim if (ret) {
244329f1662SJeuk Kim return ret;
245329f1662SJeuk Kim }
246329f1662SJeuk Kim
247329f1662SJeuk Kim ret = ufs_dma_read_prdt(req);
248329f1662SJeuk Kim if (ret) {
249329f1662SJeuk Kim return ret;
250329f1662SJeuk Kim }
251329f1662SJeuk Kim
252329f1662SJeuk Kim return 0;
253329f1662SJeuk Kim }
254329f1662SJeuk Kim
ufs_dma_write_utrd(UfsRequest * req)255329f1662SJeuk Kim static MemTxResult ufs_dma_write_utrd(UfsRequest *req)
256329f1662SJeuk Kim {
257329f1662SJeuk Kim UfsHc *u = req->hc;
258329f1662SJeuk Kim hwaddr utrd_addr = ufs_get_utrd_addr(u, req->slot);
259329f1662SJeuk Kim MemTxResult ret;
260329f1662SJeuk Kim
261329f1662SJeuk Kim ret = ufs_addr_write(u, utrd_addr, &req->utrd, sizeof(req->utrd));
262329f1662SJeuk Kim if (ret) {
263329f1662SJeuk Kim trace_ufs_err_dma_write_utrd(req->slot, utrd_addr);
264329f1662SJeuk Kim }
265329f1662SJeuk Kim return ret;
266329f1662SJeuk Kim }
267329f1662SJeuk Kim
ufs_dma_write_rsp_upiu(UfsRequest * req)268329f1662SJeuk Kim static MemTxResult ufs_dma_write_rsp_upiu(UfsRequest *req)
269329f1662SJeuk Kim {
270329f1662SJeuk Kim UfsHc *u = req->hc;
271329f1662SJeuk Kim hwaddr rsp_upiu_base_addr = ufs_get_rsp_upiu_base_addr(&req->utrd);
272329f1662SJeuk Kim uint32_t rsp_upiu_byte_len =
273329f1662SJeuk Kim le16_to_cpu(req->utrd.response_upiu_length) * sizeof(uint32_t);
274329f1662SJeuk Kim uint16_t data_segment_length =
275329f1662SJeuk Kim be16_to_cpu(req->rsp_upiu.header.data_segment_length);
276329f1662SJeuk Kim uint32_t copy_size = sizeof(UtpUpiuHeader) +
277329f1662SJeuk Kim UFS_TRANSACTION_SPECIFIC_FIELD_SIZE +
278329f1662SJeuk Kim data_segment_length;
279329f1662SJeuk Kim MemTxResult ret;
280329f1662SJeuk Kim
281329f1662SJeuk Kim if (copy_size > rsp_upiu_byte_len) {
282329f1662SJeuk Kim copy_size = rsp_upiu_byte_len;
283329f1662SJeuk Kim }
284329f1662SJeuk Kim
285f2c8aeb1SJeuk Kim if (copy_size > sizeof(req->rsp_upiu)) {
286f2c8aeb1SJeuk Kim copy_size = sizeof(req->rsp_upiu);
287f2c8aeb1SJeuk Kim }
288f2c8aeb1SJeuk Kim
289329f1662SJeuk Kim ret = ufs_addr_write(u, rsp_upiu_base_addr, &req->rsp_upiu, copy_size);
290329f1662SJeuk Kim if (ret) {
291329f1662SJeuk Kim trace_ufs_err_dma_write_rsp_upiu(req->slot, rsp_upiu_base_addr);
292329f1662SJeuk Kim }
293329f1662SJeuk Kim return ret;
294329f1662SJeuk Kim }
295329f1662SJeuk Kim
ufs_dma_write_upiu(UfsRequest * req)296329f1662SJeuk Kim static MemTxResult ufs_dma_write_upiu(UfsRequest *req)
297329f1662SJeuk Kim {
298329f1662SJeuk Kim MemTxResult ret;
299329f1662SJeuk Kim
300329f1662SJeuk Kim ret = ufs_dma_write_rsp_upiu(req);
301329f1662SJeuk Kim if (ret) {
302329f1662SJeuk Kim return ret;
303329f1662SJeuk Kim }
304329f1662SJeuk Kim
305329f1662SJeuk Kim return ufs_dma_write_utrd(req);
306329f1662SJeuk Kim }
307329f1662SJeuk Kim
ufs_irq_check(UfsHc * u)308bc4e68d3SJeuk Kim static void ufs_irq_check(UfsHc *u)
309bc4e68d3SJeuk Kim {
310bc4e68d3SJeuk Kim PCIDevice *pci = PCI_DEVICE(u);
311bc4e68d3SJeuk Kim
312bc4e68d3SJeuk Kim if ((u->reg.is & UFS_INTR_MASK) & u->reg.ie) {
313bc4e68d3SJeuk Kim trace_ufs_irq_raise();
314bc4e68d3SJeuk Kim pci_irq_assert(pci);
315bc4e68d3SJeuk Kim } else {
316bc4e68d3SJeuk Kim trace_ufs_irq_lower();
317bc4e68d3SJeuk Kim pci_irq_deassert(pci);
318bc4e68d3SJeuk Kim }
319bc4e68d3SJeuk Kim }
320bc4e68d3SJeuk Kim
ufs_process_db(UfsHc * u,uint32_t val)321329f1662SJeuk Kim static void ufs_process_db(UfsHc *u, uint32_t val)
322329f1662SJeuk Kim {
32397970daeSJeuk Kim DECLARE_BITMAP(doorbell, UFS_MAX_NUTRS);
324329f1662SJeuk Kim uint32_t slot;
325329f1662SJeuk Kim uint32_t nutrs = u->params.nutrs;
326329f1662SJeuk Kim UfsRequest *req;
327329f1662SJeuk Kim
328329f1662SJeuk Kim val &= ~u->reg.utrldbr;
329329f1662SJeuk Kim if (!val) {
330329f1662SJeuk Kim return;
331329f1662SJeuk Kim }
332329f1662SJeuk Kim
33397970daeSJeuk Kim doorbell[0] = val;
33497970daeSJeuk Kim slot = find_first_bit(doorbell, nutrs);
335329f1662SJeuk Kim
336329f1662SJeuk Kim while (slot < nutrs) {
337329f1662SJeuk Kim req = &u->req_list[slot];
338329f1662SJeuk Kim if (req->state == UFS_REQUEST_ERROR) {
339329f1662SJeuk Kim trace_ufs_err_utrl_slot_error(req->slot);
340329f1662SJeuk Kim return;
341329f1662SJeuk Kim }
342329f1662SJeuk Kim
343329f1662SJeuk Kim if (req->state != UFS_REQUEST_IDLE) {
344329f1662SJeuk Kim trace_ufs_err_utrl_slot_busy(req->slot);
345329f1662SJeuk Kim return;
346329f1662SJeuk Kim }
347329f1662SJeuk Kim
348329f1662SJeuk Kim trace_ufs_process_db(slot);
349329f1662SJeuk Kim req->state = UFS_REQUEST_READY;
35097970daeSJeuk Kim slot = find_next_bit(doorbell, nutrs, slot + 1);
351329f1662SJeuk Kim }
352329f1662SJeuk Kim
353329f1662SJeuk Kim qemu_bh_schedule(u->doorbell_bh);
354329f1662SJeuk Kim }
355329f1662SJeuk Kim
ufs_process_uiccmd(UfsHc * u,uint32_t val)356bc4e68d3SJeuk Kim static void ufs_process_uiccmd(UfsHc *u, uint32_t val)
357bc4e68d3SJeuk Kim {
358bc4e68d3SJeuk Kim trace_ufs_process_uiccmd(val, u->reg.ucmdarg1, u->reg.ucmdarg2,
359bc4e68d3SJeuk Kim u->reg.ucmdarg3);
360bc4e68d3SJeuk Kim /*
361bc4e68d3SJeuk Kim * Only the essential uic commands for running drivers on Linux and Windows
362bc4e68d3SJeuk Kim * are implemented.
363bc4e68d3SJeuk Kim */
364bc4e68d3SJeuk Kim switch (val) {
365bc4e68d3SJeuk Kim case UFS_UIC_CMD_DME_LINK_STARTUP:
366bc4e68d3SJeuk Kim u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, DP, 1);
367bc4e68d3SJeuk Kim u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTRLRDY, 1);
368bc4e68d3SJeuk Kim u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UTMRLRDY, 1);
369bc4e68d3SJeuk Kim u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
370bc4e68d3SJeuk Kim break;
371bc4e68d3SJeuk Kim /* TODO: Revisit it when Power Management is implemented */
372bc4e68d3SJeuk Kim case UFS_UIC_CMD_DME_HIBER_ENTER:
373bc4e68d3SJeuk Kim u->reg.is = FIELD_DP32(u->reg.is, IS, UHES, 1);
374bc4e68d3SJeuk Kim u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL);
375bc4e68d3SJeuk Kim u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
376bc4e68d3SJeuk Kim break;
377bc4e68d3SJeuk Kim case UFS_UIC_CMD_DME_HIBER_EXIT:
378bc4e68d3SJeuk Kim u->reg.is = FIELD_DP32(u->reg.is, IS, UHXS, 1);
379bc4e68d3SJeuk Kim u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UPMCRS, UFS_PWR_LOCAL);
380bc4e68d3SJeuk Kim u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_SUCCESS;
381bc4e68d3SJeuk Kim break;
382bc4e68d3SJeuk Kim default:
383bc4e68d3SJeuk Kim u->reg.ucmdarg2 = UFS_UIC_CMD_RESULT_FAILURE;
384bc4e68d3SJeuk Kim }
385bc4e68d3SJeuk Kim
386bc4e68d3SJeuk Kim u->reg.is = FIELD_DP32(u->reg.is, IS, UCCS, 1);
387bc4e68d3SJeuk Kim
388bc4e68d3SJeuk Kim ufs_irq_check(u);
389bc4e68d3SJeuk Kim }
390bc4e68d3SJeuk Kim
ufs_mcq_init_req(UfsHc * u,UfsRequest * req,UfsSq * sq)3915c079578SMinwoo Im static void ufs_mcq_init_req(UfsHc *u, UfsRequest *req, UfsSq *sq)
3925c079578SMinwoo Im {
3935c079578SMinwoo Im memset(req, 0, sizeof(*req));
3945c079578SMinwoo Im
3955c079578SMinwoo Im req->hc = u;
3965c079578SMinwoo Im req->state = UFS_REQUEST_IDLE;
3975c079578SMinwoo Im req->slot = UFS_INVALID_SLOT;
3985c079578SMinwoo Im req->sq = sq;
3995c079578SMinwoo Im }
4005c079578SMinwoo Im
ufs_mcq_process_sq(void * opaque)4015c079578SMinwoo Im static void ufs_mcq_process_sq(void *opaque)
4025c079578SMinwoo Im {
4035c079578SMinwoo Im UfsSq *sq = opaque;
4045c079578SMinwoo Im UfsHc *u = sq->u;
4055c079578SMinwoo Im UfsSqEntry sqe;
4065c079578SMinwoo Im UfsRequest *req;
4075c079578SMinwoo Im hwaddr addr;
4085c079578SMinwoo Im uint16_t head = ufs_mcq_sq_head(u, sq->sqid);
4095c079578SMinwoo Im int err;
4105c079578SMinwoo Im
4115c079578SMinwoo Im while (!(ufs_mcq_sq_empty(u, sq->sqid) || QTAILQ_EMPTY(&sq->req_list))) {
4125c079578SMinwoo Im addr = sq->addr + head;
4135c079578SMinwoo Im err = ufs_addr_read(sq->u, addr, (void *)&sqe, sizeof(sqe));
4145c079578SMinwoo Im if (err) {
4155c079578SMinwoo Im trace_ufs_err_dma_read_sq(sq->sqid, addr);
4165c079578SMinwoo Im return;
4175c079578SMinwoo Im }
4185c079578SMinwoo Im
4195c079578SMinwoo Im head = (head + sizeof(sqe)) % (sq->size * sizeof(sqe));
4205c079578SMinwoo Im ufs_mcq_update_sq_head(u, sq->sqid, head);
4215c079578SMinwoo Im
4225c079578SMinwoo Im req = QTAILQ_FIRST(&sq->req_list);
4235c079578SMinwoo Im QTAILQ_REMOVE(&sq->req_list, req, entry);
4245c079578SMinwoo Im
4255c079578SMinwoo Im ufs_mcq_init_req(sq->u, req, sq);
4265c079578SMinwoo Im memcpy(&req->utrd, &sqe, sizeof(req->utrd));
4275c079578SMinwoo Im
4285c079578SMinwoo Im req->state = UFS_REQUEST_RUNNING;
4295c079578SMinwoo Im ufs_exec_req(req);
4305c079578SMinwoo Im }
4315c079578SMinwoo Im }
4325c079578SMinwoo Im
ufs_mcq_process_cq(void * opaque)4335c079578SMinwoo Im static void ufs_mcq_process_cq(void *opaque)
4345c079578SMinwoo Im {
4355c079578SMinwoo Im UfsCq *cq = opaque;
4365c079578SMinwoo Im UfsHc *u = cq->u;
4375c079578SMinwoo Im UfsRequest *req, *next;
4385c079578SMinwoo Im MemTxResult ret;
4395c079578SMinwoo Im uint32_t tail = ufs_mcq_cq_tail(u, cq->cqid);
4405c079578SMinwoo Im
4415c079578SMinwoo Im QTAILQ_FOREACH_SAFE(req, &cq->req_list, entry, next)
4425c079578SMinwoo Im {
4435c079578SMinwoo Im ufs_dma_write_rsp_upiu(req);
4445c079578SMinwoo Im
4455c079578SMinwoo Im req->cqe.utp_addr =
4465c079578SMinwoo Im ((uint64_t)req->utrd.command_desc_base_addr_hi << 32ULL) |
4475c079578SMinwoo Im req->utrd.command_desc_base_addr_lo;
4485c079578SMinwoo Im req->cqe.utp_addr |= req->sq->sqid;
4495c079578SMinwoo Im req->cqe.resp_len = req->utrd.response_upiu_length;
4505c079578SMinwoo Im req->cqe.resp_off = req->utrd.response_upiu_offset;
4515c079578SMinwoo Im req->cqe.prdt_len = req->utrd.prd_table_length;
4525c079578SMinwoo Im req->cqe.prdt_off = req->utrd.prd_table_offset;
4535c079578SMinwoo Im req->cqe.status = req->utrd.header.dword_2 & 0xf;
4545c079578SMinwoo Im req->cqe.error = 0;
4555c079578SMinwoo Im
4565c079578SMinwoo Im ret = ufs_addr_write(u, cq->addr + tail, &req->cqe, sizeof(req->cqe));
4575c079578SMinwoo Im if (ret) {
4585c079578SMinwoo Im trace_ufs_err_dma_write_cq(cq->cqid, cq->addr + tail);
4595c079578SMinwoo Im }
4605c079578SMinwoo Im QTAILQ_REMOVE(&cq->req_list, req, entry);
4615c079578SMinwoo Im
4625c079578SMinwoo Im tail = (tail + sizeof(req->cqe)) % (cq->size * sizeof(req->cqe));
4635c079578SMinwoo Im ufs_mcq_update_cq_tail(u, cq->cqid, tail);
4645c079578SMinwoo Im
4655c079578SMinwoo Im ufs_clear_req(req);
4665c079578SMinwoo Im QTAILQ_INSERT_TAIL(&req->sq->req_list, req, entry);
4675c079578SMinwoo Im }
4685c079578SMinwoo Im
4695c079578SMinwoo Im if (!ufs_mcq_cq_empty(u, cq->cqid)) {
4705c079578SMinwoo Im u->mcq_op_reg[cq->cqid].cq_int.is =
4715c079578SMinwoo Im FIELD_DP32(u->mcq_op_reg[cq->cqid].cq_int.is, CQIS, TEPS, 1);
4725c079578SMinwoo Im
4735c079578SMinwoo Im u->reg.is = FIELD_DP32(u->reg.is, IS, CQES, 1);
4745c079578SMinwoo Im ufs_irq_check(u);
4755c079578SMinwoo Im }
4765c079578SMinwoo Im }
4775c079578SMinwoo Im
ufs_mcq_create_sq(UfsHc * u,uint8_t qid,uint32_t attr)4785c079578SMinwoo Im static bool ufs_mcq_create_sq(UfsHc *u, uint8_t qid, uint32_t attr)
4795c079578SMinwoo Im {
4805c079578SMinwoo Im UfsMcqReg *reg = &u->mcq_reg[qid];
4815c079578SMinwoo Im UfsSq *sq;
4825c079578SMinwoo Im uint8_t cqid = FIELD_EX32(attr, SQATTR, CQID);
4835c079578SMinwoo Im
4845c079578SMinwoo Im if (qid >= u->params.mcq_maxq) {
4855c079578SMinwoo Im trace_ufs_err_mcq_create_sq_invalid_sqid(qid);
4865c079578SMinwoo Im return false;
4875c079578SMinwoo Im }
4885c079578SMinwoo Im
4895c079578SMinwoo Im if (u->sq[qid]) {
4905c079578SMinwoo Im trace_ufs_err_mcq_create_sq_already_exists(qid);
4915c079578SMinwoo Im return false;
4925c079578SMinwoo Im }
4935c079578SMinwoo Im
4945c079578SMinwoo Im if (!u->cq[cqid]) {
4955c079578SMinwoo Im trace_ufs_err_mcq_create_sq_invalid_cqid(qid);
4965c079578SMinwoo Im return false;
4975c079578SMinwoo Im }
4985c079578SMinwoo Im
4995c079578SMinwoo Im sq = g_malloc0(sizeof(*sq));
5005c079578SMinwoo Im sq->u = u;
5015c079578SMinwoo Im sq->sqid = qid;
5025c079578SMinwoo Im sq->cq = u->cq[cqid];
5035c079578SMinwoo Im sq->addr = ((uint64_t)reg->squba << 32) | reg->sqlba;
5045c079578SMinwoo Im sq->size = ((FIELD_EX32(attr, SQATTR, SIZE) + 1) << 2) / sizeof(UfsSqEntry);
5055c079578SMinwoo Im
5065c079578SMinwoo Im sq->bh = qemu_bh_new_guarded(ufs_mcq_process_sq, sq,
5075c079578SMinwoo Im &DEVICE(u)->mem_reentrancy_guard);
5085c079578SMinwoo Im sq->req = g_new0(UfsRequest, sq->size);
5095c079578SMinwoo Im QTAILQ_INIT(&sq->req_list);
5105c079578SMinwoo Im for (int i = 0; i < sq->size; i++) {
5115c079578SMinwoo Im ufs_mcq_init_req(u, &sq->req[i], sq);
5125c079578SMinwoo Im QTAILQ_INSERT_TAIL(&sq->req_list, &sq->req[i], entry);
5135c079578SMinwoo Im }
5145c079578SMinwoo Im
5155c079578SMinwoo Im u->sq[qid] = sq;
5165c079578SMinwoo Im
5175c079578SMinwoo Im trace_ufs_mcq_create_sq(sq->sqid, sq->cq->cqid, sq->addr, sq->size);
5185c079578SMinwoo Im return true;
5195c079578SMinwoo Im }
5205c079578SMinwoo Im
ufs_mcq_delete_sq(UfsHc * u,uint8_t qid)5215c079578SMinwoo Im static bool ufs_mcq_delete_sq(UfsHc *u, uint8_t qid)
5225c079578SMinwoo Im {
5235c079578SMinwoo Im UfsSq *sq;
5245c079578SMinwoo Im
5255c079578SMinwoo Im if (qid >= u->params.mcq_maxq) {
5265c079578SMinwoo Im trace_ufs_err_mcq_delete_sq_invalid_sqid(qid);
5275c079578SMinwoo Im return false;
5285c079578SMinwoo Im }
5295c079578SMinwoo Im
5305c079578SMinwoo Im if (!u->sq[qid]) {
5315c079578SMinwoo Im trace_ufs_err_mcq_delete_sq_not_exists(qid);
5325c079578SMinwoo Im return false;
5335c079578SMinwoo Im }
5345c079578SMinwoo Im
5355c079578SMinwoo Im sq = u->sq[qid];
5365c079578SMinwoo Im
5375c079578SMinwoo Im qemu_bh_delete(sq->bh);
5385c079578SMinwoo Im g_free(sq->req);
5395c079578SMinwoo Im g_free(sq);
5405c079578SMinwoo Im u->sq[qid] = NULL;
5415c079578SMinwoo Im return true;
5425c079578SMinwoo Im }
5435c079578SMinwoo Im
ufs_mcq_create_cq(UfsHc * u,uint8_t qid,uint32_t attr)5445c079578SMinwoo Im static bool ufs_mcq_create_cq(UfsHc *u, uint8_t qid, uint32_t attr)
5455c079578SMinwoo Im {
5465c079578SMinwoo Im UfsMcqReg *reg = &u->mcq_reg[qid];
5475c079578SMinwoo Im UfsCq *cq;
5485c079578SMinwoo Im
5495c079578SMinwoo Im if (qid >= u->params.mcq_maxq) {
5505c079578SMinwoo Im trace_ufs_err_mcq_create_cq_invalid_cqid(qid);
5515c079578SMinwoo Im return false;
5525c079578SMinwoo Im }
5535c079578SMinwoo Im
5545c079578SMinwoo Im if (u->cq[qid]) {
5555c079578SMinwoo Im trace_ufs_err_mcq_create_cq_already_exists(qid);
5565c079578SMinwoo Im return false;
5575c079578SMinwoo Im }
5585c079578SMinwoo Im
5595c079578SMinwoo Im cq = g_malloc0(sizeof(*cq));
5605c079578SMinwoo Im cq->u = u;
5615c079578SMinwoo Im cq->cqid = qid;
5625c079578SMinwoo Im cq->addr = ((uint64_t)reg->cquba << 32) | reg->cqlba;
5635c079578SMinwoo Im cq->size = ((FIELD_EX32(attr, CQATTR, SIZE) + 1) << 2) / sizeof(UfsCqEntry);
5645c079578SMinwoo Im
5655c079578SMinwoo Im cq->bh = qemu_bh_new_guarded(ufs_mcq_process_cq, cq,
5665c079578SMinwoo Im &DEVICE(u)->mem_reentrancy_guard);
5675c079578SMinwoo Im QTAILQ_INIT(&cq->req_list);
5685c079578SMinwoo Im
5695c079578SMinwoo Im u->cq[qid] = cq;
5705c079578SMinwoo Im
5715c079578SMinwoo Im trace_ufs_mcq_create_cq(cq->cqid, cq->addr, cq->size);
5725c079578SMinwoo Im return true;
5735c079578SMinwoo Im }
5745c079578SMinwoo Im
ufs_mcq_delete_cq(UfsHc * u,uint8_t qid)5755c079578SMinwoo Im static bool ufs_mcq_delete_cq(UfsHc *u, uint8_t qid)
5765c079578SMinwoo Im {
5775c079578SMinwoo Im UfsCq *cq;
5785c079578SMinwoo Im
5795c079578SMinwoo Im if (qid >= u->params.mcq_maxq) {
5805c079578SMinwoo Im trace_ufs_err_mcq_delete_cq_invalid_cqid(qid);
5815c079578SMinwoo Im return false;
5825c079578SMinwoo Im }
5835c079578SMinwoo Im
5845c079578SMinwoo Im if (!u->cq[qid]) {
5855c079578SMinwoo Im trace_ufs_err_mcq_delete_cq_not_exists(qid);
5865c079578SMinwoo Im return false;
5875c079578SMinwoo Im }
5885c079578SMinwoo Im
5895c079578SMinwoo Im for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
5905c079578SMinwoo Im if (u->sq[i] && u->sq[i]->cq->cqid == qid) {
5915c079578SMinwoo Im trace_ufs_err_mcq_delete_cq_sq_not_deleted(i, qid);
5925c079578SMinwoo Im return false;
5935c079578SMinwoo Im }
5945c079578SMinwoo Im }
5955c079578SMinwoo Im
5965c079578SMinwoo Im cq = u->cq[qid];
5975c079578SMinwoo Im
5985c079578SMinwoo Im qemu_bh_delete(cq->bh);
5995c079578SMinwoo Im g_free(cq);
6005c079578SMinwoo Im u->cq[qid] = NULL;
6015c079578SMinwoo Im return true;
6025c079578SMinwoo Im }
6035c079578SMinwoo Im
ufs_write_reg(UfsHc * u,hwaddr offset,uint32_t data,unsigned size)604bc4e68d3SJeuk Kim static void ufs_write_reg(UfsHc *u, hwaddr offset, uint32_t data, unsigned size)
605bc4e68d3SJeuk Kim {
606bc4e68d3SJeuk Kim switch (offset) {
607bc4e68d3SJeuk Kim case A_IS:
608bc4e68d3SJeuk Kim u->reg.is &= ~data;
609bc4e68d3SJeuk Kim ufs_irq_check(u);
610bc4e68d3SJeuk Kim break;
611bc4e68d3SJeuk Kim case A_IE:
612bc4e68d3SJeuk Kim u->reg.ie = data;
613bc4e68d3SJeuk Kim ufs_irq_check(u);
614bc4e68d3SJeuk Kim break;
615bc4e68d3SJeuk Kim case A_HCE:
616bc4e68d3SJeuk Kim if (!FIELD_EX32(u->reg.hce, HCE, HCE) && FIELD_EX32(data, HCE, HCE)) {
617bc4e68d3SJeuk Kim u->reg.hcs = FIELD_DP32(u->reg.hcs, HCS, UCRDY, 1);
618bc4e68d3SJeuk Kim u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 1);
619bc4e68d3SJeuk Kim } else if (FIELD_EX32(u->reg.hce, HCE, HCE) &&
620bc4e68d3SJeuk Kim !FIELD_EX32(data, HCE, HCE)) {
621bc4e68d3SJeuk Kim u->reg.hcs = 0;
622bc4e68d3SJeuk Kim u->reg.hce = FIELD_DP32(u->reg.hce, HCE, HCE, 0);
623bc4e68d3SJeuk Kim }
624bc4e68d3SJeuk Kim break;
625bc4e68d3SJeuk Kim case A_UTRLBA:
626bc4e68d3SJeuk Kim u->reg.utrlba = data & R_UTRLBA_UTRLBA_MASK;
627bc4e68d3SJeuk Kim break;
628bc4e68d3SJeuk Kim case A_UTRLBAU:
629bc4e68d3SJeuk Kim u->reg.utrlbau = data;
630bc4e68d3SJeuk Kim break;
631bc4e68d3SJeuk Kim case A_UTRLDBR:
632329f1662SJeuk Kim ufs_process_db(u, data);
633329f1662SJeuk Kim u->reg.utrldbr |= data;
634bc4e68d3SJeuk Kim break;
635bc4e68d3SJeuk Kim case A_UTRLRSR:
636bc4e68d3SJeuk Kim u->reg.utrlrsr = data;
637bc4e68d3SJeuk Kim break;
638bc4e68d3SJeuk Kim case A_UTRLCNR:
639bc4e68d3SJeuk Kim u->reg.utrlcnr &= ~data;
640bc4e68d3SJeuk Kim break;
641bc4e68d3SJeuk Kim case A_UTMRLBA:
642bc4e68d3SJeuk Kim u->reg.utmrlba = data & R_UTMRLBA_UTMRLBA_MASK;
643bc4e68d3SJeuk Kim break;
644bc4e68d3SJeuk Kim case A_UTMRLBAU:
645bc4e68d3SJeuk Kim u->reg.utmrlbau = data;
646bc4e68d3SJeuk Kim break;
647bc4e68d3SJeuk Kim case A_UICCMD:
648bc4e68d3SJeuk Kim ufs_process_uiccmd(u, data);
649bc4e68d3SJeuk Kim break;
650bc4e68d3SJeuk Kim case A_UCMDARG1:
651bc4e68d3SJeuk Kim u->reg.ucmdarg1 = data;
652bc4e68d3SJeuk Kim break;
653bc4e68d3SJeuk Kim case A_UCMDARG2:
654bc4e68d3SJeuk Kim u->reg.ucmdarg2 = data;
655bc4e68d3SJeuk Kim break;
656bc4e68d3SJeuk Kim case A_UCMDARG3:
657bc4e68d3SJeuk Kim u->reg.ucmdarg3 = data;
658bc4e68d3SJeuk Kim break;
6595c079578SMinwoo Im case A_CONFIG:
6605c079578SMinwoo Im u->reg.config = data;
6615c079578SMinwoo Im break;
6625c079578SMinwoo Im case A_MCQCONFIG:
6635c079578SMinwoo Im u->reg.mcqconfig = data;
6645c079578SMinwoo Im break;
665bc4e68d3SJeuk Kim case A_UTRLCLR:
666bc4e68d3SJeuk Kim case A_UTMRLDBR:
667bc4e68d3SJeuk Kim case A_UTMRLCLR:
668bc4e68d3SJeuk Kim case A_UTMRLRSR:
669bc4e68d3SJeuk Kim trace_ufs_err_unsupport_register_offset(offset);
670bc4e68d3SJeuk Kim break;
671bc4e68d3SJeuk Kim default:
672bc4e68d3SJeuk Kim trace_ufs_err_invalid_register_offset(offset);
673bc4e68d3SJeuk Kim break;
674bc4e68d3SJeuk Kim }
675bc4e68d3SJeuk Kim }
676bc4e68d3SJeuk Kim
ufs_write_mcq_reg(UfsHc * u,hwaddr offset,uint32_t data,unsigned size)6775c079578SMinwoo Im static void ufs_write_mcq_reg(UfsHc *u, hwaddr offset, uint32_t data,
6785c079578SMinwoo Im unsigned size)
6795c079578SMinwoo Im {
6805c079578SMinwoo Im int qid = offset / sizeof(UfsMcqReg);
6815c079578SMinwoo Im UfsMcqReg *reg = &u->mcq_reg[qid];
6825c079578SMinwoo Im
6835c079578SMinwoo Im switch (offset % sizeof(UfsMcqReg)) {
6845c079578SMinwoo Im case A_SQATTR:
6855c079578SMinwoo Im if (!FIELD_EX32(reg->sqattr, SQATTR, SQEN) &&
6865c079578SMinwoo Im FIELD_EX32(data, SQATTR, SQEN)) {
6875c079578SMinwoo Im if (!ufs_mcq_create_sq(u, qid, data)) {
6885c079578SMinwoo Im break;
6895c079578SMinwoo Im }
6905c079578SMinwoo Im } else if (FIELD_EX32(reg->sqattr, SQATTR, SQEN) &&
6915c079578SMinwoo Im !FIELD_EX32(data, SQATTR, SQEN)) {
6925c079578SMinwoo Im if (!ufs_mcq_delete_sq(u, qid)) {
6935c079578SMinwoo Im break;
6945c079578SMinwoo Im }
6955c079578SMinwoo Im }
6965c079578SMinwoo Im reg->sqattr = data;
6975c079578SMinwoo Im break;
6985c079578SMinwoo Im case A_SQLBA:
6995c079578SMinwoo Im reg->sqlba = data;
7005c079578SMinwoo Im break;
7015c079578SMinwoo Im case A_SQUBA:
7025c079578SMinwoo Im reg->squba = data;
7035c079578SMinwoo Im break;
7045c079578SMinwoo Im case A_SQCFG:
7055c079578SMinwoo Im reg->sqcfg = data;
7065c079578SMinwoo Im break;
7075c079578SMinwoo Im case A_CQATTR:
7085c079578SMinwoo Im if (!FIELD_EX32(reg->cqattr, CQATTR, CQEN) &&
7095c079578SMinwoo Im FIELD_EX32(data, CQATTR, CQEN)) {
7105c079578SMinwoo Im if (!ufs_mcq_create_cq(u, qid, data)) {
7115c079578SMinwoo Im break;
7125c079578SMinwoo Im }
7135c079578SMinwoo Im } else if (FIELD_EX32(reg->cqattr, CQATTR, CQEN) &&
7145c079578SMinwoo Im !FIELD_EX32(data, CQATTR, CQEN)) {
7155c079578SMinwoo Im if (!ufs_mcq_delete_cq(u, qid)) {
7165c079578SMinwoo Im break;
7175c079578SMinwoo Im }
7185c079578SMinwoo Im }
7195c079578SMinwoo Im reg->cqattr = data;
7205c079578SMinwoo Im break;
7215c079578SMinwoo Im case A_CQLBA:
7225c079578SMinwoo Im reg->cqlba = data;
7235c079578SMinwoo Im break;
7245c079578SMinwoo Im case A_CQUBA:
7255c079578SMinwoo Im reg->cquba = data;
7265c079578SMinwoo Im break;
7275c079578SMinwoo Im case A_CQCFG:
7285c079578SMinwoo Im reg->cqcfg = data;
7295c079578SMinwoo Im break;
7305c079578SMinwoo Im case A_SQDAO:
7315c079578SMinwoo Im case A_SQISAO:
7325c079578SMinwoo Im case A_CQDAO:
7335c079578SMinwoo Im case A_CQISAO:
7345c079578SMinwoo Im trace_ufs_err_unsupport_register_offset(offset);
7355c079578SMinwoo Im break;
7365c079578SMinwoo Im default:
7375c079578SMinwoo Im trace_ufs_err_invalid_register_offset(offset);
7385c079578SMinwoo Im break;
7395c079578SMinwoo Im }
7405c079578SMinwoo Im }
7415c079578SMinwoo Im
ufs_mcq_process_db(UfsHc * u,uint8_t qid,uint32_t db)7425c079578SMinwoo Im static void ufs_mcq_process_db(UfsHc *u, uint8_t qid, uint32_t db)
7435c079578SMinwoo Im {
7445c079578SMinwoo Im UfsSq *sq;
7455c079578SMinwoo Im
7465c079578SMinwoo Im if (qid >= u->params.mcq_maxq) {
7475c079578SMinwoo Im trace_ufs_err_mcq_db_wr_invalid_sqid(qid);
7485c079578SMinwoo Im return;
7495c079578SMinwoo Im }
7505c079578SMinwoo Im
7515c079578SMinwoo Im sq = u->sq[qid];
7525c079578SMinwoo Im if (sq->size * sizeof(UfsSqEntry) <= db) {
7535c079578SMinwoo Im trace_ufs_err_mcq_db_wr_invalid_db(qid, db);
7545c079578SMinwoo Im return;
7555c079578SMinwoo Im }
7565c079578SMinwoo Im
7575c079578SMinwoo Im ufs_mcq_update_sq_tail(u, sq->sqid, db);
7585c079578SMinwoo Im qemu_bh_schedule(sq->bh);
7595c079578SMinwoo Im }
7605c079578SMinwoo Im
ufs_write_mcq_op_reg(UfsHc * u,hwaddr offset,uint32_t data,unsigned size)7615c079578SMinwoo Im static void ufs_write_mcq_op_reg(UfsHc *u, hwaddr offset, uint32_t data,
7625c079578SMinwoo Im unsigned size)
7635c079578SMinwoo Im {
7645c079578SMinwoo Im int qid = offset / sizeof(UfsMcqOpReg);
7655c079578SMinwoo Im UfsMcqOpReg *opr = &u->mcq_op_reg[qid];
7665c079578SMinwoo Im
7675c079578SMinwoo Im switch (offset % sizeof(UfsMcqOpReg)) {
7685c079578SMinwoo Im case offsetof(UfsMcqOpReg, sq.tp):
7695c079578SMinwoo Im if (opr->sq.tp != data) {
7705c079578SMinwoo Im ufs_mcq_process_db(u, qid, data);
7715c079578SMinwoo Im }
7725c079578SMinwoo Im opr->sq.tp = data;
7735c079578SMinwoo Im break;
7745c079578SMinwoo Im case offsetof(UfsMcqOpReg, cq.hp):
7755c079578SMinwoo Im opr->cq.hp = data;
7765c079578SMinwoo Im ufs_mcq_update_cq_head(u, qid, data);
7775c079578SMinwoo Im break;
7785c079578SMinwoo Im case offsetof(UfsMcqOpReg, cq_int.is):
7795c079578SMinwoo Im opr->cq_int.is &= ~data;
7805c079578SMinwoo Im break;
7815c079578SMinwoo Im default:
7825c079578SMinwoo Im trace_ufs_err_invalid_register_offset(offset);
7835c079578SMinwoo Im break;
7845c079578SMinwoo Im }
7855c079578SMinwoo Im }
7865c079578SMinwoo Im
ufs_mmio_read(void * opaque,hwaddr addr,unsigned size)787bc4e68d3SJeuk Kim static uint64_t ufs_mmio_read(void *opaque, hwaddr addr, unsigned size)
788bc4e68d3SJeuk Kim {
789bc4e68d3SJeuk Kim UfsHc *u = (UfsHc *)opaque;
790e12b11f6SMinwoo Im uint32_t *ptr;
791bc4e68d3SJeuk Kim uint64_t value;
7925c079578SMinwoo Im uint64_t offset;
793bc4e68d3SJeuk Kim
794e12b11f6SMinwoo Im if (addr + size <= sizeof(u->reg)) {
7955c079578SMinwoo Im offset = addr;
796e12b11f6SMinwoo Im ptr = (uint32_t *)&u->reg;
797e12b11f6SMinwoo Im } else if (ufs_is_mcq_reg(u, addr, size)) {
7985c079578SMinwoo Im offset = addr - ufs_mcq_reg_addr(u, 0);
799e12b11f6SMinwoo Im ptr = (uint32_t *)&u->mcq_reg;
800e12b11f6SMinwoo Im } else if (ufs_is_mcq_op_reg(u, addr, size)) {
8015c079578SMinwoo Im offset = addr - ufs_mcq_op_reg_addr(u, 0);
802e12b11f6SMinwoo Im ptr = (uint32_t *)&u->mcq_op_reg;
8035c079578SMinwoo Im } else {
804bc4e68d3SJeuk Kim trace_ufs_err_invalid_register_offset(addr);
805bc4e68d3SJeuk Kim return 0;
806bc4e68d3SJeuk Kim }
807bc4e68d3SJeuk Kim
808e12b11f6SMinwoo Im value = ptr[offset >> 2];
809bc4e68d3SJeuk Kim trace_ufs_mmio_read(addr, value, size);
810bc4e68d3SJeuk Kim return value;
811bc4e68d3SJeuk Kim }
812bc4e68d3SJeuk Kim
ufs_mmio_write(void * opaque,hwaddr addr,uint64_t data,unsigned size)813bc4e68d3SJeuk Kim static void ufs_mmio_write(void *opaque, hwaddr addr, uint64_t data,
814bc4e68d3SJeuk Kim unsigned size)
815bc4e68d3SJeuk Kim {
816bc4e68d3SJeuk Kim UfsHc *u = (UfsHc *)opaque;
817bc4e68d3SJeuk Kim
818bc4e68d3SJeuk Kim trace_ufs_mmio_write(addr, data, size);
8195c079578SMinwoo Im
820e12b11f6SMinwoo Im if (addr + size <= sizeof(u->reg)) {
821bc4e68d3SJeuk Kim ufs_write_reg(u, addr, data, size);
822e12b11f6SMinwoo Im } else if (ufs_is_mcq_reg(u, addr, size)) {
8235c079578SMinwoo Im ufs_write_mcq_reg(u, addr - ufs_mcq_reg_addr(u, 0), data, size);
824e12b11f6SMinwoo Im } else if (ufs_is_mcq_op_reg(u, addr, size)) {
8255c079578SMinwoo Im ufs_write_mcq_op_reg(u, addr - ufs_mcq_op_reg_addr(u, 0), data, size);
8265c079578SMinwoo Im } else {
8275c079578SMinwoo Im trace_ufs_err_invalid_register_offset(addr);
8285c079578SMinwoo Im }
829bc4e68d3SJeuk Kim }
830bc4e68d3SJeuk Kim
831bc4e68d3SJeuk Kim static const MemoryRegionOps ufs_mmio_ops = {
832bc4e68d3SJeuk Kim .read = ufs_mmio_read,
833bc4e68d3SJeuk Kim .write = ufs_mmio_write,
834bc4e68d3SJeuk Kim .endianness = DEVICE_LITTLE_ENDIAN,
835bc4e68d3SJeuk Kim .impl = {
836bc4e68d3SJeuk Kim .min_access_size = 4,
837bc4e68d3SJeuk Kim .max_access_size = 4,
838bc4e68d3SJeuk Kim },
839bc4e68d3SJeuk Kim };
840bc4e68d3SJeuk Kim
8412a8b36a4SJeuk 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)842096434feSJeuk Kim void ufs_build_upiu_header(UfsRequest *req, uint8_t trans_type, uint8_t flags,
843096434feSJeuk Kim uint8_t response, uint8_t scsi_status,
844329f1662SJeuk Kim uint16_t data_segment_length)
845329f1662SJeuk Kim {
846329f1662SJeuk Kim memcpy(&req->rsp_upiu.header, &req->req_upiu.header, sizeof(UtpUpiuHeader));
847329f1662SJeuk Kim req->rsp_upiu.header.trans_type = trans_type;
848329f1662SJeuk Kim req->rsp_upiu.header.flags = flags;
849329f1662SJeuk Kim req->rsp_upiu.header.response = response;
850329f1662SJeuk Kim req->rsp_upiu.header.scsi_status = scsi_status;
851329f1662SJeuk Kim req->rsp_upiu.header.data_segment_length = cpu_to_be16(data_segment_length);
852329f1662SJeuk Kim }
853329f1662SJeuk Kim
ufs_build_query_response(UfsRequest * req)854de2cc407SKyoungrul Kim void ufs_build_query_response(UfsRequest *req)
855de2cc407SKyoungrul Kim {
856de2cc407SKyoungrul Kim req->rsp_upiu.qr.opcode = req->req_upiu.qr.opcode;
857de2cc407SKyoungrul Kim req->rsp_upiu.qr.idn = req->req_upiu.qr.idn;
858de2cc407SKyoungrul Kim req->rsp_upiu.qr.index = req->req_upiu.qr.index;
859de2cc407SKyoungrul Kim req->rsp_upiu.qr.selector = req->req_upiu.qr.selector;
860de2cc407SKyoungrul Kim }
861de2cc407SKyoungrul Kim
ufs_exec_scsi_cmd(UfsRequest * req)8622a8b36a4SJeuk Kim static UfsReqResult ufs_exec_scsi_cmd(UfsRequest *req)
8632a8b36a4SJeuk Kim {
8642a8b36a4SJeuk Kim UfsHc *u = req->hc;
8652a8b36a4SJeuk Kim uint8_t lun = req->req_upiu.header.lun;
866096434feSJeuk Kim
867096434feSJeuk Kim UfsLu *lu = NULL;
8682a8b36a4SJeuk Kim
8692a8b36a4SJeuk Kim trace_ufs_exec_scsi_cmd(req->slot, lun, req->req_upiu.sc.cdb[0]);
8702a8b36a4SJeuk Kim
871096434feSJeuk Kim if (!is_wlun(lun) && (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) {
8722a8b36a4SJeuk Kim trace_ufs_err_scsi_cmd_invalid_lun(lun);
8732a8b36a4SJeuk Kim return UFS_REQUEST_FAIL;
8742a8b36a4SJeuk Kim }
8752a8b36a4SJeuk Kim
8762a8b36a4SJeuk Kim switch (lun) {
8772a8b36a4SJeuk Kim case UFS_UPIU_REPORT_LUNS_WLUN:
878096434feSJeuk Kim lu = &u->report_wlu;
8792a8b36a4SJeuk Kim break;
8802a8b36a4SJeuk Kim case UFS_UPIU_UFS_DEVICE_WLUN:
881096434feSJeuk Kim lu = &u->dev_wlu;
8822a8b36a4SJeuk Kim break;
8832a8b36a4SJeuk Kim case UFS_UPIU_BOOT_WLUN:
884096434feSJeuk Kim lu = &u->boot_wlu;
8852a8b36a4SJeuk Kim break;
8862a8b36a4SJeuk Kim case UFS_UPIU_RPMB_WLUN:
887096434feSJeuk Kim lu = &u->rpmb_wlu;
8882a8b36a4SJeuk Kim break;
8892a8b36a4SJeuk Kim default:
890096434feSJeuk Kim lu = u->lus[lun];
8912a8b36a4SJeuk Kim }
8922a8b36a4SJeuk Kim
893096434feSJeuk Kim return lu->scsi_op(lu, req);
8942a8b36a4SJeuk Kim }
8952a8b36a4SJeuk Kim
ufs_exec_nop_cmd(UfsRequest * req)896329f1662SJeuk Kim static UfsReqResult ufs_exec_nop_cmd(UfsRequest *req)
897329f1662SJeuk Kim {
898329f1662SJeuk Kim trace_ufs_exec_nop_cmd(req->slot);
899329f1662SJeuk Kim ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_NOP_IN, 0, 0, 0, 0);
900329f1662SJeuk Kim return UFS_REQUEST_SUCCESS;
901329f1662SJeuk Kim }
902329f1662SJeuk Kim
903329f1662SJeuk Kim /*
904329f1662SJeuk Kim * This defines the permission of flags based on their IDN. There are some
905329f1662SJeuk Kim * things that are declared read-only, which is inconsistent with the ufs spec,
906329f1662SJeuk Kim * because we want to return an error for features that are not yet supported.
907329f1662SJeuk Kim */
908329f1662SJeuk Kim static const int flag_permission[UFS_QUERY_FLAG_IDN_COUNT] = {
909329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_FDEVICEINIT] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET,
910329f1662SJeuk Kim /* Write protection is not supported */
911329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_PERMANENT_WPE] = UFS_QUERY_FLAG_READ,
912329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_PWR_ON_WPE] = UFS_QUERY_FLAG_READ,
913329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_BKOPS_EN] = UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET |
914329f1662SJeuk Kim UFS_QUERY_FLAG_CLEAR |
915329f1662SJeuk Kim UFS_QUERY_FLAG_TOGGLE,
916329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE] =
917329f1662SJeuk Kim UFS_QUERY_FLAG_READ | UFS_QUERY_FLAG_SET | UFS_QUERY_FLAG_CLEAR |
918329f1662SJeuk Kim UFS_QUERY_FLAG_TOGGLE,
919329f1662SJeuk Kim /* Purge Operation is not supported */
920329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_PURGE_ENABLE] = UFS_QUERY_FLAG_NONE,
921329f1662SJeuk Kim /* Refresh Operation is not supported */
922329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_REFRESH_ENABLE] = UFS_QUERY_FLAG_NONE,
923329f1662SJeuk Kim /* Physical Resource Removal is not supported */
924329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_FPHYRESOURCEREMOVAL] = UFS_QUERY_FLAG_READ,
925329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_BUSY_RTC] = UFS_QUERY_FLAG_READ,
926329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_PERMANENTLY_DISABLE_FW_UPDATE] = UFS_QUERY_FLAG_READ,
927329f1662SJeuk Kim /* Write Booster is not supported */
928329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_WB_EN] = UFS_QUERY_FLAG_READ,
929329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_EN] = UFS_QUERY_FLAG_READ,
930329f1662SJeuk Kim [UFS_QUERY_FLAG_IDN_WB_BUFF_FLUSH_DURING_HIBERN8] = UFS_QUERY_FLAG_READ,
931329f1662SJeuk Kim };
932329f1662SJeuk Kim
ufs_flag_check_idn_valid(uint8_t idn,int op)933329f1662SJeuk Kim static inline QueryRespCode ufs_flag_check_idn_valid(uint8_t idn, int op)
934329f1662SJeuk Kim {
935329f1662SJeuk Kim if (idn >= UFS_QUERY_FLAG_IDN_COUNT) {
936329f1662SJeuk Kim return UFS_QUERY_RESULT_INVALID_IDN;
937329f1662SJeuk Kim }
938329f1662SJeuk Kim
939329f1662SJeuk Kim if (!(flag_permission[idn] & op)) {
940329f1662SJeuk Kim if (op == UFS_QUERY_FLAG_READ) {
941329f1662SJeuk Kim trace_ufs_err_query_flag_not_readable(idn);
942329f1662SJeuk Kim return UFS_QUERY_RESULT_NOT_READABLE;
943329f1662SJeuk Kim }
944329f1662SJeuk Kim trace_ufs_err_query_flag_not_writable(idn);
945329f1662SJeuk Kim return UFS_QUERY_RESULT_NOT_WRITEABLE;
946329f1662SJeuk Kim }
947329f1662SJeuk Kim
948329f1662SJeuk Kim return UFS_QUERY_RESULT_SUCCESS;
949329f1662SJeuk Kim }
950329f1662SJeuk Kim
951329f1662SJeuk Kim static const int attr_permission[UFS_QUERY_ATTR_IDN_COUNT] = {
952329f1662SJeuk Kim /* booting is not supported */
953329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_BOOT_LU_EN] = UFS_QUERY_ATTR_READ,
954329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_POWER_MODE] = UFS_QUERY_ATTR_READ,
955329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL] =
956329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
957329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_OOO_DATA_EN] = UFS_QUERY_ATTR_READ,
958329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_BKOPS_STATUS] = UFS_QUERY_ATTR_READ,
959329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_PURGE_STATUS] = UFS_QUERY_ATTR_READ,
960329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_MAX_DATA_IN] =
961329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
962329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_MAX_DATA_OUT] =
963329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
964329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED] = UFS_QUERY_ATTR_READ,
965329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_REF_CLK_FREQ] =
966329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
967329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK] = UFS_QUERY_ATTR_READ,
968329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT] =
969329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
970329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_EE_CONTROL] =
971329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
972329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_EE_STATUS] = UFS_QUERY_ATTR_READ,
973329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_SECONDS_PASSED] = UFS_QUERY_ATTR_WRITE,
974329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_CNTX_CONF] = UFS_QUERY_ATTR_READ,
975329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_FFU_STATUS] = UFS_QUERY_ATTR_READ,
976329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_PSA_STATE] = UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
977329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE] =
978329f1662SJeuk Kim UFS_QUERY_ATTR_READ | UFS_QUERY_ATTR_WRITE,
979329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME] = UFS_QUERY_ATTR_READ,
980329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP] = UFS_QUERY_ATTR_READ,
981329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
982329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND] = UFS_QUERY_ATTR_READ,
983329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_THROTTLING_STATUS] = UFS_QUERY_ATTR_READ,
984329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS] = UFS_QUERY_ATTR_READ,
985329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
986329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST] = UFS_QUERY_ATTR_READ,
987329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE] = UFS_QUERY_ATTR_READ,
988329f1662SJeuk Kim /* refresh operation is not supported */
989329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_REFRESH_STATUS] = UFS_QUERY_ATTR_READ,
990329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_REFRESH_FREQ] = UFS_QUERY_ATTR_READ,
991329f1662SJeuk Kim [UFS_QUERY_ATTR_IDN_REFRESH_UNIT] = UFS_QUERY_ATTR_READ,
992329f1662SJeuk Kim };
993329f1662SJeuk Kim
ufs_attr_check_idn_valid(uint8_t idn,int op)994329f1662SJeuk Kim static inline QueryRespCode ufs_attr_check_idn_valid(uint8_t idn, int op)
995329f1662SJeuk Kim {
996329f1662SJeuk Kim if (idn >= UFS_QUERY_ATTR_IDN_COUNT) {
997329f1662SJeuk Kim return UFS_QUERY_RESULT_INVALID_IDN;
998329f1662SJeuk Kim }
999329f1662SJeuk Kim
1000329f1662SJeuk Kim if (!(attr_permission[idn] & op)) {
1001329f1662SJeuk Kim if (op == UFS_QUERY_ATTR_READ) {
1002329f1662SJeuk Kim trace_ufs_err_query_attr_not_readable(idn);
1003329f1662SJeuk Kim return UFS_QUERY_RESULT_NOT_READABLE;
1004329f1662SJeuk Kim }
1005329f1662SJeuk Kim trace_ufs_err_query_attr_not_writable(idn);
1006329f1662SJeuk Kim return UFS_QUERY_RESULT_NOT_WRITEABLE;
1007329f1662SJeuk Kim }
1008329f1662SJeuk Kim
1009329f1662SJeuk Kim return UFS_QUERY_RESULT_SUCCESS;
1010329f1662SJeuk Kim }
1011329f1662SJeuk Kim
ufs_exec_query_flag(UfsRequest * req,int op)1012329f1662SJeuk Kim static QueryRespCode ufs_exec_query_flag(UfsRequest *req, int op)
1013329f1662SJeuk Kim {
1014329f1662SJeuk Kim UfsHc *u = req->hc;
1015329f1662SJeuk Kim uint8_t idn = req->req_upiu.qr.idn;
1016329f1662SJeuk Kim uint32_t value;
1017329f1662SJeuk Kim QueryRespCode ret;
1018329f1662SJeuk Kim
1019329f1662SJeuk Kim ret = ufs_flag_check_idn_valid(idn, op);
1020329f1662SJeuk Kim if (ret) {
1021329f1662SJeuk Kim return ret;
1022329f1662SJeuk Kim }
1023329f1662SJeuk Kim
1024329f1662SJeuk Kim if (idn == UFS_QUERY_FLAG_IDN_FDEVICEINIT) {
1025329f1662SJeuk Kim value = 0;
1026329f1662SJeuk Kim } else if (op == UFS_QUERY_FLAG_READ) {
1027329f1662SJeuk Kim value = *(((uint8_t *)&u->flags) + idn);
1028329f1662SJeuk Kim } else if (op == UFS_QUERY_FLAG_SET) {
1029329f1662SJeuk Kim value = 1;
1030329f1662SJeuk Kim } else if (op == UFS_QUERY_FLAG_CLEAR) {
1031329f1662SJeuk Kim value = 0;
1032329f1662SJeuk Kim } else if (op == UFS_QUERY_FLAG_TOGGLE) {
1033329f1662SJeuk Kim value = *(((uint8_t *)&u->flags) + idn);
1034329f1662SJeuk Kim value = !value;
1035329f1662SJeuk Kim } else {
1036329f1662SJeuk Kim trace_ufs_err_query_invalid_opcode(op);
1037329f1662SJeuk Kim return UFS_QUERY_RESULT_INVALID_OPCODE;
1038329f1662SJeuk Kim }
1039329f1662SJeuk Kim
1040329f1662SJeuk Kim *(((uint8_t *)&u->flags) + idn) = value;
1041329f1662SJeuk Kim req->rsp_upiu.qr.value = cpu_to_be32(value);
1042329f1662SJeuk Kim return UFS_QUERY_RESULT_SUCCESS;
1043329f1662SJeuk Kim }
1044329f1662SJeuk Kim
ufs_read_attr_value(UfsHc * u,uint8_t idn)1045329f1662SJeuk Kim static uint32_t ufs_read_attr_value(UfsHc *u, uint8_t idn)
1046329f1662SJeuk Kim {
1047329f1662SJeuk Kim switch (idn) {
1048329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_BOOT_LU_EN:
1049329f1662SJeuk Kim return u->attributes.boot_lun_en;
1050329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_POWER_MODE:
1051329f1662SJeuk Kim return u->attributes.current_power_mode;
1052329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
1053329f1662SJeuk Kim return u->attributes.active_icc_level;
1054329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_OOO_DATA_EN:
1055329f1662SJeuk Kim return u->attributes.out_of_order_data_en;
1056329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_BKOPS_STATUS:
1057329f1662SJeuk Kim return u->attributes.background_op_status;
1058329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_PURGE_STATUS:
1059329f1662SJeuk Kim return u->attributes.purge_status;
1060329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_MAX_DATA_IN:
1061329f1662SJeuk Kim return u->attributes.max_data_in_size;
1062329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_MAX_DATA_OUT:
1063329f1662SJeuk Kim return u->attributes.max_data_out_size;
1064329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_DYN_CAP_NEEDED:
1065329f1662SJeuk Kim return be32_to_cpu(u->attributes.dyn_cap_needed);
1066329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_REF_CLK_FREQ:
1067329f1662SJeuk Kim return u->attributes.ref_clk_freq;
1068329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_CONF_DESC_LOCK:
1069329f1662SJeuk Kim return u->attributes.config_descr_lock;
1070329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
1071329f1662SJeuk Kim return u->attributes.max_num_of_rtt;
1072329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_EE_CONTROL:
1073329f1662SJeuk Kim return be16_to_cpu(u->attributes.exception_event_control);
1074329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_EE_STATUS:
1075329f1662SJeuk Kim return be16_to_cpu(u->attributes.exception_event_status);
1076329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_SECONDS_PASSED:
1077329f1662SJeuk Kim return be32_to_cpu(u->attributes.seconds_passed);
1078329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_CNTX_CONF:
1079329f1662SJeuk Kim return be16_to_cpu(u->attributes.context_conf);
1080329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_FFU_STATUS:
1081329f1662SJeuk Kim return u->attributes.device_ffu_status;
1082329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_PSA_STATE:
1083329f1662SJeuk Kim return be32_to_cpu(u->attributes.psa_state);
1084329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE:
1085329f1662SJeuk Kim return be32_to_cpu(u->attributes.psa_data_size);
1086329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_REF_CLK_GATING_WAIT_TIME:
1087329f1662SJeuk Kim return u->attributes.ref_clk_gating_wait_time;
1088329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP:
1089329f1662SJeuk Kim return u->attributes.device_case_rough_temperaure;
1090329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND:
1091329f1662SJeuk Kim return u->attributes.device_too_high_temp_boundary;
1092329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND:
1093329f1662SJeuk Kim return u->attributes.device_too_low_temp_boundary;
1094329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_THROTTLING_STATUS:
1095329f1662SJeuk Kim return u->attributes.throttling_status;
1096329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_WB_FLUSH_STATUS:
1097329f1662SJeuk Kim return u->attributes.wb_buffer_flush_status;
1098329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_AVAIL_WB_BUFF_SIZE:
1099329f1662SJeuk Kim return u->attributes.available_wb_buffer_size;
1100329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_WB_BUFF_LIFE_TIME_EST:
1101329f1662SJeuk Kim return u->attributes.wb_buffer_life_time_est;
1102329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_CURR_WB_BUFF_SIZE:
1103329f1662SJeuk Kim return be32_to_cpu(u->attributes.current_wb_buffer_size);
1104329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_REFRESH_STATUS:
1105329f1662SJeuk Kim return u->attributes.refresh_status;
1106329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_REFRESH_FREQ:
1107329f1662SJeuk Kim return u->attributes.refresh_freq;
1108329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_REFRESH_UNIT:
1109329f1662SJeuk Kim return u->attributes.refresh_unit;
1110329f1662SJeuk Kim }
1111329f1662SJeuk Kim return 0;
1112329f1662SJeuk Kim }
1113329f1662SJeuk Kim
ufs_write_attr_value(UfsHc * u,uint8_t idn,uint32_t value)11147c85332aSYoochan Jeong static QueryRespCode ufs_write_attr_value(UfsHc *u, uint8_t idn, uint32_t value)
1115329f1662SJeuk Kim {
1116329f1662SJeuk Kim switch (idn) {
1117329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL:
11187c85332aSYoochan Jeong if (value > UFS_QUERY_ATTR_ACTIVE_ICC_MAXVALUE) {
11197c85332aSYoochan Jeong return UFS_QUERY_RESULT_INVALID_VALUE;
11207c85332aSYoochan Jeong }
1121329f1662SJeuk Kim u->attributes.active_icc_level = value;
1122329f1662SJeuk Kim break;
1123329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_MAX_DATA_IN:
1124329f1662SJeuk Kim u->attributes.max_data_in_size = value;
1125329f1662SJeuk Kim break;
1126329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_MAX_DATA_OUT:
1127329f1662SJeuk Kim u->attributes.max_data_out_size = value;
1128329f1662SJeuk Kim break;
1129329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_REF_CLK_FREQ:
1130329f1662SJeuk Kim u->attributes.ref_clk_freq = value;
1131329f1662SJeuk Kim break;
1132329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_MAX_NUM_OF_RTT:
1133329f1662SJeuk Kim u->attributes.max_num_of_rtt = value;
1134329f1662SJeuk Kim break;
1135329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_EE_CONTROL:
1136329f1662SJeuk Kim u->attributes.exception_event_control = cpu_to_be16(value);
1137329f1662SJeuk Kim break;
1138329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_SECONDS_PASSED:
1139329f1662SJeuk Kim u->attributes.seconds_passed = cpu_to_be32(value);
1140329f1662SJeuk Kim break;
1141329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_PSA_STATE:
1142329f1662SJeuk Kim u->attributes.psa_state = value;
1143329f1662SJeuk Kim break;
1144329f1662SJeuk Kim case UFS_QUERY_ATTR_IDN_PSA_DATA_SIZE:
1145329f1662SJeuk Kim u->attributes.psa_data_size = cpu_to_be32(value);
1146329f1662SJeuk Kim break;
1147329f1662SJeuk Kim }
11487c85332aSYoochan Jeong return UFS_QUERY_RESULT_SUCCESS;
1149329f1662SJeuk Kim }
1150329f1662SJeuk Kim
ufs_exec_query_attr(UfsRequest * req,int op)1151329f1662SJeuk Kim static QueryRespCode ufs_exec_query_attr(UfsRequest *req, int op)
1152329f1662SJeuk Kim {
1153329f1662SJeuk Kim UfsHc *u = req->hc;
1154329f1662SJeuk Kim uint8_t idn = req->req_upiu.qr.idn;
1155329f1662SJeuk Kim uint32_t value;
1156329f1662SJeuk Kim QueryRespCode ret;
1157329f1662SJeuk Kim
1158329f1662SJeuk Kim ret = ufs_attr_check_idn_valid(idn, op);
1159329f1662SJeuk Kim if (ret) {
1160329f1662SJeuk Kim return ret;
1161329f1662SJeuk Kim }
1162329f1662SJeuk Kim
1163329f1662SJeuk Kim if (op == UFS_QUERY_ATTR_READ) {
1164329f1662SJeuk Kim value = ufs_read_attr_value(u, idn);
11657c85332aSYoochan Jeong ret = UFS_QUERY_RESULT_SUCCESS;
1166329f1662SJeuk Kim } else {
1167*48876bfcSKeoseong Park value = be32_to_cpu(req->req_upiu.qr.value);
11687c85332aSYoochan Jeong ret = ufs_write_attr_value(u, idn, value);
1169329f1662SJeuk Kim }
1170329f1662SJeuk Kim req->rsp_upiu.qr.value = cpu_to_be32(value);
11717c85332aSYoochan Jeong return ret;
1172329f1662SJeuk Kim }
1173329f1662SJeuk Kim
1174329f1662SJeuk Kim static const RpmbUnitDescriptor rpmb_unit_desc = {
1175329f1662SJeuk Kim .length = sizeof(RpmbUnitDescriptor),
1176329f1662SJeuk Kim .descriptor_idn = 2,
1177329f1662SJeuk Kim .unit_index = UFS_UPIU_RPMB_WLUN,
1178329f1662SJeuk Kim .lu_enable = 0,
1179329f1662SJeuk Kim };
1180329f1662SJeuk Kim
ufs_read_unit_desc(UfsRequest * req)1181329f1662SJeuk Kim static QueryRespCode ufs_read_unit_desc(UfsRequest *req)
1182329f1662SJeuk Kim {
11832a8b36a4SJeuk Kim UfsHc *u = req->hc;
1184329f1662SJeuk Kim uint8_t lun = req->req_upiu.qr.index;
1185329f1662SJeuk Kim
11862a8b36a4SJeuk Kim if (lun != UFS_UPIU_RPMB_WLUN &&
118797970daeSJeuk Kim (lun >= UFS_MAX_LUS || u->lus[lun] == NULL)) {
1188329f1662SJeuk Kim trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, lun);
1189329f1662SJeuk Kim return UFS_QUERY_RESULT_INVALID_INDEX;
1190329f1662SJeuk Kim }
1191329f1662SJeuk Kim
1192329f1662SJeuk Kim if (lun == UFS_UPIU_RPMB_WLUN) {
1193329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &rpmb_unit_desc, rpmb_unit_desc.length);
1194329f1662SJeuk Kim } else {
11952a8b36a4SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &u->lus[lun]->unit_desc,
11962a8b36a4SJeuk Kim sizeof(u->lus[lun]->unit_desc));
1197329f1662SJeuk Kim }
1198329f1662SJeuk Kim
1199329f1662SJeuk Kim return UFS_QUERY_RESULT_SUCCESS;
1200329f1662SJeuk Kim }
1201329f1662SJeuk Kim
manufacturer_str_desc(void)1202329f1662SJeuk Kim static inline StringDescriptor manufacturer_str_desc(void)
1203329f1662SJeuk Kim {
1204329f1662SJeuk Kim StringDescriptor desc = {
1205329f1662SJeuk Kim .length = 0x12,
1206329f1662SJeuk Kim .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
1207329f1662SJeuk Kim };
1208329f1662SJeuk Kim desc.UC[0] = cpu_to_be16('R');
1209329f1662SJeuk Kim desc.UC[1] = cpu_to_be16('E');
1210329f1662SJeuk Kim desc.UC[2] = cpu_to_be16('D');
1211329f1662SJeuk Kim desc.UC[3] = cpu_to_be16('H');
1212329f1662SJeuk Kim desc.UC[4] = cpu_to_be16('A');
1213329f1662SJeuk Kim desc.UC[5] = cpu_to_be16('T');
1214329f1662SJeuk Kim return desc;
1215329f1662SJeuk Kim }
1216329f1662SJeuk Kim
product_name_str_desc(void)1217329f1662SJeuk Kim static inline StringDescriptor product_name_str_desc(void)
1218329f1662SJeuk Kim {
1219329f1662SJeuk Kim StringDescriptor desc = {
1220329f1662SJeuk Kim .length = 0x22,
1221329f1662SJeuk Kim .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
1222329f1662SJeuk Kim };
1223329f1662SJeuk Kim desc.UC[0] = cpu_to_be16('Q');
1224329f1662SJeuk Kim desc.UC[1] = cpu_to_be16('E');
1225329f1662SJeuk Kim desc.UC[2] = cpu_to_be16('M');
1226329f1662SJeuk Kim desc.UC[3] = cpu_to_be16('U');
1227329f1662SJeuk Kim desc.UC[4] = cpu_to_be16(' ');
1228329f1662SJeuk Kim desc.UC[5] = cpu_to_be16('U');
1229329f1662SJeuk Kim desc.UC[6] = cpu_to_be16('F');
1230329f1662SJeuk Kim desc.UC[7] = cpu_to_be16('S');
1231329f1662SJeuk Kim return desc;
1232329f1662SJeuk Kim }
1233329f1662SJeuk Kim
product_rev_level_str_desc(void)1234329f1662SJeuk Kim static inline StringDescriptor product_rev_level_str_desc(void)
1235329f1662SJeuk Kim {
1236329f1662SJeuk Kim StringDescriptor desc = {
1237329f1662SJeuk Kim .length = 0x0a,
1238329f1662SJeuk Kim .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
1239329f1662SJeuk Kim };
1240329f1662SJeuk Kim desc.UC[0] = cpu_to_be16('0');
1241329f1662SJeuk Kim desc.UC[1] = cpu_to_be16('0');
1242329f1662SJeuk Kim desc.UC[2] = cpu_to_be16('0');
1243329f1662SJeuk Kim desc.UC[3] = cpu_to_be16('1');
1244329f1662SJeuk Kim return desc;
1245329f1662SJeuk Kim }
1246329f1662SJeuk Kim
1247329f1662SJeuk Kim static const StringDescriptor null_str_desc = {
1248329f1662SJeuk Kim .length = 0x02,
1249329f1662SJeuk Kim .descriptor_idn = UFS_QUERY_DESC_IDN_STRING,
1250329f1662SJeuk Kim };
1251329f1662SJeuk Kim
ufs_read_string_desc(UfsRequest * req)1252329f1662SJeuk Kim static QueryRespCode ufs_read_string_desc(UfsRequest *req)
1253329f1662SJeuk Kim {
1254329f1662SJeuk Kim UfsHc *u = req->hc;
1255329f1662SJeuk Kim uint8_t index = req->req_upiu.qr.index;
1256329f1662SJeuk Kim StringDescriptor desc;
1257329f1662SJeuk Kim
1258329f1662SJeuk Kim if (index == u->device_desc.manufacturer_name) {
1259329f1662SJeuk Kim desc = manufacturer_str_desc();
1260329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
1261329f1662SJeuk Kim } else if (index == u->device_desc.product_name) {
1262329f1662SJeuk Kim desc = product_name_str_desc();
1263329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
1264329f1662SJeuk Kim } else if (index == u->device_desc.serial_number) {
1265329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
1266329f1662SJeuk Kim } else if (index == u->device_desc.oem_id) {
1267329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &null_str_desc, null_str_desc.length);
1268329f1662SJeuk Kim } else if (index == u->device_desc.product_revision_level) {
1269329f1662SJeuk Kim desc = product_rev_level_str_desc();
1270329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &desc, desc.length);
1271329f1662SJeuk Kim } else {
1272329f1662SJeuk Kim trace_ufs_err_query_invalid_index(req->req_upiu.qr.opcode, index);
1273329f1662SJeuk Kim return UFS_QUERY_RESULT_INVALID_INDEX;
1274329f1662SJeuk Kim }
1275329f1662SJeuk Kim return UFS_QUERY_RESULT_SUCCESS;
1276329f1662SJeuk Kim }
1277329f1662SJeuk Kim
interconnect_desc(void)1278329f1662SJeuk Kim static inline InterconnectDescriptor interconnect_desc(void)
1279329f1662SJeuk Kim {
1280329f1662SJeuk Kim InterconnectDescriptor desc = {
1281329f1662SJeuk Kim .length = sizeof(InterconnectDescriptor),
1282329f1662SJeuk Kim .descriptor_idn = UFS_QUERY_DESC_IDN_INTERCONNECT,
1283329f1662SJeuk Kim };
1284329f1662SJeuk Kim desc.bcd_unipro_version = cpu_to_be16(0x180);
1285329f1662SJeuk Kim desc.bcd_mphy_version = cpu_to_be16(0x410);
1286329f1662SJeuk Kim return desc;
1287329f1662SJeuk Kim }
1288329f1662SJeuk Kim
ufs_read_desc(UfsRequest * req)1289329f1662SJeuk Kim static QueryRespCode ufs_read_desc(UfsRequest *req)
1290329f1662SJeuk Kim {
1291329f1662SJeuk Kim UfsHc *u = req->hc;
1292329f1662SJeuk Kim QueryRespCode status;
1293329f1662SJeuk Kim uint8_t idn = req->req_upiu.qr.idn;
12947c85332aSYoochan Jeong uint8_t selector = req->req_upiu.qr.selector;
1295329f1662SJeuk Kim uint16_t length = be16_to_cpu(req->req_upiu.qr.length);
1296329f1662SJeuk Kim InterconnectDescriptor desc;
12977c85332aSYoochan Jeong if (selector != 0) {
12987c85332aSYoochan Jeong return UFS_QUERY_RESULT_INVALID_SELECTOR;
12997c85332aSYoochan Jeong }
1300329f1662SJeuk Kim switch (idn) {
1301329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_DEVICE:
1302329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &u->device_desc, sizeof(u->device_desc));
1303329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1304329f1662SJeuk Kim break;
1305329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_UNIT:
1306329f1662SJeuk Kim status = ufs_read_unit_desc(req);
1307329f1662SJeuk Kim break;
1308329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_GEOMETRY:
1309329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &u->geometry_desc,
1310329f1662SJeuk Kim sizeof(u->geometry_desc));
1311329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1312329f1662SJeuk Kim break;
1313329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_INTERCONNECT: {
1314329f1662SJeuk Kim desc = interconnect_desc();
1315329f1662SJeuk Kim memcpy(&req->rsp_upiu.qr.data, &desc, sizeof(InterconnectDescriptor));
1316329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1317329f1662SJeuk Kim break;
1318329f1662SJeuk Kim }
1319329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_STRING:
1320329f1662SJeuk Kim status = ufs_read_string_desc(req);
1321329f1662SJeuk Kim break;
1322329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_POWER:
1323329f1662SJeuk Kim /* mocking of power descriptor is not supported */
1324329f1662SJeuk Kim memset(&req->rsp_upiu.qr.data, 0, sizeof(PowerParametersDescriptor));
1325329f1662SJeuk Kim req->rsp_upiu.qr.data[0] = sizeof(PowerParametersDescriptor);
1326329f1662SJeuk Kim req->rsp_upiu.qr.data[1] = UFS_QUERY_DESC_IDN_POWER;
1327329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1328329f1662SJeuk Kim break;
1329329f1662SJeuk Kim case UFS_QUERY_DESC_IDN_HEALTH:
1330329f1662SJeuk Kim /* mocking of health descriptor is not supported */
1331329f1662SJeuk Kim memset(&req->rsp_upiu.qr.data, 0, sizeof(DeviceHealthDescriptor));
1332329f1662SJeuk Kim req->rsp_upiu.qr.data[0] = sizeof(DeviceHealthDescriptor);
1333329f1662SJeuk Kim req->rsp_upiu.qr.data[1] = UFS_QUERY_DESC_IDN_HEALTH;
1334329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1335329f1662SJeuk Kim break;
1336329f1662SJeuk Kim default:
1337329f1662SJeuk Kim length = 0;
1338329f1662SJeuk Kim trace_ufs_err_query_invalid_idn(req->req_upiu.qr.opcode, idn);
1339329f1662SJeuk Kim status = UFS_QUERY_RESULT_INVALID_IDN;
1340329f1662SJeuk Kim }
1341329f1662SJeuk Kim
1342329f1662SJeuk Kim if (length > req->rsp_upiu.qr.data[0]) {
1343329f1662SJeuk Kim length = req->rsp_upiu.qr.data[0];
1344329f1662SJeuk Kim }
1345329f1662SJeuk Kim req->rsp_upiu.qr.length = cpu_to_be16(length);
1346329f1662SJeuk Kim
1347329f1662SJeuk Kim return status;
1348329f1662SJeuk Kim }
1349329f1662SJeuk Kim
ufs_exec_query_read(UfsRequest * req)1350329f1662SJeuk Kim static QueryRespCode ufs_exec_query_read(UfsRequest *req)
1351329f1662SJeuk Kim {
1352329f1662SJeuk Kim QueryRespCode status;
1353329f1662SJeuk Kim switch (req->req_upiu.qr.opcode) {
1354329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_NOP:
1355329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1356329f1662SJeuk Kim break;
1357329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_READ_DESC:
1358329f1662SJeuk Kim status = ufs_read_desc(req);
1359329f1662SJeuk Kim break;
1360329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_READ_ATTR:
1361329f1662SJeuk Kim status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_READ);
1362329f1662SJeuk Kim break;
1363329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_READ_FLAG:
1364329f1662SJeuk Kim status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_READ);
1365329f1662SJeuk Kim break;
1366329f1662SJeuk Kim default:
1367329f1662SJeuk Kim trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
1368329f1662SJeuk Kim status = UFS_QUERY_RESULT_INVALID_OPCODE;
1369329f1662SJeuk Kim break;
1370329f1662SJeuk Kim }
1371329f1662SJeuk Kim
1372329f1662SJeuk Kim return status;
1373329f1662SJeuk Kim }
1374329f1662SJeuk Kim
ufs_exec_query_write(UfsRequest * req)1375329f1662SJeuk Kim static QueryRespCode ufs_exec_query_write(UfsRequest *req)
1376329f1662SJeuk Kim {
1377329f1662SJeuk Kim QueryRespCode status;
1378329f1662SJeuk Kim switch (req->req_upiu.qr.opcode) {
1379329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_NOP:
1380329f1662SJeuk Kim status = UFS_QUERY_RESULT_SUCCESS;
1381329f1662SJeuk Kim break;
1382329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_WRITE_DESC:
1383329f1662SJeuk Kim /* write descriptor is not supported */
1384329f1662SJeuk Kim status = UFS_QUERY_RESULT_NOT_WRITEABLE;
1385329f1662SJeuk Kim break;
1386329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_WRITE_ATTR:
1387329f1662SJeuk Kim status = ufs_exec_query_attr(req, UFS_QUERY_ATTR_WRITE);
1388329f1662SJeuk Kim break;
1389329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_SET_FLAG:
1390329f1662SJeuk Kim status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_SET);
1391329f1662SJeuk Kim break;
1392329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG:
1393329f1662SJeuk Kim status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_CLEAR);
1394329f1662SJeuk Kim break;
1395329f1662SJeuk Kim case UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG:
1396329f1662SJeuk Kim status = ufs_exec_query_flag(req, UFS_QUERY_FLAG_TOGGLE);
1397329f1662SJeuk Kim break;
1398329f1662SJeuk Kim default:
1399329f1662SJeuk Kim trace_ufs_err_query_invalid_opcode(req->req_upiu.qr.opcode);
1400329f1662SJeuk Kim status = UFS_QUERY_RESULT_INVALID_OPCODE;
1401329f1662SJeuk Kim break;
1402329f1662SJeuk Kim }
1403329f1662SJeuk Kim
1404329f1662SJeuk Kim return status;
1405329f1662SJeuk Kim }
1406329f1662SJeuk Kim
ufs_exec_query_cmd(UfsRequest * req)1407329f1662SJeuk Kim static UfsReqResult ufs_exec_query_cmd(UfsRequest *req)
1408329f1662SJeuk Kim {
1409329f1662SJeuk Kim uint8_t query_func = req->req_upiu.header.query_func;
1410329f1662SJeuk Kim uint16_t data_segment_length;
1411329f1662SJeuk Kim QueryRespCode status;
1412329f1662SJeuk Kim
1413329f1662SJeuk Kim trace_ufs_exec_query_cmd(req->slot, req->req_upiu.qr.opcode);
1414329f1662SJeuk Kim if (query_func == UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST) {
1415329f1662SJeuk Kim status = ufs_exec_query_read(req);
1416329f1662SJeuk Kim } else if (query_func == UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST) {
1417329f1662SJeuk Kim status = ufs_exec_query_write(req);
1418329f1662SJeuk Kim } else {
1419329f1662SJeuk Kim status = UFS_QUERY_RESULT_GENERAL_FAILURE;
1420329f1662SJeuk Kim }
1421329f1662SJeuk Kim
1422329f1662SJeuk Kim data_segment_length = be16_to_cpu(req->rsp_upiu.qr.length);
1423329f1662SJeuk Kim ufs_build_upiu_header(req, UFS_UPIU_TRANSACTION_QUERY_RSP, 0, status, 0,
1424329f1662SJeuk Kim data_segment_length);
1425de2cc407SKyoungrul Kim ufs_build_query_response(req);
1426329f1662SJeuk Kim
1427329f1662SJeuk Kim if (status != UFS_QUERY_RESULT_SUCCESS) {
1428329f1662SJeuk Kim return UFS_REQUEST_FAIL;
1429329f1662SJeuk Kim }
1430329f1662SJeuk Kim return UFS_REQUEST_SUCCESS;
1431329f1662SJeuk Kim }
1432329f1662SJeuk Kim
ufs_exec_req(UfsRequest * req)1433329f1662SJeuk Kim static void ufs_exec_req(UfsRequest *req)
1434329f1662SJeuk Kim {
1435329f1662SJeuk Kim UfsReqResult req_result;
1436329f1662SJeuk Kim
1437329f1662SJeuk Kim if (ufs_dma_read_upiu(req)) {
1438329f1662SJeuk Kim return;
1439329f1662SJeuk Kim }
1440329f1662SJeuk Kim
1441329f1662SJeuk Kim switch (req->req_upiu.header.trans_type) {
1442329f1662SJeuk Kim case UFS_UPIU_TRANSACTION_NOP_OUT:
1443329f1662SJeuk Kim req_result = ufs_exec_nop_cmd(req);
1444329f1662SJeuk Kim break;
1445329f1662SJeuk Kim case UFS_UPIU_TRANSACTION_COMMAND:
14462a8b36a4SJeuk Kim req_result = ufs_exec_scsi_cmd(req);
1447329f1662SJeuk Kim break;
1448329f1662SJeuk Kim case UFS_UPIU_TRANSACTION_QUERY_REQ:
1449329f1662SJeuk Kim req_result = ufs_exec_query_cmd(req);
1450329f1662SJeuk Kim break;
1451329f1662SJeuk Kim default:
1452329f1662SJeuk Kim trace_ufs_err_invalid_trans_code(req->slot,
1453329f1662SJeuk Kim req->req_upiu.header.trans_type);
1454329f1662SJeuk Kim req_result = UFS_REQUEST_FAIL;
1455329f1662SJeuk Kim }
1456329f1662SJeuk Kim
14572a8b36a4SJeuk Kim /*
14582a8b36a4SJeuk Kim * The ufs_complete_req for scsi commands is handled by the
14592a8b36a4SJeuk Kim * ufs_scsi_command_complete() callback function. Therefore, to avoid
14602a8b36a4SJeuk Kim * duplicate processing, ufs_complete_req() is not called for scsi commands.
14612a8b36a4SJeuk Kim */
14622a8b36a4SJeuk Kim if (req_result != UFS_REQUEST_NO_COMPLETE) {
1463329f1662SJeuk Kim ufs_complete_req(req, req_result);
1464329f1662SJeuk Kim }
14652a8b36a4SJeuk Kim }
1466329f1662SJeuk Kim
ufs_process_req(void * opaque)1467329f1662SJeuk Kim static void ufs_process_req(void *opaque)
1468329f1662SJeuk Kim {
1469329f1662SJeuk Kim UfsHc *u = opaque;
1470329f1662SJeuk Kim UfsRequest *req;
1471329f1662SJeuk Kim int slot;
1472329f1662SJeuk Kim
1473329f1662SJeuk Kim for (slot = 0; slot < u->params.nutrs; slot++) {
1474329f1662SJeuk Kim req = &u->req_list[slot];
1475329f1662SJeuk Kim
1476329f1662SJeuk Kim if (req->state != UFS_REQUEST_READY) {
1477329f1662SJeuk Kim continue;
1478329f1662SJeuk Kim }
1479329f1662SJeuk Kim trace_ufs_process_req(slot);
1480329f1662SJeuk Kim req->state = UFS_REQUEST_RUNNING;
1481329f1662SJeuk Kim
1482329f1662SJeuk Kim ufs_exec_req(req);
1483329f1662SJeuk Kim }
1484329f1662SJeuk Kim }
1485329f1662SJeuk Kim
ufs_complete_req(UfsRequest * req,UfsReqResult req_result)1486096434feSJeuk Kim void ufs_complete_req(UfsRequest *req, UfsReqResult req_result)
1487329f1662SJeuk Kim {
1488329f1662SJeuk Kim UfsHc *u = req->hc;
1489329f1662SJeuk Kim assert(req->state == UFS_REQUEST_RUNNING);
1490329f1662SJeuk Kim
1491329f1662SJeuk Kim if (req_result == UFS_REQUEST_SUCCESS) {
1492329f1662SJeuk Kim req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_SUCCESS);
1493329f1662SJeuk Kim } else {
1494329f1662SJeuk Kim req->utrd.header.dword_2 = cpu_to_le32(UFS_OCS_INVALID_CMD_TABLE_ATTR);
1495329f1662SJeuk Kim }
1496329f1662SJeuk Kim
1497329f1662SJeuk Kim req->state = UFS_REQUEST_COMPLETE;
14985c079578SMinwoo Im
14995c079578SMinwoo Im if (ufs_mcq_req(req)) {
15005c079578SMinwoo Im trace_ufs_mcq_complete_req(req->sq->sqid);
15015c079578SMinwoo Im QTAILQ_INSERT_TAIL(&req->sq->cq->req_list, req, entry);
15025c079578SMinwoo Im qemu_bh_schedule(req->sq->cq->bh);
15035c079578SMinwoo Im } else {
15045c079578SMinwoo Im trace_ufs_complete_req(req->slot);
1505329f1662SJeuk Kim qemu_bh_schedule(u->complete_bh);
1506329f1662SJeuk Kim }
15075c079578SMinwoo Im }
1508329f1662SJeuk Kim
ufs_clear_req(UfsRequest * req)1509329f1662SJeuk Kim static void ufs_clear_req(UfsRequest *req)
1510329f1662SJeuk Kim {
1511329f1662SJeuk Kim if (req->sg != NULL) {
1512329f1662SJeuk Kim qemu_sglist_destroy(req->sg);
1513329f1662SJeuk Kim g_free(req->sg);
1514329f1662SJeuk Kim req->sg = NULL;
1515096434feSJeuk Kim req->data_len = 0;
1516329f1662SJeuk Kim }
1517329f1662SJeuk Kim
1518329f1662SJeuk Kim memset(&req->utrd, 0, sizeof(req->utrd));
1519329f1662SJeuk Kim memset(&req->req_upiu, 0, sizeof(req->req_upiu));
1520329f1662SJeuk Kim memset(&req->rsp_upiu, 0, sizeof(req->rsp_upiu));
1521329f1662SJeuk Kim }
1522329f1662SJeuk Kim
ufs_sendback_req(void * opaque)1523329f1662SJeuk Kim static void ufs_sendback_req(void *opaque)
1524329f1662SJeuk Kim {
1525329f1662SJeuk Kim UfsHc *u = opaque;
1526329f1662SJeuk Kim UfsRequest *req;
1527329f1662SJeuk Kim int slot;
1528329f1662SJeuk Kim
1529329f1662SJeuk Kim for (slot = 0; slot < u->params.nutrs; slot++) {
1530329f1662SJeuk Kim req = &u->req_list[slot];
1531329f1662SJeuk Kim
1532329f1662SJeuk Kim if (req->state != UFS_REQUEST_COMPLETE) {
1533329f1662SJeuk Kim continue;
1534329f1662SJeuk Kim }
1535329f1662SJeuk Kim
1536329f1662SJeuk Kim if (ufs_dma_write_upiu(req)) {
1537329f1662SJeuk Kim req->state = UFS_REQUEST_ERROR;
1538329f1662SJeuk Kim continue;
1539329f1662SJeuk Kim }
1540329f1662SJeuk Kim
1541329f1662SJeuk Kim /*
1542329f1662SJeuk Kim * TODO: UTP Transfer Request Interrupt Aggregation Control is not yet
1543329f1662SJeuk Kim * supported
1544329f1662SJeuk Kim */
1545329f1662SJeuk Kim if (le32_to_cpu(req->utrd.header.dword_2) != UFS_OCS_SUCCESS ||
1546329f1662SJeuk Kim le32_to_cpu(req->utrd.header.dword_0) & UFS_UTP_REQ_DESC_INT_CMD) {
1547329f1662SJeuk Kim u->reg.is = FIELD_DP32(u->reg.is, IS, UTRCS, 1);
1548329f1662SJeuk Kim }
1549329f1662SJeuk Kim
1550329f1662SJeuk Kim u->reg.utrldbr &= ~(1 << slot);
1551329f1662SJeuk Kim u->reg.utrlcnr |= (1 << slot);
1552329f1662SJeuk Kim
1553329f1662SJeuk Kim trace_ufs_sendback_req(req->slot);
1554329f1662SJeuk Kim
1555329f1662SJeuk Kim ufs_clear_req(req);
1556329f1662SJeuk Kim req->state = UFS_REQUEST_IDLE;
1557329f1662SJeuk Kim }
1558329f1662SJeuk Kim
1559329f1662SJeuk Kim ufs_irq_check(u);
1560329f1662SJeuk Kim }
1561329f1662SJeuk Kim
ufs_check_constraints(UfsHc * u,Error ** errp)1562bc4e68d3SJeuk Kim static bool ufs_check_constraints(UfsHc *u, Error **errp)
1563bc4e68d3SJeuk Kim {
1564bc4e68d3SJeuk Kim if (u->params.nutrs > UFS_MAX_NUTRS) {
1565bc4e68d3SJeuk Kim error_setg(errp, "nutrs must be less than or equal to %d",
1566bc4e68d3SJeuk Kim UFS_MAX_NUTRS);
1567bc4e68d3SJeuk Kim return false;
1568bc4e68d3SJeuk Kim }
1569bc4e68d3SJeuk Kim
1570bc4e68d3SJeuk Kim if (u->params.nutmrs > UFS_MAX_NUTMRS) {
1571bc4e68d3SJeuk Kim error_setg(errp, "nutmrs must be less than or equal to %d",
1572bc4e68d3SJeuk Kim UFS_MAX_NUTMRS);
1573bc4e68d3SJeuk Kim return false;
1574bc4e68d3SJeuk Kim }
1575bc4e68d3SJeuk Kim
15765c079578SMinwoo Im if (u->params.mcq_maxq >= UFS_MAX_MCQ_QNUM) {
15775c079578SMinwoo Im error_setg(errp, "mcq-maxq must be less than %d", UFS_MAX_MCQ_QNUM);
15785c079578SMinwoo Im return false;
15795c079578SMinwoo Im }
15805c079578SMinwoo Im
1581bc4e68d3SJeuk Kim return true;
1582bc4e68d3SJeuk Kim }
1583bc4e68d3SJeuk Kim
ufs_init_pci(UfsHc * u,PCIDevice * pci_dev)1584bc4e68d3SJeuk Kim static void ufs_init_pci(UfsHc *u, PCIDevice *pci_dev)
1585bc4e68d3SJeuk Kim {
1586bc4e68d3SJeuk Kim uint8_t *pci_conf = pci_dev->config;
1587bc4e68d3SJeuk Kim
1588bc4e68d3SJeuk Kim pci_conf[PCI_INTERRUPT_PIN] = 1;
1589bc4e68d3SJeuk Kim pci_config_set_prog_interface(pci_conf, 0x1);
1590bc4e68d3SJeuk Kim
1591bc4e68d3SJeuk Kim memory_region_init_io(&u->iomem, OBJECT(u), &ufs_mmio_ops, u, "ufs",
1592bc4e68d3SJeuk Kim u->reg_size);
1593bc4e68d3SJeuk Kim pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &u->iomem);
1594bc4e68d3SJeuk Kim u->irq = pci_allocate_irq(pci_dev);
1595bc4e68d3SJeuk Kim }
1596bc4e68d3SJeuk Kim
ufs_init_state(UfsHc * u)1597329f1662SJeuk Kim static void ufs_init_state(UfsHc *u)
1598329f1662SJeuk Kim {
1599329f1662SJeuk Kim u->req_list = g_new0(UfsRequest, u->params.nutrs);
1600329f1662SJeuk Kim
1601329f1662SJeuk Kim for (int i = 0; i < u->params.nutrs; i++) {
1602329f1662SJeuk Kim u->req_list[i].hc = u;
1603329f1662SJeuk Kim u->req_list[i].slot = i;
1604329f1662SJeuk Kim u->req_list[i].sg = NULL;
1605329f1662SJeuk Kim u->req_list[i].state = UFS_REQUEST_IDLE;
1606329f1662SJeuk Kim }
1607329f1662SJeuk Kim
1608329f1662SJeuk Kim u->doorbell_bh = qemu_bh_new_guarded(ufs_process_req, u,
1609329f1662SJeuk Kim &DEVICE(u)->mem_reentrancy_guard);
1610329f1662SJeuk Kim u->complete_bh = qemu_bh_new_guarded(ufs_sendback_req, u,
1611329f1662SJeuk Kim &DEVICE(u)->mem_reentrancy_guard);
16125c079578SMinwoo Im
16135c079578SMinwoo Im if (u->params.mcq) {
16145c079578SMinwoo Im memset(u->sq, 0, sizeof(u->sq));
16155c079578SMinwoo Im memset(u->cq, 0, sizeof(u->cq));
16165c079578SMinwoo Im }
1617329f1662SJeuk Kim }
1618329f1662SJeuk Kim
ufs_init_hc(UfsHc * u)1619bc4e68d3SJeuk Kim static void ufs_init_hc(UfsHc *u)
1620bc4e68d3SJeuk Kim {
1621bc4e68d3SJeuk Kim uint32_t cap = 0;
16225c079578SMinwoo Im uint32_t mcqconfig = 0;
16235c079578SMinwoo Im uint32_t mcqcap = 0;
1624bc4e68d3SJeuk Kim
16255c079578SMinwoo Im u->reg_size = pow2ceil(ufs_reg_size(u));
1626bc4e68d3SJeuk Kim
1627bc4e68d3SJeuk Kim memset(&u->reg, 0, sizeof(u->reg));
16285c079578SMinwoo Im memset(&u->mcq_reg, 0, sizeof(u->mcq_reg));
16295c079578SMinwoo Im memset(&u->mcq_op_reg, 0, sizeof(u->mcq_op_reg));
1630bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, NUTRS, (u->params.nutrs - 1));
1631bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, RTT, 2);
1632bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, NUTMRS, (u->params.nutmrs - 1));
1633bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, AUTOH8, 0);
1634bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, 64AS, 1);
1635bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, OODDS, 0);
1636bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, UICDMETMS, 0);
1637bc4e68d3SJeuk Kim cap = FIELD_DP32(cap, CAP, CS, 0);
16385c079578SMinwoo Im cap = FIELD_DP32(cap, CAP, LSDBS, 1);
16395c079578SMinwoo Im cap = FIELD_DP32(cap, CAP, MCQS, u->params.mcq);
1640bc4e68d3SJeuk Kim u->reg.cap = cap;
16415c079578SMinwoo Im
16425c079578SMinwoo Im if (u->params.mcq) {
16435c079578SMinwoo Im mcqconfig = FIELD_DP32(mcqconfig, MCQCONFIG, MAC, 0x1f);
16445c079578SMinwoo Im u->reg.mcqconfig = mcqconfig;
16455c079578SMinwoo Im
16465c079578SMinwoo Im mcqcap = FIELD_DP32(mcqcap, MCQCAP, MAXQ, u->params.mcq_maxq - 1);
16475c079578SMinwoo Im mcqcap = FIELD_DP32(mcqcap, MCQCAP, RRP, 1);
16485c079578SMinwoo Im mcqcap = FIELD_DP32(mcqcap, MCQCAP, QCFGPTR, UFS_MCQ_QCFGPTR);
16495c079578SMinwoo Im u->reg.mcqcap = mcqcap;
16505c079578SMinwoo Im
16515c079578SMinwoo Im for (int i = 0; i < ARRAY_SIZE(u->mcq_reg); i++) {
16525c079578SMinwoo Im uint64_t addr = ufs_mcq_op_reg_addr(u, i);
16535c079578SMinwoo Im u->mcq_reg[i].sqdao = addr;
16545c079578SMinwoo Im u->mcq_reg[i].sqisao = addr + sizeof(UfsMcqSqReg);
16555c079578SMinwoo Im addr += sizeof(UfsMcqSqReg);
16565c079578SMinwoo Im u->mcq_reg[i].cqdao = addr + sizeof(UfsMcqSqIntReg);
16575c079578SMinwoo Im addr += sizeof(UfsMcqSqIntReg);
16585c079578SMinwoo Im u->mcq_reg[i].cqisao = addr + sizeof(UfsMcqCqReg);
16595c079578SMinwoo Im }
16605c079578SMinwoo Im }
1661bc4e68d3SJeuk Kim u->reg.ver = UFS_SPEC_VER;
1662329f1662SJeuk Kim
1663329f1662SJeuk Kim memset(&u->device_desc, 0, sizeof(DeviceDescriptor));
1664329f1662SJeuk Kim u->device_desc.length = sizeof(DeviceDescriptor);
1665329f1662SJeuk Kim u->device_desc.descriptor_idn = UFS_QUERY_DESC_IDN_DEVICE;
1666329f1662SJeuk Kim u->device_desc.device_sub_class = 0x01;
1667329f1662SJeuk Kim u->device_desc.number_lu = 0x00;
1668329f1662SJeuk Kim u->device_desc.number_wlu = 0x04;
1669329f1662SJeuk Kim /* TODO: Revisit it when Power Management is implemented */
1670329f1662SJeuk Kim u->device_desc.init_power_mode = 0x01; /* Active Mode */
1671329f1662SJeuk Kim u->device_desc.high_priority_lun = 0x7F; /* Same Priority */
1672329f1662SJeuk Kim u->device_desc.spec_version = cpu_to_be16(UFS_SPEC_VER);
1673329f1662SJeuk Kim u->device_desc.manufacturer_name = 0x00;
1674329f1662SJeuk Kim u->device_desc.product_name = 0x01;
1675329f1662SJeuk Kim u->device_desc.serial_number = 0x02;
1676329f1662SJeuk Kim u->device_desc.oem_id = 0x03;
1677329f1662SJeuk Kim u->device_desc.ud_0_base_offset = 0x16;
1678329f1662SJeuk Kim u->device_desc.ud_config_p_length = 0x1A;
1679329f1662SJeuk Kim u->device_desc.device_rtt_cap = 0x02;
1680329f1662SJeuk Kim u->device_desc.queue_depth = u->params.nutrs;
1681329f1662SJeuk Kim u->device_desc.product_revision_level = 0x04;
1682329f1662SJeuk Kim
1683329f1662SJeuk Kim memset(&u->geometry_desc, 0, sizeof(GeometryDescriptor));
1684329f1662SJeuk Kim u->geometry_desc.length = sizeof(GeometryDescriptor);
1685329f1662SJeuk Kim u->geometry_desc.descriptor_idn = UFS_QUERY_DESC_IDN_GEOMETRY;
1686329f1662SJeuk Kim u->geometry_desc.max_number_lu = (UFS_MAX_LUS == 32) ? 0x1 : 0x0;
1687329f1662SJeuk Kim u->geometry_desc.segment_size = cpu_to_be32(0x2000); /* 4KB */
1688329f1662SJeuk Kim u->geometry_desc.allocation_unit_size = 0x1; /* 4KB */
1689329f1662SJeuk Kim u->geometry_desc.min_addr_block_size = 0x8; /* 4KB */
1690329f1662SJeuk Kim u->geometry_desc.max_in_buffer_size = 0x8;
1691329f1662SJeuk Kim u->geometry_desc.max_out_buffer_size = 0x8;
1692329f1662SJeuk Kim u->geometry_desc.rpmb_read_write_size = 0x40;
1693329f1662SJeuk Kim u->geometry_desc.data_ordering =
1694329f1662SJeuk Kim 0x0; /* out-of-order data transfer is not supported */
1695329f1662SJeuk Kim u->geometry_desc.max_context_id_number = 0x5;
1696329f1662SJeuk Kim u->geometry_desc.supported_memory_types = cpu_to_be16(0x8001);
1697329f1662SJeuk Kim
1698329f1662SJeuk Kim memset(&u->attributes, 0, sizeof(u->attributes));
1699329f1662SJeuk Kim u->attributes.max_data_in_size = 0x08;
1700329f1662SJeuk Kim u->attributes.max_data_out_size = 0x08;
1701329f1662SJeuk Kim u->attributes.ref_clk_freq = 0x01; /* 26 MHz */
1702329f1662SJeuk Kim /* configure descriptor is not supported */
1703329f1662SJeuk Kim u->attributes.config_descr_lock = 0x01;
1704329f1662SJeuk Kim u->attributes.max_num_of_rtt = 0x02;
1705329f1662SJeuk Kim
1706329f1662SJeuk Kim memset(&u->flags, 0, sizeof(u->flags));
1707329f1662SJeuk Kim u->flags.permanently_disable_fw_update = 1;
1708bc4e68d3SJeuk Kim }
1709bc4e68d3SJeuk Kim
ufs_realize(PCIDevice * pci_dev,Error ** errp)1710bc4e68d3SJeuk Kim static void ufs_realize(PCIDevice *pci_dev, Error **errp)
1711bc4e68d3SJeuk Kim {
1712bc4e68d3SJeuk Kim UfsHc *u = UFS(pci_dev);
1713bc4e68d3SJeuk Kim
1714bc4e68d3SJeuk Kim if (!ufs_check_constraints(u, errp)) {
1715bc4e68d3SJeuk Kim return;
1716bc4e68d3SJeuk Kim }
1717bc4e68d3SJeuk Kim
17182a8b36a4SJeuk Kim qbus_init(&u->bus, sizeof(UfsBus), TYPE_UFS_BUS, &pci_dev->qdev,
17192a8b36a4SJeuk Kim u->parent_obj.qdev.id);
17202a8b36a4SJeuk Kim
1721329f1662SJeuk Kim ufs_init_state(u);
1722bc4e68d3SJeuk Kim ufs_init_hc(u);
1723bc4e68d3SJeuk Kim ufs_init_pci(u, pci_dev);
17242a8b36a4SJeuk Kim
1725096434feSJeuk Kim ufs_init_wlu(&u->report_wlu, UFS_UPIU_REPORT_LUNS_WLUN);
1726096434feSJeuk Kim ufs_init_wlu(&u->dev_wlu, UFS_UPIU_UFS_DEVICE_WLUN);
1727096434feSJeuk Kim ufs_init_wlu(&u->boot_wlu, UFS_UPIU_BOOT_WLUN);
1728096434feSJeuk Kim ufs_init_wlu(&u->rpmb_wlu, UFS_UPIU_RPMB_WLUN);
1729bc4e68d3SJeuk Kim }
1730bc4e68d3SJeuk Kim
ufs_exit(PCIDevice * pci_dev)1731329f1662SJeuk Kim static void ufs_exit(PCIDevice *pci_dev)
1732329f1662SJeuk Kim {
1733329f1662SJeuk Kim UfsHc *u = UFS(pci_dev);
1734329f1662SJeuk Kim
1735329f1662SJeuk Kim qemu_bh_delete(u->doorbell_bh);
1736329f1662SJeuk Kim qemu_bh_delete(u->complete_bh);
1737329f1662SJeuk Kim
1738329f1662SJeuk Kim for (int i = 0; i < u->params.nutrs; i++) {
1739329f1662SJeuk Kim ufs_clear_req(&u->req_list[i]);
1740329f1662SJeuk Kim }
1741329f1662SJeuk Kim g_free(u->req_list);
17425c079578SMinwoo Im
17435c079578SMinwoo Im for (int i = 0; i < ARRAY_SIZE(u->sq); i++) {
17445c079578SMinwoo Im if (u->sq[i]) {
17455c079578SMinwoo Im ufs_mcq_delete_sq(u, i);
17465c079578SMinwoo Im }
17475c079578SMinwoo Im }
17485c079578SMinwoo Im for (int i = 0; i < ARRAY_SIZE(u->cq); i++) {
17495c079578SMinwoo Im if (u->cq[i]) {
17505c079578SMinwoo Im ufs_mcq_delete_cq(u, i);
17515c079578SMinwoo Im }
17525c079578SMinwoo Im }
1753329f1662SJeuk Kim }
1754329f1662SJeuk Kim
1755bc4e68d3SJeuk Kim static Property ufs_props[] = {
1756bc4e68d3SJeuk Kim DEFINE_PROP_STRING("serial", UfsHc, params.serial),
1757bc4e68d3SJeuk Kim DEFINE_PROP_UINT8("nutrs", UfsHc, params.nutrs, 32),
1758bc4e68d3SJeuk Kim DEFINE_PROP_UINT8("nutmrs", UfsHc, params.nutmrs, 8),
17595c079578SMinwoo Im DEFINE_PROP_BOOL("mcq", UfsHc, params.mcq, false),
17605c079578SMinwoo Im DEFINE_PROP_UINT8("mcq-maxq", UfsHc, params.mcq_maxq, 2),
1761bc4e68d3SJeuk Kim DEFINE_PROP_END_OF_LIST(),
1762bc4e68d3SJeuk Kim };
1763bc4e68d3SJeuk Kim
1764bc4e68d3SJeuk Kim static const VMStateDescription ufs_vmstate = {
1765bc4e68d3SJeuk Kim .name = "ufs",
1766bc4e68d3SJeuk Kim .unmigratable = 1,
1767bc4e68d3SJeuk Kim };
1768bc4e68d3SJeuk Kim
ufs_class_init(ObjectClass * oc,void * data)1769bc4e68d3SJeuk Kim static void ufs_class_init(ObjectClass *oc, void *data)
1770bc4e68d3SJeuk Kim {
1771bc4e68d3SJeuk Kim DeviceClass *dc = DEVICE_CLASS(oc);
1772bc4e68d3SJeuk Kim PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
1773bc4e68d3SJeuk Kim
1774bc4e68d3SJeuk Kim pc->realize = ufs_realize;
1775329f1662SJeuk Kim pc->exit = ufs_exit;
1776bc4e68d3SJeuk Kim pc->vendor_id = PCI_VENDOR_ID_REDHAT;
1777bc4e68d3SJeuk Kim pc->device_id = PCI_DEVICE_ID_REDHAT_UFS;
1778bc4e68d3SJeuk Kim pc->class_id = PCI_CLASS_STORAGE_UFS;
1779bc4e68d3SJeuk Kim
1780bc4e68d3SJeuk Kim set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
1781bc4e68d3SJeuk Kim dc->desc = "Universal Flash Storage";
1782bc4e68d3SJeuk Kim device_class_set_props(dc, ufs_props);
1783bc4e68d3SJeuk Kim dc->vmsd = &ufs_vmstate;
1784bc4e68d3SJeuk Kim }
1785bc4e68d3SJeuk Kim
ufs_bus_check_address(BusState * qbus,DeviceState * qdev,Error ** errp)17862a8b36a4SJeuk Kim static bool ufs_bus_check_address(BusState *qbus, DeviceState *qdev,
17872a8b36a4SJeuk Kim Error **errp)
17882a8b36a4SJeuk Kim {
1789096434feSJeuk Kim if (strcmp(object_get_typename(OBJECT(qdev)), TYPE_UFS_LU) != 0) {
1790096434feSJeuk Kim error_setg(errp, "%s cannot be connected to ufs-bus",
1791096434feSJeuk Kim object_get_typename(OBJECT(qdev)));
17922a8b36a4SJeuk Kim return false;
17932a8b36a4SJeuk Kim }
17942a8b36a4SJeuk Kim
17952a8b36a4SJeuk Kim return true;
17962a8b36a4SJeuk Kim }
17972a8b36a4SJeuk Kim
ufs_bus_get_dev_path(DeviceState * dev)179880a37b03SAkinobu Mita static char *ufs_bus_get_dev_path(DeviceState *dev)
179980a37b03SAkinobu Mita {
180080a37b03SAkinobu Mita BusState *bus = qdev_get_parent_bus(dev);
180180a37b03SAkinobu Mita
180280a37b03SAkinobu Mita return qdev_get_dev_path(bus->parent);
180380a37b03SAkinobu Mita }
180480a37b03SAkinobu Mita
ufs_bus_class_init(ObjectClass * class,void * data)18052a8b36a4SJeuk Kim static void ufs_bus_class_init(ObjectClass *class, void *data)
18062a8b36a4SJeuk Kim {
18072a8b36a4SJeuk Kim BusClass *bc = BUS_CLASS(class);
180880a37b03SAkinobu Mita bc->get_dev_path = ufs_bus_get_dev_path;
18092a8b36a4SJeuk Kim bc->check_address = ufs_bus_check_address;
18102a8b36a4SJeuk Kim }
18112a8b36a4SJeuk Kim
1812bc4e68d3SJeuk Kim static const TypeInfo ufs_info = {
1813bc4e68d3SJeuk Kim .name = TYPE_UFS,
1814bc4e68d3SJeuk Kim .parent = TYPE_PCI_DEVICE,
1815bc4e68d3SJeuk Kim .class_init = ufs_class_init,
1816bc4e68d3SJeuk Kim .instance_size = sizeof(UfsHc),
1817bc4e68d3SJeuk Kim .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} },
1818bc4e68d3SJeuk Kim };
1819bc4e68d3SJeuk Kim
18202a8b36a4SJeuk Kim static const TypeInfo ufs_bus_info = {
18212a8b36a4SJeuk Kim .name = TYPE_UFS_BUS,
1822096434feSJeuk Kim .parent = TYPE_BUS,
18232a8b36a4SJeuk Kim .class_init = ufs_bus_class_init,
18242a8b36a4SJeuk Kim .class_size = sizeof(UfsBusClass),
18252a8b36a4SJeuk Kim .instance_size = sizeof(UfsBus),
18262a8b36a4SJeuk Kim };
18272a8b36a4SJeuk Kim
ufs_register_types(void)1828bc4e68d3SJeuk Kim static void ufs_register_types(void)
1829bc4e68d3SJeuk Kim {
1830bc4e68d3SJeuk Kim type_register_static(&ufs_info);
18312a8b36a4SJeuk Kim type_register_static(&ufs_bus_info);
1832bc4e68d3SJeuk Kim }
1833bc4e68d3SJeuk Kim
1834bc4e68d3SJeuk Kim type_init(ufs_register_types)
1835