diff options
author | Stefan Fritsch <sf@cvs.openbsd.org> | 2017-05-27 12:40:52 +0000 |
---|---|---|
committer | Stefan Fritsch <sf@cvs.openbsd.org> | 2017-05-27 12:40:52 +0000 |
commit | 76064ec4428b2a06ce17ee4ca8edfb2b5a0a635d (patch) | |
tree | cf557ebbfd0df866ab2193c3e5c778b4701a4dd9 /sys/dev/ic/nvme.c | |
parent | a8c3f42959741b01ee7f7921bc7100caf5f2d62d (diff) |
nvme: Add suspend/resume code
Based on an initial patch by ehrhardt@ . Thanks to claudio@ for testing
and deraadt@ for advice.
"go ahead" deraadt@
Diffstat (limited to 'sys/dev/ic/nvme.c')
-rw-r--r-- | sys/dev/ic/nvme.c | 80 |
1 files changed, 75 insertions, 5 deletions
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c index 3c4b48fd822..cc40b229853 100644 --- a/sys/dev/ic/nvme.c +++ b/sys/dev/ic/nvme.c @@ -1,4 +1,4 @@ -/* $OpenBSD: nvme.c,v 1.55 2017/05/12 22:16:54 jcs Exp $ */ +/* $OpenBSD: nvme.c,v 1.56 2017/05/27 12:40:51 sf Exp $ */ /* * Copyright (c) 2014 David Gwynne <dlg@openbsd.org> @@ -45,6 +45,7 @@ int nvme_ready(struct nvme_softc *, u_int32_t); int nvme_enable(struct nvme_softc *, u_int); int nvme_disable(struct nvme_softc *); int nvme_shutdown(struct nvme_softc *); +int nvme_resume(struct nvme_softc *); void nvme_dumpregs(struct nvme_softc *); int nvme_identify(struct nvme_softc *, u_int); @@ -68,6 +69,7 @@ void nvme_empty_done(struct nvme_softc *, struct nvme_ccb *, struct nvme_queue * nvme_q_alloc(struct nvme_softc *, u_int16_t, u_int, u_int); int nvme_q_create(struct nvme_softc *, struct nvme_queue *); +int nvme_q_reset(struct nvme_softc *, struct nvme_queue *); int nvme_q_delete(struct nvme_softc *, struct nvme_queue *); void nvme_q_submit(struct nvme_softc *, struct nvme_queue *, struct nvme_ccb *, @@ -264,7 +266,6 @@ nvme_attach(struct nvme_softc *sc) struct scsibus_attach_args saa; u_int64_t cap; u_int32_t reg; - u_int dstrd; u_int mps = PAGE_SHIFT; mtx_init(&sc->sc_ccb_mtx, IPL_BIO); @@ -280,7 +281,7 @@ nvme_attach(struct nvme_softc *sc) printf(", NVMe %d.%d\n", NVME_VS_MJR(reg), NVME_VS_MNR(reg)); cap = nvme_read8(sc, NVME_CAP); - dstrd = NVME_CAP_DSTRD(cap); + sc->sc_dstrd = NVME_CAP_DSTRD(cap); if (NVME_CAP_MPSMIN(cap) > PAGE_SHIFT) { printf("%s: NVMe minimum page size %u " "is greater than CPU page size %u\n", DEVNAME(sc), @@ -292,6 +293,7 @@ nvme_attach(struct nvme_softc *sc) sc->sc_rdy_to = NVME_CAP_TO(cap); sc->sc_mps = 1 << mps; + sc->sc_mps_bits = mps; sc->sc_mdts = MAXPHYS; sc->sc_max_sgl = 2; @@ -300,7 +302,7 @@ nvme_attach(struct nvme_softc *sc) return (1); } - sc->sc_admin_q = nvme_q_alloc(sc, NVME_ADMIN_Q, 128, dstrd); + sc->sc_admin_q = nvme_q_alloc(sc, NVME_ADMIN_Q, 128, sc->sc_dstrd); if (sc->sc_admin_q == NULL) { printf("%s: unable to allocate admin queue\n", DEVNAME(sc)); return (1); @@ -330,7 +332,7 @@ nvme_attach(struct nvme_softc *sc) goto free_admin_q; } - sc->sc_q = nvme_q_alloc(sc, 1, 128, dstrd); + sc->sc_q = nvme_q_alloc(sc, 1, 128, sc->sc_dstrd); if (sc->sc_q == NULL) { printf("%s: unable to allocate io q\n", DEVNAME(sc)); goto disable; @@ -375,6 +377,47 @@ free_admin_q: } int +nvme_resume(struct nvme_softc *sc) +{ + if (nvme_disable(sc) != 0) { + printf("%s: unable to disable controller\n", DEVNAME(sc)); + return (1); + } + + if (nvme_q_reset(sc, sc->sc_admin_q) != 0) { + printf("%s: unable to reset admin queue\n", DEVNAME(sc)); + return (1); + } + + if (nvme_enable(sc, sc->sc_mps_bits) != 0) { + printf("%s: unable to enable controller\n", DEVNAME(sc)); + return (1); + } + + sc->sc_q = nvme_q_alloc(sc, 1, 128, sc->sc_dstrd); + if (sc->sc_q == NULL) { + printf("%s: unable to allocate io q\n", DEVNAME(sc)); + goto disable; + } + + if (nvme_q_create(sc, sc->sc_q) != 0) { + printf("%s: unable to create io q\n", DEVNAME(sc)); + goto free_q; + } + + nvme_write4(sc, NVME_INTMC, 1); + + return (0); + +free_q: + nvme_q_free(sc, sc->sc_q); +disable: + nvme_disable(sc); + + return (1); +} + +int nvme_scsi_probe(struct scsi_link *link) { struct nvme_softc *sc = link->adapter_softc; @@ -469,6 +512,11 @@ nvme_activate(struct nvme_softc *sc, int act) rv = config_activate_children(&sc->sc_dev, act); nvme_shutdown(sc); break; + case DVACT_RESUME: + rv = nvme_resume(sc); + if (rv == 0) + rv = config_activate_children(&sc->sc_dev, act); + break; default: rv = config_activate_children(&sc->sc_dev, act); break; @@ -1079,6 +1127,8 @@ nvme_q_delete(struct nvme_softc *sc, struct nvme_queue *q) if (rv != 0) goto fail; + nvme_q_free(sc, q); + fail: scsi_io_put(&sc->sc_iopool, ccb); return (rv); @@ -1208,6 +1258,7 @@ nvme_q_alloc(struct nvme_softc *sc, u_int16_t id, u_int entries, u_int dstrd) mtx_init(&q->q_cq_mtx, IPL_BIO); q->q_sqtdbl = NVME_SQTDBL(id, dstrd); q->q_cqhdbl = NVME_CQHDBL(id, dstrd); + q->q_id = id; q->q_entries = entries; q->q_sq_tail = 0; @@ -1227,6 +1278,25 @@ free: return (NULL); } +int +nvme_q_reset(struct nvme_softc *sc, struct nvme_queue *q) +{ + memset(NVME_DMA_KVA(q->q_sq_dmamem), 0, NVME_DMA_LEN(q->q_sq_dmamem)); + memset(NVME_DMA_KVA(q->q_cq_dmamem), 0, NVME_DMA_LEN(q->q_cq_dmamem)); + + q->q_sqtdbl = NVME_SQTDBL(q->q_id, sc->sc_dstrd); + q->q_cqhdbl = NVME_CQHDBL(q->q_id, sc->sc_dstrd); + + q->q_sq_tail = 0; + q->q_cq_head = 0; + q->q_cq_phase = NVME_CQE_PHASE; + + nvme_dmamem_sync(sc, q->q_sq_dmamem, BUS_DMASYNC_PREWRITE); + nvme_dmamem_sync(sc, q->q_cq_dmamem, BUS_DMASYNC_PREREAD); + + return (0); +} + void nvme_q_free(struct nvme_softc *sc, struct nvme_queue *q) { |