diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 1d1ba3efadc0..cf5d50ab45f0 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +115,8 @@ struct vendor_data { bool cts_event_workaround; bool always_enabled; bool fixed_options; + bool enable_car; + bool dma_workaround; unsigned int (*get_fifosize)(struct amba_device *dev); }; @@ -123,6 +126,23 @@ static unsigned int get_fifosize_arm(struct amba_device *dev) return amba_rev(dev) < 3 ? 16 : 32; } +static struct vendor_data vendor_nvidia = { + .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, + .enable_car = true, + .dma_workaround = true, + .get_fifosize = get_fifosize_arm, +}; + static struct vendor_data vendor_arm = { .reg_offset = pl011_std_offsets, .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8, @@ -255,6 +275,7 @@ struct uart_amba_port { struct uart_port port; const u16 *reg_offset; struct clk *clk; + struct reset_control *rst; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ @@ -609,6 +630,19 @@ static int pl011_dma_tx_refill(struct uart_amba_port *uap) */ count -= 1; + /* + * Tegra GPC DMA driver requires the data to be multiple of burst size. + * Make count a multiple of burst size. + */ + if (uap->vendor->dma_workaround) { + count -= (count % (uap->fifosize >> 1)); + + if (count < (uap->fifosize >> 1)) { + uap->dmatx.queued = false; + return 0; + } + } + /* Else proceed to copy the TX chars to the DMA buffer and fire DMA */ if (count > PL011_DMA_BUFFER_SIZE) count = PL011_DMA_BUFFER_SIZE; @@ -2752,6 +2786,16 @@ static int pl011_register_port(struct uart_amba_port *uap) { int ret, i; + /* Reset the controller and enable clock for initial reg_write. + * This is required for Tegra Pl011 controller. + */ + if (uap->vendor->enable_car) { + reset_control_assert(uap->rst); + udelay(10); + reset_control_deassert(uap->rst); + clk_prepare_enable(uap->clk); + } + /* Ensure interrupts from this UART are masked and cleared */ pl011_write(0, uap, REG_IMSC); pl011_write(0xffff, uap, REG_ICR); @@ -2802,6 +2846,13 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) if (IS_ERR(uap->clk)) return PTR_ERR(uap->clk); + if (vendor->enable_car) { + uap->rst = devm_reset_control_get_exclusive(&dev->dev, + "serial"); + if (IS_ERR(uap->rst)) + return PTR_ERR(uap->rst); + } + uap->reg_offset = vendor->reg_offset; uap->vendor = vendor; uap->fifosize = vendor->get_fifosize(dev); @@ -2985,6 +3036,11 @@ static const struct amba_id pl011_ids[] = { .mask = 0x00ffffff, .data = &vendor_st, }, + { + .id = 0x00051011, + .mask = 0x00ffffff, + .data = &vendor_nvidia, + }, { 0, 0 }, };