]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / altera / jtag_uart / altera_jtag_uart_tty.c
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  */
55 static struct altera_jtag_uart_softc    *aju_cons_sc;
56
57 static tsw_outwakeup_t  aju_outwakeup;
58 static void             aju_ac_callout(void *);
59 static void             aju_io_callout(void *);
60
61 static 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, one second.
69  */
70 #define AJU_JTAG_MAXMISS                5
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  */
82 static inline uint32_t
83 aju_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
90 static inline void
91 aju_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
97 static inline uint32_t
98 aju_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
105 static inline void
106 aju_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  */
116 static inline int
117 aju_writable(struct altera_jtag_uart_softc *sc)
118 {
119
120         return ((aju_control_read(sc) &
121             ALTERA_JTAG_UART_CONTROL_WSPACE) != 0);
122 }
123
124 static inline int
125 aju_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
142 static char
143 aju_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  */
156 static void
157 aju_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
168 static void
169 aju_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
180 static void
181 aju_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
192 static void
193 aju_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  */
209 static void
210 aju_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  */
240 static void
241 aju_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                         aju_data_write(sc, ch);
259                 } else {
260                         /*
261                          * If JTAG is not present, then we will drop this
262                          * character instead of perhaps scheduling an
263                          * interrupt to let us know when there is buffer
264                          * space.  Otherwise we might get a write interrupt
265                          * later even though we aren't interested in sending
266                          * anymore.  Loop to drain TTY-layer buffer.
267                          */
268                         if (*sc->ajus_jtag_presentp == 0) {
269                                 if (ttydisc_getc(tp, &ch, sizeof(ch)) !=
270                                     sizeof(ch))
271                                         panic("%s: ttydisc_getc 2", __func__);
272                                 AJU_UNLOCK(sc);
273                                 continue;
274                         }
275                         if (sc->ajus_irq_res != NULL)
276                                 aju_intr_writable_enable(sc);
277                         return;
278                 }
279                 AJU_UNLOCK(sc);
280         }
281         AJU_LOCK(sc);
282         aju_intr_writable_disable(sc);
283 }
284
285 static void
286 aju_outwakeup(struct tty *tp)
287 {
288         struct altera_jtag_uart_softc *sc = tty_softc(tp);
289
290         tty_lock_assert(tp, MA_OWNED);
291
292         AJU_LOCK(sc);
293         aju_handle_output(sc, tp);
294         AJU_UNLOCK(sc);
295 }
296
297 static void
298 aju_io_callout(void *arg)
299 {
300         struct altera_jtag_uart_softc *sc = arg;
301         struct tty *tp = sc->ajus_ttyp;
302
303         tty_lock(tp);
304         AJU_LOCK(sc);
305
306         /*
307          * It would be convenient if we could share code with aju_intr() here
308          * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and
309          * ALTERA_JTAG_UART_CONTROL_WI.  Unfortunately, it's not clear that
310          * this is supported, so do all the work to poll for both input and
311          * output.
312          */
313         aju_handle_input(sc, tp);
314         aju_handle_output(sc, tp);
315
316         /*
317          * Reschedule next poll attempt.  There's some argument that we should
318          * do adaptive polling based on the expectation of I/O: is something
319          * pending in the output buffer, or have we recently had input, but we
320          * don't.
321          */
322         callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
323             aju_io_callout, sc);
324         AJU_UNLOCK(sc);
325         tty_unlock(tp);
326 }
327
328 static void
329 aju_ac_callout(void *arg)
330 {
331         struct altera_jtag_uart_softc *sc = arg;
332         struct tty *tp = sc->ajus_ttyp;
333         uint32_t v;
334
335         tty_lock(tp);
336         AJU_LOCK(sc);
337         v = aju_control_read(sc);
338         if (v & ALTERA_JTAG_UART_CONTROL_AC) {
339                 v &= ~ALTERA_JTAG_UART_CONTROL_AC;
340                 aju_control_write(sc, v);
341                 if (*sc->ajus_jtag_presentp == 0) {
342                         *sc->ajus_jtag_missedp = 0;
343                         *sc->ajus_jtag_presentp = 1;
344                         aju_handle_output(sc, tp);
345                 }
346         } else if (*sc->ajus_jtag_presentp != 0) {
347                 (*sc->ajus_jtag_missedp)++;
348                 if (*sc->ajus_jtag_missedp >= AJU_JTAG_MAXMISS) {
349                         *sc->ajus_jtag_presentp = 0;
350                         aju_handle_output(sc, tp);
351                 }
352         }
353         callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
354             aju_ac_callout, sc);
355         AJU_UNLOCK(sc);
356         tty_unlock(tp);
357 }
358
359 static void
360 aju_intr(void *arg)
361 {
362         struct altera_jtag_uart_softc *sc = arg;
363         struct tty *tp = sc->ajus_ttyp;
364         uint32_t v;
365
366         tty_lock(tp);
367         AJU_LOCK(sc);
368         v = aju_control_read(sc);
369         if (v & ALTERA_JTAG_UART_CONTROL_RI)
370                 aju_handle_input(sc, tp);
371         if (v & ALTERA_JTAG_UART_CONTROL_WI)
372                 aju_handle_output(sc, tp);
373         AJU_UNLOCK(sc);
374         tty_unlock(tp);
375 }
376
377 int
378 altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc)
379 {
380         struct tty *tp;
381         int error;
382
383         AJU_LOCK_INIT(sc);
384
385         /*
386          * XXXRW: Currently, we detect the console solely based on it using a
387          * reserved address, and borrow console-level locks and buffer if so.
388          * Is there a better way?
389          */
390         if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) {
391                 sc->ajus_lockp = &aju_cons_lock;
392                 sc->ajus_buffer_validp = &aju_cons_buffer_valid;
393                 sc->ajus_buffer_datap = &aju_cons_buffer_data;
394                 sc->ajus_jtag_presentp = &aju_cons_jtag_present;
395                 sc->ajus_jtag_missedp = &aju_cons_jtag_missed;
396                 sc->ajus_flags |= ALTERA_JTAG_UART_FLAG_CONSOLE;
397         } else {
398                 sc->ajus_lockp = &sc->ajus_lock;
399                 sc->ajus_buffer_validp = &sc->ajus_buffer_valid;
400                 sc->ajus_buffer_datap = &sc->ajus_buffer_data;
401                 sc->ajus_jtag_presentp = &sc->ajus_jtag_present;
402                 sc->ajus_jtag_missedp = &sc->ajus_jtag_missed;
403         }
404
405         /*
406          * Disable interrupts regardless of whether or not we plan to use
407          * them.  We will register an interrupt handler now if they will be
408          * used, but not re-enable intil later once the remainder of the tty
409          * layer is properly initialised, as we're not ready for input yet.
410          */
411         AJU_LOCK(sc);
412         aju_intr_disable(sc);
413         AJU_UNLOCK(sc);
414         if (sc->ajus_irq_res != NULL) {
415                 error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res,
416                     INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL,
417                     aju_intr, sc, &sc->ajus_irq_cookie);
418                 if (error) {
419                         device_printf(sc->ajus_dev,
420                             "could not activate interrupt\n");
421                         AJU_LOCK_DESTROY(sc);
422                         return (error);
423                 }
424         }
425         tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc);
426         if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) {
427                 aju_cons_sc = sc;
428                 tty_init_console(tp, 0);
429         }
430         tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit);
431
432         /*
433          * If we will be using interrupts, enable them now; otherwise, start
434          * polling.  From this point onwards, input can arrive.
435          */
436         if (sc->ajus_irq_res != NULL) {
437                 AJU_LOCK(sc);
438                 aju_intr_readable_enable(sc);
439                 AJU_UNLOCK(sc);
440         } else {
441                 callout_init(&sc->ajus_io_callout, CALLOUT_MPSAFE);
442                 callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
443                     aju_io_callout, sc);
444         }
445         callout_init(&sc->ajus_ac_callout, CALLOUT_MPSAFE);
446         callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
447             aju_ac_callout, sc);
448         return (0);
449 }
450
451 void
452 altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc)
453 {
454         struct tty *tp = sc->ajus_ttyp;
455
456         /*
457          * If we're using interrupts, disable and release the interrupt
458          * handler now.  Otherwise drain the polling timeout.
459          */
460         if (sc->ajus_irq_res != NULL) {
461                 AJU_LOCK(sc);
462                 aju_intr_disable(sc);
463                 AJU_UNLOCK(sc);
464                 bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res,
465                     sc->ajus_irq_cookie);
466         } else
467                 callout_drain(&sc->ajus_io_callout);
468         callout_drain(&sc->ajus_ac_callout);
469         if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
470                 aju_cons_sc = NULL;
471         tty_lock(tp);
472         tty_rel_gone(tp);
473         AJU_LOCK_DESTROY(sc);
474 }