2013-01-22 03:38:47 +04:00
/*
* Copyright ( C ) 2007 Google , Inc .
* Copyright ( C ) 2012 Intel , Inc .
2017-08-29 16:53:20 +03:00
* Copyright ( C ) 2017 Imagination Technologies Ltd .
2013-01-22 03:38:47 +04:00
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*
*/
# include <linux/console.h>
# include <linux/interrupt.h>
# include <linux/platform_device.h>
# include <linux/tty.h>
# include <linux/tty_flip.h>
# include <linux/slab.h>
# include <linux/io.h>
# include <linux/module.h>
2014-05-12 19:57:05 +04:00
# include <linux/goldfish.h>
2017-08-29 16:53:19 +03:00
# include <linux/mm.h>
# include <linux/dma-mapping.h>
2017-08-29 16:53:20 +03:00
# include <linux/serial_core.h>
2013-01-22 03:38:47 +04:00
2017-08-29 16:53:18 +03:00
/* Goldfish tty register's offsets */
# define GOLDFISH_TTY_REG_BYTES_READY 0x04
# define GOLDFISH_TTY_REG_CMD 0x08
# define GOLDFISH_TTY_REG_DATA_PTR 0x10
# define GOLDFISH_TTY_REG_DATA_LEN 0x14
# define GOLDFISH_TTY_REG_DATA_PTR_HIGH 0x18
2017-08-29 16:53:19 +03:00
# define GOLDFISH_TTY_REG_VERSION 0x20
2017-08-29 16:53:18 +03:00
/* Goldfish tty commands */
# define GOLDFISH_TTY_CMD_INT_DISABLE 0
# define GOLDFISH_TTY_CMD_INT_ENABLE 1
# define GOLDFISH_TTY_CMD_WRITE_BUFFER 2
# define GOLDFISH_TTY_CMD_READ_BUFFER 3
2013-01-22 03:38:47 +04:00
struct goldfish_tty {
struct tty_port port ;
spinlock_t lock ;
void __iomem * base ;
u32 irq ;
int opencount ;
struct console console ;
2017-08-29 16:53:19 +03:00
u32 version ;
struct device * dev ;
2013-01-22 03:38:47 +04:00
} ;
static DEFINE_MUTEX ( goldfish_tty_lock ) ;
static struct tty_driver * goldfish_tty_driver ;
static u32 goldfish_tty_line_count = 8 ;
static u32 goldfish_tty_current_line_count ;
static struct goldfish_tty * goldfish_ttys ;
2017-08-29 16:53:19 +03:00
static void do_rw_io ( struct goldfish_tty * qtty ,
unsigned long address ,
unsigned int count ,
int is_write )
2013-01-22 03:38:47 +04:00
{
unsigned long irq_flags ;
void __iomem * base = qtty - > base ;
2017-08-29 16:53:19 +03:00
2013-01-22 03:38:47 +04:00
spin_lock_irqsave ( & qtty - > lock , irq_flags ) ;
2017-08-29 16:53:19 +03:00
gf_write_ptr ( ( void * ) address , base + GOLDFISH_TTY_REG_DATA_PTR ,
2017-08-29 16:53:18 +03:00
base + GOLDFISH_TTY_REG_DATA_PTR_HIGH ) ;
writel ( count , base + GOLDFISH_TTY_REG_DATA_LEN ) ;
2017-08-29 16:53:19 +03:00
if ( is_write )
writel ( GOLDFISH_TTY_CMD_WRITE_BUFFER ,
base + GOLDFISH_TTY_REG_CMD ) ;
else
writel ( GOLDFISH_TTY_CMD_READ_BUFFER ,
base + GOLDFISH_TTY_REG_CMD ) ;
2013-01-22 03:38:47 +04:00
spin_unlock_irqrestore ( & qtty - > lock , irq_flags ) ;
}
2017-08-29 16:53:19 +03:00
static void goldfish_tty_rw ( struct goldfish_tty * qtty ,
unsigned long addr ,
unsigned int count ,
int is_write )
{
dma_addr_t dma_handle ;
enum dma_data_direction dma_dir ;
dma_dir = ( is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE ) ;
if ( qtty - > version > 0 ) {
/*
* Goldfish TTY for Ranchu platform uses
* physical addresses and DMA for read / write operations
*/
unsigned long addr_end = addr + count ;
while ( addr < addr_end ) {
unsigned long pg_end = ( addr & PAGE_MASK ) + PAGE_SIZE ;
unsigned long next =
pg_end < addr_end ? pg_end : addr_end ;
unsigned long avail = next - addr ;
/*
* Map the buffer ' s virtual address to the DMA address
* so the buffer can be accessed by the device .
*/
dma_handle = dma_map_single ( qtty - > dev , ( void * ) addr ,
avail , dma_dir ) ;
if ( dma_mapping_error ( qtty - > dev , dma_handle ) ) {
dev_err ( qtty - > dev , " tty: DMA mapping error. \n " ) ;
return ;
}
do_rw_io ( qtty , dma_handle , avail , is_write ) ;
/*
* Unmap the previously mapped region after
* the completion of the read / write operation .
*/
dma_unmap_single ( qtty - > dev , dma_handle , avail , dma_dir ) ;
addr + = avail ;
}
} else {
/*
* Old style Goldfish TTY used on the Goldfish platform
* uses virtual addresses .
*/
do_rw_io ( qtty , addr , count , is_write ) ;
}
}
static void goldfish_tty_do_write ( int line , const char * buf ,
unsigned int count )
{
struct goldfish_tty * qtty = & goldfish_ttys [ line ] ;
unsigned long address = ( unsigned long ) ( void * ) buf ;
goldfish_tty_rw ( qtty , address , count , 1 ) ;
}
2013-01-22 03:38:47 +04:00
static irqreturn_t goldfish_tty_interrupt ( int irq , void * dev_id )
{
2016-02-26 22:01:05 +03:00
struct goldfish_tty * qtty = dev_id ;
2013-01-22 03:38:47 +04:00
void __iomem * base = qtty - > base ;
2017-08-29 16:53:19 +03:00
unsigned long address ;
2013-01-22 03:38:47 +04:00
unsigned char * buf ;
u32 count ;
2017-08-29 16:53:18 +03:00
count = readl ( base + GOLDFISH_TTY_REG_BYTES_READY ) ;
2014-05-12 19:57:14 +04:00
if ( count = = 0 )
2013-01-22 03:38:47 +04:00
return IRQ_NONE ;
2013-01-25 19:05:30 +04:00
count = tty_prepare_flip_string ( & qtty - > port , & buf , count ) ;
2017-08-29 16:53:19 +03:00
address = ( unsigned long ) ( void * ) buf ;
goldfish_tty_rw ( qtty , address , count , 0 ) ;
2013-01-25 19:05:30 +04:00
tty_schedule_flip ( & qtty - > port ) ;
2013-01-22 03:38:47 +04:00
return IRQ_HANDLED ;
}
static int goldfish_tty_activate ( struct tty_port * port , struct tty_struct * tty )
{
2014-05-12 19:57:14 +04:00
struct goldfish_tty * qtty = container_of ( port , struct goldfish_tty ,
port ) ;
2017-08-29 16:53:18 +03:00
writel ( GOLDFISH_TTY_CMD_INT_ENABLE , qtty - > base + GOLDFISH_TTY_REG_CMD ) ;
2013-01-22 03:38:47 +04:00
return 0 ;
}
static void goldfish_tty_shutdown ( struct tty_port * port )
{
2014-05-12 19:57:14 +04:00
struct goldfish_tty * qtty = container_of ( port , struct goldfish_tty ,
port ) ;
2017-08-29 16:53:18 +03:00
writel ( GOLDFISH_TTY_CMD_INT_DISABLE , qtty - > base + GOLDFISH_TTY_REG_CMD ) ;
2013-01-22 03:38:47 +04:00
}
2014-05-12 19:57:14 +04:00
static int goldfish_tty_open ( struct tty_struct * tty , struct file * filp )
2013-01-22 03:38:47 +04:00
{
struct goldfish_tty * qtty = & goldfish_ttys [ tty - > index ] ;
return tty_port_open ( & qtty - > port , tty , filp ) ;
}
2014-05-12 19:57:14 +04:00
static void goldfish_tty_close ( struct tty_struct * tty , struct file * filp )
2013-01-22 03:38:47 +04:00
{
tty_port_close ( tty - > port , tty , filp ) ;
}
static void goldfish_tty_hangup ( struct tty_struct * tty )
{
tty_port_hangup ( tty - > port ) ;
}
2014-05-12 19:57:14 +04:00
static int goldfish_tty_write ( struct tty_struct * tty , const unsigned char * buf ,
int count )
2013-01-22 03:38:47 +04:00
{
goldfish_tty_do_write ( tty - > index , buf , count ) ;
return count ;
}
static int goldfish_tty_write_room ( struct tty_struct * tty )
{
return 0x10000 ;
}
static int goldfish_tty_chars_in_buffer ( struct tty_struct * tty )
{
struct goldfish_tty * qtty = & goldfish_ttys [ tty - > index ] ;
void __iomem * base = qtty - > base ;
2017-08-29 16:53:18 +03:00
return readl ( base + GOLDFISH_TTY_REG_BYTES_READY ) ;
2013-01-22 03:38:47 +04:00
}
2014-05-12 19:57:14 +04:00
static void goldfish_tty_console_write ( struct console * co , const char * b ,
unsigned count )
2013-01-22 03:38:47 +04:00
{
goldfish_tty_do_write ( co - > index , b , count ) ;
}
2014-05-12 19:57:14 +04:00
static struct tty_driver * goldfish_tty_console_device ( struct console * c ,
int * index )
2013-01-22 03:38:47 +04:00
{
* index = c - > index ;
return goldfish_tty_driver ;
}
static int goldfish_tty_console_setup ( struct console * co , char * options )
{
2014-10-29 11:43:25 +03:00
if ( ( unsigned ) co - > index > = goldfish_tty_line_count )
2013-01-22 03:38:47 +04:00
return - ENODEV ;
2014-09-28 22:10:17 +04:00
if ( ! goldfish_ttys [ co - > index ] . base )
2013-01-22 03:38:47 +04:00
return - ENODEV ;
return 0 ;
}
2015-12-15 02:53:36 +03:00
static const struct tty_port_operations goldfish_port_ops = {
2013-01-22 03:38:47 +04:00
. activate = goldfish_tty_activate ,
. shutdown = goldfish_tty_shutdown
} ;
2014-05-12 19:57:14 +04:00
static const struct tty_operations goldfish_tty_ops = {
2013-01-22 03:38:47 +04:00
. open = goldfish_tty_open ,
. close = goldfish_tty_close ,
. hangup = goldfish_tty_hangup ,
. write = goldfish_tty_write ,
. write_room = goldfish_tty_write_room ,
. chars_in_buffer = goldfish_tty_chars_in_buffer ,
} ;
static int goldfish_tty_create_driver ( void )
{
int ret ;
struct tty_driver * tty ;
2014-05-12 19:57:14 +04:00
goldfish_ttys = kzalloc ( sizeof ( * goldfish_ttys ) *
goldfish_tty_line_count , GFP_KERNEL ) ;
if ( goldfish_ttys = = NULL ) {
2013-01-22 03:38:47 +04:00
ret = - ENOMEM ;
goto err_alloc_goldfish_ttys_failed ;
}
tty = alloc_tty_driver ( goldfish_tty_line_count ) ;
2014-05-12 19:57:14 +04:00
if ( tty = = NULL ) {
2013-01-22 03:38:47 +04:00
ret = - ENOMEM ;
goto err_alloc_tty_driver_failed ;
}
tty - > driver_name = " goldfish " ;
tty - > name = " ttyGF " ;
tty - > type = TTY_DRIVER_TYPE_SERIAL ;
tty - > subtype = SERIAL_TYPE_NORMAL ;
tty - > init_termios = tty_std_termios ;
2014-05-12 19:57:14 +04:00
tty - > flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW |
TTY_DRIVER_DYNAMIC_DEV ;
2013-01-22 03:38:47 +04:00
tty_set_operations ( tty , & goldfish_tty_ops ) ;
ret = tty_register_driver ( tty ) ;
2014-05-12 19:57:14 +04:00
if ( ret )
2013-01-22 03:38:47 +04:00
goto err_tty_register_driver_failed ;
goldfish_tty_driver = tty ;
return 0 ;
err_tty_register_driver_failed :
put_tty_driver ( tty ) ;
err_alloc_tty_driver_failed :
kfree ( goldfish_ttys ) ;
goldfish_ttys = NULL ;
err_alloc_goldfish_ttys_failed :
return ret ;
}
static void goldfish_tty_delete_driver ( void )
{
tty_unregister_driver ( goldfish_tty_driver ) ;
put_tty_driver ( goldfish_tty_driver ) ;
goldfish_tty_driver = NULL ;
kfree ( goldfish_ttys ) ;
goldfish_ttys = NULL ;
}
static int goldfish_tty_probe ( struct platform_device * pdev )
{
struct goldfish_tty * qtty ;
2017-08-29 16:53:19 +03:00
int ret = - ENODEV ;
2013-01-22 03:38:47 +04:00
struct resource * r ;
struct device * ttydev ;
void __iomem * base ;
u32 irq ;
2016-02-26 22:01:05 +03:00
unsigned int line ;
2013-01-22 03:38:47 +04:00
r = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2017-08-29 16:53:19 +03:00
if ( ! r ) {
pr_err ( " goldfish_tty: No MEM resource available! \n " ) ;
return - ENOMEM ;
}
2013-01-22 03:38:47 +04:00
base = ioremap ( r - > start , 0x1000 ) ;
2017-08-29 16:53:19 +03:00
if ( ! base ) {
pr_err ( " goldfish_tty: Unable to ioremap base! \n " ) ;
return - ENOMEM ;
}
2013-01-22 03:38:47 +04:00
r = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
2017-08-29 16:53:19 +03:00
if ( ! r ) {
pr_err ( " goldfish_tty: No IRQ resource available! \n " ) ;
2013-01-22 03:38:47 +04:00
goto err_unmap ;
2017-08-29 16:53:19 +03:00
}
2013-01-22 03:38:47 +04:00
irq = r - > start ;
mutex_lock ( & goldfish_tty_lock ) ;
2016-02-26 22:01:05 +03:00
if ( pdev - > id = = PLATFORM_DEVID_NONE )
line = goldfish_tty_current_line_count ;
else
line = pdev - > id ;
2017-08-29 16:53:19 +03:00
if ( line > = goldfish_tty_line_count ) {
pr_err ( " goldfish_tty: Reached maximum tty number of %d. \n " ,
goldfish_tty_current_line_count ) ;
ret = - ENOMEM ;
goto err_unlock ;
}
2016-02-26 22:01:05 +03:00
2014-05-12 19:57:14 +04:00
if ( goldfish_tty_current_line_count = = 0 ) {
2013-01-22 03:38:47 +04:00
ret = goldfish_tty_create_driver ( ) ;
2014-05-12 19:57:14 +04:00
if ( ret )
2017-08-29 16:53:19 +03:00
goto err_unlock ;
2013-01-22 03:38:47 +04:00
}
goldfish_tty_current_line_count + + ;
2016-02-26 22:01:05 +03:00
qtty = & goldfish_ttys [ line ] ;
2013-01-22 03:38:47 +04:00
spin_lock_init ( & qtty - > lock ) ;
tty_port_init ( & qtty - > port ) ;
qtty - > port . ops = & goldfish_port_ops ;
qtty - > base = base ;
qtty - > irq = irq ;
2017-08-29 16:53:19 +03:00
qtty - > dev = & pdev - > dev ;
/*
* Goldfish TTY device used by the Goldfish emulator
* should identify itself with 0 , forcing the driver
* to use virtual addresses . Goldfish TTY device
* on Ranchu emulator ( qemu2 ) returns 1 here and
* driver will use physical addresses .
*/
qtty - > version = readl ( base + GOLDFISH_TTY_REG_VERSION ) ;
/*
* Goldfish TTY device on Ranchu emulator ( qemu2 )
* will use DMA for read / write IO operations .
*/
if ( qtty - > version > 0 ) {
/*
* Initialize dma_mask to 32 - bits .
*/
if ( ! pdev - > dev . dma_mask )
pdev - > dev . dma_mask = & pdev - > dev . coherent_dma_mask ;
ret = dma_set_mask ( & pdev - > dev , DMA_BIT_MASK ( 32 ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " No suitable DMA available. \n " ) ;
goto err_dec_line_count ;
}
}
2013-01-22 03:38:47 +04:00
2017-08-29 16:53:18 +03:00
writel ( GOLDFISH_TTY_CMD_INT_DISABLE , base + GOLDFISH_TTY_REG_CMD ) ;
2013-01-22 03:38:47 +04:00
2014-05-12 19:57:14 +04:00
ret = request_irq ( irq , goldfish_tty_interrupt , IRQF_SHARED ,
2017-08-29 16:53:19 +03:00
" goldfish_tty " , qtty ) ;
if ( ret ) {
pr_err ( " goldfish_tty: No IRQ available! \n " ) ;
goto err_dec_line_count ;
}
2013-01-22 03:38:47 +04:00
ttydev = tty_port_register_device ( & qtty - > port , goldfish_tty_driver ,
2017-08-29 16:53:19 +03:00
line , & pdev - > dev ) ;
2014-05-12 19:57:14 +04:00
if ( IS_ERR ( ttydev ) ) {
2013-01-22 03:38:47 +04:00
ret = PTR_ERR ( ttydev ) ;
goto err_tty_register_device_failed ;
}
strcpy ( qtty - > console . name , " ttyGF " ) ;
qtty - > console . write = goldfish_tty_console_write ;
qtty - > console . device = goldfish_tty_console_device ;
qtty - > console . setup = goldfish_tty_console_setup ;
qtty - > console . flags = CON_PRINTBUFFER ;
2016-02-26 22:01:05 +03:00
qtty - > console . index = line ;
2013-01-22 03:38:47 +04:00
register_console ( & qtty - > console ) ;
2016-02-26 22:01:05 +03:00
platform_set_drvdata ( pdev , qtty ) ;
2013-01-22 03:38:47 +04:00
mutex_unlock ( & goldfish_tty_lock ) ;
return 0 ;
err_tty_register_device_failed :
2017-01-09 03:26:37 +03:00
free_irq ( irq , qtty ) ;
2017-08-29 16:53:19 +03:00
err_dec_line_count :
2013-01-22 03:38:47 +04:00
goldfish_tty_current_line_count - - ;
2014-05-12 19:57:14 +04:00
if ( goldfish_tty_current_line_count = = 0 )
2013-01-22 03:38:47 +04:00
goldfish_tty_delete_driver ( ) ;
2017-08-29 16:53:19 +03:00
err_unlock :
2013-01-22 03:38:47 +04:00
mutex_unlock ( & goldfish_tty_lock ) ;
err_unmap :
iounmap ( base ) ;
return ret ;
}
static int goldfish_tty_remove ( struct platform_device * pdev )
{
2016-02-26 22:01:05 +03:00
struct goldfish_tty * qtty = platform_get_drvdata ( pdev ) ;
2013-01-22 03:38:47 +04:00
mutex_lock ( & goldfish_tty_lock ) ;
unregister_console ( & qtty - > console ) ;
2016-02-26 22:01:05 +03:00
tty_unregister_device ( goldfish_tty_driver , qtty - > console . index ) ;
2013-01-22 03:38:47 +04:00
iounmap ( qtty - > base ) ;
2014-09-28 22:10:17 +04:00
qtty - > base = NULL ;
2013-01-22 03:38:47 +04:00
free_irq ( qtty - > irq , pdev ) ;
goldfish_tty_current_line_count - - ;
2014-05-12 19:57:14 +04:00
if ( goldfish_tty_current_line_count = = 0 )
2013-01-22 03:38:47 +04:00
goldfish_tty_delete_driver ( ) ;
mutex_unlock ( & goldfish_tty_lock ) ;
return 0 ;
}
2017-08-29 16:53:20 +03:00
static void gf_early_console_putchar ( struct uart_port * port , int ch )
{
__raw_writel ( ch , port - > membase ) ;
}
static void gf_early_write ( struct console * con , const char * s , unsigned int n )
{
struct earlycon_device * dev = con - > data ;
uart_console_write ( & dev - > port , s , n , gf_early_console_putchar ) ;
}
static int __init gf_earlycon_setup ( struct earlycon_device * device ,
const char * opt )
{
if ( ! device - > port . membase )
return - ENODEV ;
device - > con - > write = gf_early_write ;
return 0 ;
}
OF_EARLYCON_DECLARE ( early_gf_tty , " google,goldfish-tty " , gf_earlycon_setup ) ;
2016-02-26 22:00:44 +03:00
static const struct of_device_id goldfish_tty_of_match [ ] = {
{ . compatible = " google,goldfish-tty " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , goldfish_tty_of_match ) ;
2013-01-22 03:38:47 +04:00
static struct platform_driver goldfish_tty_platform_driver = {
. probe = goldfish_tty_probe ,
. remove = goldfish_tty_remove ,
. driver = {
2016-02-26 22:00:44 +03:00
. name = " goldfish_tty " ,
. of_match_table = goldfish_tty_of_match ,
2013-01-22 03:38:47 +04:00
}
} ;
module_platform_driver ( goldfish_tty_platform_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;