diff options
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
| -rw-r--r-- | drivers/tty/serial/amba-pl011.c | 115 |
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); } |
