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 */
55static struct altera_jtag_uart_softc	*aju_cons_sc;
56
57static tsw_outwakeup_t	aju_outwakeup;
58static void		aju_ac_callout(void *);
59static void		aju_io_callout(void *);
60
61static 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 */
82static inline uint32_t
83aju_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
90static inline void
91aju_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
97static inline uint32_t
98aju_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
105static inline void
106aju_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 */
116static inline int
117aju_writable(struct altera_jtag_uart_softc *sc)
118{
119
120	return ((aju_control_read(sc) &
121	    ALTERA_JTAG_UART_CONTROL_WSPACE) != 0);
122}
123
124static inline int
125aju_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
142static char
143aju_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 */
156static void
157aju_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
168static void
169aju_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
180static void
181aju_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
192static void
193aju_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 */
209static void
210aju_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 */
240static void
241aju_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
301static void
302aju_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
313static void
314aju_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
344static void
345aju_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
375static void
376aju_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
393int
394altera_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, 1);
458		callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
459		    aju_io_callout, sc);
460	}
461	callout_init(&sc->ajus_ac_callout, 1);
462	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
463	    aju_ac_callout, sc);
464	return (0);
465}
466
467void
468altera_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