blob: 515fc2785ef7a2fb3842f48bd020dfb92c4c1902 [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);
Haojian Zhuang836eadc2017-06-12 22:18:15 +080048 return mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK;
Haojian Zhuang20cd3232017-05-31 11:00:15 +080049}
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);
Haojian Zhuang836eadc2017-06-12 22:18:15 +080080 data = mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK;
Haojian Zhuang20cd3232017-05-31 11:00:15 +080081 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);
Haojian Zhuang836eadc2017-06-12 22:18:15 +0800109 data = mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK;
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800110 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);
Jonathan Wright39b42212018-03-13 15:24:29 +0000274 break;
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800275 }
276 if (hd->dd == DD_IN)
277 flush_dcache_range(buf, length);
278 else if (hd->dd == DD_OUT)
279 inv_dcache_range(buf, length);
280 if (length) {
281 upiu->exp_data_trans_len = htobe32(length);
282 assert(lba_cnt <= UINT16_MAX);
283 prdt = (prdt_t *)utrd->prdt;
284
285 prdt_size = 0;
286 while (length > 0) {
287 prdt->dba = (unsigned int)(buf & UINT32_MAX);
288 prdt->dbau = (unsigned int)((buf >> 32) & UINT32_MAX);
289 /* prdt->dbc counts from 0 */
290 if (length > MAX_PRDT_SIZE) {
291 prdt->dbc = MAX_PRDT_SIZE - 1;
292 length = length - MAX_PRDT_SIZE;
293 } else {
294 prdt->dbc = length - 1;
295 length = 0;
296 }
297 buf += MAX_PRDT_SIZE;
298 prdt++;
299 prdt_size += sizeof(prdt_t);
300 }
301 utrd->size_prdt = ALIGN_8(prdt_size);
302 hd->prdtl = utrd->size_prdt >> 2;
303 hd->prdto = (utrd->size_upiu + utrd->size_resp_upiu) >> 2;
304 }
305
306 flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
307 flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
308 return 0;
309}
310
311static int ufs_prepare_query(utp_utrd_t *utrd, uint8_t op, uint8_t idn,
312 uint8_t index, uint8_t sel,
313 uintptr_t buf, size_t length)
314{
315 utrd_header_t *hd;
316 query_upiu_t *query_upiu;
317
318
319 hd = (utrd_header_t *)utrd->header;
320 query_upiu = (query_upiu_t *)utrd->upiu;
321
322 mmio_write_32(ufs_params.reg_base + UTRLBA,
323 utrd->header & UINT32_MAX);
324 mmio_write_32(ufs_params.reg_base + UTRLBAU,
325 (utrd->header >> 32) & UINT32_MAX);
326
327
328 hd->i = 1;
329 hd->ct = CT_UFS_STORAGE;
330 hd->ocs = OCS_MASK;
331
332 query_upiu->trans_type = QUERY_REQUEST_UPIU;
333 query_upiu->task_tag = utrd->task_tag;
334 query_upiu->ts.desc.opcode = op;
335 query_upiu->ts.desc.idn = idn;
336 query_upiu->ts.desc.index = index;
337 query_upiu->ts.desc.selector = sel;
338 switch (op) {
339 case QUERY_READ_DESC:
340 query_upiu->query_func = QUERY_FUNC_STD_READ;
341 query_upiu->ts.desc.length = htobe16(length);
342 break;
343 case QUERY_WRITE_DESC:
344 query_upiu->query_func = QUERY_FUNC_STD_WRITE;
345 query_upiu->ts.desc.length = htobe16(length);
346 memcpy((void *)(utrd->upiu + sizeof(query_upiu_t)),
347 (void *)buf, length);
348 break;
349 case QUERY_READ_ATTR:
350 case QUERY_READ_FLAG:
351 query_upiu->query_func = QUERY_FUNC_STD_READ;
352 break;
353 case QUERY_CLEAR_FLAG:
354 case QUERY_SET_FLAG:
355 query_upiu->query_func = QUERY_FUNC_STD_WRITE;
356 break;
357 case QUERY_WRITE_ATTR:
358 query_upiu->query_func = QUERY_FUNC_STD_WRITE;
359 memcpy((void *)&query_upiu->ts.attr.value, (void *)buf, length);
360 break;
361 default:
362 assert(0);
Jonathan Wright39b42212018-03-13 15:24:29 +0000363 break;
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800364 }
365 flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
366 flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
367 return 0;
368}
369
370static void ufs_prepare_nop_out(utp_utrd_t *utrd)
371{
372 utrd_header_t *hd;
373 nop_out_upiu_t *nop_out;
374
375 mmio_write_32(ufs_params.reg_base + UTRLBA,
376 utrd->header & UINT32_MAX);
377 mmio_write_32(ufs_params.reg_base + UTRLBAU,
378 (utrd->header >> 32) & UINT32_MAX);
379
380 hd = (utrd_header_t *)utrd->header;
381 nop_out = (nop_out_upiu_t *)utrd->upiu;
382
383 hd->i = 1;
384 hd->ct = CT_UFS_STORAGE;
385 hd->ocs = OCS_MASK;
386
387 nop_out->trans_type = 0;
388 nop_out->task_tag = utrd->task_tag;
389 flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
390 flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
391}
392
393static void ufs_send_request(int task_tag)
394{
395 unsigned int data;
396 int slot;
397
398 slot = task_tag - 1;
399 /* clear all interrupts */
400 mmio_write_32(ufs_params.reg_base + IS, ~0);
401
402 mmio_write_32(ufs_params.reg_base + UTRLRSR, 1);
403 do {
404 data = mmio_read_32(ufs_params.reg_base + UTRLRSR);
405 } while (data == 0);
406
407 data = UTRIACR_IAEN | UTRIACR_CTR | UTRIACR_IACTH(0x1F) |
408 UTRIACR_IATOVAL(0xFF);
409 mmio_write_32(ufs_params.reg_base + UTRIACR, data);
410 /* send request */
411 mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1 << slot);
412}
413
414static int ufs_check_resp(utp_utrd_t *utrd, int trans_type)
415{
416 utrd_header_t *hd;
417 resp_upiu_t *resp;
418 unsigned int data;
419 int slot;
420
421 hd = (utrd_header_t *)utrd->header;
422 resp = (resp_upiu_t *)utrd->resp_upiu;
423 inv_dcache_range((uintptr_t)hd, UFS_DESC_SIZE);
424 inv_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
425 do {
426 data = mmio_read_32(ufs_params.reg_base + IS);
427 if ((data & ~(UFS_INT_UCCS | UFS_INT_UTRCS)) != 0)
428 return -EIO;
429 } while ((data & UFS_INT_UTRCS) == 0);
430 slot = utrd->task_tag - 1;
431
432 data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
433 assert((data & (1 << slot)) == 0);
434 assert(hd->ocs == OCS_SUCCESS);
435 assert((resp->trans_type & TRANS_TYPE_CODE_MASK) == trans_type);
436 (void)resp;
437 (void)slot;
438 return 0;
439}
440
441#ifdef UFS_RESP_DEBUG
442static void dump_upiu(utp_utrd_t *utrd)
443{
444 utrd_header_t *hd;
445 int i;
446
447 hd = (utrd_header_t *)utrd->header;
448 INFO("utrd:0x%x, ruo:0x%x, rul:0x%x, ocs:0x%x, UTRLDBR:0x%x\n",
449 (unsigned int)(uintptr_t)utrd, hd->ruo, hd->rul, hd->ocs,
450 mmio_read_32(ufs_params.reg_base + UTRLDBR));
451 for (i = 0; i < sizeof(utrd_header_t); i += 4) {
452 INFO("[%lx]:0x%x\n",
453 (uintptr_t)utrd->header + i,
454 *(unsigned int *)((uintptr_t)utrd->header + i));
455 }
456
457 for (i = 0; i < sizeof(cmd_upiu_t); i += 4) {
458 INFO("cmd[%lx]:0x%x\n",
459 utrd->upiu + i,
460 *(unsigned int *)(utrd->upiu + i));
461 }
462 for (i = 0; i < sizeof(resp_upiu_t); i += 4) {
463 INFO("resp[%lx]:0x%x\n",
464 utrd->resp_upiu + i,
465 *(unsigned int *)(utrd->resp_upiu + i));
466 }
467 for (i = 0; i < sizeof(prdt_t); i += 4) {
468 INFO("prdt[%lx]:0x%x\n",
469 utrd->prdt + i,
470 *(unsigned int *)(utrd->prdt + i));
471 }
472}
473#endif
474
475static void ufs_verify_init(void)
476{
477 utp_utrd_t utrd;
478 int result;
479
480 get_utrd(&utrd);
481 ufs_prepare_nop_out(&utrd);
482 ufs_send_request(utrd.task_tag);
483 result = ufs_check_resp(&utrd, NOP_IN_UPIU);
484 assert(result == 0);
485 (void)result;
486}
487
488static void ufs_verify_ready(void)
489{
490 utp_utrd_t utrd;
491 int result;
492
493 get_utrd(&utrd);
494 ufs_prepare_cmd(&utrd, CDBCMD_TEST_UNIT_READY, 0, 0, 0, 0);
495 ufs_send_request(utrd.task_tag);
496 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
497 assert(result == 0);
498 (void)result;
499}
500
501static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel,
502 uintptr_t buf, size_t size)
503{
504 utp_utrd_t utrd;
505 query_resp_upiu_t *resp;
506 int result;
507
508 switch (op) {
509 case QUERY_READ_FLAG:
510 case QUERY_READ_ATTR:
511 case QUERY_READ_DESC:
512 case QUERY_WRITE_DESC:
513 case QUERY_WRITE_ATTR:
514 assert(((buf & 3) == 0) && (size != 0));
515 break;
Jonathan Wright39b42212018-03-13 15:24:29 +0000516 default:
517 /* Do nothing in default case */
518 break;
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800519 }
520 get_utrd(&utrd);
521 ufs_prepare_query(&utrd, op, idn, index, sel, buf, size);
522 ufs_send_request(utrd.task_tag);
523 result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU);
524 assert(result == 0);
525 resp = (query_resp_upiu_t *)utrd.resp_upiu;
526#ifdef UFS_RESP_DEBUG
527 dump_upiu(&utrd);
528#endif
529 assert(resp->query_resp == QUERY_RESP_SUCCESS);
530
531 switch (op) {
532 case QUERY_READ_FLAG:
533 *(uint32_t *)buf = (uint32_t)resp->ts.flag.value;
534 break;
535 case QUERY_READ_ATTR:
536 case QUERY_READ_DESC:
537 memcpy((void *)buf,
538 (void *)(utrd.resp_upiu + sizeof(query_resp_upiu_t)),
539 size);
540 break;
Jonathan Wright39b42212018-03-13 15:24:29 +0000541 default:
542 /* Do nothing in default case */
543 break;
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800544 }
545 (void)result;
546}
547
548unsigned int ufs_read_attr(int idn)
549{
550 unsigned int value;
551
552 ufs_query(QUERY_READ_ATTR, idn, 0, 0,
553 (uintptr_t)&value, sizeof(value));
554 return value;
555}
556
557void ufs_write_attr(int idn, unsigned int value)
558{
559 ufs_query(QUERY_WRITE_ATTR, idn, 0, 0,
560 (uintptr_t)&value, sizeof(value));
561}
562
563unsigned int ufs_read_flag(int idn)
564{
565 unsigned int value;
566
567 ufs_query(QUERY_READ_FLAG, idn, 0, 0,
568 (uintptr_t)&value, sizeof(value));
569 return value;
570}
571
572void ufs_set_flag(int idn)
573{
574 ufs_query(QUERY_SET_FLAG, idn, 0, 0, 0, 0);
575}
576
577void ufs_clear_flag(int idn)
578{
579 ufs_query(QUERY_CLEAR_FLAG, idn, 0, 0, 0, 0);
580}
581
582void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size)
583{
584 ufs_query(QUERY_READ_DESC, idn, index, 0, buf, size);
585}
586
587void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size)
588{
589 ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size);
590}
591
592void ufs_read_capacity(int lun, unsigned int *num, unsigned int *size)
593{
594 utp_utrd_t utrd;
595 resp_upiu_t *resp;
596 sense_data_t *sense;
597 unsigned char data[CACHE_WRITEBACK_GRANULE << 1];
598 uintptr_t buf;
599 int result;
600 int retry;
601
602 assert((ufs_params.reg_base != 0) &&
603 (ufs_params.desc_base != 0) &&
604 (ufs_params.desc_size >= UFS_DESC_SIZE) &&
605 (num != NULL) && (size != NULL));
606
607 /* align buf address */
608 buf = (uintptr_t)data;
609 buf = (buf + CACHE_WRITEBACK_GRANULE - 1) &
610 ~(CACHE_WRITEBACK_GRANULE - 1);
611 memset((void *)buf, 0, CACHE_WRITEBACK_GRANULE);
612 flush_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
613 do {
614 get_utrd(&utrd);
615 ufs_prepare_cmd(&utrd, CDBCMD_READ_CAPACITY_10, lun, 0,
616 buf, READ_CAPACITY_LENGTH);
617 ufs_send_request(utrd.task_tag);
618 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
619 assert(result == 0);
620#ifdef UFS_RESP_DEBUG
621 dump_upiu(&utrd);
622#endif
623 resp = (resp_upiu_t *)utrd.resp_upiu;
624 retry = 0;
625 sense = &resp->sd.sense;
626 if (sense->resp_code == SENSE_DATA_VALID) {
627 if ((sense->sense_key == SENSE_KEY_UNIT_ATTENTION) &&
628 (sense->asc == 0x29) && (sense->ascq == 0)) {
629 retry = 1;
630 }
631 }
632 inv_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
633 /* last logical block address */
634 *num = be32toh(*(unsigned int *)buf);
635 if (*num)
636 *num += 1;
637 /* logical block length in bytes */
638 *size = be32toh(*(unsigned int *)(buf + 4));
639 } while (retry);
640 (void)result;
641}
642
643size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size)
644{
645 utp_utrd_t utrd;
646 resp_upiu_t *resp;
647 int result;
648
649 assert((ufs_params.reg_base != 0) &&
650 (ufs_params.desc_base != 0) &&
651 (ufs_params.desc_size >= UFS_DESC_SIZE));
652
653 memset((void *)buf, 0, size);
654 get_utrd(&utrd);
655 ufs_prepare_cmd(&utrd, CDBCMD_READ_10, lun, lba, buf, size);
656 ufs_send_request(utrd.task_tag);
657 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
658 assert(result == 0);
659#ifdef UFS_RESP_DEBUG
660 dump_upiu(&utrd);
661#endif
662 resp = (resp_upiu_t *)utrd.resp_upiu;
663 (void)result;
664 return size - resp->res_trans_cnt;
665}
666
667size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size)
668{
669 utp_utrd_t utrd;
670 resp_upiu_t *resp;
671 int result;
672
673 assert((ufs_params.reg_base != 0) &&
674 (ufs_params.desc_base != 0) &&
675 (ufs_params.desc_size >= UFS_DESC_SIZE));
676
677 memset((void *)buf, 0, size);
678 get_utrd(&utrd);
679 ufs_prepare_cmd(&utrd, CDBCMD_WRITE_10, lun, lba, buf, size);
680 ufs_send_request(utrd.task_tag);
681 result = ufs_check_resp(&utrd, RESPONSE_UPIU);
682 assert(result == 0);
683#ifdef UFS_RESP_DEBUG
684 dump_upiu(&utrd);
685#endif
686 resp = (resp_upiu_t *)utrd.resp_upiu;
687 (void)result;
688 return size - resp->res_trans_cnt;
689}
690
691static void ufs_enum(void)
692{
693 unsigned int blk_num, blk_size;
694 int i;
695
696 /* 0 means 1 slot */
697 nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;
698 if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE))
699 nutrs = ufs_params.desc_size / UFS_DESC_SIZE;
700
701 ufs_verify_init();
702 ufs_verify_ready();
703
704 ufs_set_flag(FLAG_DEVICE_INIT);
705 mdelay(100);
706 /* dump available LUNs */
707 for (i = 0; i < UFS_MAX_LUNS; i++) {
708 ufs_read_capacity(i, &blk_num, &blk_size);
709 if (blk_num && blk_size) {
710 INFO("UFS LUN%d contains %d blocks with %d-byte size\n",
711 i, blk_num, blk_size);
712 }
713 }
714}
715
fengbaopeng44070ef2018-02-12 20:53:54 +0800716static void ufs_get_device_info(struct ufs_dev_desc *card_data)
717{
718 uint8_t desc_buf[DESC_DEVICE_MAX_SIZE];
719
720 ufs_query(QUERY_READ_DESC, DESC_TYPE_DEVICE, 0, 0,
721 (uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE);
722
723 /*
724 * getting vendor (manufacturerID) and Bank Index in big endian
725 * format
726 */
727 card_data->wmanufacturerid = (uint16_t)((desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8) |
728 (desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]));
729}
730
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800731int ufs_init(const ufs_ops_t *ops, ufs_params_t *params)
732{
733 int result;
734 unsigned int data;
735 uic_cmd_t cmd;
fengbaopeng44070ef2018-02-12 20:53:54 +0800736 struct ufs_dev_desc card = {0};
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800737
738 assert((params != NULL) &&
739 (params->reg_base != 0) &&
740 (params->desc_base != 0) &&
741 (params->desc_size >= UFS_DESC_SIZE));
742
743 memcpy(&ufs_params, params, sizeof(ufs_params_t));
744
745 if (ufs_params.flags & UFS_FLAGS_SKIPINIT) {
746 result = ufshc_dme_get(0x1571, 0, &data);
747 assert(result == 0);
748 result = ufshc_dme_get(0x41, 0, &data);
749 assert(result == 0);
750 if (data == 1) {
751 /* prepare to exit hibernate mode */
752 memset(&cmd, 0, sizeof(uic_cmd_t));
753 cmd.op = DME_HIBERNATE_EXIT;
754 result = ufshc_send_uic_cmd(ufs_params.reg_base,
755 &cmd);
756 assert(result == 0);
757 data = mmio_read_32(ufs_params.reg_base + UCMDARG2);
758 assert(data == 0);
759 do {
760 data = mmio_read_32(ufs_params.reg_base + IS);
761 } while ((data & UFS_INT_UHXS) == 0);
762 mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS);
763 data = mmio_read_32(ufs_params.reg_base + HCS);
764 assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL);
765 }
766 result = ufshc_dme_get(0x1568, 0, &data);
767 assert(result == 0);
768 assert((data > 0) && (data <= 3));
769 } else {
770 assert((ops != NULL) && (ops->phy_init != NULL) &&
771 (ops->phy_set_pwr_mode != NULL));
772
773 ufshc_reset(ufs_params.reg_base);
774 ops->phy_init(&ufs_params);
775 result = ufshc_link_startup(ufs_params.reg_base);
776 assert(result == 0);
fengbaopeng44070ef2018-02-12 20:53:54 +0800777
778 ufs_enum();
779
780 ufs_get_device_info(&card);
781 if (card.wmanufacturerid == UFS_VENDOR_SKHYNIX) {
782 ufs_params.flags |= UFS_FLAGS_VENDOR_SKHYNIX;
783 }
784
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800785 ops->phy_set_pwr_mode(&ufs_params);
786 }
787
Haojian Zhuang20cd3232017-05-31 11:00:15 +0800788 (void)result;
789 return 0;
790}