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/riscv/sifive/sifive_uart.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2019 Axiado Corporation
    5  * All rights reserved.
    6  *
    7  * This software was developed in part by Kristof Provost under contract for
    8  * Axiado Corporation.
    9  *
   10  * Redistribution and use in source and binary forms, with or without
   11  * modification, are permitted provided that the following conditions
   12  * are met:
   13  * 1. Redistributions of source code must retain the above copyright
   14  *    notice, this list of conditions and the following disclaimer.
   15  * 2. Redistributions in binary form must reproduce the above copyright
   16  *    notice, this list of conditions and the following disclaimer in the
   17  *    documentation and/or other materials provided with the distribution.
   18  *
   19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   29  * SUCH DAMAGE.
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include <sys/param.h>
   36 #include <sys/systm.h>
   37 #include <sys/bus.h>
   38 #include <sys/kernel.h>
   39 #include <sys/lock.h>
   40 #include <sys/module.h>
   41 #include <sys/mutex.h>
   42 #include <sys/rman.h>
   43 
   44 #include <machine/bus.h>
   45 #include <machine/cpu.h>
   46 
   47 #include <dev/extres/clk/clk.h>
   48 
   49 #include <dev/ofw/ofw_bus.h>
   50 #include <dev/ofw/ofw_bus_subr.h>
   51 #include <dev/ofw/openfirm.h>
   52 
   53 #include <dev/uart/uart.h>
   54 #include <dev/uart/uart_bus.h>
   55 #include <dev/uart/uart_cpu.h>
   56 #include <dev/uart/uart_cpu_fdt.h>
   57 
   58 #include "uart_if.h"
   59 
   60 #define SFUART_TXDATA                   0x00
   61 #define         SFUART_TXDATA_FULL      (1 << 31)
   62 #define SFUART_RXDATA                   0x04
   63 #define         SFUART_RXDATA_EMPTY     (1 << 31)
   64 #define SFUART_TXCTRL                   0x08
   65 #define         SFUART_TXCTRL_ENABLE    0x01
   66 #define         SFUART_TXCTRL_NSTOP     0x02
   67 #define         SFUART_TXCTRL_TXCNT     0x70000
   68 #define         SFUART_TXCTRL_TXCNT_SHIFT       16
   69 #define SFUART_RXCTRL                   0x0c
   70 #define         SFUART_RXCTRL_ENABLE    0x01
   71 #define         SFUART_RXCTRL_RXCNT     0x70000
   72 #define         SFUART_RXCTRL_RXCNT_SHIFT       16
   73 #define SFUART_IRQ_ENABLE               0x10
   74 #define         SFUART_IRQ_ENABLE_TXWM  0x01
   75 #define         SFUART_IRQ_ENABLE_RXWM  0x02
   76 #define SFUART_IRQ_PENDING              0x14
   77 #define         SFUART_IRQ_PENDING_TXWM 0x01
   78 #define         SFUART_IRQ_PENDING_RXQM 0x02
   79 #define SFUART_DIV                      0x18
   80 #define SFUART_REGS_SIZE                0x1c
   81 
   82 #define SFUART_RX_FIFO_DEPTH            8
   83 #define SFUART_TX_FIFO_DEPTH            8
   84 
   85 struct sfuart_softc {
   86         struct uart_softc       uart_softc;
   87         clk_t                   clk;
   88 };
   89 
   90 static int
   91 sfuart_probe(struct uart_bas *bas)
   92 {
   93 
   94         bas->regiowidth = 4;
   95 
   96         return (0);
   97 }
   98 
   99 static void
  100 sfuart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits,
  101     int parity)
  102 {
  103         uint32_t reg;
  104 
  105         uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
  106 
  107         /* Enable RX and configure the watermark so that we get an interrupt
  108          * when a single character arrives (if interrupts are enabled). */
  109         reg = SFUART_RXCTRL_ENABLE;
  110         reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
  111         uart_setreg(bas, SFUART_RXCTRL, reg);
  112 
  113         /* Enable TX and configure the watermark so that we get an interrupt
  114          * when there's room for one more character in the TX fifo (if
  115          * interrupts are enabled). */
  116         reg = SFUART_TXCTRL_ENABLE;
  117         reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
  118         if (stopbits == 2)
  119                 reg |= SFUART_TXCTRL_NSTOP;
  120         uart_setreg(bas, SFUART_TXCTRL, reg);
  121 
  122         /* Don't touch DIV. Assume that's set correctly until we can
  123          * reconfigure. */
  124 }
  125 
  126 static void
  127 sfuart_putc(struct uart_bas *bas, int c)
  128 {
  129 
  130         while ((uart_getreg(bas, SFUART_TXDATA) & SFUART_TXDATA_FULL) 
  131             != 0)
  132                 cpu_spinwait();
  133 
  134         uart_setreg(bas, SFUART_TXDATA, c);
  135 }
  136 
  137 static int
  138 sfuart_rxready(struct uart_bas *bas)
  139 {
  140         /*
  141          * Unfortunately the FIFO empty flag is in the FIFO data register so
  142          * reading it would dequeue the character. Instead, rely on the fact
  143          * we've configured the watermark to be 0 and that interrupts are off
  144          * when using the low-level console function, and read the interrupt
  145          * pending state instead.
  146          */
  147         return ((uart_getreg(bas, SFUART_IRQ_PENDING) &
  148             SFUART_IRQ_PENDING_RXQM) != 0);
  149 }
  150 
  151 static int
  152 sfuart_getc(struct uart_bas *bas, struct mtx *hwmtx)
  153 {
  154         int c;
  155 
  156         uart_lock(hwmtx);
  157 
  158         while (((c = uart_getreg(bas, SFUART_RXDATA)) &
  159             SFUART_RXDATA_EMPTY) != 0) {
  160                 uart_unlock(hwmtx);
  161                 DELAY(4);
  162                 uart_lock(hwmtx);
  163         }
  164 
  165         uart_unlock(hwmtx);
  166 
  167         return (c & 0xff);
  168 }
  169 
  170 static int
  171 sfuart_bus_probe(struct uart_softc *sc)
  172 {
  173         int error;
  174 
  175         error = sfuart_probe(&sc->sc_bas);
  176         if (error)
  177                 return (error);
  178 
  179         sc->sc_rxfifosz = SFUART_RX_FIFO_DEPTH;
  180         sc->sc_txfifosz = SFUART_TX_FIFO_DEPTH;
  181         sc->sc_hwiflow = 0;
  182         sc->sc_hwoflow = 0;
  183 
  184         device_set_desc(sc->sc_dev, "SiFive UART");
  185 
  186         return (0);
  187 }
  188 
  189 static int
  190 sfuart_bus_attach(struct uart_softc *sc)
  191 {
  192         struct uart_bas *bas;
  193         struct sfuart_softc *sfsc;
  194         uint64_t freq;
  195         uint32_t reg;
  196         int error;
  197 
  198         sfsc = (struct sfuart_softc *)sc;
  199         bas = &sc->sc_bas;
  200 
  201         error = clk_get_by_ofw_index(sc->sc_dev, 0, 0, &sfsc->clk);
  202         if (error) {
  203                 device_printf(sc->sc_dev, "couldn't allocate clock\n");
  204                 return (ENXIO);
  205         }
  206 
  207         error = clk_enable(sfsc->clk);
  208         if (error) {
  209                 device_printf(sc->sc_dev, "couldn't enable clock\n");
  210                 return (ENXIO);
  211         }
  212 
  213         error = clk_get_freq(sfsc->clk, &freq);
  214         if (error || freq == 0) {
  215                 clk_disable(sfsc->clk);
  216                 device_printf(sc->sc_dev, "couldn't get clock frequency\n");
  217                 return (ENXIO);
  218         }
  219 
  220         bas->rclk = freq;
  221 
  222         /* Enable RX/RX */
  223         reg = SFUART_RXCTRL_ENABLE;
  224         reg |= (0 << SFUART_RXCTRL_RXCNT_SHIFT);
  225         uart_setreg(bas, SFUART_RXCTRL, reg);
  226 
  227         reg = SFUART_TXCTRL_ENABLE;
  228         reg |= (1 << SFUART_TXCTRL_TXCNT_SHIFT);
  229         uart_setreg(bas, SFUART_TXCTRL, reg);
  230 
  231         /* Enable RX interrupt */
  232         uart_setreg(bas, SFUART_IRQ_ENABLE, SFUART_IRQ_ENABLE_RXWM);
  233 
  234         return (0);
  235 }
  236 
  237 static int
  238 sfuart_bus_detach(struct uart_softc *sc)
  239 {
  240         struct sfuart_softc *sfsc;
  241         struct uart_bas *bas;
  242 
  243         sfsc = (struct sfuart_softc *)sc;
  244         bas = &sc->sc_bas;
  245 
  246         /* Disable RX/TX */
  247         uart_setreg(bas, SFUART_RXCTRL, 0);
  248         uart_setreg(bas, SFUART_TXCTRL, 0);
  249 
  250         /* Disable interrupts */
  251         uart_setreg(bas, SFUART_IRQ_ENABLE, 0);
  252 
  253         clk_disable(sfsc->clk);
  254 
  255         return (0);
  256 }
  257 
  258 static int
  259 sfuart_bus_flush(struct uart_softc *sc, int what)
  260 {
  261         struct uart_bas *bas;
  262         uint32_t reg;
  263 
  264         bas = &sc->sc_bas;
  265         uart_lock(sc->sc_hwmtx);
  266 
  267         if (what & UART_FLUSH_TRANSMITTER) {
  268                 do {
  269                         reg = uart_getreg(bas, SFUART_TXDATA);
  270                 } while ((reg & SFUART_TXDATA_FULL) != 0);
  271         }
  272 
  273         if (what & UART_FLUSH_RECEIVER) {
  274                 do {
  275                         reg = uart_getreg(bas, SFUART_RXDATA);
  276                 } while ((reg & SFUART_RXDATA_EMPTY) == 0);
  277         }
  278         uart_unlock(sc->sc_hwmtx);
  279 
  280         return (0);
  281 }
  282 
  283 #define SIGCHG(c, i, s, d)                                              \
  284         do {                                                            \
  285                 if (c)                                                  \
  286                         i |= ((i) & (s)) ? (s) : (s) | (d);             \
  287                 else                                                    \
  288                         i = ((i) & (s)) ? ((i) & ~(s)) | (d) : (i);     \
  289         } while (0)
  290 
  291 static int
  292 sfuart_bus_getsig(struct uart_softc *sc)
  293 {
  294         uint32_t new, old, sig;
  295 
  296         do {
  297                 old = sc->sc_hwsig;
  298                 sig = old;
  299                 SIGCHG(1, sig, SER_DSR, SER_DDSR);
  300                 SIGCHG(1, sig, SER_DCD, SER_DDCD);
  301                 SIGCHG(1, sig, SER_CTS, SER_DCTS);
  302                 new = sig & ~SER_MASK_DELTA;
  303         } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
  304 
  305         return (sig);
  306 }
  307 
  308 static int
  309 sfuart_bus_setsig(struct uart_softc *sc, int sig)
  310 {
  311         uint32_t new, old;
  312 
  313         do {
  314                 old = sc->sc_hwsig;
  315                 new = old;
  316                 if (sig & SER_DDTR) {
  317                         SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR);
  318                 }
  319                 if (sig & SER_DRTS) {
  320                         SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS);
  321                 }
  322          } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new));
  323 
  324         return (0);
  325 }
  326 
  327 static int
  328 sfuart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data)
  329 {
  330         struct uart_bas *bas;
  331         uint32_t reg;
  332         int error;
  333 
  334         bas = &sc->sc_bas;
  335 
  336         uart_lock(sc->sc_hwmtx);
  337 
  338         switch (request) {
  339         case UART_IOCTL_BAUD:
  340                 reg = uart_getreg(bas, SFUART_DIV);
  341                 if (reg == 0) {
  342                         /* Possible if the divisor hasn't been set up yet. */
  343                         error = ENXIO;
  344                         break;
  345                 }
  346                 *(int*)data = bas->rclk / (reg + 1);
  347                 error = 0;
  348                 break;
  349         default:
  350                 error = EINVAL;
  351                 break;
  352         }
  353 
  354         uart_unlock(sc->sc_hwmtx);
  355 
  356         return (error);
  357 }
  358 
  359 static int
  360 sfuart_bus_ipend(struct uart_softc *sc)
  361 {
  362         struct uart_bas *bas;
  363         int ipend;
  364         uint32_t reg, ie;
  365 
  366         bas = &sc->sc_bas;
  367         uart_lock(sc->sc_hwmtx);
  368 
  369         ipend = 0;
  370         reg = uart_getreg(bas, SFUART_IRQ_PENDING);
  371         ie = uart_getreg(bas, SFUART_IRQ_ENABLE);
  372 
  373         if ((reg & SFUART_IRQ_PENDING_TXWM) != 0 &&
  374             (ie & SFUART_IRQ_ENABLE_TXWM) != 0) {
  375                 ipend |= SER_INT_TXIDLE;
  376 
  377                 /* Disable TX interrupt */
  378                 ie &= ~(SFUART_IRQ_ENABLE_TXWM);
  379                 uart_setreg(bas, SFUART_IRQ_ENABLE, ie);
  380         }
  381 
  382         if ((reg & SFUART_IRQ_PENDING_RXQM) != 0)
  383                 ipend |= SER_INT_RXREADY;
  384 
  385         uart_unlock(sc->sc_hwmtx);
  386 
  387         return (ipend);
  388 }
  389 
  390 static int
  391 sfuart_bus_param(struct uart_softc *sc, int baudrate, int databits,
  392     int stopbits, int parity)
  393 {
  394         struct uart_bas *bas;
  395         uint32_t reg;
  396 
  397         bas = &sc->sc_bas;
  398 
  399         if (databits != 8)
  400                 return (EINVAL);
  401 
  402         if (parity != UART_PARITY_NONE)
  403                 return (EINVAL);
  404 
  405         uart_lock(sc->sc_hwmtx);
  406 
  407         reg = uart_getreg(bas, SFUART_TXCTRL);
  408         if (stopbits == 2) {
  409                 reg |= SFUART_TXCTRL_NSTOP;
  410         } else if (stopbits == 1) {
  411                 reg &= ~SFUART_TXCTRL_NSTOP;
  412         } else {
  413                 uart_unlock(sc->sc_hwmtx);
  414                 return (EINVAL);
  415         }
  416 
  417         if (baudrate > 0 && bas->rclk != 0) {
  418                 reg = (bas->rclk / baudrate) - 1;
  419                 uart_setreg(bas, SFUART_DIV, reg);
  420         }
  421 
  422         uart_unlock(sc->sc_hwmtx);
  423         return (0);
  424 }
  425 
  426 static int
  427 sfuart_bus_receive(struct uart_softc *sc)
  428 {
  429         struct uart_bas *bas;
  430         uint32_t reg;
  431 
  432         bas = &sc->sc_bas;
  433         uart_lock(sc->sc_hwmtx);
  434 
  435         reg = uart_getreg(bas, SFUART_RXDATA);
  436         while ((reg & SFUART_RXDATA_EMPTY) == 0) {
  437                 if (uart_rx_full(sc)) {
  438                         sc->sc_rxbuf[sc->sc_rxput] = UART_STAT_OVERRUN;
  439                         break;
  440                 }
  441 
  442                 uart_rx_put(sc, reg & 0xff);
  443 
  444                 reg = uart_getreg(bas, SFUART_RXDATA);
  445         }
  446 
  447         uart_unlock(sc->sc_hwmtx);
  448 
  449         return (0);
  450 }
  451 
  452 static int
  453 sfuart_bus_transmit(struct uart_softc *sc)
  454 {
  455         struct uart_bas *bas;
  456         int i;
  457         uint32_t reg;
  458 
  459         bas = &sc->sc_bas;
  460         uart_lock(sc->sc_hwmtx);
  461 
  462         reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
  463         reg |= SFUART_IRQ_ENABLE_TXWM;
  464         uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
  465 
  466         for (i = 0; i < sc->sc_txdatasz; i++)
  467                 sfuart_putc(bas, sc->sc_txbuf[i]);
  468 
  469         sc->sc_txbusy = 1;
  470 
  471         uart_unlock(sc->sc_hwmtx);
  472 
  473         return (0);
  474 }
  475 
  476 static void
  477 sfuart_bus_grab(struct uart_softc *sc)
  478 {
  479         struct uart_bas *bas;
  480         uint32_t reg;
  481 
  482         bas = &sc->sc_bas;
  483         uart_lock(sc->sc_hwmtx);
  484 
  485         reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
  486         reg &= ~(SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM);
  487         uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
  488 
  489         uart_unlock(sc->sc_hwmtx);
  490 }
  491 
  492 static void
  493 sfuart_bus_ungrab(struct uart_softc *sc)
  494 {
  495         struct uart_bas *bas;
  496         uint32_t reg;
  497 
  498         bas = &sc->sc_bas;
  499         uart_lock(sc->sc_hwmtx);
  500 
  501         reg = uart_getreg(bas, SFUART_IRQ_ENABLE);
  502         reg |= SFUART_IRQ_ENABLE_TXWM | SFUART_IRQ_PENDING_RXQM;
  503         uart_setreg(bas, SFUART_IRQ_ENABLE, reg);
  504 
  505         uart_unlock(sc->sc_hwmtx);
  506 }
  507 
  508 static kobj_method_t sfuart_methods[] = {
  509         KOBJMETHOD(uart_probe,          sfuart_bus_probe),
  510         KOBJMETHOD(uart_attach,         sfuart_bus_attach),
  511         KOBJMETHOD(uart_detach,         sfuart_bus_detach),
  512         KOBJMETHOD(uart_flush,          sfuart_bus_flush),
  513         KOBJMETHOD(uart_getsig,         sfuart_bus_getsig),
  514         KOBJMETHOD(uart_setsig,         sfuart_bus_setsig),
  515         KOBJMETHOD(uart_ioctl,          sfuart_bus_ioctl),
  516         KOBJMETHOD(uart_ipend,          sfuart_bus_ipend),
  517         KOBJMETHOD(uart_param,          sfuart_bus_param),
  518         KOBJMETHOD(uart_receive,        sfuart_bus_receive),
  519         KOBJMETHOD(uart_transmit,       sfuart_bus_transmit),
  520         KOBJMETHOD(uart_grab,           sfuart_bus_grab),
  521         KOBJMETHOD(uart_ungrab,         sfuart_bus_ungrab),
  522         KOBJMETHOD_END
  523 };
  524 
  525 static struct uart_ops sfuart_ops = {
  526         .probe = sfuart_probe,
  527         .init = sfuart_init,
  528         .term = NULL,
  529         .putc = sfuart_putc,
  530         .rxready = sfuart_rxready,
  531         .getc = sfuart_getc,
  532 };
  533 
  534 struct uart_class sfuart_class = {
  535         "sifiveuart",
  536         sfuart_methods,
  537         sizeof(struct sfuart_softc),
  538         .uc_ops = &sfuart_ops,
  539         .uc_range = SFUART_REGS_SIZE,
  540         .uc_rclk = 0,
  541         .uc_rshift = 0
  542 };
  543 
  544 static struct ofw_compat_data compat_data[] = {
  545         { "sifive,uart0",       (uintptr_t)&sfuart_class },
  546         { NULL,                 (uintptr_t)NULL }
  547 };
  548 
  549 UART_FDT_CLASS_AND_DEVICE(compat_data);

Cache object: f4f8b169dd71dff6b73879bca2f4a4c3


[ 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.