drivers: add ufs stack
authorHaojian Zhuang <haojian.zhuang@linaro.org>
Wed, 31 May 2017 03:00:15 +0000 (11:00 +0800)
committerHaojian Zhuang <haojian.zhuang@linaro.org>
Wed, 31 May 2017 03:00:38 +0000 (11:00 +0800)
If UFS device is initialized, we could just make it out of
hibernation by UFS_FLAGS_SKIPINIT. And vendor's dirver is always
focus on PHY setting. We could use UFS driver directly if it
exits from hibernation.

There're eight LUNs in UFS device. The UFS driver only provides
the read/write API with LUN. User could define his own read/write
since user may want to access different LUNs.

Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
drivers/ufs/ufs.c [new file with mode: 0644]
include/drivers/ufs.h [new file with mode: 0644]

diff --git a/drivers/ufs/ufs.c b/drivers/ufs/ufs.c
new file mode 100644 (file)
index 0000000..dbfcd0f
--- /dev/null
@@ -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/ufs.h b/include/drivers/ufs.h
new file mode 100644 (file)
index 0000000..3a4f1c7
--- /dev/null
@@ -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__ */