1 /* $OpenBSD: aplns.c,v 1.5 2021/08/29 11:23:29 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2014, 2021 David Gwynne <dlg@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/buf.h> 21 #include <sys/kernel.h> 22 #include <sys/malloc.h> 23 #include <sys/device.h> 24 #include <sys/timeout.h> 25 #include <sys/queue.h> 26 #include <sys/mutex.h> 27 #include <sys/pool.h> 28 29 #include <machine/bus.h> 30 #include <machine/fdt.h> 31 32 #include <dev/ofw/openfirm.h> 33 #include <dev/ofw/ofw_misc.h> 34 #include <dev/ofw/fdt.h> 35 36 #include <scsi/scsi_all.h> 37 #include <scsi/scsiconf.h> 38 39 #include <dev/ic/nvmereg.h> 40 #include <dev/ic/nvmevar.h> 41 42 #define ANS_MODESEL_REG 0x01304 43 #define ANS_LINEAR_ASQ_DB 0x2490c 44 #define ANS_LINEAR_IOSQ_DB 0x24910 45 46 #define ANS_NVMMU_NUM 0x28100 47 #define ANS_NVMMU_BASE_ASQ 0x28108 48 #define ANS_NVMMU_BASE_IOSQ 0x28110 49 #define ANS_NVMMU_TCB_INVAL 0x28118 50 #define ANS_NVMMU_TCB_STAT 0x28120 51 52 #define ANS_NVMMU_TCB_SIZE 0x4000 53 #define ANS_NVMMU_TCB_PITCH 0x80 54 55 struct ans_nvmmu_tcb { 56 uint8_t tcb_opcode; 57 uint8_t tcb_flags; 58 #define ANS_NVMMU_TCB_WRITE (1 << 0) 59 #define ANS_NVMMU_TCB_READ (1 << 1) 60 uint8_t tcb_cid; 61 uint8_t tcb_pad0[1]; 62 63 uint32_t tcb_prpl_len; 64 uint8_t tcb_pad1[16]; 65 66 uint64_t tcb_prp[2]; 67 }; 68 69 int aplns_match(struct device *, void *, void *); 70 void aplns_attach(struct device *, struct device *, void *); 71 72 struct cfattach aplns_ca = { 73 sizeof(struct device), 74 aplns_match, 75 aplns_attach 76 }; 77 78 struct cfdriver aplns_cd = { 79 NULL, "aplns", DV_DULL 80 }; 81 82 int 83 aplns_match(struct device *parent, void *match, void *aux) 84 { 85 struct fdt_attach_args *faa = aux; 86 87 return (OF_is_compatible(faa->fa_node, "apple,nvme-m1")); 88 } 89 90 void 91 aplns_attach(struct device *parent, struct device *self, void *aux) 92 { 93 struct fdt_attach_args *faa = aux; 94 95 printf("\n"); 96 97 config_found(self, faa, NULL); 98 } 99 100 struct nvme_ans_softc { 101 struct nvme_softc asc_nvme; 102 103 struct nvme_dmamem *asc_nvmmu; 104 }; 105 106 int nvme_ans_match(struct device *, void *, void *); 107 void nvme_ans_attach(struct device *, struct device *, void *); 108 109 struct cfattach nvme_ans_ca = { 110 sizeof(struct nvme_ans_softc), 111 nvme_ans_match, 112 nvme_ans_attach, 113 }; 114 115 void nvme_ans_enable(struct nvme_softc *); 116 117 int nvme_ans_q_alloc(struct nvme_softc *, 118 struct nvme_queue *); 119 void nvme_ans_q_free(struct nvme_softc *, 120 struct nvme_queue *); 121 122 uint32_t nvme_ans_sq_enter(struct nvme_softc *, 123 struct nvme_queue *, struct nvme_ccb *); 124 void nvme_ans_sq_leave(struct nvme_softc *, 125 struct nvme_queue *, struct nvme_ccb *); 126 127 void nvme_ans_cq_done(struct nvme_softc *, 128 struct nvme_queue *, struct nvme_ccb *); 129 130 static const struct nvme_ops nvme_ans_ops = { 131 .op_enable = nvme_ans_enable, 132 133 .op_q_alloc = nvme_ans_q_alloc, 134 .op_q_free = nvme_ans_q_free, 135 136 .op_sq_enter = nvme_ans_sq_enter, 137 .op_sq_leave = nvme_ans_sq_leave, 138 .op_sq_enter_locked = nvme_ans_sq_enter, 139 .op_sq_leave_locked = nvme_ans_sq_leave, 140 141 .op_cq_done = nvme_ans_cq_done, 142 }; 143 144 int 145 nvme_ans_match(struct device *parent, void *match, void *aux) 146 { 147 struct fdt_attach_args *faa = aux; 148 149 return (OF_is_compatible(faa->fa_node, "apple,nvme-m1")); 150 } 151 152 void 153 nvme_ans_attach(struct device *parent, struct device *self, void *aux) 154 { 155 struct nvme_ans_softc *asc = (struct nvme_ans_softc *)self; 156 struct nvme_softc *sc = &asc->asc_nvme; 157 struct fdt_attach_args *faa = aux; 158 159 printf(": "); 160 161 if (bus_space_map(faa->fa_iot, 162 faa->fa_reg[0].addr, faa->fa_reg[0].size, 163 0, &sc->sc_ioh) != 0) { 164 printf("unable to map registers\n"); 165 return; 166 } 167 168 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, 169 nvme_intr, sc, sc->sc_dev.dv_xname); 170 if (sc->sc_ih == NULL) { 171 printf("unable to establish interrupt\n"); 172 goto unmap; 173 } 174 175 sc->sc_dmat = faa->fa_dmat; 176 sc->sc_iot = faa->fa_iot; 177 sc->sc_ios = faa->fa_reg[0].size; 178 sc->sc_ops = &nvme_ans_ops; 179 sc->sc_openings = 1; 180 181 if (nvme_attach(sc) != 0) { 182 /* error printed by nvme_attach() */ 183 goto disestablish; 184 } 185 186 return; 187 188 disestablish: 189 fdt_intr_disestablish(sc->sc_ih); 190 sc->sc_ih = NULL; 191 192 unmap: 193 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 194 sc->sc_ios = 0; 195 } 196 197 int 198 nvme_ans_q_alloc(struct nvme_softc *sc, 199 struct nvme_queue *q) 200 { 201 bus_size_t db, base; 202 203 KASSERT(q->q_entries <= (ANS_NVMMU_TCB_SIZE / ANS_NVMMU_TCB_PITCH)); 204 205 q->q_nvmmu_dmamem = nvme_dmamem_alloc(sc, ANS_NVMMU_TCB_SIZE); 206 if (q->q_nvmmu_dmamem == NULL) 207 return (-1); 208 209 memset(NVME_DMA_KVA(q->q_nvmmu_dmamem), 210 0, NVME_DMA_LEN(q->q_nvmmu_dmamem)); 211 212 switch (q->q_id) { 213 case NVME_IO_Q: 214 db = ANS_LINEAR_IOSQ_DB; 215 base = ANS_NVMMU_BASE_IOSQ; 216 break; 217 case NVME_ADMIN_Q: 218 db = ANS_LINEAR_ASQ_DB; 219 base = ANS_NVMMU_BASE_ASQ; 220 break; 221 default: 222 panic("unsupported queue id %u", q->q_id); 223 /* NOTREACHED */ 224 } 225 226 q->q_sqtdbl = db; 227 228 nvme_dmamem_sync(sc, q->q_nvmmu_dmamem, BUS_DMASYNC_PREWRITE); 229 nvme_write8(sc, base, NVME_DMA_DVA(q->q_nvmmu_dmamem)); 230 231 return (0); 232 } 233 234 void 235 nvme_ans_enable(struct nvme_softc *sc) 236 { 237 nvme_write4(sc, ANS_NVMMU_NUM, 238 (ANS_NVMMU_TCB_SIZE / ANS_NVMMU_TCB_PITCH) - 1); 239 nvme_write4(sc, ANS_MODESEL_REG, 0); 240 } 241 242 void 243 nvme_ans_q_free(struct nvme_softc *sc, 244 struct nvme_queue *q) 245 { 246 nvme_dmamem_sync(sc, q->q_nvmmu_dmamem, BUS_DMASYNC_POSTWRITE); 247 nvme_dmamem_free(sc, q->q_nvmmu_dmamem); 248 } 249 250 uint32_t 251 nvme_ans_sq_enter(struct nvme_softc *sc, 252 struct nvme_queue *q, struct nvme_ccb *ccb) 253 { 254 return (ccb->ccb_id); 255 } 256 257 static inline struct ans_nvmmu_tcb * 258 nvme_ans_tcb(struct nvme_queue *q, unsigned int qid) 259 { 260 caddr_t ptr = NVME_DMA_KVA(q->q_nvmmu_dmamem); 261 ptr += qid * ANS_NVMMU_TCB_PITCH; 262 return ((struct ans_nvmmu_tcb *)ptr); 263 } 264 265 void 266 nvme_ans_sq_leave(struct nvme_softc *sc, 267 struct nvme_queue *q, struct nvme_ccb *ccb) 268 { 269 unsigned int id = ccb->ccb_id; 270 struct nvme_sqe_io *sqe; 271 struct ans_nvmmu_tcb *tcb = nvme_ans_tcb(q, id); 272 273 sqe = NVME_DMA_KVA(q->q_sq_dmamem); 274 sqe += id; 275 276 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 277 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_POSTWRITE); 278 279 memset(tcb, 0, sizeof(*tcb)); 280 tcb->tcb_opcode = sqe->opcode; 281 tcb->tcb_flags = ANS_NVMMU_TCB_WRITE | ANS_NVMMU_TCB_READ; 282 tcb->tcb_cid = id; 283 tcb->tcb_prpl_len = sqe->nlb; 284 tcb->tcb_prp[0] = sqe->entry.prp[0]; 285 tcb->tcb_prp[1] = sqe->entry.prp[1]; 286 287 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 288 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_PREWRITE); 289 290 nvme_write4(sc, q->q_sqtdbl, id); 291 } 292 293 void 294 nvme_ans_cq_done(struct nvme_softc *sc, 295 struct nvme_queue *q, struct nvme_ccb *ccb) 296 { 297 unsigned int id = ccb->ccb_id; 298 struct ans_nvmmu_tcb *tcb = nvme_ans_tcb(q, id); 299 uint32_t stat; 300 301 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 302 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_POSTWRITE); 303 memset(tcb, 0, sizeof(*tcb)); 304 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 305 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_PREWRITE); 306 307 nvme_write4(sc, ANS_NVMMU_TCB_INVAL, id); 308 stat = nvme_read4(sc, ANS_NVMMU_TCB_STAT); 309 if (stat != 0) { 310 printf("%s: nvmmu tcp stat is non-zero: 0x%08x\n", 311 DEVNAME(sc), stat); 312 } 313 } 314