xref: /openbsd-src/sys/dev/ic/nvme.c (revision a006a8cd0d4ccdbdbc9d0332b5f6fdc2b6b1aed3)
1*a006a8cdSkettenis /*	$OpenBSD: nvme.c,v 1.124 2024/10/08 19:41:23 kettenis Exp $ */
2282c0692Sdlg 
3282c0692Sdlg /*
4282c0692Sdlg  * Copyright (c) 2014 David Gwynne <dlg@openbsd.org>
5282c0692Sdlg  *
6282c0692Sdlg  * Permission to use, copy, modify, and distribute this software for any
7282c0692Sdlg  * purpose with or without fee is hereby granted, provided that the above
8282c0692Sdlg  * copyright notice and this permission notice appear in all copies.
9282c0692Sdlg  *
10282c0692Sdlg  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11282c0692Sdlg  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12282c0692Sdlg  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13282c0692Sdlg  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14282c0692Sdlg  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15282c0692Sdlg  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16282c0692Sdlg  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17282c0692Sdlg  */
18282c0692Sdlg 
197f4636ceSkrw #include "bio.h"
207f4636ceSkrw 
21282c0692Sdlg #include <sys/param.h>
227f4636ceSkrw #include <sys/ioctl.h>
23282c0692Sdlg #include <sys/systm.h>
24282c0692Sdlg #include <sys/buf.h>
25282c0692Sdlg #include <sys/kernel.h>
26282c0692Sdlg #include <sys/malloc.h>
27282c0692Sdlg #include <sys/device.h>
28282c0692Sdlg #include <sys/queue.h>
29282c0692Sdlg #include <sys/mutex.h>
30282c0692Sdlg #include <sys/pool.h>
317f4636ceSkrw #include <sys/disk.h>
32282c0692Sdlg 
3303d86467Sjmatthew #include <sys/atomic.h>
3403d86467Sjmatthew 
35282c0692Sdlg #include <machine/bus.h>
36282c0692Sdlg 
37282c0692Sdlg #include <scsi/scsi_all.h>
38c947e044Sdlg #include <scsi/scsi_disk.h>
39282c0692Sdlg #include <scsi/scsiconf.h>
407f4636ceSkrw #include <scsi/sdvar.h>
41282c0692Sdlg 
427f4636ceSkrw #include <dev/biovar.h>
43448b3c09Sdlg #include <dev/ic/nvmereg.h>
44448b3c09Sdlg #include <dev/ic/nvmevar.h>
454e9514d6Skrw #include <dev/ic/nvmeio.h>
46448b3c09Sdlg 
47282c0692Sdlg struct cfdriver nvme_cd = {
48282c0692Sdlg 	NULL,
49282c0692Sdlg 	"nvme",
50282c0692Sdlg 	DV_DULL
51282c0692Sdlg };
52282c0692Sdlg 
53282c0692Sdlg int	nvme_ready(struct nvme_softc *, u_int32_t);
54ee8b2d53Skrw int	nvme_enable(struct nvme_softc *);
55282c0692Sdlg int	nvme_disable(struct nvme_softc *);
565313ab17Sdlg int	nvme_shutdown(struct nvme_softc *);
57aa5ddcf9Ssf int	nvme_resume(struct nvme_softc *);
58282c0692Sdlg 
59282c0692Sdlg void	nvme_dumpregs(struct nvme_softc *);
6088aa9192Sdlg int	nvme_identify(struct nvme_softc *, u_int);
61e623ca5aSdlg void	nvme_fill_identify(struct nvme_softc *, struct nvme_ccb *, void *);
62282c0692Sdlg 
633800fc35Sjmatthew #ifndef SMALL_KERNEL
643800fc35Sjmatthew void	nvme_refresh_sensors(void *);
653800fc35Sjmatthew #endif
663800fc35Sjmatthew 
67448b3c09Sdlg int	nvme_ccbs_alloc(struct nvme_softc *, u_int);
68a1141c40Stedu void	nvme_ccbs_free(struct nvme_softc *, u_int);
69448b3c09Sdlg 
70448b3c09Sdlg void *	nvme_ccb_get(void *);
71448b3c09Sdlg void	nvme_ccb_put(void *, void *);
72448b3c09Sdlg 
73e623ca5aSdlg int	nvme_poll(struct nvme_softc *, struct nvme_queue *, struct nvme_ccb *,
7454904088Skrw 	    void (*)(struct nvme_softc *, struct nvme_ccb *, void *), u_int32_t);
75e623ca5aSdlg void	nvme_poll_fill(struct nvme_softc *, struct nvme_ccb *, void *);
76e623ca5aSdlg void	nvme_poll_done(struct nvme_softc *, struct nvme_ccb *,
77e623ca5aSdlg 	    struct nvme_cqe *);
78ad9e5681Sdlg void	nvme_sqe_fill(struct nvme_softc *, struct nvme_ccb *, void *);
79e623ca5aSdlg void	nvme_empty_done(struct nvme_softc *, struct nvme_ccb *,
80e623ca5aSdlg 	    struct nvme_cqe *);
81448b3c09Sdlg 
82e623ca5aSdlg struct nvme_queue *
8359968badSdlg 	nvme_q_alloc(struct nvme_softc *, u_int16_t, u_int, u_int);
84cd15a86fSdlg int	nvme_q_create(struct nvme_softc *, struct nvme_queue *);
850abee971Sdv int	nvme_q_reset(struct nvme_softc *, struct nvme_queue *);
865313ab17Sdlg int	nvme_q_delete(struct nvme_softc *, struct nvme_queue *);
87448b3c09Sdlg void	nvme_q_submit(struct nvme_softc *,
88448b3c09Sdlg 	    struct nvme_queue *, struct nvme_ccb *,
89e623ca5aSdlg 	    void (*)(struct nvme_softc *, struct nvme_ccb *, void *));
90e623ca5aSdlg int	nvme_q_complete(struct nvme_softc *, struct nvme_queue *);
91e623ca5aSdlg void	nvme_q_free(struct nvme_softc *, struct nvme_queue *);
92282c0692Sdlg 
9311624e4cSdlg void	nvme_scsi_cmd(struct scsi_xfer *);
94767e8532Skrw void	nvme_minphys(struct buf *, struct scsi_link *);
9511624e4cSdlg int	nvme_scsi_probe(struct scsi_link *);
9611624e4cSdlg void	nvme_scsi_free(struct scsi_link *);
977f4636ceSkrw uint64_t nvme_scsi_size(const struct nvm_identify_namespace *);
984e9514d6Skrw int	nvme_scsi_ioctl(struct scsi_link *, u_long, caddr_t, int);
994e9514d6Skrw int	nvme_passthrough_cmd(struct nvme_softc *, struct nvme_pt_cmd *,
1004e9514d6Skrw 	int, int);
10111624e4cSdlg 
10203d86467Sjmatthew #ifdef HIBERNATE
10303d86467Sjmatthew #include <uvm/uvm_extern.h>
10403d86467Sjmatthew #include <sys/hibernate.h>
10503d86467Sjmatthew #include <sys/disklabel.h>
10603d86467Sjmatthew 
10703d86467Sjmatthew int	nvme_hibernate_io(dev_t, daddr_t, vaddr_t, size_t, int, void *);
10803d86467Sjmatthew #endif
10903d86467Sjmatthew 
1107f4636ceSkrw #if NBIO > 0
1117f4636ceSkrw void	nvme_bio_status(struct bio_status *, const char *, ...);
1127f4636ceSkrw 
1137f4636ceSkrw const char *nvme_bioctl_sdname(const struct nvme_softc *, int);
1147f4636ceSkrw 
1157f4636ceSkrw int	nvme_bioctl(struct device *, u_long, caddr_t);
1167f4636ceSkrw int	nvme_bioctl_inq(struct nvme_softc *, struct bioc_inq *);
1177f4636ceSkrw int	nvme_bioctl_vol(struct nvme_softc *, struct bioc_vol *);
1187f4636ceSkrw int	nvme_bioctl_disk(struct nvme_softc *, struct bioc_disk *);
1197f4636ceSkrw #endif	/* NBIO > 0 */
1207f4636ceSkrw 
121a454aff3Snaddy const struct scsi_adapter nvme_switch = {
1224e9514d6Skrw 	nvme_scsi_cmd, nvme_minphys, nvme_scsi_probe, nvme_scsi_free,
1234e9514d6Skrw 	nvme_scsi_ioctl
12411624e4cSdlg };
12511624e4cSdlg 
1261eef25a1Sdlg void	nvme_scsi_io(struct scsi_xfer *, int);
1271eef25a1Sdlg void	nvme_scsi_io_fill(struct nvme_softc *, struct nvme_ccb *, void *);
128a1e87500Sdlg void	nvme_scsi_io_done(struct nvme_softc *, struct nvme_ccb *,
129a1e87500Sdlg 	    struct nvme_cqe *);
130a1e87500Sdlg 
131689c2c68Sdlg void	nvme_scsi_sync(struct scsi_xfer *);
132689c2c68Sdlg void	nvme_scsi_sync_fill(struct nvme_softc *, struct nvme_ccb *, void *);
133689c2c68Sdlg void	nvme_scsi_sync_done(struct nvme_softc *, struct nvme_ccb *,
134689c2c68Sdlg 	    struct nvme_cqe *);
135689c2c68Sdlg 
1369856392eSdlg void	nvme_scsi_inq(struct scsi_xfer *);
1379856392eSdlg void	nvme_scsi_inquiry(struct scsi_xfer *);
138c947e044Sdlg void	nvme_scsi_capacity16(struct scsi_xfer *);
139c947e044Sdlg void	nvme_scsi_capacity(struct scsi_xfer *);
1409856392eSdlg 
1417599295eSdlg uint32_t	nvme_op_sq_enter(struct nvme_softc *,
1427599295eSdlg 		    struct nvme_queue *, struct nvme_ccb *);
1437599295eSdlg void		nvme_op_sq_leave(struct nvme_softc *,
1447599295eSdlg 		    struct nvme_queue *, struct nvme_ccb *);
1457599295eSdlg uint32_t	nvme_op_sq_enter_locked(struct nvme_softc *,
1467599295eSdlg 		    struct nvme_queue *, struct nvme_ccb *);
1477599295eSdlg void		nvme_op_sq_leave_locked(struct nvme_softc *,
1487599295eSdlg 		    struct nvme_queue *, struct nvme_ccb *);
1497599295eSdlg 
1507599295eSdlg void		nvme_op_cq_done(struct nvme_softc *,
1517599295eSdlg 		    struct nvme_queue *, struct nvme_ccb *);
1527599295eSdlg 
1537599295eSdlg static const struct nvme_ops nvme_ops = {
1547599295eSdlg 	.op_sq_enter		= nvme_op_sq_enter,
1557599295eSdlg 	.op_sq_leave		= nvme_op_sq_leave,
1567599295eSdlg 	.op_sq_enter_locked	= nvme_op_sq_enter_locked,
1577599295eSdlg 	.op_sq_leave_locked	= nvme_op_sq_leave_locked,
1587599295eSdlg 
1597599295eSdlg 	.op_cq_done		= nvme_op_cq_done,
1607599295eSdlg };
1617599295eSdlg 
16254904088Skrw #define NVME_TIMO_QOP			5000	/* ms to create/delete queue */
1631488c4e9Skrw #define NVME_TIMO_PT			5000	/* ms to complete passthrough */
16454904088Skrw #define NVME_TIMO_IDENT			10000	/* ms to probe/identify */
1653800fc35Sjmatthew #define NVME_TIMO_LOG_PAGE		5000	/* ms to read log pages */
16654904088Skrw #define NVME_TIMO_DELAYNS		10	/* ns to delay() in poll loop */
16754904088Skrw 
168343e9e5aSmpi /*
169343e9e5aSmpi  * Some controllers, at least Apple NVMe, always require split
170343e9e5aSmpi  * transfers, so don't use bus_space_{read,write}_8() on LP64.
171343e9e5aSmpi  */
172a30550dbSdlg u_int64_t
173282c0692Sdlg nvme_read8(struct nvme_softc *sc, bus_size_t r)
174282c0692Sdlg {
175282c0692Sdlg 	u_int64_t v;
176282c0692Sdlg 
1777bd1ac27Sdlg 	v = (u_int64_t)nvme_read4(sc, r) |
1787bd1ac27Sdlg 	    (u_int64_t)nvme_read4(sc, r + 4) << 32;
179282c0692Sdlg 
180282c0692Sdlg 	return (v);
181282c0692Sdlg }
182282c0692Sdlg 
183a30550dbSdlg void
184282c0692Sdlg nvme_write8(struct nvme_softc *sc, bus_size_t r, u_int64_t v)
185282c0692Sdlg {
1867bd1ac27Sdlg 	nvme_write4(sc, r, v);
1877bd1ac27Sdlg 	nvme_write4(sc, r + 4, v >> 32);
188282c0692Sdlg }
189282c0692Sdlg 
190282c0692Sdlg void
191282c0692Sdlg nvme_dumpregs(struct nvme_softc *sc)
192282c0692Sdlg {
193282c0692Sdlg 	u_int64_t r8;
194282c0692Sdlg 	u_int32_t r4;
195282c0692Sdlg 
196282c0692Sdlg 	r8 = nvme_read8(sc, NVME_CAP);
197282c0692Sdlg 	printf("%s: cap  0x%016llx\n", DEVNAME(sc), nvme_read8(sc, NVME_CAP));
198282c0692Sdlg 	printf("%s:  mpsmax %u (%u)\n", DEVNAME(sc),
199282c0692Sdlg 	    (u_int)NVME_CAP_MPSMAX(r8), (1 << NVME_CAP_MPSMAX(r8)));
200282c0692Sdlg 	printf("%s:  mpsmin %u (%u)\n", DEVNAME(sc),
201282c0692Sdlg 	    (u_int)NVME_CAP_MPSMIN(r8), (1 << NVME_CAP_MPSMIN(r8)));
202282c0692Sdlg 	printf("%s:  css %llu\n", DEVNAME(sc), NVME_CAP_CSS(r8));
203282c0692Sdlg 	printf("%s:  nssrs %llu\n", DEVNAME(sc), NVME_CAP_NSSRS(r8));
204a7f13332Sdlg 	printf("%s:  dstrd %u\n", DEVNAME(sc), NVME_CAP_DSTRD(r8));
205282c0692Sdlg 	printf("%s:  to %llu msec\n", DEVNAME(sc), NVME_CAP_TO(r8));
206282c0692Sdlg 	printf("%s:  ams %llu\n", DEVNAME(sc), NVME_CAP_AMS(r8));
207282c0692Sdlg 	printf("%s:  cqr %llu\n", DEVNAME(sc), NVME_CAP_CQR(r8));
208282c0692Sdlg 	printf("%s:  mqes %llu\n", DEVNAME(sc), NVME_CAP_MQES(r8));
209282c0692Sdlg 
210a7f13332Sdlg 	printf("%s: vs   0x%04x\n", DEVNAME(sc), nvme_read4(sc, NVME_VS));
211282c0692Sdlg 
212282c0692Sdlg 	r4 = nvme_read4(sc, NVME_CC);
213a7f13332Sdlg 	printf("%s: cc   0x%04x\n", DEVNAME(sc), r4);
214282c0692Sdlg 	printf("%s:  iocqes %u\n", DEVNAME(sc), NVME_CC_IOCQES_R(r4));
215282c0692Sdlg 	printf("%s:  iosqes %u\n", DEVNAME(sc), NVME_CC_IOSQES_R(r4));
216282c0692Sdlg 	printf("%s:  shn %u\n", DEVNAME(sc), NVME_CC_SHN_R(r4));
217282c0692Sdlg 	printf("%s:  ams %u\n", DEVNAME(sc), NVME_CC_AMS_R(r4));
218282c0692Sdlg 	printf("%s:  mps %u\n", DEVNAME(sc), NVME_CC_MPS_R(r4));
219282c0692Sdlg 	printf("%s:  css %u\n", DEVNAME(sc), NVME_CC_CSS_R(r4));
220282c0692Sdlg 	printf("%s:  en %u\n", DEVNAME(sc), ISSET(r4, NVME_CC_EN));
221282c0692Sdlg 
222a7f13332Sdlg 	printf("%s: csts 0x%08x\n", DEVNAME(sc), nvme_read4(sc, NVME_CSTS));
223a7f13332Sdlg 	printf("%s: aqa  0x%08x\n", DEVNAME(sc), nvme_read4(sc, NVME_AQA));
224282c0692Sdlg 	printf("%s: asq  0x%016llx\n", DEVNAME(sc), nvme_read8(sc, NVME_ASQ));
225282c0692Sdlg 	printf("%s: acq  0x%016llx\n", DEVNAME(sc), nvme_read8(sc, NVME_ACQ));
226282c0692Sdlg }
227282c0692Sdlg 
228282c0692Sdlg int
229282c0692Sdlg nvme_ready(struct nvme_softc *sc, u_int32_t rdy)
230282c0692Sdlg {
2319fb4d6d3Skrw 	u_int i = 0;
232282c0692Sdlg 
2339fb4d6d3Skrw 	while ((nvme_read4(sc, NVME_CSTS) & NVME_CSTS_RDY) != rdy) {
2349fb4d6d3Skrw 		if (i++ > sc->sc_rdy_to)
2359fb4d6d3Skrw 			return (1);
236282c0692Sdlg 
237282c0692Sdlg 		delay(1000);
238282c0692Sdlg 		nvme_barrier(sc, NVME_CSTS, 4, BUS_SPACE_BARRIER_READ);
239282c0692Sdlg 	}
240282c0692Sdlg 
2419fb4d6d3Skrw 	return (0);
242282c0692Sdlg }
243282c0692Sdlg 
244282c0692Sdlg int
245ee8b2d53Skrw nvme_enable(struct nvme_softc *sc)
246282c0692Sdlg {
2479fb4d6d3Skrw 	u_int32_t cc;
248282c0692Sdlg 
249282c0692Sdlg 	cc = nvme_read4(sc, NVME_CC);
2509fb4d6d3Skrw 	if (ISSET(cc, NVME_CC_EN))
251282c0692Sdlg 		return (nvme_ready(sc, NVME_CSTS_RDY));
252282c0692Sdlg 
2537599295eSdlg 	if (sc->sc_ops->op_enable != NULL)
2547599295eSdlg 		sc->sc_ops->op_enable(sc);
2557599295eSdlg 
25683d8579cSdlg 	nvme_write4(sc, NVME_AQA, NVME_AQA_ACQS(sc->sc_admin_q->q_entries) |
25783d8579cSdlg 	    NVME_AQA_ASQS(sc->sc_admin_q->q_entries));
25883d8579cSdlg 	nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE);
25983d8579cSdlg 
260282c0692Sdlg 	nvme_write8(sc, NVME_ASQ, NVME_DMA_DVA(sc->sc_admin_q->q_sq_dmamem));
261282c0692Sdlg 	nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE);
262282c0692Sdlg 	nvme_write8(sc, NVME_ACQ, NVME_DMA_DVA(sc->sc_admin_q->q_cq_dmamem));
263282c0692Sdlg 	nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE);
264282c0692Sdlg 
265282c0692Sdlg 	CLR(cc, NVME_CC_IOCQES_MASK | NVME_CC_IOSQES_MASK | NVME_CC_SHN_MASK |
266282c0692Sdlg 	    NVME_CC_AMS_MASK | NVME_CC_MPS_MASK | NVME_CC_CSS_MASK);
2670f19ad86Skrw 	SET(cc, NVME_CC_IOSQES(6));	/* Submission queue size == 2**6 (64) */
2680f19ad86Skrw 	SET(cc, NVME_CC_IOCQES(4));	/* Completion queue size == 2**4 (16) */
269282c0692Sdlg 	SET(cc, NVME_CC_SHN(NVME_CC_SHN_NONE));
270282c0692Sdlg 	SET(cc, NVME_CC_CSS(NVME_CC_CSS_NVM));
271282c0692Sdlg 	SET(cc, NVME_CC_AMS(NVME_CC_AMS_RR));
2720f19ad86Skrw 	SET(cc, NVME_CC_MPS(ffs(sc->sc_mps) - 1));
273282c0692Sdlg 	SET(cc, NVME_CC_EN);
274282c0692Sdlg 
275282c0692Sdlg 	nvme_write4(sc, NVME_CC, cc);
276282c0692Sdlg 	nvme_barrier(sc, 0, sc->sc_ios,
277282c0692Sdlg 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
278282c0692Sdlg 
27983d8579cSdlg 	return (nvme_ready(sc, NVME_CSTS_RDY));
280282c0692Sdlg }
281282c0692Sdlg 
282282c0692Sdlg int
283282c0692Sdlg nvme_disable(struct nvme_softc *sc)
284282c0692Sdlg {
285282c0692Sdlg 	u_int32_t cc, csts;
286282c0692Sdlg 
287282c0692Sdlg 	cc = nvme_read4(sc, NVME_CC);
2889fb4d6d3Skrw 	if (ISSET(cc, NVME_CC_EN)) {
289282c0692Sdlg 		csts = nvme_read4(sc, NVME_CSTS);
2909fb4d6d3Skrw 		if (!ISSET(csts, NVME_CSTS_CFS) &&
2919fb4d6d3Skrw 		    nvme_ready(sc, NVME_CSTS_RDY) != 0)
292282c0692Sdlg 			return (1);
293282c0692Sdlg 	}
294282c0692Sdlg 
295282c0692Sdlg 	CLR(cc, NVME_CC_EN);
296282c0692Sdlg 
297282c0692Sdlg 	nvme_write4(sc, NVME_CC, cc);
298282c0692Sdlg 	nvme_barrier(sc, 0, sc->sc_ios,
299282c0692Sdlg 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
300282c0692Sdlg 
301282c0692Sdlg 	return (nvme_ready(sc, 0));
302282c0692Sdlg }
303282c0692Sdlg 
304282c0692Sdlg int
305282c0692Sdlg nvme_attach(struct nvme_softc *sc)
306282c0692Sdlg {
30701b059acSdlg 	struct scsibus_attach_args saa;
308282c0692Sdlg 	u_int64_t cap;
309282c0692Sdlg 	u_int32_t reg;
310a1141c40Stedu 	u_int nccbs = 0;
311282c0692Sdlg 
3123f4555f0Sdlg 	mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
3137f4636ceSkrw 	rw_init(&sc->sc_lock, "nvme_lock");
3143f4555f0Sdlg 	SIMPLEQ_INIT(&sc->sc_ccb_list);
3153f4555f0Sdlg 	scsi_iopool_init(&sc->sc_iopool, sc, nvme_ccb_get, nvme_ccb_put);
3167599295eSdlg 	if (sc->sc_ops == NULL)
3177599295eSdlg 		sc->sc_ops = &nvme_ops;
318e9317b4bSkettenis 	if (sc->sc_openings == 0)
319e9317b4bSkettenis 		sc->sc_openings = 64;
3203f4555f0Sdlg 
321282c0692Sdlg 	reg = nvme_read4(sc, NVME_VS);
32298f39564Skrw 	if (reg == 0xffffffff) {
3237317d3e7Sdlg 		printf("invalid mapping\n");
324282c0692Sdlg 		return (1);
325282c0692Sdlg 	}
326282c0692Sdlg 
3277317d3e7Sdlg 	printf("NVMe %d.%d\n", NVME_VS_MJR(reg), NVME_VS_MNR(reg));
328282c0692Sdlg 
329282c0692Sdlg 	cap = nvme_read8(sc, NVME_CAP);
330aa5ddcf9Ssf 	sc->sc_dstrd = NVME_CAP_DSTRD(cap);
3311be1268fSdlg 	if (NVME_CAP_MPSMIN(cap) > PAGE_SHIFT) {
3321be1268fSdlg 		printf("%s: NVMe minimum page size %u "
3331be1268fSdlg 		    "is greater than CPU page size %u\n", DEVNAME(sc),
3341be1268fSdlg 		    1 << NVME_CAP_MPSMIN(cap), 1 << PAGE_SHIFT);
3351be1268fSdlg 		return (1);
3361be1268fSdlg 	}
33794a36263Skmos 	if (NVME_CAP_MPSMAX(cap) < PAGE_SHIFT)
33894a36263Skmos 		sc->sc_mps = 1 << NVME_CAP_MPSMAX(cap);
33994a36263Skmos 	else
34094a36263Skmos 		sc->sc_mps = 1 << PAGE_SHIFT;
341282c0692Sdlg 
342282c0692Sdlg 	sc->sc_rdy_to = NVME_CAP_TO(cap);
343448b3c09Sdlg 	sc->sc_mdts = MAXPHYS;
344a8751b7cSkrw 	sc->sc_max_prpl = sc->sc_mdts / sc->sc_mps;
3453f4555f0Sdlg 
3463f4555f0Sdlg 	if (nvme_disable(sc) != 0) {
3473f4555f0Sdlg 		printf("%s: unable to disable controller\n", DEVNAME(sc));
3483f4555f0Sdlg 		return (1);
3493f4555f0Sdlg 	}
350282c0692Sdlg 
351aa5ddcf9Ssf 	sc->sc_admin_q = nvme_q_alloc(sc, NVME_ADMIN_Q, 128, sc->sc_dstrd);
352282c0692Sdlg 	if (sc->sc_admin_q == NULL) {
353282c0692Sdlg 		printf("%s: unable to allocate admin queue\n", DEVNAME(sc));
354282c0692Sdlg 		return (1);
355282c0692Sdlg 	}
356282c0692Sdlg 
357448b3c09Sdlg 	if (nvme_ccbs_alloc(sc, 16) != 0) {
358448b3c09Sdlg 		printf("%s: unable to allocate initial ccbs\n", DEVNAME(sc));
359282c0692Sdlg 		goto free_admin_q;
360282c0692Sdlg 	}
361a1141c40Stedu 	nccbs = 16;
362282c0692Sdlg 
363ee8b2d53Skrw 	if (nvme_enable(sc) != 0) {
364448b3c09Sdlg 		printf("%s: unable to enable controller\n", DEVNAME(sc));
365448b3c09Sdlg 		goto free_ccbs;
366448b3c09Sdlg 	}
367448b3c09Sdlg 
36888aa9192Sdlg 	if (nvme_identify(sc, NVME_CAP_MPSMIN(cap)) != 0) {
369448b3c09Sdlg 		printf("%s: unable to identify controller\n", DEVNAME(sc));
370448b3c09Sdlg 		goto disable;
371448b3c09Sdlg 	}
372282c0692Sdlg 
373a8751b7cSkrw 	/* We now know the real values of sc_mdts and sc_max_prpl. */
374a1141c40Stedu 	nvme_ccbs_free(sc, nccbs);
3751926545cSdlg 	if (nvme_ccbs_alloc(sc, 64) != 0) {
3761926545cSdlg 		printf("%s: unable to allocate ccbs\n", DEVNAME(sc));
3771926545cSdlg 		goto free_admin_q;
3781926545cSdlg 	}
379a1141c40Stedu 	nccbs = 64;
3801926545cSdlg 
38103d86467Sjmatthew 	sc->sc_q = nvme_q_alloc(sc, NVME_IO_Q, 128, sc->sc_dstrd);
38260c144b3Sdlg 	if (sc->sc_q == NULL) {
38360c144b3Sdlg 		printf("%s: unable to allocate io q\n", DEVNAME(sc));
38460c144b3Sdlg 		goto disable;
38560c144b3Sdlg 	}
38660c144b3Sdlg 
38760c144b3Sdlg 	if (nvme_q_create(sc, sc->sc_q) != 0) {
38860c144b3Sdlg 		printf("%s: unable to create io q\n", DEVNAME(sc));
38960c144b3Sdlg 		goto free_q;
39060c144b3Sdlg 	}
39160c144b3Sdlg 
3924d4525deSkettenis #ifdef HIBERNATE
39303d86467Sjmatthew 	sc->sc_hib_q = nvme_q_alloc(sc, NVME_HIB_Q, 4, sc->sc_dstrd);
39403d86467Sjmatthew 	if (sc->sc_hib_q == NULL) {
39503d86467Sjmatthew 		printf("%s: unable to allocate hibernate io queue\n", DEVNAME(sc));
39689715fd5Sjsg 		goto free_q;
39703d86467Sjmatthew 	}
3984d4525deSkettenis #endif
39903d86467Sjmatthew 
400ffeaebd1Sdlg 	nvme_write4(sc, NVME_INTMC, 1);
401ffeaebd1Sdlg 
402397f5692Skettenis 	sc->sc_namespaces = mallocarray(sc->sc_nn + 1,
403397f5692Skettenis 	    sizeof(*sc->sc_namespaces), M_DEVBUF, M_WAITOK|M_ZERO);
404bc407f60Sdlg 
405ead808c4Skrw 	saa.saa_adapter = &nvme_switch;
406ead808c4Skrw 	saa.saa_adapter_softc = sc;
407ead808c4Skrw 	saa.saa_adapter_buswidth = sc->sc_nn + 1;
408ead808c4Skrw 	saa.saa_luns = 1;
409ead808c4Skrw 	saa.saa_adapter_target = 0;
410e9317b4bSkettenis 	saa.saa_openings = sc->sc_openings;
411e5eae15dSkrw 	saa.saa_pool = &sc->sc_iopool;
412e5eae15dSkrw 	saa.saa_quirks = saa.saa_flags = 0;
413e5eae15dSkrw 	saa.saa_wwpn = saa.saa_wwnn = 0;
41401b059acSdlg 
4153800fc35Sjmatthew 	strlcpy(sc->sc_sensordev.xname, DEVNAME(sc), sizeof(sc->sc_sensordev.xname));
4163800fc35Sjmatthew 
4173800fc35Sjmatthew #ifndef SMALL_KERNEL
4183800fc35Sjmatthew 	sc->sc_temp_sensor.type = SENSOR_TEMP;
4193800fc35Sjmatthew 	sc->sc_temp_sensor.status = SENSOR_S_UNKNOWN;
4203800fc35Sjmatthew 	sensor_attach(&sc->sc_sensordev, &sc->sc_temp_sensor);
4213800fc35Sjmatthew 
4223800fc35Sjmatthew 	sc->sc_usage_sensor.type = SENSOR_PERCENT;
4233800fc35Sjmatthew 	sc->sc_usage_sensor.status = SENSOR_S_UNKNOWN;
4243800fc35Sjmatthew 	strlcpy(sc->sc_usage_sensor.desc, "endurance used",
4253800fc35Sjmatthew 	    sizeof(sc->sc_usage_sensor.desc));
4263800fc35Sjmatthew 	sensor_attach(&sc->sc_sensordev, &sc->sc_usage_sensor);
4273800fc35Sjmatthew 
4283800fc35Sjmatthew 	sc->sc_spare_sensor.type = SENSOR_PERCENT;
4293800fc35Sjmatthew 	sc->sc_spare_sensor.status = SENSOR_S_UNKNOWN;
4303800fc35Sjmatthew 	strlcpy(sc->sc_spare_sensor.desc, "available spare",
4313800fc35Sjmatthew 	    sizeof(sc->sc_spare_sensor.desc));
4323800fc35Sjmatthew 	sensor_attach(&sc->sc_sensordev, &sc->sc_spare_sensor);
4333800fc35Sjmatthew 
4343800fc35Sjmatthew 	if (sensor_task_register(sc, nvme_refresh_sensors, 60) == NULL)
4353800fc35Sjmatthew 		goto free_q;
4363800fc35Sjmatthew 
4373800fc35Sjmatthew 	sensordev_install(&sc->sc_sensordev);
4383800fc35Sjmatthew #endif
4393800fc35Sjmatthew 
4407f4636ceSkrw 	sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
4417f4636ceSkrw 	    &saa, scsiprint);
4427f4636ceSkrw #if NBIO > 0
4437f4636ceSkrw 	if (bio_register(&sc->sc_dev, nvme_bioctl) != 0)
4447f4636ceSkrw 		printf("%s: unable to register bioctl\n", DEVNAME(sc));
4457f4636ceSkrw #endif	/* NBIO > 0 */
44601b059acSdlg 
447282c0692Sdlg 	return (0);
448282c0692Sdlg 
44960c144b3Sdlg free_q:
45060c144b3Sdlg 	nvme_q_free(sc, sc->sc_q);
451448b3c09Sdlg disable:
452448b3c09Sdlg 	nvme_disable(sc);
453448b3c09Sdlg free_ccbs:
454a1141c40Stedu 	nvme_ccbs_free(sc, nccbs);
455282c0692Sdlg free_admin_q:
456448b3c09Sdlg 	nvme_q_free(sc, sc->sc_admin_q);
457282c0692Sdlg 
458282c0692Sdlg 	return (1);
459282c0692Sdlg }
460282c0692Sdlg 
46111624e4cSdlg int
462aa5ddcf9Ssf nvme_resume(struct nvme_softc *sc)
463aa5ddcf9Ssf {
464aa5ddcf9Ssf 	if (nvme_disable(sc) != 0) {
465aa5ddcf9Ssf 		printf("%s: unable to disable controller\n", DEVNAME(sc));
466aa5ddcf9Ssf 		return (1);
467aa5ddcf9Ssf 	}
468aa5ddcf9Ssf 
4690abee971Sdv 	if (nvme_q_reset(sc, sc->sc_admin_q) != 0) {
4700abee971Sdv 		printf("%s: unable to reset admin queue\n", DEVNAME(sc));
4710abee971Sdv 		return (1);
4720abee971Sdv 	}
473aa5ddcf9Ssf 
474ee8b2d53Skrw 	if (nvme_enable(sc) != 0) {
475aa5ddcf9Ssf 		printf("%s: unable to enable controller\n", DEVNAME(sc));
476aa5ddcf9Ssf 		return (1);
477aa5ddcf9Ssf 	}
478aa5ddcf9Ssf 
4790abee971Sdv 	sc->sc_q = nvme_q_alloc(sc, NVME_IO_Q, 128, sc->sc_dstrd);
4800abee971Sdv 	if (sc->sc_q == NULL) {
4810abee971Sdv 		printf("%s: unable to allocate io q\n", DEVNAME(sc));
4820abee971Sdv 		goto disable;
4830abee971Sdv 	}
4840abee971Sdv 
485aa5ddcf9Ssf 	if (nvme_q_create(sc, sc->sc_q) != 0) {
486aa5ddcf9Ssf 		printf("%s: unable to create io q\n", DEVNAME(sc));
4870abee971Sdv 		goto free_q;
488aa5ddcf9Ssf 	}
489aa5ddcf9Ssf 
490aa5ddcf9Ssf 	nvme_write4(sc, NVME_INTMC, 1);
491aa5ddcf9Ssf 
492aa5ddcf9Ssf 	return (0);
493aa5ddcf9Ssf 
4940abee971Sdv free_q:
4950abee971Sdv 	nvme_q_free(sc, sc->sc_q);
496aa5ddcf9Ssf disable:
497aa5ddcf9Ssf 	nvme_disable(sc);
498aa5ddcf9Ssf 
499aa5ddcf9Ssf 	return (1);
500aa5ddcf9Ssf }
501aa5ddcf9Ssf 
502aa5ddcf9Ssf int
50311624e4cSdlg nvme_scsi_probe(struct scsi_link *link)
50411624e4cSdlg {
5050b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
50665dd51f8Sdlg 	struct nvme_sqe sqe;
50765dd51f8Sdlg 	struct nvm_identify_namespace *identify;
50865dd51f8Sdlg 	struct nvme_dmamem *mem;
50965dd51f8Sdlg 	struct nvme_ccb *ccb;
51065dd51f8Sdlg 	int rv;
51165dd51f8Sdlg 
51265dd51f8Sdlg 	ccb = scsi_io_get(&sc->sc_iopool, 0);
51365dd51f8Sdlg 	KASSERT(ccb != NULL);
51465dd51f8Sdlg 
51565dd51f8Sdlg 	mem = nvme_dmamem_alloc(sc, sizeof(*identify));
51665dd51f8Sdlg 	if (mem == NULL)
51765dd51f8Sdlg 		return (ENOMEM);
51865dd51f8Sdlg 
51965dd51f8Sdlg 	memset(&sqe, 0, sizeof(sqe));
52065dd51f8Sdlg 	sqe.opcode = NVM_ADMIN_IDENTIFY;
521397f5692Skettenis 	htolem32(&sqe.nsid, link->target);
52265dd51f8Sdlg 	htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem));
52365dd51f8Sdlg 	htolem32(&sqe.cdw10, 0);
52465dd51f8Sdlg 
52565dd51f8Sdlg 	ccb->ccb_done = nvme_empty_done;
52665dd51f8Sdlg 	ccb->ccb_cookie = &sqe;
52765dd51f8Sdlg 
52865dd51f8Sdlg 	nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD);
52954904088Skrw 	rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_IDENT);
53065dd51f8Sdlg 	nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD);
53165dd51f8Sdlg 
53265dd51f8Sdlg 	scsi_io_put(&sc->sc_iopool, ccb);
53365dd51f8Sdlg 
5343f48a60eSkrw 	identify = NVME_DMA_KVA(mem);
535badf3925Sjan 	if (rv == 0) {
536daef0c50Skrw 		if (nvme_scsi_size(identify) > 0) {
5373f48a60eSkrw 			/* Commit namespace if it has a size greater than zero. */
5383f48a60eSkrw 			identify = malloc(sizeof(*identify), M_DEVBUF, M_WAITOK);
5393f48a60eSkrw 			memcpy(identify, NVME_DMA_KVA(mem), sizeof(*identify));
5403f48a60eSkrw 			sc->sc_namespaces[link->target].ident = identify;
541badf3925Sjan 		} else {
542badf3925Sjan 			/* Don't attach a namespace if its size is zero. */
543badf3925Sjan 			rv = ENXIO;
544badf3925Sjan 		}
54565dd51f8Sdlg 	}
54665dd51f8Sdlg 
54765dd51f8Sdlg 	nvme_dmamem_free(sc, mem);
54865dd51f8Sdlg 
54965dd51f8Sdlg 	return (rv);
55011624e4cSdlg }
55111624e4cSdlg 
5525313ab17Sdlg int
5535313ab17Sdlg nvme_shutdown(struct nvme_softc *sc)
5545313ab17Sdlg {
5555313ab17Sdlg 	u_int32_t cc, csts;
5565313ab17Sdlg 	int i;
5575313ab17Sdlg 
5585313ab17Sdlg 	nvme_write4(sc, NVME_INTMC, 0);
5595313ab17Sdlg 
5605313ab17Sdlg 	if (nvme_q_delete(sc, sc->sc_q) != 0) {
5615313ab17Sdlg 		printf("%s: unable to delete q, disabling\n", DEVNAME(sc));
5625313ab17Sdlg 		goto disable;
5635313ab17Sdlg 	}
5645313ab17Sdlg 
5655313ab17Sdlg 	cc = nvme_read4(sc, NVME_CC);
5665313ab17Sdlg 	CLR(cc, NVME_CC_SHN_MASK);
5675313ab17Sdlg 	SET(cc, NVME_CC_SHN(NVME_CC_SHN_NORMAL));
5685313ab17Sdlg 	nvme_write4(sc, NVME_CC, cc);
5695313ab17Sdlg 
5705313ab17Sdlg 	for (i = 0; i < 4000; i++) {
5715313ab17Sdlg 		nvme_barrier(sc, 0, sc->sc_ios,
5725313ab17Sdlg 		    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
5735313ab17Sdlg 		csts = nvme_read4(sc, NVME_CSTS);
5745313ab17Sdlg 		if ((csts & NVME_CSTS_SHST_MASK) == NVME_CSTS_SHST_DONE)
5755313ab17Sdlg 			return (0);
5765313ab17Sdlg 
5775313ab17Sdlg 		delay(1000);
5785313ab17Sdlg 	}
5795313ab17Sdlg 
5807f0b5f10Sderaadt 	printf("%s: unable to shutdown, disabling\n", DEVNAME(sc));
5815313ab17Sdlg 
5825313ab17Sdlg disable:
5835313ab17Sdlg 	nvme_disable(sc);
5845313ab17Sdlg 	return (0);
5855313ab17Sdlg }
5865313ab17Sdlg 
5875313ab17Sdlg int
5885313ab17Sdlg nvme_activate(struct nvme_softc *sc, int act)
5895313ab17Sdlg {
5905313ab17Sdlg 	int rv;
5915313ab17Sdlg 
5925313ab17Sdlg 	switch (act) {
5935313ab17Sdlg 	case DVACT_POWERDOWN:
5945313ab17Sdlg 		rv = config_activate_children(&sc->sc_dev, act);
5955313ab17Sdlg 		nvme_shutdown(sc);
5965313ab17Sdlg 		break;
597aa5ddcf9Ssf 	case DVACT_RESUME:
598aa5ddcf9Ssf 		rv = nvme_resume(sc);
599aa5ddcf9Ssf 		if (rv == 0)
600aa5ddcf9Ssf 			rv = config_activate_children(&sc->sc_dev, act);
601aa5ddcf9Ssf 		break;
6025313ab17Sdlg 	default:
6035313ab17Sdlg 		rv = config_activate_children(&sc->sc_dev, act);
6045313ab17Sdlg 		break;
6055313ab17Sdlg 	}
6065313ab17Sdlg 
6075313ab17Sdlg 	return (rv);
6085313ab17Sdlg }
6095313ab17Sdlg 
61011624e4cSdlg void
61111624e4cSdlg nvme_scsi_cmd(struct scsi_xfer *xs)
61211624e4cSdlg {
613664c6166Skrw 	switch (xs->cmd.opcode) {
614a1e87500Sdlg 	case READ_COMMAND:
615eccd596dSkrw 	case READ_10:
616a1e87500Sdlg 	case READ_12:
617a1e87500Sdlg 	case READ_16:
6181eef25a1Sdlg 		nvme_scsi_io(xs, SCSI_DATA_IN);
619a1e87500Sdlg 		return;
620a1e87500Sdlg 	case WRITE_COMMAND:
621eccd596dSkrw 	case WRITE_10:
622a1e87500Sdlg 	case WRITE_12:
623a1e87500Sdlg 	case WRITE_16:
6241eef25a1Sdlg 		nvme_scsi_io(xs, SCSI_DATA_OUT);
625d3f19a0bSdlg 		return;
626a1e87500Sdlg 
627689c2c68Sdlg 	case SYNCHRONIZE_CACHE:
628689c2c68Sdlg 		nvme_scsi_sync(xs);
629689c2c68Sdlg 		return;
630689c2c68Sdlg 
6319856392eSdlg 	case INQUIRY:
6329856392eSdlg 		nvme_scsi_inq(xs);
6339856392eSdlg 		return;
634c947e044Sdlg 	case READ_CAPACITY_16:
635c947e044Sdlg 		nvme_scsi_capacity16(xs);
636c947e044Sdlg 		return;
637c947e044Sdlg 	case READ_CAPACITY:
638c947e044Sdlg 		nvme_scsi_capacity(xs);
639c947e044Sdlg 		return;
640c947e044Sdlg 
641a50b57eaSdlg 	case TEST_UNIT_READY:
642a50b57eaSdlg 	case PREVENT_ALLOW:
643a50b57eaSdlg 	case START_STOP:
644a50b57eaSdlg 		xs->error = XS_NOERROR;
645a50b57eaSdlg 		scsi_done(xs);
646a50b57eaSdlg 		return;
647a50b57eaSdlg 
6489856392eSdlg 	default:
6499856392eSdlg 		break;
6509856392eSdlg 	}
6519856392eSdlg 
65211624e4cSdlg 	xs->error = XS_DRIVER_STUFFUP;
65311624e4cSdlg 	scsi_done(xs);
65411624e4cSdlg }
65511624e4cSdlg 
65611624e4cSdlg void
657767e8532Skrw nvme_minphys(struct buf *bp, struct scsi_link *link)
658767e8532Skrw {
6590b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
660767e8532Skrw 
661767e8532Skrw 	if (bp->b_bcount > sc->sc_mdts)
662767e8532Skrw 		bp->b_bcount = sc->sc_mdts;
663767e8532Skrw }
664767e8532Skrw 
665767e8532Skrw void
6661eef25a1Sdlg nvme_scsi_io(struct scsi_xfer *xs, int dir)
667a1e87500Sdlg {
668a1e87500Sdlg 	struct scsi_link *link = xs->sc_link;
6690b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
670a1e87500Sdlg 	struct nvme_ccb *ccb = xs->io;
671a1e87500Sdlg 	bus_dmamap_t dmap = ccb->ccb_dmamap;
672ecfae151Sdlg 	int i;
673a1e87500Sdlg 
6741eef25a1Sdlg 	if ((xs->flags & (SCSI_DATA_IN|SCSI_DATA_OUT)) != dir)
6751eef25a1Sdlg 		goto stuffup;
6761eef25a1Sdlg 
677a1e87500Sdlg 	ccb->ccb_done = nvme_scsi_io_done;
678a1e87500Sdlg 	ccb->ccb_cookie = xs;
679a1e87500Sdlg 
680a1e87500Sdlg 	if (bus_dmamap_load(sc->sc_dmat, dmap,
681a1e87500Sdlg 	    xs->data, xs->datalen, NULL, ISSET(xs->flags, SCSI_NOSLEEP) ?
682a1e87500Sdlg 	    BUS_DMA_NOWAIT : BUS_DMA_WAITOK) != 0)
683a1e87500Sdlg 		goto stuffup;
684a1e87500Sdlg 
685a1e87500Sdlg 	bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
686a1e87500Sdlg 	    ISSET(xs->flags, SCSI_DATA_IN) ?
687a1e87500Sdlg 	    BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
688a1e87500Sdlg 
689ecfae151Sdlg 	if (dmap->dm_nsegs > 2) {
690ecfae151Sdlg 		for (i = 1; i < dmap->dm_nsegs; i++) {
691ecfae151Sdlg 			htolem64(&ccb->ccb_prpl[i - 1],
692ecfae151Sdlg 			    dmap->dm_segs[i].ds_addr);
693ecfae151Sdlg 		}
694ecfae151Sdlg 		bus_dmamap_sync(sc->sc_dmat,
695ecfae151Sdlg 		    NVME_DMA_MAP(sc->sc_ccb_prpls),
696ecfae151Sdlg 		    ccb->ccb_prpl_off,
697e4727810Spatrick 		    sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1),
698ecfae151Sdlg 		    BUS_DMASYNC_PREWRITE);
699ecfae151Sdlg 	}
700ecfae151Sdlg 
701a1e87500Sdlg 	if (ISSET(xs->flags, SCSI_POLL)) {
70254904088Skrw 		nvme_poll(sc, sc->sc_q, ccb, nvme_scsi_io_fill, xs->timeout);
703a1e87500Sdlg 		return;
704a1e87500Sdlg 	}
705a1e87500Sdlg 
7061eef25a1Sdlg 	nvme_q_submit(sc, sc->sc_q, ccb, nvme_scsi_io_fill);
707a1e87500Sdlg 	return;
708a1e87500Sdlg 
709a1e87500Sdlg stuffup:
710a1e87500Sdlg 	xs->error = XS_DRIVER_STUFFUP;
711a1e87500Sdlg 	scsi_done(xs);
712a1e87500Sdlg }
713a1e87500Sdlg 
714a1e87500Sdlg void
7151eef25a1Sdlg nvme_scsi_io_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot)
716a1e87500Sdlg {
717a1e87500Sdlg 	struct nvme_sqe_io *sqe = slot;
718a1e87500Sdlg 	struct scsi_xfer *xs = ccb->ccb_cookie;
719a1e87500Sdlg 	struct scsi_link *link = xs->sc_link;
720a1e87500Sdlg 	bus_dmamap_t dmap = ccb->ccb_dmamap;
721a1e87500Sdlg 	u_int64_t lba;
722a1e87500Sdlg 	u_int32_t blocks;
723a1e87500Sdlg 
724664c6166Skrw 	scsi_cmd_rw_decode(&xs->cmd, &lba, &blocks);
725a1e87500Sdlg 
7261eef25a1Sdlg 	sqe->opcode = ISSET(xs->flags, SCSI_DATA_IN) ?
7271eef25a1Sdlg 	    NVM_CMD_READ : NVM_CMD_WRITE;
728397f5692Skettenis 	htolem32(&sqe->nsid, link->target);
729a1e87500Sdlg 
730a1e87500Sdlg 	htolem64(&sqe->entry.prp[0], dmap->dm_segs[0].ds_addr);
731a1e87500Sdlg 	switch (dmap->dm_nsegs) {
732a1e87500Sdlg 	case 1:
733a1e87500Sdlg 		break;
734a1e87500Sdlg 	case 2:
735a1e87500Sdlg 		htolem64(&sqe->entry.prp[1], dmap->dm_segs[1].ds_addr);
736a1e87500Sdlg 		break;
737a1e87500Sdlg 	default:
738ecfae151Sdlg 		/* the prp list is already set up and synced */
739ecfae151Sdlg 		htolem64(&sqe->entry.prp[1], ccb->ccb_prpl_dva);
740ecfae151Sdlg 		break;
741a1e87500Sdlg 	}
742a1e87500Sdlg 
743a1e87500Sdlg 	htolem64(&sqe->slba, lba);
744a1e87500Sdlg 	htolem16(&sqe->nlb, blocks - 1);
745a1e87500Sdlg }
746a1e87500Sdlg 
747a1e87500Sdlg void
748a1e87500Sdlg nvme_scsi_io_done(struct nvme_softc *sc, struct nvme_ccb *ccb,
749a1e87500Sdlg     struct nvme_cqe *cqe)
750a1e87500Sdlg {
751a1e87500Sdlg 	struct scsi_xfer *xs = ccb->ccb_cookie;
752a1e87500Sdlg 	bus_dmamap_t dmap = ccb->ccb_dmamap;
753a1e87500Sdlg 	u_int16_t flags;
754a1e87500Sdlg 
755ecfae151Sdlg 	if (dmap->dm_nsegs > 2) {
756ecfae151Sdlg 		bus_dmamap_sync(sc->sc_dmat,
757ecfae151Sdlg 		    NVME_DMA_MAP(sc->sc_ccb_prpls),
758ecfae151Sdlg 		    ccb->ccb_prpl_off,
759e4727810Spatrick 		    sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1),
760ecfae151Sdlg 		    BUS_DMASYNC_POSTWRITE);
761ecfae151Sdlg 	}
762ecfae151Sdlg 
763a1e87500Sdlg 	bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
764a1e87500Sdlg 	    ISSET(xs->flags, SCSI_DATA_IN) ?
765a1e87500Sdlg 	    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
766a1e87500Sdlg 
767a1e87500Sdlg 	bus_dmamap_unload(sc->sc_dmat, dmap);
768a1e87500Sdlg 
769a1e87500Sdlg 	flags = lemtoh16(&cqe->flags);
770a1e87500Sdlg 
771a1e87500Sdlg 	xs->error = (NVME_CQE_SC(flags) == NVME_CQE_SC_SUCCESS) ?
772a1e87500Sdlg 	    XS_NOERROR : XS_DRIVER_STUFFUP;
7738cfbc7f4Sdlg 	xs->status = SCSI_OK;
774a1e87500Sdlg 	xs->resid = 0;
775a1e87500Sdlg 	scsi_done(xs);
776a1e87500Sdlg }
777a1e87500Sdlg 
778a1e87500Sdlg void
779689c2c68Sdlg nvme_scsi_sync(struct scsi_xfer *xs)
780689c2c68Sdlg {
781689c2c68Sdlg 	struct scsi_link *link = xs->sc_link;
7820b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
783689c2c68Sdlg 	struct nvme_ccb *ccb = xs->io;
784689c2c68Sdlg 
785689c2c68Sdlg 	ccb->ccb_done = nvme_scsi_sync_done;
786689c2c68Sdlg 	ccb->ccb_cookie = xs;
787689c2c68Sdlg 
788689c2c68Sdlg 	if (ISSET(xs->flags, SCSI_POLL)) {
78954904088Skrw 		nvme_poll(sc, sc->sc_q, ccb, nvme_scsi_sync_fill, xs->timeout);
790689c2c68Sdlg 		return;
791689c2c68Sdlg 	}
792689c2c68Sdlg 
793689c2c68Sdlg 	nvme_q_submit(sc, sc->sc_q, ccb, nvme_scsi_sync_fill);
794689c2c68Sdlg }
795689c2c68Sdlg 
796689c2c68Sdlg void
797689c2c68Sdlg nvme_scsi_sync_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot)
798689c2c68Sdlg {
799689c2c68Sdlg 	struct nvme_sqe *sqe = slot;
800689c2c68Sdlg 	struct scsi_xfer *xs = ccb->ccb_cookie;
801689c2c68Sdlg 	struct scsi_link *link = xs->sc_link;
802689c2c68Sdlg 
803689c2c68Sdlg 	sqe->opcode = NVM_CMD_FLUSH;
804397f5692Skettenis 	htolem32(&sqe->nsid, link->target);
805689c2c68Sdlg }
806689c2c68Sdlg 
807689c2c68Sdlg void
808689c2c68Sdlg nvme_scsi_sync_done(struct nvme_softc *sc, struct nvme_ccb *ccb,
809689c2c68Sdlg     struct nvme_cqe *cqe)
810689c2c68Sdlg {
811689c2c68Sdlg 	struct scsi_xfer *xs = ccb->ccb_cookie;
812689c2c68Sdlg 	u_int16_t flags;
813689c2c68Sdlg 
814689c2c68Sdlg 	flags = lemtoh16(&cqe->flags);
815689c2c68Sdlg 
816689c2c68Sdlg 	xs->error = (NVME_CQE_SC(flags) == NVME_CQE_SC_SUCCESS) ?
817689c2c68Sdlg 	    XS_NOERROR : XS_DRIVER_STUFFUP;
818689c2c68Sdlg 	xs->status = SCSI_OK;
819689c2c68Sdlg 	xs->resid = 0;
820689c2c68Sdlg 	scsi_done(xs);
821689c2c68Sdlg }
822689c2c68Sdlg 
823689c2c68Sdlg void
8249856392eSdlg nvme_scsi_inq(struct scsi_xfer *xs)
8259856392eSdlg {
826664c6166Skrw 	struct scsi_inquiry *inq = (struct scsi_inquiry *)&xs->cmd;
8279856392eSdlg 
8289856392eSdlg 	if (!ISSET(inq->flags, SI_EVPD)) {
8299856392eSdlg 		nvme_scsi_inquiry(xs);
8309856392eSdlg 		return;
8319856392eSdlg 	}
8329856392eSdlg 
8339856392eSdlg 	switch (inq->pagecode) {
8349856392eSdlg 	default:
8359856392eSdlg 		/* printf("%s: %d\n", __func__, inq->pagecode); */
8369856392eSdlg 		break;
8379856392eSdlg 	}
8389856392eSdlg 
8399856392eSdlg 	xs->error = XS_DRIVER_STUFFUP;
8409856392eSdlg 	scsi_done(xs);
8419856392eSdlg }
8429856392eSdlg 
8439856392eSdlg void
8449856392eSdlg nvme_scsi_inquiry(struct scsi_xfer *xs)
8459856392eSdlg {
8469856392eSdlg 	struct scsi_inquiry_data inq;
8479856392eSdlg 	struct scsi_link *link = xs->sc_link;
8480b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
8499856392eSdlg 	struct nvm_identify_namespace *ns;
8509856392eSdlg 
8519856392eSdlg 	ns = sc->sc_namespaces[link->target].ident;
8529856392eSdlg 
8539856392eSdlg 	memset(&inq, 0, sizeof(inq));
8549856392eSdlg 
8559856392eSdlg 	inq.device = T_DIRECT;
856b291b595Skrw 	inq.version = SCSI_REV_SPC4;
857bb4b71ebSkrw 	inq.response_format = SID_SCSI2_RESPONSE;
8583ca8dabfSkrw 	inq.additional_length = SID_SCSI2_ALEN;
8599856392eSdlg 	inq.flags |= SID_CmdQue;
8609856392eSdlg 	memcpy(inq.vendor, "NVMe    ", sizeof(inq.vendor));
8619856392eSdlg 	memcpy(inq.product, sc->sc_identify.mn, sizeof(inq.product));
8629856392eSdlg 	memcpy(inq.revision, sc->sc_identify.fr, sizeof(inq.revision));
8639856392eSdlg 
864a83ec286Skrw 	scsi_copy_internal_data(xs, &inq, sizeof(inq));
8659856392eSdlg 
8669856392eSdlg 	xs->error = XS_NOERROR;
8679856392eSdlg 	scsi_done(xs);
8689856392eSdlg }
8699856392eSdlg 
8709856392eSdlg void
871c947e044Sdlg nvme_scsi_capacity16(struct scsi_xfer *xs)
872c947e044Sdlg {
873c947e044Sdlg 	struct scsi_read_cap_data_16 rcd;
874c947e044Sdlg 	struct scsi_link *link = xs->sc_link;
8750b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
876c947e044Sdlg 	struct nvm_identify_namespace *ns;
877c947e044Sdlg 	struct nvm_namespace_format *f;
878daef0c50Skrw 	u_int64_t addr;
879c947e044Sdlg 	u_int16_t tpe = READ_CAP_16_TPE;
880c947e044Sdlg 
881c947e044Sdlg 	ns = sc->sc_namespaces[link->target].ident;
882c947e044Sdlg 
883c947e044Sdlg 	if (xs->cmdlen != sizeof(struct scsi_read_capacity_16)) {
884c947e044Sdlg 		xs->error = XS_DRIVER_STUFFUP;
885c947e044Sdlg 		scsi_done(xs);
886c947e044Sdlg 		return;
887c947e044Sdlg 	}
888c947e044Sdlg 
889daef0c50Skrw 	addr = nvme_scsi_size(ns) - 1;
890c947e044Sdlg 	f = &ns->lbaf[NVME_ID_NS_FLBAS(ns->flbas)];
891c947e044Sdlg 
892c947e044Sdlg 	memset(&rcd, 0, sizeof(rcd));
893daef0c50Skrw 	_lto8b(addr, rcd.addr);
894c947e044Sdlg 	_lto4b(1 << f->lbads, rcd.length);
895c947e044Sdlg 	_lto2b(tpe, rcd.lowest_aligned);
896c947e044Sdlg 
897c947e044Sdlg 	memcpy(xs->data, &rcd, MIN(sizeof(rcd), xs->datalen));
898c947e044Sdlg 
899c947e044Sdlg 	xs->error = XS_NOERROR;
900c947e044Sdlg 	scsi_done(xs);
901c947e044Sdlg }
902c947e044Sdlg 
903c947e044Sdlg void
904c947e044Sdlg nvme_scsi_capacity(struct scsi_xfer *xs)
905c947e044Sdlg {
906c947e044Sdlg 	struct scsi_read_cap_data rcd;
907c947e044Sdlg 	struct scsi_link *link = xs->sc_link;
9080b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
909c947e044Sdlg 	struct nvm_identify_namespace *ns;
910c947e044Sdlg 	struct nvm_namespace_format *f;
911daef0c50Skrw 	u_int64_t addr;
912c947e044Sdlg 
913c947e044Sdlg 	ns = sc->sc_namespaces[link->target].ident;
914c947e044Sdlg 
915c947e044Sdlg 	if (xs->cmdlen != sizeof(struct scsi_read_capacity)) {
916c947e044Sdlg 		xs->error = XS_DRIVER_STUFFUP;
917c947e044Sdlg 		scsi_done(xs);
918c947e044Sdlg 		return;
919c947e044Sdlg 	}
920c947e044Sdlg 
921daef0c50Skrw 	addr = nvme_scsi_size(ns) - 1;
922daef0c50Skrw 	if (addr > 0xffffffff)
923daef0c50Skrw 		addr = 0xffffffff;
924c947e044Sdlg 
925c947e044Sdlg 	f = &ns->lbaf[NVME_ID_NS_FLBAS(ns->flbas)];
926c947e044Sdlg 
927c947e044Sdlg 	memset(&rcd, 0, sizeof(rcd));
928daef0c50Skrw 	_lto4b(addr, rcd.addr);
929c947e044Sdlg 	_lto4b(1 << f->lbads, rcd.length);
930c947e044Sdlg 
931c947e044Sdlg 	memcpy(xs->data, &rcd, MIN(sizeof(rcd), xs->datalen));
932c947e044Sdlg 
933c947e044Sdlg 	xs->error = XS_NOERROR;
934c947e044Sdlg 	scsi_done(xs);
935c947e044Sdlg }
936c947e044Sdlg 
937c947e044Sdlg void
93811624e4cSdlg nvme_scsi_free(struct scsi_link *link)
93911624e4cSdlg {
9400b29cb40Skrw 	struct nvme_softc *sc = link->bus->sb_adapter_softc;
94165dd51f8Sdlg 	struct nvm_identify_namespace *identify;
94211624e4cSdlg 
94365dd51f8Sdlg 	identify = sc->sc_namespaces[link->target].ident;
94465dd51f8Sdlg 	sc->sc_namespaces[link->target].ident = NULL;
94565dd51f8Sdlg 
94665dd51f8Sdlg 	free(identify, M_DEVBUF, sizeof(*identify));
94711624e4cSdlg }
94811624e4cSdlg 
949daef0c50Skrw uint64_t
9507f4636ceSkrw nvme_scsi_size(const struct nvm_identify_namespace *ns)
951daef0c50Skrw {
952daef0c50Skrw 	uint64_t		ncap, nsze;
953daef0c50Skrw 
954daef0c50Skrw 	ncap = lemtoh64(&ns->ncap); /* Max allowed allocation. */
955daef0c50Skrw 	nsze = lemtoh64(&ns->nsze);
956daef0c50Skrw 
957daef0c50Skrw 	if ((ns->nsfeat & NVME_ID_NS_NSFEAT_THIN_PROV) && ncap < nsze)
958daef0c50Skrw 		return ncap;
959daef0c50Skrw 	else
960daef0c50Skrw 		return nsze;
961daef0c50Skrw }
962daef0c50Skrw 
9634e9514d6Skrw int
9644e9514d6Skrw nvme_passthrough_cmd(struct nvme_softc *sc, struct nvme_pt_cmd *pt, int dv_unit,
9654e9514d6Skrw     int nsid)
9664e9514d6Skrw {
9674e9514d6Skrw 	struct nvme_pt_status		 pt_status;
9684e9514d6Skrw 	struct nvme_sqe			 sqe;
9694e9514d6Skrw 	struct nvme_dmamem		*mem = NULL;
9704e9514d6Skrw 	struct nvme_ccb			*ccb = NULL;
9714e9514d6Skrw 	int				 flags;
9724e9514d6Skrw 	int				 rv = 0;
9734e9514d6Skrw 
9744e9514d6Skrw 	ccb = nvme_ccb_get(sc);
9754e9514d6Skrw 	if (ccb == NULL)
9764e9514d6Skrw 		panic("nvme_passthrough_cmd: nvme_ccb_get returned NULL");
9774e9514d6Skrw 
9784e9514d6Skrw 	memset(&sqe, 0, sizeof(sqe));
9794e9514d6Skrw 	sqe.opcode = pt->pt_opcode;
9804e9514d6Skrw 	htolem32(&sqe.nsid, pt->pt_nsid);
9814e9514d6Skrw 	htolem32(&sqe.cdw10, pt->pt_cdw10);
9824e9514d6Skrw 	htolem32(&sqe.cdw11, pt->pt_cdw11);
9834e9514d6Skrw 	htolem32(&sqe.cdw12, pt->pt_cdw12);
9844e9514d6Skrw 	htolem32(&sqe.cdw13, pt->pt_cdw13);
9854e9514d6Skrw 	htolem32(&sqe.cdw14, pt->pt_cdw14);
9864e9514d6Skrw 	htolem32(&sqe.cdw15, pt->pt_cdw15);
9874e9514d6Skrw 
9884e9514d6Skrw 	ccb->ccb_done = nvme_empty_done;
9894e9514d6Skrw 	ccb->ccb_cookie = &sqe;
9904e9514d6Skrw 
9914e9514d6Skrw 	switch (pt->pt_opcode) {
9924e9514d6Skrw 	case NVM_ADMIN_IDENTIFY:
9934e9514d6Skrw 	case NVM_ADMIN_GET_LOG_PG:
9944e9514d6Skrw 	case NVM_ADMIN_SELFTEST:
9954e9514d6Skrw 		break;
9964e9514d6Skrw 
9974e9514d6Skrw 	default:
9984e9514d6Skrw 		rv = ENOTTY;
9994e9514d6Skrw 		goto done;
10004e9514d6Skrw 	}
10014e9514d6Skrw 
10024e9514d6Skrw 	if (pt->pt_databuflen > 0) {
10034e9514d6Skrw 		mem = nvme_dmamem_alloc(sc, pt->pt_databuflen);
10044e9514d6Skrw 		if (mem == NULL) {
10054e9514d6Skrw 			rv = ENOMEM;
10064e9514d6Skrw 			goto done;
10074e9514d6Skrw 		}
10084e9514d6Skrw 		htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem));
10094e9514d6Skrw 		nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD);
10104e9514d6Skrw 	}
10114e9514d6Skrw 
10121488c4e9Skrw 	flags = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_PT);
10134e9514d6Skrw 
10144e9514d6Skrw 	if (pt->pt_databuflen > 0) {
10154e9514d6Skrw 		nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD);
10164e9514d6Skrw 		if (flags == 0)
10174e9514d6Skrw 			rv = copyout(NVME_DMA_KVA(mem), pt->pt_databuf,
10184e9514d6Skrw 			    pt->pt_databuflen);
10194e9514d6Skrw 	}
10204e9514d6Skrw 
10214e9514d6Skrw 	if (rv == 0 && pt->pt_statuslen > 0) {
10224e9514d6Skrw 		pt_status.ps_dv_unit = dv_unit;
10234e9514d6Skrw 		pt_status.ps_nsid = nsid;
10244e9514d6Skrw 		pt_status.ps_flags = flags;
10254e9514d6Skrw 		pt_status.ps_cc = nvme_read4(sc, NVME_CC);
10264e9514d6Skrw 		pt_status.ps_csts = nvme_read4(sc, NVME_CSTS);
10274e9514d6Skrw 		rv = copyout(&pt_status, pt->pt_status, pt->pt_statuslen);
10284e9514d6Skrw 	}
10294e9514d6Skrw 
10304e9514d6Skrw  done:
10314e9514d6Skrw 	if (mem)
10324e9514d6Skrw 		nvme_dmamem_free(sc, mem);
10334e9514d6Skrw 	if (ccb)
10344e9514d6Skrw 		nvme_ccb_put(sc, ccb);
10354e9514d6Skrw 
10364e9514d6Skrw 	return rv;
10374e9514d6Skrw }
10384e9514d6Skrw 
10394e9514d6Skrw int
10404e9514d6Skrw nvme_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
10414e9514d6Skrw {
10424e9514d6Skrw 	struct nvme_softc		*sc = link->bus->sb_adapter_softc;
10434e9514d6Skrw 	struct nvme_pt_cmd		*pt = (struct nvme_pt_cmd *)addr;
10444e9514d6Skrw 	int				 rv;
10454e9514d6Skrw 
10464e9514d6Skrw 	switch (cmd) {
10474e9514d6Skrw 	case NVME_PASSTHROUGH_CMD:
10484e9514d6Skrw 		break;
10494e9514d6Skrw 	default:
10504e9514d6Skrw 		return ENOTTY;
10514e9514d6Skrw 	}
10524e9514d6Skrw 
10534e9514d6Skrw 	if ((pt->pt_cdw10 & 0xff) == 0)
10544e9514d6Skrw 		pt->pt_nsid = link->target;
10554e9514d6Skrw 
10564e9514d6Skrw 	rv = nvme_passthrough_cmd(sc, pt, sc->sc_dev.dv_unit, link->target);
10574e9514d6Skrw 	if (rv)
10584e9514d6Skrw 		goto done;
10594e9514d6Skrw 
10604e9514d6Skrw  done:
10614e9514d6Skrw 	return rv;
10624e9514d6Skrw }
10634e9514d6Skrw 
10647599295eSdlg uint32_t
10657599295eSdlg nvme_op_sq_enter(struct nvme_softc *sc,
10667599295eSdlg     struct nvme_queue *q, struct nvme_ccb *ccb)
10677599295eSdlg {
10687599295eSdlg 	mtx_enter(&q->q_sq_mtx);
10697599295eSdlg 	return (nvme_op_sq_enter_locked(sc, q, ccb));
10707599295eSdlg }
10717599295eSdlg 
10727599295eSdlg uint32_t
10737599295eSdlg nvme_op_sq_enter_locked(struct nvme_softc *sc,
10747599295eSdlg     struct nvme_queue *q, struct nvme_ccb *ccb)
10757599295eSdlg {
10767599295eSdlg 	return (q->q_sq_tail);
10777599295eSdlg }
10787599295eSdlg 
10797599295eSdlg void
10807599295eSdlg nvme_op_sq_leave_locked(struct nvme_softc *sc,
10817599295eSdlg     struct nvme_queue *q, struct nvme_ccb *ccb)
10827599295eSdlg {
10837599295eSdlg 	uint32_t tail;
10847599295eSdlg 
10857599295eSdlg 	tail = ++q->q_sq_tail;
10867599295eSdlg 	if (tail >= q->q_entries)
10877599295eSdlg 		tail = 0;
10887599295eSdlg 	q->q_sq_tail = tail;
10897599295eSdlg 	nvme_write4(sc, q->q_sqtdbl, tail);
10907599295eSdlg }
10917599295eSdlg 
10927599295eSdlg void
10937599295eSdlg nvme_op_sq_leave(struct nvme_softc *sc,
10947599295eSdlg     struct nvme_queue *q, struct nvme_ccb *ccb)
10957599295eSdlg {
10967599295eSdlg 	nvme_op_sq_leave_locked(sc, q, ccb);
10977599295eSdlg 	mtx_leave(&q->q_sq_mtx);
10987599295eSdlg }
10997599295eSdlg 
1100448b3c09Sdlg void
1101448b3c09Sdlg nvme_q_submit(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb,
1102448b3c09Sdlg     void (*fill)(struct nvme_softc *, struct nvme_ccb *, void *))
1103448b3c09Sdlg {
1104448b3c09Sdlg 	struct nvme_sqe *sqe = NVME_DMA_KVA(q->q_sq_dmamem);
1105448b3c09Sdlg 	u_int32_t tail;
1106448b3c09Sdlg 
11077599295eSdlg 	tail = sc->sc_ops->op_sq_enter(sc, q, ccb);
1108448b3c09Sdlg 
1109448b3c09Sdlg 	sqe += tail;
1110448b3c09Sdlg 
1111448b3c09Sdlg 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
1112448b3c09Sdlg 	    sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_POSTWRITE);
1113448b3c09Sdlg 	memset(sqe, 0, sizeof(*sqe));
1114448b3c09Sdlg 	(*fill)(sc, ccb, sqe);
111524aede5fSdlg 	sqe->cid = ccb->ccb_id;
1116448b3c09Sdlg 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
1117448b3c09Sdlg 	    sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_PREWRITE);
1118448b3c09Sdlg 
11197599295eSdlg 	sc->sc_ops->op_sq_leave(sc, q, ccb);
1120448b3c09Sdlg }
1121448b3c09Sdlg 
1122448b3c09Sdlg struct nvme_poll_state {
1123448b3c09Sdlg 	struct nvme_sqe s;
1124448b3c09Sdlg 	struct nvme_cqe c;
1125448b3c09Sdlg };
1126448b3c09Sdlg 
1127448b3c09Sdlg int
1128448b3c09Sdlg nvme_poll(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb,
112954904088Skrw     void (*fill)(struct nvme_softc *, struct nvme_ccb *, void *), u_int32_t ms)
1130448b3c09Sdlg {
1131448b3c09Sdlg 	struct nvme_poll_state state;
1132448b3c09Sdlg 	void (*done)(struct nvme_softc *, struct nvme_ccb *, struct nvme_cqe *);
1133448b3c09Sdlg 	void *cookie;
113454904088Skrw 	int64_t us;
11359f81ea3bSdlg 	u_int16_t flags;
1136448b3c09Sdlg 
1137448b3c09Sdlg 	memset(&state, 0, sizeof(state));
1138448b3c09Sdlg 	(*fill)(sc, ccb, &state.s);
1139448b3c09Sdlg 
1140448b3c09Sdlg 	done = ccb->ccb_done;
1141448b3c09Sdlg 	cookie = ccb->ccb_cookie;
1142448b3c09Sdlg 
1143448b3c09Sdlg 	ccb->ccb_done = nvme_poll_done;
1144448b3c09Sdlg 	ccb->ccb_cookie = &state;
1145448b3c09Sdlg 
1146448b3c09Sdlg 	nvme_q_submit(sc, q, ccb, nvme_poll_fill);
114754904088Skrw 	for (us = ms * 1000; ms == 0 || us > 0; us -= NVME_TIMO_DELAYNS) {
114854904088Skrw 		if (ISSET(state.c.flags, htole16(NVME_CQE_PHASE)))
114954904088Skrw 			break;
11505fac9627Sdlg 		if (nvme_q_complete(sc, q) == 0)
115154904088Skrw 			delay(NVME_TIMO_DELAYNS);
1152b6ac76ceSkrw 		nvme_barrier(sc, NVME_CSTS, 4, BUS_SPACE_BARRIER_READ);
1153448b3c09Sdlg 	}
1154448b3c09Sdlg 
1155448b3c09Sdlg 	ccb->ccb_cookie = cookie;
1156448b3c09Sdlg 	done(sc, ccb, &state.c);
1157448b3c09Sdlg 
11589f81ea3bSdlg 	flags = lemtoh16(&state.c.flags);
11599f81ea3bSdlg 
1160c05818fdSdlg 	return (flags & ~NVME_CQE_PHASE);
1161448b3c09Sdlg }
1162448b3c09Sdlg 
1163448b3c09Sdlg void
1164448b3c09Sdlg nvme_poll_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot)
1165448b3c09Sdlg {
1166448b3c09Sdlg 	struct nvme_sqe *sqe = slot;
1167448b3c09Sdlg 	struct nvme_poll_state *state = ccb->ccb_cookie;
1168448b3c09Sdlg 
1169448b3c09Sdlg 	*sqe = state->s;
1170448b3c09Sdlg }
1171448b3c09Sdlg 
1172448b3c09Sdlg void
1173448b3c09Sdlg nvme_poll_done(struct nvme_softc *sc, struct nvme_ccb *ccb,
1174448b3c09Sdlg     struct nvme_cqe *cqe)
1175448b3c09Sdlg {
1176448b3c09Sdlg 	struct nvme_poll_state *state = ccb->ccb_cookie;
1177448b3c09Sdlg 
1178448b3c09Sdlg 	state->c = *cqe;
1179300a229bSyasuoka 	SET(state->c.flags, htole16(NVME_CQE_PHASE));
1180448b3c09Sdlg }
1181448b3c09Sdlg 
11829f81ea3bSdlg void
1183ad9e5681Sdlg nvme_sqe_fill(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot)
1184ad9e5681Sdlg {
1185ad9e5681Sdlg 	struct nvme_sqe *src = ccb->ccb_cookie;
1186ad9e5681Sdlg 	struct nvme_sqe *dst = slot;
1187ad9e5681Sdlg 
1188ad9e5681Sdlg 	*dst = *src;
1189ad9e5681Sdlg }
1190ad9e5681Sdlg 
1191ad9e5681Sdlg void
11929f81ea3bSdlg nvme_empty_done(struct nvme_softc *sc, struct nvme_ccb *ccb,
11939f81ea3bSdlg     struct nvme_cqe *cqe)
11949f81ea3bSdlg {
11959f81ea3bSdlg }
11969f81ea3bSdlg 
11977599295eSdlg void
11987599295eSdlg nvme_op_cq_done(struct nvme_softc *sc,
11997599295eSdlg     struct nvme_queue *q, struct nvme_ccb *ccb)
12007599295eSdlg {
12017599295eSdlg 	/* nop */
12027599295eSdlg }
12037599295eSdlg 
1204448b3c09Sdlg int
1205448b3c09Sdlg nvme_q_complete(struct nvme_softc *sc, struct nvme_queue *q)
1206448b3c09Sdlg {
1207448b3c09Sdlg 	struct nvme_ccb *ccb;
1208448b3c09Sdlg 	struct nvme_cqe *ring = NVME_DMA_KVA(q->q_cq_dmamem), *cqe;
1209448b3c09Sdlg 	u_int32_t head;
1210448b3c09Sdlg 	u_int16_t flags;
1211448b3c09Sdlg 	int rv = 0;
1212448b3c09Sdlg 
1213448b3c09Sdlg 	if (!mtx_enter_try(&q->q_cq_mtx))
1214448b3c09Sdlg 		return (-1);
1215448b3c09Sdlg 
1216448b3c09Sdlg 	head = q->q_cq_head;
12170ee935ccSdlg 
12180ee935ccSdlg 	nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
1219448b3c09Sdlg 	for (;;) {
1220448b3c09Sdlg 		cqe = &ring[head];
1221448b3c09Sdlg 		flags = lemtoh16(&cqe->flags);
1222448b3c09Sdlg 		if ((flags & NVME_CQE_PHASE) != q->q_cq_phase)
1223448b3c09Sdlg 			break;
1224448b3c09Sdlg 
1225ee570821Sjmatthew 		membar_consumer();
1226ee570821Sjmatthew 
1227448b3c09Sdlg 		ccb = &sc->sc_ccbs[cqe->cid];
12287599295eSdlg 		sc->sc_ops->op_cq_done(sc, q, ccb);
1229448b3c09Sdlg 		ccb->ccb_done(sc, ccb, cqe);
1230448b3c09Sdlg 
1231448b3c09Sdlg 		if (++head >= q->q_entries) {
1232448b3c09Sdlg 			head = 0;
1233448b3c09Sdlg 			q->q_cq_phase ^= NVME_CQE_PHASE;
1234448b3c09Sdlg 		}
1235448b3c09Sdlg 
1236448b3c09Sdlg 		rv = 1;
1237448b3c09Sdlg 	}
12380ee935ccSdlg 	nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
1239448b3c09Sdlg 
1240448b3c09Sdlg 	if (rv)
124120386a6cSdlg 		nvme_write4(sc, q->q_cqhdbl, q->q_cq_head = head);
1242448b3c09Sdlg 	mtx_leave(&q->q_cq_mtx);
1243448b3c09Sdlg 
1244448b3c09Sdlg 	return (rv);
1245448b3c09Sdlg }
1246448b3c09Sdlg 
1247448b3c09Sdlg int
124898b2ae82Skrw nvme_identify(struct nvme_softc *sc, u_int mpsmin)
1249448b3c09Sdlg {
12509f81ea3bSdlg 	char sn[41], mn[81], fr[17];
12519f81ea3bSdlg 	struct nvm_identify_controller *identify;
1252448b3c09Sdlg 	struct nvme_dmamem *mem;
12539f81ea3bSdlg 	struct nvme_ccb *ccb;
1254448b3c09Sdlg 	int rv = 1;
1255448b3c09Sdlg 
1256448b3c09Sdlg 	ccb = nvme_ccb_get(sc);
1257448b3c09Sdlg 	if (ccb == NULL)
1258448b3c09Sdlg 		panic("nvme_identify: nvme_ccb_get returned NULL");
1259448b3c09Sdlg 
12609f81ea3bSdlg 	mem = nvme_dmamem_alloc(sc, sizeof(*identify));
1261448b3c09Sdlg 	if (mem == NULL)
1262448b3c09Sdlg 		return (1);
1263448b3c09Sdlg 
12649f81ea3bSdlg 	ccb->ccb_done = nvme_empty_done;
1265448b3c09Sdlg 	ccb->ccb_cookie = mem;
1266448b3c09Sdlg 
12670ee935ccSdlg 	nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD);
126854904088Skrw 	rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_fill_identify,
126954904088Skrw 	    NVME_TIMO_IDENT);
12700ee935ccSdlg 	nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD);
1271448b3c09Sdlg 
1272b62ffe02Sdlg 	nvme_ccb_put(sc, ccb);
1273b62ffe02Sdlg 
12749f81ea3bSdlg 	if (rv != 0)
12759f81ea3bSdlg 		goto done;
12769f81ea3bSdlg 
127717756a2bSdlg 	identify = NVME_DMA_KVA(mem);
127817756a2bSdlg 
12799f81ea3bSdlg 	scsi_strvis(sn, identify->sn, sizeof(identify->sn));
12809f81ea3bSdlg 	scsi_strvis(mn, identify->mn, sizeof(identify->mn));
12819f81ea3bSdlg 	scsi_strvis(fr, identify->fr, sizeof(identify->fr));
12829f81ea3bSdlg 
12839f81ea3bSdlg 	printf("%s: %s, firmware %s, serial %s\n", DEVNAME(sc), mn, fr, sn);
12849f81ea3bSdlg 
128588aa9192Sdlg 	if (identify->mdts > 0) {
128698b2ae82Skrw 		sc->sc_mdts = (1 << identify->mdts) * (1 << mpsmin);
1287767e8532Skrw 		if (sc->sc_mdts > NVME_MAXPHYS)
1288767e8532Skrw 			sc->sc_mdts = NVME_MAXPHYS;
1289a8751b7cSkrw 		sc->sc_max_prpl = sc->sc_mdts / sc->sc_mps;
129088aa9192Sdlg 	}
129188aa9192Sdlg 
129217756a2bSdlg 	sc->sc_nn = lemtoh32(&identify->nn);
129317756a2bSdlg 
1294e276f6b1Sjcs 	/*
1295e276f6b1Sjcs 	 * At least one Apple NVMe device presents a second, bogus disk that is
1296e276f6b1Sjcs 	 * inaccessible, so cap targets at 1.
1297e276f6b1Sjcs 	 *
1298397f5692Skettenis 	 * sd1 at scsibus1 targ 2 lun 0: <NVMe, APPLE SSD AP0512, 16.1> [..]
1299e276f6b1Sjcs 	 * sd1: 0MB, 4096 bytes/sector, 2 sectors
1300e276f6b1Sjcs 	 */
1301e276f6b1Sjcs 	if (sc->sc_nn > 1 &&
1302e276f6b1Sjcs 	    mn[0] == 'A' && mn[1] == 'P' && mn[2] == 'P' && mn[3] == 'L' &&
1303e276f6b1Sjcs 	    mn[4] == 'E')
1304e276f6b1Sjcs 		sc->sc_nn = 1;
1305e276f6b1Sjcs 
130617756a2bSdlg 	memcpy(&sc->sc_identify, identify, sizeof(sc->sc_identify));
130717756a2bSdlg 
13089f81ea3bSdlg done:
1309448b3c09Sdlg 	nvme_dmamem_free(sc, mem);
1310448b3c09Sdlg 
1311448b3c09Sdlg 	return (rv);
1312448b3c09Sdlg }
1313448b3c09Sdlg 
1314cd15a86fSdlg int
1315cd15a86fSdlg nvme_q_create(struct nvme_softc *sc, struct nvme_queue *q)
1316cd15a86fSdlg {
1317cd15a86fSdlg 	struct nvme_sqe_q sqe;
1318cd15a86fSdlg 	struct nvme_ccb *ccb;
1319cd15a86fSdlg 	int rv;
1320cd15a86fSdlg 
1321cd15a86fSdlg 	ccb = scsi_io_get(&sc->sc_iopool, 0);
1322cd15a86fSdlg 	KASSERT(ccb != NULL);
1323cd15a86fSdlg 
1324cd15a86fSdlg 	ccb->ccb_done = nvme_empty_done;
1325cd15a86fSdlg 	ccb->ccb_cookie = &sqe;
1326cd15a86fSdlg 
1327cd15a86fSdlg 	memset(&sqe, 0, sizeof(sqe));
1328cd15a86fSdlg 	sqe.opcode = NVM_ADMIN_ADD_IOCQ;
1329cd15a86fSdlg 	htolem64(&sqe.prp1, NVME_DMA_DVA(q->q_cq_dmamem));
1330cd15a86fSdlg 	htolem16(&sqe.qsize, q->q_entries - 1);
1331cd15a86fSdlg 	htolem16(&sqe.qid, q->q_id);
1332cd15a86fSdlg 	sqe.qflags = NVM_SQE_CQ_IEN | NVM_SQE_Q_PC;
1333cd15a86fSdlg 
133454904088Skrw 	rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
1335cd15a86fSdlg 	if (rv != 0)
1336cd15a86fSdlg 		goto fail;
1337cd15a86fSdlg 
1338cd15a86fSdlg 	ccb->ccb_done = nvme_empty_done;
1339cd15a86fSdlg 	ccb->ccb_cookie = &sqe;
1340cd15a86fSdlg 
1341cd15a86fSdlg 	memset(&sqe, 0, sizeof(sqe));
1342cd15a86fSdlg 	sqe.opcode = NVM_ADMIN_ADD_IOSQ;
1343cd15a86fSdlg 	htolem64(&sqe.prp1, NVME_DMA_DVA(q->q_sq_dmamem));
1344cd15a86fSdlg 	htolem16(&sqe.qsize, q->q_entries - 1);
1345cd15a86fSdlg 	htolem16(&sqe.qid, q->q_id);
1346cd15a86fSdlg 	htolem16(&sqe.cqid, q->q_id);
1347cd15a86fSdlg 	sqe.qflags = NVM_SQE_Q_PC;
1348cd15a86fSdlg 
134954904088Skrw 	rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
1350cd15a86fSdlg 	if (rv != 0)
1351cd15a86fSdlg 		goto fail;
1352cd15a86fSdlg 
1353cd15a86fSdlg fail:
1354cd15a86fSdlg 	scsi_io_put(&sc->sc_iopool, ccb);
1355cd15a86fSdlg 	return (rv);
1356cd15a86fSdlg }
1357cd15a86fSdlg 
13585313ab17Sdlg int
13595313ab17Sdlg nvme_q_delete(struct nvme_softc *sc, struct nvme_queue *q)
13605313ab17Sdlg {
13615313ab17Sdlg 	struct nvme_sqe_q sqe;
13625313ab17Sdlg 	struct nvme_ccb *ccb;
13635313ab17Sdlg 	int rv;
13645313ab17Sdlg 
13655313ab17Sdlg 	ccb = scsi_io_get(&sc->sc_iopool, 0);
13665313ab17Sdlg 	KASSERT(ccb != NULL);
13675313ab17Sdlg 
13685313ab17Sdlg 	ccb->ccb_done = nvme_empty_done;
13695313ab17Sdlg 	ccb->ccb_cookie = &sqe;
13705313ab17Sdlg 
13715313ab17Sdlg 	memset(&sqe, 0, sizeof(sqe));
13725313ab17Sdlg 	sqe.opcode = NVM_ADMIN_DEL_IOSQ;
13735313ab17Sdlg 	htolem16(&sqe.qid, q->q_id);
13745313ab17Sdlg 
137554904088Skrw 	rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
13765313ab17Sdlg 	if (rv != 0)
13775313ab17Sdlg 		goto fail;
13785313ab17Sdlg 
13795313ab17Sdlg 	ccb->ccb_done = nvme_empty_done;
13805313ab17Sdlg 	ccb->ccb_cookie = &sqe;
13815313ab17Sdlg 
13825313ab17Sdlg 	memset(&sqe, 0, sizeof(sqe));
13835313ab17Sdlg 	sqe.opcode = NVM_ADMIN_DEL_IOCQ;
13845313ab17Sdlg 	htolem16(&sqe.qid, q->q_id);
13855313ab17Sdlg 
138654904088Skrw 	rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
13875313ab17Sdlg 	if (rv != 0)
13885313ab17Sdlg 		goto fail;
13895313ab17Sdlg 
13900abee971Sdv 	nvme_q_free(sc, q);
1391aa5ddcf9Ssf 
13925313ab17Sdlg fail:
13935313ab17Sdlg 	scsi_io_put(&sc->sc_iopool, ccb);
13945313ab17Sdlg 	return (rv);
13955313ab17Sdlg 
13965313ab17Sdlg }
13975313ab17Sdlg 
1398448b3c09Sdlg void
1399448b3c09Sdlg nvme_fill_identify(struct nvme_softc *sc, struct nvme_ccb *ccb, void *slot)
1400448b3c09Sdlg {
1401448b3c09Sdlg 	struct nvme_sqe *sqe = slot;
1402448b3c09Sdlg 	struct nvme_dmamem *mem = ccb->ccb_cookie;
1403448b3c09Sdlg 
1404448b3c09Sdlg 	sqe->opcode = NVM_ADMIN_IDENTIFY;
1405448b3c09Sdlg 	htolem64(&sqe->entry.prp[0], NVME_DMA_DVA(mem));
1406448b3c09Sdlg 	htolem32(&sqe->cdw10, 1);
1407448b3c09Sdlg }
1408448b3c09Sdlg 
1409448b3c09Sdlg int
1410448b3c09Sdlg nvme_ccbs_alloc(struct nvme_softc *sc, u_int nccbs)
1411448b3c09Sdlg {
1412448b3c09Sdlg 	struct nvme_ccb *ccb;
1413b1699ccdSdlg 	bus_addr_t off;
1414b1699ccdSdlg 	u_int64_t *prpl;
1415448b3c09Sdlg 	u_int i;
1416448b3c09Sdlg 
14179f6fb5c7Sderaadt 	sc->sc_ccbs = mallocarray(nccbs, sizeof(*ccb), M_DEVBUF,
1418448b3c09Sdlg 	    M_WAITOK | M_CANFAIL);
1419448b3c09Sdlg 	if (sc->sc_ccbs == NULL)
1420448b3c09Sdlg 		return (1);
1421448b3c09Sdlg 
1422b1699ccdSdlg 	sc->sc_ccb_prpls = nvme_dmamem_alloc(sc,
1423a8751b7cSkrw 	    sizeof(*prpl) * sc->sc_max_prpl * nccbs);
1424b1699ccdSdlg 
1425b1699ccdSdlg 	prpl = NVME_DMA_KVA(sc->sc_ccb_prpls);
1426b1699ccdSdlg 	off = 0;
1427b1699ccdSdlg 
1428448b3c09Sdlg 	for (i = 0; i < nccbs; i++) {
1429448b3c09Sdlg 		ccb = &sc->sc_ccbs[i];
1430448b3c09Sdlg 
1431b1699ccdSdlg 		if (bus_dmamap_create(sc->sc_dmat, sc->sc_mdts,
1432a8751b7cSkrw 		    sc->sc_max_prpl + 1, /* we get a free prp in the sqe */
14332a4e68fdSdlg 		    sc->sc_mps, sc->sc_mps,
14342a4e68fdSdlg 		    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
1435448b3c09Sdlg 		    &ccb->ccb_dmamap) != 0)
1436448b3c09Sdlg 			goto free_maps;
1437448b3c09Sdlg 
1438448b3c09Sdlg 		ccb->ccb_id = i;
1439b1699ccdSdlg 		ccb->ccb_prpl = prpl;
1440b1699ccdSdlg 		ccb->ccb_prpl_off = off;
1441b1699ccdSdlg 		ccb->ccb_prpl_dva = NVME_DMA_DVA(sc->sc_ccb_prpls) + off;
1442b1699ccdSdlg 
1443448b3c09Sdlg 		SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_list, ccb, ccb_entry);
1444b1699ccdSdlg 
1445a8751b7cSkrw 		prpl += sc->sc_max_prpl;
1446a8751b7cSkrw 		off += sizeof(*prpl) * sc->sc_max_prpl;
1447448b3c09Sdlg 	}
1448448b3c09Sdlg 
1449448b3c09Sdlg 	return (0);
1450448b3c09Sdlg 
1451448b3c09Sdlg free_maps:
1452a1141c40Stedu 	nvme_ccbs_free(sc, nccbs);
1453448b3c09Sdlg 	return (1);
1454448b3c09Sdlg }
1455448b3c09Sdlg 
1456448b3c09Sdlg void *
1457448b3c09Sdlg nvme_ccb_get(void *cookie)
1458448b3c09Sdlg {
1459448b3c09Sdlg 	struct nvme_softc *sc = cookie;
1460448b3c09Sdlg 	struct nvme_ccb *ccb;
1461448b3c09Sdlg 
1462448b3c09Sdlg 	mtx_enter(&sc->sc_ccb_mtx);
1463448b3c09Sdlg 	ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list);
1464448b3c09Sdlg 	if (ccb != NULL)
1465448b3c09Sdlg 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry);
1466448b3c09Sdlg 	mtx_leave(&sc->sc_ccb_mtx);
1467448b3c09Sdlg 
1468448b3c09Sdlg 	return (ccb);
1469448b3c09Sdlg }
1470448b3c09Sdlg 
1471448b3c09Sdlg void
1472448b3c09Sdlg nvme_ccb_put(void *cookie, void *io)
1473448b3c09Sdlg {
1474448b3c09Sdlg 	struct nvme_softc *sc = cookie;
1475448b3c09Sdlg 	struct nvme_ccb *ccb = io;
1476448b3c09Sdlg 
1477448b3c09Sdlg 	mtx_enter(&sc->sc_ccb_mtx);
1478448b3c09Sdlg 	SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_list, ccb, ccb_entry);
1479448b3c09Sdlg 	mtx_leave(&sc->sc_ccb_mtx);
1480448b3c09Sdlg }
1481448b3c09Sdlg 
1482448b3c09Sdlg void
1483a1141c40Stedu nvme_ccbs_free(struct nvme_softc *sc, unsigned int nccbs)
1484448b3c09Sdlg {
1485448b3c09Sdlg 	struct nvme_ccb *ccb;
1486448b3c09Sdlg 
1487448b3c09Sdlg 	while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_list)) != NULL) {
1488448b3c09Sdlg 		SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_list, ccb_entry);
1489448b3c09Sdlg 		bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
1490448b3c09Sdlg 	}
1491448b3c09Sdlg 
1492b1699ccdSdlg 	nvme_dmamem_free(sc, sc->sc_ccb_prpls);
1493a1141c40Stedu 	free(sc->sc_ccbs, M_DEVBUF, nccbs * sizeof(*ccb));
1494448b3c09Sdlg }
1495448b3c09Sdlg 
1496282c0692Sdlg struct nvme_queue *
149759968badSdlg nvme_q_alloc(struct nvme_softc *sc, u_int16_t id, u_int entries, u_int dstrd)
1498282c0692Sdlg {
1499282c0692Sdlg 	struct nvme_queue *q;
1500282c0692Sdlg 
1501282c0692Sdlg 	q = malloc(sizeof(*q), M_DEVBUF, M_WAITOK | M_CANFAIL);
1502282c0692Sdlg 	if (q == NULL)
1503282c0692Sdlg 		return (NULL);
1504282c0692Sdlg 
1505448b3c09Sdlg 	q->q_sq_dmamem = nvme_dmamem_alloc(sc,
150669136b8eSdlg 	    sizeof(struct nvme_sqe) * entries);
1507282c0692Sdlg 	if (q->q_sq_dmamem == NULL)
1508282c0692Sdlg 		goto free;
1509282c0692Sdlg 
1510448b3c09Sdlg 	q->q_cq_dmamem = nvme_dmamem_alloc(sc,
151169136b8eSdlg 	    sizeof(struct nvme_cqe) * entries);
1512638d48daSdlg 	if (q->q_cq_dmamem == NULL)
1513282c0692Sdlg 		goto free_sq;
1514282c0692Sdlg 
1515448b3c09Sdlg 	memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem));
1516448b3c09Sdlg 	memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem));
1517448b3c09Sdlg 
1518448b3c09Sdlg 	mtx_init(&q->q_sq_mtx, IPL_BIO);
1519448b3c09Sdlg 	mtx_init(&q->q_cq_mtx, IPL_BIO);
152059968badSdlg 	q->q_sqtdbl = NVME_SQTDBL(id, dstrd);
152159968badSdlg 	q->q_cqhdbl = NVME_CQHDBL(id, dstrd);
1522aa5ddcf9Ssf 
15238f4e0057Sdlg 	q->q_id = id;
1524282c0692Sdlg 	q->q_entries = entries;
1525448b3c09Sdlg 	q->q_sq_tail = 0;
1526448b3c09Sdlg 	q->q_cq_head = 0;
1527448b3c09Sdlg 	q->q_cq_phase = NVME_CQE_PHASE;
1528448b3c09Sdlg 
15297599295eSdlg 	if (sc->sc_ops->op_q_alloc != NULL) {
15307599295eSdlg 		if (sc->sc_ops->op_q_alloc(sc, q) != 0)
15317599295eSdlg 			goto free_cq;
15327599295eSdlg 	}
15337599295eSdlg 
15340ee935ccSdlg 	nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE);
15350ee935ccSdlg 	nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
1536282c0692Sdlg 
1537282c0692Sdlg 	return (q);
1538282c0692Sdlg 
15397599295eSdlg free_cq:
15407599295eSdlg 	nvme_dmamem_free(sc, q->q_cq_dmamem);
1541282c0692Sdlg free_sq:
1542282c0692Sdlg 	nvme_dmamem_free(sc, q->q_sq_dmamem);
1543282c0692Sdlg free:
1544234dfda1Sderaadt 	free(q, M_DEVBUF, sizeof *q);
1545282c0692Sdlg 
1546282c0692Sdlg 	return (NULL);
1547282c0692Sdlg }
1548282c0692Sdlg 
15490abee971Sdv int
1550aa5ddcf9Ssf nvme_q_reset(struct nvme_softc *sc, struct nvme_queue *q)
1551aa5ddcf9Ssf {
1552aa5ddcf9Ssf 	memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem));
1553aa5ddcf9Ssf 	memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem));
1554aa5ddcf9Ssf 
1555aa5ddcf9Ssf 	q->q_sq_tail = 0;
1556aa5ddcf9Ssf 	q->q_cq_head = 0;
1557aa5ddcf9Ssf 	q->q_cq_phase = NVME_CQE_PHASE;
1558aa5ddcf9Ssf 
1559aa5ddcf9Ssf 	nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE);
1560aa5ddcf9Ssf 	nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
15610abee971Sdv 
15620abee971Sdv 	return (0);
1563aa5ddcf9Ssf }
1564aa5ddcf9Ssf 
1565282c0692Sdlg void
1566448b3c09Sdlg nvme_q_free(struct nvme_softc *sc, struct nvme_queue *q)
1567282c0692Sdlg {
15680ee935ccSdlg 	nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
15690ee935ccSdlg 	nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_POSTWRITE);
15707599295eSdlg 
1571e163d0c4Skrw 	if (sc->sc_ops->op_q_free != NULL)
15727599295eSdlg 		sc->sc_ops->op_q_free(sc, q);
15737599295eSdlg 
1574282c0692Sdlg 	nvme_dmamem_free(sc, q->q_cq_dmamem);
1575282c0692Sdlg 	nvme_dmamem_free(sc, q->q_sq_dmamem);
1576234dfda1Sderaadt 	free(q, M_DEVBUF, sizeof *q);
1577282c0692Sdlg }
1578282c0692Sdlg 
1579282c0692Sdlg int
1580282c0692Sdlg nvme_intr(void *xsc)
1581282c0692Sdlg {
1582448b3c09Sdlg 	struct nvme_softc *sc = xsc;
158387cbc23fSdlg 	int rv = 0;
1584448b3c09Sdlg 
158587cbc23fSdlg 	if (nvme_q_complete(sc, sc->sc_q))
158687cbc23fSdlg 		rv = 1;
158787cbc23fSdlg 	if (nvme_q_complete(sc, sc->sc_admin_q))
158887cbc23fSdlg 		rv = 1;
158987cbc23fSdlg 
159087cbc23fSdlg 	return (rv);
1591282c0692Sdlg }
1592282c0692Sdlg 
1593eb77e636Sdlg int
1594eb77e636Sdlg nvme_intr_intx(void *xsc)
1595eb77e636Sdlg {
1596eb77e636Sdlg 	struct nvme_softc *sc = xsc;
1597eb77e636Sdlg 	int rv;
1598eb77e636Sdlg 
1599eb77e636Sdlg 	nvme_write4(sc, NVME_INTMS, 1);
1600eb77e636Sdlg 	rv = nvme_intr(sc);
1601eb77e636Sdlg 	nvme_write4(sc, NVME_INTMC, 1);
1602eb77e636Sdlg 
1603eb77e636Sdlg 	return (rv);
1604eb77e636Sdlg }
1605eb77e636Sdlg 
1606282c0692Sdlg struct nvme_dmamem *
1607282c0692Sdlg nvme_dmamem_alloc(struct nvme_softc *sc, size_t size)
1608282c0692Sdlg {
1609282c0692Sdlg 	struct nvme_dmamem *ndm;
1610282c0692Sdlg 	int nsegs;
1611282c0692Sdlg 
161272f0e87bSdlg 	ndm = malloc(sizeof(*ndm), M_DEVBUF, M_WAITOK | M_ZERO);
1613282c0692Sdlg 	if (ndm == NULL)
1614282c0692Sdlg 		return (NULL);
1615282c0692Sdlg 
1616282c0692Sdlg 	ndm->ndm_size = size;
1617282c0692Sdlg 
1618282c0692Sdlg 	if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
16192a4e68fdSdlg 	    BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW | BUS_DMA_64BIT,
16202a4e68fdSdlg 	    &ndm->ndm_map) != 0)
1621282c0692Sdlg 		goto ndmfree;
1622282c0692Sdlg 
1623448b3c09Sdlg 	if (bus_dmamem_alloc(sc->sc_dmat, size, sc->sc_mps, 0, &ndm->ndm_seg,
1624*a006a8cdSkettenis 	    1, &nsegs, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_64BIT) != 0)
1625282c0692Sdlg 		goto destroy;
1626282c0692Sdlg 
1627282c0692Sdlg 	if (bus_dmamem_map(sc->sc_dmat, &ndm->ndm_seg, nsegs, size,
162872f0e87bSdlg 	    &ndm->ndm_kva, BUS_DMA_WAITOK) != 0)
1629282c0692Sdlg 		goto free;
1630282c0692Sdlg 
1631282c0692Sdlg 	if (bus_dmamap_load(sc->sc_dmat, ndm->ndm_map, ndm->ndm_kva, size,
163272f0e87bSdlg 	    NULL, BUS_DMA_WAITOK) != 0)
1633282c0692Sdlg 		goto unmap;
1634282c0692Sdlg 
1635282c0692Sdlg 	return (ndm);
1636282c0692Sdlg 
1637282c0692Sdlg unmap:
1638282c0692Sdlg 	bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, size);
1639282c0692Sdlg free:
1640282c0692Sdlg 	bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1);
1641282c0692Sdlg destroy:
1642282c0692Sdlg 	bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map);
1643282c0692Sdlg ndmfree:
1644234dfda1Sderaadt 	free(ndm, M_DEVBUF, sizeof *ndm);
1645282c0692Sdlg 
1646282c0692Sdlg 	return (NULL);
1647282c0692Sdlg }
1648282c0692Sdlg 
1649282c0692Sdlg void
16500ee935ccSdlg nvme_dmamem_sync(struct nvme_softc *sc, struct nvme_dmamem *mem, int ops)
16510ee935ccSdlg {
16520ee935ccSdlg 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(mem),
16530ee935ccSdlg 	    0, NVME_DMA_LEN(mem), ops);
16540ee935ccSdlg }
16550ee935ccSdlg 
16560ee935ccSdlg void
1657282c0692Sdlg nvme_dmamem_free(struct nvme_softc *sc, struct nvme_dmamem *ndm)
1658282c0692Sdlg {
1659282c0692Sdlg 	bus_dmamap_unload(sc->sc_dmat, ndm->ndm_map);
1660282c0692Sdlg 	bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, ndm->ndm_size);
1661282c0692Sdlg 	bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1);
1662282c0692Sdlg 	bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map);
1663234dfda1Sderaadt 	free(ndm, M_DEVBUF, sizeof *ndm);
1664282c0692Sdlg }
1665282c0692Sdlg 
166603d86467Sjmatthew #ifdef HIBERNATE
166703d86467Sjmatthew 
166803d86467Sjmatthew int
166903d86467Sjmatthew nvme_hibernate_admin_cmd(struct nvme_softc *sc, struct nvme_sqe *sqe,
167003d86467Sjmatthew     struct nvme_cqe *cqe, int cid)
167103d86467Sjmatthew {
167203d86467Sjmatthew 	struct nvme_sqe *asqe = NVME_DMA_KVA(sc->sc_admin_q->q_sq_dmamem);
167303d86467Sjmatthew 	struct nvme_cqe *acqe = NVME_DMA_KVA(sc->sc_admin_q->q_cq_dmamem);
167403d86467Sjmatthew 	struct nvme_queue *q = sc->sc_admin_q;
167503d86467Sjmatthew 	int tail;
167603d86467Sjmatthew 	u_int16_t flags;
167703d86467Sjmatthew 
167803d86467Sjmatthew 	/* submit command */
16797599295eSdlg 	tail = sc->sc_ops->op_sq_enter_locked(sc, q, /* XXX ccb */ NULL);
168003d86467Sjmatthew 
168103d86467Sjmatthew 	asqe += tail;
168203d86467Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
168303d86467Sjmatthew 	    sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_POSTWRITE);
168403d86467Sjmatthew 	*asqe = *sqe;
168503d86467Sjmatthew 	asqe->cid = cid;
168603d86467Sjmatthew 	bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
168703d86467Sjmatthew 	    sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_PREWRITE);
168803d86467Sjmatthew 
16897599295eSdlg 	sc->sc_ops->op_sq_leave_locked(sc, q, /* XXX ccb */ NULL);
169003d86467Sjmatthew 
169103d86467Sjmatthew 	/* wait for completion */
169203d86467Sjmatthew 	acqe += q->q_cq_head;
169303d86467Sjmatthew 	for (;;) {
169403d86467Sjmatthew 		nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
169503d86467Sjmatthew 		flags = lemtoh16(&acqe->flags);
169603d86467Sjmatthew 		if ((flags & NVME_CQE_PHASE) == q->q_cq_phase)
169703d86467Sjmatthew 			break;
169803d86467Sjmatthew 
169903d86467Sjmatthew 		delay(10);
170003d86467Sjmatthew 	}
170103d86467Sjmatthew 
170203d86467Sjmatthew 	if (++q->q_cq_head >= q->q_entries) {
170303d86467Sjmatthew 		q->q_cq_head = 0;
170403d86467Sjmatthew 		q->q_cq_phase ^= NVME_CQE_PHASE;
170503d86467Sjmatthew 	}
170603d86467Sjmatthew 	nvme_write4(sc, q->q_cqhdbl, q->q_cq_head);
170703d86467Sjmatthew 	if ((NVME_CQE_SC(flags) != NVME_CQE_SC_SUCCESS) || (acqe->cid != cid))
170803d86467Sjmatthew 		return (EIO);
170903d86467Sjmatthew 
171003d86467Sjmatthew 	return (0);
171103d86467Sjmatthew }
171203d86467Sjmatthew 
171303d86467Sjmatthew int
171403d86467Sjmatthew nvme_hibernate_io(dev_t dev, daddr_t blkno, vaddr_t addr, size_t size,
171503d86467Sjmatthew     int op, void *page)
171603d86467Sjmatthew {
171703d86467Sjmatthew 	struct nvme_hibernate_page {
171803d86467Sjmatthew 		u_int64_t		prpl[MAXPHYS / PAGE_SIZE];
171903d86467Sjmatthew 
172003d86467Sjmatthew 		struct nvme_softc	*sc;
172103d86467Sjmatthew 		int			nsid;
172203d86467Sjmatthew 		int			sq_tail;
172303d86467Sjmatthew 		int			cq_head;
172403d86467Sjmatthew 		int			cqe_phase;
172503d86467Sjmatthew 
172603d86467Sjmatthew 		daddr_t			poffset;
172703d86467Sjmatthew 		size_t			psize;
1728fc6d48fdSkrw 		u_int32_t		secsize;
172903d86467Sjmatthew 	} *my = page;
173003d86467Sjmatthew 	struct nvme_sqe_io *isqe;
173103d86467Sjmatthew 	struct nvme_cqe *icqe;
173203d86467Sjmatthew 	paddr_t data_phys, page_phys;
173303d86467Sjmatthew 	u_int64_t data_bus_phys, page_bus_phys;
173403d86467Sjmatthew 	u_int16_t flags;
173503d86467Sjmatthew 	int i;
1736cad24394Sdlg 	int error;
173703d86467Sjmatthew 
173803d86467Sjmatthew 	if (op == HIB_INIT) {
173903d86467Sjmatthew 		struct device *disk;
174003d86467Sjmatthew 		struct device *scsibus;
1741fc6d48fdSkrw 		struct nvm_identify_namespace *ns;
1742fc6d48fdSkrw 		struct nvm_namespace_format *f;
174303d86467Sjmatthew 		extern struct cfdriver sd_cd;
174403d86467Sjmatthew 		struct scsi_link *link;
174503d86467Sjmatthew 		struct scsibus_softc *bus_sc;
174603d86467Sjmatthew 		struct nvme_sqe_q qsqe;
174703d86467Sjmatthew 		struct nvme_cqe qcqe;
174803d86467Sjmatthew 
174903d86467Sjmatthew 		/* find nvme softc */
175003d86467Sjmatthew 		disk = disk_lookup(&sd_cd, DISKUNIT(dev));
175103d86467Sjmatthew 		scsibus = disk->dv_parent;
175203d86467Sjmatthew 		my->sc = (struct nvme_softc *)disk->dv_parent->dv_parent;
175303d86467Sjmatthew 
175403d86467Sjmatthew 		/* find scsi_link, which tells us the target */
175503d86467Sjmatthew 		my->nsid = 0;
175603d86467Sjmatthew 		bus_sc = (struct scsibus_softc *)scsibus;
175703d86467Sjmatthew 		SLIST_FOREACH(link, &bus_sc->sc_link_list, bus_list) {
175803d86467Sjmatthew 			if (link->device_softc == disk) {
1759397f5692Skettenis 				my->nsid = link->target;
176003d86467Sjmatthew 				break;
176103d86467Sjmatthew 			}
176203d86467Sjmatthew 		}
176303d86467Sjmatthew 		if (my->nsid == 0)
176403d86467Sjmatthew 			return (EIO);
1765fc6d48fdSkrw 		ns = my->sc->sc_namespaces[my->nsid].ident;
1766fc6d48fdSkrw 		f = &ns->lbaf[NVME_ID_NS_FLBAS(ns->flbas)];
176703d86467Sjmatthew 
176803d86467Sjmatthew 		my->poffset = blkno;
176903d86467Sjmatthew 		my->psize = size;
1770fc6d48fdSkrw 		my->secsize = 1 << f->lbads;
177103d86467Sjmatthew 
177203d86467Sjmatthew 		memset(NVME_DMA_KVA(my->sc->sc_hib_q->q_cq_dmamem), 0,
177303d86467Sjmatthew 		    my->sc->sc_hib_q->q_entries * sizeof(struct nvme_cqe));
177403d86467Sjmatthew 		memset(NVME_DMA_KVA(my->sc->sc_hib_q->q_sq_dmamem), 0,
177503d86467Sjmatthew 		    my->sc->sc_hib_q->q_entries * sizeof(struct nvme_sqe));
177603d86467Sjmatthew 
177703d86467Sjmatthew 		my->sq_tail = 0;
177803d86467Sjmatthew 		my->cq_head = 0;
177903d86467Sjmatthew 		my->cqe_phase = NVME_CQE_PHASE;
178003d86467Sjmatthew 
178103d86467Sjmatthew 		memset(&qsqe, 0, sizeof(qsqe));
178203d86467Sjmatthew 		qsqe.opcode = NVM_ADMIN_ADD_IOCQ;
178303d86467Sjmatthew 		htolem64(&qsqe.prp1,
178403d86467Sjmatthew 		    NVME_DMA_DVA(my->sc->sc_hib_q->q_cq_dmamem));
178503d86467Sjmatthew 		htolem16(&qsqe.qsize, my->sc->sc_hib_q->q_entries - 1);
178603d86467Sjmatthew 		htolem16(&qsqe.qid, my->sc->sc_hib_q->q_id);
178703d86467Sjmatthew 		qsqe.qflags = NVM_SQE_CQ_IEN | NVM_SQE_Q_PC;
178803d86467Sjmatthew 		if (nvme_hibernate_admin_cmd(my->sc, (struct nvme_sqe *)&qsqe,
178903d86467Sjmatthew 		    &qcqe, 1) != 0)
179003d86467Sjmatthew 			return (EIO);
179103d86467Sjmatthew 
179203d86467Sjmatthew 		memset(&qsqe, 0, sizeof(qsqe));
179303d86467Sjmatthew 		qsqe.opcode = NVM_ADMIN_ADD_IOSQ;
179403d86467Sjmatthew 		htolem64(&qsqe.prp1,
179503d86467Sjmatthew 		    NVME_DMA_DVA(my->sc->sc_hib_q->q_sq_dmamem));
179603d86467Sjmatthew 		htolem16(&qsqe.qsize, my->sc->sc_hib_q->q_entries - 1);
179703d86467Sjmatthew 		htolem16(&qsqe.qid, my->sc->sc_hib_q->q_id);
179803d86467Sjmatthew 		htolem16(&qsqe.cqid, my->sc->sc_hib_q->q_id);
179903d86467Sjmatthew 		qsqe.qflags = NVM_SQE_Q_PC;
180003d86467Sjmatthew 		if (nvme_hibernate_admin_cmd(my->sc, (struct nvme_sqe *)&qsqe,
180103d86467Sjmatthew 		    &qcqe, 2) != 0)
180203d86467Sjmatthew 			return (EIO);
180303d86467Sjmatthew 
180403d86467Sjmatthew 		return (0);
180503d86467Sjmatthew 	}
180603d86467Sjmatthew 
180703d86467Sjmatthew 	if (op != HIB_W)
180803d86467Sjmatthew 		return (0);
180903d86467Sjmatthew 
1810fc6d48fdSkrw 	if (blkno + (size / DEV_BSIZE) > my->psize)
1811fc6d48fdSkrw 		return E2BIG;
1812fc6d48fdSkrw 
181303d86467Sjmatthew 	isqe = NVME_DMA_KVA(my->sc->sc_hib_q->q_sq_dmamem);
181403d86467Sjmatthew 	isqe += my->sq_tail;
181503d86467Sjmatthew 	if (++my->sq_tail == my->sc->sc_hib_q->q_entries)
181603d86467Sjmatthew 		my->sq_tail = 0;
181703d86467Sjmatthew 
181803d86467Sjmatthew 	memset(isqe, 0, sizeof(*isqe));
181903d86467Sjmatthew 	isqe->opcode = NVM_CMD_WRITE;
182003d86467Sjmatthew 	htolem32(&isqe->nsid, my->nsid);
182103d86467Sjmatthew 
182203d86467Sjmatthew 	pmap_extract(pmap_kernel(), addr, &data_phys);
182303d86467Sjmatthew 	data_bus_phys = data_phys;
182403d86467Sjmatthew 	htolem64(&isqe->entry.prp[0], data_bus_phys);
182503d86467Sjmatthew 	if ((size > my->sc->sc_mps) && (size <= my->sc->sc_mps * 2)) {
182603d86467Sjmatthew 		htolem64(&isqe->entry.prp[1], data_bus_phys + my->sc->sc_mps);
182703d86467Sjmatthew 	} else if (size > my->sc->sc_mps * 2) {
182803d86467Sjmatthew 		pmap_extract(pmap_kernel(), (vaddr_t)page, &page_phys);
182903d86467Sjmatthew 		page_bus_phys = page_phys;
183003d86467Sjmatthew 		htolem64(&isqe->entry.prp[1], page_bus_phys +
183103d86467Sjmatthew 		    offsetof(struct nvme_hibernate_page, prpl));
18324e79e485Skrw 		for (i = 1; i < howmany(size, my->sc->sc_mps); i++) {
183303d86467Sjmatthew 			htolem64(&my->prpl[i - 1], data_bus_phys +
183403d86467Sjmatthew 			    (i * my->sc->sc_mps));
183503d86467Sjmatthew 		}
183603d86467Sjmatthew 	}
183703d86467Sjmatthew 
1838fc6d48fdSkrw 	isqe->slba = (blkno + my->poffset) / (my->secsize / DEV_BSIZE);
1839fc6d48fdSkrw 	isqe->nlb = (size / my->secsize) - 1;
184003d86467Sjmatthew 	isqe->cid = blkno % 0xffff;
184103d86467Sjmatthew 
184203d86467Sjmatthew 	nvme_write4(my->sc, NVME_SQTDBL(NVME_HIB_Q, my->sc->sc_dstrd),
184303d86467Sjmatthew 	    my->sq_tail);
1844cad24394Sdlg 	nvme_barrier(my->sc, NVME_SQTDBL(NVME_HIB_Q, my->sc->sc_dstrd), 4,
1845cad24394Sdlg 	    BUS_SPACE_BARRIER_WRITE);
1846cad24394Sdlg 
1847cad24394Sdlg 	error = 0;
184803d86467Sjmatthew 
184903d86467Sjmatthew 	icqe = NVME_DMA_KVA(my->sc->sc_hib_q->q_cq_dmamem);
185003d86467Sjmatthew 	icqe += my->cq_head;
1851cad24394Sdlg 
1852cad24394Sdlg 	nvme_dmamem_sync(my->sc, my->sc->sc_hib_q->q_cq_dmamem,
1853cad24394Sdlg 	    BUS_DMASYNC_POSTREAD);
185403d86467Sjmatthew 	for (;;) {
185503d86467Sjmatthew 		flags = lemtoh16(&icqe->flags);
1856cad24394Sdlg 		if ((flags & NVME_CQE_PHASE) == my->cqe_phase) {
1857cad24394Sdlg 			if ((NVME_CQE_SC(flags) != NVME_CQE_SC_SUCCESS) ||
1858cad24394Sdlg 			    (icqe->cid != blkno % 0xffff))
1859cad24394Sdlg 				error = EIO;
186003d86467Sjmatthew 
1861cad24394Sdlg 			break;
186203d86467Sjmatthew 		}
186303d86467Sjmatthew 
1864cad24394Sdlg 		delay(1);
1865cad24394Sdlg 		nvme_dmamem_sync(my->sc, my->sc->sc_hib_q->q_cq_dmamem,
1866cad24394Sdlg 		    BUS_DMASYNC_PREREAD|BUS_DMASYNC_POSTREAD);
1867cad24394Sdlg 	}
1868cad24394Sdlg 	nvme_dmamem_sync(my->sc, my->sc->sc_hib_q->q_cq_dmamem,
1869cad24394Sdlg 	    BUS_DMASYNC_PREREAD);
1870cad24394Sdlg 
187103d86467Sjmatthew 	if (++my->cq_head == my->sc->sc_hib_q->q_entries) {
187203d86467Sjmatthew 		my->cq_head = 0;
187303d86467Sjmatthew 		my->cqe_phase ^= NVME_CQE_PHASE;
187403d86467Sjmatthew 	}
1875cad24394Sdlg 
187603d86467Sjmatthew 	nvme_write4(my->sc, NVME_CQHDBL(NVME_HIB_Q, my->sc->sc_dstrd),
187703d86467Sjmatthew 	    my->cq_head);
1878cad24394Sdlg 	nvme_barrier(my->sc, NVME_CQHDBL(NVME_HIB_Q, my->sc->sc_dstrd), 4,
1879cad24394Sdlg 	    BUS_SPACE_BARRIER_WRITE);
188003d86467Sjmatthew 
1881cad24394Sdlg 	return (error);
188203d86467Sjmatthew }
188303d86467Sjmatthew 
188403d86467Sjmatthew #endif
18857f4636ceSkrw 
18867f4636ceSkrw #if NBIO > 0
18877f4636ceSkrw int
18887f4636ceSkrw nvme_bioctl(struct device *self, u_long cmd, caddr_t data)
18897f4636ceSkrw {
18907f4636ceSkrw 	struct nvme_softc	*sc = (struct nvme_softc *)self;
18914e9514d6Skrw 	struct nvme_pt_cmd	*pt;
18927f4636ceSkrw 	int			 error = 0;
18937f4636ceSkrw 
18947f4636ceSkrw 	rw_enter_write(&sc->sc_lock);
18957f4636ceSkrw 
18967f4636ceSkrw 	switch (cmd) {
18977f4636ceSkrw 	case BIOCINQ:
18987f4636ceSkrw 		error = nvme_bioctl_inq(sc, (struct bioc_inq *)data);
18997f4636ceSkrw 		break;
19007f4636ceSkrw 	case BIOCVOL:
19017f4636ceSkrw 		error = nvme_bioctl_vol(sc, (struct bioc_vol *)data);
19027f4636ceSkrw 		break;
19037f4636ceSkrw 	case BIOCDISK:
19047f4636ceSkrw 		error = nvme_bioctl_disk(sc, (struct bioc_disk *)data);
19057f4636ceSkrw 		break;
19064e9514d6Skrw 	case NVME_PASSTHROUGH_CMD:
19074e9514d6Skrw 		pt = (struct nvme_pt_cmd *)data;
19084e9514d6Skrw 		error = nvme_passthrough_cmd(sc, pt, sc->sc_dev.dv_unit, -1);
19094e9514d6Skrw 		break;
19107f4636ceSkrw 	default:
19117f4636ceSkrw 		printf("nvme_bioctl() Unknown command (%lu)\n", cmd);
19127f4636ceSkrw 		error = ENOTTY;
19137f4636ceSkrw 	}
19147f4636ceSkrw 
19157f4636ceSkrw 	rw_exit_write(&sc->sc_lock);
19167f4636ceSkrw 
19177f4636ceSkrw 	return error;
19187f4636ceSkrw }
19197f4636ceSkrw 
19207f4636ceSkrw void
19217f4636ceSkrw nvme_bio_status(struct bio_status *bs, const char *fmt, ...)
19227f4636ceSkrw {
19237f4636ceSkrw 	va_list			ap;
19247f4636ceSkrw 
19257f4636ceSkrw 	va_start(ap, fmt);
19267f4636ceSkrw 	bio_status(bs, 0, BIO_MSG_INFO, fmt, &ap);
19277f4636ceSkrw 	va_end(ap);
19287f4636ceSkrw }
19297f4636ceSkrw 
19307f4636ceSkrw const char *
19317f4636ceSkrw nvme_bioctl_sdname(const struct nvme_softc *sc, int target)
19327f4636ceSkrw {
19337f4636ceSkrw 	const struct scsi_link		*link;
19347f4636ceSkrw 	const struct sd_softc		*sd;
19357f4636ceSkrw 
19367f4636ceSkrw 	link = scsi_get_link(sc->sc_scsibus, target, 0);
19379a0b8a7eSjsg 	if (link == NULL)
19389a0b8a7eSjsg 		return NULL;
19397f4636ceSkrw 	sd = (struct sd_softc *)(link->device_softc);
19407f4636ceSkrw 	if (ISSET(link->state, SDEV_S_DYING) || sd == NULL ||
19417f4636ceSkrw 	    ISSET(sd->flags, SDF_DYING))
19427f4636ceSkrw 		return NULL;
19437f4636ceSkrw 
194498f39564Skrw 	if (nvme_read4(sc, NVME_VS) == 0xffffffff)
19457f4636ceSkrw 		return NULL;
19467f4636ceSkrw 
19477f4636ceSkrw 	return DEVNAME(sd);
19487f4636ceSkrw }
19497f4636ceSkrw 
19507f4636ceSkrw int
19517f4636ceSkrw nvme_bioctl_inq(struct nvme_softc *sc, struct bioc_inq *bi)
19527f4636ceSkrw {
19537f4636ceSkrw 	char				 sn[41], mn[81], fr[17];
19547f4636ceSkrw 	struct nvm_identify_controller	*idctrl = &sc->sc_identify;
19557f4636ceSkrw 	struct bio_status		*bs;
19567f4636ceSkrw 	unsigned int			 nn;
19577f4636ceSkrw 	uint32_t			 cc, csts, vs;
19587f4636ceSkrw 
19597f4636ceSkrw 	/* Don't tell bioctl about namespaces > last configured namespace. */
19607f4636ceSkrw 	for (nn = sc->sc_nn; nn > 0; nn--) {
19617f4636ceSkrw 		if (sc->sc_namespaces[nn].ident)
19627f4636ceSkrw 			break;
19637f4636ceSkrw 	}
19647f4636ceSkrw 	bi->bi_novol = bi->bi_nodisk = nn;
19657f4636ceSkrw 	strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
19667f4636ceSkrw 
19677f4636ceSkrw 	bs = &bi->bi_bio.bio_status;
19687f4636ceSkrw 	bio_status_init(bs, &sc->sc_dev);
19697f4636ceSkrw 	bs->bs_status = BIO_STATUS_SUCCESS;
19707f4636ceSkrw 
19717f4636ceSkrw 	scsi_strvis(sn, idctrl->sn, sizeof(idctrl->sn));
19727f4636ceSkrw 	scsi_strvis(mn, idctrl->mn, sizeof(idctrl->mn));
19737f4636ceSkrw 	scsi_strvis(fr, idctrl->fr, sizeof(idctrl->fr));
19747f4636ceSkrw 
19757f4636ceSkrw 	nvme_bio_status(bs, "%s, %s, %s", mn, fr, sn);
19767f4636ceSkrw 	nvme_bio_status(bs, "Max i/o %zu bytes%s%s%s, Sanitize 0x%b",
19777f4636ceSkrw 	    sc->sc_mdts,
19787f4636ceSkrw 	    ISSET(idctrl->lpa, NVM_ID_CTRL_LPA_PE) ?
197954fbbda3Sjsg 	    ", Persistent Event Log" : "",
19807f4636ceSkrw 	    ISSET(idctrl->fna, NVM_ID_CTRL_FNA_CRYPTOFORMAT) ?
19817f4636ceSkrw 	    ", CryptoFormat" : "",
19827f4636ceSkrw 	    ISSET(idctrl->vwc, NVM_ID_CTRL_VWC_PRESENT) ?
19837f4636ceSkrw 	    ", Volatile Write Cache" : "",
19847f4636ceSkrw 	    lemtoh32(&idctrl->sanicap), NVM_ID_CTRL_SANICAP_FMT
19857f4636ceSkrw 	);
19867f4636ceSkrw 
19877f4636ceSkrw 	if (idctrl->ctratt != 0)
19887f4636ceSkrw 		nvme_bio_status(bs, "Features 0x%b", lemtoh32(&idctrl->ctratt),
19897f4636ceSkrw 		    NVM_ID_CTRL_CTRATT_FMT);
19907f4636ceSkrw 
19917f4636ceSkrw 	if (idctrl->oacs || idctrl->oncs) {
19927f4636ceSkrw 		nvme_bio_status(bs, "Admin commands 0x%b, NVM commands 0x%b",
19937f4636ceSkrw 		    lemtoh16(&idctrl->oacs), NVM_ID_CTRL_OACS_FMT,
19947f4636ceSkrw 		    lemtoh16(&idctrl->oncs), NVM_ID_CTRL_ONCS_FMT);
19957f4636ceSkrw 	}
19967f4636ceSkrw 
19977f4636ceSkrw 	cc = nvme_read4(sc, NVME_CC);
19987f4636ceSkrw 	csts = nvme_read4(sc, NVME_CSTS);
19997f4636ceSkrw 	vs = nvme_read4(sc, NVME_VS);
20007f4636ceSkrw 
200198f39564Skrw 	if (vs == 0xffffffff) {
20027f4636ceSkrw 		nvme_bio_status(bs, "Invalid PCIe register mapping");
20037f4636ceSkrw 		return 0;
20047f4636ceSkrw 	}
20057f4636ceSkrw 
20067f4636ceSkrw 	nvme_bio_status(bs, "NVMe %u.%u%s%s%sabled, %sReady%s%s%s%s",
20077f4636ceSkrw 	    NVME_VS_MJR(vs), NVME_VS_MNR(vs),
20087f4636ceSkrw 	    (NVME_CC_CSS_R(cc) == NVME_CC_CSS_NVM) ? ", NVM I/O command set" : "",
20097f4636ceSkrw 	    (NVME_CC_CSS_R(cc) == 0x7) ? ", Admin command set only" : "",
20107f4636ceSkrw 	    ISSET(cc, NVME_CC_EN) ? ", En" : "Dis",
20117f4636ceSkrw 	    ISSET(csts, NVME_CSTS_RDY) ? "" : "Not ",
20127f4636ceSkrw 	    ISSET(csts, NVME_CSTS_CFS) ? ", Fatal Error, " : "",
20137f4636ceSkrw 	    (NVME_CC_SHN_R(cc) == NVME_CC_SHN_NORMAL) ? ", Normal shutdown" : "",
20147f4636ceSkrw 	    (NVME_CC_SHN_R(cc) == NVME_CC_SHN_ABRUPT) ? ", Abrupt shutdown" : "",
20157f4636ceSkrw 	    ISSET(csts, NVME_CSTS_SHST_DONE) ? " complete" : "");
20167f4636ceSkrw 
20177f4636ceSkrw 	return 0;
20187f4636ceSkrw }
20197f4636ceSkrw 
20207f4636ceSkrw int
20217f4636ceSkrw nvme_bioctl_vol(struct nvme_softc *sc, struct bioc_vol *bv)
20227f4636ceSkrw {
20237f4636ceSkrw 	const struct nvm_identify_namespace	*idns;
20247f4636ceSkrw 	const char				*sd;
20257f4636ceSkrw 	int					 target;
20267f4636ceSkrw 	unsigned int 				 lbaf;
20277f4636ceSkrw 
20287f4636ceSkrw 	target = bv->bv_volid + 1;
20297f4636ceSkrw 	if (target > sc->sc_nn) {
20307f4636ceSkrw 		bv->bv_status = BIOC_SVINVALID;
20317f4636ceSkrw 		return 0;
20327f4636ceSkrw 	}
20337f4636ceSkrw 
20347f4636ceSkrw 	bv->bv_level = 'c';
20357f4636ceSkrw 	bv->bv_nodisk = 1;
20367f4636ceSkrw 
20377f4636ceSkrw 	idns = sc->sc_namespaces[target].ident;
20387f4636ceSkrw 	if (idns == NULL) {
20397f4636ceSkrw 		bv->bv_status = BIOC_SVINVALID;
20407f4636ceSkrw 		return 0;
20417f4636ceSkrw 	}
20427f4636ceSkrw 
20437f4636ceSkrw 	lbaf = NVME_ID_NS_FLBAS(idns->flbas);
20447f4636ceSkrw 	if (idns->nlbaf > 16)
20457f4636ceSkrw 		lbaf |= (idns->flbas >> 1) & 0x3f;
20467f4636ceSkrw 	bv->bv_size = nvme_scsi_size(idns) << idns->lbaf[lbaf].lbads;
20477f4636ceSkrw 
20487f4636ceSkrw 	sd = nvme_bioctl_sdname(sc, target);
20497f4636ceSkrw 	if (sd) {
20507f4636ceSkrw 		strlcpy(bv->bv_dev, sd, sizeof(bv->bv_dev));
20517f4636ceSkrw 		bv->bv_status = BIOC_SVONLINE;
20527f4636ceSkrw 	} else
20537f4636ceSkrw 		bv->bv_status = BIOC_SVOFFLINE;
20547f4636ceSkrw 
20557f4636ceSkrw 	return 0;
20567f4636ceSkrw }
20577f4636ceSkrw 
20587f4636ceSkrw int
20597f4636ceSkrw nvme_bioctl_disk(struct nvme_softc *sc, struct bioc_disk *bd)
20607f4636ceSkrw {
20617f4636ceSkrw 	const char 			*rpdesc[4] = {
20627f4636ceSkrw 		" (Best)",
20637f4636ceSkrw 		" (Better)",
20647f4636ceSkrw 		" (Good)",
20657f4636ceSkrw 		" (Degraded)"
20667f4636ceSkrw 	};
20677f4636ceSkrw 	const char			*protection[4] = {
20687f4636ceSkrw 		"not enabled",
20697f4636ceSkrw 		"Type 1",
20707f4636ceSkrw 		"Type 2",
20717f4636ceSkrw 		"Type 3",
20727f4636ceSkrw 	};
20737f4636ceSkrw 	char				 buf[32], msg[BIO_MSG_LEN];
20747f4636ceSkrw 	struct nvm_identify_namespace	*idns;
20757f4636ceSkrw 	struct bio_status		*bs;
20767f4636ceSkrw 	uint64_t			 id1, id2;
20777f4636ceSkrw 	unsigned int			 i, lbaf, target;
20787f4636ceSkrw 	uint16_t			 ms;
20797f4636ceSkrw 	uint8_t				 dps;
20807f4636ceSkrw 
20817f4636ceSkrw 	target = bd->bd_volid + 1;
20827f4636ceSkrw 	if (target > sc->sc_nn)
20837f4636ceSkrw 		return EINVAL;
20847f4636ceSkrw 	bd->bd_channel = sc->sc_scsibus->sc_dev.dv_unit;
20857f4636ceSkrw 	bd->bd_target = target;
20867f4636ceSkrw 	bd->bd_lun = 0;
20877f4636ceSkrw 	snprintf(bd->bd_procdev, sizeof(bd->bd_procdev), "Namespace %u", target);
20887f4636ceSkrw 
20897f4636ceSkrw 	bs = &bd->bd_bio.bio_status;
20907f4636ceSkrw 	bs->bs_status = BIO_STATUS_SUCCESS;
20917f4636ceSkrw 	snprintf(bs->bs_controller, sizeof(bs->bs_controller), "%11u",
20927f4636ceSkrw 	    bd->bd_diskid);
20937f4636ceSkrw 
20947f4636ceSkrw 	idns = sc->sc_namespaces[target].ident;
20957f4636ceSkrw 	if (idns == NULL) {
20967f4636ceSkrw 		bd->bd_status = BIOC_SDUNUSED;
20977f4636ceSkrw 		return 0;
20987f4636ceSkrw 	}
20997f4636ceSkrw 
21007f4636ceSkrw 	lbaf = NVME_ID_NS_FLBAS(idns->flbas);
21017f4636ceSkrw 	if (idns->nlbaf > nitems(idns->lbaf))
21027f4636ceSkrw 		lbaf |= (idns->flbas >> 1) & 0x3f;
21037f4636ceSkrw 	bd->bd_size = lemtoh64(&idns->nsze) << idns->lbaf[lbaf].lbads;
21047f4636ceSkrw 
21057f4636ceSkrw 	if (memcmp(idns->nguid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16)) {
21067f4636ceSkrw 		memcpy(&id1, idns->nguid, sizeof(uint64_t));
21077f4636ceSkrw 		memcpy(&id2, idns->nguid + sizeof(uint64_t), sizeof(uint64_t));
21087f4636ceSkrw 		snprintf(bd->bd_serial, sizeof(bd->bd_serial), "%08llx%08llx",
21097f4636ceSkrw 		    id1, id2);
21107f4636ceSkrw 	} else if (memcmp(idns->eui64, "\0\0\0\0\0\0\0\0", 8)) {
21117f4636ceSkrw 		memcpy(&id1, idns->eui64, sizeof(uint64_t));
21127f4636ceSkrw 		snprintf(bd->bd_serial, sizeof(bd->bd_serial), "%08llx", id1);
21137f4636ceSkrw 	}
21147f4636ceSkrw 
21157f4636ceSkrw 	msg[0] = '\0';
21167f4636ceSkrw 	for (i = 0; i <= idns->nlbaf; i++) {
21177f4636ceSkrw 		if (idns->lbaf[i].lbads == 0)
21187f4636ceSkrw 			continue;
21197f4636ceSkrw 		snprintf(buf, sizeof(buf), "%s%s%u",
21207f4636ceSkrw 		    strlen(msg) ? ", " : "", (i == lbaf) ? "*" : "",
21217f4636ceSkrw 		    1 << idns->lbaf[i].lbads);
21227f4636ceSkrw 		strlcat(msg, buf, sizeof(msg));
21237f4636ceSkrw 		ms = lemtoh16(&idns->lbaf[i].ms);
21247f4636ceSkrw 		if (ms) {
21257f4636ceSkrw 			snprintf(buf, sizeof(buf), "+%u", ms);
21267f4636ceSkrw 			strlcat(msg, buf, sizeof(msg));
21277f4636ceSkrw 		}
21287f4636ceSkrw 		strlcat(msg, rpdesc[idns->lbaf[i].rp], sizeof(msg));
21297f4636ceSkrw 	}
21307f4636ceSkrw 	nvme_bio_status(bs, "Formats %s", msg);
21317f4636ceSkrw 
21327f4636ceSkrw 	if (idns->nsfeat)
21337f4636ceSkrw 		nvme_bio_status(bs, "Features 0x%b", idns->nsfeat,
21347f4636ceSkrw 		    NVME_ID_NS_NSFEAT_FMT);
21357f4636ceSkrw 
21367f4636ceSkrw 	if (idns->dps) {
21377f4636ceSkrw 		dps = idns->dps;
21387f4636ceSkrw 		snprintf(msg, sizeof(msg), "Data Protection (0x%02x) "
21397f4636ceSkrw 		    "Protection Data in ", dps);
21407f4636ceSkrw 		if (ISSET(dps, NVME_ID_NS_DPS_PIP))
21417f4636ceSkrw 			strlcat(msg, "first", sizeof(msg));
21427f4636ceSkrw 		else
21437f4636ceSkrw 			strlcat(msg, "last", sizeof(msg));
21447f4636ceSkrw 		strlcat(msg, "bytes of metadata, Protection ", sizeof(msg));
21457f4636ceSkrw 		if (NVME_ID_NS_DPS_TYPE(dps) >= nitems(protection))
21467f4636ceSkrw 			strlcat(msg, "Type unknown", sizeof(msg));
21477f4636ceSkrw 		else
21487f4636ceSkrw 			strlcat(msg, protection[NVME_ID_NS_DPS_TYPE(dps)],
21497f4636ceSkrw 			    sizeof(msg));
21507f4636ceSkrw 		nvme_bio_status(bs, "%s", msg);
21517f4636ceSkrw 	}
21527f4636ceSkrw 
21537f4636ceSkrw 	if (nvme_bioctl_sdname(sc, target) == NULL)
21547f4636ceSkrw 		bd->bd_status = BIOC_SDOFFLINE;
21557f4636ceSkrw 	else
21567f4636ceSkrw 		bd->bd_status = BIOC_SDONLINE;
21577f4636ceSkrw 
21587f4636ceSkrw 	return 0;
21597f4636ceSkrw }
21607f4636ceSkrw #endif	/* NBIO > 0 */
21613800fc35Sjmatthew 
21623800fc35Sjmatthew #ifndef SMALL_KERNEL
21633800fc35Sjmatthew void
21643800fc35Sjmatthew nvme_refresh_sensors(void *arg)
21653800fc35Sjmatthew {
21663800fc35Sjmatthew 	struct nvme_softc 		*sc = arg;
21673800fc35Sjmatthew 	struct nvme_sqe			 sqe;
21683800fc35Sjmatthew 	struct nvme_dmamem		*mem = NULL;
21693800fc35Sjmatthew 	struct nvme_ccb			*ccb = NULL;
21703800fc35Sjmatthew 	struct nvm_smart_health 	*health;
21713800fc35Sjmatthew 	uint32_t			 dwlen;
21723800fc35Sjmatthew 	uint8_t 			 cw;
21733800fc35Sjmatthew 	int				 flags;
21743800fc35Sjmatthew 	int64_t				 temp;
21753800fc35Sjmatthew 
21763800fc35Sjmatthew 	ccb = nvme_ccb_get(sc);
21773800fc35Sjmatthew 	if (ccb == NULL)
21783800fc35Sjmatthew 		goto failed;
21793800fc35Sjmatthew 
21803800fc35Sjmatthew 	mem = nvme_dmamem_alloc(sc, sizeof(*health));
21813800fc35Sjmatthew 	if (mem == NULL)
21823800fc35Sjmatthew 		goto failed;
21833800fc35Sjmatthew 	nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD);
21843800fc35Sjmatthew 
21853800fc35Sjmatthew 	dwlen = (sizeof(*health) >> 2) - 1;
21863800fc35Sjmatthew 	memset(&sqe, 0, sizeof(sqe));
21873800fc35Sjmatthew 	sqe.opcode = NVM_ADMIN_GET_LOG_PG;
21883800fc35Sjmatthew 	htolem32(&sqe.nsid, 0xffffffff);
21893800fc35Sjmatthew 	htolem32(&sqe.cdw10, (dwlen << 16 | NVM_LOG_PAGE_SMART_HEALTH));
21903800fc35Sjmatthew 	htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem));
21913800fc35Sjmatthew 
21923800fc35Sjmatthew 	ccb->ccb_done = nvme_empty_done;
21933800fc35Sjmatthew 	ccb->ccb_cookie = &sqe;
21943800fc35Sjmatthew 	flags = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_LOG_PAGE);
21953800fc35Sjmatthew 
21963800fc35Sjmatthew 	nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD);
21973800fc35Sjmatthew 
21983800fc35Sjmatthew 	if (flags != 0)
21993800fc35Sjmatthew 		goto failed;
22003800fc35Sjmatthew 
22013800fc35Sjmatthew 	health = NVME_DMA_KVA(mem);
22023800fc35Sjmatthew 	cw = health->critical_warning;
22033800fc35Sjmatthew 
22043800fc35Sjmatthew 	sc->sc_temp_sensor.status = (cw & NVM_HEALTH_CW_TEMP) ?
22053800fc35Sjmatthew 	    SENSOR_S_CRIT : SENSOR_S_OK;
22063800fc35Sjmatthew 	temp = letoh16(health->temperature);
22073800fc35Sjmatthew 	sc->sc_temp_sensor.value = (temp * 1000000) + 150000;
22083800fc35Sjmatthew 
22093800fc35Sjmatthew 	sc->sc_spare_sensor.status = (cw & NVM_HEALTH_CW_SPARE) ?
22103800fc35Sjmatthew 	    SENSOR_S_CRIT : SENSOR_S_OK;
22113800fc35Sjmatthew 	sc->sc_spare_sensor.value = health->avail_spare * 1000;
22123800fc35Sjmatthew 
22133800fc35Sjmatthew 	sc->sc_usage_sensor.status = SENSOR_S_OK;
22143800fc35Sjmatthew 	sc->sc_usage_sensor.value = health->percent_used * 1000;
22153800fc35Sjmatthew 	goto done;
22163800fc35Sjmatthew 
22173800fc35Sjmatthew  failed:
22183800fc35Sjmatthew 	sc->sc_temp_sensor.status = SENSOR_S_UNKNOWN;
22193800fc35Sjmatthew 	sc->sc_usage_sensor.status = SENSOR_S_UNKNOWN;
22203800fc35Sjmatthew 	sc->sc_spare_sensor.status = SENSOR_S_UNKNOWN;
22213800fc35Sjmatthew  done:
22223800fc35Sjmatthew 	if (mem != NULL)
22233800fc35Sjmatthew 		nvme_dmamem_free(sc, mem);
22243800fc35Sjmatthew 	if (ccb != NULL)
22253800fc35Sjmatthew 		nvme_ccb_put(sc, ccb);
22263800fc35Sjmatthew }
22273800fc35Sjmatthew #endif /* SMALL_KERNEL */
2228