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