[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