blob: dbfcd0ff54356674d794d6cb7ced3d4ed998b6cb [file] [log] [blame]
Haojian Zhuang20cd3232017-05-31 11:00:15 +08001/*
2 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch_helpers.h>
8#include <assert.h>
9#include <debug.h>
10#include <delay_timer.h>
11#include <endian.h>
12#include <errno.h>
13#include <mmio.h>
14#include <platform_def.h>
15#include <stdint.h>
16#include <string.h>
17#include <ufs.h>
18
19#define CDB_ADDR_MASK 127
20#define ALIGN_CDB(x) (((x) + CDB_ADDR_MASK) & ~CDB_ADDR_MASK)
21#define ALIGN_8(x) (((x) + 7) & ~7)
22
23#define UFS_DESC_SIZE 0x400
24#define MAX_UFS_DESC_SIZE 0x8000 /* 32 descriptors */
25
26#define MAX_PRDT_SIZE 0x40000 /* 256KB */
27
28static ufs_params_t ufs_params;
29static int nutrs; /* Number of UTP Transfer Request Slots */
30
31int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd)
32{
33 unsigned int data;
34
35 data = mmio_read_32(base + HCS);
36 if ((data & HCS_UCRDY) == 0)
37 return -EBUSY;
38 mmio_write_32(base + IS, ~0);
39 mmio_write_32(base + UCMDARG1, cmd->arg1);
40 mmio_write_32(base + UCMDARG2, cmd->arg2);
41 mmio_write_32(base + UCMDARG3, cmd->arg3);
42 mmio_write_32(base + UICCMD, cmd->op);
43
44 do {
45 data = mmio_read_32(base + IS);
46 } while ((data & UFS_INT_UCCS) == 0);
47 mmio_write_32(base + IS, UFS_INT_UCCS);
48 return mmio_read_32(base + UCMDARG2) && CONFIG_RESULT_CODE_MASK;
49}
50
51int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val)
52{
53 uintptr_t base;
54 unsigned int data;
55 int retries;
56
57 assert((ufs_params.reg_base != 0) && (val != NULL));
58
59 base = ufs_params.reg_base;
60 for (retries = 0; retries < 100; retries++) {
61 data = mmio_read_32(base + HCS);
62 if ((data & HCS_UCRDY) != 0)
63 break;
64 mdelay(1);
65 }
66 if (retries >= 100)
67 return -EBUSY;
68
69 mmio_write_32(base + IS, ~0);
70 mmio_write_32(base + UCMDARG1, (attr << 16) | GEN_SELECTOR_IDX(idx));
71 mmio_write_32(base + UCMDARG2, 0);
72 mmio_write_32(base + UCMDARG3, 0);
73 mmio_write_32(base + UICCMD, DME_GET);
74 do {
75 data = mmio_read_32(base + IS);
76 if (data & UFS_INT_UE)
77 return -EINVAL;
78 } while ((data & UFS_INT_UCCS) == 0);
79 mmio_write_32(base + IS, UFS_INT_UCCS);
80 data = mmio_read_32(base + UCMDARG2) && CONFIG_RESULT_CODE_MASK;
81 assert(data == 0);
82
83 *val = mmio_read_32(base + UCMDARG3);
84 return 0;
85}
86
87int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val)
88{
89 uintptr_t base;
90 unsigned int data;
91
92 assert((ufs_params.reg_base != 0));
93
94 base = ufs_params.reg_base;
95 data = mmio_read_32(base + HCS);
96 if ((data & HCS_UCRDY) == 0)
97 return -EBUSY;
98 mmio_write_32(base + IS, ~0);
99 mmio_write_32(base + UCMDARG1, (attr << 16) | GEN_SELECTOR_IDX(idx));
100 mmio_write_32(base + UCMDARG2, 0);
101 mmio_write_32(base + UCMDARG3, val);
102 mmio_write_32(base + UICCMD, DME_SET);
103 do {
104 data = mmio_read_32(base + IS);
105 if (data & UFS_INT_UE)
106 return -EINVAL;
107 } while ((data & UFS_INT_UCCS) == 0);
108 mmio_write_32(base + IS, UFS_INT_UCCS);
109 data = mmio_read_32(base + UCMDARG2) && CONFIG_RESULT_CODE_MASK;
110 assert(data == 0);
111 return 0;
112}
113
114static void ufshc_reset(uintptr_t base)
115{
116 unsigned int data;
117
118 /* Enable Host Controller */
119 mmio_write_32(base + HCE, HCE_ENABLE);
120 /* Wait until basic initialization sequence completed */
121 do {
122 data = mmio_read_32(base + HCE);
123 } while ((data & HCE_ENABLE) == 0);
124
125 /* Enable Interrupts */
126 data = UFS_INT_UCCS | UFS_INT_ULSS | UFS_INT_UE | UFS_INT_UTPES |
127 UFS_INT_DFES | UFS_INT_HCFES | UFS_INT_SBFES;
128 mmio_write_32(base + IE, data);
129}
130
131static int ufshc_link_startup(uintptr_t base)
132{
133 uic_cmd_t cmd;
134 int data, result;
135 int retries;
136
137 for (retries = 10; retries > 0; retries--) {
138 memset(&cmd, 0, sizeof(cmd));
139 cmd.op = DME_LINKSTARTUP;
140 result = ufshc_send_uic_cmd(base, &cmd);
141 if (result != 0)
142 continue;
143 while ((mmio_read_32(base + HCS) & HCS_DP) == 0)
144 ;
145 data = mmio_read_32(base + IS);
146 if (data & UFS_INT_ULSS)
147 mmio_write_32(base + IS, UFS_INT_ULSS);
148 return 0;
149 }
150 return -EIO;
151}
152
153/* Check Door Bell register to get an empty slot */
154static int get_empty_slot(int *slot)
155{
156 unsigned int data;
157 int i;
158
159 data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
160 for (i = 0; i < nutrs; i++) {
161 if ((data & 1) == 0)
162 break;
163 data = data >> 1;
164 }
165 if (i >= nutrs)
166 return -EBUSY;
167 *slot = i;
168 return 0;
169}
170
171static void get_utrd(utp_utrd_t *utrd)
172{
173 uintptr_t base;
174 int slot = 0, result;
175 utrd_header_t *hd;
176
177 assert(utrd != NULL);
178 result = get_empty_slot(&slot);
179 assert(result == 0);
180
181 /* clear utrd */
182 memset((void *)utrd, 0, sizeof(utp_utrd_t));
183 base = ufs_params.desc_base + (slot * UFS_DESC_SIZE);
184 /* clear the descriptor */
185 memset((void *)base, 0, UFS_DESC_SIZE);
186
187 utrd->header = base;
188 utrd->task_tag = slot + 1;
189 /* CDB address should be aligned with 128 bytes */
190 utrd->upiu = ALIGN_CDB(utrd->header + sizeof(utrd_header_t));
191 utrd->resp_upiu = ALIGN_8(utrd->upiu + sizeof(cmd_upiu_t));
192 utrd->size_upiu = utrd->resp_upiu - utrd->upiu;
193 utrd->size_resp_upiu = ALIGN_8(sizeof(resp_upiu_t));
194 utrd->prdt = utrd->resp_upiu + utrd->size_resp_upiu;
195
196 hd = (utrd_header_t *)utrd->header;
197 hd->ucdba = utrd->upiu & UINT32_MAX;
198 hd->ucdbau = (utrd->upiu >> 32) & UINT32_MAX;
199 /* Both RUL and RUO is based on DWORD */
200 hd->rul = utrd->size_resp_upiu >> 2;
201 hd->ruo = utrd->size_upiu >> 2;
202 (void)result;
203}
204
205/*
206 * Prepare UTRD, Command UPIU, Response UPIU.
207 */
208static int ufs_prepare_cmd(utp_utrd_t *utrd, uint8_t op, uint8_t lun,
209 int lba, uintptr_t buf, size_t length)
210{
211 utrd_header_t *hd;
212 cmd_upiu_t *upiu;
213 prdt_t *prdt;
214 unsigned int ulba;
215 unsigned int lba_cnt;
216 int prdt_size;
217
218
219 mmio_write_32(ufs_params.reg_base + UTRLBA,
220 utrd->header & UINT32_MAX);
221 mmio_write_32(ufs_params.reg_base + UTRLBAU,
222 (utrd->upiu >> 32) & UINT32_MAX);
223
224 hd = (utrd_header_t *)utrd->header;
225 upiu = (cmd_upiu_t *)utrd->upiu;
226
227 hd->i = 1;
228 hd->ct = CT_UFS_STORAGE;
229 hd->ocs = OCS_MASK;
230
231 upiu->trans_type = CMD_UPIU;
232 upiu->task_tag = utrd->task_tag;
233 upiu->cdb[0] = op;
234 ulba = (unsigned int)lba;
235 lba_cnt = (unsigned int)(length >> UFS_BLOCK_SHIFT);
236 switch (op) {
237 case CDBCMD_TEST_UNIT_READY:
238 break;
239 case CDBCMD_READ_CAPACITY_10:
240 hd->dd = DD_OUT;
241 upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
242 upiu->lun = lun;
243 break;
244 case CDBCMD_READ_10:
245 hd->dd = DD_OUT;
246 upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
247 upiu->lun = lun;
248 upiu->cdb[1] = RW_WITHOUT_CACHE;
249 /* set logical block address */
250 upiu->cdb[2] = (ulba >> 24) & 0xff;
251 upiu->cdb[3] = (ulba >> 16) & 0xff;
252 upiu->cdb[4] = (ulba >> 8) & 0xff;
253 upiu->cdb[5] = ulba & 0xff;
254 /* set transfer length */
255 upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
256 upiu->cdb[8] = lba_cnt & 0xff;
257 break;
258 case CDBCMD_WRITE_10:
259 hd->dd = DD_IN;
260 upiu->flags = UPIU_FLAGS_W | UPIU_FLAGS_ATTR_S;
261 upiu->lun = lun;
262 upiu->cdb[1] = RW_WITHOUT_CACHE;
263 /* set logical block address */
264 upiu->cdb[2] = (ulba >> 24) & 0xff;
265 upiu->cdb[3] = (ulba >> 16) & 0xff;
266 upiu->cdb[4] = (ulba >> 8) & 0xff;
267 upiu->cdb[5] = ulba & 0xff;
268 /* set transfer length */
269 upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
270 upiu->cdb[8] = lba_cnt & 0xff;
271 break;
272 default:
273 assert(0);
274 }
275 if (hd->dd == DD_IN)
276 flush_dcache_range(buf, length);
277 else if (hd->dd == DD_OUT)
278 inv_dcache_range(buf, length);
279 if (length) {
280 upiu->exp_data_trans_len = htobe32(length);
281 assert(lba_cnt <= UINT16_MAX);
282 prdt = (prdt_t *)utrd->prdt;
283
284 prdt_size = 0;
285 while (length > 0) {
286 prdt->dba = (unsigned int)(buf & UINT32_MAX);
287 prdt->dbau = (unsigned int)((buf >> 32) & UINT32_MAX);
288 /* prdt->dbc counts from 0 */
289 if (length > MAX_PRDT_SIZE) {
290 prdt->dbc = MAX_PRDT_SIZE - 1;
291 length = length - MAX_PRDT_SIZE;
292 } else {
293 prdt->dbc = length - 1;
294 length = 0;
295 }
296 buf += MAX_PRDT_SIZE;
297 prdt++;
298 prdt_size += sizeof(prdt_t);
299 }
300 utrd->size_prdt = ALIGN_8(prdt_size);
301 hd->prdtl = utrd->size_prdt >> 2;
302 hd->prdto = (utrd->size_upiu + utrd->size_resp_upiu) >> 2;
303 }
304
305 flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
306 flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
307 return 0;
308}
309
310static int ufs_prepare_query(utp_utrd_t *utrd, uint8_t op, uint8_t idn,
311 uint8_t index, uint8_t sel,
312 uintptr_t buf, size_t length)
313{
314 utrd_header_t *hd;
315 query_upiu_t *query_upiu;
316
317
318 hd = (utrd_header_t *)utrd->header;
319 query_upiu = (query_upiu_t *)utrd->upiu;
320
321 mmio_write_32(ufs_params.reg_base + UTRLBA,
322 utrd->header & UINT32_MAX);
323 mmio_write_32(ufs_params.reg_base + UTRLBAU,
324 (utrd->header >> 32) & UINT32_MAX);
325
326
327 hd->i = 1;
328 hd->ct = CT_UFS_STORAGE;
329 hd->ocs = OCS_MASK;
330
331 query_upiu->trans_type = QUERY_REQUEST_UPIU;
332 query_upiu->task_tag = utrd->task_tag;
333 query_upiu->ts.desc.opcode = op;
334 query_upiu->ts.desc.idn = idn;
335 query_upiu->ts.desc.index = index;
336 query_upiu->ts.desc.selector = sel;
337 switch (op) {
338 case QUERY_READ_DESC:
339 query_upiu->query_func = QUERY_FUNC_STD_READ;
340 query_upiu->ts.desc.length = htobe16(length);
341 break;
342 case QUERY_WRITE_DESC:
343 query_upiu->query_func = QUERY_FUNC_STD_WRITE;
344 query_upiu->ts.desc.length = htobe16(length);
345 memcpy((void *)(utrd->upiu + sizeof(query_upiu_t)),
346 (void *)buf, length);
347 break;
348 case QUERY_READ_ATTR:
349 case QUERY_READ_FLAG:
350 query_upiu->query_func = QUERY_FUNC_STD_READ;
351 break;
352 case QUERY_CLEAR_FLAG:
353 case QUERY_SET_FLAG:
354 query_upiu->query_func = QUERY_FUNC_STD_WRITE;
355 break;
356 case QUERY_WRITE_ATTR:
357 query_upiu->query_func = QUERY_FUNC_STD_WRITE;
358 memcpy((void *)&query_upiu->ts.attr.value, (void *)buf, length);
359 break;
360 default:
361 assert(0);
362 }
363 flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
364 flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
365 return 0;
366}
367
368static void ufs_prepare_nop_out(utp_utrd_t *utrd)
369{
370 utrd_header_t *hd;
371 nop_out_upiu_t *nop_out;
372
373 mmio_write_32(ufs_params.reg_base + UTRLBA,
374 utrd->header & UINT32_MAX);
375 mmio_write_32(ufs_params.reg_base + UTRLBAU,
376 (utrd->header >> 32) & UINT32_MAX);
377
378 hd = (utrd_header_t *)utrd->header;
379 nop_out = (nop_out_upiu_t *)utrd->upiu;
380
381 hd->i = 1;
382 hd->ct = CT_UFS_STORAGE;
383 hd->ocs = OCS_MASK;
384
385 nop_out->trans_type = 0;
386 nop_out->task_tag = utrd->task_tag;
387 flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
388 flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
389}
390
391static void ufs_send_request(int task_tag)
392{
393 unsigned int data;
394 int slot;
395
396 slot = task_tag - 1;
397 /* clear all interrupts */
398 mmio_write_32(ufs_params.reg_base + IS, ~0);
399
400 mmio_write_32(ufs_params.reg_base + UTRLRSR, 1);
401 do {
402 data = mmio_read_32(ufs_params.reg_base + UTRLRSR);
403 } while (data == 0);
404
405 data = UTRIACR_IAEN | UTRIACR_CTR | UTRIACR_IACTH(0x1F) |
406 UTRIACR_IATOVAL(0xFF);
407 mmio_write_32(ufs_params.reg_base + UTRIACR, data);
408 /* send request */
409 mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1 << slot);
410}
411
412static int ufs_check_resp(utp_utrd_t *utrd, int trans_type)
413{
414 utrd_header_t *hd;
415 resp_upiu_t *resp;
416 unsigned int data;
417 int slot;
418
419 hd = (utrd_header_t *)utrd->header;
420 resp = (resp_upiu_t *)utrd->resp_upiu;
421 inv_dcache_range((uintptr_t)hd, UFS_DESC_SIZE);
422 inv_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
423 do {
424 data = mmio_read_32(ufs_params.reg_base + IS);
425 if ((data & ~(UFS_INT_UCCS | UFS_INT_UTRCS)) != 0)
426 return -EIO;
427 } while ((data & UFS_INT_UTRCS) == 0);
428 slot = utrd->task_tag - 1;
429
430 data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
431 assert((data & (1 << slot)) == 0);
432 assert(hd->ocs == OCS_SUCCESS);
433 assert((resp->trans_type & TRANS_TYPE_CODE_MASK) == trans_type);
434 (void)resp;
435 (void)slot;
436 return 0;
437}
438
439#ifdef UFS_RESP_DEBUG
440static void dump_upiu(utp_utrd_t *utrd)
441{
442 utrd_header_t *hd;
443 int i;
444
445 hd = (utrd_header_t *)utrd->header;
446 INFO("utrd:0x%x, ruo:0x%x, rul:0x%x, ocs:0x%x, UTRLDBR:0x%x\n",
447 (unsigned int)(uintptr_t)utrd, hd->ruo, hd->rul, hd->ocs,
448 mmio_read_32(ufs_params.reg_base + UTRLDBR));
449 for (i = 0; i < sizeof(utrd_header_t); i += 4) {
450 INFO("[%lx]:0x%x\n",
451 (uintptr_t)utrd->header + i,
452 *(unsigned int *)((uintptr_t)utrd->header + i));
453 }
454
455 for (i = 0; i < sizeof(cmd_upiu_t); i += 4) {
456 INFO("cmd[%lx]:0x%x\n",
457 utrd->upiu + i,
458 *(unsigned int *)(utrd->upiu + i));
459 }
460 for (i = 0; i < sizeof(resp_upiu_t); i += 4) {
461 INFO("resp[%lx]:0x%x\n",
462 utrd->resp_upiu + i,
463 *(unsigned int *)(utrd->resp_upiu + i));
464 }
465 for (i = 0; i < sizeof(prdt_t); i += 4) {
466 INFO("prdt[%lx]:0x%x\n",
467 utrd->prdt + i,
468 *(unsigned int *)(utrd->prdt + i));
469 }
470}
471#endif
472
473static void ufs_verify_init(void)
474{
475 utp_utrd_t utrd;
476 int result;
477
478 get_utrd(&utrd);
479 ufs_prepare_nop_out(&utrd);
480 ufs_send_request(utrd.task_tag);
481 result = ufs_check_resp(&utrd, NOP_IN_UPIU);
482 assert(result == 0);
483 (void)result;
484}
485
486static void ufs_verify_ready(void)
487{
488 utp_utrd_t utrd;
489 int result;
490
491 get_utrd(&utrd);
492 ufs_prepare_cmd(&utrd, CDBCMD_TEST_UNIT_READY, 0, 0, 0, 0);
493 ufs_send_request(utrd.task_tag);
494 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
495 assert(result == 0);
496 (void)result;
497}
498
499static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel,
500 uintptr_t buf, size_t size)
501{
502 utp_utrd_t utrd;
503 query_resp_upiu_t *resp;
504 int result;
505
506 switch (op) {
507 case QUERY_READ_FLAG:
508 case QUERY_READ_ATTR:
509 case QUERY_READ_DESC:
510 case QUERY_WRITE_DESC:
511 case QUERY_WRITE_ATTR:
512 assert(((buf & 3) == 0) && (size != 0));
513 break;
514 }
515 get_utrd(&utrd);
516 ufs_prepare_query(&utrd, op, idn, index, sel, buf, size);
517 ufs_send_request(utrd.task_tag);
518 result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU);
519 assert(result == 0);
520 resp = (query_resp_upiu_t *)utrd.resp_upiu;
521#ifdef UFS_RESP_DEBUG
522 dump_upiu(&utrd);
523#endif
524 assert(resp->query_resp == QUERY_RESP_SUCCESS);
525
526 switch (op) {
527 case QUERY_READ_FLAG:
528 *(uint32_t *)buf = (uint32_t)resp->ts.flag.value;
529 break;
530 case QUERY_READ_ATTR:
531 case QUERY_READ_DESC:
532 memcpy((void *)buf,
533 (void *)(utrd.resp_upiu + sizeof(query_resp_upiu_t)),
534 size);
535 break;
536 }
537 (void)result;
538}
539
540unsigned int ufs_read_attr(int idn)
541{
542 unsigned int value;
543
544 ufs_query(QUERY_READ_ATTR, idn, 0, 0,
545 (uintptr_t)&value, sizeof(value));
546 return value;
547}
548
549void ufs_write_attr(int idn, unsigned int value)
550{
551 ufs_query(QUERY_WRITE_ATTR, idn, 0, 0,
552 (uintptr_t)&value, sizeof(value));
553}
554
555unsigned int ufs_read_flag(int idn)
556{
557 unsigned int value;
558
559 ufs_query(QUERY_READ_FLAG, idn, 0, 0,
560 (uintptr_t)&value, sizeof(value));
561 return value;
562}
563
564void ufs_set_flag(int idn)
565{
566 ufs_query(QUERY_SET_FLAG, idn, 0, 0, 0, 0);
567}
568
569void ufs_clear_flag(int idn)
570{
571 ufs_query(QUERY_CLEAR_FLAG, idn, 0, 0, 0, 0);
572}
573
574void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size)
575{
576 ufs_query(QUERY_READ_DESC, idn, index, 0, buf, size);
577}
578
579void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size)
580{
581 ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size);
582}
583
584void ufs_read_capacity(int lun, unsigned int *num, unsigned int *size)
585{
586 utp_utrd_t utrd;
587 resp_upiu_t *resp;
588 sense_data_t *sense;
589 unsigned char data[CACHE_WRITEBACK_GRANULE << 1];
590 uintptr_t buf;
591 int result;
592 int retry;
593
594 assert((ufs_params.reg_base != 0) &&
595 (ufs_params.desc_base != 0) &&
596 (ufs_params.desc_size >= UFS_DESC_SIZE) &&
597 (num != NULL) && (size != NULL));
598
599 /* align buf address */
600 buf = (uintptr_t)data;
601 buf = (buf + CACHE_WRITEBACK_GRANULE - 1) &
602 ~(CACHE_WRITEBACK_GRANULE - 1);
603 memset((void *)buf, 0, CACHE_WRITEBACK_GRANULE);
604 flush_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
605 do {
606 get_utrd(&utrd);
607 ufs_prepare_cmd(&utrd, CDBCMD_READ_CAPACITY_10, lun, 0,
608 buf, READ_CAPACITY_LENGTH);
609 ufs_send_request(utrd.task_tag);
610 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
611 assert(result == 0);
612#ifdef UFS_RESP_DEBUG
613 dump_upiu(&utrd);
614#endif
615 resp = (resp_upiu_t *)utrd.resp_upiu;
616 retry = 0;
617 sense = &resp->sd.sense;
618 if (sense->resp_code == SENSE_DATA_VALID) {
619 if ((sense->sense_key == SENSE_KEY_UNIT_ATTENTION) &&
620 (sense->asc == 0x29) && (sense->ascq == 0)) {
621 retry = 1;
622 }
623 }
624 inv_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
625 /* last logical block address */
626 *num = be32toh(*(unsigned int *)buf);
627 if (*num)
628 *num += 1;
629 /* logical block length in bytes */
630 *size = be32toh(*(unsigned int *)(buf + 4));
631 } while (retry);
632 (void)result;
633}
634
635size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size)
636{
637 utp_utrd_t utrd;
638 resp_upiu_t *resp;
639 int result;
640
641 assert((ufs_params.reg_base != 0) &&
642 (ufs_params.desc_base != 0) &&
643 (ufs_params.desc_size >= UFS_DESC_SIZE));
644
645 memset((void *)buf, 0, size);
646 get_utrd(&utrd);
647 ufs_prepare_cmd(&utrd, CDBCMD_READ_10, lun, lba, buf, size);
648 ufs_send_request(utrd.task_tag);
649 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
650 assert(result == 0);
651#ifdef UFS_RESP_DEBUG
652 dump_upiu(&utrd);
653#endif
654 resp = (resp_upiu_t *)utrd.resp_upiu;
655 (void)result;
656 return size - resp->res_trans_cnt;
657}
658
659size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size)
660{
661 utp_utrd_t utrd;
662 resp_upiu_t *resp;
663 int result;
664
665 assert((ufs_params.reg_base != 0) &&
666 (ufs_params.desc_base != 0) &&
667 (ufs_params.desc_size >= UFS_DESC_SIZE));
668
669 memset((void *)buf, 0, size);
670 get_utrd(&utrd);
671 ufs_prepare_cmd(&utrd, CDBCMD_WRITE_10, lun, lba, buf, size);
672 ufs_send_request(utrd.task_tag);
673 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
674 assert(result == 0);
675#ifdef UFS_RESP_DEBUG
676 dump_upiu(&utrd);
677#endif
678 resp = (resp_upiu_t *)utrd.resp_upiu;
679 (void)result;
680 return size - resp->res_trans_cnt;
681}
682
683static void ufs_enum(void)
684{
685 unsigned int blk_num, blk_size;
686 int i;
687
688 /* 0 means 1 slot */
689 nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;
690 if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE))
691 nutrs = ufs_params.desc_size / UFS_DESC_SIZE;
692
693 ufs_verify_init();
694 ufs_verify_ready();
695
696 ufs_set_flag(FLAG_DEVICE_INIT);
697 mdelay(100);
698 /* dump available LUNs */
699 for (i = 0; i < UFS_MAX_LUNS; i++) {
700 ufs_read_capacity(i, &blk_num, &blk_size);
701 if (blk_num && blk_size) {
702 INFO("UFS LUN%d contains %d blocks with %d-byte size\n",
703 i, blk_num, blk_size);
704 }
705 }
706}
707
708int ufs_init(const ufs_ops_t *ops, ufs_params_t *params)
709{
710 int result;
711 unsigned int data;
712 uic_cmd_t cmd;
713
714 assert((params != NULL) &&
715 (params->reg_base != 0) &&
716 (params->desc_base != 0) &&
717 (params->desc_size >= UFS_DESC_SIZE));
718
719 memcpy(&ufs_params, params, sizeof(ufs_params_t));
720
721 if (ufs_params.flags & UFS_FLAGS_SKIPINIT) {
722 result = ufshc_dme_get(0x1571, 0, &data);
723 assert(result == 0);
724 result = ufshc_dme_get(0x41, 0, &data);
725 assert(result == 0);
726 if (data == 1) {
727 /* prepare to exit hibernate mode */
728 memset(&cmd, 0, sizeof(uic_cmd_t));
729 cmd.op = DME_HIBERNATE_EXIT;
730 result = ufshc_send_uic_cmd(ufs_params.reg_base,
731 &cmd);
732 assert(result == 0);
733 data = mmio_read_32(ufs_params.reg_base + UCMDARG2);
734 assert(data == 0);
735 do {
736 data = mmio_read_32(ufs_params.reg_base + IS);
737 } while ((data & UFS_INT_UHXS) == 0);
738 mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS);
739 data = mmio_read_32(ufs_params.reg_base + HCS);
740 assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL);
741 }
742 result = ufshc_dme_get(0x1568, 0, &data);
743 assert(result == 0);
744 assert((data > 0) && (data <= 3));
745 } else {
746 assert((ops != NULL) && (ops->phy_init != NULL) &&
747 (ops->phy_set_pwr_mode != NULL));
748
749 ufshc_reset(ufs_params.reg_base);
750 ops->phy_init(&ufs_params);
751 result = ufshc_link_startup(ufs_params.reg_base);
752 assert(result == 0);
753 ops->phy_set_pwr_mode(&ufs_params);
754 }
755
756 ufs_enum();
757 (void)result;
758 return 0;
759}