From 2095c73744945b88b7836cfd196678a60cff8541 Mon Sep 17 00:00:00 2001 From: mglocker Date: Sat, 4 Feb 2023 23:11:59 +0000 Subject: [PATCH] Initial driver for Universal Flash Storage (UFS) Host Controllers. ok kettenis@ --- share/man/man4/Makefile | 4 +- share/man/man4/ufshci.4 | 47 + sys/arch/arm64/conf/GENERIC | 3 +- sys/conf/files | 7 +- sys/dev/acpi/files.acpi | 6 +- sys/dev/acpi/ufshci_acpi.c | 113 ++ sys/dev/ic/ufshci.c | 2001 +++++++++++++++++++++++++++++++++++ sys/dev/ic/ufshcireg.h | 365 +++++++ sys/dev/ic/ufshcivar.h | 80 ++ 9 files changed, 2621 insertions(+), 5 deletions(-) create mode 100644 share/man/man4/ufshci.4 create mode 100644 sys/dev/acpi/ufshci_acpi.c create mode 100644 sys/dev/ic/ufshci.c create mode 100644 sys/dev/ic/ufshcireg.h create mode 100644 sys/dev/ic/ufshcivar.h diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 2dad0898cf8..7bd9d9345ac 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -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 index 00000000000..12f3fa0b822 --- /dev/null +++ b/share/man/man4/ufshci.4 @@ -0,0 +1,47 @@ +.\" $OpenBSD: ufshci.4,v 1.1 2023/02/04 23:11:59 mglocker Exp $ +.\" +.\" Copyright (c) 2023 Marcus Glocker +.\" +.\" 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 . diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index e18d6c93584..3d5e9548a72 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -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? diff --git a/sys/conf/files b/sys/conf/files index f4fabbed9bb..92eef69d1a9 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -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 diff --git a/sys/dev/acpi/files.acpi b/sys/dev/acpi/files.acpi index cf0a4e83eda..9b7bb767202 100644 --- a/sys/dev/acpi/files.acpi +++ b/sys/dev/acpi/files.acpi @@ -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 index 00000000000..27b8b97b629 --- /dev/null +++ b/sys/dev/acpi/ufshci_acpi.c @@ -0,0 +1,113 @@ +/* $OpenBSD: ufshci_acpi.c,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */ +/* + * Copyright (c) 2022 Marcus Glocker + * + * 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 +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +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 index 00000000000..fab0ac042b3 --- /dev/null +++ b/sys/dev/ic/ufshci.c @@ -0,0 +1,2001 @@ +/* $OpenBSD: ufshci.c,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */ + +/* + * Copyright (c) 2022 Marcus Glocker + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +//#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 index 00000000000..f04ffbbd1dc --- /dev/null +++ b/sys/dev/ic/ufshcireg.h @@ -0,0 +1,365 @@ +/* $OpenBSD: ufshcireg.h,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */ + +/* + * Copyright (c) 2022 Marcus Glocker + * + * 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 index 00000000000..57573ecfc87 --- /dev/null +++ b/sys/dev/ic/ufshcivar.h @@ -0,0 +1,80 @@ +/* $OpenBSD: ufshcivar.h,v 1.1 2023/02/04 23:11:59 mglocker Exp $ */ + +/* + * Copyright (c) 2022 Marcus Glocker + * + * 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 *); -- 2.20.1