xref: /arm-trusted-firmware/drivers/ufs/ufs.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu #include <endian.h>
9*91f16700Schasinglulu #include <errno.h>
10*91f16700Schasinglulu #include <stdint.h>
11*91f16700Schasinglulu #include <string.h>
12*91f16700Schasinglulu 
13*91f16700Schasinglulu #include <platform_def.h>
14*91f16700Schasinglulu 
15*91f16700Schasinglulu #include <arch_helpers.h>
16*91f16700Schasinglulu #include <common/debug.h>
17*91f16700Schasinglulu #include <drivers/delay_timer.h>
18*91f16700Schasinglulu #include <drivers/ufs.h>
19*91f16700Schasinglulu #include <lib/mmio.h>
20*91f16700Schasinglulu 
21*91f16700Schasinglulu #define CDB_ADDR_MASK			127
22*91f16700Schasinglulu #define ALIGN_CDB(x)			(((x) + CDB_ADDR_MASK) & ~CDB_ADDR_MASK)
23*91f16700Schasinglulu #define ALIGN_8(x)			(((x) + 7) & ~7)
24*91f16700Schasinglulu 
25*91f16700Schasinglulu #define UFS_DESC_SIZE			0x400
26*91f16700Schasinglulu #define MAX_UFS_DESC_SIZE		0x8000		/* 32 descriptors */
27*91f16700Schasinglulu 
28*91f16700Schasinglulu #define MAX_PRDT_SIZE			0x40000		/* 256KB */
29*91f16700Schasinglulu 
30*91f16700Schasinglulu static ufs_params_t ufs_params;
31*91f16700Schasinglulu static int nutrs;	/* Number of UTP Transfer Request Slots */
32*91f16700Schasinglulu 
33*91f16700Schasinglulu /*
34*91f16700Schasinglulu  * ufs_uic_error_handler - UIC error interrupts handler
35*91f16700Schasinglulu  * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error)
36*91f16700Schasinglulu  *
37*91f16700Schasinglulu  * Returns
38*91f16700Schasinglulu  * 0 - ignore error
39*91f16700Schasinglulu  * -EIO - fatal error, needs re-init
40*91f16700Schasinglulu  * -EAGAIN - non-fatal error, retries are sufficient
41*91f16700Schasinglulu  */
42*91f16700Schasinglulu static int ufs_uic_error_handler(bool ignore_linereset)
43*91f16700Schasinglulu {
44*91f16700Schasinglulu 	uint32_t data;
45*91f16700Schasinglulu 	int result = 0;
46*91f16700Schasinglulu 
47*91f16700Schasinglulu 	data = mmio_read_32(ufs_params.reg_base + UECPA);
48*91f16700Schasinglulu 	if (data & UFS_UIC_PA_ERROR_MASK) {
49*91f16700Schasinglulu 		if (data & PA_LAYER_GEN_ERR) {
50*91f16700Schasinglulu 			if (!ignore_linereset) {
51*91f16700Schasinglulu 				return -EIO;
52*91f16700Schasinglulu 			}
53*91f16700Schasinglulu 		} else {
54*91f16700Schasinglulu 			result = -EAGAIN;
55*91f16700Schasinglulu 		}
56*91f16700Schasinglulu 	}
57*91f16700Schasinglulu 
58*91f16700Schasinglulu 	data = mmio_read_32(ufs_params.reg_base + UECDL);
59*91f16700Schasinglulu 	if (data & UFS_UIC_DL_ERROR_MASK) {
60*91f16700Schasinglulu 		if (data & PA_INIT_ERR) {
61*91f16700Schasinglulu 			return -EIO;
62*91f16700Schasinglulu 		}
63*91f16700Schasinglulu 		result = -EAGAIN;
64*91f16700Schasinglulu 	}
65*91f16700Schasinglulu 
66*91f16700Schasinglulu 	/* NL/TL/DME error requires retries */
67*91f16700Schasinglulu 	data = mmio_read_32(ufs_params.reg_base + UECN);
68*91f16700Schasinglulu 	if (data & UFS_UIC_NL_ERROR_MASK) {
69*91f16700Schasinglulu 		result = -EAGAIN;
70*91f16700Schasinglulu 	}
71*91f16700Schasinglulu 
72*91f16700Schasinglulu 	data = mmio_read_32(ufs_params.reg_base + UECT);
73*91f16700Schasinglulu 	if (data & UFS_UIC_TL_ERROR_MASK) {
74*91f16700Schasinglulu 		result = -EAGAIN;
75*91f16700Schasinglulu 	}
76*91f16700Schasinglulu 
77*91f16700Schasinglulu 	data = mmio_read_32(ufs_params.reg_base + UECDME);
78*91f16700Schasinglulu 	if (data & UFS_UIC_DME_ERROR_MASK) {
79*91f16700Schasinglulu 		result = -EAGAIN;
80*91f16700Schasinglulu 	}
81*91f16700Schasinglulu 
82*91f16700Schasinglulu 	return result;
83*91f16700Schasinglulu }
84*91f16700Schasinglulu 
85*91f16700Schasinglulu /*
86*91f16700Schasinglulu  * ufs_error_handler - error interrupts handler
87*91f16700Schasinglulu  * @status: interrupt status
88*91f16700Schasinglulu  * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error)
89*91f16700Schasinglulu  *
90*91f16700Schasinglulu  * Returns
91*91f16700Schasinglulu  * 0 - ignore error
92*91f16700Schasinglulu  * -EIO - fatal error, needs re-init
93*91f16700Schasinglulu  * -EAGAIN - non-fatal error, retries are sufficient
94*91f16700Schasinglulu  */
95*91f16700Schasinglulu static int ufs_error_handler(uint32_t status, bool ignore_linereset)
96*91f16700Schasinglulu {
97*91f16700Schasinglulu 	int result;
98*91f16700Schasinglulu 
99*91f16700Schasinglulu 	if (status & UFS_INT_UE) {
100*91f16700Schasinglulu 		result = ufs_uic_error_handler(ignore_linereset);
101*91f16700Schasinglulu 		if (result != 0) {
102*91f16700Schasinglulu 			return result;
103*91f16700Schasinglulu 		}
104*91f16700Schasinglulu 	}
105*91f16700Schasinglulu 
106*91f16700Schasinglulu 	/* Return I/O error on fatal error, it is upto the caller to re-init UFS */
107*91f16700Schasinglulu 	if (status & UFS_INT_FATAL) {
108*91f16700Schasinglulu 		return -EIO;
109*91f16700Schasinglulu 	}
110*91f16700Schasinglulu 
111*91f16700Schasinglulu 	/* retry for non-fatal errors */
112*91f16700Schasinglulu 	return -EAGAIN;
113*91f16700Schasinglulu }
114*91f16700Schasinglulu 
115*91f16700Schasinglulu /*
116*91f16700Schasinglulu  * ufs_wait_for_int_status - wait for expected interrupt status
117*91f16700Schasinglulu  * @expected: expected interrupt status bit
118*91f16700Schasinglulu  * @timeout_ms: timeout in milliseconds to poll for
119*91f16700Schasinglulu  * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error)
120*91f16700Schasinglulu  *
121*91f16700Schasinglulu  * Returns
122*91f16700Schasinglulu  * 0 - received expected interrupt and cleared it
123*91f16700Schasinglulu  * -EIO - fatal error, needs re-init
124*91f16700Schasinglulu  * -EAGAIN - non-fatal error, caller can retry
125*91f16700Schasinglulu  * -ETIMEDOUT - timed out waiting for interrupt status
126*91f16700Schasinglulu  */
127*91f16700Schasinglulu static int ufs_wait_for_int_status(const uint32_t expected_status,
128*91f16700Schasinglulu 				   unsigned int timeout_ms,
129*91f16700Schasinglulu 				   bool ignore_linereset)
130*91f16700Schasinglulu {
131*91f16700Schasinglulu 	uint32_t interrupt_status, interrupts_enabled;
132*91f16700Schasinglulu 	int result = 0;
133*91f16700Schasinglulu 
134*91f16700Schasinglulu 	interrupts_enabled = mmio_read_32(ufs_params.reg_base + IE);
135*91f16700Schasinglulu 	do {
136*91f16700Schasinglulu 		interrupt_status = mmio_read_32(ufs_params.reg_base + IS) & interrupts_enabled;
137*91f16700Schasinglulu 		if (interrupt_status & UFS_INT_ERR) {
138*91f16700Schasinglulu 			mmio_write_32(ufs_params.reg_base + IS, interrupt_status & UFS_INT_ERR);
139*91f16700Schasinglulu 			result = ufs_error_handler(interrupt_status, ignore_linereset);
140*91f16700Schasinglulu 			if (result != 0) {
141*91f16700Schasinglulu 				return result;
142*91f16700Schasinglulu 			}
143*91f16700Schasinglulu 		}
144*91f16700Schasinglulu 
145*91f16700Schasinglulu 		if (interrupt_status & expected_status) {
146*91f16700Schasinglulu 			break;
147*91f16700Schasinglulu 		}
148*91f16700Schasinglulu 		mdelay(1);
149*91f16700Schasinglulu 	} while (timeout_ms-- > 0);
150*91f16700Schasinglulu 
151*91f16700Schasinglulu 	if (!(interrupt_status & expected_status)) {
152*91f16700Schasinglulu 		return -ETIMEDOUT;
153*91f16700Schasinglulu 	}
154*91f16700Schasinglulu 
155*91f16700Schasinglulu 	mmio_write_32(ufs_params.reg_base + IS, expected_status);
156*91f16700Schasinglulu 
157*91f16700Schasinglulu 	return result;
158*91f16700Schasinglulu }
159*91f16700Schasinglulu 
160*91f16700Schasinglulu 
161*91f16700Schasinglulu int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd)
162*91f16700Schasinglulu {
163*91f16700Schasinglulu 	unsigned int data;
164*91f16700Schasinglulu 	int result, retries;
165*91f16700Schasinglulu 
166*91f16700Schasinglulu 	if (base == 0 || cmd == NULL)
167*91f16700Schasinglulu 		return -EINVAL;
168*91f16700Schasinglulu 
169*91f16700Schasinglulu 	for (retries = 0; retries < 100; retries++) {
170*91f16700Schasinglulu 		data = mmio_read_32(base + HCS);
171*91f16700Schasinglulu 		if ((data & HCS_UCRDY) != 0) {
172*91f16700Schasinglulu 			break;
173*91f16700Schasinglulu 		}
174*91f16700Schasinglulu 		mdelay(1);
175*91f16700Schasinglulu 	}
176*91f16700Schasinglulu 	if (retries >= 100) {
177*91f16700Schasinglulu 		return -EBUSY;
178*91f16700Schasinglulu 	}
179*91f16700Schasinglulu 
180*91f16700Schasinglulu 	mmio_write_32(base + IS, ~0);
181*91f16700Schasinglulu 	mmio_write_32(base + UCMDARG1, cmd->arg1);
182*91f16700Schasinglulu 	mmio_write_32(base + UCMDARG2, cmd->arg2);
183*91f16700Schasinglulu 	mmio_write_32(base + UCMDARG3, cmd->arg3);
184*91f16700Schasinglulu 	mmio_write_32(base + UICCMD, cmd->op);
185*91f16700Schasinglulu 
186*91f16700Schasinglulu 	result = ufs_wait_for_int_status(UFS_INT_UCCS, UIC_CMD_TIMEOUT_MS,
187*91f16700Schasinglulu 					 cmd->op == DME_SET);
188*91f16700Schasinglulu 	if (result != 0) {
189*91f16700Schasinglulu 		return result;
190*91f16700Schasinglulu 	}
191*91f16700Schasinglulu 
192*91f16700Schasinglulu 	return mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK;
193*91f16700Schasinglulu }
194*91f16700Schasinglulu 
195*91f16700Schasinglulu int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val)
196*91f16700Schasinglulu {
197*91f16700Schasinglulu 	uintptr_t base;
198*91f16700Schasinglulu 	int result, retries;
199*91f16700Schasinglulu 	uic_cmd_t cmd;
200*91f16700Schasinglulu 
201*91f16700Schasinglulu 	assert(ufs_params.reg_base != 0);
202*91f16700Schasinglulu 
203*91f16700Schasinglulu 	if (val == NULL)
204*91f16700Schasinglulu 		return -EINVAL;
205*91f16700Schasinglulu 
206*91f16700Schasinglulu 	base = ufs_params.reg_base;
207*91f16700Schasinglulu 	cmd.arg1 = (attr << 16) | GEN_SELECTOR_IDX(idx);
208*91f16700Schasinglulu 	cmd.arg2 = 0;
209*91f16700Schasinglulu 	cmd.arg3 = 0;
210*91f16700Schasinglulu 	cmd.op = DME_GET;
211*91f16700Schasinglulu 
212*91f16700Schasinglulu 	for (retries = 0; retries < UFS_UIC_COMMAND_RETRIES; ++retries) {
213*91f16700Schasinglulu 		result = ufshc_send_uic_cmd(base, &cmd);
214*91f16700Schasinglulu 		if (result == 0)
215*91f16700Schasinglulu 			break;
216*91f16700Schasinglulu 		/* -EIO requires UFS re-init */
217*91f16700Schasinglulu 		if (result == -EIO) {
218*91f16700Schasinglulu 			return result;
219*91f16700Schasinglulu 		}
220*91f16700Schasinglulu 	}
221*91f16700Schasinglulu 	if (retries >= UFS_UIC_COMMAND_RETRIES)
222*91f16700Schasinglulu 		return -EIO;
223*91f16700Schasinglulu 
224*91f16700Schasinglulu 	*val = mmio_read_32(base + UCMDARG3);
225*91f16700Schasinglulu 	return 0;
226*91f16700Schasinglulu }
227*91f16700Schasinglulu 
228*91f16700Schasinglulu int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val)
229*91f16700Schasinglulu {
230*91f16700Schasinglulu 	uintptr_t base;
231*91f16700Schasinglulu 	int result, retries;
232*91f16700Schasinglulu 	uic_cmd_t cmd;
233*91f16700Schasinglulu 
234*91f16700Schasinglulu 	assert((ufs_params.reg_base != 0));
235*91f16700Schasinglulu 
236*91f16700Schasinglulu 	base = ufs_params.reg_base;
237*91f16700Schasinglulu 	cmd.arg1 = (attr << 16) | GEN_SELECTOR_IDX(idx);
238*91f16700Schasinglulu 	cmd.arg2 = 0;
239*91f16700Schasinglulu 	cmd.arg3 = val;
240*91f16700Schasinglulu 	cmd.op = DME_SET;
241*91f16700Schasinglulu 
242*91f16700Schasinglulu 	for (retries = 0; retries < UFS_UIC_COMMAND_RETRIES; ++retries) {
243*91f16700Schasinglulu 		result = ufshc_send_uic_cmd(base, &cmd);
244*91f16700Schasinglulu 		if (result == 0)
245*91f16700Schasinglulu 			break;
246*91f16700Schasinglulu 		/* -EIO requires UFS re-init */
247*91f16700Schasinglulu 		if (result == -EIO) {
248*91f16700Schasinglulu 			return result;
249*91f16700Schasinglulu 		}
250*91f16700Schasinglulu 	}
251*91f16700Schasinglulu 	if (retries >= UFS_UIC_COMMAND_RETRIES)
252*91f16700Schasinglulu 		return -EIO;
253*91f16700Schasinglulu 
254*91f16700Schasinglulu 	return 0;
255*91f16700Schasinglulu }
256*91f16700Schasinglulu 
257*91f16700Schasinglulu static int ufshc_hce_enable(uintptr_t base)
258*91f16700Schasinglulu {
259*91f16700Schasinglulu 	unsigned int data;
260*91f16700Schasinglulu 	int retries;
261*91f16700Schasinglulu 
262*91f16700Schasinglulu 	/* Enable Host Controller */
263*91f16700Schasinglulu 	mmio_write_32(base + HCE, HCE_ENABLE);
264*91f16700Schasinglulu 
265*91f16700Schasinglulu 	/* Wait until basic initialization sequence completed */
266*91f16700Schasinglulu 	for (retries = 0; retries < HCE_ENABLE_INNER_RETRIES; ++retries) {
267*91f16700Schasinglulu 		data = mmio_read_32(base + HCE);
268*91f16700Schasinglulu 		if (data & HCE_ENABLE) {
269*91f16700Schasinglulu 			break;
270*91f16700Schasinglulu 		}
271*91f16700Schasinglulu 		udelay(HCE_ENABLE_TIMEOUT_US);
272*91f16700Schasinglulu 	}
273*91f16700Schasinglulu 	if (retries >= HCE_ENABLE_INNER_RETRIES) {
274*91f16700Schasinglulu 		return -ETIMEDOUT;
275*91f16700Schasinglulu 	}
276*91f16700Schasinglulu 
277*91f16700Schasinglulu 	return 0;
278*91f16700Schasinglulu }
279*91f16700Schasinglulu 
280*91f16700Schasinglulu static int ufshc_hce_disable(uintptr_t base)
281*91f16700Schasinglulu {
282*91f16700Schasinglulu 	unsigned int data;
283*91f16700Schasinglulu 	int timeout;
284*91f16700Schasinglulu 
285*91f16700Schasinglulu 	/* Disable Host Controller */
286*91f16700Schasinglulu 	mmio_write_32(base + HCE, HCE_DISABLE);
287*91f16700Schasinglulu 	timeout = HCE_DISABLE_TIMEOUT_US;
288*91f16700Schasinglulu 	do {
289*91f16700Schasinglulu 		data = mmio_read_32(base + HCE);
290*91f16700Schasinglulu 		if ((data & HCE_ENABLE) == HCE_DISABLE) {
291*91f16700Schasinglulu 			break;
292*91f16700Schasinglulu 		}
293*91f16700Schasinglulu 		udelay(1);
294*91f16700Schasinglulu 	} while (--timeout > 0);
295*91f16700Schasinglulu 
296*91f16700Schasinglulu 	if (timeout <= 0) {
297*91f16700Schasinglulu 		return -ETIMEDOUT;
298*91f16700Schasinglulu 	}
299*91f16700Schasinglulu 
300*91f16700Schasinglulu 	return 0;
301*91f16700Schasinglulu }
302*91f16700Schasinglulu 
303*91f16700Schasinglulu 
304*91f16700Schasinglulu static int ufshc_reset(uintptr_t base)
305*91f16700Schasinglulu {
306*91f16700Schasinglulu 	unsigned int data;
307*91f16700Schasinglulu 	int retries, result;
308*91f16700Schasinglulu 
309*91f16700Schasinglulu 	/* disable controller if enabled */
310*91f16700Schasinglulu 	if (mmio_read_32(base + HCE) & HCE_ENABLE) {
311*91f16700Schasinglulu 		result = ufshc_hce_disable(base);
312*91f16700Schasinglulu 		if (result != 0) {
313*91f16700Schasinglulu 			return -EIO;
314*91f16700Schasinglulu 		}
315*91f16700Schasinglulu 	}
316*91f16700Schasinglulu 
317*91f16700Schasinglulu 	for (retries = 0; retries < HCE_ENABLE_OUTER_RETRIES; ++retries) {
318*91f16700Schasinglulu 		result = ufshc_hce_enable(base);
319*91f16700Schasinglulu 		if (result == 0) {
320*91f16700Schasinglulu 			break;
321*91f16700Schasinglulu 		}
322*91f16700Schasinglulu 	}
323*91f16700Schasinglulu 	if (retries >= HCE_ENABLE_OUTER_RETRIES) {
324*91f16700Schasinglulu 		return -EIO;
325*91f16700Schasinglulu 	}
326*91f16700Schasinglulu 
327*91f16700Schasinglulu 	/* Enable UIC Interrupts alone. We can ignore other interrupts until
328*91f16700Schasinglulu 	 * link is up as there might be spurious error interrupts during link-up
329*91f16700Schasinglulu 	 */
330*91f16700Schasinglulu 	data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS;
331*91f16700Schasinglulu 	mmio_write_32(base + IE, data);
332*91f16700Schasinglulu 
333*91f16700Schasinglulu 	return 0;
334*91f16700Schasinglulu }
335*91f16700Schasinglulu 
336*91f16700Schasinglulu static int ufshc_dme_link_startup(uintptr_t base)
337*91f16700Schasinglulu {
338*91f16700Schasinglulu 	uic_cmd_t cmd;
339*91f16700Schasinglulu 
340*91f16700Schasinglulu 	memset(&cmd, 0, sizeof(cmd));
341*91f16700Schasinglulu 	cmd.op = DME_LINKSTARTUP;
342*91f16700Schasinglulu 	return ufshc_send_uic_cmd(base, &cmd);
343*91f16700Schasinglulu }
344*91f16700Schasinglulu 
345*91f16700Schasinglulu static int ufshc_link_startup(uintptr_t base)
346*91f16700Schasinglulu {
347*91f16700Schasinglulu 	int data, result;
348*91f16700Schasinglulu 	int retries;
349*91f16700Schasinglulu 
350*91f16700Schasinglulu 	for (retries = DME_LINKSTARTUP_RETRIES; retries > 0; retries--) {
351*91f16700Schasinglulu 		result = ufshc_dme_link_startup(base);
352*91f16700Schasinglulu 		if (result != 0) {
353*91f16700Schasinglulu 			/* Reset controller before trying again */
354*91f16700Schasinglulu 			result = ufshc_reset(base);
355*91f16700Schasinglulu 			if (result != 0) {
356*91f16700Schasinglulu 				return result;
357*91f16700Schasinglulu 			}
358*91f16700Schasinglulu 			continue;
359*91f16700Schasinglulu 		}
360*91f16700Schasinglulu 		assert(mmio_read_32(base + HCS) & HCS_DP);
361*91f16700Schasinglulu 		data = mmio_read_32(base + IS);
362*91f16700Schasinglulu 		if (data & UFS_INT_ULSS)
363*91f16700Schasinglulu 			mmio_write_32(base + IS, UFS_INT_ULSS);
364*91f16700Schasinglulu 
365*91f16700Schasinglulu 		/* clear UE set due to line-reset */
366*91f16700Schasinglulu 		if (data & UFS_INT_UE) {
367*91f16700Schasinglulu 			mmio_write_32(base + IS, UFS_INT_UE);
368*91f16700Schasinglulu 		}
369*91f16700Schasinglulu 		/* clearing line-reset, UECPA is cleared on read */
370*91f16700Schasinglulu 		mmio_read_32(base + UECPA);
371*91f16700Schasinglulu 		return 0;
372*91f16700Schasinglulu 	}
373*91f16700Schasinglulu 	return -EIO;
374*91f16700Schasinglulu }
375*91f16700Schasinglulu 
376*91f16700Schasinglulu /* Read Door Bell register to check if slot zero is available */
377*91f16700Schasinglulu static int is_slot_available(void)
378*91f16700Schasinglulu {
379*91f16700Schasinglulu 	if (mmio_read_32(ufs_params.reg_base + UTRLDBR) & 0x1) {
380*91f16700Schasinglulu 		return -EBUSY;
381*91f16700Schasinglulu 	}
382*91f16700Schasinglulu 	return 0;
383*91f16700Schasinglulu }
384*91f16700Schasinglulu 
385*91f16700Schasinglulu static void get_utrd(utp_utrd_t *utrd)
386*91f16700Schasinglulu {
387*91f16700Schasinglulu 	uintptr_t base;
388*91f16700Schasinglulu 	int result;
389*91f16700Schasinglulu 	utrd_header_t *hd;
390*91f16700Schasinglulu 
391*91f16700Schasinglulu 	assert(utrd != NULL);
392*91f16700Schasinglulu 	result = is_slot_available();
393*91f16700Schasinglulu 	assert(result == 0);
394*91f16700Schasinglulu 
395*91f16700Schasinglulu 	/* clear utrd */
396*91f16700Schasinglulu 	memset((void *)utrd, 0, sizeof(utp_utrd_t));
397*91f16700Schasinglulu 	base = ufs_params.desc_base;
398*91f16700Schasinglulu 	/* clear the descriptor */
399*91f16700Schasinglulu 	memset((void *)base, 0, UFS_DESC_SIZE);
400*91f16700Schasinglulu 
401*91f16700Schasinglulu 	utrd->header = base;
402*91f16700Schasinglulu 	utrd->task_tag = 1; /* We always use the first slot */
403*91f16700Schasinglulu 	/* CDB address should be aligned with 128 bytes */
404*91f16700Schasinglulu 	utrd->upiu = ALIGN_CDB(utrd->header + sizeof(utrd_header_t));
405*91f16700Schasinglulu 	utrd->resp_upiu = ALIGN_8(utrd->upiu + sizeof(cmd_upiu_t));
406*91f16700Schasinglulu 	utrd->size_upiu = utrd->resp_upiu - utrd->upiu;
407*91f16700Schasinglulu 	utrd->size_resp_upiu = ALIGN_8(sizeof(resp_upiu_t));
408*91f16700Schasinglulu 	utrd->prdt = utrd->resp_upiu + utrd->size_resp_upiu;
409*91f16700Schasinglulu 
410*91f16700Schasinglulu 	hd = (utrd_header_t *)utrd->header;
411*91f16700Schasinglulu 	hd->ucdba = utrd->upiu & UINT32_MAX;
412*91f16700Schasinglulu 	hd->ucdbau = (utrd->upiu >> 32) & UINT32_MAX;
413*91f16700Schasinglulu 	/* Both RUL and RUO is based on DWORD */
414*91f16700Schasinglulu 	hd->rul = utrd->size_resp_upiu >> 2;
415*91f16700Schasinglulu 	hd->ruo = utrd->size_upiu >> 2;
416*91f16700Schasinglulu 	(void)result;
417*91f16700Schasinglulu }
418*91f16700Schasinglulu 
419*91f16700Schasinglulu /*
420*91f16700Schasinglulu  * Prepare UTRD, Command UPIU, Response UPIU.
421*91f16700Schasinglulu  */
422*91f16700Schasinglulu static int ufs_prepare_cmd(utp_utrd_t *utrd, uint8_t op, uint8_t lun,
423*91f16700Schasinglulu 			   int lba, uintptr_t buf, size_t length)
424*91f16700Schasinglulu {
425*91f16700Schasinglulu 	utrd_header_t *hd;
426*91f16700Schasinglulu 	cmd_upiu_t *upiu;
427*91f16700Schasinglulu 	prdt_t *prdt;
428*91f16700Schasinglulu 	unsigned int ulba;
429*91f16700Schasinglulu 	unsigned int lba_cnt;
430*91f16700Schasinglulu 	uintptr_t desc_limit;
431*91f16700Schasinglulu 	uintptr_t prdt_end;
432*91f16700Schasinglulu 
433*91f16700Schasinglulu 	hd = (utrd_header_t *)utrd->header;
434*91f16700Schasinglulu 	upiu = (cmd_upiu_t *)utrd->upiu;
435*91f16700Schasinglulu 
436*91f16700Schasinglulu 	hd->i = 1;
437*91f16700Schasinglulu 	hd->ct = CT_UFS_STORAGE;
438*91f16700Schasinglulu 	hd->ocs = OCS_MASK;
439*91f16700Schasinglulu 
440*91f16700Schasinglulu 	upiu->trans_type = CMD_UPIU;
441*91f16700Schasinglulu 	upiu->task_tag = utrd->task_tag;
442*91f16700Schasinglulu 	upiu->cdb[0] = op;
443*91f16700Schasinglulu 	ulba = (unsigned int)lba;
444*91f16700Schasinglulu 	lba_cnt = (unsigned int)(length >> UFS_BLOCK_SHIFT);
445*91f16700Schasinglulu 	switch (op) {
446*91f16700Schasinglulu 	case CDBCMD_TEST_UNIT_READY:
447*91f16700Schasinglulu 		break;
448*91f16700Schasinglulu 	case CDBCMD_READ_CAPACITY_10:
449*91f16700Schasinglulu 		hd->dd = DD_OUT;
450*91f16700Schasinglulu 		upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
451*91f16700Schasinglulu 		upiu->lun = lun;
452*91f16700Schasinglulu 		break;
453*91f16700Schasinglulu 	case CDBCMD_READ_10:
454*91f16700Schasinglulu 		hd->dd = DD_OUT;
455*91f16700Schasinglulu 		upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
456*91f16700Schasinglulu 		upiu->lun = lun;
457*91f16700Schasinglulu 		upiu->cdb[1] = RW_WITHOUT_CACHE;
458*91f16700Schasinglulu 		/* set logical block address */
459*91f16700Schasinglulu 		upiu->cdb[2] = (ulba >> 24) & 0xff;
460*91f16700Schasinglulu 		upiu->cdb[3] = (ulba >> 16) & 0xff;
461*91f16700Schasinglulu 		upiu->cdb[4] = (ulba >> 8) & 0xff;
462*91f16700Schasinglulu 		upiu->cdb[5] = ulba & 0xff;
463*91f16700Schasinglulu 		/* set transfer length */
464*91f16700Schasinglulu 		upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
465*91f16700Schasinglulu 		upiu->cdb[8] = lba_cnt & 0xff;
466*91f16700Schasinglulu 		break;
467*91f16700Schasinglulu 	case CDBCMD_WRITE_10:
468*91f16700Schasinglulu 		hd->dd = DD_IN;
469*91f16700Schasinglulu 		upiu->flags = UPIU_FLAGS_W | UPIU_FLAGS_ATTR_S;
470*91f16700Schasinglulu 		upiu->lun = lun;
471*91f16700Schasinglulu 		upiu->cdb[1] = RW_WITHOUT_CACHE;
472*91f16700Schasinglulu 		/* set logical block address */
473*91f16700Schasinglulu 		upiu->cdb[2] = (ulba >> 24) & 0xff;
474*91f16700Schasinglulu 		upiu->cdb[3] = (ulba >> 16) & 0xff;
475*91f16700Schasinglulu 		upiu->cdb[4] = (ulba >> 8) & 0xff;
476*91f16700Schasinglulu 		upiu->cdb[5] = ulba & 0xff;
477*91f16700Schasinglulu 		/* set transfer length */
478*91f16700Schasinglulu 		upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
479*91f16700Schasinglulu 		upiu->cdb[8] = lba_cnt & 0xff;
480*91f16700Schasinglulu 		break;
481*91f16700Schasinglulu 	default:
482*91f16700Schasinglulu 		assert(0);
483*91f16700Schasinglulu 		break;
484*91f16700Schasinglulu 	}
485*91f16700Schasinglulu 	if (hd->dd == DD_IN) {
486*91f16700Schasinglulu 		flush_dcache_range(buf, length);
487*91f16700Schasinglulu 	} else if (hd->dd == DD_OUT) {
488*91f16700Schasinglulu 		inv_dcache_range(buf, length);
489*91f16700Schasinglulu 	}
490*91f16700Schasinglulu 
491*91f16700Schasinglulu 	utrd->prdt_length = 0;
492*91f16700Schasinglulu 	if (length) {
493*91f16700Schasinglulu 		upiu->exp_data_trans_len = htobe32(length);
494*91f16700Schasinglulu 		assert(lba_cnt <= UINT16_MAX);
495*91f16700Schasinglulu 		prdt = (prdt_t *)utrd->prdt;
496*91f16700Schasinglulu 
497*91f16700Schasinglulu 		desc_limit = ufs_params.desc_base + ufs_params.desc_size;
498*91f16700Schasinglulu 		while (length > 0) {
499*91f16700Schasinglulu 			if ((uintptr_t)prdt + sizeof(prdt_t) > desc_limit) {
500*91f16700Schasinglulu 				ERROR("UFS: Exceeded descriptor limit. Image is too large\n");
501*91f16700Schasinglulu 				panic();
502*91f16700Schasinglulu 			}
503*91f16700Schasinglulu 			prdt->dba = (unsigned int)(buf & UINT32_MAX);
504*91f16700Schasinglulu 			prdt->dbau = (unsigned int)((buf >> 32) & UINT32_MAX);
505*91f16700Schasinglulu 			/* prdt->dbc counts from 0 */
506*91f16700Schasinglulu 			if (length > MAX_PRDT_SIZE) {
507*91f16700Schasinglulu 				prdt->dbc = MAX_PRDT_SIZE - 1;
508*91f16700Schasinglulu 				length = length - MAX_PRDT_SIZE;
509*91f16700Schasinglulu 			} else {
510*91f16700Schasinglulu 				prdt->dbc = length - 1;
511*91f16700Schasinglulu 				length = 0;
512*91f16700Schasinglulu 			}
513*91f16700Schasinglulu 			buf += MAX_PRDT_SIZE;
514*91f16700Schasinglulu 			prdt++;
515*91f16700Schasinglulu 			utrd->prdt_length++;
516*91f16700Schasinglulu 		}
517*91f16700Schasinglulu 		hd->prdtl = utrd->prdt_length;
518*91f16700Schasinglulu 		hd->prdto = (utrd->size_upiu + utrd->size_resp_upiu) >> 2;
519*91f16700Schasinglulu 	}
520*91f16700Schasinglulu 
521*91f16700Schasinglulu 	prdt_end = utrd->prdt + utrd->prdt_length * sizeof(prdt_t);
522*91f16700Schasinglulu 	flush_dcache_range(utrd->header, prdt_end - utrd->header);
523*91f16700Schasinglulu 	return 0;
524*91f16700Schasinglulu }
525*91f16700Schasinglulu 
526*91f16700Schasinglulu static int ufs_prepare_query(utp_utrd_t *utrd, uint8_t op, uint8_t idn,
527*91f16700Schasinglulu 			     uint8_t index, uint8_t sel,
528*91f16700Schasinglulu 			     uintptr_t buf, size_t length)
529*91f16700Schasinglulu {
530*91f16700Schasinglulu 	utrd_header_t *hd;
531*91f16700Schasinglulu 	query_upiu_t *query_upiu;
532*91f16700Schasinglulu 
533*91f16700Schasinglulu 
534*91f16700Schasinglulu 	hd = (utrd_header_t *)utrd->header;
535*91f16700Schasinglulu 	query_upiu = (query_upiu_t *)utrd->upiu;
536*91f16700Schasinglulu 
537*91f16700Schasinglulu 	hd->i = 1;
538*91f16700Schasinglulu 	hd->ct = CT_UFS_STORAGE;
539*91f16700Schasinglulu 	hd->ocs = OCS_MASK;
540*91f16700Schasinglulu 
541*91f16700Schasinglulu 	query_upiu->trans_type = QUERY_REQUEST_UPIU;
542*91f16700Schasinglulu 	query_upiu->task_tag = utrd->task_tag;
543*91f16700Schasinglulu 	query_upiu->data_segment_len = htobe16(length);
544*91f16700Schasinglulu 	query_upiu->ts.desc.opcode = op;
545*91f16700Schasinglulu 	query_upiu->ts.desc.idn = idn;
546*91f16700Schasinglulu 	query_upiu->ts.desc.index = index;
547*91f16700Schasinglulu 	query_upiu->ts.desc.selector = sel;
548*91f16700Schasinglulu 	switch (op) {
549*91f16700Schasinglulu 	case QUERY_READ_DESC:
550*91f16700Schasinglulu 		query_upiu->query_func = QUERY_FUNC_STD_READ;
551*91f16700Schasinglulu 		query_upiu->ts.desc.length = htobe16(length);
552*91f16700Schasinglulu 		break;
553*91f16700Schasinglulu 	case QUERY_WRITE_DESC:
554*91f16700Schasinglulu 		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
555*91f16700Schasinglulu 		query_upiu->ts.desc.length = htobe16(length);
556*91f16700Schasinglulu 		memcpy((void *)(utrd->upiu + sizeof(query_upiu_t)),
557*91f16700Schasinglulu 		       (void *)buf, length);
558*91f16700Schasinglulu 		break;
559*91f16700Schasinglulu 	case QUERY_READ_ATTR:
560*91f16700Schasinglulu 	case QUERY_READ_FLAG:
561*91f16700Schasinglulu 		query_upiu->query_func = QUERY_FUNC_STD_READ;
562*91f16700Schasinglulu 		break;
563*91f16700Schasinglulu 	case QUERY_CLEAR_FLAG:
564*91f16700Schasinglulu 	case QUERY_SET_FLAG:
565*91f16700Schasinglulu 		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
566*91f16700Schasinglulu 		break;
567*91f16700Schasinglulu 	case QUERY_WRITE_ATTR:
568*91f16700Schasinglulu 		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
569*91f16700Schasinglulu 		query_upiu->ts.attr.value = htobe32(*((uint32_t *)buf));
570*91f16700Schasinglulu 		break;
571*91f16700Schasinglulu 	default:
572*91f16700Schasinglulu 		assert(0);
573*91f16700Schasinglulu 		break;
574*91f16700Schasinglulu 	}
575*91f16700Schasinglulu 	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
576*91f16700Schasinglulu 	return 0;
577*91f16700Schasinglulu }
578*91f16700Schasinglulu 
579*91f16700Schasinglulu static void ufs_prepare_nop_out(utp_utrd_t *utrd)
580*91f16700Schasinglulu {
581*91f16700Schasinglulu 	utrd_header_t *hd;
582*91f16700Schasinglulu 	nop_out_upiu_t *nop_out;
583*91f16700Schasinglulu 
584*91f16700Schasinglulu 	hd = (utrd_header_t *)utrd->header;
585*91f16700Schasinglulu 	nop_out = (nop_out_upiu_t *)utrd->upiu;
586*91f16700Schasinglulu 
587*91f16700Schasinglulu 	hd->i = 1;
588*91f16700Schasinglulu 	hd->ct = CT_UFS_STORAGE;
589*91f16700Schasinglulu 	hd->ocs = OCS_MASK;
590*91f16700Schasinglulu 
591*91f16700Schasinglulu 	nop_out->trans_type = 0;
592*91f16700Schasinglulu 	nop_out->task_tag = utrd->task_tag;
593*91f16700Schasinglulu 	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
594*91f16700Schasinglulu }
595*91f16700Schasinglulu 
596*91f16700Schasinglulu static void ufs_send_request(int task_tag)
597*91f16700Schasinglulu {
598*91f16700Schasinglulu 	unsigned int data;
599*91f16700Schasinglulu 	int slot;
600*91f16700Schasinglulu 
601*91f16700Schasinglulu 	slot = task_tag - 1;
602*91f16700Schasinglulu 	/* clear all interrupts */
603*91f16700Schasinglulu 	mmio_write_32(ufs_params.reg_base + IS, ~0);
604*91f16700Schasinglulu 
605*91f16700Schasinglulu 	mmio_write_32(ufs_params.reg_base + UTRLRSR, 1);
606*91f16700Schasinglulu 	assert(mmio_read_32(ufs_params.reg_base + UTRLRSR) == 1);
607*91f16700Schasinglulu 
608*91f16700Schasinglulu 	data = UTRIACR_IAEN | UTRIACR_CTR | UTRIACR_IACTH(0x1F) |
609*91f16700Schasinglulu 	       UTRIACR_IATOVAL(0xFF);
610*91f16700Schasinglulu 	mmio_write_32(ufs_params.reg_base + UTRIACR, data);
611*91f16700Schasinglulu 	/* send request */
612*91f16700Schasinglulu 	mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1U << slot);
613*91f16700Schasinglulu }
614*91f16700Schasinglulu 
615*91f16700Schasinglulu static int ufs_check_resp(utp_utrd_t *utrd, int trans_type, unsigned int timeout_ms)
616*91f16700Schasinglulu {
617*91f16700Schasinglulu 	utrd_header_t *hd;
618*91f16700Schasinglulu 	resp_upiu_t *resp;
619*91f16700Schasinglulu 	sense_data_t *sense;
620*91f16700Schasinglulu 	unsigned int data;
621*91f16700Schasinglulu 	int slot, result;
622*91f16700Schasinglulu 
623*91f16700Schasinglulu 	hd = (utrd_header_t *)utrd->header;
624*91f16700Schasinglulu 	resp = (resp_upiu_t *)utrd->resp_upiu;
625*91f16700Schasinglulu 
626*91f16700Schasinglulu 	result = ufs_wait_for_int_status(UFS_INT_UTRCS, timeout_ms, false);
627*91f16700Schasinglulu 	if (result != 0) {
628*91f16700Schasinglulu 		return result;
629*91f16700Schasinglulu 	}
630*91f16700Schasinglulu 
631*91f16700Schasinglulu 	slot = utrd->task_tag - 1;
632*91f16700Schasinglulu 
633*91f16700Schasinglulu 	data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
634*91f16700Schasinglulu 	assert((data & (1 << slot)) == 0);
635*91f16700Schasinglulu 	/*
636*91f16700Schasinglulu 	 * Invalidate the header after DMA read operation has
637*91f16700Schasinglulu 	 * completed to avoid cpu referring to the prefetched
638*91f16700Schasinglulu 	 * data brought in before DMA completion.
639*91f16700Schasinglulu 	 */
640*91f16700Schasinglulu 	inv_dcache_range((uintptr_t)hd, UFS_DESC_SIZE);
641*91f16700Schasinglulu 	assert(hd->ocs == OCS_SUCCESS);
642*91f16700Schasinglulu 	assert((resp->trans_type & TRANS_TYPE_CODE_MASK) == trans_type);
643*91f16700Schasinglulu 
644*91f16700Schasinglulu 	sense = &resp->sd.sense;
645*91f16700Schasinglulu 	if (sense->resp_code == SENSE_DATA_VALID &&
646*91f16700Schasinglulu 	    sense->sense_key == SENSE_KEY_UNIT_ATTENTION && sense->asc == 0x29 &&
647*91f16700Schasinglulu 	    sense->ascq == 0) {
648*91f16700Schasinglulu 		WARN("Unit Attention Condition\n");
649*91f16700Schasinglulu 		return -EAGAIN;
650*91f16700Schasinglulu 	}
651*91f16700Schasinglulu 
652*91f16700Schasinglulu 	(void)resp;
653*91f16700Schasinglulu 	(void)slot;
654*91f16700Schasinglulu 	(void)data;
655*91f16700Schasinglulu 	return 0;
656*91f16700Schasinglulu }
657*91f16700Schasinglulu 
658*91f16700Schasinglulu static void ufs_send_cmd(utp_utrd_t *utrd, uint8_t cmd_op, uint8_t lun, int lba, uintptr_t buf,
659*91f16700Schasinglulu 			 size_t length)
660*91f16700Schasinglulu {
661*91f16700Schasinglulu 	int result, i;
662*91f16700Schasinglulu 
663*91f16700Schasinglulu 	for (i = 0; i < UFS_CMD_RETRIES; ++i) {
664*91f16700Schasinglulu 		get_utrd(utrd);
665*91f16700Schasinglulu 		result = ufs_prepare_cmd(utrd, cmd_op, lun, lba, buf, length);
666*91f16700Schasinglulu 		assert(result == 0);
667*91f16700Schasinglulu 		ufs_send_request(utrd->task_tag);
668*91f16700Schasinglulu 		result = ufs_check_resp(utrd, RESPONSE_UPIU, CMD_TIMEOUT_MS);
669*91f16700Schasinglulu 		if (result == 0 || result == -EIO) {
670*91f16700Schasinglulu 			break;
671*91f16700Schasinglulu 		}
672*91f16700Schasinglulu 	}
673*91f16700Schasinglulu 	assert(result == 0);
674*91f16700Schasinglulu 	(void)result;
675*91f16700Schasinglulu }
676*91f16700Schasinglulu 
677*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG
678*91f16700Schasinglulu static void dump_upiu(utp_utrd_t *utrd)
679*91f16700Schasinglulu {
680*91f16700Schasinglulu 	utrd_header_t *hd;
681*91f16700Schasinglulu 	int i;
682*91f16700Schasinglulu 
683*91f16700Schasinglulu 	hd = (utrd_header_t *)utrd->header;
684*91f16700Schasinglulu 	INFO("utrd:0x%x, ruo:0x%x, rul:0x%x, ocs:0x%x, UTRLDBR:0x%x\n",
685*91f16700Schasinglulu 		(unsigned int)(uintptr_t)utrd, hd->ruo, hd->rul, hd->ocs,
686*91f16700Schasinglulu 		mmio_read_32(ufs_params.reg_base + UTRLDBR));
687*91f16700Schasinglulu 	for (i = 0; i < sizeof(utrd_header_t); i += 4) {
688*91f16700Schasinglulu 		INFO("[%lx]:0x%x\n",
689*91f16700Schasinglulu 			(uintptr_t)utrd->header + i,
690*91f16700Schasinglulu 			*(unsigned int *)((uintptr_t)utrd->header + i));
691*91f16700Schasinglulu 	}
692*91f16700Schasinglulu 
693*91f16700Schasinglulu 	for (i = 0; i < sizeof(cmd_upiu_t); i += 4) {
694*91f16700Schasinglulu 		INFO("cmd[%lx]:0x%x\n",
695*91f16700Schasinglulu 			utrd->upiu + i,
696*91f16700Schasinglulu 			*(unsigned int *)(utrd->upiu + i));
697*91f16700Schasinglulu 	}
698*91f16700Schasinglulu 	for (i = 0; i < sizeof(resp_upiu_t); i += 4) {
699*91f16700Schasinglulu 		INFO("resp[%lx]:0x%x\n",
700*91f16700Schasinglulu 			utrd->resp_upiu + i,
701*91f16700Schasinglulu 			*(unsigned int *)(utrd->resp_upiu + i));
702*91f16700Schasinglulu 	}
703*91f16700Schasinglulu 	for (i = 0; i < sizeof(prdt_t); i += 4) {
704*91f16700Schasinglulu 		INFO("prdt[%lx]:0x%x\n",
705*91f16700Schasinglulu 			utrd->prdt + i,
706*91f16700Schasinglulu 			*(unsigned int *)(utrd->prdt + i));
707*91f16700Schasinglulu 	}
708*91f16700Schasinglulu }
709*91f16700Schasinglulu #endif
710*91f16700Schasinglulu 
711*91f16700Schasinglulu static void ufs_verify_init(void)
712*91f16700Schasinglulu {
713*91f16700Schasinglulu 	utp_utrd_t utrd;
714*91f16700Schasinglulu 	int result;
715*91f16700Schasinglulu 
716*91f16700Schasinglulu 	get_utrd(&utrd);
717*91f16700Schasinglulu 	ufs_prepare_nop_out(&utrd);
718*91f16700Schasinglulu 	ufs_send_request(utrd.task_tag);
719*91f16700Schasinglulu 	result = ufs_check_resp(&utrd, NOP_IN_UPIU, NOP_OUT_TIMEOUT_MS);
720*91f16700Schasinglulu 	assert(result == 0);
721*91f16700Schasinglulu 	(void)result;
722*91f16700Schasinglulu }
723*91f16700Schasinglulu 
724*91f16700Schasinglulu static void ufs_verify_ready(void)
725*91f16700Schasinglulu {
726*91f16700Schasinglulu 	utp_utrd_t utrd;
727*91f16700Schasinglulu 	ufs_send_cmd(&utrd, CDBCMD_TEST_UNIT_READY, 0, 0, 0, 0);
728*91f16700Schasinglulu }
729*91f16700Schasinglulu 
730*91f16700Schasinglulu static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel,
731*91f16700Schasinglulu 		      uintptr_t buf, size_t size)
732*91f16700Schasinglulu {
733*91f16700Schasinglulu 	utp_utrd_t utrd;
734*91f16700Schasinglulu 	query_resp_upiu_t *resp;
735*91f16700Schasinglulu 	int result;
736*91f16700Schasinglulu 
737*91f16700Schasinglulu 	switch (op) {
738*91f16700Schasinglulu 	case QUERY_READ_FLAG:
739*91f16700Schasinglulu 	case QUERY_READ_ATTR:
740*91f16700Schasinglulu 	case QUERY_READ_DESC:
741*91f16700Schasinglulu 	case QUERY_WRITE_DESC:
742*91f16700Schasinglulu 	case QUERY_WRITE_ATTR:
743*91f16700Schasinglulu 		assert(((buf & 3) == 0) && (size != 0));
744*91f16700Schasinglulu 		break;
745*91f16700Schasinglulu 	default:
746*91f16700Schasinglulu 		/* Do nothing in default case */
747*91f16700Schasinglulu 		break;
748*91f16700Schasinglulu 	}
749*91f16700Schasinglulu 	get_utrd(&utrd);
750*91f16700Schasinglulu 	ufs_prepare_query(&utrd, op, idn, index, sel, buf, size);
751*91f16700Schasinglulu 	ufs_send_request(utrd.task_tag);
752*91f16700Schasinglulu 	result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU, QUERY_REQ_TIMEOUT_MS);
753*91f16700Schasinglulu 	assert(result == 0);
754*91f16700Schasinglulu 	resp = (query_resp_upiu_t *)utrd.resp_upiu;
755*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG
756*91f16700Schasinglulu 	dump_upiu(&utrd);
757*91f16700Schasinglulu #endif
758*91f16700Schasinglulu 	assert(resp->query_resp == QUERY_RESP_SUCCESS);
759*91f16700Schasinglulu 
760*91f16700Schasinglulu 	switch (op) {
761*91f16700Schasinglulu 	case QUERY_READ_FLAG:
762*91f16700Schasinglulu 		*(uint32_t *)buf = (uint32_t)resp->ts.flag.value;
763*91f16700Schasinglulu 		break;
764*91f16700Schasinglulu 	case QUERY_READ_DESC:
765*91f16700Schasinglulu 		memcpy((void *)buf,
766*91f16700Schasinglulu 		       (void *)(utrd.resp_upiu + sizeof(query_resp_upiu_t)),
767*91f16700Schasinglulu 		       size);
768*91f16700Schasinglulu 		break;
769*91f16700Schasinglulu 	case QUERY_READ_ATTR:
770*91f16700Schasinglulu 		*(uint32_t *)buf = htobe32(resp->ts.attr.value);
771*91f16700Schasinglulu 		break;
772*91f16700Schasinglulu 	default:
773*91f16700Schasinglulu 		/* Do nothing in default case */
774*91f16700Schasinglulu 		break;
775*91f16700Schasinglulu 	}
776*91f16700Schasinglulu 	(void)result;
777*91f16700Schasinglulu }
778*91f16700Schasinglulu 
779*91f16700Schasinglulu unsigned int ufs_read_attr(int idn)
780*91f16700Schasinglulu {
781*91f16700Schasinglulu 	unsigned int value;
782*91f16700Schasinglulu 
783*91f16700Schasinglulu 	ufs_query(QUERY_READ_ATTR, idn, 0, 0,
784*91f16700Schasinglulu 		  (uintptr_t)&value, sizeof(value));
785*91f16700Schasinglulu 	return value;
786*91f16700Schasinglulu }
787*91f16700Schasinglulu 
788*91f16700Schasinglulu void ufs_write_attr(int idn, unsigned int value)
789*91f16700Schasinglulu {
790*91f16700Schasinglulu 	ufs_query(QUERY_WRITE_ATTR, idn, 0, 0,
791*91f16700Schasinglulu 		  (uintptr_t)&value, sizeof(value));
792*91f16700Schasinglulu }
793*91f16700Schasinglulu 
794*91f16700Schasinglulu unsigned int ufs_read_flag(int idn)
795*91f16700Schasinglulu {
796*91f16700Schasinglulu 	unsigned int value;
797*91f16700Schasinglulu 
798*91f16700Schasinglulu 	ufs_query(QUERY_READ_FLAG, idn, 0, 0,
799*91f16700Schasinglulu 		  (uintptr_t)&value, sizeof(value));
800*91f16700Schasinglulu 	return value;
801*91f16700Schasinglulu }
802*91f16700Schasinglulu 
803*91f16700Schasinglulu void ufs_set_flag(int idn)
804*91f16700Schasinglulu {
805*91f16700Schasinglulu 	ufs_query(QUERY_SET_FLAG, idn, 0, 0, 0, 0);
806*91f16700Schasinglulu }
807*91f16700Schasinglulu 
808*91f16700Schasinglulu void ufs_clear_flag(int idn)
809*91f16700Schasinglulu {
810*91f16700Schasinglulu 	ufs_query(QUERY_CLEAR_FLAG, idn, 0, 0, 0, 0);
811*91f16700Schasinglulu }
812*91f16700Schasinglulu 
813*91f16700Schasinglulu void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size)
814*91f16700Schasinglulu {
815*91f16700Schasinglulu 	ufs_query(QUERY_READ_DESC, idn, index, 0, buf, size);
816*91f16700Schasinglulu }
817*91f16700Schasinglulu 
818*91f16700Schasinglulu void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size)
819*91f16700Schasinglulu {
820*91f16700Schasinglulu 	ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size);
821*91f16700Schasinglulu }
822*91f16700Schasinglulu 
823*91f16700Schasinglulu static int ufs_read_capacity(int lun, unsigned int *num, unsigned int *size)
824*91f16700Schasinglulu {
825*91f16700Schasinglulu 	utp_utrd_t utrd;
826*91f16700Schasinglulu 	resp_upiu_t *resp;
827*91f16700Schasinglulu 	sense_data_t *sense;
828*91f16700Schasinglulu 	unsigned char data[CACHE_WRITEBACK_GRANULE << 1];
829*91f16700Schasinglulu 	uintptr_t buf;
830*91f16700Schasinglulu 	int retries = UFS_READ_CAPACITY_RETRIES;
831*91f16700Schasinglulu 
832*91f16700Schasinglulu 	assert((ufs_params.reg_base != 0) &&
833*91f16700Schasinglulu 	       (ufs_params.desc_base != 0) &&
834*91f16700Schasinglulu 	       (ufs_params.desc_size >= UFS_DESC_SIZE) &&
835*91f16700Schasinglulu 	       (num != NULL) && (size != NULL));
836*91f16700Schasinglulu 
837*91f16700Schasinglulu 	/* align buf address */
838*91f16700Schasinglulu 	buf = (uintptr_t)data;
839*91f16700Schasinglulu 	buf = (buf + CACHE_WRITEBACK_GRANULE - 1) &
840*91f16700Schasinglulu 	      ~(CACHE_WRITEBACK_GRANULE - 1);
841*91f16700Schasinglulu 	do {
842*91f16700Schasinglulu 		ufs_send_cmd(&utrd, CDBCMD_READ_CAPACITY_10, lun, 0,
843*91f16700Schasinglulu 			    buf, READ_CAPACITY_LENGTH);
844*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG
845*91f16700Schasinglulu 		dump_upiu(&utrd);
846*91f16700Schasinglulu #endif
847*91f16700Schasinglulu 		resp = (resp_upiu_t *)utrd.resp_upiu;
848*91f16700Schasinglulu 		sense = &resp->sd.sense;
849*91f16700Schasinglulu 		if (!((sense->resp_code == SENSE_DATA_VALID) &&
850*91f16700Schasinglulu 		    (sense->sense_key == SENSE_KEY_UNIT_ATTENTION) &&
851*91f16700Schasinglulu 		    (sense->asc == 0x29) && (sense->ascq == 0))) {
852*91f16700Schasinglulu 			inv_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
853*91f16700Schasinglulu 			/* last logical block address */
854*91f16700Schasinglulu 			*num = be32toh(*(unsigned int *)buf);
855*91f16700Schasinglulu 			if (*num)
856*91f16700Schasinglulu 				*num += 1;
857*91f16700Schasinglulu 			/* logical block length in bytes */
858*91f16700Schasinglulu 			*size = be32toh(*(unsigned int *)(buf + 4));
859*91f16700Schasinglulu 
860*91f16700Schasinglulu 			return 0;
861*91f16700Schasinglulu 		}
862*91f16700Schasinglulu 
863*91f16700Schasinglulu 	} while (retries-- > 0);
864*91f16700Schasinglulu 
865*91f16700Schasinglulu 	return -ETIMEDOUT;
866*91f16700Schasinglulu }
867*91f16700Schasinglulu 
868*91f16700Schasinglulu size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size)
869*91f16700Schasinglulu {
870*91f16700Schasinglulu 	utp_utrd_t utrd;
871*91f16700Schasinglulu 	resp_upiu_t *resp;
872*91f16700Schasinglulu 
873*91f16700Schasinglulu 	assert((ufs_params.reg_base != 0) &&
874*91f16700Schasinglulu 	       (ufs_params.desc_base != 0) &&
875*91f16700Schasinglulu 	       (ufs_params.desc_size >= UFS_DESC_SIZE));
876*91f16700Schasinglulu 
877*91f16700Schasinglulu 	ufs_send_cmd(&utrd, CDBCMD_READ_10, lun, lba, buf, size);
878*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG
879*91f16700Schasinglulu 	dump_upiu(&utrd);
880*91f16700Schasinglulu #endif
881*91f16700Schasinglulu 	/*
882*91f16700Schasinglulu 	 * Invalidate prefetched cache contents before cpu
883*91f16700Schasinglulu 	 * accesses the buf.
884*91f16700Schasinglulu 	 */
885*91f16700Schasinglulu 	inv_dcache_range(buf, size);
886*91f16700Schasinglulu 	resp = (resp_upiu_t *)utrd.resp_upiu;
887*91f16700Schasinglulu 	return size - resp->res_trans_cnt;
888*91f16700Schasinglulu }
889*91f16700Schasinglulu 
890*91f16700Schasinglulu size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size)
891*91f16700Schasinglulu {
892*91f16700Schasinglulu 	utp_utrd_t utrd;
893*91f16700Schasinglulu 	resp_upiu_t *resp;
894*91f16700Schasinglulu 
895*91f16700Schasinglulu 	assert((ufs_params.reg_base != 0) &&
896*91f16700Schasinglulu 	       (ufs_params.desc_base != 0) &&
897*91f16700Schasinglulu 	       (ufs_params.desc_size >= UFS_DESC_SIZE));
898*91f16700Schasinglulu 
899*91f16700Schasinglulu 	ufs_send_cmd(&utrd, CDBCMD_WRITE_10, lun, lba, buf, size);
900*91f16700Schasinglulu #ifdef UFS_RESP_DEBUG
901*91f16700Schasinglulu 	dump_upiu(&utrd);
902*91f16700Schasinglulu #endif
903*91f16700Schasinglulu 	resp = (resp_upiu_t *)utrd.resp_upiu;
904*91f16700Schasinglulu 	return size - resp->res_trans_cnt;
905*91f16700Schasinglulu }
906*91f16700Schasinglulu 
907*91f16700Schasinglulu static int ufs_set_fdevice_init(void)
908*91f16700Schasinglulu {
909*91f16700Schasinglulu 	unsigned int result;
910*91f16700Schasinglulu 	int timeout;
911*91f16700Schasinglulu 
912*91f16700Schasinglulu 	ufs_set_flag(FLAG_DEVICE_INIT);
913*91f16700Schasinglulu 
914*91f16700Schasinglulu 	timeout = FDEVICEINIT_TIMEOUT_MS;
915*91f16700Schasinglulu 	do {
916*91f16700Schasinglulu 		result = ufs_read_flag(FLAG_DEVICE_INIT);
917*91f16700Schasinglulu 		if (!result) {
918*91f16700Schasinglulu 			break;
919*91f16700Schasinglulu 		}
920*91f16700Schasinglulu 		mdelay(5);
921*91f16700Schasinglulu 		timeout -= 5;
922*91f16700Schasinglulu 	} while (timeout > 0);
923*91f16700Schasinglulu 
924*91f16700Schasinglulu 	if (result != 0U) {
925*91f16700Schasinglulu 		return -ETIMEDOUT;
926*91f16700Schasinglulu 	}
927*91f16700Schasinglulu 
928*91f16700Schasinglulu 	return 0;
929*91f16700Schasinglulu }
930*91f16700Schasinglulu 
931*91f16700Schasinglulu static void ufs_enum(void)
932*91f16700Schasinglulu {
933*91f16700Schasinglulu 	unsigned int blk_num, blk_size;
934*91f16700Schasinglulu 	int i, result;
935*91f16700Schasinglulu 
936*91f16700Schasinglulu 	mmio_write_32(ufs_params.reg_base + UTRLBA,
937*91f16700Schasinglulu 		      ufs_params.desc_base & UINT32_MAX);
938*91f16700Schasinglulu 	mmio_write_32(ufs_params.reg_base + UTRLBAU,
939*91f16700Schasinglulu 		      (ufs_params.desc_base >> 32) & UINT32_MAX);
940*91f16700Schasinglulu 
941*91f16700Schasinglulu 	ufs_verify_init();
942*91f16700Schasinglulu 	ufs_verify_ready();
943*91f16700Schasinglulu 
944*91f16700Schasinglulu 	result = ufs_set_fdevice_init();
945*91f16700Schasinglulu 	assert(result == 0);
946*91f16700Schasinglulu 
947*91f16700Schasinglulu 	blk_num = 0;
948*91f16700Schasinglulu 	blk_size = 0;
949*91f16700Schasinglulu 
950*91f16700Schasinglulu 	/* dump available LUNs */
951*91f16700Schasinglulu 	for (i = 0; i < UFS_MAX_LUNS; i++) {
952*91f16700Schasinglulu 		result = ufs_read_capacity(i, &blk_num, &blk_size);
953*91f16700Schasinglulu 		if (result != 0) {
954*91f16700Schasinglulu 			WARN("UFS LUN%d dump failed\n", i);
955*91f16700Schasinglulu 		}
956*91f16700Schasinglulu 		if (blk_num && blk_size) {
957*91f16700Schasinglulu 			INFO("UFS LUN%d contains %d blocks with %d-byte size\n",
958*91f16700Schasinglulu 			     i, blk_num, blk_size);
959*91f16700Schasinglulu 		}
960*91f16700Schasinglulu 	}
961*91f16700Schasinglulu 
962*91f16700Schasinglulu 	(void)result;
963*91f16700Schasinglulu }
964*91f16700Schasinglulu 
965*91f16700Schasinglulu static void ufs_get_device_info(struct ufs_dev_desc *card_data)
966*91f16700Schasinglulu {
967*91f16700Schasinglulu 	uint8_t desc_buf[DESC_DEVICE_MAX_SIZE];
968*91f16700Schasinglulu 
969*91f16700Schasinglulu 	ufs_query(QUERY_READ_DESC, DESC_TYPE_DEVICE, 0, 0,
970*91f16700Schasinglulu 				(uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE);
971*91f16700Schasinglulu 
972*91f16700Schasinglulu 	/*
973*91f16700Schasinglulu 	 * getting vendor (manufacturerID) and Bank Index in big endian
974*91f16700Schasinglulu 	 * format
975*91f16700Schasinglulu 	 */
976*91f16700Schasinglulu 	card_data->wmanufacturerid = (uint16_t)((desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8) |
977*91f16700Schasinglulu 				     (desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]));
978*91f16700Schasinglulu }
979*91f16700Schasinglulu 
980*91f16700Schasinglulu int ufs_init(const ufs_ops_t *ops, ufs_params_t *params)
981*91f16700Schasinglulu {
982*91f16700Schasinglulu 	int result;
983*91f16700Schasinglulu 	unsigned int data;
984*91f16700Schasinglulu 	uic_cmd_t cmd;
985*91f16700Schasinglulu 	struct ufs_dev_desc card = {0};
986*91f16700Schasinglulu 
987*91f16700Schasinglulu 	assert((params != NULL) &&
988*91f16700Schasinglulu 	       (params->reg_base != 0) &&
989*91f16700Schasinglulu 	       (params->desc_base != 0) &&
990*91f16700Schasinglulu 	       (params->desc_size >= UFS_DESC_SIZE));
991*91f16700Schasinglulu 
992*91f16700Schasinglulu 	memcpy(&ufs_params, params, sizeof(ufs_params_t));
993*91f16700Schasinglulu 
994*91f16700Schasinglulu 	/* 0 means 1 slot */
995*91f16700Schasinglulu 	nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;
996*91f16700Schasinglulu 	if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE)) {
997*91f16700Schasinglulu 		nutrs = ufs_params.desc_size / UFS_DESC_SIZE;
998*91f16700Schasinglulu 	}
999*91f16700Schasinglulu 
1000*91f16700Schasinglulu 
1001*91f16700Schasinglulu 	if (ufs_params.flags & UFS_FLAGS_SKIPINIT) {
1002*91f16700Schasinglulu 		mmio_write_32(ufs_params.reg_base + UTRLBA,
1003*91f16700Schasinglulu 			      ufs_params.desc_base & UINT32_MAX);
1004*91f16700Schasinglulu 		mmio_write_32(ufs_params.reg_base + UTRLBAU,
1005*91f16700Schasinglulu 			      (ufs_params.desc_base >> 32) & UINT32_MAX);
1006*91f16700Schasinglulu 
1007*91f16700Schasinglulu 		result = ufshc_dme_get(0x1571, 0, &data);
1008*91f16700Schasinglulu 		assert(result == 0);
1009*91f16700Schasinglulu 		result = ufshc_dme_get(0x41, 0, &data);
1010*91f16700Schasinglulu 		assert(result == 0);
1011*91f16700Schasinglulu 		if (data == 1) {
1012*91f16700Schasinglulu 			/* prepare to exit hibernate mode */
1013*91f16700Schasinglulu 			memset(&cmd, 0, sizeof(uic_cmd_t));
1014*91f16700Schasinglulu 			cmd.op = DME_HIBERNATE_EXIT;
1015*91f16700Schasinglulu 			result = ufshc_send_uic_cmd(ufs_params.reg_base,
1016*91f16700Schasinglulu 						    &cmd);
1017*91f16700Schasinglulu 			assert(result == 0);
1018*91f16700Schasinglulu 			data = mmio_read_32(ufs_params.reg_base + UCMDARG2);
1019*91f16700Schasinglulu 			assert(data == 0);
1020*91f16700Schasinglulu 			do {
1021*91f16700Schasinglulu 				data = mmio_read_32(ufs_params.reg_base + IS);
1022*91f16700Schasinglulu 			} while ((data & UFS_INT_UHXS) == 0);
1023*91f16700Schasinglulu 			mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS);
1024*91f16700Schasinglulu 			data = mmio_read_32(ufs_params.reg_base + HCS);
1025*91f16700Schasinglulu 			assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL);
1026*91f16700Schasinglulu 		}
1027*91f16700Schasinglulu 		result = ufshc_dme_get(0x1568, 0, &data);
1028*91f16700Schasinglulu 		assert(result == 0);
1029*91f16700Schasinglulu 		assert((data > 0) && (data <= 3));
1030*91f16700Schasinglulu 	} else {
1031*91f16700Schasinglulu 		assert((ops != NULL) && (ops->phy_init != NULL) &&
1032*91f16700Schasinglulu 		       (ops->phy_set_pwr_mode != NULL));
1033*91f16700Schasinglulu 
1034*91f16700Schasinglulu 		result = ufshc_reset(ufs_params.reg_base);
1035*91f16700Schasinglulu 		assert(result == 0);
1036*91f16700Schasinglulu 		ops->phy_init(&ufs_params);
1037*91f16700Schasinglulu 		result = ufshc_link_startup(ufs_params.reg_base);
1038*91f16700Schasinglulu 		assert(result == 0);
1039*91f16700Schasinglulu 
1040*91f16700Schasinglulu 		/* enable all interrupts */
1041*91f16700Schasinglulu 		data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS;
1042*91f16700Schasinglulu 		data |= UFS_INT_UTRCS | UFS_INT_ERR;
1043*91f16700Schasinglulu 		mmio_write_32(ufs_params.reg_base + IE, data);
1044*91f16700Schasinglulu 
1045*91f16700Schasinglulu 		ufs_enum();
1046*91f16700Schasinglulu 
1047*91f16700Schasinglulu 		ufs_get_device_info(&card);
1048*91f16700Schasinglulu 		if (card.wmanufacturerid == UFS_VENDOR_SKHYNIX) {
1049*91f16700Schasinglulu 			ufs_params.flags |= UFS_FLAGS_VENDOR_SKHYNIX;
1050*91f16700Schasinglulu 		}
1051*91f16700Schasinglulu 
1052*91f16700Schasinglulu 		ops->phy_set_pwr_mode(&ufs_params);
1053*91f16700Schasinglulu 	}
1054*91f16700Schasinglulu 
1055*91f16700Schasinglulu 	(void)result;
1056*91f16700Schasinglulu 	return 0;
1057*91f16700Schasinglulu }
1058