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