2009-06-11 17:05:39 +04:00
/*
* timbuart . c timberdale FPGA UART driver
* Copyright ( c ) 2009 Intel Corporation
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/* Supports:
* Timberdale FPGA UART
*/
# include <linux/pci.h>
# include <linux/interrupt.h>
# include <linux/serial_core.h>
# include <linux/kernel.h>
# include <linux/platform_device.h>
# include <linux/ioport.h>
# include "timbuart.h"
struct timbuart_port {
struct uart_port port ;
struct tasklet_struct tasklet ;
int usedma ;
2009-06-22 21:43:03 +04:00
u32 last_ier ;
2009-06-11 17:05:39 +04:00
struct platform_device * dev ;
} ;
static int baudrates [ ] = { 9600 , 19200 , 38400 , 57600 , 115200 , 230400 , 460800 ,
921600 , 1843200 , 3250000 } ;
2009-06-22 21:43:03 +04:00
static void timbuart_mctrl_check ( struct uart_port * port , u32 isr , u32 * ier ) ;
2009-06-11 17:05:39 +04:00
static irqreturn_t timbuart_handleinterrupt ( int irq , void * devid ) ;
static void timbuart_stop_rx ( struct uart_port * port )
{
/* spin lock held by upper layer, disable all RX interrupts */
2009-06-22 21:43:03 +04:00
u32 ier = ioread32 ( port - > membase + TIMBUART_IER ) & ~ RXFLAGS ;
iowrite32 ( ier , port - > membase + TIMBUART_IER ) ;
2009-06-11 17:05:39 +04:00
}
static void timbuart_stop_tx ( struct uart_port * port )
{
/* spinlock held by upper layer, disable TX interrupt */
2009-06-22 21:43:03 +04:00
u32 ier = ioread32 ( port - > membase + TIMBUART_IER ) & ~ TXBAE ;
iowrite32 ( ier , port - > membase + TIMBUART_IER ) ;
2009-06-11 17:05:39 +04:00
}
static void timbuart_start_tx ( struct uart_port * port )
{
struct timbuart_port * uart =
container_of ( port , struct timbuart_port , port ) ;
/* do not transfer anything here -> fire off the tasklet */
tasklet_schedule ( & uart - > tasklet ) ;
}
static void timbuart_flush_buffer ( struct uart_port * port )
{
u8 ctl = ioread8 ( port - > membase + TIMBUART_CTRL ) | TIMBUART_CTRL_FLSHTX ;
iowrite8 ( ctl , port - > membase + TIMBUART_CTRL ) ;
2009-06-22 21:43:03 +04:00
iowrite32 ( TXBF , port - > membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
}
static void timbuart_rx_chars ( struct uart_port * port )
{
struct tty_struct * tty = port - > info - > port . tty ;
2009-06-22 21:43:03 +04:00
while ( ioread32 ( port - > membase + TIMBUART_ISR ) & RXDP ) {
2009-06-11 17:05:39 +04:00
u8 ch = ioread8 ( port - > membase + TIMBUART_RXFIFO ) ;
port - > icount . rx + + ;
tty_insert_flip_char ( tty , ch , TTY_NORMAL ) ;
}
spin_unlock ( & port - > lock ) ;
tty_flip_buffer_push ( port - > info - > port . tty ) ;
spin_lock ( & port - > lock ) ;
dev_dbg ( port - > dev , " %s - total read %d bytes \n " ,
__func__ , port - > icount . rx ) ;
}
static void timbuart_tx_chars ( struct uart_port * port )
{
struct circ_buf * xmit = & port - > info - > xmit ;
2009-06-22 21:43:03 +04:00
while ( ! ( ioread32 ( port - > membase + TIMBUART_ISR ) & TXBF ) & &
2009-06-11 17:05:39 +04:00
! uart_circ_empty ( xmit ) ) {
iowrite8 ( xmit - > buf [ xmit - > tail ] ,
port - > membase + TIMBUART_TXFIFO ) ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
}
dev_dbg ( port - > dev ,
" %s - total written %d bytes, CTL: %x, RTS: %x, baud: %x \n " ,
__func__ ,
port - > icount . tx ,
ioread8 ( port - > membase + TIMBUART_CTRL ) ,
port - > mctrl & TIOCM_RTS ,
ioread8 ( port - > membase + TIMBUART_BAUDRATE ) ) ;
}
2009-06-22 21:43:03 +04:00
static void timbuart_handle_tx_port ( struct uart_port * port , u32 isr , u32 * ier )
2009-06-11 17:05:39 +04:00
{
struct timbuart_port * uart =
container_of ( port , struct timbuart_port , port ) ;
struct circ_buf * xmit = & port - > info - > xmit ;
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) )
return ;
if ( port - > x_char )
return ;
if ( isr & TXFLAGS ) {
timbuart_tx_chars ( port ) ;
/* clear all TX interrupts */
2009-06-22 21:43:03 +04:00
iowrite32 ( TXFLAGS , port - > membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
} else
/* Re-enable any tx interrupt */
* ier | = uart - > last_ier & TXFLAGS ;
/* enable interrupts if there are chars in the transmit buffer,
* Or if we delivered some bytes and want the almost empty interrupt
* we wake up the upper layer later when we got the interrupt
* to give it some time to go out . . .
*/
if ( ! uart_circ_empty ( xmit ) )
* ier | = TXBAE ;
dev_dbg ( port - > dev , " %s - leaving \n " , __func__ ) ;
}
2009-06-22 21:43:03 +04:00
void timbuart_handle_rx_port ( struct uart_port * port , u32 isr , u32 * ier )
2009-06-11 17:05:39 +04:00
{
if ( isr & RXFLAGS ) {
/* Some RX status is set */
if ( isr & RXBF ) {
u8 ctl = ioread8 ( port - > membase + TIMBUART_CTRL ) |
TIMBUART_CTRL_FLSHRX ;
iowrite8 ( ctl , port - > membase + TIMBUART_CTRL ) ;
port - > icount . overrun + + ;
} else if ( isr & ( RXDP ) )
timbuart_rx_chars ( port ) ;
/* ack all RX interrupts */
2009-06-22 21:43:03 +04:00
iowrite32 ( RXFLAGS , port - > membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
}
/* always have the RX interrupts enabled */
* ier | = RXBAF | RXBF | RXTT ;
dev_dbg ( port - > dev , " %s - leaving \n " , __func__ ) ;
}
void timbuart_tasklet ( unsigned long arg )
{
struct timbuart_port * uart = ( struct timbuart_port * ) arg ;
2009-06-22 21:43:03 +04:00
u32 isr , ier = 0 ;
2009-06-11 17:05:39 +04:00
spin_lock ( & uart - > port . lock ) ;
2009-06-22 21:43:03 +04:00
isr = ioread32 ( uart - > port . membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
dev_dbg ( uart - > port . dev , " %s ISR: %x \n " , __func__ , isr ) ;
if ( ! uart - > usedma )
timbuart_handle_tx_port ( & uart - > port , isr , & ier ) ;
timbuart_mctrl_check ( & uart - > port , isr , & ier ) ;
if ( ! uart - > usedma )
timbuart_handle_rx_port ( & uart - > port , isr , & ier ) ;
2009-06-22 21:43:03 +04:00
iowrite32 ( ier , uart - > port . membase + TIMBUART_IER ) ;
2009-06-11 17:05:39 +04:00
spin_unlock ( & uart - > port . lock ) ;
dev_dbg ( uart - > port . dev , " %s leaving \n " , __func__ ) ;
}
static unsigned int timbuart_tx_empty ( struct uart_port * port )
{
2009-06-22 21:43:03 +04:00
u32 isr = ioread32 ( port - > membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
2009-06-22 21:43:03 +04:00
return ( isr & TXBE ) ? TIOCSER_TEMT : 0 ;
2009-06-11 17:05:39 +04:00
}
static unsigned int timbuart_get_mctrl ( struct uart_port * port )
{
u8 cts = ioread8 ( port - > membase + TIMBUART_CTRL ) ;
dev_dbg ( port - > dev , " %s - cts %x \n " , __func__ , cts ) ;
if ( cts & TIMBUART_CTRL_CTS )
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR ;
else
return TIOCM_DSR | TIOCM_CAR ;
}
static void timbuart_set_mctrl ( struct uart_port * port , unsigned int mctrl )
{
dev_dbg ( port - > dev , " %s - %x \n " , __func__ , mctrl ) ;
if ( mctrl & TIOCM_RTS )
iowrite8 ( TIMBUART_CTRL_RTS , port - > membase + TIMBUART_CTRL ) ;
else
iowrite8 ( TIMBUART_CTRL_RTS , port - > membase + TIMBUART_CTRL ) ;
}
2009-06-22 21:43:03 +04:00
static void timbuart_mctrl_check ( struct uart_port * port , u32 isr , u32 * ier )
2009-06-11 17:05:39 +04:00
{
unsigned int cts ;
if ( isr & CTS_DELTA ) {
/* ack */
2009-06-22 21:43:03 +04:00
iowrite32 ( CTS_DELTA , port - > membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
cts = timbuart_get_mctrl ( port ) ;
uart_handle_cts_change ( port , cts & TIOCM_CTS ) ;
wake_up_interruptible ( & port - > info - > delta_msr_wait ) ;
}
* ier | = CTS_DELTA ;
}
static void timbuart_enable_ms ( struct uart_port * port )
{
/* N/A */
}
static void timbuart_break_ctl ( struct uart_port * port , int ctl )
{
/* N/A */
}
static int timbuart_startup ( struct uart_port * port )
{
struct timbuart_port * uart =
container_of ( port , struct timbuart_port , port ) ;
dev_dbg ( port - > dev , " %s \n " , __func__ ) ;
iowrite8 ( TIMBUART_CTRL_FLSHRX , port - > membase + TIMBUART_CTRL ) ;
2009-06-22 21:43:03 +04:00
iowrite32 ( 0x1ff , port - > membase + TIMBUART_ISR ) ;
2009-06-11 17:05:39 +04:00
/* Enable all but TX interrupts */
2009-06-22 21:43:03 +04:00
iowrite32 ( RXBAF | RXBF | RXTT | CTS_DELTA ,
2009-06-11 17:05:39 +04:00
port - > membase + TIMBUART_IER ) ;
return request_irq ( port - > irq , timbuart_handleinterrupt , IRQF_SHARED ,
" timb-uart " , uart ) ;
}
static void timbuart_shutdown ( struct uart_port * port )
{
struct timbuart_port * uart =
container_of ( port , struct timbuart_port , port ) ;
dev_dbg ( port - > dev , " %s \n " , __func__ ) ;
free_irq ( port - > irq , uart ) ;
2009-06-22 21:43:03 +04:00
iowrite32 ( 0 , port - > membase + TIMBUART_IER ) ;
2009-06-11 17:05:39 +04:00
}
static int get_bindex ( int baud )
{
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( baudrates ) ; i + + )
2009-06-11 17:27:13 +04:00
if ( baud < = baudrates [ i ] )
2009-06-11 17:05:39 +04:00
return i ;
return - 1 ;
}
static void timbuart_set_termios ( struct uart_port * port ,
struct ktermios * termios ,
struct ktermios * old )
{
unsigned int baud ;
short bindex ;
unsigned long flags ;
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
bindex = get_bindex ( baud ) ;
dev_dbg ( port - > dev , " %s - bindex %d \n " , __func__ , bindex ) ;
2009-06-11 17:27:13 +04:00
if ( bindex < 0 )
bindex = 0 ;
baud = baudrates [ bindex ] ;
/* The serial layer calls into this once with old = NULL when setting
up initially */
if ( old )
tty_termios_copy_hw ( termios , old ) ;
tty_termios_encode_baud_rate ( termios , baud , baud ) ;
spin_lock_irqsave ( & port - > lock , flags ) ;
iowrite8 ( ( u8 ) bindex , port - > membase + TIMBUART_BAUDRATE ) ;
uart_update_timeout ( port , termios - > c_cflag , baud ) ;
spin_unlock_irqrestore ( & port - > lock , flags ) ;
2009-06-11 17:05:39 +04:00
}
static const char * timbuart_type ( struct uart_port * port )
{
return port - > type = = PORT_UNKNOWN ? " timbuart " : NULL ;
}
/* We do not request/release mappings of the registers here,
* currently it ' s done in the proble function .
*/
static void timbuart_release_port ( struct uart_port * port )
{
struct platform_device * pdev = to_platform_device ( port - > dev ) ;
int size =
resource_size ( platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ) ;
if ( port - > flags & UPF_IOREMAP ) {
iounmap ( port - > membase ) ;
port - > membase = NULL ;
}
release_mem_region ( port - > mapbase , size ) ;
}
static int timbuart_request_port ( struct uart_port * port )
{
struct platform_device * pdev = to_platform_device ( port - > dev ) ;
int size =
resource_size ( platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ) ;
if ( ! request_mem_region ( port - > mapbase , size , " timb-uart " ) )
return - EBUSY ;
if ( port - > flags & UPF_IOREMAP ) {
port - > membase = ioremap ( port - > mapbase , size ) ;
if ( port - > membase = = NULL ) {
release_mem_region ( port - > mapbase , size ) ;
return - ENOMEM ;
}
}
return 0 ;
}
static irqreturn_t timbuart_handleinterrupt ( int irq , void * devid )
{
struct timbuart_port * uart = ( struct timbuart_port * ) devid ;
if ( ioread8 ( uart - > port . membase + TIMBUART_IPR ) ) {
2009-06-22 21:43:03 +04:00
uart - > last_ier = ioread32 ( uart - > port . membase + TIMBUART_IER ) ;
2009-06-11 17:05:39 +04:00
/* disable interrupts, the tasklet enables them again */
2009-06-22 21:43:03 +04:00
iowrite32 ( 0 , uart - > port . membase + TIMBUART_IER ) ;
2009-06-11 17:05:39 +04:00
/* fire off bottom half */
tasklet_schedule ( & uart - > tasklet ) ;
return IRQ_HANDLED ;
} else
return IRQ_NONE ;
}
/*
* Configure / autoconfigure the port .
*/
static void timbuart_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE ) {
port - > type = PORT_TIMBUART ;
timbuart_request_port ( port ) ;
}
}
static int timbuart_verify_port ( struct uart_port * port ,
struct serial_struct * ser )
{
/* we don't want the core code to modify any port params */
return - EINVAL ;
}
static struct uart_ops timbuart_ops = {
. tx_empty = timbuart_tx_empty ,
. set_mctrl = timbuart_set_mctrl ,
. get_mctrl = timbuart_get_mctrl ,
. stop_tx = timbuart_stop_tx ,
. start_tx = timbuart_start_tx ,
. flush_buffer = timbuart_flush_buffer ,
. stop_rx = timbuart_stop_rx ,
. enable_ms = timbuart_enable_ms ,
. break_ctl = timbuart_break_ctl ,
. startup = timbuart_startup ,
. shutdown = timbuart_shutdown ,
. set_termios = timbuart_set_termios ,
. type = timbuart_type ,
. release_port = timbuart_release_port ,
. request_port = timbuart_request_port ,
. config_port = timbuart_config_port ,
. verify_port = timbuart_verify_port
} ;
static struct uart_driver timbuart_driver = {
. owner = THIS_MODULE ,
. driver_name = " timberdale_uart " ,
. dev_name = " ttyTU " ,
. major = TIMBUART_MAJOR ,
. minor = TIMBUART_MINOR ,
. nr = 1
} ;
static int timbuart_probe ( struct platform_device * dev )
{
int err ;
struct timbuart_port * uart ;
struct resource * iomem ;
dev_dbg ( & dev - > dev , " %s \n " , __func__ ) ;
uart = kzalloc ( sizeof ( * uart ) , GFP_KERNEL ) ;
if ( ! uart ) {
err = - EINVAL ;
goto err_mem ;
}
uart - > usedma = 0 ;
uart - > port . uartclk = 3250000 * 16 ;
uart - > port . fifosize = TIMBUART_FIFO_SIZE ;
uart - > port . regshift = 2 ;
uart - > port . iotype = UPIO_MEM ;
uart - > port . ops = & timbuart_ops ;
uart - > port . irq = 0 ;
uart - > port . flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP ;
uart - > port . line = 0 ;
uart - > port . dev = & dev - > dev ;
iomem = platform_get_resource ( dev , IORESOURCE_MEM , 0 ) ;
if ( ! iomem ) {
err = - ENOMEM ;
goto err_register ;
}
uart - > port . mapbase = iomem - > start ;
uart - > port . membase = NULL ;
uart - > port . irq = platform_get_irq ( dev , 0 ) ;
if ( uart - > port . irq < 0 ) {
err = - EINVAL ;
goto err_register ;
}
tasklet_init ( & uart - > tasklet , timbuart_tasklet , ( unsigned long ) uart ) ;
err = uart_register_driver ( & timbuart_driver ) ;
if ( err )
goto err_register ;
err = uart_add_one_port ( & timbuart_driver , & uart - > port ) ;
if ( err )
goto err_add_port ;
platform_set_drvdata ( dev , uart ) ;
return 0 ;
err_add_port :
uart_unregister_driver ( & timbuart_driver ) ;
err_register :
kfree ( uart ) ;
err_mem :
printk ( KERN_ERR " timberdale: Failed to register Timberdale UART: %d \n " ,
err ) ;
return err ;
}
static int timbuart_remove ( struct platform_device * dev )
{
struct timbuart_port * uart = platform_get_drvdata ( dev ) ;
tasklet_kill ( & uart - > tasklet ) ;
uart_remove_one_port ( & timbuart_driver , & uart - > port ) ;
uart_unregister_driver ( & timbuart_driver ) ;
kfree ( uart ) ;
return 0 ;
}
static struct platform_driver timbuart_platform_driver = {
. driver = {
. name = " timb-uart " ,
. owner = THIS_MODULE ,
} ,
. probe = timbuart_probe ,
. remove = timbuart_remove ,
} ;
/*--------------------------------------------------------------------------*/
static int __init timbuart_init ( void )
{
return platform_driver_register ( & timbuart_platform_driver ) ;
}
static void __exit timbuart_exit ( void )
{
platform_driver_unregister ( & timbuart_platform_driver ) ;
}
module_init ( timbuart_init ) ;
module_exit ( timbuart_exit ) ;
MODULE_DESCRIPTION ( " Timberdale UART driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:timb-uart " ) ;