2010-05-28 23:09:12 -04:00
/*
2010-07-02 14:19:35 -04:00
* Copyright 2010 Tilera Corporation . All Rights Reserved .
2010-05-28 23:09:12 -04:00
*
2010-07-02 14:19:35 -04:00
* 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.
2010-05-28 23:09:12 -04:00
*
2010-07-02 14:19:35 -04:00
* 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 .
2010-05-28 23:09:12 -04:00
*
* Tilera TILE Processor hypervisor console
*/
# include <linux/console.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/init.h>
2013-08-06 14:11:21 -04:00
# include <linux/interrupt.h>
# include <linux/irq.h>
2010-05-28 23:09:12 -04:00
# include <linux/moduleparam.h>
2013-08-06 14:11:21 -04:00
# include <linux/platform_device.h>
2010-05-28 23:09:12 -04:00
# include <linux/types.h>
2013-08-06 14:11:21 -04:00
# include <asm/setup.h>
# include <arch/sim_def.h>
2010-05-28 23:09:12 -04:00
# include <hv/hypervisor.h>
# include "hvc_console.h"
2013-08-06 14:11:21 -04:00
static int use_sim_console ;
static int __init sim_console ( char * str )
{
use_sim_console = 1 ;
return 0 ;
}
early_param ( " sim_console " , sim_console ) ;
int tile_console_write ( const char * buf , int count )
{
if ( unlikely ( use_sim_console ) ) {
int i ;
for ( i = 0 ; i < count ; + + i )
__insn_mtspr ( SPR_SIM_CONTROL , SIM_CONTROL_PUTC |
( buf [ i ] < < _SIM_CONTROL_OPERATOR_BITS ) ) ;
__insn_mtspr ( SPR_SIM_CONTROL , SIM_CONTROL_PUTC |
( SIM_PUTC_FLUSH_BINARY < <
_SIM_CONTROL_OPERATOR_BITS ) ) ;
return 0 ;
} else {
2015-05-04 17:25:12 -04:00
/* Translate 0 bytes written to EAGAIN for hvc_console_print. */
return hv_console_write ( ( HV_VirtAddr ) buf , count ) ? : - EAGAIN ;
2013-08-06 14:11:21 -04:00
}
}
2010-05-28 23:09:12 -04:00
static int hvc_tile_put_chars ( uint32_t vt , const char * buf , int count )
{
2013-08-06 14:11:21 -04:00
return tile_console_write ( buf , count ) ;
2010-05-28 23:09:12 -04:00
}
static int hvc_tile_get_chars ( uint32_t vt , char * buf , int count )
{
int i , c ;
for ( i = 0 ; i < count ; + + i ) {
c = hv_console_read_if_ready ( ) ;
if ( c < 0 )
break ;
buf [ i ] = c ;
}
return i ;
}
2013-08-06 14:11:21 -04:00
# ifdef __tilegx__
/*
* IRQ based callbacks .
*/
static int hvc_tile_notifier_add_irq ( struct hvc_struct * hp , int irq )
{
int rc ;
int cpu = raw_smp_processor_id ( ) ; /* Choose an arbitrary cpu */
HV_Coord coord = { . x = cpu_x ( cpu ) , . y = cpu_y ( cpu ) } ;
rc = notifier_add_irq ( hp , irq ) ;
if ( rc )
return rc ;
/*
* Request that the hypervisor start sending us interrupts .
* If the hypervisor returns an error , we still return 0 , so that
* we can fall back to polling .
*/
if ( hv_console_set_ipi ( KERNEL_PL , irq , coord ) < 0 )
notifier_del_irq ( hp , irq ) ;
return 0 ;
}
static void hvc_tile_notifier_del_irq ( struct hvc_struct * hp , int irq )
{
HV_Coord coord = { 0 , 0 } ;
/* Tell the hypervisor to stop sending us interrupts. */
hv_console_set_ipi ( KERNEL_PL , - 1 , coord ) ;
notifier_del_irq ( hp , irq ) ;
}
static void hvc_tile_notifier_hangup_irq ( struct hvc_struct * hp , int irq )
{
hvc_tile_notifier_del_irq ( hp , irq ) ;
}
# endif
2010-05-28 23:09:12 -04:00
static const struct hv_ops hvc_tile_get_put_ops = {
. get_chars = hvc_tile_get_chars ,
. put_chars = hvc_tile_put_chars ,
2013-08-06 14:11:21 -04:00
# ifdef __tilegx__
. notifier_add = hvc_tile_notifier_add_irq ,
. notifier_del = hvc_tile_notifier_del_irq ,
. notifier_hangup = hvc_tile_notifier_hangup_irq ,
# endif
} ;
# ifdef __tilegx__
static int hvc_tile_probe ( struct platform_device * pdev )
{
struct hvc_struct * hp ;
int tile_hvc_irq ;
/* Create our IRQ and register it. */
2014-05-07 15:44:15 +00:00
tile_hvc_irq = irq_alloc_hwirq ( - 1 ) ;
if ( ! tile_hvc_irq )
2013-08-06 14:11:21 -04:00
return - ENXIO ;
tile_irq_activate ( tile_hvc_irq , TILE_IRQ_PERCPU ) ;
hp = hvc_alloc ( 0 , tile_hvc_irq , & hvc_tile_get_put_ops , 128 ) ;
if ( IS_ERR ( hp ) ) {
2014-05-07 15:44:15 +00:00
irq_free_hwirq ( tile_hvc_irq ) ;
2013-08-06 14:11:21 -04:00
return PTR_ERR ( hp ) ;
}
dev_set_drvdata ( & pdev - > dev , hp ) ;
return 0 ;
}
static int hvc_tile_remove ( struct platform_device * pdev )
{
int rc ;
struct hvc_struct * hp = dev_get_drvdata ( & pdev - > dev ) ;
rc = hvc_remove ( hp ) ;
if ( rc = = 0 )
2014-05-07 15:44:15 +00:00
irq_free_hwirq ( hp - > data ) ;
2013-08-06 14:11:21 -04:00
return rc ;
}
static void hvc_tile_shutdown ( struct platform_device * pdev )
{
struct hvc_struct * hp = dev_get_drvdata ( & pdev - > dev ) ;
hvc_tile_notifier_del_irq ( hp , hp - > data ) ;
}
static struct platform_device hvc_tile_pdev = {
. name = " hvc-tile " ,
. id = 0 ,
} ;
static struct platform_driver hvc_tile_driver = {
. probe = hvc_tile_probe ,
. remove = hvc_tile_remove ,
. shutdown = hvc_tile_shutdown ,
. driver = {
. name = " hvc-tile " ,
}
2010-05-28 23:09:12 -04:00
} ;
2013-08-06 14:11:21 -04:00
# endif
2010-05-28 23:09:12 -04:00
static int __init hvc_tile_console_init ( void )
{
hvc_instantiate ( 0 , 0 , & hvc_tile_get_put_ops ) ;
add_preferred_console ( " hvc " , 0 , NULL ) ;
return 0 ;
}
console_initcall ( hvc_tile_console_init ) ;
static int __init hvc_tile_init ( void )
{
2013-08-06 14:11:21 -04:00
# ifndef __tilegx__
struct hvc_struct * hp ;
hp = hvc_alloc ( 0 , 0 , & hvc_tile_get_put_ops , 128 ) ;
2014-06-01 13:56:23 +02:00
return PTR_ERR_OR_ZERO ( hp ) ;
2013-08-06 14:11:21 -04:00
# else
platform_device_register ( & hvc_tile_pdev ) ;
return platform_driver_register ( & hvc_tile_driver ) ;
# endif
2010-05-28 23:09:12 -04:00
}
device_initcall ( hvc_tile_init ) ;