diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 16:12:24 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-22 16:12:24 -0700 |
commit | 94b5aff4c6f72fee6b0f49d49e4fa8b204e8ded9 (patch) | |
tree | 39197121b6ef8cddaa0f4057fe24b4ced58e8982 /drivers/tty/serial/amba-pl011.c | |
parent | 5d4e2d08e7fdf7339f84a1c670d296a77e02f881 (diff) | |
parent | 59bd234b72fc29887839d792b7d6c7e8d2a577a6 (diff) |
Merge tag 'tty-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull TTY updates from Greg Kroah-Hartman:
"Here's the big TTY/serial driver pull request for the 3.5-rc1 merge
window.
Nothing major in here, just lots of incremental changes from Alan and
Jiri reworking some tty core things to behave better and to get a more
solid grasp on some of the nasty tty locking issues.
There are a few tty and serial driver updates in here as well.
All of this has been in the linux-next releases for a while with no
problems.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
* tag 'tty-3.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (115 commits)
serial: bfin_uart: Make MMR access compatible with 32 bits bf609 style controller.
serial: bfin_uart: RTS and CTS MMRs can be either 16-bit width or 32-bit width.
serial: bfin_uart: narrow the reboot condition in DMA tx interrupt
serial: bfin_uart: Adapt bf5xx serial driver to bf60x serial4 controller.
Revert "serial_core: Update buffer overrun statistics."
tty: hvc_xen: NULL dereference on allocation failure
tty: Fix LED error return
tty: Allow uart_register/unregister/register
tty: move global ldisc idle waitqueue to the individual ldisc
serial8250-em: Add DT support
serial8250-em: clk_get() IS_ERR() error handling fix
serial_core: Update buffer overrun statistics.
tty: drop the pty lock during hangup
cris: fix missing tty arg in wait_event_interruptible_tty call
tty/amiserial: Add missing argument for tty_unlock()
tty_lock: Localise the lock
pty: Lock the devpts bits privately
tty_lock: undo the old tty_lock use on the ctty
serial8250-em: Emma Mobile UART driver V2
Add missing call to uart_update_timeout()
...
Diffstat (limited to 'drivers/tty/serial/amba-pl011.c')
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 109 |
1 files changed, 18 insertions, 91 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index 062ef8c2b3c..4ad721fb840 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -68,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; @@ -101,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 = { @@ -110,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 = { @@ -120,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]; @@ -1055,69 +1034,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; @@ -1246,12 +1162,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); @@ -1268,11 +1198,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); |