1*3d5e1d3eSmglocker /* $OpenBSD: ufshci.c,v 1.46 2025/01/18 19:42:39 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 24*3d5e1d3eSmglocker #include "kstat.h" 25*3d5e1d3eSmglocker 262095c737Smglocker #include <sys/param.h> 272095c737Smglocker #include <sys/systm.h> 282095c737Smglocker #include <sys/buf.h> 292095c737Smglocker #include <sys/kernel.h> 302095c737Smglocker #include <sys/malloc.h> 312095c737Smglocker #include <sys/device.h> 322095c737Smglocker #include <sys/queue.h> 332095c737Smglocker #include <sys/mutex.h> 342095c737Smglocker #include <sys/pool.h> 35*3d5e1d3eSmglocker #include <sys/kstat.h> 362095c737Smglocker 372095c737Smglocker #include <sys/atomic.h> 382095c737Smglocker 392095c737Smglocker #include <machine/bus.h> 402095c737Smglocker 412095c737Smglocker #include <scsi/scsi_all.h> 422095c737Smglocker #include <scsi/scsi_disk.h> 432095c737Smglocker #include <scsi/scsiconf.h> 442095c737Smglocker 452095c737Smglocker #include <dev/ic/ufshcivar.h> 462095c737Smglocker #include <dev/ic/ufshcireg.h> 472095c737Smglocker 4890de4d2dSmglocker #ifdef HIBERNATE 4990de4d2dSmglocker #include <uvm/uvm_extern.h> 5090de4d2dSmglocker #include <sys/hibernate.h> 5190de4d2dSmglocker #include <sys/disklabel.h> 5290de4d2dSmglocker #include <sys/disk.h> 5390de4d2dSmglocker #endif 5490de4d2dSmglocker 552095c737Smglocker #ifdef UFSHCI_DEBUG 563ad2ef2dSmglocker int ufshci_debug = 1; 572095c737Smglocker #endif 582095c737Smglocker 592095c737Smglocker struct cfdriver ufshci_cd = { 602095c737Smglocker NULL, "ufshci", DV_DULL 612095c737Smglocker }; 622095c737Smglocker 632095c737Smglocker int ufshci_reset(struct ufshci_softc *); 6481f602bfSmglocker int ufshci_is_poll(struct ufshci_softc *, uint32_t); 652095c737Smglocker struct ufshci_dmamem *ufshci_dmamem_alloc(struct ufshci_softc *, size_t); 662095c737Smglocker void ufshci_dmamem_free(struct ufshci_softc *, 672095c737Smglocker struct ufshci_dmamem *); 6890de4d2dSmglocker int ufshci_alloc(struct ufshci_softc *); 692095c737Smglocker int ufshci_init(struct ufshci_softc *); 700fe5d515Smglocker void ufshci_disable(struct ufshci_softc *); 712095c737Smglocker int ufshci_doorbell_read(struct ufshci_softc *); 7212f70f1cSmglocker void ufshci_doorbell_write(struct ufshci_softc *, int); 7393bf5056Smglocker int ufshci_doorbell_poll(struct ufshci_softc *, int, 7493bf5056Smglocker uint32_t); 7512f70f1cSmglocker int ufshci_utr_cmd_nop(struct ufshci_softc *, 7612f70f1cSmglocker struct ufshci_ccb *, struct scsi_xfer *); 772095c737Smglocker int ufshci_utr_cmd_lun(struct ufshci_softc *, 7844c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 792095c737Smglocker int ufshci_utr_cmd_inquiry(struct ufshci_softc *, 8044c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 812095c737Smglocker int ufshci_utr_cmd_capacity16(struct ufshci_softc *, 8244c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 832095c737Smglocker int ufshci_utr_cmd_capacity(struct ufshci_softc *, 8444c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *); 85a2f0dcb2Smglocker int ufshci_utr_cmd_io(struct ufshci_softc *, 86a2f0dcb2Smglocker struct ufshci_ccb *, struct scsi_xfer *, int); 872095c737Smglocker int ufshci_utr_cmd_sync(struct ufshci_softc *, 8844c52867Smglocker struct ufshci_ccb *, struct scsi_xfer *, 8944c52867Smglocker uint32_t, uint16_t); 900fe5d515Smglocker void ufshci_xfer_complete(struct ufshci_softc *); 912095c737Smglocker 922095c737Smglocker /* SCSI */ 932095c737Smglocker int ufshci_ccb_alloc(struct ufshci_softc *, int); 942095c737Smglocker void *ufshci_ccb_get(void *); 952095c737Smglocker void ufshci_ccb_put(void *, void *); 962095c737Smglocker void ufshci_ccb_free(struct ufshci_softc*, int); 972095c737Smglocker 982095c737Smglocker void ufshci_scsi_cmd(struct scsi_xfer *); 992095c737Smglocker 1002095c737Smglocker void ufshci_scsi_inquiry(struct scsi_xfer *); 1012095c737Smglocker void ufshci_scsi_capacity16(struct scsi_xfer *); 1022095c737Smglocker void ufshci_scsi_capacity(struct scsi_xfer *); 1032095c737Smglocker void ufshci_scsi_sync(struct scsi_xfer *); 1042095c737Smglocker void ufshci_scsi_io(struct scsi_xfer *, int); 1052095c737Smglocker void ufshci_scsi_io_done(struct ufshci_softc *, 1062095c737Smglocker struct ufshci_ccb *); 1072095c737Smglocker void ufshci_scsi_done(struct ufshci_softc *, 1082095c737Smglocker struct ufshci_ccb *); 1092095c737Smglocker 110ce37c3ccSmglocker #ifdef HIBERNATE 11190de4d2dSmglocker int ufshci_hibernate_io(dev_t, daddr_t, vaddr_t, size_t, 11290de4d2dSmglocker int, void *); 11390de4d2dSmglocker #endif 11490de4d2dSmglocker 115*3d5e1d3eSmglocker #if NKSTAT > 0 116*3d5e1d3eSmglocker void ufshci_kstat_attach(struct ufshci_softc *); 117*3d5e1d3eSmglocker int ufshci_kstat_read_ccb(struct kstat *); 118*3d5e1d3eSmglocker int ufshci_kstat_read_slot(struct kstat *); 1193ad2ef2dSmglocker #endif 1203ad2ef2dSmglocker 1212095c737Smglocker const struct scsi_adapter ufshci_switch = { 1222095c737Smglocker ufshci_scsi_cmd, NULL, NULL, NULL, NULL 1232095c737Smglocker }; 1242095c737Smglocker 1252095c737Smglocker int 1262095c737Smglocker ufshci_intr(void *arg) 1272095c737Smglocker { 1282095c737Smglocker struct ufshci_softc *sc = arg; 1299251d8a3Smglocker uint32_t status, hcs; 1302095c737Smglocker int handled = 0; 1312095c737Smglocker 1322095c737Smglocker status = UFSHCI_READ_4(sc, UFSHCI_REG_IS); 1332095c737Smglocker 1342095c737Smglocker if (status == 0) 135c45d22d4Smglocker return handled; 1362095c737Smglocker 137dfeddfe6Smglocker /* ACK interrupt */ 138dfeddfe6Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IS, status); 139dfeddfe6Smglocker 1402095c737Smglocker if (status & UFSHCI_REG_IS_UCCS) { 1412095c737Smglocker handled = 1; 1422095c737Smglocker } 1432095c737Smglocker if (status & UFSHCI_REG_IS_UTRCS) { 14412f70f1cSmglocker ufshci_xfer_complete(sc); 14512f70f1cSmglocker 1462095c737Smglocker handled = 1; 1472095c737Smglocker } 1489251d8a3Smglocker /* If Auto-Hibernate raises an interrupt, it's to yield an error. */ 1499251d8a3Smglocker if (status & UFSHCI_REG_IS_UHES) { 1509251d8a3Smglocker hcs = UFSHCI_READ_4(sc, UFSHCI_REG_HCS); 1519251d8a3Smglocker printf("%s: Auto-Hibernate enter error UPMCRS=0x%x\n", 1529251d8a3Smglocker __func__, UFSHCI_REG_HCS_UPMCRS(hcs)); 153c45d22d4Smglocker handled = 1; 1549251d8a3Smglocker } 1559251d8a3Smglocker if (status & UFSHCI_REG_IS_UHXS) { 1569251d8a3Smglocker hcs = UFSHCI_READ_4(sc, UFSHCI_REG_HCS); 1579251d8a3Smglocker printf("%s: Auto-Hibernate exit error UPMCRS=0x%x\n", 1589251d8a3Smglocker __func__, UFSHCI_REG_HCS_UPMCRS(hcs)); 159c45d22d4Smglocker handled = 1; 1609251d8a3Smglocker } 1612095c737Smglocker 1622095c737Smglocker if (handled == 0) { 1632095c737Smglocker printf("%s: UNKNOWN interrupt, status=0x%08x\n", 1642095c737Smglocker sc->sc_dev.dv_xname, status); 1652095c737Smglocker } 1662095c737Smglocker 167c45d22d4Smglocker return handled; 1682095c737Smglocker } 1692095c737Smglocker 1702095c737Smglocker int 1712095c737Smglocker ufshci_attach(struct ufshci_softc *sc) 1722095c737Smglocker { 1732095c737Smglocker struct scsibus_attach_args saa; 1742095c737Smglocker 175e58b468bSmglocker mtx_init(&sc->sc_cmd_mtx, IPL_BIO); 1762095c737Smglocker mtx_init(&sc->sc_ccb_mtx, IPL_BIO); 1772095c737Smglocker SIMPLEQ_INIT(&sc->sc_ccb_list); 1782095c737Smglocker scsi_iopool_init(&sc->sc_iopool, sc, ufshci_ccb_get, ufshci_ccb_put); 1792095c737Smglocker 1800fe5d515Smglocker if (ufshci_reset(sc)) 1810fe5d515Smglocker return 1; 1822095c737Smglocker 1832095c737Smglocker sc->sc_ver = UFSHCI_READ_4(sc, UFSHCI_REG_VER); 1842095c737Smglocker printf(", UFSHCI %d.%d%d\n", 1852095c737Smglocker UFSHCI_REG_VER_MAJOR(sc->sc_ver), 1862095c737Smglocker UFSHCI_REG_VER_MINOR(sc->sc_ver), 1872095c737Smglocker UFSHCI_REG_VER_SUFFIX(sc->sc_ver)); 1882095c737Smglocker 1892095c737Smglocker sc->sc_cap = UFSHCI_READ_4(sc, UFSHCI_REG_CAP); 1902095c737Smglocker sc->sc_hcpid = UFSHCI_READ_4(sc, UFSHCI_REG_HCPID); 1912095c737Smglocker sc->sc_hcmid = UFSHCI_READ_4(sc, UFSHCI_REG_HCMID); 1922095c737Smglocker sc->sc_nutmrs = UFSHCI_REG_CAP_NUTMRS(sc->sc_cap) + 1; 1932095c737Smglocker sc->sc_rtt = UFSHCI_REG_CAP_RTT(sc->sc_cap) + 1; 1946920fd9bSmglocker sc->sc_nutrs = UFSHCI_REG_CAP_NUTRS(sc->sc_cap) + 1; 1956920fd9bSmglocker 1963ad2ef2dSmglocker #ifdef UFSHCI_DEBUG 1973ad2ef2dSmglocker printf("Capabilities (0x%08x):\n", sc->sc_cap); 1983ad2ef2dSmglocker printf(" CS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_CS ? 1 : 0); 1993ad2ef2dSmglocker printf(" UICDMETMS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_UICDMETMS ? 1 :0); 2003ad2ef2dSmglocker printf(" OODDS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_OODDS ? 1 : 0); 2013ad2ef2dSmglocker printf(" 64AS=%d\n", sc->sc_cap & UFSHCI_REG_CAP_64AS ? 1 : 0); 2023ad2ef2dSmglocker printf(" AUTOH8=%d\n", sc->sc_cap & UFSHCI_REG_AUTOH8 ? 1 : 0); 2033ad2ef2dSmglocker printf(" NUTMRS=%d\n", sc->sc_nutmrs); 2043ad2ef2dSmglocker printf(" RTT=%d\n", sc->sc_rtt); 2053ad2ef2dSmglocker printf(" NUTRS=%d\n", sc->sc_nutrs); 2063ad2ef2dSmglocker printf(" HCPID=0x%08x\n", sc->sc_hcpid); 2073ad2ef2dSmglocker printf("HCMID (0x%08x):\n", sc->sc_hcmid); 2083ad2ef2dSmglocker printf(" BI=0x%04x\n", UFSHCI_REG_HCMID_BI(sc->sc_hcmid)); 2093ad2ef2dSmglocker printf(" MIC=0x%04x\n", UFSHCI_REG_HCMID_MIC(sc->sc_hcmid)); 2103ad2ef2dSmglocker #endif 211d0fe8ebaSmglocker if (sc->sc_nutrs < UFSHCI_SLOTS_MIN || 212d0fe8ebaSmglocker sc->sc_nutrs > UFSHCI_SLOTS_MAX) { 213d0fe8ebaSmglocker printf("%s: Invalid NUTRS value %d (must be %d-%d)!\n", 214d0fe8ebaSmglocker sc->sc_dev.dv_xname, sc->sc_nutrs, 215d0fe8ebaSmglocker UFSHCI_SLOTS_MIN, UFSHCI_SLOTS_MAX); 2162095c737Smglocker return 1; 2172095c737Smglocker } 218d0fe8ebaSmglocker if (sc->sc_nutrs == UFSHCI_SLOTS_MAX) 219d0fe8ebaSmglocker sc->sc_iacth = UFSHCI_INTR_AGGR_COUNT_MAX; 220d0fe8ebaSmglocker else 221d0fe8ebaSmglocker sc->sc_iacth = sc->sc_nutrs; 2226920fd9bSmglocker DPRINTF(1, "Intr. aggr. counter threshold:\nIACTH=%d\n", sc->sc_iacth); 2232095c737Smglocker 224ceedf4ccSmglocker /* 225ceedf4ccSmglocker * XXX: 226ceedf4ccSmglocker * At the moment normal interrupts work better for us than interrupt 227ceedf4ccSmglocker * aggregation, because: 228ceedf4ccSmglocker * 229ceedf4ccSmglocker * 1. With interrupt aggregation enabled, the I/O performance 230ceedf4ccSmglocker * isn't better, but even slightly worse depending on the 231ceedf4ccSmglocker * UFS controller and architecture. 232ceedf4ccSmglocker * 2. With interrupt aggregation enabled we currently see 233ceedf4ccSmglocker * intermittent SCSI command stalling. Probably there is a 234ceedf4ccSmglocker * race condition where new SCSI commands are getting 235ceedf4ccSmglocker * scheduled, while we miss to reset the interrupt aggregation 236ceedf4ccSmglocker * counter/timer, which leaves us with no more interrupts 237ceedf4ccSmglocker * triggered. This needs to be fixed, but I couldn't figure 238ceedf4ccSmglocker * out yet how. 239ceedf4ccSmglocker */ 240ceedf4ccSmglocker #if 0 241ceedf4ccSmglocker sc->sc_flags |= UFSHCI_FLAGS_AGGR_INTR; /* Enable intr. aggregation */ 242ceedf4ccSmglocker #endif 24390de4d2dSmglocker /* Allocate the DMA buffers and initialize the controller. */ 2440fe5d515Smglocker if (ufshci_alloc(sc)) 2450fe5d515Smglocker return 1; 2460fe5d515Smglocker if (ufshci_init(sc)) 2470fe5d515Smglocker return 1; 2482095c737Smglocker 2492095c737Smglocker if (ufshci_ccb_alloc(sc, sc->sc_nutrs) != 0) { 2502095c737Smglocker printf("%s: %s: Can't allocate CCBs\n", 2512095c737Smglocker sc->sc_dev.dv_xname, __func__); 2522095c737Smglocker return 1; 2532095c737Smglocker } 2542095c737Smglocker 2559251d8a3Smglocker /* Enable Auto-Hibernate Idle Timer (AHIT) and set it to 150ms. */ 2569251d8a3Smglocker if (sc->sc_cap & UFSHCI_REG_AUTOH8) { 2579251d8a3Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_AHIT, 2589251d8a3Smglocker UFSHCI_REG_AHIT_TS(UFSHCI_REG_AHIT_TS_1MS) | 150); 2599251d8a3Smglocker } 2609251d8a3Smglocker 2612095c737Smglocker /* Attach to SCSI layer */ 2622095c737Smglocker saa.saa_adapter = &ufshci_switch; 2632095c737Smglocker saa.saa_adapter_softc = sc; 2642ee5e265Smglocker saa.saa_adapter_buswidth = UFSHCI_TARGETS_MAX + 1; 2652ee5e265Smglocker saa.saa_luns = 1; 2662095c737Smglocker saa.saa_adapter_target = 0; 2672095c737Smglocker saa.saa_openings = sc->sc_nutrs; 2682095c737Smglocker saa.saa_pool = &sc->sc_iopool; 2692095c737Smglocker saa.saa_quirks = saa.saa_flags = 0; 2702095c737Smglocker saa.saa_wwpn = saa.saa_wwnn = 0; 271*3d5e1d3eSmglocker #if NKSTAT > 0 272*3d5e1d3eSmglocker ufshci_kstat_attach(sc); 2733ad2ef2dSmglocker #endif 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 3162095c737Smglocker for (i = 0; i < retry; i++) { 3172095c737Smglocker status = UFSHCI_READ_4(sc, UFSHCI_REG_IS); 31881f602bfSmglocker if (status & type) 3192095c737Smglocker break; 3202095c737Smglocker delay(10); 3212095c737Smglocker } 3222095c737Smglocker if (i == retry) { 3232095c737Smglocker printf("%s: %s: timeout\n", sc->sc_dev.dv_xname, __func__); 3240fe5d515Smglocker return 1; 3252095c737Smglocker } 3266920fd9bSmglocker DPRINTF(3, "%s: completed after %d retries\n", __func__, i); 3272095c737Smglocker 3282095c737Smglocker /* ACK interrupt */ 3292095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IS, status); 3302095c737Smglocker 3312095c737Smglocker return 0; 3322095c737Smglocker } 3332095c737Smglocker 3342095c737Smglocker struct ufshci_dmamem * 3352095c737Smglocker ufshci_dmamem_alloc(struct ufshci_softc *sc, size_t size) 3362095c737Smglocker { 3372095c737Smglocker struct ufshci_dmamem *udm; 3382095c737Smglocker int nsegs; 3392095c737Smglocker 3402095c737Smglocker udm = malloc(sizeof(*udm), M_DEVBUF, M_WAITOK | M_ZERO); 3412095c737Smglocker if (udm == NULL) 3422095c737Smglocker return NULL; 3432095c737Smglocker 3442095c737Smglocker udm->udm_size = size; 3452095c737Smglocker 3462095c737Smglocker if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 3474acd6882Smglocker BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | 348c476ff44Sjsg ((sc->sc_cap & UFSHCI_REG_CAP_64AS) ? BUS_DMA_64BIT : 0), 3492095c737Smglocker &udm->udm_map) != 0) 3502095c737Smglocker goto udmfree; 3512095c737Smglocker 3522095c737Smglocker if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &udm->udm_seg, 3532095c737Smglocker 1, &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO) != 0) 3542095c737Smglocker goto destroy; 3552095c737Smglocker 3562095c737Smglocker if (bus_dmamem_map(sc->sc_dmat, &udm->udm_seg, nsegs, size, 3572095c737Smglocker &udm->udm_kva, BUS_DMA_WAITOK) != 0) 3582095c737Smglocker goto free; 3592095c737Smglocker 3602095c737Smglocker if (bus_dmamap_load(sc->sc_dmat, udm->udm_map, udm->udm_kva, size, 3612095c737Smglocker NULL, BUS_DMA_WAITOK) != 0) 3622095c737Smglocker goto unmap; 3632095c737Smglocker 3646920fd9bSmglocker DPRINTF(2, "%s: size=%lu, page_size=%d, nsegs=%d\n", 3652095c737Smglocker __func__, size, PAGE_SIZE, nsegs); 3662095c737Smglocker 3672095c737Smglocker return udm; 3682095c737Smglocker 3692095c737Smglocker unmap: 3702095c737Smglocker bus_dmamem_unmap(sc->sc_dmat, udm->udm_kva, size); 3712095c737Smglocker free: 3722095c737Smglocker bus_dmamem_free(sc->sc_dmat, &udm->udm_seg, 1); 3732095c737Smglocker destroy: 3742095c737Smglocker bus_dmamap_destroy(sc->sc_dmat, udm->udm_map); 3752095c737Smglocker udmfree: 3762095c737Smglocker free(udm, M_DEVBUF, sizeof(*udm)); 3772095c737Smglocker 3782095c737Smglocker return NULL; 3792095c737Smglocker } 3802095c737Smglocker 3812095c737Smglocker void 3822095c737Smglocker ufshci_dmamem_free(struct ufshci_softc *sc, struct ufshci_dmamem *udm) 3832095c737Smglocker { 3842095c737Smglocker bus_dmamap_unload(sc->sc_dmat, udm->udm_map); 3852095c737Smglocker bus_dmamem_unmap(sc->sc_dmat, udm->udm_kva, udm->udm_size); 3862095c737Smglocker bus_dmamem_free(sc->sc_dmat, &udm->udm_seg, 1); 3872095c737Smglocker bus_dmamap_destroy(sc->sc_dmat, udm->udm_map); 3882095c737Smglocker free(udm, M_DEVBUF, sizeof(*udm)); 3892095c737Smglocker } 3902095c737Smglocker 3912095c737Smglocker int 39290de4d2dSmglocker ufshci_alloc(struct ufshci_softc *sc) 39390de4d2dSmglocker { 39490de4d2dSmglocker /* 7.1.1 Host Controller Initialization: 13) */ 39590de4d2dSmglocker sc->sc_dmamem_utmrd = ufshci_dmamem_alloc(sc, 39690de4d2dSmglocker sizeof(struct ufshci_utmrd) * sc->sc_nutmrs); 39790de4d2dSmglocker if (sc->sc_dmamem_utmrd == NULL) { 39890de4d2dSmglocker printf("%s: Can't allocate DMA memory for UTMRD\n", 39990de4d2dSmglocker sc->sc_dev.dv_xname); 4000fe5d515Smglocker return 1; 40190de4d2dSmglocker } 40290de4d2dSmglocker 40390de4d2dSmglocker /* 7.1.1 Host Controller Initialization: 15) */ 40490de4d2dSmglocker sc->sc_dmamem_utrd = ufshci_dmamem_alloc(sc, 40590de4d2dSmglocker sizeof(struct ufshci_utrd) * sc->sc_nutrs); 40690de4d2dSmglocker if (sc->sc_dmamem_utrd == NULL) { 40790de4d2dSmglocker printf("%s: Can't allocate DMA memory for UTRD\n", 40890de4d2dSmglocker sc->sc_dev.dv_xname); 4090fe5d515Smglocker return 1; 41090de4d2dSmglocker } 41190de4d2dSmglocker 41290de4d2dSmglocker /* Allocate UCDs. */ 41390de4d2dSmglocker sc->sc_dmamem_ucd = ufshci_dmamem_alloc(sc, 41490de4d2dSmglocker sizeof(struct ufshci_ucd) * sc->sc_nutrs); 41590de4d2dSmglocker if (sc->sc_dmamem_ucd == NULL) { 41690de4d2dSmglocker printf("%s: Can't allocate DMA memory for UCD\n", 41790de4d2dSmglocker sc->sc_dev.dv_xname); 4180fe5d515Smglocker return 1; 41990de4d2dSmglocker } 42090de4d2dSmglocker 42190de4d2dSmglocker return 0; 42290de4d2dSmglocker } 42390de4d2dSmglocker 42490de4d2dSmglocker int 4252095c737Smglocker ufshci_init(struct ufshci_softc *sc) 4262095c737Smglocker { 4272095c737Smglocker uint32_t reg; 4282095c737Smglocker uint64_t dva; 4292095c737Smglocker 4302095c737Smglocker /* 4312095c737Smglocker * 7.1.1 Host Controller Initialization: 4) 4322095c737Smglocker * TODO: Do we need to set DME_SET? 4332095c737Smglocker */ 4342095c737Smglocker 4352095c737Smglocker /* 7.1.1 Host Controller Initialization: 5) */ 4369251d8a3Smglocker if (sc->sc_cap & UFSHCI_REG_AUTOH8) { 4379251d8a3Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, 4389251d8a3Smglocker UFSHCI_REG_IE_UTRCE | UFSHCI_REG_IE_UTMRCE | 4399251d8a3Smglocker UFSHCI_REG_IS_UHES | UFSHCI_REG_IS_UHXS); 4409251d8a3Smglocker } else { 4412095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, 4422095c737Smglocker UFSHCI_REG_IE_UTRCE | UFSHCI_REG_IE_UTMRCE); 4439251d8a3Smglocker } 4442095c737Smglocker 4452095c737Smglocker /* 7.1.1 Host Controller Initialization: 6) */ 4462095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UICCMD, 4472095c737Smglocker UFSHCI_REG_UICCMD_CMDOP_DME_LINKSTARTUP); 4480fe5d515Smglocker if (ufshci_is_poll(sc, UFSHCI_REG_IS_UCCS)) 4490fe5d515Smglocker return 1; 4502095c737Smglocker 4512095c737Smglocker /* 4522095c737Smglocker * 7.1.1 Host Controller Initialization: 7), 8), 9) 4532095c737Smglocker * TODO: Implement retry in case UFSHCI_REG_HCS returns 0 4542095c737Smglocker */ 4552095c737Smglocker reg = UFSHCI_READ_4(sc, UFSHCI_REG_HCS); 4562095c737Smglocker if (reg & UFSHCI_REG_HCS_DP) 4576920fd9bSmglocker DPRINTF(2, "%s: Device Presence SET\n", __func__); 4582095c737Smglocker else 4596920fd9bSmglocker DPRINTF(2, "%s: Device Presence NOT SET\n", __func__); 4602095c737Smglocker 4612095c737Smglocker /* 4622095c737Smglocker * 7.1.1 Host Controller Initialization: 10) 4632095c737Smglocker * TODO: Enable additional interrupt on the IE register 4642095c737Smglocker */ 4652095c737Smglocker 4662095c737Smglocker /* 7.1.1 Host Controller Initialization: 11) */ 467ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) { 468f0ed5855Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR, 469f0ed5855Smglocker UFSHCI_REG_UTRIACR_IAEN | 470f0ed5855Smglocker UFSHCI_REG_UTRIACR_IAPWEN | 471f0ed5855Smglocker UFSHCI_REG_UTRIACR_CTR | 472f0ed5855Smglocker UFSHCI_REG_UTRIACR_IACTH(sc->sc_iacth) | 473f0ed5855Smglocker UFSHCI_REG_UTRIACR_IATOVAL(UFSHCI_INTR_AGGR_TIMEOUT)); 474ceedf4ccSmglocker } else { 475ceedf4ccSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR, 0); 476ceedf4ccSmglocker } 4772095c737Smglocker 4782095c737Smglocker /* 4792095c737Smglocker * 7.1.1 Host Controller Initialization: 12) 4802095c737Smglocker * TODO: More UIC commands to issue? 4812095c737Smglocker */ 4822095c737Smglocker 4832095c737Smglocker /* 7.1.1 Host Controller Initialization: 14) */ 4842095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_utmrd); 4856920fd9bSmglocker DPRINTF(2, "%s: utmrd dva=%llu\n", __func__, dva); 4862095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLBA, (uint32_t)dva); 4872095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLBAU, (uint32_t)(dva >> 32)); 4882095c737Smglocker 4892095c737Smglocker /* 7.1.1 Host Controller Initialization: 16) */ 4902095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_utrd); 4916920fd9bSmglocker DPRINTF(2, "%s: utrd dva=%llu\n", __func__, dva); 4922095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLBA, (uint32_t)dva); 4932095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLBAU, (uint32_t)(dva >> 32)); 4942095c737Smglocker 4952095c737Smglocker /* 7.1.1 Host Controller Initialization: 17) */ 4962095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLRSR, UFSHCI_REG_UTMRLRSR_START); 4972095c737Smglocker 4982095c737Smglocker /* 7.1.1 Host Controller Initialization: 18) */ 4992095c737Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLRSR, UFSHCI_REG_UTRLRSR_START); 5002095c737Smglocker 5012095c737Smglocker /* 7.1.1 Host Controller Initialization: 19) */ 5022095c737Smglocker /* TODO: bMaxNumOfRTT will be set as the minimum value of 5032095c737Smglocker * bDeviceRTTCap and NORTT. ??? 5042095c737Smglocker */ 5052095c737Smglocker 5062095c737Smglocker return 0; 5072095c737Smglocker } 5082095c737Smglocker 5090fe5d515Smglocker void 51090de4d2dSmglocker ufshci_disable(struct ufshci_softc *sc) 51190de4d2dSmglocker { 51290de4d2dSmglocker /* Stop run queues. */ 51390de4d2dSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTMRLRSR, UFSHCI_REG_UTMRLRSR_STOP); 51490de4d2dSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLRSR, UFSHCI_REG_UTRLRSR_STOP); 51590de4d2dSmglocker 51690de4d2dSmglocker /* Disable interrupts. */ 51790de4d2dSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_IE, 0); 51890de4d2dSmglocker } 51990de4d2dSmglocker 52090de4d2dSmglocker int 5212095c737Smglocker ufshci_doorbell_read(struct ufshci_softc *sc) 5222095c737Smglocker { 5232095c737Smglocker uint32_t reg; 5242095c737Smglocker 5252095c737Smglocker reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR); 5262095c737Smglocker 5272095c737Smglocker return reg; 5282095c737Smglocker } 5292095c737Smglocker 53012f70f1cSmglocker void 53112f70f1cSmglocker ufshci_doorbell_write(struct ufshci_softc *sc, int slot) 53212f70f1cSmglocker { 53312f70f1cSmglocker uint32_t reg; 53412f70f1cSmglocker 535c4dc76d1Smglocker reg = (1U << slot); 53612f70f1cSmglocker 53712f70f1cSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLDBR, reg); 53812f70f1cSmglocker } 53912f70f1cSmglocker 5402095c737Smglocker int 54193bf5056Smglocker ufshci_doorbell_poll(struct ufshci_softc *sc, int slot, uint32_t timeout_ms) 5422095c737Smglocker { 5432095c737Smglocker uint32_t reg; 54493bf5056Smglocker uint64_t timeout_us; 5452095c737Smglocker 54693bf5056Smglocker for (timeout_us = timeout_ms * 1000; timeout_us != 0; 54793bf5056Smglocker timeout_us -= 1000) { 5482095c737Smglocker reg = UFSHCI_READ_4(sc, UFSHCI_REG_UTRLDBR); 549c4dc76d1Smglocker if ((reg & (1U << slot)) == 0) 5502095c737Smglocker break; 55193bf5056Smglocker delay(1000); 5522095c737Smglocker } 55393bf5056Smglocker if (timeout_us == 0) { 5542095c737Smglocker printf("%s: %s: timeout\n", sc->sc_dev.dv_xname, __func__); 5550fe5d515Smglocker return 1; 5562095c737Smglocker } 5572095c737Smglocker 5582095c737Smglocker return 0; 5592095c737Smglocker } 5602095c737Smglocker 5612095c737Smglocker int 56212f70f1cSmglocker ufshci_utr_cmd_nop(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 56312f70f1cSmglocker struct scsi_xfer *xs) 5642095c737Smglocker { 5652095c737Smglocker int slot, off, len; 5662095c737Smglocker uint64_t dva; 5672095c737Smglocker struct ufshci_utrd *utrd; 5682095c737Smglocker struct ufshci_ucd *ucd; 5692095c737Smglocker 5702095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 57112f70f1cSmglocker slot = ccb->ccb_slot; 572b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 573b3b05a10Smglocker utrd += slot; 5742095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 5752095c737Smglocker 5762095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 5772095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 5782095c737Smglocker 5792095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 5802095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_NO; 5812095c737Smglocker 5822095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 583ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 58412f70f1cSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 585ceedf4ccSmglocker else 586ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 5872095c737Smglocker 5882095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 5892095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 5902095c737Smglocker 5912095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 592b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 593b3b05a10Smglocker ucd += slot; 5942095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 5952095c737Smglocker 5962095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 5972095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_NOP_OUT; 5982095c737Smglocker ucd->cmd.hdr.flags = 0; 5992095c737Smglocker ucd->cmd.hdr.lun = 0; 60038304948Smglocker ucd->cmd.hdr.task_tag = slot; 6012095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 6022095c737Smglocker ucd->cmd.hdr.query = 0; 6032095c737Smglocker ucd->cmd.hdr.response = 0; 6042095c737Smglocker ucd->cmd.hdr.status = 0; 6052095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 6062095c737Smglocker ucd->cmd.hdr.device_info = 0; 6072095c737Smglocker ucd->cmd.hdr.ds_len = 0; 6082095c737Smglocker 6092095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 6102095c737Smglocker /* Already done with above memset */ 6112095c737Smglocker 6122095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 6132095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd); 6142095c737Smglocker utrd->dw4 = (uint32_t)dva; 6152095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 6162095c737Smglocker 6172095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 6182095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 6192095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 6202095c737Smglocker 6212095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 6222095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 6232095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 6242095c737Smglocker 6252095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 6262095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 6272095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 6282095c737Smglocker 6292095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 6302095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(0); /* No data xfer */ 6312095c737Smglocker 6322095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 6332095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 6342095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 6352095c737Smglocker sc->sc_dev.dv_xname, __func__); 6360fe5d515Smglocker return 1; 6372095c737Smglocker } 6382095c737Smglocker 6393bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 6403bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 6413bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 6423bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 6433bc7f528Smglocker 6442095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 64512f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 64612f70f1cSmglocker ufshci_doorbell_write(sc, slot); 6472095c737Smglocker 6482095c737Smglocker return 0; 6492095c737Smglocker } 6502095c737Smglocker 6512095c737Smglocker int 6522095c737Smglocker ufshci_utr_cmd_lun(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 65344c52867Smglocker struct scsi_xfer *xs) 6542095c737Smglocker { 6552095c737Smglocker int slot, off, len, i; 6562095c737Smglocker uint64_t dva; 6572095c737Smglocker struct ufshci_utrd *utrd; 6582095c737Smglocker struct ufshci_ucd *ucd; 6592095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 6602095c737Smglocker 6612095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 66212f70f1cSmglocker slot = ccb->ccb_slot; 663b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 664b3b05a10Smglocker utrd += slot; 6652095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 6662095c737Smglocker 6672095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 6682095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 6692095c737Smglocker 6702095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 6712095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 6722095c737Smglocker 6732095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 674ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 6752095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 676ceedf4ccSmglocker else 677ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 6782095c737Smglocker 6792095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 6802095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 6812095c737Smglocker 6822095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 683b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 684b3b05a10Smglocker ucd += slot; 6852095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 6862095c737Smglocker 6872095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 6882095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 6892095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 6902095c737Smglocker ucd->cmd.hdr.lun = 0; 69138304948Smglocker ucd->cmd.hdr.task_tag = slot; 6922095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 6932095c737Smglocker ucd->cmd.hdr.query = 0; 6942095c737Smglocker ucd->cmd.hdr.response = 0; 6952095c737Smglocker ucd->cmd.hdr.status = 0; 6962095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 6972095c737Smglocker ucd->cmd.hdr.device_info = 0; 6982095c737Smglocker ucd->cmd.hdr.ds_len = 0; 6992095c737Smglocker 70044c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 7012095c737Smglocker 7022095c737Smglocker ucd->cmd.cdb[0] = REPORT_LUNS; 7032095c737Smglocker ucd->cmd.cdb[6] = 0; 7042095c737Smglocker ucd->cmd.cdb[7] = 0; 7052095c737Smglocker ucd->cmd.cdb[8] = 0; 70644c52867Smglocker ucd->cmd.cdb[9] = xs->datalen; 7072095c737Smglocker 7082095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 7092095c737Smglocker /* Already done with above memset */ 7102095c737Smglocker 7112095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 7122095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd); 7132095c737Smglocker utrd->dw4 = (uint32_t)dva; 7142095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 7152095c737Smglocker 7162095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 7172095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 7182095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 7192095c737Smglocker 7202095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 7212095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 7222095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 7232095c737Smglocker 7242095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 7252095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 7262095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 7272095c737Smglocker 7282095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 7292095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 7302095c737Smglocker 7312095c737Smglocker /* Build PRDT data segment. */ 7322095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 7332095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 7342095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 7352095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 7362095c737Smglocker ucd->prdt[i].dw2 = 0; 7372095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 7382095c737Smglocker } 7392095c737Smglocker 7402095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 7412095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 7422095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 7432095c737Smglocker sc->sc_dev.dv_xname, __func__); 7440fe5d515Smglocker return 1; 7452095c737Smglocker } 7462095c737Smglocker 7473bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 7483bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 7493bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 7503bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 7513bc7f528Smglocker 7522095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 75312f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 75412f70f1cSmglocker ufshci_doorbell_write(sc, slot); 7552095c737Smglocker 7562095c737Smglocker return 0; 7572095c737Smglocker } 7582095c737Smglocker 7592095c737Smglocker int 7602095c737Smglocker ufshci_utr_cmd_inquiry(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 76144c52867Smglocker struct scsi_xfer *xs) 7622095c737Smglocker { 7632095c737Smglocker int slot, off, len, i; 7642095c737Smglocker uint64_t dva; 7652095c737Smglocker struct ufshci_utrd *utrd; 7662095c737Smglocker struct ufshci_ucd *ucd; 7672095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 7682095c737Smglocker 7692095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 77012f70f1cSmglocker slot = ccb->ccb_slot; 771b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 772b3b05a10Smglocker utrd += slot; 7732095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 7742095c737Smglocker 7752095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 7762095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 7772095c737Smglocker 7782095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 7792095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 7802095c737Smglocker 7812095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 782ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 7832095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 784ceedf4ccSmglocker else 785ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 7862095c737Smglocker 7872095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 7882095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 7892095c737Smglocker 7902095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 791b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 792b3b05a10Smglocker ucd += slot; 7932095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 7942095c737Smglocker 7952095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 7962095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 7972095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 7982095c737Smglocker ucd->cmd.hdr.lun = 0; 79938304948Smglocker ucd->cmd.hdr.task_tag = slot; 8002095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 8012095c737Smglocker ucd->cmd.hdr.query = 0; 8022095c737Smglocker ucd->cmd.hdr.response = 0; 8032095c737Smglocker ucd->cmd.hdr.status = 0; 8042095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 8052095c737Smglocker ucd->cmd.hdr.device_info = 0; 8062095c737Smglocker ucd->cmd.hdr.ds_len = 0; 8072095c737Smglocker 80844c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 8092095c737Smglocker 8102095c737Smglocker ucd->cmd.cdb[0] = INQUIRY; /* 0x12 */ 8112095c737Smglocker ucd->cmd.cdb[3] = 0; 81244c52867Smglocker ucd->cmd.cdb[4] = xs->datalen; 8132095c737Smglocker 8142095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 8152095c737Smglocker /* Already done with above memset */ 8162095c737Smglocker 8172095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 8182095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 8192095c737Smglocker utrd->dw4 = (uint32_t)dva; 8202095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 8212095c737Smglocker 8222095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 8232095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 8242095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 8252095c737Smglocker 8262095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 8272095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 8282095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 8292095c737Smglocker 8302095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 8312095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 8322095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 8332095c737Smglocker 8342095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 8352095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 8362095c737Smglocker 8372095c737Smglocker /* Build PRDT data segment. */ 8382095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 8392095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 8402095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 8412095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 8422095c737Smglocker ucd->prdt[i].dw2 = 0; 8432095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 8442095c737Smglocker } 8452095c737Smglocker 8462095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 8472095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 8482095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 8492095c737Smglocker sc->sc_dev.dv_xname, __func__); 8500fe5d515Smglocker return 1; 8512095c737Smglocker } 8522095c737Smglocker 8533bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 8543bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 8553bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 8563bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 8573bc7f528Smglocker 8582095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 85912f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 86012f70f1cSmglocker ufshci_doorbell_write(sc, slot); 8612095c737Smglocker 8620fe5d515Smglocker return 0; 8632095c737Smglocker } 8642095c737Smglocker 8652095c737Smglocker int 8662095c737Smglocker ufshci_utr_cmd_capacity16(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 86744c52867Smglocker struct scsi_xfer *xs) 8682095c737Smglocker { 8692095c737Smglocker int slot, off, len, i; 8702095c737Smglocker uint64_t dva; 8712095c737Smglocker struct ufshci_utrd *utrd; 8722095c737Smglocker struct ufshci_ucd *ucd; 8732095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 8742095c737Smglocker 8752095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 87612f70f1cSmglocker slot = ccb->ccb_slot; 877b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 878b3b05a10Smglocker utrd += slot; 8792095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 8802095c737Smglocker 8812095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 8822095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 8832095c737Smglocker 8842095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 8852095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 8862095c737Smglocker 8872095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 888ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 8892095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 890ceedf4ccSmglocker else 891ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 8922095c737Smglocker 8932095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 8942095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 8952095c737Smglocker 8962095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 897b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 898b3b05a10Smglocker ucd += slot; 8992095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 9002095c737Smglocker 9012095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 9022095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 9032095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 9042095c737Smglocker ucd->cmd.hdr.lun = 0; 90538304948Smglocker ucd->cmd.hdr.task_tag = slot; 9062095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 9072095c737Smglocker ucd->cmd.hdr.query = 0; 9082095c737Smglocker ucd->cmd.hdr.response = 0; 9092095c737Smglocker ucd->cmd.hdr.status = 0; 9102095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 9112095c737Smglocker ucd->cmd.hdr.device_info = 0; 9122095c737Smglocker ucd->cmd.hdr.ds_len = 0; 9132095c737Smglocker 91444c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 9152095c737Smglocker 9162095c737Smglocker ucd->cmd.cdb[0] = READ_CAPACITY_16; /* 0x9e */ 9172095c737Smglocker ucd->cmd.cdb[1] = 0x10; /* Service Action */ 9182095c737Smglocker /* Logical Block Address = 0 for UFS */ 9192095c737Smglocker ucd->cmd.cdb[10] = 0; 9202095c737Smglocker ucd->cmd.cdb[11] = 0; 9212095c737Smglocker ucd->cmd.cdb[12] = 0; 92244c52867Smglocker ucd->cmd.cdb[13] = xs->datalen; 9232095c737Smglocker 9242095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 9252095c737Smglocker /* Already done with above memset */ 9262095c737Smglocker 9272095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 9282095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 9292095c737Smglocker utrd->dw4 = (uint32_t)dva; 9302095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 9312095c737Smglocker 9322095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 9332095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 9342095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 9352095c737Smglocker 9362095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 9372095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 9382095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 9392095c737Smglocker 9402095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 9412095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 9422095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 9432095c737Smglocker 9442095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 9452095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 9462095c737Smglocker 9472095c737Smglocker /* Build PRDT data segment. */ 9482095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 9492095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 9502095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 9512095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 9522095c737Smglocker ucd->prdt[i].dw2 = 0; 9532095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 9542095c737Smglocker } 9552095c737Smglocker 9562095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 9572095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 9582095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 9592095c737Smglocker sc->sc_dev.dv_xname, __func__); 9600fe5d515Smglocker return 1; 9612095c737Smglocker } 9622095c737Smglocker 9633bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 9643bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 9653bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 9663bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 9673bc7f528Smglocker 9682095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 96912f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 97012f70f1cSmglocker ufshci_doorbell_write(sc, slot); 9712095c737Smglocker 9720fe5d515Smglocker return 0; 9732095c737Smglocker } 9742095c737Smglocker 9752095c737Smglocker int 9762095c737Smglocker ufshci_utr_cmd_capacity(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 97744c52867Smglocker struct scsi_xfer *xs) 9782095c737Smglocker { 9792095c737Smglocker int slot, off, len, i; 9802095c737Smglocker uint64_t dva; 9812095c737Smglocker struct ufshci_utrd *utrd; 9822095c737Smglocker struct ufshci_ucd *ucd; 9832095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 9842095c737Smglocker 9852095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 98612f70f1cSmglocker slot = ccb->ccb_slot; 987b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 988b3b05a10Smglocker utrd += slot; 9892095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 9902095c737Smglocker 9912095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 9922095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 9932095c737Smglocker 9942095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 9952095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 9962095c737Smglocker 9972095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 998ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 9992095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 1000ceedf4ccSmglocker else 1001ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 10022095c737Smglocker 10032095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 10042095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 10052095c737Smglocker 10062095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 1007b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1008b3b05a10Smglocker ucd += slot; 10092095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 10102095c737Smglocker 10112095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 10122095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 10132095c737Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-5 = Write, Bit-6 = Read */ 10142095c737Smglocker ucd->cmd.hdr.lun = 0; 101538304948Smglocker ucd->cmd.hdr.task_tag = slot; 10162095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 10172095c737Smglocker ucd->cmd.hdr.query = 0; 10182095c737Smglocker ucd->cmd.hdr.response = 0; 10192095c737Smglocker ucd->cmd.hdr.status = 0; 10202095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 10212095c737Smglocker ucd->cmd.hdr.device_info = 0; 10222095c737Smglocker ucd->cmd.hdr.ds_len = 0; 10232095c737Smglocker 102444c52867Smglocker ucd->cmd.expected_xfer_len = htobe32(xs->datalen); 10252095c737Smglocker 10262095c737Smglocker ucd->cmd.cdb[0] = READ_CAPACITY; /* 0x25 */ 10272095c737Smglocker /* Logical Block Address = 0 for UFS */ 10282095c737Smglocker ucd->cmd.cdb[2] = 0; 10292095c737Smglocker ucd->cmd.cdb[3] = 0; 10302095c737Smglocker ucd->cmd.cdb[4] = 0; 10312095c737Smglocker ucd->cmd.cdb[5] = 0; 10322095c737Smglocker 10332095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 10342095c737Smglocker /* Already done with above memset */ 10352095c737Smglocker 10362095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 10372095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 10382095c737Smglocker utrd->dw4 = (uint32_t)dva; 10392095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 10402095c737Smglocker 10412095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 10422095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 10432095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 10442095c737Smglocker 10452095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 10462095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 10472095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 10482095c737Smglocker 10492095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 10502095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 10512095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 10522095c737Smglocker 10532095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 10542095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 10552095c737Smglocker 10562095c737Smglocker /* Build PRDT data segment. */ 10572095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 10582095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 10592095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 10602095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 10612095c737Smglocker ucd->prdt[i].dw2 = 0; 10622095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 10632095c737Smglocker } 10642095c737Smglocker 10652095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 10662095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 10672095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 10682095c737Smglocker sc->sc_dev.dv_xname, __func__); 10690fe5d515Smglocker return 1; 10702095c737Smglocker } 10712095c737Smglocker 10723bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 10733bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 10743bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 10753bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 10763bc7f528Smglocker 10772095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 107812f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 107912f70f1cSmglocker ufshci_doorbell_write(sc, slot); 10802095c737Smglocker 10810fe5d515Smglocker return 0; 10822095c737Smglocker } 10832095c737Smglocker 10842095c737Smglocker int 1085a2f0dcb2Smglocker ufshci_utr_cmd_io(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 1086a2f0dcb2Smglocker struct scsi_xfer *xs, int dir) 10872095c737Smglocker { 10882095c737Smglocker int slot, off, len, i; 10892095c737Smglocker uint64_t dva; 10902095c737Smglocker struct ufshci_utrd *utrd; 10912095c737Smglocker struct ufshci_ucd *ucd; 10922095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 1093a9129097Smglocker uint32_t blocks; 1094a9129097Smglocker uint64_t lba; 10952095c737Smglocker 10962095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 109712f70f1cSmglocker slot = ccb->ccb_slot; 1098b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1099b3b05a10Smglocker utrd += slot; 11002095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 11012095c737Smglocker 11022095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 11032095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 11042095c737Smglocker 11052095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 1106a2f0dcb2Smglocker if (dir == SCSI_DATA_IN) 11072095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_T2I; 1108a2f0dcb2Smglocker else 11092095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_I2T; 11102095c737Smglocker 11112095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 1112ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 11132095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 1114ceedf4ccSmglocker else 1115ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 11162095c737Smglocker 11172095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 11182095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 11192095c737Smglocker 11202095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 1121b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1122b3b05a10Smglocker ucd += slot; 11232095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 11242095c737Smglocker 11252095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 11262095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 1127a2f0dcb2Smglocker if (dir == SCSI_DATA_IN) 1128a2f0dcb2Smglocker ucd->cmd.hdr.flags = (1 << 6); /* Bit-6 = Read */ 1129a2f0dcb2Smglocker else 1130a2f0dcb2Smglocker ucd->cmd.hdr.flags = (1 << 5); /* Bit-5 = Write */ 11312095c737Smglocker ucd->cmd.hdr.lun = 0; 113238304948Smglocker ucd->cmd.hdr.task_tag = slot; 11332095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 11342095c737Smglocker ucd->cmd.hdr.query = 0; 11352095c737Smglocker ucd->cmd.hdr.response = 0; 11362095c737Smglocker ucd->cmd.hdr.status = 0; 11372095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 11382095c737Smglocker ucd->cmd.hdr.device_info = 0; 11392095c737Smglocker ucd->cmd.hdr.ds_len = 0; 11402095c737Smglocker 1141a9129097Smglocker /* 1142a9129097Smglocker * JESD220C-2_1.pdf, page 88, d) Expected Data Transfer Length: 1143a9129097Smglocker * "When the COMMAND UPIU encodes a SCSI WRITE or SCSI READ command 1144a9129097Smglocker * (specifically WRITE (6), READ (6), WRITE (10), READ (10), 1145a9129097Smglocker * WRITE (16), or READ (16)), the value of this field shall be the 1146a9129097Smglocker * product of the Logical Block Size (bLogicalBlockSize) and the 1147a9129097Smglocker * TRANSFER LENGTH field of the CDB." 1148a9129097Smglocker */ 1149a9129097Smglocker scsi_cmd_rw_decode(&xs->cmd, &lba, &blocks); 1150a9129097Smglocker ucd->cmd.expected_xfer_len = htobe32(UFSHCI_LBS * blocks); 11512095c737Smglocker 1152a2f0dcb2Smglocker memcpy(ucd->cmd.cdb, &xs->cmd, sizeof(ucd->cmd.cdb)); 11532095c737Smglocker 11542095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 11552095c737Smglocker /* Already done with above memset */ 11562095c737Smglocker 11572095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 11582095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 11592095c737Smglocker utrd->dw4 = (uint32_t)dva; 11602095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 11612095c737Smglocker 11622095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 11632095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 11642095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 11652095c737Smglocker 11662095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 11672095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 11682095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 11692095c737Smglocker 11702095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 11712095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 11722095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 11732095c737Smglocker 11742095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 11752095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(dmap->dm_nsegs); 11762095c737Smglocker 11772095c737Smglocker /* Build PRDT data segment. */ 11782095c737Smglocker for (i = 0; i < dmap->dm_nsegs; i++) { 11792095c737Smglocker dva = dmap->dm_segs[i].ds_addr; 11802095c737Smglocker ucd->prdt[i].dw0 = (uint32_t)dva; 11812095c737Smglocker ucd->prdt[i].dw1 = (uint32_t)(dva >> 32); 11822095c737Smglocker ucd->prdt[i].dw2 = 0; 11832095c737Smglocker ucd->prdt[i].dw3 = dmap->dm_segs[i].ds_len - 1; 11842095c737Smglocker } 11852095c737Smglocker 11862095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 11872095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 11882095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 11892095c737Smglocker sc->sc_dev.dv_xname, __func__); 11900fe5d515Smglocker return 1; 11912095c737Smglocker } 11922095c737Smglocker 11933bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 11943bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 11953bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 11963bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 1197*3d5e1d3eSmglocker #if NKSTAT > 0 11983ad2ef2dSmglocker if (sc->sc_stats_slots) 11993ad2ef2dSmglocker sc->sc_stats_slots[slot]++; 12003ad2ef2dSmglocker #endif 12012095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 120212f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 120312f70f1cSmglocker ufshci_doorbell_write(sc, slot); 12042095c737Smglocker 12050fe5d515Smglocker return 0; 12062095c737Smglocker } 12072095c737Smglocker 12082095c737Smglocker int 12092095c737Smglocker ufshci_utr_cmd_sync(struct ufshci_softc *sc, struct ufshci_ccb *ccb, 121044c52867Smglocker struct scsi_xfer *xs, uint32_t lba, uint16_t blocks) 12112095c737Smglocker { 12122095c737Smglocker int slot, off, len; 12132095c737Smglocker uint64_t dva; 12142095c737Smglocker struct ufshci_utrd *utrd; 12152095c737Smglocker struct ufshci_ucd *ucd; 12162095c737Smglocker 12172095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 1) */ 121812f70f1cSmglocker slot = ccb->ccb_slot; 1219b3b05a10Smglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1220b3b05a10Smglocker utrd += slot; 12212095c737Smglocker memset(utrd, 0, sizeof(*utrd)); 12222095c737Smglocker 12232095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2a) */ 12242095c737Smglocker utrd->dw0 = UFSHCI_UTRD_DW0_CT_UFS; 12252095c737Smglocker 12262095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2b) */ 12272095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_DD_I2T; 12282095c737Smglocker 12292095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2c) */ 1230ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) 12312095c737Smglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_REG; 1232ceedf4ccSmglocker else 1233ceedf4ccSmglocker utrd->dw0 |= UFSHCI_UTRD_DW0_I_INT; 12342095c737Smglocker 12352095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2d) */ 12362095c737Smglocker utrd->dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 12372095c737Smglocker 12382095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2e) */ 1239b3b05a10Smglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1240b3b05a10Smglocker ucd += slot; 12412095c737Smglocker memset(ucd, 0, sizeof(*ucd)); 12422095c737Smglocker 12432095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2f) */ 12442095c737Smglocker ucd->cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 12452095c737Smglocker ucd->cmd.hdr.flags = 0; /* No data transfer */ 12462095c737Smglocker ucd->cmd.hdr.lun = 0; 124738304948Smglocker ucd->cmd.hdr.task_tag = slot; 12482095c737Smglocker ucd->cmd.hdr.cmd_set_type = 0; /* SCSI command */ 12492095c737Smglocker ucd->cmd.hdr.query = 0; 12502095c737Smglocker ucd->cmd.hdr.response = 0; 12512095c737Smglocker ucd->cmd.hdr.status = 0; 12522095c737Smglocker ucd->cmd.hdr.ehs_len = 0; 12532095c737Smglocker ucd->cmd.hdr.device_info = 0; 12542095c737Smglocker ucd->cmd.hdr.ds_len = 0; 12552095c737Smglocker 12562095c737Smglocker ucd->cmd.expected_xfer_len = htobe32(0); /* No data transfer */ 12572095c737Smglocker 12582095c737Smglocker ucd->cmd.cdb[0] = SYNCHRONIZE_CACHE; /* 0x35 */ 12592095c737Smglocker ucd->cmd.cdb[2] = (lba >> 24) & 0xff; 12602095c737Smglocker ucd->cmd.cdb[3] = (lba >> 16) & 0xff; 12612095c737Smglocker ucd->cmd.cdb[4] = (lba >> 8) & 0xff; 12622095c737Smglocker ucd->cmd.cdb[5] = (lba >> 0) & 0xff; 12632095c737Smglocker ucd->cmd.cdb[7] = (blocks >> 8) & 0xff; 12642095c737Smglocker ucd->cmd.cdb[8] = (blocks >> 0) & 0xff; 12652095c737Smglocker 12662095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 2g) */ 12672095c737Smglocker /* Already done with above memset */ 12682095c737Smglocker 12692095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 3) */ 12702095c737Smglocker dva = UFSHCI_DMA_DVA(sc->sc_dmamem_ucd) + (sizeof(*ucd) * slot); 12712095c737Smglocker utrd->dw4 = (uint32_t)dva; 12722095c737Smglocker utrd->dw5 = (uint32_t)(dva >> 32); 12732095c737Smglocker 12742095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 4) */ 12752095c737Smglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 12762095c737Smglocker utrd->dw6 = UFSHCI_UTRD_DW6_RUO(off); 12772095c737Smglocker 12782095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 5) */ 12792095c737Smglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 12802095c737Smglocker utrd->dw6 |= UFSHCI_UTRD_DW6_RUL(len); 12812095c737Smglocker 12822095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 6) */ 12832095c737Smglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 12842095c737Smglocker utrd->dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 12852095c737Smglocker 12862095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 7) */ 12872095c737Smglocker utrd->dw7 |= UFSHCI_UTRD_DW7_PRDTL(0); /* No data xfer */ 12882095c737Smglocker 12892095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 9) */ 12902095c737Smglocker if (UFSHCI_READ_4(sc, UFSHCI_REG_UTRLRSR) != 1) { 12912095c737Smglocker printf("%s: %s: UTRLRSR not set\n", 12922095c737Smglocker sc->sc_dev.dv_xname, __func__); 12930fe5d515Smglocker return 1; 12942095c737Smglocker } 12952095c737Smglocker 12963bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 12973bc7f528Smglocker sizeof(*utrd) * slot, sizeof(*utrd), BUS_DMASYNC_PREWRITE); 12983bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 12993bc7f528Smglocker sizeof(*ucd) * slot, sizeof(*ucd), BUS_DMASYNC_PREWRITE); 13003bc7f528Smglocker 13012095c737Smglocker /* 7.2.1 Basic Steps when Building a UTP Transfer Request: 14) */ 130212f70f1cSmglocker ccb->ccb_status = CCB_STATUS_INPROGRESS; 130312f70f1cSmglocker ufshci_doorbell_write(sc, slot); 13042095c737Smglocker 13050fe5d515Smglocker return 0; 13062095c737Smglocker } 13072095c737Smglocker 13080fe5d515Smglocker void 13092095c737Smglocker ufshci_xfer_complete(struct ufshci_softc *sc) 13102095c737Smglocker { 13112095c737Smglocker struct ufshci_ccb *ccb; 13122095c737Smglocker uint32_t reg; 131338304948Smglocker int i, timeout; 13142095c737Smglocker 1315e58b468bSmglocker mtx_enter(&sc->sc_cmd_mtx); 1316e58b468bSmglocker 131712f70f1cSmglocker /* Wait for all commands to complete. */ 131838304948Smglocker for (timeout = 5000; timeout != 0; timeout--) { 131938304948Smglocker reg = ufshci_doorbell_read(sc); 132012f70f1cSmglocker if (reg == 0) 132112f70f1cSmglocker break; 132238304948Smglocker delay(10); 132312f70f1cSmglocker } 132438304948Smglocker if (timeout == 0) 132538304948Smglocker printf("%s: timeout (reg=0x%x)\n", __func__, reg); 13262095c737Smglocker 13272095c737Smglocker for (i = 0; i < sc->sc_nutrs; i++) { 13282095c737Smglocker ccb = &sc->sc_ccbs[i]; 13292095c737Smglocker 133012f70f1cSmglocker /* Skip unused CCBs. */ 133112f70f1cSmglocker if (ccb->ccb_status != CCB_STATUS_INPROGRESS) 13322095c737Smglocker continue; 13332095c737Smglocker 13342095c737Smglocker if (ccb->ccb_done == NULL) 133512f70f1cSmglocker panic("ccb done wasn't defined"); 133612f70f1cSmglocker 133712f70f1cSmglocker /* 7.2.3: Clear completion notification 3b) */ 133812f70f1cSmglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRLCNR, (1U << i)); 133912f70f1cSmglocker 134031479484Smglocker /* 7.2.3: Mark software slot for reuse 3c) */ 134112f70f1cSmglocker ccb->ccb_status = CCB_STATUS_READY2FREE; 134212f70f1cSmglocker } 1343f0ed5855Smglocker 1344f0ed5855Smglocker /* 7.2.3: Reset Interrupt Aggregation Counter and Timer 4) */ 1345ceedf4ccSmglocker if (sc->sc_flags & UFSHCI_FLAGS_AGGR_INTR) { 1346f0ed5855Smglocker UFSHCI_WRITE_4(sc, UFSHCI_REG_UTRIACR, 1347f0ed5855Smglocker UFSHCI_REG_UTRIACR_IAEN | UFSHCI_REG_UTRIACR_CTR); 1348ceedf4ccSmglocker } 1349f0ed5855Smglocker 1350e58b468bSmglocker mtx_leave(&sc->sc_cmd_mtx); 135112f70f1cSmglocker 135212f70f1cSmglocker /* 135312f70f1cSmglocker * Complete the CCB, which will re-schedule new transfers if any are 135412f70f1cSmglocker * pending. 135512f70f1cSmglocker */ 135612f70f1cSmglocker for (i = 0; i < sc->sc_nutrs; i++) { 135712f70f1cSmglocker ccb = &sc->sc_ccbs[i]; 135812f70f1cSmglocker 135912f70f1cSmglocker /* 7.2.3: Process the transfer by higher OS layer 3a) */ 136012f70f1cSmglocker if (ccb->ccb_status == CCB_STATUS_READY2FREE) 13612095c737Smglocker ccb->ccb_done(sc, ccb); 13622095c737Smglocker } 13632095c737Smglocker } 13642095c737Smglocker 13654b489d0aSmglocker int 13668579f9a0Sjsg ufshci_activate(struct device *self, int act) 13674b489d0aSmglocker { 13688579f9a0Sjsg struct ufshci_softc *sc = (struct ufshci_softc *)self; 13694b489d0aSmglocker int rv = 0; 13704b489d0aSmglocker 13714b489d0aSmglocker switch (act) { 13724b489d0aSmglocker case DVACT_POWERDOWN: 13734b489d0aSmglocker DPRINTF(1, "%s: POWERDOWN\n", __func__); 13744b489d0aSmglocker rv = config_activate_children(&sc->sc_dev, act); 137590de4d2dSmglocker ufshci_disable(sc); 13764b489d0aSmglocker break; 13774b489d0aSmglocker case DVACT_RESUME: 13784b489d0aSmglocker DPRINTF(1, "%s: RESUME\n", __func__); 137990de4d2dSmglocker rv = ufshci_init(sc); 13804b489d0aSmglocker if (rv == 0) 13814b489d0aSmglocker rv = config_activate_children(&sc->sc_dev, act); 13824b489d0aSmglocker break; 13834b489d0aSmglocker default: 13844b489d0aSmglocker rv = config_activate_children(&sc->sc_dev, act); 13854b489d0aSmglocker break; 13864b489d0aSmglocker } 13874b489d0aSmglocker 13884b489d0aSmglocker return rv; 13894b489d0aSmglocker } 13904b489d0aSmglocker 13912095c737Smglocker /* SCSI */ 13922095c737Smglocker 13932095c737Smglocker int 13942095c737Smglocker ufshci_ccb_alloc(struct ufshci_softc *sc, int nccbs) 13952095c737Smglocker { 13962095c737Smglocker struct ufshci_ccb *ccb; 13972095c737Smglocker int i; 13982095c737Smglocker 13996920fd9bSmglocker DPRINTF(2, "%s: nccbs=%d, dma_size=%d, dma_nsegs=%d, " 14002095c737Smglocker "dma_segmaxsize=%d\n", 14012095c737Smglocker __func__, nccbs, UFSHCI_UCD_PRDT_MAX_XFER, UFSHCI_UCD_PRDT_MAX_SEGS, 14022095c737Smglocker UFSHCI_UCD_PRDT_MAX_XFER); 14032095c737Smglocker 14042095c737Smglocker sc->sc_ccbs = mallocarray(nccbs, sizeof(*ccb), M_DEVBUF, 14052095c737Smglocker M_WAITOK | M_CANFAIL); 14062095c737Smglocker if (sc->sc_ccbs == NULL) 14072095c737Smglocker return 1; 14082095c737Smglocker 14092095c737Smglocker for (i = 0; i < nccbs; i++) { 14102095c737Smglocker ccb = &sc->sc_ccbs[i]; 14112095c737Smglocker 14122095c737Smglocker if (bus_dmamap_create(sc->sc_dmat, UFSHCI_UCD_PRDT_MAX_XFER, 14132095c737Smglocker UFSHCI_UCD_PRDT_MAX_SEGS, UFSHCI_UCD_PRDT_MAX_XFER, 0, 14144acd6882Smglocker BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | 1415c476ff44Sjsg ((sc->sc_cap & UFSHCI_REG_CAP_64AS) ? BUS_DMA_64BIT : 0), 14162095c737Smglocker &ccb->ccb_dmamap) != 0) 14172095c737Smglocker goto free_maps; 14182095c737Smglocker 14192095c737Smglocker ccb->ccb_cookie = NULL; 142032835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 142112f70f1cSmglocker ccb->ccb_slot = i; 14222095c737Smglocker 14232095c737Smglocker SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry); 14242095c737Smglocker } 14252095c737Smglocker 14262095c737Smglocker return 0; 14272095c737Smglocker 14282095c737Smglocker free_maps: 14292095c737Smglocker ufshci_ccb_free(sc, nccbs); 14302095c737Smglocker return 1; 14312095c737Smglocker } 14322095c737Smglocker 14332095c737Smglocker void * 14342095c737Smglocker ufshci_ccb_get(void *cookie) 14352095c737Smglocker { 14362095c737Smglocker struct ufshci_softc *sc = cookie; 14372095c737Smglocker struct ufshci_ccb *ccb; 14382095c737Smglocker 14392095c737Smglocker mtx_enter(&sc->sc_ccb_mtx); 14402095c737Smglocker ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list); 14412095c737Smglocker if (ccb != NULL) 14422095c737Smglocker SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); 14432095c737Smglocker mtx_leave(&sc->sc_ccb_mtx); 14442095c737Smglocker 14452095c737Smglocker return ccb; 14462095c737Smglocker } 14472095c737Smglocker 14482095c737Smglocker void 14492095c737Smglocker ufshci_ccb_put(void *cookie, void *io) 14502095c737Smglocker { 14512095c737Smglocker struct ufshci_softc *sc = cookie; 14522095c737Smglocker struct ufshci_ccb *ccb = io; 14532095c737Smglocker 14542095c737Smglocker mtx_enter(&sc->sc_ccb_mtx); 14552095c737Smglocker SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_list, ccb, ccb_entry); 14562095c737Smglocker mtx_leave(&sc->sc_ccb_mtx); 14572095c737Smglocker } 14582095c737Smglocker 14592095c737Smglocker void 14602095c737Smglocker ufshci_ccb_free(struct ufshci_softc *sc, int nccbs) 14612095c737Smglocker { 14622095c737Smglocker struct ufshci_ccb *ccb; 14632095c737Smglocker 14642095c737Smglocker while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list)) != NULL) { 14652095c737Smglocker SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry); 14662095c737Smglocker bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); 14672095c737Smglocker } 14682095c737Smglocker 14692095c737Smglocker ufshci_dmamem_free(sc, sc->sc_dmamem_utrd); 14702095c737Smglocker free(sc->sc_ccbs, M_DEVBUF, nccbs * sizeof(*ccb)); 14712095c737Smglocker } 14722095c737Smglocker 14732095c737Smglocker void 14742095c737Smglocker ufshci_scsi_cmd(struct scsi_xfer *xs) 14752095c737Smglocker { 14762095c737Smglocker struct scsi_link *link = xs->sc_link; 14772095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 14782095c737Smglocker 1479e58b468bSmglocker mtx_enter(&sc->sc_cmd_mtx); 1480e58b468bSmglocker 14812095c737Smglocker switch (xs->cmd.opcode) { 14822095c737Smglocker 14832095c737Smglocker case READ_COMMAND: 14842095c737Smglocker case READ_10: 14852095c737Smglocker case READ_12: 14862095c737Smglocker case READ_16: 14872095c737Smglocker ufshci_scsi_io(xs, SCSI_DATA_IN); 1488e58b468bSmglocker break; 14892095c737Smglocker case WRITE_COMMAND: 14902095c737Smglocker case WRITE_10: 14912095c737Smglocker case WRITE_12: 14922095c737Smglocker case WRITE_16: 14932095c737Smglocker ufshci_scsi_io(xs, SCSI_DATA_OUT); 1494e58b468bSmglocker break; 14952095c737Smglocker case SYNCHRONIZE_CACHE: 14962095c737Smglocker ufshci_scsi_sync(xs); 1497e58b468bSmglocker break; 14982095c737Smglocker case INQUIRY: 14992095c737Smglocker ufshci_scsi_inquiry(xs); 1500e58b468bSmglocker break; 15012095c737Smglocker case READ_CAPACITY_16: 15022095c737Smglocker ufshci_scsi_capacity16(xs); 1503e58b468bSmglocker break; 15042095c737Smglocker case READ_CAPACITY: 15052095c737Smglocker ufshci_scsi_capacity(xs); 1506e58b468bSmglocker break; 15072095c737Smglocker case TEST_UNIT_READY: 15082095c737Smglocker case PREVENT_ALLOW: 15092095c737Smglocker case START_STOP: 15102095c737Smglocker xs->error = XS_NOERROR; 15112095c737Smglocker scsi_done(xs); 1512e58b468bSmglocker break; 15132095c737Smglocker default: 15146920fd9bSmglocker DPRINTF(3, "%s: unhandled scsi command 0x%02x\n", 15152095c737Smglocker __func__, xs->cmd.opcode); 1516e58b468bSmglocker xs->error = XS_DRIVER_STUFFUP; 1517e58b468bSmglocker scsi_done(xs); 15182095c737Smglocker break; 15192095c737Smglocker } 15202095c737Smglocker 1521e58b468bSmglocker mtx_leave(&sc->sc_cmd_mtx); 15222095c737Smglocker } 15232095c737Smglocker 15242095c737Smglocker void 15252095c737Smglocker ufshci_scsi_inquiry(struct scsi_xfer *xs) 15262095c737Smglocker { 15272095c737Smglocker struct scsi_link *link = xs->sc_link; 15282095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 15292095c737Smglocker struct ufshci_ccb *ccb = xs->io; 15302095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 15312095c737Smglocker int error; 15322095c737Smglocker 15336920fd9bSmglocker DPRINTF(3, "%s: INQUIRY (%s)\n", 15342095c737Smglocker __func__, ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 15352095c737Smglocker 15362095c737Smglocker if (xs->datalen > UPIU_SCSI_RSP_INQUIRY_SIZE) { 15376920fd9bSmglocker DPRINTF(2, "%s: request len too large\n", __func__); 15382095c737Smglocker goto error1; 15392095c737Smglocker } 15402095c737Smglocker 15412095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 15422095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 15430fe5d515Smglocker if (error) { 15442095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 15452095c737Smglocker goto error1; 15462095c737Smglocker } 15472095c737Smglocker 15482095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 15492095c737Smglocker BUS_DMASYNC_PREREAD); 15502095c737Smglocker 15512095c737Smglocker ccb->ccb_cookie = xs; 15522095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 15532095c737Smglocker 15542095c737Smglocker /* Response length should be UPIU_SCSI_RSP_INQUIRY_SIZE. */ 155512f70f1cSmglocker error = ufshci_utr_cmd_inquiry(sc, ccb, xs); 15560fe5d515Smglocker if (error) 15572095c737Smglocker goto error2; 15582095c737Smglocker 15592095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 156093bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 15612095c737Smglocker ccb->ccb_done(sc, ccb); 15622095c737Smglocker return; 15632095c737Smglocker } 15642095c737Smglocker goto error2; 15652095c737Smglocker } 15662095c737Smglocker 15672095c737Smglocker return; 15682095c737Smglocker 15692095c737Smglocker error2: 15702095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 15712095c737Smglocker ccb->ccb_cookie = NULL; 157232835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 15732095c737Smglocker ccb->ccb_done = NULL; 15742095c737Smglocker error1: 15752095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 15762095c737Smglocker scsi_done(xs); 15772095c737Smglocker } 15782095c737Smglocker 15792095c737Smglocker void 15802095c737Smglocker ufshci_scsi_capacity16(struct scsi_xfer *xs) 15812095c737Smglocker { 15822095c737Smglocker struct scsi_link *link = xs->sc_link; 15832095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 15842095c737Smglocker struct ufshci_ccb *ccb = xs->io; 15852095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 15862095c737Smglocker int error; 15872095c737Smglocker 15886920fd9bSmglocker DPRINTF(3, "%s: CAPACITY16 (%s)\n", 15892095c737Smglocker __func__, ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 15902095c737Smglocker 15912095c737Smglocker if (xs->datalen > UPIU_SCSI_RSP_CAPACITY16_SIZE) { 15926920fd9bSmglocker DPRINTF(2, "%s: request len too large\n", __func__); 15932095c737Smglocker goto error1; 15942095c737Smglocker } 15952095c737Smglocker 15962095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 15972095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 15980fe5d515Smglocker if (error) { 15992095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 16002095c737Smglocker goto error1; 16012095c737Smglocker } 16022095c737Smglocker 16032095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 16042095c737Smglocker BUS_DMASYNC_PREREAD); 16052095c737Smglocker 16062095c737Smglocker ccb->ccb_cookie = xs; 16072095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 16082095c737Smglocker 16092095c737Smglocker /* Response length should be UPIU_SCSI_RSP_CAPACITY16_SIZE. */ 161012f70f1cSmglocker error = ufshci_utr_cmd_capacity16(sc, ccb, xs); 16110fe5d515Smglocker if (error) 16122095c737Smglocker goto error2; 16132095c737Smglocker 16142095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 161593bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 16162095c737Smglocker ccb->ccb_done(sc, ccb); 16172095c737Smglocker return; 16182095c737Smglocker } 16192095c737Smglocker goto error2; 16202095c737Smglocker } 16212095c737Smglocker 16222095c737Smglocker return; 16232095c737Smglocker 16242095c737Smglocker error2: 16252095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 16262095c737Smglocker ccb->ccb_cookie = NULL; 162732835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 16282095c737Smglocker ccb->ccb_done = NULL; 16292095c737Smglocker error1: 16302095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 16312095c737Smglocker scsi_done(xs); 16322095c737Smglocker } 16332095c737Smglocker 16342095c737Smglocker void 16352095c737Smglocker ufshci_scsi_capacity(struct scsi_xfer *xs) 16362095c737Smglocker { 16372095c737Smglocker struct scsi_link *link = xs->sc_link; 16382095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 16392095c737Smglocker struct ufshci_ccb *ccb = xs->io; 16402095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 16412095c737Smglocker int error; 16422095c737Smglocker 16436920fd9bSmglocker DPRINTF(3, "%s: CAPACITY (%s)\n", 16442095c737Smglocker __func__, ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 16452095c737Smglocker 16462095c737Smglocker if (xs->datalen > UPIU_SCSI_RSP_CAPACITY_SIZE) { 16476920fd9bSmglocker DPRINTF(2, "%s: request len too large\n", __func__); 16482095c737Smglocker goto error1; 16492095c737Smglocker } 16502095c737Smglocker 16512095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 16522095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 16530fe5d515Smglocker if (error) { 16542095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 16552095c737Smglocker goto error1; 16562095c737Smglocker } 16572095c737Smglocker 16582095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 16592095c737Smglocker BUS_DMASYNC_PREREAD); 16602095c737Smglocker 16612095c737Smglocker ccb->ccb_cookie = xs; 16622095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 16632095c737Smglocker 16642095c737Smglocker /* Response length should be UPIU_SCSI_RSP_CAPACITY_SIZE */ 166512f70f1cSmglocker error = ufshci_utr_cmd_capacity(sc, ccb, xs); 16660fe5d515Smglocker if (error) 16672095c737Smglocker goto error2; 16682095c737Smglocker 16692095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 167093bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 16712095c737Smglocker ccb->ccb_done(sc, ccb); 16722095c737Smglocker return; 16732095c737Smglocker } 16742095c737Smglocker goto error2; 16752095c737Smglocker } 16762095c737Smglocker 16772095c737Smglocker return; 16782095c737Smglocker 16792095c737Smglocker error2: 16802095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 16812095c737Smglocker ccb->ccb_cookie = NULL; 168232835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 16832095c737Smglocker ccb->ccb_done = NULL; 16842095c737Smglocker error1: 16852095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 16862095c737Smglocker scsi_done(xs); 16872095c737Smglocker } 16882095c737Smglocker 16892095c737Smglocker void 16902095c737Smglocker ufshci_scsi_sync(struct scsi_xfer *xs) 16912095c737Smglocker { 16922095c737Smglocker struct scsi_link *link = xs->sc_link; 16932095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 16942095c737Smglocker struct ufshci_ccb *ccb = xs->io; 16952095c737Smglocker uint64_t lba; 16962095c737Smglocker uint32_t blocks; 169712f70f1cSmglocker int error; 16982095c737Smglocker 16992095c737Smglocker /* lba = 0, blocks = 0: Synchronize all logical blocks. */ 17002095c737Smglocker lba = 0; blocks = 0; 17012095c737Smglocker 17026920fd9bSmglocker DPRINTF(3, "%s: SYNC, lba=%llu, blocks=%u (%s)\n", 17032095c737Smglocker __func__, lba, blocks, 17042095c737Smglocker ISSET(xs->flags, SCSI_POLL) ? "poll" : "no poll"); 17052095c737Smglocker 17062095c737Smglocker ccb->ccb_cookie = xs; 17072095c737Smglocker ccb->ccb_done = ufshci_scsi_done; 17082095c737Smglocker 170912f70f1cSmglocker error = ufshci_utr_cmd_sync(sc, ccb, xs, (uint32_t)lba, 17102095c737Smglocker (uint16_t)blocks); 17110fe5d515Smglocker if (error) 17122095c737Smglocker goto error; 17132095c737Smglocker 17142095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 171593bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 17162095c737Smglocker ccb->ccb_done(sc, ccb); 17172095c737Smglocker return; 17182095c737Smglocker } 17192095c737Smglocker goto error; 17202095c737Smglocker } 17212095c737Smglocker 17222095c737Smglocker return; 17232095c737Smglocker 17242095c737Smglocker error: 17252095c737Smglocker ccb->ccb_cookie = NULL; 172632835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 17272095c737Smglocker ccb->ccb_done = NULL; 17282095c737Smglocker 17292095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 17302095c737Smglocker scsi_done(xs); 17312095c737Smglocker } 17322095c737Smglocker 17332095c737Smglocker void 17342095c737Smglocker ufshci_scsi_io(struct scsi_xfer *xs, int dir) 17352095c737Smglocker { 17362095c737Smglocker struct scsi_link *link = xs->sc_link; 17372095c737Smglocker struct ufshci_softc *sc = link->bus->sb_adapter_softc; 17382095c737Smglocker struct ufshci_ccb *ccb = xs->io; 17392095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 17402095c737Smglocker int error; 17412095c737Smglocker 17422095c737Smglocker if ((xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) != dir) 17432095c737Smglocker goto error1; 17442095c737Smglocker 17452095c737Smglocker error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, xs->datalen, NULL, 17462095c737Smglocker ISSET(xs->flags, SCSI_NOSLEEP) ? BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 17470fe5d515Smglocker if (error) { 17482095c737Smglocker printf("%s: bus_dmamap_load error=%d\n", __func__, error); 17492095c737Smglocker goto error1; 17502095c737Smglocker } 17512095c737Smglocker 17522095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 17532095c737Smglocker ISSET(xs->flags, SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : 17542095c737Smglocker BUS_DMASYNC_PREWRITE); 17552095c737Smglocker 17562095c737Smglocker ccb->ccb_cookie = xs; 17572095c737Smglocker ccb->ccb_done = ufshci_scsi_io_done; 17582095c737Smglocker 1759a2f0dcb2Smglocker if (dir == SCSI_DATA_IN) 176012f70f1cSmglocker error = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_IN); 1761a2f0dcb2Smglocker else 176212f70f1cSmglocker error = ufshci_utr_cmd_io(sc, ccb, xs, SCSI_DATA_OUT); 17630fe5d515Smglocker if (error) 17642095c737Smglocker goto error2; 17652095c737Smglocker 17662095c737Smglocker if (ISSET(xs->flags, SCSI_POLL)) { 176793bf5056Smglocker if (ufshci_doorbell_poll(sc, ccb->ccb_slot, xs->timeout) == 0) { 17682095c737Smglocker ccb->ccb_done(sc, ccb); 17692095c737Smglocker return; 17702095c737Smglocker } 17712095c737Smglocker goto error2; 17722095c737Smglocker } 17732095c737Smglocker 17742095c737Smglocker return; 17752095c737Smglocker 17762095c737Smglocker error2: 17772095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 17782095c737Smglocker ccb->ccb_cookie = NULL; 177932835f4cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 17802095c737Smglocker ccb->ccb_done = NULL; 17812095c737Smglocker error1: 17822095c737Smglocker xs->error = XS_DRIVER_STUFFUP; 17832095c737Smglocker scsi_done(xs); 17842095c737Smglocker } 17852095c737Smglocker 17862095c737Smglocker void 17872095c737Smglocker ufshci_scsi_io_done(struct ufshci_softc *sc, struct ufshci_ccb *ccb) 17882095c737Smglocker { 17892095c737Smglocker struct scsi_xfer *xs = ccb->ccb_cookie; 17902095c737Smglocker bus_dmamap_t dmap = ccb->ccb_dmamap; 17913bc7f528Smglocker struct ufshci_ucd *ucd; 17923bc7f528Smglocker struct ufshci_utrd *utrd; 17930b701acdSmglocker 17942095c737Smglocker bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize, 17952095c737Smglocker ISSET(xs->flags, SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : 17962095c737Smglocker BUS_DMASYNC_POSTWRITE); 17972095c737Smglocker 17982095c737Smglocker bus_dmamap_unload(sc->sc_dmat, dmap); 17992095c737Smglocker 18003bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 18013bc7f528Smglocker sizeof(*ucd) * ccb->ccb_slot, sizeof(*ucd), 18023bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18033bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 18043bc7f528Smglocker sizeof(*utrd) * ccb->ccb_slot, sizeof(*utrd), 18053bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18063bc7f528Smglocker 1807effc6b3aSmglocker /* TODO: Do more checks on the Response UPIU in case of errors? */ 1808effc6b3aSmglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1809effc6b3aSmglocker utrd += ccb->ccb_slot; 1810effc6b3aSmglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1811effc6b3aSmglocker ucd += ccb->ccb_slot; 1812effc6b3aSmglocker if (utrd->dw2 != UFSHCI_UTRD_DW2_OCS_SUCCESS) { 1813effc6b3aSmglocker printf("%s: error: slot=%d, ocs=0x%x, rsp-tc=0x%x\n", 1814effc6b3aSmglocker __func__, ccb->ccb_slot, utrd->dw2, ucd->rsp.hdr.tc); 1815effc6b3aSmglocker } 1816effc6b3aSmglocker 18172095c737Smglocker ccb->ccb_cookie = NULL; 181812f70f1cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 18192095c737Smglocker ccb->ccb_done = NULL; 18202095c737Smglocker 1821effc6b3aSmglocker xs->error = (utrd->dw2 == UFSHCI_UTRD_DW2_OCS_SUCCESS) ? 1822effc6b3aSmglocker XS_NOERROR : XS_DRIVER_STUFFUP; 18232095c737Smglocker xs->status = SCSI_OK; 18242095c737Smglocker xs->resid = 0; 18252095c737Smglocker scsi_done(xs); 18262095c737Smglocker } 18272095c737Smglocker 18282095c737Smglocker void 18292095c737Smglocker ufshci_scsi_done(struct ufshci_softc *sc, struct ufshci_ccb *ccb) 18302095c737Smglocker { 18312095c737Smglocker struct scsi_xfer *xs = ccb->ccb_cookie; 18323bc7f528Smglocker struct ufshci_ucd *ucd; 18333bc7f528Smglocker struct ufshci_utrd *utrd; 18343bc7f528Smglocker 18353bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_ucd), 18363bc7f528Smglocker sizeof(*ucd) * ccb->ccb_slot, sizeof(*ucd), 18373bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18383bc7f528Smglocker bus_dmamap_sync(sc->sc_dmat, UFSHCI_DMA_MAP(sc->sc_dmamem_utrd), 18393bc7f528Smglocker sizeof(*utrd) * ccb->ccb_slot, sizeof(*utrd), 18403bc7f528Smglocker BUS_DMASYNC_POSTWRITE); 18412095c737Smglocker 1842effc6b3aSmglocker /* TODO: Do more checks on the Response UPIU in case of errors? */ 1843effc6b3aSmglocker utrd = UFSHCI_DMA_KVA(sc->sc_dmamem_utrd); 1844effc6b3aSmglocker utrd += ccb->ccb_slot; 1845effc6b3aSmglocker ucd = UFSHCI_DMA_KVA(sc->sc_dmamem_ucd); 1846effc6b3aSmglocker ucd += ccb->ccb_slot; 1847effc6b3aSmglocker if (utrd->dw2 != UFSHCI_UTRD_DW2_OCS_SUCCESS) { 1848effc6b3aSmglocker printf("%s: error: slot=%d, ocs=0x%x, rsp-tc=0x%x\n", 1849effc6b3aSmglocker __func__, ccb->ccb_slot, utrd->dw2, ucd->rsp.hdr.tc); 1850effc6b3aSmglocker } 1851effc6b3aSmglocker 18522095c737Smglocker ccb->ccb_cookie = NULL; 185312f70f1cSmglocker ccb->ccb_status = CCB_STATUS_FREE; 18542095c737Smglocker ccb->ccb_done = NULL; 18552095c737Smglocker 1856effc6b3aSmglocker xs->error = (utrd->dw2 == UFSHCI_UTRD_DW2_OCS_SUCCESS) ? 1857effc6b3aSmglocker XS_NOERROR : XS_DRIVER_STUFFUP; 18582095c737Smglocker xs->status = SCSI_OK; 18592095c737Smglocker xs->resid = 0; 18602095c737Smglocker scsi_done(xs); 18612095c737Smglocker } 186290de4d2dSmglocker 1863ce37c3ccSmglocker #ifdef HIBERNATE 186490de4d2dSmglocker int 186590de4d2dSmglocker ufshci_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size, 186690de4d2dSmglocker int op, void *page) 186790de4d2dSmglocker { 186890de4d2dSmglocker struct ufshci_hibernate_page { 186990de4d2dSmglocker struct ufshci_utrd utrd; 187090de4d2dSmglocker struct ufshci_ucd ucd; 187190de4d2dSmglocker 187290de4d2dSmglocker struct ufshci_softc *sc; /* Copy of softc */ 187390de4d2dSmglocker 187490de4d2dSmglocker daddr_t poffset; /* Start of SWAP partition */ 187590de4d2dSmglocker size_t psize; /* Size of SWAP partition */ 187690de4d2dSmglocker uint32_t secsize; /* Our sector size */ 187790de4d2dSmglocker } *my = page; 187890de4d2dSmglocker paddr_t data_phys, page_phys; 187990de4d2dSmglocker uint64_t data_bus_phys, page_bus_phys; 188090de4d2dSmglocker uint64_t timeout_us; 188190de4d2dSmglocker int off, len, slot; 188290de4d2dSmglocker uint32_t blocks, reg; 188390de4d2dSmglocker uint64_t lba; 188490de4d2dSmglocker 188590de4d2dSmglocker if (op == HIB_INIT) { 188690de4d2dSmglocker struct device *disk; 188790de4d2dSmglocker struct device *scsibus; 188890de4d2dSmglocker extern struct cfdriver sd_cd; 188990de4d2dSmglocker 189090de4d2dSmglocker /* Find ufshci softc. */ 189190de4d2dSmglocker disk = disk_lookup(&sd_cd, DISKUNIT(dev)); 189290de4d2dSmglocker if (disk == NULL) 189390de4d2dSmglocker return ENOTTY; 189490de4d2dSmglocker scsibus = disk->dv_parent; 189590de4d2dSmglocker my->sc = (struct ufshci_softc *)disk->dv_parent->dv_parent; 189690de4d2dSmglocker 189790de4d2dSmglocker /* Stop run queues and disable interrupts. */ 189890de4d2dSmglocker ufshci_disable(my->sc); 189990de4d2dSmglocker 190031479484Smglocker /* Tell the controller the new hibernate UTRD address. */ 190190de4d2dSmglocker pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys); 190290de4d2dSmglocker page_bus_phys = page_phys + ((void *)&my->utrd - page); 190390de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLBA, 190490de4d2dSmglocker (uint32_t)page_bus_phys); 190590de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLBAU, 190690de4d2dSmglocker (uint32_t)(page_bus_phys >> 32)); 190790de4d2dSmglocker 190890de4d2dSmglocker /* Start run queues. */ 190990de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTMRLRSR, 191090de4d2dSmglocker UFSHCI_REG_UTMRLRSR_START); 191190de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLRSR, 191290de4d2dSmglocker UFSHCI_REG_UTRLRSR_START); 191390de4d2dSmglocker 191490de4d2dSmglocker my->poffset = blkno; 191590de4d2dSmglocker my->psize = size; 191690de4d2dSmglocker my->secsize = UFSHCI_LBS; 191790de4d2dSmglocker 191890de4d2dSmglocker return 0; 191990de4d2dSmglocker } 192090de4d2dSmglocker 192190de4d2dSmglocker if (op != HIB_W) 192290de4d2dSmglocker return 0; 192390de4d2dSmglocker 192490de4d2dSmglocker if (blkno + (size / DEV_BSIZE) > my->psize) 192590de4d2dSmglocker return E2BIG; 192690de4d2dSmglocker blocks = size / my->secsize; 192790de4d2dSmglocker lba = (blkno + my->poffset) / (my->secsize / DEV_BSIZE); 192890de4d2dSmglocker 192990de4d2dSmglocker /* 193090de4d2dSmglocker * The following code is a ripped down version of ufshci_utr_cmd_io() 193190de4d2dSmglocker * adapted for hibernate. 193290de4d2dSmglocker */ 193390de4d2dSmglocker slot = 0; /* We only use the first slot for hibernate */ 193490de4d2dSmglocker 193590de4d2dSmglocker memset(&my->utrd, 0, sizeof(struct ufshci_utrd)); 193690de4d2dSmglocker 193790de4d2dSmglocker my->utrd.dw0 = UFSHCI_UTRD_DW0_CT_UFS; 193890de4d2dSmglocker my->utrd.dw0 |= UFSHCI_UTRD_DW0_DD_I2T; 193990de4d2dSmglocker my->utrd.dw0 |= UFSHCI_UTRD_DW0_I_REG; 194090de4d2dSmglocker my->utrd.dw2 = UFSHCI_UTRD_DW2_OCS_IOV; 194190de4d2dSmglocker 194290de4d2dSmglocker memset(&my->ucd, 0, sizeof(struct ufshci_ucd)); 194390de4d2dSmglocker 194490de4d2dSmglocker my->ucd.cmd.hdr.tc = UPIU_TC_I2T_COMMAND; 194590de4d2dSmglocker my->ucd.cmd.hdr.flags = (1 << 5); /* Bit-5 = Write */ 194690de4d2dSmglocker 194790de4d2dSmglocker my->ucd.cmd.hdr.lun = 0; 194890de4d2dSmglocker my->ucd.cmd.hdr.task_tag = slot; 194990de4d2dSmglocker my->ucd.cmd.hdr.cmd_set_type = 0; /* SCSI command */ 195090de4d2dSmglocker my->ucd.cmd.hdr.query = 0; 195190de4d2dSmglocker my->ucd.cmd.hdr.response = 0; 195290de4d2dSmglocker my->ucd.cmd.hdr.status = 0; 195390de4d2dSmglocker my->ucd.cmd.hdr.ehs_len = 0; 195490de4d2dSmglocker my->ucd.cmd.hdr.device_info = 0; 195590de4d2dSmglocker my->ucd.cmd.hdr.ds_len = 0; 195690de4d2dSmglocker 195790de4d2dSmglocker my->ucd.cmd.expected_xfer_len = htobe32(UFSHCI_LBS * blocks); 195890de4d2dSmglocker my->ucd.cmd.cdb[0] = WRITE_10; /* 0x2a */ 195990de4d2dSmglocker my->ucd.cmd.cdb[1] = (1 << 3); /* FUA: Force Unit Access */ 196090de4d2dSmglocker my->ucd.cmd.cdb[2] = (lba >> 24) & 0xff; 196190de4d2dSmglocker my->ucd.cmd.cdb[3] = (lba >> 16) & 0xff; 196290de4d2dSmglocker my->ucd.cmd.cdb[4] = (lba >> 8) & 0xff; 196390de4d2dSmglocker my->ucd.cmd.cdb[5] = (lba >> 0) & 0xff; 196490de4d2dSmglocker my->ucd.cmd.cdb[7] = (blocks >> 8) & 0xff; 196590de4d2dSmglocker my->ucd.cmd.cdb[8] = (blocks >> 0) & 0xff; 196690de4d2dSmglocker 196790de4d2dSmglocker pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys); 196890de4d2dSmglocker page_bus_phys = page_phys + ((void *)&my->ucd - page); 196990de4d2dSmglocker my->utrd.dw4 = (uint32_t)page_bus_phys; 197090de4d2dSmglocker my->utrd.dw5 = (uint32_t)(page_bus_phys >> 32); 197190de4d2dSmglocker 197290de4d2dSmglocker off = sizeof(struct upiu_command) / 4; /* DWORD offset */ 197390de4d2dSmglocker my->utrd.dw6 = UFSHCI_UTRD_DW6_RUO(off); 197490de4d2dSmglocker 197590de4d2dSmglocker len = sizeof(struct upiu_response) / 4; /* DWORD length */ 197690de4d2dSmglocker my->utrd.dw6 |= UFSHCI_UTRD_DW6_RUL(len); 197790de4d2dSmglocker 197890de4d2dSmglocker off = (sizeof(struct upiu_command) + sizeof(struct upiu_response)) / 4; 197990de4d2dSmglocker my->utrd.dw7 = UFSHCI_UTRD_DW7_PRDTO(off); 198090de4d2dSmglocker 198190de4d2dSmglocker my->utrd.dw7 |= UFSHCI_UTRD_DW7_PRDTL(1); /* dm_nsegs */ 198290de4d2dSmglocker 198390de4d2dSmglocker pmap_extract(pmap_kernel(), (vaddr_t)addr, &data_phys); 198490de4d2dSmglocker data_bus_phys = data_phys; 198590de4d2dSmglocker my->ucd.prdt[0].dw0 = (uint32_t)data_bus_phys; 198690de4d2dSmglocker my->ucd.prdt[0].dw1 = (uint32_t)(data_bus_phys >> 32); 198790de4d2dSmglocker my->ucd.prdt[0].dw2 = 0; 198890de4d2dSmglocker my->ucd.prdt[0].dw3 = size - 1; /* ds_len */ 198990de4d2dSmglocker 199090de4d2dSmglocker if (UFSHCI_READ_4(my->sc, UFSHCI_REG_UTRLRSR) != 1) 199190de4d2dSmglocker return EIO; 199290de4d2dSmglocker 199390de4d2dSmglocker ufshci_doorbell_write(my->sc, slot); 199490de4d2dSmglocker 199590de4d2dSmglocker /* ufshci_doorbell_poll() adaption for hibernate. */ 199690de4d2dSmglocker for (timeout_us = 1000000 * 1000; timeout_us != 0; 199790de4d2dSmglocker timeout_us -= 1000) { 199890de4d2dSmglocker reg = UFSHCI_READ_4(my->sc, UFSHCI_REG_UTRLDBR); 199990de4d2dSmglocker if ((reg & (1U << slot)) == 0) 200090de4d2dSmglocker break; 200190de4d2dSmglocker delay(1000); 200290de4d2dSmglocker } 200390de4d2dSmglocker if (timeout_us == 0) 200490de4d2dSmglocker return EIO; 200590de4d2dSmglocker UFSHCI_WRITE_4(my->sc, UFSHCI_REG_UTRLCNR, (1U << slot)); 200690de4d2dSmglocker 200731479484Smglocker /* Check if the command was successfully executed. */ 200890de4d2dSmglocker if (my->utrd.dw2 != UFSHCI_UTRD_DW2_OCS_SUCCESS) 200990de4d2dSmglocker return EIO; 201090de4d2dSmglocker 201190de4d2dSmglocker return 0; 201290de4d2dSmglocker } 201390de4d2dSmglocker #endif /* HIBERNATE */ 20143ad2ef2dSmglocker 2015*3d5e1d3eSmglocker #if NKSTAT > 0 2016*3d5e1d3eSmglocker struct kstat_kv ufshci_counters_slot[CCB_STATUS_COUNT] = { 2017*3d5e1d3eSmglocker KSTAT_KV_UNIT_INITIALIZER("slots free", KSTAT_KV_T_COUNTER16, 2018*3d5e1d3eSmglocker KSTAT_KV_U_NONE), 2019*3d5e1d3eSmglocker KSTAT_KV_UNIT_INITIALIZER("slots inpr", KSTAT_KV_T_COUNTER16, 2020*3d5e1d3eSmglocker KSTAT_KV_U_NONE), 2021*3d5e1d3eSmglocker KSTAT_KV_UNIT_INITIALIZER("slots r2fr", KSTAT_KV_T_COUNTER16, 2022*3d5e1d3eSmglocker KSTAT_KV_U_NONE), 2023*3d5e1d3eSmglocker }; 20243ad2ef2dSmglocker 2025*3d5e1d3eSmglocker void 2026*3d5e1d3eSmglocker ufshci_kstat_attach(struct ufshci_softc *sc) 2027*3d5e1d3eSmglocker { 2028*3d5e1d3eSmglocker struct kstat *ks; 2029*3d5e1d3eSmglocker struct kstat_kv *kvs; 2030*3d5e1d3eSmglocker char name[KSTAT_KV_NAMELEN]; 2031*3d5e1d3eSmglocker int i; 2032*3d5e1d3eSmglocker 2033*3d5e1d3eSmglocker /* 2034*3d5e1d3eSmglocker * Allocate array to count ccb slot utilization. 2035*3d5e1d3eSmglocker */ 2036*3d5e1d3eSmglocker sc->sc_stats_slots = mallocarray(sc->sc_nutrs, sizeof(uint64_t), 2037*3d5e1d3eSmglocker M_DEVBUF, M_WAITOK | M_ZERO); 2038*3d5e1d3eSmglocker if (sc->sc_stats_slots == NULL) { 2039*3d5e1d3eSmglocker printf("%s: can't allocate stats_slots array\n", 2040*3d5e1d3eSmglocker sc->sc_dev.dv_xname); 2041*3d5e1d3eSmglocker return; 2042*3d5e1d3eSmglocker } 2043*3d5e1d3eSmglocker 2044*3d5e1d3eSmglocker /* 2045*3d5e1d3eSmglocker * Setup 'ccbs' kstat. 2046*3d5e1d3eSmglocker */ 2047*3d5e1d3eSmglocker kvs = mallocarray(sc->sc_nutrs, sizeof(*kvs), M_DEVBUF, 2048*3d5e1d3eSmglocker M_WAITOK | M_ZERO); 2049*3d5e1d3eSmglocker if (kvs == NULL) { 2050*3d5e1d3eSmglocker printf("%s: can't allocate kvs ccbs array\n", 2051*3d5e1d3eSmglocker sc->sc_dev.dv_xname); 2052*3d5e1d3eSmglocker return; 2053*3d5e1d3eSmglocker } 2054*3d5e1d3eSmglocker for (i = 0; i < sc->sc_nutrs; i++) { 2055*3d5e1d3eSmglocker snprintf(name, sizeof(name), "slot %d ccbs", i); 2056*3d5e1d3eSmglocker kstat_kv_unit_init(&kvs[i], name, KSTAT_KV_T_COUNTER64, 2057*3d5e1d3eSmglocker KSTAT_KV_U_NONE); 2058*3d5e1d3eSmglocker } 2059*3d5e1d3eSmglocker 2060*3d5e1d3eSmglocker mtx_init(&sc->sc_kstat_mtx_ccb, IPL_SOFTCLOCK); 2061*3d5e1d3eSmglocker 2062*3d5e1d3eSmglocker ks = kstat_create(sc->sc_dev.dv_xname, 0, "ccbs", 0, KSTAT_T_KV, 0); 2063*3d5e1d3eSmglocker if (ks == NULL) { 2064*3d5e1d3eSmglocker printf("%s: can't create ccbs kstats\n", sc->sc_dev.dv_xname); 2065*3d5e1d3eSmglocker free(kvs, M_DEVBUF, sc->sc_nutrs * sizeof(*kvs)); 2066*3d5e1d3eSmglocker return; 2067*3d5e1d3eSmglocker } 2068*3d5e1d3eSmglocker 2069*3d5e1d3eSmglocker kstat_set_mutex(ks, &sc->sc_kstat_mtx_ccb); 2070*3d5e1d3eSmglocker ks->ks_softc = sc; 2071*3d5e1d3eSmglocker ks->ks_data = kvs; 2072*3d5e1d3eSmglocker ks->ks_datalen = sc->sc_nutrs * sizeof(*kvs); 2073*3d5e1d3eSmglocker ks->ks_read = ufshci_kstat_read_ccb; 2074*3d5e1d3eSmglocker 2075*3d5e1d3eSmglocker sc->sc_kstat_ccb = ks; 2076*3d5e1d3eSmglocker kstat_install(ks); 2077*3d5e1d3eSmglocker 2078*3d5e1d3eSmglocker /* 2079*3d5e1d3eSmglocker * Setup 'slots' kstat. 2080*3d5e1d3eSmglocker */ 2081*3d5e1d3eSmglocker mtx_init(&sc->sc_kstat_mtx_slot, IPL_SOFTCLOCK); 2082*3d5e1d3eSmglocker 2083*3d5e1d3eSmglocker ks = kstat_create(sc->sc_dev.dv_xname, 0, "slots", 0, KSTAT_T_KV, 0); 2084*3d5e1d3eSmglocker if (ks == NULL) { 2085*3d5e1d3eSmglocker printf("%s: can't create slots kstats\n", sc->sc_dev.dv_xname); 2086*3d5e1d3eSmglocker return; 2087*3d5e1d3eSmglocker } 2088*3d5e1d3eSmglocker 2089*3d5e1d3eSmglocker kstat_set_mutex(ks, &sc->sc_kstat_mtx_slot); 2090*3d5e1d3eSmglocker ks->ks_softc = sc; 2091*3d5e1d3eSmglocker ks->ks_data = ufshci_counters_slot; 2092*3d5e1d3eSmglocker ks->ks_datalen = CCB_STATUS_COUNT * sizeof(*kvs); 2093*3d5e1d3eSmglocker ks->ks_read = ufshci_kstat_read_slot; 2094*3d5e1d3eSmglocker 2095*3d5e1d3eSmglocker sc->sc_kstat_slot = ks; 2096*3d5e1d3eSmglocker kstat_install(ks); 2097*3d5e1d3eSmglocker } 2098*3d5e1d3eSmglocker 2099*3d5e1d3eSmglocker int 2100*3d5e1d3eSmglocker ufshci_kstat_read_ccb(struct kstat *ks) 2101*3d5e1d3eSmglocker { 2102*3d5e1d3eSmglocker struct ufshci_softc *sc = ks->ks_softc; 2103*3d5e1d3eSmglocker struct kstat_kv *kvs = ks->ks_data; 2104*3d5e1d3eSmglocker int i; 2105*3d5e1d3eSmglocker 2106*3d5e1d3eSmglocker for (i = 0; i < sc->sc_nutrs; i++) 2107*3d5e1d3eSmglocker kstat_kv_u64(&kvs[i]) = sc->sc_stats_slots[i]; 2108*3d5e1d3eSmglocker 2109*3d5e1d3eSmglocker return 0; 2110*3d5e1d3eSmglocker } 2111*3d5e1d3eSmglocker 2112*3d5e1d3eSmglocker int 2113*3d5e1d3eSmglocker ufshci_kstat_read_slot(struct kstat *ks) 2114*3d5e1d3eSmglocker { 2115*3d5e1d3eSmglocker struct ufshci_softc *sc = ks->ks_softc; 2116*3d5e1d3eSmglocker struct kstat_kv *kvs = ks->ks_data; 2117*3d5e1d3eSmglocker struct ufshci_ccb *ccb; 2118*3d5e1d3eSmglocker uint16_t free, inprogress, ready2free; 2119*3d5e1d3eSmglocker int i; 2120*3d5e1d3eSmglocker 2121*3d5e1d3eSmglocker free = inprogress = ready2free = 0; 21223ad2ef2dSmglocker 21233ad2ef2dSmglocker for (i = 0; i < sc->sc_nutrs; i++) { 21243ad2ef2dSmglocker ccb = &sc->sc_ccbs[i]; 21253ad2ef2dSmglocker 21263ad2ef2dSmglocker switch (ccb->ccb_status) { 21273ad2ef2dSmglocker case CCB_STATUS_FREE: 21283ad2ef2dSmglocker free++; 21293ad2ef2dSmglocker break; 21303ad2ef2dSmglocker case CCB_STATUS_INPROGRESS: 21313ad2ef2dSmglocker inprogress++; 21323ad2ef2dSmglocker break; 21333ad2ef2dSmglocker case CCB_STATUS_READY2FREE: 21343ad2ef2dSmglocker ready2free++; 21353ad2ef2dSmglocker break; 21363ad2ef2dSmglocker } 21373ad2ef2dSmglocker } 21383ad2ef2dSmglocker 2139*3d5e1d3eSmglocker kstat_kv_u16(&kvs[0]) = free; 2140*3d5e1d3eSmglocker kstat_kv_u16(&kvs[1]) = inprogress; 2141*3d5e1d3eSmglocker kstat_kv_u16(&kvs[2]) = ready2free; 2142*3d5e1d3eSmglocker 2143*3d5e1d3eSmglocker return 0; 21443ad2ef2dSmglocker } 2145*3d5e1d3eSmglocker #endif /* NKSTAT > 0 */ 2146