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