Merge pull request #955 from hzhuang1/ufs

Add ufs stack and designware phy
diff --git a/drivers/synopsys/ufs/dw_ufs.c b/drivers/synopsys/ufs/dw_ufs.c
new file mode 100644
index 0000000..d8ed5b6
--- /dev/null
+++ b/drivers/synopsys/ufs/dw_ufs.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <dw_ufs.h>
+#include <mmio.h>
+#include <stdint.h>
+#include <string.h>
+#include <ufs.h>
+
+static int dwufs_phy_init(ufs_params_t *params)
+{
+	uintptr_t base;
+	unsigned int fsm0, fsm1;
+	unsigned int data;
+	int result;
+
+	assert((params != NULL) && (params->reg_base != 0));
+
+	base = params->reg_base;
+
+	/* Unipro VS_MPHY disable */
+	ufshc_dme_set(VS_MPHY_DISABLE_OFFSET, 0, VS_MPHY_DISABLE_MPHYDIS);
+	ufshc_dme_set(PA_HS_SERIES_OFFSET, 0, 2);
+	/* MPHY CBRATESEL */
+	ufshc_dme_set(0x8114, 0, 1);
+	/* MPHY CBOVRCTRL2 */
+	ufshc_dme_set(0x8121, 0, 0x2d);
+	/* MPHY CBOVRCTRL3 */
+	ufshc_dme_set(0x8122, 0, 0x1);
+	ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
+
+	/* MPHY RXOVRCTRL4 rx0 */
+	ufshc_dme_set(0x800d, 4, 0x58);
+	/* MPHY RXOVRCTRL4 rx1 */
+	ufshc_dme_set(0x800d, 5, 0x58);
+	/* MPHY RXOVRCTRL5 rx0 */
+	ufshc_dme_set(0x800e, 4, 0xb);
+	/* MPHY RXOVRCTRL5 rx1 */
+	ufshc_dme_set(0x800e, 5, 0xb);
+	/* MPHY RXSQCONTROL rx0 */
+	ufshc_dme_set(0x8009, 4, 0x1);
+	/* MPHY RXSQCONTROL rx1 */
+	ufshc_dme_set(0x8009, 5, 0x1);
+	ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
+
+	ufshc_dme_set(0x8113, 0, 0x1);
+	ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
+
+	ufshc_dme_set(RX_HS_G3_SYNC_LENGTH_CAP_OFFSET, 4, 0x4a);
+	ufshc_dme_set(RX_HS_G3_SYNC_LENGTH_CAP_OFFSET, 5, 0x4a);
+	ufshc_dme_set(RX_HS_G2_SYNC_LENGTH_CAP_OFFSET, 4, 0x4a);
+	ufshc_dme_set(RX_HS_G2_SYNC_LENGTH_CAP_OFFSET, 5, 0x4a);
+	ufshc_dme_set(RX_MIN_ACTIVATETIME_CAP_OFFSET, 4, 0x7);
+	ufshc_dme_set(RX_MIN_ACTIVATETIME_CAP_OFFSET, 5, 0x7);
+	ufshc_dme_set(TX_HIBERN8TIME_CAP_OFFSET, 0, 0x5);
+	ufshc_dme_set(TX_HIBERN8TIME_CAP_OFFSET, 1, 0x5);
+	ufshc_dme_set(VS_MPHY_CFG_UPDT_OFFSET, 0, 1);
+
+	result = ufshc_dme_get(VS_MPHY_DISABLE_OFFSET, 0, &data);
+	assert((result == 0) && (data == VS_MPHY_DISABLE_MPHYDIS));
+	/* enable Unipro VS MPHY */
+	ufshc_dme_set(VS_MPHY_DISABLE_OFFSET, 0, 0);
+
+	while (1) {
+		result = ufshc_dme_get(TX_FSM_STATE_OFFSET, 0, &fsm0);
+		assert(result == 0);
+		result = ufshc_dme_get(TX_FSM_STATE_OFFSET, 1, &fsm1);
+		assert(result == 0);
+		if ((fsm0 == TX_FSM_STATE_HIBERN8) &&
+		    (fsm1 == TX_FSM_STATE_HIBERN8))
+			break;
+	}
+
+	mmio_write_32(base + HCLKDIV, 0xE4);
+	mmio_clrbits_32(base + AHIT, 0x3FF);
+
+	ufshc_dme_set(PA_LOCAL_TX_LCC_ENABLE_OFFSET, 0, 0);
+	ufshc_dme_set(VS_MK2_EXTN_SUPPORT_OFFSET, 0, 0);
+
+	result = ufshc_dme_get(VS_MK2_EXTN_SUPPORT_OFFSET, 0, &data);
+	assert((result == 0) && (data == 0));
+
+	ufshc_dme_set(DL_AFC0_CREDIT_THRESHOLD_OFFSET, 0, 0);
+	ufshc_dme_set(DL_TC0_OUT_ACK_THRESHOLD_OFFSET, 0, 0);
+	ufshc_dme_set(DL_TC0_TX_FC_THRESHOLD_OFFSET, 0, 9);
+	(void)result;
+	return 0;
+}
+
+static int dwufs_phy_set_pwr_mode(ufs_params_t *params)
+{
+	int result;
+	unsigned int data, tx_lanes, rx_lanes;
+	uintptr_t base;
+
+	assert((params != NULL) && (params->reg_base != 0));
+
+	base = params->reg_base;
+
+	result = ufshc_dme_get(PA_TACTIVATE_OFFSET, 0, &data);
+	assert(result == 0);
+	if (data < 7) {
+		result = ufshc_dme_set(PA_TACTIVATE_OFFSET, 0, 7);
+		assert(result == 0);
+	}
+	result = ufshc_dme_get(PA_CONNECTED_TX_DATA_LANES_OFFSET, 0, &tx_lanes);
+	assert(result == 0);
+	result = ufshc_dme_get(PA_CONNECTED_RX_DATA_LANES_OFFSET, 0, &rx_lanes);
+	assert(result == 0);
+
+	result = ufshc_dme_set(PA_TX_SKIP_OFFSET, 0, 0);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_TX_GEAR_OFFSET, 0, 3);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_RX_GEAR_OFFSET, 0, 3);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_HS_SERIES_OFFSET, 0, 2);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_TX_TERMINATION_OFFSET, 0, 1);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_RX_TERMINATION_OFFSET, 0, 1);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_SCRAMBLING_OFFSET, 0, 0);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_ACTIVE_TX_DATA_LANES_OFFSET, 0, tx_lanes);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_ACTIVE_RX_DATA_LANES_OFFSET, 0, rx_lanes);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_PWR_MODE_USER_DATA0_OFFSET, 0, 8191);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_PWR_MODE_USER_DATA1_OFFSET, 0, 65535);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_PWR_MODE_USER_DATA2_OFFSET, 0, 32767);
+	assert(result == 0);
+	result = ufshc_dme_set(DME_FC0_PROTECTION_TIMEOUT_OFFSET, 0, 8191);
+	assert(result == 0);
+	result = ufshc_dme_set(DME_TC0_REPLAY_TIMEOUT_OFFSET, 0, 65535);
+	assert(result == 0);
+	result = ufshc_dme_set(DME_AFC0_REQ_TIMEOUT_OFFSET, 0, 32767);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_PWR_MODE_USER_DATA3_OFFSET, 0, 8191);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_PWR_MODE_USER_DATA4_OFFSET, 0, 65535);
+	assert(result == 0);
+	result = ufshc_dme_set(PA_PWR_MODE_USER_DATA5_OFFSET, 0, 32767);
+	assert(result == 0);
+	result = ufshc_dme_set(DME_FC1_PROTECTION_TIMEOUT_OFFSET, 0, 8191);
+	assert(result == 0);
+	result = ufshc_dme_set(DME_TC1_REPLAY_TIMEOUT_OFFSET, 0, 65535);
+	assert(result == 0);
+	result = ufshc_dme_set(DME_AFC1_REQ_TIMEOUT_OFFSET, 0, 32767);
+	assert(result == 0);
+
+	result = ufshc_dme_set(PA_PWR_MODE_OFFSET, 0, 0x11);
+	assert(result == 0);
+	do {
+		data = mmio_read_32(base + IS);
+	} while ((data & UFS_INT_UPMS) == 0);
+	mmio_write_32(base + IS, UFS_INT_UPMS);
+	data = mmio_read_32(base + HCS);
+	if ((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL)
+		INFO("ufs: change power mode success\n");
+	else
+		WARN("ufs: HCS.UPMCRS error, HCS:0x%x\n", data);
+	(void)result;
+	return 0;
+}
+
+const ufs_ops_t dw_ufs_ops = {
+	.phy_init		= dwufs_phy_init,
+	.phy_set_pwr_mode	= dwufs_phy_set_pwr_mode,
+};
+
+int dw_ufs_init(dw_ufs_params_t *params)
+{
+	ufs_params_t ufs_params;
+
+	memset(&ufs_params, 0, sizeof(ufs_params));
+	ufs_params.reg_base = params->reg_base;
+	ufs_params.desc_base = params->desc_base;
+	ufs_params.desc_size = params->desc_size;
+	ufs_params.flags = params->flags;
+	ufs_init(&dw_ufs_ops, &ufs_params);
+	return 0;
+}
diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c
new file mode 100644
index 0000000..dbfcd0f
--- /dev/null
+++ b/drivers/ufs/ufs.c
@@ -0,0 +1,759 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <assert.h>
+#include <debug.h>
+#include <delay_timer.h>
+#include <endian.h>
+#include <errno.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <stdint.h>
+#include <string.h>
+#include <ufs.h>
+
+#define CDB_ADDR_MASK			127
+#define ALIGN_CDB(x)			(((x) + CDB_ADDR_MASK) & ~CDB_ADDR_MASK)
+#define ALIGN_8(x)			(((x) + 7) & ~7)
+
+#define UFS_DESC_SIZE			0x400
+#define MAX_UFS_DESC_SIZE		0x8000		/* 32 descriptors */
+
+#define MAX_PRDT_SIZE			0x40000		/* 256KB */
+
+static ufs_params_t ufs_params;
+static int nutrs;	/* Number of UTP Transfer Request Slots */
+
+int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd)
+{
+	unsigned int data;
+
+	data = mmio_read_32(base + HCS);
+	if ((data & HCS_UCRDY) == 0)
+		return -EBUSY;
+	mmio_write_32(base + IS, ~0);
+	mmio_write_32(base + UCMDARG1, cmd->arg1);
+	mmio_write_32(base + UCMDARG2, cmd->arg2);
+	mmio_write_32(base + UCMDARG3, cmd->arg3);
+	mmio_write_32(base + UICCMD, cmd->op);
+
+	do {
+		data = mmio_read_32(base + IS);
+	} while ((data & UFS_INT_UCCS) == 0);
+	mmio_write_32(base + IS, UFS_INT_UCCS);
+	return mmio_read_32(base + UCMDARG2) && CONFIG_RESULT_CODE_MASK;
+}
+
+int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val)
+{
+	uintptr_t base;
+	unsigned int data;
+	int retries;
+
+	assert((ufs_params.reg_base != 0) && (val != NULL));
+
+	base = ufs_params.reg_base;
+	for (retries = 0; retries < 100; retries++) {
+		data = mmio_read_32(base + HCS);
+		if ((data & HCS_UCRDY) != 0)
+			break;
+		mdelay(1);
+	}
+	if (retries >= 100)
+		return -EBUSY;
+
+	mmio_write_32(base + IS, ~0);
+	mmio_write_32(base + UCMDARG1, (attr << 16) | GEN_SELECTOR_IDX(idx));
+	mmio_write_32(base + UCMDARG2, 0);
+	mmio_write_32(base + UCMDARG3, 0);
+	mmio_write_32(base + UICCMD, DME_GET);
+	do {
+		data = mmio_read_32(base + IS);
+		if (data & UFS_INT_UE)
+			return -EINVAL;
+	} while ((data & UFS_INT_UCCS) == 0);
+	mmio_write_32(base + IS, UFS_INT_UCCS);
+	data = mmio_read_32(base + UCMDARG2) && CONFIG_RESULT_CODE_MASK;
+	assert(data == 0);
+
+	*val = mmio_read_32(base + UCMDARG3);
+	return 0;
+}
+
+int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val)
+{
+	uintptr_t base;
+	unsigned int data;
+
+	assert((ufs_params.reg_base != 0));
+
+	base = ufs_params.reg_base;
+	data = mmio_read_32(base + HCS);
+	if ((data & HCS_UCRDY) == 0)
+		return -EBUSY;
+	mmio_write_32(base + IS, ~0);
+	mmio_write_32(base + UCMDARG1, (attr << 16) | GEN_SELECTOR_IDX(idx));
+	mmio_write_32(base + UCMDARG2, 0);
+	mmio_write_32(base + UCMDARG3, val);
+	mmio_write_32(base + UICCMD, DME_SET);
+	do {
+		data = mmio_read_32(base + IS);
+		if (data & UFS_INT_UE)
+			return -EINVAL;
+	} while ((data & UFS_INT_UCCS) == 0);
+	mmio_write_32(base + IS, UFS_INT_UCCS);
+	data = mmio_read_32(base + UCMDARG2) && CONFIG_RESULT_CODE_MASK;
+	assert(data == 0);
+	return 0;
+}
+
+static void ufshc_reset(uintptr_t base)
+{
+	unsigned int data;
+
+	/* Enable Host Controller */
+	mmio_write_32(base + HCE, HCE_ENABLE);
+	/* Wait until basic initialization sequence completed */
+	do {
+		data = mmio_read_32(base + HCE);
+	} while ((data & HCE_ENABLE) == 0);
+
+	/* Enable Interrupts */
+	data = UFS_INT_UCCS | UFS_INT_ULSS | UFS_INT_UE | UFS_INT_UTPES |
+	       UFS_INT_DFES | UFS_INT_HCFES | UFS_INT_SBFES;
+	mmio_write_32(base + IE, data);
+}
+
+static int ufshc_link_startup(uintptr_t base)
+{
+	uic_cmd_t cmd;
+	int data, result;
+	int retries;
+
+	for (retries = 10; retries > 0; retries--) {
+		memset(&cmd, 0, sizeof(cmd));
+		cmd.op = DME_LINKSTARTUP;
+		result = ufshc_send_uic_cmd(base, &cmd);
+		if (result != 0)
+			continue;
+		while ((mmio_read_32(base + HCS) & HCS_DP) == 0)
+			;
+		data = mmio_read_32(base + IS);
+		if (data & UFS_INT_ULSS)
+			mmio_write_32(base + IS, UFS_INT_ULSS);
+		return 0;
+	}
+	return -EIO;
+}
+
+/* Check Door Bell register to get an empty slot */
+static int get_empty_slot(int *slot)
+{
+	unsigned int data;
+	int i;
+
+	data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
+	for (i = 0; i < nutrs; i++) {
+		if ((data & 1) == 0)
+			break;
+		data = data >> 1;
+	}
+	if (i >= nutrs)
+		return -EBUSY;
+	*slot = i;
+	return 0;
+}
+
+static void get_utrd(utp_utrd_t *utrd)
+{
+	uintptr_t base;
+	int slot = 0, result;
+	utrd_header_t *hd;
+
+	assert(utrd != NULL);
+	result = get_empty_slot(&slot);
+	assert(result == 0);
+
+	/* clear utrd */
+	memset((void *)utrd, 0, sizeof(utp_utrd_t));
+	base = ufs_params.desc_base + (slot * UFS_DESC_SIZE);
+	/* clear the descriptor */
+	memset((void *)base, 0, UFS_DESC_SIZE);
+
+	utrd->header = base;
+	utrd->task_tag = slot + 1;
+	/* CDB address should be aligned with 128 bytes */
+	utrd->upiu = ALIGN_CDB(utrd->header + sizeof(utrd_header_t));
+	utrd->resp_upiu = ALIGN_8(utrd->upiu + sizeof(cmd_upiu_t));
+	utrd->size_upiu = utrd->resp_upiu - utrd->upiu;
+	utrd->size_resp_upiu = ALIGN_8(sizeof(resp_upiu_t));
+	utrd->prdt = utrd->resp_upiu + utrd->size_resp_upiu;
+
+	hd = (utrd_header_t *)utrd->header;
+	hd->ucdba = utrd->upiu & UINT32_MAX;
+	hd->ucdbau = (utrd->upiu >> 32) & UINT32_MAX;
+	/* Both RUL and RUO is based on DWORD */
+	hd->rul = utrd->size_resp_upiu >> 2;
+	hd->ruo = utrd->size_upiu >> 2;
+	(void)result;
+}
+
+/*
+ * Prepare UTRD, Command UPIU, Response UPIU.
+ */
+static int ufs_prepare_cmd(utp_utrd_t *utrd, uint8_t op, uint8_t lun,
+			   int lba, uintptr_t buf, size_t length)
+{
+	utrd_header_t *hd;
+	cmd_upiu_t *upiu;
+	prdt_t *prdt;
+	unsigned int ulba;
+	unsigned int lba_cnt;
+	int prdt_size;
+
+
+	mmio_write_32(ufs_params.reg_base + UTRLBA,
+		      utrd->header & UINT32_MAX);
+	mmio_write_32(ufs_params.reg_base + UTRLBAU,
+		      (utrd->upiu >> 32) & UINT32_MAX);
+
+	hd = (utrd_header_t *)utrd->header;
+	upiu = (cmd_upiu_t *)utrd->upiu;
+
+	hd->i = 1;
+	hd->ct = CT_UFS_STORAGE;
+	hd->ocs = OCS_MASK;
+
+	upiu->trans_type = CMD_UPIU;
+	upiu->task_tag = utrd->task_tag;
+	upiu->cdb[0] = op;
+	ulba = (unsigned int)lba;
+	lba_cnt = (unsigned int)(length >> UFS_BLOCK_SHIFT);
+	switch (op) {
+	case CDBCMD_TEST_UNIT_READY:
+		break;
+	case CDBCMD_READ_CAPACITY_10:
+		hd->dd = DD_OUT;
+		upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
+		upiu->lun = lun;
+		break;
+	case CDBCMD_READ_10:
+		hd->dd = DD_OUT;
+		upiu->flags = UPIU_FLAGS_R | UPIU_FLAGS_ATTR_S;
+		upiu->lun = lun;
+		upiu->cdb[1] = RW_WITHOUT_CACHE;
+		/* set logical block address */
+		upiu->cdb[2] = (ulba >> 24) & 0xff;
+		upiu->cdb[3] = (ulba >> 16) & 0xff;
+		upiu->cdb[4] = (ulba >> 8) & 0xff;
+		upiu->cdb[5] = ulba & 0xff;
+		/* set transfer length */
+		upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
+		upiu->cdb[8] = lba_cnt & 0xff;
+		break;
+	case CDBCMD_WRITE_10:
+		hd->dd = DD_IN;
+		upiu->flags = UPIU_FLAGS_W | UPIU_FLAGS_ATTR_S;
+		upiu->lun = lun;
+		upiu->cdb[1] = RW_WITHOUT_CACHE;
+		/* set logical block address */
+		upiu->cdb[2] = (ulba >> 24) & 0xff;
+		upiu->cdb[3] = (ulba >> 16) & 0xff;
+		upiu->cdb[4] = (ulba >> 8) & 0xff;
+		upiu->cdb[5] = ulba & 0xff;
+		/* set transfer length */
+		upiu->cdb[7] = (lba_cnt >> 8) & 0xff;
+		upiu->cdb[8] = lba_cnt & 0xff;
+		break;
+	default:
+		assert(0);
+	}
+	if (hd->dd == DD_IN)
+		flush_dcache_range(buf, length);
+	else if (hd->dd == DD_OUT)
+		inv_dcache_range(buf, length);
+	if (length) {
+		upiu->exp_data_trans_len = htobe32(length);
+		assert(lba_cnt <= UINT16_MAX);
+		prdt = (prdt_t *)utrd->prdt;
+
+		prdt_size = 0;
+		while (length > 0) {
+			prdt->dba = (unsigned int)(buf & UINT32_MAX);
+			prdt->dbau = (unsigned int)((buf >> 32) & UINT32_MAX);
+			/* prdt->dbc counts from 0 */
+			if (length > MAX_PRDT_SIZE) {
+				prdt->dbc = MAX_PRDT_SIZE - 1;
+				length = length - MAX_PRDT_SIZE;
+			} else {
+				prdt->dbc = length - 1;
+				length = 0;
+			}
+			buf += MAX_PRDT_SIZE;
+			prdt++;
+			prdt_size += sizeof(prdt_t);
+		}
+		utrd->size_prdt = ALIGN_8(prdt_size);
+		hd->prdtl = utrd->size_prdt >> 2;
+		hd->prdto = (utrd->size_upiu + utrd->size_resp_upiu) >> 2;
+	}
+
+	flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
+	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
+	return 0;
+}
+
+static int 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)
+{
+	utrd_header_t *hd;
+	query_upiu_t *query_upiu;
+
+
+	hd = (utrd_header_t *)utrd->header;
+	query_upiu = (query_upiu_t *)utrd->upiu;
+
+	mmio_write_32(ufs_params.reg_base + UTRLBA,
+		      utrd->header & UINT32_MAX);
+	mmio_write_32(ufs_params.reg_base + UTRLBAU,
+		      (utrd->header >> 32) & UINT32_MAX);
+
+
+	hd->i = 1;
+	hd->ct = CT_UFS_STORAGE;
+	hd->ocs = OCS_MASK;
+
+	query_upiu->trans_type = QUERY_REQUEST_UPIU;
+	query_upiu->task_tag = utrd->task_tag;
+	query_upiu->ts.desc.opcode = op;
+	query_upiu->ts.desc.idn = idn;
+	query_upiu->ts.desc.index = index;
+	query_upiu->ts.desc.selector = sel;
+	switch (op) {
+	case QUERY_READ_DESC:
+		query_upiu->query_func = QUERY_FUNC_STD_READ;
+		query_upiu->ts.desc.length = htobe16(length);
+		break;
+	case QUERY_WRITE_DESC:
+		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
+		query_upiu->ts.desc.length = htobe16(length);
+		memcpy((void *)(utrd->upiu + sizeof(query_upiu_t)),
+		       (void *)buf, length);
+		break;
+	case QUERY_READ_ATTR:
+	case QUERY_READ_FLAG:
+		query_upiu->query_func = QUERY_FUNC_STD_READ;
+		break;
+	case QUERY_CLEAR_FLAG:
+	case QUERY_SET_FLAG:
+		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
+		break;
+	case QUERY_WRITE_ATTR:
+		query_upiu->query_func = QUERY_FUNC_STD_WRITE;
+		memcpy((void *)&query_upiu->ts.attr.value, (void *)buf, length);
+		break;
+	default:
+		assert(0);
+	}
+	flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
+	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
+	return 0;
+}
+
+static void ufs_prepare_nop_out(utp_utrd_t *utrd)
+{
+	utrd_header_t *hd;
+	nop_out_upiu_t *nop_out;
+
+	mmio_write_32(ufs_params.reg_base + UTRLBA,
+		      utrd->header & UINT32_MAX);
+	mmio_write_32(ufs_params.reg_base + UTRLBAU,
+		      (utrd->header >> 32) & UINT32_MAX);
+
+	hd = (utrd_header_t *)utrd->header;
+	nop_out = (nop_out_upiu_t *)utrd->upiu;
+
+	hd->i = 1;
+	hd->ct = CT_UFS_STORAGE;
+	hd->ocs = OCS_MASK;
+
+	nop_out->trans_type = 0;
+	nop_out->task_tag = utrd->task_tag;
+	flush_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
+	flush_dcache_range((uintptr_t)utrd->header, UFS_DESC_SIZE);
+}
+
+static void ufs_send_request(int task_tag)
+{
+	unsigned int data;
+	int slot;
+
+	slot = task_tag - 1;
+	/* clear all interrupts */
+	mmio_write_32(ufs_params.reg_base + IS, ~0);
+
+	mmio_write_32(ufs_params.reg_base + UTRLRSR, 1);
+	do {
+		data = mmio_read_32(ufs_params.reg_base + UTRLRSR);
+	} while (data == 0);
+
+	data = UTRIACR_IAEN | UTRIACR_CTR | UTRIACR_IACTH(0x1F) |
+	       UTRIACR_IATOVAL(0xFF);
+	mmio_write_32(ufs_params.reg_base + UTRIACR, data);
+	/* send request */
+	mmio_setbits_32(ufs_params.reg_base + UTRLDBR, 1 << slot);
+}
+
+static int ufs_check_resp(utp_utrd_t *utrd, int trans_type)
+{
+	utrd_header_t *hd;
+	resp_upiu_t *resp;
+	unsigned int data;
+	int slot;
+
+	hd = (utrd_header_t *)utrd->header;
+	resp = (resp_upiu_t *)utrd->resp_upiu;
+	inv_dcache_range((uintptr_t)hd, UFS_DESC_SIZE);
+	inv_dcache_range((uintptr_t)utrd, sizeof(utp_utrd_t));
+	do {
+		data = mmio_read_32(ufs_params.reg_base + IS);
+		if ((data & ~(UFS_INT_UCCS | UFS_INT_UTRCS)) != 0)
+			return -EIO;
+	} while ((data & UFS_INT_UTRCS) == 0);
+	slot = utrd->task_tag - 1;
+
+	data = mmio_read_32(ufs_params.reg_base + UTRLDBR);
+	assert((data & (1 << slot)) == 0);
+	assert(hd->ocs == OCS_SUCCESS);
+	assert((resp->trans_type & TRANS_TYPE_CODE_MASK) == trans_type);
+	(void)resp;
+	(void)slot;
+	return 0;
+}
+
+#ifdef UFS_RESP_DEBUG
+static void dump_upiu(utp_utrd_t *utrd)
+{
+	utrd_header_t *hd;
+	int i;
+
+	hd = (utrd_header_t *)utrd->header;
+	INFO("utrd:0x%x, ruo:0x%x, rul:0x%x, ocs:0x%x, UTRLDBR:0x%x\n",
+		(unsigned int)(uintptr_t)utrd, hd->ruo, hd->rul, hd->ocs,
+		mmio_read_32(ufs_params.reg_base + UTRLDBR));
+	for (i = 0; i < sizeof(utrd_header_t); i += 4) {
+		INFO("[%lx]:0x%x\n",
+			(uintptr_t)utrd->header + i,
+			*(unsigned int *)((uintptr_t)utrd->header + i));
+	}
+
+	for (i = 0; i < sizeof(cmd_upiu_t); i += 4) {
+		INFO("cmd[%lx]:0x%x\n",
+			utrd->upiu + i,
+			*(unsigned int *)(utrd->upiu + i));
+	}
+	for (i = 0; i < sizeof(resp_upiu_t); i += 4) {
+		INFO("resp[%lx]:0x%x\n",
+			utrd->resp_upiu + i,
+			*(unsigned int *)(utrd->resp_upiu + i));
+	}
+	for (i = 0; i < sizeof(prdt_t); i += 4) {
+		INFO("prdt[%lx]:0x%x\n",
+			utrd->prdt + i,
+			*(unsigned int *)(utrd->prdt + i));
+	}
+}
+#endif
+
+static void ufs_verify_init(void)
+{
+	utp_utrd_t utrd;
+	int result;
+
+	get_utrd(&utrd);
+	ufs_prepare_nop_out(&utrd);
+	ufs_send_request(utrd.task_tag);
+	result = ufs_check_resp(&utrd, NOP_IN_UPIU);
+	assert(result == 0);
+	(void)result;
+}
+
+static void ufs_verify_ready(void)
+{
+	utp_utrd_t utrd;
+	int result;
+
+	get_utrd(&utrd);
+	ufs_prepare_cmd(&utrd, CDBCMD_TEST_UNIT_READY, 0, 0, 0, 0);
+	ufs_send_request(utrd.task_tag);
+	result = ufs_check_resp(&utrd, RESPONSE_UPIU);
+	assert(result == 0);
+	(void)result;
+}
+
+static void ufs_query(uint8_t op, uint8_t idn, uint8_t index, uint8_t sel,
+		      uintptr_t buf, size_t size)
+{
+	utp_utrd_t utrd;
+	query_resp_upiu_t *resp;
+	int result;
+
+	switch (op) {
+	case QUERY_READ_FLAG:
+	case QUERY_READ_ATTR:
+	case QUERY_READ_DESC:
+	case QUERY_WRITE_DESC:
+	case QUERY_WRITE_ATTR:
+		assert(((buf & 3) == 0) && (size != 0));
+		break;
+	}
+	get_utrd(&utrd);
+	ufs_prepare_query(&utrd, op, idn, index, sel, buf, size);
+	ufs_send_request(utrd.task_tag);
+	result = ufs_check_resp(&utrd, QUERY_RESPONSE_UPIU);
+	assert(result == 0);
+	resp = (query_resp_upiu_t *)utrd.resp_upiu;
+#ifdef UFS_RESP_DEBUG
+	dump_upiu(&utrd);
+#endif
+	assert(resp->query_resp == QUERY_RESP_SUCCESS);
+
+	switch (op) {
+	case QUERY_READ_FLAG:
+		*(uint32_t *)buf = (uint32_t)resp->ts.flag.value;
+		break;
+	case QUERY_READ_ATTR:
+	case QUERY_READ_DESC:
+		memcpy((void *)buf,
+		       (void *)(utrd.resp_upiu + sizeof(query_resp_upiu_t)),
+		       size);
+		break;
+	}
+	(void)result;
+}
+
+unsigned int ufs_read_attr(int idn)
+{
+	unsigned int value;
+
+	ufs_query(QUERY_READ_ATTR, idn, 0, 0,
+		  (uintptr_t)&value, sizeof(value));
+	return value;
+}
+
+void ufs_write_attr(int idn, unsigned int value)
+{
+	ufs_query(QUERY_WRITE_ATTR, idn, 0, 0,
+		  (uintptr_t)&value, sizeof(value));
+}
+
+unsigned int ufs_read_flag(int idn)
+{
+	unsigned int value;
+
+	ufs_query(QUERY_READ_FLAG, idn, 0, 0,
+		  (uintptr_t)&value, sizeof(value));
+	return value;
+}
+
+void ufs_set_flag(int idn)
+{
+	ufs_query(QUERY_SET_FLAG, idn, 0, 0, 0, 0);
+}
+
+void ufs_clear_flag(int idn)
+{
+	ufs_query(QUERY_CLEAR_FLAG, idn, 0, 0, 0, 0);
+}
+
+void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size)
+{
+	ufs_query(QUERY_READ_DESC, idn, index, 0, buf, size);
+}
+
+void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size)
+{
+	ufs_query(QUERY_WRITE_DESC, idn, index, 0, buf, size);
+}
+
+void ufs_read_capacity(int lun, unsigned int *num, unsigned int *size)
+{
+	utp_utrd_t utrd;
+	resp_upiu_t *resp;
+	sense_data_t *sense;
+	unsigned char data[CACHE_WRITEBACK_GRANULE << 1];
+	uintptr_t buf;
+	int result;
+	int retry;
+
+	assert((ufs_params.reg_base != 0) &&
+	       (ufs_params.desc_base != 0) &&
+	       (ufs_params.desc_size >= UFS_DESC_SIZE) &&
+	       (num != NULL) && (size != NULL));
+
+	/* align buf address */
+	buf = (uintptr_t)data;
+	buf = (buf + CACHE_WRITEBACK_GRANULE - 1) &
+	      ~(CACHE_WRITEBACK_GRANULE - 1);
+	memset((void *)buf, 0, CACHE_WRITEBACK_GRANULE);
+	flush_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
+	do {
+		get_utrd(&utrd);
+		ufs_prepare_cmd(&utrd, CDBCMD_READ_CAPACITY_10, lun, 0,
+				buf, READ_CAPACITY_LENGTH);
+		ufs_send_request(utrd.task_tag);
+		result = ufs_check_resp(&utrd, RESPONSE_UPIU);
+		assert(result == 0);
+#ifdef UFS_RESP_DEBUG
+		dump_upiu(&utrd);
+#endif
+		resp = (resp_upiu_t *)utrd.resp_upiu;
+		retry = 0;
+		sense = &resp->sd.sense;
+		if (sense->resp_code == SENSE_DATA_VALID) {
+			if ((sense->sense_key == SENSE_KEY_UNIT_ATTENTION) &&
+			    (sense->asc == 0x29) && (sense->ascq == 0)) {
+				retry = 1;
+			}
+		}
+		inv_dcache_range(buf, CACHE_WRITEBACK_GRANULE);
+		/* last logical block address */
+		*num = be32toh(*(unsigned int *)buf);
+		if (*num)
+			*num += 1;
+		/* logical block length in bytes */
+		*size = be32toh(*(unsigned int *)(buf + 4));
+	} while (retry);
+	(void)result;
+}
+
+size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size)
+{
+	utp_utrd_t utrd;
+	resp_upiu_t *resp;
+	int result;
+
+	assert((ufs_params.reg_base != 0) &&
+	       (ufs_params.desc_base != 0) &&
+	       (ufs_params.desc_size >= UFS_DESC_SIZE));
+
+	memset((void *)buf, 0, size);
+	get_utrd(&utrd);
+	ufs_prepare_cmd(&utrd, CDBCMD_READ_10, lun, lba, buf, size);
+	ufs_send_request(utrd.task_tag);
+	result = ufs_check_resp(&utrd, RESPONSE_UPIU);
+	assert(result == 0);
+#ifdef UFS_RESP_DEBUG
+	dump_upiu(&utrd);
+#endif
+	resp = (resp_upiu_t *)utrd.resp_upiu;
+	(void)result;
+	return size - resp->res_trans_cnt;
+}
+
+size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size)
+{
+	utp_utrd_t utrd;
+	resp_upiu_t *resp;
+	int result;
+
+	assert((ufs_params.reg_base != 0) &&
+	       (ufs_params.desc_base != 0) &&
+	       (ufs_params.desc_size >= UFS_DESC_SIZE));
+
+	memset((void *)buf, 0, size);
+	get_utrd(&utrd);
+	ufs_prepare_cmd(&utrd, CDBCMD_WRITE_10, lun, lba, buf, size);
+	ufs_send_request(utrd.task_tag);
+	result = ufs_check_resp(&utrd, RESPONSE_UPIU);
+	assert(result == 0);
+#ifdef UFS_RESP_DEBUG
+	dump_upiu(&utrd);
+#endif
+	resp = (resp_upiu_t *)utrd.resp_upiu;
+	(void)result;
+	return size - resp->res_trans_cnt;
+}
+
+static void ufs_enum(void)
+{
+	unsigned int blk_num, blk_size;
+	int i;
+
+	/* 0 means 1 slot */
+	nutrs = (mmio_read_32(ufs_params.reg_base + CAP) & CAP_NUTRS_MASK) + 1;
+	if (nutrs > (ufs_params.desc_size / UFS_DESC_SIZE))
+		nutrs = ufs_params.desc_size / UFS_DESC_SIZE;
+
+	ufs_verify_init();
+	ufs_verify_ready();
+
+	ufs_set_flag(FLAG_DEVICE_INIT);
+	mdelay(100);
+	/* dump available LUNs */
+	for (i = 0; i < UFS_MAX_LUNS; i++) {
+		ufs_read_capacity(i, &blk_num, &blk_size);
+		if (blk_num && blk_size) {
+			INFO("UFS LUN%d contains %d blocks with %d-byte size\n",
+			     i, blk_num, blk_size);
+		}
+	}
+}
+
+int ufs_init(const ufs_ops_t *ops, ufs_params_t *params)
+{
+	int result;
+	unsigned int data;
+	uic_cmd_t cmd;
+
+	assert((params != NULL) &&
+	       (params->reg_base != 0) &&
+	       (params->desc_base != 0) &&
+	       (params->desc_size >= UFS_DESC_SIZE));
+
+	memcpy(&ufs_params, params, sizeof(ufs_params_t));
+
+	if (ufs_params.flags & UFS_FLAGS_SKIPINIT) {
+		result = ufshc_dme_get(0x1571, 0, &data);
+		assert(result == 0);
+		result = ufshc_dme_get(0x41, 0, &data);
+		assert(result == 0);
+		if (data == 1) {
+			/* prepare to exit hibernate mode */
+			memset(&cmd, 0, sizeof(uic_cmd_t));
+			cmd.op = DME_HIBERNATE_EXIT;
+			result = ufshc_send_uic_cmd(ufs_params.reg_base,
+						    &cmd);
+			assert(result == 0);
+			data = mmio_read_32(ufs_params.reg_base + UCMDARG2);
+			assert(data == 0);
+			do {
+				data = mmio_read_32(ufs_params.reg_base + IS);
+			} while ((data & UFS_INT_UHXS) == 0);
+			mmio_write_32(ufs_params.reg_base + IS, UFS_INT_UHXS);
+			data = mmio_read_32(ufs_params.reg_base + HCS);
+			assert((data & HCS_UPMCRS_MASK) == HCS_PWR_LOCAL);
+		}
+		result = ufshc_dme_get(0x1568, 0, &data);
+		assert(result == 0);
+		assert((data > 0) && (data <= 3));
+	} else {
+		assert((ops != NULL) && (ops->phy_init != NULL) &&
+		       (ops->phy_set_pwr_mode != NULL));
+
+		ufshc_reset(ufs_params.reg_base);
+		ops->phy_init(&ufs_params);
+		result = ufshc_link_startup(ufs_params.reg_base);
+		assert(result == 0);
+		ops->phy_set_pwr_mode(&ufs_params);
+	}
+
+	ufs_enum();
+	(void)result;
+	return 0;
+}
diff --git a/include/drivers/dw_ufs.h b/include/drivers/dw_ufs.h
new file mode 100644
index 0000000..b05c7f5
--- /dev/null
+++ b/include/drivers/dw_ufs.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __DW_UFS_H__
+#define __DW_UFS_H__
+
+#include <sys/types.h>
+
+/* Bus Throtting */
+#define BUSTHRTL				0xC0
+/* Outstanding OCP Requests */
+#define OOCPR					0xC4
+/* Fatal Error Interrupt Enable */
+#define FEIE					0xC8
+/* C-Port Direct Access Configuration register */
+#define CDACFG					0xD0
+/* C-Port Direct Access Transmit 1 register */
+#define CDATX1					0xD4
+/* C-Port Direct Access Transmit 2 register */
+#define CDATX2					0xD8
+/* C-Port Direct Access Receive 1 register */
+#define CDARX1					0xDC
+/* C-Port Direct Access Receive 2 register */
+#define CDARX2					0xE0
+/* C-Port Direct Access Status register */
+#define CDASTA					0xE4
+/* UPIU Loopback Configuration register */
+#define LBMCFG					0xF0
+/* UPIU Loopback Status */
+#define LBMSTA					0xF4
+/* Debug register */
+#define DBG					0xF8
+/* HClk Divider register */
+#define HCLKDIV					0xFC
+
+#define TX_HIBERN8TIME_CAP_OFFSET		0x000F
+#define TX_FSM_STATE_OFFSET			0x0041
+#define TX_FSM_STATE_LINE_RESET			7
+#define TX_FSM_STATE_LINE_CFG			6
+#define TX_FSM_STATE_HS_BURST			5
+#define TX_FSM_STATE_LS_BURST			4
+#define TX_FSM_STATE_STALL			3
+#define TX_FSM_STATE_SLEEP			2
+#define TX_FSM_STATE_HIBERN8			1
+#define TX_FSM_STATE_DISABLE			0
+
+#define RX_MIN_ACTIVATETIME_CAP_OFFSET		0x008F
+#define RX_HS_G2_SYNC_LENGTH_CAP_OFFSET		0x0094
+#define RX_HS_G3_SYNC_LENGTH_CAP_OFFSET		0x0095
+
+#define PA_AVAIL_TX_DATA_LANES_OFFSET		0x1520
+#define PA_TX_SKIP_OFFSET			0x155C
+#define PA_TX_SKIP_PERIOD_OFFSET		0x155D
+#define PA_LOCAL_TX_LCC_ENABLE_OFFSET		0x155E
+#define PA_ACTIVE_TX_DATA_LANES_OFFSET		0x1560
+#define PA_CONNECTED_TX_DATA_LANES_OFFSET	0x1561
+#define PA_TX_TRAILING_CLOCKS_OFFSET		0x1564
+#define PA_TX_GEAR_OFFSET			0x1568
+#define PA_TX_TERMINATION_OFFSET		0x1569
+#define PA_HS_SERIES_OFFSET			0x156A
+#define PA_PWR_MODE_OFFSET			0x1571
+#define PA_ACTIVE_RX_DATA_LANES_OFFSET		0x1580
+#define PA_CONNECTED_RX_DATA_LANES_OFFSET	0x1581
+#define PA_RX_PWR_STATUS_OFFSET			0x1582
+#define PA_RX_GEAR_OFFSET			0x1583
+#define PA_RX_TERMINATION_OFFSET		0x1584
+#define PA_SCRAMBLING_OFFSET			0x1585
+#define PA_MAX_RX_PWM_GEAR_OFFSET		0x1586
+#define PA_MAX_RX_HS_GEAR_OFFSET		0x1587
+#define PA_PACP_REQ_TIMEOUT_OFFSET		0x1590
+#define PA_PACP_REQ_EOB_TIMEOUT_OFFSET		0x1591
+#define PA_REMOTE_VER_INFO_OFFSET		0x15A0
+#define PA_LOGICAL_LANE_MAP_OFFSET		0x15A1
+#define PA_TACTIVATE_OFFSET			0x15A8
+#define PA_PWR_MODE_USER_DATA0_OFFSET		0x15B0
+#define PA_PWR_MODE_USER_DATA1_OFFSET		0x15B1
+#define PA_PWR_MODE_USER_DATA2_OFFSET		0x15B2
+#define PA_PWR_MODE_USER_DATA3_OFFSET		0x15B3
+#define PA_PWR_MODE_USER_DATA4_OFFSET		0x15B4
+#define PA_PWR_MODE_USER_DATA5_OFFSET		0x15B5
+
+#define DL_TC0_TX_FC_THRESHOLD_OFFSET		0x2040
+#define DL_AFC0_CREDIT_THRESHOLD_OFFSET		0x2044
+#define DL_TC0_OUT_ACK_THRESHOLD_OFFSET		0x2045
+
+#define DME_FC0_PROTECTION_TIMEOUT_OFFSET	0xD041
+#define DME_TC0_REPLAY_TIMEOUT_OFFSET		0xD042
+#define DME_AFC0_REQ_TIMEOUT_OFFSET		0xD043
+#define DME_FC1_PROTECTION_TIMEOUT_OFFSET	0xD044
+#define DME_TC1_REPLAY_TIMEOUT_OFFSET		0xD045
+#define DME_AFC1_REQ_TIMEOUT_OFFSET		0xD046
+
+#define VS_MPHY_CFG_UPDT_OFFSET			0xD085
+#define VS_MK2_EXTN_SUPPORT_OFFSET		0xD0AB
+#define VS_MPHY_DISABLE_OFFSET			0xD0C1
+#define VS_MPHY_DISABLE_MPHYDIS			(1 << 0)
+
+typedef struct dw_ufs_params {
+	uintptr_t		reg_base;
+	uintptr_t		desc_base;
+	size_t			desc_size;
+	unsigned long		flags;
+} dw_ufs_params_t;
+
+int dw_ufs_init(dw_ufs_params_t *params);
+
+#endif /* __DW_UFS_H__ */
diff --git a/include/drivers/ufs.h b/include/drivers/ufs.h
new file mode 100644
index 0000000..3a4f1c7
--- /dev/null
+++ b/include/drivers/ufs.h
@@ -0,0 +1,529 @@
+/*
+ * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __UFS_H__
+#define __UFS_H__
+
+/* register map of UFSHCI */
+/* Controller Capabilities */
+#define CAP				0x00
+#define CAP_NUTRS_MASK			0x1F
+
+/* UFS Version */
+#define VER				0x08
+/* Host Controller Identification - Product ID */
+#define HCDDID				0x10
+/* Host Controller Identification Descriptor - Manufacturer ID */
+#define HCPMID				0x14
+/* Auto-Hibernate Idle Timer */
+#define AHIT				0x18
+/* Interrupt Status */
+#define IS				0x20
+/* Interrupt Enable */
+#define IE				0x24
+/* System Bus Fatal Error Status */
+#define UFS_INT_SBFES			(1 << 17)
+/* Host Controller Fatal Error Status */
+#define UFS_INT_HCFES			(1 << 16)
+/* UTP Error Status */
+#define UFS_INT_UTPES			(1 << 12)
+/* Device Fatal Error Status */
+#define UFS_INT_DFES			(1 << 11)
+/* UIC Command Completion Status */
+#define UFS_INT_UCCS			(1 << 10)
+/* UTP Task Management Request Completion Status */
+#define UFS_INT_UTMRCS			(1 << 9)
+/* UIC Link Startup Status */
+#define UFS_INT_ULSS			(1 << 8)
+/* UIC Link Lost Status */
+#define UFS_INT_ULLS			(1 << 7)
+/* UIC Hibernate Enter Status */
+#define UFS_INT_UHES			(1 << 6)
+/* UIC Hibernate Exit Status */
+#define UFS_INT_UHXS			(1 << 5)
+/* UIC Power Mode Status */
+#define UFS_INT_UPMS			(1 << 4)
+/* UIC Test Mode Status */
+#define UFS_INT_UTMS			(1 << 3)
+/* UIC Error */
+#define UFS_INT_UE			(1 << 2)
+/* UIC DME_ENDPOINTRESET Indication */
+#define UFS_INT_UDEPRI			(1 << 1)
+/* UTP Transfer Request Completion Status */
+#define UFS_INT_UTRCS			(1 << 0)
+
+/* Host Controller Status */
+#define HCS				0x30
+#define HCS_UPMCRS_MASK			(7 << 8)
+#define HCS_PWR_LOCAL			(1 << 8)
+#define HCS_UCRDY			(1 << 3)
+#define HCS_UTMRLRDY			(1 << 2)
+#define HCS_UTRLRDY			(1 << 1)
+#define HCS_DP				(1 << 0)
+
+/* Host Controller Enable */
+#define HCE				0x34
+#define HCE_ENABLE			1
+
+/* Host UIC Error Code PHY Adapter Layer */
+#define UECPA				0x38
+/* Host UIC Error Code Data Link Layer */
+#define UECDL				0x3C
+/* Host UIC Error Code Network Layer */
+#define UECN				0x40
+/* Host UIC Error Code Transport Layer */
+#define UECT				0x44
+/* Host UIC Error Code */
+#define UECDME				0x48
+/* UTP Transfer Request Interrupt Aggregation Control Register */
+#define UTRIACR				0x4C
+#define UTRIACR_IAEN			(1 << 31)
+#define UTRIACR_IAPWEN			(1 << 24)
+#define UTRIACR_IASB			(1 << 20)
+#define UTRIACR_CTR			(1 << 16)
+#define UTRIACR_IACTH(x)		(((x) & 0x1F) << 8)
+#define UTRIACR_IATOVAL(x)		((x) & 0xFF)
+
+/* UTP Transfer Request List Base Address */
+#define UTRLBA				0x50
+/* UTP Transfer Request List Base Address Upper 32-bits */
+#define UTRLBAU				0x54
+/* UTP Transfer Request List Door Bell Register */
+#define UTRLDBR				0x58
+/* UTP Transfer Request List Clear Register */
+#define UTRLCLR				0x5C
+/* UTP Transfer Request List Run Stop Register */
+#define UTRLRSR				0x60
+#define UTMRLBA				0x70
+#define UTMRLBAU			0x74
+#define UTMRLDBR			0x78
+#define UTMRLCLR			0x7C
+#define UTMRLRSR			0x80
+/* UIC Command */
+#define UICCMD				0x90
+/* UIC Command Argument 1 */
+#define UCMDARG1			0x94
+/* UIC Command Argument 2 */
+#define UCMDARG2			0x98
+/* UIC Command Argument 3 */
+#define UCMDARG3			0x9C
+
+#define UFS_BLOCK_SHIFT			12		/* 4KB */
+#define UFS_BLOCK_SIZE			(1 << UFS_BLOCK_SHIFT)
+#define UFS_BLOCK_MASK			(UFS_BLOCK_SIZE - 1)
+#define UFS_MAX_LUNS			8
+
+/* UTP Transfer Request Descriptor */
+/* Command Type */
+#define CT_UFS_STORAGE			1
+#define CT_SCSI				0
+
+/* Data Direction */
+#define DD_OUT				2		/* Device --> Host */
+#define DD_IN				1		/* Host --> Device */
+#define DD_NO_DATA_TRANSFER		0
+
+#define UTP_TRD_SIZE			32
+
+/* Transaction Type */
+#define TRANS_TYPE_HD			(1 << 7)	/* E2ECRC */
+#define TRANS_TYPE_DD			(1 << 6)
+#define TRANS_TYPE_CODE_MASK		0x3F
+#define QUERY_RESPONSE_UPIU		(0x36 << 0)
+#define READY_TO_TRANSACTION_UPIU	(0x31 << 0)
+#define DATA_IN_UPIU			(0x22 << 0)
+#define RESPONSE_UPIU			(0x21 << 0)
+#define NOP_IN_UPIU			(0x20 << 0)
+#define QUERY_REQUEST_UPIU		(0x16 << 0)
+#define DATA_OUT_UPIU			(0x02 << 0)
+#define CMD_UPIU			(0x01 << 0)
+#define NOP_OUT_UPIU			(0x00 << 0)
+
+#define OCS_SUCCESS			0x0
+#define OCS_INVALID_FUNC_ATTRIBUTE	0x1
+#define OCS_MISMATCH_REQUEST_SIZE	0x2
+#define OCS_MISMATCH_RESPONSE_SIZE	0x3
+#define OCS_PEER_COMMUNICATION_FAILURE	0x4
+#define OCS_ABORTED			0x5
+#define OCS_FATAL_ERROR			0x6
+#define OCS_MASK			0xF
+
+/* UIC Command */
+#define DME_GET				0x01
+#define DME_SET				0x02
+#define DME_PEER_GET			0x03
+#define DME_PEER_SET			0x04
+#define DME_POWERON			0x10
+#define DME_POWEROFF			0x11
+#define DME_ENABLE			0x12
+#define DME_RESET			0x14
+#define DME_ENDPOINTRESET		0x15
+#define DME_LINKSTARTUP			0x16
+#define DME_HIBERNATE_ENTER		0x17
+#define DME_HIBERNATE_EXIT		0x18
+#define DME_TEST_MODE			0x1A
+
+#define GEN_SELECTOR_IDX(x)		((x) & 0xFFFF)
+
+#define CONFIG_RESULT_CODE_MASK		0xFF
+
+#define CDBCMD_TEST_UNIT_READY		0x00
+#define CDBCMD_READ_6			0x08
+#define CDBCMD_WRITE_6			0x0A
+#define CDBCMD_START_STOP_UNIT		0x1B
+#define CDBCMD_READ_CAPACITY_10		0x25
+#define CDBCMD_READ_10			0x28
+#define CDBCMD_WRITE_10			0x2A
+#define CDBCMD_READ_16			0x88
+#define CDBCMD_WRITE_16			0x8A
+#define CDBCMD_READ_CAPACITY_16		0x9E
+#define CDBCMD_REPORT_LUNS		0xA0
+
+#define UPIU_FLAGS_R			(1 << 6)
+#define UPIU_FLAGS_W			(1 << 5)
+#define UPIU_FLAGS_ATTR_MASK		(3 << 0)
+#define UPIU_FLAGS_ATTR_S		(0 << 0)	/* Simple */
+#define UPIU_FLAGS_ATTR_O		(1 << 0)	/* Ordered */
+#define UPIU_FLAGS_ATTR_HQ		(2 << 0)	/* Head of Queue */
+#define UPIU_FLAGS_ATTR_ACA		(3 << 0)
+#define UPIU_FLAGS_O			(1 << 6)
+#define UPIU_FLAGS_U			(1 << 5)
+#define UPIU_FLAGS_D			(1 << 4)
+
+#define QUERY_FUNC_STD_READ		0x01
+#define QUERY_FUNC_STD_WRITE		0x81
+
+#define QUERY_NOP			0x00
+#define QUERY_READ_DESC			0x01
+#define QUERY_WRITE_DESC		0x02
+#define QUERY_READ_ATTR			0x03
+#define QUERY_WRITE_ATTR		0x04
+#define QUERY_READ_FLAG			0x05
+#define QUERY_SET_FLAG			0x06
+#define QUERY_CLEAR_FLAG		0x07
+#define QUERY_TOGGLE_FLAG		0x08
+
+#define RW_WITHOUT_CACHE		0x18
+
+#define DESC_TYPE_DEVICE		0x00
+#define DESC_TYPE_CONFIGURATION		0x01
+#define DESC_TYPE_UNIT			0x02
+#define DESC_TYPE_INTERCONNECT		0x04
+#define DESC_TYPE_STRING		0x05
+
+#define ATTR_CUR_PWR_MODE		0x02	/* bCurrentPowerMode */
+#define ATTR_ACTIVECC			0x03	/* bActiveICCLevel */
+
+#define DEVICE_DESCRIPTOR_LEN		0x40
+#define UNIT_DESCRIPTOR_LEN		0x23
+
+#define QUERY_RESP_SUCCESS		0x00
+#define QUERY_RESP_OPCODE		0xFE
+#define QUERY_RESP_GENERAL_FAIL		0xFF
+
+#define SENSE_KEY_NO_SENSE		0x00
+#define SENSE_KEY_RECOVERED_ERROR	0x01
+#define SENSE_KEY_NOT_READY		0x02
+#define SENSE_KEY_MEDIUM_ERROR		0x03
+#define SENSE_KEY_HARDWARE_ERROR	0x04
+#define SENSE_KEY_ILLEGAL_REQUEST	0x05
+#define SENSE_KEY_UNIT_ATTENTION	0x06
+#define SENSE_KEY_DATA_PROTECT		0x07
+#define SENSE_KEY_BLANK_CHECK		0x08
+#define SENSE_KEY_VENDOR_SPECIFIC	0x09
+#define SENSE_KEY_COPY_ABORTED		0x0A
+#define SENSE_KEY_ABORTED_COMMAND	0x0B
+#define SENSE_KEY_VOLUME_OVERFLOW	0x0D
+#define SENSE_KEY_MISCOMPARE		0x0E
+
+#define SENSE_DATA_VALID		0x70
+#define SENSE_DATA_LENGTH		18
+
+#define READ_CAPACITY_LENGTH		8
+
+#define FLAG_DEVICE_INIT		0x01
+
+/* UFS Driver Flags */
+#define UFS_FLAGS_SKIPINIT		(1 << 0)
+
+typedef struct sense_data {
+	uint8_t		resp_code : 7;
+	uint8_t		valid : 1;
+	uint8_t		reserved0;
+	uint8_t		sense_key : 4;
+	uint8_t		reserved1 : 1;
+	uint8_t		ili : 1;
+	uint8_t		eom : 1;
+	uint8_t		file_mark : 1;
+	uint8_t		info[4];
+	uint8_t		asl;
+	uint8_t		cmd_spec_len[4];
+	uint8_t		asc;
+	uint8_t		ascq;
+	uint8_t		fruc;
+	uint8_t		sense_key_spec0 : 7;
+	uint8_t		sksv : 1;
+	uint8_t		sense_key_spec1;
+	uint8_t		sense_key_spec2;
+} sense_data_t;
+
+/* UTP Transfer Request Descriptor */
+typedef struct utrd_header {
+	uint32_t	reserved0 : 24;
+	uint32_t	i : 1;		/* interrupt */
+	uint32_t	dd : 2;		/* data direction */
+	uint32_t	reserved1 : 1;
+	uint32_t	ct : 4;		/* command type */
+	uint32_t	reserved2;
+	uint32_t	ocs : 8;	/* Overall Command Status */
+	uint32_t	reserved3 : 24;
+	uint32_t	reserved4;
+	uint32_t	ucdba;		/* aligned to 128-byte */
+	uint32_t	ucdbau;		/* Upper 32-bits */
+	uint32_t	rul : 16;	/* Response UPIU Length */
+	uint32_t	ruo : 16;	/* Response UPIU Offset */
+	uint32_t	prdtl : 16;	/* PRDT Length */
+	uint32_t	prdto : 16;	/* PRDT Offset */
+} utrd_header_t;	/* 8 words with little endian */
+
+/* UTP Task Management Request Descriptor */
+typedef struct utp_utmrd {
+	/* 4 words with little endian */
+	uint32_t	reserved0 : 24;
+	uint32_t	i : 1;		/* interrupt */
+	uint32_t	reserved1 : 7;
+	uint32_t	reserved2;
+	uint32_t	ocs : 8;	/* Overall Command Status */
+	uint32_t	reserved3 : 24;
+	uint32_t	reserved4;
+
+	/* followed by 8 words UPIU with big endian */
+
+	/* followed by 8 words Response UPIU with big endian */
+} utp_utmrd_t;
+
+/* NOP OUT UPIU */
+typedef struct nop_out_upiu {
+	uint8_t		trans_type;
+	uint8_t		flags;
+	uint8_t		reserved0;
+	uint8_t		task_tag;
+	uint8_t		reserved1;
+	uint8_t		reserved2;
+	uint8_t		reserved3;
+	uint8_t		reserved4;
+	uint8_t		total_ehs_len;
+	uint8_t		reserved5;
+	uint16_t	data_segment_len;
+	uint32_t	reserved6;
+	uint32_t	reserved7;
+	uint32_t	reserved8;
+	uint32_t	reserved9;
+	uint32_t	reserved10;
+	uint32_t	e2ecrc;
+} nop_out_upiu_t;	/* 36 bytes with big endian */
+
+/* NOP IN UPIU */
+typedef struct nop_in_upiu {
+	uint8_t		trans_type;
+	uint8_t		flags;
+	uint8_t		reserved0;
+	uint8_t		task_tag;
+	uint8_t		reserved1;
+	uint8_t		reserved2;
+	uint8_t		response;
+	uint8_t		reserved3;
+	uint8_t		total_ehs_len;
+	uint8_t		dev_info;
+	uint16_t	data_segment_len;
+	uint32_t	reserved4;
+	uint32_t	reserved5;
+	uint32_t	reserved6;
+	uint32_t	reserved7;
+	uint32_t	reserved8;
+	uint32_t	e2ecrc;
+} nop_in_upiu_t;	/* 36 bytes with big endian */
+
+/* Command UPIU */
+typedef struct cmd_upiu {
+	uint8_t		trans_type;
+	uint8_t		flags;
+	uint8_t		lun;
+	uint8_t		task_tag;
+	uint8_t		cmd_set_type;
+	uint8_t		reserved0;
+	uint8_t		reserved1;
+	uint8_t		reserved2;
+	uint8_t		total_ehs_len;
+	uint8_t		reserved3;
+	uint16_t	data_segment_len;
+	uint32_t	exp_data_trans_len;
+	/*
+	 * A CDB has a fixed length of 16bytes or a variable length
+	 * of between 12 and 260 bytes
+	 */
+	uint8_t		cdb[16];	/* little endian */
+} cmd_upiu_t;	/* 32 bytes with big endian except for cdb[] */
+
+typedef struct query_desc {
+	uint8_t		opcode;
+	uint8_t		idn;
+	uint8_t		index;
+	uint8_t		selector;
+	uint8_t		reserved0[2];
+	uint16_t	length;
+	uint32_t	reserved2[2];
+} query_desc_t;		/* 16 bytes with big endian */
+
+typedef struct query_flag {
+	uint8_t		opcode;
+	uint8_t		idn;
+	uint8_t		index;
+	uint8_t		selector;
+	uint8_t		reserved0[7];
+	uint8_t		value;
+	uint32_t	reserved8;
+} query_flag_t;		/* 16 bytes with big endian */
+
+typedef struct query_attr {
+	uint8_t		opcode;
+	uint8_t		idn;
+	uint8_t		index;
+	uint8_t		selector;
+	uint8_t		reserved0[4];
+	uint32_t	value;	/* little endian */
+	uint32_t	reserved4;
+} query_attr_t;		/* 16 bytes with big endian except for value */
+
+/* Query Request UPIU */
+typedef struct query_upiu {
+	uint8_t		trans_type;
+	uint8_t		flags;
+	uint8_t		reserved0;
+	uint8_t		task_tag;
+	uint8_t		reserved1;
+	uint8_t		query_func;
+	uint8_t		reserved2;
+	uint8_t		reserved3;
+	uint8_t		total_ehs_len;
+	uint8_t		reserved4;
+	uint16_t	data_segment_len;
+	/* Transaction Specific Fields */
+	union {
+		query_desc_t	desc;
+		query_flag_t	flag;
+		query_attr_t	attr;
+	} ts;
+	uint32_t	reserved5;
+} query_upiu_t; /* 32 bytes with big endian */
+
+/* Query Response UPIU */
+typedef struct query_resp_upiu {
+	uint8_t		trans_type;
+	uint8_t		flags;
+	uint8_t		reserved0;
+	uint8_t		task_tag;
+	uint8_t		reserved1;
+	uint8_t		query_func;
+	uint8_t		query_resp;
+	uint8_t		reserved2;
+	uint8_t		total_ehs_len;
+	uint8_t		dev_info;
+	uint16_t	data_segment_len;
+	union {
+		query_desc_t	desc;
+		query_flag_t	flag;
+		query_attr_t	attr;
+	} ts;
+	uint32_t	reserved3;
+} query_resp_upiu_t;	/* 32 bytes with big endian */
+
+/* Response UPIU */
+typedef struct resp_upiu {
+	uint8_t		trans_type;
+	uint8_t		flags;
+	uint8_t		lun;
+	uint8_t		task_tag;
+	uint8_t		cmd_set_type;
+	uint8_t		reserved0;
+	uint8_t		reserved1;
+	uint8_t		status;
+	uint8_t		total_ehs_len;
+	uint8_t		dev_info;
+	uint16_t	data_segment_len;
+	uint32_t	res_trans_cnt;	/* Residual Transfer Count */
+	uint32_t	reserved2[4];
+	uint16_t	sense_data_len;
+	union {
+		uint8_t		sense_data[18];
+		sense_data_t	sense;
+	} sd;
+} resp_upiu_t;		/* 52 bytes with big endian */
+
+typedef struct cmd_info {
+	uintptr_t	buf;
+	size_t		length;
+	int		lba;
+	uint8_t		op;
+	uint8_t		direction;
+	uint8_t		lun;
+} cmd_info_t;
+
+typedef struct utp_utrd {
+	uintptr_t	header;		/* utrd_header_t */
+	uintptr_t	upiu;
+	uintptr_t	resp_upiu;
+	uintptr_t	prdt;
+	size_t		size_upiu;
+	size_t		size_resp_upiu;
+	size_t		size_prdt;
+	int		task_tag;
+} utp_utrd_t;
+
+/* Physical Region Description Table */
+typedef struct prdt {
+	uint32_t	dba;		/* Data Base Address */
+	uint32_t	dbau;		/* Data Base Address Upper 32-bits */
+	uint32_t	reserved0;
+	uint32_t	dbc : 18;	/* Data Byte Count */
+	uint32_t	reserved1 : 14;
+} prdt_t;
+
+typedef struct uic_cmd {
+	uint32_t	op;
+	uint32_t	arg1;
+	uint32_t	arg2;
+	uint32_t	arg3;
+} uic_cmd_t;
+
+typedef struct ufs_params {
+	uintptr_t	reg_base;
+	uintptr_t	desc_base;
+	size_t		desc_size;
+	unsigned long	flags;
+} ufs_params_t;
+
+typedef struct ufs_ops {
+	int		(*phy_init)(ufs_params_t *params);
+	int		(*phy_set_pwr_mode)(ufs_params_t *params);
+} ufs_ops_t;
+
+int ufshc_send_uic_cmd(uintptr_t base, uic_cmd_t *cmd);
+int ufshc_dme_get(unsigned int attr, unsigned int idx, unsigned int *val);
+int ufshc_dme_set(unsigned int attr, unsigned int idx, unsigned int val);
+
+unsigned int ufs_read_attr(int idn);
+void ufs_write_attr(int idn, unsigned int value);
+unsigned int ufs_read_flag(int idn);
+void ufs_set_flag(int idn);
+void ufs_clear_flag(int idn);
+void ufs_read_desc(int idn, int index, uintptr_t buf, size_t size);
+void ufs_write_desc(int idn, int index, uintptr_t buf, size_t size);
+size_t ufs_read_blocks(int lun, int lba, uintptr_t buf, size_t size);
+size_t ufs_write_blocks(int lun, int lba, const uintptr_t buf, size_t size);
+int ufs_init(const ufs_ops_t *ops, ufs_params_t *params);
+
+#endif /* __UFS_H__ */