1697a77c1SRobert Watson /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
4697a77c1SRobert Watson * Copyright (c) 2011-2012 Robert N. M. Watson
5697a77c1SRobert Watson * All rights reserved.
6697a77c1SRobert Watson *
7697a77c1SRobert Watson * This software was developed by SRI International and the University of
8697a77c1SRobert Watson * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
9697a77c1SRobert Watson * ("CTSRD"), as part of the DARPA CRASH research programme.
10697a77c1SRobert Watson *
11697a77c1SRobert Watson * Redistribution and use in source and binary forms, with or without
12697a77c1SRobert Watson * modification, are permitted provided that the following conditions
13697a77c1SRobert Watson * are met:
14697a77c1SRobert Watson * 1. Redistributions of source code must retain the above copyright
15697a77c1SRobert Watson * notice, this list of conditions and the following disclaimer.
16697a77c1SRobert Watson * 2. Redistributions in binary form must reproduce the above copyright
17697a77c1SRobert Watson * notice, this list of conditions and the following disclaimer in the
18697a77c1SRobert Watson * documentation and/or other materials provided with the distribution.
19697a77c1SRobert Watson *
20697a77c1SRobert Watson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21697a77c1SRobert Watson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22697a77c1SRobert Watson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23697a77c1SRobert Watson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24697a77c1SRobert Watson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25697a77c1SRobert Watson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26697a77c1SRobert Watson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27697a77c1SRobert Watson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28697a77c1SRobert Watson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29697a77c1SRobert Watson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30697a77c1SRobert Watson * SUCH DAMAGE.
31697a77c1SRobert Watson */
32697a77c1SRobert Watson
33697a77c1SRobert Watson #include <sys/param.h>
346a347d02SRobert Watson #include <sys/bus.h>
35697a77c1SRobert Watson #include <sys/cons.h>
36697a77c1SRobert Watson #include <sys/endian.h>
37697a77c1SRobert Watson #include <sys/kdb.h>
38697a77c1SRobert Watson #include <sys/kernel.h>
39697a77c1SRobert Watson #include <sys/lock.h>
40697a77c1SRobert Watson #include <sys/mutex.h>
41697a77c1SRobert Watson #include <sys/reboot.h>
4295fadd99SRobert Watson #include <sys/sysctl.h>
43697a77c1SRobert Watson #include <sys/systm.h>
44697a77c1SRobert Watson #include <sys/tty.h>
45697a77c1SRobert Watson
46697a77c1SRobert Watson #include <ddb/ddb.h>
47697a77c1SRobert Watson
48697a77c1SRobert Watson #include <dev/altera/jtag_uart/altera_jtag_uart.h>
49697a77c1SRobert Watson
507029da5cSPawel Biernacki static SYSCTL_NODE(_hw, OID_AUTO, altera_jtag_uart,
517029da5cSPawel Biernacki CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
5295fadd99SRobert Watson "Altera JTAG UART configuration knobs");
5395fadd99SRobert Watson
54697a77c1SRobert Watson /*
55697a77c1SRobert Watson * One-byte buffer as we can't check whether the UART is readable without
56697a77c1SRobert Watson * actually reading from it, synchronised by a spinlock; this lock also
57697a77c1SRobert Watson * synchronises access to the I/O ports for non-atomic sequences. These
58697a77c1SRobert Watson * symbols are public so that the TTY layer can use them when working on an
59697a77c1SRobert Watson * instance of the UART that is also a low-level console.
60697a77c1SRobert Watson */
61697a77c1SRobert Watson char aju_cons_buffer_data;
62697a77c1SRobert Watson int aju_cons_buffer_valid;
63697a77c1SRobert Watson int aju_cons_jtag_present;
64697a77c1SRobert Watson u_int aju_cons_jtag_missed;
65697a77c1SRobert Watson struct mtx aju_cons_lock;
66697a77c1SRobert Watson
67697a77c1SRobert Watson /*
68697a77c1SRobert Watson * Low-level console driver functions.
69697a77c1SRobert Watson */
70697a77c1SRobert Watson static cn_probe_t aju_cnprobe;
71697a77c1SRobert Watson static cn_init_t aju_cninit;
72697a77c1SRobert Watson static cn_term_t aju_cnterm;
73697a77c1SRobert Watson static cn_getc_t aju_cngetc;
74697a77c1SRobert Watson static cn_putc_t aju_cnputc;
75697a77c1SRobert Watson static cn_grab_t aju_cngrab;
76697a77c1SRobert Watson static cn_ungrab_t aju_cnungrab;
77697a77c1SRobert Watson
78697a77c1SRobert Watson /*
79697a77c1SRobert Watson * JTAG sets the ALTERA_JTAG_UART_CONTROL_AC bit whenever it accesses the
80697a77c1SRobert Watson * FIFO. This allows us to (sort of) tell when JTAG is present, so that we
81697a77c1SRobert Watson * can adopt lossy, rather than blocking, behaviour when JTAG isn't there.
82697a77c1SRobert Watson * When it is present, we do full flow control. This delay is how long we
83697a77c1SRobert Watson * wait to see if JTAG has really disappeared when finding a full buffer and
84697a77c1SRobert Watson * no AC bit set.
85697a77c1SRobert Watson */
86697a77c1SRobert Watson #define ALTERA_JTAG_UART_AC_POLL_DELAY 10000
8795fadd99SRobert Watson static u_int altera_jtag_uart_ac_poll_delay =
8895fadd99SRobert Watson ALTERA_JTAG_UART_AC_POLL_DELAY;
8995fadd99SRobert Watson SYSCTL_UINT(_hw_altera_jtag_uart, OID_AUTO, ac_poll_delay,
9095fadd99SRobert Watson CTLFLAG_RW, &altera_jtag_uart_ac_poll_delay, 0,
9195fadd99SRobert Watson "Maximum delay waiting for JTAG present flag when buffer is full");
92697a77c1SRobert Watson
93697a77c1SRobert Watson /*
94697a77c1SRobert Watson * I/O routines lifted from Deimos. This is not only MIPS-specific, but also
9596240c89SEitan Adler * BERI-specific, as we're hard coding the address at which we expect to
96697a77c1SRobert Watson * find the Altera JTAG UART and using it unconditionally. We use these
97697a77c1SRobert Watson * low-level routines so that we can perform console I/O long before newbus
98697a77c1SRobert Watson * has initialised and devices have attached. The TTY layer of the driver
99697a77c1SRobert Watson * knows about this, and uses the console-layer spinlock instead of the
100697a77c1SRobert Watson * TTY-layer lock to avoid confusion between layers for the console UART.
101697a77c1SRobert Watson *
102697a77c1SRobert Watson * XXXRW: The only place this inter-layer behaviour breaks down is if the
103697a77c1SRobert Watson * low-level console is used for polled read while the TTY driver is also
104697a77c1SRobert Watson * looking for input. Probably we should also share buffers between layers.
105697a77c1SRobert Watson */
106697a77c1SRobert Watson #define MIPS_XKPHYS_UNCACHED_BASE 0x9000000000000000
107697a77c1SRobert Watson
108697a77c1SRobert Watson typedef uint64_t paddr_t;
109697a77c1SRobert Watson typedef uint64_t vaddr_t;
110697a77c1SRobert Watson
111697a77c1SRobert Watson static inline vaddr_t
mips_phys_to_uncached(paddr_t phys)112697a77c1SRobert Watson mips_phys_to_uncached(paddr_t phys)
113697a77c1SRobert Watson {
114697a77c1SRobert Watson
115697a77c1SRobert Watson return (phys | MIPS_XKPHYS_UNCACHED_BASE);
116697a77c1SRobert Watson }
117697a77c1SRobert Watson
118697a77c1SRobert Watson static inline uint32_t
mips_ioread_uint32(vaddr_t vaddr)119697a77c1SRobert Watson mips_ioread_uint32(vaddr_t vaddr)
120697a77c1SRobert Watson {
121697a77c1SRobert Watson uint32_t v;
122697a77c1SRobert Watson
123697a77c1SRobert Watson __asm__ __volatile__ ("lw %0, 0(%1)" : "=r" (v) : "r" (vaddr));
124697a77c1SRobert Watson return (v);
125697a77c1SRobert Watson }
126697a77c1SRobert Watson
127697a77c1SRobert Watson static inline void
mips_iowrite_uint32(vaddr_t vaddr,uint32_t v)128697a77c1SRobert Watson mips_iowrite_uint32(vaddr_t vaddr, uint32_t v)
129697a77c1SRobert Watson {
130697a77c1SRobert Watson
131697a77c1SRobert Watson __asm__ __volatile__ ("sw %0, 0(%1)" : : "r" (v), "r" (vaddr));
132697a77c1SRobert Watson }
133697a77c1SRobert Watson
134697a77c1SRobert Watson /*
135697a77c1SRobert Watson * Little-endian versions of 32-bit I/O routines.
136697a77c1SRobert Watson */
137697a77c1SRobert Watson static inline uint32_t
mips_ioread_uint32le(vaddr_t vaddr)138697a77c1SRobert Watson mips_ioread_uint32le(vaddr_t vaddr)
139697a77c1SRobert Watson {
140697a77c1SRobert Watson
141697a77c1SRobert Watson return (le32toh(mips_ioread_uint32(vaddr)));
142697a77c1SRobert Watson }
143697a77c1SRobert Watson
144697a77c1SRobert Watson static inline void
mips_iowrite_uint32le(vaddr_t vaddr,uint32_t v)145697a77c1SRobert Watson mips_iowrite_uint32le(vaddr_t vaddr, uint32_t v)
146697a77c1SRobert Watson {
147697a77c1SRobert Watson
148697a77c1SRobert Watson mips_iowrite_uint32(vaddr, htole32(v));
149697a77c1SRobert Watson }
150697a77c1SRobert Watson
151697a77c1SRobert Watson /*
152697a77c1SRobert Watson * Low-level read and write register routines; the Altera UART is little
153697a77c1SRobert Watson * endian, so we byte swap 32-bit reads and writes.
154697a77c1SRobert Watson */
155697a77c1SRobert Watson static inline uint32_t
aju_cons_data_read(void)156697a77c1SRobert Watson aju_cons_data_read(void)
157697a77c1SRobert Watson {
158697a77c1SRobert Watson
159697a77c1SRobert Watson return (mips_ioread_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
160697a77c1SRobert Watson ALTERA_JTAG_UART_DATA_OFF)));
161697a77c1SRobert Watson }
162697a77c1SRobert Watson
163697a77c1SRobert Watson static inline void
aju_cons_data_write(uint32_t v)164697a77c1SRobert Watson aju_cons_data_write(uint32_t v)
165697a77c1SRobert Watson {
166697a77c1SRobert Watson
167697a77c1SRobert Watson mips_iowrite_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
168697a77c1SRobert Watson ALTERA_JTAG_UART_DATA_OFF), v);
169697a77c1SRobert Watson }
170697a77c1SRobert Watson
171697a77c1SRobert Watson static inline uint32_t
aju_cons_control_read(void)172697a77c1SRobert Watson aju_cons_control_read(void)
173697a77c1SRobert Watson {
174697a77c1SRobert Watson
175697a77c1SRobert Watson return (mips_ioread_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
176697a77c1SRobert Watson ALTERA_JTAG_UART_CONTROL_OFF)));
177697a77c1SRobert Watson }
178697a77c1SRobert Watson
179697a77c1SRobert Watson static inline void
aju_cons_control_write(uint32_t v)180697a77c1SRobert Watson aju_cons_control_write(uint32_t v)
181697a77c1SRobert Watson {
182697a77c1SRobert Watson
183697a77c1SRobert Watson mips_iowrite_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
184697a77c1SRobert Watson ALTERA_JTAG_UART_CONTROL_OFF), v);
185697a77c1SRobert Watson }
186697a77c1SRobert Watson
187697a77c1SRobert Watson /*
188697a77c1SRobert Watson * Slightly higher-level routines aware of buffering and flow control.
189697a77c1SRobert Watson */
190697a77c1SRobert Watson static int
aju_cons_readable(void)191697a77c1SRobert Watson aju_cons_readable(void)
192697a77c1SRobert Watson {
193697a77c1SRobert Watson uint32_t v;
194697a77c1SRobert Watson
195697a77c1SRobert Watson AJU_CONSOLE_LOCK_ASSERT();
196697a77c1SRobert Watson
197697a77c1SRobert Watson if (aju_cons_buffer_valid)
198697a77c1SRobert Watson return (1);
199697a77c1SRobert Watson v = aju_cons_data_read();
200697a77c1SRobert Watson if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
201697a77c1SRobert Watson aju_cons_buffer_valid = 1;
202697a77c1SRobert Watson aju_cons_buffer_data = (v & ALTERA_JTAG_UART_DATA_DATA);
203697a77c1SRobert Watson return (1);
204697a77c1SRobert Watson }
205697a77c1SRobert Watson return (0);
206697a77c1SRobert Watson }
207697a77c1SRobert Watson
208697a77c1SRobert Watson static void
aju_cons_write(char ch)209697a77c1SRobert Watson aju_cons_write(char ch)
210697a77c1SRobert Watson {
211697a77c1SRobert Watson uint32_t v;
212697a77c1SRobert Watson
213697a77c1SRobert Watson AJU_CONSOLE_LOCK_ASSERT();
214697a77c1SRobert Watson
215697a77c1SRobert Watson /*
216697a77c1SRobert Watson * The flow control logic here is somewhat subtle: we want to wait for
217697a77c1SRobert Watson * write buffer space only while JTAG is present. However, we can't
218697a77c1SRobert Watson * directly ask if JTAG is present -- just whether it's been seen
219697a77c1SRobert Watson * since we last cleared the ALTERA_JTAG_UART_CONTROL_AC bit. As
220697a77c1SRobert Watson * such, implement a polling loop in which we both wait for space and
221697a77c1SRobert Watson * try to decide whether JTAG has disappeared on us. We will have to
222697a77c1SRobert Watson * wait one complete polling delay to detect that JTAG has gone away,
223697a77c1SRobert Watson * but otherwise shouldn't wait any further once it has gone. And we
224697a77c1SRobert Watson * had to wait for buffer space anyway, if it was there.
225697a77c1SRobert Watson *
226697a77c1SRobert Watson * If JTAG is spotted, reset the TTY-layer miss counter so console-
227697a77c1SRobert Watson * layer clearing of the bit doesn't trigger a TTY-layer
228697a77c1SRobert Watson * disconnection.
229697a77c1SRobert Watson *
230a1f76946SBrooks Davis * XXXRW: Notice the inherent race with hardware: in clearing the
23195fadd99SRobert Watson * bit, we may race with hardware setting the same bit. This can
23295fadd99SRobert Watson * cause real-world reliability problems due to lost output on the
23395fadd99SRobert Watson * console.
234697a77c1SRobert Watson */
235697a77c1SRobert Watson v = aju_cons_control_read();
236697a77c1SRobert Watson if (v & ALTERA_JTAG_UART_CONTROL_AC) {
237697a77c1SRobert Watson aju_cons_jtag_present = 1;
238697a77c1SRobert Watson aju_cons_jtag_missed = 0;
239697a77c1SRobert Watson v &= ~ALTERA_JTAG_UART_CONTROL_AC;
240697a77c1SRobert Watson aju_cons_control_write(v);
241697a77c1SRobert Watson }
242697a77c1SRobert Watson while ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) == 0) {
243697a77c1SRobert Watson if (!aju_cons_jtag_present)
244697a77c1SRobert Watson return;
24595fadd99SRobert Watson DELAY(altera_jtag_uart_ac_poll_delay);
246697a77c1SRobert Watson v = aju_cons_control_read();
247697a77c1SRobert Watson if (v & ALTERA_JTAG_UART_CONTROL_AC) {
248697a77c1SRobert Watson aju_cons_jtag_present = 1;
249697a77c1SRobert Watson v &= ~ALTERA_JTAG_UART_CONTROL_AC;
250697a77c1SRobert Watson aju_cons_control_write(v);
251697a77c1SRobert Watson } else
252697a77c1SRobert Watson aju_cons_jtag_present = 0;
253697a77c1SRobert Watson }
254697a77c1SRobert Watson aju_cons_data_write(ch);
255697a77c1SRobert Watson }
256697a77c1SRobert Watson
257697a77c1SRobert Watson static char
aju_cons_read(void)258697a77c1SRobert Watson aju_cons_read(void)
259697a77c1SRobert Watson {
260697a77c1SRobert Watson
261697a77c1SRobert Watson AJU_CONSOLE_LOCK_ASSERT();
262697a77c1SRobert Watson
263697a77c1SRobert Watson while (!aju_cons_readable());
264697a77c1SRobert Watson aju_cons_buffer_valid = 0;
265697a77c1SRobert Watson return (aju_cons_buffer_data);
266697a77c1SRobert Watson }
267697a77c1SRobert Watson
268697a77c1SRobert Watson /*
269697a77c1SRobert Watson * Implementation of a FreeBSD low-level, polled console driver.
270697a77c1SRobert Watson */
271697a77c1SRobert Watson static void
aju_cnprobe(struct consdev * cp)272697a77c1SRobert Watson aju_cnprobe(struct consdev *cp)
273697a77c1SRobert Watson {
274697a77c1SRobert Watson
275697a77c1SRobert Watson sprintf(cp->cn_name, "%s%d", AJU_TTYNAME, 0);
276697a77c1SRobert Watson cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
277697a77c1SRobert Watson }
278697a77c1SRobert Watson
279697a77c1SRobert Watson static void
aju_cninit(struct consdev * cp)280697a77c1SRobert Watson aju_cninit(struct consdev *cp)
281697a77c1SRobert Watson {
282697a77c1SRobert Watson uint32_t v;
283697a77c1SRobert Watson
284697a77c1SRobert Watson AJU_CONSOLE_LOCK_INIT();
285697a77c1SRobert Watson
286697a77c1SRobert Watson AJU_CONSOLE_LOCK();
287697a77c1SRobert Watson v = aju_cons_control_read();
288697a77c1SRobert Watson v &= ~ALTERA_JTAG_UART_CONTROL_AC;
289697a77c1SRobert Watson aju_cons_control_write(v);
290697a77c1SRobert Watson AJU_CONSOLE_UNLOCK();
291697a77c1SRobert Watson }
292697a77c1SRobert Watson
293697a77c1SRobert Watson static void
aju_cnterm(struct consdev * cp)294697a77c1SRobert Watson aju_cnterm(struct consdev *cp)
295697a77c1SRobert Watson {
296697a77c1SRobert Watson
297697a77c1SRobert Watson }
298697a77c1SRobert Watson
299697a77c1SRobert Watson static int
aju_cngetc(struct consdev * cp)300697a77c1SRobert Watson aju_cngetc(struct consdev *cp)
301697a77c1SRobert Watson {
302697a77c1SRobert Watson int ret;
303697a77c1SRobert Watson
304697a77c1SRobert Watson AJU_CONSOLE_LOCK();
305697a77c1SRobert Watson ret = aju_cons_read();
306697a77c1SRobert Watson AJU_CONSOLE_UNLOCK();
307697a77c1SRobert Watson return (ret);
308697a77c1SRobert Watson }
309697a77c1SRobert Watson
310697a77c1SRobert Watson static void
aju_cnputc(struct consdev * cp,int c)311697a77c1SRobert Watson aju_cnputc(struct consdev *cp, int c)
312697a77c1SRobert Watson {
313697a77c1SRobert Watson
314697a77c1SRobert Watson AJU_CONSOLE_LOCK();
315697a77c1SRobert Watson aju_cons_write(c);
316697a77c1SRobert Watson AJU_CONSOLE_UNLOCK();
317697a77c1SRobert Watson }
318697a77c1SRobert Watson
319697a77c1SRobert Watson static void
aju_cngrab(struct consdev * cp)320697a77c1SRobert Watson aju_cngrab(struct consdev *cp)
321697a77c1SRobert Watson {
322697a77c1SRobert Watson
323697a77c1SRobert Watson }
324697a77c1SRobert Watson
325697a77c1SRobert Watson static void
aju_cnungrab(struct consdev * cp)326697a77c1SRobert Watson aju_cnungrab(struct consdev *cp)
327697a77c1SRobert Watson {
328697a77c1SRobert Watson
329697a77c1SRobert Watson }
330697a77c1SRobert Watson
331697a77c1SRobert Watson CONSOLE_DRIVER(aju);
332