diff options
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 154 |
1 files changed, 63 insertions, 91 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 3d569cd68f58..c17923ec6e95 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -52,6 +52,7 @@ #include <linux/scatterlist.h> #include <linux/delay.h> #include <linux/types.h> +#include <linux/pinctrl/consumer.h> #include <asm/io.h> #include <asm/sizes.h> @@ -67,30 +68,6 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) - -#define UART_WA_SAVE_NR 14 - -static void pl011_lockup_wa(unsigned long data); -static const u32 uart_wa_reg[UART_WA_SAVE_NR] = { - ST_UART011_DMAWM, - ST_UART011_TIMEOUT, - ST_UART011_LCRH_RX, - UART011_IBRD, - UART011_FBRD, - ST_UART011_LCRH_TX, - UART011_IFLS, - ST_UART011_XFCR, - ST_UART011_XON1, - ST_UART011_XON2, - ST_UART011_XOFF1, - ST_UART011_XOFF2, - UART011_CR, - UART011_IMSC -}; - -static u32 uart_wa_regdata[UART_WA_SAVE_NR]; -static DECLARE_TASKLET(pl011_lockup_tlet, pl011_lockup_wa, 0); - /* There is by now at least one vendor with differing details, so handle it */ struct vendor_data { unsigned int ifls; @@ -100,6 +77,7 @@ struct vendor_data { bool oversampling; bool interrupt_may_hang; /* vendor-specific */ bool dma_threshold; + bool cts_event_workaround; }; static struct vendor_data vendor_arm = { @@ -109,6 +87,7 @@ static struct vendor_data vendor_arm = { .lcrh_rx = UART011_LCRH, .oversampling = false, .dma_threshold = false, + .cts_event_workaround = false, }; static struct vendor_data vendor_st = { @@ -119,6 +98,7 @@ static struct vendor_data vendor_st = { .oversampling = true, .interrupt_may_hang = true, .dma_threshold = true, + .cts_event_workaround = true, }; static struct uart_amba_port *amba_ports[UART_NR]; @@ -153,6 +133,10 @@ struct pl011_dmatx_data { struct uart_amba_port { struct uart_port port; struct clk *clk; + /* Two optional pin states - default & sleep */ + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_sleep; const struct vendor_data *vendor; unsigned int dmacr; /* dma control reg */ unsigned int im; /* interrupt mask */ @@ -1054,69 +1038,6 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap) #define pl011_dma_flush_buffer NULL #endif - -/* - * pl011_lockup_wa - * This workaround aims to break the deadlock situation - * when after long transfer over uart in hardware flow - * control, uart interrupt registers cannot be cleared. - * Hence uart transfer gets blocked. - * - * It is seen that during such deadlock condition ICR - * don't get cleared even on multiple write. This leads - * pass_counter to decrease and finally reach zero. This - * can be taken as trigger point to run this UART_BT_WA. - * - */ -static void pl011_lockup_wa(unsigned long data) -{ - struct uart_amba_port *uap = amba_ports[0]; - void __iomem *base = uap->port.membase; - struct circ_buf *xmit = &uap->port.state->xmit; - struct tty_struct *tty = uap->port.state->port.tty; - int buf_empty_retries = 200; - int loop; - - /* Stop HCI layer from submitting data for tx */ - tty->hw_stopped = 1; - while (!uart_circ_empty(xmit)) { - if (buf_empty_retries-- == 0) - break; - udelay(100); - } - - /* Backup registers */ - for (loop = 0; loop < UART_WA_SAVE_NR; loop++) - uart_wa_regdata[loop] = readl(base + uart_wa_reg[loop]); - - /* Disable UART so that FIFO data is flushed out */ - writew(0x00, uap->port.membase + UART011_CR); - - /* Soft reset UART module */ - if (uap->port.dev->platform_data) { - struct amba_pl011_data *plat; - - plat = uap->port.dev->platform_data; - if (plat->reset) - plat->reset(); - } - - /* Restore registers */ - for (loop = 0; loop < UART_WA_SAVE_NR; loop++) - writew(uart_wa_regdata[loop] , - uap->port.membase + uart_wa_reg[loop]); - - /* Initialise the old status of the modem signals */ - uap->old_status = readw(uap->port.membase + UART01x_FR) & - UART01x_FR_MODEM_ANY; - - if (readl(base + UART011_MIS) & 0x2) - printk(KERN_EMERG "UART_BT_WA: ***FAILED***\n"); - - /* Start Tx/Rx */ - tty->hw_stopped = 0; -} - static void pl011_stop_tx(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; @@ -1245,12 +1166,26 @@ static irqreturn_t pl011_int(int irq, void *dev_id) unsigned long flags; unsigned int status, pass_counter = AMBA_ISR_PASS_LIMIT; int handled = 0; + unsigned int dummy_read; spin_lock_irqsave(&uap->port.lock, flags); status = readw(uap->port.membase + UART011_MIS); if (status) { do { + if (uap->vendor->cts_event_workaround) { + /* workaround to make sure that all bits are unlocked.. */ + writew(0x00, uap->port.membase + UART011_ICR); + + /* + * WA: introduce 26ns(1 uart clk) delay before W1C; + * single apb access will incur 2 pclk(133.12Mhz) delay, + * so add 2 dummy reads + */ + dummy_read = readw(uap->port.membase + UART011_ICR); + dummy_read = readw(uap->port.membase + UART011_ICR); + } + writew(status & ~(UART011_TXIS|UART011_RTIS| UART011_RXIS), uap->port.membase + UART011_ICR); @@ -1267,11 +1202,8 @@ static irqreturn_t pl011_int(int irq, void *dev_id) if (status & UART011_TXIS) pl011_tx_chars(uap); - if (pass_counter-- == 0) { - if (uap->interrupt_may_hang) - tasklet_schedule(&pl011_lockup_tlet); + if (pass_counter-- == 0) break; - } status = readw(uap->port.membase + UART011_MIS); } while (status != 0); @@ -1384,6 +1316,14 @@ static int pl011_startup(struct uart_port *port) unsigned int cr; int retval; + /* Optionaly enable pins to be muxed in and configured */ + if (!IS_ERR(uap->pins_default)) { + retval = pinctrl_select_state(uap->pinctrl, uap->pins_default); + if (retval) + dev_err(port->dev, + "could not set default pins\n"); + } + retval = clk_prepare(uap->clk); if (retval) goto out; @@ -1492,6 +1432,7 @@ static void pl011_shutdown(struct uart_port *port) { struct uart_amba_port *uap = (struct uart_amba_port *)port; unsigned int cr; + int retval; /* * disable all interrupts @@ -1534,6 +1475,14 @@ static void pl011_shutdown(struct uart_port *port) */ clk_disable(uap->clk); clk_unprepare(uap->clk); + /* Optionally let pins go into sleep states */ + if (!IS_ERR(uap->pins_sleep)) { + retval = pinctrl_select_state(uap->pinctrl, uap->pins_sleep); + if (retval) + dev_err(port->dev, + "could not set pins to sleep state\n"); + } + if (uap->port.dev->platform_data) { struct amba_pl011_data *plat; @@ -1864,6 +1813,14 @@ static int __init pl011_console_setup(struct console *co, char *options) if (!uap) return -ENODEV; + /* Allow pins to be muxed in and configured */ + if (!IS_ERR(uap->pins_default)) { + ret = pinctrl_select_state(uap->pinctrl, uap->pins_default); + if (ret) + dev_err(uap->port.dev, + "could not set default pins\n"); + } + ret = clk_prepare(uap->clk); if (ret) return ret; @@ -1940,6 +1897,21 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id) goto free; } + uap->pinctrl = devm_pinctrl_get(&dev->dev); + if (IS_ERR(uap->pinctrl)) { + ret = PTR_ERR(uap->pinctrl); + goto unmap; + } + uap->pins_default = pinctrl_lookup_state(uap->pinctrl, + PINCTRL_STATE_DEFAULT); + if (IS_ERR(uap->pins_default)) + dev_err(&dev->dev, "could not get default pinstate\n"); + + uap->pins_sleep = pinctrl_lookup_state(uap->pinctrl, + PINCTRL_STATE_SLEEP); + if (IS_ERR(uap->pins_sleep)) + dev_dbg(&dev->dev, "could not get sleep pinstate\n"); + uap->clk = clk_get(&dev->dev, NULL); if (IS_ERR(uap->clk)) { ret = PTR_ERR(uap->clk); |