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