[PATCH v4 10/11] drivers: PL011: add support for the ARM SBSA generic UART

Graeme Gregory gg at slimlogic.co.uk
Wed May 13 08:03:44 PDT 2015



On Wed, 13 May 2015, at 03:32 PM, Mark Langsdorf wrote:
> On 05/12/2015 09:14 AM, Andre Przywara wrote:
> > The ARM Server Base System Architecture[1] document describes a
> > generic UART which is a subset of the PL011 UART.
> > It lacks DMA support, baud rate control and modem status line
> > control, among other things.
> > The idea is to move the UART initialization and setup into the
> > firmware (which does this job today already) and let the kernel just
> > use the UART for sending and receiving characters.
> >
> > We use the recent refactoring to build a new struct uart_ops
> > variable which points to some new functions avoiding access to the
> > missing registers. We reuse as much existing PL011 code as possible.
> >
> > In contrast to the PL011 the SBSA UART does not define any AMBA or
> > PrimeCell relations, so we go with a pretty generic probe function
> > which only uses platform device functions.
> > A DT binding is provided with this patch, ACPI support is added in a
> > separate one.
> >
> > Signed-off-by: Andre Przywara <andre.przywara at arm.com>
> > ---
> >   .../devicetree/bindings/serial/arm_sbsa_uart.txt   |  10 ++
> >   drivers/tty/serial/amba-pl011.c                    | 168 +++++++++++++++++++++
> >   2 files changed, 178 insertions(+)
> >   create mode 100644 Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
> >
> > diff --git a/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt b/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
> > new file mode 100644
> > index 0000000..4163e7e
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/serial/arm_sbsa_uart.txt
> > @@ -0,0 +1,10 @@
> > +* ARM SBSA defined generic UART
> > +This UART uses a subset of the PL011 registers and consequently lives
> > +in the PL011 driver. It's baudrate and other communication parameters
> > +cannot be adjusted at runtime, so it lacks a clock specifier here.
> > +
> > +Required properties:
> > +- compatible: must be "arm,sbsa-uart"
> > +- reg: exactly one register range
> > +- interrupts: exactly one interrupt specifier
> > +- current-speed: the (fixed) baud rate set by the firmware
> > diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
> > index 70e2958..cca93d9 100644
> > --- a/drivers/tty/serial/amba-pl011.c
> > +++ b/drivers/tty/serial/amba-pl011.c
> > @@ -101,6 +101,14 @@ static struct vendor_data vendor_arm = {
> >   	.get_fifosize		= get_fifosize_arm,
> >   };
> >
> > +static struct vendor_data vendor_sbsa = {
> > +	.oversampling		= false,
> > +	.dma_threshold		= false,
> > +	.cts_event_workaround	= false,
> > +	.always_enabled		= true,
> > +	.fixed_options		= true,
> > +};
> > +
> >   static unsigned int get_fifosize_st(struct amba_device *dev)
> >   {
> >   	return 64;
> > @@ -1641,6 +1649,28 @@ static int pl011_startup(struct uart_port *port)
> >   	return retval;
> >   }
> >
> > +static int sbsa_uart_startup(struct uart_port *port)
> > +{
> > +	struct uart_amba_port *uap =
> > +		container_of(port, struct uart_amba_port, port);
> > +	int retval;
> > +
> > +	retval = pl011_hwinit(port);
> > +	if (retval)
> > +		return retval;
> > +
> > +	retval = pl011_allocate_irq(uap);
> > +	if (retval)
> > +		return retval;
> > +
> > +	/* The SBSA UART does not support any modem status lines. */
> > +	uap->old_status = 0;
> > +
> > +	pl011_enable_interrupts(uap);
> > +
> > +	return 0;
> > +}
> > +
> >   static void pl011_shutdown_channel(struct uart_amba_port *uap,
> >   					unsigned int lcrh)
> >   {
> > @@ -1721,6 +1751,19 @@ static void pl011_shutdown(struct uart_port *port)
> >   		uap->port.ops->flush_buffer(port);
> >   }
> >
> > +static void sbsa_uart_shutdown(struct uart_port *port)
> > +{
> > +	struct uart_amba_port *uap =
> > +		container_of(port, struct uart_amba_port, port);
> > +
> > +	pl011_disable_interrupts(uap);
> > +
> > +	free_irq(uap->port.irq, uap);
> > +
> > +	if (uap->port.ops->flush_buffer)
> > +		uap->port.ops->flush_buffer(port);
> > +}
> > +
> >   static void
> >   pl011_setup_status_masks(struct uart_port *port, struct ktermios *termios)
> >   {
> > @@ -1872,6 +1915,24 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
> >   	spin_unlock_irqrestore(&port->lock, flags);
> >   }
> >
> > +static void
> > +sbsa_uart_set_termios(struct uart_port *port, struct ktermios *termios,
> > +		      struct ktermios *old)
> > +{
> > +	struct uart_amba_port *uap =
> > +	    container_of(port, struct uart_amba_port, port);
> > +	unsigned long flags;
> > +
> > +	if (old)
> > +		tty_termios_copy_hw(termios, old);
> 
> This code prevented login via the serial console on our test hardware.
> 
> Mark Salter suggested the following patch:
> 
> -       if (old)
> +       /*
> +        * The first call to set_termios() comes when the console is
> +        * registered via uart_add_one_port(). The serial core will
> +        * pass in a dummy old termios rather than NULL. Check to make
> +        * sure the old termios has reasonable info before copying from
> +        * it.
> +        */
> +       if (old && old->c_cflag)
>                  tty_termios_copy_hw(termios, old);
> 
> which fixed the issue.
> 

This certainly fixes the problem I was seeing with Fedora on Seattle.

Graeme

> > +	tty_termios_encode_baud_rate(termios, uap->fixed_baud, uap->fixed_baud);
> > +
> > +	spin_lock_irqsave(&port->lock, flags);
> > +	uart_update_timeout(port, CS8, uap->fixed_baud);
> > +	pl011_setup_status_masks(port, termios);
> > +	spin_unlock_irqrestore(&port->lock, flags);
> > +}
> > +
> >   static const char *pl011_type(struct uart_port *port)
> >   {
> >   	struct uart_amba_port *uap =
> > @@ -1947,6 +2008,37 @@ static struct uart_ops amba_pl011_pops = {
> >   #endif
> >   };
> >
> > +static void sbsa_uart_set_mctrl(struct uart_port *port, unsigned int mctrl)
> > +{
> > +}
> > +
> > +static unsigned int sbsa_uart_get_mctrl(struct uart_port *port)
> > +{
> > +	return 0;
> > +}
> > +
> > +static const struct uart_ops sbsa_uart_pops = {
> > +	.tx_empty	= pl011_tx_empty,
> > +	.set_mctrl	= sbsa_uart_set_mctrl,
> > +	.get_mctrl	= sbsa_uart_get_mctrl,
> > +	.stop_tx	= pl011_stop_tx,
> > +	.start_tx	= pl011_start_tx,
> > +	.stop_rx	= pl011_stop_rx,
> > +	.startup	= sbsa_uart_startup,
> > +	.shutdown	= sbsa_uart_shutdown,
> > +	.set_termios	= sbsa_uart_set_termios,
> > +	.type		= pl011_type,
> > +	.release_port	= pl011_release_port,
> > +	.request_port	= pl011_request_port,
> > +	.config_port	= pl011_config_port,
> > +	.verify_port	= pl011_verify_port,
> > +#ifdef CONFIG_CONSOLE_POLL
> > +	.poll_init     = pl011_hwinit,
> > +	.poll_get_char = pl011_get_poll_char,
> > +	.poll_put_char = pl011_put_poll_char,
> > +#endif
> > +};
> > +
> >   static struct uart_amba_port *amba_ports[UART_NR];
> >
> >   #ifdef CONFIG_SERIAL_AMBA_PL011_CONSOLE
> > @@ -2327,6 +2419,79 @@ static int pl011_resume(struct device *dev)
> >
> >   static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume);
> >
> > +static int sbsa_uart_probe(struct platform_device *pdev)
> > +{
> > +	struct uart_amba_port *uap;
> > +	struct resource *r;
> > +	int portnr, ret;
> > +	int baudrate;
> > +
> > +	/*
> > +	 * Check the mandatory baud rate parameter in the DT node early
> > +	 * so that we can easily exit with the error.
> > +	 */
> > +	if (pdev->dev.of_node) {
> > +		struct device_node *np = pdev->dev.of_node;
> > +
> > +		ret = of_property_read_u32(np, "current-speed", &baudrate);
> > +		if (ret)
> > +			return ret;
> > +	} else {
> > +		baudrate = 115200;
> > +	}
> > +
> > +	portnr = pl011_find_free_port();
> > +	if (portnr < 0)
> > +		return portnr;
> > +
> > +	uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port),
> > +			   GFP_KERNEL);
> > +	if (uap == NULL)
> > +		return -ENOMEM;
> > +
> > +	uap->vendor	= &vendor_sbsa;
> > +	uap->fifosize	= 32;
> > +	uap->port.irq	= platform_get_irq(pdev, 0);
> > +	uap->port.ops	= &sbsa_uart_pops;
> > +	uap->fixed_baud = baudrate;
> > +
> > +	snprintf(uap->type, sizeof(uap->type), "SBSA");
> > +
> > +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +
> > +	ret = pl011_setup_port(&pdev->dev, uap, r, portnr);
> > +	if (ret)
> > +		return ret;
> > +
> > +	platform_set_drvdata(pdev, uap);
> > +
> > +	return pl011_register_port(uap);
> > +}
> > +
> > +static int sbsa_uart_remove(struct platform_device *pdev)
> > +{
> > +	struct uart_amba_port *uap = platform_get_drvdata(pdev);
> > +
> > +	uart_remove_one_port(&amba_reg, &uap->port);
> > +	pl011_unregister_port(uap);
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id sbsa_uart_of_match[] = {
> > +	{ .compatible = "arm,sbsa-uart", },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(of, sbsa_uart_of_match);
> > +
> > +static struct platform_driver arm_sbsa_uart_platform_driver = {
> > +	.probe		= sbsa_uart_probe,
> > +	.remove		= sbsa_uart_remove,
> > +	.driver	= {
> > +		.name	= "sbsa-uart",
> > +		.of_match_table = of_match_ptr(sbsa_uart_of_match),
> > +	},
> > +};
> > +
> >   static struct amba_id pl011_ids[] = {
> >   	{
> >   		.id	= 0x00041011,
> > @@ -2357,11 +2522,14 @@ static int __init pl011_init(void)
> >   {
> >   	printk(KERN_INFO "Serial: AMBA PL011 UART driver\n");
> >
> > +	if (platform_driver_register(&arm_sbsa_uart_platform_driver))
> > +		pr_warn("could not register SBSA UART platform driver\n");
> >   	return amba_driver_register(&pl011_driver);
> >   }
> >
> >   static void __exit pl011_exit(void)
> >   {
> > +	platform_driver_unregister(&arm_sbsa_uart_platform_driver);
> >   	amba_driver_unregister(&pl011_driver);
> >   }
> 
> --Mark Langsdorf
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list