From 9f2a59cadaca55934a724c854eb1baab3e363abc Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Wed, 12 Oct 2022 13:24:51 +0100 Subject: [PATCH] serial: pl011: rp1 uart support Signed-off-by: Phil Elwell ARM: pl011: Add rs485 to the RP1 support pl011_axi_probe, added for RP1 support, lacks the rs485 additions that appeared during its development. Signed-off-by: Phil Elwell tty/serial: pl011: restrict RX burst FIFO threshold If the associated DMA controller has lower burst length support than the level the FIFO is set to, then bytes will be left in the RX FIFO at the end of a DMA block - requiring a round-trip through the timeout interrupt handler rather than an end-of-block DMA interrupt. Signed-off-by: Jonathan Bell tty/serial: pl011: Also unregister pl011_axi_platform_driver See: https://github.com/raspberrypi/linux/issues/6379 Signed-off-by: Dom Cobley --- drivers/tty/serial/amba-pl011.c | 104 ++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -152,6 +152,20 @@ static const struct vendor_data vendor_s .fixed_options = true, }; +static struct vendor_data vendor_arm_axi = { + .reg_offset = pl011_std_offsets, + .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8, + .fr_busy = UART01x_FR_BUSY, + .fr_dsr = UART01x_FR_DSR, + .fr_cts = UART01x_FR_CTS, + .fr_ri = UART011_FR_RI, + .oversampling = false, + .dma_threshold = false, + .cts_event_workaround = false, + .always_enabled = false, + .fixed_options = false, +}; + #ifdef CONFIG_ACPI_SPCR_TABLE static const struct vendor_data vendor_qdt_qdf2400_e44 = { .reg_offset = pl011_std_offsets, @@ -470,6 +484,12 @@ static void pl011_dma_probe(struct uart_ "RX DMA disabled - no residue processing\n"); return; } + /* + * DMA controllers with smaller burst capabilities than 1/4 + * the FIFO depth will leave more bytes than expected in the + * RX FIFO if mismatched. + */ + rx_conf.src_maxburst = min(caps.max_burst, rx_conf.src_maxburst); } dmaengine_slave_config(chan, &rx_conf); uap->dmarx.chan = chan; @@ -2964,6 +2984,87 @@ static struct platform_driver arm_sbsa_u }, }; +static int pl011_axi_probe(struct platform_device *pdev) +{ + struct uart_amba_port *uap; + struct vendor_data *vendor = &vendor_arm_axi; + struct resource *r; + unsigned int periphid; + int portnr, ret, irq; + + portnr = pl011_find_free_port(); + if (portnr < 0) + return portnr; + + uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port), + GFP_KERNEL); + if (!uap) + return -ENOMEM; + + uap->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(uap->clk)) + return PTR_ERR(uap->clk); + + if (of_property_read_bool(pdev->dev.of_node, "cts-event-workaround")) { + vendor->cts_event_workaround = true; + dev_info(&pdev->dev, "cts_event_workaround enabled\n"); + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + periphid = 0x00241011; /* A safe default */ + of_property_read_u32(pdev->dev.of_node, "arm,primecell-periphid", + &periphid); + + uap->reg_offset = vendor->reg_offset; + uap->vendor = vendor; + uap->fifosize = (AMBA_REV_BITS(periphid) < 3) ? 16 : 32; + uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM; + uap->port.irq = irq; + uap->port.ops = &amba_pl011_pops; + uap->port.rs485_config = pl011_rs485_config; + uap->port.rs485_supported = pl011_rs485_supported; + + snprintf(uap->type, sizeof(uap->type), "PL011 AXI"); + + 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 void pl011_axi_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); +} + +static const struct of_device_id pl011_axi_of_match[] = { + { .compatible = "arm,pl011-axi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, pl011_axi_of_match); + +static struct platform_driver pl011_axi_platform_driver = { + .probe = pl011_axi_probe, + .remove = pl011_axi_remove, + .driver = { + .name = "pl011-axi", + .pm = &pl011_dev_pm_ops, + .of_match_table = of_match_ptr(pl011_axi_of_match), + .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011), + }, +}; + static const struct amba_id pl011_ids[] = { { .id = 0x00041011, @@ -2997,12 +3098,15 @@ static int __init pl011_init(void) if (platform_driver_register(&arm_sbsa_uart_platform_driver)) pr_warn("could not register SBSA UART platform driver\n"); + if (platform_driver_register(&pl011_axi_platform_driver)) + pr_warn("could not register PL011 AXI platform driver\n"); return amba_driver_register(&pl011_driver); } static void __exit pl011_exit(void) { platform_driver_unregister(&arm_sbsa_uart_platform_driver); + platform_driver_unregister(&pl011_axi_platform_driver); amba_driver_unregister(&pl011_driver); }