Message ID | 20250501003113.1609342-3-jm@ti.com |
---|---|
State | New |
Headers | show |
Series | Introduce PRU UART driver | expand |
On Wed, Apr 30, 2025 at 07:31:13PM -0500, Judith Mendez wrote: > From: Bin Liu <b-liu@ti.com> > > This adds a new serial 8250 driver that supports the UART in PRUSS > module. > > The PRUSS has a UART sub-module which is based on the industry standard > TL16C550 UART controller, which has 16-bytes FIFO and supports 16x and > 13x over samplings. > Signed-off-by: Bin Liu <b-liu@ti.com> > Signed-off-by: Judith Mendez <jm@ti.com> Are you just a committer and not a co-developer of this code? ... > +/* > + * Serial Port driver for PRUSS UART on TI platforms > + * > + * Copyright (C) 2020-2021 by Texas Instruments Incorporated - http://www.ti.com/ My calendar shows 2025... > + * Author: Bin Liu <b-liu@ti.com> > + */ ... The list of the inclusions is semi-random. Please, follow the IWYU principle. > +#include <linux/clk.h> > +#include <linux/module.h> > +#include <linux/serial_reg.h> > +#include <linux/serial_core.h> > +#include <linux/of_irq.h> > +#include <linux/of_address.h> > +#include <linux/of_platform.h> Please, no of*.h in a new code. > +#include <linux/remoteproc.h> Keep them ordered as well. + blank line here. > +#include "8250.h" ... > +#define DEFAULT_CLK_SPEED 192000000 Units as a suffix? HZ_PER_MHZ? You can also fix 8250_omap in the same way. ... > +static inline void uart_writel(struct uart_port *p, u32 offset, int value) > +{ > + writel(value, p->membase + (offset << p->regshift)); > +} Why? Or how does it differ from the ones that serial core provides? ... > +static int pruss8250_startup(struct uart_port *port) > +{ > + int ret; > + > + uart_writel(port, PRUSS_UART_PEREMU_MGMT, 0); > + > + ret = serial8250_do_startup(port); > + if (!ret) Please, use usual pattern, check for errors first if (ret) return ret; ... return 0; > + uart_writel(port, PRUSS_UART_PEREMU_MGMT, PRUSS_UART_TX_EN | > + PRUSS_UART_RX_EN | > + PRUSS_UART_FREE_RUN); > + return ret; > +} ... > +static unsigned int pruss8250_get_divisor(struct uart_port *port, > + unsigned int baud, > + unsigned int *frac) > +{ > + unsigned int uartclk = port->uartclk; > + unsigned int div_13, div_16; > + unsigned int abs_d13, abs_d16; > + u16 quot; > + /* Old custom speed handling */ > + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { > + quot = port->custom_divisor & UART_DIV_MAX; > + if (port->custom_divisor & (1 << 16)) > + *frac = PRUSS_UART_MDR_13X_MODE; > + else > + *frac = PRUSS_UART_MDR_16X_MODE; > + > + return quot; > + } Why?! Please, try to avoid adding more drivers with this ugly hack. > + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); > + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); > + div_13 = div_13 ? : 1; > + div_16 = div_16 ? : 1; > + > + abs_d13 = abs(baud - uartclk / 13 / div_13); > + abs_d16 = abs(baud - uartclk / 16 / div_16); > + > + if (abs_d13 >= abs_d16) { > + *frac = PRUSS_UART_MDR_16X_MODE; > + quot = div_16; > + } else { > + *frac = PRUSS_UART_MDR_13X_MODE; > + quot = div_13; > + } > + > + return quot; Are you sure it doesn't repeat existing things from other driver(s)? > +} ... > +static int pruss8250_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; You don't need this. > + struct uart_8250_port port8250; > + struct uart_port *up = &port8250.port; > + struct pruss8250_info *info; > + struct resource resource; > + unsigned int port_type; > + struct clk *clk; > + int ret; > + > + port_type = (unsigned long)of_device_get_match_data(&pdev->dev); > + if (port_type == PORT_UNKNOWN) > + return -EINVAL; > + > + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); > + if (!info) > + return -ENOMEM; > + > + memset(&port8250, 0, sizeof(port8250)); > + > + ret = of_address_to_resource(np, 0, &resource); Yeah, no modifications from 2021? > + if (ret) { > + dev_err(&pdev->dev, "invalid address\n"); > + return ret; > + } > + ret = of_alias_get_id(np, "serial"); > + if (ret > 0) > + up->line = ret; Use uart_read_port_properties() instead of this and other related code. > + clk = devm_clk_get(&pdev->dev, NULL); > + if (IS_ERR(clk)) { > + if (PTR_ERR(clk) == -EPROBE_DEFER) > + return -EPROBE_DEFER; We have other errors which are effectively being ignored here, why? > + up->uartclk = DEFAULT_CLK_SPEED; > + } else { > + up->uartclk = clk_get_rate(clk); > + devm_clk_put(&pdev->dev, clk); Why? Maybe you should not to try devm_ to begin with? > + } > + > + up->dev = &pdev->dev; > + up->mapbase = resource.start; > + up->mapsize = resource_size(&resource); > + up->type = port_type; > + up->iotype = UPIO_MEM; > + up->regshift = 2; > + up->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | > + UPF_FIXED_TYPE | UPF_IOREMAP; > + up->irqflags |= IRQF_SHARED; > + up->startup = pruss8250_startup; > + up->rs485_config = serial8250_em485_config; > + up->get_divisor = pruss8250_get_divisor; > + up->set_divisor = pruss8250_set_divisor; > + > + ret = of_irq_get(np, 0); > + if (ret < 0) { > + if (ret != -EPROBE_DEFER) > + dev_err(&pdev->dev, "missing irq\n"); > + return ret; > + } A lot of this will be simplified by using the above mentioned API. > + up->irq = ret; > + spin_lock_init(&port8250.port.lock); > + port8250.capabilities = UART_CAP_FIFO | UART_CAP_AFE; > + > + ret = serial8250_register_8250_port(&port8250); > + if (ret < 0) > + goto err_dispose; > + > + info->type = port_type; > + info->line = ret; > + platform_set_drvdata(pdev, info); > + > + return 0; > +err_dispose: > + irq_dispose_mapping(port8250.port.irq); Why this is needed? > + return ret; > +} ... > +static const struct of_device_id pruss8250_table[] = { > + { .compatible = "ti,pruss-uart", .data = (void *)PORT_16550A, }, Inner comma is redundant. > + { /* end of list */ }, No trailing comma in the terminator. > +}; ... > +config SERIAL_8250_PRUSS > + tristate "TI PRU-ICSS UART support" > + depends on SERIAL_8250 > + depends on PRU_REMOTEPROC && TI_PRUSS_INTC No COMPILE_TEST? > + help > + This driver is to support the UART module in PRU-ICSS which is > + available in some TI platforms. > + Say 'Y' here if you wish to use PRU-ICSS UART. > + Otherwise, say 'N'. If m, how would the module be called? See many new driver examples in Kconfigs (in particular IIO follows this pattern).
Hi Andrew, On 5/1/25 11:28 AM, Andrew Davis wrote: > On 4/30/25 7:31 PM, Judith Mendez wrote: >> From: Bin Liu <b-liu@ti.com> >> >> This adds a new serial 8250 driver that supports the UART in PRUSS >> module. >> >> The PRUSS has a UART sub-module which is based on the industry standard >> TL16C550 UART controller, which has 16-bytes FIFO and supports 16x and >> 13x over samplings. >> >> Signed-off-by: Bin Liu <b-liu@ti.com> >> Signed-off-by: Judith Mendez <jm@ti.com> >> --- >> drivers/tty/serial/8250/8250_pruss.c | 213 +++++++++++++++++++++++++++ >> drivers/tty/serial/8250/Kconfig | 10 ++ >> drivers/tty/serial/8250/Makefile | 1 + >> 3 files changed, 224 insertions(+) >> create mode 100644 drivers/tty/serial/8250/8250_pruss.c >> >> diff --git a/drivers/tty/serial/8250/8250_pruss.c >> b/drivers/tty/serial/8250/8250_pruss.c >> new file mode 100644 >> index 000000000000..2943bf7d6645 >> --- /dev/null >> +++ b/drivers/tty/serial/8250/8250_pruss.c >> @@ -0,0 +1,213 @@ >> +// SPDX-License-Identifier: GPL-2.0-only >> +/* >> + * Serial Port driver for PRUSS UART on TI platforms >> + * >> + * Copyright (C) 2020-2021 by Texas Instruments Incorporated - >> http://www.ti.com/ >> + * Author: Bin Liu <b-liu@ti.com> >> + */ >> +#include <linux/clk.h> >> +#include <linux/module.h> >> +#include <linux/serial_reg.h> >> +#include <linux/serial_core.h> >> +#include <linux/of_irq.h> >> +#include <linux/of_address.h> >> +#include <linux/of_platform.h> >> +#include <linux/remoteproc.h> >> +#include "8250.h" >> + >> +#define DEFAULT_CLK_SPEED 192000000 >> + >> +/* extra registers */ >> +#define PRUSS_UART_PEREMU_MGMT 12 >> +#define PRUSS_UART_TX_EN BIT(14) >> +#define PRUSS_UART_RX_EN BIT(13) >> +#define PRUSS_UART_FREE_RUN BIT(0) >> + >> +#define PRUSS_UART_MDR 13 >> +#define PRUSS_UART_MDR_OSM_SEL_MASK BIT(0) >> +#define PRUSS_UART_MDR_16X_MODE 0 >> +#define PRUSS_UART_MDR_13X_MODE 1 >> + >> +struct pruss8250_info { >> + int type; > > You never use type, why store it here? removed > >> + int line; >> +}; >> + >> +static inline void uart_writel(struct uart_port *p, u32 offset, int >> value) >> +{ >> + writel(value, p->membase + (offset << p->regshift)); >> +} >> + >> +static int pruss8250_startup(struct uart_port *port) >> +{ >> + int ret; >> + >> + uart_writel(port, PRUSS_UART_PEREMU_MGMT, 0); >> + >> + ret = serial8250_do_startup(port); >> + if (!ret) >> + uart_writel(port, PRUSS_UART_PEREMU_MGMT, PRUSS_UART_TX_EN | >> + PRUSS_UART_RX_EN | >> + PRUSS_UART_FREE_RUN); >> + return ret; >> +} >> + >> +static unsigned int pruss8250_get_divisor(struct uart_port *port, >> + unsigned int baud, >> + unsigned int *frac) >> +{ >> + unsigned int uartclk = port->uartclk; >> + unsigned int div_13, div_16; >> + unsigned int abs_d13, abs_d16; >> + u16 quot; >> + >> + /* Old custom speed handling */ >> + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { >> + quot = port->custom_divisor & UART_DIV_MAX; >> + if (port->custom_divisor & (1 << 16)) >> + *frac = PRUSS_UART_MDR_13X_MODE; >> + else >> + *frac = PRUSS_UART_MDR_16X_MODE; >> + >> + return quot; >> + } >> + >> + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); >> + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); >> + div_13 = div_13 ? : 1; >> + div_16 = div_16 ? : 1; >> + >> + abs_d13 = abs(baud - uartclk / 13 / div_13); >> + abs_d16 = abs(baud - uartclk / 16 / div_16); >> + >> + if (abs_d13 >= abs_d16) { >> + *frac = PRUSS_UART_MDR_16X_MODE; >> + quot = div_16; >> + } else { >> + *frac = PRUSS_UART_MDR_13X_MODE; >> + quot = div_13; >> + } >> + >> + return quot; >> +} >> + >> +static void pruss8250_set_divisor(struct uart_port *port, unsigned >> int baud, >> + unsigned int quot, unsigned int quot_frac) >> +{ >> + serial8250_do_set_divisor(port, baud, quot); >> + /* >> + * quot_frac holds the MDR over-sampling mode >> + * which is set in pruss8250_get_divisor() >> + */ >> + quot_frac &= PRUSS_UART_MDR_OSM_SEL_MASK; >> + serial_port_out(port, PRUSS_UART_MDR, quot_frac); >> +} >> + >> +static int pruss8250_probe(struct platform_device *pdev) >> +{ >> + struct device_node *np = pdev->dev.of_node; >> + struct uart_8250_port port8250; >> + struct uart_port *up = &port8250.port; >> + struct pruss8250_info *info; >> + struct resource resource; >> + unsigned int port_type; >> + struct clk *clk; >> + int ret; >> + >> + port_type = (unsigned long)of_device_get_match_data(&pdev->dev); > > Will the port type ever not be PORT_16550A? So far each UART module is has been the same, I think it might be safe to hardcode: port->type = PORT_16550A; > >> + if (port_type == PORT_UNKNOWN) >> + return -EINVAL; >> + >> + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); >> + if (!info) >> + return -ENOMEM; >> + >> + memset(&port8250, 0, sizeof(port8250)); >> + >> + ret = of_address_to_resource(np, 0, &resource); > > platform_get_resource() Will add > >> + if (ret) { >> + dev_err(&pdev->dev, "invalid address\n"); >> + return ret; >> + } >> + >> + ret = of_alias_get_id(np, "serial"); > > Can you make use of the uart_read_port_properties() helper here? yes we can, thanks for reviewing. ~ Judith > > Andrew > >> + if (ret > 0) >> + up->line = ret; >> + >> + clk = devm_clk_get(&pdev->dev, NULL); >> + if (IS_ERR(clk)) { >> + if (PTR_ERR(clk) == -EPROBE_DEFER) >> + return -EPROBE_DEFER; >> + up->uartclk = DEFAULT_CLK_SPEED; >> + } else { >> + up->uartclk = clk_get_rate(clk); >> + devm_clk_put(&pdev->dev, clk); >> + } >> + >> + up->dev = &pdev->dev; >> + up->mapbase = resource.start; >> + up->mapsize = resource_size(&resource); >> + up->type = port_type; >> + up->iotype = UPIO_MEM; >> + up->regshift = 2; >> + up->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | >> + UPF_FIXED_TYPE | UPF_IOREMAP; >> + up->irqflags |= IRQF_SHARED; >> + up->startup = pruss8250_startup; >> + up->rs485_config = serial8250_em485_config; >> + up->get_divisor = pruss8250_get_divisor; >> + up->set_divisor = pruss8250_set_divisor; >> + >> + ret = of_irq_get(np, 0); >> + if (ret < 0) { >> + if (ret != -EPROBE_DEFER) >> + dev_err(&pdev->dev, "missing irq\n"); >> + return ret; >> + } >> + >> + up->irq = ret; >> + spin_lock_init(&port8250.port.lock); >> + port8250.capabilities = UART_CAP_FIFO | UART_CAP_AFE; >> + >> + ret = serial8250_register_8250_port(&port8250); >> + if (ret < 0) >> + goto err_dispose; >> + >> + info->type = port_type; >> + info->line = ret; >> + platform_set_drvdata(pdev, info); >> + >> + return 0; >> + >> +err_dispose: >> + irq_dispose_mapping(port8250.port.irq); >> + return ret; >> +} >> + >> +static void pruss8250_remove(struct platform_device *pdev) >> +{ >> + struct pruss8250_info *info = platform_get_drvdata(pdev); >> + >> + serial8250_unregister_port(info->line); >> +} >> + >> +static const struct of_device_id pruss8250_table[] = { >> + { .compatible = "ti,pruss-uart", .data = (void *)PORT_16550A, }, >> + { /* end of list */ }, >> +}; >> +MODULE_DEVICE_TABLE(of, pruss8250_table); >> + >> +static struct platform_driver pruss8250_driver = { >> + .driver = { >> + .name = "pruss8250", >> + .of_match_table = pruss8250_table, >> + }, >> + .probe = pruss8250_probe, >> + .remove = pruss8250_remove, >> +}; >> + >> +module_platform_driver(pruss8250_driver); >> + >> +MODULE_AUTHOR("Bin Liu <b-liu@ti.com"); >> +MODULE_LICENSE("GPL v2"); >> +MODULE_DESCRIPTION("Serial Port driver for PRUSS UART on TI platforms"); >> diff --git a/drivers/tty/serial/8250/Kconfig >> b/drivers/tty/serial/8250/Kconfig >> index f64ef0819cd4..cd4346609c55 100644 >> --- a/drivers/tty/serial/8250/Kconfig >> +++ b/drivers/tty/serial/8250/Kconfig >> @@ -582,6 +582,16 @@ config SERIAL_8250_NI >> To compile this driver as a module, choose M here: the module >> will be called 8250_ni. >> +config SERIAL_8250_PRUSS >> + tristate "TI PRU-ICSS UART support" >> + depends on SERIAL_8250 >> + depends on PRU_REMOTEPROC && TI_PRUSS_INTC >> + help >> + This driver is to support the UART module in PRU-ICSS which is >> + available in some TI platforms. >> + Say 'Y' here if you wish to use PRU-ICSS UART. >> + Otherwise, say 'N'. >> + >> config SERIAL_OF_PLATFORM >> tristate "Devicetree based probing for 8250 ports" >> depends on SERIAL_8250 && OF >> diff --git a/drivers/tty/serial/8250/Makefile >> b/drivers/tty/serial/8250/Makefile >> index b04eeda03b23..3132b4f40a34 100644 >> --- a/drivers/tty/serial/8250/Makefile >> +++ b/drivers/tty/serial/8250/Makefile >> @@ -47,6 +47,7 @@ obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o >> obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o >> obj-$(CONFIG_SERIAL_8250_PCI1XXXX) += 8250_pci1xxxx.o >> obj-$(CONFIG_SERIAL_8250_PERICOM) += 8250_pericom.o >> +obj-$(CONFIG_SERIAL_8250_PRUSS) += 8250_pruss.o >> obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o >> obj-$(CONFIG_SERIAL_8250_RT288X) += 8250_rt288x.o >> obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
diff --git a/drivers/tty/serial/8250/8250_pruss.c b/drivers/tty/serial/8250/8250_pruss.c new file mode 100644 index 000000000000..2943bf7d6645 --- /dev/null +++ b/drivers/tty/serial/8250/8250_pruss.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Serial Port driver for PRUSS UART on TI platforms + * + * Copyright (C) 2020-2021 by Texas Instruments Incorporated - http://www.ti.com/ + * Author: Bin Liu <b-liu@ti.com> + */ +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/serial_reg.h> +#include <linux/serial_core.h> +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/remoteproc.h> +#include "8250.h" + +#define DEFAULT_CLK_SPEED 192000000 + +/* extra registers */ +#define PRUSS_UART_PEREMU_MGMT 12 +#define PRUSS_UART_TX_EN BIT(14) +#define PRUSS_UART_RX_EN BIT(13) +#define PRUSS_UART_FREE_RUN BIT(0) + +#define PRUSS_UART_MDR 13 +#define PRUSS_UART_MDR_OSM_SEL_MASK BIT(0) +#define PRUSS_UART_MDR_16X_MODE 0 +#define PRUSS_UART_MDR_13X_MODE 1 + +struct pruss8250_info { + int type; + int line; +}; + +static inline void uart_writel(struct uart_port *p, u32 offset, int value) +{ + writel(value, p->membase + (offset << p->regshift)); +} + +static int pruss8250_startup(struct uart_port *port) +{ + int ret; + + uart_writel(port, PRUSS_UART_PEREMU_MGMT, 0); + + ret = serial8250_do_startup(port); + if (!ret) + uart_writel(port, PRUSS_UART_PEREMU_MGMT, PRUSS_UART_TX_EN | + PRUSS_UART_RX_EN | + PRUSS_UART_FREE_RUN); + return ret; +} + +static unsigned int pruss8250_get_divisor(struct uart_port *port, + unsigned int baud, + unsigned int *frac) +{ + unsigned int uartclk = port->uartclk; + unsigned int div_13, div_16; + unsigned int abs_d13, abs_d16; + u16 quot; + + /* Old custom speed handling */ + if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) { + quot = port->custom_divisor & UART_DIV_MAX; + if (port->custom_divisor & (1 << 16)) + *frac = PRUSS_UART_MDR_13X_MODE; + else + *frac = PRUSS_UART_MDR_16X_MODE; + + return quot; + } + + div_13 = DIV_ROUND_CLOSEST(uartclk, 13 * baud); + div_16 = DIV_ROUND_CLOSEST(uartclk, 16 * baud); + div_13 = div_13 ? : 1; + div_16 = div_16 ? : 1; + + abs_d13 = abs(baud - uartclk / 13 / div_13); + abs_d16 = abs(baud - uartclk / 16 / div_16); + + if (abs_d13 >= abs_d16) { + *frac = PRUSS_UART_MDR_16X_MODE; + quot = div_16; + } else { + *frac = PRUSS_UART_MDR_13X_MODE; + quot = div_13; + } + + return quot; +} + +static void pruss8250_set_divisor(struct uart_port *port, unsigned int baud, + unsigned int quot, unsigned int quot_frac) +{ + serial8250_do_set_divisor(port, baud, quot); + /* + * quot_frac holds the MDR over-sampling mode + * which is set in pruss8250_get_divisor() + */ + quot_frac &= PRUSS_UART_MDR_OSM_SEL_MASK; + serial_port_out(port, PRUSS_UART_MDR, quot_frac); +} + +static int pruss8250_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct uart_8250_port port8250; + struct uart_port *up = &port8250.port; + struct pruss8250_info *info; + struct resource resource; + unsigned int port_type; + struct clk *clk; + int ret; + + port_type = (unsigned long)of_device_get_match_data(&pdev->dev); + if (port_type == PORT_UNKNOWN) + return -EINVAL; + + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + memset(&port8250, 0, sizeof(port8250)); + + ret = of_address_to_resource(np, 0, &resource); + if (ret) { + dev_err(&pdev->dev, "invalid address\n"); + return ret; + } + + ret = of_alias_get_id(np, "serial"); + if (ret > 0) + up->line = ret; + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return -EPROBE_DEFER; + up->uartclk = DEFAULT_CLK_SPEED; + } else { + up->uartclk = clk_get_rate(clk); + devm_clk_put(&pdev->dev, clk); + } + + up->dev = &pdev->dev; + up->mapbase = resource.start; + up->mapsize = resource_size(&resource); + up->type = port_type; + up->iotype = UPIO_MEM; + up->regshift = 2; + up->flags = UPF_SHARE_IRQ | UPF_BOOT_AUTOCONF | UPF_FIXED_PORT | + UPF_FIXED_TYPE | UPF_IOREMAP; + up->irqflags |= IRQF_SHARED; + up->startup = pruss8250_startup; + up->rs485_config = serial8250_em485_config; + up->get_divisor = pruss8250_get_divisor; + up->set_divisor = pruss8250_set_divisor; + + ret = of_irq_get(np, 0); + if (ret < 0) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "missing irq\n"); + return ret; + } + + up->irq = ret; + spin_lock_init(&port8250.port.lock); + port8250.capabilities = UART_CAP_FIFO | UART_CAP_AFE; + + ret = serial8250_register_8250_port(&port8250); + if (ret < 0) + goto err_dispose; + + info->type = port_type; + info->line = ret; + platform_set_drvdata(pdev, info); + + return 0; + +err_dispose: + irq_dispose_mapping(port8250.port.irq); + return ret; +} + +static void pruss8250_remove(struct platform_device *pdev) +{ + struct pruss8250_info *info = platform_get_drvdata(pdev); + + serial8250_unregister_port(info->line); +} + +static const struct of_device_id pruss8250_table[] = { + { .compatible = "ti,pruss-uart", .data = (void *)PORT_16550A, }, + { /* end of list */ }, +}; +MODULE_DEVICE_TABLE(of, pruss8250_table); + +static struct platform_driver pruss8250_driver = { + .driver = { + .name = "pruss8250", + .of_match_table = pruss8250_table, + }, + .probe = pruss8250_probe, + .remove = pruss8250_remove, +}; + +module_platform_driver(pruss8250_driver); + +MODULE_AUTHOR("Bin Liu <b-liu@ti.com"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Serial Port driver for PRUSS UART on TI platforms"); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index f64ef0819cd4..cd4346609c55 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -582,6 +582,16 @@ config SERIAL_8250_NI To compile this driver as a module, choose M here: the module will be called 8250_ni. +config SERIAL_8250_PRUSS + tristate "TI PRU-ICSS UART support" + depends on SERIAL_8250 + depends on PRU_REMOTEPROC && TI_PRUSS_INTC + help + This driver is to support the UART module in PRU-ICSS which is + available in some TI platforms. + Say 'Y' here if you wish to use PRU-ICSS UART. + Otherwise, say 'N'. + config SERIAL_OF_PLATFORM tristate "Devicetree based probing for 8250 ports" depends on SERIAL_8250 && OF diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index b04eeda03b23..3132b4f40a34 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -47,6 +47,7 @@ obj-$(CONFIG_SERIAL_8250_PARISC) += 8250_parisc.o obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o obj-$(CONFIG_SERIAL_8250_PCI1XXXX) += 8250_pci1xxxx.o obj-$(CONFIG_SERIAL_8250_PERICOM) += 8250_pericom.o +obj-$(CONFIG_SERIAL_8250_PRUSS) += 8250_pruss.o obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o obj-$(CONFIG_SERIAL_8250_RT288X) += 8250_rt288x.o obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o