2017-11-06 20:11:51 +03:00
// SPDX-License-Identifier: GPL-2.0
2011-04-05 16:10:57 +04:00
/*
* Based on drivers / char / serial . c , by Linus Torvalds , Theodore Ts ' o .
*
* Copyright ( C ) 2004 Infineon IFAP DC COM CPE
* Copyright ( C ) 2007 Felix Fietkau < nbd @ openwrt . org >
2016-12-20 22:01:45 +03:00
* Copyright ( C ) 2007 John Crispin < john @ phrozen . org >
2011-04-05 16:10:57 +04:00
* Copyright ( C ) 2010 Thomas Langer , < thomas . langer @ lantiq . com >
*/
2018-10-16 12:19:11 +03:00
# include <linux/clk.h>
2011-04-05 16:10:57 +04:00
# include <linux/console.h>
# include <linux/device.h>
2018-10-16 12:19:11 +03:00
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/ioport.h>
2018-10-16 12:19:13 +03:00
# include <linux/lantiq.h>
2012-04-13 11:37:09 +04:00
# include <linux/of_address.h>
# include <linux/of_irq.h>
2018-10-16 12:19:11 +03:00
# include <linux/of_platform.h>
# include <linux/serial.h>
# include <linux/serial_core.h>
# include <linux/slab.h>
# include <linux/sysrq.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
2011-04-05 16:10:57 +04:00
# define PORT_LTQ_ASC 111
# define MAXPORTS 2
# define UART_DUMMY_UER_RX 1
2012-04-13 11:37:09 +04:00
# define DRVNAME "lantiq,asc"
2011-04-05 16:10:57 +04:00
# ifdef __BIG_ENDIAN
# define LTQ_ASC_TBUF (0x0020 + 3)
# define LTQ_ASC_RBUF (0x0024 + 3)
# else
# define LTQ_ASC_TBUF 0x0020
# define LTQ_ASC_RBUF 0x0024
# endif
# define LTQ_ASC_FSTAT 0x0048
# define LTQ_ASC_WHBSTATE 0x0018
# define LTQ_ASC_STATE 0x0014
# define LTQ_ASC_IRNCR 0x00F8
# define LTQ_ASC_CLC 0x0000
# define LTQ_ASC_ID 0x0008
# define LTQ_ASC_PISEL 0x0004
# define LTQ_ASC_TXFCON 0x0044
# define LTQ_ASC_RXFCON 0x0040
# define LTQ_ASC_CON 0x0010
# define LTQ_ASC_BG 0x0050
# define LTQ_ASC_IRNREN 0x00F4
# define ASC_IRNREN_TX 0x1
# define ASC_IRNREN_RX 0x2
# define ASC_IRNREN_ERR 0x4
# define ASC_IRNREN_TX_BUF 0x8
# define ASC_IRNCR_TIR 0x1
# define ASC_IRNCR_RIR 0x2
# define ASC_IRNCR_EIR 0x4
2019-08-08 13:02:08 +03:00
# define ASC_IRNCR_MASK GENMASK(2, 0)
2011-04-05 16:10:57 +04:00
# define ASCOPT_CSIZE 0x3
# define TXFIFO_FL 1
# define RXFIFO_FL 1
# define ASCCLC_DISS 0x2
# define ASCCLC_RMCMASK 0x0000FF00
# define ASCCLC_RMCOFFSET 8
# define ASCCON_M_8ASYNC 0x0
# define ASCCON_M_7ASYNC 0x2
# define ASCCON_ODD 0x00000020
# define ASCCON_STP 0x00000080
# define ASCCON_BRS 0x00000100
# define ASCCON_FDE 0x00000200
# define ASCCON_R 0x00008000
# define ASCCON_FEN 0x00020000
# define ASCCON_ROEN 0x00080000
# define ASCCON_TOEN 0x00100000
# define ASCSTATE_PE 0x00010000
# define ASCSTATE_FE 0x00020000
# define ASCSTATE_ROE 0x00080000
# define ASCSTATE_ANY (ASCSTATE_ROE|ASCSTATE_PE|ASCSTATE_FE)
# define ASCWHBSTATE_CLRREN 0x00000001
# define ASCWHBSTATE_SETREN 0x00000002
# define ASCWHBSTATE_CLRPE 0x00000004
# define ASCWHBSTATE_CLRFE 0x00000008
# define ASCWHBSTATE_CLRROE 0x00000020
# define ASCTXFCON_TXFEN 0x0001
# define ASCTXFCON_TXFFLU 0x0002
# define ASCTXFCON_TXFITLMASK 0x3F00
# define ASCTXFCON_TXFITLOFF 8
# define ASCRXFCON_RXFEN 0x0001
# define ASCRXFCON_RXFFLU 0x0002
# define ASCRXFCON_RXFITLMASK 0x3F00
# define ASCRXFCON_RXFITLOFF 8
# define ASCFSTAT_RXFFLMASK 0x003F
# define ASCFSTAT_TXFFLMASK 0x3F00
# define ASCFSTAT_TXFREEMASK 0x3F000000
# define ASCFSTAT_TXFREEOFF 24
static void lqasc_tx_chars ( struct uart_port * port ) ;
static struct ltq_uart_port * lqasc_port [ MAXPORTS ] ;
static struct uart_driver lqasc_reg ;
2019-08-08 13:02:07 +03:00
struct ltq_soc_data {
int ( * fetch_irq ) ( struct device * dev , struct ltq_uart_port * ltq_port ) ;
int ( * request_irq ) ( struct uart_port * port ) ;
void ( * free_irq ) ( struct uart_port * port ) ;
} ;
2011-04-05 16:10:57 +04:00
struct ltq_uart_port {
struct uart_port port ;
2012-04-13 11:37:09 +04:00
/* clock used to derive divider */
2018-10-16 12:19:08 +03:00
struct clk * freqclk ;
2012-04-13 11:37:09 +04:00
/* clock gating of the ASC core */
2011-04-05 16:10:57 +04:00
struct clk * clk ;
unsigned int tx_irq ;
unsigned int rx_irq ;
unsigned int err_irq ;
2019-08-08 13:02:08 +03:00
unsigned int common_irq ;
2019-08-07 12:21:31 +03:00
spinlock_t lock ; /* exclusive access for multi core */
2019-08-08 13:02:07 +03:00
const struct ltq_soc_data * soc ;
2011-04-05 16:10:57 +04:00
} ;
2018-10-16 12:19:05 +03:00
static inline void asc_update_bits ( u32 clear , u32 set , void __iomem * reg )
{
2019-01-06 21:50:37 +03:00
u32 tmp = __raw_readl ( reg ) ;
2018-10-16 12:19:05 +03:00
2019-01-06 21:50:37 +03:00
__raw_writel ( ( tmp & ~ clear ) | set , reg ) ;
2018-10-16 12:19:05 +03:00
}
2011-04-05 16:10:57 +04:00
static inline struct
ltq_uart_port * to_ltq_uart_port ( struct uart_port * port )
{
return container_of ( port , struct ltq_uart_port , port ) ;
}
static void
lqasc_stop_tx ( struct uart_port * port )
{
return ;
}
static void
lqasc_start_tx ( struct uart_port * port )
{
unsigned long flags ;
2019-08-07 12:21:31 +03:00
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
lqasc_tx_chars ( port ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
return ;
}
static void
lqasc_stop_rx ( struct uart_port * port )
{
2019-01-06 21:50:37 +03:00
__raw_writel ( ASCWHBSTATE_CLRREN , port - > membase + LTQ_ASC_WHBSTATE ) ;
2011-04-05 16:10:57 +04:00
}
static int
lqasc_rx_chars ( struct uart_port * port )
{
2013-01-03 18:53:03 +04:00
struct tty_port * tport = & port - > state - > port ;
2011-04-05 16:10:57 +04:00
unsigned int ch = 0 , rsr = 0 , fifocnt ;
2019-01-06 21:50:37 +03:00
fifocnt = __raw_readl ( port - > membase + LTQ_ASC_FSTAT ) &
ASCFSTAT_RXFFLMASK ;
2011-04-05 16:10:57 +04:00
while ( fifocnt - - ) {
u8 flag = TTY_NORMAL ;
2018-10-16 12:19:07 +03:00
ch = readb ( port - > membase + LTQ_ASC_RBUF ) ;
2019-01-06 21:50:37 +03:00
rsr = ( __raw_readl ( port - > membase + LTQ_ASC_STATE )
2011-04-05 16:10:57 +04:00
& ASCSTATE_ANY ) | UART_DUMMY_UER_RX ;
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( tport ) ;
2011-04-05 16:10:57 +04:00
port - > icount . rx + + ;
/*
* Note that the error handling code is
* out of the main execution path
*/
if ( rsr & ASCSTATE_ANY ) {
if ( rsr & ASCSTATE_PE ) {
port - > icount . parity + + ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , ASCWHBSTATE_CLRPE ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_WHBSTATE ) ;
} else if ( rsr & ASCSTATE_FE ) {
port - > icount . frame + + ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , ASCWHBSTATE_CLRFE ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_WHBSTATE ) ;
}
if ( rsr & ASCSTATE_ROE ) {
port - > icount . overrun + + ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , ASCWHBSTATE_CLRROE ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_WHBSTATE ) ;
}
rsr & = port - > read_status_mask ;
if ( rsr & ASCSTATE_PE )
flag = TTY_PARITY ;
else if ( rsr & ASCSTATE_FE )
flag = TTY_FRAME ;
}
if ( ( rsr & port - > ignore_status_mask ) = = 0 )
2013-01-03 18:53:03 +04:00
tty_insert_flip_char ( tport , ch , flag ) ;
2011-04-05 16:10:57 +04:00
if ( rsr & ASCSTATE_ROE )
/*
* Overrun is special , since it ' s reported
* immediately , and doesn ' t affect the current
* character
*/
2013-01-03 18:53:03 +04:00
tty_insert_flip_char ( tport , 0 , TTY_OVERRUN ) ;
2011-04-05 16:10:57 +04:00
}
2013-01-03 18:53:06 +04:00
2011-04-05 16:10:57 +04:00
if ( ch ! = 0 )
2013-01-03 18:53:06 +04:00
tty_flip_buffer_push ( tport ) ;
2011-04-05 16:10:57 +04:00
return 0 ;
}
static void
lqasc_tx_chars ( struct uart_port * port )
{
struct circ_buf * xmit = & port - > state - > xmit ;
if ( uart_tx_stopped ( port ) ) {
lqasc_stop_tx ( port ) ;
return ;
}
2019-01-06 21:50:37 +03:00
while ( ( ( __raw_readl ( port - > membase + LTQ_ASC_FSTAT ) &
2011-04-05 16:10:57 +04:00
ASCFSTAT_TXFREEMASK ) > > ASCFSTAT_TXFREEOFF ) ! = 0 ) {
if ( port - > x_char ) {
2018-10-16 12:19:07 +03:00
writeb ( port - > x_char , port - > membase + LTQ_ASC_TBUF ) ;
2011-04-05 16:10:57 +04:00
port - > icount . tx + + ;
port - > x_char = 0 ;
continue ;
}
if ( uart_circ_empty ( xmit ) )
break ;
2018-10-16 12:19:07 +03:00
writeb ( port - > state - > xmit . buf [ port - > state - > xmit . tail ] ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_TBUF ) ;
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
lqasc_tx_int ( int irq , void * _port )
{
unsigned long flags ;
struct uart_port * port = ( struct uart_port * ) _port ;
2019-08-07 12:21:31 +03:00
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2019-01-06 21:50:37 +03:00
__raw_writel ( ASC_IRNCR_TIR , port - > membase + LTQ_ASC_IRNCR ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
lqasc_start_tx ( port ) ;
return IRQ_HANDLED ;
}
static irqreturn_t
lqasc_err_int ( int irq , void * _port )
{
unsigned long flags ;
struct uart_port * port = ( struct uart_port * ) _port ;
2019-08-07 12:21:31 +03:00
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
/* clear any pending interrupts */
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , ASCWHBSTATE_CLRPE | ASCWHBSTATE_CLRFE |
2011-04-05 16:10:57 +04:00
ASCWHBSTATE_CLRROE , port - > membase + LTQ_ASC_WHBSTATE ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
return IRQ_HANDLED ;
}
static irqreturn_t
lqasc_rx_int ( int irq , void * _port )
{
unsigned long flags ;
struct uart_port * port = ( struct uart_port * ) _port ;
2019-08-07 12:21:31 +03:00
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2019-01-06 21:50:37 +03:00
__raw_writel ( ASC_IRNCR_RIR , port - > membase + LTQ_ASC_IRNCR ) ;
2011-04-05 16:10:57 +04:00
lqasc_rx_chars ( port ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
return IRQ_HANDLED ;
}
2019-08-08 13:02:08 +03:00
static irqreturn_t lqasc_irq ( int irq , void * p )
{
unsigned long flags ;
u32 stat ;
struct uart_port * port = p ;
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
stat = readl ( port - > membase + LTQ_ASC_IRNCR ) ;
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
if ( ! ( stat & ASC_IRNCR_MASK ) )
return IRQ_NONE ;
if ( stat & ASC_IRNCR_TIR )
lqasc_tx_int ( irq , p ) ;
if ( stat & ASC_IRNCR_RIR )
lqasc_rx_int ( irq , p ) ;
if ( stat & ASC_IRNCR_EIR )
lqasc_err_int ( irq , p ) ;
return IRQ_HANDLED ;
}
2011-04-05 16:10:57 +04:00
static unsigned int
lqasc_tx_empty ( struct uart_port * port )
{
int status ;
2019-01-06 21:50:37 +03:00
status = __raw_readl ( port - > membase + LTQ_ASC_FSTAT ) &
ASCFSTAT_TXFFLMASK ;
2011-04-05 16:10:57 +04:00
return status ? 0 : TIOCSER_TEMT ;
}
static unsigned int
lqasc_get_mctrl ( struct uart_port * port )
{
return TIOCM_CTS | TIOCM_CAR | TIOCM_DSR ;
}
static void
lqasc_set_mctrl ( struct uart_port * port , u_int mctrl )
{
}
static void
lqasc_break_ctl ( struct uart_port * port , int break_state )
{
}
static int
lqasc_startup ( struct uart_port * port )
{
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
int retval ;
2019-08-07 12:21:31 +03:00
unsigned long flags ;
2011-04-05 16:10:57 +04:00
2013-08-09 22:54:31 +04:00
if ( ! IS_ERR ( ltq_port - > clk ) )
2018-10-16 12:19:09 +03:00
clk_prepare_enable ( ltq_port - > clk ) ;
2018-10-16 12:19:08 +03:00
port - > uartclk = clk_get_rate ( ltq_port - > freqclk ) ;
2011-04-05 16:10:57 +04:00
2019-08-07 12:21:31 +03:00
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( ASCCLC_DISS | ASCCLC_RMCMASK , ( 1 < < ASCCLC_RMCOFFSET ) ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_CLC ) ;
2019-01-06 21:50:37 +03:00
__raw_writel ( 0 , port - > membase + LTQ_ASC_PISEL ) ;
__raw_writel (
2011-04-05 16:10:57 +04:00
( ( TXFIFO_FL < < ASCTXFCON_TXFITLOFF ) & ASCTXFCON_TXFITLMASK ) |
ASCTXFCON_TXFEN | ASCTXFCON_TXFFLU ,
port - > membase + LTQ_ASC_TXFCON ) ;
2019-01-06 21:50:37 +03:00
__raw_writel (
2011-04-05 16:10:57 +04:00
( ( RXFIFO_FL < < ASCRXFCON_RXFITLOFF ) & ASCRXFCON_RXFITLMASK )
| ASCRXFCON_RXFEN | ASCRXFCON_RXFFLU ,
port - > membase + LTQ_ASC_RXFCON ) ;
/* make sure other settings are written to hardware before
* setting enable bits
*/
wmb ( ) ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , ASCCON_M_8ASYNC | ASCCON_FEN | ASCCON_TOEN |
2011-04-05 16:10:57 +04:00
ASCCON_ROEN , port - > membase + LTQ_ASC_CON ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2019-08-08 13:02:07 +03:00
retval = ltq_port - > soc - > request_irq ( port ) ;
if ( retval )
2011-04-05 16:10:57 +04:00
return retval ;
2019-01-06 21:50:37 +03:00
__raw_writel ( ASC_IRNREN_RX | ASC_IRNREN_ERR | ASC_IRNREN_TX ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_IRNREN ) ;
return retval ;
}
static void
lqasc_shutdown ( struct uart_port * port )
{
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
2019-08-07 12:21:31 +03:00
unsigned long flags ;
2019-08-08 13:02:07 +03:00
ltq_port - > soc - > free_irq ( port ) ;
2011-04-05 16:10:57 +04:00
2019-08-07 12:21:31 +03:00
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2019-01-06 21:50:37 +03:00
__raw_writel ( 0 , port - > membase + LTQ_ASC_CON ) ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( ASCRXFCON_RXFEN , ASCRXFCON_RXFFLU ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_RXFCON ) ;
2018-10-16 12:19:05 +03:00
asc_update_bits ( ASCTXFCON_TXFEN , ASCTXFCON_TXFFLU ,
2011-04-05 16:10:57 +04:00
port - > membase + LTQ_ASC_TXFCON ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2013-08-09 22:54:31 +04:00
if ( ! IS_ERR ( ltq_port - > clk ) )
2018-10-16 12:19:09 +03:00
clk_disable_unprepare ( ltq_port - > clk ) ;
2011-04-05 16:10:57 +04:00
}
static void
lqasc_set_termios ( struct uart_port * port ,
struct ktermios * new , struct ktermios * old )
{
unsigned int cflag ;
unsigned int iflag ;
unsigned int divisor ;
unsigned int baud ;
unsigned int con = 0 ;
unsigned long flags ;
2019-08-07 12:21:31 +03:00
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
2011-04-05 16:10:57 +04:00
cflag = new - > c_cflag ;
iflag = new - > c_iflag ;
switch ( cflag & CSIZE ) {
case CS7 :
con = ASCCON_M_7ASYNC ;
break ;
case CS5 :
case CS6 :
default :
new - > c_cflag & = ~ CSIZE ;
new - > c_cflag | = CS8 ;
con = ASCCON_M_8ASYNC ;
break ;
}
cflag & = ~ CMSPAR ; /* Mark/Space parity is not supported */
if ( cflag & CSTOPB )
con | = ASCCON_STP ;
if ( cflag & PARENB ) {
if ( ! ( cflag & PARODD ) )
con & = ~ ASCCON_ODD ;
else
con | = ASCCON_ODD ;
}
port - > read_status_mask = ASCSTATE_ROE ;
if ( iflag & INPCK )
port - > read_status_mask | = ASCSTATE_FE | ASCSTATE_PE ;
port - > ignore_status_mask = 0 ;
if ( iflag & IGNPAR )
port - > ignore_status_mask | = ASCSTATE_FE | ASCSTATE_PE ;
if ( iflag & IGNBRK ) {
/*
* If we ' re ignoring parity and break indicators ,
* ignore overruns too ( for real raw support ) .
*/
if ( iflag & IGNPAR )
port - > ignore_status_mask | = ASCSTATE_ROE ;
}
if ( ( cflag & CREAD ) = = 0 )
port - > ignore_status_mask | = UART_DUMMY_UER_RX ;
/* set error signals - framing, parity and overrun, enable receiver */
con | = ASCCON_FEN | ASCCON_TOEN | ASCCON_ROEN ;
2019-08-07 12:21:31 +03:00
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
/* set up CON */
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , con , port - > membase + LTQ_ASC_CON ) ;
2011-04-05 16:10:57 +04:00
/* Set baud rate - take a divider of 2 into account */
baud = uart_get_baud_rate ( port , new , old , 0 , port - > uartclk / 16 ) ;
divisor = uart_get_divisor ( port , baud ) ;
divisor = divisor / 2 - 1 ;
/* disable the baudrate generator */
2018-10-16 12:19:05 +03:00
asc_update_bits ( ASCCON_R , 0 , port - > membase + LTQ_ASC_CON ) ;
2011-04-05 16:10:57 +04:00
/* make sure the fractional divider is off */
2018-10-16 12:19:05 +03:00
asc_update_bits ( ASCCON_FDE , 0 , port - > membase + LTQ_ASC_CON ) ;
2011-04-05 16:10:57 +04:00
/* set up to use divisor of 2 */
2018-10-16 12:19:05 +03:00
asc_update_bits ( ASCCON_BRS , 0 , port - > membase + LTQ_ASC_CON ) ;
2011-04-05 16:10:57 +04:00
/* now we can write the new baudrate into the register */
2019-01-06 21:50:37 +03:00
__raw_writel ( divisor , port - > membase + LTQ_ASC_BG ) ;
2011-04-05 16:10:57 +04:00
/* turn the baudrate generator back on */
2018-10-16 12:19:05 +03:00
asc_update_bits ( 0 , ASCCON_R , port - > membase + LTQ_ASC_CON ) ;
2011-04-05 16:10:57 +04:00
/* enable rx */
2019-01-06 21:50:37 +03:00
__raw_writel ( ASCWHBSTATE_SETREN , port - > membase + LTQ_ASC_WHBSTATE ) ;
2011-04-05 16:10:57 +04:00
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
/* Don't rewrite B0 */
2011-08-03 16:37:24 +04:00
if ( tty_termios_baud_rate ( new ) )
2011-04-05 16:10:57 +04:00
tty_termios_encode_baud_rate ( new , baud , baud ) ;
2011-08-03 16:37:24 +04:00
uart_update_timeout ( port , cflag , baud ) ;
2011-04-05 16:10:57 +04:00
}
static const char *
lqasc_type ( struct uart_port * port )
{
if ( port - > type = = PORT_LTQ_ASC )
return DRVNAME ;
else
return NULL ;
}
static void
lqasc_release_port ( struct uart_port * port )
{
2014-11-06 16:30:42 +03:00
struct platform_device * pdev = to_platform_device ( port - > dev ) ;
2011-04-05 16:10:57 +04:00
if ( port - > flags & UPF_IOREMAP ) {
2014-11-06 16:30:42 +03:00
devm_iounmap ( & pdev - > dev , port - > membase ) ;
2011-04-05 16:10:57 +04:00
port - > membase = NULL ;
}
}
static int
lqasc_request_port ( struct uart_port * port )
{
struct platform_device * pdev = to_platform_device ( port - > dev ) ;
struct resource * res ;
int size ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " cannot obtain I/O memory region " ) ;
return - ENODEV ;
}
size = resource_size ( res ) ;
res = devm_request_mem_region ( & pdev - > dev , res - > start ,
size , dev_name ( & pdev - > dev ) ) ;
if ( ! res ) {
dev_err ( & pdev - > dev , " cannot request I/O memory region " ) ;
return - EBUSY ;
}
if ( port - > flags & UPF_IOREMAP ) {
port - > membase = devm_ioremap_nocache ( & pdev - > dev ,
port - > mapbase , size ) ;
if ( port - > membase = = NULL )
return - ENOMEM ;
}
return 0 ;
}
static void
lqasc_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE ) {
port - > type = PORT_LTQ_ASC ;
lqasc_request_port ( port ) ;
}
}
static int
lqasc_verify_port ( struct uart_port * port ,
struct serial_struct * ser )
{
int ret = 0 ;
if ( ser - > type ! = PORT_UNKNOWN & & ser - > type ! = PORT_LTQ_ASC )
ret = - EINVAL ;
if ( ser - > irq < 0 | | ser - > irq > = NR_IRQS )
ret = - EINVAL ;
if ( ser - > baud_base < 9600 )
ret = - EINVAL ;
return ret ;
}
2017-01-25 20:48:52 +03:00
static const struct uart_ops lqasc_pops = {
2011-04-05 16:10:57 +04:00
. tx_empty = lqasc_tx_empty ,
. set_mctrl = lqasc_set_mctrl ,
. get_mctrl = lqasc_get_mctrl ,
. stop_tx = lqasc_stop_tx ,
. start_tx = lqasc_start_tx ,
. stop_rx = lqasc_stop_rx ,
. break_ctl = lqasc_break_ctl ,
. startup = lqasc_startup ,
. shutdown = lqasc_shutdown ,
. set_termios = lqasc_set_termios ,
. type = lqasc_type ,
. release_port = lqasc_release_port ,
. request_port = lqasc_request_port ,
. config_port = lqasc_config_port ,
. verify_port = lqasc_verify_port ,
} ;
static void
lqasc_console_putchar ( struct uart_port * port , int ch )
{
int fifofree ;
if ( ! port - > membase )
return ;
do {
2019-01-06 21:50:37 +03:00
fifofree = ( __raw_readl ( port - > membase + LTQ_ASC_FSTAT )
2011-04-05 16:10:57 +04:00
& ASCFSTAT_TXFREEMASK ) > > ASCFSTAT_TXFREEOFF ;
} while ( fifofree = = 0 ) ;
2018-10-16 12:19:07 +03:00
writeb ( ch , port - > membase + LTQ_ASC_TBUF ) ;
2011-04-05 16:10:57 +04:00
}
2016-12-11 23:42:23 +03:00
static void lqasc_serial_port_write ( struct uart_port * port , const char * s ,
u_int count )
{
uart_console_write ( port , s , count , lqasc_console_putchar ) ;
}
2011-04-05 16:10:57 +04:00
static void
lqasc_console_write ( struct console * co , const char * s , u_int count )
{
struct ltq_uart_port * ltq_port ;
2019-08-07 12:21:31 +03:00
unsigned long flags ;
2011-04-05 16:10:57 +04:00
if ( co - > index > = MAXPORTS )
return ;
ltq_port = lqasc_port [ co - > index ] ;
if ( ! ltq_port )
return ;
2019-08-07 12:21:31 +03:00
spin_lock_irqsave ( & ltq_port - > lock , flags ) ;
2016-12-11 23:42:23 +03:00
lqasc_serial_port_write ( & ltq_port - > port , s , count ) ;
2019-08-07 12:21:31 +03:00
spin_unlock_irqrestore ( & ltq_port - > lock , flags ) ;
2011-04-05 16:10:57 +04:00
}
static int __init
lqasc_console_setup ( struct console * co , char * options )
{
struct ltq_uart_port * ltq_port ;
struct uart_port * port ;
int baud = 115200 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
if ( co - > index > = MAXPORTS )
return - ENODEV ;
ltq_port = lqasc_port [ co - > index ] ;
if ( ! ltq_port )
return - ENODEV ;
port = & ltq_port - > port ;
2013-08-09 22:54:30 +04:00
if ( ! IS_ERR ( ltq_port - > clk ) )
2018-10-16 12:19:09 +03:00
clk_prepare_enable ( ltq_port - > clk ) ;
2013-08-09 22:54:30 +04:00
2018-10-16 12:19:08 +03:00
port - > uartclk = clk_get_rate ( ltq_port - > freqclk ) ;
2011-04-05 16:10:57 +04:00
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
return uart_set_options ( port , co , baud , parity , bits , flow ) ;
}
static struct console lqasc_console = {
. name = " ttyLTQ " ,
. write = lqasc_console_write ,
. device = uart_console_device ,
. setup = lqasc_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & lqasc_reg ,
} ;
static int __init
lqasc_console_init ( void )
{
register_console ( & lqasc_console ) ;
return 0 ;
}
console_initcall ( lqasc_console_init ) ;
2016-12-11 23:42:23 +03:00
static void lqasc_serial_early_console_write ( struct console * co ,
const char * s ,
u_int count )
{
struct earlycon_device * dev = co - > data ;
lqasc_serial_port_write ( & dev - > port , s , count ) ;
}
static int __init
lqasc_serial_early_console_setup ( struct earlycon_device * device ,
const char * opt )
{
if ( ! device - > port . membase )
return - ENODEV ;
device - > con - > write = lqasc_serial_early_console_write ;
return 0 ;
}
2019-08-08 13:02:06 +03:00
OF_EARLYCON_DECLARE ( lantiq , " lantiq,asc " , lqasc_serial_early_console_setup ) ;
2019-08-08 13:02:08 +03:00
OF_EARLYCON_DECLARE ( lantiq , " intel,lgm-asc " , lqasc_serial_early_console_setup ) ;
2016-12-11 23:42:23 +03:00
2011-04-05 16:10:57 +04:00
static struct uart_driver lqasc_reg = {
. owner = THIS_MODULE ,
. driver_name = DRVNAME ,
. dev_name = " ttyLTQ " ,
. major = 0 ,
. minor = 0 ,
. nr = MAXPORTS ,
. cons = & lqasc_console ,
} ;
2019-08-08 13:02:07 +03:00
static int fetch_irq_lantiq ( struct device * dev , struct ltq_uart_port * ltq_port )
{
struct uart_port * port = & ltq_port - > port ;
struct resource irqres [ 3 ] ;
int ret ;
ret = of_irq_to_resource_table ( dev - > of_node , irqres , 3 ) ;
if ( ret ! = 3 ) {
dev_err ( dev ,
" failed to get IRQs for serial port \n " ) ;
return - ENODEV ;
}
ltq_port - > tx_irq = irqres [ 0 ] . start ;
ltq_port - > rx_irq = irqres [ 1 ] . start ;
ltq_port - > err_irq = irqres [ 2 ] . start ;
port - > irq = irqres [ 0 ] . start ;
return 0 ;
}
static int request_irq_lantiq ( struct uart_port * port )
{
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
int retval ;
retval = request_irq ( ltq_port - > tx_irq , lqasc_tx_int ,
0 , " asc_tx " , port ) ;
if ( retval ) {
dev_err ( port - > dev , " failed to request asc_tx \n " ) ;
return retval ;
}
retval = request_irq ( ltq_port - > rx_irq , lqasc_rx_int ,
0 , " asc_rx " , port ) ;
if ( retval ) {
dev_err ( port - > dev , " failed to request asc_rx \n " ) ;
goto err1 ;
}
retval = request_irq ( ltq_port - > err_irq , lqasc_err_int ,
0 , " asc_err " , port ) ;
if ( retval ) {
dev_err ( port - > dev , " failed to request asc_err \n " ) ;
goto err2 ;
}
return 0 ;
err2 :
free_irq ( ltq_port - > rx_irq , port ) ;
err1 :
free_irq ( ltq_port - > tx_irq , port ) ;
return retval ;
}
static void free_irq_lantiq ( struct uart_port * port )
{
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
free_irq ( ltq_port - > tx_irq , port ) ;
free_irq ( ltq_port - > rx_irq , port ) ;
free_irq ( ltq_port - > err_irq , port ) ;
}
2019-08-08 13:02:08 +03:00
static int fetch_irq_intel ( struct device * dev , struct ltq_uart_port * ltq_port )
{
struct uart_port * port = & ltq_port - > port ;
int ret ;
ret = of_irq_get ( dev - > of_node , 0 ) ;
if ( ret < 0 ) {
dev_err ( dev , " failed to fetch IRQ for serial port \n " ) ;
return ret ;
}
ltq_port - > common_irq = ret ;
port - > irq = ret ;
return 0 ;
}
static int request_irq_intel ( struct uart_port * port )
{
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
int retval ;
retval = request_irq ( ltq_port - > common_irq , lqasc_irq , 0 ,
" asc_irq " , port ) ;
if ( retval )
dev_err ( port - > dev , " failed to request asc_irq \n " ) ;
return retval ;
}
static void free_irq_intel ( struct uart_port * port )
{
struct ltq_uart_port * ltq_port = to_ltq_uart_port ( port ) ;
free_irq ( ltq_port - > common_irq , port ) ;
}
2011-04-05 16:10:57 +04:00
static int __init
lqasc_probe ( struct platform_device * pdev )
{
2012-04-13 11:37:09 +04:00
struct device_node * node = pdev - > dev . of_node ;
2011-04-05 16:10:57 +04:00
struct ltq_uart_port * ltq_port ;
struct uart_port * port ;
2019-08-08 13:02:07 +03:00
struct resource * mmres ;
2018-10-16 12:19:04 +03:00
int line ;
2011-04-05 16:10:57 +04:00
int ret ;
mmres = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2019-08-08 13:02:07 +03:00
if ( ! mmres ) {
2012-04-13 11:37:09 +04:00
dev_err ( & pdev - > dev ,
2019-08-08 13:02:07 +03:00
" failed to get memory for serial port \n " ) ;
2011-04-05 16:10:57 +04:00
return - ENODEV ;
2012-04-13 11:37:09 +04:00
}
2011-04-05 16:10:57 +04:00
2019-08-08 13:02:07 +03:00
ltq_port = devm_kzalloc ( & pdev - > dev , sizeof ( struct ltq_uart_port ) ,
GFP_KERNEL ) ;
if ( ! ltq_port )
return - ENOMEM ;
port = & ltq_port - > port ;
ltq_port - > soc = of_device_get_match_data ( & pdev - > dev ) ;
ret = ltq_port - > soc - > fetch_irq ( & pdev - > dev , ltq_port ) ;
if ( ret )
return ret ;
2018-10-16 12:19:04 +03:00
/* get serial id */
line = of_alias_get_id ( node , " serial " ) ;
if ( line < 0 ) {
if ( IS_ENABLED ( CONFIG_LANTIQ ) ) {
if ( mmres - > start = = CPHYSADDR ( LTQ_EARLY_ASC ) )
line = 0 ;
else
line = 1 ;
} else {
dev_err ( & pdev - > dev , " failed to get alias id, errno %d \n " ,
line ) ;
return line ;
}
}
2011-04-05 16:10:57 +04:00
2012-04-13 11:37:09 +04:00
if ( lqasc_port [ line ] ) {
dev_err ( & pdev - > dev , " port %d already allocated \n " , line ) ;
2011-04-05 16:10:57 +04:00
return - EBUSY ;
}
port - > iotype = SERIAL_IO_MEM ;
2014-06-16 17:17:05 +04:00
port - > flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP ;
2011-04-05 16:10:57 +04:00
port - > ops = & lqasc_pops ;
port - > fifosize = 16 ;
port - > type = PORT_LTQ_ASC ,
2012-04-13 11:37:09 +04:00
port - > line = line ;
2011-04-05 16:10:57 +04:00
port - > dev = & pdev - > dev ;
2012-04-13 11:37:09 +04:00
/* unused, just to be backward-compatible */
2011-04-05 16:10:57 +04:00
port - > mapbase = mmres - > start ;
2018-10-16 12:19:10 +03:00
if ( IS_ENABLED ( CONFIG_LANTIQ ) & & ! IS_ENABLED ( CONFIG_COMMON_CLK ) )
ltq_port - > freqclk = clk_get_fpi ( ) ;
else
ltq_port - > freqclk = devm_clk_get ( & pdev - > dev , " freq " ) ;
2018-10-16 12:19:08 +03:00
if ( IS_ERR ( ltq_port - > freqclk ) ) {
2012-04-13 11:37:09 +04:00
pr_err ( " failed to get fpi clk \n " ) ;
return - ENOENT ;
}
2011-04-05 16:10:57 +04:00
2012-04-13 11:37:09 +04:00
/* not all asc ports have clock gates, lets ignore the return code */
2018-10-16 12:19:10 +03:00
if ( IS_ENABLED ( CONFIG_LANTIQ ) & & ! IS_ENABLED ( CONFIG_COMMON_CLK ) )
ltq_port - > clk = clk_get ( & pdev - > dev , NULL ) ;
else
ltq_port - > clk = devm_clk_get ( & pdev - > dev , " asc " ) ;
2011-04-05 16:10:57 +04:00
2019-08-07 12:21:31 +03:00
spin_lock_init ( & ltq_port - > lock ) ;
2012-04-13 11:37:09 +04:00
lqasc_port [ line ] = ltq_port ;
2011-04-05 16:10:57 +04:00
platform_set_drvdata ( pdev , ltq_port ) ;
ret = uart_add_one_port ( & lqasc_reg , port ) ;
return ret ;
}
2019-08-08 13:02:07 +03:00
static const struct ltq_soc_data soc_data_lantiq = {
. fetch_irq = fetch_irq_lantiq ,
. request_irq = request_irq_lantiq ,
. free_irq = free_irq_lantiq ,
} ;
2019-08-08 13:02:08 +03:00
static const struct ltq_soc_data soc_data_intel = {
. fetch_irq = fetch_irq_intel ,
. request_irq = request_irq_intel ,
. free_irq = free_irq_intel ,
} ;
2012-04-13 11:37:09 +04:00
static const struct of_device_id ltq_asc_match [ ] = {
2019-08-08 13:02:07 +03:00
{ . compatible = " lantiq,asc " , . data = & soc_data_lantiq } ,
2019-08-08 13:02:08 +03:00
{ . compatible = " intel,lgm-asc " , . data = & soc_data_intel } ,
2012-04-13 11:37:09 +04:00
{ } ,
} ;
2011-04-05 16:10:57 +04:00
static struct platform_driver lqasc_driver = {
. driver = {
. name = DRVNAME ,
2012-04-13 11:37:09 +04:00
. of_match_table = ltq_asc_match ,
2011-04-05 16:10:57 +04:00
} ,
} ;
2018-10-16 12:19:14 +03:00
static int __init
2011-04-05 16:10:57 +04:00
init_lqasc ( void )
{
int ret ;
ret = uart_register_driver ( & lqasc_reg ) ;
if ( ret ! = 0 )
return ret ;
ret = platform_driver_probe ( & lqasc_driver , lqasc_probe ) ;
if ( ret ! = 0 )
uart_unregister_driver ( & lqasc_reg ) ;
return ret ;
}
2015-07-22 05:02:16 +03:00
device_initcall ( init_lqasc ) ;