1*c45d22d4Smglocker /* $OpenBSD: ufshci.c,v 1.37 2024/06/14 20:52:07 mglocker Exp $ */ 22095c737Smglocker 32095c737Smglocker /* 42095c737Smglocker * Copyright (c) 2022 Marcus Glocker <mglocker@openbsd.org> 52095c737Smglocker * 62095c737Smglocker * Permission to use, copy, modify, and distribute this software for any 72095c737Smglocker * purpose with or without fee is hereby granted, provided that the above 82095c737Smglocker * copyright notice and this permission notice appear in all copies. 92095c737Smglocker * 102095c737Smglocker * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 112095c737Smglocker * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 122095c737Smglocker * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 132095c737Smglocker * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 142095c737Smglocker * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 152095c737Smglocker * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 162095c737Smglocker * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 172095c737Smglocker */ 182095c737Smglocker 192095c737Smglocker /* 202095c737Smglocker * Universal Flash Storage Host Controller Interface (UFSHCI) 2.1 driver based 212095c737Smglocker * on the JEDEC JESD223C.pdf and JESD220C-2_1.pdf specifications. 222095c737Smglocker */ 232095c737Smglocker 242095c737Smglocker #include <sys/param.h> 252095c737Smglocker #include <sys/systm.h> 262095c737Smglocker #include <sys/buf.h> 272095c737Smglocker #include <sys/kernel.h> 282095c737Smglocker #include <sys/malloc.h> 292095c737Smglocker #include <sys/device.h> 302095c737Smglocker #include <sys/queue.h> 312095c737Smglocker #include <sys/mutex.h> 322095c737Smglocker #include <sys/pool.h> 332095c737Smglocker 342095c737Smglocker #include <sys/atomic.h> 352095c737Smglocker 362095c737Smglocker #include <machine/bus.h> 372095c737Smglocker 382095c737Smglocker #include <scsi/scsi_all.h> 392095c737Smglocker #include <scsi/scsi_disk.h> 402095c737Smglocker #include <scsi/scsiconf.h> 412095c737Smglocker 422095c737Smglocker #include <dev/ic/ufshcivar.h> 432095c737Smglocker #include <dev/ic/ufshcireg.h> 442095c737Smglocker 4590de4d2dSmglocker #ifdef HIBERNATE 4690de4d2dSmglocker #include <uvm/uvm_extern.h> 4790de4d2dSmglocker #include <sys/hibernate.h> 4890de4d2dSmglocker #include <sys/disklabel.h> 4990de4d2dSmglocker #include <sys/disk.h> 5090de4d2dSmglocker #endif 5190de4d2dSmglocker 522095c737Smglocker #ifdef UFSHCI_DEBUG 532095c737Smglocker int ufshci_dbglvl = 1; 546920fd9bSmglocker #define DPRINTF(l, x...) do { if ((l) <= ufshci_dbglvl) printf(x); } \ 556920fd9bSmglocker while (0) 562095c737Smglocker #else 576920fd9bSmglocker #define DPRINTF(l, x...) 582095c737Smglocker #endif 592095c737Smglocker 602095c737Smglocker struct cfdriver ufshci_cd = { 612095c737Smglocker NULL, "ufshci", DV_DULL 622095c737Smglocker }; 632095c737Smglocker 642095c737Smglocker int ufshci_reset(struct ufshci_softc *); 6581f602bfSmglocker int ufshci_is_poll(struct ufshci_softc *, uint32_t); 662095c737Smglocker struct ufshci_dmamem *ufshci_dmamem_alloc(struct ufshci_softc *, size_t); 672095c737Smglocker void ufshci_dmamem_free(struct ufshci_softc *, 682095c737Smglocker struct ufshci_dmamem *); 6990de4d2dSmglocker int ufshci_alloc(struct ufshci_softc *); 702095c737Smglocker int ufshci_init(struct ufshci_softc *); 710fe5d515Smglocker void ufshci_disable(struct ufshci_softc *); 722095c737Smglocker int ufshci_doorbell_read(struct ufshci_softc *); 7312f70f1cSmglocker void ufshci_doorbell_write(struct ufshci_softc *, int); 7493bf5056Smglocker int ufshci_doorbell_poll(struct ufshci_softc *, int, 7593bf5056Smglocker uint32_t); 7612f70f1cSmglocker int ufshci_utr_cmd_nop(struct ufshci_softc *, 7712f70f1cSmglocker struct ufshci_ccb *, struct scsi_xfer *); 782095c737Smglocker int ufshci_utr_cmd_lun(struct ufshci_softc *, 7944c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 802095c737Smglocker int ufshci_utr_cmd_inquiry(struct ufshci_softc *, 8144c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 822095c737Smglocker int ufshci_utr_cmd_capacity16(struct ufshci_softc *, 8344c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 842095c737Smglocker int ufshci_utr_cmd_capacity(struct ufshci_softc *, 8544c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 86a2f0dcb2Smglocker int ufshci_utr_cmd_io(struct ufshci_softc *, 87a2f0dcb2Smglocker struct ufshci_ccb *, struct scsi_xfer *, int); 882095c737Smglocker int ufshci_utr_cmd_sync(struct ufshci_softc *, 8944c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *, 9044c52867Smglocker uint32_t, uint16_t); 910fe5d515Smglocker void ufshci_xfer_complete(struct ufshci_softc *); 922095c737Smglocker 932095c737Smglocker /* SCSI */ 942095c737Smglocker int ufshci_ccb_alloc(struct ufshci_softc *, int); 952095c737Smglocker void *ufshci_ccb_get(void *); 962095c737Smglocker void ufshci_ccb_put(void *, void *); 972095c737Smglocker void ufshci_ccb_free(struct ufshci_softc*, int); 982095c737Smglocker 992095c737Smglocker void ufshci_scsi_cmd(struct scsi_xfer *); 1002095c737Smglocker void ufshci_minphys(struct buf *, struct scsi_link *); 1012095c737Smglocker int ufshci_scsi_probe(struct scsi_link *); 1022095c737Smglocker void ufshci_scsi_free(struct scsi_link *); 1032095c737Smglocker 1042095c737Smglocker void ufshci_scsi_inquiry(struct scsi_xfer *); 1052095c737Smglocker void ufshci_scsi_capacity16(struct scsi_xfer *); 1062095c737Smglocker void ufshci_scsi_capacity(struct scsi_xfer *); 1072095c737Smglocker void ufshci_scsi_sync(struct scsi_xfer *); 1082095c737Smglocker void ufshci_scsi_io(struct scsi_xfer *, int); 1092095c737Smglocker void ufshci_scsi_io_done(struct ufshci_softc *, 1102095c737Smglocker struct ufshci_ccb *); 1112095c737Smglocker void ufshci_scsi_done(struct ufshci_softc *, 1122095c737Smglocker struct ufshci_ccb *); 1132095c737Smglocker 11490de4d2dSmglocker #if HIBERNATE 11590de4d2dSmglocker int ufshci_hibernate_io(dev_t, daddr_t, vaddr_t, size_t, 11690de4d2dSmglocker int, void *); 11790de4d2dSmglocker #endif 11890de4d2dSmglocker 1192095c737Smglocker const struct scsi_adapter ufshci_switch = { 1202095c737Smglocker ufshci_scsi_cmd, NULL, NULL, NULL, NULL 1212095c737Smglocker }; 1222095c737Smglocker 1232095c737Smglocker int 1242095c737Smglocker ufshci_intr(void *arg) 1252095c737Smglocker { 1262095c737Smglocker struct ufshci_softc *sc = arg; 1279251d8a3Smglocker uint32_t status, hcs; 1282095c737Smglocker int handled = 0; 1292095c737Smglocker 1302095c737Smglocker status = UFSHCI_READ_4(sc, UFSHCI_REG_IS); 1316920fd9bSmglocker DPRINTF(3, "%s: status=0x%08x\n", __func__, status); 1322095c737Smglocker 1332095c737Smglocker if (status == 0) 134*c45d22d4Smglocker return handled; 1352095c737Smglocker 1362095c737Smglocker if (status & UFSHCI_REG_IS_UCCS) { 1376920fd9bSmglocker DPRINTF(3, "%s: UCCS interrupt\n", __func__); 1382095c737Smglocker handled = 1; 1392095c737Smglocker } 1402095c737Smglocker if (status & UFSHCI_REG_IS_UTRCS) { 1416920fd9bSmglocker DPRINTF(3, "%s: UTRCS interrupt\n", __func__); 1422095c737Smglocker 14312f70f1cSmglocker ufshci_xfer_complete(sc); 14412f70f1cSmglocker 1452095c737Smglocker handled = 1; 1462095c737Smglocker } 1479251d8a3Smglocker /* If Auto-Hibernate raises an interrupt, it's to yield an error. */ 1489251d8a3Smglocker if (status & UFSHCI_REG_IS_UHES) { 1499251d8a3Smglocker hcs = UFSHCI_READ_4(sc, UFSHCI_REG_HCS); 1509251d8a3Smglocker printf("%s: Auto-Hibernate enter error UPMCRS=0x%x\n", 1519251d8a3Smglocker __func__, UFSHCI_REG_HCS_UPMCRS(hcs)); 152*c45d22d4Smglocker handled = 1; 1539251d8a3Smglocker } 1549251d8a3Smglocker if (status & UFSHCI_REG_IS_UHXS) { 1559251d8a3Smglocker hcs = UFSHCI_READ_4(sc, UFSHCI_REG_HCS); 1569251d8a3Smglocker printf("%s: Auto-Hibernate exit error UPMCRS=0x%x\n", 1579251d8a3Smglocker __func__, UFSHCI_REG_HCS_UPMCRS(hcs)); 158*c45d22d4Smglocker handled = 1; 1599251d8a3Smglocker } 1602095c737Smglocker 1612095c737Smglocker if (handled == 0) { 1622095c737Smglocker printf("%s: UNKNOWN interrupt, status=0x%08x\n", 1632095c737Smglocker sc->sc_dev.dv_xname, status); 1642095c737Smglocker } 1652095c737Smglocker 1662095c737Smglocker /* ACK interrupt */ 1672095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IS, status); 1682095c737Smglocker 169*c45d22d4Smglocker return handled; 1702095c737Smglocker } 1712095c737Smglocker 1722095c737Smglocker int 1732095c737Smglocker ufshci_attach(struct ufshci_softc *sc) 1742095c737Smglocker { 1752095c737Smglocker struct scsibus_attach_args saa; 1762095c737Smglocker 177e58b468bSmglocker mtx_init(&sc->sc_cmd_mtx, IPL_BIO); 1782095c737Smglocker mtx_init(&sc->sc_ccb_mtx, IPL_BIO); 1792095c737Smglocker SIMPLEQ_INIT(&sc->sc_ccb_list); 1802095c737Smglocker scsi_iopool_init(&sc->sc_iopool, sc, ufshci_ccb_get, ufshci_ccb_put); 1812095c737Smglocker 1820fe5d515Smglocker if (ufshci_reset(sc)) 1830fe5d515Smglocker return 1; 1842095c737Smglocker 1852095c737Smglocker sc->sc_ver = UFSHCI_READ_4(sc, UFSHCI_REG_VER); 1862095c737Smglocker printf(", UFSHCI %d.%d%d\n", 1872095c737Smglocker UFSHCI_REG_VER_MAJOR(sc->sc_ver), 1882095c737Smglocker UFSHCI_REG_VER_MINOR(sc->sc_ver), 1892095c737Smglocker UFSHCI_REG_VER_SUFFIX(sc->sc_ver)); 1902095c737Smglocker 1912095c737Smglocker sc->sc_cap = UFSHCI_READ_4(sc, UFSHCI_REG_CAP); 1922095c737Smglocker sc->sc_hcpid = UFSHCI_READ_4(sc, UFSHCI_REG_HCPID); 1932095c737Smglocker sc->sc_hcmid = UFSHCI_READ_4(sc, UFSHCI_REG_HCMID); 1942095c737Smglocker sc->sc_nutmrs = UFSHCI_REG_CAP_NUTMRS(sc->sc_cap) + 1; 1952095c737Smglocker sc->sc_rtt = UFSHCI_REG_CAP_RTT(sc->sc_cap) + 1; 1966920fd9bSmglocker sc->sc_nutrs = UFSHCI_REG_CAP_NUTRS(sc->sc_cap) + 1; 1976920fd9bSmglocker 1986920fd9bSmglocker DPRINTF(1, "Capabilities (0x%08x):\n", sc->sc_cap); 1996920fd9bSmglocker DPRINTF(1, "CS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_CS ? 1 : 0); 2006920fd9bSmglocker DPRINTF(1, "UICDMETMS=%d\n", 2016920fd9bSmglocker sc->sc_cap & UFSHCI_REG_CAP_UICDMETMS ? 1 : 0); 2026920fd9bSmglocker DPRINTF(1, "OODDS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_OODDS ? 1 : 0); 2036920fd9bSmglocker DPRINTF(1, "64AS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_64AS ? 1 : 0); 2046920fd9bSmglocker DPRINTF(1, "AUTOH8=%d\n", sc->sc_cap & UFSHCI_REG_AUTOH8 ? 1 : 0); 2056920fd9bSmglocker DPRINTF(1, "NUTMRS=%d\n", sc->sc_nutmrs); 2066920fd9bSmglocker DPRINTF(1, "RTT=%d\n", sc->sc_rtt); 2076920fd9bSmglocker DPRINTF(1, "NUTRS=%d\n", sc->sc_nutrs); 2086920fd9bSmglocker DPRINTF(1, "HCPID=0x%08x\n", sc->sc_hcpid); 2096920fd9bSmglocker DPRINTF(1, "HCMID (0x%08x):\n", sc->sc_hcmid); 2106920fd9bSmglocker DPRINTF(1, " BI=0x%04x\n", UFSHCI_REG_HCMID_BI(sc->sc_hcmid)); 2116920fd9bSmglocker DPRINTF(1, " MIC=0x%04x\n", UFSHCI_REG_HCMID_MIC(sc->sc_hcmid)); 2126920fd9bSmglocker 213d0fe8ebaSmglocker if (sc->sc_nutrs < UFSHCI_SLOTS_MIN || 214d0fe8ebaSmglocker sc->sc_nutrs > UFSHCI_SLOTS_MAX) { 215d0fe8ebaSmglocker printf("%s: Invalid NUTRS value %d (must be %d-%d)!\n", 216d0fe8ebaSmglocker sc->sc_dev.dv_xname, sc->sc_nutrs, 217d0fe8ebaSmglocker UFSHCI_SLOTS_MIN, UFSHCI_SLOTS_MAX); 2182095c737Smglocker return 1; 2192095c737Smglocker } 220d0fe8ebaSmglocker if (sc->sc_nutrs == UFSHCI_SLOTS_MAX) 221d0fe8ebaSmglocker sc->sc_iacth = UFSHCI_INTR_AGGR_COUNT_MAX; 222d0fe8ebaSmglocker else 223d0fe8ebaSmglocker sc->sc_iacth = sc->sc_nutrs; 2246920fd9bSmglocker DPRINTF(1, "Intr. aggr. counter threshold:\nIACTH=%d\n", sc->sc_iacth); 2252095c737Smglocker 226ceedf4ccSmglocker /* 227ceedf4ccSmglocker * XXX: 228ceedf4ccSmglocker * At the moment normal interrupts work better for us than interrupt 229ceedf4ccSmglocker * aggregation, because: 230ceedf4ccSmglocker * 231ceedf4ccSmglocker * 1. With interrupt aggregation enabled, the I/O performance 232ceedf4ccSmglocker * isn't better, but even slightly worse depending on the 233ceedf4ccSmglocker * UFS controller and architecture. 234ceedf4ccSmglocker * 2. With interrupt aggregation enabled we currently see 235ceedf4ccSmglocker * intermittent SCSI command stalling. Probably there is a 236ceedf4ccSmglocker * race condition where new SCSI commands are getting 237ceedf4ccSmglocker * scheduled, while we miss to reset the interrupt aggregation 238ceedf4ccSmglocker * counter/timer, which leaves us with no more interrupts 239ceedf4ccSmglocker * triggered. This needs to be fixed, but I couldn't figure 240ceedf4ccSmglocker * out yet how. 241ceedf4ccSmglocker */ 242ceedf4ccSmglocker #if 0 243ceedf4ccSmglocker sc->sc_flags |= UFSHCI_FLAGS_AGGR_INTR; /* Enable intr. aggregation */ 244ceedf4ccSmglocker #endif 24590de4d2dSmglocker /* Allocate the DMA buffers and initialize the controller. */ 2460fe5d515Smglocker if (ufshci_alloc(sc)) 2470fe5d515Smglocker return 1; 2480fe5d515Smglocker if (ufshci_init(sc)) 2490fe5d515Smglocker return 1; 2502095c737Smglocker 2512095c737Smglocker if (ufshci_ccb_alloc(sc, sc->sc_nutrs) != 0) { 2522095c737Smglocker printf("%s: %s: Can't allocate CCBs\n", 2532095c737Smglocker sc->sc_dev.dv_xname, __func__); 2542095c737Smglocker return 1; 2552095c737Smglocker } 2562095c737Smglocker 2579251d8a3Smglocker /* Enable Auto-Hibernate Idle Timer (AHIT) and set it to 150ms. */ 2589251d8a3Smglocker if (sc->sc_cap & UFSHCI_REG_AUTOH8) { 2599251d8a3Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_AHIT, 2609251d8a3Smglocker UFSHCI_REG_AHIT_TS(UFSHCI_REG_AHIT_TS_1MS) | 150); 2619251d8a3Smglocker } 2629251d8a3Smglocker 2632095c737Smglocker /* Attach to SCSI layer */ 2642095c737Smglocker saa.saa_adapter = &ufshci_switch; 2652095c737Smglocker saa.saa_adapter_softc = sc; 2662095c737Smglocker saa.saa_adapter_buswidth = 2; /* XXX: What's the right value? */ 2672095c737Smglocker saa.saa_luns = 1; /* XXX: Should we use ufshci_utr_cmd_lun() */ 2682095c737Smglocker saa.saa_adapter_target = 0; 2692095c737Smglocker saa.saa_openings = sc->sc_nutrs; 2702095c737Smglocker saa.saa_pool = &sc->sc_iopool; 2712095c737Smglocker saa.saa_quirks = saa.saa_flags = 0; 2722095c737Smglocker saa.saa_wwpn = saa.saa_wwnn = 0; 2732095c737Smglocker 2742095c737Smglocker config_found(&sc->sc_dev, &saa, scsiprint); 2752095c737Smglocker 2762095c737Smglocker return 0; 2772095c737Smglocker } 2782095c737Smglocker 2792095c737Smglocker int 2802095c737Smglocker ufshci_reset(struct ufshci_softc *sc) 2812095c737Smglocker { 2822095c737Smglocker int i; 2832095c737Smglocker int retry = 10; 2842095c737Smglocker uint32_t hce; 2852095c737Smglocker 2862095c737Smglocker /* 2872095c737Smglocker * 7.1.1 Host Controller Initialization: 2) 2882095c737Smglocker * Reset and enable host controller 2892095c737Smglocker */ 2902095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_HCE, UFSHCI_REG_HCE_HCE); 291dedce11aSmglocker 2922095c737Smglocker /* 7.1.1 Host Controller Initialization: 3) */ 2932095c737Smglocker for (i = 0; i < retry; i++) { 2942095c737Smglocker hce = UFSHCI_READ_4(sc, UFSHCI_REG_HCE); 2952095c737Smglocker if (hce == 1) 2962095c737Smglocker break; 2972095c737Smglocker delay(1); 2982095c737Smglocker } 2992095c737Smglocker if (i == retry) { 3002095c737Smglocker printf("%s: Enabling Host Controller failed!\n", 3012095c737Smglocker sc->sc_dev.dv_xname); 3020fe5d515Smglocker return 1; 3032095c737Smglocker } 3042095c737Smglocker 3056920fd9bSmglocker DPRINTF(2, "\n%s: Host Controller enabled (i=%d)\n", __func__, i); 3062095c737Smglocker 3072095c737Smglocker return 0; 3082095c737Smglocker } 3092095c737Smglocker 3102095c737Smglocker int 31181f602bfSmglocker ufshci_is_poll(struct ufshci_softc *sc, uint32_t type) 3122095c737Smglocker { 3132095c737Smglocker uint32_t status; 3142095c737Smglocker int i, retry = 25; 3152095c737Smglocker 3166920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 3172095c737Smglocker 3182095c737Smglocker for (i = 0; i < retry; i++) { 3192095c737Smglocker status = UFSHCI_READ_4(sc, UFSHCI_REG_IS); 32081f602bfSmglocker if (status & type) 3212095c737Smglocker break; 3222095c737Smglocker delay(10); 3232095c737Smglocker } 3242095c737Smglocker if (i == retry) { 3252095c737Smglocker printf("%s: %s: timeout\n", sc->sc_dev.dv_xname, __func__); 3260fe5d515Smglocker return 1; 3272095c737Smglocker } 3286920fd9bSmglocker DPRINTF(3, "%s: completed after %d retries\n", __func__, i); 3292095c737Smglocker 3302095c737Smglocker /* ACK interrupt */ 3312095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IS, status); 3322095c737Smglocker 3332095c737Smglocker return 0; 3342095c737Smglocker } 3352095c737Smglocker 3362095c737Smglocker struct ufshci_dmamem * 3372095c737Smglocker ufshci_dmamem_alloc(struct ufshci_softc *sc, size_t size) 3382095c737Smglocker { 3392095c737Smglocker struct ufshci_dmamem *udm; 3402095c737Smglocker int nsegs; 3412095c737Smglocker 3422095c737Smglocker udm = malloc(sizeof(*udm), M_DEVBUF, M_WAITOK | M_ZERO); 3432095c737Smglocker if (udm == NULL) 3442095c737Smglocker return NULL; 3452095c737Smglocker 3462095c737Smglocker udm->udm_size = size; 3472095c737Smglocker 3482095c737Smglocker if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 3494acd6882Smglocker BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | 3504acd6882Smglocker (sc->sc_cap & UFSHCI_REG_CAP_64AS) ? BUS_DMA_64BIT : 0, 3512095c737Smglocker &udm->udm_map) != 0) 3522095c737Smglocker goto udmfree; 3532095c737Smglocker 3542095c737Smglocker if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &udm->udm_seg, 3552095c737Smglocker 1, &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO) != 0) 3562095c737Smglocker goto destroy; 3572095c737Smglocker 3582095c737Smglocker if (bus_dmamem_map(sc->sc_dmat, &udm->udm_seg, nsegs, size, 3592095c737Smglocker &udm->udm_kva, BUS_DMA_WAITOK) != 0) 3602095c737Smglocker goto free; 3612095c737Smglocker 3622095c737Smglocker if (bus_dmamap_load(sc->sc_dmat, udm->udm_map, udm->udm_kva, size, 3632095c737Smglocker NULL, BUS_DMA_WAITOK) != 0) 3642095c737Smglocker goto unmap; 3652095c737Smglocker 3666920fd9bSmglocker DPRINTF(2, "%s: size=%lu, page_size=%d, nsegs=%d\n", 3672095c737Smglocker __func__, size, PAGE_SIZE, nsegs); 3682095c737Smglocker 3692095c737Smglocker return udm; 3702095c737Smglocker 3712095c737Smglocker unmap: 3722095c737Smglocker bus_dmamem_unmap(sc->sc_dmat, udm->udm_kva, size); 3732095c737Smglocker free: 3742095c737Smglocker bus_dmamem_free(sc->sc_dmat, &udm->udm_seg, 1); 3752095c737Smglocker destroy: 3762095c737Smglocker bus_dmamap_destroy(sc->sc_dmat, udm->udm_map); 3772095c737Smglocker udmfree: 3782095c737Smglocker free(udm, M_DEVBUF, sizeof(*udm)); 3792095c737Smglocker 3802095c737Smglocker return NULL; 3812095c737Smglocker } 3822095c737Smglocker 3832095c737Smglocker void 3842095c737Smglocker ufshci_dmamem_free(struct ufshci_softc *sc, struct ufshci_dmamem *udm) 3852095c737Smglocker { 3862095c737Smglocker bus_dmamap_unload(sc->sc_dmat, udm->udm_map); 3872095c737Smglocker bus_dmamem_unmap(sc->sc_dmat, udm->udm_kva, udm->udm_size); 3882095c737Smglocker bus_dmamem_free(sc->sc_dmat, &udm->udm_seg, 1); 3892095c737Smglocker bus_dmamap_destroy(sc->sc_dmat, udm->udm_map); 3902095c737Smglocker free(udm, M_DEVBUF, sizeof(*udm)); 3912095c737Smglocker } 3922095c737Smglocker 3932095c737Smglocker int 39490de4d2dSmglocker ufshci_alloc(struct ufshci_softc *sc) 39590de4d2dSmglocker { 39690de4d2dSmglocker /* 7.1.1 Host Controller Initialization: 13) */ 39790de4d2dSmglocker sc->sc_dmamem_utmrd = ufshci_dmamem_alloc(sc, 39890de4d2dSmglocker sizeof(struct ufshci_utmrd) * sc->sc_nutmrs); 39990de4d2dSmglocker if (sc->sc_dmamem_utmrd == NULL) { 40090de4d2dSmglocker printf("%s: Can't allocate DMA memory for UTMRD\n", 40190de4d2dSmglocker sc->sc_dev.dv_xname); 4020fe5d515Smglocker return 1; 40390de4d2dSmglocker } 40490de4d2dSmglocker 40590de4d2dSmglocker /* 7.1.1 Host Controller Initialization: 15) */ 40690de4d2dSmglocker sc->sc_dmamem_utrd = ufshci_dmamem_alloc(sc, 40790de4d2dSmglocker sizeof(struct ufshci_utrd) * sc->sc_nutrs); 40890de4d2dSmglocker if (sc->sc_dmamem_utrd == NULL) { 40990de4d2dSmglocker printf("%s: Can't allocate DMA memory for UTRD\n", 41090de4d2dSmglocker sc->sc_dev.dv_xname); 4110fe5d515Smglocker return 1; 41290de4d2dSmglocker } 41390de4d2dSmglocker 41490de4d2dSmglocker /* Allocate UCDs. */ 41590de4d2dSmglocker sc->sc_dmamem_ucd = ufshci_dmamem_alloc(sc, 41690de4d2dSmglocker sizeof(struct ufshci_ucd) * sc->sc_nutrs); 41790de4d2dSmglocker if (sc->sc_dmamem_ucd == NULL) { 41890de4d2dSmglocker printf("%s: Can't allocate DMA memory for UCD\n", 41990de4d2dSmglocker sc->sc_dev.dv_xname); 4200fe5d515Smglocker return 1; 42190de4d2dSmglocker } 42290de4d2dSmglocker 42390de4d2dSmglocker return 0; 42490de4d2dSmglocker } 42590de4d2dSmglocker 42690de4d2dSmglocker int 4272095c737Smglocker ufshci_init(struct ufshci_softc *sc) 4282095c737Smglocker { 4292095c737Smglocker uint32_t reg; 4302095c737Smglocker uint64_t dva; 4312095c737Smglocker 4322095c737Smglocker /* 4332095c737Smglocker * 7.1.1 Host Controller Initialization: 4) 4342095c737Smglocker * TODO: Do we need to set DME_SET? 4352095c737Smglocker */ 4362095c737Smglocker 4372095c737Smglocker /* 7.1.1 Host Controller Initialization: 5) */ 4389251d8a3Smglocker if (sc->sc_cap & UFSHCI_REG_AUTOH8) { 4399251d8a3Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, 4409251d8a3Smglocker UFSHCI_REG_IE_UTRCE | UFSHCI_REG_IE_UTMRCE | 4419251d8a3Smglocker UFSHCI_REG_IS_UHES | UFSHCI_REG_IS_UHXS); 4429251d8a3Smglocker } else { 4432095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, 4442095c737Smglocker UFSHCI_REG_IE_UTRCE | UFSHCI_REG_IE_UTMRCE); 4459251d8a3Smglocker } 4462095c737Smglocker 4472095c737Smglocker /* 7.1.1 Host Controller Initialization: 6) */ 4482095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UICCMD, 4492095c737Smglocker UFSHCI_REG_UICCMD_CMDOP_DME_LINKSTARTUP); 4500fe5d515Smglocker if (ufshci_is_poll(sc, UFSHCI_REG_IS_UCCS)) 4510fe5d515Smglocker return 1; 4522095c737Smglocker 4532095c737Smglocker /* 4542095c737Smglocker * 7.1.1 Host Controller Initialization: 7), 8), 9) 4552095c737Smglocker * TODO: Implement retry in case UFSHCI_REG_HCS returns 0 4562095c737Smglocker */ 4572095c737Smglocker reg = UFSHCI_READ_4(sc, UFSHCI_REG_HCS); 4582095c737Smglocker if (reg & UFSHCI_REG_HCS_DP) 4596920fd9bSmglocker DPRINTF(2, "%s: Device Presence SET\n", __func__); 4602095c737Smglocker else 4616920fd9bSmglocker DPRINTF(2, "%s: Device Presence NOT SET\n", __func__); 4622095c737Smglocker 4632095c737Smglocker /* 4642095c737Smglocker * 7.1.1 Host Controller Initialization: 10) 4652095c737Smglocker * TODO: Enable additional interrupt on the IE register 4662095c737Smglocker */ 4672095c737Smglocker 4682095c737Smglocker /* 7.1.1 Host Controller Initialization: 11) */ 469ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) { 470f0ed5855Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR, 471f0ed5855Smglocker UFSHCI_REG_UTRIACR_IAEN | 472f0ed5855Smglocker UFSHCI_REG_UTRIACR_IAPWEN | 473f0ed5855Smglocker UFSHCI_REG_UTRIACR_CTR | 474f0ed5855Smglocker UFSHCI_REG_UTRIACR_IACTH(sc->sc_iacth) | 475f0ed5855Smglocker UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT)); 476ceedf4ccSmglocker } else { 477ceedf4ccSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR, 0); 478ceedf4ccSmglocker } 4792095c737Smglocker 4802095c737Smglocker /* 4812095c737Smglocker * 7.1.1 Host Controller Initialization: 12) 4822095c737Smglocker * TODO: More UIC commands to issue? 4832095c737Smglocker */ 4842095c737Smglocker 4852095c737Smglocker /* 7.1.1 Host Controller Initialization: 14) */ 4862095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_utmrd); 4876920fd9bSmglocker DPRINTF(2, "%s: utmrd dva=%llu\n", __func__, dva); 4882095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLBA, (uint32_t)dva); 4892095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLBAU, (uint32_t)(dva >> 32)); 4902095c737Smglocker 4912095c737Smglocker /* 7.1.1 Host Controller Initialization: 16) */ 4922095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_utrd); 4936920fd9bSmglocker DPRINTF(2, "%s: utrd dva=%llu\n", __func__, dva); 4942095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLBA, (uint32_t)dva); 4952095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLBAU, (uint32_t)(dva >> 32)); 4962095c737Smglocker 4972095c737Smglocker /* 7.1.1 Host Controller Initialization: 17) */ 4982095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLRSR, UFSHCI_REG_UTMRLRSR_START); 4992095c737Smglocker 5002095c737Smglocker /* 7.1.1 Host Controller Initialization: 18) */ 5012095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLRSR, UFSHCI_REG_UTRLRSR_START); 5022095c737Smglocker 5032095c737Smglocker /* 7.1.1 Host Controller Initialization: 19) */ 5042095c737Smglocker /* TODO: bMaxNumOfRTT will be set as the minimum value of 5052095c737Smglocker * bDeviceRTTCap and NORTT. ??? 5062095c737Smglocker */ 5072095c737Smglocker 5082095c737Smglocker return 0; 5092095c737Smglocker } 5102095c737Smglocker 5110fe5d515Smglocker void 51290de4d2dSmglocker ufshci_disable(struct ufshci_softc *sc) 51390de4d2dSmglocker { 51490de4d2dSmglocker /* Stop run queues. */ 51590de4d2dSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLRSR, UFSHCI_REG_UTMRLRSR_STOP); 51690de4d2dSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLRSR, UFSHCI_REG_UTRLRSR_STOP); 51790de4d2dSmglocker 51890de4d2dSmglocker /* Disable interrupts. */ 51990de4d2dSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, 0); 52090de4d2dSmglocker } 52190de4d2dSmglocker 52290de4d2dSmglocker int 5232095c737Smglocker ufshci_doorbell_read(struct ufshci_softc *sc) 5242095c737Smglocker { 5252095c737Smglocker uint32_t reg; 5262095c737Smglocker 5272095c737Smglocker reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR); 5282095c737Smglocker 5292095c737Smglocker return reg; 5302095c737Smglocker } 5312095c737Smglocker 53212f70f1cSmglocker void 53312f70f1cSmglocker ufshci_doorbell_write(struct ufshci_softc *sc, int slot) 53412f70f1cSmglocker { 53512f70f1cSmglocker uint32_t reg; 53612f70f1cSmglocker 537c4dc76d1Smglocker reg = (1U << slot); 53812f70f1cSmglocker 53912f70f1cSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLDBR, reg); 54012f70f1cSmglocker } 54112f70f1cSmglocker 5422095c737Smglocker int 54393bf5056Smglocker ufshci_doorbell_poll(struct ufshci_softc *sc, int slot, uint32_t timeout_ms) 5442095c737Smglocker { 5452095c737Smglocker uint32_t reg; 54693bf5056Smglocker uint64_t timeout_us; 5472095c737Smglocker 5486920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 5492095c737Smglocker 55093bf5056Smglocker for (timeout_us = timeout_ms * 1000; timeout_us != 0; 55193bf5056Smglocker timeout_us -= 1000) { 5522095c737Smglocker reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR); 553c4dc76d1Smglocker if ((reg & (1U << slot)) == 0) 5542095c737Smglocker break; 55593bf5056Smglocker delay(1000); 5562095c737Smglocker } 55793bf5056Smglocker if (timeout_us == 0) { 5582095c737Smglocker printf("%s: %s: timeout\n", sc->sc_dev.dv_xname, __func__); 5590fe5d515Smglocker return 1; 5602095c737Smglocker } 5612095c737Smglocker 5622095c737Smglocker return 0; 5632095c737Smglocker } 5642095c737Smglocker 5652095c737Smglocker int 56612f70f1cSmglocker ufshci_utr_cmd_nop(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 56712f70f1cSmglocker struct scsi_xfer *xs) 5682095c737Smglocker { 5692095c737Smglocker int slot, off, len; 5702095c737Smglocker uint64_t dva; 5712095c737Smglocker struct ufshci_utrd *utrd; 5722095c737Smglocker struct ufshci_ucd *ucd; 5732095c737Smglocker 5742095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 57512f70f1cSmglocker slot = ccb->ccb_slot; 576b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 577b3b05a10Smglocker utrd += slot; 5782095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 5796920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 5802095c737Smglocker 5812095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 5822095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 5832095c737Smglocker 5842095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 5852095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_NO; 5862095c737Smglocker 5872095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 588ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 58912f70f1cSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 590ceedf4ccSmglocker else 591ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 5922095c737Smglocker 5932095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 5942095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 5952095c737Smglocker 5962095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 597b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 598b3b05a10Smglocker ucd += slot; 5992095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 6002095c737Smglocker 6012095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 6022095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_NOP_OUT; 6032095c737Smglocker ucd->cmd.hdr.flags = 0; 6042095c737Smglocker ucd->cmd.hdr.lun = 0; 60538304948Smglocker ucd->cmd.hdr.task_tag = slot; 6062095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 6072095c737Smglocker ucd->cmd.hdr.query = 0; 6082095c737Smglocker ucd->cmd.hdr.response = 0; 6092095c737Smglocker ucd->cmd.hdr.status = 0; 6102095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 6112095c737Smglocker ucd->cmd.hdr.device_info = 0; 6122095c737Smglocker ucd->cmd.hdr.ds_len = 0; 6132095c737Smglocker 6142095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 6152095c737Smglocker /* Already done with above memset */ 6162095c737Smglocker 6172095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 6182095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd); 6196920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 6202095c737Smglocker utrd->dw4 = (uint32_t)dva; 6212095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 6222095c737Smglocker 6232095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 6242095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 6252095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 6262095c737Smglocker 6272095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 6282095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 6292095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 6302095c737Smglocker 6312095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 6322095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 6332095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 6342095c737Smglocker 6352095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 6362095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(0); /* No data xfer */ 6372095c737Smglocker 6382095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 6392095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 6402095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 6412095c737Smglocker sc->sc_dev.dv_xname, __func__); 6420fe5d515Smglocker return 1; 6432095c737Smglocker } 6442095c737Smglocker 6453bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 6463bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 6473bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 6483bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 6493bc7f528Smglocker 6502095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 65112f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 65212f70f1cSmglocker ufshci_doorbell_write(sc, slot); 6532095c737Smglocker 6542095c737Smglocker return 0; 6552095c737Smglocker } 6562095c737Smglocker 6572095c737Smglocker int 6582095c737Smglocker ufshci_utr_cmd_lun(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 65944c52867Smglocker struct scsi_xfer *xs) 6602095c737Smglocker { 6612095c737Smglocker int slot, off, len, i; 6622095c737Smglocker uint64_t dva; 6632095c737Smglocker struct ufshci_utrd *utrd; 6642095c737Smglocker struct ufshci_ucd *ucd; 6652095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 6662095c737Smglocker 6672095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 66812f70f1cSmglocker slot = ccb->ccb_slot; 669b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 670b3b05a10Smglocker utrd += slot; 6712095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 6726920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 6732095c737Smglocker 6742095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 6752095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 6762095c737Smglocker 6772095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 6782095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 6792095c737Smglocker 6802095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 681ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 6822095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 683ceedf4ccSmglocker else 684ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 6852095c737Smglocker 6862095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 6872095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 6882095c737Smglocker 6892095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 690b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 691b3b05a10Smglocker ucd += slot; 6922095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 6932095c737Smglocker 6942095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 6952095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 6962095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 6972095c737Smglocker ucd->cmd.hdr.lun = 0; 69838304948Smglocker ucd->cmd.hdr.task_tag = slot; 6992095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 7002095c737Smglocker ucd->cmd.hdr.query = 0; 7012095c737Smglocker ucd->cmd.hdr.response = 0; 7022095c737Smglocker ucd->cmd.hdr.status = 0; 7032095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 7042095c737Smglocker ucd->cmd.hdr.device_info = 0; 7052095c737Smglocker ucd->cmd.hdr.ds_len = 0; 7062095c737Smglocker 70744c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 7082095c737Smglocker 7092095c737Smglocker ucd->cmd.cdb[0] = REPORT_LUNS; 7102095c737Smglocker ucd->cmd.cdb[6] = 0; 7112095c737Smglocker ucd->cmd.cdb[7] = 0; 7122095c737Smglocker ucd->cmd.cdb[8] = 0; 71344c52867Smglocker ucd->cmd.cdb[9] = xs->datalen; 7142095c737Smglocker 7152095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 7162095c737Smglocker /* Already done with above memset */ 7172095c737Smglocker 7182095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 7192095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd); 7206920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 7212095c737Smglocker utrd->dw4 = (uint32_t)dva; 7222095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 7232095c737Smglocker 7242095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 7252095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 7262095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 7272095c737Smglocker 7282095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 7292095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 7302095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 7312095c737Smglocker 7322095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 7332095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 7342095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 7352095c737Smglocker 7362095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 7372095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 7382095c737Smglocker 7392095c737Smglocker /* Build PRDT data segment. */ 7402095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 7412095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 7422095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 7432095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 7442095c737Smglocker ucd->prdt[i].dw2 = 0; 7452095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 7462095c737Smglocker } 7472095c737Smglocker 7482095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 7492095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 7502095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 7512095c737Smglocker sc->sc_dev.dv_xname, __func__); 7520fe5d515Smglocker return 1; 7532095c737Smglocker } 7542095c737Smglocker 7553bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 7563bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 7573bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 7583bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 7593bc7f528Smglocker 7602095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 76112f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 76212f70f1cSmglocker ufshci_doorbell_write(sc, slot); 7632095c737Smglocker 7642095c737Smglocker return 0; 7652095c737Smglocker } 7662095c737Smglocker 7672095c737Smglocker int 7682095c737Smglocker ufshci_utr_cmd_inquiry(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 76944c52867Smglocker struct scsi_xfer *xs) 7702095c737Smglocker { 7712095c737Smglocker int slot, off, len, i; 7722095c737Smglocker uint64_t dva; 7732095c737Smglocker struct ufshci_utrd *utrd; 7742095c737Smglocker struct ufshci_ucd *ucd; 7752095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 7762095c737Smglocker 7772095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 77812f70f1cSmglocker slot = ccb->ccb_slot; 779b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 780b3b05a10Smglocker utrd += slot; 7812095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 7826920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 7832095c737Smglocker 7842095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 7852095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 7862095c737Smglocker 7872095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 7882095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 7892095c737Smglocker 7902095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 791ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 7922095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 793ceedf4ccSmglocker else 794ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 7952095c737Smglocker 7962095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 7972095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 7982095c737Smglocker 7992095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 800b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 801b3b05a10Smglocker ucd += slot; 8022095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 8032095c737Smglocker 8042095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 8052095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 8062095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 8072095c737Smglocker ucd->cmd.hdr.lun = 0; 80838304948Smglocker ucd->cmd.hdr.task_tag = slot; 8092095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 8102095c737Smglocker ucd->cmd.hdr.query = 0; 8112095c737Smglocker ucd->cmd.hdr.response = 0; 8122095c737Smglocker ucd->cmd.hdr.status = 0; 8132095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 8142095c737Smglocker ucd->cmd.hdr.device_info = 0; 8152095c737Smglocker ucd->cmd.hdr.ds_len = 0; 8162095c737Smglocker 81744c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 8182095c737Smglocker 8192095c737Smglocker ucd->cmd.cdb[0] = INQUIRY; /* 0x12 */ 8202095c737Smglocker ucd->cmd.cdb[3] = 0; 82144c52867Smglocker ucd->cmd.cdb[4] = xs->datalen; 8222095c737Smglocker 8232095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 8242095c737Smglocker /* Already done with above memset */ 8252095c737Smglocker 8262095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 8272095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 8286920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 8292095c737Smglocker utrd->dw4 = (uint32_t)dva; 8302095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 8312095c737Smglocker 8322095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 8332095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 8342095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 8352095c737Smglocker 8362095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 8372095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 8382095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 8392095c737Smglocker 8402095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 8412095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 8422095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 8432095c737Smglocker 8442095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 8452095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 8462095c737Smglocker 8472095c737Smglocker /* Build PRDT data segment. */ 8482095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 8492095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 8502095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 8512095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 8522095c737Smglocker ucd->prdt[i].dw2 = 0; 8532095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 8542095c737Smglocker } 8552095c737Smglocker 8562095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 8572095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 8582095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 8592095c737Smglocker sc->sc_dev.dv_xname, __func__); 8600fe5d515Smglocker return 1; 8612095c737Smglocker } 8622095c737Smglocker 8633bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 8643bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 8653bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 8663bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 8673bc7f528Smglocker 8682095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 86912f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 87012f70f1cSmglocker ufshci_doorbell_write(sc, slot); 8712095c737Smglocker 8720fe5d515Smglocker return 0; 8732095c737Smglocker } 8742095c737Smglocker 8752095c737Smglocker int 8762095c737Smglocker ufshci_utr_cmd_capacity16(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 87744c52867Smglocker struct scsi_xfer *xs) 8782095c737Smglocker { 8792095c737Smglocker int slot, off, len, i; 8802095c737Smglocker uint64_t dva; 8812095c737Smglocker struct ufshci_utrd *utrd; 8822095c737Smglocker struct ufshci_ucd *ucd; 8832095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 8842095c737Smglocker 8852095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 88612f70f1cSmglocker slot = ccb->ccb_slot; 887b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 888b3b05a10Smglocker utrd += slot; 8892095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 8906920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 8912095c737Smglocker 8922095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 8932095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 8942095c737Smglocker 8952095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 8962095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 8972095c737Smglocker 8982095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 899ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 9002095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 901ceedf4ccSmglocker else 902ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 9032095c737Smglocker 9042095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 9052095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 9062095c737Smglocker 9072095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 908b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 909b3b05a10Smglocker ucd += slot; 9102095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 9112095c737Smglocker 9122095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 9132095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 9142095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 9152095c737Smglocker ucd->cmd.hdr.lun = 0; 91638304948Smglocker ucd->cmd.hdr.task_tag = slot; 9172095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 9182095c737Smglocker ucd->cmd.hdr.query = 0; 9192095c737Smglocker ucd->cmd.hdr.response = 0; 9202095c737Smglocker ucd->cmd.hdr.status = 0; 9212095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 9222095c737Smglocker ucd->cmd.hdr.device_info = 0; 9232095c737Smglocker ucd->cmd.hdr.ds_len = 0; 9242095c737Smglocker 92544c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 9262095c737Smglocker 9272095c737Smglocker ucd->cmd.cdb[0] = READ_CAPACITY_16; /* 0x9e */ 9282095c737Smglocker ucd->cmd.cdb[1] = 0x10; /* Service Action */ 9292095c737Smglocker /* Logical Block Address = 0 for UFS */ 9302095c737Smglocker ucd->cmd.cdb[10] = 0; 9312095c737Smglocker ucd->cmd.cdb[11] = 0; 9322095c737Smglocker ucd->cmd.cdb[12] = 0; 93344c52867Smglocker ucd->cmd.cdb[13] = xs->datalen; 9342095c737Smglocker 9352095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 9362095c737Smglocker /* Already done with above memset */ 9372095c737Smglocker 9382095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 9392095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 9406920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 9412095c737Smglocker utrd->dw4 = (uint32_t)dva; 9422095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 9432095c737Smglocker 9442095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 9452095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 9462095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 9472095c737Smglocker 9482095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 9492095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 9502095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 9512095c737Smglocker 9522095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 9532095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 9542095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 9552095c737Smglocker 9562095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 9572095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 9582095c737Smglocker 9592095c737Smglocker /* Build PRDT data segment. */ 9602095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 9612095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 9622095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 9632095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 9642095c737Smglocker ucd->prdt[i].dw2 = 0; 9652095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 9662095c737Smglocker } 9672095c737Smglocker 9682095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 9692095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 9702095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 9712095c737Smglocker sc->sc_dev.dv_xname, __func__); 9720fe5d515Smglocker return 1; 9732095c737Smglocker } 9742095c737Smglocker 9753bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 9763bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 9773bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 9783bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 9793bc7f528Smglocker 9802095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 98112f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 98212f70f1cSmglocker ufshci_doorbell_write(sc, slot); 9832095c737Smglocker 9840fe5d515Smglocker return 0; 9852095c737Smglocker } 9862095c737Smglocker 9872095c737Smglocker int 9882095c737Smglocker ufshci_utr_cmd_capacity(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 98944c52867Smglocker struct scsi_xfer *xs) 9902095c737Smglocker { 9912095c737Smglocker int slot, off, len, i; 9922095c737Smglocker uint64_t dva; 9932095c737Smglocker struct ufshci_utrd *utrd; 9942095c737Smglocker struct ufshci_ucd *ucd; 9952095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 9962095c737Smglocker 9972095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 99812f70f1cSmglocker slot = ccb->ccb_slot; 999b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1000b3b05a10Smglocker utrd += slot; 10012095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 10026920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 10032095c737Smglocker 10042095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 10052095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 10062095c737Smglocker 10072095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 10082095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 10092095c737Smglocker 10102095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 1011ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 10122095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 1013ceedf4ccSmglocker else 1014ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 10152095c737Smglocker 10162095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 10172095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 10182095c737Smglocker 10192095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 1020b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1021b3b05a10Smglocker ucd += slot; 10222095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 10232095c737Smglocker 10242095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 10252095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 10262095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 10272095c737Smglocker ucd->cmd.hdr.lun = 0; 102838304948Smglocker ucd->cmd.hdr.task_tag = slot; 10292095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 10302095c737Smglocker ucd->cmd.hdr.query = 0; 10312095c737Smglocker ucd->cmd.hdr.response = 0; 10322095c737Smglocker ucd->cmd.hdr.status = 0; 10332095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 10342095c737Smglocker ucd->cmd.hdr.device_info = 0; 10352095c737Smglocker ucd->cmd.hdr.ds_len = 0; 10362095c737Smglocker 103744c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 10382095c737Smglocker 10392095c737Smglocker ucd->cmd.cdb[0] = READ_CAPACITY; /* 0x25 */ 10402095c737Smglocker /* Logical Block Address = 0 for UFS */ 10412095c737Smglocker ucd->cmd.cdb[2] = 0; 10422095c737Smglocker ucd->cmd.cdb[3] = 0; 10432095c737Smglocker ucd->cmd.cdb[4] = 0; 10442095c737Smglocker ucd->cmd.cdb[5] = 0; 10452095c737Smglocker 10462095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 10472095c737Smglocker /* Already done with above memset */ 10482095c737Smglocker 10492095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 10502095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 10516920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 10522095c737Smglocker utrd->dw4 = (uint32_t)dva; 10532095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 10542095c737Smglocker 10552095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 10562095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 10572095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 10582095c737Smglocker 10592095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 10602095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 10612095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 10622095c737Smglocker 10632095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 10642095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 10652095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 10662095c737Smglocker 10672095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 10682095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 10692095c737Smglocker 10702095c737Smglocker /* Build PRDT data segment. */ 10712095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 10722095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 10732095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 10742095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 10752095c737Smglocker ucd->prdt[i].dw2 = 0; 10762095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 10772095c737Smglocker } 10782095c737Smglocker 10792095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 10802095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 10812095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 10822095c737Smglocker sc->sc_dev.dv_xname, __func__); 10830fe5d515Smglocker return 1; 10842095c737Smglocker } 10852095c737Smglocker 10863bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 10873bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 10883bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 10893bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 10903bc7f528Smglocker 10912095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 109212f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 109312f70f1cSmglocker ufshci_doorbell_write(sc, slot); 10942095c737Smglocker 10950fe5d515Smglocker return 0; 10962095c737Smglocker } 10972095c737Smglocker 10982095c737Smglocker int 1099a2f0dcb2Smglocker ufshci_utr_cmd_io(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 1100a2f0dcb2Smglocker struct scsi_xfer *xs, int dir) 11012095c737Smglocker { 11022095c737Smglocker int slot, off, len, i; 11032095c737Smglocker uint64_t dva; 11042095c737Smglocker struct ufshci_utrd *utrd; 11052095c737Smglocker struct ufshci_ucd *ucd; 11062095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 1107a9129097Smglocker uint32_t blocks; 1108a9129097Smglocker uint64_t lba; 11092095c737Smglocker 11102095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 111112f70f1cSmglocker slot = ccb->ccb_slot; 1112b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1113b3b05a10Smglocker utrd += slot; 11142095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 11156920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 11162095c737Smglocker 11172095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 11182095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 11192095c737Smglocker 11202095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 1121a2f0dcb2Smglocker if (dir == SCSI_DATA_IN) 11222095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 1123a2f0dcb2Smglocker else 11242095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_I2T; 11252095c737Smglocker 11262095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 1127ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 11282095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 1129ceedf4ccSmglocker else 1130ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 11312095c737Smglocker 11322095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 11332095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 11342095c737Smglocker 11352095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 1136b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1137b3b05a10Smglocker ucd += slot; 11382095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 11392095c737Smglocker 11402095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 11412095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 1142a2f0dcb2Smglocker if (dir == SCSI_DATA_IN) 1143a2f0dcb2Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-6 = Read */ 1144a2f0dcb2Smglocker else 1145a2f0dcb2Smglocker ucd->cmd.hdr.flags = (1 << 5); /* Bit-5 = Write */ 11462095c737Smglocker ucd->cmd.hdr.lun = 0; 114738304948Smglocker ucd->cmd.hdr.task_tag = slot; 11482095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 11492095c737Smglocker ucd->cmd.hdr.query = 0; 11502095c737Smglocker ucd->cmd.hdr.response = 0; 11512095c737Smglocker ucd->cmd.hdr.status = 0; 11522095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 11532095c737Smglocker ucd->cmd.hdr.device_info = 0; 11542095c737Smglocker ucd->cmd.hdr.ds_len = 0; 11552095c737Smglocker 1156a9129097Smglocker /* 1157a9129097Smglocker * JESD220C-2_1.pdf, page 88, d) Expected Data Transfer Length: 1158a9129097Smglocker * "When the COMMAND UPIU encodes a SCSI WRITE or SCSI READ command 1159a9129097Smglocker * (specifically WRITE (6), READ (6), WRITE (10), READ (10), 1160a9129097Smglocker * WRITE (16), or READ (16)), the value of this field shall be the 1161a9129097Smglocker * product of the Logical Block Size (bLogicalBlockSize) and the 1162a9129097Smglocker * TRANSFER LENGTH field of the CDB." 1163a9129097Smglocker */ 1164a9129097Smglocker scsi_cmd_rw_decode(&xs->cmd, &lba, &blocks); 1165a9129097Smglocker ucd->cmd.expected_xfer_len = htobe32(UFSHCI_LBS * blocks); 11662095c737Smglocker 1167a2f0dcb2Smglocker memcpy(ucd->cmd.cdb, &xs->cmd, sizeof(ucd->cmd.cdb)); 11682095c737Smglocker 11692095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 11702095c737Smglocker /* Already done with above memset */ 11712095c737Smglocker 11722095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 11732095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 11746920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 11752095c737Smglocker utrd->dw4 = (uint32_t)dva; 11762095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 11772095c737Smglocker 11782095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 11792095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 11802095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 11812095c737Smglocker 11822095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 11832095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 11842095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 11852095c737Smglocker 11862095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 11872095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 11882095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 11892095c737Smglocker 11902095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 11912095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 11922095c737Smglocker 11932095c737Smglocker /* Build PRDT data segment. */ 11942095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 11952095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 11962095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 11972095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 11982095c737Smglocker ucd->prdt[i].dw2 = 0; 11992095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 12002095c737Smglocker } 12012095c737Smglocker 12022095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 12032095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 12042095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 12052095c737Smglocker sc->sc_dev.dv_xname, __func__); 12060fe5d515Smglocker return 1; 12072095c737Smglocker } 12082095c737Smglocker 12093bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 12103bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 12113bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 12123bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 12133bc7f528Smglocker 12142095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 121512f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 121612f70f1cSmglocker ufshci_doorbell_write(sc, slot); 12172095c737Smglocker 12180fe5d515Smglocker return 0; 12192095c737Smglocker } 12202095c737Smglocker 12212095c737Smglocker int 12222095c737Smglocker ufshci_utr_cmd_sync(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 122344c52867Smglocker struct scsi_xfer *xs, uint32_t lba, uint16_t blocks) 12242095c737Smglocker { 12252095c737Smglocker int slot, off, len; 12262095c737Smglocker uint64_t dva; 12272095c737Smglocker struct ufshci_utrd *utrd; 12282095c737Smglocker struct ufshci_ucd *ucd; 12292095c737Smglocker 12302095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 123112f70f1cSmglocker slot = ccb->ccb_slot; 1232b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1233b3b05a10Smglocker utrd += slot; 12342095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 12356920fd9bSmglocker DPRINTF(3, "%s: slot=%d\n", __func__, slot); 12362095c737Smglocker 12372095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 12382095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 12392095c737Smglocker 12402095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 12412095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_I2T; 12422095c737Smglocker 12432095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 1244ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 12452095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 1246ceedf4ccSmglocker else 1247ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 12482095c737Smglocker 12492095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 12502095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 12512095c737Smglocker 12522095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 1253b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1254b3b05a10Smglocker ucd += slot; 12552095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 12562095c737Smglocker 12572095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 12582095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 12592095c737Smglocker ucd->cmd.hdr.flags = 0; /* No data transfer */ 12602095c737Smglocker ucd->cmd.hdr.lun = 0; 126138304948Smglocker ucd->cmd.hdr.task_tag = slot; 12622095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 12632095c737Smglocker ucd->cmd.hdr.query = 0; 12642095c737Smglocker ucd->cmd.hdr.response = 0; 12652095c737Smglocker ucd->cmd.hdr.status = 0; 12662095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 12672095c737Smglocker ucd->cmd.hdr.device_info = 0; 12682095c737Smglocker ucd->cmd.hdr.ds_len = 0; 12692095c737Smglocker 12702095c737Smglocker ucd->cmd.expected_xfer_len = htobe32(0); /* No data transfer */ 12712095c737Smglocker 12722095c737Smglocker ucd->cmd.cdb[0] = SYNCHRONIZE_CACHE; /* 0x35 */ 12732095c737Smglocker ucd->cmd.cdb[2] = (lba >> 24) & 0xff; 12742095c737Smglocker ucd->cmd.cdb[3] = (lba >> 16) & 0xff; 12752095c737Smglocker ucd->cmd.cdb[4] = (lba >> 8) & 0xff; 12762095c737Smglocker ucd->cmd.cdb[5] = (lba >> 0) & 0xff; 12772095c737Smglocker ucd->cmd.cdb[7] = (blocks >> 8) & 0xff; 12782095c737Smglocker ucd->cmd.cdb[8] = (blocks >> 0) & 0xff; 12792095c737Smglocker 12802095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 12812095c737Smglocker /* Already done with above memset */ 12822095c737Smglocker 12832095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 12842095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 12856920fd9bSmglocker DPRINTF(3, "%s: ucd dva=%llu\n", __func__, dva); 12862095c737Smglocker utrd->dw4 = (uint32_t)dva; 12872095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 12882095c737Smglocker 12892095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 12902095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 12912095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 12922095c737Smglocker 12932095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 12942095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 12952095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 12962095c737Smglocker 12972095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 12982095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 12992095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 13002095c737Smglocker 13012095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 13022095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(0); /* No data xfer */ 13032095c737Smglocker 13042095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 13052095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 13062095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 13072095c737Smglocker sc->sc_dev.dv_xname, __func__); 13080fe5d515Smglocker return 1; 13092095c737Smglocker } 13102095c737Smglocker 13113bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 13123bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 13133bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 13143bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 13153bc7f528Smglocker 13162095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 131712f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 131812f70f1cSmglocker ufshci_doorbell_write(sc, slot); 13192095c737Smglocker 13200fe5d515Smglocker return 0; 13212095c737Smglocker } 13222095c737Smglocker 13230fe5d515Smglocker void 13242095c737Smglocker ufshci_xfer_complete(struct ufshci_softc *sc) 13252095c737Smglocker { 13262095c737Smglocker struct ufshci_ccb *ccb; 13272095c737Smglocker uint32_t reg; 132838304948Smglocker int i, timeout; 13292095c737Smglocker 1330e58b468bSmglocker mtx_enter(&sc->sc_cmd_mtx); 1331e58b468bSmglocker 133212f70f1cSmglocker /* Wait for all commands to complete. */ 133338304948Smglocker for (timeout = 5000; timeout != 0; timeout--) { 133438304948Smglocker reg = ufshci_doorbell_read(sc); 133512f70f1cSmglocker if (reg == 0) 133612f70f1cSmglocker break; 133738304948Smglocker delay(10); 133812f70f1cSmglocker } 133938304948Smglocker if (timeout == 0) 134038304948Smglocker printf("%s: timeout (reg=0x%x)\n", __func__, reg); 13412095c737Smglocker 13422095c737Smglocker for (i = 0; i < sc->sc_nutrs; i++) { 13432095c737Smglocker ccb = &sc->sc_ccbs[i]; 13442095c737Smglocker 134512f70f1cSmglocker /* Skip unused CCBs. */ 134612f70f1cSmglocker if (ccb->ccb_status != CCB_STATUS_INPROGRESS) 13472095c737Smglocker continue; 13482095c737Smglocker 13492095c737Smglocker if (ccb->ccb_done == NULL) 135012f70f1cSmglocker panic("ccb done wasn't defined"); 135112f70f1cSmglocker 135212f70f1cSmglocker /* 7.2.3: Clear completion notification 3b) */ 135312f70f1cSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLCNR, (1U << i)); 135412f70f1cSmglocker 135512f70f1cSmglocker /* 7.2.3: Mark software slot for re-use 3c) */ 135612f70f1cSmglocker ccb->ccb_status = CCB_STATUS_READY2FREE; 135712f70f1cSmglocker 13586920fd9bSmglocker DPRINTF(3, "slot %d completed\n", i); 135912f70f1cSmglocker } 1360f0ed5855Smglocker 1361f0ed5855Smglocker /* 7.2.3: Reset Interrupt Aggregation Counter and Timer 4) */ 1362ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) { 1363f0ed5855Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR, 1364f0ed5855Smglocker UFSHCI_REG_UTRIACR_IAEN | UFSHCI_REG_UTRIACR_CTR); 1365ceedf4ccSmglocker } 1366f0ed5855Smglocker 1367e58b468bSmglocker mtx_leave(&sc->sc_cmd_mtx); 136812f70f1cSmglocker 136912f70f1cSmglocker /* 137012f70f1cSmglocker * Complete the CCB, which will re-schedule new transfers if any are 137112f70f1cSmglocker * pending. 137212f70f1cSmglocker */ 137312f70f1cSmglocker for (i = 0; i < sc->sc_nutrs; i++) { 137412f70f1cSmglocker ccb = &sc->sc_ccbs[i]; 137512f70f1cSmglocker 137612f70f1cSmglocker /* 7.2.3: Process the transfer by higher OS layer 3a) */ 137712f70f1cSmglocker if (ccb->ccb_status == CCB_STATUS_READY2FREE) 13782095c737Smglocker ccb->ccb_done(sc, ccb); 13792095c737Smglocker } 13802095c737Smglocker } 13812095c737Smglocker 13824b489d0aSmglocker int 13834b489d0aSmglocker ufshci_activate(struct ufshci_softc *sc, int act) 13844b489d0aSmglocker { 13854b489d0aSmglocker int rv = 0; 13864b489d0aSmglocker 13874b489d0aSmglocker switch (act) { 13884b489d0aSmglocker case DVACT_POWERDOWN: 13894b489d0aSmglocker DPRINTF(1, "%s: POWERDOWN\n", __func__); 13904b489d0aSmglocker rv = config_activate_children(&sc->sc_dev, act); 139190de4d2dSmglocker ufshci_disable(sc); 13924b489d0aSmglocker break; 13934b489d0aSmglocker case DVACT_RESUME: 13944b489d0aSmglocker DPRINTF(1, "%s: RESUME\n", __func__); 139590de4d2dSmglocker rv = ufshci_init(sc); 13964b489d0aSmglocker if (rv == 0) 13974b489d0aSmglocker rv = config_activate_children(&sc->sc_dev, act); 13984b489d0aSmglocker break; 13994b489d0aSmglocker default: 14004b489d0aSmglocker rv = config_activate_children(&sc->sc_dev, act); 14014b489d0aSmglocker break; 14024b489d0aSmglocker } 14034b489d0aSmglocker 14044b489d0aSmglocker return rv; 14054b489d0aSmglocker } 14064b489d0aSmglocker 14072095c737Smglocker /* SCSI */ 14082095c737Smglocker 14092095c737Smglocker int 14102095c737Smglocker ufshci_ccb_alloc(struct ufshci_softc *sc, int nccbs) 14112095c737Smglocker { 14122095c737Smglocker struct ufshci_ccb *ccb; 14132095c737Smglocker int i; 14142095c737Smglocker 14156920fd9bSmglocker DPRINTF(2, "%s: nccbs=%d, dma_size=%d, dma_nsegs=%d, " 14162095c737Smglocker "dma_segmaxsize=%d\n", 14172095c737Smglocker __func__, nccbs, UFSHCI_UCD_PRDT_MAX_XFER, UFSHCI_UCD_PRDT_MAX_SEGS, 14182095c737Smglocker UFSHCI_UCD_PRDT_MAX_XFER); 14192095c737Smglocker 14202095c737Smglocker sc->sc_ccbs = mallocarray(nccbs, sizeof(*ccb), M_DEVBUF, 14212095c737Smglocker M_WAITOK | M_CANFAIL); 14222095c737Smglocker if (sc->sc_ccbs == NULL) 14232095c737Smglocker return 1; 14242095c737Smglocker 14252095c737Smglocker for (i = 0; i < nccbs; i++) { 14262095c737Smglocker ccb = &sc->sc_ccbs[i]; 14272095c737Smglocker 14282095c737Smglocker if (bus_dmamap_create(sc->sc_dmat, UFSHCI_UCD_PRDT_MAX_XFER, 14292095c737Smglocker UFSHCI_UCD_PRDT_MAX_SEGS, UFSHCI_UCD_PRDT_MAX_XFER, 0, 14304acd6882Smglocker BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | 14314acd6882Smglocker (sc->sc_cap & UFSHCI_REG_CAP_64AS) ? BUS_DMA_64BIT : 0, 14322095c737Smglocker &ccb->ccb_dmamap) != 0) 14332095c737Smglocker goto free_maps; 14342095c737Smglocker 14352095c737Smglocker ccb->ccb_cookie = NULL; 143632835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 143712f70f1cSmglocker ccb->ccb_slot = i; 14382095c737Smglocker 14392095c737Smglocker SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry); 14402095c737Smglocker } 14412095c737Smglocker 14422095c737Smglocker return 0; 14432095c737Smglocker 14442095c737Smglocker free_maps: 14452095c737Smglocker ufshci_ccb_free(sc, nccbs); 14462095c737Smglocker return 1; 14472095c737Smglocker } 14482095c737Smglocker 14492095c737Smglocker void * 14502095c737Smglocker ufshci_ccb_get(void *cookie) 14512095c737Smglocker { 14522095c737Smglocker struct ufshci_softc *sc = cookie; 14532095c737Smglocker struct ufshci_ccb *ccb; 14542095c737Smglocker 14556920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 14562095c737Smglocker 14572095c737Smglocker mtx_enter(&sc->sc_ccb_mtx); 14582095c737Smglocker ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list); 14592095c737Smglocker if (ccb != NULL) 14602095c737Smglocker SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); 14612095c737Smglocker mtx_leave(&sc->sc_ccb_mtx); 14622095c737Smglocker 14632095c737Smglocker return ccb; 14642095c737Smglocker } 14652095c737Smglocker 14662095c737Smglocker void 14672095c737Smglocker ufshci_ccb_put(void *cookie, void *io) 14682095c737Smglocker { 14692095c737Smglocker struct ufshci_softc *sc = cookie; 14702095c737Smglocker struct ufshci_ccb *ccb = io; 14712095c737Smglocker 14726920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 14732095c737Smglocker 14742095c737Smglocker mtx_enter(&sc->sc_ccb_mtx); 14752095c737Smglocker SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_list, ccb, ccb_entry); 14762095c737Smglocker mtx_leave(&sc->sc_ccb_mtx); 14772095c737Smglocker } 14782095c737Smglocker 14792095c737Smglocker void 14802095c737Smglocker ufshci_ccb_free(struct ufshci_softc *sc, int nccbs) 14812095c737Smglocker { 14822095c737Smglocker struct ufshci_ccb *ccb; 14832095c737Smglocker 14846920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 14852095c737Smglocker 14862095c737Smglocker while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list)) != NULL) { 14872095c737Smglocker SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); 14882095c737Smglocker bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); 14892095c737Smglocker } 14902095c737Smglocker 14912095c737Smglocker ufshci_dmamem_free(sc, sc->sc_dmamem_utrd); 14922095c737Smglocker free(sc->sc_ccbs, M_DEVBUF, nccbs * sizeof(*ccb)); 14932095c737Smglocker } 14942095c737Smglocker 14952095c737Smglocker void 14962095c737Smglocker ufshci_scsi_cmd(struct scsi_xfer *xs) 14972095c737Smglocker { 14982095c737Smglocker struct scsi_link *link = xs->sc_link; 14992095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 15002095c737Smglocker 1501e58b468bSmglocker mtx_enter(&sc->sc_cmd_mtx); 1502e58b468bSmglocker 15036920fd9bSmglocker DPRINTF(3, "%s: cmd=0x%x\n", __func__, xs->cmd.opcode); 15042095c737Smglocker 15052095c737Smglocker switch (xs->cmd.opcode) { 15062095c737Smglocker 15072095c737Smglocker case READ_COMMAND: 15082095c737Smglocker case READ_10: 15092095c737Smglocker case READ_12: 15102095c737Smglocker case READ_16: 15116920fd9bSmglocker DPRINTF(3, "io read\n"); 15122095c737Smglocker ufshci_scsi_io(xs, SCSI_DATA_IN); 1513e58b468bSmglocker break; 15142095c737Smglocker case WRITE_COMMAND: 15152095c737Smglocker case WRITE_10: 15162095c737Smglocker case WRITE_12: 15172095c737Smglocker case WRITE_16: 15186920fd9bSmglocker DPRINTF(3, "io write\n"); 15192095c737Smglocker ufshci_scsi_io(xs, SCSI_DATA_OUT); 1520e58b468bSmglocker break; 15212095c737Smglocker case SYNCHRONIZE_CACHE: 15226920fd9bSmglocker DPRINTF(3, "sync\n"); 15232095c737Smglocker ufshci_scsi_sync(xs); 1524e58b468bSmglocker break; 15252095c737Smglocker case INQUIRY: 15266920fd9bSmglocker DPRINTF(3, "inquiry\n"); 15272095c737Smglocker ufshci_scsi_inquiry(xs); 1528e58b468bSmglocker break; 15292095c737Smglocker case READ_CAPACITY_16: 15306920fd9bSmglocker DPRINTF(3, "capacity16\n"); 15312095c737Smglocker ufshci_scsi_capacity16(xs); 1532e58b468bSmglocker break; 15332095c737Smglocker case READ_CAPACITY: 15346920fd9bSmglocker DPRINTF(3, "capacity\n"); 15352095c737Smglocker ufshci_scsi_capacity(xs); 1536e58b468bSmglocker break; 15372095c737Smglocker case TEST_UNIT_READY: 15382095c737Smglocker case PREVENT_ALLOW: 15392095c737Smglocker case START_STOP: 15402095c737Smglocker xs->error = XS_NOERROR; 15412095c737Smglocker scsi_done(xs); 1542e58b468bSmglocker break; 15432095c737Smglocker default: 15446920fd9bSmglocker DPRINTF(3, "%s: unhandled scsi command 0x%02x\n", 15452095c737Smglocker __func__, xs->cmd.opcode); 1546e58b468bSmglocker xs->error = XS_DRIVER_STUFFUP; 1547e58b468bSmglocker scsi_done(xs); 15482095c737Smglocker break; 15492095c737Smglocker } 15502095c737Smglocker 1551e58b468bSmglocker mtx_leave(&sc->sc_cmd_mtx); 15522095c737Smglocker } 15532095c737Smglocker 15542095c737Smglocker void 15552095c737Smglocker ufshci_minphys(struct buf *bp, struct scsi_link *link) 15562095c737Smglocker { 15576920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 15582095c737Smglocker } 15592095c737Smglocker 15602095c737Smglocker int 15612095c737Smglocker ufshci_scsi_probe(struct scsi_link *link) 15622095c737Smglocker { 15636920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 15642095c737Smglocker 15652095c737Smglocker return 0; 15662095c737Smglocker } 15672095c737Smglocker 15682095c737Smglocker void 15692095c737Smglocker ufshci_scsi_free(struct scsi_link *link) 15702095c737Smglocker { 15716920fd9bSmglocker DPRINTF(3, "%s\n", __func__); 15722095c737Smglocker } 15732095c737Smglocker 15742095c737Smglocker void 15752095c737Smglocker ufshci_scsi_inquiry(struct scsi_xfer *xs) 15762095c737Smglocker { 15772095c737Smglocker struct scsi_link *link = xs->sc_link; 15782095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 15792095c737Smglocker struct ufshci_ccb *ccb = xs->io; 15802095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 15812095c737Smglocker int error; 15822095c737Smglocker 15836920fd9bSmglocker DPRINTF(3, "%s: INQUIRY (%s)\n", 15842095c737Smglocker __func__, ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 15852095c737Smglocker 15862095c737Smglocker if (xs->datalen > UPIU_SCSI_RSP_INQUIRY_SIZE) { 15876920fd9bSmglocker DPRINTF(2, "%s: request len too large\n", __func__); 15882095c737Smglocker goto error1; 15892095c737Smglocker } 15902095c737Smglocker 15912095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 15922095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 15930fe5d515Smglocker if (error) { 15942095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 15952095c737Smglocker goto error1; 15962095c737Smglocker } 15972095c737Smglocker 15982095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 15992095c737Smglocker BUS_DMASYNC_PREREAD); 16002095c737Smglocker 16012095c737Smglocker ccb->ccb_cookie = xs; 16022095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 16032095c737Smglocker 16042095c737Smglocker /* Response length should be UPIU_SCSI_RSP_INQUIRY_SIZE. */ 160512f70f1cSmglocker error = ufshci_utr_cmd_inquiry(sc, ccb, xs); 16060fe5d515Smglocker if (error) 16072095c737Smglocker goto error2; 16082095c737Smglocker 16092095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 161093bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 16112095c737Smglocker ccb->ccb_done(sc, ccb); 16122095c737Smglocker return; 16132095c737Smglocker } 16142095c737Smglocker goto error2; 16152095c737Smglocker } 16162095c737Smglocker 16172095c737Smglocker return; 16182095c737Smglocker 16192095c737Smglocker error2: 16202095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 16212095c737Smglocker ccb->ccb_cookie = NULL; 162232835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 16232095c737Smglocker ccb->ccb_done = NULL; 16242095c737Smglocker error1: 16252095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 16262095c737Smglocker scsi_done(xs); 16272095c737Smglocker } 16282095c737Smglocker 16292095c737Smglocker void 16302095c737Smglocker ufshci_scsi_capacity16(struct scsi_xfer *xs) 16312095c737Smglocker { 16322095c737Smglocker struct scsi_link *link = xs->sc_link; 16332095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 16342095c737Smglocker struct ufshci_ccb *ccb = xs->io; 16352095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 16362095c737Smglocker int error; 16372095c737Smglocker 16386920fd9bSmglocker DPRINTF(3, "%s: CAPACITY16 (%s)\n", 16392095c737Smglocker __func__, ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 16402095c737Smglocker 16412095c737Smglocker if (xs->datalen > UPIU_SCSI_RSP_CAPACITY16_SIZE) { 16426920fd9bSmglocker DPRINTF(2, "%s: request len too large\n", __func__); 16432095c737Smglocker goto error1; 16442095c737Smglocker } 16452095c737Smglocker 16462095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 16472095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 16480fe5d515Smglocker if (error) { 16492095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 16502095c737Smglocker goto error1; 16512095c737Smglocker } 16522095c737Smglocker 16532095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 16542095c737Smglocker BUS_DMASYNC_PREREAD); 16552095c737Smglocker 16562095c737Smglocker ccb->ccb_cookie = xs; 16572095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 16582095c737Smglocker 16592095c737Smglocker /* Response length should be UPIU_SCSI_RSP_CAPACITY16_SIZE. */ 166012f70f1cSmglocker error = ufshci_utr_cmd_capacity16(sc, ccb, xs); 16610fe5d515Smglocker if (error) 16622095c737Smglocker goto error2; 16632095c737Smglocker 16642095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 166593bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 16662095c737Smglocker ccb->ccb_done(sc, ccb); 16672095c737Smglocker return; 16682095c737Smglocker } 16692095c737Smglocker goto error2; 16702095c737Smglocker } 16712095c737Smglocker 16722095c737Smglocker return; 16732095c737Smglocker 16742095c737Smglocker error2: 16752095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 16762095c737Smglocker ccb->ccb_cookie = NULL; 167732835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 16782095c737Smglocker ccb->ccb_done = NULL; 16792095c737Smglocker error1: 16802095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 16812095c737Smglocker scsi_done(xs); 16822095c737Smglocker } 16832095c737Smglocker 16842095c737Smglocker void 16852095c737Smglocker ufshci_scsi_capacity(struct scsi_xfer *xs) 16862095c737Smglocker { 16872095c737Smglocker struct scsi_link *link = xs->sc_link; 16882095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 16892095c737Smglocker struct ufshci_ccb *ccb = xs->io; 16902095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 16912095c737Smglocker int error; 16922095c737Smglocker 16936920fd9bSmglocker DPRINTF(3, "%s: CAPACITY (%s)\n", 16942095c737Smglocker __func__, ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 16952095c737Smglocker 16962095c737Smglocker if (xs->datalen > UPIU_SCSI_RSP_CAPACITY_SIZE) { 16976920fd9bSmglocker DPRINTF(2, "%s: request len too large\n", __func__); 16982095c737Smglocker goto error1; 16992095c737Smglocker } 17002095c737Smglocker 17012095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 17022095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 17030fe5d515Smglocker if (error) { 17042095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 17052095c737Smglocker goto error1; 17062095c737Smglocker } 17072095c737Smglocker 17082095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 17092095c737Smglocker BUS_DMASYNC_PREREAD); 17102095c737Smglocker 17112095c737Smglocker ccb->ccb_cookie = xs; 17122095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 17132095c737Smglocker 17142095c737Smglocker /* Response length should be UPIU_SCSI_RSP_CAPACITY_SIZE */ 171512f70f1cSmglocker error = ufshci_utr_cmd_capacity(sc, ccb, xs); 17160fe5d515Smglocker if (error) 17172095c737Smglocker goto error2; 17182095c737Smglocker 17192095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 172093bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 17212095c737Smglocker ccb->ccb_done(sc, ccb); 17222095c737Smglocker return; 17232095c737Smglocker } 17242095c737Smglocker goto error2; 17252095c737Smglocker } 17262095c737Smglocker 17272095c737Smglocker return; 17282095c737Smglocker 17292095c737Smglocker error2: 17302095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 17312095c737Smglocker ccb->ccb_cookie = NULL; 173232835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 17332095c737Smglocker ccb->ccb_done = NULL; 17342095c737Smglocker error1: 17352095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 17362095c737Smglocker scsi_done(xs); 17372095c737Smglocker } 17382095c737Smglocker 17392095c737Smglocker void 17402095c737Smglocker ufshci_scsi_sync(struct scsi_xfer *xs) 17412095c737Smglocker { 17422095c737Smglocker struct scsi_link *link = xs->sc_link; 17432095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 17442095c737Smglocker struct ufshci_ccb *ccb = xs->io; 17452095c737Smglocker uint64_t lba; 17462095c737Smglocker uint32_t blocks; 174712f70f1cSmglocker int error; 17482095c737Smglocker 17492095c737Smglocker /* lba = 0, blocks = 0: Synchronize all logical blocks. */ 17502095c737Smglocker lba = 0; blocks = 0; 17512095c737Smglocker 17526920fd9bSmglocker DPRINTF(3, "%s: SYNC, lba=%llu, blocks=%u (%s)\n", 17532095c737Smglocker __func__, lba, blocks, 17542095c737Smglocker ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 17552095c737Smglocker 17562095c737Smglocker ccb->ccb_cookie = xs; 17572095c737Smglocker ccb->ccb_done = ufshci_scsi_done; 17582095c737Smglocker 175912f70f1cSmglocker error = ufshci_utr_cmd_sync(sc, ccb, xs, (uint32_t)lba, 17602095c737Smglocker (uint16_t)blocks); 17610fe5d515Smglocker if (error) 17622095c737Smglocker goto error; 17632095c737Smglocker 17642095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 176593bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 17662095c737Smglocker ccb->ccb_done(sc, ccb); 17672095c737Smglocker return; 17682095c737Smglocker } 17692095c737Smglocker goto error; 17702095c737Smglocker } 17712095c737Smglocker 17722095c737Smglocker return; 17732095c737Smglocker 17742095c737Smglocker error: 17752095c737Smglocker ccb->ccb_cookie = NULL; 177632835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 17772095c737Smglocker ccb->ccb_done = NULL; 17782095c737Smglocker 17792095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 17802095c737Smglocker scsi_done(xs); 17812095c737Smglocker } 17822095c737Smglocker 17832095c737Smglocker void 17842095c737Smglocker ufshci_scsi_io(struct scsi_xfer *xs, int dir) 17852095c737Smglocker { 17862095c737Smglocker struct scsi_link *link = xs->sc_link; 17872095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 17882095c737Smglocker struct ufshci_ccb *ccb = xs->io; 17892095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 17902095c737Smglocker int error; 17912095c737Smglocker 17922095c737Smglocker if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) != dir) 17932095c737Smglocker goto error1; 17942095c737Smglocker 17956920fd9bSmglocker DPRINTF(3, "%s: %s, datalen=%d (%s)\n", __func__, 1796898e5d06Smglocker ISSET(xs->flags, SCSI_DATA_IN) ? "READ" : "WRITE", xs->datalen, 17972095c737Smglocker ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 17982095c737Smglocker 17992095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 18002095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 18010fe5d515Smglocker if (error) { 18022095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 18032095c737Smglocker goto error1; 18042095c737Smglocker } 18052095c737Smglocker 18062095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 18072095c737Smglocker ISSET(xs->flags, SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : 18082095c737Smglocker BUS_DMASYNC_PREWRITE); 18092095c737Smglocker 18102095c737Smglocker ccb->ccb_cookie = xs; 18112095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 18122095c737Smglocker 1813a2f0dcb2Smglocker if (dir == SCSI_DATA_IN) 181412f70f1cSmglocker error = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_IN); 1815a2f0dcb2Smglocker else 181612f70f1cSmglocker error = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_OUT); 18170fe5d515Smglocker if (error) 18182095c737Smglocker goto error2; 18192095c737Smglocker 18202095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 182193bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 18222095c737Smglocker ccb->ccb_done(sc, ccb); 18232095c737Smglocker return; 18242095c737Smglocker } 18252095c737Smglocker goto error2; 18262095c737Smglocker } 18272095c737Smglocker 18282095c737Smglocker return; 18292095c737Smglocker 18302095c737Smglocker error2: 18312095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 18322095c737Smglocker ccb->ccb_cookie = NULL; 183332835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 18342095c737Smglocker ccb->ccb_done = NULL; 18352095c737Smglocker error1: 18362095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 18372095c737Smglocker scsi_done(xs); 18382095c737Smglocker } 18392095c737Smglocker 18402095c737Smglocker void 18412095c737Smglocker ufshci_scsi_io_done(struct ufshci_softc *sc, struct ufshci_ccb *ccb) 18422095c737Smglocker { 18432095c737Smglocker struct scsi_xfer *xs = ccb->ccb_cookie; 18442095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 18453bc7f528Smglocker struct ufshci_ucd *ucd; 18463bc7f528Smglocker struct ufshci_utrd *utrd; 18470b701acdSmglocker 18482095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 18492095c737Smglocker ISSET(xs->flags, SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : 18502095c737Smglocker BUS_DMASYNC_POSTWRITE); 18512095c737Smglocker 18522095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 18532095c737Smglocker 18543bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 18553bc7f528Smglocker sizeof(*ucd) * ccb->ccb_slot, sizeof(*ucd), 18563bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18573bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 18583bc7f528Smglocker sizeof(*utrd) * ccb->ccb_slot, sizeof(*utrd), 18593bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18603bc7f528Smglocker 1861effc6b3aSmglocker /* TODO: Do more checks on the Response UPIU in case of errors? */ 1862effc6b3aSmglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1863effc6b3aSmglocker utrd += ccb->ccb_slot; 1864effc6b3aSmglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1865effc6b3aSmglocker ucd += ccb->ccb_slot; 1866effc6b3aSmglocker if (utrd->dw2 != UFSHCI_UTRD_DW2_OCS_SUCCESS) { 1867effc6b3aSmglocker printf("%s: error: slot=%d, ocs=0x%x, rsp-tc=0x%x\n", 1868effc6b3aSmglocker __func__, ccb->ccb_slot, utrd->dw2, ucd->rsp.hdr.tc); 1869effc6b3aSmglocker } 1870effc6b3aSmglocker 18712095c737Smglocker ccb->ccb_cookie = NULL; 187212f70f1cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 18732095c737Smglocker ccb->ccb_done = NULL; 18742095c737Smglocker 1875effc6b3aSmglocker xs->error = (utrd->dw2 == UFSHCI_UTRD_DW2_OCS_SUCCESS) ? 1876effc6b3aSmglocker XS_NOERROR : XS_DRIVER_STUFFUP; 18772095c737Smglocker xs->status = SCSI_OK; 18782095c737Smglocker xs->resid = 0; 18792095c737Smglocker scsi_done(xs); 18802095c737Smglocker } 18812095c737Smglocker 18822095c737Smglocker void 18832095c737Smglocker ufshci_scsi_done(struct ufshci_softc *sc, struct ufshci_ccb *ccb) 18842095c737Smglocker { 18852095c737Smglocker struct scsi_xfer *xs = ccb->ccb_cookie; 18863bc7f528Smglocker struct ufshci_ucd *ucd; 18873bc7f528Smglocker struct ufshci_utrd *utrd; 18883bc7f528Smglocker 18893bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 18903bc7f528Smglocker sizeof(*ucd) * ccb->ccb_slot, sizeof(*ucd), 18913bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18923bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 18933bc7f528Smglocker sizeof(*utrd) * ccb->ccb_slot, sizeof(*utrd), 18943bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18952095c737Smglocker 1896effc6b3aSmglocker /* TODO: Do more checks on the Response UPIU in case of errors? */ 1897effc6b3aSmglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1898effc6b3aSmglocker utrd += ccb->ccb_slot; 1899effc6b3aSmglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1900effc6b3aSmglocker ucd += ccb->ccb_slot; 1901effc6b3aSmglocker if (utrd->dw2 != UFSHCI_UTRD_DW2_OCS_SUCCESS) { 1902effc6b3aSmglocker printf("%s: error: slot=%d, ocs=0x%x, rsp-tc=0x%x\n", 1903effc6b3aSmglocker __func__, ccb->ccb_slot, utrd->dw2, ucd->rsp.hdr.tc); 1904effc6b3aSmglocker } 1905effc6b3aSmglocker 19062095c737Smglocker ccb->ccb_cookie = NULL; 190712f70f1cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 19082095c737Smglocker ccb->ccb_done = NULL; 19092095c737Smglocker 1910effc6b3aSmglocker xs->error = (utrd->dw2 == UFSHCI_UTRD_DW2_OCS_SUCCESS) ? 1911effc6b3aSmglocker XS_NOERROR : XS_DRIVER_STUFFUP; 19122095c737Smglocker xs->status = SCSI_OK; 19132095c737Smglocker xs->resid = 0; 19142095c737Smglocker scsi_done(xs); 19152095c737Smglocker } 191690de4d2dSmglocker 191790de4d2dSmglocker #if HIBERNATE 191890de4d2dSmglocker int 191990de4d2dSmglocker ufshci_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, 192090de4d2dSmglocker int op, void *page) 192190de4d2dSmglocker { 192290de4d2dSmglocker struct ufshci_hibernate_page { 192390de4d2dSmglocker struct ufshci_utrd utrd; 192490de4d2dSmglocker struct ufshci_ucd ucd; 192590de4d2dSmglocker 192690de4d2dSmglocker struct ufshci_softc *sc; /* Copy of softc */ 192790de4d2dSmglocker 192890de4d2dSmglocker daddr_t poffset; /* Start of SWAP partition */ 192990de4d2dSmglocker size_t psize; /* Size of SWAP partition */ 193090de4d2dSmglocker uint32_t secsize; /* Our sector size */ 193190de4d2dSmglocker } *my = page; 193290de4d2dSmglocker paddr_t data_phys, page_phys; 193390de4d2dSmglocker uint64_t data_bus_phys, page_bus_phys; 193490de4d2dSmglocker uint64_t timeout_us; 193590de4d2dSmglocker int off, len, slot; 193690de4d2dSmglocker uint32_t blocks, reg; 193790de4d2dSmglocker uint64_t lba; 193890de4d2dSmglocker 193990de4d2dSmglocker if (op == HIB_INIT) { 194090de4d2dSmglocker struct device *disk; 194190de4d2dSmglocker struct device *scsibus; 194290de4d2dSmglocker extern struct cfdriver sd_cd; 194390de4d2dSmglocker 194490de4d2dSmglocker /* Find ufshci softc. */ 194590de4d2dSmglocker disk = disk_lookup(&sd_cd, DISKUNIT(dev)); 194690de4d2dSmglocker if (disk == NULL) 194790de4d2dSmglocker return ENOTTY; 194890de4d2dSmglocker scsibus = disk->dv_parent; 194990de4d2dSmglocker my->sc = (struct ufshci_softc *)disk->dv_parent->dv_parent; 195090de4d2dSmglocker 195190de4d2dSmglocker /* Stop run queues and disable interrupts. */ 195290de4d2dSmglocker ufshci_disable(my->sc); 195390de4d2dSmglocker 195490de4d2dSmglocker /* Tell the controler the new hibernate UTRD address. */ 195590de4d2dSmglocker pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys); 195690de4d2dSmglocker page_bus_phys = page_phys + ((void *)&my->utrd - page); 195790de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLBA, 195890de4d2dSmglocker (uint32_t)page_bus_phys); 195990de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLBAU, 196090de4d2dSmglocker (uint32_t)(page_bus_phys >> 32)); 196190de4d2dSmglocker 196290de4d2dSmglocker /* Start run queues. */ 196390de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTMRLRSR, 196490de4d2dSmglocker UFSHCI_REG_UTMRLRSR_START); 196590de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLRSR, 196690de4d2dSmglocker UFSHCI_REG_UTRLRSR_START); 196790de4d2dSmglocker 196890de4d2dSmglocker my->poffset = blkno; 196990de4d2dSmglocker my->psize = size; 197090de4d2dSmglocker my->secsize = UFSHCI_LBS; 197190de4d2dSmglocker 197290de4d2dSmglocker return 0; 197390de4d2dSmglocker } 197490de4d2dSmglocker 197590de4d2dSmglocker if (op != HIB_W) 197690de4d2dSmglocker return 0; 197790de4d2dSmglocker 197890de4d2dSmglocker if (blkno + (size / DEV_BSIZE) > my->psize) 197990de4d2dSmglocker return E2BIG; 198090de4d2dSmglocker blocks = size / my->secsize; 198190de4d2dSmglocker lba = (blkno + my->poffset) / (my->secsize / DEV_BSIZE); 198290de4d2dSmglocker 198390de4d2dSmglocker /* 198490de4d2dSmglocker * The following code is a ripped down version of ufshci_utr_cmd_io() 198590de4d2dSmglocker * adapted for hibernate. 198690de4d2dSmglocker */ 198790de4d2dSmglocker slot = 0; /* We only use the first slot for hibernate */ 198890de4d2dSmglocker 198990de4d2dSmglocker memset(&my->utrd, 0, sizeof(struct ufshci_utrd)); 199090de4d2dSmglocker 199190de4d2dSmglocker my->utrd.dw0 = UFSHCI_UTRD_DW0_CT_UFS; 199290de4d2dSmglocker my->utrd.dw0 |= UFSHCI_UTRD_DW0_DD_I2T; 199390de4d2dSmglocker my->utrd.dw0 |= UFSHCI_UTRD_DW0_I_REG; 199490de4d2dSmglocker my->utrd.dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 199590de4d2dSmglocker 199690de4d2dSmglocker memset(&my->ucd, 0, sizeof(struct ufshci_ucd)); 199790de4d2dSmglocker 199890de4d2dSmglocker my->ucd.cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 199990de4d2dSmglocker my->ucd.cmd.hdr.flags = (1 << 5); /* Bit-5 = Write */ 200090de4d2dSmglocker 200190de4d2dSmglocker my->ucd.cmd.hdr.lun = 0; 200290de4d2dSmglocker my->ucd.cmd.hdr.task_tag = slot; 200390de4d2dSmglocker my->ucd.cmd.hdr.cmd_set_type = 0; /* SCSI command */ 200490de4d2dSmglocker my->ucd.cmd.hdr.query = 0; 200590de4d2dSmglocker my->ucd.cmd.hdr.response = 0; 200690de4d2dSmglocker my->ucd.cmd.hdr.status = 0; 200790de4d2dSmglocker my->ucd.cmd.hdr.ehs_len = 0; 200890de4d2dSmglocker my->ucd.cmd.hdr.device_info = 0; 200990de4d2dSmglocker my->ucd.cmd.hdr.ds_len = 0; 201090de4d2dSmglocker 201190de4d2dSmglocker my->ucd.cmd.expected_xfer_len = htobe32(UFSHCI_LBS * blocks); 201290de4d2dSmglocker my->ucd.cmd.cdb[0] = WRITE_10; /* 0x2a */ 201390de4d2dSmglocker my->ucd.cmd.cdb[1] = (1 << 3); /* FUA: Force Unit Access */ 201490de4d2dSmglocker my->ucd.cmd.cdb[2] = (lba >> 24) & 0xff; 201590de4d2dSmglocker my->ucd.cmd.cdb[3] = (lba >> 16) & 0xff; 201690de4d2dSmglocker my->ucd.cmd.cdb[4] = (lba >> 8) & 0xff; 201790de4d2dSmglocker my->ucd.cmd.cdb[5] = (lba >> 0) & 0xff; 201890de4d2dSmglocker my->ucd.cmd.cdb[7] = (blocks >> 8) & 0xff; 201990de4d2dSmglocker my->ucd.cmd.cdb[8] = (blocks >> 0) & 0xff; 202090de4d2dSmglocker 202190de4d2dSmglocker pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys); 202290de4d2dSmglocker page_bus_phys = page_phys + ((void *)&my->ucd - page); 202390de4d2dSmglocker my->utrd.dw4 = (uint32_t)page_bus_phys; 202490de4d2dSmglocker my->utrd.dw5 = (uint32_t)(page_bus_phys >> 32); 202590de4d2dSmglocker 202690de4d2dSmglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 202790de4d2dSmglocker my->utrd.dw6 = UFSHCI_UTRD_DW6_RUO(off); 202890de4d2dSmglocker 202990de4d2dSmglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 203090de4d2dSmglocker my->utrd.dw6 |= UFSHCI_UTRD_DW6_RUL(len); 203190de4d2dSmglocker 203290de4d2dSmglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 203390de4d2dSmglocker my->utrd.dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 203490de4d2dSmglocker 203590de4d2dSmglocker my->utrd.dw7 |= UFSHCI_UTRD_DW7_PRDTL(1); /* dm_nsegs */ 203690de4d2dSmglocker 203790de4d2dSmglocker pmap_extract(pmap_kernel(), (vaddr_t)addr, &data_phys); 203890de4d2dSmglocker data_bus_phys = data_phys; 203990de4d2dSmglocker my->ucd.prdt[0].dw0 = (uint32_t)data_bus_phys; 204090de4d2dSmglocker my->ucd.prdt[0].dw1 = (uint32_t)(data_bus_phys >> 32); 204190de4d2dSmglocker my->ucd.prdt[0].dw2 = 0; 204290de4d2dSmglocker my->ucd.prdt[0].dw3 = size - 1; /* ds_len */ 204390de4d2dSmglocker 204490de4d2dSmglocker if (UFSHCI_READ_4(my->sc, UFSHCI_REG_UTRLRSR) != 1) 204590de4d2dSmglocker return EIO; 204690de4d2dSmglocker 204790de4d2dSmglocker ufshci_doorbell_write(my->sc, slot); 204890de4d2dSmglocker 204990de4d2dSmglocker /* ufshci_doorbell_poll() adaption for hibernate. */ 205090de4d2dSmglocker for (timeout_us = 1000000 * 1000; timeout_us != 0; 205190de4d2dSmglocker timeout_us -= 1000) { 205290de4d2dSmglocker reg = UFSHCI_READ_4(my->sc, UFSHCI_REG_UTRLDBR); 205390de4d2dSmglocker if ((reg & (1U << slot)) == 0) 205490de4d2dSmglocker break; 205590de4d2dSmglocker delay(1000); 205690de4d2dSmglocker } 205790de4d2dSmglocker if (timeout_us == 0) 205890de4d2dSmglocker return EIO; 205990de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLCNR, (1U << slot)); 206090de4d2dSmglocker 206190de4d2dSmglocker /* Check if the command was succesfully executed. */ 206290de4d2dSmglocker if (my->utrd.dw2 != UFSHCI_UTRD_DW2_OCS_SUCCESS) 206390de4d2dSmglocker return EIO; 206490de4d2dSmglocker 206590de4d2dSmglocker return 0; 206690de4d2dSmglocker } 206790de4d2dSmglocker #endif /* HIBERNATE */ 2068