devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Ilpo Järvinen" <ilpo.jarvinen@linux.intel.com>
To: Binbin Zhou <zhoubinbin@loongson.cn>
Cc: Binbin Zhou <zhoubb.aaron@gmail.com>,
	Huacai Chen <chenhuacai@loongson.cn>,
	 Rob Herring <robh+dt@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	 Conor Dooley <conor+dt@kernel.org>,
	 Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	 Jiri Slaby <jirislaby@kernel.org>,
	Haowei Zheng <zhenghaowei@loongson.cn>,
	 Huacai Chen <chenhuacai@kernel.org>,
	Xuerui Wang <kernel@xen0n.name>,
	 loongarch@lists.linux.dev, devicetree@vger.kernel.org,
	 linux-serial <linux-serial@vger.kernel.org>
Subject: Re: [PATCH v5 2/3] serial: 8250: Add Loongson uart driver support
Date: Tue, 30 Sep 2025 14:58:07 +0300 (EEST)	[thread overview]
Message-ID: <9cd368a1-4ba2-3b96-5cfe-0e600e77a3fe@linux.intel.com> (raw)
In-Reply-To: <9823e7afe713450e210dab9dba6fa18683dc1fe0.1758676290.git.zhoubinbin@loongson.cn>

On Wed, 24 Sep 2025, Binbin Zhou wrote:

> Add the driver for on-chip UART used on Loongson family chips.
> 
> The hardware is similar to 8250, but there are the following
> differences:
>  - Some chips (such as Loongson-2K2000) have added a fractional division
>    register to obtain the required baud rate accurately, so the
>    {get,set}_divisor callback is overridden.
>  - Due to hardware defects, quirk handling is required for
>    UART_MCR/UART_MSR.
> 
> Co-developed-by: Haowei Zheng <zhenghaowei@loongson.cn>
> Signed-off-by: Haowei Zheng <zhenghaowei@loongson.cn>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  drivers/tty/serial/8250/8250_loongson.c | 202 ++++++++++++++++++++++++
>  drivers/tty/serial/8250/8250_port.c     |   8 +
>  drivers/tty/serial/8250/Kconfig         |  10 ++
>  drivers/tty/serial/8250/Makefile        |   1 +
>  include/uapi/linux/serial_core.h        |   1 +
>  5 files changed, 222 insertions(+)
>  create mode 100644 drivers/tty/serial/8250/8250_loongson.c
> 
> diff --git a/drivers/tty/serial/8250/8250_loongson.c b/drivers/tty/serial/8250/8250_loongson.c
> new file mode 100644
> index 000000000000..a114b4e6d5c3
> --- /dev/null
> +++ b/drivers/tty/serial/8250/8250_loongson.c
> @@ -0,0 +1,202 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Serial Port driver for Loongson family chips
> + *
> + * Copyright (C) 2020-2025 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/module.h>
> +#include <linux/property.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/reset.h>
> +
> +#include "8250.h"
> +
> +/* Divisor Latch Fraction Register */
> +#define LOONGSON_UART_DLF		0x2
> +
> +/* Flags */
> +#define LOONGSON_UART_HAS_FRAC		BIT(0)
> +#define LOONGSON_UART_QUIRK_MCR		BIT(1)
> +#define LOONGSON_UART_QUIRK_MSR		BIT(2)
> +
> +#define LS2K0500_UART_FLAG	(LOONGSON_UART_QUIRK_MCR | LOONGSON_UART_QUIRK_MSR)
> +#define LS2K1500_UART_FLAG	(LOONGSON_UART_HAS_FRAC | LOONGSON_UART_QUIRK_MCR)
> +
> +struct loongson_uart_data {
> +	int line;
> +	int mcr_invert;
> +	int msr_invert;
> +	struct reset_control *rst;
> +};
> +
> +static unsigned int serial_fixup(struct uart_port *p, unsigned int offset, unsigned int val)
> +{
> +	struct loongson_uart_data *ddata = p->private_data;
> +
> +	if (offset == UART_MCR)
> +		val ^= ddata->mcr_invert;
> +
> +	if (offset == UART_MSR)
> +		val ^= ddata->msr_invert;
> +
> +	return val;
> +}
> +
> +static u32 loongson_serial_in(struct uart_port *p, unsigned int offset)
> +{
> +	unsigned int val;
> +
> +	val = readb(p->membase + (offset << p->regshift));
> +
> +	return serial_fixup(p, offset, val);
> +}
> +
> +static void loongson_serial_out(struct uart_port *p, unsigned int offset, unsigned int value)
> +{
> +	offset <<= p->regshift;
> +	writeb(serial_fixup(p, offset, value), p->membase + offset);

This would be cleaner if you do that serial_fixup() on a preceeding line.

Include for writeb()?

> +}
> +
> +static unsigned int loongson_frac_get_divisor(struct uart_port *port, unsigned int baud,
> +					      unsigned int *frac)
> +{
> +	unsigned int quot;
> +
> +	quot = DIV_ROUND_CLOSEST((port->uartclk << 4), baud);

Missing include.

> +	*frac = FIELD_GET(GENMASK(7, 0), quot);
> +
> +	return FIELD_GET(GENMASK(15, 8), quot);

Please name the fields properly with defines.

You're also missing #include for GENMASK().

> +}
> +
> +static void loongson_frac_set_divisor(struct uart_port *port, unsigned int baud,
> +				      unsigned int quot, unsigned int quot_frac)
> +{
> +	struct uart_8250_port *up = up_to_u8250p(port);
> +
> +	serial_port_out(port, UART_LCR, up->lcr | UART_LCR_DLAB);
> +	serial_dl_write(up, quot);
> +	serial_port_out(port, LOONGSON_UART_DLF, quot_frac);
> +}
> +
> +static int loongson_uart_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct uart_8250_port uart = {};
> +	struct loongson_uart_data *ddata;
> +	struct resource *res;
> +	unsigned int flags;
> +	int ret;
> +
> +	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
> +	if (!ddata)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res)
> +		return -ENODEV;
> +
> +	uart.port.irq = platform_get_irq(pdev, 0);
> +	if (uart.port.irq < 0)
> +		return -EINVAL;
> +
> +	device_property_read_u32(dev, "clock-frequency", &uart.port.uartclk);
> +
> +	spin_lock_init(&uart.port.lock);
> +	uart.port.flags = UPF_SHARE_IRQ | UPF_FIXED_PORT | UPF_FIXED_TYPE | UPF_IOREMAP;
> +	uart.port.iotype = UPIO_MEM;
> +	uart.port.regshift = 0;
> +	uart.port.dev = dev;
> +	uart.port.type = PORT_LOONGSON;
> +	uart.port.private_data = ddata;
> +
> +	uart.port.mapbase = res->start;
> +	uart.port.mapsize = resource_size(res);
> +	uart.port.serial_in = loongson_serial_in;
> +	uart.port.serial_out = loongson_serial_out;
> +
> +	flags = (uintptr_t)device_get_match_data(dev);

Perhaps flags should be unsigned long?

> +	if (flags & LOONGSON_UART_HAS_FRAC) {
> +		uart.port.get_divisor = loongson_frac_get_divisor;
> +		uart.port.set_divisor = loongson_frac_set_divisor;
> +	}
> +
> +	if (flags & LOONGSON_UART_QUIRK_MCR)
> +		ddata->mcr_invert |= (UART_MCR_RTS | UART_MCR_DTR);
> +
> +	if (flags & LOONGSON_UART_QUIRK_MSR)
> +		ddata->msr_invert |= (UART_MSR_CTS | UART_MSR_DSR);

I think it would be better to put these invert masks directly into a 
struct which is then put into .data. LOONGSON_UART_HAS_FRAC can be bool 
in that struct.

> +	ddata->rst = devm_reset_control_get_optional_shared(dev, NULL);
> +	if (IS_ERR(ddata->rst))
> +		return PTR_ERR(ddata->rst);
> +
> +	ret = reset_control_deassert(ddata->rst);
> +	if (ret)
> +		return ret;
> +
> +	ret = serial8250_register_8250_port(&uart);
> +	if (ret < 0) {
> +		reset_control_assert(ddata->rst);
> +		return ret;
> +	}
> +
> +	ddata->line = ret;
> +	platform_set_drvdata(pdev, ddata);
> +
> +	return 0;
> +}
> +
> +static void loongson_uart_remove(struct platform_device *pdev)
> +{
> +	struct loongson_uart_data *ddata = platform_get_drvdata(pdev);
> +
> +	serial8250_unregister_port(ddata->line);
> +	reset_control_assert(ddata->rst);
> +}
> +
> +static int loongson_uart_suspend(struct device *dev)
> +{
> +	struct loongson_uart_data *ddata = dev_get_drvdata(dev);
> +
> +	serial8250_suspend_port(ddata->line);
> +
> +	return 0;
> +}
> +
> +static int loongson_uart_resume(struct device *dev)
> +{
> +	struct loongson_uart_data *data = dev_get_drvdata(dev);
> +
> +	serial8250_resume_port(data->line);
> +
> +	return 0;
> +}
> +
> +static DEFINE_SIMPLE_DEV_PM_OPS(loongson_uart_pm_ops, loongson_uart_suspend,
> +				loongson_uart_resume);

Include? Please check if you miss even more includes beyond those I've 
highlighted here.

> +static const struct of_device_id loongson_uart_of_ids[] = {
> +	{ .compatible = "loongson,ls2k0500-uart", .data = (void *)LS2K0500_UART_FLAG },
> +	{ .compatible = "loongson,ls2k1500-uart", .data = (void *)LS2K1500_UART_FLAG },
> +	{ /* sentinel */ },

Nit, no need for comma in the terminator entry.

> +};
> +MODULE_DEVICE_TABLE(of, loongson_uart_of_ids);
> +
> +static struct platform_driver loongson_uart_driver = {
> +	.probe = loongson_uart_probe,
> +	.remove = loongson_uart_remove,
> +	.driver = {
> +		.name = "loongson-uart",
> +		.pm = pm_ptr(&loongson_uart_pm_ops),
> +		.of_match_table = loongson_uart_of_ids,
> +	},
> +};
> +
> +module_platform_driver(loongson_uart_driver);
> +
> +MODULE_DESCRIPTION("Loongson UART driver");
> +MODULE_AUTHOR("Loongson Technology Corporation Limited.");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
> index 719faf92aa8a..53efe841656f 100644
> --- a/drivers/tty/serial/8250/8250_port.c
> +++ b/drivers/tty/serial/8250/8250_port.c
> @@ -310,6 +310,14 @@ static const struct serial8250_config uart_config[] = {
>  		.rxtrig_bytes	= {1, 8, 16, 30},
>  		.flags		= UART_CAP_FIFO | UART_CAP_AFE,
>  	},
> +	[PORT_LOONGSON] = {
> +		.name		= "Loongson",
> +		.fifo_size	= 16,
> +		.tx_loadsz	= 16,
> +		.fcr		= UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
> +		.rxtrig_bytes   = {1, 4, 8, 14},
> +		.flags		= UART_CAP_FIFO,
> +	},

This is exactly identical to PORT_16550A. Just use PORT_16550A instead of 
adding a new one unnecessarily.

--
 i.

>  };
>  
>  /* Uart divisor latch read */
> diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
> index f64ef0819cd4..98236b3bec10 100644
> --- a/drivers/tty/serial/8250/Kconfig
> +++ b/drivers/tty/serial/8250/Kconfig
> @@ -468,6 +468,16 @@ config SERIAL_8250_OMAP_TTYO_FIXUP
>  	  not booting kernel because the serial console remains silent in case
>  	  they forgot to update the command line.
>  
> +config SERIAL_8250_LOONGSON
> +	tristate "Loongson 8250 based serial port"
> +	depends on SERIAL_8250
> +	depends on LOONGARCH || COMPILE_TEST
> +	help
> +	  If you have a machine based on LoongArch CPU you can enable
> +	  its onboard serial ports by enabling this option. The option
> +	  is applicable to both devicetree and ACPI, say Y to this option.
> +	  If unsure, say N.
> +
>  config SERIAL_8250_LPC18XX
>  	tristate "NXP LPC18xx/43xx serial port support"
>  	depends on SERIAL_8250 && OF && (ARCH_LPC18XX || COMPILE_TEST)
> diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
> index 513a0941c284..e318a3240789 100644
> --- a/drivers/tty/serial/8250/Makefile
> +++ b/drivers/tty/serial/8250/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_SERIAL_8250_HP300)		+= 8250_hp300.o
>  obj-$(CONFIG_SERIAL_8250_HUB6)		+= 8250_hub6.o
>  obj-$(CONFIG_SERIAL_8250_INGENIC)	+= 8250_ingenic.o
>  obj-$(CONFIG_SERIAL_8250_IOC3)		+= 8250_ioc3.o
> +obj-$(CONFIG_SERIAL_8250_LOONGSON)	+= 8250_loongson.o
>  obj-$(CONFIG_SERIAL_8250_LPC18XX)	+= 8250_lpc18xx.o
>  obj-$(CONFIG_SERIAL_8250_LPSS)		+= 8250_lpss.o
>  obj-$(CONFIG_SERIAL_8250_MEN_MCB)	+= 8250_men_mcb.o
> diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h
> index 9c007a106330..607cf060a72a 100644
> --- a/include/uapi/linux/serial_core.h
> +++ b/include/uapi/linux/serial_core.h
> @@ -31,6 +31,7 @@
>  #define PORT_ALTR_16550_F128 28 /* Altera 16550 UART with 128 FIFOs */
>  #define PORT_RT2880	29	/* Ralink RT2880 internal UART */
>  #define PORT_16550A_FSL64 30	/* Freescale 16550 UART with 64 FIFOs */
> +#define PORT_LOONGSON	31	/* Loongson 16550 UART */
>  
>  /*
>   * ARM specific type numbers.  These are not currently guaranteed
> 

  parent reply	other threads:[~2025-09-30 11:58 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-24  6:29 [PATCH v5 0/3] uart: Introduce uart driver for the Loongson family Binbin Zhou
2025-09-24  6:29 ` [PATCH v5 1/3] dt-bindings: serial: 8250: Add Loongson uart compatible Binbin Zhou
2025-09-24 19:19   ` Conor Dooley
2025-09-24  6:29 ` [PATCH v5 2/3] serial: 8250: Add Loongson uart driver support Binbin Zhou
2025-09-24 10:22   ` Greg Kroah-Hartman
2025-09-28  2:48     ` Binbin Zhou
2025-09-29  6:19       ` Jiri Slaby
2025-09-30  3:03         ` Binbin Zhou
2025-10-08 12:16           ` Greg Kroah-Hartman
2025-09-29  6:26   ` Jiri Slaby
2025-09-30  3:07     ` Binbin Zhou
2025-09-30 12:01     ` Ilpo Järvinen
2025-09-30 11:58   ` Ilpo Järvinen [this message]
2025-10-09  2:52     ` Binbin Zhou
2025-10-09 11:46       ` Ilpo Järvinen
2025-09-24  6:29 ` [PATCH v5 3/3] LoongArch: dts: Add uart new compatible string Binbin Zhou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=9cd368a1-4ba2-3b96-5cfe-0e600e77a3fe@linux.intel.com \
    --to=ilpo.jarvinen@linux.intel.com \
    --cc=chenhuacai@kernel.org \
    --cc=chenhuacai@loongson.cn \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=jirislaby@kernel.org \
    --cc=kernel@xen0n.name \
    --cc=krzk+dt@kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=loongarch@lists.linux.dev \
    --cc=robh+dt@kernel.org \
    --cc=zhenghaowei@loongson.cn \
    --cc=zhoubb.aaron@gmail.com \
    --cc=zhoubinbin@loongson.cn \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).