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
ufshci_intr(void * arg)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
ufshci_attach(struct ufshci_softc * sc)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
ufshci_reset(struct ufshci_softc * sc)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
ufshci_is_poll(struct ufshci_softc * sc,uint32_t type)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 *
ufshci_dmamem_alloc(struct ufshci_softc * sc,size_t size)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
ufshci_dmamem_free(struct ufshci_softc * sc,struct ufshci_dmamem * udm)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
ufshci_alloc(struct ufshci_softc * sc)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
ufshci_init(struct ufshci_softc * sc)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
ufshci_disable(struct ufshci_softc * sc)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
ufshci_doorbell_read(struct ufshci_softc * sc)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
ufshci_doorbell_write(struct ufshci_softc * sc,int slot)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
ufshci_doorbell_poll(struct ufshci_softc * sc,int slot,uint32_t timeout_ms)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
ufshci_utr_cmd_nop(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs)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
ufshci_utr_cmd_lun(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs)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
ufshci_utr_cmd_inquiry(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs)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
ufshci_utr_cmd_capacity16(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs)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
ufshci_utr_cmd_capacity(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs)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
ufshci_utr_cmd_io(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs,int dir)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
ufshci_utr_cmd_sync(struct ufshci_softc * sc,struct ufshci_ccb * ccb,struct scsi_xfer * xs,uint32_t lba,uint16_t blocks)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
ufshci_xfer_complete(struct ufshci_softc * sc)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
ufshci_activate(struct device * self,int act)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
ufshci_ccb_alloc(struct ufshci_softc * sc,int nccbs)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 *
ufshci_ccb_get(void * cookie)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
ufshci_ccb_put(void * cookie,void * io)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
ufshci_ccb_free(struct ufshci_softc * sc,int nccbs)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
ufshci_scsi_cmd(struct scsi_xfer * xs)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
ufshci_scsi_inquiry(struct scsi_xfer * xs)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
ufshci_scsi_capacity16(struct scsi_xfer * xs)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
ufshci_scsi_capacity(struct scsi_xfer * xs)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
ufshci_scsi_sync(struct scsi_xfer * xs)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
ufshci_scsi_io(struct scsi_xfer * xs,int dir)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
ufshci_scsi_io_done(struct ufshci_softc * sc,struct ufshci_ccb * ccb)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
ufshci_scsi_done(struct ufshci_softc * sc,struct ufshci_ccb * ccb)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
ufshci_hibernate_io(dev_t dev,daddr_t blkno,vaddr_t addr,size_t size,int op,void * page)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
ufshci_kstat_attach(struct ufshci_softc * sc)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
ufshci_kstat_read_ccb(struct kstat * ks)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
ufshci_kstat_read_slot(struct kstat * ks)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