/*
* Driver for msm7k serial device and console
*
* Copyright (C) 2007 Google, Inc.
* Author: Robert Love <rlove@google.com>
* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#if defined(CONFIG_SERIAL_MSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
# define SUPPORT_SYSRQ
#endif
#include <linux/hrtimer.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/init.h>
#include <linux/console.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include "msm_serial.h"
struct msm_port {
struct uart_port uart;
char name[16];
struct clk *clk;
struct clk *pclk;
unsigned int imr;
unsigned int *gsbi_base;
int is_uartdm;
unsigned int old_snap_state;
};
static inline void wait_for_xmitr(struct uart_port *port, int bits)
{
if (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY))
while ((msm_read(port, UART_ISR) & bits) != bits)
cpu_relax();
}
static void msm_stop_tx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
msm_port->imr &= ~UART_IMR_TXLEV;
msm_write(port, msm_port->imr, UART_IMR);
}
static void msm_start_tx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
msm_port->imr |= UART_IMR_TXLEV;
msm_write(port, msm_port->imr, UART_IMR);
}
static void msm_stop_rx(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE);
msm_write(port, msm_port->imr, UART_IMR);
}
static void msm_enable_ms(struct uart_port *port)
{
struct msm_port *msm_port = UART_TO_MSM(port);
msm_port->imr |= UART_IMR_DELTA_CTS;
msm_write(port, msm_port->imr, UART_IMR);
}
static void handle_rx_dm(struct uart_port *port, unsigned int misr)
{
struct tty_struct *tty = port->state->port.tty;
unsigned int sr;
int count = 0;
struct msm_port *msm_port = UART_TO_MSM(port);
if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) {
port->icount.overrun++;
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR);
}
if (misr & UART_IMR_RXSTALE) {
count = msm_read(port, UARTDM_RX_TOTAL_SNAP) -
msm_port->old_snap_state;
msm_port->old_snap_state = 0;
} else {
count = 4 * (msm_read(port, UART_RFWR));
msm_port->old_snap_state += count;
}
/* TODO: Precise error reporting */
port->icount.rx += count;
while (count > 0) {
unsigned int c;
sr = msm_read(port, UART_SR);
if ((sr & UART_SR_RX_READY) == 0) {
msm_port->old_snap_state -= count;
break;
}
c = msm_read(port, UARTDM_RF);
if (sr & UART_SR_RX_BREAK) {
port->icount.brk++;
if (uart_handle_break(port))
continue;
} else if (sr & UART_SR_PAR_FRAME_ERR)
port->icount.frame++;
/* TODO: handle sysrq */
tty_insert_flip_string(tty, (char *) &c,
(count > 4) ? 4 : count);
count -= 4;
}
tty_flip_buffer_push(tty);
if (misr & (UART_IMR_RXSTALE))
msm_write(port, UART_CR_CMD_RESET_STALE_INT, UART_CR);
msm_write(port, 0xFFFFFF, UARTDM_DMRX);
msm_write(port, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
}
static void handle_rx(struct uart_port *port)
{
struct tty_struct *tty = port->state->port.tty;
unsigned int sr;
/*
* Handle overr