[PATCH v4 10/11] drivers: PL011: add support for the ARM SBSA generic UART
Mark Langsdorf
mlangsdo at redhat.com
Wed May 13 07:32:54 PDT 2015
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.
> + 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
More information about the linux-arm-kernel
mailing list