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>
This commit is contained in:
Phil Elwell
2022-10-12 13:24:51 +01:00
committed by Dom Cobley
parent 3f15d06535
commit 07c0fe0f55

View File

@@ -152,6 +152,20 @@ static const struct vendor_data vendor_sbsa = {
.fixed_options = true, .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 #ifdef CONFIG_ACPI_SPCR_TABLE
static const struct vendor_data vendor_qdt_qdf2400_e44 = { static const struct vendor_data vendor_qdt_qdf2400_e44 = {
.reg_offset = pl011_std_offsets, .reg_offset = pl011_std_offsets,
@@ -480,6 +494,12 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
"RX DMA disabled - no residue processing\n"); "RX DMA disabled - no residue processing\n");
return; 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); dmaengine_slave_config(chan, &rx_conf);
uap->dmarx.chan = chan; uap->dmarx.chan = chan;
@@ -3080,6 +3100,87 @@ static struct platform_driver arm_sbsa_uart_platform_driver = {
}, },
}; };
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[] = { static const struct amba_id pl011_ids[] = {
{ {
.id = 0x00041011, .id = 0x00041011,
@@ -3113,12 +3214,15 @@ static int __init pl011_init(void)
if (platform_driver_register(&arm_sbsa_uart_platform_driver)) if (platform_driver_register(&arm_sbsa_uart_platform_driver))
pr_warn("could not register SBSA UART platform driver\n"); 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); return amba_driver_register(&pl011_driver);
} }
static void __exit pl011_exit(void) static void __exit pl011_exit(void)
{ {
platform_driver_unregister(&arm_sbsa_uart_platform_driver); platform_driver_unregister(&arm_sbsa_uart_platform_driver);
platform_driver_unregister(&pl011_axi_platform_driver);
amba_driver_unregister(&pl011_driver); amba_driver_unregister(&pl011_driver);
} }