Hello again everyone.
I figured out this problem and hope that my solution will help someone.
The problem turned out to be in the driver, namely in the files 8250_dw.c and 8250_port.c.
When calling the function to change the speed, one of the registers is changed to the port configuration value, and after the configuration it was not changed back, which is why the port stopped working.
This git helped me, from which I took several functions and reworked them for my project:
As a result, I ended up with this patch:
--- a/drivers/tty/serial/8250/8250_dw.c 2024-06-21 15:38:16.654830271 +0400
+++ b/drivers/tty/serial/8250/8250_dw.c 2024-06-21 15:18:39.458800042 +0400
@@ -352,25 +352,60 @@ dw8250_do_pm(struct uart_port *port, uns
static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
const struct ktermios *old)
{
- unsigned long newrate = tty_termios_baud_rate(termios) * 16;
- struct dw8250_data *d = to_dw8250_data(p->private_data);
- long rate;
+ unsigned int baud = tty_termios_baud_rate(termios);
+ struct dw8250_data *d = p->private_data;
+ unsigned int rate;
+#ifdef CONFIG_ARCH_ROCKCHIP
+ unsigned int rate_temp, diff;
+#endif
int ret;
+ if (IS_ERR(d->clk))
+ goto out;
+
clk_disable_unprepare(d->clk);
- rate = clk_round_rate(d->clk, newrate);
- if (rate > 0) {
- /*
- * Note that any clock-notifer worker will block in
- * serial8250_update_uartclk() until we are done.
- */
- ret = clk_set_rate(d->clk, newrate);
- if (!ret)
- p->uartclk = rate;
+#ifdef CONFIG_ARCH_ROCKCHIP
+ if (baud <= 115200)
+ rate = 24000000;
+ else if (baud == 230400)
+ rate = baud * 16 * 2;
+ else if (baud == 1152000)
+ rate = baud * 16 * 2;
+ else
+ rate = baud * 16;
+
+ ret = clk_set_rate(d->clk, rate);
+ rate_temp = clk_get_rate(d->clk);
+ diff = rate * 20 / 1000;
+ /*
+ * If rate_temp is not equal to rate, is means fractional frequency
+ * division is failed. Then use Integer frequency division, and
+ * the buad rate error must be under -+2%
+ */
+ if ((rate_temp < rate) && ((rate - rate_temp) > diff)) {
+ ret = clk_set_rate(d->clk, rate + diff);
+ rate_temp = clk_get_rate(d->clk);
+ if ((rate_temp < rate) && ((rate - rate_temp) > diff))
+ dev_info(p->dev, "set rate:%d, but get rate:%d\n",
+ rate, rate_temp);
+ else if ((rate < rate_temp) && ((rate_temp - rate) > diff))
+ dev_info(p->dev, "set rate:%d, but get rate:%d\n",
+ rate, rate_temp);
}
+#else
+ rate = clk_round_rate(d->clk, baud * 16);
+ ret = clk_set_rate(d->clk, rate);
+#endif
clk_prepare_enable(d->clk);
- dw8250_do_set_termios(p, termios, old);
+ if (!ret)
+ p->uartclk = rate;
+out:
+ p->status &= ~UPSTAT_AUTOCTS;
+ if (termios->c_cflag & CRTSCTS)
+ p->status |= UPSTAT_AUTOCTS;
+
+ serial8250_do_set_termios(p, termios, old);
}
static void dw8250_set_ldisc(struct uart_port *p, struct ktermios *termios)
--- a/drivers/tty/serial/8250/8250_port.c 2024-06-21 15:38:22.015061526 +0400
+++ b/drivers/tty/serial/8250/8250_port.c 2024-06-21 15:18:39.458800042 +0400
@@ -2644,6 +2644,10 @@ void serial8250_do_set_divisor(struct ua
{
struct uart_8250_port *up = up_to_u8250p(port);
+#ifdef CONFIG_ARCH_ROCKCHIP
+ serial_port_out(port, UART_MCR, UART_MCR_LOOP);
+#endif
+
/* Workaround to enable 115200 baud on OMAP1510 internal ports */
if (is_omap1510_8250(up)) {
if (baud == 115200) {
@@ -2663,6 +2667,24 @@ void serial8250_do_set_divisor(struct ua
serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
serial_dl_write(up, quot);
+#ifdef CONFIG_ARCH_ROCKCHIP
+ if (quot != serial_dl_read(up))
+ dev_warn_ratelimited(port->dev, "ttyS%d set divisor fail, quot:%d != dll,dlh:%d\n",
+ serial_index(port), quot, serial_dl_read(up));
+#endif
+ if (port->type != PORT_16750)
+ serial_port_out(port, UART_LCR, up->lcr); /* reset DLAB */
+
+#ifdef CONFIG_ARCH_ROCKCHIP
+ serial_port_out(port, UART_MCR, up->mcr);
+#endif
+
+ /* XR17V35x UARTs have an extra fractional divisor register (DLD) */
+ if (up->port.type == PORT_XR17V35X) {
+ /* Preserve bits not related to baudrate; DLD[7:4]. */
+ quot_frac |= serial_port_in(port, 0x2) & 0xf0;
+ serial_port_out(port, 0x2, quot_frac);
+ }
}
EXPORT_SYMBOL_GPL(serial8250_do_set_divisor);
@@ -2770,12 +2792,6 @@ serial8250_do_set_termios(struct uart_po
unsigned long flags;
unsigned int baud, quot, frac = 0;
- if (up->capabilities & UART_CAP_MINI) {
- termios->c_cflag &= ~(CSTOPB | PARENB | PARODD | CMSPAR);
- if ((termios->c_cflag & CSIZE) == CS5 ||
- (termios->c_cflag & CSIZE) == CS6)
- termios->c_cflag = (termios->c_cflag & ~CSIZE) | CS7;
- }
cval = serial8250_compute_lcr(up, termios->c_cflag);
baud = serial8250_get_baud_rate(port, termios, old);
@@ -2791,6 +2807,7 @@ serial8250_do_set_termios(struct uart_po
up->lcr = cval; /* Save computed LCR */
if (up->capabilities & UART_CAP_FIFO && port->fifosize > 1) {
+ /* NOTE: If fifo_bug is not set, a user can set RX_trigger. */
if (baud < 2400 && !up->dma) {
up->fcr &= ~UART_FCR_TRIGGER_MASK;
up->fcr |= UART_FCR_TRIGGER_1;
@@ -2800,9 +2817,12 @@ serial8250_do_set_termios(struct uart_po
/*
* MCR-based auto flow control. When AFE is enabled, RTS will be
* deasserted when the receive FIFO contains more characters than
- * the trigger, or the MCR RTS bit is cleared.
+ * the trigger, or the MCR RTS bit is cleared. In the case where
+ * the remote UART is not using CTS auto flow control, we must
+ * have sufficient FIFO entries for the latency of the remote
+ * UART to respond. IOW, at least 32 bytes of FIFO.
*/
- if (up->capabilities & UART_CAP_AFE) {
+ if (up->capabilities & UART_CAP_AFE && port->fifosize >= 32) {
up->mcr &= ~UART_MCR_AFE;
if (termios->c_cflag & CRTSCTS)
up->mcr |= UART_MCR_AFE;
@@ -2820,7 +2840,7 @@ serial8250_do_set_termios(struct uart_po
port->read_status_mask |= UART_LSR_BI;
/*
- * Characters to ignore
+ * Characteres to ignore
*/
port->ignore_status_mask = 0;
if (termios->c_iflag & IGNPAR)
@@ -2841,6 +2861,7 @@ serial8250_do_set_termios(struct uart_po
if ((termios->c_cflag & CREAD) == 0)
port->ignore_status_mask |= UART_LSR_DR;
+#ifndef CONFIG_ARCH_ROCKCHIP
/*
* CTS flow control flag and modem status interrupts
*/
@@ -2854,6 +2875,7 @@ serial8250_do_set_termios(struct uart_po
up->ier |= UART_IER_RTOIE;
serial_port_out(port, UART_IER, up->ier);
+#endif
if (up->capabilities & UART_CAP_EFR) {
unsigned char efr = 0;
@@ -2872,16 +2894,25 @@ serial8250_do_set_termios(struct uart_po
serial_port_out(port, UART_EFR, efr);
}
+#ifdef CONFIG_ARCH_ROCKCHIP
+ /* Reset uart to make sure it is idle, then set buad rate */
+ serial_port_out(port, 0x88 >> 2, 0x7);
+#endif
+
serial8250_set_divisor(port, baud, quot, frac);
+#ifdef CONFIG_ARCH_ROCKCHIP
+ up->fcr = UART_FCR_ENABLE_FIFO | UART_FCR_T_TRIG_10 | UART_FCR_R_TRIG_10;
+#endif
/*
* LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
* is written without DLAB set, this mode will be disabled.
*/
- if (port->type == PORT_16750)
+ if (port->type == PORT_16750) {
serial_port_out(port, UART_FCR, up->fcr);
+ serial_port_out(port, UART_LCR, up->lcr); /* reset DLAB */
+ }
- serial_port_out(port, UART_LCR, up->lcr); /* reset DLAB */
if (port->type != PORT_16750) {
/* emulated UARTs (Lucent Venus 167x) need two steps */
if (up->fcr & UART_FCR_ENABLE_FIFO)
@@ -2889,6 +2920,23 @@ serial8250_do_set_termios(struct uart_po
serial_port_out(port, UART_FCR, up->fcr); /* set fcr */
}
serial8250_set_mctrl(port, port->mctrl);
+
+#ifdef CONFIG_ARCH_ROCKCHIP
+ /*
+ * CTS flow control flag and modem status interrupts
+ */
+ up->ier &= ~UART_IER_MSI;
+ if (!(up->bugs & UART_BUG_NOMSR) &&
+ UART_ENABLE_MS(&up->port, termios->c_cflag))
+ up->ier |= UART_IER_MSI;
+ if (up->capabilities & UART_CAP_UUE)
+ up->ier |= UART_IER_UUE;
+ if (up->capabilities & UART_CAP_RTOIE)
+ up->ier |= UART_IER_RTOIE;
+
+ serial_port_out(port, UART_IER, up->ier);
+#endif
+
spin_unlock_irqrestore(&port->lock, flags);
serial8250_rpm_put(up);