xref: /openbsd/sys/dev/ic/ufshci.c (revision 3d5e1d3e)
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