* [PATCH 10/10] serial: max310x: add comments for PLL limits
From: Hugo Villeneuve @ 2026-04-17 14:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: hugo, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260417-max310x-2-v1-0-b424e105ecac@dimonoff.com>
From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Add comments to help clarify the provenance of the various hardcoded values
used in computing the ref clk.
Assisted-by: Gemini:Pro
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
drivers/tty/serial/max310x.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 748306d1a9329694e90ec4f096dd00e39d457fda..9f423b3b4201d0db07bbd7b15934db36249e7620 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -585,6 +585,23 @@ static u8 max310x_pll_mult_to_id(u8 pll_mult)
}
}
+/*
+ * From table 7 in datasheet: PLLFactor Selector Guide
+ *
+ * +-----------+----------------+-------------------+-------------------+
+ * | PLLFactor | MULTIPLICATION | fPLLIN | fREF |
+ * | (1 & 0) | FACTOR +---------+---------+---------+---------+
+ * | | | MIN | MAX | MIN | MAX |
+ * +-----------+----------------+---------+---------+---------+---------+
+ * | 0 | 6 | 500kHz | 800kHz | 3MHz | 4.8MHz |
+ * +-----------+----------------+---------+---------+---------+---------+
+ * | 1 | 48 | 850kHz | 1.2MHz | 40.8MHz | 56MHz |
+ * +-----------+----------------+---------+---------+---------+---------+
+ * | 2 | 96 | 425kHz | 1MHz | 40.8MHz | 96MHz |
+ * +-----------+----------------+---------+---------+---------+---------+
+ * | 3 | 144 | 390kHz | 667kHz | 56MHz | 96MHz |
+ * +-----------+----------------+---------+---------+---------+---------+
+ */
static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
unsigned int freq, unsigned int *fref, bool xtal)
{
--
2.47.3
^ permalink raw reply related
* [PATCH 09/10] serial: max310x: move variables to while() scope
From: Hugo Villeneuve @ 2026-04-17 14:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: hugo, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260417-max310x-2-v1-0-b424e105ecac@dimonoff.com>
From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
txlen, to_send and tail variables are only used within the while() loop.
Move them to that scope to keep them closer to their usage and improve
readability.
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
drivers/tty/serial/max310x.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index b4449b68cfeee07d1d6d3206cf58f72fac00044e..748306d1a9329694e90ec4f096dd00e39d457fda 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -761,8 +761,6 @@ static void max310x_handle_rx(struct uart_port *port, unsigned int rxlen)
static void max310x_handle_tx(struct uart_port *port)
{
struct tty_port *tport = &port->state->port;
- unsigned int txlen, to_send;
- unsigned char *tail;
if (unlikely(port->x_char)) {
max310x_port_write(port, MAX310X_THR_REG, port->x_char);
@@ -779,6 +777,9 @@ static void max310x_handle_tx(struct uart_port *port)
* We could do that in one SPI transaction, but meh.
*/
while (!kfifo_is_empty(&tport->xmit_fifo)) {
+ unsigned int txlen, to_send;
+ unsigned char *tail;
+
/* Limit to space available in TX FIFO */
txlen = max310x_port_read(port, MAX310X_TXFIFOLVL_REG);
txlen = port->fifosize - txlen;
--
2.47.3
^ permalink raw reply related
* [PATCH 08/10] serial: max310x: allow driver to be built with SPI or I2C
From: Hugo Villeneuve @ 2026-04-17 14:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: hugo, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260417-max310x-2-v1-0-b424e105ecac@dimonoff.com>
From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
If SPI is disabled, the max310x driver cannot be selected. Allow driver to
be selected if either I2C or SPI is set.
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
drivers/tty/serial/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 9aa61c93d7bc6d164b9c493f140477b793ca8afe..296fa20340bd59196f7d1394687edac10fc73adf 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -321,7 +321,7 @@ config SERIAL_MAX3100
config SERIAL_MAX310X
tristate "MAX310X support"
- depends on SPI_MASTER
+ depends on SPI_MASTER || I2C
select SERIAL_CORE
select REGMAP_SPI if SPI_MASTER
select REGMAP_I2C if I2C
--
2.47.3
^ permalink raw reply related
* [PATCH 06/10] serial: max310x: use regmap_read_poll_timeout() for busy wait
From: Hugo Villeneuve @ 2026-04-17 14:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: hugo, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260417-max310x-2-v1-0-b424e105ecac@dimonoff.com>
From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Simplify busy wait stages by using regmap_read_poll_timeout().
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
Note that the "val" variable is move to the function scope in prevision for
reuse by the next patch.
---
drivers/tty/serial/max310x.c | 44 +++++++++++++++++---------------------------
1 file changed, 17 insertions(+), 27 deletions(-)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 1cd7632831a2826ddd42127b1cde2a10dfa412f0..7ab5e4bd87ee6c87dd211bc52a3a4eb169a2b202 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -241,12 +241,12 @@
#define MAX310X_WRITE_BIT 0x80
/* Port startup definitions */
-#define MAX310X_PORT_STARTUP_WAIT_RETRIES 20 /* Number of retries */
-#define MAX310X_PORT_STARTUP_WAIT_DELAY_MS 10 /* Delay between retries */
+#define MAX310X_PORT_STARTUP_SLEEP_US 10000 /* Delay between retries */
+#define MAX310X_PORT_STARTUP_TIMEOUT_US (20 * MAX310X_PORT_STARTUP_SLEEP_US) /* Total timeout */
/* Crystal-related definitions */
-#define MAX310X_XTAL_WAIT_RETRIES 20 /* Number of retries */
-#define MAX310X_XTAL_WAIT_DELAY_MS 10 /* Delay between retries */
+#define MAX310X_XTAL_SLEEP_US 10000 /* Delay between retries */
+#define MAX310X_XTAL_TIMEOUT_US (20 * MAX310X_XTAL_SLEEP_US) /* Total timeout */
/* MAX3107 specific */
#define MAX3107_REV_ID (0xa0)
@@ -587,7 +587,7 @@ static u8 max310x_pll_mult_to_id(u8 pll_mult)
static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
unsigned int freq, unsigned int *fref, bool xtal)
{
- unsigned int div, fdiv, clksrc;
+ unsigned int div, fdiv, clksrc, val;
struct max310x_clk_config_t cfg;
cfg.err = UINT_MAX;
@@ -629,18 +629,13 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
/* Wait for crystal */
if (xtal) {
- bool stable = false;
- unsigned int try = 0, val = 0;
+ int ret;
- do {
- msleep(MAX310X_XTAL_WAIT_DELAY_MS);
- regmap_read(s->regmap, MAX310X_STS_IRQSTS_REG, &val);
-
- if (val & MAX310X_STS_CLKREADY_BIT)
- stable = true;
- } while (!stable && (++try < MAX310X_XTAL_WAIT_RETRIES));
-
- if (!stable)
+ ret = regmap_read_poll_timeout(s->regmap, MAX310X_STS_IRQSTS_REG,
+ val, val & MAX310X_STS_CLKREADY_BIT,
+ MAX310X_XTAL_SLEEP_US,
+ MAX310X_XTAL_TIMEOUT_US);
+ if (ret)
return dev_err_probe(dev, -EAGAIN,
"clock is not stable\n");
}
@@ -1346,8 +1341,7 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
goto out_clk;
for (i = 0; i < devtype->nr; i++) {
- bool started = false;
- unsigned int try = 0, val = 0;
+ unsigned int val;
/* Reset port */
regmap_write(regmaps[i], MAX310X_MODE2_REG,
@@ -1356,15 +1350,11 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
regmap_write(regmaps[i], MAX310X_MODE2_REG, 0);
/* Wait for port startup */
- do {
- msleep(MAX310X_PORT_STARTUP_WAIT_DELAY_MS);
- regmap_read(regmaps[i], MAX310X_BRGDIVLSB_REG, &val);
-
- if (val == 0x01)
- started = true;
- } while (!started && (++try < MAX310X_PORT_STARTUP_WAIT_RETRIES));
-
- if (!started) {
+ ret = regmap_read_poll_timeout(regmaps[i], MAX310X_BRGDIVLSB_REG,
+ val, val == 0x01,
+ MAX310X_PORT_STARTUP_SLEEP_US,
+ MAX310X_PORT_STARTUP_TIMEOUT_US);
+ if (ret) {
ret = dev_err_probe(dev, -EAGAIN, "port reset failed\n");
goto out_uart;
}
--
2.47.3
^ permalink raw reply related
* [PATCH 05/10] serial: max310x: improve max310x_set_ref_clk() efficiency
From: Hugo Villeneuve @ 2026-04-17 14:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: hugo, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260417-max310x-2-v1-0-b424e105ecac@dimonoff.com>
From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
The same basic code is duplicated for each of the four PLL multiplier
values, so simplify and streamline it. Doing so allows us to avoid
some unnecessary (fdiv * factor) multiplications for some fdiv values and
replace the second "if" statement with an "else if" and avoid a useless
comparison.
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
drivers/tty/serial/max310x.c | 91 +++++++++++++++++++++++++-------------------
1 file changed, 52 insertions(+), 39 deletions(-)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index f689958736f71b15b7c113214cda60db4aa7c593..1cd7632831a2826ddd42127b1cde2a10dfa412f0 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -258,6 +258,17 @@
#define MAX14830_BRGCFG_CLKDIS_BIT (1 << 6) /* Clock Disable */
#define MAX14830_REV_ID (0xb0)
+struct max310x_clk_config_t {
+ u8 prediv; /* Predivider */
+ u8 pll_mult; /* PLL multiplier */
+ unsigned int fref; /*
+ * Reference clock for fractional baud rate generator:
+ * PLL enabled: (freq / prediv) x pll_mult
+ * PLL disabled: freq
+ */
+ unsigned int err; /* Computed error for selected parameters */
+};
+
struct max310x_if_cfg {
int (*extended_reg_enable)(struct device *dev, bool enable);
u8 rev_id_offset;
@@ -545,70 +556,72 @@ static int max310x_set_baud(struct uart_port *port, int baud)
return (16*port->uartclk) / (c*(16*div + frac));
}
-static int max310x_update_best_err(unsigned int f, unsigned int *besterr)
+static void max310x_try_cfg(unsigned int fdiv, u8 div, u8 pll_mult,
+ struct max310x_clk_config_t *cfg)
{
+ unsigned int fmul = fdiv * pll_mult;
+ unsigned int err;
+
/* Use high-enough baudrate to calculate error */
- unsigned int err = f % (460800 * 16);
+ err = fmul % (460800 * 16);
- if (*besterr > err) {
- *besterr = err;
- return 0;
+ if (cfg->err > err) {
+ cfg->err = err;
+ cfg->pll_mult = pll_mult;
+ cfg->prediv = div;
+ cfg->fref = fmul;
}
+}
- return 1;
+static u8 max310x_pll_mult_to_id(u8 pll_mult)
+{
+ switch (pll_mult) {
+ case 144: return 3;
+ case 96: return 2;
+ case 48: return 1;
+ case 6:
+ default: return 0;
+ }
}
static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
unsigned int freq, unsigned int *fref, bool xtal)
{
- unsigned int div, clksrc, pllcfg = 0;
- unsigned int besterr = UINT_MAX;
- unsigned int fdiv, fmul, bestfreq = freq;
+ unsigned int div, fdiv, clksrc;
+ struct max310x_clk_config_t cfg;
+
+ cfg.err = UINT_MAX;
+ cfg.prediv = 0;
+ cfg.fref = freq;
/* First, update error without PLL */
- max310x_update_best_err(freq, &besterr);
+ max310x_try_cfg(freq, 1, 1, &cfg);
/* Try all possible PLL dividers */
- for (div = 1; (div <= 63) && besterr; div++) {
+ for (div = 1; (div <= 63) && cfg.err; div++) {
fdiv = DIV_ROUND_CLOSEST(freq, div);
- /* Try multiplier 6 */
- fmul = fdiv * 6;
if ((fdiv >= 500000) && (fdiv <= 800000))
- if (!max310x_update_best_err(fmul, &besterr)) {
- pllcfg = (0 << 6) | div;
- bestfreq = fmul;
- }
- /* Try multiplier 48 */
- fmul = fdiv * 48;
- if ((fdiv >= 850000) && (fdiv <= 1200000))
- if (!max310x_update_best_err(fmul, &besterr)) {
- pllcfg = (1 << 6) | div;
- bestfreq = fmul;
- }
- /* Try multiplier 96 */
- fmul = fdiv * 96;
+ max310x_try_cfg(fdiv, div, 6, &cfg); /* PLL x6 */
+ else if ((fdiv >= 850000) && (fdiv <= 1200000))
+ max310x_try_cfg(fdiv, div, 48, &cfg); /* PLL x48 */
+
if ((fdiv >= 425000) && (fdiv <= 1000000))
- if (!max310x_update_best_err(fmul, &besterr)) {
- pllcfg = (2 << 6) | div;
- bestfreq = fmul;
- }
- /* Try multiplier 144 */
- fmul = fdiv * 144;
+ max310x_try_cfg(fdiv, div, 96, &cfg); /* PLL x96 */
+
if ((fdiv >= 390000) && (fdiv <= 667000))
- if (!max310x_update_best_err(fmul, &besterr)) {
- pllcfg = (3 << 6) | div;
- bestfreq = fmul;
- }
+ max310x_try_cfg(fdiv, div, 144, &cfg); /* PLL x144 */
}
/* Configure clock source */
clksrc = MAX310X_CLKSRC_EXTCLK_BIT | (xtal ? MAX310X_CLKSRC_CRYST_BIT : 0);
/* Configure PLL */
- if (pllcfg) {
+ if (cfg.prediv) {
+ u8 pll_id = max310x_pll_mult_to_id(cfg.pll_mult);
+
clksrc |= MAX310X_CLKSRC_PLL_BIT;
- regmap_write(s->regmap, MAX310X_PLLCFG_REG, pllcfg);
+ regmap_write(s->regmap, MAX310X_PLLCFG_REG, (pll_id << 6) | cfg.prediv);
} else
clksrc |= MAX310X_CLKSRC_PLLBYP_BIT;
@@ -632,7 +645,7 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
"clock is not stable\n");
}
- *fref = bestfreq;
+ *fref = cfg.fref;
return 0;
}
--
2.47.3
^ permalink raw reply related
* [PATCH 07/10] serial: max310x: use FIELD_PREP macro to set PLL bitfields
From: Hugo Villeneuve @ 2026-04-17 14:53 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: hugo, linux-kernel, linux-serial, Hugo Villeneuve
In-Reply-To: <20260417-max310x-2-v1-0-b424e105ecac@dimonoff.com>
From: Hugo Villeneuve <hvilleneuve@dimonoff.com>
Use FIELD_PREP macros to improve code readability.
Signed-off-by: Hugo Villeneuve <hvilleneuve@dimonoff.com>
---
drivers/tty/serial/max310x.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
index 7ab5e4bd87ee6c87dd211bc52a3a4eb169a2b202..b4449b68cfeee07d1d6d3206cf58f72fac00044e 100644
--- a/drivers/tty/serial/max310x.c
+++ b/drivers/tty/serial/max310x.c
@@ -9,6 +9,7 @@
* Based on max3107.c, by Aavamobile
*/
+#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/delay.h>
@@ -621,7 +622,9 @@ static int max310x_set_ref_clk(struct device *dev, struct max310x_port *s,
u8 pll_id = max310x_pll_mult_to_id(cfg.pll_mult);
clksrc |= MAX310X_CLKSRC_PLL_BIT;
- regmap_write(s->regmap, MAX310X_PLLCFG_REG, (pll_id << 6) | cfg.prediv);
+ val = FIELD_PREP(MAX310X_PLLCFG_PLLFACTOR_MASK, pll_id) |
+ FIELD_PREP(MAX310X_PLLCFG_PREDIV_MASK, cfg.prediv);
+ regmap_write(s->regmap, MAX310X_PLLCFG_REG, val);
} else
clksrc |= MAX310X_CLKSRC_PLLBYP_BIT;
--
2.47.3
^ permalink raw reply related
* Re: [PATCH tty v3 1/6] serial: core: Add dedicated uart_port field for console flow
From: John Ogness @ 2026-04-17 13:15 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby; +Cc: linux-kernel, linux-serial
In-Reply-To: <20260417102423.40984-2-john.ogness@linutronix.de>
On 2026-04-17, Sashiko wrote:
>> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
>> index 666430b478997..2327a364ded16 100644
>> --- a/include/linux/serial_core.h
>> +++ b/include/linux/serial_core.h
>> @@ -567,6 +568,7 @@ struct uart_port {
>> #define UPSTAT_SYNC_FIFO ((__force upstat_t) (1 << 5))
>>
>> bool hw_stopped; /* sw-assisted CTS flow state */
>> + bool cons_flow; /* user specified console flow control */
>> unsigned int mctrl; /* current modem ctrl settings */
>> unsigned int frame_time; /* frame timing in ns */
>> unsigned int type; /* port type */
>
> Since cons_flow is now separate from the flags field, do manual
> assignments of struct uart_port need to be updated to copy this new
> field?
My goal was to catch everywhere where uart_port.flags is set explicitly
or from some "external" source. I did not want to filter anytime one
uart_port.flags is to another uart_port.flags. If UPF_CONS_FLOW is set
in the source uart_port.flags, then _that_ is the problem.
> While subsequent patches in this series update
> serial8250_register_8250_port() to explicitly copy this new field,
> early_serial_setup() does not appear to receive a similar update.
>
> For example, looking at early_serial_setup() in
> drivers/tty/serial/8250/8250_core.c:
>
> int __init early_serial_setup(struct uart_port *port)
> {
> ...
> p = &serial8250_ports[port->line].port;
> p->iobase = port->iobase;
> ...
> p->iotype = port->iotype;
> p->flags = port->flags;
> p->mapbase = port->mapbase;
> ...
> }
>
> Because UPF_CONS_FLOW was previously copied as part of port->flags,
> will omitting cons_flow here cause a regression where the
> configuration is silently lost if any platform sets console flow
> control prior to early registration?
In this particular case, I checked all callers of
early_serial_setup(). They are passing explicit flags and UPF_CONS_FLOW
is not one of them.
Likewise, I could not find any examples, where UPF_CONS_FLOW could slip
into uart_port.flags, aside from the 2 examples I found and deal with in
my series.
I have no control over out-of-tree drivers that call
early_serial_setup() with UPF_CONS_FLOW. I could sprinkle bitmasks and
WARN_ON's to try to catch such out-of-tree misfits. But honestly, I
think that would introduce unnecesary code churn and complexity.
Considering the current questionable kernel state of console flow
control today, I doubt any out-of-tree drivers are really using the
appropriate policies anyway... meaning that the out-of-tree driver is
probably just unsafely using (uart_port.flags & UPF_CONS_FLOW) now,
which would likely continue to work as good/bad as it does now.
John
P.S. It looks like Sashiko ran out of gas [0] trying to review the 2nd
patch. So hopefully another reviewer (human or !human) will find some
time to take a look at the series.
At this point my only real concern is the community opinions on
deprecating UPF_CONS_FLOW.
[0] https://sashiko.dev/#/patchset/20260417102423.40984-1-john.ogness%40linutronix.de
^ permalink raw reply
* Re: [PATCH] serial: mxs-auart: Compare the return value of gpiod_get_direction against GPIO_LINE_DIRECTION_IN
From: Nikola Z. Ivanov @ 2026-04-17 10:31 UTC (permalink / raw)
To: Frank Li
Cc: gregkh, jirislaby, s.hauer, kernel, festevam, linux-kernel,
linux-serial, imx, linux-arm-kernel
In-Reply-To: <aeHt3c9Fqp6-WdLV@lizhi-Precision-Tower-5810>
On 4/17/26 11:22 AM, Frank Li wrote:
> On Thu, Apr 16, 2026 at 11:32:54AM +0300, Nikola Z. Ivanov wrote:
>
> subjust suggest change to
>
> Replace hardcode 1 with predefined macro GPIO_LINE_DIRECTION_IN
>
> Frank
Hello,
I suppose you are suggesting a subject change, in that case I will send v2.
Also I can see that automated code review notes that this will not build
on greg's tty tree as 7.0 is not yet merged into it. I will wait for
that to happen
before mailing v2.
BR,
Nikola
>
>> The GPIO_LINE_DIRECTION_* definitions have just recently been exposed to
>> gpio consumers.h by breaking them out in a separate defs.h file.
>>
>> Use this to validate the gpio direction instead of the hard-coded literal.
>>
>> Signed-off-by: Nikola Z. Ivanov <zlatistiv@gmail.com>
>> ---
>> drivers/tty/serial/mxs-auart.c | 2 +-
>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>
>> diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
>> index cc65c9fb6446..6c6df4d5c21f 100644
>> --- a/drivers/tty/serial/mxs-auart.c
>> +++ b/drivers/tty/serial/mxs-auart.c
>> @@ -1519,7 +1519,7 @@ static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
>>
>> for (i = 0; i < UART_GPIO_MAX; i++) {
>> gpiod = mctrl_gpio_to_gpiod(s->gpios, i);
>> - if (gpiod && (gpiod_get_direction(gpiod) == 1))
>> + if (gpiod && (gpiod_get_direction(gpiod) == GPIO_LINE_DIRECTION_IN))
>> s->gpio_irq[i] = gpiod_to_irq(gpiod);
>> else
>> s->gpio_irq[i] = -EINVAL;
>> --
>> 2.53.0
>>
^ permalink raw reply
* [PATCH tty v3 6/6] serial: 8250: Add support for console flow control
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: linux-kernel, Ilpo Järvinen, Ingo Molnar, Osama Abdelkader,
Andy Shevchenko, Krzysztof Kozlowski, Gerhard Engleder,
Lukas Wunner, Dr. David Alan Gilbert, Joseph Tilahun,
linux-serial
In-Reply-To: <20260417102423.40984-1-john.ogness@linutronix.de>
The kernel documentation specifies that the console option 'r' can
be used to enable hardware flow control for console writes. The 8250
driver does include code for hardware flow control on the console if
cons_flow is set, but there is no code path that actually sets this.
However, that is not the only issue. The problems are:
1. Specifying the console option 'r' does not lead to cons_flow being
set.
2. Even if cons_flow would be set, serial8250_register_8250_port()
clears it.
3. When the console option 'r' is specified, uart_set_options()
attempts to initialize the port for CRTSCTS. However, afterwards
it does not set the UPSTAT_CTS_ENABLE status bit and therefore on
boot, uart_cts_enabled() is always false. This policy bit is
important for console drivers as a criteria if they may poll CTS.
4. Even though uart_set_options() attempts to initialize the port
for CRTSCTS, the 8250 set_termios() callback does not enable the
RTS signal (TIOCM_RTS) and thus the hardware is not properly
initialized for CTS polling.
5. Even if modem control was properly setup for CTS polling
(TIOCM_RTS), uart_configure_port() clears TIOCM_RTS, thus
breaking CTS polling.
6. wait_for_xmitr() and serial8250_console_write() use cons_flow
to decide if CTS polling should occur. However, the condition
should also include a check that it is not in RS485 mode and
CRTSCTS is actually enabled in the hardware.
Address all these issues as conservatively as possible by gating them
behind checks focussed on the user specifying console hardware flow
control support and the hardware being configured for CTS polling
at the time of the write to the UART.
Since checking the UPSTAT_CTS_ENABLE status bit is a part of the new
condition gate, these changes also support runtime termios updates to
disable/enable CRTSCTS.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
drivers/tty/serial/8250/8250_core.c | 6 +++++-
drivers/tty/serial/8250/8250_port.c | 13 +++++++++++--
drivers/tty/serial/serial_core.c | 21 ++++++++++++++++++++-
include/linux/serial_core.h | 8 ++++++++
4 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index 32b894ab26768..a19646e24d883 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -693,6 +693,7 @@ static void serial_8250_overrun_backoff_work(struct work_struct *work)
int serial8250_register_8250_port(const struct uart_8250_port *up)
{
struct uart_8250_port *uart;
+ bool cons_flow;
int ret;
if (up->port.uartclk == 0)
@@ -716,6 +717,9 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
if (uart->port.type == PORT_8250_CIR)
return -ENODEV;
+ /* Preserve specified console flow control. */
+ cons_flow = uart_get_cons_flow(&uart->port);
+
if (uart->port.dev)
uart_remove_one_port(&serial8250_reg, &uart->port);
@@ -746,7 +750,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
uart->lsr_save_mask = up->lsr_save_mask;
uart->dma = up->dma;
- uart_set_cons_flow(&uart->port, uart_get_cons_flow(&up->port));
+ uart_set_cons_flow(&uart->port, uart_get_cons_flow(&up->port) | cons_flow);
/* Take tx_loadsz from fifosize if it wasn't set separately */
if (uart->port.fifosize && !uart->tx_loadsz)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index b3247c55eebd5..f585145a6211a 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1989,7 +1989,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
tx_ready = wait_for_lsr(up, bits);
/* Wait up to 1s for flow control if necessary */
- if (uart_get_cons_flow(&up->port)) {
+ if (uart_console_hwflow_active(&up->port)) {
for (tmout = 1000000; tmout; tmout--) {
unsigned int msr = serial_in(up, UART_MSR);
up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
@@ -2786,6 +2786,12 @@ serial8250_do_set_termios(struct uart_port *port, struct ktermios *termios,
serial8250_set_efr(port, termios);
serial8250_set_divisor(port, baud, quot, frac);
serial8250_set_fcr(port, termios);
+ /* Consoles manually poll CTS for hardware flow control. */
+ if (uart_console(port) &&
+ !(port->rs485.flags & SER_RS485_ENABLED)
+ && termios->c_cflag & CRTSCTS) {
+ port->mctrl |= TIOCM_RTS;
+ }
serial8250_set_mctrl(port, port->mctrl);
}
@@ -3355,7 +3361,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
* it regardless of the CTS state. Therefore, only use fifo
* if we don't use control flow.
*/
- !uart_get_cons_flow(&up->port);
+ !uart_console_hwflow_active(&up->port);
if (likely(use_fifo))
serial8250_console_fifo_write(up, s, count);
@@ -3425,6 +3431,9 @@ int serial8250_console_setup(struct uart_port *port, char *options, bool probe)
if (ret)
return ret;
+ /* Track user-specified console flow control. */
+ uart_set_cons_flow(port, flow == 'r');
+
if (port->dev)
pm_runtime_get_sync(port->dev);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 89cebdd278410..840336f95c5f6 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2235,6 +2235,18 @@ uart_set_options(struct uart_port *port, struct console *co,
port->mctrl |= TIOCM_DTR;
port->ops->set_termios(port, &termios, &dummy);
+
+ /*
+ * If console hardware flow control was specified and is supported,
+ * the related policy UPSTAT_CTS_ENABLE must be set to allow console
+ * drivers to identify if CTS should be used for polling.
+ */
+ if (flow == 'r' && (termios.c_cflag & CRTSCTS)) {
+ /* Synchronize @status RMW update against the console. */
+ guard(uart_port_lock_irqsave)(port);
+ port->status |= UPSTAT_CTS_ENABLE;
+ }
+
/*
* Allow the setting of the UART parameters with a NULL console
* too:
@@ -2541,7 +2553,14 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
* We probably don't need a spinlock around this, but
*/
scoped_guard(uart_port_lock_irqsave, port) {
- port->mctrl &= TIOCM_DTR;
+ unsigned int mask = TIOCM_DTR;
+
+ /* Console hardware flow control polls CTS. */
+ if (uart_console_hwflow_active(port))
+ mask |= TIOCM_RTS;
+
+ port->mctrl &= mask;
+
if (!(port->rs485.flags & SER_RS485_ENABLED))
port->ops->set_mctrl(port, port->mctrl);
}
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 2327a364ded16..ff1145e86088b 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -1175,6 +1175,14 @@ static inline bool uart_get_cons_flow(const struct uart_port *uport)
return uport->cons_flow;
}
+static inline bool uart_console_hwflow_active(struct uart_port *uport)
+{
+ return uart_console(uport) &&
+ !(uport->rs485.flags & SER_RS485_ENABLED) &&
+ uart_get_cons_flow(uport) &&
+ uart_cts_enabled(uport);
+}
+
/*
* The following are helper functions for the low level drivers.
*/
--
2.47.3
^ permalink raw reply related
* [PATCH tty v3 5/6] serial: 8250: Check LSR timeout on console flow control
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: linux-kernel, Ilpo Järvinen, Andy Shevchenko, linux-serial
In-Reply-To: <20260417102423.40984-1-john.ogness@linutronix.de>
wait_for_xmitr() calls wait_for_lsr() to wait for the transmission
registers to be empty. wait_for_lsr() can timeout after a reasonable
amount of time.
When console flow control is active, wait_for_xmitr() additionally
polls CTS, waiting for the peer to signal that it is ready to receive
more data.
If hardware flow control is enabled (auto CTS) and the peer deasserts
CTS, wait_for_lsr() will timeout. If additionally console flow
control is active and while polling CTS the peer asserts CTS, the
console will assume it can immediately transmit, even though the
transmission registers may not be empty. This can lead to data loss.
Avoid this problem by performing an extra wait_for_lsr() upon CTS
assertion if wait_for_lsr() previously timed out.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
drivers/tty/serial/8250/8250_port.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index c91b0fa7111a7..b3247c55eebd5 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1984,16 +1984,20 @@ static bool wait_for_lsr(struct uart_8250_port *up, int bits)
static void wait_for_xmitr(struct uart_8250_port *up, int bits)
{
unsigned int tmout;
+ bool tx_ready;
- wait_for_lsr(up, bits);
+ tx_ready = wait_for_lsr(up, bits);
/* Wait up to 1s for flow control if necessary */
if (uart_get_cons_flow(&up->port)) {
for (tmout = 1000000; tmout; tmout--) {
unsigned int msr = serial_in(up, UART_MSR);
up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
- if (msr & UART_MSR_CTS)
+ if (msr & UART_MSR_CTS) {
+ if (!tx_ready)
+ wait_for_lsr(up, bits);
break;
+ }
udelay(1);
touch_nmi_watchdog();
}
--
2.47.3
^ permalink raw reply related
* [PATCH tty v3 4/6] serial: 8250: Set cons_flow on port registration
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: linux-kernel, Ilpo Järvinen, Thomas Gleixner,
Osama Abdelkader, Xin Zhao, Ingo Molnar, linux-serial
In-Reply-To: <20260417102423.40984-1-john.ogness@linutronix.de>
Since console flow control policy is no longer part of uart_port.flags,
explicitly set the policy for the port.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
drivers/tty/serial/8250/8250_core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
index a428e88938eb7..32b894ab26768 100644
--- a/drivers/tty/serial/8250/8250_core.c
+++ b/drivers/tty/serial/8250/8250_core.c
@@ -746,6 +746,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
uart->lsr_save_mask = up->lsr_save_mask;
uart->dma = up->dma;
+ uart_set_cons_flow(&uart->port, uart_get_cons_flow(&up->port));
+
/* Take tx_loadsz from fifosize if it wasn't set separately */
if (uart->port.fifosize && !uart->tx_loadsz)
uart->tx_loadsz = uart->port.fifosize;
--
2.47.3
^ permalink raw reply related
* [PATCH tty v3 3/6] serial: sh-sci: Avoid deprecated UPF_CONS_FLOW
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: linux-kernel, Biju Das, Geert Uytterhoeven, Lad Prabhakar,
Wolfram Sang, Thierry Bultel, linux-serial
In-Reply-To: <20260417102423.40984-1-john.ogness@linutronix.de>
Avoid setting the uart_port.flags deprecated UPF_CONS_FLOW bit if it
has been configured in the platform data. Use the new cons_flow
wrappers instead.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
drivers/tty/serial/sh-sci.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 6c819b6b24258..07d272dc4e74a 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -3369,9 +3369,12 @@ static int sci_init_single(struct platform_device *dev,
}
port->type = SCI_PUBLIC_PORT_ID(p->type);
- port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF | p->flags;
+ port->flags = UPF_FIXED_PORT | UPF_BOOT_AUTOCONF |
+ (p->flags & ~UPF_CONS_FLOW);
port->fifosize = sci_port->params->fifosize;
+ uart_set_cons_flow(port, p->flags & UPF_CONS_FLOW);
+
if (p->type == PORT_SCI && !dev->dev.of_node) {
if (sci_port->reg_size >= 0x20)
port->regshift = 2;
--
2.47.3
^ permalink raw reply related
* [PATCH tty v3 1/6] serial: core: Add dedicated uart_port field for console flow
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby; +Cc: linux-kernel, linux-serial
In-Reply-To: <20260417102423.40984-1-john.ogness@linutronix.de>
Currently the UPF_CONS_FLOW bit in the uart_port.flags field is used
by serial console drivers to identify if a user has configured flow
control on the console. This policy is most commonly setup during
early boot, but can be changed at runtime.
The bits in uart_port.flags are either hardware and driver properties
that are initialized before usage or are properties that can be
changed via the tty layer.
The UPF_CONS_FLOW is an exception because it is a console-only policy
that can change at runtime and its setting and usage have nothing to
do with the tty layer. This actually causes a problem for its usage
because uart_port.flags is synchronized by a related tty_port.mutex,
but a console has no relation to a tty (other than sharing the port).
This is probably why console flow control is not properly available
for most serial drivers. And it is hindering being able to provide a
proper implementation. Commit d01f4d181c92 ("serial: core: Privatize
tty->hw_stopped") addressed a similar issue to deal with software
assisted CTS flow state tracking.
Add a new uart_port boolean field "cons_flow" to store the user
configuration for console flow control. Add get/set wrappers to allow
for adding more policies later and/or locking constraint validation.
Mark UPF_CONS_FLOW as deprecated.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
include/linux/serial_core.h | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 666430b478997..2327a364ded16 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -533,6 +533,7 @@ struct uart_port {
#define UPF_HARD_FLOW ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW ((__force upf_t) BIT_ULL(22))
+/* Deprecated: use uart_get_cons_flow()/uart_set_cons_flow() instead. */
#define UPF_CONS_FLOW ((__force upf_t) BIT_ULL(23))
#define UPF_SHARE_IRQ ((__force upf_t) BIT_ULL(24))
#define UPF_EXAR_EFR ((__force upf_t) BIT_ULL(25))
@@ -567,6 +568,7 @@ struct uart_port {
#define UPSTAT_SYNC_FIFO ((__force upstat_t) (1 << 5))
bool hw_stopped; /* sw-assisted CTS flow state */
+ bool cons_flow; /* user specified console flow control */
unsigned int mctrl; /* current modem ctrl settings */
unsigned int frame_time; /* frame timing in ns */
unsigned int type; /* port type */
@@ -1163,6 +1165,16 @@ static inline bool uart_softcts_mode(struct uart_port *uport)
return ((uport->status & mask) == UPSTAT_CTS_ENABLE);
}
+static inline void uart_set_cons_flow(struct uart_port *uport, bool on)
+{
+ uport->cons_flow = on;
+}
+
+static inline bool uart_get_cons_flow(const struct uart_port *uport)
+{
+ return uport->cons_flow;
+}
+
/*
* The following are helper functions for the low level drivers.
*/
--
2.47.3
^ permalink raw reply related
* [PATCH tty v3 0/6] 8250: Add console flow control
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: linux-kernel, linux-serial, Krzysztof Kozlowski, Alim Akhtar,
David S. Miller, Ilpo Järvinen, Andy Shevchenko,
Thomas Fourier, Kees Cook, linux-arm-kernel, linux-samsung-soc,
sparclinux, Biju Das, Geert Uytterhoeven, Lad Prabhakar,
Wolfram Sang, Thierry Bultel, Thomas Gleixner, Osama Abdelkader,
Xin Zhao, Ingo Molnar, Andy Shevchenko, Krzysztof Kozlowski,
Gerhard Engleder, Lukas Wunner, Dr. David Alan Gilbert,
Joseph Tilahun
Hi,
This is v3 of a series to implement console flow control for the
8250 serial driver. v2 is here [0].
The 8250 driver already has code in place to support console flow
control. However, there is no way to activate it and it is
incomplete. This series provides the necessary missing pieces while
attempting to be as conservative as possible, so as not to introduce
any side effects into the many 8250 variants or other non-8250 serial
drivers.
Note that as of v3 I am now deprecating UPF_CONS_FLOW in favor of a
separate boolean field. In the commit message (patch 1) I explain my
reasoning for this decision.
For patch 2 I used the following Coccinelle script to perform the
modifications...
===== BEGIN cons_flow.cocci =====
// SPDX-License-Identifier: GPL-2.0-only
// Options: --all-includes
virtual patch
@r1@
type T1;
identifier U;
@@
T1 {
...
struct uart_port U;
...
};
@r2@
r1.T1 *E;
@@
- (E->port.flags & UPF_CONS_FLOW)
+ uart_get_cons_flow(&E->port)
@r3@
struct uart_port *U;
@@
- (U->flags & UPF_CONS_FLOW)
+ uart_get_cons_flow(U)
@r4@
struct uart_port *U;
@@
- U->flags |= UPF_CONS_FLOW
+ uart_set_cons_flow(U, true)
===== END cons_flow.cocci =====
Changes since v2:
- Deprecate UPF_CONS_FLOW. Provide separate boolean with wrappers as
alternative.
- Update all UPF_CONS_FLOW users to new cons_flow wrappers.
- Use irqsave variant of spin lock for status update.
- When 8250 console flow control is not specified, clear the policy.
Changes since v1:
- Prepend a patch to perform an extra LSR wait after CTS assertion if
the initial LSR wait timed out.
- Close a window in serial8250_register_8250_port() where console
flow control was briefly disabled.
- Add port lock synchronization to the port->status RMW update in
uart_set_options().
John Ogness
[0] https://lore.kernel.org/lkml/20260410144949.16581-1-john.ogness@linutronix.de
John Ogness (6):
serial: core: Add dedicated uart_port field for console flow
serial: Replace driver usage of UPF_CONS_FLOW
serial: sh-sci: Avoid deprecated UPF_CONS_FLOW
serial: 8250: Set cons_flow on port registration
serial: 8250: Check LSR timeout on console flow control
serial: 8250: Add support for console flow control
drivers/tty/serial/8250/8250_core.c | 6 ++++++
drivers/tty/serial/8250/8250_port.c | 21 +++++++++++++++++----
drivers/tty/serial/bcm63xx_uart.c | 2 +-
drivers/tty/serial/omap-serial.c | 2 +-
drivers/tty/serial/pch_uart.c | 2 +-
drivers/tty/serial/pxa.c | 2 +-
drivers/tty/serial/samsung_tty.c | 8 ++++----
drivers/tty/serial/serial_core.c | 21 ++++++++++++++++++++-
drivers/tty/serial/serial_txx9.c | 4 ++--
drivers/tty/serial/sh-sci.c | 5 ++++-
drivers/tty/serial/sunsu.c | 2 +-
include/linux/serial_core.h | 20 ++++++++++++++++++++
12 files changed, 78 insertions(+), 17 deletions(-)
base-commit: a1a81aef99e853dec84241d701fbf587d713eb5b
--
2.47.3
^ permalink raw reply
* [PATCH tty v3 2/6] serial: Replace driver usage of UPF_CONS_FLOW
From: John Ogness @ 2026-04-17 10:24 UTC (permalink / raw)
To: Greg Kroah-Hartman, Jiri Slaby
Cc: linux-kernel, Krzysztof Kozlowski, Alim Akhtar, David S. Miller,
Ilpo Järvinen, Andy Shevchenko, Thomas Fourier, Kees Cook,
linux-serial, linux-arm-kernel, linux-samsung-soc, sparclinux
In-Reply-To: <20260417102423.40984-1-john.ogness@linutronix.de>
Rather than using the UPF_CONS_FLOW bit of uart_port.flags to track
the user configuration of console flow control, use the newly added
uart_port.cons_flow (via its get/set functions).
A coccinelle script was used to perform the search/replace.
Note1: The sh-sci driver is blindly copying platform data
configuration flags to uart_port.flags. Thus UPF_CONS_FLOW
could get set. A follow-up commit will address this.
Note2: Aside from sh-sci, the samsung_tty driver is also using
UPF_CONS_FLOW as a platform data configuration flag.
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
drivers/tty/serial/8250/8250_port.c | 4 ++--
drivers/tty/serial/bcm63xx_uart.c | 2 +-
drivers/tty/serial/omap-serial.c | 2 +-
drivers/tty/serial/pch_uart.c | 2 +-
drivers/tty/serial/pxa.c | 2 +-
drivers/tty/serial/samsung_tty.c | 8 ++++----
drivers/tty/serial/serial_txx9.c | 4 ++--
drivers/tty/serial/sunsu.c | 2 +-
8 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index af78cc02f38e7..c91b0fa7111a7 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -1988,7 +1988,7 @@ static void wait_for_xmitr(struct uart_8250_port *up, int bits)
wait_for_lsr(up, bits);
/* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(&up->port)) {
for (tmout = 1000000; tmout; tmout--) {
unsigned int msr = serial_in(up, UART_MSR);
up->msr_saved_flags |= msr & MSR_SAVE_FLAGS;
@@ -3351,7 +3351,7 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s,
* it regardless of the CTS state. Therefore, only use fifo
* if we don't use control flow.
*/
- !(up->port.flags & UPF_CONS_FLOW);
+ !uart_get_cons_flow(&up->port);
if (likely(use_fifo))
serial8250_console_fifo_write(up, s, count);
diff --git a/drivers/tty/serial/bcm63xx_uart.c b/drivers/tty/serial/bcm63xx_uart.c
index 51df9d2d8bfc5..be6777dfdc532 100644
--- a/drivers/tty/serial/bcm63xx_uart.c
+++ b/drivers/tty/serial/bcm63xx_uart.c
@@ -675,7 +675,7 @@ static void wait_for_xmitr(struct uart_port *port)
}
/* Wait up to 1s for flow control if necessary */
- if (port->flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(port)) {
tmout = 1000000;
while (--tmout) {
unsigned int val;
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 0b85f47ff19e0..a9879bc655745 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -1092,7 +1092,7 @@ static void __maybe_unused wait_for_xmitr(struct uart_omap_port *up)
} while (!uart_lsr_tx_empty(status));
/* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(&up->port)) {
for (tmout = 1000000; tmout; tmout--) {
unsigned int msr = serial_in(up, UART_MSR);
diff --git a/drivers/tty/serial/pch_uart.c b/drivers/tty/serial/pch_uart.c
index 6729d8e83c3c5..08cb9ff30506f 100644
--- a/drivers/tty/serial/pch_uart.c
+++ b/drivers/tty/serial/pch_uart.c
@@ -1444,7 +1444,7 @@ static void wait_for_xmitr(struct eg20t_port *up, int bits)
}
/* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(&up->port)) {
unsigned int tmout;
for (tmout = 1000000; tmout; tmout--) {
unsigned int msr = ioread8(up->membase + UART_MSR);
diff --git a/drivers/tty/serial/pxa.c b/drivers/tty/serial/pxa.c
index fea0255067ccd..80afa47f09880 100644
--- a/drivers/tty/serial/pxa.c
+++ b/drivers/tty/serial/pxa.c
@@ -573,7 +573,7 @@ static void wait_for_xmitr(struct uart_pxa_port *up)
} while (!uart_lsr_tx_empty(status));
/* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(&up->port)) {
tmout = 1000000;
while (--tmout &&
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
diff --git a/drivers/tty/serial/samsung_tty.c b/drivers/tty/serial/samsung_tty.c
index e27806bf2cf3e..f9b0dbded1f43 100644
--- a/drivers/tty/serial/samsung_tty.c
+++ b/drivers/tty/serial/samsung_tty.c
@@ -319,7 +319,7 @@ static void s3c24xx_serial_stop_tx(struct uart_port *port)
ourport->tx_enabled = 0;
ourport->tx_in_progress = 0;
- if (port->flags & UPF_CONS_FLOW)
+ if (uart_get_cons_flow(port))
s3c24xx_serial_rx_enable(port);
ourport->tx_mode = 0;
@@ -493,7 +493,7 @@ static void s3c24xx_serial_start_tx(struct uart_port *port)
struct tty_port *tport = &port->state->port;
if (!ourport->tx_enabled) {
- if (port->flags & UPF_CONS_FLOW)
+ if (uart_get_cons_flow(port))
s3c24xx_serial_rx_disable(port);
ourport->tx_enabled = 1;
@@ -781,7 +781,7 @@ static void s3c24xx_serial_rx_drain_fifo(struct s3c24xx_uart_port *ourport)
uerstat = rd_regl(port, S3C2410_UERSTAT);
ch = rd_reg(port, S3C2410_URXH);
- if (port->flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(port)) {
bool txe = s3c24xx_serial_txempty_nofifo(port);
if (ourport->rx_enabled) {
@@ -1830,7 +1830,7 @@ static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
if (cfg->uart_flags & UPF_CONS_FLOW) {
dev_dbg(port->dev, "enabling flow control\n");
- port->flags |= UPF_CONS_FLOW;
+ uart_set_cons_flow(port, true);
}
/* sort our the physical and virtual addresses for each UART */
diff --git a/drivers/tty/serial/serial_txx9.c b/drivers/tty/serial/serial_txx9.c
index 436a559234dfe..103f03c1fe748 100644
--- a/drivers/tty/serial/serial_txx9.c
+++ b/drivers/tty/serial/serial_txx9.c
@@ -422,7 +422,7 @@ static void wait_for_xmitr(struct uart_port *up)
udelay(1);
/* Wait up to 1s for flow control if necessary */
- if (up->flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(up)) {
tmout = 1000000;
while (--tmout &&
(sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS))
@@ -857,7 +857,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count)
* Disable flow-control if enabled (and unnecessary)
*/
flcr = sio_in(up, TXX9_SIFLCR);
- if (!(up->flags & UPF_CONS_FLOW) && (flcr & TXX9_SIFLCR_TES))
+ if (!uart_get_cons_flow(up) && (flcr & TXX9_SIFLCR_TES))
sio_out(up, TXX9_SIFLCR, flcr & ~TXX9_SIFLCR_TES);
uart_console_write(up, s, count, serial_txx9_console_putchar);
diff --git a/drivers/tty/serial/sunsu.c b/drivers/tty/serial/sunsu.c
index 6505a1930da9a..97019b5ec49e2 100644
--- a/drivers/tty/serial/sunsu.c
+++ b/drivers/tty/serial/sunsu.c
@@ -1245,7 +1245,7 @@ static void wait_for_xmitr(struct uart_sunsu_port *up)
} while (!uart_lsr_tx_empty(status));
/* Wait up to 1s for flow control if necessary */
- if (up->port.flags & UPF_CONS_FLOW) {
+ if (uart_get_cons_flow(&up->port)) {
tmout = 1000000;
while (--tmout &&
((serial_in(up, UART_MSR) & UART_MSR_CTS) == 0))
--
2.47.3
^ permalink raw reply related
* Re: [PATCH v4 7/8] ARM: dts: Declare UART1 on zx297520v3 boards
From: Arnd Bergmann @ 2026-04-17 8:59 UTC (permalink / raw)
To: Stefan Dösinger, Jonathan Corbet, Shuah Khan, Russell King,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial
In-Reply-To: <20260416-send-v4-7-e19d02b944ec@gmail.com>
On Thu, Apr 16, 2026, at 22:19, Stefan Dösinger wrote:
>
> The reason why I add the serial1=uart1 alias is to keep console=ttyAMA1
> stable regardless of the other enabled UARTs. UART0, as the name
> implies, has a lower MMIO address, but uart1 is the one that usually has
> the boot output and console.
I'm not sure I'm following here. You generally want to either make
sure the alias matches whatever number is printed on the product
if there are multiple numbered ports, or you just use 'serial0'
as the only alias if there is only one port.
> + aliases {
> + serial1 = &uart1;
> + };
Either way, the alias should go into the board specific file, not
the general SoC file, as a board might be using a different
set of UARTs.
> +
> + /* The UART clock defaults to 26 mhz. It will be replaced when the zx29 clock
> + * framework is added.
> + */
> + uartclk: uartclk: clock-26000000 {
> + #clock-cells = <0>;
> + compatible = "fixed-clock";
> + clock-frequency = <26000000>;
> + };
> +
> + uart1: serial@1408000 {
> + compatible = "arm,pl011", "arm,primecell";
> + arm,primecell-periphid = <0x001feffe>;
> + reg = <0x01408000 0x1000>;
> + interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&uartclk>;
> + clock-names = "apb_pclk";
> + };
Since you know the addresses of the other uart instances, I would
suggest you add all of them at the same time.
Arnd
^ permalink raw reply
* Re: [PATCH] serial: mxs-auart: Compare the return value of gpiod_get_direction against GPIO_LINE_DIRECTION_IN
From: Frank Li @ 2026-04-17 8:22 UTC (permalink / raw)
To: Nikola Z. Ivanov
Cc: gregkh, jirislaby, s.hauer, kernel, festevam, linux-kernel,
linux-serial, imx, linux-arm-kernel
In-Reply-To: <20260416083254.1798-1-zlatistiv@gmail.com>
On Thu, Apr 16, 2026 at 11:32:54AM +0300, Nikola Z. Ivanov wrote:
subjust suggest change to
Replace hardcode 1 with predefined macro GPIO_LINE_DIRECTION_IN
Frank
> The GPIO_LINE_DIRECTION_* definitions have just recently been exposed to
> gpio consumers.h by breaking them out in a separate defs.h file.
>
> Use this to validate the gpio direction instead of the hard-coded literal.
>
> Signed-off-by: Nikola Z. Ivanov <zlatistiv@gmail.com>
> ---
> drivers/tty/serial/mxs-auart.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
> index cc65c9fb6446..6c6df4d5c21f 100644
> --- a/drivers/tty/serial/mxs-auart.c
> +++ b/drivers/tty/serial/mxs-auart.c
> @@ -1519,7 +1519,7 @@ static int mxs_auart_init_gpios(struct mxs_auart_port *s, struct device *dev)
>
> for (i = 0; i < UART_GPIO_MAX; i++) {
> gpiod = mctrl_gpio_to_gpiod(s->gpios, i);
> - if (gpiod && (gpiod_get_direction(gpiod) == 1))
> + if (gpiod && (gpiod_get_direction(gpiod) == GPIO_LINE_DIRECTION_IN))
> s->gpio_irq[i] = gpiod_to_irq(gpiod);
> else
> s->gpio_irq[i] = -EINVAL;
> --
> 2.53.0
>
^ permalink raw reply
* Re: [PATCH v2 2/2] riscv: ultrarisc: 8250_dw: support DP1000 uart
From: Andy Shevchenko @ 2026-04-17 7:46 UTC (permalink / raw)
To: Jia Wang
Cc: Ilpo Järvinen, Greg Kroah-Hartman, Jiri Slaby, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-kernel, linux-serial,
linux-riscv, devicetree, Zhang Xincheng
In-Reply-To: <177641113786.3193169.8990532982066985425.b4-reply@b4>
On Fri, Apr 17, 2026 at 03:32:17PM +0800, Jia Wang wrote:
> On 2026-03-16 13:35 +0200, Andy Shevchenko wrote:
> > On Mon, Mar 16, 2026 at 02:33:23PM +0800, Jia Wang via B4 Relay wrote:
...
> > > +#define DW_UART_QUIRK_FIXED_TYPE BIT(6)
> >
> > Seems unrequired.
> >
> > But to make sure, can you elaborate what's going on here?
> > What is the reads from UCV and CPR registers?
>
> Apologies for the delayed response.
>
> Our DW UART implementation on DP1000 does not provide the CPR/UCV capability
> registers, and reads from both registers always return 0. As a result, the
> autodetection logic in 8250_dw cannot obtain meaningful capability
> information.
>
> To handle this, the current approach is to skip autodetection and rely on
> fixed configuration via a quirk.
>
> If there is a preferred or more appropriate way to support DW UART instances
> without CPR/UCV, I would be happy to adjust the implementation based on your
> suggestions.
Why can't you provide a CPR value via the existing quirk?
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH v2 2/2] riscv: ultrarisc: 8250_dw: support DP1000 uart
From: Jia Wang @ 2026-04-17 7:32 UTC (permalink / raw)
To: Andy Shevchenko
Cc: wangjia, Ilpo Järvinen, Greg Kroah-Hartman, Jiri Slaby,
Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, linux-kernel,
linux-serial, linux-riscv, devicetree, Zhang Xincheng
In-Reply-To: <abfrDBeJrValJR9a@ashevche-desk.local>
On 2026-03-16 13:35 +0200, Andy Shevchenko wrote:
> On Mon, Mar 16, 2026 at 02:33:23PM +0800, Jia Wang via B4 Relay wrote:
>
> > The UART of DP1000 does not support automatic detection of
> > buffer size. skip_autocfg needs to be set to true
>
> Missed period at the end.
>
Thanks, I will fix the missing period.
>
> ...
>
> > +#define DW_UART_QUIRK_FIXED_TYPE BIT(6)
>
> Seems unrequired.
>
> But to make sure, can you elaborate what's going on here?
> What is the reads from UCV and CPR registers?
>
Apologies for the delayed response.
Our DW UART implementation on DP1000 does not provide the CPR/UCV capability
registers, and reads from both registers always return 0. As a result, the
autodetection logic in 8250_dw cannot obtain meaningful capability
information.
To handle this, the current approach is to skip autodetection and rely on
fixed configuration via a quirk.
If there is a preferred or more appropriate way to support DW UART instances
without CPR/UCV, I would be happy to adjust the implementation based on your
suggestions.
> --
> With Best Regards,
> Andy Shevchenko
>
>
>
Best Regards,
Jia Wang
^ permalink raw reply
* Re: [PATCH] serial: 8250: Clear CON_PRINTBUFFER on port re-registration
From: Fushuai Wang @ 2026-04-17 4:00 UTC (permalink / raw)
To: andy.shevchenko
Cc: fushuai.wang, gregkh, ilpo.jarvinen, jirislaby, kees,
linux-kernel, linux-serial, osama.abdelkader, wangfushuai
In-Reply-To: <CAHp75VfyArBYG6p17A__hASERs6-rAp9U_NV68u5OOCUU_qoxA@mail.gmail.com>
>> ...
>> When BIOS "Serial Device" option is set to BMC, both 00:04 and 00:05 are mapped to
>> the same physical I/O port 0x3f8. And then:
>>
>> 1.00:04 is probed first and registers the console for port 0x3f8
>> 2.00:05 is detected and also needs to use port 0x3f8
>
> This is simply wrong. Do we ever support such a FW configuration?
> Why on earth are there two devices for the same resource exposed to
> the OS? It smells like a bug in BIOS.
My BIOS has this configuration option, and I also found other vendors'
BIOS with the same option, e.g.:
https://download.msi.com/archive/mnu_exe/server/S3066-S377-v1.1-BIOS-UG.pdf
Serial Device [BMC]/[S3M] Sets the Serial Device used to output bios serial log
Also, the kernel's serial8250_register_8250_port() already
handles the case where multiple devices map to the same physical
port. When serial8250_find_match_or_unused() detects that
a device with the same port already exists, the caller
(serial8250_register_8250_port()) removes the existing
port before registering the new one.
Since the kernel already supports this remove-re-add process, I
think it makes sense to fix the CON_PRINTBUFFER issue to handle
this scenario, rather than relying on BIOS to avoid such configurations.
--
Regards,
WANG
^ permalink raw reply
* Re: [PATCH v4 1/8] ARM: zte: Add zx297520v3 platform support
From: Randy Dunlap @ 2026-04-16 21:17 UTC (permalink / raw)
To: Stefan Dösinger, Jonathan Corbet, Shuah Khan, Russell King,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial
In-Reply-To: <20260416-send-v4-1-e19d02b944ec@gmail.com>
On 4/16/26 1:19 PM, Stefan Dösinger wrote:
> diff --git a/arch/arm/mach-zte/Kconfig b/arch/arm/mach-zte/Kconfig
> new file mode 100644
> index 000000000000..24699256863b
> --- /dev/null
> +++ b/arch/arm/mach-zte/Kconfig
> @@ -0,0 +1,24 @@
> +# SPDX-License-Identifier: GPL-2.0
> +menuconfig ARCH_ZTE
> + bool "ZTE zx family"
> + depends on ARCH_MULTI_V7
> + help
> + Support for ZTE zx-based family of processors.
> +
> +if ARCH_ZTE
> +
> +config SOC_ZX297520V3
> + default y if ARCH_ZTE
> + bool "ZX297520v3"
> + select ARM_GIC_V3
> + select ARM_AMBA
> + select HAVE_ARM_ARCH_TIMER
> + select PM_GENERIC_DOMAINS if PM
> + help
> + Support for ZTE zx297520v3 SoC. It a single core SoC used in cheap LTE to WiFi routers.
It is
> + These devices can be Identified by the occurrence of the string "zx297520v3" in the boot
identified
> + output and /proc/cpuinfo of their stock firmware.
> +
> + Please read Documentation/arch/arm/zte/zx297520v3.rst on how to boot the kernel.
--
~Randy
^ permalink raw reply
* [PATCH v4 8/8] ARM: defconfig: Add a zx29 defconfig file
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This enables existing drivers that already are (UART) or will be (USB,
GPIO) necessary to operate this board even if they aren't declared in
the DTS yet.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
arch/arm/configs/zx29_defconfig | 90 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/arch/arm/configs/zx29_defconfig b/arch/arm/configs/zx29_defconfig
new file mode 100644
index 000000000000..dae2d86c7583
--- /dev/null
+++ b/arch/arm/configs/zx29_defconfig
@@ -0,0 +1,90 @@
+CONFIG_SYSVIPC=y
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZ4 is not set
+CONFIG_EXPERT=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_MMU=y
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_ZTE=y
+CONFIG_SOC_ZX297520V3=y
+# FIXME: There is no PSCI on this board, but ARM_GIC_V3 depends on it
+CONFIG_ARM_PSCI=y
+CONFIG_ARM_APPENDED_DTB=y
+CONFIG_CMDLINE="console=ttyAMA1 earlyprintk root=/dev/ram rw"
+# CONFIG_SUSPEND is not set
+CONFIG_BINFMT_FLAT=y
+# CONFIG_UEVENT_HELPER is not set
+# CONFIG_STANDALONE is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_ALLOW_DEV_COREDUMP is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=4
+CONFIG_CPU_FREQ=y
+CONFIG_CPUFREQ_DT_PLATDEV=y
+CONFIG_PM=y
+CONFIG_PM_CLK=y
+CONFIG_PM_GENERIC_DOMAINS=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_DEVTMPFS=y # FIXME: This is specific to my initrd. Remove before upstream
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_KEYBOARD_GPIO_POLLED=y
+CONFIG_GPIOLIB=y
+CONFIG_OF_GPIO=y
+CONFIG_GPIO_GENERIC_PLATFORM=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_DEV_BUS=y
+CONFIG_SERIAL_DEV_CTRL_TTYPORT=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_MFD_SYSCON=y
+# CONFIG_HID is not set
+CONFIG_PINCTRL=y
+CONFIG_GENERIC_PINCTRL_GROUPS=y
+CONFIG_PINMUX=y
+CONFIG_GENERIC_PINMUX_FUNCTIONS=y
+CONFIG_PINCONF=y
+CONFIG_GENERIC_PINCONF=y
+CONFIG_RESET_CONTROLLER=y
+CONFIG_POWER_RESET=y
+CONFIG_RESET_SIMPLE=y
+CONFIG_LEDS_GPIO=y
+CONFIG_USB_DWC2=y
+CONFIG_USB_GADGET=y
+CONFIG_MTD=y
+CONFIG_MTD_OF_PARTS=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_SPI_NAND=y
+CONFIG_SPI_MASTER=y
+CONFIG_MMC=y
+CONFIG_MMC_DW=y
+CONFIG_MMC_DW_PLTFM=y
+CONFIG_STMMAC_ETH=y
+CONFIG_STMMAC_PLATFORM=y
+CONFIG_MDIO_BUS=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_SRAM=y
+CONFIG_MISC_FILESYSTEMS=y
+CONFIG_JFFS2_FS=y
+CONFIG_CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_ZTE_ZX=y
+CONFIG_DEBUG_LL_INCLUDE="debug/pl01x.S"
+CONFIG_DEBUG_UART_PL01X=y
+CONFIG_DEBUG_UART_PHYS=0x01408000
+CONFIG_DEBUG_UART_VIRT=0xf4708000
--
2.52.0
^ permalink raw reply related
* [PATCH v4 7/8] ARM: dts: Declare UART1 on zx297520v3 boards
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This is the UART that sends Uboot messages and is accessible via pins on
the boards I have seen so far. UART0 and UART2 exist as well in the SoC
and can be used with the right pinmux settings on some boards. They will
be added later.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
The reason why I add the serial1=uart1 alias is to keep console=ttyAMA1
stable regardless of the other enabled UARTs. UART0, as the name
implies, has a lower MMIO address, but uart1 is the one that usually has
the boot output and console.
---
arch/arm/boot/dts/zte/zx297520v3.dtsi | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm/boot/dts/zte/zx297520v3.dtsi b/arch/arm/boot/dts/zte/zx297520v3.dtsi
index ecd07f3fb8b3..09fbb1d052e3 100644
--- a/arch/arm/boot/dts/zte/zx297520v3.dtsi
+++ b/arch/arm/boot/dts/zte/zx297520v3.dtsi
@@ -6,6 +6,10 @@ / {
#address-cells = <1>;
#size-cells = <1>;
+ aliases {
+ serial1 = &uart1;
+ };
+
cpus {
#address-cells = <1>;
#size-cells = <0>;
@@ -57,5 +61,23 @@ timer {
*/
arm,cpu-registers-not-fw-configured;
};
+
+ /* The UART clock defaults to 26 mhz. It will be replaced when the zx29 clock
+ * framework is added.
+ */
+ uartclk: uartclk: clock-26000000 {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <26000000>;
+ };
+
+ uart1: serial@1408000 {
+ compatible = "arm,pl011", "arm,primecell";
+ arm,primecell-periphid = <0x001feffe>;
+ reg = <0x01408000 0x1000>;
+ interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&uartclk>;
+ clock-names = "apb_pclk";
+ };
};
};
--
2.52.0
^ permalink raw reply related
* [PATCH v4 6/8] ARM: zte: Bring back zx29 UART support
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
This is based on code removed in commit 89d4f98ae90d ("ARM: remove zte
zx platform"). I did not bring back the zx29-uart .compatible as the
arm,primecell-periphid does the job.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
drivers/tty/serial/amba-pl011.c | 37 +++++++++++++++++++++++++++++++++++++
include/linux/amba/bus.h | 6 ++++++
2 files changed, 43 insertions(+)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 7f17d288c807..858a0edd3e3b 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -216,6 +216,38 @@ static struct vendor_data vendor_st = {
.get_fifosize = get_fifosize_st,
};
+static const u16 pl011_zte_offsets[REG_ARRAY_SIZE] = {
+ [REG_DR] = ZX_UART011_DR,
+ [REG_FR] = ZX_UART011_FR,
+ [REG_LCRH_RX] = ZX_UART011_LCRH,
+ [REG_LCRH_TX] = ZX_UART011_LCRH,
+ [REG_IBRD] = ZX_UART011_IBRD,
+ [REG_FBRD] = ZX_UART011_FBRD,
+ [REG_CR] = ZX_UART011_CR,
+ [REG_IFLS] = ZX_UART011_IFLS,
+ [REG_IMSC] = ZX_UART011_IMSC,
+ [REG_RIS] = ZX_UART011_RIS,
+ [REG_MIS] = ZX_UART011_MIS,
+ [REG_ICR] = ZX_UART011_ICR,
+ [REG_DMACR] = ZX_UART011_DMACR,
+};
+
+static unsigned int get_fifosize_zte(struct amba_device *dev)
+{
+ return 16;
+}
+
+static struct vendor_data vendor_zte = {
+ .reg_offset = pl011_zte_offsets,
+ .access_32b = true,
+ .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8,
+ .fr_busy = ZX_UART01x_FR_BUSY,
+ .fr_dsr = ZX_UART01x_FR_DSR,
+ .fr_cts = ZX_UART01x_FR_CTS,
+ .fr_ri = ZX_UART011_FR_RI,
+ .get_fifosize = get_fifosize_zte,
+};
+
/* Deals with DMA transactions */
struct pl011_dmabuf {
@@ -3081,6 +3113,11 @@ static const struct amba_id pl011_ids[] = {
.mask = 0x00ffffff,
.data = &vendor_st,
},
+ {
+ .id = AMBA_LINUX_ID(0x00, 0x1, 0xffe),
+ .mask = 0x00ffffff,
+ .data = &vendor_zte,
+ },
{ 0, 0 },
};
diff --git a/include/linux/amba/bus.h b/include/linux/amba/bus.h
index 9946276aff73..854c962d70f5 100644
--- a/include/linux/amba/bus.h
+++ b/include/linux/amba/bus.h
@@ -103,8 +103,14 @@ enum amba_vendor {
AMBA_VENDOR_ST = 0x80,
AMBA_VENDOR_QCOM = 0x51,
AMBA_VENDOR_LSI = 0xb6,
+ AMBA_VENDOR_LINUX = 0xfe, /* This value is not official */
};
+/* This is used to generate pseudo-ID for AMBA device */
+#define AMBA_LINUX_ID(conf, rev, part) \
+ (((conf) & 0xff) << 24 | ((rev) & 0xf) << 20 | \
+ AMBA_VENDOR_LINUX << 12 | ((part) & 0xfff))
+
extern const struct bus_type amba_bustype;
#define to_amba_device(d) container_of_const(d, struct amba_device, dev)
--
2.52.0
^ permalink raw reply related
* [PATCH v4 5/8] ARM: dts: Add an armv7 timer for zx297520v3
From: Stefan Dösinger @ 2026-04-16 20:19 UTC (permalink / raw)
To: Jonathan Corbet, Shuah Khan, Russell King, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Arnd Bergmann,
Krzysztof Kozlowski, Alexandre Belloni, Linus Walleij,
Drew Fustini, Greg Kroah-Hartman, Jiri Slaby
Cc: linux-doc, linux-kernel, linux-arm-kernel, devicetree, soc,
linux-serial, Stefan Dösinger
In-Reply-To: <20260416-send-v4-0-e19d02b944ec@gmail.com>
The stock kernel does not use this timer, but it seems to work fine. The
board has other board-specific timers that would need a driver and I see
no reason to bother with them since the arm standard timer works.
The caveat is the non-standard GIC setup needed to handle the timer's
level-low PPI. This is the responsibility of the boot loader and
documented in Documentation/arch/arm/zte/zx297520v3.rst.
Signed-off-by: Stefan Dösinger <stefandoesinger@gmail.com>
---
arch/arm/boot/dts/zte/zx297520v3.dtsi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/arch/arm/boot/dts/zte/zx297520v3.dtsi b/arch/arm/boot/dts/zte/zx297520v3.dtsi
index d6c71d52b26c..ecd07f3fb8b3 100644
--- a/arch/arm/boot/dts/zte/zx297520v3.dtsi
+++ b/arch/arm/boot/dts/zte/zx297520v3.dtsi
@@ -24,6 +24,15 @@ soc {
interrupt-parent = <&gic>;
ranges;
+ /* The GIC has a non-standard way of configuring ints between level-low/level
+ * high or rising edge/falling edge at 0xf2202070 and onwards. See AP_INT_MODE_BASE
+ * and AP_PPI_MODE_REG in the ZTE kernel, although the offsets in the kernel source
+ * seem wrong.
+ *
+ * Everything defaults to active-high/rising edge, but the timer is active-low. We
+ * currently rely on the boot loader to change timer IRQs to active-low for us for
+ * now.
+ */
gic: interrupt-controller@f2000000 {
compatible = "arm,gic-v3";
interrupt-controller;
@@ -33,5 +42,20 @@ gic: interrupt-controller@f2000000 {
reg = <0xf2000000 0x10000>,
<0xf2040000 0x20000>;
};
+
+ timer {
+ compatible = "arm,armv7-timer";
+ interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 14 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 11 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_PPI 10 IRQ_TYPE_LEVEL_LOW>;
+ clock-frequency = <26000000>;
+ interrupt-parent = <&gic>;
+ /* I don't think uboot sets CNTVOFF and the stock kernel doesn't use the
+ * arm timer at all. Since this is a single CPU system I don't think it
+ * really matters that the offset is random though.
+ */
+ arm,cpu-registers-not-fw-configured;
+ };
};
};
--
2.52.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox