Initial driver for Universal Flash Storage (UFS) Host Controllers.
authormglocker <mglocker@openbsd.org>
Sat, 4 Feb 2023 23:11:59 +0000 (23:11 +0000)
committermglocker <mglocker@openbsd.org>
Sat, 4 Feb 2023 23:11:59 +0000 (23:11 +0000)
ok kettenis@

share/man/man4/Makefile
share/man/man4/ufshci.4 [new file with mode: 0644]
sys/arch/arm64/conf/GENERIC
sys/conf/files
sys/dev/acpi/files.acpi
sys/dev/acpi/ufshci_acpi.c [new file with mode: 0644]
sys/dev/ic/ufshci.c [new file with mode: 0644]
sys/dev/ic/ufshcireg.h [new file with mode: 0644]
sys/dev/ic/ufshcivar.h [new file with mode: 0644]

index 2dad089..7bd9d93 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: Makefile,v 1.829 2023/02/03 13:28:51 kettenis Exp $
+#      $OpenBSD: Makefile,v 1.830 2023/02/04 23:11:59 mglocker Exp $
 
 MAN=   aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 acrtc.4 \
        acpi.4 acpiac.4 acpials.4 acpiasus.4 acpibat.4 \
@@ -94,7 +94,7 @@ MAN=  aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 acrtc.4 \
        tun.4 tap.4 twe.4 \
        txp.4 txphy.4 uaudio.4 uaq.4 uark.4 uath.4 ubcmtp.4 uberry.4 ubsa.4 \
        ucc.4 ucom.4 uchcom.4 ucrcom.4 ucycom.4 ukspan.4 uslhcom.4 \
-       udav.4 udcf.4 udl.4 udp.4 udsbr.4 \
+       udav.4 udcf.4 udl.4 udp.4 udsbr.4 ufshci.4 \
        uftdi.4 ugen.4 ugl.4 ugold.4 uguru.4 uhci.4 uhid.4 uhidev.4 uhidpp.4 \
        uipaq.4 ujoy.4 uk.4 ukbd.4 \
        ukphy.4 ulpt.4 umass.4 umb.4 umbg.4 umcs.4 umct.4 umidi.4 umodem.4 \
diff --git a/share/man/man4/ufshci.4 b/share/man/man4/ufshci.4
new file mode 100644 (file)
index 0000000..12f3fa0
--- /dev/null
@@ -0,0 +1,47 @@
+.\"    $OpenBSD: ufshci.4,v 1.1 2023/02/04 23:11:59 mglocker Exp $
+.\"
+.\" Copyright (c) 2023 Marcus Glocker <mglocker@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\"
+.Dd $Mdocdate: February 4 2023 $
+.Dt UFSHCI 4
+.Os
+.Sh NAME
+.Nm ufshci
+.Nd Universal Flash Storage Host Controller Interface
+.Sh SYNOPSIS
+.Cd "ufshci* at acpi?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for Universal Flash Storage (UFS) Host Controllers
+conforming to the JESD223C and JESD220C version 2.1 specifications.
+.Pp
+UFS uses NAND flash, and is based on the SCSI architectural model.
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr intro 4 ,
+.Xr scsi 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 7.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written by
+.An Marcus Glocker Aq Mt mglocker@openbsd.org .
index e18d6c9..3d5e954 100644 (file)
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.255 2023/02/03 14:48:57 kettenis Exp $
+# $OpenBSD: GENERIC,v 1.256 2023/02/04 23:11:59 mglocker Exp $
 #
 # GENERIC machine description file
 #
@@ -62,6 +62,7 @@ ipmi*         at acpi?
 ohci*          at acpi?
 pluart*                at acpi?
 sdhc*          at acpi?
+ufshci*                at acpi?
 xhci*          at acpi?
 simplebus*     at fdt?
 
index f4fabbe..92eef69 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files,v 1.720 2022/12/22 05:59:26 dlg Exp $
+#      $OpenBSD: files,v 1.721 2023/02/04 23:11:59 mglocker Exp $
 #      $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
 
 #      @(#)files.newconf       7.5 (Berkeley) 5/10/93
@@ -536,6 +536,11 @@ device     qcgpio
 # Qualcomm I2C controller
 device qciic: i2cbus
 
+# UFS HC
+device ufshci: scsi
+#device        ufshci
+file   dev/ic/ufshci.c                 ufshci
+
 # legitimate pseudo-devices
 pseudo-device vnd: disk
 pseudo-device rd: disk
index cf0a4e8..9b7bb76 100644 (file)
@@ -1,4 +1,4 @@
-#      $OpenBSD: files.acpi,v 1.67 2022/11/06 15:36:13 patrick Exp $
+#      $OpenBSD: files.acpi,v 1.68 2023/02/04 23:11:59 mglocker Exp $
 #
 # Config file and device description for machine-independent ACPI code.
 # Included by ports that need it.
@@ -271,3 +271,7 @@ file        dev/acpi/qcgpio.c               qcgpio
 # Qualcomm I2C controller
 attach qciic at acpi with qciic_acpi
 file   dev/acpi/qciic.c                qciic
+
+# UFS HC
+attach ufshci at acpi with ufshci_acpi
+file   dev/acpi/ufshci_acpi.c          ufshci_acpi
diff --git a/sys/dev/acpi/ufshci_acpi.c b/sys/dev/acpi/ufshci_acpi.c
new file mode 100644 (file)
index 0000000..27b8b97
--- /dev/null
@@ -0,0 +1,113 @@
+/*     $OpenBSD: ufshci_acpi.c,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */
+/*
+ * Copyright (c) 2022 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/ic/ufshcivar.h>
+
+struct ufshci_acpi_softc {
+       struct ufshci_softc      sc;
+       struct acpi_softc       *sc_acpi;
+       struct aml_node         *sc_node;
+       void                    *sc_ih;
+};
+
+int    ufshci_acpi_match(struct device *, void *, void *);
+void   ufshci_acpi_attach(struct device *, struct device *, void *);
+
+const struct cfattach ufshci_acpi_ca = {
+       sizeof(struct ufshci_acpi_softc), ufshci_acpi_match, ufshci_acpi_attach
+};
+
+const char *ufshci_hids[] = {
+       "QCOM24A5",
+       NULL
+};
+
+int
+ufshci_acpi_match(struct device *parent, void *match, void *aux)
+{
+       struct acpi_attach_args *aaa = aux;
+       struct cfdata *cf = match;
+
+       if (aaa->aaa_naddr < 1)
+               return 0;
+
+       return acpi_matchhids(aaa, ufshci_hids, cf->cf_driver->cd_name);
+}
+
+void
+ufshci_acpi_attach(struct device *parent, struct device *self, void *aux)
+{
+       struct ufshci_acpi_softc *sc = (struct ufshci_acpi_softc *)self;
+       struct acpi_attach_args *aaa = aux;
+       int error;
+
+       sc->sc_acpi = (struct acpi_softc *)parent;
+       sc->sc_node = aaa->aaa_node;
+       printf(" %s", sc->sc_node->name);
+
+       if (aaa->aaa_nirq < 1) {
+               printf(": no interrupt\n");
+               return;
+       }
+
+       printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
+       printf(" irq %d", aaa->aaa_irq[0]);
+
+       sc->sc.sc_iot = aaa->aaa_bst[0];
+       sc->sc.sc_ios = aaa->aaa_size[0];
+       sc->sc.sc_dmat = aaa->aaa_dmat;
+
+       if (bus_space_map(sc->sc.sc_iot, aaa->aaa_addr[0], aaa->aaa_size[0], 0,
+           &sc->sc.sc_ioh)) {
+               printf(": can't map registers\n");
+               return;
+       }
+
+       sc->sc_ih = acpi_intr_establish(aaa->aaa_irq[0], aaa->aaa_irq_flags[0],
+           IPL_BIO, ufshci_intr, sc, sc->sc.sc_dev.dv_xname);
+       if (sc->sc_ih == NULL) {
+               printf(": can't establish interrupt\n");
+               return;
+       }
+#if 0
+       /* XXX: Only for testing */
+       config_mountroot(self, ufshci_attach_hook);
+#endif
+       error = ufshci_attach(&sc->sc);
+       if (error) {
+               printf("%s: attach failed, error=%d\n",
+                   sc->sc.sc_dev.dv_xname, error);
+               return;
+       }
+}
diff --git a/sys/dev/ic/ufshci.c b/sys/dev/ic/ufshci.c
new file mode 100644 (file)
index 0000000..fab0ac0
--- /dev/null
@@ -0,0 +1,2001 @@
+/*     $OpenBSD: ufshci.c,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2022 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Universal Flash Storage Host Controller Interface (UFSHCI) 2.1 driver based
+ * on the JEDEC JESD223C.pdf and JESD220C-2_1.pdf specifications.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/queue.h>
+#include <sys/mutex.h>
+#include <sys/pool.h>
+
+#include <sys/atomic.h>
+
+#include <machine/bus.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_disk.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/ic/ufshcivar.h>
+#include <dev/ic/ufshcireg.h>
+
+//#define UFSHCI_DEBUG 1
+
+#ifdef UFSHCI_DEBUG
+int ufshci_dbglvl = 1;
+#define DPRINTF(x...)  do { printf(x); } while (0)
+#else
+#define DPRINTF(x...)
+#endif
+
+struct cfdriver ufshci_cd = {
+       NULL, "ufshci", DV_DULL
+};
+
+int                     ufshci_reset(struct ufshci_softc *);
+int                     ufshci_uccs_poll(struct ufshci_softc *);
+struct ufshci_dmamem   *ufshci_dmamem_alloc(struct ufshci_softc *, size_t);
+void                    ufshci_dmamem_free(struct ufshci_softc *,
+                            struct ufshci_dmamem *);
+int                     ufshci_init(struct ufshci_softc *);
+int                     ufshci_doorbell_get_free(struct ufshci_softc *);
+int                     ufshci_doorbell_read(struct ufshci_softc *);
+int                     ufshci_doorbell_poll(struct ufshci_softc *, int);
+void                    ufshci_doorbell_set(struct ufshci_softc *, int);
+uint8_t                         ufshci_get_taskid(struct ufshci_softc *);
+int                     ufshci_utr_cmd_nop(struct ufshci_softc *);
+int                     ufshci_utr_cmd_lun(struct ufshci_softc *,
+                            struct ufshci_ccb *, int);
+int                     ufshci_utr_cmd_inquiry(struct ufshci_softc *,
+                            struct ufshci_ccb *, int, int);
+int                     ufshci_utr_cmd_capacity16(struct ufshci_softc *,
+                            struct ufshci_ccb *, int, int);
+int                     ufshci_utr_cmd_capacity(struct ufshci_softc *,
+                            struct ufshci_ccb *, int, int);
+int                     ufshci_utr_cmd_read(struct ufshci_softc *,
+                            struct ufshci_ccb *, int, int, uint32_t, uint16_t);
+int                     ufshci_utr_cmd_write(struct ufshci_softc *,
+                            struct ufshci_ccb *, int, int, uint32_t, uint16_t);
+int                     ufshci_utr_cmd_sync(struct ufshci_softc *,
+                            struct ufshci_ccb *, int, uint32_t, uint16_t);
+int                     ufshci_xfer_complete(struct ufshci_softc *);
+void                    ufshci_hexdump(void *, int, char *, int);
+
+/* SCSI */
+int                     ufshci_ccb_alloc(struct ufshci_softc *, int);
+void                   *ufshci_ccb_get(void *);
+void                    ufshci_ccb_put(void *, void *);
+void                    ufshci_ccb_free(struct ufshci_softc*, int);
+
+void                    ufshci_scsi_cmd(struct scsi_xfer *);
+void                    ufshci_minphys(struct buf *, struct scsi_link *);
+int                     ufshci_scsi_probe(struct scsi_link *);
+void                    ufshci_scsi_free(struct scsi_link *);
+
+void                    ufshci_scsi_inquiry(struct scsi_xfer *);
+void                    ufshci_scsi_capacity16(struct scsi_xfer *);
+void                    ufshci_scsi_capacity(struct scsi_xfer *);
+void                    ufshci_scsi_sync(struct scsi_xfer *);
+void                    ufshci_scsi_io(struct scsi_xfer *, int);
+void                    ufshci_scsi_io_done(struct ufshci_softc *,
+                            struct ufshci_ccb *);
+void                    ufshci_scsi_done(struct ufshci_softc *,
+                            struct ufshci_ccb *);
+
+#if 0
+const struct scsi_adapter ufshci_switch = {
+       ufshci_scsi_cmd, ufshci_minphys, ufshci_scsi_probe, ufshci_scsi_free,
+       NULL
+};
+#endif
+const struct scsi_adapter ufshci_switch = {
+       ufshci_scsi_cmd, NULL, NULL, NULL, NULL
+};
+
+int
+ufshci_intr(void *arg)
+{
+       struct ufshci_softc *sc = arg;
+       uint32_t status;
+       int handled = 0;
+
+       status = UFSHCI_READ_4(sc, UFSHCI_REG_IS);
+       DPRINTF("%s: status=0x%08x\n", __func__, status);
+
+       if (status == 0)
+               return 0;
+
+       if (status & UFSHCI_REG_IS_UCCS) {
+               DPRINTF("%s: UCCS interrupt\n", __func__);
+               handled = 1;
+       }
+       if (status & UFSHCI_REG_IS_UTRCS) {
+               DPRINTF("%s: UTRCS interrupt\n", __func__);
+
+               ufshci_xfer_complete(sc);
+
+               /* Reset Interrupt Aggregation Counter and Timer. */
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN | UFSHCI_REG_UTRIACR_CTR);
+
+               handled = 1;
+       }
+
+       if (handled == 0) {
+               printf("%s: UNKNOWN interrupt, status=0x%08x\n",
+                   sc->sc_dev.dv_xname, status);
+       }
+
+       /* ACK interrupt */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_IS, status);
+
+       return 1;
+}
+
+/* XXX: Only for testing */
+void
+ufshci_attach_hook(struct device *self)
+{
+       struct ufshci_softc *sc = (struct ufshci_softc *)self;
+
+       ufshci_attach(sc);
+}
+
+int
+ufshci_attach(struct ufshci_softc *sc)
+{
+       struct scsibus_attach_args saa;
+
+       mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
+       SIMPLEQ_INIT(&sc->sc_ccb_list);
+       scsi_iopool_init(&sc->sc_iopool, sc, ufshci_ccb_get, ufshci_ccb_put);
+
+       ufshci_reset(sc);
+
+       sc->sc_ver = UFSHCI_READ_4(sc, UFSHCI_REG_VER);
+       printf(", UFSHCI %d.%d%d\n",
+           UFSHCI_REG_VER_MAJOR(sc->sc_ver),
+           UFSHCI_REG_VER_MINOR(sc->sc_ver),
+           UFSHCI_REG_VER_SUFFIX(sc->sc_ver));
+
+       sc->sc_cap = UFSHCI_READ_4(sc, UFSHCI_REG_CAP);
+       sc->sc_hcpid = UFSHCI_READ_4(sc, UFSHCI_REG_HCPID);
+       sc->sc_hcmid = UFSHCI_READ_4(sc, UFSHCI_REG_HCMID);
+       sc->sc_nutmrs = UFSHCI_REG_CAP_NUTMRS(sc->sc_cap) + 1;
+       sc->sc_rtt = UFSHCI_REG_CAP_RTT(sc->sc_cap) + 1;
+       sc->sc_nutrs = UFSHCI_REG_CAP_NUTRS(sc->sc_cap) + 1;
+
+#if UFSHCI_DEBUG
+       printf("Capabilities (0x%08x):\n", sc->sc_cap);
+       printf(" CS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_CS ? 1 : 0);
+       printf(" UICDMETMS=%d\n",
+           sc->sc_cap & UFSHCI_REG_CAP_UICDMETMS ? 1 : 0);
+       printf(" OODDS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_OODDS ? 1 : 0);
+       printf(" 64AS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_64AS ? 1 : 0);
+       printf(" AUTOH8=%d\n", sc->sc_cap & UFSHCI_REG_AUTOH8 ? 1 : 0);
+       printf(" NUTMRS=%d\n", sc->sc_nutmrs);
+       printf(" RTT=%d\n", sc->sc_rtt);
+       printf(" NUTRS=%d\n", sc->sc_nutrs);
+       printf("HCPID=0x%08x:\n", sc->sc_hcpid);
+       printf("HCMID (0x%08x):\n", sc->sc_hcmid);
+       printf(" BI=0x%04x\n", UFSHCI_REG_HCMID_BI(sc->sc_hcmid));
+       printf(" MIC=0x%04x\n", UFSHCI_REG_HCMID_MIC(sc->sc_hcmid));
+#endif
+
+       if (sc->sc_nutrs > 32) {
+               printf("%s: NUTRS can't be >32 (is %d)!\n",
+                   sc->sc_dev.dv_xname, sc->sc_nutrs);
+               return 1;
+       }
+
+       ufshci_init(sc);
+
+       if (ufshci_ccb_alloc(sc, sc->sc_nutrs) != 0) {
+               printf("%s: %s: Can't allocate CCBs\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return 1;
+       }
+
+       /* Attach to SCSI layer */
+       saa.saa_adapter = &ufshci_switch;
+       saa.saa_adapter_softc = sc;
+       saa.saa_adapter_buswidth = 2; /* XXX: What's the right value? */
+       saa.saa_luns = 1; /* XXX: Should we use ufshci_utr_cmd_lun() */
+       saa.saa_adapter_target = 0;
+       saa.saa_openings = sc->sc_nutrs;
+       saa.saa_pool = &sc->sc_iopool;
+       saa.saa_quirks = saa.saa_flags = 0;
+       saa.saa_wwpn = saa.saa_wwnn = 0;
+
+       config_found(&sc->sc_dev, &saa, scsiprint);
+
+       return 0;
+}
+
+int
+ufshci_reset(struct ufshci_softc *sc)
+{
+       int i;
+       int retry = 10;
+       uint32_t hce;
+
+       /*
+        * 7.1.1 Host Controller Initialization: 2)
+        * Reset and enable host controller
+        */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_HCE, UFSHCI_REG_HCE_HCE);
+       /* 7.1.1 Host Controller Initialization: 3) */
+       for (i = 0; i < retry; i++) {
+               hce = UFSHCI_READ_4(sc, UFSHCI_REG_HCE);
+               if (hce == 1)
+                       break;
+               delay(1);
+       }
+       if (i == retry) {
+               printf("%s: Enabling Host Controller failed!\n",
+                   sc->sc_dev.dv_xname);
+               return -1;
+       }
+
+       DPRINTF("\n%s: Host Controller enabled (i=%d)\n", __func__, i);
+
+       return 0;
+}
+
+int
+ufshci_uccs_poll(struct ufshci_softc *sc)
+{
+       uint32_t status;
+       int i, retry = 25;
+
+       DPRINTF("%s\n", __func__);
+
+       for (i = 0; i < retry; i++) {
+               status = UFSHCI_READ_4(sc, UFSHCI_REG_IS);
+               if (status & UFSHCI_REG_IS_UCCS)
+                       break;
+               delay(10);
+       }
+       if (i == retry) {
+               printf("%s: %s: timeout\n", sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+       DPRINTF("%s: completed after %d retries\n", __func__, i);
+
+       /* ACK interrupt */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_IS, status);
+
+       return 0;
+}
+
+struct ufshci_dmamem *
+ufshci_dmamem_alloc(struct ufshci_softc *sc, size_t size)
+{
+       struct ufshci_dmamem *udm;
+       int nsegs;
+
+       udm = malloc(sizeof(*udm), M_DEVBUF, M_WAITOK | M_ZERO);
+       if (udm == NULL)
+               return NULL;
+
+       udm->udm_size = size;
+
+       if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
+           BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
+           &udm->udm_map) != 0)
+               goto udmfree;
+
+       if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &udm->udm_seg,
+           1, &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO) != 0)
+               goto destroy;
+
+       if (bus_dmamem_map(sc->sc_dmat, &udm->udm_seg, nsegs, size,
+           &udm->udm_kva, BUS_DMA_WAITOK) != 0)
+               goto free;
+
+       if (bus_dmamap_load(sc->sc_dmat, udm->udm_map, udm->udm_kva, size,
+           NULL, BUS_DMA_WAITOK) != 0)
+               goto unmap;
+
+       DPRINTF("%s: size=%lu, page_size=%d, nsegs=%d\n",
+           __func__, size, PAGE_SIZE, nsegs);
+
+       return udm;
+
+unmap:
+       bus_dmamem_unmap(sc->sc_dmat, udm->udm_kva, size);
+free:
+       bus_dmamem_free(sc->sc_dmat, &udm->udm_seg, 1);
+destroy:
+       bus_dmamap_destroy(sc->sc_dmat, udm->udm_map);
+udmfree:
+       free(udm, M_DEVBUF, sizeof(*udm));
+
+       return NULL;
+}
+
+void
+ufshci_dmamem_free(struct ufshci_softc *sc, struct ufshci_dmamem *udm)
+{
+       bus_dmamap_unload(sc->sc_dmat, udm->udm_map);
+       bus_dmamem_unmap(sc->sc_dmat, udm->udm_kva, udm->udm_size);
+       bus_dmamem_free(sc->sc_dmat, &udm->udm_seg, 1);
+       bus_dmamap_destroy(sc->sc_dmat, udm->udm_map);
+       free(udm, M_DEVBUF, sizeof(*udm));
+}
+
+int
+ufshci_init(struct ufshci_softc *sc)
+{
+       uint32_t reg;
+       uint64_t dva;
+
+       /*
+        * 7.1.1 Host Controller Initialization: 4)
+        * TODO: Do we need to set DME_SET?
+        */
+
+       /* 7.1.1 Host Controller Initialization: 5) */
+       //UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, UFSHCI_REG_IE_UCCE |
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_IE,
+           UFSHCI_REG_IE_UTRCE | UFSHCI_REG_IE_UTMRCE);
+
+       /* 7.1.1 Host Controller Initialization: 6) */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UICCMD,
+           UFSHCI_REG_UICCMD_CMDOP_DME_LINKSTARTUP);
+       if (ufshci_uccs_poll(sc) != 0)
+               return -1;
+
+       /*
+        * 7.1.1 Host Controller Initialization: 7), 8), 9)
+        * TODO: Implement retry in case UFSHCI_REG_HCS returns 0
+        */
+       reg = UFSHCI_READ_4(sc, UFSHCI_REG_HCS);
+       if (reg & UFSHCI_REG_HCS_DP)
+               DPRINTF("%s: Device Presence SET\n", __func__);
+       else
+               DPRINTF("%s: Device Presence NOT SET\n", __func__);
+
+       /*
+        * 7.1.1 Host Controller Initialization: 10)
+        * TODO: Enable additional interrupt on the IE register
+        */
+
+       /* 7.1.1 Host Controller Initialization: 11) */
+       reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRIACR);
+       DPRINTF("%s: UTRIACR=0x%08x\n", __func__, reg);
+       /*
+        * Only enable interrupt aggregation when interrupts are available.
+        * Otherwise, the interrupt aggregation counter already starts to
+        * count completed commands, and will keep interrupts disabled once
+        * reaching the threshold.  We only issue the interrupt aggregation
+        * counter reset in the interrupt handler during runtime, so we would
+        * have a kind of chicken/egg problem.
+        */
+       if (!cold) {
+               DPRINTF("%s: Enable interrupt aggregation\n", __func__);
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_CTR |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+               sc->sc_intraggr_enabled = 1;
+       }
+
+       /*
+        * 7.1.1 Host Controller Initialization: 12)
+        * TODO: More UIC commands to issue?
+        */
+
+       /* 7.1.1 Host Controller Initialization: 13) */
+       sc->sc_dmamem_utmrd = ufshci_dmamem_alloc(sc,
+           sizeof(struct ufshci_utmrd) * sc->sc_nutmrs);
+       if (sc->sc_dmamem_utmrd == NULL) {
+               printf("%s: Can't allocate DMA memory for UTMRD\n",
+                   sc->sc_dev.dv_xname);
+               return -1;
+       }
+       /* 7.1.1 Host Controller Initialization: 14) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_utmrd);
+       DPRINTF("%s: utmrd dva=%llu\n", __func__, dva);
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLBA, (uint32_t)dva);
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLBAU, (uint32_t)(dva >> 32));
+
+       /* 7.1.1 Host Controller Initialization: 15) */
+       sc->sc_dmamem_utrd = ufshci_dmamem_alloc(sc,
+           sizeof(struct ufshci_utrd) * sc->sc_nutrs);
+       if (sc->sc_dmamem_utrd == NULL) {
+               printf("%s: Can't allocate DMA memory for UTRD\n",
+                   sc->sc_dev.dv_xname);
+               return -1;
+       }
+       /* 7.1.1 Host Controller Initialization: 16) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_utrd);
+       DPRINTF("%s: utrd dva=%llu\n", __func__, dva);
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLBA, (uint32_t)dva);
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLBAU, (uint32_t)(dva >> 32));
+
+
+       /* Allocate UCDs. */
+       sc->sc_dmamem_ucd = ufshci_dmamem_alloc(sc,
+           sizeof(struct ufshci_ucd) * sc->sc_nutrs);
+       if (sc->sc_dmamem_ucd == NULL) {
+               printf("%s: Can't allocate DMA memory for UCD\n",
+                   sc->sc_dev.dv_xname);
+               return -1;
+       }
+
+       /* 7.1.1 Host Controller Initialization: 17) */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLRSR, UFSHCI_REG_UTMRLRSR_START);
+
+       /* 7.1.1 Host Controller Initialization: 18) */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLRSR, UFSHCI_REG_UTRLRSR_START);
+
+       /* 7.1.1 Host Controller Initialization: 19) */
+       /* TODO: bMaxNumOfRTT will be set as the minimum value of
+        * bDeviceRTTCap and NORTT. ???
+        */
+
+       return 0;
+}
+
+int
+ufshci_doorbell_get_free(struct ufshci_softc *sc)
+{
+       int slot;
+       uint32_t reg;
+
+       reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR);
+
+       for (slot = 0; slot < sc->sc_nutrs; slot++) {
+               if ((reg & (1 << slot)) == 0)
+                       return slot;
+       }
+
+       return -1;
+}
+
+int
+ufshci_doorbell_read(struct ufshci_softc *sc)
+{
+       uint32_t reg;
+
+       reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR);
+
+       return reg;
+}
+
+int
+ufshci_doorbell_poll(struct ufshci_softc *sc, int slot)
+{
+       uint32_t reg;
+       int i, retry = 25;
+
+       DPRINTF("%s\n", __func__);
+
+       for (i = 0; i < retry; i++) {
+               reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR);
+               if ((reg & (1 << slot)) == 0)
+                       break;
+               delay(10);
+       }
+       if (i == retry) {
+               printf("%s: %s: timeout\n", sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       return 0;
+}
+
+void
+ufshci_doorbell_set(struct ufshci_softc *sc, int slot)
+{
+       uint32_t reg;
+
+       reg = (1 << slot);
+
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLDBR, reg);
+}
+
+uint8_t
+ufshci_get_taskid(struct ufshci_softc *sc)
+{
+       if (sc->sc_taskid == 255)
+               sc->sc_taskid = 0;
+       else
+               sc->sc_taskid++;
+
+       return sc->sc_taskid;
+}
+
+int
+ufshci_utr_cmd_nop(struct ufshci_softc *sc)
+{
+       int slot, off, len;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_NO;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_NOP_OUT;
+       ucd->cmd.hdr.flags = 0;
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(0); /* No data xfer */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+           UFSHCI_REG_UTRIACR_IAEN |
+           UFSHCI_REG_UTRIACR_IAPWEN |
+           UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+           UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return 0;
+}
+
+int
+ufshci_utr_cmd_lun(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int rsp_size)
+{
+       int slot, off, len, i;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(rsp_size);
+
+       ucd->cmd.cdb[0] = REPORT_LUNS;
+       ucd->cmd.cdb[6] = 0;
+       ucd->cmd.cdb[7] = 0;
+       ucd->cmd.cdb[8] = 0;
+       ucd->cmd.cdb[9] = rsp_size;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs);
+
+       /* Build PRDT data segment. */
+       for (i = 0; i < dmap->dm_nsegs; i++) {
+               dva = dmap->dm_segs[i].ds_addr;
+               ucd->prdt[i].dw0 = (uint32_t)dva;
+               ucd->prdt[i].dw1 = (uint32_t)(dva >> 32);
+               ucd->prdt[i].dw2 = 0;
+               ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+           UFSHCI_REG_UTRIACR_IAEN |
+           UFSHCI_REG_UTRIACR_IAPWEN |
+           UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+           UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return 0;
+}
+
+int
+ufshci_utr_cmd_inquiry(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int rsp_size, int flags)
+{
+       int slot, off, len, i;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(rsp_size);
+
+       ucd->cmd.cdb[0] = INQUIRY; /* 0x12 */
+       ucd->cmd.cdb[3] = 0;
+       ucd->cmd.cdb[4] = rsp_size;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs);
+
+       /* Build PRDT data segment. */
+       for (i = 0; i < dmap->dm_nsegs; i++) {
+               dva = dmap->dm_segs[i].ds_addr;
+               ucd->prdt[i].dw0 = (uint32_t)dva;
+               ucd->prdt[i].dw1 = (uint32_t)(dva >> 32);
+               ucd->prdt[i].dw2 = 0;
+               ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       if (!ISSET(flags, SCSI_POLL)) {
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return slot;
+}
+
+int
+ufshci_utr_cmd_capacity16(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int rsp_size, int flags)
+{
+       int slot, off, len, i;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(rsp_size);
+
+       ucd->cmd.cdb[0] = READ_CAPACITY_16; /* 0x9e */
+       ucd->cmd.cdb[1] = 0x10; /* Service Action */
+       /* Logical Block Address = 0 for UFS */
+       ucd->cmd.cdb[10] = 0;
+       ucd->cmd.cdb[11] = 0;
+       ucd->cmd.cdb[12] = 0;
+       ucd->cmd.cdb[13] = rsp_size;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs);
+
+       /* Build PRDT data segment. */
+       for (i = 0; i < dmap->dm_nsegs; i++) {
+               dva = dmap->dm_segs[i].ds_addr;
+               ucd->prdt[i].dw0 = (uint32_t)dva;
+               ucd->prdt[i].dw1 = (uint32_t)(dva >> 32);
+               ucd->prdt[i].dw2 = 0;
+               ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       if (!ISSET(flags, SCSI_POLL)) {
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return slot;
+}
+
+int
+ufshci_utr_cmd_capacity(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int rsp_size, int flags)
+{
+       int slot, off, len, i;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(rsp_size);
+
+       ucd->cmd.cdb[0] = READ_CAPACITY; /* 0x25 */
+       /* Logical Block Address = 0 for UFS */
+       ucd->cmd.cdb[2] = 0;
+       ucd->cmd.cdb[3] = 0;
+       ucd->cmd.cdb[4] = 0;
+       ucd->cmd.cdb[5] = 0;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs);
+
+       /* Build PRDT data segment. */
+       for (i = 0; i < dmap->dm_nsegs; i++) {
+               dva = dmap->dm_segs[i].ds_addr;
+               ucd->prdt[i].dw0 = (uint32_t)dva;
+               ucd->prdt[i].dw1 = (uint32_t)(dva >> 32);
+               ucd->prdt[i].dw2 = 0;
+               ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       if (!ISSET(flags, SCSI_POLL)) {
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return slot;
+}
+
+int
+ufshci_utr_cmd_read(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int rsp_size, int flags, uint32_t lba, uint16_t blocks)
+{
+       int slot, off, len, i;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(rsp_size);
+
+       ucd->cmd.cdb[0] = READ_10; /* 0x28 */
+       //ucd->cmd.cdb[1] = (1 << 3); /* FUA: Force Unit Access */
+       ucd->cmd.cdb[2] = (lba >> 24) & 0xff;
+       ucd->cmd.cdb[3] = (lba >> 16) & 0xff;
+       ucd->cmd.cdb[4] = (lba >>  8) & 0xff;
+       ucd->cmd.cdb[5] = (lba >>  0) & 0xff;
+       ucd->cmd.cdb[7] = (blocks >> 8) & 0xff;
+       ucd->cmd.cdb[8] = (blocks >> 0) & 0xff;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs);
+
+       /* Build PRDT data segment. */
+       for (i = 0; i < dmap->dm_nsegs; i++) {
+               dva = dmap->dm_segs[i].ds_addr;
+               ucd->prdt[i].dw0 = (uint32_t)dva;
+               ucd->prdt[i].dw1 = (uint32_t)(dva >> 32);
+               ucd->prdt[i].dw2 = 0;
+               ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       if (!ISSET(flags, SCSI_POLL)) {
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return slot;
+}
+
+int
+ufshci_utr_cmd_write(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int rsp_size, int flags, uint32_t lba, uint16_t blocks)
+{
+       int slot, off, len, i;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_I2T;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = (1 << 5); /* Bit-5 = Write, Bit-6 = Read */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(rsp_size);
+
+       ucd->cmd.cdb[0] = WRITE_10; /* 0x2a */
+       //ucd->cmd.cdb[1] = (1 << 3); /* FUA: Force Unit Access */
+       ucd->cmd.cdb[2] = (lba >> 24) & 0xff;
+       ucd->cmd.cdb[3] = (lba >> 16) & 0xff;
+       ucd->cmd.cdb[4] = (lba >>  8) & 0xff;
+       ucd->cmd.cdb[5] = (lba >>  0) & 0xff;
+       ucd->cmd.cdb[7] = (blocks >> 8) & 0xff;
+       ucd->cmd.cdb[8] = (blocks >> 0) & 0xff;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs);
+
+       /* Build PRDT data segment. */
+       for (i = 0; i < dmap->dm_nsegs; i++) {
+               dva = dmap->dm_segs[i].ds_addr;
+               ucd->prdt[i].dw0 = (uint32_t)dva;
+               ucd->prdt[i].dw1 = (uint32_t)(dva >> 32);
+               ucd->prdt[i].dw2 = 0;
+               ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       if (!ISSET(flags, SCSI_POLL)) {
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_COUNT));
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return slot;
+}
+
+int
+ufshci_utr_cmd_sync(struct ufshci_softc *sc, struct ufshci_ccb *ccb,
+    int flags, uint32_t lba, uint16_t blocks)
+{
+       int slot, off, len;
+       uint64_t dva;
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */
+       slot = ufshci_doorbell_get_free(sc);
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       memset(utrd, 0, sizeof(*utrd));
+       DPRINTF("%s: slot=%d\n", __func__, slot);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */
+       utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_DD_I2T;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */
+       utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */
+       utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       memset(ucd, 0, sizeof(*ucd));
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */
+       ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND;
+       ucd->cmd.hdr.flags = 0; /* No data transfer */
+       ucd->cmd.hdr.lun = 0;
+       ucd->cmd.hdr.taskid = ufshci_get_taskid(sc);;
+       ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */
+       ucd->cmd.hdr.query = 0;
+       ucd->cmd.hdr.response = 0;
+       ucd->cmd.hdr.status = 0;
+       ucd->cmd.hdr.ehs_len = 0;
+       ucd->cmd.hdr.device_info = 0;
+       ucd->cmd.hdr.ds_len = 0;
+
+       ucd->cmd.expected_xfer_len = htobe32(0); /* No data transfer */
+
+       ucd->cmd.cdb[0] = SYNCHRONIZE_CACHE; /* 0x35 */
+       ucd->cmd.cdb[2] = (lba >> 24) & 0xff;
+       ucd->cmd.cdb[3] = (lba >> 16) & 0xff;
+       ucd->cmd.cdb[4] = (lba >>  8) & 0xff;
+       ucd->cmd.cdb[5] = (lba >>  0) & 0xff;
+       ucd->cmd.cdb[7] = (blocks >> 8) & 0xff;
+       ucd->cmd.cdb[8] = (blocks >> 0) & 0xff;
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */
+       /* Already done with above memset */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */
+       dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+       DPRINTF("%s: ucd dva=%llu\n", __func__, dva);
+       utrd->dw4 = (uint32_t)dva;
+       utrd->dw5 = (uint32_t)(dva >> 32);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */
+       off = sizeof(struct upiu_command) / 4; /* DWORD offset */
+       utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */
+       len = sizeof(struct upiu_response) / 4; /* DWORD length */
+       utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */
+       off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4;
+       utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off);
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */
+       utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(0); /* No data xfer */
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */
+       if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) {
+               printf("%s: %s: UTRLRSR not set\n",
+                   sc->sc_dev.dv_xname, __func__);
+               return -1;
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 10) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 11) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 12) */
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 13) */
+       if (!ISSET(flags, SCSI_POLL)) {
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+       }
+
+       /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */
+       ufshci_doorbell_set(sc, slot);
+
+       return slot;
+}
+
+int
+ufshci_xfer_complete(struct ufshci_softc *sc)
+{
+       struct ufshci_ccb *ccb;
+       uint32_t reg;
+       int i;
+
+       reg = ufshci_doorbell_read(sc);
+
+       for (i = 0; i < sc->sc_nutrs; i++) {
+               ccb = &sc->sc_ccbs[i];
+
+               if (ccb->ccb_slot == -1)
+                       /* CCB isn't used. */
+                       continue;
+
+               if (reg & (1 << ccb->ccb_slot))
+                       /* Transfer is still in progress. */
+                       continue;
+
+               /* Transfer has completed. */
+               if (ccb->ccb_done == NULL)
+                       panic("ccb_done not defined");
+               ccb->ccb_done(sc, ccb);
+       }
+
+       return 0;
+}
+
+#ifdef UFSHCI_DEBUG
+void
+ufshci_hexdump(void *buf, int len, char *title, int dbglvl)
+{
+       u_char b[16];
+       int i, j, l;
+
+       if (dbglvl > ufshci_dbglvl)
+               return;
+
+       printf("hexdump for %s (size=%d bytes)\n", title, len);
+
+       for (i = 0; i < len; i += l) {
+               printf("%4i:", i);
+               l = min(sizeof(b), len - i);
+               bcopy(buf + i, b, l);
+
+               for (j = 0; j < sizeof(b); j++) {
+                       if (j % 2 == 0)
+                               printf(" "); 
+                       if (j % 8 == 0)
+                               printf(" ");
+                       if (j < l)
+                               printf("%02x", (int)b[j]);
+                       else
+                               printf("  ");
+               }
+               printf("  |");
+               for (j = 0; j < l; j++) {
+                       if (b[j] >= 0x20 && b[j] <= 0x7e)
+                               printf("%c", b[j]);
+                       else
+                               printf(".");
+               }
+               printf("|\n");
+       }
+}
+#else
+void
+ufshci_hexdump(void *buf, int len, char *title, int dbglvl)
+{
+
+}
+#endif
+
+/* SCSI */
+
+int
+ufshci_ccb_alloc(struct ufshci_softc *sc, int nccbs)
+{
+       struct ufshci_ccb *ccb;
+       int i;
+
+       DPRINTF("%s: nccbs=%d, dma_size=%d, dma_nsegs=%d, "
+           "dma_segmaxsize=%d\n",
+           __func__, nccbs, UFSHCI_UCD_PRDT_MAX_XFER, UFSHCI_UCD_PRDT_MAX_SEGS,
+           UFSHCI_UCD_PRDT_MAX_XFER);
+
+       sc->sc_ccbs = mallocarray(nccbs, sizeof(*ccb), M_DEVBUF,
+           M_WAITOK | M_CANFAIL);
+       if (sc->sc_ccbs == NULL)
+               return 1;
+
+       for (i = 0; i < nccbs; i++) {
+               ccb = &sc->sc_ccbs[i];
+
+               if (bus_dmamap_create(sc->sc_dmat, UFSHCI_UCD_PRDT_MAX_XFER,
+                   UFSHCI_UCD_PRDT_MAX_SEGS, UFSHCI_UCD_PRDT_MAX_XFER, 0,
+                   BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
+                   &ccb->ccb_dmamap) != 0)
+                       goto free_maps;
+
+               ccb->ccb_cookie = NULL;
+               ccb->ccb_slot = -1;
+
+               SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry);
+       }
+
+       return 0;
+
+free_maps:
+       ufshci_ccb_free(sc, nccbs);
+       return 1;
+}
+
+void *
+ufshci_ccb_get(void *cookie)
+{
+       struct ufshci_softc *sc = cookie;
+       struct ufshci_ccb *ccb;
+
+       DPRINTF("%s\n", __func__);
+
+       mtx_enter(&sc->sc_ccb_mtx);
+       ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list);
+       if (ccb != NULL)
+               SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry);
+       mtx_leave(&sc->sc_ccb_mtx);
+
+       return ccb;
+}
+
+void
+ufshci_ccb_put(void *cookie, void *io)
+{
+       struct ufshci_softc *sc = cookie;
+       struct ufshci_ccb *ccb = io;
+
+       DPRINTF("%s\n", __func__);
+
+       mtx_enter(&sc->sc_ccb_mtx);
+       SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_list, ccb, ccb_entry);
+       mtx_leave(&sc->sc_ccb_mtx);
+}
+
+void
+ufshci_ccb_free(struct ufshci_softc *sc, int nccbs)
+{
+       struct ufshci_ccb *ccb;
+
+       DPRINTF("%s\n", __func__);
+
+       while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list)) != NULL) {
+               SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry);
+               bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
+       }
+
+       ufshci_dmamem_free(sc, sc->sc_dmamem_utrd);
+       free(sc->sc_ccbs, M_DEVBUF, nccbs * sizeof(*ccb));
+}
+
+void
+ufshci_scsi_cmd(struct scsi_xfer *xs)
+{
+       struct scsi_link *link = xs->sc_link;
+       struct ufshci_softc *sc = link->bus->sb_adapter_softc;
+
+       DPRINTF("%s: cmd=0x%x\n", __func__, xs->cmd.opcode);
+
+       if (!cold && !sc->sc_intraggr_enabled) {
+               DPRINTF("%s: Enable interrupt aggregation\n", __func__);
+               UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR,
+                   UFSHCI_REG_UTRIACR_IAEN |
+                   UFSHCI_REG_UTRIACR_IAPWEN |
+                   UFSHCI_REG_UTRIACR_CTR |
+                   UFSHCI_REG_UTRIACR_IACTH(UFSHCI_INTR_AGGR_COUNT) |
+                   UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT));
+               sc->sc_intraggr_enabled = 1;
+       }
+
+       switch (xs->cmd.opcode) {
+
+       case READ_COMMAND:
+       case READ_10:
+       case READ_12:
+       case READ_16:
+               ufshci_scsi_io(xs, SCSI_DATA_IN);
+               return;
+
+       case WRITE_COMMAND:
+       case WRITE_10:
+       case WRITE_12:
+       case WRITE_16:
+               ufshci_scsi_io(xs, SCSI_DATA_OUT);
+               return;
+
+       case SYNCHRONIZE_CACHE:
+               ufshci_scsi_sync(xs);
+               return;
+
+       case INQUIRY:
+               ufshci_scsi_inquiry(xs);
+               return;
+
+       case READ_CAPACITY_16:
+               ufshci_scsi_capacity16(xs);
+               return;
+       case READ_CAPACITY:
+               ufshci_scsi_capacity(xs);
+               return;
+
+       case TEST_UNIT_READY:
+       case PREVENT_ALLOW:
+       case START_STOP:
+               xs->error = XS_NOERROR;
+               scsi_done(xs);
+               return;
+       default:
+               DPRINTF("%s: unhandled scsi command 0x%02x\n",
+                   __func__, xs->cmd.opcode);
+               break;
+       }
+
+       xs->error = XS_DRIVER_STUFFUP;
+       scsi_done(xs);
+}
+
+void
+ufshci_minphys(struct buf *bp, struct scsi_link *link)
+{
+       DPRINTF("%s\n", __func__);
+}
+
+int
+ufshci_scsi_probe(struct scsi_link *link)
+{
+       DPRINTF("%s\n", __func__);
+
+       return 0;
+}
+
+void
+ufshci_scsi_free(struct scsi_link *link)
+{
+       DPRINTF("%s\n", __func__);
+}
+
+void
+ufshci_scsi_inquiry(struct scsi_xfer *xs)
+{
+       struct scsi_link *link = xs->sc_link;
+       struct ufshci_softc *sc = link->bus->sb_adapter_softc;
+       struct ufshci_ccb *ccb = xs->io;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+       int error;
+
+       DPRINTF("%s: INQUIRY (%s)\n",
+           __func__, ISSET(xs->flags, SCSI_POLL) ? "poll"  : "no poll");
+
+       if (xs->datalen > UPIU_SCSI_RSP_INQUIRY_SIZE) {
+               DPRINTF("%s: request len too large\n", __func__);
+               goto error1;
+       }
+
+       error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL,
+           ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+       if (error != 0) {
+               printf("%s: bus_dmamap_load error=%d\n", __func__, error);
+               goto error1;
+       }
+
+       bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
+           BUS_DMASYNC_PREREAD);
+
+       ccb->ccb_cookie = xs;
+       ccb->ccb_done = ufshci_scsi_io_done;
+
+       /* Response length should be UPIU_SCSI_RSP_INQUIRY_SIZE. */
+       ccb->ccb_slot = ufshci_utr_cmd_inquiry(sc, ccb, xs->datalen, xs->flags);
+       if (ccb->ccb_slot == -1)
+               goto error2;
+
+       if (ISSET(xs->flags, SCSI_POLL)) {
+               if (ufshci_doorbell_poll(sc, ccb->ccb_slot) == 0) {
+                       ccb->ccb_done(sc, ccb);
+                       return;
+               }
+               goto error2;
+        }
+
+       return;
+
+error2:
+       bus_dmamap_unload(sc->sc_dmat, dmap);
+       ccb->ccb_cookie = NULL;
+       ccb->ccb_slot = -1;
+       ccb->ccb_done = NULL;
+error1:
+       xs->error = XS_DRIVER_STUFFUP;
+       scsi_done(xs);
+}
+
+void
+ufshci_scsi_capacity16(struct scsi_xfer *xs)
+{
+       struct scsi_link *link = xs->sc_link;
+       struct ufshci_softc *sc = link->bus->sb_adapter_softc;
+       struct ufshci_ccb *ccb = xs->io;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+       int error;
+
+       DPRINTF("%s: CAPACITY16 (%s)\n",
+           __func__, ISSET(xs->flags, SCSI_POLL) ? "poll"  : "no poll");
+
+       if (xs->datalen > UPIU_SCSI_RSP_CAPACITY16_SIZE) {
+               DPRINTF("%s: request len too large\n", __func__);
+               goto error1;
+       }
+
+       error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL,
+           ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+       if (error != 0) {
+               printf("%s: bus_dmamap_load error=%d\n", __func__, error);
+               goto error1;
+       }
+
+       bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
+           BUS_DMASYNC_PREREAD);
+
+       ccb->ccb_cookie = xs;
+       ccb->ccb_done = ufshci_scsi_io_done;
+
+       /* Response length should be UPIU_SCSI_RSP_CAPACITY16_SIZE. */
+       ccb->ccb_slot = ufshci_utr_cmd_capacity16(sc, ccb, xs->datalen,
+           xs->flags);
+       if (ccb->ccb_slot == -1)
+               goto error2;
+
+       if (ISSET(xs->flags, SCSI_POLL)) {
+               if (ufshci_doorbell_poll(sc, ccb->ccb_slot) == 0) {
+                       ccb->ccb_done(sc, ccb);
+                       return;
+               }
+               goto error2;
+       }
+
+       return;
+
+error2:
+       bus_dmamap_unload(sc->sc_dmat, dmap);
+       ccb->ccb_cookie = NULL;
+       ccb->ccb_slot = -1;
+       ccb->ccb_done = NULL;
+error1:
+       xs->error = XS_DRIVER_STUFFUP;
+       scsi_done(xs);
+}
+
+void
+ufshci_scsi_capacity(struct scsi_xfer *xs)
+{
+       struct scsi_link *link = xs->sc_link;
+       struct ufshci_softc *sc = link->bus->sb_adapter_softc;
+       struct ufshci_ccb *ccb = xs->io;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+       int error;
+
+       DPRINTF("%s: CAPACITY (%s)\n",
+           __func__, ISSET(xs->flags, SCSI_POLL) ? "poll"  : "no poll");
+
+       if (xs->datalen > UPIU_SCSI_RSP_CAPACITY_SIZE) {
+               DPRINTF("%s: request len too large\n", __func__);
+               goto error1;
+       }
+
+       error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL,
+           ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+       if (error != 0) {
+               printf("%s: bus_dmamap_load error=%d\n", __func__, error);
+               goto error1;
+        }
+
+       bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
+           BUS_DMASYNC_PREREAD);
+
+       ccb->ccb_cookie = xs;
+       ccb->ccb_done = ufshci_scsi_io_done;
+
+       /* Response length should be UPIU_SCSI_RSP_CAPACITY_SIZE */
+       ccb->ccb_slot = ufshci_utr_cmd_capacity(sc, ccb, xs->datalen,
+           xs->flags);
+       if (ccb->ccb_slot == -1)
+               goto error2;
+
+       if (ISSET(xs->flags, SCSI_POLL)) {
+               if (ufshci_doorbell_poll(sc, ccb->ccb_slot) == 0) {
+                       ccb->ccb_done(sc, ccb);
+                       return;
+               }
+               goto error2;
+       }
+       return;
+
+error2:
+       bus_dmamap_unload(sc->sc_dmat, dmap);
+       ccb->ccb_cookie = NULL;
+       ccb->ccb_slot = -1;
+       ccb->ccb_done = NULL;
+error1:
+       xs->error = XS_DRIVER_STUFFUP;
+       scsi_done(xs);
+}
+
+void
+ufshci_scsi_sync(struct scsi_xfer *xs)
+{
+       struct scsi_link *link = xs->sc_link;
+       struct ufshci_softc *sc = link->bus->sb_adapter_softc;
+       struct ufshci_ccb *ccb = xs->io;
+       uint64_t lba;
+       uint32_t blocks;
+
+       /* lba = 0, blocks = 0: Synchronize all logical blocks. */
+       lba = 0; blocks = 0;
+
+       DPRINTF("%s: SYNC, lba=%llu, blocks=%u (%s)\n",
+           __func__, lba, blocks,
+           ISSET(xs->flags, SCSI_POLL) ? "poll"  : "no poll");
+
+       ccb->ccb_cookie = xs;
+       ccb->ccb_done = ufshci_scsi_done;
+
+       ccb->ccb_slot = ufshci_utr_cmd_sync(sc, ccb, xs->flags, (uint32_t)lba,
+           (uint16_t)blocks);
+       if (ccb->ccb_slot == -1)
+               goto error;
+
+       if (ISSET(xs->flags, SCSI_POLL)) {
+               if (ufshci_doorbell_poll(sc, ccb->ccb_slot) == 0) {
+                       ccb->ccb_done(sc, ccb);
+                       return;
+               }
+               goto error;
+       }
+
+       return;
+
+error:
+        ccb->ccb_cookie = NULL;
+        ccb->ccb_slot = -1;
+        ccb->ccb_done = NULL;
+
+       xs->error = XS_DRIVER_STUFFUP;
+       scsi_done(xs);
+}
+
+void
+ufshci_scsi_io(struct scsi_xfer *xs, int dir)
+{
+       struct scsi_link *link = xs->sc_link;
+       struct ufshci_softc *sc = link->bus->sb_adapter_softc;
+       struct ufshci_ccb *ccb = xs->io;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+       uint64_t lba;
+       uint32_t blocks;
+       int error;
+
+       if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) != dir)
+               goto error1;
+
+       scsi_cmd_rw_decode(&xs->cmd, &lba, &blocks);
+
+       DPRINTF("%s: %s, lba=%llu, blocks=%u, datalen=%d (%s)\n",
+           __func__,
+           ISSET(xs->flags, SCSI_DATA_IN) ? "READ" : "WRITE",
+           lba, blocks, xs->datalen,
+           ISSET(xs->flags, SCSI_POLL) ? "poll"  : "no poll");
+
+       error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL,
+           ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+       if (error != 0) {
+               printf("%s: bus_dmamap_load error=%d\n", __func__, error);
+               goto error1;
+       }
+
+       bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
+           ISSET(xs->flags, SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
+           BUS_DMASYNC_PREWRITE);
+
+       ccb->ccb_cookie = xs;
+       ccb->ccb_done = ufshci_scsi_io_done;
+
+       if (dir == SCSI_DATA_IN) {
+               ccb->ccb_slot = ufshci_utr_cmd_read(sc, ccb, xs->datalen,
+                   xs->flags, (uint32_t)lba, (uint16_t)blocks);
+       } else {
+               ccb->ccb_slot = ufshci_utr_cmd_write(sc, ccb, xs->datalen,
+                   xs->flags, (uint32_t)lba, (uint16_t)blocks);
+       }
+
+       if (ccb->ccb_slot == -1)
+               goto error2;
+
+       if (ISSET(xs->flags, SCSI_POLL)) {
+               if (ufshci_doorbell_poll(sc, ccb->ccb_slot) == 0) {
+                       ccb->ccb_done(sc, ccb);
+                       return;
+               }
+               goto error2;
+       }
+
+       return;
+
+error2:
+       bus_dmamap_unload(sc->sc_dmat, dmap);
+       ccb->ccb_cookie = NULL;
+       ccb->ccb_slot = -1;
+       ccb->ccb_done = NULL;
+error1:
+       xs->error = XS_DRIVER_STUFFUP;
+       scsi_done(xs);
+}
+
+void
+ufshci_scsi_io_done(struct ufshci_softc *sc, struct ufshci_ccb *ccb)
+{
+       struct scsi_xfer *xs = ccb->ccb_cookie;
+       bus_dmamap_t dmap = ccb->ccb_dmamap;
+#if 0
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       int slot = ccb->ccb_slot;
+#endif
+       bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
+           ISSET(xs->flags, SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
+           BUS_DMASYNC_POSTWRITE);
+#if 0
+       ufshci_hexdump(xs->data, xs->datalen, "xs->data", 1);
+
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+
+       printf("ucd rsp tc=0x%02x\n", ucd->rsp.hdr.tc);
+       printf("ucd rsp flags=0x%02x\n", ucd->rsp.hdr.flags);
+       printf("ucd rsp lun=%d\n", ucd->rsp.hdr.lun);
+       printf("ucd rsp taskid=%d\n", ucd->rsp.hdr.taskid);
+       printf("ucd rsp cmd_set_type=0x%02x\n", ucd->rsp.hdr.cmd_set_type);
+       printf("ucd rsp query=0x%02x\n", ucd->rsp.hdr.query);
+       printf("ucd rsp response=0x%02x\n", ucd->rsp.hdr.response);
+       printf("ucd rsp status=0x%02x\n", ucd->rsp.hdr.status);
+       printf("ucd rsp ehs_len=%d\n", ucd->rsp.hdr.ehs_len);
+       printf("ucd rsp device_info=0x%02x\n", ucd->rsp.hdr.device_info);
+       printf("ucd rsp ds_len=%d\n", ucd->rsp.hdr.ds_len);
+       printf("ucd rsp rxl=%d\n", be32toh(ucd->rsp.residual_xfer_len));
+#endif
+       bus_dmamap_unload(sc->sc_dmat, dmap);
+
+       ccb->ccb_cookie = NULL;
+       ccb->ccb_slot = -1;
+       ccb->ccb_done = NULL;
+
+       xs->error = XS_NOERROR;
+       xs->status = SCSI_OK;
+       xs->resid = 0;
+       scsi_done(xs);
+}
+
+void
+ufshci_scsi_done(struct ufshci_softc *sc, struct ufshci_ccb *ccb)
+{
+       struct scsi_xfer *xs = ccb->ccb_cookie;
+#if 0
+       struct ufshci_utrd *utrd;
+       struct ufshci_ucd *ucd;
+       int slot = ccb->ccb_slot;
+
+       ufshci_hexdump(xs->data, xs->datalen, "xs->data", 1);
+
+       utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd) + (sizeof(*utrd) * slot);
+       ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot);
+
+       printf("ucd rsp tc=0x%02x\n", ucd->rsp.hdr.tc);
+       printf("ucd rsp flags=0x%02x\n", ucd->rsp.hdr.flags);
+       printf("ucd rsp lun=%d\n", ucd->rsp.hdr.lun);
+       printf("ucd rsp taskid=%d\n", ucd->rsp.hdr.taskid);
+       printf("ucd rsp cmd_set_type=0x%02x\n", ucd->rsp.hdr.cmd_set_type);
+       printf("ucd rsp query=0x%02x\n", ucd->rsp.hdr.query);
+       printf("ucd rsp response=0x%02x\n", ucd->rsp.hdr.response);
+       printf("ucd rsp status=0x%02x\n", ucd->rsp.hdr.status);
+       printf("ucd rsp ehs_len=%d\n", ucd->rsp.hdr.ehs_len);
+       printf("ucd rsp device_info=0x%02x\n", ucd->rsp.hdr.device_info);
+       printf("ucd rsp ds_len=%d\n", ucd->rsp.hdr.ds_len);
+       printf("ucd rsp rxl=%d\n", be32toh(ucd->rsp.residual_xfer_len));
+#endif
+       ccb->ccb_cookie = NULL;
+       ccb->ccb_slot = -1;
+       ccb->ccb_done = NULL;
+
+       xs->error = XS_NOERROR;
+       xs->status = SCSI_OK;
+       xs->resid = 0;
+       scsi_done(xs);
+}
diff --git a/sys/dev/ic/ufshcireg.h b/sys/dev/ic/ufshcireg.h
new file mode 100644 (file)
index 0000000..f04ffbb
--- /dev/null
@@ -0,0 +1,365 @@
+/*     $OpenBSD: ufshcireg.h,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2022 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/*
+ * Generic parameters.
+ */
+#define UFSHCI_UCD_PRDT_MAX_SEGS       64
+#define UFSHCI_UCD_PRDT_MAX_XFER       (UFSHCI_UCD_PRDT_MAX_SEGS * PAGE_SIZE)
+#define UFSHCI_INTR_AGGR_COUNT         6
+#define UFSHCI_INTR_AGGR_TIMEOUT       0x64 /* 4ms */
+#define UFSHCI_MAX_UNITS               32
+
+/*
+ * Controller Capabilties Registers
+ */
+
+/* Controller Capabilities */
+#define UFSHCI_REG_CAP                 0x00
+#define  UFSHCI_REG_CAP_CS             (1 << 28) /* RO */
+#define  UFSHCI_REG_CAP_UICDMETMS      (1 << 26) /* RO */
+#define  UFSHCI_REG_CAP_OODDS          (1 << 25) /* RO */
+#define  UFSHCI_REG_CAP_64AS           (1 << 24) /* RO */
+#define  UFSHCI_REG_AUTOH8             (1 << 23) /* RO */
+#define  UFSHCI_REG_CAP_NUTMRS(x)      ((x >> 16) & 0x00000007) /* RO */
+#define  UFSHCI_REG_CAP_RTT(x)         ((x >>  8) & 0x000000ff) /* RO */
+#define  UFSHCI_REG_CAP_NUTRS(x)       ((x >>  0) & 0x0000001f) /* RO */
+/* UFS Version in BCD format */
+#define UFSHCI_REG_VER                 0x08
+#define  UFSHCI_REG_VER_MAJOR(x)       ((x >> 8) & 0x0000000f) /* RO */
+#define  UFSHCI_REG_VER_MINOR(x)       ((x >> 4) & 0x0000000f) /* RO */
+#define  UFSHCI_REG_VER_SUFFIX(x)      ((x >> 0) & 0x0000000f) /* RO */
+/* Product ID */
+#define UFSHCI_REG_HCPID               0x10
+/* Manufacturer ID */
+#define UFSHCI_REG_HCMID               0x14
+#define  UFSHCI_REG_HCMID_BI(x)                ((x >> 8) & 0x000000ff) /* RO */
+#define  UFSHCI_REG_HCMID_MIC(x)       ((x >> 0) & 0x000000ff) /* RO */
+/* Auto-Hibernate Idle Timer */
+#define UFSHCI_REG_AHIT                        0x18
+
+/*
+ * Operation and Runtime Registers
+ */
+
+/* Interrupt Status */
+#define UFSHCI_REG_IS                  0x20
+#define  UFSHCI_REG_IS_CEFES           (1 << 18) /* RWC */
+#define  UFSHCI_REG_IS_SBFES           (1 << 17) /* RWC */
+#define  UFSHCI_REG_IS_HCFES           (1 << 16) /* RWC */
+#define  UFSHCI_REG_IS_UTPES           (1 << 12) /* RWC */
+#define  UFSHCI_REG_IS_DFES            (1 << 11) /* RWC */
+#define  UFSHCI_REG_IS_UCCS            (1 << 10) /* RWC */
+#define  UFSHCI_REG_IS_UTMRCS          (1 <<  9) /* RWC */
+#define  UFSHCI_REG_IS_ULSS            (1 <<  8) /* RWC */
+#define  UFSHCI_REG_IS_ULLS            (1 <<  7) /* RWC */
+#define  UFSHCI_REG_IS_UHES            (1 <<  6) /* RWC */
+#define  UFSHCI_REG_IS_UHXS            (1 <<  5) /* RWC */
+#define  UFSHCI_REG_IS_UPMS            (1 <<  4) /* RWC */
+#define  UFSHCI_REG_IS_UTMS            (1 <<  3) /* RWC */
+#define  UFSHCI_REG_IS_UE              (1 <<  2) /* RWC */
+#define  UFSHCI_REG_IS_UDEPRI          (1 <<  1) /* RWC */
+#define  UFSHCI_REG_IS_UTRCS           (1 <<  0) /* RWC */
+/* Interrupt Enable */
+#define UFSHCI_REG_IE                  0x24
+#define  UFSHCI_REG_IE_CEFFE           (1 << 18) /* RW */
+#define  UFSHCI_REG_IE_SBFEE           (1 << 17) /* RW */
+#define  UFSHCI_REG_IE_HCFEE           (1 << 16) /* RW */
+#define  UFSHCI_REG_IE_UTPEE           (1 << 12) /* RW */
+#define  UFSHCI_REG_IE_DFEE            (1 << 11) /* RW */
+#define  UFSHCI_REG_IE_UCCE            (1 << 10) /* RW */
+#define  UFSHCI_REG_IE_UTMRCE          (1 <<  9) /* RW */
+#define  UFSHCI_REG_IE_ULSSE           (1 <<  8) /* RW */
+#define  UFSHCI_REG_IE_ULLSE           (1 <<  7) /* RW */
+#define  UFSHCI_REG_IE_UHESE           (1 <<  6) /* RW */
+#define  UFSHCI_REG_IE_UHXSE           (1 <<  5) /* RW */
+#define  UFSHCI_REG_IE_UPMSE           (1 <<  4) /* RW */
+#define  UFSHCI_REG_IE_UTMSE           (1 <<  3) /* RW */
+#define  UFSHCI_REG_IE_UEE             (1 <<  2) /* RW */
+#define  UFSHCI_REG_IE_UDEPRIE         (1 <<  1) /* RW */
+#define  UFSHCI_REG_IE_UTRCE           (1 <<  0) /* RW */
+/* Host Controller Status */
+#define UFSHCI_REG_HCS                 0x30
+#define  UFSHCI_REG_HCS_TLUNUTPE(x)    ((x << 24) & 0xff000000) /* RO */
+#define  UFSHCI_REG_HCS_TTAGUTPE(x)    ((x << 16) & 0x00ff0000) /* RO */
+#define  UFSHCI_REG_HCS_UTPEC(x)       ((x << 12) & 0x0000f000) /* RO */
+#define  UFSHCI_REG_HCS_UPMCRS(x)      ((x <<  8) & 0x00000700) /* RO */
+#define  UFSHCI_REG_HCS_UCRDY          (1 << 3) /* RO */
+#define  UFSHCI_REG_HCS_UTMRLRDY       (1 << 2) /* RO */
+#define  UFSHCI_REG_HCS_UTRLRDY                (1 << 1) /* RO */
+#define  UFSHCI_REG_HCS_DP             (1 << 0) /* RO */
+/* Host Controller Enable */
+#define UFSHCI_REG_HCE                 0x34
+#define  UFSHCI_REG_HCE_CGE            (1 << 1) /* RW */
+#define  UFSHCI_REG_HCE_HCE            (1 << 0) /* RW */
+/* Host UIC Error Code PHY Adapter Layer */
+#define UFSHCI_REG_UECPA               0x38
+/* Host UIC Error Code Data Link Layer */
+#define UFSHCI_REG_UECDL               0x3C
+/* Host UIC Error Code Network Layer */
+#define UFSHCI_REG_UECN                        0x40
+/* Host UIC Error Code Transport Layer */
+#define UFSHCI_REG_UECT                        0x44
+/* Host UIC Error Code */
+#define UFSHCI_REG_UECDME              0x48
+/* UTP Transfer Request Interrupt Aggregation Control Register */
+#define UFSHCI_REG_UTRIACR             0x4C
+#define  UFSHCI_REG_UTRIACR_IAEN       (1 << 31) /* RW */
+#define  UFSHCI_REG_UTRIACR_IAPWEN     (1 << 24) /* WO */
+#define  UFSHCI_REG_UTRIACR_IASB       (1 << 20) /* RO */
+#define  UFSHCI_REG_UTRIACR_CTR                (1 << 16) /* WO */
+#define  UFSHCI_REG_UTRIACR_IACTH(x)   (x <<  8) /* RW, max. val = 31 */
+#define  UFSHCI_REG_UTRIACR_IATOVAL(x) (x <<  0) /* RW, 40us units (1=40us) */
+
+/*
+ * UTP Transfer Request List Registers
+ */
+
+/* Base Address */
+#define UFSHCI_REG_UTRLBA                      0x50 /* RW */
+/* Base Address Upper 32-bits */
+#define UFSHCI_REG_UTRLBAU                     0x54 /* RW */
+/* Door Bell Register */
+#define UFSHCI_REG_UTRLDBR                     0x58 /* RWS */
+/* Clear Register */
+#define UFSHCI_REG_UTRLCLR                     0x5C /* WO */
+/* Run-Stop Register */
+#define UFSHCI_REG_UTRLRSR                     0x60 /* RW */
+#define  UFSHCI_REG_UTRLRSR_STOP               0x00
+#define  UFSHCI_REG_UTRLRSR_START              0x01
+/* Completion Notification Register */
+#define UFSHCI_REG_UTRLCNR                     0x64 /* RWC */
+
+/*
+ * UTP Task Management Request List Registers
+ */
+
+/* Base Address */
+#define UFSHCI_REG_UTMRLBA                     0x70 /* RW */
+/* Base Address Upper 32-bits */
+#define UFSHCI_REG_UTMRLBAU                    0x74 /* RW */
+/* Door Bell Register */
+#define UFSHCI_REG_UTMRLDBR                    0x78 /* RWS */
+/* Clear Register */
+#define UFSHCI_REG_UTMRLCLR                    0x7C /* WO */
+/* Run-Stop Register */
+#define UFSHCI_REG_UTMRLRSR                    0x80 /* RW */
+#define  UFSHCI_REG_UTMRLRSR_STOP              0x00
+#define  UFSHCI_REG_UTMRLRSR_START             0x01
+
+/*
+ * UIC Command Registers
+ */
+
+/* UIC Command Register */
+#define UFSHCI_REG_UICCMD                              0x90
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_GET               0x01 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_SET               0x02 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_PEER_GET          0x03 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_PEER_SET          0x04 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_POWERON           0x10 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_POWEROFF          0x11 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_ENABLE            0x12 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_RESET             0x14 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_ENDPOINTRESET     0x15 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_LINKSTARTUP       0x16 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_HIBERNATE_ENTER   0x17 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_HIBERNATE_EXIT    0x18 /* RW */
+#define  UFSHCI_REG_UICCMD_CMDOP_DME_TEST_MODE         0x1A /* RW */
+/* UIC Command Argument 1 */
+#define UFSHCI_REG_UICCMDARG1                          0x94
+/* UIC Command Argument 2 */
+#define UFSHCI_REG_UICCMDARG2                          0x98
+/* UIC Command Argument 3 */
+#define UFSHCI_REG_UICCMDARG3                          0x9C
+
+/*
+ * Vendor Specific Registers (0xC0 - 0xFF)
+ */
+
+/*
+ * UTP Transfer Request Descriptor Structure
+ */
+
+/* Command Type (CT) */
+#define UFSHCI_UTRD_DW0_CT_UFS         (1 << 28) /* UFS Storage */
+/* Data Direction (DD) */
+#define UFSHCI_UTRD_DW0_DD_NO          (0 << 25) /* No transfer */
+#define UFSHCI_UTRD_DW0_DD_I2T         (1 << 25) /* From Initiator to Target */
+#define UFSHCI_UTRD_DW0_DD_T2I         (2 << 25) /* From Target to Initiator */
+/* Interrupt (I) */
+#define UFSHCI_UTRD_DW0_I_REG          (0 << 24) /* Regular Command */
+#define UFSHCI_UTRD_DW0_I_INT          (1 << 24) /* Interrupt Command */
+/* Crypto Enable (CE) */
+#define UFSHCI_UTRD_DW0_CE_DISABLE     (0 << 23) /* Disable Crypto */
+#define UFSHCI_UTRD_DW0_CE_ENABLE      (1 << 23) /* Enable Crypto */
+/* Crypto Configuration Index (CCI) */
+#define UFSHCI_UTRD_DW0_CCI(x)         (x & 0x000000ff)
+
+/* Data Unit Number Upper 32-bits (DUNL) */
+#define UFSHCI_UTRD_DW1_DUNL(x)                (x << 0)
+
+/* Overall Command Status (OCS) */
+#define UFSHCI_UTRD_DW2_OCS(x)         (x & 0x000000ff)
+#define UFSHCI_UTRD_DW2_OCS_SUCCESS    0x00 /* Success */
+#define UFSHCI_UTRD_DW2_OCS_ICTA       0x01 /* Invalid Command Table Attr. */
+#define UFSHCI_UTRD_DW2_OCS_IPA                0x02 /* Invalid PRDT Attr. */
+#define UFSHCI_UTRD_DW2_OCS_MDBS       0x03 /* Missmatch Data Buffer Size */
+#define UFSHCI_UTRD_DW2_OCS_MRUS       0x04 /* Missmatch Response UPIU Size */
+#define UFSHCI_UTRD_DW2_OCS_CF         0x05 /* Communcation Failure */
+#define UFSHCI_UTRD_DW2_OCS_ABRT       0x06 /* Aborted */
+#define UFSHCI_UTRD_DW2_OCS_FE         0x07 /* Fatal Error */
+#define UFSHCI_UTRD_DW2_OCS_DFE                0x08 /* Device Fatal Error */
+#define UFSHCI_UTRD_DW2_OCS_ICC                0x09 /* Invalid Crypto Configuration */
+#define UFSHCI_UTRD_DW2_OCS_GCE                0x0A /* General Crypto Error */
+#define UFSHCI_UTRD_DW2_OCS_IOV                0x0F /* Invalid OCS Value */
+
+/* Data Unit Number Upper 32-bits Upper 32-bits (DUNU) */
+#define UFSHCI_UTRD_DW3_DUNU(x)                (x << 0)
+
+/* UTP Command Descriptor Base Address (UCDBA) */
+#define UFSHCI_UTRD_DW4_UCDBA(x)       (x << 7)
+
+/* UTP Command Descriptor Base Address Upper 32-bits (UCDBAU) */
+#define UFSHCI_UTRD_DW5_UCDBAU(x)      (x << 0)
+
+/* Response UPIU Offset (RUO) */
+#define UFSHCI_UTRD_DW6_RUO(x)         (x << 16)
+/* Response UPIU Length (RUL) */
+#define UFSHCI_UTRD_DW6_RUL(x)         (x & 0x0000ffff)
+
+/* PRDT Offset (PRDTO) */
+#define UFSHCI_UTRD_DW7_PRDTO(x)       (x << 16)
+/* PRDT Length (PRDTL) */
+#define UFSHCI_UTRD_DW7_PRDTL(x)       (x & 0x0000ffff)
+
+struct ufshci_utrd {
+       uint32_t dw0; /* CT, DD, I, CE, CCI */
+       uint32_t dw1; /* Data Unit Number Lower 32-bits (DUNL) */
+       uint32_t dw2; /* OCS */
+       uint32_t dw3; /* Data Unit Number Upper 32-bits (DUNU) */
+       uint32_t dw4; /* UTP Cmd. Desc. Base Addr. Lower 32-bits (UCDBA) */
+       uint32_t dw5; /* UTP Cmd. Desc. Base Addr. Upper 32-bits (UCDBAU) */
+       uint32_t dw6; /* RUO, RUL */
+       uint32_t dw7; /* PRDTO, PRDTL */
+};
+
+/*
+ * UTP Command Descriptor, PRDT (Physical Region Description Table) Structure
+ */
+
+/* Data Base Address (DBA) */
+#define UFSHCI_UCD_DW0_DBA(x)  (x & 0xfffffffc)
+
+/* Data Byte Count (DBC) */
+#define UFSHCI_UCD_DW3_DBC(x)  (x & 0x0003ffff)
+
+struct ufshci_ucd_prdt {
+       uint32_t dw0; /* Data base Address Lower 32-bits (DBA) */
+       uint32_t dw1; /* Data base Address Upper 32-bits (DBAU) */
+       uint32_t dw2; /* Reserved */
+       uint32_t dw3; /* Data Byte Count (DBC) */
+};
+
+/*
+ * UTP Task Management Request Descriptor Structure
+ */
+
+/* Interrupt (I) */
+#define UFSHCI_UTMRD_DW0_I_DISABLE     (0 << 24)
+#define UFSHCI_UTMRD_DW0_I_ENABLE      (1 << 24)
+
+/* Overall Command Status (OCS) */
+#define UFSHCI_UTMRD_DW2_OCS(x)                (x & 0x000000ff)
+#define UFSHCI_UTMRD_DW2_OCS_SUCCESS   0x00 /* Success */
+#define UFSHCI_UTMRD_DW2_OCS_ICTA      0x01 /* Invalid Command Table Attr. */
+#define UFSHCI_UTMRD_DW2_OCS_IPA       0x02 /* Invalid PRDT Attr. */
+#define UFSHCI_UTMRD_DW2_OCS_MDBS      0x03 /* Missmatch Data Buffer Size */
+#define UFSHCI_UTMRD_DW2_OCS_MRUS      0x04 /* Missmatch Response UPIU Size */
+#define UFSHCI_UTMRD_DW2_OCS_CF                0x05 /* Communcation Failure */
+#define UFSHCI_UTMRD_DW2_OCS_ABRT      0x06 /* Aborted */
+#define UFSHCI_UTMRD_DW2_OCS_FE                0x07 /* Fatal Error */
+#define UFSHCI_UTMRD_DW2_OCS_DFE       0x08 /* Device Fatal Error */
+#define UFSHCI_UTMRD_DW2_OCS_ICC       0x09 /* Invalid Crypto Configuration */
+#define UFSHCI_UTMRD_DW2_OCS_GCE       0x0A /* General Crypto Error */
+#define UFSHCI_UTMRD_DW2_OCS_IOV       0x0F /* Invalid OCS Value */
+
+struct ufshci_utmrd {
+       uint32_t dw0; /* I */
+       uint32_t dw1; /* Reserved */
+       uint32_t dw2; /* OCS */
+       uint32_t dw3; /* Reserved */
+       uint8_t dw4_w11[32]; /* Task Management Request UPIU */
+       uint8_t dw12_dw19[32]; /* Task Management Response UPIU */
+};
+
+/*
+ * ****************************************************************************
+ * Universal Flash Storage (UFS) Version 2.1 Specs from JESD220C
+ * ****************************************************************************
+ */
+
+/* UPIU structures are in Big Endian! */
+
+#define UPIU_TC_I2T_NOP_OUT            0x00
+#define UPIU_TC_I2T_COMMAND            0x01
+#define UPIU_TC_I2T_DATA_OUT           0x02
+#define UPIU_TC_I2T_TMR                        0x04
+#define UPIU_TC_I2T_QUERY_REQUEST      0x16
+#define UPIU_TC_T2I_NOP_IN             0x20
+#define UPIU_TC_T2I_RESPONSE           0x21
+#define UPIU_TC_T2I_DATA_IN            0x22
+#define UPIU_TC_T2I_TMR                        0x24
+#define UPIU_TC_T2I_QUERY_RESPONSE     0x36
+#define UPIU_TC_T2I_REJECT             0x3f
+
+#define UPIU_SCSI_RSP_INQUIRY_SIZE     36
+#define UPIU_SCSI_RSP_CAPACITY16_SIZE  32
+#define UPIU_SCSI_RSP_CAPACITY_SIZE    8
+
+struct upiu_hdr {
+       uint8_t tc;                     /* Transaction Code */
+       uint8_t flags;
+       uint8_t lun;
+       uint8_t taskid;
+       uint8_t cmd_set_type;
+       uint8_t query;
+       uint8_t response;
+       uint8_t status;
+       uint8_t ehs_len;
+       uint8_t device_info;
+       uint16_t ds_len;                /* Data Segment Length */
+};
+
+struct upiu_command {
+       struct upiu_hdr hdr;
+       uint32_t expected_xfer_len;
+       uint8_t cdb[16];
+};
+
+struct upiu_response {
+       struct upiu_hdr hdr;
+       uint32_t residual_xfer_len;
+       uint8_t cdb[16];
+};
+
+struct ufshci_ucd {
+       struct upiu_command cmd;
+       struct upiu_response rsp;
+       struct ufshci_ucd_prdt prdt[UFSHCI_UCD_PRDT_MAX_SEGS];
+};
diff --git a/sys/dev/ic/ufshcivar.h b/sys/dev/ic/ufshcivar.h
new file mode 100644 (file)
index 0000000..57573ec
--- /dev/null
@@ -0,0 +1,80 @@
+/*     $OpenBSD: ufshcivar.h,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */
+
+/*
+ * Copyright (c) 2022 Marcus Glocker <mglocker@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define UFSHCI_READ_4(sc, x) \
+    bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (x))
+#define UFSHCI_WRITE_4(sc, x, y) \
+    bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (x), (y))
+
+#define UFSHCI_DMA_MAP(_udm)   ((_udm)->udm_map)
+#define UFSHCI_DMA_LEN(_udm)   ((_udm)->udm_map->dm_segs[0].ds_len)
+#define UFSHCI_DMA_DVA(_udm)   ((uint64_t)(_udm)->udm_map->dm_segs[0].ds_addr)
+#define UFSHCI_DMA_KVA(_udm)   ((void *)(_udm)->udm_kva)
+struct ufshci_dmamem {
+       bus_dmamap_t            udm_map;
+       bus_dma_segment_t       udm_seg;
+       size_t                  udm_size;
+       caddr_t                 udm_kva;
+};
+
+struct ufshci_softc;
+
+/* SCSI */
+struct ufshci_ccb {
+       SIMPLEQ_ENTRY(ufshci_ccb)        ccb_entry;
+       bus_dmamap_t                     ccb_dmamap;
+       void                            *ccb_cookie;
+       int                              ccb_slot;
+       void                             (*ccb_done)(struct ufshci_softc *,
+                                            struct ufshci_ccb *);
+};
+SIMPLEQ_HEAD(ufshci_ccb_list, ufshci_ccb);
+
+struct ufshci_softc {
+       struct device            sc_dev;
+
+       bus_space_tag_t          sc_iot;
+       bus_space_handle_t       sc_ioh;
+       bus_size_t               sc_ios;
+       bus_dma_tag_t            sc_dmat;
+
+       uint8_t                  sc_intraggr_enabled;
+
+       uint32_t                 sc_ver;
+       uint32_t                 sc_cap;
+       uint32_t                 sc_hcpid;
+       uint32_t                 sc_hcmid;
+       uint8_t                  sc_nutmrs;
+       uint8_t                  sc_rtt;
+       uint8_t                  sc_nutrs;
+       uint8_t                  sc_taskid;
+
+       struct ufshci_dmamem    *sc_dmamem_utmrd;
+       struct ufshci_dmamem    *sc_dmamem_utrd;
+       struct ufshci_dmamem    *sc_dmamem_ucd;
+
+       /* SCSI */
+       struct scsi_iopool       sc_iopool;
+       struct mutex             sc_ccb_mtx;
+       struct ufshci_ccb_list   sc_ccb_list;
+       struct ufshci_ccb       *sc_ccbs;
+};
+
+int    ufshci_intr(void *);
+void   ufshci_attach_hook(struct device *);    /* XXX: Only for testing */
+int    ufshci_attach(struct ufshci_softc *);