From mboxrd@z Thu Jan 1 00:00:00 1970 From: gg@slimlogic.co.uk (Graeme Gregory) Date: Wed, 13 May 2015 16:03:44 +0100 Subject: [PATCH v4 10/11] drivers: PL011: add support for the ARM SBSA generic UART In-Reply-To: <55536096.4020008@redhat.com> References: <1431440095-5146-1-git-send-email-andre.przywara@arm.com> <1431440095-5146-11-git-send-email-andre.przywara@arm.com> <55536096.4020008@redhat.com> Message-ID: <1431529424.690869.267807025.1EB03568@webmail.messagingengine.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org 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 > > --- > > .../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