1 /* $OpenBSD: aplns.c,v 1.6 2021/12/09 11:38:27 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/ofw_power.h> 35 #include <dev/ofw/fdt.h> 36 37 #include <scsi/scsi_all.h> 38 #include <scsi/scsiconf.h> 39 40 #include <dev/ic/nvmereg.h> 41 #include <dev/ic/nvmevar.h> 42 43 #define ANS_MODESEL_REG 0x01304 44 #define ANS_LINEAR_ASQ_DB 0x2490c 45 #define ANS_LINEAR_IOSQ_DB 0x24910 46 47 #define ANS_NVMMU_NUM 0x28100 48 #define ANS_NVMMU_BASE_ASQ 0x28108 49 #define ANS_NVMMU_BASE_IOSQ 0x28110 50 #define ANS_NVMMU_TCB_INVAL 0x28118 51 #define ANS_NVMMU_TCB_STAT 0x28120 52 53 #define ANS_NVMMU_TCB_SIZE 0x4000 54 #define ANS_NVMMU_TCB_PITCH 0x80 55 56 struct ans_nvmmu_tcb { 57 uint8_t tcb_opcode; 58 uint8_t tcb_flags; 59 #define ANS_NVMMU_TCB_WRITE (1 << 0) 60 #define ANS_NVMMU_TCB_READ (1 << 1) 61 uint8_t tcb_cid; 62 uint8_t tcb_pad0[1]; 63 64 uint32_t tcb_prpl_len; 65 uint8_t tcb_pad1[16]; 66 67 uint64_t tcb_prp[2]; 68 }; 69 70 int aplns_match(struct device *, void *, void *); 71 void aplns_attach(struct device *, struct device *, void *); 72 73 struct cfattach aplns_ca = { 74 sizeof(struct device), 75 aplns_match, 76 aplns_attach 77 }; 78 79 struct cfdriver aplns_cd = { 80 NULL, "aplns", DV_DULL 81 }; 82 83 int 84 aplns_match(struct device *parent, void *match, void *aux) 85 { 86 struct fdt_attach_args *faa = aux; 87 88 return (OF_is_compatible(faa->fa_node, "apple,nvme-m1")); 89 } 90 91 void 92 aplns_attach(struct device *parent, struct device *self, void *aux) 93 { 94 struct fdt_attach_args *faa = aux; 95 96 printf("\n"); 97 98 config_found(self, faa, NULL); 99 } 100 101 struct nvme_ans_softc { 102 struct nvme_softc asc_nvme; 103 104 struct nvme_dmamem *asc_nvmmu; 105 }; 106 107 int nvme_ans_match(struct device *, void *, void *); 108 void nvme_ans_attach(struct device *, struct device *, void *); 109 110 struct cfattach nvme_ans_ca = { 111 sizeof(struct nvme_ans_softc), 112 nvme_ans_match, 113 nvme_ans_attach, 114 }; 115 116 void nvme_ans_enable(struct nvme_softc *); 117 118 int nvme_ans_q_alloc(struct nvme_softc *, 119 struct nvme_queue *); 120 void nvme_ans_q_free(struct nvme_softc *, 121 struct nvme_queue *); 122 123 uint32_t nvme_ans_sq_enter(struct nvme_softc *, 124 struct nvme_queue *, struct nvme_ccb *); 125 void nvme_ans_sq_leave(struct nvme_softc *, 126 struct nvme_queue *, struct nvme_ccb *); 127 128 void nvme_ans_cq_done(struct nvme_softc *, 129 struct nvme_queue *, struct nvme_ccb *); 130 131 static const struct nvme_ops nvme_ans_ops = { 132 .op_enable = nvme_ans_enable, 133 134 .op_q_alloc = nvme_ans_q_alloc, 135 .op_q_free = nvme_ans_q_free, 136 137 .op_sq_enter = nvme_ans_sq_enter, 138 .op_sq_leave = nvme_ans_sq_leave, 139 .op_sq_enter_locked = nvme_ans_sq_enter, 140 .op_sq_leave_locked = nvme_ans_sq_leave, 141 142 .op_cq_done = nvme_ans_cq_done, 143 }; 144 145 int 146 nvme_ans_match(struct device *parent, void *match, void *aux) 147 { 148 struct fdt_attach_args *faa = aux; 149 150 return (OF_is_compatible(faa->fa_node, "apple,nvme-m1")); 151 } 152 153 void 154 nvme_ans_attach(struct device *parent, struct device *self, void *aux) 155 { 156 struct nvme_ans_softc *asc = (struct nvme_ans_softc *)self; 157 struct nvme_softc *sc = &asc->asc_nvme; 158 struct fdt_attach_args *faa = aux; 159 160 printf(": "); 161 162 if (bus_space_map(faa->fa_iot, 163 faa->fa_reg[0].addr, faa->fa_reg[0].size, 164 0, &sc->sc_ioh) != 0) { 165 printf("unable to map registers\n"); 166 return; 167 } 168 169 power_domain_enable(faa->fa_node); 170 171 sc->sc_ih = fdt_intr_establish(faa->fa_node, IPL_BIO, 172 nvme_intr, sc, sc->sc_dev.dv_xname); 173 if (sc->sc_ih == NULL) { 174 printf("unable to establish interrupt\n"); 175 goto unmap; 176 } 177 178 sc->sc_dmat = faa->fa_dmat; 179 sc->sc_iot = faa->fa_iot; 180 sc->sc_ios = faa->fa_reg[0].size; 181 sc->sc_ops = &nvme_ans_ops; 182 sc->sc_openings = 1; 183 184 if (nvme_attach(sc) != 0) { 185 /* error printed by nvme_attach() */ 186 goto disestablish; 187 } 188 189 return; 190 191 disestablish: 192 fdt_intr_disestablish(sc->sc_ih); 193 sc->sc_ih = NULL; 194 195 unmap: 196 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 197 sc->sc_ios = 0; 198 } 199 200 int 201 nvme_ans_q_alloc(struct nvme_softc *sc, 202 struct nvme_queue *q) 203 { 204 bus_size_t db, base; 205 206 KASSERT(q->q_entries <= (ANS_NVMMU_TCB_SIZE / ANS_NVMMU_TCB_PITCH)); 207 208 q->q_nvmmu_dmamem = nvme_dmamem_alloc(sc, ANS_NVMMU_TCB_SIZE); 209 if (q->q_nvmmu_dmamem == NULL) 210 return (-1); 211 212 memset(NVME_DMA_KVA(q->q_nvmmu_dmamem), 213 0, NVME_DMA_LEN(q->q_nvmmu_dmamem)); 214 215 switch (q->q_id) { 216 case NVME_IO_Q: 217 db = ANS_LINEAR_IOSQ_DB; 218 base = ANS_NVMMU_BASE_IOSQ; 219 break; 220 case NVME_ADMIN_Q: 221 db = ANS_LINEAR_ASQ_DB; 222 base = ANS_NVMMU_BASE_ASQ; 223 break; 224 default: 225 panic("unsupported queue id %u", q->q_id); 226 /* NOTREACHED */ 227 } 228 229 q->q_sqtdbl = db; 230 231 nvme_dmamem_sync(sc, q->q_nvmmu_dmamem, BUS_DMASYNC_PREWRITE); 232 nvme_write8(sc, base, NVME_DMA_DVA(q->q_nvmmu_dmamem)); 233 234 return (0); 235 } 236 237 void 238 nvme_ans_enable(struct nvme_softc *sc) 239 { 240 nvme_write4(sc, ANS_NVMMU_NUM, 241 (ANS_NVMMU_TCB_SIZE / ANS_NVMMU_TCB_PITCH) - 1); 242 nvme_write4(sc, ANS_MODESEL_REG, 0); 243 } 244 245 void 246 nvme_ans_q_free(struct nvme_softc *sc, 247 struct nvme_queue *q) 248 { 249 nvme_dmamem_sync(sc, q->q_nvmmu_dmamem, BUS_DMASYNC_POSTWRITE); 250 nvme_dmamem_free(sc, q->q_nvmmu_dmamem); 251 } 252 253 uint32_t 254 nvme_ans_sq_enter(struct nvme_softc *sc, 255 struct nvme_queue *q, struct nvme_ccb *ccb) 256 { 257 return (ccb->ccb_id); 258 } 259 260 static inline struct ans_nvmmu_tcb * 261 nvme_ans_tcb(struct nvme_queue *q, unsigned int qid) 262 { 263 caddr_t ptr = NVME_DMA_KVA(q->q_nvmmu_dmamem); 264 ptr += qid * ANS_NVMMU_TCB_PITCH; 265 return ((struct ans_nvmmu_tcb *)ptr); 266 } 267 268 void 269 nvme_ans_sq_leave(struct nvme_softc *sc, 270 struct nvme_queue *q, struct nvme_ccb *ccb) 271 { 272 unsigned int id = ccb->ccb_id; 273 struct nvme_sqe_io *sqe; 274 struct ans_nvmmu_tcb *tcb = nvme_ans_tcb(q, id); 275 276 sqe = NVME_DMA_KVA(q->q_sq_dmamem); 277 sqe += id; 278 279 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 280 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_POSTWRITE); 281 282 memset(tcb, 0, sizeof(*tcb)); 283 tcb->tcb_opcode = sqe->opcode; 284 tcb->tcb_flags = ANS_NVMMU_TCB_WRITE | ANS_NVMMU_TCB_READ; 285 tcb->tcb_cid = id; 286 tcb->tcb_prpl_len = sqe->nlb; 287 tcb->tcb_prp[0] = sqe->entry.prp[0]; 288 tcb->tcb_prp[1] = sqe->entry.prp[1]; 289 290 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 291 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_PREWRITE); 292 293 nvme_write4(sc, q->q_sqtdbl, id); 294 } 295 296 void 297 nvme_ans_cq_done(struct nvme_softc *sc, 298 struct nvme_queue *q, struct nvme_ccb *ccb) 299 { 300 unsigned int id = ccb->ccb_id; 301 struct ans_nvmmu_tcb *tcb = nvme_ans_tcb(q, id); 302 uint32_t stat; 303 304 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 305 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_POSTWRITE); 306 memset(tcb, 0, sizeof(*tcb)); 307 bus_dmamap_sync(sc->sc_dmat, NVME_DMA_MAP(q->q_nvmmu_dmamem), 308 ANS_NVMMU_TCB_PITCH * id, sizeof(*tcb), BUS_DMASYNC_PREWRITE); 309 310 nvme_write4(sc, ANS_NVMMU_TCB_INVAL, id); 311 stat = nvme_read4(sc, ANS_NVMMU_TCB_STAT); 312 if (stat != 0) { 313 printf("%s: nvmmu tcp stat is non-zero: 0x%08x\n", 314 DEVNAME(sc), stat); 315 } 316 } 317