xref: /openbsd-src/sys/arch/arm64/dev/aplns.c (revision 4e1ee0786f11cc571bd0be17d38e46f635c719fc)
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