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