2019-08-09 11:29:16 +00:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
2019-10-16 15:48:25 +03:00
* Freescale LINFlexD UART serial port driver
2019-08-09 11:29:16 +00:00
*
* Copyright 2012 - 2016 Freescale Semiconductor , Inc .
2019-09-18 18:45:49 +00:00
* Copyright 2017 - 2019 NXP
2019-08-09 11:29:16 +00:00
*/
# include <linux/console.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/serial_core.h>
# include <linux/slab.h>
# include <linux/tty_flip.h>
# include <linux/delay.h>
/* All registers are 32-bit width */
# define LINCR1 0x0000 /* LIN control register */
# define LINIER 0x0004 /* LIN interrupt enable register */
# define LINSR 0x0008 /* LIN status register */
# define LINESR 0x000C /* LIN error status register */
# define UARTCR 0x0010 /* UART mode control register */
# define UARTSR 0x0014 /* UART mode status register */
# define LINTCSR 0x0018 /* LIN timeout control status register */
# define LINOCR 0x001C /* LIN output compare register */
# define LINTOCR 0x0020 /* LIN timeout control register */
# define LINFBRR 0x0024 /* LIN fractional baud rate register */
# define LINIBRR 0x0028 /* LIN integer baud rate register */
# define LINCFR 0x002C /* LIN checksum field register */
# define LINCR2 0x0030 /* LIN control register 2 */
# define BIDR 0x0034 /* Buffer identifier register */
# define BDRL 0x0038 /* Buffer data register least significant */
# define BDRM 0x003C /* Buffer data register most significant */
# define IFER 0x0040 /* Identifier filter enable register */
# define IFMI 0x0044 /* Identifier filter match index */
# define IFMR 0x0048 /* Identifier filter mode register */
# define GCR 0x004C /* Global control register */
# define UARTPTO 0x0050 /* UART preset timeout register */
# define UARTCTO 0x0054 /* UART current timeout register */
/*
* Register field definitions
*/
# define LINFLEXD_LINCR1_INIT BIT(0)
# define LINFLEXD_LINCR1_MME BIT(4)
# define LINFLEXD_LINCR1_BF BIT(7)
# define LINFLEXD_LINSR_LINS_INITMODE BIT(12)
# define LINFLEXD_LINSR_LINS_MASK (0xF << 12)
# define LINFLEXD_LINIER_SZIE BIT(15)
# define LINFLEXD_LINIER_OCIE BIT(14)
# define LINFLEXD_LINIER_BEIE BIT(13)
# define LINFLEXD_LINIER_CEIE BIT(12)
# define LINFLEXD_LINIER_HEIE BIT(11)
# define LINFLEXD_LINIER_FEIE BIT(8)
# define LINFLEXD_LINIER_BOIE BIT(7)
# define LINFLEXD_LINIER_LSIE BIT(6)
# define LINFLEXD_LINIER_WUIE BIT(5)
# define LINFLEXD_LINIER_DBFIE BIT(4)
# define LINFLEXD_LINIER_DBEIETOIE BIT(3)
# define LINFLEXD_LINIER_DRIE BIT(2)
# define LINFLEXD_LINIER_DTIE BIT(1)
# define LINFLEXD_LINIER_HRIE BIT(0)
# define LINFLEXD_UARTCR_OSR_MASK (0xF << 24)
# define LINFLEXD_UARTCR_OSR(uartcr) (((uartcr) \
& LINFLEXD_UARTCR_OSR_MASK ) > > 24 )
# define LINFLEXD_UARTCR_ROSE BIT(23)
# define LINFLEXD_UARTCR_RFBM BIT(9)
# define LINFLEXD_UARTCR_TFBM BIT(8)
# define LINFLEXD_UARTCR_WL1 BIT(7)
# define LINFLEXD_UARTCR_PC1 BIT(6)
# define LINFLEXD_UARTCR_RXEN BIT(5)
# define LINFLEXD_UARTCR_TXEN BIT(4)
# define LINFLEXD_UARTCR_PC0 BIT(3)
# define LINFLEXD_UARTCR_PCE BIT(2)
# define LINFLEXD_UARTCR_WL0 BIT(1)
# define LINFLEXD_UARTCR_UART BIT(0)
# define LINFLEXD_UARTSR_SZF BIT(15)
# define LINFLEXD_UARTSR_OCF BIT(14)
# define LINFLEXD_UARTSR_PE3 BIT(13)
# define LINFLEXD_UARTSR_PE2 BIT(12)
# define LINFLEXD_UARTSR_PE1 BIT(11)
# define LINFLEXD_UARTSR_PE0 BIT(10)
# define LINFLEXD_UARTSR_RMB BIT(9)
# define LINFLEXD_UARTSR_FEF BIT(8)
# define LINFLEXD_UARTSR_BOF BIT(7)
# define LINFLEXD_UARTSR_RPS BIT(6)
# define LINFLEXD_UARTSR_WUF BIT(5)
# define LINFLEXD_UARTSR_4 BIT(4)
# define LINFLEXD_UARTSR_TO BIT(3)
# define LINFLEXD_UARTSR_DRFRFE BIT(2)
# define LINFLEXD_UARTSR_DTFTFF BIT(1)
# define LINFLEXD_UARTSR_NF BIT(0)
# define LINFLEXD_UARTSR_PE (LINFLEXD_UARTSR_PE0 |\
LINFLEXD_UARTSR_PE1 | \
LINFLEXD_UARTSR_PE2 | \
LINFLEXD_UARTSR_PE3 )
# define LINFLEX_LDIV_MULTIPLIER (16)
# define DRIVER_NAME "fsl-linflexuart"
# define DEV_NAME "ttyLF"
# define UART_NR 4
# define EARLYCON_BUFFER_INITIAL_CAP 8
# define PREINIT_DELAY 2000 /* us */
static const struct of_device_id linflex_dt_ids [ ] = {
{
2019-08-23 19:11:35 +00:00
. compatible = " fsl,s32v234-linflexuart " ,
2019-08-09 11:29:16 +00:00
} ,
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , linflex_dt_ids ) ;
# ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE
static struct uart_port * earlycon_port ;
static bool linflex_earlycon_same_instance ;
2019-08-27 11:46:14 +00:00
static DEFINE_SPINLOCK ( init_lock ) ;
2019-08-09 11:29:16 +00:00
static bool during_init ;
static struct {
char * content ;
unsigned int len , cap ;
} earlycon_buf ;
# endif
static void linflex_stop_tx ( struct uart_port * port )
{
unsigned long ier ;
ier = readl ( port - > membase + LINIER ) ;
ier & = ~ ( LINFLEXD_LINIER_DTIE ) ;
writel ( ier , port - > membase + LINIER ) ;
}
static void linflex_stop_rx ( struct uart_port * port )
{
unsigned long ier ;
ier = readl ( port - > membase + LINIER ) ;
writel ( ier & ~ LINFLEXD_LINIER_DRIE , port - > membase + LINIER ) ;
}
static inline void linflex_transmit_buffer ( struct uart_port * sport )
{
struct circ_buf * xmit = & sport - > state - > xmit ;
unsigned char c ;
unsigned long status ;
while ( ! uart_circ_empty ( xmit ) ) {
c = xmit - > buf [ xmit - > tail ] ;
writeb ( c , sport - > membase + BDRL ) ;
/* Waiting for data transmission completed. */
while ( ( ( status = readl ( sport - > membase + UARTSR ) ) &
LINFLEXD_UARTSR_DTFTFF ) ! =
LINFLEXD_UARTSR_DTFTFF )
;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
sport - > icount . tx + + ;
writel ( status | LINFLEXD_UARTSR_DTFTFF ,
sport - > membase + UARTSR ) ;
}
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( sport ) ;
if ( uart_circ_empty ( xmit ) )
linflex_stop_tx ( sport ) ;
}
static void linflex_start_tx ( struct uart_port * port )
{
unsigned long ier ;
linflex_transmit_buffer ( port ) ;
ier = readl ( port - > membase + LINIER ) ;
writel ( ier | LINFLEXD_LINIER_DTIE , port - > membase + LINIER ) ;
}
static irqreturn_t linflex_txint ( int irq , void * dev_id )
{
struct uart_port * sport = dev_id ;
struct circ_buf * xmit = & sport - > state - > xmit ;
unsigned long flags ;
unsigned long status ;
spin_lock_irqsave ( & sport - > lock , flags ) ;
if ( sport - > x_char ) {
writeb ( sport - > x_char , sport - > membase + BDRL ) ;
/* waiting for data transmission completed */
while ( ( ( status = readl ( sport - > membase + UARTSR ) ) &
LINFLEXD_UARTSR_DTFTFF ) ! = LINFLEXD_UARTSR_DTFTFF )
;
writel ( status | LINFLEXD_UARTSR_DTFTFF ,
sport - > membase + UARTSR ) ;
goto out ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( sport ) ) {
linflex_stop_tx ( sport ) ;
goto out ;
}
linflex_transmit_buffer ( sport ) ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( sport ) ;
out :
spin_unlock_irqrestore ( & sport - > lock , flags ) ;
return IRQ_HANDLED ;
}
static irqreturn_t linflex_rxint ( int irq , void * dev_id )
{
struct uart_port * sport = dev_id ;
unsigned int flg ;
struct tty_port * port = & sport - > state - > port ;
unsigned long flags , status ;
unsigned char rx ;
2019-09-18 18:45:49 +00:00
bool brk ;
2019-08-09 11:29:16 +00:00
spin_lock_irqsave ( & sport - > lock , flags ) ;
status = readl ( sport - > membase + UARTSR ) ;
while ( status & LINFLEXD_UARTSR_RMB ) {
rx = readb ( sport - > membase + BDRM ) ;
2019-09-18 18:45:49 +00:00
brk = false ;
2019-08-09 11:29:16 +00:00
flg = TTY_NORMAL ;
sport - > icount . rx + + ;
if ( status & ( LINFLEXD_UARTSR_BOF | LINFLEXD_UARTSR_SZF |
LINFLEXD_UARTSR_FEF | LINFLEXD_UARTSR_PE ) ) {
if ( status & LINFLEXD_UARTSR_SZF )
status | = LINFLEXD_UARTSR_SZF ;
if ( status & LINFLEXD_UARTSR_BOF )
status | = LINFLEXD_UARTSR_BOF ;
2019-09-18 18:45:49 +00:00
if ( status & LINFLEXD_UARTSR_FEF ) {
if ( ! rx )
brk = true ;
2019-08-09 11:29:16 +00:00
status | = LINFLEXD_UARTSR_FEF ;
2019-09-18 18:45:49 +00:00
}
2019-08-09 11:29:16 +00:00
if ( status & LINFLEXD_UARTSR_PE )
status | = LINFLEXD_UARTSR_PE ;
}
writel ( status | LINFLEXD_UARTSR_RMB | LINFLEXD_UARTSR_DRFRFE ,
sport - > membase + UARTSR ) ;
status = readl ( sport - > membase + UARTSR ) ;
2019-09-18 18:45:49 +00:00
if ( brk ) {
uart_handle_break ( sport ) ;
} else {
if ( uart_handle_sysrq_char ( sport , ( unsigned char ) rx ) )
continue ;
tty_insert_flip_char ( port , rx , flg ) ;
}
2019-08-09 11:29:16 +00:00
}
spin_unlock_irqrestore ( & sport - > lock , flags ) ;
tty_flip_buffer_push ( port ) ;
return IRQ_HANDLED ;
}
static irqreturn_t linflex_int ( int irq , void * dev_id )
{
struct uart_port * sport = dev_id ;
unsigned long status ;
status = readl ( sport - > membase + UARTSR ) ;
if ( status & LINFLEXD_UARTSR_DRFRFE )
linflex_rxint ( irq , dev_id ) ;
if ( status & LINFLEXD_UARTSR_DTFTFF )
linflex_txint ( irq , dev_id ) ;
return IRQ_HANDLED ;
}
/* return TIOCSER_TEMT when transmitter is not busy */
static unsigned int linflex_tx_empty ( struct uart_port * port )
{
unsigned long status ;
status = readl ( port - > membase + UARTSR ) & LINFLEXD_UARTSR_DTFTFF ;
return status ? TIOCSER_TEMT : 0 ;
}
static unsigned int linflex_get_mctrl ( struct uart_port * port )
{
return 0 ;
}
static void linflex_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
}
static void linflex_break_ctl ( struct uart_port * port , int break_state )
{
}
static void linflex_setup_watermark ( struct uart_port * sport )
{
unsigned long cr , ier , cr1 ;
/* Disable transmission/reception */
ier = readl ( sport - > membase + LINIER ) ;
ier & = ~ ( LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE ) ;
writel ( ier , sport - > membase + LINIER ) ;
cr = readl ( sport - > membase + UARTCR ) ;
cr & = ~ ( LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN ) ;
writel ( cr , sport - > membase + UARTCR ) ;
/* Enter initialization mode by setting INIT bit */
/* set the Linflex in master mode and activate by-pass filter */
cr1 = LINFLEXD_LINCR1_BF | LINFLEXD_LINCR1_MME
| LINFLEXD_LINCR1_INIT ;
writel ( cr1 , sport - > membase + LINCR1 ) ;
/* wait for init mode entry */
while ( ( readl ( sport - > membase + LINSR )
& LINFLEXD_LINSR_LINS_MASK )
! = LINFLEXD_LINSR_LINS_INITMODE )
;
/*
* UART = 0x1 ; - Linflex working in UART mode
* TXEN = 0x1 ; - Enable transmission of data now
* RXEn = 0x1 ; - Receiver enabled
* WL0 = 0x1 ; - 8 bit data
* PCE = 0x0 ; - No parity
*/
/* set UART bit to allow writing other bits */
writel ( LINFLEXD_UARTCR_UART , sport - > membase + UARTCR ) ;
cr = ( LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN |
LINFLEXD_UARTCR_WL0 | LINFLEXD_UARTCR_UART ) ;
writel ( cr , sport - > membase + UARTCR ) ;
cr1 & = ~ ( LINFLEXD_LINCR1_INIT ) ;
writel ( cr1 , sport - > membase + LINCR1 ) ;
ier = readl ( sport - > membase + LINIER ) ;
ier | = LINFLEXD_LINIER_DRIE ;
ier | = LINFLEXD_LINIER_DTIE ;
writel ( ier , sport - > membase + LINIER ) ;
}
static int linflex_startup ( struct uart_port * port )
{
int ret = 0 ;
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
linflex_setup_watermark ( port ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
ret = devm_request_irq ( port - > dev , port - > irq , linflex_int , 0 ,
DRIVER_NAME , port ) ;
return ret ;
}
static void linflex_shutdown ( struct uart_port * port )
{
unsigned long ier ;
unsigned long flags ;
spin_lock_irqsave ( & port - > lock , flags ) ;
/* disable interrupts */
ier = readl ( port - > membase + LINIER ) ;
ier & = ~ ( LINFLEXD_LINIER_DRIE | LINFLEXD_LINIER_DTIE ) ;
writel ( ier , port - > membase + LINIER ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
devm_free_irq ( port - > dev , port - > irq , port ) ;
}
static void
linflex_set_termios ( struct uart_port * port , struct ktermios * termios ,
struct ktermios * old )
{
unsigned long flags ;
unsigned long cr , old_cr , cr1 ;
unsigned int old_csize = old ? old - > c_cflag & CSIZE : CS8 ;
cr = readl ( port - > membase + UARTCR ) ;
old_cr = cr ;
/* Enter initialization mode by setting INIT bit */
cr1 = readl ( port - > membase + LINCR1 ) ;
cr1 | = LINFLEXD_LINCR1_INIT ;
writel ( cr1 , port - > membase + LINCR1 ) ;
/* wait for init mode entry */
while ( ( readl ( port - > membase + LINSR )
& LINFLEXD_LINSR_LINS_MASK )
! = LINFLEXD_LINSR_LINS_INITMODE )
;
/*
* only support CS8 and CS7 , and for CS7 must enable PE .
* supported mode :
* - ( 7 , e / o , 1 )
* - ( 8 , n , 1 )
* - ( 8 , e / o , 1 )
*/
/* enter the UART into configuration mode */
while ( ( termios - > c_cflag & CSIZE ) ! = CS8 & &
( termios - > c_cflag & CSIZE ) ! = CS7 ) {
termios - > c_cflag & = ~ CSIZE ;
termios - > c_cflag | = old_csize ;
old_csize = CS8 ;
}
if ( ( termios - > c_cflag & CSIZE ) = = CS7 ) {
/* Word length: WL1WL0:00 */
cr = old_cr & ~ LINFLEXD_UARTCR_WL1 & ~ LINFLEXD_UARTCR_WL0 ;
}
if ( ( termios - > c_cflag & CSIZE ) = = CS8 ) {
/* Word length: WL1WL0:01 */
cr = ( old_cr | LINFLEXD_UARTCR_WL0 ) & ~ LINFLEXD_UARTCR_WL1 ;
}
if ( termios - > c_cflag & CMSPAR ) {
if ( ( termios - > c_cflag & CSIZE ) ! = CS8 ) {
termios - > c_cflag & = ~ CSIZE ;
termios - > c_cflag | = CS8 ;
}
/* has a space/sticky bit */
cr | = LINFLEXD_UARTCR_WL0 ;
}
if ( termios - > c_cflag & CSTOPB )
termios - > c_cflag & = ~ CSTOPB ;
/* parity must be enabled when CS7 to match 8-bits format */
if ( ( termios - > c_cflag & CSIZE ) = = CS7 )
termios - > c_cflag | = PARENB ;
if ( ( termios - > c_cflag & PARENB ) ) {
cr | = LINFLEXD_UARTCR_PCE ;
if ( termios - > c_cflag & PARODD )
cr = ( cr | LINFLEXD_UARTCR_PC0 ) &
( ~ LINFLEXD_UARTCR_PC1 ) ;
else
cr = cr & ( ~ LINFLEXD_UARTCR_PC1 &
~ LINFLEXD_UARTCR_PC0 ) ;
} else {
cr & = ~ LINFLEXD_UARTCR_PCE ;
}
spin_lock_irqsave ( & port - > lock , flags ) ;
port - > read_status_mask = 0 ;
if ( termios - > c_iflag & INPCK )
port - > read_status_mask | = ( LINFLEXD_UARTSR_FEF |
LINFLEXD_UARTSR_PE0 |
LINFLEXD_UARTSR_PE1 |
LINFLEXD_UARTSR_PE2 |
LINFLEXD_UARTSR_PE3 ) ;
if ( termios - > c_iflag & ( IGNBRK | BRKINT | PARMRK ) )
port - > read_status_mask | = LINFLEXD_UARTSR_FEF ;
/* characters to ignore */
port - > ignore_status_mask = 0 ;
if ( termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = LINFLEXD_UARTSR_PE ;
if ( termios - > c_iflag & IGNBRK ) {
port - > ignore_status_mask | = LINFLEXD_UARTSR_PE ;
/*
* if we ' re ignoring parity and break indicators ,
* ignore overruns too ( for real raw support ) .
*/
if ( termios - > c_iflag & IGNPAR )
port - > ignore_status_mask | = LINFLEXD_UARTSR_BOF ;
}
writel ( cr , port - > membase + UARTCR ) ;
cr1 & = ~ ( LINFLEXD_LINCR1_INIT ) ;
writel ( cr1 , port - > membase + LINCR1 ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
}
static const char * linflex_type ( struct uart_port * port )
{
return " FSL_LINFLEX " ;
}
static void linflex_release_port ( struct uart_port * port )
{
/* nothing to do */
}
static int linflex_request_port ( struct uart_port * port )
{
return 0 ;
}
/* configure/auto-configure the port */
static void linflex_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE )
port - > type = PORT_LINFLEXUART ;
}
static const struct uart_ops linflex_pops = {
. tx_empty = linflex_tx_empty ,
. set_mctrl = linflex_set_mctrl ,
. get_mctrl = linflex_get_mctrl ,
. stop_tx = linflex_stop_tx ,
. start_tx = linflex_start_tx ,
. stop_rx = linflex_stop_rx ,
. break_ctl = linflex_break_ctl ,
. startup = linflex_startup ,
. shutdown = linflex_shutdown ,
. set_termios = linflex_set_termios ,
. type = linflex_type ,
. request_port = linflex_request_port ,
. release_port = linflex_release_port ,
. config_port = linflex_config_port ,
} ;
static struct uart_port * linflex_ports [ UART_NR ] ;
# ifdef CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE
static void linflex_console_putchar ( struct uart_port * port , int ch )
{
unsigned long cr ;
cr = readl ( port - > membase + UARTCR ) ;
writeb ( ch , port - > membase + BDRL ) ;
if ( ! ( cr & LINFLEXD_UARTCR_TFBM ) )
while ( ( readl ( port - > membase + UARTSR ) &
LINFLEXD_UARTSR_DTFTFF )
! = LINFLEXD_UARTSR_DTFTFF )
;
else
while ( readl ( port - > membase + UARTSR ) &
LINFLEXD_UARTSR_DTFTFF )
;
if ( ! ( cr & LINFLEXD_UARTCR_TFBM ) ) {
writel ( ( readl ( port - > membase + UARTSR ) |
LINFLEXD_UARTSR_DTFTFF ) ,
port - > membase + UARTSR ) ;
}
}
static void linflex_earlycon_putchar ( struct uart_port * port , int ch )
{
unsigned long flags ;
char * ret ;
if ( ! linflex_earlycon_same_instance ) {
linflex_console_putchar ( port , ch ) ;
return ;
}
spin_lock_irqsave ( & init_lock , flags ) ;
if ( ! during_init )
goto outside_init ;
if ( earlycon_buf . len > = 1 < < CONFIG_LOG_BUF_SHIFT )
goto init_release ;
if ( ! earlycon_buf . cap ) {
earlycon_buf . content = kmalloc ( EARLYCON_BUFFER_INITIAL_CAP ,
GFP_ATOMIC ) ;
earlycon_buf . cap = earlycon_buf . content ?
EARLYCON_BUFFER_INITIAL_CAP : 0 ;
} else if ( earlycon_buf . len = = earlycon_buf . cap ) {
ret = krealloc ( earlycon_buf . content , earlycon_buf . cap < < 1 ,
GFP_ATOMIC ) ;
if ( ret ) {
earlycon_buf . content = ret ;
earlycon_buf . cap < < = 1 ;
}
}
if ( earlycon_buf . len < earlycon_buf . cap )
earlycon_buf . content [ earlycon_buf . len + + ] = ch ;
goto init_release ;
outside_init :
linflex_console_putchar ( port , ch ) ;
init_release :
spin_unlock_irqrestore ( & init_lock , flags ) ;
}
static void linflex_string_write ( struct uart_port * sport , const char * s ,
unsigned int count )
{
unsigned long cr , ier = 0 ;
ier = readl ( sport - > membase + LINIER ) ;
linflex_stop_tx ( sport ) ;
cr = readl ( sport - > membase + UARTCR ) ;
cr | = ( LINFLEXD_UARTCR_TXEN ) ;
writel ( cr , sport - > membase + UARTCR ) ;
uart_console_write ( sport , s , count , linflex_console_putchar ) ;
writel ( ier , sport - > membase + LINIER ) ;
}
static void
linflex_console_write ( struct console * co , const char * s , unsigned int count )
{
struct uart_port * sport = linflex_ports [ co - > index ] ;
unsigned long flags ;
int locked = 1 ;
if ( sport - > sysrq )
locked = 0 ;
else if ( oops_in_progress )
locked = spin_trylock_irqsave ( & sport - > lock , flags ) ;
else
spin_lock_irqsave ( & sport - > lock , flags ) ;
linflex_string_write ( sport , s , count ) ;
if ( locked )
spin_unlock_irqrestore ( & sport - > lock , flags ) ;
}
/*
* if the port was already initialised ( eg , by a boot loader ) ,
* try to determine the current setup .
*/
static void __init
linflex_console_get_options ( struct uart_port * sport , int * parity , int * bits )
{
unsigned long cr ;
cr = readl ( sport - > membase + UARTCR ) ;
cr & = LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN ;
if ( ! cr )
return ;
/* ok, the port was enabled */
* parity = ' n ' ;
if ( cr & LINFLEXD_UARTCR_PCE ) {
if ( cr & LINFLEXD_UARTCR_PC0 )
* parity = ' o ' ;
else
* parity = ' e ' ;
}
if ( ( cr & LINFLEXD_UARTCR_WL0 ) & & ( ( cr & LINFLEXD_UARTCR_WL1 ) = = 0 ) ) {
if ( cr & LINFLEXD_UARTCR_PCE )
* bits = 9 ;
else
* bits = 8 ;
}
}
static int __init linflex_console_setup ( struct console * co , char * options )
{
struct uart_port * sport ;
int baud = 115200 ;
int bits = 8 ;
int parity = ' n ' ;
int flow = ' n ' ;
int ret ;
int i ;
unsigned long flags ;
/*
* check whether an invalid uart number has been specified , and
* if so , search for the first available port that does have
* console support .
*/
if ( co - > index = = - 1 | | co - > index > = ARRAY_SIZE ( linflex_ports ) )
co - > index = 0 ;
sport = linflex_ports [ co - > index ] ;
if ( ! sport )
return - ENODEV ;
if ( options )
uart_parse_options ( options , & baud , & parity , & bits , & flow ) ;
else
linflex_console_get_options ( sport , & parity , & bits ) ;
if ( earlycon_port & & sport - > mapbase = = earlycon_port - > mapbase ) {
linflex_earlycon_same_instance = true ;
spin_lock_irqsave ( & init_lock , flags ) ;
during_init = true ;
spin_unlock_irqrestore ( & init_lock , flags ) ;
/* Workaround for character loss or output of many invalid
* characters , when INIT mode is entered shortly after a
* character has just been printed .
*/
udelay ( PREINIT_DELAY ) ;
}
linflex_setup_watermark ( sport ) ;
ret = uart_set_options ( sport , co , baud , parity , bits , flow ) ;
if ( ! linflex_earlycon_same_instance )
goto done ;
spin_lock_irqsave ( & init_lock , flags ) ;
/* Emptying buffer */
if ( earlycon_buf . len ) {
for ( i = 0 ; i < earlycon_buf . len ; i + + )
linflex_console_putchar ( earlycon_port ,
earlycon_buf . content [ i ] ) ;
kfree ( earlycon_buf . content ) ;
earlycon_buf . len = 0 ;
}
during_init = false ;
spin_unlock_irqrestore ( & init_lock , flags ) ;
done :
return ret ;
}
static struct uart_driver linflex_reg ;
static struct console linflex_console = {
. name = DEV_NAME ,
. write = linflex_console_write ,
. device = uart_console_device ,
. setup = linflex_console_setup ,
. flags = CON_PRINTBUFFER ,
. index = - 1 ,
. data = & linflex_reg ,
} ;
static void linflex_earlycon_write ( struct console * con , const char * s ,
unsigned int n )
{
struct earlycon_device * dev = con - > data ;
uart_console_write ( & dev - > port , s , n , linflex_earlycon_putchar ) ;
}
static int __init linflex_early_console_setup ( struct earlycon_device * device ,
const char * options )
{
if ( ! device - > port . membase )
return - ENODEV ;
device - > con - > write = linflex_earlycon_write ;
earlycon_port = & device - > port ;
return 0 ;
}
2019-08-23 19:11:35 +00:00
OF_EARLYCON_DECLARE ( linflex , " fsl,s32v234-linflexuart " ,
2019-08-09 11:29:16 +00:00
linflex_early_console_setup ) ;
# define LINFLEX_CONSOLE (&linflex_console)
# else
# define LINFLEX_CONSOLE NULL
# endif
static struct uart_driver linflex_reg = {
. owner = THIS_MODULE ,
. driver_name = DRIVER_NAME ,
. dev_name = DEV_NAME ,
. nr = ARRAY_SIZE ( linflex_ports ) ,
. cons = LINFLEX_CONSOLE ,
} ;
static int linflex_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct uart_port * sport ;
struct resource * res ;
int ret ;
sport = devm_kzalloc ( & pdev - > dev , sizeof ( * sport ) , GFP_KERNEL ) ;
if ( ! sport )
return - ENOMEM ;
ret = of_alias_get_id ( np , " serial " ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " failed to get alias id, errno %d \n " , ret ) ;
return ret ;
}
if ( ret > = UART_NR ) {
dev_err ( & pdev - > dev , " driver limited to %d serial ports \n " ,
UART_NR ) ;
return - ENOMEM ;
}
sport - > line = ret ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res )
return - ENODEV ;
sport - > mapbase = res - > start ;
sport - > membase = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( sport - > membase ) )
return PTR_ERR ( sport - > membase ) ;
sport - > dev = & pdev - > dev ;
sport - > type = PORT_LINFLEXUART ;
sport - > iotype = UPIO_MEM ;
sport - > irq = platform_get_irq ( pdev , 0 ) ;
sport - > ops = & linflex_pops ;
sport - > flags = UPF_BOOT_AUTOCONF ;
2019-12-13 00:06:16 +00:00
sport - > has_sysrq = IS_ENABLED ( CONFIG_SERIAL_FSL_LINFLEXUART_CONSOLE ) ;
2019-08-09 11:29:16 +00:00
linflex_ports [ sport - > line ] = sport ;
platform_set_drvdata ( pdev , sport ) ;
ret = uart_add_one_port ( & linflex_reg , sport ) ;
if ( ret )
return ret ;
return 0 ;
}
static int linflex_remove ( struct platform_device * pdev )
{
struct uart_port * sport = platform_get_drvdata ( pdev ) ;
uart_remove_one_port ( & linflex_reg , sport ) ;
return 0 ;
}
# ifdef CONFIG_PM_SLEEP
static int linflex_suspend ( struct device * dev )
{
struct uart_port * sport = dev_get_drvdata ( dev ) ;
uart_suspend_port ( & linflex_reg , sport ) ;
return 0 ;
}
static int linflex_resume ( struct device * dev )
{
struct uart_port * sport = dev_get_drvdata ( dev ) ;
uart_resume_port ( & linflex_reg , sport ) ;
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( linflex_pm_ops , linflex_suspend , linflex_resume ) ;
static struct platform_driver linflex_driver = {
. probe = linflex_probe ,
. remove = linflex_remove ,
. driver = {
. name = DRIVER_NAME ,
. of_match_table = linflex_dt_ids ,
. pm = & linflex_pm_ops ,
} ,
} ;
static int __init linflex_serial_init ( void )
{
int ret ;
ret = uart_register_driver ( & linflex_reg ) ;
if ( ret )
return ret ;
ret = platform_driver_register ( & linflex_driver ) ;
if ( ret )
uart_unregister_driver ( & linflex_reg ) ;
return ret ;
}
static void __exit linflex_serial_exit ( void )
{
platform_driver_unregister ( & linflex_driver ) ;
uart_unregister_driver ( & linflex_reg ) ;
}
module_init ( linflex_serial_init ) ;
module_exit ( linflex_serial_exit ) ;
2019-10-16 15:48:25 +03:00
MODULE_DESCRIPTION ( " Freescale LINFlexD serial port driver " ) ;
2019-08-09 11:29:16 +00:00
MODULE_LICENSE ( " GPL v2 " ) ;