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