2014-04-17 17:47:58 +04:00
/*
* MEN 16 z135 High Speed UART
*
* Copyright ( C ) 2014 MEN Mikroelektronik GmbH ( www . men . de )
* Author : Johannes Thumshirn < johannes . thumshirn @ men . 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 ; version 2 of the License .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ":" fmt
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/serial_core.h>
# include <linux/ioport.h>
# include <linux/io.h>
# include <linux/tty_flip.h>
# include <linux/bitops.h>
# include <linux/mcb.h>
# define MEN_Z135_MAX_PORTS 12
# define MEN_Z135_BASECLK 29491200
# define MEN_Z135_FIFO_SIZE 1024
# define MEN_Z135_FIFO_WATERMARK 1020
# define MEN_Z135_STAT_REG 0x0
# define MEN_Z135_RX_RAM 0x4
# define MEN_Z135_TX_RAM 0x400
# define MEN_Z135_RX_CTRL 0x800
# define MEN_Z135_TX_CTRL 0x804
# define MEN_Z135_CONF_REG 0x808
# define MEN_Z135_UART_FREQ 0x80c
# define MEN_Z135_BAUD_REG 0x810
2015-01-19 09:44:41 +03:00
# define MEN_Z135_TIMEOUT 0x814
2014-04-17 17:47:58 +04:00
# define MEN_Z135_MEM_SIZE 0x818
2015-01-19 09:44:41 +03:00
# define IRQ_ID(x) ((x) & 0x1f)
2014-04-17 17:47:58 +04:00
2014-05-12 12:34:59 +04:00
# define MEN_Z135_IER_RXCIEN BIT(0) /* RX Space IRQ */
# define MEN_Z135_IER_TXCIEN BIT(1) /* TX Space IRQ */
2014-04-17 17:47:58 +04:00
# define MEN_Z135_IER_RLSIEN BIT(2) /* Receiver Line Status IRQ */
# define MEN_Z135_IER_MSIEN BIT(3) /* Modem Status IRQ */
# define MEN_Z135_ALL_IRQS (MEN_Z135_IER_RXCIEN \
| MEN_Z135_IER_RLSIEN \
| MEN_Z135_IER_MSIEN \
| MEN_Z135_IER_TXCIEN )
# define MEN_Z135_MCR_DTR BIT(24)
# define MEN_Z135_MCR_RTS BIT(25)
# define MEN_Z135_MCR_OUT1 BIT(26)
# define MEN_Z135_MCR_OUT2 BIT(27)
# define MEN_Z135_MCR_LOOP BIT(28)
# define MEN_Z135_MCR_RCFC BIT(29)
# define MEN_Z135_MSR_DCTS BIT(0)
# define MEN_Z135_MSR_DDSR BIT(1)
# define MEN_Z135_MSR_DRI BIT(2)
# define MEN_Z135_MSR_DDCD BIT(3)
# define MEN_Z135_MSR_CTS BIT(4)
# define MEN_Z135_MSR_DSR BIT(5)
# define MEN_Z135_MSR_RI BIT(6)
# define MEN_Z135_MSR_DCD BIT(7)
# define MEN_Z135_LCR_SHIFT 8 /* LCR shift mask */
# define MEN_Z135_WL5 0 /* CS5 */
# define MEN_Z135_WL6 1 /* CS6 */
# define MEN_Z135_WL7 2 /* CS7 */
# define MEN_Z135_WL8 3 /* CS8 */
# define MEN_Z135_STB_SHIFT 2 /* Stopbits */
# define MEN_Z135_NSTB1 0
# define MEN_Z135_NSTB2 1
# define MEN_Z135_PEN_SHIFT 3 /* Parity enable */
# define MEN_Z135_PAR_DIS 0
# define MEN_Z135_PAR_ENA 1
# define MEN_Z135_PTY_SHIFT 4 /* Parity type */
# define MEN_Z135_PTY_ODD 0
# define MEN_Z135_PTY_EVN 1
# define MEN_Z135_LSR_DR BIT(0)
# define MEN_Z135_LSR_OE BIT(1)
# define MEN_Z135_LSR_PE BIT(2)
# define MEN_Z135_LSR_FE BIT(3)
# define MEN_Z135_LSR_BI BIT(4)
# define MEN_Z135_LSR_THEP BIT(5)
# define MEN_Z135_LSR_TEXP BIT(6)
# define MEN_Z135_LSR_RXFIFOERR BIT(7)
2015-01-19 09:44:41 +03:00
# define MEN_Z135_IRQ_ID_RLS BIT(0)
# define MEN_Z135_IRQ_ID_RDA BIT(1)
# define MEN_Z135_IRQ_ID_CTI BIT(2)
# define MEN_Z135_IRQ_ID_TSA BIT(3)
# define MEN_Z135_IRQ_ID_MST BIT(4)
2014-04-17 17:47:58 +04:00
# define LCR(x) (((x) >> MEN_Z135_LCR_SHIFT) & 0xff)
# define BYTES_TO_ALIGN(x) ((x) & 0x3)
static int line ;
static int txlvl = 5 ;
module_param ( txlvl , int , S_IRUGO ) ;
MODULE_PARM_DESC ( txlvl , " TX IRQ trigger level 0-7, default 5 (128 byte) " ) ;
static int rxlvl = 6 ;
module_param ( rxlvl , int , S_IRUGO ) ;
MODULE_PARM_DESC ( rxlvl , " RX IRQ trigger level 0-7, default 6 (256 byte) " ) ;
static int align ;
module_param ( align , int , S_IRUGO ) ;
MODULE_PARM_DESC ( align , " Keep hardware FIFO write pointer aligned, default 0 " ) ;
2015-01-19 09:44:41 +03:00
static uint rx_timeout ;
module_param ( rx_timeout , uint , S_IRUGO ) ;
MODULE_PARM_DESC ( rx_timeout , " RX timeout. "
" Timeout in seconds = (timeout_reg * baud_reg * 4) / freq_reg " ) ;
2014-04-17 17:47:58 +04:00
struct men_z135_port {
struct uart_port port ;
struct mcb_device * mdev ;
unsigned char * rxbuf ;
u32 stat_reg ;
spinlock_t lock ;
2015-01-19 09:44:41 +03:00
bool automode ;
2014-04-17 17:47:58 +04:00
} ;
# define to_men_z135(port) container_of((port), struct men_z135_port, port)
/**
* men_z135_reg_set ( ) - Set value in register
* @ uart : The UART port
* @ addr : Register address
* @ val : value to set
*/
static inline void men_z135_reg_set ( struct men_z135_port * uart ,
u32 addr , u32 val )
{
struct uart_port * port = & uart - > port ;
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & uart - > lock , flags ) ;
reg = ioread32 ( port - > membase + addr ) ;
reg | = val ;
iowrite32 ( reg , port - > membase + addr ) ;
spin_unlock_irqrestore ( & uart - > lock , flags ) ;
}
/**
* men_z135_reg_clr ( ) - Unset value in register
* @ uart : The UART port
* @ addr : Register address
* @ val : value to clear
*/
static inline void men_z135_reg_clr ( struct men_z135_port * uart ,
u32 addr , u32 val )
{
struct uart_port * port = & uart - > port ;
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & uart - > lock , flags ) ;
reg = ioread32 ( port - > membase + addr ) ;
reg & = ~ val ;
iowrite32 ( reg , port - > membase + addr ) ;
spin_unlock_irqrestore ( & uart - > lock , flags ) ;
}
/**
* men_z135_handle_modem_status ( ) - Handle change of modem status
* @ port : The UART port
*
* Handle change of modem status register . This is done by reading the " delta "
* versions of DCD ( Data Carrier Detect ) and CTS ( Clear To Send ) .
*/
static void men_z135_handle_modem_status ( struct men_z135_port * uart )
{
2015-01-19 09:44:41 +03:00
u8 msr ;
msr = ( uart - > stat_reg > > 8 ) & 0xff ;
if ( msr & MEN_Z135_MSR_DDCD )
2014-04-17 17:47:58 +04:00
uart_handle_dcd_change ( & uart - > port ,
2015-01-19 09:44:41 +03:00
msr & MEN_Z135_MSR_DCD ) ;
if ( msr & MEN_Z135_MSR_DCTS )
2014-04-17 17:47:58 +04:00
uart_handle_cts_change ( & uart - > port ,
2015-01-19 09:44:41 +03:00
msr & MEN_Z135_MSR_CTS ) ;
2014-04-17 17:47:58 +04:00
}
static void men_z135_handle_lsr ( struct men_z135_port * uart )
{
struct uart_port * port = & uart - > port ;
u8 lsr ;
lsr = ( uart - > stat_reg > > 16 ) & 0xff ;
if ( lsr & MEN_Z135_LSR_OE )
port - > icount . overrun + + ;
if ( lsr & MEN_Z135_LSR_PE )
port - > icount . parity + + ;
if ( lsr & MEN_Z135_LSR_FE )
port - > icount . frame + + ;
if ( lsr & MEN_Z135_LSR_BI ) {
port - > icount . brk + + ;
uart_handle_break ( port ) ;
}
}
/**
* get_rx_fifo_content ( ) - Get the number of bytes in RX FIFO
* @ uart : The UART port
*
* Read RXC register from hardware and return current FIFO fill size .
*/
static u16 get_rx_fifo_content ( struct men_z135_port * uart )
{
struct uart_port * port = & uart - > port ;
u32 stat_reg ;
u16 rxc ;
u8 rxc_lo ;
u8 rxc_hi ;
stat_reg = ioread32 ( port - > membase + MEN_Z135_STAT_REG ) ;
rxc_lo = stat_reg > > 24 ;
rxc_hi = ( stat_reg & 0xC0 ) > > 6 ;
rxc = rxc_lo | ( rxc_hi < < 8 ) ;
return rxc ;
}
/**
* men_z135_handle_rx ( ) - RX tasklet routine
* @ arg : Pointer to struct men_z135_port
*
* Copy from RX FIFO and acknowledge number of bytes copied .
*/
static void men_z135_handle_rx ( struct men_z135_port * uart )
{
struct uart_port * port = & uart - > port ;
struct tty_port * tport = & port - > state - > port ;
int copied ;
u16 size ;
int room ;
size = get_rx_fifo_content ( uart ) ;
if ( size = = 0 )
return ;
/* Avoid accidently accessing TX FIFO instead of RX FIFO. Last
* longword in RX FIFO cannot be read . ( 0x004 - 0x3FF )
*/
if ( size > MEN_Z135_FIFO_WATERMARK )
size = MEN_Z135_FIFO_WATERMARK ;
room = tty_buffer_request_room ( tport , size ) ;
if ( room ! = size )
dev_warn ( & uart - > mdev - > dev ,
" Not enough room in flip buffer, truncating to %d \n " ,
room ) ;
if ( room = = 0 )
return ;
memcpy_fromio ( uart - > rxbuf , port - > membase + MEN_Z135_RX_RAM , room ) ;
/* Be sure to first copy all data and then acknowledge it */
mb ( ) ;
iowrite32 ( room , port - > membase + MEN_Z135_RX_CTRL ) ;
copied = tty_insert_flip_string ( tport , uart - > rxbuf , room ) ;
if ( copied ! = room )
dev_warn ( & uart - > mdev - > dev ,
" Only copied %d instead of %d bytes \n " ,
copied , room ) ;
port - > icount . rx + = copied ;
tty_flip_buffer_push ( tport ) ;
}
/**
* men_z135_handle_tx ( ) - TX tasklet routine
* @ arg : Pointer to struct men_z135_port
*
*/
static void men_z135_handle_tx ( struct men_z135_port * uart )
{
struct uart_port * port = & uart - > port ;
struct circ_buf * xmit = & port - > state - > xmit ;
u32 txc ;
u32 wptr ;
int qlen ;
int n ;
int txfree ;
int head ;
int tail ;
int s ;
if ( uart_circ_empty ( xmit ) )
goto out ;
if ( uart_tx_stopped ( port ) )
goto out ;
if ( port - > x_char )
goto out ;
/* calculate bytes to copy */
qlen = uart_circ_chars_pending ( xmit ) ;
if ( qlen < = 0 )
goto out ;
wptr = ioread32 ( port - > membase + MEN_Z135_TX_CTRL ) ;
txc = ( wptr > > 16 ) & 0x3ff ;
wptr & = 0x3ff ;
if ( txc > MEN_Z135_FIFO_WATERMARK )
txc = MEN_Z135_FIFO_WATERMARK ;
txfree = MEN_Z135_FIFO_WATERMARK - txc ;
if ( txfree < = 0 ) {
2015-01-19 09:44:41 +03:00
dev_err ( & uart - > mdev - > dev ,
" Not enough room in TX FIFO have %d, need %d \n " ,
2014-04-17 17:47:58 +04:00
txfree , qlen ) ;
goto irq_en ;
}
/* if we're not aligned, it's better to copy only 1 or 2 bytes and
* then the rest .
*/
if ( align & & qlen > = 3 & & BYTES_TO_ALIGN ( wptr ) )
n = 4 - BYTES_TO_ALIGN ( wptr ) ;
else if ( qlen > txfree )
n = txfree ;
else
n = qlen ;
if ( n < = 0 )
goto irq_en ;
head = xmit - > head & ( UART_XMIT_SIZE - 1 ) ;
tail = xmit - > tail & ( UART_XMIT_SIZE - 1 ) ;
s = ( ( head > = tail ) ? head : UART_XMIT_SIZE ) - tail ;
n = min ( n , s ) ;
memcpy_toio ( port - > membase + MEN_Z135_TX_RAM , & xmit - > buf [ xmit - > tail ] , n ) ;
xmit - > tail = ( xmit - > tail + n ) & ( UART_XMIT_SIZE - 1 ) ;
mmiowb ( ) ;
iowrite32 ( n & 0x3ff , port - > membase + MEN_Z135_TX_CTRL ) ;
port - > icount . tx + = n ;
2014-06-16 09:33:38 +04:00
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
2014-04-17 17:47:58 +04:00
irq_en :
if ( ! uart_circ_empty ( xmit ) )
men_z135_reg_set ( uart , MEN_Z135_CONF_REG , MEN_Z135_IER_TXCIEN ) ;
else
men_z135_reg_clr ( uart , MEN_Z135_CONF_REG , MEN_Z135_IER_TXCIEN ) ;
out :
return ;
}
/**
* men_z135_intr ( ) - Handle legacy IRQs
* @ irq : The IRQ number
* @ data : Pointer to UART port
*
2015-01-19 09:44:41 +03:00
* Check IIR register to find the cause of the interrupt and handle it .
* It is possible that multiple interrupts reason bits are set and reading
* the IIR is a destructive read , so we always need to check for all possible
* interrupts and handle them .
2014-04-17 17:47:58 +04:00
*/
static irqreturn_t men_z135_intr ( int irq , void * data )
{
struct men_z135_port * uart = ( struct men_z135_port * ) data ;
struct uart_port * port = & uart - > port ;
2015-01-19 09:44:41 +03:00
bool handled = false ;
2014-04-17 17:47:58 +04:00
int irq_id ;
uart - > stat_reg = ioread32 ( port - > membase + MEN_Z135_STAT_REG ) ;
irq_id = IRQ_ID ( uart - > stat_reg ) ;
2015-01-19 09:44:41 +03:00
if ( ! irq_id )
goto out ;
2015-08-06 10:16:37 +03:00
spin_lock ( & port - > lock ) ;
2015-01-19 09:44:41 +03:00
/* It's save to write to IIR[7:6] RXC[9:8] */
iowrite8 ( irq_id , port - > membase + MEN_Z135_STAT_REG ) ;
if ( irq_id & MEN_Z135_IRQ_ID_RLS ) {
2014-04-17 17:47:58 +04:00
men_z135_handle_lsr ( uart ) ;
2015-01-19 09:44:41 +03:00
handled = true ;
}
if ( irq_id & ( MEN_Z135_IRQ_ID_RDA | MEN_Z135_IRQ_ID_CTI ) ) {
if ( irq_id & MEN_Z135_IRQ_ID_CTI )
dev_dbg ( & uart - > mdev - > dev , " Character Timeout Indication \n " ) ;
men_z135_handle_rx ( uart ) ;
handled = true ;
}
if ( irq_id & MEN_Z135_IRQ_ID_TSA ) {
men_z135_handle_tx ( uart ) ;
handled = true ;
2014-04-17 17:47:58 +04:00
}
2015-01-19 09:44:41 +03:00
if ( irq_id & MEN_Z135_IRQ_ID_MST ) {
men_z135_handle_modem_status ( uart ) ;
handled = true ;
}
2015-08-06 10:16:37 +03:00
spin_unlock ( & port - > lock ) ;
2015-01-19 09:44:41 +03:00
out :
return IRQ_RETVAL ( handled ) ;
2014-04-17 17:47:58 +04:00
}
/**
* men_z135_request_irq ( ) - Request IRQ for 16 z135 core
* @ uart : z135 private uart port structure
*
* Request an IRQ for 16 z135 to use . First try using MSI , if it fails
* fall back to using legacy interrupts .
*/
static int men_z135_request_irq ( struct men_z135_port * uart )
{
struct device * dev = & uart - > mdev - > dev ;
struct uart_port * port = & uart - > port ;
int err = 0 ;
err = request_irq ( port - > irq , men_z135_intr , IRQF_SHARED ,
" men_z135_intr " , uart ) ;
if ( err )
dev_err ( dev , " Error %d getting interrupt \n " , err ) ;
return err ;
}
/**
* men_z135_tx_empty ( ) - Handle tx_empty call
* @ port : The UART port
*
* This function tests whether the TX FIFO and shifter for the port
* described by @ port is empty .
*/
static unsigned int men_z135_tx_empty ( struct uart_port * port )
{
u32 wptr ;
u16 txc ;
wptr = ioread32 ( port - > membase + MEN_Z135_TX_CTRL ) ;
txc = ( wptr > > 16 ) & 0x3ff ;
if ( txc = = 0 )
return TIOCSER_TEMT ;
else
return 0 ;
}
/**
* men_z135_set_mctrl ( ) - Set modem control lines
* @ port : The UART port
* @ mctrl : The modem control lines
*
* This function sets the modem control lines for a port described by @ port
* to the state described by @ mctrl
*/
static void men_z135_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
2015-01-19 09:44:41 +03:00
u32 old ;
u32 conf_reg ;
2014-04-17 17:47:58 +04:00
2015-01-19 09:44:41 +03:00
conf_reg = old = ioread32 ( port - > membase + MEN_Z135_CONF_REG ) ;
2014-04-17 17:47:58 +04:00
if ( mctrl & TIOCM_RTS )
conf_reg | = MEN_Z135_MCR_RTS ;
2015-01-19 09:44:41 +03:00
else
conf_reg & = ~ MEN_Z135_MCR_RTS ;
2014-04-17 17:47:58 +04:00
if ( mctrl & TIOCM_DTR )
conf_reg | = MEN_Z135_MCR_DTR ;
2015-01-19 09:44:41 +03:00
else
conf_reg & = ~ MEN_Z135_MCR_DTR ;
2014-04-17 17:47:58 +04:00
if ( mctrl & TIOCM_OUT1 )
conf_reg | = MEN_Z135_MCR_OUT1 ;
2015-01-19 09:44:41 +03:00
else
conf_reg & = ~ MEN_Z135_MCR_OUT1 ;
2014-04-17 17:47:58 +04:00
if ( mctrl & TIOCM_OUT2 )
conf_reg | = MEN_Z135_MCR_OUT2 ;
2015-01-19 09:44:41 +03:00
else
conf_reg & = ~ MEN_Z135_MCR_OUT2 ;
2014-04-17 17:47:58 +04:00
if ( mctrl & TIOCM_LOOP )
conf_reg | = MEN_Z135_MCR_LOOP ;
2015-01-19 09:44:41 +03:00
else
conf_reg & = ~ MEN_Z135_MCR_LOOP ;
2014-04-17 17:47:58 +04:00
2015-01-19 09:44:41 +03:00
if ( conf_reg ! = old )
iowrite32 ( conf_reg , port - > membase + MEN_Z135_CONF_REG ) ;
2014-04-17 17:47:58 +04:00
}
/**
* men_z135_get_mctrl ( ) - Get modem control lines
* @ port : The UART port
*
* Retruns the current state of modem control inputs .
*/
static unsigned int men_z135_get_mctrl ( struct uart_port * port )
{
unsigned int mctrl = 0 ;
u8 msr ;
2015-01-19 09:44:41 +03:00
msr = ioread8 ( port - > membase + MEN_Z135_STAT_REG + 1 ) ;
2014-04-17 17:47:58 +04:00
if ( msr & MEN_Z135_MSR_CTS )
mctrl | = TIOCM_CTS ;
if ( msr & MEN_Z135_MSR_DSR )
mctrl | = TIOCM_DSR ;
if ( msr & MEN_Z135_MSR_RI )
mctrl | = TIOCM_RI ;
if ( msr & MEN_Z135_MSR_DCD )
mctrl | = TIOCM_CAR ;
return mctrl ;
}
/**
* men_z135_stop_tx ( ) - Stop transmitting characters
* @ port : The UART port
*
* Stop transmitting characters . This might be due to CTS line becomming
* inactive or the tty layer indicating we want to stop transmission due to
* an XOFF character .
*/
static void men_z135_stop_tx ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
men_z135_reg_clr ( uart , MEN_Z135_CONF_REG , MEN_Z135_IER_TXCIEN ) ;
}
2015-01-19 09:44:41 +03:00
/*
* men_z135_disable_ms ( ) - Disable Modem Status
* port : The UART port
*
* Enable Modem Status IRQ .
*/
static void men_z135_disable_ms ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
men_z135_reg_clr ( uart , MEN_Z135_CONF_REG , MEN_Z135_IER_MSIEN ) ;
}
2014-04-17 17:47:58 +04:00
/**
* men_z135_start_tx ( ) - Start transmitting characters
* @ port : The UART port
*
* Start transmitting character . This actually doesn ' t transmit anything , but
* fires off the TX tasklet .
*/
static void men_z135_start_tx ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
2015-01-19 09:44:41 +03:00
if ( uart - > automode )
men_z135_disable_ms ( port ) ;
2014-04-17 17:47:58 +04:00
men_z135_handle_tx ( uart ) ;
}
/**
* men_z135_stop_rx ( ) - Stop receiving characters
* @ port : The UART port
*
* Stop receiving characters ; the port is in the process of being closed .
*/
static void men_z135_stop_rx ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
men_z135_reg_clr ( uart , MEN_Z135_CONF_REG , MEN_Z135_IER_RXCIEN ) ;
}
/**
* men_z135_enable_ms ( ) - Enable Modem Status
* port :
*
* Enable Modem Status IRQ .
*/
static void men_z135_enable_ms ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
men_z135_reg_set ( uart , MEN_Z135_CONF_REG , MEN_Z135_IER_MSIEN ) ;
}
static int men_z135_startup ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
int err ;
u32 conf_reg = 0 ;
err = men_z135_request_irq ( uart ) ;
if ( err )
return - ENODEV ;
conf_reg = ioread32 ( port - > membase + MEN_Z135_CONF_REG ) ;
2014-05-12 12:34:59 +04:00
/* Activate all but TX space available IRQ */
conf_reg | = MEN_Z135_ALL_IRQS & ~ MEN_Z135_IER_TXCIEN ;
2014-04-17 17:47:58 +04:00
conf_reg & = ~ ( 0xff < < 16 ) ;
conf_reg | = ( txlvl < < 16 ) ;
conf_reg | = ( rxlvl < < 20 ) ;
iowrite32 ( conf_reg , port - > membase + MEN_Z135_CONF_REG ) ;
2015-01-19 09:44:41 +03:00
if ( rx_timeout )
iowrite32 ( rx_timeout , port - > membase + MEN_Z135_TIMEOUT ) ;
2014-04-17 17:47:58 +04:00
return 0 ;
}
static void men_z135_shutdown ( struct uart_port * port )
{
struct men_z135_port * uart = to_men_z135 ( port ) ;
u32 conf_reg = 0 ;
conf_reg | = MEN_Z135_ALL_IRQS ;
men_z135_reg_clr ( uart , MEN_Z135_CONF_REG , conf_reg ) ;
free_irq ( uart - > port . irq , uart ) ;
}
static void men_z135_set_termios ( struct uart_port * port ,
struct ktermios * termios ,
struct ktermios * old )
{
2015-01-19 09:44:41 +03:00
struct men_z135_port * uart = to_men_z135 ( port ) ;
2014-04-17 17:47:58 +04:00
unsigned int baud ;
u32 conf_reg ;
u32 bd_reg ;
u32 uart_freq ;
u8 lcr ;
conf_reg = ioread32 ( port - > membase + MEN_Z135_CONF_REG ) ;
lcr = LCR ( conf_reg ) ;
/* byte size */
switch ( termios - > c_cflag & CSIZE ) {
case CS5 :
lcr | = MEN_Z135_WL5 ;
break ;
case CS6 :
lcr | = MEN_Z135_WL6 ;
break ;
case CS7 :
lcr | = MEN_Z135_WL7 ;
break ;
case CS8 :
lcr | = MEN_Z135_WL8 ;
break ;
}
/* stop bits */
if ( termios - > c_cflag & CSTOPB )
lcr | = MEN_Z135_NSTB2 < < MEN_Z135_STB_SHIFT ;
/* parity */
if ( termios - > c_cflag & PARENB ) {
lcr | = MEN_Z135_PAR_ENA < < MEN_Z135_PEN_SHIFT ;
if ( termios - > c_cflag & PARODD )
lcr | = MEN_Z135_PTY_ODD < < MEN_Z135_PTY_SHIFT ;
else
lcr | = MEN_Z135_PTY_EVN < < MEN_Z135_PTY_SHIFT ;
} else
lcr | = MEN_Z135_PAR_DIS < < MEN_Z135_PEN_SHIFT ;
2015-01-19 09:44:41 +03:00
conf_reg | = MEN_Z135_IER_MSIEN ;
if ( termios - > c_cflag & CRTSCTS ) {
conf_reg | = MEN_Z135_MCR_RCFC ;
uart - > automode = true ;
termios - > c_cflag & = ~ CLOCAL ;
} else {
conf_reg & = ~ MEN_Z135_MCR_RCFC ;
uart - > automode = false ;
}
2014-04-17 17:47:58 +04:00
termios - > c_cflag & = ~ CMSPAR ; /* Mark/Space parity is not supported */
conf_reg | = lcr < < MEN_Z135_LCR_SHIFT ;
iowrite32 ( conf_reg , port - > membase + MEN_Z135_CONF_REG ) ;
uart_freq = ioread32 ( port - > membase + MEN_Z135_UART_FREQ ) ;
if ( uart_freq = = 0 )
uart_freq = MEN_Z135_BASECLK ;
baud = uart_get_baud_rate ( port , termios , old , 0 , uart_freq / 16 ) ;
2015-08-06 10:16:37 +03:00
spin_lock_irq ( & port - > lock ) ;
2014-04-17 17:47:58 +04:00
if ( tty_termios_baud_rate ( termios ) )
tty_termios_encode_baud_rate ( termios , baud , baud ) ;
bd_reg = uart_freq / ( 4 * baud ) ;
iowrite32 ( bd_reg , port - > membase + MEN_Z135_BAUD_REG ) ;
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
2015-08-06 10:16:37 +03:00
spin_unlock_irq ( & port - > lock ) ;
2014-04-17 17:47:58 +04:00
}
static const char * men_z135_type ( struct uart_port * port )
{
return KBUILD_MODNAME ;
}
static void men_z135_release_port ( struct uart_port * port )
{
iounmap ( port - > membase ) ;
port - > membase = NULL ;
release_mem_region ( port - > mapbase , MEN_Z135_MEM_SIZE ) ;
}
static int men_z135_request_port ( struct uart_port * port )
{
int size = MEN_Z135_MEM_SIZE ;
if ( ! request_mem_region ( port - > mapbase , size , " men_z135_port " ) )
return - EBUSY ;
port - > membase = ioremap ( port - > mapbase , MEN_Z135_MEM_SIZE ) ;
if ( port - > membase = = NULL ) {
release_mem_region ( port - > mapbase , MEN_Z135_MEM_SIZE ) ;
return - ENOMEM ;
}
return 0 ;
}
static void men_z135_config_port ( struct uart_port * port , int type )
{
port - > type = PORT_MEN_Z135 ;
men_z135_request_port ( port ) ;
}
static int men_z135_verify_port ( struct uart_port * port ,
struct serial_struct * serinfo )
{
return - EINVAL ;
}
static struct uart_ops men_z135_ops = {
. tx_empty = men_z135_tx_empty ,
. set_mctrl = men_z135_set_mctrl ,
. get_mctrl = men_z135_get_mctrl ,
. stop_tx = men_z135_stop_tx ,
. start_tx = men_z135_start_tx ,
. stop_rx = men_z135_stop_rx ,
. enable_ms = men_z135_enable_ms ,
. startup = men_z135_startup ,
. shutdown = men_z135_shutdown ,
. set_termios = men_z135_set_termios ,
. type = men_z135_type ,
. release_port = men_z135_release_port ,
. request_port = men_z135_request_port ,
. config_port = men_z135_config_port ,
. verify_port = men_z135_verify_port ,
} ;
static struct uart_driver men_z135_driver = {
. owner = THIS_MODULE ,
. driver_name = KBUILD_MODNAME ,
. dev_name = " ttyHSU " ,
. major = 0 ,
. minor = 0 ,
. nr = MEN_Z135_MAX_PORTS ,
} ;
/**
* men_z135_probe ( ) - Probe a z135 instance
* @ mdev : The MCB device
* @ id : The MCB device ID
*
* men_z135_probe does the basic setup of hardware resources and registers the
* new uart port to the tty layer .
*/
static int men_z135_probe ( struct mcb_device * mdev ,
const struct mcb_device_id * id )
{
struct men_z135_port * uart ;
struct resource * mem ;
struct device * dev ;
int err ;
dev = & mdev - > dev ;
uart = devm_kzalloc ( dev , sizeof ( struct men_z135_port ) , GFP_KERNEL ) ;
if ( ! uart )
return - ENOMEM ;
uart - > rxbuf = ( unsigned char * ) __get_free_page ( GFP_KERNEL ) ;
if ( ! uart - > rxbuf )
return - ENOMEM ;
mem = & mdev - > mem ;
mcb_set_drvdata ( mdev , uart ) ;
uart - > port . uartclk = MEN_Z135_BASECLK * 16 ;
uart - > port . fifosize = MEN_Z135_FIFO_SIZE ;
uart - > port . iotype = UPIO_MEM ;
uart - > port . ops = & men_z135_ops ;
uart - > port . irq = mcb_get_irq ( mdev ) ;
uart - > port . iotype = UPIO_MEM ;
uart - > port . flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP ;
uart - > port . line = line + + ;
uart - > port . dev = dev ;
uart - > port . type = PORT_MEN_Z135 ;
uart - > port . mapbase = mem - > start ;
uart - > port . membase = NULL ;
uart - > mdev = mdev ;
spin_lock_init ( & uart - > lock ) ;
err = uart_add_one_port ( & men_z135_driver , & uart - > port ) ;
if ( err )
goto err ;
return 0 ;
err :
free_page ( ( unsigned long ) uart - > rxbuf ) ;
dev_err ( dev , " Failed to add UART: %d \n " , err ) ;
return err ;
}
/**
* men_z135_remove ( ) - Remove a z135 instance from the system
*
* @ mdev : The MCB device
*/
static void men_z135_remove ( struct mcb_device * mdev )
{
struct men_z135_port * uart = mcb_get_drvdata ( mdev ) ;
line - - ;
uart_remove_one_port ( & men_z135_driver , & uart - > port ) ;
free_page ( ( unsigned long ) uart - > rxbuf ) ;
}
static const struct mcb_device_id men_z135_ids [ ] = {
{ . device = 0x87 } ,
2014-11-10 11:05:03 +03:00
{ }
2014-04-17 17:47:58 +04:00
} ;
MODULE_DEVICE_TABLE ( mcb , men_z135_ids ) ;
static struct mcb_driver mcb_driver = {
. driver = {
. name = " z135-uart " ,
. owner = THIS_MODULE ,
} ,
. probe = men_z135_probe ,
. remove = men_z135_remove ,
. id_table = men_z135_ids ,
} ;
/**
* men_z135_init ( ) - Driver Registration Routine
*
* men_z135_init is the first routine called when the driver is loaded . All it
* does is register with the legacy MEN Chameleon subsystem .
*/
static int __init men_z135_init ( void )
{
int err ;
err = uart_register_driver ( & men_z135_driver ) ;
if ( err ) {
pr_err ( " Failed to register UART: %d \n " , err ) ;
return err ;
}
err = mcb_register_driver ( & mcb_driver ) ;
if ( err ) {
pr_err ( " Failed to register MCB driver: %d \n " , err ) ;
uart_unregister_driver ( & men_z135_driver ) ;
return err ;
}
return 0 ;
}
module_init ( men_z135_init ) ;
/**
* men_z135_exit ( ) - Driver Exit Routine
*
* men_z135_exit is called just before the driver is removed from memory .
*/
static void __exit men_z135_exit ( void )
{
mcb_unregister_driver ( & mcb_driver ) ;
uart_unregister_driver ( & men_z135_driver ) ;
}
module_exit ( men_z135_exit ) ;
MODULE_AUTHOR ( " Johannes Thumshirn <johannes.thumshirn@men.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_DESCRIPTION ( " MEN 16z135 High Speed UART " ) ;
MODULE_ALIAS ( " mcb:16z135 " ) ;