2012-08-25 19:24:19 +04:00
/*
* 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>
2012-09-06 09:05:04 +04:00
# include <linux/console.h>
2012-08-25 19:24:19 +04:00
# include <linux/serial_core.h>
# include <linux/serial.h>
# include <linux/io.h>
# include <linux/tty.h>
# include <linux/tty_flip.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 ] ;
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
struct mutex sccnxp_mutex ;
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 [ ] = {
{ 0 , ACR_BAUD0 , MR0_BAUD_NORMAL , 50 , } ,
{ 0 , ACR_BAUD1 , MR0_BAUD_NORMAL , 75 , } ,
{ 1 , ACR_BAUD0 , MR0_BAUD_NORMAL , 110 , } ,
{ 2 , ACR_BAUD0 , MR0_BAUD_NORMAL , 134 , } ,
{ 3 , ACR_BAUD1 , MR0_BAUD_NORMAL , 150 , } ,
{ 3 , ACR_BAUD0 , MR0_BAUD_NORMAL , 200 , } ,
{ 4 , ACR_BAUD0 , MR0_BAUD_NORMAL , 300 , } ,
{ 0 , ACR_BAUD1 , MR0_BAUD_EXT1 , 450 , } ,
{ 1 , ACR_BAUD0 , MR0_BAUD_EXT2 , 880 , } ,
{ 3 , ACR_BAUD1 , MR0_BAUD_EXT1 , 900 , } ,
{ 5 , ACR_BAUD0 , MR0_BAUD_NORMAL , 600 , } ,
{ 7 , ACR_BAUD0 , MR0_BAUD_NORMAL , 1050 , } ,
{ 2 , ACR_BAUD0 , MR0_BAUD_EXT2 , 1076 , } ,
{ 6 , ACR_BAUD0 , MR0_BAUD_NORMAL , 1200 , } ,
{ 10 , ACR_BAUD1 , MR0_BAUD_NORMAL , 1800 , } ,
{ 7 , ACR_BAUD1 , MR0_BAUD_NORMAL , 2000 , } ,
{ 8 , ACR_BAUD0 , MR0_BAUD_NORMAL , 2400 , } ,
{ 5 , ACR_BAUD1 , MR0_BAUD_EXT1 , 3600 , } ,
{ 9 , ACR_BAUD0 , MR0_BAUD_NORMAL , 4800 , } ,
{ 10 , ACR_BAUD0 , MR0_BAUD_NORMAL , 7200 , } ,
{ 11 , ACR_BAUD0 , MR0_BAUD_NORMAL , 9600 , } ,
{ 8 , ACR_BAUD0 , MR0_BAUD_EXT1 , 14400 , } ,
{ 12 , ACR_BAUD1 , MR0_BAUD_NORMAL , 19200 , } ,
{ 9 , ACR_BAUD0 , MR0_BAUD_EXT1 , 28800 , } ,
{ 12 , ACR_BAUD0 , MR0_BAUD_NORMAL , 38400 , } ,
{ 11 , ACR_BAUD0 , MR0_BAUD_EXT1 , 57600 , } ,
{ 12 , ACR_BAUD1 , MR0_BAUD_EXT1 , 115200 , } ,
{ 12 , ACR_BAUD0 , MR0_BAUD_EXT1 , 230400 , } ,
{ 0 , 0 , 0 , 0 }
} ;
static void sccnxp_set_baud ( struct uart_port * port , int baud )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
int div_std , tmp_baud , bestbaud = baud , besterr = - 1 ;
u8 i , acr = 0 , csr = 0 , mr0 = 0 ;
/* Find best baud from table */
for ( i = 0 ; baud_std [ i ] . baud & & besterr ; i + + ) {
if ( baud_std [ i ] . mr0 & & ! ( s - > flags & SCCNXP_HAVE_MR0 ) )
continue ;
div_std = DIV_ROUND_CLOSEST ( s - > freq_std , baud_std [ i ] . baud ) ;
tmp_baud = DIV_ROUND_CLOSEST ( port - > uartclk , div_std ) ;
if ( ! sccnxp_update_best_err ( baud , tmp_baud , & besterr ) ) {
acr = baud_std [ i ] . acr ;
csr = baud_std [ i ] . csr ;
mr0 = baud_std [ i ] . mr0 ;
bestbaud = tmp_baud ;
}
}
if ( s - > flags & SCCNXP_HAVE_MR0 ) {
/* Enable FIFO, set half level for TX */
mr0 | = MR0_FIFO | MR0_TXLVL ;
/* Update MR0 */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_MRPTR0 ) ;
sccnxp_port_write ( port , SCCNXP_MR_REG , mr0 ) ;
}
sccnxp_port_write ( port , SCCNXP_ACR_REG , acr | ACR_TIMER_MODE ) ;
sccnxp_port_write ( port , SCCNXP_CSR_REG , ( csr < < 4 ) | csr ) ;
dev_dbg ( port - > dev , " Baudrate desired: %i, calculated: %i \n " ,
baud , bestbaud ) ;
}
static void sccnxp_enable_irq ( struct uart_port * port , int mask )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
s - > imr | = mask < < ( port - > line * 4 ) ;
sccnxp_write ( port , SCCNXP_IMR_REG , s - > imr ) ;
}
static void sccnxp_disable_irq ( struct uart_port * port , int mask )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
s - > imr & = ~ ( mask < < ( port - > line * 4 ) ) ;
sccnxp_write ( port , SCCNXP_IMR_REG , s - > imr ) ;
}
static void sccnxp_set_bit ( struct uart_port * port , int sig , int state )
{
u8 bitmask ;
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
if ( s - > pdata . mctrl_cfg [ port - > line ] & MCTRL_MASK ( sig ) ) {
bitmask = 1 < < MCTRL_OBIT ( s - > pdata . mctrl_cfg [ port - > line ] , sig ) ;
if ( state )
sccnxp_write ( port , SCCNXP_SOP_REG , bitmask ) ;
else
sccnxp_write ( port , SCCNXP_ROP_REG , bitmask ) ;
}
}
static void sccnxp_handle_rx ( struct uart_port * port )
{
u8 sr ;
unsigned int ch , flag ;
struct tty_struct * tty = tty_port_tty_get ( & port - > state - > port ) ;
if ( ! tty )
return ;
for ( ; ; ) {
sr = sccnxp_port_read ( port , SCCNXP_SR_REG ) ;
if ( ! ( sr & SR_RXRDY ) )
break ;
sr & = SR_PE | SR_FE | SR_OVR | SR_BRK ;
ch = sccnxp_port_read ( port , SCCNXP_RHR_REG ) ;
port - > icount . rx + + ;
flag = TTY_NORMAL ;
if ( unlikely ( sr ) ) {
if ( sr & SR_BRK ) {
port - > icount . brk + + ;
if ( uart_handle_break ( port ) )
continue ;
} else if ( sr & SR_PE )
port - > icount . parity + + ;
else if ( sr & SR_FE )
port - > icount . frame + + ;
else if ( sr & SR_OVR )
port - > icount . overrun + + ;
sr & = port - > read_status_mask ;
if ( sr & SR_BRK )
flag = TTY_BREAK ;
else if ( sr & SR_PE )
flag = TTY_PARITY ;
else if ( sr & SR_FE )
flag = TTY_FRAME ;
else if ( sr & SR_OVR )
flag = TTY_OVERRUN ;
}
if ( uart_handle_sysrq_char ( port , ch ) )
continue ;
if ( sr & port - > ignore_status_mask )
continue ;
uart_insert_char ( port , sr , SR_OVR , ch , flag ) ;
}
tty_flip_buffer_push ( tty ) ;
tty_kref_put ( tty ) ;
}
static void sccnxp_handle_tx ( struct uart_port * port )
{
u8 sr ;
struct circ_buf * xmit = & port - > state - > xmit ;
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
if ( unlikely ( port - > x_char ) ) {
sccnxp_port_write ( port , SCCNXP_THR_REG , port - > x_char ) ;
port - > icount . tx + + ;
port - > x_char = 0 ;
return ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) ) {
/* Disable TX if FIFO is empty */
if ( sccnxp_port_read ( port , SCCNXP_SR_REG ) & SR_TXEMT ) {
sccnxp_disable_irq ( port , IMR_TXRDY ) ;
/* Set direction to input */
if ( s - > flags & SCCNXP_HAVE_IO )
sccnxp_set_bit ( port , DIR_OP , 0 ) ;
}
return ;
}
while ( ! uart_circ_empty ( xmit ) ) {
sr = sccnxp_port_read ( port , SCCNXP_SR_REG ) ;
if ( ! ( sr & SR_TXRDY ) )
break ;
sccnxp_port_write ( port , SCCNXP_THR_REG , xmit - > buf [ xmit - > tail ] ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
}
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
}
static irqreturn_t sccnxp_ist ( int irq , void * dev_id )
{
int i ;
u8 isr ;
struct sccnxp_port * s = ( struct sccnxp_port * ) dev_id ;
mutex_lock ( & s - > sccnxp_mutex ) ;
for ( ; ; ) {
isr = sccnxp_read ( & s - > port [ 0 ] , SCCNXP_ISR_REG ) ;
isr & = s - > imr ;
if ( ! isr )
break ;
dev_dbg ( s - > port [ 0 ] . dev , " IRQ status: 0x%02x \n " , isr ) ;
for ( i = 0 ; i < s - > uart . nr ; i + + ) {
if ( isr & ISR_RXRDY ( i ) )
sccnxp_handle_rx ( & s - > port [ i ] ) ;
if ( isr & ISR_TXRDY ( i ) )
sccnxp_handle_tx ( & s - > port [ i ] ) ;
}
}
mutex_unlock ( & s - > sccnxp_mutex ) ;
return IRQ_HANDLED ;
}
static void sccnxp_start_tx ( struct uart_port * port )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
mutex_lock ( & s - > sccnxp_mutex ) ;
/* Set direction to output */
if ( s - > flags & SCCNXP_HAVE_IO )
sccnxp_set_bit ( port , DIR_OP , 1 ) ;
sccnxp_enable_irq ( port , IMR_TXRDY ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static void sccnxp_stop_tx ( struct uart_port * port )
{
/* Do nothing */
}
static void sccnxp_stop_rx ( struct uart_port * port )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
mutex_lock ( & s - > sccnxp_mutex ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_RX_DISABLE ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static unsigned int sccnxp_tx_empty ( struct uart_port * port )
{
u8 val ;
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
mutex_lock ( & s - > sccnxp_mutex ) ;
val = sccnxp_port_read ( port , SCCNXP_SR_REG ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
return ( val & SR_TXEMT ) ? TIOCSER_TEMT : 0 ;
}
static void sccnxp_enable_ms ( struct uart_port * port )
{
/* Do nothing */
}
static void sccnxp_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
if ( ! ( s - > flags & SCCNXP_HAVE_IO ) )
return ;
mutex_lock ( & s - > sccnxp_mutex ) ;
sccnxp_set_bit ( port , DTR_OP , mctrl & TIOCM_DTR ) ;
sccnxp_set_bit ( port , RTS_OP , mctrl & TIOCM_RTS ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static unsigned int sccnxp_get_mctrl ( struct uart_port * port )
{
u8 bitmask , ipr ;
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
unsigned int mctrl = TIOCM_DSR | TIOCM_CTS | TIOCM_CAR ;
if ( ! ( s - > flags & SCCNXP_HAVE_IO ) )
return mctrl ;
mutex_lock ( & s - > sccnxp_mutex ) ;
ipr = ~ sccnxp_read ( port , SCCNXP_IPCR_REG ) ;
if ( s - > pdata . mctrl_cfg [ port - > line ] & MCTRL_MASK ( DSR_IP ) ) {
bitmask = 1 < < MCTRL_IBIT ( s - > pdata . mctrl_cfg [ port - > line ] ,
DSR_IP ) ;
mctrl & = ~ TIOCM_DSR ;
mctrl | = ( ipr & bitmask ) ? TIOCM_DSR : 0 ;
}
if ( s - > pdata . mctrl_cfg [ port - > line ] & MCTRL_MASK ( CTS_IP ) ) {
bitmask = 1 < < MCTRL_IBIT ( s - > pdata . mctrl_cfg [ port - > line ] ,
CTS_IP ) ;
mctrl & = ~ TIOCM_CTS ;
mctrl | = ( ipr & bitmask ) ? TIOCM_CTS : 0 ;
}
if ( s - > pdata . mctrl_cfg [ port - > line ] & MCTRL_MASK ( DCD_IP ) ) {
bitmask = 1 < < MCTRL_IBIT ( s - > pdata . mctrl_cfg [ port - > line ] ,
DCD_IP ) ;
mctrl & = ~ TIOCM_CAR ;
mctrl | = ( ipr & bitmask ) ? TIOCM_CAR : 0 ;
}
if ( s - > pdata . mctrl_cfg [ port - > line ] & MCTRL_MASK ( RNG_IP ) ) {
bitmask = 1 < < MCTRL_IBIT ( s - > pdata . mctrl_cfg [ port - > line ] ,
RNG_IP ) ;
mctrl & = ~ TIOCM_RNG ;
mctrl | = ( ipr & bitmask ) ? TIOCM_RNG : 0 ;
}
mutex_unlock ( & s - > sccnxp_mutex ) ;
return mctrl ;
}
static void sccnxp_break_ctl ( struct uart_port * port , int break_state )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
mutex_lock ( & s - > sccnxp_mutex ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , break_state ?
CR_CMD_START_BREAK : CR_CMD_STOP_BREAK ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static void sccnxp_set_termios ( struct uart_port * port ,
struct ktermios * termios , struct ktermios * old )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
u8 mr1 , mr2 ;
int baud ;
mutex_lock ( & s - > sccnxp_mutex ) ;
/* Mask termios capabilities we don't support */
termios - > c_cflag & = ~ CMSPAR ;
termios - > c_iflag & = ~ ( IXON | IXOFF | IXANY ) ;
/* Disable RX & TX, reset break condition, status and FIFOs */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_RX_RESET |
CR_RX_DISABLE | CR_TX_DISABLE ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_TX_RESET ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_STATUS_RESET ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_BREAK_RESET ) ;
/* Word size */
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
mr1 = MR1_BITS_5 ;
break ;
case CS6 :
mr1 = MR1_BITS_6 ;
break ;
case CS7 :
mr1 = MR1_BITS_7 ;
break ;
default :
case CS8 :
mr1 = MR1_BITS_8 ;
break ;
}
/* Parity */
if ( termios - > c_cflag & PARENB ) {
if ( termios - > c_cflag & PARODD )
mr1 | = MR1_PAR_ODD ;
} else
mr1 | = MR1_PAR_NO ;
/* Stop bits */
mr2 = ( termios - > c_cflag & CSTOPB ) ? MR2_STOP2 : MR2_STOP1 ;
/* Update desired format */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_MRPTR1 ) ;
sccnxp_port_write ( port , SCCNXP_MR_REG , mr1 ) ;
sccnxp_port_write ( port , SCCNXP_MR_REG , mr2 ) ;
/* Set read status mask */
port - > read_status_mask = SR_OVR ;
if ( termios - > c_iflag & INPCK )
port - > read_status_mask | = SR_PE | SR_FE ;
if ( termios - > c_iflag & ( BRKINT | PARMRK ) )
port - > read_status_mask | = SR_BRK ;
/* Set status ignore mask */
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNBRK )
port - > ignore_status_mask | = SR_BRK ;
if ( ! ( termios - > c_cflag & CREAD ) )
port - > ignore_status_mask | = SR_PE | SR_OVR | SR_FE | SR_BRK ;
/* Setup baudrate */
baud = uart_get_baud_rate ( port , termios , old , 50 ,
( s - > flags & SCCNXP_HAVE_MR0 ) ?
230400 : 38400 ) ;
sccnxp_set_baud ( port , baud ) ;
/* Update timeout according to new baud rate */
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
/* Enable RX & TX */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_RX_ENABLE | CR_TX_ENABLE ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static int sccnxp_startup ( struct uart_port * port )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
mutex_lock ( & s - > sccnxp_mutex ) ;
if ( s - > flags & SCCNXP_HAVE_IO ) {
/* Outputs are controlled manually */
sccnxp_write ( port , SCCNXP_OPCR_REG , 0 ) ;
}
/* Reset break condition, status and FIFOs */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_RX_RESET ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_TX_RESET ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_STATUS_RESET ) ;
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_CMD_BREAK_RESET ) ;
/* Enable RX & TX */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_RX_ENABLE | CR_TX_ENABLE ) ;
/* Enable RX interrupt */
sccnxp_enable_irq ( port , IMR_RXRDY ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
return 0 ;
}
static void sccnxp_shutdown ( struct uart_port * port )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
mutex_lock ( & s - > sccnxp_mutex ) ;
/* Disable interrupts */
sccnxp_disable_irq ( port , IMR_TXRDY | IMR_RXRDY ) ;
/* Disable TX & RX */
sccnxp_port_write ( port , SCCNXP_CR_REG , CR_RX_DISABLE | CR_TX_DISABLE ) ;
/* Leave direction to input */
if ( s - > flags & SCCNXP_HAVE_IO )
sccnxp_set_bit ( port , DIR_OP , 0 ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static const char * sccnxp_type ( struct uart_port * port )
{
struct sccnxp_port * s = dev_get_drvdata ( port - > dev ) ;
return ( port - > type = = PORT_SC26XX ) ? s - > name : NULL ;
}
static void sccnxp_release_port ( struct uart_port * port )
{
/* Do nothing */
}
static int sccnxp_request_port ( struct uart_port * port )
{
/* Do nothing */
return 0 ;
}
static void sccnxp_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE )
port - > type = PORT_SC26XX ;
}
static int sccnxp_verify_port ( struct uart_port * port , struct serial_struct * s )
{
if ( ( s - > type = = PORT_UNKNOWN ) | | ( s - > type = = PORT_SC26XX ) )
return 0 ;
if ( s - > irq = = port - > irq )
return 0 ;
return - EINVAL ;
}
static const struct uart_ops sccnxp_ops = {
. tx_empty = sccnxp_tx_empty ,
. set_mctrl = sccnxp_set_mctrl ,
. get_mctrl = sccnxp_get_mctrl ,
. stop_tx = sccnxp_stop_tx ,
. start_tx = sccnxp_start_tx ,
. stop_rx = sccnxp_stop_rx ,
. enable_ms = sccnxp_enable_ms ,
. break_ctl = sccnxp_break_ctl ,
. startup = sccnxp_startup ,
. shutdown = sccnxp_shutdown ,
. set_termios = sccnxp_set_termios ,
. type = sccnxp_type ,
. release_port = sccnxp_release_port ,
. request_port = sccnxp_request_port ,
. config_port = sccnxp_config_port ,
. verify_port = sccnxp_verify_port ,
} ;
# ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
static void sccnxp_console_putchar ( struct uart_port * port , int c )
{
int tryes = 100000 ;
while ( tryes - - ) {
if ( sccnxp_port_read ( port , SCCNXP_SR_REG ) & SR_TXRDY ) {
sccnxp_port_write ( port , SCCNXP_THR_REG , c ) ;
break ;
}
barrier ( ) ;
}
}
static void sccnxp_console_write ( struct console * co , const char * c , unsigned n )
{
struct sccnxp_port * s = ( struct sccnxp_port * ) co - > data ;
struct uart_port * port = & s - > port [ co - > index ] ;
mutex_lock ( & s - > sccnxp_mutex ) ;
uart_console_write ( port , c , n , sccnxp_console_putchar ) ;
mutex_unlock ( & s - > sccnxp_mutex ) ;
}
static int sccnxp_console_setup ( struct console * co , char * options )
{
struct sccnxp_port * s = ( struct sccnxp_port * ) co - > data ;
struct uart_port * port = & s - > port [ ( co - > index > 0 ) ? co - > index : 0 ] ;
int baud = 9600 , bits = 8 , parity = ' n ' , flow = ' n ' ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( port , co , baud , parity , bits , flow ) ;
}
# endif
static int __devinit sccnxp_probe ( struct platform_device * pdev )
{
struct resource * res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
int chiptype = pdev - > id_entry - > driver_data ;
struct sccnxp_pdata * pdata = dev_get_platdata ( & pdev - > dev ) ;
int i , ret , fifosize , freq_min , freq_max ;
struct sccnxp_port * s ;
void __iomem * membase ;
if ( ! res ) {
dev_err ( & pdev - > dev , " Missing memory resource data \n " ) ;
return - EADDRNOTAVAIL ;
}
dev_set_name ( & pdev - > dev , SCCNXP_NAME ) ;
s = devm_kzalloc ( & pdev - > dev , sizeof ( struct sccnxp_port ) , GFP_KERNEL ) ;
if ( ! s ) {
dev_err ( & pdev - > dev , " Error allocating port structure \n " ) ;
return - ENOMEM ;
}
platform_set_drvdata ( pdev , s ) ;
mutex_init ( & s - > sccnxp_mutex ) ;
/* Individual chip settings */
switch ( chiptype ) {
case SCCNXP_TYPE_SC2681 :
s - > name = " SC2681 " ;
s - > uart . nr = 2 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x0f ;
s - > flags = SCCNXP_HAVE_IO ;
fifosize = 3 ;
freq_min = 1000000 ;
freq_max = 4000000 ;
break ;
case SCCNXP_TYPE_SC2691 :
s - > name = " SC2691 " ;
s - > uart . nr = 1 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x07 ;
s - > flags = 0 ;
fifosize = 3 ;
freq_min = 1000000 ;
freq_max = 4000000 ;
break ;
case SCCNXP_TYPE_SC2692 :
s - > name = " SC2692 " ;
s - > uart . nr = 2 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x0f ;
s - > flags = SCCNXP_HAVE_IO ;
fifosize = 3 ;
freq_min = 1000000 ;
freq_max = 4000000 ;
break ;
case SCCNXP_TYPE_SC2891 :
s - > name = " SC2891 " ;
s - > uart . nr = 1 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x0f ;
s - > flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0 ;
fifosize = 16 ;
freq_min = 100000 ;
freq_max = 8000000 ;
break ;
case SCCNXP_TYPE_SC2892 :
s - > name = " SC2892 " ;
s - > uart . nr = 2 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x0f ;
s - > flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0 ;
fifosize = 16 ;
freq_min = 100000 ;
freq_max = 8000000 ;
break ;
case SCCNXP_TYPE_SC28202 :
s - > name = " SC28202 " ;
s - > uart . nr = 2 ;
s - > freq_std = 14745600 ;
s - > addr_mask = 0x7f ;
s - > flags = SCCNXP_HAVE_IO | SCCNXP_HAVE_MR0 ;
fifosize = 256 ;
freq_min = 1000000 ;
freq_max = 50000000 ;
break ;
case SCCNXP_TYPE_SC68681 :
s - > name = " SC68681 " ;
s - > uart . nr = 2 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x0f ;
s - > flags = SCCNXP_HAVE_IO ;
fifosize = 3 ;
freq_min = 1000000 ;
freq_max = 4000000 ;
break ;
case SCCNXP_TYPE_SC68692 :
s - > name = " SC68692 " ;
s - > uart . nr = 2 ;
s - > freq_std = 3686400 ;
s - > addr_mask = 0x0f ;
s - > flags = SCCNXP_HAVE_IO ;
fifosize = 3 ;
freq_min = 1000000 ;
freq_max = 4000000 ;
break ;
default :
dev_err ( & pdev - > dev , " Unsupported chip type %i \n " , chiptype ) ;
ret = - ENOTSUPP ;
goto err_out ;
}
if ( ! pdata ) {
dev_warn ( & pdev - > dev ,
" No platform data supplied, using defaults \n " ) ;
s - > pdata . frequency = s - > freq_std ;
} else
memcpy ( & s - > pdata , pdata , sizeof ( struct sccnxp_pdata ) ) ;
s - > irq = platform_get_irq ( pdev , 0 ) ;
if ( s - > irq < = 0 ) {
dev_err ( & pdev - > dev , " Missing irq resource data \n " ) ;
ret = - ENXIO ;
goto err_out ;
}
/* Check input frequency */
if ( ( s - > pdata . frequency < freq_min ) | |
( s - > pdata . frequency > freq_max ) ) {
dev_err ( & pdev - > dev , " Frequency out of bounds \n " ) ;
ret = - EINVAL ;
goto err_out ;
}
membase = devm_request_and_ioremap ( & pdev - > dev , res ) ;
if ( ! membase ) {
dev_err ( & pdev - > dev , " Failed to ioremap \n " ) ;
ret = - EIO ;
goto err_out ;
}
s - > uart . owner = THIS_MODULE ;
s - > uart . dev_name = " ttySC " ;
s - > uart . major = SCCNXP_MAJOR ;
s - > uart . minor = SCCNXP_MINOR ;
# ifdef CONFIG_SERIAL_SCCNXP_CONSOLE
s - > uart . cons = & s - > console ;
s - > uart . cons - > device = uart_console_device ;
s - > uart . cons - > write = sccnxp_console_write ;
s - > uart . cons - > setup = sccnxp_console_setup ;
s - > uart . cons - > flags = CON_PRINTBUFFER ;
s - > uart . cons - > index = - 1 ;
s - > uart . cons - > data = s ;
strcpy ( s - > uart . cons - > name , " ttySC " ) ;
# endif
ret = uart_register_driver ( & s - > uart ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Registering UART driver failed \n " ) ;
goto err_out ;
}
for ( i = 0 ; i < s - > uart . nr ; i + + ) {
s - > port [ i ] . line = i ;
s - > port [ i ] . dev = & pdev - > dev ;
s - > port [ i ] . irq = s - > irq ;
s - > port [ i ] . type = PORT_SC26XX ;
s - > port [ i ] . fifosize = fifosize ;
s - > port [ i ] . flags = UPF_SKIP_TEST | UPF_FIXED_TYPE ;
s - > port [ i ] . iotype = UPIO_MEM ;
s - > port [ i ] . mapbase = res - > start ;
s - > port [ i ] . membase = membase ;
s - > port [ i ] . regshift = s - > pdata . reg_shift ;
s - > port [ i ] . uartclk = s - > pdata . frequency ;
s - > port [ i ] . ops = & sccnxp_ops ;
uart_add_one_port ( & s - > uart , & s - > port [ i ] ) ;
/* Set direction to input */
if ( s - > flags & SCCNXP_HAVE_IO )
sccnxp_set_bit ( & s - > port [ i ] , DIR_OP , 0 ) ;
}
/* Disable interrupts */
s - > imr = 0 ;
sccnxp_write ( & s - > port [ 0 ] , SCCNXP_IMR_REG , 0 ) ;
/* Board specific configure */
if ( s - > pdata . init )
s - > pdata . init ( ) ;
ret = devm_request_threaded_irq ( & pdev - > dev , s - > irq , NULL , sccnxp_ist ,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
dev_name ( & pdev - > dev ) , s ) ;
if ( ! ret )
return 0 ;
dev_err ( & pdev - > dev , " Unable to reguest IRQ %i \n " , s - > irq ) ;
err_out :
platform_set_drvdata ( pdev , NULL ) ;
return ret ;
}
static int __devexit sccnxp_remove ( struct platform_device * pdev )
{
int i ;
struct sccnxp_port * s = platform_get_drvdata ( pdev ) ;
devm_free_irq ( & pdev - > dev , s - > irq , s ) ;
for ( i = 0 ; i < s - > uart . nr ; i + + )
uart_remove_one_port ( & s - > uart , & s - > port [ i ] ) ;
uart_unregister_driver ( & s - > uart ) ;
platform_set_drvdata ( pdev , NULL ) ;
if ( s - > pdata . exit )
s - > pdata . exit ( ) ;
return 0 ;
}
static const struct platform_device_id sccnxp_id_table [ ] = {
{ " sc2681 " , SCCNXP_TYPE_SC2681 } ,
{ " sc2691 " , SCCNXP_TYPE_SC2691 } ,
{ " sc2692 " , SCCNXP_TYPE_SC2692 } ,
{ " sc2891 " , SCCNXP_TYPE_SC2891 } ,
{ " sc2892 " , SCCNXP_TYPE_SC2892 } ,
{ " sc28202 " , SCCNXP_TYPE_SC28202 } ,
{ " sc68681 " , SCCNXP_TYPE_SC68681 } ,
{ " sc68692 " , SCCNXP_TYPE_SC68692 } ,
} ;
MODULE_DEVICE_TABLE ( platform , sccnxp_id_table ) ;
static struct platform_driver sccnxp_uart_driver = {
. driver = {
. name = SCCNXP_NAME ,
. owner = THIS_MODULE ,
} ,
. probe = sccnxp_probe ,
. remove = __devexit_p ( sccnxp_remove ) ,
. id_table = sccnxp_id_table ,
} ;
module_platform_driver ( sccnxp_uart_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_AUTHOR ( " Alexander Shiyan <shc_work@mail.ru> " ) ;
MODULE_DESCRIPTION ( " SCCNXP serial driver " ) ;