/*
* linux/drivers/mmc/card/sdio_uart.c - SDIO UART/GPS driver
*
* Based on drivers/serial/8250.c and drivers/serial/serial_core.c
* by Russell King.
*
* Author: Nicolas Pitre
* Created: June 15, 2007
* Copyright: MontaVista Software, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*/
/*
* Note: Although this driver assumes a 16550A-like UART implementation,
* it is not possible to leverage the common 8250/16550 driver, nor the
* core UART infrastructure, as they assumes direct access to the hardware
* registers, often under a spinlock. This is not possible in the SDIO
* context as SDIO access functions must be able to sleep.
*
* Because we need to lock the SDIO host to ensure an exclusive access to
* the card, we simply rely on that lock to also prevent and serialize
* concurrent access to the same port.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/serial_reg.h>
#include <linux/circ_buf.h>
#include <linux/gfp.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kfifo.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#define UART_NR 8 /* Number of UARTs this driver can handle */
#define FIFO_SIZE PAGE_SIZE
#define WAKEUP_CHARS 256
struct uart_icount {
__u32 cts;
__u32 dsr;
__u32 rng;
__u32 dcd;
__u32 rx;
__u32 tx;
__u32 frame;
__u32 overrun;
__u32 parity;
__u32 brk;
};
struct sdio_uart_port {
struct tty_port port;
struct kref kref;
struct tty_struct *tty;
unsigned int index;
struct sdio_func *func;
struct mutex func_lock;
struct task_struct *in_sdio_uart_irq;
unsigned int regs_offset;
struct kfifo xmit_fifo;
spinlock_t write_lock;
struct uart_icount icount;
unsigned int uartclk;
unsigned int mctrl;
unsigned int rx_mctrl;
unsigned int read_status_mask;
unsigned int ignore_status_mask;
unsigned char x_char;
unsigned char ier;
unsigned char lcr;
};
static struct sdio_uart_port *sdio_uart_table[UART_NR];
static DEFINE_SPINLOCK(sdio_uart_table_lock);
static int sdio_uart_add_port(struct sdio_uart_port *port)
{
int index, ret = -EBUSY;
kref_init(&port->kref);
mutex_init(&port->func_lock);
spin_lock_init(&port->write_lock);
if (kfifo_alloc(&port->xmit_fifo, FIFO_SIZE, GFP_KERNEL))
return -ENOMEM;
spin_lock(&sdio_uart_table_lock);
for (index = 0; index < UART_NR; index++) {
if (!sdio_uart_table[index]) {
port->index = index;
sdio_uart_table[index] = port;
ret = 0;
break;
}
}
spin_unlock(&sdio_uart_table_lock);
return ret;
}
static struct sdio_uart_port *sdio_uart_port_get(unsigned index)
{
struct sdio_uart_port *port;
if (index >= UART_NR)
return NULL;
spin_lock(&sdio_uart_table_lock);
port = s