2013-08-12 14:11:44 -04:00
/*
* Copyright 2013 Tilera Corporation . All Rights Reserved .
*
* 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.
*
* 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 , GOOD TITLE or
* NON INFRINGEMENT . See the GNU General Public License for
* more details .
*
* TILEGx UART driver .
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/module.h>
# include <linux/serial_core.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <gxio/common.h>
# include <gxio/iorpc_globals.h>
# include <gxio/iorpc_uart.h>
# include <gxio/kiorpc.h>
# include <hv/drv_uart_intf.h>
/*
* Use device name ttyS , major 4 , minor 64 - 65.
* This is the usual serial port name , 8250 conventional range .
*/
# define TILEGX_UART_MAJOR TTY_MAJOR
# define TILEGX_UART_MINOR 64
# define TILEGX_UART_NAME "ttyS"
# define DRIVER_NAME_STRING "TILEGx_Serial"
# define TILEGX_UART_REF_CLK 125000000; /* REF_CLK is always 125 MHz. */
struct tile_uart_port {
/* UART port. */
struct uart_port uart ;
/* GXIO device context. */
gxio_uart_context_t context ;
/* UART access mutex. */
struct mutex mutex ;
/* CPU receiving interrupts. */
int irq_cpu ;
} ;
static struct tile_uart_port tile_uart_ports [ TILEGX_UART_NR ] ;
static struct uart_driver tilegx_uart_driver ;
/*
* Read UART rx fifo , and insert the chars into tty buffer .
*/
static void receive_chars ( struct tile_uart_port * tile_uart ,
struct tty_struct * tty )
{
int i ;
char c ;
UART_FIFO_COUNT_t count ;
gxio_uart_context_t * context = & tile_uart - > context ;
struct tty_port * port = tty - > port ;
count . word = gxio_uart_read ( context , UART_FIFO_COUNT ) ;
for ( i = 0 ; i < count . rfifo_count ; i + + ) {
c = ( char ) gxio_uart_read ( context , UART_RECEIVE_DATA ) ;
tty_insert_flip_char ( port , c , TTY_NORMAL ) ;
}
}
/*
* Drain the Rx FIFO , called by interrupt handler .
*/
static void handle_receive ( struct tile_uart_port * tile_uart )
{
struct tty_port * port = & tile_uart - > uart . state - > port ;
struct tty_struct * tty = tty_port_tty_get ( port ) ;
gxio_uart_context_t * context = & tile_uart - > context ;
if ( ! tty )
return ;
/* First read UART rx fifo. */
receive_chars ( tile_uart , tty ) ;
/* Reset RFIFO_WE interrupt. */
gxio_uart_write ( context , UART_INTERRUPT_STATUS ,
UART_INTERRUPT_MASK__RFIFO_WE_MASK ) ;
/* Final read, if any chars comes between the first read and
* the interrupt reset .
*/
receive_chars ( tile_uart , tty ) ;
spin_unlock ( & tile_uart - > uart . lock ) ;
tty_flip_buffer_push ( port ) ;
spin_lock ( & tile_uart - > uart . lock ) ;
tty_kref_put ( tty ) ;
}
/*
* Push one char to UART Write FIFO .
* Return 0 on success , - 1 if write filo is full .
*/
static int tilegx_putchar ( gxio_uart_context_t * context , char c )
{
UART_FLAG_t flag ;
flag . word = gxio_uart_read ( context , UART_FLAG ) ;
if ( flag . wfifo_full )
return - 1 ;
gxio_uart_write ( context , UART_TRANSMIT_DATA , ( unsigned long ) c ) ;
return 0 ;
}
/*
* Send chars to UART Write FIFO ; called by interrupt handler .
*/
static void handle_transmit ( struct tile_uart_port * tile_uart )
{
unsigned char ch ;
struct uart_port * port ;
struct circ_buf * xmit ;
gxio_uart_context_t * context = & tile_uart - > context ;
/* First reset WFIFO_RE interrupt. */
gxio_uart_write ( context , UART_INTERRUPT_STATUS ,
UART_INTERRUPT_MASK__WFIFO_RE_MASK ) ;
port = & tile_uart - > uart ;
xmit = & port - > state - > xmit ;
if ( port - > x_char ) {
if ( tilegx_putchar ( context , port - > x_char ) )
return ;
port - > x_char = 0 ;
port - > icount . tx + + ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) )
return ;
while ( ! uart_circ_empty ( xmit ) ) {
ch = xmit - > buf [ xmit - > tail ] ;
if ( tilegx_putchar ( context , ch ) )
break ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
}
/* Reset WFIFO_RE interrupt. */
gxio_uart_write ( context , UART_INTERRUPT_STATUS ,
UART_INTERRUPT_MASK__WFIFO_RE_MASK ) ;
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
}
/*
* UART Interrupt handler .
*/
static irqreturn_t tilegx_interrupt ( int irq , void * dev_id )
{
unsigned long flags ;
UART_INTERRUPT_STATUS_t intr_stat ;
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
struct uart_port * port = dev_id ;
irqreturn_t ret = IRQ_NONE ;
spin_lock_irqsave ( & port - > lock , flags ) ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
context = & tile_uart - > context ;
intr_stat . word = gxio_uart_read ( context , UART_INTERRUPT_STATUS ) ;
if ( intr_stat . rfifo_we ) {
handle_receive ( tile_uart ) ;
ret = IRQ_HANDLED ;
}
if ( intr_stat . wfifo_re ) {
handle_transmit ( tile_uart ) ;
ret = IRQ_HANDLED ;
}
spin_unlock_irqrestore ( & port - > lock , flags ) ;
return ret ;
}
/*
* Return TIOCSER_TEMT when transmitter FIFO is empty .
*/
static u_int tilegx_tx_empty ( struct uart_port * port )
{
int ret ;
UART_FLAG_t flag ;
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
if ( ! mutex_trylock ( & tile_uart - > mutex ) )
return 0 ;
context = & tile_uart - > context ;
flag . word = gxio_uart_read ( context , UART_FLAG ) ;
ret = ( flag . wfifo_empty ) ? TIOCSER_TEMT : 0 ;
mutex_unlock ( & tile_uart - > mutex ) ;
return ret ;
}
/*
* Set state of the modem control output lines .
*/
static void tilegx_set_mctrl ( struct uart_port * port , u_int mctrl )
{
/* N/A */
}
/*
* Get state of the modem control input lines .
*/
static u_int tilegx_get_mctrl ( struct uart_port * port )
{
return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR ;
}
/*
* Stop transmitting .
*/
static void tilegx_stop_tx ( struct uart_port * port )
{
/* N/A */
}
/*
* Start transmitting .
*/
static void tilegx_start_tx ( struct uart_port * port )
{
unsigned char ch ;
struct circ_buf * xmit ;
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
if ( ! mutex_trylock ( & tile_uart - > mutex ) )
return ;
context = & tile_uart - > context ;
xmit = & port - > state - > xmit ;
if ( port - > x_char ) {
if ( tilegx_putchar ( context , port - > x_char ) )
return ;
port - > x_char = 0 ;
port - > icount . tx + + ;
}
if ( uart_circ_empty ( xmit ) | | uart_tx_stopped ( port ) ) {
mutex_unlock ( & tile_uart - > mutex ) ;
return ;
}
while ( ! uart_circ_empty ( xmit ) ) {
ch = xmit - > buf [ xmit - > tail ] ;
if ( tilegx_putchar ( context , ch ) )
break ;
xmit - > tail = ( xmit - > tail + 1 ) & ( UART_XMIT_SIZE - 1 ) ;
port - > icount . tx + + ;
}
if ( uart_circ_chars_pending ( xmit ) < WAKEUP_CHARS )
uart_write_wakeup ( port ) ;
mutex_unlock ( & tile_uart - > mutex ) ;
}
/*
* Stop receiving - port is in process of being closed .
*/
static void tilegx_stop_rx ( struct uart_port * port )
{
int err ;
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
int cpu ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
if ( ! mutex_trylock ( & tile_uart - > mutex ) )
return ;
context = & tile_uart - > context ;
cpu = tile_uart - > irq_cpu ;
err = gxio_uart_cfg_interrupt ( context , cpu_x ( cpu ) , cpu_y ( cpu ) ,
KERNEL_PL , - 1 ) ;
mutex_unlock ( & tile_uart - > mutex ) ;
}
/*
* Control the transmission of a break signal .
*/
static void tilegx_break_ctl ( struct uart_port * port , int break_state )
{
/* N/A */
}
/*
* Perform initialization and enable port for reception .
*/
static int tilegx_startup ( struct uart_port * port )
{
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
int ret = 0 ;
int cpu = raw_smp_processor_id ( ) ; /* pick an arbitrary cpu */
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
if ( mutex_lock_interruptible ( & tile_uart - > mutex ) )
return - EBUSY ;
context = & tile_uart - > context ;
/* Now open the hypervisor device if we haven't already. */
if ( context - > fd < 0 ) {
UART_INTERRUPT_MASK_t intr_mask ;
/* Initialize UART device. */
ret = gxio_uart_init ( context , port - > line ) ;
if ( ret ) {
ret = - ENXIO ;
goto err ;
}
/* Create our IRQs. */
2014-05-07 15:44:15 +00:00
port - > irq = irq_alloc_hwirq ( - 1 ) ;
if ( ! port - > irq )
2013-08-12 14:11:44 -04:00
goto err_uart_dest ;
tile_irq_activate ( port - > irq , TILE_IRQ_PERCPU ) ;
/* Register our IRQs. */
ret = request_irq ( port - > irq , tilegx_interrupt , 0 ,
tilegx_uart_driver . driver_name , port ) ;
if ( ret )
goto err_dest_irq ;
/* Request that the hardware start sending us interrupts. */
tile_uart - > irq_cpu = cpu ;
ret = gxio_uart_cfg_interrupt ( context , cpu_x ( cpu ) , cpu_y ( cpu ) ,
KERNEL_PL , port - > irq ) ;
if ( ret )
goto err_free_irq ;
/* Enable UART Tx/Rx Interrupt. */
intr_mask . word = gxio_uart_read ( context , UART_INTERRUPT_MASK ) ;
intr_mask . wfifo_re = 0 ;
intr_mask . rfifo_we = 0 ;
gxio_uart_write ( context , UART_INTERRUPT_MASK , intr_mask . word ) ;
/* Reset the Tx/Rx interrupt in case it's set. */
gxio_uart_write ( context , UART_INTERRUPT_STATUS ,
UART_INTERRUPT_MASK__WFIFO_RE_MASK |
UART_INTERRUPT_MASK__RFIFO_WE_MASK ) ;
}
mutex_unlock ( & tile_uart - > mutex ) ;
return ret ;
err_free_irq :
free_irq ( port - > irq , port ) ;
err_dest_irq :
2014-05-07 15:44:15 +00:00
irq_free_hwirq ( port - > irq ) ;
2013-08-12 14:11:44 -04:00
err_uart_dest :
gxio_uart_destroy ( context ) ;
ret = - ENXIO ;
err :
mutex_unlock ( & tile_uart - > mutex ) ;
return ret ;
}
/*
* Release kernel resources if it is the last close , disable the port ,
* free IRQ and close the port .
*/
static void tilegx_shutdown ( struct uart_port * port )
{
int err ;
UART_INTERRUPT_MASK_t intr_mask ;
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
int cpu ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
if ( mutex_lock_interruptible ( & tile_uart - > mutex ) )
return ;
context = & tile_uart - > context ;
/* Disable UART Tx/Rx Interrupt. */
intr_mask . word = gxio_uart_read ( context , UART_INTERRUPT_MASK ) ;
intr_mask . wfifo_re = 1 ;
intr_mask . rfifo_we = 1 ;
gxio_uart_write ( context , UART_INTERRUPT_MASK , intr_mask . word ) ;
/* Request that the hardware stop sending us interrupts. */
cpu = tile_uart - > irq_cpu ;
err = gxio_uart_cfg_interrupt ( context , cpu_x ( cpu ) , cpu_y ( cpu ) ,
KERNEL_PL , - 1 ) ;
if ( port - > irq > 0 ) {
free_irq ( port - > irq , port ) ;
2014-05-07 15:44:15 +00:00
irq_free_hwirq ( port - > irq ) ;
2013-08-12 14:11:44 -04:00
port - > irq = 0 ;
}
gxio_uart_destroy ( context ) ;
mutex_unlock ( & tile_uart - > mutex ) ;
}
/*
* Flush the buffer .
*/
static void tilegx_flush_buffer ( struct uart_port * port )
{
/* N/A */
}
/*
* Change the port parameters .
*/
static void tilegx_set_termios ( struct uart_port * port ,
struct ktermios * termios , struct ktermios * old )
{
int err ;
UART_DIVISOR_t divisor ;
UART_TYPE_t type ;
unsigned int baud ;
struct tile_uart_port * tile_uart ;
gxio_uart_context_t * context ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
if ( ! mutex_trylock ( & tile_uart - > mutex ) )
return ;
context = & tile_uart - > context ;
/* Open the hypervisor device if we haven't already. */
if ( context - > fd < 0 ) {
err = gxio_uart_init ( context , port - > line ) ;
if ( err ) {
mutex_unlock ( & tile_uart - > mutex ) ;
return ;
}
}
divisor . word = gxio_uart_read ( context , UART_DIVISOR ) ;
type . word = gxio_uart_read ( context , UART_TYPE ) ;
/* Divisor. */
baud = uart_get_baud_rate ( port , termios , old , 0 , port - > uartclk / 16 ) ;
divisor . divisor = uart_get_divisor ( port , baud ) ;
/* Byte size. */
if ( ( termios - > c_cflag & CSIZE ) = = CS7 )
type . dbits = UART_TYPE__DBITS_VAL_SEVEN_DBITS ;
else
type . dbits = UART_TYPE__DBITS_VAL_EIGHT_DBITS ;
/* Parity. */
if ( termios - > c_cflag & PARENB ) {
/* Mark or Space parity. */
if ( termios - > c_cflag & CMSPAR )
if ( termios - > c_cflag & PARODD )
type . ptype = UART_TYPE__PTYPE_VAL_MARK ;
else
type . ptype = UART_TYPE__PTYPE_VAL_SPACE ;
else if ( termios - > c_cflag & PARODD )
type . ptype = UART_TYPE__PTYPE_VAL_ODD ;
else
type . ptype = UART_TYPE__PTYPE_VAL_EVEN ;
} else
type . ptype = UART_TYPE__PTYPE_VAL_NONE ;
/* Stop bits. */
if ( termios - > c_cflag & CSTOPB )
type . sbits = UART_TYPE__SBITS_VAL_TWO_SBITS ;
else
type . sbits = UART_TYPE__SBITS_VAL_ONE_SBITS ;
/* Set the uart paramters. */
gxio_uart_write ( context , UART_DIVISOR , divisor . word ) ;
gxio_uart_write ( context , UART_TYPE , type . word ) ;
mutex_unlock ( & tile_uart - > mutex ) ;
}
/*
* Return string describing the specified port .
*/
static const char * tilegx_type ( struct uart_port * port )
{
return port - > type = = PORT_TILEGX ? DRIVER_NAME_STRING : NULL ;
}
/*
* Release the resources being used by ' port ' .
*/
static void tilegx_release_port ( struct uart_port * port )
{
/* Nothing to release. */
}
/*
* Request the resources being used by ' port ' .
*/
static int tilegx_request_port ( struct uart_port * port )
{
/* Always present. */
return 0 ;
}
/*
* Configure / autoconfigure the port .
*/
static void tilegx_config_port ( struct uart_port * port , int flags )
{
if ( flags & UART_CONFIG_TYPE )
port - > type = PORT_TILEGX ;
}
/*
* Verify the new serial_struct ( for TIOCSSERIAL ) .
*/
static int tilegx_verify_port ( struct uart_port * port ,
struct serial_struct * ser )
{
if ( ( ser - > type ! = PORT_UNKNOWN ) & & ( ser - > type ! = PORT_TILEGX ) )
return - EINVAL ;
return 0 ;
}
# ifdef CONFIG_CONSOLE_POLL
/*
* Console polling routines for writing and reading from the uart while
* in an interrupt or debug context .
*/
static int tilegx_poll_get_char ( struct uart_port * port )
{
UART_FIFO_COUNT_t count ;
gxio_uart_context_t * context ;
struct tile_uart_port * tile_uart ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
context = & tile_uart - > context ;
count . word = gxio_uart_read ( context , UART_FIFO_COUNT ) ;
if ( count . rfifo_count = = 0 )
return NO_POLL_CHAR ;
return ( char ) gxio_uart_read ( context , UART_RECEIVE_DATA ) ;
}
static void tilegx_poll_put_char ( struct uart_port * port , unsigned char c )
{
gxio_uart_context_t * context ;
struct tile_uart_port * tile_uart ;
tile_uart = container_of ( port , struct tile_uart_port , uart ) ;
context = & tile_uart - > context ;
gxio_uart_write ( context , UART_TRANSMIT_DATA , ( unsigned long ) c ) ;
}
# endif /* CONFIG_CONSOLE_POLL */
static const struct uart_ops tilegx_ops = {
. tx_empty = tilegx_tx_empty ,
. set_mctrl = tilegx_set_mctrl ,
. get_mctrl = tilegx_get_mctrl ,
. stop_tx = tilegx_stop_tx ,
. start_tx = tilegx_start_tx ,
. stop_rx = tilegx_stop_rx ,
. break_ctl = tilegx_break_ctl ,
. startup = tilegx_startup ,
. shutdown = tilegx_shutdown ,
. flush_buffer = tilegx_flush_buffer ,
. set_termios = tilegx_set_termios ,
. type = tilegx_type ,
. release_port = tilegx_release_port ,
. request_port = tilegx_request_port ,
. config_port = tilegx_config_port ,
. verify_port = tilegx_verify_port ,
# ifdef CONFIG_CONSOLE_POLL
. poll_get_char = tilegx_poll_get_char ,
. poll_put_char = tilegx_poll_put_char ,
# endif
} ;
static void tilegx_init_ports ( void )
{
int i ;
struct uart_port * port ;
for ( i = 0 ; i < TILEGX_UART_NR ; i + + ) {
port = & tile_uart_ports [ i ] . uart ;
port - > ops = & tilegx_ops ;
port - > line = i ;
port - > type = PORT_TILEGX ;
port - > uartclk = TILEGX_UART_REF_CLK ;
port - > flags = UPF_BOOT_AUTOCONF ;
tile_uart_ports [ i ] . context . fd = - 1 ;
mutex_init ( & tile_uart_ports [ i ] . mutex ) ;
}
}
static struct uart_driver tilegx_uart_driver = {
. owner = THIS_MODULE ,
. driver_name = DRIVER_NAME_STRING ,
. dev_name = TILEGX_UART_NAME ,
. major = TILEGX_UART_MAJOR ,
. minor = TILEGX_UART_MINOR ,
. nr = TILEGX_UART_NR ,
} ;
static int __init tilegx_init ( void )
{
int i ;
int ret ;
struct tty_driver * tty_drv ;
ret = uart_register_driver ( & tilegx_uart_driver ) ;
if ( ret )
return ret ;
tty_drv = tilegx_uart_driver . tty_driver ;
tty_drv - > init_termios . c_cflag = B115200 | CS8 | CREAD | HUPCL | CLOCAL ;
tty_drv - > init_termios . c_ispeed = 115200 ;
tty_drv - > init_termios . c_ospeed = 115200 ;
tilegx_init_ports ( ) ;
for ( i = 0 ; i < TILEGX_UART_NR ; i + + ) {
struct uart_port * port = & tile_uart_ports [ i ] . uart ;
ret = uart_add_one_port ( & tilegx_uart_driver , port ) ;
}
return 0 ;
}
static void __exit tilegx_exit ( void )
{
int i ;
struct uart_port * port ;
for ( i = 0 ; i < TILEGX_UART_NR ; i + + ) {
port = & tile_uart_ports [ i ] . uart ;
uart_remove_one_port ( & tilegx_uart_driver , port ) ;
}
uart_unregister_driver ( & tilegx_uart_driver ) ;
}
module_init ( tilegx_init ) ;
module_exit ( tilegx_exit ) ;
MODULE_AUTHOR ( " Tilera Corporation " ) ;
MODULE_DESCRIPTION ( " TILEGx serial port driver " ) ;
MODULE_LICENSE ( " GPL " ) ;