1 /*-
2  * Copyright (c) 2011-2012 Robert N. M. Watson
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 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/cons.h>
37 #include <sys/endian.h>
38 #include <sys/kdb.h>
39 #include <sys/rman.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/reboot.h>
43 #include <sys/tty.h>
44 
45 #include <ddb/ddb.h>
46 
47 #include <machine/bus.h>
48 
49 #include <dev/altera/jtag_uart/altera_jtag_uart.h>
50 
51 /*
52  * If one of the Altera JTAG UARTs is currently the system console, register
53  * it here.
54  */
55 static struct altera_jtag_uart_softc	*aju_cons_sc;
56 
57 static tsw_outwakeup_t	aju_outwakeup;
58 static void		aju_ac_callout(void *);
59 static void		aju_io_callout(void *);
60 
61 static struct ttydevsw aju_ttydevsw = {
62 	.tsw_flags	= TF_NOPREFIX,
63 	.tsw_outwakeup	= aju_outwakeup,
64 };
65 
66 /*
67  * When polling for the AC bit, the number of times we have to not see it
68  * before assuming JTAG has disappeared on us.  By default, two seconds.
69  */
70 #define	AJU_JTAG_MAXMISS		10
71 
72 /*
73  * Polling intervals for input/output and JTAG connection events.
74  */
75 #define	AJU_IO_POLLINTERVAL		(hz/100)
76 #define	AJU_AC_POLLINTERVAL		(hz/5)
77 
78 /*
79  * Low-level read and write register routines; the Altera UART is little
80  * endian, so we byte swap 32-bit reads and writes.
81  */
82 static inline uint32_t
83 aju_data_read(struct altera_jtag_uart_softc *sc)
84 {
85 
86 	return (le32toh(bus_read_4(sc->ajus_mem_res,
87 	    ALTERA_JTAG_UART_DATA_OFF)));
88 }
89 
90 static inline void
91 aju_data_write(struct altera_jtag_uart_softc *sc, uint32_t v)
92 {
93 
94 	bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_DATA_OFF, htole32(v));
95 }
96 
97 static inline uint32_t
98 aju_control_read(struct altera_jtag_uart_softc *sc)
99 {
100 
101 	return (le32toh(bus_read_4(sc->ajus_mem_res,
102 	    ALTERA_JTAG_UART_CONTROL_OFF)));
103 }
104 
105 static inline void
106 aju_control_write(struct altera_jtag_uart_softc *sc, uint32_t v)
107 {
108 
109 	bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_CONTROL_OFF,
110 	    htole32(v));
111 }
112 
113 /*
114  * Slightly higher-level routines aware of buffering and flow control.
115  */
116 static inline int
117 aju_writable(struct altera_jtag_uart_softc *sc)
118 {
119 
120 	return ((aju_control_read(sc) &
121 	    ALTERA_JTAG_UART_CONTROL_WSPACE) != 0);
122 }
123 
124 static inline int
125 aju_readable(struct altera_jtag_uart_softc *sc)
126 {
127 	uint32_t v;
128 
129 	AJU_LOCK_ASSERT(sc);
130 
131 	if (*sc->ajus_buffer_validp)
132 		return (1);
133 	v = aju_data_read(sc);
134 	if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
135 		*sc->ajus_buffer_validp = 1;
136 		*sc->ajus_buffer_datap = (v & ALTERA_JTAG_UART_DATA_DATA);
137 		return (1);
138 	}
139 	return (0);
140 }
141 
142 static char
143 aju_read(struct altera_jtag_uart_softc *sc)
144 {
145 
146 	AJU_LOCK_ASSERT(sc);
147 
148 	while (!aju_readable(sc));
149 	*sc->ajus_buffer_validp = 0;
150 	return (*sc->ajus_buffer_datap);
151 }
152 
153 /*
154  * Routines for enabling and disabling interrupts for read and write.
155  */
156 static void
157 aju_intr_readable_enable(struct altera_jtag_uart_softc *sc)
158 {
159 	uint32_t v;
160 
161 	AJU_LOCK_ASSERT(sc);
162 
163 	v = aju_control_read(sc);
164 	v |= ALTERA_JTAG_UART_CONTROL_RE;
165 	aju_control_write(sc, v);
166 }
167 
168 static void
169 aju_intr_writable_enable(struct altera_jtag_uart_softc *sc)
170 {
171 	uint32_t v;
172 
173 	AJU_LOCK_ASSERT(sc);
174 
175 	v = aju_control_read(sc);
176 	v |= ALTERA_JTAG_UART_CONTROL_WE;
177 	aju_control_write(sc, v);
178 }
179 
180 static void
181 aju_intr_writable_disable(struct altera_jtag_uart_softc *sc)
182 {
183 	uint32_t v;
184 
185 	AJU_LOCK_ASSERT(sc);
186 
187 	v = aju_control_read(sc);
188 	v &= ~ALTERA_JTAG_UART_CONTROL_WE;
189 	aju_control_write(sc, v);
190 }
191 
192 static void
193 aju_intr_disable(struct altera_jtag_uart_softc *sc)
194 {
195 	uint32_t v;
196 
197 	AJU_LOCK_ASSERT(sc);
198 
199 	v = aju_control_read(sc);
200 	v &= ~(ALTERA_JTAG_UART_CONTROL_RE | ALTERA_JTAG_UART_CONTROL_WE);
201 	aju_control_write(sc, v);
202 }
203 
204 /*
205  * The actual work of checking for, and handling, available reads.  This is
206  * used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked
207  * up with, or without, IRQs allocated.
208  */
209 static void
210 aju_handle_input(struct altera_jtag_uart_softc *sc, struct tty *tp)
211 {
212 	int c;
213 
214 	tty_lock_assert(tp, MA_OWNED);
215 	AJU_LOCK_ASSERT(sc);
216 
217 	while (aju_readable(sc)) {
218 		c = aju_read(sc);
219 		AJU_UNLOCK(sc);
220 #ifdef KDB
221 		if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
222 			kdb_alt_break(c, &sc->ajus_alt_break_state);
223 #endif
224 		ttydisc_rint(tp, c, 0);
225 		AJU_LOCK(sc);
226 	}
227 	AJU_UNLOCK(sc);
228 	ttydisc_rint_done(tp);
229 	AJU_LOCK(sc);
230 }
231 
232 /*
233  * Send output to the UART until either there's none left to send, or we run
234  * out of room and need to await an interrupt so that we can start sending
235  * again.
236  *
237  * XXXRW: It would be nice to query WSPACE at the beginning and write to the
238  * FIFO in bugger chunks.
239  */
240 static void
241 aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp)
242 {
243 	uint32_t v;
244 	uint8_t ch;
245 
246 	tty_lock_assert(tp, MA_OWNED);
247 	AJU_LOCK_ASSERT(sc);
248 
249 	AJU_UNLOCK(sc);
250 	while (ttydisc_getc_poll(tp) != 0) {
251 		AJU_LOCK(sc);
252 		v = aju_control_read(sc);
253 		if ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) != 0) {
254 			AJU_UNLOCK(sc);
255 			if (ttydisc_getc(tp, &ch, sizeof(ch)) != sizeof(ch))
256 				panic("%s: ttydisc_getc", __func__);
257 			AJU_LOCK(sc);
258 
259 			/*
260 			 * XXXRW: There is a slight race here in which we test
261 			 * for writability, drop the lock, get the character
262 			 * from the tty layer, re-acquire the lock, and then
263 			 * write.  It's possible for other code --
264 			 * specifically, the low-level console -- to have
265 			 * written in the mean time, which might mean that
266 			 * there is no longer space.  The BERI memory bus will
267 			 * cause this write to block, wedging the processor
268 			 * until space is available -- which could be a while
269 			 * if JTAG is not attached!
270 			 *
271 			 * The 'easy' fix is to drop the character if WSPACE
272 			 * has become unset.  Not sure what the 'hard' fix is.
273 			 */
274 			aju_data_write(sc, ch);
275 		} else {
276 			/*
277 			 * If JTAG is not present, then we will drop this
278 			 * character instead of perhaps scheduling an
279 			 * interrupt to let us know when there is buffer
280 			 * space.  Otherwise we might get a write interrupt
281 			 * later even though we aren't interested in sending
282 			 * anymore.  Loop to drain TTY-layer buffer.
283 			 */
284 			if (*sc->ajus_jtag_presentp == 0) {
285 				if (ttydisc_getc(tp, &ch, sizeof(ch)) !=
286 				    sizeof(ch))
287 					panic("%s: ttydisc_getc 2", __func__);
288 				AJU_UNLOCK(sc);
289 				continue;
290 			}
291 			if (sc->ajus_irq_res != NULL)
292 				aju_intr_writable_enable(sc);
293 			return;
294 		}
295 		AJU_UNLOCK(sc);
296 	}
297 	AJU_LOCK(sc);
298 	aju_intr_writable_disable(sc);
299 }
300 
301 static void
302 aju_outwakeup(struct tty *tp)
303 {
304 	struct altera_jtag_uart_softc *sc = tty_softc(tp);
305 
306 	tty_lock_assert(tp, MA_OWNED);
307 
308 	AJU_LOCK(sc);
309 	aju_handle_output(sc, tp);
310 	AJU_UNLOCK(sc);
311 }
312 
313 static void
314 aju_io_callout(void *arg)
315 {
316 	struct altera_jtag_uart_softc *sc = arg;
317 	struct tty *tp = sc->ajus_ttyp;
318 
319 	tty_lock(tp);
320 	AJU_LOCK(sc);
321 
322 	/*
323 	 * It would be convenient if we could share code with aju_intr() here
324 	 * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and
325 	 * ALTERA_JTAG_UART_CONTROL_WI.  Unfortunately, it's not clear that
326 	 * this is supported, so do all the work to poll for both input and
327 	 * output.
328 	 */
329 	aju_handle_input(sc, tp);
330 	aju_handle_output(sc, tp);
331 
332 	/*
333 	 * Reschedule next poll attempt.  There's some argument that we should
334 	 * do adaptive polling based on the expectation of I/O: is something
335 	 * pending in the output buffer, or have we recently had input, but we
336 	 * don't.
337 	 */
338 	callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
339 	    aju_io_callout, sc);
340 	AJU_UNLOCK(sc);
341 	tty_unlock(tp);
342 }
343 
344 static void
345 aju_ac_callout(void *arg)
346 {
347 	struct altera_jtag_uart_softc *sc = arg;
348 	struct tty *tp = sc->ajus_ttyp;
349 	uint32_t v;
350 
351 	tty_lock(tp);
352 	AJU_LOCK(sc);
353 	v = aju_control_read(sc);
354 	if (v & ALTERA_JTAG_UART_CONTROL_AC) {
355 		v &= ~ALTERA_JTAG_UART_CONTROL_AC;
356 		aju_control_write(sc, v);
357 		if (*sc->ajus_jtag_presentp == 0) {
358 			*sc->ajus_jtag_missedp = 0;
359 			*sc->ajus_jtag_presentp = 1;
360 			aju_handle_output(sc, tp);
361 		}
362 	} else if (*sc->ajus_jtag_presentp != 0) {
363 		(*sc->ajus_jtag_missedp)++;
364 		if (*sc->ajus_jtag_missedp >= AJU_JTAG_MAXMISS) {
365 			*sc->ajus_jtag_presentp = 0;
366 			aju_handle_output(sc, tp);
367 		}
368 	}
369 	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
370 	    aju_ac_callout, sc);
371 	AJU_UNLOCK(sc);
372 	tty_unlock(tp);
373 }
374 
375 static void
376 aju_intr(void *arg)
377 {
378 	struct altera_jtag_uart_softc *sc = arg;
379 	struct tty *tp = sc->ajus_ttyp;
380 	uint32_t v;
381 
382 	tty_lock(tp);
383 	AJU_LOCK(sc);
384 	v = aju_control_read(sc);
385 	if (v & ALTERA_JTAG_UART_CONTROL_RI)
386 		aju_handle_input(sc, tp);
387 	if (v & ALTERA_JTAG_UART_CONTROL_WI)
388 		aju_handle_output(sc, tp);
389 	AJU_UNLOCK(sc);
390 	tty_unlock(tp);
391 }
392 
393 int
394 altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc)
395 {
396 	struct tty *tp;
397 	int error;
398 
399 	AJU_LOCK_INIT(sc);
400 
401 	/*
402 	 * XXXRW: Currently, we detect the console solely based on it using a
403 	 * reserved address, and borrow console-level locks and buffer if so.
404 	 * Is there a better way?
405 	 */
406 	if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) {
407 		sc->ajus_lockp = &aju_cons_lock;
408 		sc->ajus_buffer_validp = &aju_cons_buffer_valid;
409 		sc->ajus_buffer_datap = &aju_cons_buffer_data;
410 		sc->ajus_jtag_presentp = &aju_cons_jtag_present;
411 		sc->ajus_jtag_missedp = &aju_cons_jtag_missed;
412 		sc->ajus_flags |= ALTERA_JTAG_UART_FLAG_CONSOLE;
413 	} else {
414 		sc->ajus_lockp = &sc->ajus_lock;
415 		sc->ajus_buffer_validp = &sc->ajus_buffer_valid;
416 		sc->ajus_buffer_datap = &sc->ajus_buffer_data;
417 		sc->ajus_jtag_presentp = &sc->ajus_jtag_present;
418 		sc->ajus_jtag_missedp = &sc->ajus_jtag_missed;
419 	}
420 
421 	/*
422 	 * Disable interrupts regardless of whether or not we plan to use
423 	 * them.  We will register an interrupt handler now if they will be
424 	 * used, but not re-enable intil later once the remainder of the tty
425 	 * layer is properly initialised, as we're not ready for input yet.
426 	 */
427 	AJU_LOCK(sc);
428 	aju_intr_disable(sc);
429 	AJU_UNLOCK(sc);
430 	if (sc->ajus_irq_res != NULL) {
431 		error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res,
432 		    INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL,
433 		    aju_intr, sc, &sc->ajus_irq_cookie);
434 		if (error) {
435 			device_printf(sc->ajus_dev,
436 			    "could not activate interrupt\n");
437 			AJU_LOCK_DESTROY(sc);
438 			return (error);
439 		}
440 	}
441 	tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc);
442 	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) {
443 		aju_cons_sc = sc;
444 		tty_init_console(tp, 0);
445 	}
446 	tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit);
447 
448 	/*
449 	 * If we will be using interrupts, enable them now; otherwise, start
450 	 * polling.  From this point onwards, input can arrive.
451 	 */
452 	if (sc->ajus_irq_res != NULL) {
453 		AJU_LOCK(sc);
454 		aju_intr_readable_enable(sc);
455 		AJU_UNLOCK(sc);
456 	} else {
457 		callout_init(&sc->ajus_io_callout, CALLOUT_MPSAFE);
458 		callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
459 		    aju_io_callout, sc);
460 	}
461 	callout_init(&sc->ajus_ac_callout, CALLOUT_MPSAFE);
462 	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
463 	    aju_ac_callout, sc);
464 	return (0);
465 }
466 
467 void
468 altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc)
469 {
470 	struct tty *tp = sc->ajus_ttyp;
471 
472 	/*
473 	 * If we're using interrupts, disable and release the interrupt
474 	 * handler now.  Otherwise drain the polling timeout.
475 	 */
476 	if (sc->ajus_irq_res != NULL) {
477 		AJU_LOCK(sc);
478 		aju_intr_disable(sc);
479 		AJU_UNLOCK(sc);
480 		bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res,
481 		    sc->ajus_irq_cookie);
482 	} else
483 		callout_drain(&sc->ajus_io_callout);
484 	callout_drain(&sc->ajus_ac_callout);
485 	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
486 		aju_cons_sc = NULL;
487 	tty_lock(tp);
488 	tty_rel_gone(tp);
489 	AJU_LOCK_DESTROY(sc);
490 }
491