uart: Introduce uart driver for the Loongson family
Subsystems (3)
v6 cover uart: Introduce uart driver for the Loongson family
Hi all:
For various reasons, I will be taking over from Haowei and continuing to push forward with this patch set. Thanks to Haowei for his efforts so far.
This patchset introduce a generic UART framework driver for Loongson family. It can be found on Loongson3 series cpus, Loongson-2K series cpus and Loongson LS7A bridge chips.
Thanks.
------
V6:
Patch-1:
- Add Conor's Acked-by tag, thanks.
Patch-2:
- Add more missing #include;
- Convert mcr_invert/msr_invert from int to u8;
- Split serial_fixup() on a preceding line;
- Add macro definition to avoid magic numbers, such as LOONGSON_QUOT_FRAC_MASK;
- Rework the relevant data structures, where loongson_uart_ddata represents the Soc's driver_data;
- Drop PORT_LOONGSON and use PORT_16550A instead.
- devm_platform_get_and_ioremap_resource() instead of platform_get_resource();
- Use uart_read_port_properties() and parse the clock attributes;
- Use switch-case instead of if-else in serial_fixup().
Link to V5:
https://lore.kernel.org/all/[email protected]/
V5:
Patch-1:
- Since the Loongson UART is NS8250A-compatible, simply add ls2k* compatible to 8250.yaml.
DTS{i} tested by make dtbs_check W=1
Link to V4:
https://lore.kernel.org/all/[email protected]/
V4:
Patch-1:
- Rename binding name from loongson,uart.yaml to
loongson,ls2k0500-uart.yaml;
- Drop ls7a compatible;
- According to the manual, ls3a and ls2k uart are the same, so merge their compatible.
Patch-2:
- Format code;
- Add the LOONGSON_UART_DLF macro definition to avoid magic numbers;
- Simplify the code, merge flags and quirks, and remove struct
loongson_uart_config;
- Use DEFINE_SIMPLE_DEV_PM_OPS;
- Drop loongson,ls7a-uart compatible.
Patch-3:
- Add ls2k* compatible string, and ns16550a as the fallback compatible.
Link to V3:
https://lore.kernel.org/all/[email protected]/
Binbin Zhou (3):
dt-bindings: serial: 8250: Add Loongson uart compatible
serial: 8250: Add Loongson uart driver support
LoongArch: dts: Add uart new compatible string
.../devicetree/bindings/serial/8250.yaml | 14 ++ arch/loongarch/boot/dts/loongson-2k0500.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k1000.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k2000.dtsi | 2 +- drivers/tty/serial/8250/8250_loongson.c | 238 ++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 10 + drivers/tty/serial/8250/Makefile | 1 + 7 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 drivers/tty/serial/8250/8250_loongson.c
base-commit: 0d97f2067c166eb495771fede9f7b73999c67f66
Signature · 1 line
2.47.3
PATCH v6 1/3 dt-bindings: serial: 8250: Add Loongson uart compatible
The Loongson family have a mostly NS16550A-compatible UART and High-Speed UART hardware with the exception of custom frequency divider latch settings register.
--- Documentation/devicetree/bindings/serial/8250.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
Documentation/devicetree/bindings/serial/8250.yaml +14 -0
@@ -125,6 +125,8 @@ properties: - nxp,lpc1850-uart - opencores,uart16550-rtlsvn105 - ti,da830-uart + - loongson,ls2k0500-uart + - loongson,ls2k1500-uart - const: ns16550a - items: - enum:
@@ -169,6 +171,18 @@ properties: - nvidia,tegra194-uart - nvidia,tegra234-uart - const: nvidia,tegra20-uart + - items: + - enum: + - loongson,ls2k1000-uart + - const: loongson,ls2k0500-uart + - const: ns16550a + - items: + - enum: + - loongson,ls3a5000-uart + - loongson,ls3a6000-uart + - loongson,ls2k2000-uart + - const: loongson,ls2k1500-uart + - const: ns16550a reg: maxItems: 1
Signature · 1 line
2.47.3
PATCH v6 2/3 serial: 8250: Add Loongson uart driver support
In reply to Binbin ZhouAdd the driver for on-chip UART used on Loongson family chips.
The hardware is similar to NS16550A, 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.
--- drivers/tty/serial/8250/8250_loongson.c | 238 ++++++++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 10 + drivers/tty/serial/8250/Makefile | 1 + 3 files changed, 249 insertions(+) create mode 100644 drivers/tty/serial/8250/8250_loongson.c
drivers/tty/serial/8250/8250_loongson.c new file +238 -0
@@ -0,0 +1,238 @@ +// 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/bits.h> +#include <linux/clk.h> +#include <linux/console.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/property.h> +#include <linux/math.h> +#include <linux/mod_devicetable.h> +#include <linux/pm.h> +#include <linux/reset.h> + +#include "8250.h" + +/* Divisor Latch Fraction Register */ +#define LOONGSON_UART_DLF 0x2 + +#define LOONGSON_QUOT_FRAC_MASK GENMASK(7, 0) +#define LOONGSON_QUOT_DIV_MASK GENMASK(15, 8) + +struct loongson_uart_ddata { +bool has_frac; +u8 mcr_invert; +u8 msr_invert; +}; + +static const struct loongson_uart_ddata ls2k0500_uart_data = { + .has_frac = false, + .mcr_invert =UART_MCR_RTS |UART_MCR_DTR , + .msr_invert =UART_MSR_CTS |UART_MSR_DSR , +}; + +static const struct loongson_uart_ddata ls2k1500_uart_data = { + .has_frac = true, + .mcr_invert =UART_MCR_RTS |UART_MCR_DTR , + .msr_invert =0 , +}; + +struct loongson_uart_priv { +int line; +struct clk *clk; +struct resource *res; +struct reset_control *rst; +const struct loongson_uart_ddata *ddata; +}; + +static u8 serial_fixup (struct uart_port *p,unsigned int offset,u8 val) +{ +struct loongson_uart_priv *priv = p->private_data; + +switch (offset) { +case UART_MCR : +return val ^ priv->ddata->mcr_invert; +case UART_MSR : +return val ^ priv->ddata->msr_invert; +default : +return val; + } +} + +static u32 loongson_serial_in (struct uart_port *p,unsigned int offset) +{ +u8 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) +{ +u8 val; + + offset <<= p->regshift; + val =serial_fixup (p, offset, value); +writeb (val, p->membase + offset); +} + +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); + *frac =FIELD_GET (LOONGSON_QUOT_FRAC_MASK , quot); + +return FIELD_GET (LOONGSON_QUOT_DIV_MASK , quot); +} + +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_priv *priv; +struct uart_port *port; +int ret; + + priv =devm_kzalloc (dev,sizeof (*priv),GFP_KERNEL ); +if (!priv) +return -ENOMEM ; + + priv->ddata =device_get_match_data (dev); + + port = &uart.port; +spin_lock_init (&port->lock); + port->flags =UPF_SHARE_IRQ |UPF_FIXED_PORT |UPF_FIXED_TYPE |UPF_IOREMAP ; + port->iotype =UPIO_MEM ; + port->regshift =0 ; + port->dev = dev; + port->type =PORT_16550A ; + port->private_data = priv; + + port->membase =devm_platform_get_and_ioremap_resource (pdev,0 , &priv->res); +if (!port->membase) +return -ENOMEM ; + + port->mapbase = priv->res->start; + port->mapsize =resource_size (priv->res); + port->serial_in = loongson_serial_in; + port->serial_out = loongson_serial_out; + +if (priv->ddata->has_frac) { + port->get_divisor = loongson_frac_get_divisor; + port->set_divisor = loongson_frac_set_divisor; + } + + ret =uart_read_port_properties (port); +if (ret) +return ret; + +if (!port->uartclk) { + priv->clk =devm_clk_get_enabled (dev,NULL ); +if (IS_ERR (priv->clk)) +return dev_err_probe (dev,PTR_ERR (priv->clk), +"Unable to determine clock frequency!\n" ); + port->uartclk =clk_get_rate (priv->clk); + } + + priv->rst =devm_reset_control_get_optional_shared (dev,NULL ); +if (IS_ERR (priv->rst)) +return PTR_ERR (priv->rst); + + ret =reset_control_deassert (priv->rst); +if (ret) +return ret; + + ret =serial8250_register_8250_port (&uart); +if (ret <0 ) { +reset_control_assert (priv->rst); +return ret; + } + + priv->line = ret; +platform_set_drvdata (pdev, priv); + +return 0 ; +} + +static void loongson_uart_remove (struct platform_device *pdev) +{ +struct loongson_uart_priv *priv =platform_get_drvdata (pdev); + +serial8250_unregister_port (priv->line); +reset_control_assert (priv->rst); +} + +static int loongson_uart_suspend (struct device *dev) +{ +struct loongson_uart_priv *priv =dev_get_drvdata (dev); +struct uart_8250_port *up =serial8250_get_port (priv->line); + +serial8250_suspend_port (priv->line); + +if (!uart_console (&up->port) || console_suspend_enabled) +clk_disable_unprepare (priv->clk); + +return 0 ; +} + +static int loongson_uart_resume (struct device *dev) +{ +struct loongson_uart_priv *priv =dev_get_drvdata (dev); +struct uart_8250_port *up =serial8250_get_port (priv->line); +int ret; + +if (!uart_console (&up->port) || console_suspend_enabled) { + ret =clk_prepare_enable (priv->clk); +if (ret) +return ret; + } + +serial8250_resume_port (priv->line); + +return 0 ; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(loongson_uart_pm_ops, loongson_uart_suspend, + loongson_uart_resume); + +static const struct of_device_id loongson_uart_of_ids[] = { + { .compatible ="loongson,ls2k0500-uart" , .data = &ls2k0500_uart_data }, + { .compatible ="loongson,ls2k1500-uart" , .data = &ls2k1500_uart_data }, + { }, +}; +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" );
drivers/tty/serial/8250/Kconfig +10 -0
@@ -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)
drivers/tty/serial/8250/Makefile +1 -0
@@ -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
Signature · 1 line
2.47.3
PATCH v6 3/3 LoongArch: dts: Add uart new compatible string
In reply to Binbin ZhouAdd loongson,ls2k*-uart compatible string on uarts.
--- arch/loongarch/boot/dts/loongson-2k0500.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k1000.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k2000.dtsi | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
arch/loongarch/boot/dts/loongson-2k0500.dtsi +1 -1
@@ -380,7 +380,7 @@ tsensor: thermal-sensor@1fe11500 { }; uart0: serial@1ff40800 { - compatible = "ns16550a"; + compatible = "loongson,ls2k0500-uart", "ns16550a"; reg = <0x0 0x1ff40800 0x0 0x10>; clock-frequency = <100000000>; interrupt-parent = <&eiointc>;
arch/loongarch/boot/dts/loongson-2k1000.dtsi +1 -1
@@ -297,7 +297,7 @@ dma-controller@1fe00c40 { }; uart0: serial@1fe20000 { - compatible = "ns16550a"; + compatible = "loongson,ls2k1000-uart", "loongson,ls2k0500-uart", "ns16550a"; reg = <0x0 0x1fe20000 0x0 0x10>; clock-frequency = <125000000>; interrupt-parent = <&liointc0>;
arch/loongarch/boot/dts/loongson-2k2000.dtsi +1 -1
@@ -250,7 +250,7 @@ i2c@1fe00130 { }; uart0: serial@1fe001e0 { - compatible = "ns16550a"; + compatible = "loongson,ls2k2000-uart", "loongson,ls2k1500-uart", "ns16550a"; reg = <0x0 0x1fe001e0 0x0 0x10>; clock-frequency = <100000000>; interrupt-parent = <&liointc>;
Signature · 1 line
2.47.3
For the whole series:
Quoted 86 lines · Binbin Zhou
Hi all:
For various reasons, I will be taking over from Haowei and continuing to push forward with this patch set. Thanks to Haowei for his efforts so far.
This patchset introduce a generic UART framework driver for Loongson family. It can be found on Loongson3 series cpus, Loongson-2K series cpus and Loongson LS7A bridge chips.
Thanks.
------
V6:
Patch-1:
- Add Conor's Acked-by tag, thanks.Patch-2:
- Add more missing #include;
- Convert mcr_invert/msr_invert from int to u8;
- Splitserial_fixup()on a preceding line;
- Add macro definition to avoid magic numbers, such asLOONGSON_QUOT_FRAC_MASK;
- Rework the relevant data structures, whereloongson_uart_ddatarepresents the Soc'sdriver_data;
- DropPORT_LOONGSONand usePORT_16550Ainstead.
-devm_platform_get_and_ioremap_resource()instead ofplatform_get_resource();
- Useuart_read_port_properties()and parse the clock attributes;
- Use switch-case instead of if-else inserial_fixup().Link to V5:
https://lore.kernel.org/all/[email protected]/V5:
Patch-1:
- Since the Loongson UART is NS8250A-compatible, simply add ls2k* compatible to 8250.yaml.DTS{i} tested by
make dtbs_check W=1Link to V4:
https://lore.kernel.org/all/[email protected]/V4:
Patch-1:
- Rename binding name from loongson,uart.yaml toloongson,ls2k0500-uart.yaml;- Drop ls7a compatible;
- According to the manual, ls3a and ls2k uart are the same, so merge their compatible.Patch-2:
- Format code;
- Add theLOONGSON_UART_DLFmacro definition to avoid magic numbers;
- Simplify the code, merge flags and quirks, and remove structloongson_uart_config;- Use
DEFINE_SIMPLE_DEV_PM_OPS;
- Drop loongson,ls7a-uart compatible.Patch-3:
- Add ls2k* compatible string, and ns16550a as the fallback compatible.Link to V3:
https://lore.kernel.org/all/[email protected]/Binbin Zhou (3):
dt-bindings: serial: 8250: Add Loongson uart compatible
serial: 8250: Add Loongson uart driver support
LoongArch: dts: Add uart new compatible string.../devicetree/bindings/serial/8250.yaml | 14 ++ arch/loongarch/boot/dts/loongson-2k0500.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k1000.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k2000.dtsi | 2 +- drivers/tty/serial/8250/8250_loongson.c | 238 ++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 10 + drivers/tty/serial/8250/Makefile | 1 + 7 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 drivers/tty/serial/8250/8250_loongson.cbase-commit:
0d97f2067c166eb495771fede9f7b73999c67f66--
2.47.3
Quoted 31 lines · Binbin Zhou
Hi all:
For various reasons, I will be taking over from Haowei and continuing to push forward with this patch set. Thanks to Haowei for his efforts so far.
This patchset introduce a generic UART framework driver for Loongson family. It can be found on Loongson3 series cpus, Loongson-2K series cpus and Loongson LS7A bridge chips.
Thanks.
------
V6:
Patch-1:
- Add Conor's Acked-by tag, thanks.Patch-2:
- Add more missing #include;
- Convert mcr_invert/msr_invert from int to u8;
- Splitserial_fixup()on a preceding line;
- Add macro definition to avoid magic numbers, such asLOONGSON_QUOT_FRAC_MASK;
- Rework the relevant data structures, whereloongson_uart_ddatarepresents the Soc'sdriver_data;
- DropPORT_LOONGSONand usePORT_16550Ainstead.
-devm_platform_get_and_ioremap_resource()instead ofplatform_get_resource();
- Useuart_read_port_properties()and parse the clock attributes;
- Use switch-case instead of if-else inserial_fixup().
Quoted 2 lines
Hi all:
Gentle ping.
Any comments about this patchset ?
Quoted 49 lines
V5:
Patch-1:
- Since the Loongson UART is NS8250A-compatible, simply add ls2k* compatible to 8250.yaml.DTS{i} tested by
make dtbs_check W=1Link to V4:
https://lore.kernel.org/all/[email protected]/V4:
Patch-1:
- Rename binding name from loongson,uart.yaml toloongson,ls2k0500-uart.yaml;- Drop ls7a compatible;
- According to the manual, ls3a and ls2k uart are the same, so merge their compatible.Patch-2:
- Format code;
- Add theLOONGSON_UART_DLFmacro definition to avoid magic numbers;
- Simplify the code, merge flags and quirks, and remove structloongson_uart_config;- Use
DEFINE_SIMPLE_DEV_PM_OPS;
- Drop loongson,ls7a-uart compatible.Patch-3:
- Add ls2k* compatible string, and ns16550a as the fallback compatible.Link to V3:
https://lore.kernel.org/all/[email protected]/Binbin Zhou (3):
dt-bindings: serial: 8250: Add Loongson uart compatible
serial: 8250: Add Loongson uart driver support
LoongArch: dts: Add uart new compatible string.../devicetree/bindings/serial/8250.yaml | 14 ++ arch/loongarch/boot/dts/loongson-2k0500.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k1000.dtsi | 2 +- arch/loongarch/boot/dts/loongson-2k2000.dtsi | 2 +- drivers/tty/serial/8250/8250_loongson.c | 238 ++++++++++++++++++ drivers/tty/serial/8250/Kconfig | 10 + drivers/tty/serial/8250/Makefile | 1 + 7 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 drivers/tty/serial/8250/8250_loongson.c
Quoted 4 lines
base-commit:
0d97f2067c166eb495771fede9f7b73999c67f66--
2.47.3
--
Thanks.
Binbin
Greg Kroah-Hartman
maintainerQuoted 32 lines · Binbin Zhou
Quoted 31 lines · Binbin Zhou
Hi all:
For various reasons, I will be taking over from Haowei and continuing to push forward with this patch set. Thanks to Haowei for his efforts so far.
This patchset introduce a generic UART framework driver for Loongson family. It can be found on Loongson3 series cpus, Loongson-2K series cpus and Loongson LS7A bridge chips.
Thanks.
------
V6:
Patch-1:
- Add Conor's Acked-by tag, thanks.Patch-2:
- Add more missing #include;
- Convert mcr_invert/msr_invert from int to u8;
- Splitserial_fixup()on a preceding line;
- Add macro definition to avoid magic numbers, such asLOONGSON_QUOT_FRAC_MASK;
- Rework the relevant data structures, whereloongson_uart_ddatarepresents the Soc'sdriver_data;
- DropPORT_LOONGSONand usePORT_16550Ainstead.
-devm_platform_get_and_ioremap_resource()instead ofplatform_get_resource();
- Useuart_read_port_properties()and parse the clock attributes;
- Use switch-case instead of if-else inserial_fixup().
Quoted 7 lines
Quoted 2 lines
Hi all:
Gentle ping.
Any comments about this patchset ?
Sorry, will try to get to it next week...