xref: /freebsd/sys/dev/beri/beri_ring.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
1 /*-
2  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3  * All rights reserved.
4  *
5  * This software was developed by SRI International and the University of
6  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7  * ("CTSRD"), as part of the DARPA CRASH research programme.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 /*
32  * SRI-Cambridge BERI soft processor <-> ARM core ring buffer.
33  */
34 
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/malloc.h>
44 #include <sys/rman.h>
45 #include <sys/timeet.h>
46 #include <sys/timetc.h>
47 #include <sys/conf.h>
48 #include <sys/uio.h>
49 #include <sys/stat.h>
50 #include <sys/time.h>
51 #include <sys/event.h>
52 #include <sys/selinfo.h>
53 
54 #include <dev/fdt/fdt_common.h>
55 #include <dev/ofw/openfirm.h>
56 #include <dev/ofw/ofw_bus.h>
57 #include <dev/ofw/ofw_bus_subr.h>
58 
59 #include <machine/bus.h>
60 #include <machine/fdt.h>
61 #include <machine/cpu.h>
62 #include <machine/intr.h>
63 
64 #define READ4(_sc, _reg) \
65 	bus_read_4((_sc)->res[0], _reg)
66 #define WRITE4(_sc, _reg, _val) \
67 	bus_write_4((_sc)->res[0], _reg, _val)
68 
69 #define CDES_INT_EN		(1 << 15)
70 #define CDES_CAUSE_MASK		0x3
71 #define CDES_CAUSE_SHIFT	13
72 #define DEVNAME_MAXLEN		256
73 
74 typedef struct
75 {
76 	uint16_t cdes;
77 	uint16_t interrupt_level;
78 	uint16_t in;
79 	uint16_t out;
80 } control_reg_t;
81 
82 struct beri_softc {
83 	struct resource		*res[3];
84 	bus_space_tag_t		bst;
85 	bus_space_handle_t	bsh;
86 	struct cdev		*cdev;
87 	device_t		dev;
88 	void			*read_ih;
89 	void			*write_ih;
90 	struct selinfo		beri_rsel;
91 	struct mtx		beri_mtx;
92 	int			opened;
93 
94 	char			devname[DEVNAME_MAXLEN];
95 	int			control_read;
96 	int			control_write;
97 	int			data_read;
98 	int			data_write;
99 	int			data_size;
100 };
101 
102 static struct resource_spec beri_spec[] = {
103 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
104 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
105 	{ SYS_RES_IRQ,		1,	RF_ACTIVE },
106 	{ -1, 0 }
107 };
108 
109 static control_reg_t
110 get_control_reg(struct beri_softc *sc, int dir)
111 {
112 	uint32_t offset;
113 	uint16_t dst[4];
114 	control_reg_t c;
115 	uint16_t *cp;
116 	int i;
117 
118 	cp = (uint16_t *)&c;
119 
120 	offset = dir ? sc->control_write : sc->control_read;
121 	((uint32_t *)dst)[0] = READ4(sc, offset);
122 	((uint32_t *)dst)[1] = READ4(sc, offset + 4);
123 
124 	for (i = 0; i < 4; i++)
125 		cp[i] = dst[3 - i];
126 
127 	return (c);
128 }
129 
130 static void
131 set_control_reg(struct beri_softc *sc, int dir, control_reg_t *c)
132 {
133 	uint32_t offset;
134 	uint16_t src[4];
135 	uint16_t *cp;
136 	int i;
137 
138 	cp = (uint16_t *)c;
139 
140 	for (i = 0; i < 4; i++)
141 		src[3 - i] = cp[i];
142 
143 	offset = dir ? sc->control_write : sc->control_read;
144 	WRITE4(sc, offset + 0, ((uint32_t *)src)[0]);
145 	WRITE4(sc, offset + 4, ((uint32_t *)src)[1]);
146 }
147 
148 static int
149 get_stock(struct beri_softc *sc, int dir, control_reg_t *c)
150 {
151 	uint32_t fill;
152 
153 	fill = (c->in - c->out + sc->data_size) % sc->data_size;
154 
155 	if (dir)
156 		return (sc->data_size - fill - 1);
157 	else
158 		return (fill);
159 }
160 
161 static void
162 beri_intr_write(void *arg)
163 {
164 	struct beri_softc *sc;
165 	control_reg_t c;
166 
167 	sc = arg;
168 
169 	c = get_control_reg(sc, 1);
170 	if (c.cdes & CDES_INT_EN) {
171 		c.cdes &= ~(CDES_INT_EN);
172 		set_control_reg(sc, 1, &c);
173 	}
174 
175 	mtx_lock(&sc->beri_mtx);
176 	selwakeuppri(&sc->beri_rsel, PZERO + 1);
177 	KNOTE_LOCKED(&sc->beri_rsel.si_note, 0);
178 	mtx_unlock(&sc->beri_mtx);
179 }
180 
181 static void
182 beri_intr_read(void *arg)
183 {
184 	struct beri_softc *sc;
185 	control_reg_t c;
186 
187 	sc = arg;
188 
189 	c = get_control_reg(sc, 0);
190 	if (c.cdes & CDES_INT_EN) {
191 		c.cdes &= ~(CDES_INT_EN);
192 		set_control_reg(sc, 0, &c);
193 	}
194 
195 	mtx_lock(&sc->beri_mtx);
196 	selwakeuppri(&sc->beri_rsel, PZERO + 1);
197 	KNOTE_LOCKED(&sc->beri_rsel.si_note, 0);
198 	mtx_unlock(&sc->beri_mtx);
199 }
200 
201 static int
202 beri_open(struct cdev *dev, int flags __unused,
203     int fmt __unused, struct thread *td __unused)
204 {
205 	struct beri_softc *sc;
206 	control_reg_t c;
207 
208 	sc = dev->si_drv1;
209 
210 	if (sc->opened)
211 		return (1);
212 
213 	/* Setup interrupt handlers */
214 	if (bus_setup_intr(sc->dev, sc->res[1], INTR_TYPE_BIO | INTR_MPSAFE,
215 		NULL, beri_intr_read, sc, &sc->read_ih)) {
216 		device_printf(sc->dev, "Unable to setup read intr\n");
217 		return (1);
218 	}
219 	if (bus_setup_intr(sc->dev, sc->res[2], INTR_TYPE_BIO | INTR_MPSAFE,
220 		NULL, beri_intr_write, sc, &sc->write_ih)) {
221 		device_printf(sc->dev, "Unable to setup write intr\n");
222 		return (1);
223 	}
224 
225 	sc->opened = 1;
226 
227 	/* Clear write buffer */
228 	c = get_control_reg(sc, 1);
229 	c.in = c.out;
230 	c.cdes = 0;
231 	set_control_reg(sc, 1, &c);
232 
233 	/* Clear read buffer */
234 	c = get_control_reg(sc, 0);
235 	c.out = c.in;
236 	c.cdes = 0;
237 	set_control_reg(sc, 0, &c);
238 
239 	return (0);
240 }
241 
242 static int
243 beri_close(struct cdev *dev, int flags __unused,
244     int fmt __unused, struct thread *td __unused)
245 {
246 	struct beri_softc *sc;
247 
248 	sc = dev->si_drv1;
249 
250 	if (sc->opened) {
251 		sc->opened = 0;
252 
253 		/* Unsetup interrupt handlers */
254 		bus_teardown_intr(sc->dev, sc->res[1], sc->read_ih);
255 		bus_teardown_intr(sc->dev, sc->res[2], sc->write_ih);
256 	}
257 
258 	return (0);
259 }
260 
261 static int
262 beri_rdwr(struct cdev *dev, struct uio *uio, int ioflag)
263 {
264 	struct beri_softc *sc;
265 	uint32_t offset;
266 	control_reg_t c;
267 	uint16_t *ptr;
268 	uint8_t *dst;
269 	int stock;
270 	int dir;
271 	int amount;
272 	int count;
273 
274 	sc = dev->si_drv1;
275 
276 	dir = uio->uio_rw ? 1 : 0;
277 
278 	c = get_control_reg(sc, dir);
279 	stock = get_stock(sc, dir, &c);
280 	if (stock < uio->uio_resid) {
281 		device_printf(sc->dev, "Err: no data/space available\n");
282 		return (1);
283 	}
284 
285 	amount = uio->uio_resid;
286 	ptr = dir ? &c.in : &c.out;
287 	count = (sc->data_size - *ptr);
288 
289 	offset = dir ? sc->data_write : sc->data_read;
290 	dst = (uint8_t *)(sc->bsh + offset);
291 
292 	if (amount <= count) {
293 		uiomove(dst + *ptr, amount, uio);
294 	} else {
295 		uiomove(dst + *ptr, count, uio);
296 		uiomove(dst, (amount - count), uio);
297 	}
298 
299 	*ptr = (*ptr + amount) % sc->data_size;
300 	set_control_reg(sc, dir, &c);
301 
302 	return (0);
303 }
304 
305 static int
306 beri_kqread(struct knote *kn, long hint)
307 {
308 	struct beri_softc *sc;
309 	control_reg_t c;
310 	int stock;
311 
312 	sc = kn->kn_hook;
313 
314 	c = get_control_reg(sc, 0);
315 	stock = get_stock(sc, 0, &c);
316 	if (stock) {
317 		kn->kn_data = stock;
318 		return (1);
319 	}
320 
321 	kn->kn_data = 0;
322 
323 	/* Wait at least one new byte in buffer */
324 	c.interrupt_level = 1;
325 
326 	/* Enable interrupts */
327 	c.cdes |= (CDES_INT_EN);
328 	set_control_reg(sc, 0, &c);
329 
330 	return (0);
331 }
332 
333 static int
334 beri_kqwrite(struct knote *kn, long hint)
335 {
336 	struct beri_softc *sc;
337 	control_reg_t c;
338 	int stock;
339 
340 	sc = kn->kn_hook;
341 
342 	c = get_control_reg(sc, 1);
343 	stock = get_stock(sc, 1, &c);
344 	if (stock) {
345 		kn->kn_data = stock;
346 		return (1);
347 	}
348 
349 	kn->kn_data = 0;
350 
351 	/* Wait at least one free position in buffer */
352 	c.interrupt_level = sc->data_size - 2;
353 
354 	/* Enable interrupts */
355 	c.cdes |= (CDES_INT_EN);
356 	set_control_reg(sc, 1, &c);
357 
358 	return (0);
359 }
360 
361 static void
362 beri_kqdetach(struct knote *kn)
363 {
364 	struct beri_softc *sc;
365 
366 	sc = kn->kn_hook;
367 
368 	knlist_remove(&sc->beri_rsel.si_note, kn, 0);
369 }
370 
371 static struct filterops beri_read_filterops = {
372 	.f_isfd =       1,
373 	.f_attach =     NULL,
374 	.f_detach =     beri_kqdetach,
375 	.f_event =      beri_kqread,
376 };
377 
378 static struct filterops beri_write_filterops = {
379 	.f_isfd =       1,
380 	.f_attach =     NULL,
381 	.f_detach =     beri_kqdetach,
382 	.f_event =      beri_kqwrite,
383 };
384 
385 static int
386 beri_kqfilter(struct cdev *dev, struct knote *kn)
387 {
388 	struct beri_softc *sc;
389 
390 	sc = dev->si_drv1;
391 
392 	switch(kn->kn_filter) {
393 	case EVFILT_READ:
394 		kn->kn_fop = &beri_read_filterops;
395 		break;
396 	case EVFILT_WRITE:
397 		kn->kn_fop = &beri_write_filterops;
398 		break;
399 	default:
400 		return(EINVAL);
401 	}
402 
403 	kn->kn_hook = sc;
404 	knlist_add(&sc->beri_rsel.si_note, kn, 0);
405 
406 	return (0);
407 }
408 
409 static struct cdevsw beri_cdevsw = {
410 	.d_version =	D_VERSION,
411 	.d_open =	beri_open,
412 	.d_close =	beri_close,
413 	.d_write =	beri_rdwr,
414 	.d_read =	beri_rdwr,
415 	.d_kqfilter =	beri_kqfilter,
416 	.d_name =	"beri ring buffer",
417 };
418 
419 static int
420 parse_fdt(struct beri_softc *sc)
421 {
422 	pcell_t dts_value[0];
423 	phandle_t node;
424 	int len;
425 
426 	if ((node = ofw_bus_get_node(sc->dev)) == -1)
427 		return (ENXIO);
428 
429 	/* get device name */
430 	if (OF_getprop(ofw_bus_get_node(sc->dev), "device_name",
431 		&sc->devname, sizeof(sc->devname)) <= 0) {
432 		device_printf(sc->dev, "Can't get device_name\n");
433 		return (ENXIO);
434 	}
435 
436 	if ((len = OF_getproplen(node, "data_size")) <= 0)
437 		return (ENXIO);
438 	OF_getencprop(node, "data_size", dts_value, len);
439 	sc->data_size = dts_value[0];
440 
441 	if ((len = OF_getproplen(node, "data_read")) <= 0)
442 		return (ENXIO);
443 	OF_getencprop(node, "data_read", dts_value, len);
444 	sc->data_read = dts_value[0];
445 
446 	if ((len = OF_getproplen(node, "data_write")) <= 0)
447 		return (ENXIO);
448 	OF_getencprop(node, "data_write", dts_value, len);
449 	sc->data_write = dts_value[0];
450 
451 	if ((len = OF_getproplen(node, "control_read")) <= 0)
452 		return (ENXIO);
453 	OF_getencprop(node, "control_read", dts_value, len);
454 	sc->control_read = dts_value[0];
455 
456 	if ((len = OF_getproplen(node, "control_write")) <= 0)
457 		return (ENXIO);
458 	OF_getencprop(node, "control_write", dts_value, len);
459 	sc->control_write = dts_value[0];
460 
461 	return (0);
462 }
463 
464 static int
465 beri_probe(device_t dev)
466 {
467 
468 	if (!ofw_bus_status_okay(dev))
469 		return (ENXIO);
470 
471 	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-ring"))
472 		return (ENXIO);
473 
474 	device_set_desc(dev, "SRI-Cambridge BERI ring buffer");
475 	return (BUS_PROBE_DEFAULT);
476 }
477 
478 static int
479 beri_attach(device_t dev)
480 {
481 	struct beri_softc *sc;
482 
483 	sc = device_get_softc(dev);
484 	sc->dev = dev;
485 
486 	if (bus_alloc_resources(dev, beri_spec, sc->res)) {
487 		device_printf(dev, "could not allocate resources\n");
488 		return (ENXIO);
489 	}
490 
491 	/* Memory interface */
492 	sc->bst = rman_get_bustag(sc->res[0]);
493 	sc->bsh = rman_get_bushandle(sc->res[0]);
494 
495 	if (parse_fdt(sc)) {
496 		device_printf(sc->dev, "Can't get FDT values\n");
497 		return (ENXIO);
498 	}
499 
500 	sc->cdev = make_dev(&beri_cdevsw, 0, UID_ROOT, GID_WHEEL,
501 	    S_IRWXU, "%s", sc->devname);
502 	if (sc->cdev == NULL) {
503 		device_printf(dev, "Failed to create character device.\n");
504 		return (ENXIO);
505 	}
506 
507 	sc->cdev->si_drv1 = sc;
508 
509 	mtx_init(&sc->beri_mtx, "beri_mtx", NULL, MTX_DEF);
510 	knlist_init_mtx(&sc->beri_rsel.si_note, &sc->beri_mtx);
511 
512 	return (0);
513 }
514 
515 static device_method_t beri_methods[] = {
516 	DEVMETHOD(device_probe,		beri_probe),
517 	DEVMETHOD(device_attach,	beri_attach),
518 	{ 0, 0 }
519 };
520 
521 static driver_t beri_driver = {
522 	"beri_ring",
523 	beri_methods,
524 	sizeof(struct beri_softc),
525 };
526 
527 DRIVER_MODULE(beri_ring, simplebus, beri_driver, 0, 0);
528