1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2011-2012, 2016 Robert N. M. Watson
5  * All rights reserved.
6  *
7  * This software was developed by SRI International and the University of
8  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9  * ("CTSRD"), as part of the DARPA CRASH research programme.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/bus.h>
35 #include <sys/cons.h>
36 #include <sys/endian.h>
37 #include <sys/kdb.h>
38 #include <sys/rman.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/reboot.h>
42 #include <sys/sysctl.h>
43 #include <sys/tty.h>
44 
45 #include <ddb/ddb.h>
46 
47 #include <machine/atomic.h>
48 #include <machine/bus.h>
49 
50 #include <dev/altera/jtag_uart/altera_jtag_uart.h>
51 
52 /*
53  * If one of the Altera JTAG UARTs is currently the system console, register
54  * it here.
55  */
56 static struct altera_jtag_uart_softc	*aju_cons_sc;
57 
58 static tsw_outwakeup_t	aju_outwakeup;
59 static void		aju_ac_callout(void *);
60 static void		aju_io_callout(void *);
61 
62 static struct ttydevsw aju_ttydevsw = {
63 	.tsw_flags	= TF_NOPREFIX,
64 	.tsw_outwakeup	= aju_outwakeup,
65 };
66 
67 /*
68  * When polling for the AC bit, the number of times we have to not see it
69  * before assuming JTAG has disappeared on us.  By default, four seconds.
70  */
71 #define	AJU_JTAG_MAXMISS		20
72 
73 /*
74  * Polling intervals for input/output and JTAG connection events.
75  */
76 #define	AJU_IO_POLLINTERVAL		(hz/100)
77 #define	AJU_AC_POLLINTERVAL		(hz/5)
78 
79 /*
80  * Statistics on JTAG removal events when sending, for debugging purposes
81  * only.
82  */
83 static u_int aju_jtag_vanished;
84 SYSCTL_UINT(_debug, OID_AUTO, aju_jtag_vanished, CTLFLAG_RW,
85     &aju_jtag_vanished, 0, "Number of times JTAG has vanished");
86 
87 static u_int aju_jtag_appeared;
88 SYSCTL_UINT(_debug, OID_AUTO, aju_jtag_appeared, CTLFLAG_RW,
89     &aju_jtag_appeared, 0, "Number of times JTAG has appeared");
90 
91 SYSCTL_INT(_debug, OID_AUTO, aju_cons_jtag_present, CTLFLAG_RW,
92     &aju_cons_jtag_present, 0, "JTAG console present flag");
93 
94 SYSCTL_UINT(_debug, OID_AUTO, aju_cons_jtag_missed, CTLFLAG_RW,
95     &aju_cons_jtag_missed, 0, "JTAG console missed counter");
96 
97 /*
98  * Interrupt-related statistics.
99  */
100 static u_int aju_intr_readable_enabled;
101 SYSCTL_UINT(_debug, OID_AUTO, aju_intr_readable_enabled, CTLFLAG_RW,
102     &aju_intr_readable_enabled, 0, "Number of times read interrupt enabled");
103 
104 static u_int aju_intr_writable_disabled;
105 SYSCTL_UINT(_debug, OID_AUTO, aju_intr_writable_disabled, CTLFLAG_RW,
106     &aju_intr_writable_disabled, 0,
107     "Number of times write interrupt disabled");
108 
109 static u_int aju_intr_writable_enabled;
110 SYSCTL_UINT(_debug, OID_AUTO, aju_intr_writable_enabled, CTLFLAG_RW,
111     &aju_intr_writable_enabled, 0,
112     "Number of times write interrupt enabled");
113 
114 static u_int aju_intr_disabled;
115 SYSCTL_UINT(_debug, OID_AUTO, aju_intr_disabled, CTLFLAG_RW,
116     &aju_intr_disabled, 0, "Number of times write interrupt disabled");
117 
118 static u_int aju_intr_read_count;
119 SYSCTL_UINT(_debug, OID_AUTO, aju_intr_read_count, CTLFLAG_RW,
120     &aju_intr_read_count, 0, "Number of times read interrupt fired");
121 
122 static u_int aju_intr_write_count;
123 SYSCTL_UINT(_debug, OID_AUTO, aju_intr_write_count, CTLFLAG_RW,
124     &aju_intr_write_count, 0, "Number of times write interrupt fired");
125 
126 /*
127  * Low-level read and write register routines; the Altera UART is little
128  * endian, so we byte swap 32-bit reads and writes.
129  */
130 static inline uint32_t
aju_data_read(struct altera_jtag_uart_softc * sc)131 aju_data_read(struct altera_jtag_uart_softc *sc)
132 {
133 
134 	return (le32toh(bus_read_4(sc->ajus_mem_res,
135 	    ALTERA_JTAG_UART_DATA_OFF)));
136 }
137 
138 static inline void
aju_data_write(struct altera_jtag_uart_softc * sc,uint32_t v)139 aju_data_write(struct altera_jtag_uart_softc *sc, uint32_t v)
140 {
141 
142 	bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_DATA_OFF, htole32(v));
143 }
144 
145 static inline uint32_t
aju_control_read(struct altera_jtag_uart_softc * sc)146 aju_control_read(struct altera_jtag_uart_softc *sc)
147 {
148 
149 	return (le32toh(bus_read_4(sc->ajus_mem_res,
150 	    ALTERA_JTAG_UART_CONTROL_OFF)));
151 }
152 
153 static inline void
aju_control_write(struct altera_jtag_uart_softc * sc,uint32_t v)154 aju_control_write(struct altera_jtag_uart_softc *sc, uint32_t v)
155 {
156 
157 	bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_CONTROL_OFF,
158 	    htole32(v));
159 }
160 
161 /*
162  * Slightly higher-level routines aware of buffering and flow control.
163  */
164 static inline int
aju_writable(struct altera_jtag_uart_softc * sc)165 aju_writable(struct altera_jtag_uart_softc *sc)
166 {
167 
168 	return ((aju_control_read(sc) &
169 	    ALTERA_JTAG_UART_CONTROL_WSPACE) != 0);
170 }
171 
172 static inline int
aju_readable(struct altera_jtag_uart_softc * sc)173 aju_readable(struct altera_jtag_uart_softc *sc)
174 {
175 	uint32_t v;
176 
177 	AJU_LOCK_ASSERT(sc);
178 
179 	if (*sc->ajus_buffer_validp)
180 		return (1);
181 	v = aju_data_read(sc);
182 	if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
183 		*sc->ajus_buffer_validp = 1;
184 		*sc->ajus_buffer_datap = (v & ALTERA_JTAG_UART_DATA_DATA);
185 		return (1);
186 	}
187 	return (0);
188 }
189 
190 static char
aju_read(struct altera_jtag_uart_softc * sc)191 aju_read(struct altera_jtag_uart_softc *sc)
192 {
193 
194 	AJU_LOCK_ASSERT(sc);
195 
196 	while (!aju_readable(sc));
197 	*sc->ajus_buffer_validp = 0;
198 	return (*sc->ajus_buffer_datap);
199 }
200 
201 /*
202  * Routines for enabling and disabling interrupts for read and write.
203  */
204 static void
aju_intr_readable_enable(struct altera_jtag_uart_softc * sc)205 aju_intr_readable_enable(struct altera_jtag_uart_softc *sc)
206 {
207 	uint32_t v;
208 
209 	AJU_LOCK_ASSERT(sc);
210 
211 	atomic_add_int(&aju_intr_readable_enabled, 1);
212 	v = aju_control_read(sc);
213 	v |= ALTERA_JTAG_UART_CONTROL_RE;
214 	aju_control_write(sc, v);
215 }
216 
217 static void
aju_intr_writable_enable(struct altera_jtag_uart_softc * sc)218 aju_intr_writable_enable(struct altera_jtag_uart_softc *sc)
219 {
220 	uint32_t v;
221 
222 	AJU_LOCK_ASSERT(sc);
223 
224 	atomic_add_int(&aju_intr_writable_enabled, 1);
225 	v = aju_control_read(sc);
226 	v |= ALTERA_JTAG_UART_CONTROL_WE;
227 	aju_control_write(sc, v);
228 }
229 
230 static void
aju_intr_writable_disable(struct altera_jtag_uart_softc * sc)231 aju_intr_writable_disable(struct altera_jtag_uart_softc *sc)
232 {
233 	uint32_t v;
234 
235 	AJU_LOCK_ASSERT(sc);
236 
237 	atomic_add_int(&aju_intr_writable_disabled, 1);
238 	v = aju_control_read(sc);
239 	v &= ~ALTERA_JTAG_UART_CONTROL_WE;
240 	aju_control_write(sc, v);
241 }
242 
243 static void
aju_intr_disable(struct altera_jtag_uart_softc * sc)244 aju_intr_disable(struct altera_jtag_uart_softc *sc)
245 {
246 	uint32_t v;
247 
248 	AJU_LOCK_ASSERT(sc);
249 
250 	atomic_add_int(&aju_intr_disabled, 1);
251 	v = aju_control_read(sc);
252 	v &= ~(ALTERA_JTAG_UART_CONTROL_RE | ALTERA_JTAG_UART_CONTROL_WE);
253 	aju_control_write(sc, v);
254 }
255 
256 /*
257  * The actual work of checking for, and handling, available reads.  This is
258  * used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked
259  * up with, or without, IRQs allocated.
260  */
261 static void
aju_handle_input(struct altera_jtag_uart_softc * sc,struct tty * tp)262 aju_handle_input(struct altera_jtag_uart_softc *sc, struct tty *tp)
263 {
264 	int c;
265 
266 	tty_assert_locked(tp);
267 	AJU_LOCK_ASSERT(sc);
268 
269 	while (aju_readable(sc)) {
270 		c = aju_read(sc);
271 		AJU_UNLOCK(sc);
272 #ifdef KDB
273 		if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
274 			kdb_alt_break(c, &sc->ajus_alt_break_state);
275 #endif
276 		ttydisc_rint(tp, c, 0);
277 		AJU_LOCK(sc);
278 	}
279 	AJU_UNLOCK(sc);
280 	ttydisc_rint_done(tp);
281 	AJU_LOCK(sc);
282 }
283 
284 /*
285  * Send output to the UART until either there's none left to send, or we run
286  * out of room and need to await an interrupt so that we can start sending
287  * again.
288  *
289  * XXXRW: It would be nice to query WSPACE at the beginning and write to the
290  * FIFO in bugger chunks.
291  */
292 static void
aju_handle_output(struct altera_jtag_uart_softc * sc,struct tty * tp)293 aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp)
294 {
295 	uint32_t v;
296 	uint8_t ch;
297 
298 	tty_assert_locked(tp);
299 	AJU_LOCK_ASSERT(sc);
300 
301 	AJU_UNLOCK(sc);
302 	while (ttydisc_getc_poll(tp) != 0) {
303 		AJU_LOCK(sc);
304 		if (*sc->ajus_jtag_presentp == 0) {
305 			/*
306 			 * If JTAG is not present, then we will drop this
307 			 * character instead of perhaps scheduling an
308 			 * interrupt to let us know when there is buffer
309 			 * space.  Otherwise we might get a write interrupt
310 			 * later even though we aren't interested in sending
311 			 * anymore.  Loop to drain TTY-layer buffer.
312 			 */
313 			AJU_UNLOCK(sc);
314 			if (ttydisc_getc(tp, &ch, sizeof(ch)) !=
315 			    sizeof(ch))
316 				panic("%s: ttydisc_getc", __func__);
317 			continue;
318 		}
319 		v = aju_control_read(sc);
320 		if ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) == 0) {
321 			if (sc->ajus_irq_res != NULL &&
322 			    (v & ALTERA_JTAG_UART_CONTROL_WE) == 0)
323 				aju_intr_writable_enable(sc);
324 			return;
325 		}
326 		AJU_UNLOCK(sc);
327 		if (ttydisc_getc(tp, &ch, sizeof(ch)) != sizeof(ch))
328 			panic("%s: ttydisc_getc 2", __func__);
329 		AJU_LOCK(sc);
330 
331 		/*
332 		 * XXXRW: There is a slight race here in which we test for
333 		 * writability, drop the lock, get the character from the tty
334 		 * layer, re-acquire the lock, and then write.  It's possible
335 		 * for other code -- specifically, the low-level console -- to
336 		 * have* written in the mean time, which might mean that there
337 		 * is no longer space.  The BERI memory bus will cause this
338 		 * write to block, wedging the processor until space is
339 		 * available -- which could be a while if JTAG is not
340 		 * attached!
341 		 *
342 		 * The 'easy' fix is to drop the character if WSPACE has
343 		 * become unset.  Not sure what the 'hard' fix is.
344 		 */
345 		aju_data_write(sc, ch);
346 		AJU_UNLOCK(sc);
347 	}
348 	AJU_LOCK(sc);
349 
350 	/*
351 	 * If interrupts are configured, and there's no data to write, but we
352 	 * had previously enabled write interrupts, disable them now.
353 	 */
354 	v = aju_control_read(sc);
355 	if (sc->ajus_irq_res != NULL && (v & ALTERA_JTAG_UART_CONTROL_WE) != 0)
356 		aju_intr_writable_disable(sc);
357 }
358 
359 static void
aju_outwakeup(struct tty * tp)360 aju_outwakeup(struct tty *tp)
361 {
362 	struct altera_jtag_uart_softc *sc = tty_softc(tp);
363 
364 	tty_assert_locked(tp);
365 
366 	AJU_LOCK(sc);
367 	aju_handle_output(sc, tp);
368 	AJU_UNLOCK(sc);
369 }
370 
371 static void
aju_io_callout(void * arg)372 aju_io_callout(void *arg)
373 {
374 	struct altera_jtag_uart_softc *sc = arg;
375 	struct tty *tp = sc->ajus_ttyp;
376 
377 	tty_lock(tp);
378 	AJU_LOCK(sc);
379 
380 	/*
381 	 * It would be convenient if we could share code with aju_intr() here
382 	 * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and
383 	 * ALTERA_JTAG_UART_CONTROL_WI.  Unfortunately, it's not clear that
384 	 * this is supported, so do all the work to poll for both input and
385 	 * output.
386 	 */
387 	aju_handle_input(sc, tp);
388 	aju_handle_output(sc, tp);
389 
390 	/*
391 	 * Reschedule next poll attempt.  There's some argument that we should
392 	 * do adaptive polling based on the expectation of I/O: is something
393 	 * pending in the output buffer, or have we recently had input, but we
394 	 * don't.
395 	 */
396 	callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
397 	    aju_io_callout, sc);
398 	AJU_UNLOCK(sc);
399 	tty_unlock(tp);
400 }
401 
402 static void
aju_ac_callout(void * arg)403 aju_ac_callout(void *arg)
404 {
405 	struct altera_jtag_uart_softc *sc = arg;
406 	struct tty *tp = sc->ajus_ttyp;
407 	uint32_t v;
408 
409 	tty_lock(tp);
410 	AJU_LOCK(sc);
411 	v = aju_control_read(sc);
412 	if (v & ALTERA_JTAG_UART_CONTROL_AC) {
413 		v &= ~ALTERA_JTAG_UART_CONTROL_AC;
414 		aju_control_write(sc, v);
415 		if (*sc->ajus_jtag_presentp == 0) {
416 			*sc->ajus_jtag_presentp = 1;
417 			atomic_add_int(&aju_jtag_appeared, 1);
418 			aju_handle_output(sc, tp);
419 		}
420 
421 		/* Any hit eliminates all recent misses. */
422 		*sc->ajus_jtag_missedp = 0;
423 	} else if (*sc->ajus_jtag_presentp != 0) {
424 		/*
425 		 * If we've exceeded our tolerance for misses, mark JTAG as
426 		 * disconnected and drain output.  Otherwise, bump the miss
427 		 * counter.
428 		 */
429 		if (*sc->ajus_jtag_missedp > AJU_JTAG_MAXMISS) {
430 			*sc->ajus_jtag_presentp = 0;
431 			atomic_add_int(&aju_jtag_vanished, 1);
432 			aju_handle_output(sc, tp);
433 		} else
434 			(*sc->ajus_jtag_missedp)++;
435 	}
436 	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
437 	    aju_ac_callout, sc);
438 	AJU_UNLOCK(sc);
439 	tty_unlock(tp);
440 }
441 
442 static void
aju_intr(void * arg)443 aju_intr(void *arg)
444 {
445 	struct altera_jtag_uart_softc *sc = arg;
446 	struct tty *tp = sc->ajus_ttyp;
447 	uint32_t v;
448 
449 	tty_lock(tp);
450 	AJU_LOCK(sc);
451 	v = aju_control_read(sc);
452 	if (v & ALTERA_JTAG_UART_CONTROL_RI) {
453 		atomic_add_int(&aju_intr_read_count, 1);
454 		aju_handle_input(sc, tp);
455 	}
456 	if (v & ALTERA_JTAG_UART_CONTROL_WI) {
457 		atomic_add_int(&aju_intr_write_count, 1);
458 		aju_handle_output(sc, tp);
459 	}
460 	AJU_UNLOCK(sc);
461 	tty_unlock(tp);
462 }
463 
464 int
altera_jtag_uart_attach(struct altera_jtag_uart_softc * sc)465 altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc)
466 {
467 	struct tty *tp;
468 	int error;
469 
470 	AJU_LOCK_INIT(sc);
471 
472 	/*
473 	 * XXXRW: Currently, we detect the console solely based on it using a
474 	 * reserved address, and borrow console-level locks and buffer if so.
475 	 * Is there a better way?
476 	 */
477 	if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) {
478 		sc->ajus_lockp = &aju_cons_lock;
479 		sc->ajus_buffer_validp = &aju_cons_buffer_valid;
480 		sc->ajus_buffer_datap = &aju_cons_buffer_data;
481 		sc->ajus_jtag_presentp = &aju_cons_jtag_present;
482 		sc->ajus_jtag_missedp = &aju_cons_jtag_missed;
483 		sc->ajus_flags |= ALTERA_JTAG_UART_FLAG_CONSOLE;
484 	} else {
485 		sc->ajus_lockp = &sc->ajus_lock;
486 		sc->ajus_buffer_validp = &sc->ajus_buffer_valid;
487 		sc->ajus_buffer_datap = &sc->ajus_buffer_data;
488 		sc->ajus_jtag_presentp = &sc->ajus_jtag_present;
489 		sc->ajus_jtag_missedp = &sc->ajus_jtag_missed;
490 	}
491 
492 	/*
493 	 * Disable interrupts regardless of whether or not we plan to use
494 	 * them.  We will register an interrupt handler now if they will be
495 	 * used, but not re-enable intil later once the remainder of the tty
496 	 * layer is properly initialised, as we're not ready for input yet.
497 	 */
498 	AJU_LOCK(sc);
499 	aju_intr_disable(sc);
500 	AJU_UNLOCK(sc);
501 	if (sc->ajus_irq_res != NULL) {
502 		error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res,
503 		    INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL,
504 		    aju_intr, sc, &sc->ajus_irq_cookie);
505 		if (error) {
506 			device_printf(sc->ajus_dev,
507 			    "could not activate interrupt\n");
508 			AJU_LOCK_DESTROY(sc);
509 			return (error);
510 		}
511 	}
512 	tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc);
513 	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) {
514 		aju_cons_sc = sc;
515 		tty_init_console(tp, 0);
516 	}
517 	tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit);
518 
519 	/*
520 	 * If we will be using interrupts, enable them now; otherwise, start
521 	 * polling.  From this point onwards, input can arrive.
522 	 */
523 	if (sc->ajus_irq_res != NULL) {
524 		AJU_LOCK(sc);
525 		aju_intr_readable_enable(sc);
526 		AJU_UNLOCK(sc);
527 	} else {
528 		callout_init(&sc->ajus_io_callout, 1);
529 		callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
530 		    aju_io_callout, sc);
531 	}
532 	callout_init(&sc->ajus_ac_callout, 1);
533 	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
534 	    aju_ac_callout, sc);
535 	return (0);
536 }
537 
538 void
altera_jtag_uart_detach(struct altera_jtag_uart_softc * sc)539 altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc)
540 {
541 	struct tty *tp = sc->ajus_ttyp;
542 
543 	/*
544 	 * If we're using interrupts, disable and release the interrupt
545 	 * handler now.  Otherwise drain the polling timeout.
546 	 */
547 	if (sc->ajus_irq_res != NULL) {
548 		AJU_LOCK(sc);
549 		aju_intr_disable(sc);
550 		AJU_UNLOCK(sc);
551 		bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res,
552 		    sc->ajus_irq_cookie);
553 	} else
554 		callout_drain(&sc->ajus_io_callout);
555 	callout_drain(&sc->ajus_ac_callout);
556 	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
557 		aju_cons_sc = NULL;
558 	tty_lock(tp);
559 	tty_rel_gone(tp);
560 	AJU_LOCK_DESTROY(sc);
561 }
562