/*
* NXP (Philips) SCC+++(SCN+++) serial driver
*
* Copyright (C) 2012 Alexander Shiyan <shc_work@mail.ru>
*
* Based on sc26xx.c, by Thomas Bogendörfer (tsbogend@alpha.franken.de)
*
* 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.
*/
#if defined(CONFIG_SERIAL_SCCNXP_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include <linux/module.h>
#include <linux/device.h>
#include <linux/console.h>
#include <linux/serial_core.h>
#include <linux/serial.h>
#include <linux/io.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/platform_data/sccnxp.h>
#define SCCNXP_NAME "uart-sccnxp"
#define SCCNXP_MAJOR 204
#define SCCNXP_MINOR 205
#define SCCNXP_MR_REG (0x00)
# define MR0_BAUD_NORMAL (0 << 0)
# define MR0_BAUD_EXT1 (1 << 0)
# define MR0_BAUD_EXT2 (5 << 0)
# define MR0_FIFO (1 << 3)
# define MR0_TXLVL (1 << 4)
# define MR1_BITS_5 (0 << 0)
# define MR1_BITS_6 (1 << 0)
# define MR1_BITS_7 (2 << 0)
# define MR1_BITS_8 (3 << 0)
# define MR1_PAR_EVN (0 << 2)
# define MR1_PAR_ODD (1 << 2)
# define MR1_PAR_NO (4 << 2)
# define MR2_STOP1 (7 << 0)
# define MR2_STOP2 (0xf << 0)
#define SCCNXP_SR_REG (0x01)
#define SCCNXP_CSR_REG SCCNXP_SR_REG
# define SR_RXRDY (1 << 0)
# define SR_FULL (1 << 1)
# define SR_TXRDY (1 << 2)
# define SR_TXEMT (1 << 3)
# define SR_OVR (1 << 4)
# define SR_PE (1 << 5)
# define SR_FE (1 << 6)
# define SR_BRK (1 << 7)
#define SCCNXP_CR_REG (0x02)
# define CR_RX_ENABLE (1 << 0)
# define CR_RX_DISABLE (1 << 1)
# define CR_TX_ENABLE (1 << 2)
# define CR_TX_DISABLE (1 << 3)
# define CR_CMD_MRPTR1 (0x01 << 4)
# define CR_CMD_RX_RESET (0x02 << 4)
# define CR_CMD_TX_RESET (0x03 << 4)
# define CR_CMD_STATUS_RESET (0x04 << 4)
# define CR_CMD_BREAK_RESET (0x05 << 4)
# define CR_CMD_START_BREAK (0x06 << 4)
# define CR_CMD_STOP_BREAK (0x07 << 4)
# define CR_CMD_MRPTR0 (0x0b << 4)
#define SCCNXP_RHR_REG (0x03)
#define SCCNXP_THR_REG SCCNXP_RHR_REG
#define SCCNXP_IPCR_REG (0x04)
#define SCCNXP_ACR_REG SCCNXP_IPCR_REG
# define ACR_BAUD0 (0 << 7)
# define ACR_BAUD1 (1 << 7)
# define ACR_TIMER_MODE (6 << 4)
#define SCCNXP_ISR_REG (0x05)
#define SCCNXP_IMR_REG SCCNXP_ISR_REG
# define IMR_TXRDY (1 << 0)
# define IMR_RXRDY (1 << 1)
# define ISR_TXRDY(x) (1 << ((x * 4) + 0))
# define ISR_RXRDY(x) (1 << ((x * 4) + 1))
#define SCCNXP_IPR_REG (0x0d)
#define SCCNXP_OPCR_REG SCCNXP_IPR_REG
#define SCCNXP_SOP_REG (0x0e)
#define SCCNXP_ROP_REG (0x0f)
/* Route helpers */
#define MCTRL_MASK(sig) (0xf << (sig))
#define MCTRL_IBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_IP0)
#define MCTRL_OBIT(cfg, sig) ((((cfg) >> (sig)) & 0xf) - LINE_OP0)
/* Supported chip types */
enum {
SCCNXP_TYPE_SC2681 = 2681,
SCCNXP_TYPE_SC2691 = 2691,
SCCNXP_TYPE_SC2692 = 2692,
SCCNXP_TYPE_SC2891 = 2891,
SCCNXP_TYPE_SC2892 = 2892,
SCCNXP_TYPE_SC28202 = 28202,
SCCNXP_TYPE_SC68681 = 68681,
SCCNXP_TYPE_SC68692 = 68692,
};
struct sccnxp_port {
struct uart_driver uart;
struct uart_port port[SCCNXP_MAX_UARTS];
bool opened[SCCNXP_MAX_UARTS];
const char *name;
int irq;
u8 imr;
u8 addr_mask;
int freq_std;
int flags;
#define SCCNXP_HAVE_IO 0x00000001
#define SCCNXP_HAVE_MR0 0x00000002
#ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
struct console console;
#endif
spinlock_t lock;
bool poll;
struct timer_list timer;
struct sccnxp_pdata pdata;
};
static inline u8 sccnxp_raw_read(void __iomem *base, u8 reg, u8 shift)
{
return readb(base + (reg << shift));
}
static inline void sccnxp_raw_write(void __iomem *base, u8 reg, u8 shift, u8 v)
{
writeb(v, base + (reg << shift));
}
static inline u8 sccnxp_read(struct uart_port *port, u8 reg)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
return sccnxp_raw_read(port->membase, reg & s->addr_mask,
port->regshift);
}
static inline void sccnxp_write(struct uart_port *port, u8 reg, u8 v)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
sccnxp_raw_write(port->membase, reg & s->addr_mask, port->regshift, v);
}
static inline u8 sccnxp_port_read(struct uart_port *port, u8 reg)
{
return sccnxp_read(port, (port->line << 3) + reg);
}
static inline void sccnxp_port_write(struct uart_port *port, u8 reg, u8 v)
{
sccnxp_write(port, (port->line << 3) + reg, v);
}
static int sccnxp_update_best_err(int a, int b, int *besterr)
{
int err = abs(a - b);
if ((*besterr < 0) || (*besterr > err)) {
*besterr = err;
return 0;
}
return 1;
}
struct baud_table {
u8 csr;
u8 acr;
u8 mr0;
int baud;
};
const struct baud_table baud_std