The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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: releng/10.4/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c 314667 2017-03-04 13:03:31Z avg $");
   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, 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  */
   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 
  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 
  301 static void
  302 aju_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 
  313 static void
  314 aju_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 
  344 static void
  345 aju_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 
  375 static void
  376 aju_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 
  393 int
  394 altera_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 
  467 void
  468 altera_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 }

Cache object: 7caeca5f088a9f98c581351a576a1db0


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.