From 9f2a59cadaca55934a724c854eb1baab3e363abc Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.com>
Date: Wed, 12 Oct 2022 13:24:51 +0100
Subject: [PATCH] serial: pl011: rp1 uart support

Signed-off-by: Phil Elwell <phil@raspberrypi.com>

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 <phil@raspberrypi.com>

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 <jonathan@raspberrypi.com>

tty/serial: pl011: Also unregister pl011_axi_platform_driver

See: https://github.com/raspberrypi/linux/issues/6379
Signed-off-by: Dom Cobley <popcornmix@gmail.com>
---
 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);
 }
 
