// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 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.
*/
/*
* 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.h>
#include <linux/serial_reg.h>
#include <linux/circ_buf.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kfifo.h>
#include <linux/slab.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;
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;
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 = sdio_uart_table[index];
if (port)
tty_port_get(&port->port);
spin_unlock(&sdio_uart_table_lock);
return port;
}
static void sdio_uart_port_put(struct sdio_uart_port *port)
{
tty_port_put(&port->port);
}
static void sdio_uart_port_remove(struct sdio_uart_port *port)
{
struct sdio_func *func;
spin_lock(&sdio_uart_table_lock);
sdio_uart_table[port->index] = NULL;
spin_unlock(&sdio_uart_table_lock);
/*
* We're killing a port that potentially still is in use by
* the tty layer. Be careful to prevent any further access
* to the SDIO function and arrange for the tty layer to
* give up on that port ASAP.
* Beware: the lock ordering is critical.
*/
mutex_lock(&port->port.mutex);
mutex_lock(&port->func_lock);
func = port->func;
sdio_claim_host(func);
port->func = NULL;
mutex_unlock(&port->func_lock);
/* tty_hangup is async so is this safe as is ?? */
tty_port_tty_hangup(&port->port, false);
mutex_unlock(&port->port.mutex);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);
sdio_uart_port_put(port);
}
static int sdio_uart_claim_func(struct sdio_uart_port *port)
{
mutex_lock(&port->func_lock);
if (unlikely(!port->func)) {
mutex_unlock(