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