xref: /rk3399_ARM-atf/drivers/ufs/ufs.c (revision c9f097ec68961862340079cf65f039882850a81f)
1eb5073f4SHaojian Zhuang /*
2c5ee8588SWing Li  * Copyright (c) 2017-2021, ARM Limited and Contributors. All rights reserved.
3eb5073f4SHaojian Zhuang  *
4eb5073f4SHaojian Zhuang  * SPDX-License-Identifier: BSD-3-Clause
5eb5073f4SHaojian Zhuang  */
6eb5073f4SHaojian Zhuang 
7eb5073f4SHaojian Zhuang #include <assert.h>
8eb5073f4SHaojian Zhuang #include <endian.h>
9eb5073f4SHaojian Zhuang #include <errno.h>
10eb5073f4SHaojian Zhuang #include <stdint.h>
11eb5073f4SHaojian Zhuang #include <string.h>
1209d40e0eSAntonio Nino Diaz 
1309d40e0eSAntonio Nino Diaz #include <platform_def.h>
1409d40e0eSAntonio Nino Diaz 
1509d40e0eSAntonio Nino Diaz #include <arch_helpers.h>
1609d40e0eSAntonio Nino Diaz #include <common/debug.h>
1709d40e0eSAntonio Nino Diaz #include <drivers/delay_timer.h>
1809d40e0eSAntonio Nino Diaz #include <drivers/ufs.h>
1909d40e0eSAntonio Nino Diaz #include <lib/mmio.h>
20eb5073f4SHaojian Zhuang 
21eb5073f4SHaojian Zhuang #define CDB_ADDR_MASK			127
22eb5073f4SHaojian Zhuang #define ALIGN_CDB(x)			(((x) + CDB_ADDR_MASK) & ~CDB_ADDR_MASK)
23eb5073f4SHaojian Zhuang #define ALIGN_8(x)			(((x) + 7) & ~7)
24eb5073f4SHaojian Zhuang 
25eb5073f4SHaojian Zhuang #define UFS_DESC_SIZE			0x400
26eb5073f4SHaojian Zhuang #define MAX_UFS_DESC_SIZE		0x8000		/* 32 descriptors */
27eb5073f4SHaojian Zhuang 
28eb5073f4SHaojian Zhuang #define MAX_PRDT_SIZE			0x40000		/* 256KB */
29eb5073f4SHaojian Zhuang 
30eb5073f4SHaojian Zhuang static ufs_params_t ufs_params;
31eb5073f4SHaojian Zhuang static int nutrs;	/* Number of UTP Transfer Request Slots */
32eb5073f4SHaojian Zhuang 
332c5bce38SAnand Saminathan /*
342c5bce38SAnand Saminathan  * ufs_uic_error_handler - UIC error interrupts handler
352c5bce38SAnand Saminathan  * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error)
362c5bce38SAnand Saminathan  *
372c5bce38SAnand Saminathan  * Returns
382c5bce38SAnand Saminathan  * 0 - ignore error
392c5bce38SAnand Saminathan  * -EIO - fatal error, needs re-init
402c5bce38SAnand Saminathan  * -EAGAIN - non-fatal error, retries are sufficient
412c5bce38SAnand Saminathan  */
ufs_uic_error_handler(bool ignore_linereset)422c5bce38SAnand Saminathan static int ufs_uic_error_handler(bool ignore_linereset)
432c5bce38SAnand Saminathan {
442c5bce38SAnand Saminathan 	uint32_t data;
452c5bce38SAnand Saminathan 	int result = 0;
462c5bce38SAnand Saminathan 
472c5bce38SAnand Saminathan 	data = mmio_read_32(ufs_params.reg_base + UECPA);
482c5bce38SAnand Saminathan 	if (data & UFS_UIC_PA_ERROR_MASK) {
492c5bce38SAnand Saminathan 		if (data & PA_LAYER_GEN_ERR) {
502c5bce38SAnand Saminathan 			if (!ignore_linereset) {
512c5bce38SAnand Saminathan 				return -EIO;
522c5bce38SAnand Saminathan 			}
532c5bce38SAnand Saminathan 		} else {
542c5bce38SAnand Saminathan 			result = -EAGAIN;
552c5bce38SAnand Saminathan 		}
562c5bce38SAnand Saminathan 	}
572c5bce38SAnand Saminathan 
582c5bce38SAnand Saminathan 	data = mmio_read_32(ufs_params.reg_base + UECDL);
592c5bce38SAnand Saminathan 	if (data & UFS_UIC_DL_ERROR_MASK) {
602c5bce38SAnand Saminathan 		if (data & PA_INIT_ERR) {
612c5bce38SAnand Saminathan 			return -EIO;
622c5bce38SAnand Saminathan 		}
632c5bce38SAnand Saminathan 		result = -EAGAIN;
642c5bce38SAnand Saminathan 	}
652c5bce38SAnand Saminathan 
662c5bce38SAnand Saminathan 	/* NL/TL/DME error requires retries */
672c5bce38SAnand Saminathan 	data = mmio_read_32(ufs_params.reg_base + UECN);
682c5bce38SAnand Saminathan 	if (data & UFS_UIC_NL_ERROR_MASK) {
692c5bce38SAnand Saminathan 		result = -EAGAIN;
702c5bce38SAnand Saminathan 	}
712c5bce38SAnand Saminathan 
722c5bce38SAnand Saminathan 	data = mmio_read_32(ufs_params.reg_base + UECT);
732c5bce38SAnand Saminathan 	if (data & UFS_UIC_TL_ERROR_MASK) {
742c5bce38SAnand Saminathan 		result = -EAGAIN;
752c5bce38SAnand Saminathan 	}
762c5bce38SAnand Saminathan 
772c5bce38SAnand Saminathan 	data = mmio_read_32(ufs_params.reg_base + UECDME);
782c5bce38SAnand Saminathan 	if (data & UFS_UIC_DME_ERROR_MASK) {
792c5bce38SAnand Saminathan 		result = -EAGAIN;
802c5bce38SAnand Saminathan 	}
812c5bce38SAnand Saminathan 
822c5bce38SAnand Saminathan 	return result;
832c5bce38SAnand Saminathan }
842c5bce38SAnand Saminathan 
852c5bce38SAnand Saminathan /*
862c5bce38SAnand Saminathan  * ufs_error_handler - error interrupts handler
872c5bce38SAnand Saminathan  * @status: interrupt status
882c5bce38SAnand Saminathan  * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error)
892c5bce38SAnand Saminathan  *
902c5bce38SAnand Saminathan  * Returns
912c5bce38SAnand Saminathan  * 0 - ignore error
922c5bce38SAnand Saminathan  * -EIO - fatal error, needs re-init
932c5bce38SAnand Saminathan  * -EAGAIN - non-fatal error, retries are sufficient
942c5bce38SAnand Saminathan  */
ufs_error_handler(uint32_t status,bool ignore_linereset)952c5bce38SAnand Saminathan static int ufs_error_handler(uint32_t status, bool ignore_linereset)
962c5bce38SAnand Saminathan {
972c5bce38SAnand Saminathan 	int result;
982c5bce38SAnand Saminathan 
992c5bce38SAnand Saminathan 	if (status & UFS_INT_UE) {
1002c5bce38SAnand Saminathan 		result = ufs_uic_error_handler(ignore_linereset);
1012c5bce38SAnand Saminathan 		if (result != 0) {
1022c5bce38SAnand Saminathan 			return result;
1032c5bce38SAnand Saminathan 		}
1042c5bce38SAnand Saminathan 	}
1052c5bce38SAnand Saminathan 
1062c5bce38SAnand Saminathan 	/* Return I/O error on fatal error, it is upto the caller to re-init UFS */
1072c5bce38SAnand Saminathan 	if (status & UFS_INT_FATAL) {
1082c5bce38SAnand Saminathan 		return -EIO;
1092c5bce38SAnand Saminathan 	}
1102c5bce38SAnand Saminathan 
1112c5bce38SAnand Saminathan 	/* retry for non-fatal errors */
1122c5bce38SAnand Saminathan 	return -EAGAIN;
1132c5bce38SAnand Saminathan }
1142c5bce38SAnand Saminathan 
1152c5bce38SAnand Saminathan /*
1162c5bce38SAnand Saminathan  * ufs_wait_for_int_status - wait for expected interrupt status
1172c5bce38SAnand Saminathan  * @expected: expected interrupt status bit
1182c5bce38SAnand Saminathan  * @timeout_ms: timeout in milliseconds to poll for
1192c5bce38SAnand Saminathan  * @ignore_linereset: set to ignore PA_LAYER_GEN_ERR (UIC error)
1202c5bce38SAnand Saminathan  *
1212c5bce38SAnand Saminathan  * Returns
1222c5bce38SAnand Saminathan  * 0 - received expected interrupt and cleared it
1232c5bce38SAnand Saminathan  * -EIO - fatal error, needs re-init
1242c5bce38SAnand Saminathan  * -EAGAIN - non-fatal error, caller can retry
1252c5bce38SAnand Saminathan  * -ETIMEDOUT - timed out waiting for interrupt status
1262c5bce38SAnand Saminathan  */
ufs_wait_for_int_status(const uint32_t expected_status,unsigned int timeout_ms,bool ignore_linereset)1272c5bce38SAnand Saminathan static int ufs_wait_for_int_status(const uint32_t expected_status,
1282c5bce38SAnand Saminathan 				   unsigned int timeout_ms,
1292c5bce38SAnand Saminathan 				   bool ignore_linereset)
1302c5bce38SAnand Saminathan {
1312c5bce38SAnand Saminathan 	uint32_t interrupt_status, interrupts_enabled;
1322c5bce38SAnand Saminathan 	int result = 0;
1332c5bce38SAnand Saminathan 
1342c5bce38SAnand Saminathan 	interrupts_enabled = mmio_read_32(ufs_params.reg_base + IE);
1352c5bce38SAnand Saminathan 	do {
1362c5bce38SAnand Saminathan 		interrupt_status = mmio_read_32(ufs_params.reg_base + IS) & interrupts_enabled;
1372c5bce38SAnand Saminathan 		if (interrupt_status & UFS_INT_ERR) {
1382c5bce38SAnand Saminathan 			mmio_write_32(ufs_params.reg_base + IS, interrupt_status & UFS_INT_ERR);
1392c5bce38SAnand Saminathan 			result = ufs_error_handler(interrupt_status, ignore_linereset);
1402c5bce38SAnand Saminathan 			if (result != 0) {
1412c5bce38SAnand Saminathan 				return result;
1422c5bce38SAnand Saminathan 			}
1432c5bce38SAnand Saminathan 		}
1442c5bce38SAnand Saminathan 
1452c5bce38SAnand Saminathan 		if (interrupt_status & expected_status) {
1462c5bce38SAnand Saminathan 			break;
1472c5bce38SAnand Saminathan 		}
1482c5bce38SAnand Saminathan 		mdelay(1);
1492c5bce38SAnand Saminathan 	} while (timeout_ms-- > 0);
1502c5bce38SAnand Saminathan 
1512c5bce38SAnand Saminathan 	if (!(interrupt_status & expected_status)) {
1522c5bce38SAnand Saminathan 		return -ETIMEDOUT;
1532c5bce38SAnand Saminathan 	}
1542c5bce38SAnand Saminathan 
1552c5bce38SAnand Saminathan 	mmio_write_32(ufs_params.reg_base + IS, expected_status);
1562c5bce38SAnand Saminathan 
1572c5bce38SAnand Saminathan 	return result;
1582c5bce38SAnand Saminathan }
1592c5bce38SAnand Saminathan 
1602c5bce38SAnand Saminathan 
ufshc_send_uic_cmd(uintptr_t base,uic_cmd_t * cmd)161eb5073f4SHaojian Zhuang int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd)
162eb5073f4SHaojian Zhuang {
163eb5073f4SHaojian Zhuang 	unsigned int data;
1646e57b2f0SRohit Ner 	int result, retries;
165eb5073f4SHaojian Zhuang 
166d68d163dSJorge Troncoso 	if (base == 0 || cmd == NULL)
167d68d163dSJorge Troncoso 		return -EINVAL;
168d68d163dSJorge Troncoso 
1696e57b2f0SRohit Ner 	for (retries = 0; retries < 100; retries++) {
170eb5073f4SHaojian Zhuang 		data = mmio_read_32(base + HCS);
1716e57b2f0SRohit Ner 		if ((data & HCS_UCRDY) != 0) {
1726e57b2f0SRohit Ner 			break;
1736e57b2f0SRohit Ner 		}
1746e57b2f0SRohit Ner 		mdelay(1);
1756e57b2f0SRohit Ner 	}
1766e57b2f0SRohit Ner 	if (retries >= 100) {
177eb5073f4SHaojian Zhuang 		return -EBUSY;
1786e57b2f0SRohit Ner 	}
1796e57b2f0SRohit Ner 
180eb5073f4SHaojian Zhuang 	mmio_write_32(base + IS, ~0);
181eb5073f4SHaojian Zhuang 	mmio_write_32(base + UCMDARG1, cmd->arg1);
182eb5073f4SHaojian Zhuang 	mmio_write_32(base + UCMDARG2, cmd->arg2);
183eb5073f4SHaojian Zhuang 	mmio_write_32(base + UCMDARG3, cmd->arg3);
184eb5073f4SHaojian Zhuang 	mmio_write_32(base + UICCMD, cmd->op);
185eb5073f4SHaojian Zhuang 
1862c5bce38SAnand Saminathan 	result = ufs_wait_for_int_status(UFS_INT_UCCS, UIC_CMD_TIMEOUT_MS,
1872c5bce38SAnand Saminathan 					 cmd->op == DME_SET);
1882c5bce38SAnand Saminathan 	if (result != 0) {
1892c5bce38SAnand Saminathan 		return result;
1902c5bce38SAnand Saminathan 	}
1912c5bce38SAnand Saminathan 
192101afa02SHaojian Zhuang 	return mmio_read_32(base + UCMDARG2) & CONFIG_RESULT_CODE_MASK;
193eb5073f4SHaojian Zhuang }
194eb5073f4SHaojian Zhuang 
ufshc_dme_get(unsigned int attr,unsigned int idx,unsigned int * val)195eb5073f4SHaojian Zhuang int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val)
196eb5073f4SHaojian Zhuang {
197eb5073f4SHaojian Zhuang 	uintptr_t base;
198d68d163dSJorge Troncoso 	int result, retries;
199d68d163dSJorge Troncoso 	uic_cmd_t cmd;
200eb5073f4SHaojian Zhuang 
201d68d163dSJorge Troncoso 	assert(ufs_params.reg_base != 0);
202d68d163dSJorge Troncoso 
203d68d163dSJorge Troncoso 	if (val == NULL)
204d68d163dSJorge Troncoso 		return -EINVAL;
205eb5073f4SHaojian Zhuang 
206eb5073f4SHaojian Zhuang 	base = ufs_params.reg_base;
207d68d163dSJorge Troncoso 	cmd.arg1 = (attr << 16) | GEN_SELECTOR_IDX(idx);
208d68d163dSJorge Troncoso 	cmd.arg2 = 0;
209d68d163dSJorge Troncoso 	cmd.arg3 = 0;
210d68d163dSJorge Troncoso 	cmd.op = DME_GET;
2116e57b2f0SRohit Ner 
212d68d163dSJorge Troncoso 	for (retries = 0; retries < UFS_UIC_COMMAND_RETRIES; ++retries) {
213d68d163dSJorge Troncoso 		result = ufshc_send_uic_cmd(base, &cmd);
214d68d163dSJorge Troncoso 		if (result == 0)
215d68d163dSJorge Troncoso 			break;
2162c5bce38SAnand Saminathan 		/* -EIO requires UFS re-init */
2172c5bce38SAnand Saminathan 		if (result == -EIO) {
2182c5bce38SAnand Saminathan 			return result;
2192c5bce38SAnand Saminathan 		}
220d68d163dSJorge Troncoso 	}
221d68d163dSJorge Troncoso 	if (retries >= UFS_UIC_COMMAND_RETRIES)
222d68d163dSJorge Troncoso 		return -EIO;
223eb5073f4SHaojian Zhuang 
224eb5073f4SHaojian Zhuang 	*val = mmio_read_32(base + UCMDARG3);
225eb5073f4SHaojian Zhuang 	return 0;
226eb5073f4SHaojian Zhuang }
227eb5073f4SHaojian Zhuang 
ufshc_dme_set(unsigned int attr,unsigned int idx,unsigned int val)228eb5073f4SHaojian Zhuang int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val)
229eb5073f4SHaojian Zhuang {
230eb5073f4SHaojian Zhuang 	uintptr_t base;
231d68d163dSJorge Troncoso 	int result, retries;
232d68d163dSJorge Troncoso 	uic_cmd_t cmd;
233eb5073f4SHaojian Zhuang 
234eb5073f4SHaojian Zhuang 	assert((ufs_params.reg_base != 0));
235eb5073f4SHaojian Zhuang 
236eb5073f4SHaojian Zhuang 	base = ufs_params.reg_base;
237d68d163dSJorge Troncoso 	cmd.arg1 = (attr << 16) | GEN_SELECTOR_IDX(idx);
238d68d163dSJorge Troncoso 	cmd.arg2 = 0;
239d68d163dSJorge Troncoso 	cmd.arg3 = val;
240d68d163dSJorge Troncoso 	cmd.op = DME_SET;
241d68d163dSJorge Troncoso 
242d68d163dSJorge Troncoso 	for (retries = 0; retries < UFS_UIC_COMMAND_RETRIES; ++retries) {
243d68d163dSJorge Troncoso 		result = ufshc_send_uic_cmd(base, &cmd);
244d68d163dSJorge Troncoso 		if (result == 0)
245d68d163dSJorge Troncoso 			break;
2462c5bce38SAnand Saminathan 		/* -EIO requires UFS re-init */
2472c5bce38SAnand Saminathan 		if (result == -EIO) {
2482c5bce38SAnand Saminathan 			return result;
2492c5bce38SAnand Saminathan 		}
250d68d163dSJorge Troncoso 	}
251d68d163dSJorge Troncoso 	if (retries >= UFS_UIC_COMMAND_RETRIES)
252d68d163dSJorge Troncoso 		return -EIO;
253d68d163dSJorge Troncoso 
254eb5073f4SHaojian Zhuang 	return 0;
255eb5073f4SHaojian Zhuang }
256eb5073f4SHaojian Zhuang 
ufshc_hce_enable(uintptr_t base)25799ff1a35SJorge Troncoso static int ufshc_hce_enable(uintptr_t base)
258eb5073f4SHaojian Zhuang {
259eb5073f4SHaojian Zhuang 	unsigned int data;
26099ff1a35SJorge Troncoso 	int retries;
261eb5073f4SHaojian Zhuang 
262eb5073f4SHaojian Zhuang 	/* Enable Host Controller */
263eb5073f4SHaojian Zhuang 	mmio_write_32(base + HCE, HCE_ENABLE);
26499ff1a35SJorge Troncoso 
265eb5073f4SHaojian Zhuang 	/* Wait until basic initialization sequence completed */
26699ff1a35SJorge Troncoso 	for (retries = 0; retries < HCE_ENABLE_INNER_RETRIES; ++retries) {
267eb5073f4SHaojian Zhuang 		data = mmio_read_32(base + HCE);
26899ff1a35SJorge Troncoso 		if (data & HCE_ENABLE) {
26999ff1a35SJorge Troncoso 			break;
27099ff1a35SJorge Troncoso 		}
27199ff1a35SJorge Troncoso 		udelay(HCE_ENABLE_TIMEOUT_US);
27299ff1a35SJorge Troncoso 	}
27399ff1a35SJorge Troncoso 	if (retries >= HCE_ENABLE_INNER_RETRIES) {
27499ff1a35SJorge Troncoso 		return -ETIMEDOUT;
27599ff1a35SJorge Troncoso 	}
27699ff1a35SJorge Troncoso 
27799ff1a35SJorge Troncoso 	return 0;
27899ff1a35SJorge Troncoso }
27999ff1a35SJorge Troncoso 
ufshc_hce_disable(uintptr_t base)280b3f03b20Sanans static int ufshc_hce_disable(uintptr_t base)
281b3f03b20Sanans {
282b3f03b20Sanans 	unsigned int data;
283b3f03b20Sanans 	int timeout;
284b3f03b20Sanans 
285b3f03b20Sanans 	/* Disable Host Controller */
286b3f03b20Sanans 	mmio_write_32(base + HCE, HCE_DISABLE);
287b3f03b20Sanans 	timeout = HCE_DISABLE_TIMEOUT_US;
288b3f03b20Sanans 	do {
289b3f03b20Sanans 		data = mmio_read_32(base + HCE);
290b3f03b20Sanans 		if ((data & HCE_ENABLE) == HCE_DISABLE) {
291b3f03b20Sanans 			break;
292b3f03b20Sanans 		}
293b3f03b20Sanans 		udelay(1);
294b3f03b20Sanans 	} while (--timeout > 0);
295b3f03b20Sanans 
296b3f03b20Sanans 	if (timeout <= 0) {
297b3f03b20Sanans 		return -ETIMEDOUT;
298b3f03b20Sanans 	}
299b3f03b20Sanans 
300b3f03b20Sanans 	return 0;
301b3f03b20Sanans }
302b3f03b20Sanans 
303b3f03b20Sanans 
ufshc_reset(uintptr_t base)30499ff1a35SJorge Troncoso static int ufshc_reset(uintptr_t base)
30599ff1a35SJorge Troncoso {
30699ff1a35SJorge Troncoso 	unsigned int data;
30799ff1a35SJorge Troncoso 	int retries, result;
30899ff1a35SJorge Troncoso 
309b3f03b20Sanans 	/* disable controller if enabled */
310b3f03b20Sanans 	if (mmio_read_32(base + HCE) & HCE_ENABLE) {
311b3f03b20Sanans 		result = ufshc_hce_disable(base);
312b3f03b20Sanans 		if (result != 0) {
313b3f03b20Sanans 			return -EIO;
314b3f03b20Sanans 		}
315b3f03b20Sanans 	}
316b3f03b20Sanans 
31799ff1a35SJorge Troncoso 	for (retries = 0; retries < HCE_ENABLE_OUTER_RETRIES; ++retries) {
31899ff1a35SJorge Troncoso 		result = ufshc_hce_enable(base);
31999ff1a35SJorge Troncoso 		if (result == 0) {
32099ff1a35SJorge Troncoso 			break;
32199ff1a35SJorge Troncoso 		}
32299ff1a35SJorge Troncoso 	}
32399ff1a35SJorge Troncoso 	if (retries >= HCE_ENABLE_OUTER_RETRIES) {
32499ff1a35SJorge Troncoso 		return -EIO;
32599ff1a35SJorge Troncoso 	}
326eb5073f4SHaojian Zhuang 
3272c5bce38SAnand Saminathan 	/* Enable UIC Interrupts alone. We can ignore other interrupts until
3282c5bce38SAnand Saminathan 	 * link is up as there might be spurious error interrupts during link-up
3292c5bce38SAnand Saminathan 	 */
3302c5bce38SAnand Saminathan 	data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS;
331eb5073f4SHaojian Zhuang 	mmio_write_32(base + IE, data);
33299ff1a35SJorge Troncoso 
33399ff1a35SJorge Troncoso 	return 0;
334eb5073f4SHaojian Zhuang }
335eb5073f4SHaojian Zhuang 
ufshc_dme_link_startup(uintptr_t base)336905635d5SJorge Troncoso static int ufshc_dme_link_startup(uintptr_t base)
337eb5073f4SHaojian Zhuang {
338eb5073f4SHaojian Zhuang 	uic_cmd_t cmd;
339905635d5SJorge Troncoso 
340905635d5SJorge Troncoso 	memset(&cmd, 0, sizeof(cmd));
341905635d5SJorge Troncoso 	cmd.op = DME_LINKSTARTUP;
342905635d5SJorge Troncoso 	return ufshc_send_uic_cmd(base, &cmd);
343905635d5SJorge Troncoso }
344905635d5SJorge Troncoso 
ufshc_link_startup(uintptr_t base)345905635d5SJorge Troncoso static int ufshc_link_startup(uintptr_t base)
346905635d5SJorge Troncoso {
347eb5073f4SHaojian Zhuang 	int data, result;
348eb5073f4SHaojian Zhuang 	int retries;
349eb5073f4SHaojian Zhuang 
350905635d5SJorge Troncoso 	for (retries = DME_LINKSTARTUP_RETRIES; retries > 0; retries--) {
351905635d5SJorge Troncoso 		result = ufshc_dme_link_startup(base);
352905635d5SJorge Troncoso 		if (result != 0) {
353905635d5SJorge Troncoso 			/* Reset controller before trying again */
354905635d5SJorge Troncoso 			result = ufshc_reset(base);
355905635d5SJorge Troncoso 			if (result != 0) {
356905635d5SJorge Troncoso 				return result;
357905635d5SJorge Troncoso 			}
358eb5073f4SHaojian Zhuang 			continue;
359905635d5SJorge Troncoso 		}
36083103d12SJorge Troncoso 		assert(mmio_read_32(base + HCS) & HCS_DP);
361eb5073f4SHaojian Zhuang 		data = mmio_read_32(base + IS);
362eb5073f4SHaojian Zhuang 		if (data & UFS_INT_ULSS)
363eb5073f4SHaojian Zhuang 			mmio_write_32(base + IS, UFS_INT_ULSS);
3642c5bce38SAnand Saminathan 
3652c5bce38SAnand Saminathan 		/* clear UE set due to line-reset */
3662c5bce38SAnand Saminathan 		if (data & UFS_INT_UE) {
3672c5bce38SAnand Saminathan 			mmio_write_32(base + IS, UFS_INT_UE);
3682c5bce38SAnand Saminathan 		}
3692c5bce38SAnand Saminathan 		/* clearing line-reset, UECPA is cleared on read */
3702c5bce38SAnand Saminathan 		mmio_read_32(base + UECPA);
371eb5073f4SHaojian Zhuang 		return 0;
372eb5073f4SHaojian Zhuang 	}
373eb5073f4SHaojian Zhuang 	return -EIO;
374eb5073f4SHaojian Zhuang }
375eb5073f4SHaojian Zhuang 
37656db7b8bSJorge Troncoso /* Read Door Bell register to check if slot zero is available */
is_slot_available(void)37756db7b8bSJorge Troncoso static int is_slot_available(void)
378eb5073f4SHaojian Zhuang {
37956db7b8bSJorge Troncoso 	if (mmio_read_32(ufs_params.reg_base + UTRLDBR) & 0x1) {
380eb5073f4SHaojian Zhuang 		return -EBUSY;
38156db7b8bSJorge Troncoso 	}
382eb5073f4SHaojian Zhuang 	return 0;
383eb5073f4SHaojian Zhuang }
384eb5073f4SHaojian Zhuang 
get_utrd(utp_utrd_t * utrd)385eb5073f4SHaojian Zhuang static void get_utrd(utp_utrd_t *utrd)
386eb5073f4SHaojian Zhuang {
387eb5073f4SHaojian Zhuang 	uintptr_t base;
38856db7b8bSJorge Troncoso 	int result;
389eb5073f4SHaojian Zhuang 	utrd_header_t *hd;
390eb5073f4SHaojian Zhuang 
391eb5073f4SHaojian Zhuang 	assert(utrd != NULL);
39256db7b8bSJorge Troncoso 	result = is_slot_available();
393eb5073f4SHaojian Zhuang 	assert(result == 0);
394eb5073f4SHaojian Zhuang 
395eb5073f4SHaojian Zhuang 	/* clear utrd */
396eb5073f4SHaojian Zhuang 	memset((void *)utrd, 0, sizeof(utp_utrd_t));
39756db7b8bSJorge Troncoso 	base = ufs_params.desc_base;
398eb5073f4SHaojian Zhuang 	/* clear the descriptor */
399eb5073f4SHaojian Zhuang 	memset((void *)base, 0, UFS_DESC_SIZE);
400eb5073f4SHaojian Zhuang 
401eb5073f4SHaojian Zhuang 	utrd->header = base;
40256db7b8bSJorge Troncoso 	utrd->task_tag = 1; /* We always use the first slot */
403eb5073f4SHaojian Zhuang 	/* CDB address should be aligned with 128 bytes */
404eb5073f4SHaojian Zhuang 	utrd->upiu = ALIGN_CDB(utrd->header + sizeof(utrd_header_t));
405eb5073f4SHaojian Zhuang 	utrd->resp_upiu = ALIGN_8(utrd->upiu + sizeof(cmd_upiu_t));
406eb5073f4SHaojian Zhuang 	utrd->size_upiu = utrd->resp_upiu - utrd->upiu;
407eb5073f4SHaojian Zhuang 	utrd->size_resp_upiu = ALIGN_8(sizeof(resp_upiu_t));
408eb5073f4SHaojian Zhuang 	utrd->prdt = utrd->resp_upiu + utrd->size_resp_upiu;
409eb5073f4SHaojian Zhuang 
410eb5073f4SHaojian Zhuang 	hd = (utrd_header_t *)utrd->header;
411eb5073f4SHaojian Zhuang 	hd->ucdba = utrd->upiu & UINT32_MAX;
412eb5073f4SHaojian Zhuang 	hd->ucdbau = (utrd->upiu >> 32) & UINT32_MAX;
413eb5073f4SHaojian Zhuang 	/* Both RUL and RUO is based on DWORD */
414eb5073f4SHaojian Zhuang 	hd->rul = utrd->size_resp_upiu >> 2;
415eb5073f4SHaojian Zhuang 	hd->ruo = utrd->size_upiu >> 2;
416eb5073f4SHaojian Zhuang 	(void)result;
417eb5073f4SHaojian Zhuang }
418eb5073f4SHaojian Zhuang 
419eb5073f4SHaojian Zhuang /*
420eb5073f4SHaojian Zhuang  * Prepare UTRD, Command UPIU, Response UPIU.
421eb5073f4SHaojian Zhuang  */
ufs_prepare_cmd(utp_utrd_t * utrd,uint8_t op,uint8_t lun,int lba,uintptr_t buf,size_t length)422eb5073f4SHaojian Zhuang static int ufs_prepare_cmd(utp_utrd_t *utrd, uint8_t op, uint8_t lun,
423eb5073f4SHaojian Zhuang 			   int lba, uintptr_t buf, size_t length)
424eb5073f4SHaojian Zhuang {
425eb5073f4SHaojian Zhuang 	utrd_header_t *hd;
426eb5073f4SHaojian Zhuang 	cmd_upiu_t *upiu;
427eb5073f4SHaojian Zhuang 	prdt_t *prdt;
428eb5073f4SHaojian Zhuang 	unsigned int ulba;
429eb5073f4SHaojian Zhuang 	unsigned int lba_cnt;
43083ef8698SJorge Troncoso 	uintptr_t desc_limit;
43120fdbcf5SJorge Troncoso 	uintptr_t prdt_end;
432eb5073f4SHaojian Zhuang 
433eb5073f4SHaojian Zhuang 	hd = (utrd_header_t *)utrd->header;
434eb5073f4SHaojian Zhuang 	upiu = (cmd_upiu_t *)utrd->upiu;
435eb5073f4SHaojian Zhuang 
436eb5073f4SHaojian Zhuang 	hd->i = 1;
437eb5073f4SHaojian Zhuang 	hd->ct = CT_UFS_STORAGE;
438eb5073f4SHaojian Zhuang 	hd->ocs = OCS_MASK;
439eb5073f4SHaojian Zhuang 
440eb5073f4SHaojian Zhuang 	upiu->trans_type = CMD_UPIU;
441eb5073f4SHaojian Zhuang 	upiu->task_tag = utrd->task_tag;
442eb5073f4SHaojian Zhuang 	upiu->cdb[0] = op;
443eb5073f4SHaojian Zhuang 	ulba = (unsigned int)lba;
444eb5073f4SHaojian Zhuang 	lba_cnt = (unsigned int)(length >> UFS_BLOCK_SHIFT);
445eb5073f4SHaojian Zhuang 	switch (op) {
446eb5073f4SHaojian Zhuang 	case CDBCMD_TEST_UNIT_READY:
447eb5073f4SHaojian Zhuang 		break;
448eb5073f4SHaojian Zhuang 	case CDBCMD_READ_CAPACITY_10:
449eb5073f4SHaojian Zhuang 		hd->dd = DD_OUT;
450eb5073f4SHaojian Zhuang 		upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
451eb5073f4SHaojian Zhuang 		upiu->lun = lun;
452eb5073f4SHaojian Zhuang 		break;
453eb5073f4SHaojian Zhuang 	case CDBCMD_READ_10:
454eb5073f4SHaojian Zhuang 		hd->dd = DD_OUT;
455eb5073f4SHaojian Zhuang 		upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
456eb5073f4SHaojian Zhuang 		upiu->lun = lun;
457eb5073f4SHaojian Zhuang 		upiu->cdb[1] = RW_WITHOUT_CACHE;
458eb5073f4SHaojian Zhuang 		/* set logical block address */
459eb5073f4SHaojian Zhuang 		upiu->cdb[2] = (ulba >> 24) & 0xff;
460eb5073f4SHaojian Zhuang 		upiu->cdb[3] = (ulba >> 16) & 0xff;
461eb5073f4SHaojian Zhuang 		upiu->cdb[4] = (ulba >> 8) & 0xff;
462eb5073f4SHaojian Zhuang 		upiu->cdb[5] = ulba & 0xff;
463eb5073f4SHaojian Zhuang 		/* set transfer length */
464eb5073f4SHaojian Zhuang 		upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
465eb5073f4SHaojian Zhuang 		upiu->cdb[8] = lba_cnt & 0xff;
466eb5073f4SHaojian Zhuang 		break;
467eb5073f4SHaojian Zhuang 	case CDBCMD_WRITE_10:
468eb5073f4SHaojian Zhuang 		hd->dd = DD_IN;
469eb5073f4SHaojian Zhuang 		upiu->flags = UPIU_FLAGS_W | UPIU_FLAGS_ATTR_S;
470eb5073f4SHaojian Zhuang 		upiu->lun = lun;
471eb5073f4SHaojian Zhuang 		upiu->cdb[1] = RW_WITHOUT_CACHE;
472eb5073f4SHaojian Zhuang 		/* set logical block address */
473eb5073f4SHaojian Zhuang 		upiu->cdb[2] = (ulba >> 24) & 0xff;
474eb5073f4SHaojian Zhuang 		upiu->cdb[3] = (ulba >> 16) & 0xff;
475eb5073f4SHaojian Zhuang 		upiu->cdb[4] = (ulba >> 8) & 0xff;
476eb5073f4SHaojian Zhuang 		upiu->cdb[5] = ulba & 0xff;
477eb5073f4SHaojian Zhuang 		/* set transfer length */
478eb5073f4SHaojian Zhuang 		upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
479eb5073f4SHaojian Zhuang 		upiu->cdb[8] = lba_cnt & 0xff;
480eb5073f4SHaojian Zhuang 		break;
481eb5073f4SHaojian Zhuang 	default:
482eb5073f4SHaojian Zhuang 		assert(0);
4835aa7498aSJonathan Wright 		break;
484eb5073f4SHaojian Zhuang 	}
48583ef8698SJorge Troncoso 	if (hd->dd == DD_IN) {
486eb5073f4SHaojian Zhuang 		flush_dcache_range(buf, length);
48783ef8698SJorge Troncoso 	} else if (hd->dd == DD_OUT) {
488eb5073f4SHaojian Zhuang 		inv_dcache_range(buf, length);
48983ef8698SJorge Troncoso 	}
49083ef8698SJorge Troncoso 
49120fdbcf5SJorge Troncoso 	utrd->prdt_length = 0;
492eb5073f4SHaojian Zhuang 	if (length) {
493eb5073f4SHaojian Zhuang 		upiu->exp_data_trans_len = htobe32(length);
494eb5073f4SHaojian Zhuang 		assert(lba_cnt <= UINT16_MAX);
495eb5073f4SHaojian Zhuang 		prdt = (prdt_t *)utrd->prdt;
496eb5073f4SHaojian Zhuang 
49783ef8698SJorge Troncoso 		desc_limit = ufs_params.desc_base + ufs_params.desc_size;
498eb5073f4SHaojian Zhuang 		while (length > 0) {
49983ef8698SJorge Troncoso 			if ((uintptr_t)prdt + sizeof(prdt_t) > desc_limit) {
50083ef8698SJorge Troncoso 				ERROR("UFS: Exceeded descriptor limit. Image is too large\n");
50183ef8698SJorge Troncoso 				panic();
50283ef8698SJorge Troncoso 			}
503eb5073f4SHaojian Zhuang 			prdt->dba = (unsigned int)(buf & UINT32_MAX);
504eb5073f4SHaojian Zhuang 			prdt->dbau = (unsigned int)((buf >> 32) & UINT32_MAX);
505eb5073f4SHaojian Zhuang 			/* prdt->dbc counts from 0 */
506eb5073f4SHaojian Zhuang 			if (length > MAX_PRDT_SIZE) {
507eb5073f4SHaojian Zhuang 				prdt->dbc = MAX_PRDT_SIZE - 1;
508eb5073f4SHaojian Zhuang 				length = length - MAX_PRDT_SIZE;
509eb5073f4SHaojian Zhuang 			} else {
510eb5073f4SHaojian Zhuang 				prdt->dbc = length - 1;
511eb5073f4SHaojian Zhuang 				length = 0;
512eb5073f4SHaojian Zhuang 			}
513eb5073f4SHaojian Zhuang 			buf += MAX_PRDT_SIZE;
514eb5073f4SHaojian Zhuang 			prdt++;
51520fdbcf5SJorge Troncoso 			utrd->prdt_length++;
516eb5073f4SHaojian Zhuang 		}
51720fdbcf5SJorge Troncoso 		hd->prdtl = utrd->prdt_length;
518eb5073f4SHaojian Zhuang 		hd->prdto = (utrd->size_upiu + utrd->size_resp_upiu) >> 2;
519eb5073f4SHaojian Zhuang 	}
520eb5073f4SHaojian Zhuang 
52120fdbcf5SJorge Troncoso 	prdt_end = utrd->prdt + utrd->prdt_length * sizeof(prdt_t);
52220fdbcf5SJorge Troncoso 	flush_dcache_range(utrd->header, prdt_end - utrd->header);
523eb5073f4SHaojian Zhuang 	return 0;
524eb5073f4SHaojian Zhuang }
525eb5073f4SHaojian Zhuang 
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)526eb5073f4SHaojian Zhuang static int ufs_prepare_query(utp_utrd_t *utrd, uint8_t op, uint8_t idn,
527eb5073f4SHaojian Zhuang 			     uint8_t index, uint8_t sel,
528eb5073f4SHaojian Zhuang 			     uintptr_t buf, size_t length)
529eb5073f4SHaojian Zhuang {
530eb5073f4SHaojian Zhuang 	utrd_header_t *hd;
531eb5073f4SHaojian Zhuang 	query_upiu_t *query_upiu;
532eb5073f4SHaojian Zhuang 
533eb5073f4SHaojian Zhuang 
534eb5073f4SHaojian Zhuang 	hd = (utrd_header_t *)utrd->header;
535eb5073f4SHaojian Zhuang 	query_upiu = (query_upiu_t *)utrd->upiu;
536eb5073f4SHaojian Zhuang 
537eb5073f4SHaojian Zhuang 	hd->i = 1;
538eb5073f4SHaojian Zhuang 	hd->ct = CT_UFS_STORAGE;
539eb5073f4SHaojian Zhuang 	hd->ocs = OCS_MASK;
540eb5073f4SHaojian Zhuang 
541eb5073f4SHaojian Zhuang 	query_upiu->trans_type = QUERY_REQUEST_UPIU;
542eb5073f4SHaojian Zhuang 	query_upiu->task_tag = utrd->task_tag;
5439d6786caSRohit Ner 	query_upiu->data_segment_len = htobe16(length);
544eb5073f4SHaojian Zhuang 	query_upiu->ts.desc.opcode = op;
545eb5073f4SHaojian Zhuang 	query_upiu->ts.desc.idn = idn;
546eb5073f4SHaojian Zhuang 	query_upiu->ts.desc.index = index;
547eb5073f4SHaojian Zhuang 	query_upiu->ts.desc.selector = sel;
548eb5073f4SHaojian Zhuang 	switch (op) {
549eb5073f4SHaojian Zhuang 	case QUERY_READ_DESC:
550eb5073f4SHaojian Zhuang 		query_upiu->query_func = QUERY_FUNC_STD_READ;
551eb5073f4SHaojian Zhuang 		query_upiu->ts.desc.length = htobe16(length);
552eb5073f4SHaojian Zhuang 		break;
553eb5073f4SHaojian Zhuang 	case QUERY_WRITE_DESC:
554eb5073f4SHaojian Zhuang 		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
555eb5073f4SHaojian Zhuang 		query_upiu->ts.desc.length = htobe16(length);
556eb5073f4SHaojian Zhuang 		memcpy((void *)(utrd->upiu + sizeof(query_upiu_t)),
557eb5073f4SHaojian Zhuang 		       (void *)buf, length);
558eb5073f4SHaojian Zhuang 		break;
559eb5073f4SHaojian Zhuang 	case QUERY_READ_ATTR:
560eb5073f4SHaojian Zhuang 	case QUERY_READ_FLAG:
561eb5073f4SHaojian Zhuang 		query_upiu->query_func = QUERY_FUNC_STD_READ;
562eb5073f4SHaojian Zhuang 		break;
563eb5073f4SHaojian Zhuang 	case QUERY_CLEAR_FLAG:
564eb5073f4SHaojian Zhuang 	case QUERY_SET_FLAG:
565eb5073f4SHaojian Zhuang 		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
566eb5073f4SHaojian Zhuang 		break;
567eb5073f4SHaojian Zhuang 	case QUERY_WRITE_ATTR:
568eb5073f4SHaojian Zhuang 		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
569a4755183Sanans 		query_upiu->ts.attr.value = htobe32(*((uint32_t *)buf));
570eb5073f4SHaojian Zhuang 		break;
571eb5073f4SHaojian Zhuang 	default:
572eb5073f4SHaojian Zhuang 		assert(0);
5735aa7498aSJonathan Wright 		break;
574eb5073f4SHaojian Zhuang 	}
575eb5073f4SHaojian Zhuang 	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
576eb5073f4SHaojian Zhuang 	return 0;
577eb5073f4SHaojian Zhuang }
578eb5073f4SHaojian Zhuang 
ufs_prepare_nop_out(utp_utrd_t * utrd)579eb5073f4SHaojian Zhuang static void ufs_prepare_nop_out(utp_utrd_t *utrd)
580eb5073f4SHaojian Zhuang {
581eb5073f4SHaojian Zhuang 	utrd_header_t *hd;
582eb5073f4SHaojian Zhuang 	nop_out_upiu_t *nop_out;
583eb5073f4SHaojian Zhuang 
584eb5073f4SHaojian Zhuang 	hd = (utrd_header_t *)utrd->header;
585eb5073f4SHaojian Zhuang 	nop_out = (nop_out_upiu_t *)utrd->upiu;
586eb5073f4SHaojian Zhuang 
587eb5073f4SHaojian Zhuang 	hd->i = 1;
588eb5073f4SHaojian Zhuang 	hd->ct = CT_UFS_STORAGE;
589eb5073f4SHaojian Zhuang 	hd->ocs = OCS_MASK;
590eb5073f4SHaojian Zhuang 
591eb5073f4SHaojian Zhuang 	nop_out->trans_type = 0;
592eb5073f4SHaojian Zhuang 	nop_out->task_tag = utrd->task_tag;
593eb5073f4SHaojian Zhuang 	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
594eb5073f4SHaojian Zhuang }
595eb5073f4SHaojian Zhuang 
ufs_send_request(int task_tag)596eb5073f4SHaojian Zhuang static void ufs_send_request(int task_tag)
597eb5073f4SHaojian Zhuang {
598eb5073f4SHaojian Zhuang 	unsigned int data;
599eb5073f4SHaojian Zhuang 	int slot;
600eb5073f4SHaojian Zhuang 
601eb5073f4SHaojian Zhuang 	slot = task_tag - 1;
602eb5073f4SHaojian Zhuang 	/* clear all interrupts */
603eb5073f4SHaojian Zhuang 	mmio_write_32(ufs_params.reg_base + IS, ~0);
604eb5073f4SHaojian Zhuang 
605eb5073f4SHaojian Zhuang 	mmio_write_32(ufs_params.reg_base + UTRLRSR, 1);
606660c208dSanans 	assert(mmio_read_32(ufs_params.reg_base + UTRLRSR) == 1);
607eb5073f4SHaojian Zhuang 
608eb5073f4SHaojian Zhuang 	data = UTRIACR_IAEN | UTRIACR_CTR | UTRIACR_IACTH(0x1F) |
609eb5073f4SHaojian Zhuang 	       UTRIACR_IATOVAL(0xFF);
610eb5073f4SHaojian Zhuang 	mmio_write_32(ufs_params.reg_base + UTRIACR, data);
611eb5073f4SHaojian Zhuang 	/* send request */
612e47d8a58SAnand Saminathan 	mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1U << slot);
613eb5073f4SHaojian Zhuang }
614eb5073f4SHaojian Zhuang 
ufs_check_resp(utp_utrd_t * utrd,int trans_type,unsigned int timeout_ms)6152c5bce38SAnand Saminathan static int ufs_check_resp(utp_utrd_t *utrd, int trans_type, unsigned int timeout_ms)
616eb5073f4SHaojian Zhuang {
617eb5073f4SHaojian Zhuang 	utrd_header_t *hd;
618eb5073f4SHaojian Zhuang 	resp_upiu_t *resp;
6193d309556SAnand Saminathan 	sense_data_t *sense;
620eb5073f4SHaojian Zhuang 	unsigned int data;
6212c5bce38SAnand Saminathan 	int slot, result;
622eb5073f4SHaojian Zhuang 
623eb5073f4SHaojian Zhuang 	hd = (utrd_header_t *)utrd->header;
624eb5073f4SHaojian Zhuang 	resp = (resp_upiu_t *)utrd->resp_upiu;
6252c5bce38SAnand Saminathan 
6262c5bce38SAnand Saminathan 	result = ufs_wait_for_int_status(UFS_INT_UTRCS, timeout_ms, false);
6272c5bce38SAnand Saminathan 	if (result != 0) {
6282c5bce38SAnand Saminathan 		return result;
6292c5bce38SAnand Saminathan 	}
6302c5bce38SAnand Saminathan 
631eb5073f4SHaojian Zhuang 	slot = utrd->task_tag - 1;
632eb5073f4SHaojian Zhuang 
633eb5073f4SHaojian Zhuang 	data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
634eb5073f4SHaojian Zhuang 	assert((data & (1 << slot)) == 0);
63538a5ecb7SChannagoud kadabi 	/*
63638a5ecb7SChannagoud kadabi 	 * Invalidate the header after DMA read operation has
63738a5ecb7SChannagoud kadabi 	 * completed to avoid cpu referring to the prefetched
63838a5ecb7SChannagoud kadabi 	 * data brought in before DMA completion.
63938a5ecb7SChannagoud kadabi 	 */
64038a5ecb7SChannagoud kadabi 	inv_dcache_range((uintptr_t)hd, UFS_DESC_SIZE);
641eb5073f4SHaojian Zhuang 	assert(hd->ocs == OCS_SUCCESS);
642eb5073f4SHaojian Zhuang 	assert((resp->trans_type & TRANS_TYPE_CODE_MASK) == trans_type);
6433d309556SAnand Saminathan 
6443d309556SAnand Saminathan 	sense = &resp->sd.sense;
6453d309556SAnand Saminathan 	if (sense->resp_code == SENSE_DATA_VALID &&
6463d309556SAnand Saminathan 	    sense->sense_key == SENSE_KEY_UNIT_ATTENTION && sense->asc == 0x29 &&
6473d309556SAnand Saminathan 	    sense->ascq == 0) {
6483d309556SAnand Saminathan 		WARN("Unit Attention Condition\n");
6493d309556SAnand Saminathan 		return -EAGAIN;
6503d309556SAnand Saminathan 	}
6513d309556SAnand Saminathan 
652eb5073f4SHaojian Zhuang 	(void)resp;
653eb5073f4SHaojian Zhuang 	(void)slot;
6542c5bce38SAnand Saminathan 	(void)data;
655eb5073f4SHaojian Zhuang 	return 0;
656eb5073f4SHaojian Zhuang }
657eb5073f4SHaojian Zhuang 
ufs_send_cmd(utp_utrd_t * utrd,uint8_t cmd_op,uint8_t lun,int lba,uintptr_t buf,size_t length)6586e16f7f0Sanans static void ufs_send_cmd(utp_utrd_t *utrd, uint8_t cmd_op, uint8_t lun, int lba, uintptr_t buf,
6596e16f7f0Sanans 			 size_t length)
6606e16f7f0Sanans {
6613d309556SAnand Saminathan 	int result, i;
6626e16f7f0Sanans 
6633d309556SAnand Saminathan 	for (i = 0; i < UFS_CMD_RETRIES; ++i) {
6646e16f7f0Sanans 		get_utrd(utrd);
6656e16f7f0Sanans 		result = ufs_prepare_cmd(utrd, cmd_op, lun, lba, buf, length);
6666e16f7f0Sanans 		assert(result == 0);
6676e16f7f0Sanans 		ufs_send_request(utrd->task_tag);
6682c5bce38SAnand Saminathan 		result = ufs_check_resp(utrd, RESPONSE_UPIU, CMD_TIMEOUT_MS);
6693d309556SAnand Saminathan 		if (result == 0 || result == -EIO) {
6703d309556SAnand Saminathan 			break;
6713d309556SAnand Saminathan 		}
6723d309556SAnand Saminathan 	}
6736e16f7f0Sanans 	assert(result == 0);
6746e16f7f0Sanans 	(void)result;
6756e16f7f0Sanans }
6766e16f7f0Sanans 
677eb5073f4SHaojian Zhuang #ifdef UFS_RESP_DEBUG
dump_upiu(utp_utrd_t * utrd)678eb5073f4SHaojian Zhuang static void dump_upiu(utp_utrd_t *utrd)
679eb5073f4SHaojian Zhuang {
680eb5073f4SHaojian Zhuang 	utrd_header_t *hd;
681eb5073f4SHaojian Zhuang 	int i;
682eb5073f4SHaojian Zhuang 
683eb5073f4SHaojian Zhuang 	hd = (utrd_header_t *)utrd->header;
684eb5073f4SHaojian Zhuang 	INFO("utrd:0x%x, ruo:0x%x, rul:0x%x, ocs:0x%x, UTRLDBR:0x%x\n",
685eb5073f4SHaojian Zhuang 		(unsigned int)(uintptr_t)utrd, hd->ruo, hd->rul, hd->ocs,
686eb5073f4SHaojian Zhuang 		mmio_read_32(ufs_params.reg_base + UTRLDBR));
687eb5073f4SHaojian Zhuang 	for (i = 0; i < sizeof(utrd_header_t); i += 4) {
688eb5073f4SHaojian Zhuang 		INFO("[%lx]:0x%x\n",
689eb5073f4SHaojian Zhuang 			(uintptr_t)utrd->header + i,
690eb5073f4SHaojian Zhuang 			*(unsigned int *)((uintptr_t)utrd->header + i));
691eb5073f4SHaojian Zhuang 	}
692eb5073f4SHaojian Zhuang 
693eb5073f4SHaojian Zhuang 	for (i = 0; i < sizeof(cmd_upiu_t); i += 4) {
694eb5073f4SHaojian Zhuang 		INFO("cmd[%lx]:0x%x\n",
695eb5073f4SHaojian Zhuang 			utrd->upiu + i,
696eb5073f4SHaojian Zhuang 			*(unsigned int *)(utrd->upiu + i));
697eb5073f4SHaojian Zhuang 	}
698eb5073f4SHaojian Zhuang 	for (i = 0; i < sizeof(resp_upiu_t); i += 4) {
699eb5073f4SHaojian Zhuang 		INFO("resp[%lx]:0x%x\n",
700eb5073f4SHaojian Zhuang 			utrd->resp_upiu + i,
701eb5073f4SHaojian Zhuang 			*(unsigned int *)(utrd->resp_upiu + i));
702eb5073f4SHaojian Zhuang 	}
703eb5073f4SHaojian Zhuang 	for (i = 0; i < sizeof(prdt_t); i += 4) {
704eb5073f4SHaojian Zhuang 		INFO("prdt[%lx]:0x%x\n",
705eb5073f4SHaojian Zhuang 			utrd->prdt + i,
706eb5073f4SHaojian Zhuang 			*(unsigned int *)(utrd->prdt + i));
707eb5073f4SHaojian Zhuang 	}
708eb5073f4SHaojian Zhuang }
709eb5073f4SHaojian Zhuang #endif
710eb5073f4SHaojian Zhuang 
ufs_verify_init(void)711eb5073f4SHaojian Zhuang static void ufs_verify_init(void)
712eb5073f4SHaojian Zhuang {
713eb5073f4SHaojian Zhuang 	utp_utrd_t utrd;
714eb5073f4SHaojian Zhuang 	int result;
715eb5073f4SHaojian Zhuang 
716eb5073f4SHaojian Zhuang 	get_utrd(&utrd);
717eb5073f4SHaojian Zhuang 	ufs_prepare_nop_out(&utrd);
718eb5073f4SHaojian Zhuang 	ufs_send_request(utrd.task_tag);
7192c5bce38SAnand Saminathan 	result = ufs_check_resp(&utrd, NOP_IN_UPIU, NOP_OUT_TIMEOUT_MS);
720eb5073f4SHaojian Zhuang 	assert(result == 0);
721eb5073f4SHaojian Zhuang 	(void)result;
722eb5073f4SHaojian Zhuang }
723eb5073f4SHaojian Zhuang 
ufs_verify_ready(void)724eb5073f4SHaojian Zhuang static void ufs_verify_ready(void)
725eb5073f4SHaojian Zhuang {
726eb5073f4SHaojian Zhuang 	utp_utrd_t utrd;
7276e16f7f0Sanans 	ufs_send_cmd(&utrd, CDBCMD_TEST_UNIT_READY, 0, 0, 0, 0);
728eb5073f4SHaojian Zhuang }
729eb5073f4SHaojian Zhuang 
ufs_query(uint8_t op,uint8_t idn,uint8_t index,uint8_t sel,uintptr_t buf,size_t size)730eb5073f4SHaojian Zhuang static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel,
731eb5073f4SHaojian Zhuang 		      uintptr_t buf, size_t size)
732eb5073f4SHaojian Zhuang {
733eb5073f4SHaojian Zhuang 	utp_utrd_t utrd;
734eb5073f4SHaojian Zhuang 	query_resp_upiu_t *resp;
735eb5073f4SHaojian Zhuang 	int result;
736eb5073f4SHaojian Zhuang 
737eb5073f4SHaojian Zhuang 	switch (op) {
738eb5073f4SHaojian Zhuang 	case QUERY_READ_FLAG:
739eb5073f4SHaojian Zhuang 	case QUERY_READ_ATTR:
740eb5073f4SHaojian Zhuang 	case QUERY_READ_DESC:
741eb5073f4SHaojian Zhuang 	case QUERY_WRITE_DESC:
742eb5073f4SHaojian Zhuang 	case QUERY_WRITE_ATTR:
743eb5073f4SHaojian Zhuang 		assert(((buf & 3) == 0) && (size != 0));
744eb5073f4SHaojian Zhuang 		break;
7455aa7498aSJonathan Wright 	default:
7465aa7498aSJonathan Wright 		/* Do nothing in default case */
7475aa7498aSJonathan Wright 		break;
748eb5073f4SHaojian Zhuang 	}
749eb5073f4SHaojian Zhuang 	get_utrd(&utrd);
750eb5073f4SHaojian Zhuang 	ufs_prepare_query(&utrd, op, idn, index, sel, buf, size);
751eb5073f4SHaojian Zhuang 	ufs_send_request(utrd.task_tag);
7522c5bce38SAnand Saminathan 	result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU, QUERY_REQ_TIMEOUT_MS);
753eb5073f4SHaojian Zhuang 	assert(result == 0);
754eb5073f4SHaojian Zhuang 	resp = (query_resp_upiu_t *)utrd.resp_upiu;
755eb5073f4SHaojian Zhuang #ifdef UFS_RESP_DEBUG
756eb5073f4SHaojian Zhuang 	dump_upiu(&utrd);
757eb5073f4SHaojian Zhuang #endif
758eb5073f4SHaojian Zhuang 	assert(resp->query_resp == QUERY_RESP_SUCCESS);
759eb5073f4SHaojian Zhuang 
760eb5073f4SHaojian Zhuang 	switch (op) {
761eb5073f4SHaojian Zhuang 	case QUERY_READ_FLAG:
762eb5073f4SHaojian Zhuang 		*(uint32_t *)buf = (uint32_t)resp->ts.flag.value;
763eb5073f4SHaojian Zhuang 		break;
764eb5073f4SHaojian Zhuang 	case QUERY_READ_DESC:
765eb5073f4SHaojian Zhuang 		memcpy((void *)buf,
766eb5073f4SHaojian Zhuang 		       (void *)(utrd.resp_upiu + sizeof(query_resp_upiu_t)),
767eb5073f4SHaojian Zhuang 		       size);
768eb5073f4SHaojian Zhuang 		break;
769a4755183Sanans 	case QUERY_READ_ATTR:
770a4755183Sanans 		*(uint32_t *)buf = htobe32(resp->ts.attr.value);
771a4755183Sanans 		break;
7725aa7498aSJonathan Wright 	default:
7735aa7498aSJonathan Wright 		/* Do nothing in default case */
7745aa7498aSJonathan Wright 		break;
775eb5073f4SHaojian Zhuang 	}
776eb5073f4SHaojian Zhuang 	(void)result;
777eb5073f4SHaojian Zhuang }
778eb5073f4SHaojian Zhuang 
ufs_read_attr(int idn)779eb5073f4SHaojian Zhuang unsigned int ufs_read_attr(int idn)
780eb5073f4SHaojian Zhuang {
781eb5073f4SHaojian Zhuang 	unsigned int value;
782eb5073f4SHaojian Zhuang 
783eb5073f4SHaojian Zhuang 	ufs_query(QUERY_READ_ATTR, idn, 0, 0,
784eb5073f4SHaojian Zhuang 		  (uintptr_t)&value, sizeof(value));
785eb5073f4SHaojian Zhuang 	return value;
786eb5073f4SHaojian Zhuang }
787eb5073f4SHaojian Zhuang 
ufs_write_attr(int idn,unsigned int value)788eb5073f4SHaojian Zhuang void ufs_write_attr(int idn, unsigned int value)
789eb5073f4SHaojian Zhuang {
790eb5073f4SHaojian Zhuang 	ufs_query(QUERY_WRITE_ATTR, idn, 0, 0,
791eb5073f4SHaojian Zhuang 		  (uintptr_t)&value, sizeof(value));
792eb5073f4SHaojian Zhuang }
793eb5073f4SHaojian Zhuang 
ufs_read_flag(int idn)794eb5073f4SHaojian Zhuang unsigned int ufs_read_flag(int idn)
795eb5073f4SHaojian Zhuang {
796eb5073f4SHaojian Zhuang 	unsigned int value;
797eb5073f4SHaojian Zhuang 
798eb5073f4SHaojian Zhuang 	ufs_query(QUERY_READ_FLAG, idn, 0, 0,
799eb5073f4SHaojian Zhuang 		  (uintptr_t)&value, sizeof(value));
800eb5073f4SHaojian Zhuang 	return value;
801eb5073f4SHaojian Zhuang }
802eb5073f4SHaojian Zhuang 
ufs_set_flag(int idn)803eb5073f4SHaojian Zhuang void ufs_set_flag(int idn)
804eb5073f4SHaojian Zhuang {
805eb5073f4SHaojian Zhuang 	ufs_query(QUERY_SET_FLAG, idn, 0, 0, 0, 0);
806eb5073f4SHaojian Zhuang }
807eb5073f4SHaojian Zhuang 
ufs_clear_flag(int idn)808eb5073f4SHaojian Zhuang void ufs_clear_flag(int idn)
809eb5073f4SHaojian Zhuang {
810eb5073f4SHaojian Zhuang 	ufs_query(QUERY_CLEAR_FLAG, idn, 0, 0, 0, 0);
811eb5073f4SHaojian Zhuang }
812eb5073f4SHaojian Zhuang 
ufs_read_desc(int idn,int index,uintptr_t buf,size_t size)813eb5073f4SHaojian Zhuang void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size)
814eb5073f4SHaojian Zhuang {
815eb5073f4SHaojian Zhuang 	ufs_query(QUERY_READ_DESC, idn, index, 0, buf, size);
816eb5073f4SHaojian Zhuang }
817eb5073f4SHaojian Zhuang 
ufs_write_desc(int idn,int index,uintptr_t buf,size_t size)818eb5073f4SHaojian Zhuang void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size)
819eb5073f4SHaojian Zhuang {
820eb5073f4SHaojian Zhuang 	ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size);
821eb5073f4SHaojian Zhuang }
822eb5073f4SHaojian Zhuang 
ufs_read_capacity(int lun,unsigned int * num,unsigned int * size)82328645ebdSRohit Ner static int ufs_read_capacity(int lun, unsigned int *num, unsigned int *size)
824eb5073f4SHaojian Zhuang {
825eb5073f4SHaojian Zhuang 	utp_utrd_t utrd;
826eb5073f4SHaojian Zhuang 	resp_upiu_t *resp;
827eb5073f4SHaojian Zhuang 	sense_data_t *sense;
828eb5073f4SHaojian Zhuang 	unsigned char data[CACHE_WRITEBACK_GRANULE << 1];
829eb5073f4SHaojian Zhuang 	uintptr_t buf;
83028645ebdSRohit Ner 	int retries = UFS_READ_CAPACITY_RETRIES;
831eb5073f4SHaojian Zhuang 
832eb5073f4SHaojian Zhuang 	assert((ufs_params.reg_base != 0) &&
833eb5073f4SHaojian Zhuang 	       (ufs_params.desc_base != 0) &&
834eb5073f4SHaojian Zhuang 	       (ufs_params.desc_size >= UFS_DESC_SIZE) &&
835eb5073f4SHaojian Zhuang 	       (num != NULL) && (size != NULL));
836eb5073f4SHaojian Zhuang 
837eb5073f4SHaojian Zhuang 	/* align buf address */
838eb5073f4SHaojian Zhuang 	buf = (uintptr_t)data;
839eb5073f4SHaojian Zhuang 	buf = (buf + CACHE_WRITEBACK_GRANULE - 1) &
840eb5073f4SHaojian Zhuang 	      ~(CACHE_WRITEBACK_GRANULE - 1);
841eb5073f4SHaojian Zhuang 	do {
8426e16f7f0Sanans 		ufs_send_cmd(&utrd, CDBCMD_READ_CAPACITY_10, lun, 0,
843eb5073f4SHaojian Zhuang 			    buf, READ_CAPACITY_LENGTH);
844eb5073f4SHaojian Zhuang #ifdef UFS_RESP_DEBUG
845eb5073f4SHaojian Zhuang 		dump_upiu(&utrd);
846eb5073f4SHaojian Zhuang #endif
847eb5073f4SHaojian Zhuang 		resp = (resp_upiu_t *)utrd.resp_upiu;
848eb5073f4SHaojian Zhuang 		sense = &resp->sd.sense;
84928645ebdSRohit Ner 		if (!((sense->resp_code == SENSE_DATA_VALID) &&
85028645ebdSRohit Ner 		    (sense->sense_key == SENSE_KEY_UNIT_ATTENTION) &&
85128645ebdSRohit Ner 		    (sense->asc == 0x29) && (sense->ascq == 0))) {
852eb5073f4SHaojian Zhuang 			inv_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
853eb5073f4SHaojian Zhuang 			/* last logical block address */
854eb5073f4SHaojian Zhuang 			*num = be32toh(*(unsigned int *)buf);
855eb5073f4SHaojian Zhuang 			if (*num)
856eb5073f4SHaojian Zhuang 				*num += 1;
857eb5073f4SHaojian Zhuang 			/* logical block length in bytes */
858eb5073f4SHaojian Zhuang 			*size = be32toh(*(unsigned int *)(buf + 4));
85928645ebdSRohit Ner 
86028645ebdSRohit Ner 			return 0;
86128645ebdSRohit Ner 		}
86228645ebdSRohit Ner 
86328645ebdSRohit Ner 	} while (retries-- > 0);
86428645ebdSRohit Ner 
86528645ebdSRohit Ner 	return -ETIMEDOUT;
866eb5073f4SHaojian Zhuang }
867eb5073f4SHaojian Zhuang 
ufs_read_blocks(int lun,int lba,uintptr_t buf,size_t size)868eb5073f4SHaojian Zhuang size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size)
869eb5073f4SHaojian Zhuang {
870eb5073f4SHaojian Zhuang 	utp_utrd_t utrd;
871eb5073f4SHaojian Zhuang 	resp_upiu_t *resp;
872eb5073f4SHaojian Zhuang 
873eb5073f4SHaojian Zhuang 	assert((ufs_params.reg_base != 0) &&
874eb5073f4SHaojian Zhuang 	       (ufs_params.desc_base != 0) &&
875eb5073f4SHaojian Zhuang 	       (ufs_params.desc_size >= UFS_DESC_SIZE));
876eb5073f4SHaojian Zhuang 
8776e16f7f0Sanans 	ufs_send_cmd(&utrd, CDBCMD_READ_10, lun, lba, buf, size);
878eb5073f4SHaojian Zhuang #ifdef UFS_RESP_DEBUG
879eb5073f4SHaojian Zhuang 	dump_upiu(&utrd);
880eb5073f4SHaojian Zhuang #endif
88138a5ecb7SChannagoud kadabi 	/*
88238a5ecb7SChannagoud kadabi 	 * Invalidate prefetched cache contents before cpu
88338a5ecb7SChannagoud kadabi 	 * accesses the buf.
88438a5ecb7SChannagoud kadabi 	 */
88538a5ecb7SChannagoud kadabi 	inv_dcache_range(buf, size);
886eb5073f4SHaojian Zhuang 	resp = (resp_upiu_t *)utrd.resp_upiu;
887eb5073f4SHaojian Zhuang 	return size - resp->res_trans_cnt;
888eb5073f4SHaojian Zhuang }
889eb5073f4SHaojian Zhuang 
ufs_write_blocks(int lun,int lba,const uintptr_t buf,size_t size)890eb5073f4SHaojian Zhuang size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size)
891eb5073f4SHaojian Zhuang {
892eb5073f4SHaojian Zhuang 	utp_utrd_t utrd;
893eb5073f4SHaojian Zhuang 	resp_upiu_t *resp;
894eb5073f4SHaojian Zhuang 
895eb5073f4SHaojian Zhuang 	assert((ufs_params.reg_base != 0) &&
896eb5073f4SHaojian Zhuang 	       (ufs_params.desc_base != 0) &&
897eb5073f4SHaojian Zhuang 	       (ufs_params.desc_size >= UFS_DESC_SIZE));
898eb5073f4SHaojian Zhuang 
8996e16f7f0Sanans 	ufs_send_cmd(&utrd, CDBCMD_WRITE_10, lun, lba, buf, size);
900eb5073f4SHaojian Zhuang #ifdef UFS_RESP_DEBUG
901eb5073f4SHaojian Zhuang 	dump_upiu(&utrd);
902eb5073f4SHaojian Zhuang #endif
903eb5073f4SHaojian Zhuang 	resp = (resp_upiu_t *)utrd.resp_upiu;
904eb5073f4SHaojian Zhuang 	return size - resp->res_trans_cnt;
905eb5073f4SHaojian Zhuang }
906eb5073f4SHaojian Zhuang 
ufs_set_fdevice_init(void)90750593e69Sanans static int ufs_set_fdevice_init(void)
90850593e69Sanans {
90950593e69Sanans 	unsigned int result;
91050593e69Sanans 	int timeout;
91150593e69Sanans 
91250593e69Sanans 	ufs_set_flag(FLAG_DEVICE_INIT);
91350593e69Sanans 
91450593e69Sanans 	timeout = FDEVICEINIT_TIMEOUT_MS;
91550593e69Sanans 	do {
91650593e69Sanans 		result = ufs_read_flag(FLAG_DEVICE_INIT);
91750593e69Sanans 		if (!result) {
91850593e69Sanans 			break;
91950593e69Sanans 		}
92050593e69Sanans 		mdelay(5);
92150593e69Sanans 		timeout -= 5;
92250593e69Sanans 	} while (timeout > 0);
92350593e69Sanans 
92450593e69Sanans 	if (result != 0U) {
92550593e69Sanans 		return -ETIMEDOUT;
92650593e69Sanans 	}
92750593e69Sanans 
92850593e69Sanans 	return 0;
92950593e69Sanans }
93050593e69Sanans 
ufs_enum(void)931eb5073f4SHaojian Zhuang static void ufs_enum(void)
932eb5073f4SHaojian Zhuang {
933eb5073f4SHaojian Zhuang 	unsigned int blk_num, blk_size;
93450593e69Sanans 	int i, result;
935eb5073f4SHaojian Zhuang 
9369d6d1a94Sanans 	mmio_write_32(ufs_params.reg_base + UTRLBA,
9379d6d1a94Sanans 		      ufs_params.desc_base & UINT32_MAX);
9389d6d1a94Sanans 	mmio_write_32(ufs_params.reg_base + UTRLBAU,
9399d6d1a94Sanans 		      (ufs_params.desc_base >> 32) & UINT32_MAX);
9409d6d1a94Sanans 
941eb5073f4SHaojian Zhuang 	ufs_verify_init();
942eb5073f4SHaojian Zhuang 	ufs_verify_ready();
943eb5073f4SHaojian Zhuang 
94450593e69Sanans 	result = ufs_set_fdevice_init();
94550593e69Sanans 	assert(result == 0);
94650593e69Sanans 
94728645ebdSRohit Ner 	blk_num = 0;
94828645ebdSRohit Ner 	blk_size = 0;
94928645ebdSRohit Ner 
950eb5073f4SHaojian Zhuang 	/* dump available LUNs */
951eb5073f4SHaojian Zhuang 	for (i = 0; i < UFS_MAX_LUNS; i++) {
95228645ebdSRohit Ner 		result = ufs_read_capacity(i, &blk_num, &blk_size);
95328645ebdSRohit Ner 		if (result != 0) {
95428645ebdSRohit Ner 			WARN("UFS LUN%d dump failed\n", i);
95528645ebdSRohit Ner 		}
956eb5073f4SHaojian Zhuang 		if (blk_num && blk_size) {
957eb5073f4SHaojian Zhuang 			INFO("UFS LUN%d contains %d blocks with %d-byte size\n",
958eb5073f4SHaojian Zhuang 			     i, blk_num, blk_size);
959eb5073f4SHaojian Zhuang 		}
960eb5073f4SHaojian Zhuang 	}
96150593e69Sanans 
96250593e69Sanans 	(void)result;
963eb5073f4SHaojian Zhuang }
964eb5073f4SHaojian Zhuang 
ufs_get_device_info(struct ufs_dev_desc * card_data)9655ac25de6Sfengbaopeng static void ufs_get_device_info(struct ufs_dev_desc *card_data)
9665ac25de6Sfengbaopeng {
9675ac25de6Sfengbaopeng 	uint8_t desc_buf[DESC_DEVICE_MAX_SIZE];
9685ac25de6Sfengbaopeng 
969*74ac476cSRohit Ner 	ufs_read_desc(DESC_TYPE_DEVICE, 0, (uintptr_t)desc_buf, DESC_DEVICE_MAX_SIZE);
9705ac25de6Sfengbaopeng 
9715ac25de6Sfengbaopeng 	/*
9725ac25de6Sfengbaopeng 	 * getting vendor (manufacturerID) and Bank Index in big endian
9735ac25de6Sfengbaopeng 	 * format
9745ac25de6Sfengbaopeng 	 */
9755ac25de6Sfengbaopeng 	card_data->wmanufacturerid = (uint16_t)((desc_buf[DEVICE_DESC_PARAM_MANF_ID] << 8) |
9765ac25de6Sfengbaopeng 				     (desc_buf[DEVICE_DESC_PARAM_MANF_ID + 1]));
9775ac25de6Sfengbaopeng }
9785ac25de6Sfengbaopeng 
ufs_init(const ufs_ops_t * ops,ufs_params_t * params)979eb5073f4SHaojian Zhuang int ufs_init(const ufs_ops_t *ops, ufs_params_t *params)
980eb5073f4SHaojian Zhuang {
981eb5073f4SHaojian Zhuang 	int result;
982eb5073f4SHaojian Zhuang 	unsigned int data;
983eb5073f4SHaojian Zhuang 	uic_cmd_t cmd;
9845ac25de6Sfengbaopeng 	struct ufs_dev_desc card = {0};
985eb5073f4SHaojian Zhuang 
986eb5073f4SHaojian Zhuang 	assert((params != NULL) &&
987eb5073f4SHaojian Zhuang 	       (params->reg_base != 0) &&
988eb5073f4SHaojian Zhuang 	       (params->desc_base != 0) &&
989eb5073f4SHaojian Zhuang 	       (params->desc_size >= UFS_DESC_SIZE));
990eb5073f4SHaojian Zhuang 
991eb5073f4SHaojian Zhuang 	memcpy(&ufs_params, params, sizeof(ufs_params_t));
992eb5073f4SHaojian Zhuang 
9930956319bSanans 	/* 0 means 1 slot */
9940956319bSanans 	nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;
9950956319bSanans 	if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE)) {
9960956319bSanans 		nutrs = ufs_params.desc_size / UFS_DESC_SIZE;
9970956319bSanans 	}
9980956319bSanans 
9990956319bSanans 
1000eb5073f4SHaojian Zhuang 	if (ufs_params.flags & UFS_FLAGS_SKIPINIT) {
10019d6d1a94Sanans 		mmio_write_32(ufs_params.reg_base + UTRLBA,
10029d6d1a94Sanans 			      ufs_params.desc_base & UINT32_MAX);
10039d6d1a94Sanans 		mmio_write_32(ufs_params.reg_base + UTRLBAU,
10049d6d1a94Sanans 			      (ufs_params.desc_base >> 32) & UINT32_MAX);
10059d6d1a94Sanans 
1006eb5073f4SHaojian Zhuang 		result = ufshc_dme_get(0x1571, 0, &data);
1007eb5073f4SHaojian Zhuang 		assert(result == 0);
1008eb5073f4SHaojian Zhuang 		result = ufshc_dme_get(0x41, 0, &data);
1009eb5073f4SHaojian Zhuang 		assert(result == 0);
1010eb5073f4SHaojian Zhuang 		if (data == 1) {
1011eb5073f4SHaojian Zhuang 			/* prepare to exit hibernate mode */
1012eb5073f4SHaojian Zhuang 			memset(&cmd, 0, sizeof(uic_cmd_t));
1013eb5073f4SHaojian Zhuang 			cmd.op = DME_HIBERNATE_EXIT;
1014eb5073f4SHaojian Zhuang 			result = ufshc_send_uic_cmd(ufs_params.reg_base,
1015eb5073f4SHaojian Zhuang 						    &cmd);
1016eb5073f4SHaojian Zhuang 			assert(result == 0);
1017eb5073f4SHaojian Zhuang 			data = mmio_read_32(ufs_params.reg_base + UCMDARG2);
1018eb5073f4SHaojian Zhuang 			assert(data == 0);
1019eb5073f4SHaojian Zhuang 			do {
1020eb5073f4SHaojian Zhuang 				data = mmio_read_32(ufs_params.reg_base + IS);
1021eb5073f4SHaojian Zhuang 			} while ((data & UFS_INT_UHXS) == 0);
1022eb5073f4SHaojian Zhuang 			mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS);
1023eb5073f4SHaojian Zhuang 			data = mmio_read_32(ufs_params.reg_base + HCS);
1024eb5073f4SHaojian Zhuang 			assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL);
1025eb5073f4SHaojian Zhuang 		}
1026eb5073f4SHaojian Zhuang 		result = ufshc_dme_get(0x1568, 0, &data);
1027eb5073f4SHaojian Zhuang 		assert(result == 0);
1028eb5073f4SHaojian Zhuang 		assert((data > 0) && (data <= 3));
1029eb5073f4SHaojian Zhuang 	} else {
1030eb5073f4SHaojian Zhuang 		assert((ops != NULL) && (ops->phy_init != NULL) &&
1031eb5073f4SHaojian Zhuang 		       (ops->phy_set_pwr_mode != NULL));
1032eb5073f4SHaojian Zhuang 
103399ff1a35SJorge Troncoso 		result = ufshc_reset(ufs_params.reg_base);
103499ff1a35SJorge Troncoso 		assert(result == 0);
1035eb5073f4SHaojian Zhuang 		ops->phy_init(&ufs_params);
1036eb5073f4SHaojian Zhuang 		result = ufshc_link_startup(ufs_params.reg_base);
1037eb5073f4SHaojian Zhuang 		assert(result == 0);
10385ac25de6Sfengbaopeng 
10392c5bce38SAnand Saminathan 		/* enable all interrupts */
10402c5bce38SAnand Saminathan 		data = UFS_INT_UCCS | UFS_INT_UHES | UFS_INT_UHXS | UFS_INT_UPMS;
10412c5bce38SAnand Saminathan 		data |= UFS_INT_UTRCS | UFS_INT_ERR;
10422c5bce38SAnand Saminathan 		mmio_write_32(ufs_params.reg_base + IE, data);
10432c5bce38SAnand Saminathan 
10445ac25de6Sfengbaopeng 		ufs_enum();
10455ac25de6Sfengbaopeng 
10465ac25de6Sfengbaopeng 		ufs_get_device_info(&card);
10475ac25de6Sfengbaopeng 		if (card.wmanufacturerid == UFS_VENDOR_SKHYNIX) {
10485ac25de6Sfengbaopeng 			ufs_params.flags |= UFS_FLAGS_VENDOR_SKHYNIX;
10495ac25de6Sfengbaopeng 		}
10505ac25de6Sfengbaopeng 
1051eb5073f4SHaojian Zhuang 		ops->phy_set_pwr_mode(&ufs_params);
1052eb5073f4SHaojian Zhuang 	}
1053eb5073f4SHaojian Zhuang 
1054eb5073f4SHaojian Zhuang 	(void)result;
1055eb5073f4SHaojian Zhuang 	return 0;
1056eb5073f4SHaojian Zhuang }
1057