blob: 2de2f3c249fe7485ff31425aa0333129f28ada35 [file] [log] [blame]
/*******************************************************************************
* Copyright 2012 Broadcom Corporation. All rights reserved.
*
* @file drivers/tty/serial/8250/8250_brcm_bt.c
*
* Unless you and Broadcom execute a separate written software license agreement
* governing use of this software, this software is licensed to you under the
* terms of the GNU General Public License version 2, available at
* http://www.gnu.org/copyleft/gpl.html (the "GPL").
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a
* license other than the GPL, without Broadcom's express prior written consent.
* *******************************************************************************/
#include <linux/serial_8250.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/tty_flip.h>
#include "8250.h"
#include "8250_dw.h"
#ifdef CONFIG_BRCM_UART_CHANGES
static unsigned char
bt_serial8250_rx_chars(struct uart_8250_port *up, unsigned char lsr)
{
struct uart_port *port = &up->port;
unsigned char ch;
int max_count = 256;
char flag;
int afe_status = 0;
int n_fifo;
int mcr;
/* Handle port gets called from either the interrupt context
* _OR_ from the timeout thread context (serial8250_timeout).
*
* Now first using qos APIs prevent the shutting down of UART clocks.
* for RX context. For TX we can determine when to enable and disable
* going to and coming out of retention. But for Rx, we need to have
* a timer. Once a RX happens we restart the timer to expire after
* say 'n' milliseconds. So when the timer function gets executed
* we know that 'n' milliseconds has expired without any RX activity
* so release the UART Rx context to go to retention.
*
* Now for from the tx perspective we'll enable the system to go to
* retention
* i.e release the clocks when the tx circular buffer is empty and
* the TX FIFO + THR is also empty.
*
* From the Power Mgr perspective if from both the TX and RX context
* if we have released the clocks then this block will be put into
* retention. Note that the aggregation is done by the Power
* Management code. */
pi_mgr_qos_request_update(&up->qos_rx_node,0);
/* Some RX activity has happened either byte received _OR_ some RX
* error, whatever may be the case we have requested the PI MGR not
* to go to retention, so start the timer immediately. If there are
* no more Rx activity for another RX_SHUTOFF_DELAY_MSECS, then this
* timer would come and call the pi mgr API to release the RX context
* */
mod_timer(&up->rx_shutoff_timer,
jiffies + msecs_to_jiffies(RX_SHUTOFF_DELAY_MSECS));
do {
/* Jira-1744 UART Line Status Register's Data Ready bit is not
* updated sometime when data is received in fifo and a Rx
* interrupt is generated.
* UART RX interrupt happened but in LSR Data Ready is not set.
* This fix is to take the decission based on iir also. */
if (likely((lsr & UART_LSR_DR) ||
(up->iir & UART_IIR_TIME_OUT)))
ch = port->serial_in(port, UART_RX);
else
ch = 0;
flag = TTY_NORMAL;
up->port.icount.rx++;
lsr |= up->lsr_saved_flags;
up->lsr_saved_flags = 0;
if (unlikely(lsr & UART_LSR_BRK_ERROR_BITS)) {
if (lsr & UART_LSR_BI) {
lsr &= ~(UART_LSR_FE | UART_LSR_PE);
up->port.icount.brk++;
/* We do the SysRQ and SAK checking
* here because otherwise the break
* may get masked by ignore_status_mask
* or read_status_mask. */
if (uart_handle_break(&up->port))
goto ignore_char;
}
else if (lsr & UART_LSR_PE)
up->port.icount.parity++;
else if (lsr & UART_LSR_FE)
up->port.icount.frame++;
if (lsr & UART_LSR_OE)
up->port.icount.overrun++;
/* Mask off conditions which should be ignored.*/
lsr &= up->port.read_status_mask;
if (lsr & UART_LSR_BI)
flag = TTY_BREAK;
else if (lsr & UART_LSR_PE)
flag = TTY_PARITY;
else if (lsr & UART_LSR_FE)
flag = TTY_FRAME;
}
uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
ignore_char:
lsr = port->serial_in(port, UART_LSR);
} while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
spin_unlock(&up->port.lock);
tty_flip_buffer_push(&port->state->port);
spin_lock(&up->port.lock);
return lsr;
}
static int bt_serial8250_handle_irq(struct uart_port *port, unsigned int iir)
{
unsigned char status;
unsigned long flags;
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
if (iir & UART_IIR_NO_INT)
return 0;
spin_lock_irqsave(&port->lock, flags);
status = port->serial_in(port, UART_LSR);
/* Jira-1744 UART Line Status Register's Data Ready bit is not
* updated sometime when data is received in fifo and a R
* interrupt is generated.
* UART RX interrupt happened but in LSR Data Ready is not set.
* This fix is to take the decission based on iir also. */
if ((iir & UART_IIR_RDI) || (status & (UART_LSR_DR | UART_LSR_BI))) {
#if defined(CONFIG_HAS_WAKELOCK)
wake_lock_timeout(&up->uart_lock,
msecs_to_jiffies(WAKELOCK_TIMEOUT_VAL));
#endif /* CONFIG_HAS_WAKELOCK */
status = bt_serial8250_rx_chars(up, status);
}
serial8250_modem_status(up);
/* Note that from the serial8250_tx_chars we are NOT disabling the TX
* interrupt when the circular buffer becomes empty. So we will get
* a TX over interrupt once the FIFO is flushed. In that case, go
* and stop the Tx and disable the clocks so that the CCU can go to
* retention. */
if ((status & (UART_LSR_TEMT | UART_LSR_THRE)) &&
uart_circ_empty(&up->port.state->xmit)) {
if (up->ier & UART_IER_THRI) {
up->ier &= ~UART_IER_THRI;
port->serial_out(port, UART_IER, up->ier);
}
pi_mgr_qos_request_update(&up->qos_tx_node, PI_MGR_QOS_DEFAULT_VALUE);
} else if (status & UART_LSR_THRE)
serial8250_tx_chars(up);
spin_unlock_irqrestore(&port->lock, flags);
return 1;
}
int bt_dw8250_handle_irq(struct uart_port *port)
{
struct dw8250_data *d = port->private_data;
unsigned int iir = port->serial_in(port, UART_IIR);
#ifdef CONFIG_BRCM_UART_CHANGES
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
/* This is used for checking the Timeout interrupt in
* serial8250_rx_chars() */
up->iir = iir;
#endif
if (bt_serial8250_handle_irq(port, iir)) {
return 1;
} else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) {
/* Clear the USR and write the LCR again. */
(void)port->serial_in(port, UART_USR);
#ifdef CONFIG_BRCM_UART_CHANGES
/* Stop writing to LCR if the value is same. */
if (port->serial_in(port, UART_LCR) != d->last_lcr)
port->serial_out(port, UART_LCR, d->last_lcr);
#endif
return 1;
}
return 0;
}
EXPORT_SYMBOL(bt_dw8250_handle_irq);
#endif