2012-04-07 00:38:03 +04:00
/*
* Copyright 2012 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 .
*/
/*
* Implementation of mpipe gxio calls .
*/
# include <linux/errno.h>
# include <linux/io.h>
# include <linux/module.h>
# include <gxio/iorpc_globals.h>
# include <gxio/iorpc_mpipe.h>
# include <gxio/iorpc_mpipe_info.h>
# include <gxio/kiorpc.h>
# include <gxio/mpipe.h>
/* HACK: Avoid pointless "shadow" warnings. */
# define link link_shadow
2014-09-03 00:25:22 +04:00
/**
* strscpy - Copy a C - string into a sized buffer , but only if it fits
* @ dest : Where to copy the string to
* @ src : Where to copy the string from
* @ size : size of destination buffer
*
* Use this routine to avoid copying too - long strings .
* The routine returns the total number of bytes copied
* ( including the trailing NUL ) or zero if the buffer wasn ' t
* big enough . To ensure that programmers pay attention
* to the return code , the destination has a single NUL
* written at the front ( if size is non - zero ) when the
* buffer is not big enough .
*/
static size_t strscpy ( char * dest , const char * src , size_t size )
{
size_t len = strnlen ( src , size ) + 1 ;
if ( len > size ) {
if ( size )
dest [ 0 ] = ' \0 ' ;
return 0 ;
}
memcpy ( dest , src , len ) ;
return len ;
}
2012-04-07 00:38:03 +04:00
int gxio_mpipe_init ( gxio_mpipe_context_t * context , unsigned int mpipe_index )
{
char file [ 32 ] ;
int fd ;
int i ;
2013-08-01 19:36:42 +04:00
if ( mpipe_index > = GXIO_MPIPE_INSTANCE_MAX )
return - EINVAL ;
2012-04-07 00:38:03 +04:00
snprintf ( file , sizeof ( file ) , " mpipe/%d/iorpc " , mpipe_index ) ;
fd = hv_dev_open ( ( HV_VirtAddr ) file , 0 ) ;
2013-08-01 19:36:42 +04:00
context - > fd = fd ;
2012-04-07 00:38:03 +04:00
if ( fd < 0 ) {
if ( fd > = GXIO_ERR_MIN & & fd < = GXIO_ERR_MAX )
return fd ;
else
return - ENODEV ;
}
/* Map in the MMIO space. */
context - > mmio_cfg_base = ( void __force * )
iorpc_ioremap ( fd , HV_MPIPE_CONFIG_MMIO_OFFSET ,
HV_MPIPE_CONFIG_MMIO_SIZE ) ;
if ( context - > mmio_cfg_base = = NULL )
goto cfg_failed ;
context - > mmio_fast_base = ( void __force * )
iorpc_ioremap ( fd , HV_MPIPE_FAST_MMIO_OFFSET ,
HV_MPIPE_FAST_MMIO_SIZE ) ;
if ( context - > mmio_fast_base = = NULL )
goto fast_failed ;
/* Initialize the stacks. */
for ( i = 0 ; i < 8 ; i + + )
context - > __stacks . stacks [ i ] = 255 ;
2013-08-01 19:36:42 +04:00
context - > instance = mpipe_index ;
2012-04-07 00:38:03 +04:00
return 0 ;
fast_failed :
iounmap ( ( void __force __iomem * ) ( context - > mmio_cfg_base ) ) ;
cfg_failed :
hv_dev_close ( context - > fd ) ;
2013-08-01 19:36:42 +04:00
context - > fd = - 1 ;
2012-04-07 00:38:03 +04:00
return - ENODEV ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_init ) ;
int gxio_mpipe_destroy ( gxio_mpipe_context_t * context )
{
iounmap ( ( void __force __iomem * ) ( context - > mmio_cfg_base ) ) ;
iounmap ( ( void __force __iomem * ) ( context - > mmio_fast_base ) ) ;
return hv_dev_close ( context - > fd ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_destroy ) ;
static int16_t gxio_mpipe_buffer_sizes [ 8 ] =
{ 128 , 256 , 512 , 1024 , 1664 , 4096 , 10368 , 16384 } ;
gxio_mpipe_buffer_size_enum_t gxio_mpipe_buffer_size_to_buffer_size_enum ( size_t
size )
{
int i ;
for ( i = 0 ; i < 7 ; i + + )
if ( size < = gxio_mpipe_buffer_sizes [ i ] )
break ;
return i ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_buffer_size_to_buffer_size_enum ) ;
size_t gxio_mpipe_buffer_size_enum_to_buffer_size ( gxio_mpipe_buffer_size_enum_t
buffer_size_enum )
{
if ( buffer_size_enum > 7 )
buffer_size_enum = 7 ;
return gxio_mpipe_buffer_sizes [ buffer_size_enum ] ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_buffer_size_enum_to_buffer_size ) ;
size_t gxio_mpipe_calc_buffer_stack_bytes ( unsigned long buffers )
{
const int BUFFERS_PER_LINE = 12 ;
/* Count the number of cachlines. */
unsigned long lines =
( buffers + BUFFERS_PER_LINE - 1 ) / BUFFERS_PER_LINE ;
/* Convert to bytes. */
return lines * CHIP_L2_LINE_SIZE ( ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_calc_buffer_stack_bytes ) ;
int gxio_mpipe_init_buffer_stack ( gxio_mpipe_context_t * context ,
unsigned int stack ,
gxio_mpipe_buffer_size_enum_t
buffer_size_enum , void * mem , size_t mem_size ,
unsigned int mem_flags )
{
int result ;
memset ( mem , 0 , mem_size ) ;
result = gxio_mpipe_init_buffer_stack_aux ( context , mem , mem_size ,
mem_flags , stack ,
buffer_size_enum ) ;
if ( result < 0 )
return result ;
/* Save the stack. */
context - > __stacks . stacks [ buffer_size_enum ] = stack ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_init_buffer_stack ) ;
int gxio_mpipe_init_notif_ring ( gxio_mpipe_context_t * context ,
unsigned int ring ,
void * mem , size_t mem_size ,
unsigned int mem_flags )
{
return gxio_mpipe_init_notif_ring_aux ( context , mem , mem_size ,
mem_flags , ring ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_init_notif_ring ) ;
int gxio_mpipe_init_notif_group_and_buckets ( gxio_mpipe_context_t * context ,
unsigned int group ,
unsigned int ring ,
unsigned int num_rings ,
unsigned int bucket ,
unsigned int num_buckets ,
gxio_mpipe_bucket_mode_t mode )
{
int i ;
int result ;
gxio_mpipe_bucket_info_t bucket_info = { {
. group = group ,
. mode = mode ,
}
} ;
gxio_mpipe_notif_group_bits_t bits = { { 0 } } ;
for ( i = 0 ; i < num_rings ; i + + )
gxio_mpipe_notif_group_add_ring ( & bits , ring + i ) ;
result = gxio_mpipe_init_notif_group ( context , group , bits ) ;
if ( result ! = 0 )
return result ;
for ( i = 0 ; i < num_buckets ; i + + ) {
bucket_info . notifring = ring + ( i % num_rings ) ;
result = gxio_mpipe_init_bucket ( context , bucket + i ,
bucket_info ) ;
if ( result ! = 0 )
return result ;
}
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_init_notif_group_and_buckets ) ;
int gxio_mpipe_init_edma_ring ( gxio_mpipe_context_t * context ,
unsigned int ring , unsigned int channel ,
void * mem , size_t mem_size ,
unsigned int mem_flags )
{
memset ( mem , 0 , mem_size ) ;
return gxio_mpipe_init_edma_ring_aux ( context , mem , mem_size , mem_flags ,
ring , channel ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_init_edma_ring ) ;
void gxio_mpipe_rules_init ( gxio_mpipe_rules_t * rules ,
gxio_mpipe_context_t * context )
{
rules - > context = context ;
memset ( & rules - > list , 0 , sizeof ( rules - > list ) ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_rules_init ) ;
int gxio_mpipe_rules_begin ( gxio_mpipe_rules_t * rules ,
unsigned int bucket , unsigned int num_buckets ,
gxio_mpipe_rules_stacks_t * stacks )
{
int i ;
int stack = 255 ;
gxio_mpipe_rules_list_t * list = & rules - > list ;
/* Current rule. */
gxio_mpipe_rules_rule_t * rule =
( gxio_mpipe_rules_rule_t * ) ( list - > rules + list - > head ) ;
unsigned int head = list - > tail ;
/*
* Align next rule properly .
* Note that " dmacs_and_vlans " will also be aligned .
*/
unsigned int pad = 0 ;
while ( ( ( head + pad ) % __alignof__ ( gxio_mpipe_rules_rule_t ) ) ! = 0 )
pad + + ;
/*
* Verify room .
* ISSUE : Mark rules as broken on error ?
*/
if ( head + pad + sizeof ( * rule ) > = sizeof ( list - > rules ) )
return GXIO_MPIPE_ERR_RULES_FULL ;
/* Verify num_buckets is a power of 2. */
if ( __builtin_popcount ( num_buckets ) ! = 1 )
return GXIO_MPIPE_ERR_RULES_INVALID ;
/* Add padding to previous rule. */
rule - > size + = pad ;
/* Start a new rule. */
list - > head = head + pad ;
rule = ( gxio_mpipe_rules_rule_t * ) ( list - > rules + list - > head ) ;
/* Default some values. */
rule - > headroom = 2 ;
rule - > tailroom = 0 ;
rule - > capacity = 16384 ;
/* Save the bucket info. */
rule - > bucket_mask = num_buckets - 1 ;
rule - > bucket_first = bucket ;
for ( i = 8 - 1 ; i > = 0 ; i - - ) {
int maybe =
stacks ? stacks - > stacks [ i ] : rules - > context - > __stacks .
stacks [ i ] ;
if ( maybe ! = 255 )
stack = maybe ;
rule - > stacks . stacks [ i ] = stack ;
}
if ( stack = = 255 )
return GXIO_MPIPE_ERR_RULES_INVALID ;
/* NOTE: Only entries at the end of the array can be 255. */
for ( i = 8 - 1 ; i > 0 ; i - - ) {
if ( rule - > stacks . stacks [ i ] = = 255 ) {
rule - > stacks . stacks [ i ] = stack ;
rule - > capacity =
gxio_mpipe_buffer_size_enum_to_buffer_size ( i -
1 ) ;
}
}
rule - > size = sizeof ( * rule ) ;
list - > tail = list - > head + rule - > size ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_rules_begin ) ;
int gxio_mpipe_rules_add_channel ( gxio_mpipe_rules_t * rules ,
unsigned int channel )
{
gxio_mpipe_rules_list_t * list = & rules - > list ;
gxio_mpipe_rules_rule_t * rule =
( gxio_mpipe_rules_rule_t * ) ( list - > rules + list - > head ) ;
/* Verify channel. */
if ( channel > = 32 )
return GXIO_MPIPE_ERR_RULES_INVALID ;
/* Verify begun. */
if ( list - > tail = = 0 )
return GXIO_MPIPE_ERR_RULES_EMPTY ;
rule - > channel_bits | = ( 1UL < < channel ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_rules_add_channel ) ;
int gxio_mpipe_rules_set_headroom ( gxio_mpipe_rules_t * rules , uint8_t headroom )
{
gxio_mpipe_rules_list_t * list = & rules - > list ;
gxio_mpipe_rules_rule_t * rule =
( gxio_mpipe_rules_rule_t * ) ( list - > rules + list - > head ) ;
/* Verify begun. */
if ( list - > tail = = 0 )
return GXIO_MPIPE_ERR_RULES_EMPTY ;
rule - > headroom = headroom ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_rules_set_headroom ) ;
int gxio_mpipe_rules_commit ( gxio_mpipe_rules_t * rules )
{
gxio_mpipe_rules_list_t * list = & rules - > list ;
unsigned int size =
offsetof ( gxio_mpipe_rules_list_t , rules ) + list - > tail ;
return gxio_mpipe_commit_rules ( rules - > context , list , size ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_rules_commit ) ;
int gxio_mpipe_iqueue_init ( gxio_mpipe_iqueue_t * iqueue ,
gxio_mpipe_context_t * context ,
unsigned int ring ,
void * mem , size_t mem_size , unsigned int mem_flags )
{
/* The init call below will verify that "mem_size" is legal. */
unsigned int num_entries = mem_size / sizeof ( gxio_mpipe_idesc_t ) ;
iqueue - > context = context ;
iqueue - > idescs = ( gxio_mpipe_idesc_t * ) mem ;
iqueue - > ring = ring ;
iqueue - > num_entries = num_entries ;
iqueue - > mask_num_entries = num_entries - 1 ;
iqueue - > log2_num_entries = __builtin_ctz ( num_entries ) ;
iqueue - > head = 1 ;
# ifdef __BIG_ENDIAN__
iqueue - > swapped = 0 ;
# endif
/* Initialize the "tail". */
__gxio_mmio_write ( mem , iqueue - > head ) ;
return gxio_mpipe_init_notif_ring ( context , ring , mem , mem_size ,
mem_flags ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_iqueue_init ) ;
int gxio_mpipe_equeue_init ( gxio_mpipe_equeue_t * equeue ,
gxio_mpipe_context_t * context ,
2013-08-01 19:36:42 +04:00
unsigned int ering ,
2012-04-07 00:38:03 +04:00
unsigned int channel ,
void * mem , unsigned int mem_size ,
unsigned int mem_flags )
{
/* The init call below will verify that "mem_size" is legal. */
unsigned int num_entries = mem_size / sizeof ( gxio_mpipe_edesc_t ) ;
/* Offset used to read number of completed commands. */
MPIPE_EDMA_POST_REGION_ADDR_t offset ;
2013-08-01 19:36:42 +04:00
int result = gxio_mpipe_init_edma_ring ( context , ering , channel ,
2012-04-07 00:38:03 +04:00
mem , mem_size , mem_flags ) ;
if ( result < 0 )
return result ;
memset ( equeue , 0 , sizeof ( * equeue ) ) ;
offset . word = 0 ;
offset . region =
MPIPE_MMIO_ADDR__REGION_VAL_EDMA -
MPIPE_MMIO_ADDR__REGION_VAL_IDMA ;
2013-08-01 19:36:42 +04:00
offset . ring = ering ;
2012-04-07 00:38:03 +04:00
__gxio_dma_queue_init ( & equeue - > dma_queue ,
context - > mmio_fast_base + offset . word ,
num_entries ) ;
equeue - > edescs = mem ;
equeue - > mask_num_entries = num_entries - 1 ;
equeue - > log2_num_entries = __builtin_ctz ( num_entries ) ;
2013-08-01 19:36:42 +04:00
equeue - > context = context ;
equeue - > ering = ering ;
equeue - > channel = channel ;
2012-04-07 00:38:03 +04:00
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_equeue_init ) ;
int gxio_mpipe_set_timestamp ( gxio_mpipe_context_t * context ,
const struct timespec * ts )
{
cycles_t cycles = get_cycles ( ) ;
return gxio_mpipe_set_timestamp_aux ( context , ( uint64_t ) ts - > tv_sec ,
( uint64_t ) ts - > tv_nsec ,
( uint64_t ) cycles ) ;
}
2014-11-09 13:32:03 +03:00
EXPORT_SYMBOL_GPL ( gxio_mpipe_set_timestamp ) ;
2012-04-07 00:38:03 +04:00
int gxio_mpipe_get_timestamp ( gxio_mpipe_context_t * context ,
struct timespec * ts )
{
int ret ;
cycles_t cycles_prev , cycles_now , clock_rate ;
cycles_prev = get_cycles ( ) ;
ret = gxio_mpipe_get_timestamp_aux ( context , ( uint64_t * ) & ts - > tv_sec ,
( uint64_t * ) & ts - > tv_nsec ,
( uint64_t * ) & cycles_now ) ;
if ( ret < 0 ) {
return ret ;
}
clock_rate = get_clock_rate ( ) ;
ts - > tv_nsec - = ( cycles_now - cycles_prev ) * 1000000000LL / clock_rate ;
if ( ts - > tv_nsec < 0 ) {
ts - > tv_nsec + = 1000000000LL ;
ts - > tv_sec - = 1 ;
}
return ret ;
}
2014-11-09 13:32:03 +03:00
EXPORT_SYMBOL_GPL ( gxio_mpipe_get_timestamp ) ;
2012-04-07 00:38:03 +04:00
int gxio_mpipe_adjust_timestamp ( gxio_mpipe_context_t * context , int64_t delta )
{
return gxio_mpipe_adjust_timestamp_aux ( context , delta ) ;
}
2014-11-09 13:32:03 +03:00
EXPORT_SYMBOL_GPL ( gxio_mpipe_adjust_timestamp ) ;
2012-04-07 00:38:03 +04:00
/* Get our internal context used for link name access. This context is
* special in that it is not associated with an mPIPE service domain .
*/
static gxio_mpipe_context_t * _gxio_get_link_context ( void )
{
static gxio_mpipe_context_t context ;
static gxio_mpipe_context_t * contextp ;
static int tried_open = 0 ;
static DEFINE_MUTEX ( mutex ) ;
mutex_lock ( & mutex ) ;
if ( ! tried_open ) {
int i = 0 ;
tried_open = 1 ;
/*
* " 4 " here is the maximum possible number of mPIPE shims ; it ' s
* an exaggeration but we shouldn ' t ever go beyond 2 anyway .
*/
for ( i = 0 ; i < 4 ; i + + ) {
char file [ 80 ] ;
snprintf ( file , sizeof ( file ) , " mpipe/%d/iorpc_info " , i ) ;
context . fd = hv_dev_open ( ( HV_VirtAddr ) file , 0 ) ;
if ( context . fd < 0 )
continue ;
contextp = & context ;
break ;
}
}
mutex_unlock ( & mutex ) ;
return contextp ;
}
2013-08-01 19:36:42 +04:00
int gxio_mpipe_link_instance ( const char * link_name )
{
_gxio_mpipe_link_name_t name ;
gxio_mpipe_context_t * context = _gxio_get_link_context ( ) ;
if ( ! context )
return GXIO_ERR_NO_DEVICE ;
2014-09-03 00:25:22 +04:00
if ( strscpy ( name . name , link_name , sizeof ( name . name ) ) = = 0 )
return GXIO_ERR_NO_DEVICE ;
2013-08-01 19:36:42 +04:00
return gxio_mpipe_info_instance_aux ( context , name ) ;
}
2014-11-09 13:32:03 +03:00
EXPORT_SYMBOL_GPL ( gxio_mpipe_link_instance ) ;
2013-08-01 19:36:42 +04:00
2012-04-07 00:38:03 +04:00
int gxio_mpipe_link_enumerate_mac ( int idx , char * link_name , uint8_t * link_mac )
{
int rv ;
_gxio_mpipe_link_name_t name ;
_gxio_mpipe_link_mac_t mac ;
gxio_mpipe_context_t * context = _gxio_get_link_context ( ) ;
if ( ! context )
return GXIO_ERR_NO_DEVICE ;
rv = gxio_mpipe_info_enumerate_aux ( context , idx , & name , & mac ) ;
if ( rv > = 0 ) {
2014-09-03 00:25:22 +04:00
if ( strscpy ( link_name , name . name , sizeof ( name . name ) ) = = 0 )
return GXIO_ERR_INVAL_MEMORY_SIZE ;
2012-04-07 00:38:03 +04:00
memcpy ( link_mac , mac . mac , sizeof ( mac . mac ) ) ;
}
return rv ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_link_enumerate_mac ) ;
int gxio_mpipe_link_open ( gxio_mpipe_link_t * link ,
gxio_mpipe_context_t * context , const char * link_name ,
unsigned int flags )
{
_gxio_mpipe_link_name_t name ;
int rv ;
2014-09-03 00:25:22 +04:00
if ( strscpy ( name . name , link_name , sizeof ( name . name ) ) = = 0 )
return GXIO_ERR_NO_DEVICE ;
2012-04-07 00:38:03 +04:00
rv = gxio_mpipe_link_open_aux ( context , name , flags ) ;
if ( rv < 0 )
return rv ;
link - > context = context ;
link - > channel = rv > > 8 ;
link - > mac = rv & 0xFF ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_link_open ) ;
int gxio_mpipe_link_close ( gxio_mpipe_link_t * link )
{
return gxio_mpipe_link_close_aux ( link - > context , link - > mac ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_link_close ) ;
2013-08-01 19:36:42 +04:00
int gxio_mpipe_link_set_attr ( gxio_mpipe_link_t * link , uint32_t attr ,
int64_t val )
{
return gxio_mpipe_link_set_attr_aux ( link - > context , link - > mac , attr ,
val ) ;
}
EXPORT_SYMBOL_GPL ( gxio_mpipe_link_set_attr ) ;