1*006a6769Sriastradh /* $NetBSD: nvme.c,v 1.69 2024/03/11 21:10:46 riastradh Exp $ */
28b5163f0Snonaka /* $OpenBSD: nvme.c,v 1.49 2016/04/18 05:59:50 dlg Exp $ */
38b5163f0Snonaka
48b5163f0Snonaka /*
58b5163f0Snonaka * Copyright (c) 2014 David Gwynne <dlg@openbsd.org>
68b5163f0Snonaka *
78b5163f0Snonaka * Permission to use, copy, modify, and distribute this software for any
88b5163f0Snonaka * purpose with or without fee is hereby granted, provided that the above
98b5163f0Snonaka * copyright notice and this permission notice appear in all copies.
108b5163f0Snonaka *
118b5163f0Snonaka * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
128b5163f0Snonaka * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
138b5163f0Snonaka * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
148b5163f0Snonaka * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
158b5163f0Snonaka * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
168b5163f0Snonaka * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
178b5163f0Snonaka * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
188b5163f0Snonaka */
198b5163f0Snonaka
208b5163f0Snonaka #include <sys/cdefs.h>
21*006a6769Sriastradh __KERNEL_RCSID(0, "$NetBSD: nvme.c,v 1.69 2024/03/11 21:10:46 riastradh Exp $");
228b5163f0Snonaka
238b5163f0Snonaka #include <sys/param.h>
248b5163f0Snonaka #include <sys/systm.h>
258b5163f0Snonaka #include <sys/kernel.h>
268b5163f0Snonaka #include <sys/atomic.h>
278b5163f0Snonaka #include <sys/bus.h>
288b5163f0Snonaka #include <sys/buf.h>
29e7c0cc5dSnonaka #include <sys/conf.h>
308b5163f0Snonaka #include <sys/device.h>
318b5163f0Snonaka #include <sys/kmem.h>
328b5163f0Snonaka #include <sys/once.h>
33e7c0cc5dSnonaka #include <sys/proc.h>
348b5163f0Snonaka #include <sys/queue.h>
358b5163f0Snonaka #include <sys/mutex.h>
368b5163f0Snonaka
37e7c0cc5dSnonaka #include <uvm/uvm_extern.h>
38e7c0cc5dSnonaka
398b5163f0Snonaka #include <dev/ic/nvmereg.h>
408b5163f0Snonaka #include <dev/ic/nvmevar.h>
41e7c0cc5dSnonaka #include <dev/ic/nvmeio.h>
428b5163f0Snonaka
4382b8cabaSriastradh #include "ioconf.h"
442685996bSthorpej #include "locators.h"
4582b8cabaSriastradh
4645a08be1Snonaka #define B4_CHK_RDY_DELAY_MS 2300 /* workaround controller bug */
4745a08be1Snonaka
483a357b71Sjdolecek int nvme_adminq_size = 32;
49ef172b9fSjdolecek int nvme_ioq_size = 1024;
508b5163f0Snonaka
518b5163f0Snonaka static int nvme_print(void *, const char *);
528b5163f0Snonaka
538b5163f0Snonaka static int nvme_ready(struct nvme_softc *, uint32_t);
548b5163f0Snonaka static int nvme_enable(struct nvme_softc *, u_int);
558b5163f0Snonaka static int nvme_disable(struct nvme_softc *);
568b5163f0Snonaka static int nvme_shutdown(struct nvme_softc *);
578b5163f0Snonaka
58e24a9df7Sskrll uint32_t nvme_op_sq_enter(struct nvme_softc *,
59e24a9df7Sskrll struct nvme_queue *, struct nvme_ccb *);
60e24a9df7Sskrll void nvme_op_sq_leave(struct nvme_softc *,
61e24a9df7Sskrll struct nvme_queue *, struct nvme_ccb *);
62e24a9df7Sskrll uint32_t nvme_op_sq_enter_locked(struct nvme_softc *,
63e24a9df7Sskrll struct nvme_queue *, struct nvme_ccb *);
64e24a9df7Sskrll void nvme_op_sq_leave_locked(struct nvme_softc *,
65e24a9df7Sskrll struct nvme_queue *, struct nvme_ccb *);
66e24a9df7Sskrll
67e24a9df7Sskrll void nvme_op_cq_done(struct nvme_softc *,
68e24a9df7Sskrll struct nvme_queue *, struct nvme_ccb *);
69e24a9df7Sskrll
70e24a9df7Sskrll static const struct nvme_ops nvme_ops = {
71e24a9df7Sskrll .op_sq_enter = nvme_op_sq_enter,
72e24a9df7Sskrll .op_sq_leave = nvme_op_sq_leave,
73e24a9df7Sskrll .op_sq_enter_locked = nvme_op_sq_enter_locked,
74e24a9df7Sskrll .op_sq_leave_locked = nvme_op_sq_leave_locked,
75e24a9df7Sskrll
76e24a9df7Sskrll .op_cq_done = nvme_op_cq_done,
77e24a9df7Sskrll };
78e24a9df7Sskrll
798b5163f0Snonaka #ifdef NVME_DEBUG
808b5163f0Snonaka static void nvme_dumpregs(struct nvme_softc *);
818b5163f0Snonaka #endif
828b5163f0Snonaka static int nvme_identify(struct nvme_softc *, u_int);
838b5163f0Snonaka static void nvme_fill_identify(struct nvme_queue *, struct nvme_ccb *,
848b5163f0Snonaka void *);
858b5163f0Snonaka
86b5e5261cSjdolecek static int nvme_ccbs_alloc(struct nvme_queue *, uint16_t);
878b5163f0Snonaka static void nvme_ccbs_free(struct nvme_queue *);
888b5163f0Snonaka
898b5163f0Snonaka static struct nvme_ccb *
901bfffd71Sjdolecek nvme_ccb_get(struct nvme_queue *, bool);
91b4fea417Sjmcneill static struct nvme_ccb *
92b4fea417Sjmcneill nvme_ccb_get_bio(struct nvme_softc *, struct buf *,
93b4fea417Sjmcneill struct nvme_queue **);
948b5163f0Snonaka static void nvme_ccb_put(struct nvme_queue *, struct nvme_ccb *);
958b5163f0Snonaka
968b5163f0Snonaka static int nvme_poll(struct nvme_softc *, struct nvme_queue *,
978b5163f0Snonaka struct nvme_ccb *, void (*)(struct nvme_queue *,
98b8b74a3cSjdolecek struct nvme_ccb *, void *), int);
998b5163f0Snonaka static void nvme_poll_fill(struct nvme_queue *, struct nvme_ccb *, void *);
1008b5163f0Snonaka static void nvme_poll_done(struct nvme_queue *, struct nvme_ccb *,
1018b5163f0Snonaka struct nvme_cqe *);
1028b5163f0Snonaka static void nvme_sqe_fill(struct nvme_queue *, struct nvme_ccb *, void *);
1038b5163f0Snonaka static void nvme_empty_done(struct nvme_queue *, struct nvme_ccb *,
1048b5163f0Snonaka struct nvme_cqe *);
1058b5163f0Snonaka
1068b5163f0Snonaka static struct nvme_queue *
1078b5163f0Snonaka nvme_q_alloc(struct nvme_softc *, uint16_t, u_int, u_int);
1088b5163f0Snonaka static int nvme_q_create(struct nvme_softc *, struct nvme_queue *);
109a6e80066Sriastradh static void nvme_q_reset(struct nvme_softc *, struct nvme_queue *);
1108b5163f0Snonaka static int nvme_q_delete(struct nvme_softc *, struct nvme_queue *);
1118b5163f0Snonaka static void nvme_q_submit(struct nvme_softc *, struct nvme_queue *,
1128b5163f0Snonaka struct nvme_ccb *, void (*)(struct nvme_queue *,
1138b5163f0Snonaka struct nvme_ccb *, void *));
1148b5163f0Snonaka static int nvme_q_complete(struct nvme_softc *, struct nvme_queue *q);
1158b5163f0Snonaka static void nvme_q_free(struct nvme_softc *, struct nvme_queue *);
1161bfffd71Sjdolecek static void nvme_q_wait_complete(struct nvme_softc *, struct nvme_queue *,
1171bfffd71Sjdolecek bool (*)(void *), void *);
1188b5163f0Snonaka
1198b5163f0Snonaka static void nvme_ns_io_fill(struct nvme_queue *, struct nvme_ccb *,
1208b5163f0Snonaka void *);
1218b5163f0Snonaka static void nvme_ns_io_done(struct nvme_queue *, struct nvme_ccb *,
1228b5163f0Snonaka struct nvme_cqe *);
1238b5163f0Snonaka static void nvme_ns_sync_fill(struct nvme_queue *, struct nvme_ccb *,
1248b5163f0Snonaka void *);
1258b5163f0Snonaka static void nvme_ns_sync_done(struct nvme_queue *, struct nvme_ccb *,
1268b5163f0Snonaka struct nvme_cqe *);
1275df68754Sjdolecek static void nvme_getcache_fill(struct nvme_queue *, struct nvme_ccb *,
1285df68754Sjdolecek void *);
1295df68754Sjdolecek static void nvme_getcache_done(struct nvme_queue *, struct nvme_ccb *,
1305df68754Sjdolecek struct nvme_cqe *);
1318b5163f0Snonaka
132e7c0cc5dSnonaka static void nvme_pt_fill(struct nvme_queue *, struct nvme_ccb *,
133e7c0cc5dSnonaka void *);
134e7c0cc5dSnonaka static void nvme_pt_done(struct nvme_queue *, struct nvme_ccb *,
135e7c0cc5dSnonaka struct nvme_cqe *);
136e7c0cc5dSnonaka static int nvme_command_passthrough(struct nvme_softc *,
1372437fdbaSmlelstv struct nvme_pt_command *, uint32_t, struct lwp *, bool);
138e7c0cc5dSnonaka
13911d4ba74Snonaka static int nvme_set_number_of_queues(struct nvme_softc *, u_int, u_int *,
14011d4ba74Snonaka u_int *);
1415bfb6e7eSnonaka
142b8b74a3cSjdolecek #define NVME_TIMO_QOP 5 /* queue create and delete timeout */
143b8b74a3cSjdolecek #define NVME_TIMO_IDENT 10 /* probe identify timeout */
144b8b74a3cSjdolecek #define NVME_TIMO_PT -1 /* passthrough cmd timeout */
145471f5c28Sjdolecek #define NVME_TIMO_SY 60 /* sync cache timeout */
146b8b74a3cSjdolecek
1470fb17da2Snonaka /*
1480fb17da2Snonaka * Some controllers, at least Apple NVMe, always require split
1490fb17da2Snonaka * transfers, so don't use bus_space_{read,write}_8() on LP64.
1500fb17da2Snonaka */
151e24a9df7Sskrll uint64_t
nvme_read8(struct nvme_softc * sc,bus_size_t r)1528b5163f0Snonaka nvme_read8(struct nvme_softc *sc, bus_size_t r)
1538b5163f0Snonaka {
1548b5163f0Snonaka uint64_t v;
1558b5163f0Snonaka uint32_t *a = (uint32_t *)&v;
1568b5163f0Snonaka
1578b5163f0Snonaka #if _BYTE_ORDER == _LITTLE_ENDIAN
1588b5163f0Snonaka a[0] = nvme_read4(sc, r);
1598b5163f0Snonaka a[1] = nvme_read4(sc, r + 4);
1608b5163f0Snonaka #else /* _BYTE_ORDER == _LITTLE_ENDIAN */
1618b5163f0Snonaka a[1] = nvme_read4(sc, r);
1628b5163f0Snonaka a[0] = nvme_read4(sc, r + 4);
1638b5163f0Snonaka #endif
1648b5163f0Snonaka
1658b5163f0Snonaka return v;
1668b5163f0Snonaka }
1678b5163f0Snonaka
168e24a9df7Sskrll void
nvme_write8(struct nvme_softc * sc,bus_size_t r,uint64_t v)1698b5163f0Snonaka nvme_write8(struct nvme_softc *sc, bus_size_t r, uint64_t v)
1708b5163f0Snonaka {
1718b5163f0Snonaka uint32_t *a = (uint32_t *)&v;
1728b5163f0Snonaka
1738b5163f0Snonaka #if _BYTE_ORDER == _LITTLE_ENDIAN
1748b5163f0Snonaka nvme_write4(sc, r, a[0]);
1758b5163f0Snonaka nvme_write4(sc, r + 4, a[1]);
1768b5163f0Snonaka #else /* _BYTE_ORDER == _LITTLE_ENDIAN */
1778b5163f0Snonaka nvme_write4(sc, r, a[1]);
1788b5163f0Snonaka nvme_write4(sc, r + 4, a[0]);
1798b5163f0Snonaka #endif
1808b5163f0Snonaka }
1818b5163f0Snonaka
1828b5163f0Snonaka #ifdef NVME_DEBUG
183da4d70cbSjdolecek static __used void
nvme_dumpregs(struct nvme_softc * sc)1848b5163f0Snonaka nvme_dumpregs(struct nvme_softc *sc)
1858b5163f0Snonaka {
1868b5163f0Snonaka uint64_t r8;
1878b5163f0Snonaka uint32_t r4;
1888b5163f0Snonaka
1898b5163f0Snonaka #define DEVNAME(_sc) device_xname((_sc)->sc_dev)
1908b5163f0Snonaka r8 = nvme_read8(sc, NVME_CAP);
1914549505dSjdolecek printf("%s: cap 0x%016"PRIx64"\n", DEVNAME(sc), nvme_read8(sc, NVME_CAP));
1928b5163f0Snonaka printf("%s: mpsmax %u (%u)\n", DEVNAME(sc),
1938b5163f0Snonaka (u_int)NVME_CAP_MPSMAX(r8), (1 << NVME_CAP_MPSMAX(r8)));
1948b5163f0Snonaka printf("%s: mpsmin %u (%u)\n", DEVNAME(sc),
1958b5163f0Snonaka (u_int)NVME_CAP_MPSMIN(r8), (1 << NVME_CAP_MPSMIN(r8)));
1964549505dSjdolecek printf("%s: css %"PRIu64"\n", DEVNAME(sc), NVME_CAP_CSS(r8));
1974549505dSjdolecek printf("%s: nssrs %"PRIu64"\n", DEVNAME(sc), NVME_CAP_NSSRS(r8));
1984549505dSjdolecek printf("%s: dstrd %"PRIu64"\n", DEVNAME(sc), NVME_CAP_DSTRD(r8));
1994549505dSjdolecek printf("%s: to %"PRIu64" msec\n", DEVNAME(sc), NVME_CAP_TO(r8));
2004549505dSjdolecek printf("%s: ams %"PRIu64"\n", DEVNAME(sc), NVME_CAP_AMS(r8));
2014549505dSjdolecek printf("%s: cqr %"PRIu64"\n", DEVNAME(sc), NVME_CAP_CQR(r8));
2024549505dSjdolecek printf("%s: mqes %"PRIu64"\n", DEVNAME(sc), NVME_CAP_MQES(r8));
2038b5163f0Snonaka
2048b5163f0Snonaka printf("%s: vs 0x%04x\n", DEVNAME(sc), nvme_read4(sc, NVME_VS));
2058b5163f0Snonaka
2068b5163f0Snonaka r4 = nvme_read4(sc, NVME_CC);
2078b5163f0Snonaka printf("%s: cc 0x%04x\n", DEVNAME(sc), r4);
2084549505dSjdolecek printf("%s: iocqes %u (%u)\n", DEVNAME(sc), NVME_CC_IOCQES_R(r4),
2094549505dSjdolecek (1 << NVME_CC_IOCQES_R(r4)));
2104549505dSjdolecek printf("%s: iosqes %u (%u)\n", DEVNAME(sc), NVME_CC_IOSQES_R(r4),
2114549505dSjdolecek (1 << NVME_CC_IOSQES_R(r4)));
2128b5163f0Snonaka printf("%s: shn %u\n", DEVNAME(sc), NVME_CC_SHN_R(r4));
2138b5163f0Snonaka printf("%s: ams %u\n", DEVNAME(sc), NVME_CC_AMS_R(r4));
2144549505dSjdolecek printf("%s: mps %u (%u)\n", DEVNAME(sc), NVME_CC_MPS_R(r4),
2154549505dSjdolecek (1 << NVME_CC_MPS_R(r4)));
2168b5163f0Snonaka printf("%s: css %u\n", DEVNAME(sc), NVME_CC_CSS_R(r4));
217da4d70cbSjdolecek printf("%s: en %u\n", DEVNAME(sc), ISSET(r4, NVME_CC_EN) ? 1 : 0);
2188b5163f0Snonaka
2194549505dSjdolecek r4 = nvme_read4(sc, NVME_CSTS);
2204549505dSjdolecek printf("%s: csts 0x%08x\n", DEVNAME(sc), r4);
2214549505dSjdolecek printf("%s: rdy %u\n", DEVNAME(sc), r4 & NVME_CSTS_RDY);
2224549505dSjdolecek printf("%s: cfs %u\n", DEVNAME(sc), r4 & NVME_CSTS_CFS);
2234549505dSjdolecek printf("%s: shst %x\n", DEVNAME(sc), r4 & NVME_CSTS_SHST_MASK);
2244549505dSjdolecek
2254549505dSjdolecek r4 = nvme_read4(sc, NVME_AQA);
2264549505dSjdolecek printf("%s: aqa 0x%08x\n", DEVNAME(sc), r4);
2274549505dSjdolecek printf("%s: acqs %u\n", DEVNAME(sc), NVME_AQA_ACQS_R(r4));
2284549505dSjdolecek printf("%s: asqs %u\n", DEVNAME(sc), NVME_AQA_ASQS_R(r4));
2294549505dSjdolecek
2304549505dSjdolecek printf("%s: asq 0x%016"PRIx64"\n", DEVNAME(sc), nvme_read8(sc, NVME_ASQ));
2314549505dSjdolecek printf("%s: acq 0x%016"PRIx64"\n", DEVNAME(sc), nvme_read8(sc, NVME_ACQ));
2328b5163f0Snonaka #undef DEVNAME
2338b5163f0Snonaka }
2348b5163f0Snonaka #endif /* NVME_DEBUG */
2358b5163f0Snonaka
2368b5163f0Snonaka static int
nvme_ready(struct nvme_softc * sc,uint32_t rdy)2378b5163f0Snonaka nvme_ready(struct nvme_softc *sc, uint32_t rdy)
2388b5163f0Snonaka {
2398b5163f0Snonaka u_int i = 0;
2408b5163f0Snonaka
2418b5163f0Snonaka while ((nvme_read4(sc, NVME_CSTS) & NVME_CSTS_RDY) != rdy) {
2428b5163f0Snonaka if (i++ > sc->sc_rdy_to)
2434549505dSjdolecek return ENXIO;
2448b5163f0Snonaka
2458b5163f0Snonaka delay(1000);
2468b5163f0Snonaka nvme_barrier(sc, NVME_CSTS, 4, BUS_SPACE_BARRIER_READ);
2478b5163f0Snonaka }
2488b5163f0Snonaka
2498b5163f0Snonaka return 0;
2508b5163f0Snonaka }
2518b5163f0Snonaka
2528b5163f0Snonaka static int
nvme_enable(struct nvme_softc * sc,u_int mps)2538b5163f0Snonaka nvme_enable(struct nvme_softc *sc, u_int mps)
2548b5163f0Snonaka {
2554549505dSjdolecek uint32_t cc, csts;
25645a08be1Snonaka int error;
2578b5163f0Snonaka
2588b5163f0Snonaka cc = nvme_read4(sc, NVME_CC);
2594549505dSjdolecek csts = nvme_read4(sc, NVME_CSTS);
2604549505dSjdolecek
26145a08be1Snonaka /*
26245a08be1Snonaka * See note in nvme_disable. Short circuit if we're already enabled.
26345a08be1Snonaka */
264b8b74a3cSjdolecek if (ISSET(cc, NVME_CC_EN)) {
2654549505dSjdolecek if (ISSET(csts, NVME_CSTS_RDY))
26645a08be1Snonaka return 0;
2674549505dSjdolecek
2684549505dSjdolecek goto waitready;
26945a08be1Snonaka } else {
27045a08be1Snonaka /* EN == 0 already wait for RDY == 0 or fail */
27145a08be1Snonaka error = nvme_ready(sc, 0);
27245a08be1Snonaka if (error)
27345a08be1Snonaka return error;
274b8b74a3cSjdolecek }
2758b5163f0Snonaka
276e24a9df7Sskrll if (sc->sc_ops->op_enable != NULL)
277e24a9df7Sskrll sc->sc_ops->op_enable(sc);
278e24a9df7Sskrll
2794549505dSjdolecek nvme_write8(sc, NVME_ASQ, NVME_DMA_DVA(sc->sc_admin_q->q_sq_dmamem));
2804549505dSjdolecek nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE);
2814549505dSjdolecek delay(5000);
2824549505dSjdolecek nvme_write8(sc, NVME_ACQ, NVME_DMA_DVA(sc->sc_admin_q->q_cq_dmamem));
2834549505dSjdolecek nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE);
2844549505dSjdolecek delay(5000);
2854549505dSjdolecek
2868b5163f0Snonaka nvme_write4(sc, NVME_AQA, NVME_AQA_ACQS(sc->sc_admin_q->q_entries) |
2878b5163f0Snonaka NVME_AQA_ASQS(sc->sc_admin_q->q_entries));
2888b5163f0Snonaka nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_WRITE);
2894549505dSjdolecek delay(5000);
2908b5163f0Snonaka
2918b5163f0Snonaka CLR(cc, NVME_CC_IOCQES_MASK | NVME_CC_IOSQES_MASK | NVME_CC_SHN_MASK |
2928b5163f0Snonaka NVME_CC_AMS_MASK | NVME_CC_MPS_MASK | NVME_CC_CSS_MASK);
2938b5163f0Snonaka SET(cc, NVME_CC_IOSQES(ffs(64) - 1) | NVME_CC_IOCQES(ffs(16) - 1));
2948b5163f0Snonaka SET(cc, NVME_CC_SHN(NVME_CC_SHN_NONE));
2958b5163f0Snonaka SET(cc, NVME_CC_CSS(NVME_CC_CSS_NVM));
2968b5163f0Snonaka SET(cc, NVME_CC_AMS(NVME_CC_AMS_RR));
2978b5163f0Snonaka SET(cc, NVME_CC_MPS(mps));
2988b5163f0Snonaka SET(cc, NVME_CC_EN);
2998b5163f0Snonaka
3008b5163f0Snonaka nvme_write4(sc, NVME_CC, cc);
3018b5163f0Snonaka nvme_barrier(sc, 0, sc->sc_ios,
3028b5163f0Snonaka BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
3038b5163f0Snonaka
3044549505dSjdolecek waitready:
3058b5163f0Snonaka return nvme_ready(sc, NVME_CSTS_RDY);
3068b5163f0Snonaka }
3078b5163f0Snonaka
3088b5163f0Snonaka static int
nvme_disable(struct nvme_softc * sc)3098b5163f0Snonaka nvme_disable(struct nvme_softc *sc)
3108b5163f0Snonaka {
3118b5163f0Snonaka uint32_t cc, csts;
31245a08be1Snonaka int error;
3138b5163f0Snonaka
3148b5163f0Snonaka cc = nvme_read4(sc, NVME_CC);
3158b5163f0Snonaka csts = nvme_read4(sc, NVME_CSTS);
3164549505dSjdolecek
31745a08be1Snonaka /*
31845a08be1Snonaka * Per 3.1.5 in NVME 1.3 spec, transitioning CC.EN from 0 to 1
31945a08be1Snonaka * when CSTS.RDY is 1 or transitioning CC.EN from 1 to 0 when
32045a08be1Snonaka * CSTS.RDY is 0 "has undefined results" So make sure that CSTS.RDY
32145a08be1Snonaka * isn't the desired value. Short circuit if we're already disabled.
32245a08be1Snonaka */
32345a08be1Snonaka if (ISSET(cc, NVME_CC_EN)) {
32445a08be1Snonaka if (!ISSET(csts, NVME_CSTS_RDY)) {
32545a08be1Snonaka /* EN == 1, wait for RDY == 1 or fail */
32645a08be1Snonaka error = nvme_ready(sc, NVME_CSTS_RDY);
32745a08be1Snonaka if (error)
32845a08be1Snonaka return error;
32945a08be1Snonaka }
33045a08be1Snonaka } else {
33145a08be1Snonaka /* EN == 0 already wait for RDY == 0 */
33245a08be1Snonaka if (!ISSET(csts, NVME_CSTS_RDY))
33345a08be1Snonaka return 0;
33445a08be1Snonaka
33545a08be1Snonaka goto waitready;
33645a08be1Snonaka }
3378b5163f0Snonaka
3388b5163f0Snonaka CLR(cc, NVME_CC_EN);
3398b5163f0Snonaka nvme_write4(sc, NVME_CC, cc);
3404549505dSjdolecek nvme_barrier(sc, 0, sc->sc_ios, BUS_SPACE_BARRIER_READ);
3414549505dSjdolecek
34245a08be1Snonaka /*
34345a08be1Snonaka * Some drives have issues with accessing the mmio after we disable,
34445a08be1Snonaka * so delay for a bit after we write the bit to cope with these issues.
34545a08be1Snonaka */
34645a08be1Snonaka if (ISSET(sc->sc_quirks, NVME_QUIRK_DELAY_B4_CHK_RDY))
34745a08be1Snonaka delay(B4_CHK_RDY_DELAY_MS);
3488b5163f0Snonaka
34945a08be1Snonaka waitready:
3508b5163f0Snonaka return nvme_ready(sc, 0);
3518b5163f0Snonaka }
3528b5163f0Snonaka
3538b5163f0Snonaka int
nvme_attach(struct nvme_softc * sc)3548b5163f0Snonaka nvme_attach(struct nvme_softc *sc)
3558b5163f0Snonaka {
3568b5163f0Snonaka uint64_t cap;
3578b5163f0Snonaka uint32_t reg;
3588b5163f0Snonaka u_int mps = PAGE_SHIFT;
35911d4ba74Snonaka u_int ncq, nsq;
360b5e5261cSjdolecek uint16_t adminq_entries = nvme_adminq_size;
361b5e5261cSjdolecek uint16_t ioq_entries = nvme_ioq_size;
3628b5163f0Snonaka int i;
3638b5163f0Snonaka
364e24a9df7Sskrll if (sc->sc_ops == NULL)
365e24a9df7Sskrll sc->sc_ops = &nvme_ops;
366e24a9df7Sskrll
3678b5163f0Snonaka reg = nvme_read4(sc, NVME_VS);
3688b5163f0Snonaka if (reg == 0xffffffff) {
3698b5163f0Snonaka aprint_error_dev(sc->sc_dev, "invalid mapping\n");
3708b5163f0Snonaka return 1;
3718b5163f0Snonaka }
3728b5163f0Snonaka
37374b49282Snonaka if (NVME_VS_TER(reg) == 0)
37474b49282Snonaka aprint_normal_dev(sc->sc_dev, "NVMe %d.%d\n", NVME_VS_MJR(reg),
37574b49282Snonaka NVME_VS_MNR(reg));
37674b49282Snonaka else
37774b49282Snonaka aprint_normal_dev(sc->sc_dev, "NVMe %d.%d.%d\n", NVME_VS_MJR(reg),
37874b49282Snonaka NVME_VS_MNR(reg), NVME_VS_TER(reg));
3798b5163f0Snonaka
3808b5163f0Snonaka cap = nvme_read8(sc, NVME_CAP);
381a6e80066Sriastradh sc->sc_dstrd = NVME_CAP_DSTRD(cap);
3828b5163f0Snonaka if (NVME_CAP_MPSMIN(cap) > PAGE_SHIFT) {
3838b5163f0Snonaka aprint_error_dev(sc->sc_dev, "NVMe minimum page size %u "
3848b5163f0Snonaka "is greater than CPU page size %u\n",
3858b5163f0Snonaka 1 << NVME_CAP_MPSMIN(cap), 1 << PAGE_SHIFT);
3868b5163f0Snonaka return 1;
3878b5163f0Snonaka }
3888b5163f0Snonaka if (NVME_CAP_MPSMAX(cap) < mps)
3898b5163f0Snonaka mps = NVME_CAP_MPSMAX(cap);
3902f979a4fSnonaka if (ioq_entries > NVME_CAP_MQES(cap))
3912f979a4fSnonaka ioq_entries = NVME_CAP_MQES(cap);
3928b5163f0Snonaka
3934549505dSjdolecek /* set initial values to be used for admin queue during probe */
3948b5163f0Snonaka sc->sc_rdy_to = NVME_CAP_TO(cap);
3958b5163f0Snonaka sc->sc_mps = 1 << mps;
3968b5163f0Snonaka sc->sc_mdts = MAXPHYS;
39704092511Smrg sc->sc_max_sgl = btoc(round_page(sc->sc_mdts));
3988b5163f0Snonaka
3998b5163f0Snonaka if (nvme_disable(sc) != 0) {
4008b5163f0Snonaka aprint_error_dev(sc->sc_dev, "unable to disable controller\n");
4018b5163f0Snonaka return 1;
4028b5163f0Snonaka }
4038b5163f0Snonaka
404a6e80066Sriastradh sc->sc_admin_q = nvme_q_alloc(sc, NVME_ADMIN_Q, adminq_entries,
405a6e80066Sriastradh sc->sc_dstrd);
4068b5163f0Snonaka if (sc->sc_admin_q == NULL) {
4078b5163f0Snonaka aprint_error_dev(sc->sc_dev,
4088b5163f0Snonaka "unable to allocate admin queue\n");
4098b5163f0Snonaka return 1;
4108b5163f0Snonaka }
4118b5163f0Snonaka if (sc->sc_intr_establish(sc, NVME_ADMIN_Q, sc->sc_admin_q))
4128b5163f0Snonaka goto free_admin_q;
4138b5163f0Snonaka
4148b5163f0Snonaka if (nvme_enable(sc, mps) != 0) {
4158b5163f0Snonaka aprint_error_dev(sc->sc_dev, "unable to enable controller\n");
4168b5163f0Snonaka goto disestablish_admin_q;
4178b5163f0Snonaka }
4188b5163f0Snonaka
4198b5163f0Snonaka if (nvme_identify(sc, NVME_CAP_MPSMIN(cap)) != 0) {
4208b5163f0Snonaka aprint_error_dev(sc->sc_dev, "unable to identify controller\n");
4218b5163f0Snonaka goto disable;
4228b5163f0Snonaka }
423067319d1Snonaka if (sc->sc_nn == 0) {
424067319d1Snonaka aprint_error_dev(sc->sc_dev, "namespace not found\n");
425067319d1Snonaka goto disable;
426067319d1Snonaka }
4278b5163f0Snonaka
4288b5163f0Snonaka /* we know how big things are now */
4298b5163f0Snonaka sc->sc_max_sgl = sc->sc_mdts / sc->sc_mps;
4308b5163f0Snonaka
4318b5163f0Snonaka /* reallocate ccbs of admin queue with new max sgl. */
4328b5163f0Snonaka nvme_ccbs_free(sc->sc_admin_q);
4338b5163f0Snonaka nvme_ccbs_alloc(sc->sc_admin_q, sc->sc_admin_q->q_entries);
4348b5163f0Snonaka
4355bfb6e7eSnonaka if (sc->sc_use_mq) {
4365bfb6e7eSnonaka /* Limit the number of queues to the number allocated in HW */
43711d4ba74Snonaka if (nvme_set_number_of_queues(sc, sc->sc_nq, &ncq, &nsq) != 0) {
4385bfb6e7eSnonaka aprint_error_dev(sc->sc_dev,
4395bfb6e7eSnonaka "unable to get number of queues\n");
4405bfb6e7eSnonaka goto disable;
4415bfb6e7eSnonaka }
44211d4ba74Snonaka if (sc->sc_nq > ncq)
44311d4ba74Snonaka sc->sc_nq = ncq;
44411d4ba74Snonaka if (sc->sc_nq > nsq)
44511d4ba74Snonaka sc->sc_nq = nsq;
4465bfb6e7eSnonaka }
4475bfb6e7eSnonaka
4488b5163f0Snonaka sc->sc_q = kmem_zalloc(sizeof(*sc->sc_q) * sc->sc_nq, KM_SLEEP);
4498b5163f0Snonaka for (i = 0; i < sc->sc_nq; i++) {
450a6e80066Sriastradh sc->sc_q[i] = nvme_q_alloc(sc, i + 1, ioq_entries,
451a6e80066Sriastradh sc->sc_dstrd);
4528b5163f0Snonaka if (sc->sc_q[i] == NULL) {
4538b5163f0Snonaka aprint_error_dev(sc->sc_dev,
4548b5163f0Snonaka "unable to allocate io queue\n");
4558b5163f0Snonaka goto free_q;
4568b5163f0Snonaka }
4578b5163f0Snonaka if (nvme_q_create(sc, sc->sc_q[i]) != 0) {
4588b5163f0Snonaka aprint_error_dev(sc->sc_dev,
4598b5163f0Snonaka "unable to create io queue\n");
4608b5163f0Snonaka nvme_q_free(sc, sc->sc_q[i]);
4618b5163f0Snonaka goto free_q;
4628b5163f0Snonaka }
4638b5163f0Snonaka }
4648b5163f0Snonaka
4658b5163f0Snonaka if (!sc->sc_use_mq)
4668b5163f0Snonaka nvme_write4(sc, NVME_INTMC, 1);
4678b5163f0Snonaka
468ef172b9fSjdolecek /* probe subdevices */
4698b5163f0Snonaka sc->sc_namespaces = kmem_zalloc(sizeof(*sc->sc_namespaces) * sc->sc_nn,
4708b5163f0Snonaka KM_SLEEP);
4712685996bSthorpej nvme_rescan(sc->sc_dev, NULL, NULL);
4728b5163f0Snonaka
4738b5163f0Snonaka return 0;
4748b5163f0Snonaka
4758b5163f0Snonaka free_q:
4768b5163f0Snonaka while (--i >= 0) {
4778b5163f0Snonaka nvme_q_delete(sc, sc->sc_q[i]);
4788b5163f0Snonaka nvme_q_free(sc, sc->sc_q[i]);
4798b5163f0Snonaka }
4808b5163f0Snonaka disable:
4818b5163f0Snonaka nvme_disable(sc);
4828b5163f0Snonaka disestablish_admin_q:
4838b5163f0Snonaka sc->sc_intr_disestablish(sc, NVME_ADMIN_Q);
4848b5163f0Snonaka free_admin_q:
4858b5163f0Snonaka nvme_q_free(sc, sc->sc_admin_q);
4868b5163f0Snonaka
4878b5163f0Snonaka return 1;
4888b5163f0Snonaka }
4898b5163f0Snonaka
490916bdfa5Spgoyette int
nvme_rescan(device_t self,const char * ifattr,const int * locs)4912685996bSthorpej nvme_rescan(device_t self, const char *ifattr, const int *locs)
492916bdfa5Spgoyette {
493916bdfa5Spgoyette struct nvme_softc *sc = device_private(self);
494916bdfa5Spgoyette struct nvme_attach_args naa;
4952f7af14dSkardel struct nvm_namespace_format *f;
4962f7af14dSkardel struct nvme_namespace *ns;
4972f979a4fSnonaka uint64_t cap;
4982f979a4fSnonaka int ioq_entries = nvme_ioq_size;
4992685996bSthorpej int i, mlocs[NVMECF_NLOCS];
5002f7af14dSkardel int error;
5012f979a4fSnonaka
5022f979a4fSnonaka cap = nvme_read8(sc, NVME_CAP);
5032f979a4fSnonaka if (ioq_entries > NVME_CAP_MQES(cap))
5042f979a4fSnonaka ioq_entries = NVME_CAP_MQES(cap);
505916bdfa5Spgoyette
5062f7af14dSkardel for (i = 1; i <= sc->sc_nn; i++) {
5072f7af14dSkardel if (sc->sc_namespaces[i - 1].dev)
508916bdfa5Spgoyette continue;
5092f7af14dSkardel
5102f7af14dSkardel /* identify to check for availability */
5112f7af14dSkardel error = nvme_ns_identify(sc, i);
5122f7af14dSkardel if (error) {
5132f7af14dSkardel aprint_error_dev(self, "couldn't identify namespace #%d\n", i);
5142f7af14dSkardel continue;
5152f7af14dSkardel }
5162f7af14dSkardel
5172f7af14dSkardel ns = nvme_ns_get(sc, i);
5182f7af14dSkardel KASSERT(ns);
5192f7af14dSkardel
5202f7af14dSkardel f = &ns->ident->lbaf[NVME_ID_NS_FLBAS(ns->ident->flbas)];
5212f7af14dSkardel
5222f7af14dSkardel /*
5232f7af14dSkardel * NVME1.0e 6.11 Identify command
5242f7af14dSkardel *
5252f7af14dSkardel * LBADS values smaller than 9 are not supported, a value
5262f7af14dSkardel * of zero means that the format is not used.
5272f7af14dSkardel */
5282f7af14dSkardel if (f->lbads < 9) {
5292f7af14dSkardel if (f->lbads > 0)
5302f7af14dSkardel aprint_error_dev(self,
5312f7af14dSkardel "unsupported logical data size %u\n", f->lbads);
5322f7af14dSkardel continue;
5332f7af14dSkardel }
5342f7af14dSkardel
5352685996bSthorpej mlocs[NVMECF_NSID] = i;
5362685996bSthorpej
537916bdfa5Spgoyette memset(&naa, 0, sizeof(naa));
5382f7af14dSkardel naa.naa_nsid = i;
539a2a95605Sjdolecek naa.naa_qentries = (ioq_entries - 1) * sc->sc_nq;
540a2a95605Sjdolecek naa.naa_maxphys = sc->sc_mdts;
5419653889bSmlelstv naa.naa_typename = sc->sc_modelname;
5422685996bSthorpej sc->sc_namespaces[i - 1].dev =
5432685996bSthorpej config_found(sc->sc_dev, &naa, nvme_print,
544c7fb772bSthorpej CFARGS(.submatch = config_stdsubmatch,
545c7fb772bSthorpej .locators = mlocs));
546916bdfa5Spgoyette }
547916bdfa5Spgoyette return 0;
548916bdfa5Spgoyette }
549916bdfa5Spgoyette
5508b5163f0Snonaka static int
nvme_print(void * aux,const char * pnp)5518b5163f0Snonaka nvme_print(void *aux, const char *pnp)
5528b5163f0Snonaka {
5538b5163f0Snonaka struct nvme_attach_args *naa = aux;
5548b5163f0Snonaka
5558b5163f0Snonaka if (pnp)
55649d5f035Sjdolecek aprint_normal("ld at %s", pnp);
5578b5163f0Snonaka
5588b5163f0Snonaka if (naa->naa_nsid > 0)
5598b5163f0Snonaka aprint_normal(" nsid %d", naa->naa_nsid);
5608b5163f0Snonaka
5618b5163f0Snonaka return UNCONF;
5628b5163f0Snonaka }
5638b5163f0Snonaka
5648b5163f0Snonaka int
nvme_detach(struct nvme_softc * sc,int flags)5658b5163f0Snonaka nvme_detach(struct nvme_softc *sc, int flags)
5668b5163f0Snonaka {
5678b5163f0Snonaka int i, error;
5688b5163f0Snonaka
5698b5163f0Snonaka error = config_detach_children(sc->sc_dev, flags);
5708b5163f0Snonaka if (error)
5718b5163f0Snonaka return error;
5728b5163f0Snonaka
5738b5163f0Snonaka error = nvme_shutdown(sc);
5748b5163f0Snonaka if (error)
5758b5163f0Snonaka return error;
5768b5163f0Snonaka
577ef172b9fSjdolecek /* from now on we are committed to detach, following will never fail */
5788b5163f0Snonaka for (i = 0; i < sc->sc_nq; i++)
5798b5163f0Snonaka nvme_q_free(sc, sc->sc_q[i]);
5808b5163f0Snonaka kmem_free(sc->sc_q, sizeof(*sc->sc_q) * sc->sc_nq);
5818b5163f0Snonaka nvme_q_free(sc, sc->sc_admin_q);
5828b5163f0Snonaka
5838b5163f0Snonaka return 0;
5848b5163f0Snonaka }
5858b5163f0Snonaka
586a6e80066Sriastradh int
nvme_suspend(struct nvme_softc * sc)587a6e80066Sriastradh nvme_suspend(struct nvme_softc *sc)
588a6e80066Sriastradh {
589a6e80066Sriastradh
590a6e80066Sriastradh return nvme_shutdown(sc);
591a6e80066Sriastradh }
592a6e80066Sriastradh
593a6e80066Sriastradh int
nvme_resume(struct nvme_softc * sc)594a6e80066Sriastradh nvme_resume(struct nvme_softc *sc)
595a6e80066Sriastradh {
596a6e80066Sriastradh int i, error;
597a6e80066Sriastradh
598a6e80066Sriastradh error = nvme_disable(sc);
599a6e80066Sriastradh if (error) {
600a6e80066Sriastradh device_printf(sc->sc_dev, "unable to disable controller\n");
601a6e80066Sriastradh return error;
602a6e80066Sriastradh }
603a6e80066Sriastradh
604a6e80066Sriastradh nvme_q_reset(sc, sc->sc_admin_q);
605*006a6769Sriastradh if (sc->sc_intr_establish(sc, NVME_ADMIN_Q, sc->sc_admin_q)) {
606*006a6769Sriastradh error = EIO;
607*006a6769Sriastradh device_printf(sc->sc_dev, "unable to establish admin q\n");
608*006a6769Sriastradh goto disable;
609*006a6769Sriastradh }
610a6e80066Sriastradh
611a6e80066Sriastradh error = nvme_enable(sc, ffs(sc->sc_mps) - 1);
612a6e80066Sriastradh if (error) {
613a6e80066Sriastradh device_printf(sc->sc_dev, "unable to enable controller\n");
614a6e80066Sriastradh return error;
615a6e80066Sriastradh }
616a6e80066Sriastradh
617a6e80066Sriastradh for (i = 0; i < sc->sc_nq; i++) {
618a97f000bSriastradh nvme_q_reset(sc, sc->sc_q[i]);
619a6e80066Sriastradh if (nvme_q_create(sc, sc->sc_q[i]) != 0) {
620a6e80066Sriastradh error = EIO;
621a6e80066Sriastradh device_printf(sc->sc_dev, "unable to create io q %d"
622a6e80066Sriastradh "\n", i);
623a97f000bSriastradh goto disable;
624a6e80066Sriastradh }
625a6e80066Sriastradh }
626a6e80066Sriastradh
627639abfc2Smrg if (!sc->sc_use_mq)
628a6e80066Sriastradh nvme_write4(sc, NVME_INTMC, 1);
629a6e80066Sriastradh
630a6e80066Sriastradh return 0;
631a6e80066Sriastradh
632a6e80066Sriastradh disable:
633a6e80066Sriastradh (void)nvme_disable(sc);
634a6e80066Sriastradh
635a6e80066Sriastradh return error;
636a6e80066Sriastradh }
637a6e80066Sriastradh
6386517cc8aSriastradh static int
nvme_shutdown(struct nvme_softc * sc)6398b5163f0Snonaka nvme_shutdown(struct nvme_softc *sc)
6408b5163f0Snonaka {
6418b5163f0Snonaka uint32_t cc, csts;
6428b5163f0Snonaka bool disabled = false;
6438b5163f0Snonaka int i;
6448b5163f0Snonaka
6458b5163f0Snonaka if (!sc->sc_use_mq)
6468b5163f0Snonaka nvme_write4(sc, NVME_INTMS, 1);
6478b5163f0Snonaka
6488b5163f0Snonaka for (i = 0; i < sc->sc_nq; i++) {
6498b5163f0Snonaka if (nvme_q_delete(sc, sc->sc_q[i]) != 0) {
6508b5163f0Snonaka aprint_error_dev(sc->sc_dev,
6518b5163f0Snonaka "unable to delete io queue %d, disabling\n", i + 1);
6528b5163f0Snonaka disabled = true;
6538b5163f0Snonaka }
6548b5163f0Snonaka }
6558b5163f0Snonaka if (disabled)
6568b5163f0Snonaka goto disable;
6578b5163f0Snonaka
658*006a6769Sriastradh sc->sc_intr_disestablish(sc, NVME_ADMIN_Q);
659*006a6769Sriastradh
6608b5163f0Snonaka cc = nvme_read4(sc, NVME_CC);
6618b5163f0Snonaka CLR(cc, NVME_CC_SHN_MASK);
6628b5163f0Snonaka SET(cc, NVME_CC_SHN(NVME_CC_SHN_NORMAL));
6638b5163f0Snonaka nvme_write4(sc, NVME_CC, cc);
6648b5163f0Snonaka
6658b5163f0Snonaka for (i = 0; i < 4000; i++) {
6668b5163f0Snonaka nvme_barrier(sc, 0, sc->sc_ios,
6678b5163f0Snonaka BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
6688b5163f0Snonaka csts = nvme_read4(sc, NVME_CSTS);
6698b5163f0Snonaka if ((csts & NVME_CSTS_SHST_MASK) == NVME_CSTS_SHST_DONE)
6708b5163f0Snonaka return 0;
6718b5163f0Snonaka
6728b5163f0Snonaka delay(1000);
6738b5163f0Snonaka }
6748b5163f0Snonaka
6758b5163f0Snonaka aprint_error_dev(sc->sc_dev, "unable to shudown, disabling\n");
6768b5163f0Snonaka
6778b5163f0Snonaka disable:
6788b5163f0Snonaka nvme_disable(sc);
6798b5163f0Snonaka return 0;
6808b5163f0Snonaka }
6818b5163f0Snonaka
6828b5163f0Snonaka void
nvme_childdet(device_t self,device_t child)6838b5163f0Snonaka nvme_childdet(device_t self, device_t child)
6848b5163f0Snonaka {
6858b5163f0Snonaka struct nvme_softc *sc = device_private(self);
6868b5163f0Snonaka int i;
6878b5163f0Snonaka
6888b5163f0Snonaka for (i = 0; i < sc->sc_nn; i++) {
6898b5163f0Snonaka if (sc->sc_namespaces[i].dev == child) {
6908b5163f0Snonaka /* Already freed ns->ident. */
6918b5163f0Snonaka sc->sc_namespaces[i].dev = NULL;
6928b5163f0Snonaka break;
6938b5163f0Snonaka }
6948b5163f0Snonaka }
6958b5163f0Snonaka }
6968b5163f0Snonaka
6978b5163f0Snonaka int
nvme_ns_identify(struct nvme_softc * sc,uint16_t nsid)6988b5163f0Snonaka nvme_ns_identify(struct nvme_softc *sc, uint16_t nsid)
6998b5163f0Snonaka {
7008b5163f0Snonaka struct nvme_sqe sqe;
7018b5163f0Snonaka struct nvm_identify_namespace *identify;
702f4a9f41eSjdolecek struct nvme_dmamem *mem;
7038b5163f0Snonaka struct nvme_ccb *ccb;
7048b5163f0Snonaka struct nvme_namespace *ns;
705f4a9f41eSjdolecek int rv;
7068b5163f0Snonaka
7078b5163f0Snonaka KASSERT(nsid > 0);
7088b5163f0Snonaka
7093f3878ddSkardel ns = nvme_ns_get(sc, nsid);
7103f3878ddSkardel KASSERT(ns);
7113f3878ddSkardel
7123f3878ddSkardel if (ns->ident != NULL)
7133f3878ddSkardel return 0;
7143f3878ddSkardel
7151bfffd71Sjdolecek ccb = nvme_ccb_get(sc->sc_admin_q, false);
716ac7944cbSjdolecek KASSERT(ccb != NULL); /* it's a bug if we don't have spare ccb here */
7178b5163f0Snonaka
718f4a9f41eSjdolecek mem = nvme_dmamem_alloc(sc, sizeof(*identify));
7199ac12f16Schristos if (mem == NULL) {
7209ac12f16Schristos nvme_ccb_put(sc->sc_admin_q, ccb);
721f4a9f41eSjdolecek return ENOMEM;
7229ac12f16Schristos }
7238b5163f0Snonaka
7248b5163f0Snonaka memset(&sqe, 0, sizeof(sqe));
7258b5163f0Snonaka sqe.opcode = NVM_ADMIN_IDENTIFY;
7268b5163f0Snonaka htolem32(&sqe.nsid, nsid);
7278b5163f0Snonaka htolem64(&sqe.entry.prp[0], NVME_DMA_DVA(mem));
7288b5163f0Snonaka htolem32(&sqe.cdw10, 0);
7298b5163f0Snonaka
7308b5163f0Snonaka ccb->ccb_done = nvme_empty_done;
7318b5163f0Snonaka ccb->ccb_cookie = &sqe;
7328b5163f0Snonaka
7338b5163f0Snonaka nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD);
734f4a9f41eSjdolecek rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_IDENT);
7358b5163f0Snonaka nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD);
7368b5163f0Snonaka
7378b5163f0Snonaka nvme_ccb_put(sc->sc_admin_q, ccb);
7388b5163f0Snonaka
739f4a9f41eSjdolecek if (rv != 0) {
740f4a9f41eSjdolecek rv = EIO;
7418b5163f0Snonaka goto done;
7428b5163f0Snonaka }
7438b5163f0Snonaka
7448b5163f0Snonaka /* commit */
7458b5163f0Snonaka
7468b5163f0Snonaka identify = kmem_zalloc(sizeof(*identify), KM_SLEEP);
747f4a9f41eSjdolecek *identify = *((volatile struct nvm_identify_namespace *)NVME_DMA_KVA(mem));
7486ac76f5bSnonaka
7496ac76f5bSnonaka /* Convert data to host endian */
7506ac76f5bSnonaka nvme_identify_namespace_swapbytes(identify);
7518b5163f0Snonaka
7528b5163f0Snonaka ns->ident = identify;
7538b5163f0Snonaka
7548b5163f0Snonaka done:
755f4a9f41eSjdolecek nvme_dmamem_free(sc, mem);
7568b5163f0Snonaka
757f4a9f41eSjdolecek return rv;
7588b5163f0Snonaka }
7598b5163f0Snonaka
7608b5163f0Snonaka int
nvme_ns_dobio(struct nvme_softc * sc,uint16_t nsid,void * cookie,struct buf * bp,void * data,size_t datasize,int secsize,daddr_t blkno,int flags,nvme_nnc_done nnc_done)761ac7944cbSjdolecek nvme_ns_dobio(struct nvme_softc *sc, uint16_t nsid, void *cookie,
762ac7944cbSjdolecek struct buf *bp, void *data, size_t datasize,
763ac7944cbSjdolecek int secsize, daddr_t blkno, int flags, nvme_nnc_done nnc_done)
7648b5163f0Snonaka {
765b4fea417Sjmcneill struct nvme_queue *q;
7668b5163f0Snonaka struct nvme_ccb *ccb;
7678b5163f0Snonaka bus_dmamap_t dmap;
7688b5163f0Snonaka int i, error;
7698b5163f0Snonaka
770b4fea417Sjmcneill ccb = nvme_ccb_get_bio(sc, bp, &q);
7718b5163f0Snonaka if (ccb == NULL)
7728b5163f0Snonaka return EAGAIN;
7738b5163f0Snonaka
7748b5163f0Snonaka ccb->ccb_done = nvme_ns_io_done;
775ac7944cbSjdolecek ccb->ccb_cookie = cookie;
776ac7944cbSjdolecek
777ac7944cbSjdolecek /* namespace context */
778ac7944cbSjdolecek ccb->nnc_nsid = nsid;
779ac7944cbSjdolecek ccb->nnc_flags = flags;
780ac7944cbSjdolecek ccb->nnc_buf = bp;
781ac7944cbSjdolecek ccb->nnc_datasize = datasize;
782ac7944cbSjdolecek ccb->nnc_secsize = secsize;
783ac7944cbSjdolecek ccb->nnc_blkno = blkno;
784ac7944cbSjdolecek ccb->nnc_done = nnc_done;
7858b5163f0Snonaka
7868b5163f0Snonaka dmap = ccb->ccb_dmamap;
787ac7944cbSjdolecek error = bus_dmamap_load(sc->sc_dmat, dmap, data,
788ac7944cbSjdolecek datasize, NULL,
789ac7944cbSjdolecek (ISSET(flags, NVME_NS_CTX_F_POLL) ?
7908b5163f0Snonaka BUS_DMA_NOWAIT : BUS_DMA_WAITOK) |
791ac7944cbSjdolecek (ISSET(flags, NVME_NS_CTX_F_READ) ?
7928b5163f0Snonaka BUS_DMA_READ : BUS_DMA_WRITE));
7938b5163f0Snonaka if (error) {
7948b5163f0Snonaka nvme_ccb_put(q, ccb);
7958b5163f0Snonaka return error;
7968b5163f0Snonaka }
7978b5163f0Snonaka
7988b5163f0Snonaka bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
799ac7944cbSjdolecek ISSET(flags, NVME_NS_CTX_F_READ) ?
8008b5163f0Snonaka BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
8018b5163f0Snonaka
8028b5163f0Snonaka if (dmap->dm_nsegs > 2) {
8038b5163f0Snonaka for (i = 1; i < dmap->dm_nsegs; i++) {
8048b5163f0Snonaka htolem64(&ccb->ccb_prpl[i - 1],
8058b5163f0Snonaka dmap->dm_segs[i].ds_addr);
8068b5163f0Snonaka }
8078b5163f0Snonaka bus_dmamap_sync(sc->sc_dmat,
8088b5163f0Snonaka NVME_DMA_MAP(q->q_ccb_prpls),
8098b5163f0Snonaka ccb->ccb_prpl_off,
8101648ffa0Snonaka sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1),
8118b5163f0Snonaka BUS_DMASYNC_PREWRITE);
8128b5163f0Snonaka }
8138b5163f0Snonaka
814ac7944cbSjdolecek if (ISSET(flags, NVME_NS_CTX_F_POLL)) {
815b8b74a3cSjdolecek if (nvme_poll(sc, q, ccb, nvme_ns_io_fill, NVME_TIMO_PT) != 0)
8168b5163f0Snonaka return EIO;
8178b5163f0Snonaka return 0;
8188b5163f0Snonaka }
8198b5163f0Snonaka
8208b5163f0Snonaka nvme_q_submit(sc, q, ccb, nvme_ns_io_fill);
8218b5163f0Snonaka return 0;
8228b5163f0Snonaka }
8238b5163f0Snonaka
8248b5163f0Snonaka static void
nvme_ns_io_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)8258b5163f0Snonaka nvme_ns_io_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
8268b5163f0Snonaka {
8278b5163f0Snonaka struct nvme_sqe_io *sqe = slot;
8288b5163f0Snonaka bus_dmamap_t dmap = ccb->ccb_dmamap;
8298b5163f0Snonaka
830ac7944cbSjdolecek sqe->opcode = ISSET(ccb->nnc_flags, NVME_NS_CTX_F_READ) ?
8318b5163f0Snonaka NVM_CMD_READ : NVM_CMD_WRITE;
832ac7944cbSjdolecek htolem32(&sqe->nsid, ccb->nnc_nsid);
8338b5163f0Snonaka
8348b5163f0Snonaka htolem64(&sqe->entry.prp[0], dmap->dm_segs[0].ds_addr);
8358b5163f0Snonaka switch (dmap->dm_nsegs) {
8368b5163f0Snonaka case 1:
8378b5163f0Snonaka break;
8388b5163f0Snonaka case 2:
8398b5163f0Snonaka htolem64(&sqe->entry.prp[1], dmap->dm_segs[1].ds_addr);
8408b5163f0Snonaka break;
8418b5163f0Snonaka default:
8428b5163f0Snonaka /* the prp list is already set up and synced */
8438b5163f0Snonaka htolem64(&sqe->entry.prp[1], ccb->ccb_prpl_dva);
8448b5163f0Snonaka break;
8458b5163f0Snonaka }
8468b5163f0Snonaka
847ac7944cbSjdolecek htolem64(&sqe->slba, ccb->nnc_blkno);
848ac7944cbSjdolecek
8496801660cSjdolecek if (ISSET(ccb->nnc_flags, NVME_NS_CTX_F_FUA))
8506801660cSjdolecek htolem16(&sqe->ioflags, NVM_SQE_IO_FUA);
8516801660cSjdolecek
852ac7944cbSjdolecek /* guaranteed by upper layers, but check just in case */
853ac7944cbSjdolecek KASSERT((ccb->nnc_datasize % ccb->nnc_secsize) == 0);
854ac7944cbSjdolecek htolem16(&sqe->nlb, (ccb->nnc_datasize / ccb->nnc_secsize) - 1);
8558b5163f0Snonaka }
8568b5163f0Snonaka
8578b5163f0Snonaka static void
nvme_ns_io_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)8588b5163f0Snonaka nvme_ns_io_done(struct nvme_queue *q, struct nvme_ccb *ccb,
8598b5163f0Snonaka struct nvme_cqe *cqe)
8608b5163f0Snonaka {
8618b5163f0Snonaka struct nvme_softc *sc = q->q_sc;
8628b5163f0Snonaka bus_dmamap_t dmap = ccb->ccb_dmamap;
863ac7944cbSjdolecek void *nnc_cookie = ccb->ccb_cookie;
864ac7944cbSjdolecek nvme_nnc_done nnc_done = ccb->nnc_done;
865ac7944cbSjdolecek struct buf *bp = ccb->nnc_buf;
8668b5163f0Snonaka
8678b5163f0Snonaka if (dmap->dm_nsegs > 2) {
8688b5163f0Snonaka bus_dmamap_sync(sc->sc_dmat,
8698b5163f0Snonaka NVME_DMA_MAP(q->q_ccb_prpls),
8708b5163f0Snonaka ccb->ccb_prpl_off,
8711648ffa0Snonaka sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1),
8728b5163f0Snonaka BUS_DMASYNC_POSTWRITE);
8738b5163f0Snonaka }
8748b5163f0Snonaka
8758b5163f0Snonaka bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
876ac7944cbSjdolecek ISSET(ccb->nnc_flags, NVME_NS_CTX_F_READ) ?
8778b5163f0Snonaka BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
8788b5163f0Snonaka
8798b5163f0Snonaka bus_dmamap_unload(sc->sc_dmat, dmap);
8808b5163f0Snonaka nvme_ccb_put(q, ccb);
8818b5163f0Snonaka
8825df68754Sjdolecek nnc_done(nnc_cookie, bp, lemtoh16(&cqe->flags), lemtoh32(&cqe->cdw0));
8835df68754Sjdolecek }
8845df68754Sjdolecek
8855df68754Sjdolecek /*
8865df68754Sjdolecek * If there is no volatile write cache, it makes no sense to issue
8875df68754Sjdolecek * flush commands or query for the status.
8885df68754Sjdolecek */
8891bfffd71Sjdolecek static bool
nvme_has_volatile_write_cache(struct nvme_softc * sc)8905df68754Sjdolecek nvme_has_volatile_write_cache(struct nvme_softc *sc)
8915df68754Sjdolecek {
8925df68754Sjdolecek /* sc_identify is filled during attachment */
8935df68754Sjdolecek return ((sc->sc_identify.vwc & NVME_ID_CTRLR_VWC_PRESENT) != 0);
8948b5163f0Snonaka }
8958b5163f0Snonaka
8961bfffd71Sjdolecek static bool
nvme_ns_sync_finished(void * cookie)8971bfffd71Sjdolecek nvme_ns_sync_finished(void *cookie)
8981bfffd71Sjdolecek {
8991bfffd71Sjdolecek int *result = cookie;
9001bfffd71Sjdolecek
9011bfffd71Sjdolecek return (*result != 0);
9021bfffd71Sjdolecek }
9031bfffd71Sjdolecek
9048b5163f0Snonaka int
nvme_ns_sync(struct nvme_softc * sc,uint16_t nsid,int flags)9051bfffd71Sjdolecek nvme_ns_sync(struct nvme_softc *sc, uint16_t nsid, int flags)
9068b5163f0Snonaka {
907b4fea417Sjmcneill struct nvme_queue *q = nvme_get_q(sc);
9088b5163f0Snonaka struct nvme_ccb *ccb;
9091bfffd71Sjdolecek int result = 0;
9108b5163f0Snonaka
9111bfffd71Sjdolecek if (!nvme_has_volatile_write_cache(sc)) {
9121bfffd71Sjdolecek /* cache not present, no value in trying to flush it */
9131bfffd71Sjdolecek return 0;
9141bfffd71Sjdolecek }
9151bfffd71Sjdolecek
9161bfffd71Sjdolecek ccb = nvme_ccb_get(q, true);
917ea947a44Sjmcneill KASSERT(ccb != NULL);
9188b5163f0Snonaka
9198b5163f0Snonaka ccb->ccb_done = nvme_ns_sync_done;
9201bfffd71Sjdolecek ccb->ccb_cookie = &result;
9218b5163f0Snonaka
922ac7944cbSjdolecek /* namespace context */
923ac7944cbSjdolecek ccb->nnc_nsid = nsid;
924ac7944cbSjdolecek ccb->nnc_flags = flags;
9251bfffd71Sjdolecek ccb->nnc_done = NULL;
926ac7944cbSjdolecek
927ac7944cbSjdolecek if (ISSET(flags, NVME_NS_CTX_F_POLL)) {
928b8b74a3cSjdolecek if (nvme_poll(sc, q, ccb, nvme_ns_sync_fill, NVME_TIMO_SY) != 0)
9298b5163f0Snonaka return EIO;
9308b5163f0Snonaka return 0;
9318b5163f0Snonaka }
9328b5163f0Snonaka
9338b5163f0Snonaka nvme_q_submit(sc, q, ccb, nvme_ns_sync_fill);
9341bfffd71Sjdolecek
9351bfffd71Sjdolecek /* wait for completion */
9361bfffd71Sjdolecek nvme_q_wait_complete(sc, q, nvme_ns_sync_finished, &result);
9371bfffd71Sjdolecek KASSERT(result != 0);
9381bfffd71Sjdolecek
9391bfffd71Sjdolecek return (result > 0) ? 0 : EIO;
9408b5163f0Snonaka }
9418b5163f0Snonaka
9428b5163f0Snonaka static void
nvme_ns_sync_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)9438b5163f0Snonaka nvme_ns_sync_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
9448b5163f0Snonaka {
9458b5163f0Snonaka struct nvme_sqe *sqe = slot;
9468b5163f0Snonaka
9478b5163f0Snonaka sqe->opcode = NVM_CMD_FLUSH;
948ac7944cbSjdolecek htolem32(&sqe->nsid, ccb->nnc_nsid);
9498b5163f0Snonaka }
9508b5163f0Snonaka
9518b5163f0Snonaka static void
nvme_ns_sync_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)9528b5163f0Snonaka nvme_ns_sync_done(struct nvme_queue *q, struct nvme_ccb *ccb,
9538b5163f0Snonaka struct nvme_cqe *cqe)
9548b5163f0Snonaka {
9551bfffd71Sjdolecek int *result = ccb->ccb_cookie;
9561bfffd71Sjdolecek uint16_t status = NVME_CQE_SC(lemtoh16(&cqe->flags));
9571bfffd71Sjdolecek
9581bfffd71Sjdolecek if (status == NVME_CQE_SC_SUCCESS)
9591bfffd71Sjdolecek *result = 1;
9601bfffd71Sjdolecek else
9611bfffd71Sjdolecek *result = -1;
9628b5163f0Snonaka
9638b5163f0Snonaka nvme_ccb_put(q, ccb);
9641bfffd71Sjdolecek }
9658b5163f0Snonaka
9661bfffd71Sjdolecek static bool
nvme_getcache_finished(void * xc)9671bfffd71Sjdolecek nvme_getcache_finished(void *xc)
9681bfffd71Sjdolecek {
9691bfffd71Sjdolecek int *addr = xc;
9701bfffd71Sjdolecek
9711bfffd71Sjdolecek return (*addr != 0);
9725df68754Sjdolecek }
9735df68754Sjdolecek
9745df68754Sjdolecek /*
9755df68754Sjdolecek * Get status of volatile write cache. Always asynchronous.
9765df68754Sjdolecek */
9775df68754Sjdolecek int
nvme_admin_getcache(struct nvme_softc * sc,int * addr)9781bfffd71Sjdolecek nvme_admin_getcache(struct nvme_softc *sc, int *addr)
9795df68754Sjdolecek {
9805df68754Sjdolecek struct nvme_ccb *ccb;
9815df68754Sjdolecek struct nvme_queue *q = sc->sc_admin_q;
9821bfffd71Sjdolecek int result = 0, error;
9835df68754Sjdolecek
9841bfffd71Sjdolecek if (!nvme_has_volatile_write_cache(sc)) {
9851bfffd71Sjdolecek /* cache simply not present */
9861bfffd71Sjdolecek *addr = 0;
9871bfffd71Sjdolecek return 0;
9881bfffd71Sjdolecek }
9891bfffd71Sjdolecek
9901bfffd71Sjdolecek ccb = nvme_ccb_get(q, true);
9911bfffd71Sjdolecek KASSERT(ccb != NULL);
9925df68754Sjdolecek
9935df68754Sjdolecek ccb->ccb_done = nvme_getcache_done;
9941bfffd71Sjdolecek ccb->ccb_cookie = &result;
9955df68754Sjdolecek
9965df68754Sjdolecek /* namespace context */
9975df68754Sjdolecek ccb->nnc_flags = 0;
9981bfffd71Sjdolecek ccb->nnc_done = NULL;
9995df68754Sjdolecek
10005df68754Sjdolecek nvme_q_submit(sc, q, ccb, nvme_getcache_fill);
10011bfffd71Sjdolecek
10021bfffd71Sjdolecek /* wait for completion */
10031bfffd71Sjdolecek nvme_q_wait_complete(sc, q, nvme_getcache_finished, &result);
10041bfffd71Sjdolecek KASSERT(result != 0);
10051bfffd71Sjdolecek
10061bfffd71Sjdolecek if (result > 0) {
10071bfffd71Sjdolecek *addr = result;
10081bfffd71Sjdolecek error = 0;
10091bfffd71Sjdolecek } else
10101bfffd71Sjdolecek error = EINVAL;
10111bfffd71Sjdolecek
10121bfffd71Sjdolecek return error;
10135df68754Sjdolecek }
10145df68754Sjdolecek
10155df68754Sjdolecek static void
nvme_getcache_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)10165df68754Sjdolecek nvme_getcache_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
10175df68754Sjdolecek {
10185df68754Sjdolecek struct nvme_sqe *sqe = slot;
10195df68754Sjdolecek
10205df68754Sjdolecek sqe->opcode = NVM_ADMIN_GET_FEATURES;
10216ac76f5bSnonaka htolem32(&sqe->cdw10, NVM_FEATURE_VOLATILE_WRITE_CACHE);
1022b7a0ea19Sjdolecek htolem32(&sqe->cdw11, NVM_VOLATILE_WRITE_CACHE_WCE);
10235df68754Sjdolecek }
10245df68754Sjdolecek
10255df68754Sjdolecek static void
nvme_getcache_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)10265df68754Sjdolecek nvme_getcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
10275df68754Sjdolecek struct nvme_cqe *cqe)
10285df68754Sjdolecek {
10291bfffd71Sjdolecek int *addr = ccb->ccb_cookie;
10301bfffd71Sjdolecek uint16_t status = NVME_CQE_SC(lemtoh16(&cqe->flags));
10311bfffd71Sjdolecek uint32_t cdw0 = lemtoh32(&cqe->cdw0);
10321bfffd71Sjdolecek int result;
10331bfffd71Sjdolecek
10341bfffd71Sjdolecek if (status == NVME_CQE_SC_SUCCESS) {
10351bfffd71Sjdolecek result = 0;
10361bfffd71Sjdolecek
10371bfffd71Sjdolecek /*
10381bfffd71Sjdolecek * DPO not supported, Dataset Management (DSM) field doesn't
10391bfffd71Sjdolecek * specify the same semantics. FUA is always supported.
10401bfffd71Sjdolecek */
10411bfffd71Sjdolecek result = DKCACHE_FUA;
10421bfffd71Sjdolecek
1043b7a0ea19Sjdolecek if (cdw0 & NVM_VOLATILE_WRITE_CACHE_WCE)
10441bfffd71Sjdolecek result |= DKCACHE_WRITE;
10451bfffd71Sjdolecek
10461bfffd71Sjdolecek /*
10471bfffd71Sjdolecek * If volatile write cache is present, the flag shall also be
10481bfffd71Sjdolecek * settable.
10491bfffd71Sjdolecek */
10501bfffd71Sjdolecek result |= DKCACHE_WCHANGE;
1051b7a0ea19Sjdolecek
1052b7a0ea19Sjdolecek /*
1053b7a0ea19Sjdolecek * ONCS field indicates whether the optional SAVE is also
1054b7a0ea19Sjdolecek * supported for Set Features. According to spec v1.3,
1055b7a0ea19Sjdolecek * Volatile Write Cache however doesn't support persistency
1056b7a0ea19Sjdolecek * across power cycle/reset.
1057b7a0ea19Sjdolecek */
1058b7a0ea19Sjdolecek
10591bfffd71Sjdolecek } else {
10601bfffd71Sjdolecek result = -1;
10611bfffd71Sjdolecek }
10621bfffd71Sjdolecek
10631bfffd71Sjdolecek *addr = result;
10645df68754Sjdolecek
10655df68754Sjdolecek nvme_ccb_put(q, ccb);
10668b5163f0Snonaka }
10678b5163f0Snonaka
1068b7a0ea19Sjdolecek struct nvme_setcache_state {
1069b7a0ea19Sjdolecek int dkcache;
1070b7a0ea19Sjdolecek int result;
1071b7a0ea19Sjdolecek };
1072b7a0ea19Sjdolecek
1073b7a0ea19Sjdolecek static bool
nvme_setcache_finished(void * xc)1074b7a0ea19Sjdolecek nvme_setcache_finished(void *xc)
1075b7a0ea19Sjdolecek {
1076b7a0ea19Sjdolecek struct nvme_setcache_state *st = xc;
1077b7a0ea19Sjdolecek
1078b7a0ea19Sjdolecek return (st->result != 0);
1079b7a0ea19Sjdolecek }
1080b7a0ea19Sjdolecek
1081b7a0ea19Sjdolecek static void
nvme_setcache_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)1082b7a0ea19Sjdolecek nvme_setcache_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
1083b7a0ea19Sjdolecek {
1084b7a0ea19Sjdolecek struct nvme_sqe *sqe = slot;
1085b7a0ea19Sjdolecek struct nvme_setcache_state *st = ccb->ccb_cookie;
1086b7a0ea19Sjdolecek
1087b7a0ea19Sjdolecek sqe->opcode = NVM_ADMIN_SET_FEATURES;
1088b7a0ea19Sjdolecek htolem32(&sqe->cdw10, NVM_FEATURE_VOLATILE_WRITE_CACHE);
1089b7a0ea19Sjdolecek if (st->dkcache & DKCACHE_WRITE)
1090b7a0ea19Sjdolecek htolem32(&sqe->cdw11, NVM_VOLATILE_WRITE_CACHE_WCE);
1091b7a0ea19Sjdolecek }
1092b7a0ea19Sjdolecek
1093b7a0ea19Sjdolecek static void
nvme_setcache_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)1094b7a0ea19Sjdolecek nvme_setcache_done(struct nvme_queue *q, struct nvme_ccb *ccb,
1095b7a0ea19Sjdolecek struct nvme_cqe *cqe)
1096b7a0ea19Sjdolecek {
1097b7a0ea19Sjdolecek struct nvme_setcache_state *st = ccb->ccb_cookie;
1098b7a0ea19Sjdolecek uint16_t status = NVME_CQE_SC(lemtoh16(&cqe->flags));
1099b7a0ea19Sjdolecek
1100b7a0ea19Sjdolecek if (status == NVME_CQE_SC_SUCCESS) {
1101b7a0ea19Sjdolecek st->result = 1;
1102b7a0ea19Sjdolecek } else {
1103b7a0ea19Sjdolecek st->result = -1;
1104b7a0ea19Sjdolecek }
1105b7a0ea19Sjdolecek
1106b7a0ea19Sjdolecek nvme_ccb_put(q, ccb);
1107b7a0ea19Sjdolecek }
1108b7a0ea19Sjdolecek
1109b7a0ea19Sjdolecek /*
1110b7a0ea19Sjdolecek * Set status of volatile write cache. Always asynchronous.
1111b7a0ea19Sjdolecek */
1112b7a0ea19Sjdolecek int
nvme_admin_setcache(struct nvme_softc * sc,int dkcache)1113b7a0ea19Sjdolecek nvme_admin_setcache(struct nvme_softc *sc, int dkcache)
1114b7a0ea19Sjdolecek {
1115b7a0ea19Sjdolecek struct nvme_ccb *ccb;
1116b7a0ea19Sjdolecek struct nvme_queue *q = sc->sc_admin_q;
1117b7a0ea19Sjdolecek int error;
1118b7a0ea19Sjdolecek struct nvme_setcache_state st;
1119b7a0ea19Sjdolecek
1120b7a0ea19Sjdolecek if (!nvme_has_volatile_write_cache(sc)) {
1121b7a0ea19Sjdolecek /* cache simply not present */
1122b7a0ea19Sjdolecek return EOPNOTSUPP;
1123b7a0ea19Sjdolecek }
1124b7a0ea19Sjdolecek
1125b7a0ea19Sjdolecek if (dkcache & ~(DKCACHE_WRITE)) {
1126b7a0ea19Sjdolecek /* unsupported parameters */
1127b7a0ea19Sjdolecek return EOPNOTSUPP;
1128b7a0ea19Sjdolecek }
1129b7a0ea19Sjdolecek
1130b7a0ea19Sjdolecek ccb = nvme_ccb_get(q, true);
1131b7a0ea19Sjdolecek KASSERT(ccb != NULL);
1132b7a0ea19Sjdolecek
1133b7a0ea19Sjdolecek memset(&st, 0, sizeof(st));
1134b7a0ea19Sjdolecek st.dkcache = dkcache;
1135b7a0ea19Sjdolecek
1136b7a0ea19Sjdolecek ccb->ccb_done = nvme_setcache_done;
1137b7a0ea19Sjdolecek ccb->ccb_cookie = &st;
1138b7a0ea19Sjdolecek
1139b7a0ea19Sjdolecek /* namespace context */
1140b7a0ea19Sjdolecek ccb->nnc_flags = 0;
1141b7a0ea19Sjdolecek ccb->nnc_done = NULL;
1142b7a0ea19Sjdolecek
1143b7a0ea19Sjdolecek nvme_q_submit(sc, q, ccb, nvme_setcache_fill);
1144b7a0ea19Sjdolecek
1145b7a0ea19Sjdolecek /* wait for completion */
1146b7a0ea19Sjdolecek nvme_q_wait_complete(sc, q, nvme_setcache_finished, &st);
1147b7a0ea19Sjdolecek KASSERT(st.result != 0);
1148b7a0ea19Sjdolecek
1149b7a0ea19Sjdolecek if (st.result > 0)
1150b7a0ea19Sjdolecek error = 0;
1151b7a0ea19Sjdolecek else
1152b7a0ea19Sjdolecek error = EINVAL;
1153b7a0ea19Sjdolecek
1154b7a0ea19Sjdolecek return error;
1155b7a0ea19Sjdolecek }
1156b7a0ea19Sjdolecek
11578b5163f0Snonaka void
nvme_ns_free(struct nvme_softc * sc,uint16_t nsid)11588b5163f0Snonaka nvme_ns_free(struct nvme_softc *sc, uint16_t nsid)
11598b5163f0Snonaka {
11608b5163f0Snonaka struct nvme_namespace *ns;
11618b5163f0Snonaka struct nvm_identify_namespace *identify;
11628b5163f0Snonaka
11638b5163f0Snonaka ns = nvme_ns_get(sc, nsid);
11648b5163f0Snonaka KASSERT(ns);
11658b5163f0Snonaka
11668b5163f0Snonaka identify = ns->ident;
11678b5163f0Snonaka ns->ident = NULL;
11688b5163f0Snonaka if (identify != NULL)
11698b5163f0Snonaka kmem_free(identify, sizeof(*identify));
11708b5163f0Snonaka }
11718b5163f0Snonaka
11723b8b7556Sjdolecek struct nvme_pt_state {
11733b8b7556Sjdolecek struct nvme_pt_command *pt;
11743b8b7556Sjdolecek bool finished;
11753b8b7556Sjdolecek };
11763b8b7556Sjdolecek
11778b5163f0Snonaka static void
nvme_pt_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)1178e7c0cc5dSnonaka nvme_pt_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
1179e7c0cc5dSnonaka {
1180e7c0cc5dSnonaka struct nvme_softc *sc = q->q_sc;
1181e7c0cc5dSnonaka struct nvme_sqe *sqe = slot;
11823b8b7556Sjdolecek struct nvme_pt_state *state = ccb->ccb_cookie;
11833b8b7556Sjdolecek struct nvme_pt_command *pt = state->pt;
1184e7c0cc5dSnonaka bus_dmamap_t dmap = ccb->ccb_dmamap;
1185e7c0cc5dSnonaka int i;
1186e7c0cc5dSnonaka
1187e7c0cc5dSnonaka sqe->opcode = pt->cmd.opcode;
1188e7c0cc5dSnonaka htolem32(&sqe->nsid, pt->cmd.nsid);
1189e7c0cc5dSnonaka
1190e7c0cc5dSnonaka if (pt->buf != NULL && pt->len > 0) {
1191e7c0cc5dSnonaka htolem64(&sqe->entry.prp[0], dmap->dm_segs[0].ds_addr);
1192e7c0cc5dSnonaka switch (dmap->dm_nsegs) {
1193e7c0cc5dSnonaka case 1:
1194e7c0cc5dSnonaka break;
1195e7c0cc5dSnonaka case 2:
1196e7c0cc5dSnonaka htolem64(&sqe->entry.prp[1], dmap->dm_segs[1].ds_addr);
1197e7c0cc5dSnonaka break;
1198e7c0cc5dSnonaka default:
1199e7c0cc5dSnonaka for (i = 1; i < dmap->dm_nsegs; i++) {
1200e7c0cc5dSnonaka htolem64(&ccb->ccb_prpl[i - 1],
1201e7c0cc5dSnonaka dmap->dm_segs[i].ds_addr);
1202e7c0cc5dSnonaka }
1203e7c0cc5dSnonaka bus_dmamap_sync(sc->sc_dmat,
1204e7c0cc5dSnonaka NVME_DMA_MAP(q->q_ccb_prpls),
1205e7c0cc5dSnonaka ccb->ccb_prpl_off,
12061648ffa0Snonaka sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1),
1207e7c0cc5dSnonaka BUS_DMASYNC_PREWRITE);
1208e7c0cc5dSnonaka htolem64(&sqe->entry.prp[1], ccb->ccb_prpl_dva);
1209e7c0cc5dSnonaka break;
1210e7c0cc5dSnonaka }
1211e7c0cc5dSnonaka }
1212e7c0cc5dSnonaka
1213e7c0cc5dSnonaka htolem32(&sqe->cdw10, pt->cmd.cdw10);
1214e7c0cc5dSnonaka htolem32(&sqe->cdw11, pt->cmd.cdw11);
1215e7c0cc5dSnonaka htolem32(&sqe->cdw12, pt->cmd.cdw12);
1216e7c0cc5dSnonaka htolem32(&sqe->cdw13, pt->cmd.cdw13);
1217e7c0cc5dSnonaka htolem32(&sqe->cdw14, pt->cmd.cdw14);
1218e7c0cc5dSnonaka htolem32(&sqe->cdw15, pt->cmd.cdw15);
1219e7c0cc5dSnonaka }
1220e7c0cc5dSnonaka
1221e7c0cc5dSnonaka static void
nvme_pt_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)1222e7c0cc5dSnonaka nvme_pt_done(struct nvme_queue *q, struct nvme_ccb *ccb, struct nvme_cqe *cqe)
1223e7c0cc5dSnonaka {
1224e7c0cc5dSnonaka struct nvme_softc *sc = q->q_sc;
12253b8b7556Sjdolecek struct nvme_pt_state *state = ccb->ccb_cookie;
12263b8b7556Sjdolecek struct nvme_pt_command *pt = state->pt;
1227e7c0cc5dSnonaka bus_dmamap_t dmap = ccb->ccb_dmamap;
1228e7c0cc5dSnonaka
1229e7c0cc5dSnonaka if (pt->buf != NULL && pt->len > 0) {
1230e7c0cc5dSnonaka if (dmap->dm_nsegs > 2) {
1231e7c0cc5dSnonaka bus_dmamap_sync(sc->sc_dmat,
1232e7c0cc5dSnonaka NVME_DMA_MAP(q->q_ccb_prpls),
1233e7c0cc5dSnonaka ccb->ccb_prpl_off,
12341648ffa0Snonaka sizeof(*ccb->ccb_prpl) * (dmap->dm_nsegs - 1),
1235e7c0cc5dSnonaka BUS_DMASYNC_POSTWRITE);
1236e7c0cc5dSnonaka }
1237e7c0cc5dSnonaka
1238e7c0cc5dSnonaka bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
1239e7c0cc5dSnonaka pt->is_read ? BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
1240e7c0cc5dSnonaka bus_dmamap_unload(sc->sc_dmat, dmap);
1241e7c0cc5dSnonaka }
1242e7c0cc5dSnonaka
12435bfb6e7eSnonaka pt->cpl.cdw0 = lemtoh32(&cqe->cdw0);
12445bfb6e7eSnonaka pt->cpl.flags = lemtoh16(&cqe->flags) & ~NVME_CQE_PHASE;
12453b8b7556Sjdolecek
12463b8b7556Sjdolecek state->finished = true;
12473b8b7556Sjdolecek
12483b8b7556Sjdolecek nvme_ccb_put(q, ccb);
12493b8b7556Sjdolecek }
12503b8b7556Sjdolecek
12513b8b7556Sjdolecek static bool
nvme_pt_finished(void * cookie)12523b8b7556Sjdolecek nvme_pt_finished(void *cookie)
12533b8b7556Sjdolecek {
12543b8b7556Sjdolecek struct nvme_pt_state *state = cookie;
12553b8b7556Sjdolecek
12563b8b7556Sjdolecek return state->finished;
1257e7c0cc5dSnonaka }
1258e7c0cc5dSnonaka
1259e7c0cc5dSnonaka static int
nvme_command_passthrough(struct nvme_softc * sc,struct nvme_pt_command * pt,uint32_t nsid,struct lwp * l,bool is_adminq)1260e7c0cc5dSnonaka nvme_command_passthrough(struct nvme_softc *sc, struct nvme_pt_command *pt,
12612437fdbaSmlelstv uint32_t nsid, struct lwp *l, bool is_adminq)
1262e7c0cc5dSnonaka {
1263e7c0cc5dSnonaka struct nvme_queue *q;
1264e7c0cc5dSnonaka struct nvme_ccb *ccb;
1265e7c0cc5dSnonaka void *buf = NULL;
12663b8b7556Sjdolecek struct nvme_pt_state state;
1267e7c0cc5dSnonaka int error;
1268e7c0cc5dSnonaka
1269ef172b9fSjdolecek /* limit command size to maximum data transfer size */
1270e7c0cc5dSnonaka if ((pt->buf == NULL && pt->len > 0) ||
1271ef172b9fSjdolecek (pt->buf != NULL && (pt->len == 0 || pt->len > sc->sc_mdts)))
1272e7c0cc5dSnonaka return EINVAL;
1273e7c0cc5dSnonaka
1274b4fea417Sjmcneill q = is_adminq ? sc->sc_admin_q : nvme_get_q(sc);
12751bfffd71Sjdolecek ccb = nvme_ccb_get(q, true);
12761bfffd71Sjdolecek KASSERT(ccb != NULL);
1277e7c0cc5dSnonaka
1278ef172b9fSjdolecek if (pt->buf != NULL) {
1279ef172b9fSjdolecek KASSERT(pt->len > 0);
1280e7c0cc5dSnonaka buf = kmem_alloc(pt->len, KM_SLEEP);
1281e7c0cc5dSnonaka if (!pt->is_read) {
1282e7c0cc5dSnonaka error = copyin(pt->buf, buf, pt->len);
1283e7c0cc5dSnonaka if (error)
1284e7c0cc5dSnonaka goto kmem_free;
1285e7c0cc5dSnonaka }
1286e7c0cc5dSnonaka error = bus_dmamap_load(sc->sc_dmat, ccb->ccb_dmamap, buf,
1287e7c0cc5dSnonaka pt->len, NULL,
1288e7c0cc5dSnonaka BUS_DMA_WAITOK |
1289e7c0cc5dSnonaka (pt->is_read ? BUS_DMA_READ : BUS_DMA_WRITE));
1290e7c0cc5dSnonaka if (error)
1291e7c0cc5dSnonaka goto kmem_free;
1292e7c0cc5dSnonaka bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap,
1293e7c0cc5dSnonaka 0, ccb->ccb_dmamap->dm_mapsize,
1294e7c0cc5dSnonaka pt->is_read ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE);
1295e7c0cc5dSnonaka }
1296e7c0cc5dSnonaka
12973b8b7556Sjdolecek memset(&state, 0, sizeof(state));
12983b8b7556Sjdolecek state.pt = pt;
12993b8b7556Sjdolecek state.finished = false;
13003b8b7556Sjdolecek
1301e7c0cc5dSnonaka ccb->ccb_done = nvme_pt_done;
13023b8b7556Sjdolecek ccb->ccb_cookie = &state;
1303e7c0cc5dSnonaka
1304e7c0cc5dSnonaka pt->cmd.nsid = nsid;
13053b8b7556Sjdolecek
13063b8b7556Sjdolecek nvme_q_submit(sc, q, ccb, nvme_pt_fill);
13073b8b7556Sjdolecek
13083b8b7556Sjdolecek /* wait for completion */
13093b8b7556Sjdolecek nvme_q_wait_complete(sc, q, nvme_pt_finished, &state);
13103b8b7556Sjdolecek KASSERT(state.finished);
1311e7c0cc5dSnonaka
1312e7c0cc5dSnonaka error = 0;
13133b8b7556Sjdolecek
1314e7c0cc5dSnonaka if (buf != NULL) {
1315e7c0cc5dSnonaka if (error == 0 && pt->is_read)
1316e7c0cc5dSnonaka error = copyout(buf, pt->buf, pt->len);
1317e7c0cc5dSnonaka kmem_free:
1318e7c0cc5dSnonaka kmem_free(buf, pt->len);
1319e7c0cc5dSnonaka }
13203b8b7556Sjdolecek
1321e7c0cc5dSnonaka return error;
1322e7c0cc5dSnonaka }
1323e7c0cc5dSnonaka
1324e24a9df7Sskrll uint32_t
nvme_op_sq_enter(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb)1325e24a9df7Sskrll nvme_op_sq_enter(struct nvme_softc *sc,
1326e24a9df7Sskrll struct nvme_queue *q, struct nvme_ccb *ccb)
1327e24a9df7Sskrll {
1328e24a9df7Sskrll mutex_enter(&q->q_sq_mtx);
1329e24a9df7Sskrll
1330e24a9df7Sskrll return nvme_op_sq_enter_locked(sc, q, ccb);
1331e24a9df7Sskrll }
1332e24a9df7Sskrll
1333e24a9df7Sskrll uint32_t
nvme_op_sq_enter_locked(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb)1334e24a9df7Sskrll nvme_op_sq_enter_locked(struct nvme_softc *sc,
1335e24a9df7Sskrll struct nvme_queue *q, struct nvme_ccb *ccb)
1336e24a9df7Sskrll {
1337e24a9df7Sskrll return q->q_sq_tail;
1338e24a9df7Sskrll }
1339e24a9df7Sskrll
1340e24a9df7Sskrll void
nvme_op_sq_leave_locked(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb)1341e24a9df7Sskrll nvme_op_sq_leave_locked(struct nvme_softc *sc,
1342e24a9df7Sskrll struct nvme_queue *q, struct nvme_ccb *ccb)
1343e24a9df7Sskrll {
1344e24a9df7Sskrll uint32_t tail;
1345e24a9df7Sskrll
1346e24a9df7Sskrll tail = ++q->q_sq_tail;
1347e24a9df7Sskrll if (tail >= q->q_entries)
1348e24a9df7Sskrll tail = 0;
1349e24a9df7Sskrll q->q_sq_tail = tail;
1350e24a9df7Sskrll nvme_write4(sc, q->q_sqtdbl, tail);
1351e24a9df7Sskrll }
1352e24a9df7Sskrll
1353e24a9df7Sskrll void
nvme_op_sq_leave(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb)1354e24a9df7Sskrll nvme_op_sq_leave(struct nvme_softc *sc,
1355e24a9df7Sskrll struct nvme_queue *q, struct nvme_ccb *ccb)
1356e24a9df7Sskrll {
1357e24a9df7Sskrll nvme_op_sq_leave_locked(sc, q, ccb);
1358e24a9df7Sskrll
1359e24a9df7Sskrll mutex_exit(&q->q_sq_mtx);
1360e24a9df7Sskrll }
1361e24a9df7Sskrll
1362e7c0cc5dSnonaka static void
nvme_q_submit(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb,void (* fill)(struct nvme_queue *,struct nvme_ccb *,void *))13638b5163f0Snonaka nvme_q_submit(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb,
13648b5163f0Snonaka void (*fill)(struct nvme_queue *, struct nvme_ccb *, void *))
13658b5163f0Snonaka {
13668b5163f0Snonaka struct nvme_sqe *sqe = NVME_DMA_KVA(q->q_sq_dmamem);
13678b5163f0Snonaka uint32_t tail;
13688b5163f0Snonaka
1369e24a9df7Sskrll tail = sc->sc_ops->op_sq_enter(sc, q, ccb);
13708b5163f0Snonaka
13718b5163f0Snonaka sqe += tail;
13728b5163f0Snonaka
13738b5163f0Snonaka bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
13748b5163f0Snonaka sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_POSTWRITE);
13758b5163f0Snonaka memset(sqe, 0, sizeof(*sqe));
13768b5163f0Snonaka (*fill)(q, ccb, sqe);
13776ac76f5bSnonaka htolem16(&sqe->cid, ccb->ccb_id);
13788b5163f0Snonaka bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_sq_dmamem),
13798b5163f0Snonaka sizeof(*sqe) * tail, sizeof(*sqe), BUS_DMASYNC_PREWRITE);
13808b5163f0Snonaka
1381e24a9df7Sskrll sc->sc_ops->op_sq_leave(sc, q, ccb);
13828b5163f0Snonaka }
13838b5163f0Snonaka
13848b5163f0Snonaka struct nvme_poll_state {
13858b5163f0Snonaka struct nvme_sqe s;
13868b5163f0Snonaka struct nvme_cqe c;
13871bfffd71Sjdolecek void *cookie;
13881bfffd71Sjdolecek void (*done)(struct nvme_queue *, struct nvme_ccb *, struct nvme_cqe *);
13898b5163f0Snonaka };
13908b5163f0Snonaka
13918b5163f0Snonaka static int
nvme_poll(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb,void (* fill)(struct nvme_queue *,struct nvme_ccb *,void *),int timo_sec)13928b5163f0Snonaka nvme_poll(struct nvme_softc *sc, struct nvme_queue *q, struct nvme_ccb *ccb,
1393b8b74a3cSjdolecek void (*fill)(struct nvme_queue *, struct nvme_ccb *, void *), int timo_sec)
13948b5163f0Snonaka {
13958b5163f0Snonaka struct nvme_poll_state state;
13968b5163f0Snonaka uint16_t flags;
1397b8b74a3cSjdolecek int step = 10;
1398b8b74a3cSjdolecek int maxloop = timo_sec * 1000000 / step;
1399b8b74a3cSjdolecek int error = 0;
14008b5163f0Snonaka
14018b5163f0Snonaka memset(&state, 0, sizeof(state));
14028b5163f0Snonaka (*fill)(q, ccb, &state.s);
14038b5163f0Snonaka
14041bfffd71Sjdolecek state.done = ccb->ccb_done;
14051bfffd71Sjdolecek state.cookie = ccb->ccb_cookie;
14068b5163f0Snonaka
14078b5163f0Snonaka ccb->ccb_done = nvme_poll_done;
14088b5163f0Snonaka ccb->ccb_cookie = &state;
14098b5163f0Snonaka
14108b5163f0Snonaka nvme_q_submit(sc, q, ccb, nvme_poll_fill);
14118b5163f0Snonaka while (!ISSET(state.c.flags, htole16(NVME_CQE_PHASE))) {
14128b5163f0Snonaka if (nvme_q_complete(sc, q) == 0)
1413b8b74a3cSjdolecek delay(step);
14148b5163f0Snonaka
1415b8b74a3cSjdolecek if (timo_sec >= 0 && --maxloop <= 0) {
1416b8b74a3cSjdolecek error = ETIMEDOUT;
1417b8b74a3cSjdolecek break;
1418b8b74a3cSjdolecek }
14198b5163f0Snonaka }
14208b5163f0Snonaka
1421b8b74a3cSjdolecek if (error == 0) {
14228b5163f0Snonaka flags = lemtoh16(&state.c.flags);
14238b5163f0Snonaka return flags & ~NVME_CQE_PHASE;
1424b8b74a3cSjdolecek } else {
14251bfffd71Sjdolecek /*
14261bfffd71Sjdolecek * If it succeds later, it would hit ccb which will have been
14271bfffd71Sjdolecek * already reused for something else. Not good. Cross
14281bfffd71Sjdolecek * fingers and hope for best. XXX do controller reset?
14291bfffd71Sjdolecek */
14301bfffd71Sjdolecek aprint_error_dev(sc->sc_dev, "polled command timed out\n");
14311bfffd71Sjdolecek
14321bfffd71Sjdolecek /* Invoke the callback to clean state anyway */
14331bfffd71Sjdolecek struct nvme_cqe cqe;
14341bfffd71Sjdolecek memset(&cqe, 0, sizeof(cqe));
14351bfffd71Sjdolecek ccb->ccb_done(q, ccb, &cqe);
14361bfffd71Sjdolecek
1437b8b74a3cSjdolecek return 1;
1438b8b74a3cSjdolecek }
14398b5163f0Snonaka }
14408b5163f0Snonaka
14418b5163f0Snonaka static void
nvme_poll_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)14428b5163f0Snonaka nvme_poll_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
14438b5163f0Snonaka {
14448b5163f0Snonaka struct nvme_sqe *sqe = slot;
14458b5163f0Snonaka struct nvme_poll_state *state = ccb->ccb_cookie;
14468b5163f0Snonaka
14478b5163f0Snonaka *sqe = state->s;
14488b5163f0Snonaka }
14498b5163f0Snonaka
14508b5163f0Snonaka static void
nvme_poll_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)14518b5163f0Snonaka nvme_poll_done(struct nvme_queue *q, struct nvme_ccb *ccb,
14528b5163f0Snonaka struct nvme_cqe *cqe)
14538b5163f0Snonaka {
14548b5163f0Snonaka struct nvme_poll_state *state = ccb->ccb_cookie;
14558b5163f0Snonaka
14568b5163f0Snonaka state->c = *cqe;
14570c2fac9cSnonaka SET(state->c.flags, htole16(NVME_CQE_PHASE));
14581bfffd71Sjdolecek
14591bfffd71Sjdolecek ccb->ccb_cookie = state->cookie;
14601bfffd71Sjdolecek state->done(q, ccb, &state->c);
14618b5163f0Snonaka }
14628b5163f0Snonaka
14638b5163f0Snonaka static void
nvme_sqe_fill(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)14648b5163f0Snonaka nvme_sqe_fill(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
14658b5163f0Snonaka {
14668b5163f0Snonaka struct nvme_sqe *src = ccb->ccb_cookie;
14678b5163f0Snonaka struct nvme_sqe *dst = slot;
14688b5163f0Snonaka
14698b5163f0Snonaka *dst = *src;
14708b5163f0Snonaka }
14718b5163f0Snonaka
14728b5163f0Snonaka static void
nvme_empty_done(struct nvme_queue * q,struct nvme_ccb * ccb,struct nvme_cqe * cqe)14738b5163f0Snonaka nvme_empty_done(struct nvme_queue *q, struct nvme_ccb *ccb,
14748b5163f0Snonaka struct nvme_cqe *cqe)
14758b5163f0Snonaka {
14768b5163f0Snonaka }
14778b5163f0Snonaka
1478e24a9df7Sskrll void
nvme_op_cq_done(struct nvme_softc * sc,struct nvme_queue * q,struct nvme_ccb * ccb)1479e24a9df7Sskrll nvme_op_cq_done(struct nvme_softc *sc,
1480e24a9df7Sskrll struct nvme_queue *q, struct nvme_ccb *ccb)
1481e24a9df7Sskrll {
1482e24a9df7Sskrll /* nop */
1483e24a9df7Sskrll }
1484e24a9df7Sskrll
14858b5163f0Snonaka static int
nvme_q_complete(struct nvme_softc * sc,struct nvme_queue * q)14868b5163f0Snonaka nvme_q_complete(struct nvme_softc *sc, struct nvme_queue *q)
14878b5163f0Snonaka {
14888b5163f0Snonaka struct nvme_ccb *ccb;
14898b5163f0Snonaka struct nvme_cqe *ring = NVME_DMA_KVA(q->q_cq_dmamem), *cqe;
14908b5163f0Snonaka uint16_t flags;
14918b5163f0Snonaka int rv = 0;
14928b5163f0Snonaka
1493ef172b9fSjdolecek mutex_enter(&q->q_cq_mtx);
14948b5163f0Snonaka
14958b5163f0Snonaka nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
14968b5163f0Snonaka for (;;) {
1497ef172b9fSjdolecek cqe = &ring[q->q_cq_head];
14988b5163f0Snonaka flags = lemtoh16(&cqe->flags);
14998b5163f0Snonaka if ((flags & NVME_CQE_PHASE) != q->q_cq_phase)
15008b5163f0Snonaka break;
15018b5163f0Snonaka
1502fb93569fSriastradh /*
1503fb93569fSriastradh * Make sure we have read the flags _before_ we read
1504fb93569fSriastradh * the cid. Otherwise the CPU might speculatively read
1505fb93569fSriastradh * the cid before the entry has been assigned to our
1506fb93569fSriastradh * phase.
1507fb93569fSriastradh */
1508fb93569fSriastradh nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
1509fb93569fSriastradh
1510cfa13e7eSrin ccb = &q->q_ccbs[lemtoh16(&cqe->cid)];
15118b5163f0Snonaka
1512ef172b9fSjdolecek if (++q->q_cq_head >= q->q_entries) {
1513ef172b9fSjdolecek q->q_cq_head = 0;
15148b5163f0Snonaka q->q_cq_phase ^= NVME_CQE_PHASE;
15158b5163f0Snonaka }
15168b5163f0Snonaka
151718cb8180Sjdolecek #ifdef DEBUG
151818cb8180Sjdolecek /*
151918cb8180Sjdolecek * If we get spurious completion notification, something
152018cb8180Sjdolecek * is seriously hosed up. Very likely DMA to some random
152118cb8180Sjdolecek * memory place happened, so just bail out.
152218cb8180Sjdolecek */
152318cb8180Sjdolecek if ((intptr_t)ccb->ccb_cookie == NVME_CCB_FREE) {
152418cb8180Sjdolecek panic("%s: invalid ccb detected",
152518cb8180Sjdolecek device_xname(sc->sc_dev));
152618cb8180Sjdolecek /* NOTREACHED */
152718cb8180Sjdolecek }
152818cb8180Sjdolecek #endif
1529b5e5261cSjdolecek
1530b5e5261cSjdolecek rv++;
1531ef172b9fSjdolecek
1532e24a9df7Sskrll sc->sc_ops->op_cq_done(sc, q, ccb);
1533e24a9df7Sskrll
1534ef172b9fSjdolecek /*
153590b052c4Sjdolecek * Unlock the mutex before calling the ccb_done callback
1536ef172b9fSjdolecek * and re-lock afterwards. The callback triggers lddone()
1537ef172b9fSjdolecek * which schedules another i/o, and also calls nvme_ccb_put().
1538ef172b9fSjdolecek * Unlock/relock avoids possibility of deadlock.
1539ef172b9fSjdolecek */
1540ef172b9fSjdolecek mutex_exit(&q->q_cq_mtx);
1541ef172b9fSjdolecek ccb->ccb_done(q, ccb, cqe);
1542ef172b9fSjdolecek mutex_enter(&q->q_cq_mtx);
15438b5163f0Snonaka }
15448b5163f0Snonaka nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
15458b5163f0Snonaka
15468b5163f0Snonaka if (rv)
1547ef172b9fSjdolecek nvme_write4(sc, q->q_cqhdbl, q->q_cq_head);
1548ef172b9fSjdolecek
15498b5163f0Snonaka mutex_exit(&q->q_cq_mtx);
15508b5163f0Snonaka
15518b5163f0Snonaka return rv;
15528b5163f0Snonaka }
15538b5163f0Snonaka
15541bfffd71Sjdolecek static void
nvme_q_wait_complete(struct nvme_softc * sc,struct nvme_queue * q,bool (* finished)(void *),void * cookie)15551bfffd71Sjdolecek nvme_q_wait_complete(struct nvme_softc *sc,
15561bfffd71Sjdolecek struct nvme_queue *q, bool (*finished)(void *), void *cookie)
15571bfffd71Sjdolecek {
15581bfffd71Sjdolecek mutex_enter(&q->q_ccb_mtx);
15591bfffd71Sjdolecek if (finished(cookie))
15601bfffd71Sjdolecek goto out;
15611bfffd71Sjdolecek
15621bfffd71Sjdolecek for(;;) {
15631bfffd71Sjdolecek q->q_ccb_waiting = true;
15641bfffd71Sjdolecek cv_wait(&q->q_ccb_wait, &q->q_ccb_mtx);
15651bfffd71Sjdolecek
15661bfffd71Sjdolecek if (finished(cookie))
15671bfffd71Sjdolecek break;
15681bfffd71Sjdolecek }
15691bfffd71Sjdolecek
15701bfffd71Sjdolecek out:
15711bfffd71Sjdolecek mutex_exit(&q->q_ccb_mtx);
15721bfffd71Sjdolecek }
15731bfffd71Sjdolecek
15748b5163f0Snonaka static int
nvme_identify(struct nvme_softc * sc,u_int mps)15758b5163f0Snonaka nvme_identify(struct nvme_softc *sc, u_int mps)
15768b5163f0Snonaka {
15778b5163f0Snonaka char sn[41], mn[81], fr[17];
15788b5163f0Snonaka struct nvm_identify_controller *identify;
1579f4a9f41eSjdolecek struct nvme_dmamem *mem;
15808b5163f0Snonaka struct nvme_ccb *ccb;
15818b5163f0Snonaka u_int mdts;
1582f4a9f41eSjdolecek int rv = 1;
15838b5163f0Snonaka
15841bfffd71Sjdolecek ccb = nvme_ccb_get(sc->sc_admin_q, false);
1585ac7944cbSjdolecek KASSERT(ccb != NULL); /* it's a bug if we don't have spare ccb here */
15868b5163f0Snonaka
1587f4a9f41eSjdolecek mem = nvme_dmamem_alloc(sc, sizeof(*identify));
1588f4a9f41eSjdolecek if (mem == NULL)
1589f4a9f41eSjdolecek return 1;
15908b5163f0Snonaka
15918b5163f0Snonaka ccb->ccb_done = nvme_empty_done;
1592f4a9f41eSjdolecek ccb->ccb_cookie = mem;
15938b5163f0Snonaka
15948b5163f0Snonaka nvme_dmamem_sync(sc, mem, BUS_DMASYNC_PREREAD);
1595f4a9f41eSjdolecek rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_fill_identify,
1596b8b74a3cSjdolecek NVME_TIMO_IDENT);
15978b5163f0Snonaka nvme_dmamem_sync(sc, mem, BUS_DMASYNC_POSTREAD);
15988b5163f0Snonaka
15998b5163f0Snonaka nvme_ccb_put(sc->sc_admin_q, ccb);
16008b5163f0Snonaka
1601f4a9f41eSjdolecek if (rv != 0)
16028b5163f0Snonaka goto done;
16038b5163f0Snonaka
16048b5163f0Snonaka identify = NVME_DMA_KVA(mem);
16056ac76f5bSnonaka sc->sc_identify = *identify;
16066ac76f5bSnonaka identify = NULL;
16078b5163f0Snonaka
16086ac76f5bSnonaka /* Convert data to host endian */
16096ac76f5bSnonaka nvme_identify_controller_swapbytes(&sc->sc_identify);
16106ac76f5bSnonaka
16116ac76f5bSnonaka strnvisx(sn, sizeof(sn), (const char *)sc->sc_identify.sn,
16126ac76f5bSnonaka sizeof(sc->sc_identify.sn), VIS_TRIM|VIS_SAFE|VIS_OCTAL);
16136ac76f5bSnonaka strnvisx(mn, sizeof(mn), (const char *)sc->sc_identify.mn,
16146ac76f5bSnonaka sizeof(sc->sc_identify.mn), VIS_TRIM|VIS_SAFE|VIS_OCTAL);
16156ac76f5bSnonaka strnvisx(fr, sizeof(fr), (const char *)sc->sc_identify.fr,
16166ac76f5bSnonaka sizeof(sc->sc_identify.fr), VIS_TRIM|VIS_SAFE|VIS_OCTAL);
16178b5163f0Snonaka aprint_normal_dev(sc->sc_dev, "%s, firmware %s, serial %s\n", mn, fr,
16188b5163f0Snonaka sn);
16198b5163f0Snonaka
16209653889bSmlelstv strlcpy(sc->sc_modelname, mn, sizeof(sc->sc_modelname));
16219653889bSmlelstv
16226ac76f5bSnonaka if (sc->sc_identify.mdts > 0) {
16236ac76f5bSnonaka mdts = (1 << sc->sc_identify.mdts) * (1 << mps);
16248b5163f0Snonaka if (mdts < sc->sc_mdts)
16258b5163f0Snonaka sc->sc_mdts = mdts;
16268b5163f0Snonaka }
16278b5163f0Snonaka
16286ac76f5bSnonaka sc->sc_nn = sc->sc_identify.nn;
16298b5163f0Snonaka
16308b5163f0Snonaka done:
1631f4a9f41eSjdolecek nvme_dmamem_free(sc, mem);
16328b5163f0Snonaka
1633f4a9f41eSjdolecek return rv;
16348b5163f0Snonaka }
16358b5163f0Snonaka
16368b5163f0Snonaka static int
nvme_q_create(struct nvme_softc * sc,struct nvme_queue * q)16378b5163f0Snonaka nvme_q_create(struct nvme_softc *sc, struct nvme_queue *q)
16388b5163f0Snonaka {
16398b5163f0Snonaka struct nvme_sqe_q sqe;
16408b5163f0Snonaka struct nvme_ccb *ccb;
16418b5163f0Snonaka int rv;
16428b5163f0Snonaka
1643ef172b9fSjdolecek if (sc->sc_use_mq && sc->sc_intr_establish(sc, q->q_id, q) != 0)
16448b5163f0Snonaka return 1;
16458b5163f0Snonaka
16461bfffd71Sjdolecek ccb = nvme_ccb_get(sc->sc_admin_q, false);
16478b5163f0Snonaka KASSERT(ccb != NULL);
16488b5163f0Snonaka
16498b5163f0Snonaka ccb->ccb_done = nvme_empty_done;
16508b5163f0Snonaka ccb->ccb_cookie = &sqe;
16518b5163f0Snonaka
16528b5163f0Snonaka memset(&sqe, 0, sizeof(sqe));
16538b5163f0Snonaka sqe.opcode = NVM_ADMIN_ADD_IOCQ;
16548b5163f0Snonaka htolem64(&sqe.prp1, NVME_DMA_DVA(q->q_cq_dmamem));
16558b5163f0Snonaka htolem16(&sqe.qsize, q->q_entries - 1);
16568b5163f0Snonaka htolem16(&sqe.qid, q->q_id);
16578b5163f0Snonaka sqe.qflags = NVM_SQE_CQ_IEN | NVM_SQE_Q_PC;
16588b5163f0Snonaka if (sc->sc_use_mq)
16598b5163f0Snonaka htolem16(&sqe.cqid, q->q_id); /* qid == vector */
16608b5163f0Snonaka
1661b8b74a3cSjdolecek rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
16628b5163f0Snonaka if (rv != 0)
16638b5163f0Snonaka goto fail;
16648b5163f0Snonaka
16658b5163f0Snonaka ccb->ccb_done = nvme_empty_done;
16668b5163f0Snonaka ccb->ccb_cookie = &sqe;
16678b5163f0Snonaka
16688b5163f0Snonaka memset(&sqe, 0, sizeof(sqe));
16698b5163f0Snonaka sqe.opcode = NVM_ADMIN_ADD_IOSQ;
16708b5163f0Snonaka htolem64(&sqe.prp1, NVME_DMA_DVA(q->q_sq_dmamem));
16718b5163f0Snonaka htolem16(&sqe.qsize, q->q_entries - 1);
16728b5163f0Snonaka htolem16(&sqe.qid, q->q_id);
16738b5163f0Snonaka htolem16(&sqe.cqid, q->q_id);
16748b5163f0Snonaka sqe.qflags = NVM_SQE_Q_PC;
16758b5163f0Snonaka
1676b8b74a3cSjdolecek rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
16778b5163f0Snonaka if (rv != 0)
16788b5163f0Snonaka goto fail;
16798b5163f0Snonaka
16808f9ff5faSjdolecek nvme_ccb_put(sc->sc_admin_q, ccb);
16818f9ff5faSjdolecek return 0;
16828f9ff5faSjdolecek
16838b5163f0Snonaka fail:
16848f9ff5faSjdolecek if (sc->sc_use_mq)
16858f9ff5faSjdolecek sc->sc_intr_disestablish(sc, q->q_id);
16868f9ff5faSjdolecek
16878b5163f0Snonaka nvme_ccb_put(sc->sc_admin_q, ccb);
16888b5163f0Snonaka return rv;
16898b5163f0Snonaka }
16908b5163f0Snonaka
16918b5163f0Snonaka static int
nvme_q_delete(struct nvme_softc * sc,struct nvme_queue * q)16928b5163f0Snonaka nvme_q_delete(struct nvme_softc *sc, struct nvme_queue *q)
16938b5163f0Snonaka {
16948b5163f0Snonaka struct nvme_sqe_q sqe;
16958b5163f0Snonaka struct nvme_ccb *ccb;
16968b5163f0Snonaka int rv;
16978b5163f0Snonaka
16981bfffd71Sjdolecek ccb = nvme_ccb_get(sc->sc_admin_q, false);
16998b5163f0Snonaka KASSERT(ccb != NULL);
17008b5163f0Snonaka
17018b5163f0Snonaka ccb->ccb_done = nvme_empty_done;
17028b5163f0Snonaka ccb->ccb_cookie = &sqe;
17038b5163f0Snonaka
17048b5163f0Snonaka memset(&sqe, 0, sizeof(sqe));
17058b5163f0Snonaka sqe.opcode = NVM_ADMIN_DEL_IOSQ;
17068b5163f0Snonaka htolem16(&sqe.qid, q->q_id);
17078b5163f0Snonaka
1708b8b74a3cSjdolecek rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
17098b5163f0Snonaka if (rv != 0)
17108b5163f0Snonaka goto fail;
17118b5163f0Snonaka
17128b5163f0Snonaka ccb->ccb_done = nvme_empty_done;
17138b5163f0Snonaka ccb->ccb_cookie = &sqe;
17148b5163f0Snonaka
17158b5163f0Snonaka memset(&sqe, 0, sizeof(sqe));
17168b5163f0Snonaka sqe.opcode = NVM_ADMIN_DEL_IOCQ;
17178b5163f0Snonaka htolem16(&sqe.qid, q->q_id);
17188b5163f0Snonaka
1719b8b74a3cSjdolecek rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_sqe_fill, NVME_TIMO_QOP);
17208b5163f0Snonaka if (rv != 0)
17218b5163f0Snonaka goto fail;
17228b5163f0Snonaka
17238b5163f0Snonaka fail:
17248b5163f0Snonaka nvme_ccb_put(sc->sc_admin_q, ccb);
17258b5163f0Snonaka
17268b5163f0Snonaka if (rv == 0 && sc->sc_use_mq) {
17278b5163f0Snonaka if (sc->sc_intr_disestablish(sc, q->q_id))
17288b5163f0Snonaka rv = 1;
17298b5163f0Snonaka }
17308b5163f0Snonaka
17318b5163f0Snonaka return rv;
17328b5163f0Snonaka }
17338b5163f0Snonaka
17348b5163f0Snonaka static void
nvme_fill_identify(struct nvme_queue * q,struct nvme_ccb * ccb,void * slot)17358b5163f0Snonaka nvme_fill_identify(struct nvme_queue *q, struct nvme_ccb *ccb, void *slot)
17368b5163f0Snonaka {
17378b5163f0Snonaka struct nvme_sqe *sqe = slot;
17388b5163f0Snonaka struct nvme_dmamem *mem = ccb->ccb_cookie;
17398b5163f0Snonaka
17408b5163f0Snonaka sqe->opcode = NVM_ADMIN_IDENTIFY;
1741f4a9f41eSjdolecek htolem64(&sqe->entry.prp[0], NVME_DMA_DVA(mem));
17428b5163f0Snonaka htolem32(&sqe->cdw10, 1);
17438b5163f0Snonaka }
17448b5163f0Snonaka
17458b5163f0Snonaka static int
nvme_set_number_of_queues(struct nvme_softc * sc,u_int nq,u_int * ncqa,u_int * nsqa)174611d4ba74Snonaka nvme_set_number_of_queues(struct nvme_softc *sc, u_int nq, u_int *ncqa,
174711d4ba74Snonaka u_int *nsqa)
17485bfb6e7eSnonaka {
17494fa80ed0Sjdolecek struct nvme_pt_state state;
17505bfb6e7eSnonaka struct nvme_pt_command pt;
17515bfb6e7eSnonaka struct nvme_ccb *ccb;
17525bfb6e7eSnonaka int rv;
17535bfb6e7eSnonaka
17541bfffd71Sjdolecek ccb = nvme_ccb_get(sc->sc_admin_q, false);
17555bfb6e7eSnonaka KASSERT(ccb != NULL); /* it's a bug if we don't have spare ccb here */
17565bfb6e7eSnonaka
17575bfb6e7eSnonaka memset(&pt, 0, sizeof(pt));
175811d4ba74Snonaka pt.cmd.opcode = NVM_ADMIN_SET_FEATURES;
1759459b824aSryo pt.cmd.cdw10 = NVM_FEATURE_NUMBER_OF_QUEUES;
1760459b824aSryo pt.cmd.cdw11 = ((nq - 1) << 16) | (nq - 1);
17615bfb6e7eSnonaka
17624fa80ed0Sjdolecek memset(&state, 0, sizeof(state));
17634fa80ed0Sjdolecek state.pt = &pt;
17644fa80ed0Sjdolecek state.finished = false;
17654fa80ed0Sjdolecek
17665bfb6e7eSnonaka ccb->ccb_done = nvme_pt_done;
17674fa80ed0Sjdolecek ccb->ccb_cookie = &state;
17685bfb6e7eSnonaka
17695bfb6e7eSnonaka rv = nvme_poll(sc, sc->sc_admin_q, ccb, nvme_pt_fill, NVME_TIMO_QOP);
17705bfb6e7eSnonaka
17715bfb6e7eSnonaka if (rv != 0) {
177211d4ba74Snonaka *ncqa = *nsqa = 0;
17735bfb6e7eSnonaka return EIO;
17745bfb6e7eSnonaka }
17755bfb6e7eSnonaka
177611d4ba74Snonaka *ncqa = (pt.cpl.cdw0 >> 16) + 1;
177711d4ba74Snonaka *nsqa = (pt.cpl.cdw0 & 0xffff) + 1;
17785bfb6e7eSnonaka
17795bfb6e7eSnonaka return 0;
17805bfb6e7eSnonaka }
17815bfb6e7eSnonaka
17825bfb6e7eSnonaka static int
nvme_ccbs_alloc(struct nvme_queue * q,uint16_t nccbs)1783b5e5261cSjdolecek nvme_ccbs_alloc(struct nvme_queue *q, uint16_t nccbs)
17848b5163f0Snonaka {
17858b5163f0Snonaka struct nvme_softc *sc = q->q_sc;
17868b5163f0Snonaka struct nvme_ccb *ccb;
17878b5163f0Snonaka bus_addr_t off;
17888b5163f0Snonaka uint64_t *prpl;
17898b5163f0Snonaka u_int i;
17908b5163f0Snonaka
17918b5163f0Snonaka mutex_init(&q->q_ccb_mtx, MUTEX_DEFAULT, IPL_BIO);
17921bfffd71Sjdolecek cv_init(&q->q_ccb_wait, "nvmeqw");
17931bfffd71Sjdolecek q->q_ccb_waiting = false;
17948b5163f0Snonaka SIMPLEQ_INIT(&q->q_ccb_list);
17958b5163f0Snonaka
17968b5163f0Snonaka q->q_ccbs = kmem_alloc(sizeof(*ccb) * nccbs, KM_SLEEP);
17978b5163f0Snonaka
17988b5163f0Snonaka q->q_nccbs = nccbs;
1799f4a9f41eSjdolecek q->q_ccb_prpls = nvme_dmamem_alloc(sc,
1800f4a9f41eSjdolecek sizeof(*prpl) * sc->sc_max_sgl * nccbs);
18018b5163f0Snonaka
18028b5163f0Snonaka prpl = NVME_DMA_KVA(q->q_ccb_prpls);
18038b5163f0Snonaka off = 0;
18048b5163f0Snonaka
18058b5163f0Snonaka for (i = 0; i < nccbs; i++) {
18068b5163f0Snonaka ccb = &q->q_ccbs[i];
18078b5163f0Snonaka
18088b5163f0Snonaka if (bus_dmamap_create(sc->sc_dmat, sc->sc_mdts,
18098b5163f0Snonaka sc->sc_max_sgl + 1 /* we get a free prp in the sqe */,
18108b5163f0Snonaka sc->sc_mps, sc->sc_mps, BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW,
18118b5163f0Snonaka &ccb->ccb_dmamap) != 0)
18128b5163f0Snonaka goto free_maps;
18138b5163f0Snonaka
18148b5163f0Snonaka ccb->ccb_id = i;
18158b5163f0Snonaka ccb->ccb_prpl = prpl;
18168b5163f0Snonaka ccb->ccb_prpl_off = off;
18178b5163f0Snonaka ccb->ccb_prpl_dva = NVME_DMA_DVA(q->q_ccb_prpls) + off;
18188b5163f0Snonaka
18198b5163f0Snonaka SIMPLEQ_INSERT_TAIL(&q->q_ccb_list, ccb, ccb_entry);
18208b5163f0Snonaka
18218b5163f0Snonaka prpl += sc->sc_max_sgl;
18228b5163f0Snonaka off += sizeof(*prpl) * sc->sc_max_sgl;
18238b5163f0Snonaka }
18248b5163f0Snonaka
18258b5163f0Snonaka return 0;
18268b5163f0Snonaka
18278b5163f0Snonaka free_maps:
18288b5163f0Snonaka nvme_ccbs_free(q);
18298b5163f0Snonaka return 1;
18308b5163f0Snonaka }
18318b5163f0Snonaka
18328b5163f0Snonaka static struct nvme_ccb *
nvme_ccb_get(struct nvme_queue * q,bool wait)18331bfffd71Sjdolecek nvme_ccb_get(struct nvme_queue *q, bool wait)
18348b5163f0Snonaka {
1835b5e5261cSjdolecek struct nvme_ccb *ccb = NULL;
18368b5163f0Snonaka
18378b5163f0Snonaka mutex_enter(&q->q_ccb_mtx);
18381bfffd71Sjdolecek again:
18398b5163f0Snonaka ccb = SIMPLEQ_FIRST(&q->q_ccb_list);
1840b685be4fSjdolecek if (ccb != NULL) {
18418b5163f0Snonaka SIMPLEQ_REMOVE_HEAD(&q->q_ccb_list, ccb_entry);
184218cb8180Sjdolecek #ifdef DEBUG
184318cb8180Sjdolecek ccb->ccb_cookie = NULL;
184418cb8180Sjdolecek #endif
18451bfffd71Sjdolecek } else {
18461bfffd71Sjdolecek if (__predict_false(wait)) {
18471bfffd71Sjdolecek q->q_ccb_waiting = true;
18481bfffd71Sjdolecek cv_wait(&q->q_ccb_wait, &q->q_ccb_mtx);
18491bfffd71Sjdolecek goto again;
18501bfffd71Sjdolecek }
185118cb8180Sjdolecek }
18528b5163f0Snonaka mutex_exit(&q->q_ccb_mtx);
18538b5163f0Snonaka
18548b5163f0Snonaka return ccb;
18558b5163f0Snonaka }
18568b5163f0Snonaka
1857b4fea417Sjmcneill static struct nvme_ccb *
nvme_ccb_get_bio(struct nvme_softc * sc,struct buf * bp,struct nvme_queue ** selq)1858b4fea417Sjmcneill nvme_ccb_get_bio(struct nvme_softc *sc, struct buf *bp,
1859b4fea417Sjmcneill struct nvme_queue **selq)
1860b4fea417Sjmcneill {
1861e3088cb1Sriastradh u_int cpuindex = cpu_index((bp && bp->b_ci) ? bp->b_ci : curcpu());
1862b4fea417Sjmcneill
1863b4fea417Sjmcneill /*
1864b4fea417Sjmcneill * Find a queue with available ccbs, preferring the originating
1865b4fea417Sjmcneill * CPU's queue.
1866b4fea417Sjmcneill */
1867b4fea417Sjmcneill
1868b4fea417Sjmcneill for (u_int qoff = 0; qoff < sc->sc_nq; qoff++) {
1869b4fea417Sjmcneill struct nvme_queue *q = sc->sc_q[(cpuindex + qoff) % sc->sc_nq];
1870b4fea417Sjmcneill struct nvme_ccb *ccb;
1871b4fea417Sjmcneill
1872b4fea417Sjmcneill mutex_enter(&q->q_ccb_mtx);
1873b4fea417Sjmcneill ccb = SIMPLEQ_FIRST(&q->q_ccb_list);
1874b4fea417Sjmcneill if (ccb != NULL) {
1875b4fea417Sjmcneill SIMPLEQ_REMOVE_HEAD(&q->q_ccb_list, ccb_entry);
1876b4fea417Sjmcneill #ifdef DEBUG
1877b4fea417Sjmcneill ccb->ccb_cookie = NULL;
1878b4fea417Sjmcneill #endif
1879b4fea417Sjmcneill }
1880b4fea417Sjmcneill mutex_exit(&q->q_ccb_mtx);
1881b4fea417Sjmcneill
1882b4fea417Sjmcneill if (ccb != NULL) {
1883b4fea417Sjmcneill *selq = q;
1884b4fea417Sjmcneill return ccb;
1885b4fea417Sjmcneill }
1886b4fea417Sjmcneill }
1887b4fea417Sjmcneill
1888b4fea417Sjmcneill return NULL;
1889b4fea417Sjmcneill }
1890b4fea417Sjmcneill
18918b5163f0Snonaka static void
nvme_ccb_put(struct nvme_queue * q,struct nvme_ccb * ccb)18928b5163f0Snonaka nvme_ccb_put(struct nvme_queue *q, struct nvme_ccb *ccb)
18938b5163f0Snonaka {
18948b5163f0Snonaka
18958b5163f0Snonaka mutex_enter(&q->q_ccb_mtx);
189618cb8180Sjdolecek #ifdef DEBUG
189718cb8180Sjdolecek ccb->ccb_cookie = (void *)NVME_CCB_FREE;
189818cb8180Sjdolecek #endif
18998b5163f0Snonaka SIMPLEQ_INSERT_HEAD(&q->q_ccb_list, ccb, ccb_entry);
19001bfffd71Sjdolecek
19011bfffd71Sjdolecek /* It's unlikely there are any waiters, it's not used for regular I/O */
19021bfffd71Sjdolecek if (__predict_false(q->q_ccb_waiting)) {
19031bfffd71Sjdolecek q->q_ccb_waiting = false;
19041bfffd71Sjdolecek cv_broadcast(&q->q_ccb_wait);
19051bfffd71Sjdolecek }
19061bfffd71Sjdolecek
19078b5163f0Snonaka mutex_exit(&q->q_ccb_mtx);
19088b5163f0Snonaka }
19098b5163f0Snonaka
19108b5163f0Snonaka static void
nvme_ccbs_free(struct nvme_queue * q)19118b5163f0Snonaka nvme_ccbs_free(struct nvme_queue *q)
19128b5163f0Snonaka {
19138b5163f0Snonaka struct nvme_softc *sc = q->q_sc;
19148b5163f0Snonaka struct nvme_ccb *ccb;
19158b5163f0Snonaka
19168b5163f0Snonaka mutex_enter(&q->q_ccb_mtx);
19178b5163f0Snonaka while ((ccb = SIMPLEQ_FIRST(&q->q_ccb_list)) != NULL) {
19188b5163f0Snonaka SIMPLEQ_REMOVE_HEAD(&q->q_ccb_list, ccb_entry);
19191518c978Sryo /*
19201518c978Sryo * bus_dmamap_destroy() may call vm_map_lock() and rw_enter()
19211518c978Sryo * internally. don't hold spin mutex
19221518c978Sryo */
19231518c978Sryo mutex_exit(&q->q_ccb_mtx);
19248b5163f0Snonaka bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
19251518c978Sryo mutex_enter(&q->q_ccb_mtx);
19268b5163f0Snonaka }
19278b5163f0Snonaka mutex_exit(&q->q_ccb_mtx);
19288b5163f0Snonaka
1929f4a9f41eSjdolecek nvme_dmamem_free(sc, q->q_ccb_prpls);
19308b5163f0Snonaka kmem_free(q->q_ccbs, sizeof(*ccb) * q->q_nccbs);
19318b5163f0Snonaka q->q_ccbs = NULL;
19321bfffd71Sjdolecek cv_destroy(&q->q_ccb_wait);
19338b5163f0Snonaka mutex_destroy(&q->q_ccb_mtx);
19348b5163f0Snonaka }
19358b5163f0Snonaka
19368b5163f0Snonaka static struct nvme_queue *
nvme_q_alloc(struct nvme_softc * sc,uint16_t id,u_int entries,u_int dstrd)19378b5163f0Snonaka nvme_q_alloc(struct nvme_softc *sc, uint16_t id, u_int entries, u_int dstrd)
19388b5163f0Snonaka {
19398b5163f0Snonaka struct nvme_queue *q;
19408b5163f0Snonaka
19418b5163f0Snonaka q = kmem_alloc(sizeof(*q), KM_SLEEP);
19428b5163f0Snonaka q->q_sc = sc;
1943f4a9f41eSjdolecek q->q_sq_dmamem = nvme_dmamem_alloc(sc,
1944f4a9f41eSjdolecek sizeof(struct nvme_sqe) * entries);
1945f4a9f41eSjdolecek if (q->q_sq_dmamem == NULL)
19468b5163f0Snonaka goto free;
19478b5163f0Snonaka
1948f4a9f41eSjdolecek q->q_cq_dmamem = nvme_dmamem_alloc(sc,
1949f4a9f41eSjdolecek sizeof(struct nvme_cqe) * entries);
1950f4a9f41eSjdolecek if (q->q_cq_dmamem == NULL)
19518b5163f0Snonaka goto free_sq;
19528b5163f0Snonaka
19538b5163f0Snonaka memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem));
19548b5163f0Snonaka memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem));
19558b5163f0Snonaka
19568b5163f0Snonaka mutex_init(&q->q_sq_mtx, MUTEX_DEFAULT, IPL_BIO);
19578b5163f0Snonaka mutex_init(&q->q_cq_mtx, MUTEX_DEFAULT, IPL_BIO);
19588b5163f0Snonaka q->q_sqtdbl = NVME_SQTDBL(id, dstrd);
19598b5163f0Snonaka q->q_cqhdbl = NVME_CQHDBL(id, dstrd);
19608b5163f0Snonaka q->q_id = id;
19618b5163f0Snonaka q->q_entries = entries;
19628b5163f0Snonaka q->q_sq_tail = 0;
19638b5163f0Snonaka q->q_cq_head = 0;
19648b5163f0Snonaka q->q_cq_phase = NVME_CQE_PHASE;
19658b5163f0Snonaka
1966e24a9df7Sskrll if (sc->sc_ops->op_q_alloc != NULL) {
1967e24a9df7Sskrll if (sc->sc_ops->op_q_alloc(sc, q) != 0)
1968e24a9df7Sskrll goto free_cq;
1969e24a9df7Sskrll }
1970e24a9df7Sskrll
19718b5163f0Snonaka nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE);
19728b5163f0Snonaka nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
19738b5163f0Snonaka
1974b5e5261cSjdolecek /*
1975b5e5261cSjdolecek * Due to definition of full and empty queue (queue is empty
1976b5e5261cSjdolecek * when head == tail, full when tail is one less then head),
1977b5e5261cSjdolecek * we can actually only have (entries - 1) in-flight commands.
1978b5e5261cSjdolecek */
1979b5e5261cSjdolecek if (nvme_ccbs_alloc(q, entries - 1) != 0) {
19808b5163f0Snonaka aprint_error_dev(sc->sc_dev, "unable to allocate ccbs\n");
19818b5163f0Snonaka goto free_cq;
19828b5163f0Snonaka }
19838b5163f0Snonaka
19848b5163f0Snonaka return q;
19858b5163f0Snonaka
19868b5163f0Snonaka free_cq:
1987f4a9f41eSjdolecek nvme_dmamem_free(sc, q->q_cq_dmamem);
19888b5163f0Snonaka free_sq:
1989f4a9f41eSjdolecek nvme_dmamem_free(sc, q->q_sq_dmamem);
19908b5163f0Snonaka free:
19918b5163f0Snonaka kmem_free(q, sizeof(*q));
19928b5163f0Snonaka
19938b5163f0Snonaka return NULL;
19948b5163f0Snonaka }
19958b5163f0Snonaka
19968b5163f0Snonaka static void
nvme_q_reset(struct nvme_softc * sc,struct nvme_queue * q)1997a6e80066Sriastradh nvme_q_reset(struct nvme_softc *sc, struct nvme_queue *q)
1998a6e80066Sriastradh {
1999a6e80066Sriastradh
2000a6e80066Sriastradh memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem));
2001a6e80066Sriastradh memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem));
2002a6e80066Sriastradh
2003a6e80066Sriastradh q->q_sq_tail = 0;
2004a6e80066Sriastradh q->q_cq_head = 0;
2005a6e80066Sriastradh q->q_cq_phase = NVME_CQE_PHASE;
2006a6e80066Sriastradh
2007a6e80066Sriastradh nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE);
2008a6e80066Sriastradh nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD);
2009a6e80066Sriastradh }
2010a6e80066Sriastradh
2011a6e80066Sriastradh static void
nvme_q_free(struct nvme_softc * sc,struct nvme_queue * q)20128b5163f0Snonaka nvme_q_free(struct nvme_softc *sc, struct nvme_queue *q)
20138b5163f0Snonaka {
20148b5163f0Snonaka nvme_ccbs_free(q);
2015ef172b9fSjdolecek mutex_destroy(&q->q_sq_mtx);
2016ef172b9fSjdolecek mutex_destroy(&q->q_cq_mtx);
20178b5163f0Snonaka nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_POSTREAD);
20188b5163f0Snonaka nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_POSTWRITE);
2019e24a9df7Sskrll
2020e24a9df7Sskrll if (sc->sc_ops->op_q_alloc != NULL)
2021e24a9df7Sskrll sc->sc_ops->op_q_free(sc, q);
2022e24a9df7Sskrll
2023f4a9f41eSjdolecek nvme_dmamem_free(sc, q->q_cq_dmamem);
2024f4a9f41eSjdolecek nvme_dmamem_free(sc, q->q_sq_dmamem);
20258b5163f0Snonaka kmem_free(q, sizeof(*q));
20268b5163f0Snonaka }
20278b5163f0Snonaka
20288b5163f0Snonaka int
nvme_intr(void * xsc)20298b5163f0Snonaka nvme_intr(void *xsc)
20308b5163f0Snonaka {
20318b5163f0Snonaka struct nvme_softc *sc = xsc;
20328b5163f0Snonaka
2033639abfc2Smrg KASSERT(!sc->sc_use_mq);
2034639abfc2Smrg
203590b052c4Sjdolecek /*
203690b052c4Sjdolecek * INTx is level triggered, controller deasserts the interrupt only
203790b052c4Sjdolecek * when we advance command queue head via write to the doorbell.
2038684dc5aaSjdolecek * Tell the controller to block the interrupts while we process
2039684dc5aaSjdolecek * the queue(s).
204090b052c4Sjdolecek */
2041684dc5aaSjdolecek nvme_write4(sc, NVME_INTMS, 1);
20428b5163f0Snonaka
2043684dc5aaSjdolecek softint_schedule(sc->sc_softih[0]);
2044684dc5aaSjdolecek
2045684dc5aaSjdolecek /* don't know, might not have been for us */
2046684dc5aaSjdolecek return 1;
2047684dc5aaSjdolecek }
2048684dc5aaSjdolecek
2049684dc5aaSjdolecek void
nvme_softintr_intx(void * xq)2050684dc5aaSjdolecek nvme_softintr_intx(void *xq)
2051684dc5aaSjdolecek {
2052684dc5aaSjdolecek struct nvme_queue *q = xq;
2053684dc5aaSjdolecek struct nvme_softc *sc = q->q_sc;
2054684dc5aaSjdolecek
2055639abfc2Smrg KASSERT(!sc->sc_use_mq);
2056639abfc2Smrg
2057684dc5aaSjdolecek nvme_q_complete(sc, sc->sc_admin_q);
2058684dc5aaSjdolecek if (sc->sc_q != NULL)
2059684dc5aaSjdolecek nvme_q_complete(sc, sc->sc_q[0]);
2060684dc5aaSjdolecek
2061684dc5aaSjdolecek /*
2062684dc5aaSjdolecek * Processing done, tell controller to issue interrupts again. There
2063684dc5aaSjdolecek * is no race, as NVMe spec requires the controller to maintain state,
2064684dc5aaSjdolecek * and assert the interrupt whenever there are unacknowledged
2065684dc5aaSjdolecek * completion queue entries.
2066684dc5aaSjdolecek */
2067684dc5aaSjdolecek nvme_write4(sc, NVME_INTMC, 1);
20688b5163f0Snonaka }
20698b5163f0Snonaka
20708b5163f0Snonaka int
nvme_intr_msi(void * xq)2071ef172b9fSjdolecek nvme_intr_msi(void *xq)
2072ef172b9fSjdolecek {
2073ef172b9fSjdolecek struct nvme_queue *q = xq;
2074ef172b9fSjdolecek
2075161f0155Sriastradh KASSERT(q);
2076161f0155Sriastradh KASSERT(q->q_sc);
2077161f0155Sriastradh KASSERT(q->q_sc->sc_softih);
2078161f0155Sriastradh KASSERT(q->q_sc->sc_softih[q->q_id]);
2079ef172b9fSjdolecek
2080684dc5aaSjdolecek /*
2081684dc5aaSjdolecek * MSI/MSI-X are edge triggered, so can handover processing to softint
2082684dc5aaSjdolecek * without masking the interrupt.
2083684dc5aaSjdolecek */
2084ef172b9fSjdolecek softint_schedule(q->q_sc->sc_softih[q->q_id]);
2085ef172b9fSjdolecek
2086ef172b9fSjdolecek return 1;
2087ef172b9fSjdolecek }
2088ef172b9fSjdolecek
2089ef172b9fSjdolecek void
nvme_softintr_msi(void * xq)2090ef172b9fSjdolecek nvme_softintr_msi(void *xq)
20918b5163f0Snonaka {
20928b5163f0Snonaka struct nvme_queue *q = xq;
20938b5163f0Snonaka struct nvme_softc *sc = q->q_sc;
20948b5163f0Snonaka
2095ef172b9fSjdolecek nvme_q_complete(sc, q);
20968b5163f0Snonaka }
20978b5163f0Snonaka
2098e24a9df7Sskrll struct nvme_dmamem *
nvme_dmamem_alloc(struct nvme_softc * sc,size_t size)2099f4a9f41eSjdolecek nvme_dmamem_alloc(struct nvme_softc *sc, size_t size)
21008b5163f0Snonaka {
2101f4a9f41eSjdolecek struct nvme_dmamem *ndm;
21028b5163f0Snonaka int nsegs;
21038b5163f0Snonaka
2104f4a9f41eSjdolecek ndm = kmem_zalloc(sizeof(*ndm), KM_SLEEP);
2105f4a9f41eSjdolecek if (ndm == NULL)
2106f4a9f41eSjdolecek return NULL;
2107f4a9f41eSjdolecek
21088b5163f0Snonaka ndm->ndm_size = size;
21098b5163f0Snonaka
211004092511Smrg if (bus_dmamap_create(sc->sc_dmat, size, btoc(round_page(size)), size, 0,
21118b5163f0Snonaka BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &ndm->ndm_map) != 0)
21128b5163f0Snonaka goto ndmfree;
21138b5163f0Snonaka
21148b5163f0Snonaka if (bus_dmamem_alloc(sc->sc_dmat, size, sc->sc_mps, 0, &ndm->ndm_seg,
21158b5163f0Snonaka 1, &nsegs, BUS_DMA_WAITOK) != 0)
21168b5163f0Snonaka goto destroy;
21178b5163f0Snonaka
21188b5163f0Snonaka if (bus_dmamem_map(sc->sc_dmat, &ndm->ndm_seg, nsegs, size,
21198b5163f0Snonaka &ndm->ndm_kva, BUS_DMA_WAITOK) != 0)
21208b5163f0Snonaka goto free;
21218b5163f0Snonaka
21228b5163f0Snonaka if (bus_dmamap_load(sc->sc_dmat, ndm->ndm_map, ndm->ndm_kva, size,
21238b5163f0Snonaka NULL, BUS_DMA_WAITOK) != 0)
21248b5163f0Snonaka goto unmap;
21258b5163f0Snonaka
2126ca0bc97dSjmcneill memset(ndm->ndm_kva, 0, size);
2127ca0bc97dSjmcneill bus_dmamap_sync(sc->sc_dmat, ndm->ndm_map, 0, size, BUS_DMASYNC_PREREAD);
2128ca0bc97dSjmcneill
2129f4a9f41eSjdolecek return ndm;
21308b5163f0Snonaka
21318b5163f0Snonaka unmap:
21328b5163f0Snonaka bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, size);
21338b5163f0Snonaka free:
21348b5163f0Snonaka bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1);
21358b5163f0Snonaka destroy:
21368b5163f0Snonaka bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map);
21378b5163f0Snonaka ndmfree:
2138f4a9f41eSjdolecek kmem_free(ndm, sizeof(*ndm));
2139f4a9f41eSjdolecek return NULL;
2140f4a9f41eSjdolecek }
2141f4a9f41eSjdolecek
2142e24a9df7Sskrll void
nvme_dmamem_sync(struct nvme_softc * sc,struct nvme_dmamem * mem,int ops)2143f4a9f41eSjdolecek nvme_dmamem_sync(struct nvme_softc *sc, struct nvme_dmamem *mem, int ops)
2144f4a9f41eSjdolecek {
2145f4a9f41eSjdolecek bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(mem),
2146f4a9f41eSjdolecek 0, NVME_DMA_LEN(mem), ops);
21478b5163f0Snonaka }
21488b5163f0Snonaka
21498b5163f0Snonaka void
nvme_dmamem_free(struct nvme_softc * sc,struct nvme_dmamem * ndm)21508b5163f0Snonaka nvme_dmamem_free(struct nvme_softc *sc, struct nvme_dmamem *ndm)
21518b5163f0Snonaka {
21528b5163f0Snonaka bus_dmamap_unload(sc->sc_dmat, ndm->ndm_map);
21538b5163f0Snonaka bus_dmamem_unmap(sc->sc_dmat, ndm->ndm_kva, ndm->ndm_size);
21548b5163f0Snonaka bus_dmamem_free(sc->sc_dmat, &ndm->ndm_seg, 1);
21558b5163f0Snonaka bus_dmamap_destroy(sc->sc_dmat, ndm->ndm_map);
2156f4a9f41eSjdolecek kmem_free(ndm, sizeof(*ndm));
21578b5163f0Snonaka }
2158e7c0cc5dSnonaka
2159e7c0cc5dSnonaka /*
2160e7c0cc5dSnonaka * ioctl
2161e7c0cc5dSnonaka */
2162e7c0cc5dSnonaka
2163e7c0cc5dSnonaka dev_type_open(nvmeopen);
2164e7c0cc5dSnonaka dev_type_close(nvmeclose);
2165e7c0cc5dSnonaka dev_type_ioctl(nvmeioctl);
2166e7c0cc5dSnonaka
2167e7c0cc5dSnonaka const struct cdevsw nvme_cdevsw = {
2168e7c0cc5dSnonaka .d_open = nvmeopen,
2169e7c0cc5dSnonaka .d_close = nvmeclose,
2170e7c0cc5dSnonaka .d_read = noread,
2171e7c0cc5dSnonaka .d_write = nowrite,
2172e7c0cc5dSnonaka .d_ioctl = nvmeioctl,
2173e7c0cc5dSnonaka .d_stop = nostop,
2174e7c0cc5dSnonaka .d_tty = notty,
2175e7c0cc5dSnonaka .d_poll = nopoll,
2176e7c0cc5dSnonaka .d_mmap = nommap,
2177e7c0cc5dSnonaka .d_kqfilter = nokqfilter,
2178e7c0cc5dSnonaka .d_discard = nodiscard,
2179e7c0cc5dSnonaka .d_flag = D_OTHER,
2180e7c0cc5dSnonaka };
2181e7c0cc5dSnonaka
2182e7c0cc5dSnonaka /*
2183e7c0cc5dSnonaka * Accept an open operation on the control device.
2184e7c0cc5dSnonaka */
2185e7c0cc5dSnonaka int
nvmeopen(dev_t dev,int flag,int mode,struct lwp * l)2186e7c0cc5dSnonaka nvmeopen(dev_t dev, int flag, int mode, struct lwp *l)
2187e7c0cc5dSnonaka {
2188e7c0cc5dSnonaka struct nvme_softc *sc;
21894a15838fSnonaka int unit = minor(dev) / 0x10000;
21904a15838fSnonaka int nsid = minor(dev) & 0xffff;
21914a15838fSnonaka int nsidx;
2192e7c0cc5dSnonaka
2193e7c0cc5dSnonaka if ((sc = device_lookup_private(&nvme_cd, unit)) == NULL)
2194e7c0cc5dSnonaka return ENXIO;
2195e7c0cc5dSnonaka if ((sc->sc_flags & NVME_F_ATTACHED) == 0)
2196e7c0cc5dSnonaka return ENXIO;
21974a15838fSnonaka
21984a15838fSnonaka if (nsid == 0) {
21994a15838fSnonaka /* controller */
2200e7c0cc5dSnonaka if (ISSET(sc->sc_flags, NVME_F_OPEN))
2201e7c0cc5dSnonaka return EBUSY;
2202e7c0cc5dSnonaka SET(sc->sc_flags, NVME_F_OPEN);
22034a15838fSnonaka } else {
22044a15838fSnonaka /* namespace */
22054a15838fSnonaka nsidx = nsid - 1;
22064a15838fSnonaka if (nsidx >= sc->sc_nn || sc->sc_namespaces[nsidx].dev == NULL)
22074a15838fSnonaka return ENXIO;
22084a15838fSnonaka if (ISSET(sc->sc_namespaces[nsidx].flags, NVME_NS_F_OPEN))
22094a15838fSnonaka return EBUSY;
22104a15838fSnonaka SET(sc->sc_namespaces[nsidx].flags, NVME_NS_F_OPEN);
22114a15838fSnonaka }
2212e7c0cc5dSnonaka return 0;
2213e7c0cc5dSnonaka }
2214e7c0cc5dSnonaka
2215e7c0cc5dSnonaka /*
2216e7c0cc5dSnonaka * Accept the last close on the control device.
2217e7c0cc5dSnonaka */
2218e7c0cc5dSnonaka int
nvmeclose(dev_t dev,int flag,int mode,struct lwp * l)2219e7c0cc5dSnonaka nvmeclose(dev_t dev, int flag, int mode, struct lwp *l)
2220e7c0cc5dSnonaka {
2221e7c0cc5dSnonaka struct nvme_softc *sc;
22224a15838fSnonaka int unit = minor(dev) / 0x10000;
22234a15838fSnonaka int nsid = minor(dev) & 0xffff;
22244a15838fSnonaka int nsidx;
2225e7c0cc5dSnonaka
2226e7c0cc5dSnonaka sc = device_lookup_private(&nvme_cd, unit);
2227e7c0cc5dSnonaka if (sc == NULL)
2228e7c0cc5dSnonaka return ENXIO;
2229e7c0cc5dSnonaka
22304a15838fSnonaka if (nsid == 0) {
22314a15838fSnonaka /* controller */
2232e7c0cc5dSnonaka CLR(sc->sc_flags, NVME_F_OPEN);
22334a15838fSnonaka } else {
22344a15838fSnonaka /* namespace */
22354a15838fSnonaka nsidx = nsid - 1;
22364a15838fSnonaka if (nsidx >= sc->sc_nn)
22374a15838fSnonaka return ENXIO;
22384a15838fSnonaka CLR(sc->sc_namespaces[nsidx].flags, NVME_NS_F_OPEN);
22394a15838fSnonaka }
22404a15838fSnonaka
2241e7c0cc5dSnonaka return 0;
2242e7c0cc5dSnonaka }
2243e7c0cc5dSnonaka
2244e7c0cc5dSnonaka /*
2245e7c0cc5dSnonaka * Handle control operations.
2246e7c0cc5dSnonaka */
2247e7c0cc5dSnonaka int
nvmeioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)2248e7c0cc5dSnonaka nvmeioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
2249e7c0cc5dSnonaka {
2250e7c0cc5dSnonaka struct nvme_softc *sc;
22514a15838fSnonaka int unit = minor(dev) / 0x10000;
22524a15838fSnonaka int nsid = minor(dev) & 0xffff;
2253e7c0cc5dSnonaka struct nvme_pt_command *pt;
2254e7c0cc5dSnonaka
2255e7c0cc5dSnonaka sc = device_lookup_private(&nvme_cd, unit);
2256e7c0cc5dSnonaka if (sc == NULL)
2257e7c0cc5dSnonaka return ENXIO;
2258e7c0cc5dSnonaka
2259e7c0cc5dSnonaka switch (cmd) {
2260e7c0cc5dSnonaka case NVME_PASSTHROUGH_CMD:
22614a15838fSnonaka pt = data;
22624a15838fSnonaka return nvme_command_passthrough(sc, data,
22632437fdbaSmlelstv nsid == 0 ? pt->cmd.nsid : (uint32_t)nsid, l, nsid == 0);
2264e7c0cc5dSnonaka }
2265e7c0cc5dSnonaka
2266e7c0cc5dSnonaka return ENOTTY;
2267e7c0cc5dSnonaka }
2268