aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/serial/amba-pl011.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r--drivers/tty/serial/amba-pl011.c115
1 files changed, 115 insertions, 0 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 7f17d288c807..711aace90483 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -152,6 +152,20 @@ static const struct vendor_data vendor_sbsa = {
.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,
@@ -461,6 +475,7 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
.src_addr = uap->port.mapbase +
pl011_reg_to_offset(uap, REG_DR),
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
+ .dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
.direction = DMA_DEV_TO_MEM,
.src_maxburst = uap->fifosize >> 2,
.device_fc = false,
@@ -480,6 +495,12 @@ static void pl011_dma_probe(struct uart_amba_port *uap)
"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;
@@ -1495,6 +1516,7 @@ static bool pl011_tx_char(struct uart_amba_port *uap, unsigned char c,
return false; /* unable to transmit character */
pl011_write(c, uap, REG_DR);
+ mb();
uap->port.icount.tx++;
return true;
@@ -1527,6 +1549,10 @@ static bool pl011_tx_chars(struct uart_amba_port *uap, bool from_irq)
if (likely(from_irq) && count-- == 0)
break;
+ if (likely(from_irq) && count == 0 &&
+ pl011_read(uap, REG_FR) & UART01x_FR_TXFF)
+ break;
+
if (!kfifo_peek(&tport->xmit_fifo, &c))
break;
@@ -2894,6 +2920,11 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
if (IS_ERR(uap->clk))
return PTR_ERR(uap->clk);
+ if (of_property_read_bool(dev->dev.of_node, "cts-event-workaround")) {
+ vendor->cts_event_workaround = true;
+ dev_info(&dev->dev, "cts_event_workaround enabled\n");
+ }
+
uap->reg_offset = vendor->reg_offset;
uap->vendor = vendor;
uap->fifosize = vendor->get_fifosize(dev);
@@ -3070,6 +3101,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[] = {
{
.id = 0x00041011,
@@ -3103,12 +3215,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);
}