altera_jtag_uart_cons.c revision 239676
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: head/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c 239676 2012-08-25 11:30:36Z rwatson $");
33
34#include <sys/param.h>
35#include <sys/cons.h>
36#include <sys/endian.h>
37#include <sys/kdb.h>
38#include <sys/kernel.h>
39#include <sys/lock.h>
40#include <sys/mutex.h>
41#include <sys/reboot.h>
42#include <sys/systm.h>
43#include <sys/tty.h>
44
45#include <ddb/ddb.h>
46
47#include <dev/altera/jtag_uart/altera_jtag_uart.h>
48
49/*
50 * One-byte buffer as we can't check whether the UART is readable without
51 * actually reading from it, synchronised by a spinlock; this lock also
52 * synchronises access to the I/O ports for non-atomic sequences.  These
53 * symbols are public so that the TTY layer can use them when working on an
54 * instance of the UART that is also a low-level console.
55 */
56char		aju_cons_buffer_data;
57int		aju_cons_buffer_valid;
58int		aju_cons_jtag_present;
59u_int		aju_cons_jtag_missed;
60struct mtx	aju_cons_lock;
61
62/*
63 * Low-level console driver functions.
64 */
65static cn_probe_t	aju_cnprobe;
66static cn_init_t	aju_cninit;
67static cn_term_t	aju_cnterm;
68static cn_getc_t	aju_cngetc;
69static cn_putc_t	aju_cnputc;
70static cn_grab_t	aju_cngrab;
71static cn_ungrab_t	aju_cnungrab;
72
73/*
74 * JTAG sets the ALTERA_JTAG_UART_CONTROL_AC bit whenever it accesses the
75 * FIFO.  This allows us to (sort of) tell when JTAG is present, so that we
76 * can adopt lossy, rather than blocking, behaviour when JTAG isn't there.
77 * When it is present, we do full flow control.  This delay is how long we
78 * wait to see if JTAG has really disappeared when finding a full buffer and
79 * no AC bit set.
80 */
81#define	ALTERA_JTAG_UART_AC_POLL_DELAY	10000
82
83/*
84 * I/O routines lifted from Deimos.  This is not only MIPS-specific, but also
85 * BERI-specific, as we're hard coding the the address at which we expect to
86 * find the Altera JTAG UART and using it unconditionally.  We use these
87 * low-level routines so that we can perform console I/O long before newbus
88 * has initialised and devices have attached.  The TTY layer of the driver
89 * knows about this, and uses the console-layer spinlock instead of the
90 * TTY-layer lock to avoid confusion between layers for the console UART.
91 *
92 * XXXRW: The only place this inter-layer behaviour breaks down is if the
93 * low-level console is used for polled read while the TTY driver is also
94 * looking for input.  Probably we should also share buffers between layers.
95 */
96#define	MIPS_XKPHYS_UNCACHED_BASE	0x9000000000000000
97
98typedef	uint64_t	paddr_t;
99typedef	uint64_t	vaddr_t;
100
101static inline vaddr_t
102mips_phys_to_uncached(paddr_t phys)
103{
104
105	return (phys | MIPS_XKPHYS_UNCACHED_BASE);
106}
107
108static inline uint32_t
109mips_ioread_uint32(vaddr_t vaddr)
110{
111	uint32_t v;
112
113	__asm__ __volatile__ ("lw %0, 0(%1)" : "=r" (v) : "r" (vaddr));
114	return (v);
115}
116
117static inline void
118mips_iowrite_uint32(vaddr_t vaddr, uint32_t v)
119{
120
121	__asm__ __volatile__ ("sw %0, 0(%1)" : : "r" (v), "r" (vaddr));
122}
123
124/*
125 * Little-endian versions of 32-bit I/O routines.
126 */
127static inline uint32_t
128mips_ioread_uint32le(vaddr_t vaddr)
129{
130
131	return (le32toh(mips_ioread_uint32(vaddr)));
132}
133
134static inline void
135mips_iowrite_uint32le(vaddr_t vaddr, uint32_t v)
136{
137
138	mips_iowrite_uint32(vaddr, htole32(v));
139}
140
141/*
142 * Low-level read and write register routines; the Altera UART is little
143 * endian, so we byte swap 32-bit reads and writes.
144 */
145static inline uint32_t
146aju_cons_data_read(void)
147{
148
149	return (mips_ioread_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
150	    ALTERA_JTAG_UART_DATA_OFF)));
151}
152
153static inline void
154aju_cons_data_write(uint32_t v)
155{
156
157	mips_iowrite_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
158	    ALTERA_JTAG_UART_DATA_OFF), v);
159}
160
161static inline uint32_t
162aju_cons_control_read(void)
163{
164
165	return (mips_ioread_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
166	    ALTERA_JTAG_UART_CONTROL_OFF)));
167}
168
169static inline void
170aju_cons_control_write(uint32_t v)
171{
172
173	mips_iowrite_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
174	    ALTERA_JTAG_UART_CONTROL_OFF), v);
175}
176
177/*
178 * Slightly higher-level routines aware of buffering and flow control.
179 */
180static int
181aju_cons_readable(void)
182{
183	uint32_t v;
184
185	AJU_CONSOLE_LOCK_ASSERT();
186
187	if (aju_cons_buffer_valid)
188		return (1);
189	v = aju_cons_data_read();
190	if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
191		aju_cons_buffer_valid = 1;
192		aju_cons_buffer_data = (v & ALTERA_JTAG_UART_DATA_DATA);
193		return (1);
194	}
195	return (0);
196}
197
198static void
199aju_cons_write(char ch)
200{
201	uint32_t v;
202
203	AJU_CONSOLE_LOCK_ASSERT();
204
205	/*
206	 * The flow control logic here is somewhat subtle: we want to wait for
207	 * write buffer space only while JTAG is present.  However, we can't
208	 * directly ask if JTAG is present -- just whether it's been seen
209	 * since we last cleared the ALTERA_JTAG_UART_CONTROL_AC bit.  As
210	 * such, implement a polling loop in which we both wait for space and
211	 * try to decide whether JTAG has disappeared on us.  We will have to
212	 * wait one complete polling delay to detect that JTAG has gone away,
213	 * but otherwise shouldn't wait any further once it has gone.  And we
214	 * had to wait for buffer space anyway, if it was there.
215	 *
216	 * If JTAG is spotted, reset the TTY-layer miss counter so console-
217	 * layer clearing of the bit doesn't trigger a TTY-layer
218	 * disconnection.
219	 *
220	 * XXXRW: The polling delay may require tuning.
221	 */
222	v = aju_cons_control_read();
223	if (v & ALTERA_JTAG_UART_CONTROL_AC) {
224		aju_cons_jtag_present = 1;
225		aju_cons_jtag_missed = 0;
226		v &= ~ALTERA_JTAG_UART_CONTROL_AC;
227		aju_cons_control_write(v);
228	}
229	while ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) == 0) {
230		if (!aju_cons_jtag_present)
231			return;
232		DELAY(ALTERA_JTAG_UART_AC_POLL_DELAY);
233		v = aju_cons_control_read();
234		if (v & ALTERA_JTAG_UART_CONTROL_AC) {
235			aju_cons_jtag_present = 1;
236			v &= ~ALTERA_JTAG_UART_CONTROL_AC;
237			aju_cons_control_write(v);
238		} else
239			aju_cons_jtag_present = 0;
240	}
241	aju_cons_data_write(ch);
242}
243
244static char
245aju_cons_read(void)
246{
247
248	AJU_CONSOLE_LOCK_ASSERT();
249
250	while (!aju_cons_readable());
251	aju_cons_buffer_valid = 0;
252	return (aju_cons_buffer_data);
253}
254
255/*
256 * Implementation of a FreeBSD low-level, polled console driver.
257 */
258static void
259aju_cnprobe(struct consdev *cp)
260{
261
262	sprintf(cp->cn_name, "%s%d", AJU_TTYNAME, 0);
263	cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
264}
265
266static void
267aju_cninit(struct consdev *cp)
268{
269	uint32_t v;
270
271	AJU_CONSOLE_LOCK_INIT();
272
273	AJU_CONSOLE_LOCK();
274	v = aju_cons_control_read();
275	v &= ~ALTERA_JTAG_UART_CONTROL_AC;
276	aju_cons_control_write(v);
277	AJU_CONSOLE_UNLOCK();
278}
279
280static void
281aju_cnterm(struct consdev *cp)
282{
283
284}
285
286static int
287aju_cngetc(struct consdev *cp)
288{
289	int ret;
290
291	AJU_CONSOLE_LOCK();
292	ret = aju_cons_read();
293	AJU_CONSOLE_UNLOCK();
294	return (ret);
295}
296
297static void
298aju_cnputc(struct consdev *cp, int c)
299{
300
301	AJU_CONSOLE_LOCK();
302	aju_cons_write(c);
303	AJU_CONSOLE_UNLOCK();
304}
305
306static void
307aju_cngrab(struct consdev *cp)
308{
309
310}
311
312static void
313aju_cnungrab(struct consdev *cp)
314{
315
316}
317
318CONSOLE_DRIVER(aju);
319