On Sun, Mar 13, 2022 at 04:17:07PM +0100, Mark Kettenis wrote:
> > Date: Fri, 11 Mar 2022 07:53:13 +0100
> > From: Anton Lindqvist <an...@basename.se>
> > 
> > On Tue, Mar 08, 2022 at 01:44:47PM +0000, Visa Hankala wrote:
> > > On Tue, Mar 08, 2022 at 08:04:36AM +0100, Anton Lindqvist wrote:
> > > > On Mon, Mar 07, 2022 at 07:36:35AM +0000, Visa Hankala wrote:
> > > > > I still think that checking TXFF and using the same code for both
> > > > > SBSA and true PL011 UARTs would be the best choice. This would avoid
> > > > > fragmenting the code and improve robustness by relying on 
> > > > > functionality
> > > > > that is common to the different controller variants.
> > > > 
> > > > Fair enough, new diff.
> > > 
> > > Maybe the comments should omit the FIFO space description and just
> > > mention the lack of the level control register in the SBSA UART
> > > register interface.
> > 
> > I ended up tweaking the comments before committing. Thanks for all the
> > feedback.
> > 
> 
> Hi Anton,
> 
> This diff seems to break things.  When I boot my rpi4 it now prints:
> 
>   pluart0 at simplebus0: rev 0, 16 byte fifo
>   pluart0: console
>                   bcmbsc0 at simplebus0
>   iic0 at bcmbsc0
> 
> so it appears that a carriage return character is lost here.
> 
> Later on output stops at:
> 
>   reordering libraries: done.
> 
> and only when I reboot the machine the login prompt appears, but with
> some wierd respawning:
> 
>   OpenBSD/arm64 (rutter.sibelius.xs4all.nl) (console)
> 
>   login: init: getty repeating too quickly on port /dev/console, sleeping
>   init: getty repeating too quickly on port /dev/console, sleeping
> 
>   OpenBSD/arm64 (rutter.sibelius.xs4all.nl) (console)
> 
>   login:
>   OpenBSD/arm64 (rutter.sibelius.xs4all.nl) (console)
> 
>   login:
> 
> If you don't have a quick fix for this, may I suggest reverting the
> commit?  We're heading towards release and we don't want the serial
> console on the rpi4 to be broken.

Circling back to this: what happens is that no tx interrupt is raised
when sending less data than the configured interrupt fifo level, causing
the tty to end up in a forever busy state. Clearing the busy flag after
a successful transmission of all queued data solves the problem.

While here, the hardware revision in the optional arm,primecell-periphid
fdt node have higher precedence.

Testing would be much appreciated.

diff --git sys/dev/acpi/pluart_acpi.c sys/dev/acpi/pluart_acpi.c
index e90e810e76f..284769d159e 100644
--- sys/dev/acpi/pluart_acpi.c
+++ sys/dev/acpi/pluart_acpi.c
@@ -91,6 +91,8 @@ pluart_acpi_attach(struct device *parent, struct device 
*self, void *aux)
                return;
        }
 
+       sc->sc.sc_hwflags |= COM_HW_SBSA;
+
        pluart_attach_common(&sc->sc, pluart_acpi_is_console(sc));
 }
 
diff --git sys/dev/fdt/pluart_fdt.c sys/dev/fdt/pluart_fdt.c
index 7be34b37f19..a1d7e006f39 100644
--- sys/dev/fdt/pluart_fdt.c
+++ sys/dev/fdt/pluart_fdt.c
@@ -63,12 +63,20 @@ pluart_fdt_attach(struct device *parent, struct device 
*self, void *aux)
 {
        struct fdt_attach_args *faa = aux;
        struct pluart_softc *sc = (struct pluart_softc *) self;
+       uint32_t periphid;
 
        if (faa->fa_nreg < 1) {
                printf(": no registers\n");
                return;
        }
 
+       if (OF_is_compatible(faa->fa_node, "arm,sbsa-uart"))
+               sc->sc_hwflags |= COM_HW_SBSA;
+
+       periphid = OF_getpropint(faa->fa_node, "arm,primecell-periphid", 0);
+       if (periphid != 0)
+               sc->sc_hwrev = (periphid >> 20) & 0x0f;
+
        sc->sc_irq = fdt_intr_establish(faa->fa_node, IPL_TTY, pluart_intr,
            sc, sc->sc_dev.dv_xname);
 
diff --git sys/dev/ic/pluart.c sys/dev/ic/pluart.c
index 8a2b512eaeb..69654426a34 100644
--- sys/dev/ic/pluart.c
+++ sys/dev/ic/pluart.c
@@ -99,6 +99,13 @@
 #define UART_CR_CTSE           (1 << 14)       /* CTS hardware flow control 
enable */
 #define UART_CR_RTSE           (1 << 15)       /* RTS hardware flow control 
enable */
 #define UART_IFLS              0x34            /* Interrupt FIFO level select 
register */
+#define UART_IFLS_RX_SHIFT     3               /* RX level in bits [5:3] */
+#define UART_IFLS_TX_SHIFT     0               /* TX level in bits [2:0] */
+#define UART_IFLS_1_8          0               /* FIFO 1/8 full */
+#define UART_IFLS_1_4          1               /* FIFO 1/4 full */
+#define UART_IFLS_1_2          2               /* FIFO 1/2 full */
+#define UART_IFLS_3_4          3               /* FIFO 3/4 full */
+#define UART_IFLS_7_8          4               /* FIFO 7/8 full */
 #define UART_IMSC              0x38            /* Interrupt mask set/clear 
register */
 #define UART_IMSC_RIMIM                (1 << 0)
 #define UART_IMSC_CTSMIM       (1 << 1)
@@ -115,8 +122,16 @@
 #define UART_MIS               0x40            /* Masked interrupt status 
register */
 #define UART_ICR               0x44            /* Interrupt clear register */
 #define UART_DMACR             0x48            /* DMA control register */
+#define UART_PID0              0xfe0           /* Peripheral identification 
register 0 */
+#define UART_PID1              0xfe4           /* Peripheral identification 
register 1 */
+#define UART_PID2              0xfe8           /* Peripheral identification 
register 2 */
+#define UART_PID2_REV(x)       (((x) & 0xf0) >> 4)
+#define UART_PID3              0xfec           /* Peripheral identification 
register 3 */
 #define UART_SPACE             0x100
 
+#define UART_FIFO_SIZE         16
+#define UART_FIFO_SIZE_R3      32
+
 void pluartcnprobe(struct consdev *cp);
 void pluartcninit(struct consdev *cp);
 int pluartcngetc(dev_t dev);
@@ -150,7 +165,30 @@ struct cdevsw pluartdev =
 void
 pluart_attach_common(struct pluart_softc *sc, int console)
 {
-       int maj;
+       int fifolen, maj;
+       int lcr;
+
+       if ((sc->sc_hwflags & COM_HW_SBSA) == 0) {
+               if (sc->sc_hwrev == 0)
+                       sc->sc_hwrev = 
UART_PID2_REV(bus_space_read_4(sc->sc_iot,
+                           sc->sc_ioh, UART_PID2));
+               if (sc->sc_hwrev < 3)
+                       fifolen = UART_FIFO_SIZE;
+               else
+                       fifolen = UART_FIFO_SIZE_R3;
+               printf(": rev %d, %d byte fifo\n", sc->sc_hwrev, fifolen);
+       } else {
+               /*
+                * The SBSA UART is PL011 r1p5 compliant which implies revision
+                * 3 with a 32 byte FIFO. However, we cannot expect to configure
+                * RX/TX interrupt levels using the UARTIFLS register making it
+                * impossible to make assumptions about the number of available
+                * bytes in the FIFO. Therefore disable FIFO support for such
+                * devices.
+                */
+               fifolen = 0;
+               printf("\n");
+       }
 
        if (console) {
                /* Locate the major number. */
@@ -159,7 +197,7 @@ pluart_attach_common(struct pluart_softc *sc, int console)
                                break;
                cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
 
-               printf(": console");
+               printf("%s: console\n", sc->sc_dev.dv_xname);
                SET(sc->sc_hwflags, COM_HW_CONSOLE);
        }
 
@@ -171,13 +209,20 @@ pluart_attach_common(struct pluart_softc *sc, int console)
                panic("%s: can't establish soft interrupt.",
                    sc->sc_dev.dv_xname);
 
-       bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_IMSC, (UART_IMSC_RXIM | 
UART_IMSC_TXIM));
+       if (fifolen > 0) {
+               bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_IFLS,
+                   (UART_IFLS_3_4 << UART_IFLS_RX_SHIFT) |
+                   (UART_IFLS_1_4 << UART_IFLS_TX_SHIFT));
+       }
+       sc->sc_imsc = UART_IMSC_RXIM | UART_IMSC_RTIM | UART_IMSC_TXIM;
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_IMSC, sc->sc_imsc);
        bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_ICR, 0x7ff);
-       bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_LCR_H,
-           bus_space_read_4(sc->sc_iot, sc->sc_ioh, UART_LCR_H) &
-           ~UART_LCR_H_FEN);
-
-       printf("\n");
+       lcr = bus_space_read_4(sc->sc_iot, sc->sc_ioh, UART_LCR_H);
+       if (fifolen > 0)
+               lcr |= UART_LCR_H_FEN;
+       else
+               lcr &= ~UART_LCR_H_FEN;
+       bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_LCR_H, lcr);
 }
 
 int
@@ -197,7 +242,8 @@ pluart_intr(void *arg)
        if (sc->sc_tty == NULL)
                return 0;
 
-       if (!ISSET(is, UART_IMSC_RXIM) && !ISSET(is, UART_IMSC_TXIM))
+       if (!ISSET(is, UART_IMSC_RXIM) && !ISSET(is, UART_IMSC_RTIM) &&
+           !ISSET(is, UART_IMSC_TXIM))
                return 0;
 
        if (ISSET(is, UART_IMSC_TXIM) && ISSET(tp->t_state, TS_BUSY)) {
@@ -209,7 +255,7 @@ pluart_intr(void *arg)
 
        p = sc->sc_ibufp;
 
-       while (ISSET(bus_space_read_4(iot, ioh, UART_FR), UART_FR_RXFF)) {
+       while (!ISSET(bus_space_read_4(iot, ioh, UART_FR), UART_FR_RXFE)) {
                c = bus_space_read_2(iot, ioh, UART_DR);
                if (c & UART_DR_BE) {
 #ifdef DDB
@@ -325,22 +371,44 @@ pluart_start(struct tty *tp)
        struct pluart_softc *sc = pluart_cd.cd_devs[DEVUNIT(tp->t_dev)];
        bus_space_tag_t iot = sc->sc_iot;
        bus_space_handle_t ioh = sc->sc_ioh;
-       u_int16_t fr;
        int s;
 
        s = spltty();
        if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP))
                goto out;
        ttwakeupwr(tp);
-       if (tp->t_outq.c_cc == 0)
+       if (tp->t_outq.c_cc == 0) {
+               /* Disable transmit interrupt. */
+               if (ISSET(sc->sc_imsc, UART_IMSC_TXIM)) {
+                       sc->sc_imsc &= ~UART_IMSC_TXIM;
+                       bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_IMSC,
+                           sc->sc_imsc);
+               }
                goto out;
+       }
        SET(tp->t_state, TS_BUSY);
 
-       fr = bus_space_read_4(iot, ioh, UART_FR);
-       while (tp->t_outq.c_cc != 0 && ISSET(fr, UART_FR_TXFE)) {
-               bus_space_write_4(iot, ioh, UART_DR, getc(&tp->t_outq));
+       while (tp->t_outq.c_cc > 0) {
+               uint16_t fr;
+
                fr = bus_space_read_4(iot, ioh, UART_FR);
+               if (ISSET(fr, UART_FR_TXFF))
+                       break;
+
+               bus_space_write_4(iot, ioh, UART_DR, getc(&tp->t_outq));
        }
+
+       if (tp->t_outq.c_cc > 0) {
+               /* Enable transmit interrupt. */
+               if (!ISSET(sc->sc_imsc, UART_IMSC_TXIM)) {
+                       sc->sc_imsc |= UART_IMSC_TXIM;
+                       bus_space_write_4(sc->sc_iot, sc->sc_ioh, UART_IMSC,
+                           sc->sc_imsc);
+               }
+       } else {
+               CLR(tp->t_state, TS_BUSY);
+       }
+
 out:
        splx(s);
 }
diff --git sys/dev/ic/pluartvar.h sys/dev/ic/pluartvar.h
index 6fff43b249f..03854afa7d8 100644
--- sys/dev/ic/pluartvar.h
+++ sys/dev/ic/pluartvar.h
@@ -34,10 +34,12 @@ struct pluart_softc {
        u_int16_t       sc_ucr3;
        u_int16_t       sc_ucr4;
        u_int8_t        sc_hwflags;
+       u_int8_t        sc_hwrev;
 #define COM_HW_NOIEN    0x01
 #define COM_HW_FIFO     0x02
 #define COM_HW_SIR      0x20
 #define COM_HW_CONSOLE  0x40
+#define COM_HW_SBSA    0x80
        u_int8_t        sc_swflags;
 #define COM_SW_SOFTCAR  0x01
 #define COM_SW_CLOCAL   0x02
@@ -45,6 +47,7 @@ struct pluart_softc {
 #define COM_SW_MDMBUF   0x08
 #define COM_SW_PPS      0x10
        int             sc_fifolen;
+       int             sc_imsc;
 
        u_int8_t        sc_initialize;
        u_int8_t        sc_cua;

Reply via email to