2020-03-13 17:17:06 +05:30
// SPDX-License-Identifier: GPL-2.0
/* Marvell OcteonTX CPT driver
*
* Copyright ( C ) 2019 Marvell International Ltd .
*
* 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 .
*/
# include <linux/ctype.h>
# include <linux/firmware.h>
# include "otx_cpt_common.h"
# include "otx_cptpf_ucode.h"
# include "otx_cptpf.h"
# define CSR_DELAY 30
/* Tar archive defines */
# define TAR_MAGIC "ustar"
# define TAR_MAGIC_LEN 6
# define TAR_BLOCK_LEN 512
# define REGTYPE '0'
# define AREGTYPE '\0'
/* tar header as defined in POSIX 1003.1-1990. */
struct tar_hdr_t {
char name [ 100 ] ;
char mode [ 8 ] ;
char uid [ 8 ] ;
char gid [ 8 ] ;
char size [ 12 ] ;
char mtime [ 12 ] ;
char chksum [ 8 ] ;
char typeflag ;
char linkname [ 100 ] ;
char magic [ 6 ] ;
char version [ 2 ] ;
char uname [ 32 ] ;
char gname [ 32 ] ;
char devmajor [ 8 ] ;
char devminor [ 8 ] ;
char prefix [ 155 ] ;
} ;
struct tar_blk_t {
union {
struct tar_hdr_t hdr ;
char block [ TAR_BLOCK_LEN ] ;
} ;
} ;
struct tar_arch_info_t {
struct list_head ucodes ;
const struct firmware * fw ;
} ;
static struct otx_cpt_bitmap get_cores_bmap ( struct device * dev ,
struct otx_cpt_eng_grp_info * eng_grp )
{
struct otx_cpt_bitmap bmap = { { 0 } } ;
bool found = false ;
int i ;
if ( eng_grp - > g - > engs_num > OTX_CPT_MAX_ENGINES ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " unsupported number of engines %d on octeontx \n " ,
2020-03-13 17:17:06 +05:30
eng_grp - > g - > engs_num ) ;
return bmap ;
}
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
if ( eng_grp - > engs [ i ] . type ) {
bitmap_or ( bmap . bits , bmap . bits ,
eng_grp - > engs [ i ] . bmap ,
eng_grp - > g - > engs_num ) ;
bmap . size = eng_grp - > g - > engs_num ;
found = true ;
}
}
if ( ! found )
2020-04-11 14:06:33 +02:00
dev_err ( dev , " No engines reserved for engine group %d \n " ,
2020-03-13 17:17:06 +05:30
eng_grp - > idx ) ;
return bmap ;
}
static int is_eng_type ( int val , int eng_type )
{
return val & ( 1 < < eng_type ) ;
}
static int dev_supports_eng_type ( struct otx_cpt_eng_grps * eng_grps ,
int eng_type )
{
return is_eng_type ( eng_grps - > eng_types_supported , eng_type ) ;
}
static void set_ucode_filename ( struct otx_cpt_ucode * ucode ,
const char * filename )
{
2022-08-18 23:00:03 +02:00
strscpy ( ucode - > filename , filename , OTX_CPT_UCODE_NAME_LENGTH ) ;
2020-03-13 17:17:06 +05:30
}
static char * get_eng_type_str ( int eng_type )
{
char * str = " unknown " ;
switch ( eng_type ) {
case OTX_CPT_SE_TYPES :
str = " SE " ;
break ;
case OTX_CPT_AE_TYPES :
str = " AE " ;
break ;
}
return str ;
}
static char * get_ucode_type_str ( int ucode_type )
{
char * str = " unknown " ;
switch ( ucode_type ) {
case ( 1 < < OTX_CPT_SE_TYPES ) :
str = " SE " ;
break ;
case ( 1 < < OTX_CPT_AE_TYPES ) :
str = " AE " ;
break ;
}
return str ;
}
static int get_ucode_type ( struct otx_cpt_ucode_hdr * ucode_hdr , int * ucode_type )
{
char tmp_ver_str [ OTX_CPT_UCODE_VER_STR_SZ ] ;
u32 i , val = 0 ;
u8 nn ;
2022-08-18 23:00:03 +02:00
strscpy ( tmp_ver_str , ucode_hdr - > ver_str , OTX_CPT_UCODE_VER_STR_SZ ) ;
2020-03-13 17:17:06 +05:30
for ( i = 0 ; i < strlen ( tmp_ver_str ) ; i + + )
tmp_ver_str [ i ] = tolower ( tmp_ver_str [ i ] ) ;
nn = ucode_hdr - > ver_num . nn ;
if ( strnstr ( tmp_ver_str , " se- " , OTX_CPT_UCODE_VER_STR_SZ ) & &
( nn = = OTX_CPT_SE_UC_TYPE1 | | nn = = OTX_CPT_SE_UC_TYPE2 | |
nn = = OTX_CPT_SE_UC_TYPE3 ) )
val | = 1 < < OTX_CPT_SE_TYPES ;
if ( strnstr ( tmp_ver_str , " ae " , OTX_CPT_UCODE_VER_STR_SZ ) & &
nn = = OTX_CPT_AE_UC_TYPE )
val | = 1 < < OTX_CPT_AE_TYPES ;
* ucode_type = val ;
if ( ! val )
return - EINVAL ;
if ( is_eng_type ( val , OTX_CPT_AE_TYPES ) & &
is_eng_type ( val , OTX_CPT_SE_TYPES ) )
return - EINVAL ;
return 0 ;
}
static int is_mem_zero ( const char * ptr , int size )
{
int i ;
for ( i = 0 ; i < size ; i + + ) {
if ( ptr [ i ] )
return 0 ;
}
return 1 ;
}
static int cpt_set_ucode_base ( struct otx_cpt_eng_grp_info * eng_grp , void * obj )
{
struct otx_cpt_device * cpt = ( struct otx_cpt_device * ) obj ;
dma_addr_t dma_addr ;
struct otx_cpt_bitmap bmap ;
int i ;
bmap = get_cores_bmap ( & cpt - > pdev - > dev , eng_grp ) ;
if ( ! bmap . size )
return - EINVAL ;
if ( eng_grp - > mirror . is_ena )
dma_addr =
eng_grp - > g - > grp [ eng_grp - > mirror . idx ] . ucode [ 0 ] . align_dma ;
else
dma_addr = eng_grp - > ucode [ 0 ] . align_dma ;
/*
* Set UCODE_BASE only for the cores which are not used ,
* other cores should have already valid UCODE_BASE set
*/
for_each_set_bit ( i , bmap . bits , bmap . size )
if ( ! eng_grp - > g - > eng_ref_cnt [ i ] )
writeq ( ( u64 ) dma_addr , cpt - > reg_base +
OTX_CPT_PF_ENGX_UCODE_BASE ( i ) ) ;
return 0 ;
}
static int cpt_detach_and_disable_cores ( struct otx_cpt_eng_grp_info * eng_grp ,
void * obj )
{
struct otx_cpt_device * cpt = ( struct otx_cpt_device * ) obj ;
struct otx_cpt_bitmap bmap = { { 0 } } ;
int timeout = 10 ;
int i , busy ;
u64 reg ;
bmap = get_cores_bmap ( & cpt - > pdev - > dev , eng_grp ) ;
if ( ! bmap . size )
return - EINVAL ;
/* Detach the cores from group */
reg = readq ( cpt - > reg_base + OTX_CPT_PF_GX_EN ( eng_grp - > idx ) ) ;
for_each_set_bit ( i , bmap . bits , bmap . size ) {
if ( reg & ( 1ull < < i ) ) {
eng_grp - > g - > eng_ref_cnt [ i ] - - ;
reg & = ~ ( 1ull < < i ) ;
}
}
writeq ( reg , cpt - > reg_base + OTX_CPT_PF_GX_EN ( eng_grp - > idx ) ) ;
/* Wait for cores to become idle */
do {
busy = 0 ;
usleep_range ( 10000 , 20000 ) ;
if ( timeout - - < 0 )
return - EBUSY ;
reg = readq ( cpt - > reg_base + OTX_CPT_PF_EXEC_BUSY ) ;
for_each_set_bit ( i , bmap . bits , bmap . size )
if ( reg & ( 1ull < < i ) ) {
busy = 1 ;
break ;
}
} while ( busy ) ;
/* Disable the cores only if they are not used anymore */
reg = readq ( cpt - > reg_base + OTX_CPT_PF_EXE_CTL ) ;
for_each_set_bit ( i , bmap . bits , bmap . size )
if ( ! eng_grp - > g - > eng_ref_cnt [ i ] )
reg & = ~ ( 1ull < < i ) ;
writeq ( reg , cpt - > reg_base + OTX_CPT_PF_EXE_CTL ) ;
return 0 ;
}
static int cpt_attach_and_enable_cores ( struct otx_cpt_eng_grp_info * eng_grp ,
void * obj )
{
struct otx_cpt_device * cpt = ( struct otx_cpt_device * ) obj ;
struct otx_cpt_bitmap bmap ;
u64 reg ;
int i ;
bmap = get_cores_bmap ( & cpt - > pdev - > dev , eng_grp ) ;
if ( ! bmap . size )
return - EINVAL ;
/* Attach the cores to the group */
reg = readq ( cpt - > reg_base + OTX_CPT_PF_GX_EN ( eng_grp - > idx ) ) ;
for_each_set_bit ( i , bmap . bits , bmap . size ) {
if ( ! ( reg & ( 1ull < < i ) ) ) {
eng_grp - > g - > eng_ref_cnt [ i ] + + ;
reg | = 1ull < < i ;
}
}
writeq ( reg , cpt - > reg_base + OTX_CPT_PF_GX_EN ( eng_grp - > idx ) ) ;
/* Enable the cores */
reg = readq ( cpt - > reg_base + OTX_CPT_PF_EXE_CTL ) ;
for_each_set_bit ( i , bmap . bits , bmap . size )
reg | = 1ull < < i ;
writeq ( reg , cpt - > reg_base + OTX_CPT_PF_EXE_CTL ) ;
return 0 ;
}
static int process_tar_file ( struct device * dev ,
struct tar_arch_info_t * tar_arch , char * filename ,
const u8 * data , u32 size )
{
struct tar_ucode_info_t * tar_info ;
struct otx_cpt_ucode_hdr * ucode_hdr ;
int ucode_type , ucode_size ;
2022-09-19 09:43:19 +03:00
unsigned int code_length ;
2020-03-13 17:17:06 +05:30
/*
* If size is less than microcode header size then don ' t report
* an error because it might not be microcode file , just process
* next file from archive
*/
if ( size < sizeof ( struct otx_cpt_ucode_hdr ) )
return 0 ;
ucode_hdr = ( struct otx_cpt_ucode_hdr * ) data ;
/*
* If microcode version can ' t be found don ' t report an error
* because it might not be microcode file , just process next file
*/
if ( get_ucode_type ( ucode_hdr , & ucode_type ) )
return 0 ;
2022-09-19 09:43:19 +03:00
code_length = ntohl ( ucode_hdr - > code_length ) ;
if ( code_length > = INT_MAX / 2 ) {
dev_err ( dev , " Invalid code_length %u \n " , code_length ) ;
return - EINVAL ;
}
ucode_size = code_length * 2 ;
2020-03-13 17:17:06 +05:30
if ( ! ucode_size | | ( size < round_up ( ucode_size , 16 ) +
sizeof ( struct otx_cpt_ucode_hdr ) + OTX_CPT_UCODE_SIGN_LEN ) ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Ucode %s invalid size \n " , filename ) ;
2020-03-13 17:17:06 +05:30
return - EINVAL ;
}
tar_info = kzalloc ( sizeof ( struct tar_ucode_info_t ) , GFP_KERNEL ) ;
if ( ! tar_info )
return - ENOMEM ;
tar_info - > ucode_ptr = data ;
set_ucode_filename ( & tar_info - > ucode , filename ) ;
memcpy ( tar_info - > ucode . ver_str , ucode_hdr - > ver_str ,
OTX_CPT_UCODE_VER_STR_SZ ) ;
tar_info - > ucode . ver_num = ucode_hdr - > ver_num ;
tar_info - > ucode . type = ucode_type ;
tar_info - > ucode . size = ucode_size ;
list_add_tail ( & tar_info - > list , & tar_arch - > ucodes ) ;
return 0 ;
}
static void release_tar_archive ( struct tar_arch_info_t * tar_arch )
{
struct tar_ucode_info_t * curr , * temp ;
if ( ! tar_arch )
return ;
list_for_each_entry_safe ( curr , temp , & tar_arch - > ucodes , list ) {
list_del ( & curr - > list ) ;
kfree ( curr ) ;
}
2022-11-22 15:49:00 +08:00
release_firmware ( tar_arch - > fw ) ;
2020-03-13 17:17:06 +05:30
kfree ( tar_arch ) ;
}
static struct tar_ucode_info_t * get_uc_from_tar_archive (
struct tar_arch_info_t * tar_arch ,
int ucode_type )
{
struct tar_ucode_info_t * curr , * uc_found = NULL ;
list_for_each_entry ( curr , & tar_arch - > ucodes , list ) {
if ( ! is_eng_type ( curr - > ucode . type , ucode_type ) )
continue ;
if ( ! uc_found ) {
uc_found = curr ;
continue ;
}
switch ( ucode_type ) {
case OTX_CPT_AE_TYPES :
break ;
case OTX_CPT_SE_TYPES :
if ( uc_found - > ucode . ver_num . nn = = OTX_CPT_SE_UC_TYPE2 | |
( uc_found - > ucode . ver_num . nn = = OTX_CPT_SE_UC_TYPE3
& & curr - > ucode . ver_num . nn = = OTX_CPT_SE_UC_TYPE1 ) )
uc_found = curr ;
break ;
}
}
return uc_found ;
}
static void print_tar_dbg_info ( struct tar_arch_info_t * tar_arch ,
char * tar_filename )
{
struct tar_ucode_info_t * curr ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Tar archive filename %s \n " , tar_filename ) ;
pr_debug ( " Tar archive pointer %p, size %ld \n " , tar_arch - > fw - > data ,
2020-03-13 17:17:06 +05:30
tar_arch - > fw - > size ) ;
list_for_each_entry ( curr , & tar_arch - > ucodes , list ) {
2020-04-11 14:06:33 +02:00
pr_debug ( " Ucode filename %s \n " , curr - > ucode . filename ) ;
pr_debug ( " Ucode version string %s \n " , curr - > ucode . ver_str ) ;
pr_debug ( " Ucode version %d.%d.%d.%d \n " ,
2020-03-13 17:17:06 +05:30
curr - > ucode . ver_num . nn , curr - > ucode . ver_num . xx ,
curr - > ucode . ver_num . yy , curr - > ucode . ver_num . zz ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Ucode type (%d) %s \n " , curr - > ucode . type ,
2020-03-13 17:17:06 +05:30
get_ucode_type_str ( curr - > ucode . type ) ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Ucode size %d \n " , curr - > ucode . size ) ;
2020-03-13 17:17:06 +05:30
pr_debug ( " Ucode ptr %p \n " , curr - > ucode_ptr ) ;
}
}
static struct tar_arch_info_t * load_tar_archive ( struct device * dev ,
char * tar_filename )
{
struct tar_arch_info_t * tar_arch = NULL ;
struct tar_blk_t * tar_blk ;
unsigned int cur_size ;
size_t tar_offs = 0 ;
size_t tar_size ;
int ret ;
tar_arch = kzalloc ( sizeof ( struct tar_arch_info_t ) , GFP_KERNEL ) ;
if ( ! tar_arch )
return NULL ;
INIT_LIST_HEAD ( & tar_arch - > ucodes ) ;
/* Load tar archive */
ret = request_firmware ( & tar_arch - > fw , tar_filename , dev ) ;
if ( ret )
goto release_tar_arch ;
if ( tar_arch - > fw - > size < TAR_BLOCK_LEN ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Invalid tar archive %s \n " , tar_filename ) ;
2020-03-13 17:17:06 +05:30
goto release_tar_arch ;
}
tar_size = tar_arch - > fw - > size ;
tar_blk = ( struct tar_blk_t * ) tar_arch - > fw - > data ;
if ( strncmp ( tar_blk - > hdr . magic , TAR_MAGIC , TAR_MAGIC_LEN - 1 ) ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Unsupported format of tar archive %s \n " ,
2020-03-13 17:17:06 +05:30
tar_filename ) ;
goto release_tar_arch ;
}
while ( 1 ) {
/* Read current file size */
ret = kstrtouint ( tar_blk - > hdr . size , 8 , & cur_size ) ;
if ( ret )
goto release_tar_arch ;
if ( tar_offs + cur_size > tar_size | |
tar_offs + 2 * TAR_BLOCK_LEN > tar_size ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Invalid tar archive %s \n " , tar_filename ) ;
2020-03-13 17:17:06 +05:30
goto release_tar_arch ;
}
tar_offs + = TAR_BLOCK_LEN ;
if ( tar_blk - > hdr . typeflag = = REGTYPE | |
tar_blk - > hdr . typeflag = = AREGTYPE ) {
ret = process_tar_file ( dev , tar_arch ,
tar_blk - > hdr . name ,
& tar_arch - > fw - > data [ tar_offs ] ,
cur_size ) ;
if ( ret )
goto release_tar_arch ;
}
tar_offs + = ( cur_size / TAR_BLOCK_LEN ) * TAR_BLOCK_LEN ;
if ( cur_size % TAR_BLOCK_LEN )
tar_offs + = TAR_BLOCK_LEN ;
/* Check for the end of the archive */
if ( tar_offs + 2 * TAR_BLOCK_LEN > tar_size ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Invalid tar archive %s \n " , tar_filename ) ;
2020-03-13 17:17:06 +05:30
goto release_tar_arch ;
}
if ( is_mem_zero ( & tar_arch - > fw - > data [ tar_offs ] ,
2 * TAR_BLOCK_LEN ) )
break ;
/* Read next block from tar archive */
tar_blk = ( struct tar_blk_t * ) & tar_arch - > fw - > data [ tar_offs ] ;
}
print_tar_dbg_info ( tar_arch , tar_filename ) ;
return tar_arch ;
release_tar_arch :
release_tar_archive ( tar_arch ) ;
return NULL ;
}
static struct otx_cpt_engs_rsvd * find_engines_by_type (
struct otx_cpt_eng_grp_info * eng_grp ,
int eng_type )
{
int i ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
if ( ! eng_grp - > engs [ i ] . type )
continue ;
if ( eng_grp - > engs [ i ] . type = = eng_type )
return & eng_grp - > engs [ i ] ;
}
return NULL ;
}
int otx_cpt_uc_supports_eng_type ( struct otx_cpt_ucode * ucode , int eng_type )
{
return is_eng_type ( ucode - > type , eng_type ) ;
}
EXPORT_SYMBOL_GPL ( otx_cpt_uc_supports_eng_type ) ;
int otx_cpt_eng_grp_has_eng_type ( struct otx_cpt_eng_grp_info * eng_grp ,
int eng_type )
{
struct otx_cpt_engs_rsvd * engs ;
engs = find_engines_by_type ( eng_grp , eng_type ) ;
return ( engs ! = NULL ? 1 : 0 ) ;
}
EXPORT_SYMBOL_GPL ( otx_cpt_eng_grp_has_eng_type ) ;
static void print_ucode_info ( struct otx_cpt_eng_grp_info * eng_grp ,
char * buf , int size )
{
if ( eng_grp - > mirror . is_ena ) {
scnprintf ( buf , size , " %s (shared with engine_group%d) " ,
eng_grp - > g - > grp [ eng_grp - > mirror . idx ] . ucode [ 0 ] . ver_str ,
eng_grp - > mirror . idx ) ;
} else {
scnprintf ( buf , size , " %s " , eng_grp - > ucode [ 0 ] . ver_str ) ;
}
}
static void print_engs_info ( struct otx_cpt_eng_grp_info * eng_grp ,
char * buf , int size , int idx )
{
struct otx_cpt_engs_rsvd * mirrored_engs = NULL ;
struct otx_cpt_engs_rsvd * engs ;
int len , i ;
buf [ 0 ] = ' \0 ' ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
engs = & eng_grp - > engs [ i ] ;
if ( ! engs - > type )
continue ;
if ( idx ! = - 1 & & idx ! = i )
continue ;
if ( eng_grp - > mirror . is_ena )
mirrored_engs = find_engines_by_type (
& eng_grp - > g - > grp [ eng_grp - > mirror . idx ] ,
engs - > type ) ;
if ( i > 0 & & idx = = - 1 ) {
len = strlen ( buf ) ;
scnprintf ( buf + len , size - len , " , " ) ;
}
len = strlen ( buf ) ;
scnprintf ( buf + len , size - len , " %d %s " , mirrored_engs ?
engs - > count + mirrored_engs - > count : engs - > count ,
get_eng_type_str ( engs - > type ) ) ;
if ( mirrored_engs ) {
len = strlen ( buf ) ;
scnprintf ( buf + len , size - len ,
" (%d shared with engine_group%d) " ,
engs - > count < = 0 ? engs - > count +
mirrored_engs - > count : mirrored_engs - > count ,
eng_grp - > mirror . idx ) ;
}
}
}
static void print_ucode_dbg_info ( struct otx_cpt_ucode * ucode )
{
2020-04-11 14:06:33 +02:00
pr_debug ( " Ucode info \n " ) ;
pr_debug ( " Ucode version string %s \n " , ucode - > ver_str ) ;
pr_debug ( " Ucode version %d.%d.%d.%d \n " , ucode - > ver_num . nn ,
2020-03-13 17:17:06 +05:30
ucode - > ver_num . xx , ucode - > ver_num . yy , ucode - > ver_num . zz ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Ucode type %s \n " , get_ucode_type_str ( ucode - > type ) ) ;
pr_debug ( " Ucode size %d \n " , ucode - > size ) ;
pr_debug ( " Ucode virt address %16.16llx \n " , ( u64 ) ucode - > align_va ) ;
2020-03-13 17:17:06 +05:30
pr_debug ( " Ucode phys address %16.16llx \n " , ucode - > align_dma ) ;
}
static void cpt_print_engines_mask ( struct otx_cpt_eng_grp_info * eng_grp ,
struct device * dev , char * buf , int size )
{
struct otx_cpt_bitmap bmap ;
u32 mask [ 2 ] ;
bmap = get_cores_bmap ( dev , eng_grp ) ;
if ( ! bmap . size ) {
scnprintf ( buf , size , " unknown " ) ;
return ;
}
bitmap_to_arr32 ( mask , bmap . bits , bmap . size ) ;
scnprintf ( buf , size , " %8.8x %8.8x " , mask [ 1 ] , mask [ 0 ] ) ;
}
static void print_dbg_info ( struct device * dev ,
struct otx_cpt_eng_grps * eng_grps )
{
char engs_info [ 2 * OTX_CPT_UCODE_NAME_LENGTH ] ;
struct otx_cpt_eng_grp_info * mirrored_grp ;
char engs_mask [ OTX_CPT_UCODE_NAME_LENGTH ] ;
struct otx_cpt_eng_grp_info * grp ;
struct otx_cpt_engs_rsvd * engs ;
u32 mask [ 4 ] ;
int i , j ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Engine groups global info \n " ) ;
pr_debug ( " max SE %d, max AE %d \n " ,
2020-03-13 17:17:06 +05:30
eng_grps - > avail . max_se_cnt , eng_grps - > avail . max_ae_cnt ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " free SE %d \n " , eng_grps - > avail . se_cnt ) ;
pr_debug ( " free AE %d \n " , eng_grps - > avail . ae_cnt ) ;
2020-03-13 17:17:06 +05:30
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + ) {
grp = & eng_grps - > grp [ i ] ;
2020-04-11 14:06:33 +02:00
pr_debug ( " engine_group%d, state %s \n " , i , grp - > is_enabled ?
2020-03-13 17:17:06 +05:30
" enabled " : " disabled " ) ;
if ( grp - > is_enabled ) {
mirrored_grp = & eng_grps - > grp [ grp - > mirror . idx ] ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Ucode0 filename %s, version %s \n " ,
2020-03-13 17:17:06 +05:30
grp - > mirror . is_ena ?
mirrored_grp - > ucode [ 0 ] . filename :
grp - > ucode [ 0 ] . filename ,
grp - > mirror . is_ena ?
mirrored_grp - > ucode [ 0 ] . ver_str :
grp - > ucode [ 0 ] . ver_str ) ;
}
for ( j = 0 ; j < OTX_CPT_MAX_ETYPES_PER_GRP ; j + + ) {
engs = & grp - > engs [ j ] ;
if ( engs - > type ) {
print_engs_info ( grp , engs_info ,
2 * OTX_CPT_UCODE_NAME_LENGTH , j ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Slot%d: %s \n " , j , engs_info ) ;
2020-03-13 17:17:06 +05:30
bitmap_to_arr32 ( mask , engs - > bmap ,
eng_grps - > engs_num ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Mask: %8.8x %8.8x %8.8x %8.8x \n " ,
2020-03-13 17:17:06 +05:30
mask [ 3 ] , mask [ 2 ] , mask [ 1 ] , mask [ 0 ] ) ;
} else
2020-04-11 14:06:33 +02:00
pr_debug ( " Slot%d not used \n " , j ) ;
2020-03-13 17:17:06 +05:30
}
if ( grp - > is_enabled ) {
cpt_print_engines_mask ( grp , dev , engs_mask ,
OTX_CPT_UCODE_NAME_LENGTH ) ;
2020-04-11 14:06:33 +02:00
pr_debug ( " Cmask: %s \n " , engs_mask ) ;
2020-03-13 17:17:06 +05:30
}
}
}
static int update_engines_avail_count ( struct device * dev ,
struct otx_cpt_engs_available * avail ,
struct otx_cpt_engs_rsvd * engs , int val )
{
switch ( engs - > type ) {
case OTX_CPT_SE_TYPES :
avail - > se_cnt + = val ;
break ;
case OTX_CPT_AE_TYPES :
avail - > ae_cnt + = val ;
break ;
default :
dev_err ( dev , " Invalid engine type %d \n " , engs - > type ) ;
return - EINVAL ;
}
return 0 ;
}
static int update_engines_offset ( struct device * dev ,
struct otx_cpt_engs_available * avail ,
struct otx_cpt_engs_rsvd * engs )
{
switch ( engs - > type ) {
case OTX_CPT_SE_TYPES :
engs - > offset = 0 ;
break ;
case OTX_CPT_AE_TYPES :
engs - > offset = avail - > max_se_cnt ;
break ;
default :
dev_err ( dev , " Invalid engine type %d \n " , engs - > type ) ;
return - EINVAL ;
}
return 0 ;
}
static int release_engines ( struct device * dev , struct otx_cpt_eng_grp_info * grp )
{
int i , ret = 0 ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
if ( ! grp - > engs [ i ] . type )
continue ;
if ( grp - > engs [ i ] . count > 0 ) {
ret = update_engines_avail_count ( dev , & grp - > g - > avail ,
& grp - > engs [ i ] ,
grp - > engs [ i ] . count ) ;
if ( ret )
return ret ;
}
grp - > engs [ i ] . type = 0 ;
grp - > engs [ i ] . count = 0 ;
grp - > engs [ i ] . offset = 0 ;
grp - > engs [ i ] . ucode = NULL ;
bitmap_zero ( grp - > engs [ i ] . bmap , grp - > g - > engs_num ) ;
}
return 0 ;
}
static int do_reserve_engines ( struct device * dev ,
struct otx_cpt_eng_grp_info * grp ,
struct otx_cpt_engines * req_engs )
{
struct otx_cpt_engs_rsvd * engs = NULL ;
int i , ret ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
if ( ! grp - > engs [ i ] . type ) {
engs = & grp - > engs [ i ] ;
break ;
}
}
if ( ! engs )
return - ENOMEM ;
engs - > type = req_engs - > type ;
engs - > count = req_engs - > count ;
ret = update_engines_offset ( dev , & grp - > g - > avail , engs ) ;
if ( ret )
return ret ;
if ( engs - > count > 0 ) {
ret = update_engines_avail_count ( dev , & grp - > g - > avail , engs ,
- engs - > count ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static int check_engines_availability ( struct device * dev ,
struct otx_cpt_eng_grp_info * grp ,
struct otx_cpt_engines * req_eng )
{
int avail_cnt = 0 ;
switch ( req_eng - > type ) {
case OTX_CPT_SE_TYPES :
avail_cnt = grp - > g - > avail . se_cnt ;
break ;
case OTX_CPT_AE_TYPES :
avail_cnt = grp - > g - > avail . ae_cnt ;
break ;
default :
dev_err ( dev , " Invalid engine type %d \n " , req_eng - > type ) ;
return - EINVAL ;
}
if ( avail_cnt < req_eng - > count ) {
dev_err ( dev ,
2020-04-11 14:06:33 +02:00
" Error available %s engines %d < than requested %d \n " ,
2020-03-13 17:17:06 +05:30
get_eng_type_str ( req_eng - > type ) ,
avail_cnt , req_eng - > count ) ;
return - EBUSY ;
}
return 0 ;
}
static int reserve_engines ( struct device * dev , struct otx_cpt_eng_grp_info * grp ,
struct otx_cpt_engines * req_engs , int req_cnt )
{
int i , ret ;
/* Validate if a number of requested engines is available */
for ( i = 0 ; i < req_cnt ; i + + ) {
ret = check_engines_availability ( dev , grp , & req_engs [ i ] ) ;
if ( ret )
return ret ;
}
/* Reserve requested engines for this engine group */
for ( i = 0 ; i < req_cnt ; i + + ) {
ret = do_reserve_engines ( dev , grp , & req_engs [ i ] ) ;
if ( ret )
return ret ;
}
return 0 ;
}
static ssize_t eng_grp_info_show ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
char ucode_info [ 2 * OTX_CPT_UCODE_NAME_LENGTH ] ;
char engs_info [ 2 * OTX_CPT_UCODE_NAME_LENGTH ] ;
char engs_mask [ OTX_CPT_UCODE_NAME_LENGTH ] ;
struct otx_cpt_eng_grp_info * eng_grp ;
int ret ;
eng_grp = container_of ( attr , struct otx_cpt_eng_grp_info , info_attr ) ;
mutex_lock ( & eng_grp - > g - > lock ) ;
print_engs_info ( eng_grp , engs_info , 2 * OTX_CPT_UCODE_NAME_LENGTH , - 1 ) ;
print_ucode_info ( eng_grp , ucode_info , 2 * OTX_CPT_UCODE_NAME_LENGTH ) ;
cpt_print_engines_mask ( eng_grp , dev , engs_mask ,
OTX_CPT_UCODE_NAME_LENGTH ) ;
ret = scnprintf ( buf , PAGE_SIZE ,
" Microcode : %s \n Engines: %s \n Engines mask: %s \n " ,
ucode_info , engs_info , engs_mask ) ;
mutex_unlock ( & eng_grp - > g - > lock ) ;
return ret ;
}
static int create_sysfs_eng_grps_info ( struct device * dev ,
struct otx_cpt_eng_grp_info * eng_grp )
{
eng_grp - > info_attr . show = eng_grp_info_show ;
eng_grp - > info_attr . store = NULL ;
eng_grp - > info_attr . attr . name = eng_grp - > sysfs_info_name ;
eng_grp - > info_attr . attr . mode = 0440 ;
sysfs_attr_init ( & eng_grp - > info_attr . attr ) ;
2020-09-21 21:10:07 +08:00
return device_create_file ( dev , & eng_grp - > info_attr ) ;
2020-03-13 17:17:06 +05:30
}
static void ucode_unload ( struct device * dev , struct otx_cpt_ucode * ucode )
{
if ( ucode - > va ) {
dma_free_coherent ( dev , ucode - > size + OTX_CPT_UCODE_ALIGNMENT ,
ucode - > va , ucode - > dma ) ;
ucode - > va = NULL ;
ucode - > align_va = NULL ;
ucode - > dma = 0 ;
ucode - > align_dma = 0 ;
ucode - > size = 0 ;
}
memset ( & ucode - > ver_str , 0 , OTX_CPT_UCODE_VER_STR_SZ ) ;
memset ( & ucode - > ver_num , 0 , sizeof ( struct otx_cpt_ucode_ver_num ) ) ;
set_ucode_filename ( ucode , " " ) ;
ucode - > type = 0 ;
}
static int copy_ucode_to_dma_mem ( struct device * dev ,
struct otx_cpt_ucode * ucode ,
const u8 * ucode_data )
{
u32 i ;
/* Allocate DMAable space */
ucode - > va = dma_alloc_coherent ( dev , ucode - > size +
OTX_CPT_UCODE_ALIGNMENT ,
& ucode - > dma , GFP_KERNEL ) ;
if ( ! ucode - > va ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Unable to allocate space for microcode \n " ) ;
2020-03-13 17:17:06 +05:30
return - ENOMEM ;
}
ucode - > align_va = PTR_ALIGN ( ucode - > va , OTX_CPT_UCODE_ALIGNMENT ) ;
ucode - > align_dma = PTR_ALIGN ( ucode - > dma , OTX_CPT_UCODE_ALIGNMENT ) ;
memcpy ( ( void * ) ucode - > align_va , ( void * ) ucode_data +
sizeof ( struct otx_cpt_ucode_hdr ) , ucode - > size ) ;
/* Byte swap 64-bit */
for ( i = 0 ; i < ( ucode - > size / 8 ) ; i + + )
2020-06-12 21:16:22 +10:00
( ( __be64 * ) ucode - > align_va ) [ i ] =
2020-03-13 17:17:06 +05:30
cpu_to_be64 ( ( ( u64 * ) ucode - > align_va ) [ i ] ) ;
/* Ucode needs 16-bit swap */
for ( i = 0 ; i < ( ucode - > size / 2 ) ; i + + )
2020-06-12 21:16:22 +10:00
( ( __be16 * ) ucode - > align_va ) [ i ] =
2020-03-13 17:17:06 +05:30
cpu_to_be16 ( ( ( u16 * ) ucode - > align_va ) [ i ] ) ;
return 0 ;
}
static int ucode_load ( struct device * dev , struct otx_cpt_ucode * ucode ,
const char * ucode_filename )
{
struct otx_cpt_ucode_hdr * ucode_hdr ;
const struct firmware * fw ;
2022-09-19 09:43:19 +03:00
unsigned int code_length ;
2020-03-13 17:17:06 +05:30
int ret ;
set_ucode_filename ( ucode , ucode_filename ) ;
ret = request_firmware ( & fw , ucode - > filename , dev ) ;
if ( ret )
return ret ;
ucode_hdr = ( struct otx_cpt_ucode_hdr * ) fw - > data ;
memcpy ( ucode - > ver_str , ucode_hdr - > ver_str , OTX_CPT_UCODE_VER_STR_SZ ) ;
ucode - > ver_num = ucode_hdr - > ver_num ;
2022-09-19 09:43:19 +03:00
code_length = ntohl ( ucode_hdr - > code_length ) ;
if ( code_length > = INT_MAX / 2 ) {
dev_err ( dev , " Ucode invalid code_length %u \n " , code_length ) ;
ret = - EINVAL ;
goto release_fw ;
}
ucode - > size = code_length * 2 ;
2020-03-13 17:17:06 +05:30
if ( ! ucode - > size | | ( fw - > size < round_up ( ucode - > size , 16 )
+ sizeof ( struct otx_cpt_ucode_hdr ) + OTX_CPT_UCODE_SIGN_LEN ) ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Ucode %s invalid size \n " , ucode_filename ) ;
2020-03-13 17:17:06 +05:30
ret = - EINVAL ;
goto release_fw ;
}
ret = get_ucode_type ( ucode_hdr , & ucode - > type ) ;
if ( ret ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Microcode %s unknown type 0x%x \n " ,
ucode - > filename , ucode - > type ) ;
2020-03-13 17:17:06 +05:30
goto release_fw ;
}
ret = copy_ucode_to_dma_mem ( dev , ucode , fw - > data ) ;
if ( ret )
goto release_fw ;
print_ucode_dbg_info ( ucode ) ;
release_fw :
release_firmware ( fw ) ;
return ret ;
}
static int enable_eng_grp ( struct otx_cpt_eng_grp_info * eng_grp ,
void * obj )
{
int ret ;
ret = cpt_set_ucode_base ( eng_grp , obj ) ;
if ( ret )
return ret ;
ret = cpt_attach_and_enable_cores ( eng_grp , obj ) ;
return ret ;
}
static int disable_eng_grp ( struct device * dev ,
struct otx_cpt_eng_grp_info * eng_grp ,
void * obj )
{
int i , ret ;
ret = cpt_detach_and_disable_cores ( eng_grp , obj ) ;
if ( ret )
return ret ;
/* Unload ucode used by this engine group */
ucode_unload ( dev , & eng_grp - > ucode [ 0 ] ) ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
if ( ! eng_grp - > engs [ i ] . type )
continue ;
eng_grp - > engs [ i ] . ucode = & eng_grp - > ucode [ 0 ] ;
}
ret = cpt_set_ucode_base ( eng_grp , obj ) ;
return ret ;
}
static void setup_eng_grp_mirroring ( struct otx_cpt_eng_grp_info * dst_grp ,
struct otx_cpt_eng_grp_info * src_grp )
{
/* Setup fields for engine group which is mirrored */
src_grp - > mirror . is_ena = false ;
src_grp - > mirror . idx = 0 ;
src_grp - > mirror . ref_count + + ;
/* Setup fields for mirroring engine group */
dst_grp - > mirror . is_ena = true ;
dst_grp - > mirror . idx = src_grp - > idx ;
dst_grp - > mirror . ref_count = 0 ;
}
static void remove_eng_grp_mirroring ( struct otx_cpt_eng_grp_info * dst_grp )
{
struct otx_cpt_eng_grp_info * src_grp ;
if ( ! dst_grp - > mirror . is_ena )
return ;
src_grp = & dst_grp - > g - > grp [ dst_grp - > mirror . idx ] ;
src_grp - > mirror . ref_count - - ;
dst_grp - > mirror . is_ena = false ;
dst_grp - > mirror . idx = 0 ;
dst_grp - > mirror . ref_count = 0 ;
}
static void update_requested_engs ( struct otx_cpt_eng_grp_info * mirrored_eng_grp ,
struct otx_cpt_engines * engs , int engs_cnt )
{
struct otx_cpt_engs_rsvd * mirrored_engs ;
int i ;
for ( i = 0 ; i < engs_cnt ; i + + ) {
mirrored_engs = find_engines_by_type ( mirrored_eng_grp ,
engs [ i ] . type ) ;
if ( ! mirrored_engs )
continue ;
/*
* If mirrored group has this type of engines attached then
* there are 3 scenarios possible :
* 1 ) mirrored_engs . count = = engs [ i ] . count then all engines
* from mirrored engine group will be shared with this engine
* group
* 2 ) mirrored_engs . count > engs [ i ] . count then only a subset of
* engines from mirrored engine group will be shared with this
* engine group
* 3 ) mirrored_engs . count < engs [ i ] . count then all engines
* from mirrored engine group will be shared with this group
* and additional engines will be reserved for exclusively use
* by this engine group
*/
engs [ i ] . count - = mirrored_engs - > count ;
}
}
static struct otx_cpt_eng_grp_info * find_mirrored_eng_grp (
struct otx_cpt_eng_grp_info * grp )
{
struct otx_cpt_eng_grps * eng_grps = grp - > g ;
int i ;
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + ) {
if ( ! eng_grps - > grp [ i ] . is_enabled )
continue ;
if ( eng_grps - > grp [ i ] . ucode [ 0 ] . type )
continue ;
if ( grp - > idx = = i )
continue ;
if ( ! strncasecmp ( eng_grps - > grp [ i ] . ucode [ 0 ] . ver_str ,
grp - > ucode [ 0 ] . ver_str ,
OTX_CPT_UCODE_VER_STR_SZ ) )
return & eng_grps - > grp [ i ] ;
}
return NULL ;
}
static struct otx_cpt_eng_grp_info * find_unused_eng_grp (
struct otx_cpt_eng_grps * eng_grps )
{
int i ;
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + ) {
if ( ! eng_grps - > grp [ i ] . is_enabled )
return & eng_grps - > grp [ i ] ;
}
return NULL ;
}
static int eng_grp_update_masks ( struct device * dev ,
struct otx_cpt_eng_grp_info * eng_grp )
{
struct otx_cpt_engs_rsvd * engs , * mirrored_engs ;
struct otx_cpt_bitmap tmp_bmap = { { 0 } } ;
int i , j , cnt , max_cnt ;
int bit ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
engs = & eng_grp - > engs [ i ] ;
if ( ! engs - > type )
continue ;
if ( engs - > count < = 0 )
continue ;
switch ( engs - > type ) {
case OTX_CPT_SE_TYPES :
max_cnt = eng_grp - > g - > avail . max_se_cnt ;
break ;
case OTX_CPT_AE_TYPES :
max_cnt = eng_grp - > g - > avail . max_ae_cnt ;
break ;
default :
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Invalid engine type %d \n " , engs - > type ) ;
2020-03-13 17:17:06 +05:30
return - EINVAL ;
}
cnt = engs - > count ;
WARN_ON ( engs - > offset + max_cnt > OTX_CPT_MAX_ENGINES ) ;
bitmap_zero ( tmp_bmap . bits , eng_grp - > g - > engs_num ) ;
for ( j = engs - > offset ; j < engs - > offset + max_cnt ; j + + ) {
if ( ! eng_grp - > g - > eng_ref_cnt [ j ] ) {
bitmap_set ( tmp_bmap . bits , j , 1 ) ;
cnt - - ;
if ( ! cnt )
break ;
}
}
if ( cnt )
return - ENOSPC ;
bitmap_copy ( engs - > bmap , tmp_bmap . bits , eng_grp - > g - > engs_num ) ;
}
if ( ! eng_grp - > mirror . is_ena )
return 0 ;
for ( i = 0 ; i < OTX_CPT_MAX_ETYPES_PER_GRP ; i + + ) {
engs = & eng_grp - > engs [ i ] ;
if ( ! engs - > type )
continue ;
mirrored_engs = find_engines_by_type (
& eng_grp - > g - > grp [ eng_grp - > mirror . idx ] ,
engs - > type ) ;
WARN_ON ( ! mirrored_engs & & engs - > count < = 0 ) ;
if ( ! mirrored_engs )
continue ;
bitmap_copy ( tmp_bmap . bits , mirrored_engs - > bmap ,
eng_grp - > g - > engs_num ) ;
if ( engs - > count < 0 ) {
bit = find_first_bit ( mirrored_engs - > bmap ,
eng_grp - > g - > engs_num ) ;
bitmap_clear ( tmp_bmap . bits , bit , - engs - > count ) ;
}
bitmap_or ( engs - > bmap , engs - > bmap , tmp_bmap . bits ,
eng_grp - > g - > engs_num ) ;
}
return 0 ;
}
static int delete_engine_group ( struct device * dev ,
struct otx_cpt_eng_grp_info * eng_grp )
{
int i , ret ;
if ( ! eng_grp - > is_enabled )
return - EINVAL ;
if ( eng_grp - > mirror . ref_count ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Can't delete engine_group%d as it is used by engine_group(s): " ,
2020-03-13 17:17:06 +05:30
eng_grp - > idx ) ;
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + ) {
if ( eng_grp - > g - > grp [ i ] . mirror . is_ena & &
eng_grp - > g - > grp [ i ] . mirror . idx = = eng_grp - > idx )
2020-04-11 14:06:33 +02:00
pr_cont ( " %d " , i ) ;
2020-03-13 17:17:06 +05:30
}
2020-04-11 14:06:33 +02:00
pr_cont ( " \n " ) ;
2020-03-13 17:17:06 +05:30
return - EINVAL ;
}
/* Removing engine group mirroring if enabled */
remove_eng_grp_mirroring ( eng_grp ) ;
/* Disable engine group */
ret = disable_eng_grp ( dev , eng_grp , eng_grp - > g - > obj ) ;
if ( ret )
return ret ;
/* Release all engines held by this engine group */
ret = release_engines ( dev , eng_grp ) ;
if ( ret )
return ret ;
device_remove_file ( dev , & eng_grp - > info_attr ) ;
eng_grp - > is_enabled = false ;
return 0 ;
}
static int validate_1_ucode_scenario ( struct device * dev ,
struct otx_cpt_eng_grp_info * eng_grp ,
struct otx_cpt_engines * engs , int engs_cnt )
{
int i ;
/* Verify that ucode loaded supports requested engine types */
for ( i = 0 ; i < engs_cnt ; i + + ) {
if ( ! otx_cpt_uc_supports_eng_type ( & eng_grp - > ucode [ 0 ] ,
engs [ i ] . type ) ) {
dev_err ( dev ,
2020-04-11 14:06:33 +02:00
" Microcode %s does not support %s engines \n " ,
2020-03-13 17:17:06 +05:30
eng_grp - > ucode [ 0 ] . filename ,
get_eng_type_str ( engs [ i ] . type ) ) ;
return - EINVAL ;
}
}
return 0 ;
}
static void update_ucode_ptrs ( struct otx_cpt_eng_grp_info * eng_grp )
{
struct otx_cpt_ucode * ucode ;
if ( eng_grp - > mirror . is_ena )
ucode = & eng_grp - > g - > grp [ eng_grp - > mirror . idx ] . ucode [ 0 ] ;
else
ucode = & eng_grp - > ucode [ 0 ] ;
WARN_ON ( ! eng_grp - > engs [ 0 ] . type ) ;
eng_grp - > engs [ 0 ] . ucode = ucode ;
}
static int create_engine_group ( struct device * dev ,
struct otx_cpt_eng_grps * eng_grps ,
struct otx_cpt_engines * engs , int engs_cnt ,
void * ucode_data [ ] , int ucodes_cnt ,
bool use_uc_from_tar_arch )
{
struct otx_cpt_eng_grp_info * mirrored_eng_grp ;
struct tar_ucode_info_t * tar_info ;
struct otx_cpt_eng_grp_info * eng_grp ;
int i , ret = 0 ;
if ( ucodes_cnt > OTX_CPT_MAX_ETYPES_PER_GRP )
return - EINVAL ;
/* Validate if requested engine types are supported by this device */
for ( i = 0 ; i < engs_cnt ; i + + )
if ( ! dev_supports_eng_type ( eng_grps , engs [ i ] . type ) ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Device does not support %s engines \n " ,
2020-03-13 17:17:06 +05:30
get_eng_type_str ( engs [ i ] . type ) ) ;
return - EPERM ;
}
/* Find engine group which is not used */
eng_grp = find_unused_eng_grp ( eng_grps ) ;
if ( ! eng_grp ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Error all engine groups are being used \n " ) ;
2020-03-13 17:17:06 +05:30
return - ENOSPC ;
}
/* Load ucode */
for ( i = 0 ; i < ucodes_cnt ; i + + ) {
if ( use_uc_from_tar_arch ) {
tar_info = ( struct tar_ucode_info_t * ) ucode_data [ i ] ;
eng_grp - > ucode [ i ] = tar_info - > ucode ;
ret = copy_ucode_to_dma_mem ( dev , & eng_grp - > ucode [ i ] ,
tar_info - > ucode_ptr ) ;
} else
ret = ucode_load ( dev , & eng_grp - > ucode [ i ] ,
( char * ) ucode_data [ i ] ) ;
if ( ret )
goto err_ucode_unload ;
}
/* Validate scenario where 1 ucode is used */
ret = validate_1_ucode_scenario ( dev , eng_grp , engs , engs_cnt ) ;
if ( ret )
goto err_ucode_unload ;
/* Check if this group mirrors another existing engine group */
mirrored_eng_grp = find_mirrored_eng_grp ( eng_grp ) ;
if ( mirrored_eng_grp ) {
/* Setup mirroring */
setup_eng_grp_mirroring ( eng_grp , mirrored_eng_grp ) ;
/*
* Update count of requested engines because some
* of them might be shared with mirrored group
*/
update_requested_engs ( mirrored_eng_grp , engs , engs_cnt ) ;
}
/* Reserve engines */
ret = reserve_engines ( dev , eng_grp , engs , engs_cnt ) ;
if ( ret )
goto err_ucode_unload ;
/* Update ucode pointers used by engines */
update_ucode_ptrs ( eng_grp ) ;
/* Update engine masks used by this group */
ret = eng_grp_update_masks ( dev , eng_grp ) ;
if ( ret )
goto err_release_engs ;
/* Create sysfs entry for engine group info */
ret = create_sysfs_eng_grps_info ( dev , eng_grp ) ;
if ( ret )
goto err_release_engs ;
/* Enable engine group */
ret = enable_eng_grp ( eng_grp , eng_grps - > obj ) ;
if ( ret )
goto err_release_engs ;
/*
* If this engine group mirrors another engine group
* then we need to unload ucode as we will use ucode
* from mirrored engine group
*/
if ( eng_grp - > mirror . is_ena )
ucode_unload ( dev , & eng_grp - > ucode [ 0 ] ) ;
eng_grp - > is_enabled = true ;
if ( eng_grp - > mirror . is_ena )
dev_info ( dev ,
2020-04-11 14:06:33 +02:00
" Engine_group%d: reuse microcode %s from group %d \n " ,
2020-03-13 17:17:06 +05:30
eng_grp - > idx , mirrored_eng_grp - > ucode [ 0 ] . ver_str ,
mirrored_eng_grp - > idx ) ;
else
2020-04-11 14:06:33 +02:00
dev_info ( dev , " Engine_group%d: microcode loaded %s \n " ,
2020-03-13 17:17:06 +05:30
eng_grp - > idx , eng_grp - > ucode [ 0 ] . ver_str ) ;
return 0 ;
err_release_engs :
release_engines ( dev , eng_grp ) ;
err_ucode_unload :
ucode_unload ( dev , & eng_grp - > ucode [ 0 ] ) ;
return ret ;
}
static ssize_t ucode_load_store ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t count )
{
struct otx_cpt_engines engs [ OTX_CPT_MAX_ETYPES_PER_GRP ] = { { 0 } } ;
char * ucode_filename [ OTX_CPT_MAX_ETYPES_PER_GRP ] ;
char tmp_buf [ OTX_CPT_UCODE_NAME_LENGTH ] = { 0 } ;
char * start , * val , * err_msg , * tmp ;
struct otx_cpt_eng_grps * eng_grps ;
int grp_idx = 0 , ret = - EINVAL ;
bool has_se , has_ie , has_ae ;
int del_grp_idx = - 1 ;
int ucode_idx = 0 ;
if ( strlen ( buf ) > OTX_CPT_UCODE_NAME_LENGTH )
return - EINVAL ;
eng_grps = container_of ( attr , struct otx_cpt_eng_grps , ucode_load_attr ) ;
err_msg = " Invalid engine group format " ;
2022-08-18 23:00:03 +02:00
strscpy ( tmp_buf , buf , OTX_CPT_UCODE_NAME_LENGTH ) ;
2020-03-13 17:17:06 +05:30
start = tmp_buf ;
has_se = has_ie = has_ae = false ;
for ( ; ; ) {
val = strsep ( & start , " ; " ) ;
if ( ! val )
break ;
val = strim ( val ) ;
if ( ! * val )
continue ;
if ( ! strncasecmp ( val , " engine_group " , 12 ) ) {
if ( del_grp_idx ! = - 1 )
goto err_print ;
tmp = strim ( strsep ( & val , " : " ) ) ;
if ( ! val )
goto err_print ;
if ( strlen ( tmp ) ! = 13 )
goto err_print ;
if ( kstrtoint ( ( tmp + 12 ) , 10 , & del_grp_idx ) )
goto err_print ;
val = strim ( val ) ;
if ( strncasecmp ( val , " null " , 4 ) )
goto err_print ;
if ( strlen ( val ) ! = 4 )
goto err_print ;
} else if ( ! strncasecmp ( val , " se " , 2 ) & & strchr ( val , ' : ' ) ) {
if ( has_se | | ucode_idx )
goto err_print ;
tmp = strim ( strsep ( & val , " : " ) ) ;
if ( ! val )
goto err_print ;
if ( strlen ( tmp ) ! = 2 )
goto err_print ;
if ( kstrtoint ( strim ( val ) , 10 , & engs [ grp_idx ] . count ) )
goto err_print ;
engs [ grp_idx + + ] . type = OTX_CPT_SE_TYPES ;
has_se = true ;
} else if ( ! strncasecmp ( val , " ae " , 2 ) & & strchr ( val , ' : ' ) ) {
if ( has_ae | | ucode_idx )
goto err_print ;
tmp = strim ( strsep ( & val , " : " ) ) ;
if ( ! val )
goto err_print ;
if ( strlen ( tmp ) ! = 2 )
goto err_print ;
if ( kstrtoint ( strim ( val ) , 10 , & engs [ grp_idx ] . count ) )
goto err_print ;
engs [ grp_idx + + ] . type = OTX_CPT_AE_TYPES ;
has_ae = true ;
} else {
if ( ucode_idx > 1 )
goto err_print ;
if ( ! strlen ( val ) )
goto err_print ;
if ( strnstr ( val , " " , strlen ( val ) ) )
goto err_print ;
ucode_filename [ ucode_idx + + ] = val ;
}
}
/* Validate input parameters */
if ( del_grp_idx = = - 1 ) {
if ( ! ( grp_idx & & ucode_idx ) )
goto err_print ;
if ( ucode_idx > 1 & & grp_idx < 2 )
goto err_print ;
if ( grp_idx > OTX_CPT_MAX_ETYPES_PER_GRP ) {
err_msg = " Error max 2 engine types can be attached " ;
goto err_print ;
}
} else {
if ( del_grp_idx < 0 | |
del_grp_idx > = OTX_CPT_MAX_ENGINE_GROUPS ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Invalid engine group index %d \n " ,
2020-03-13 17:17:06 +05:30
del_grp_idx ) ;
ret = - EINVAL ;
return ret ;
}
if ( ! eng_grps - > grp [ del_grp_idx ] . is_enabled ) {
2020-04-11 14:06:33 +02:00
dev_err ( dev , " Error engine_group%d is not configured \n " ,
2020-03-13 17:17:06 +05:30
del_grp_idx ) ;
ret = - EINVAL ;
return ret ;
}
if ( grp_idx | | ucode_idx )
goto err_print ;
}
mutex_lock ( & eng_grps - > lock ) ;
if ( eng_grps - > is_rdonly ) {
dev_err ( dev , " Disable VFs before modifying engine groups \n " ) ;
ret = - EACCES ;
goto err_unlock ;
}
if ( del_grp_idx = = - 1 )
/* create engine group */
ret = create_engine_group ( dev , eng_grps , engs , grp_idx ,
( void * * ) ucode_filename ,
ucode_idx , false ) ;
else
/* delete engine group */
ret = delete_engine_group ( dev , & eng_grps - > grp [ del_grp_idx ] ) ;
if ( ret )
goto err_unlock ;
print_dbg_info ( dev , eng_grps ) ;
err_unlock :
mutex_unlock ( & eng_grps - > lock ) ;
return ret ? ret : count ;
err_print :
dev_err ( dev , " %s \n " , err_msg ) ;
return ret ;
}
int otx_cpt_try_create_default_eng_grps ( struct pci_dev * pdev ,
struct otx_cpt_eng_grps * eng_grps ,
int pf_type )
{
2020-06-12 21:16:22 +10:00
struct tar_ucode_info_t * tar_info [ OTX_CPT_MAX_ETYPES_PER_GRP ] = { } ;
struct otx_cpt_engines engs [ OTX_CPT_MAX_ETYPES_PER_GRP ] = { } ;
2020-03-13 17:17:06 +05:30
struct tar_arch_info_t * tar_arch = NULL ;
char * tar_filename ;
int i , ret = 0 ;
mutex_lock ( & eng_grps - > lock ) ;
/*
* We don ' t create engine group for kernel crypto if attempt to create
* it was already made ( when user enabled VFs for the first time )
*/
if ( eng_grps - > is_first_try )
goto unlock_mutex ;
eng_grps - > is_first_try = true ;
/* We create group for kcrypto only if no groups are configured */
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + )
if ( eng_grps - > grp [ i ] . is_enabled )
goto unlock_mutex ;
switch ( pf_type ) {
case OTX_CPT_AE :
case OTX_CPT_SE :
tar_filename = OTX_CPT_UCODE_TAR_FILE_NAME ;
break ;
default :
dev_err ( & pdev - > dev , " Unknown PF type %d \n " , pf_type ) ;
ret = - EINVAL ;
goto unlock_mutex ;
}
tar_arch = load_tar_archive ( & pdev - > dev , tar_filename ) ;
if ( ! tar_arch )
goto unlock_mutex ;
/*
* If device supports SE engines and there is SE microcode in tar
* archive try to create engine group with SE engines for kernel
* crypto functionality ( symmetric crypto )
*/
tar_info [ 0 ] = get_uc_from_tar_archive ( tar_arch , OTX_CPT_SE_TYPES ) ;
if ( tar_info [ 0 ] & &
dev_supports_eng_type ( eng_grps , OTX_CPT_SE_TYPES ) ) {
engs [ 0 ] . type = OTX_CPT_SE_TYPES ;
engs [ 0 ] . count = eng_grps - > avail . max_se_cnt ;
ret = create_engine_group ( & pdev - > dev , eng_grps , engs , 1 ,
( void * * ) tar_info , 1 , true ) ;
if ( ret )
goto release_tar_arch ;
}
/*
* If device supports AE engines and there is AE microcode in tar
* archive try to create engine group with AE engines for asymmetric
* crypto functionality .
*/
tar_info [ 0 ] = get_uc_from_tar_archive ( tar_arch , OTX_CPT_AE_TYPES ) ;
if ( tar_info [ 0 ] & &
dev_supports_eng_type ( eng_grps , OTX_CPT_AE_TYPES ) ) {
engs [ 0 ] . type = OTX_CPT_AE_TYPES ;
engs [ 0 ] . count = eng_grps - > avail . max_ae_cnt ;
ret = create_engine_group ( & pdev - > dev , eng_grps , engs , 1 ,
( void * * ) tar_info , 1 , true ) ;
if ( ret )
goto release_tar_arch ;
}
print_dbg_info ( & pdev - > dev , eng_grps ) ;
release_tar_arch :
release_tar_archive ( tar_arch ) ;
unlock_mutex :
mutex_unlock ( & eng_grps - > lock ) ;
return ret ;
}
void otx_cpt_set_eng_grps_is_rdonly ( struct otx_cpt_eng_grps * eng_grps ,
bool is_rdonly )
{
mutex_lock ( & eng_grps - > lock ) ;
eng_grps - > is_rdonly = is_rdonly ;
mutex_unlock ( & eng_grps - > lock ) ;
}
void otx_cpt_disable_all_cores ( struct otx_cpt_device * cpt )
{
int grp , timeout = 100 ;
u64 reg ;
/* Disengage the cores from groups */
for ( grp = 0 ; grp < OTX_CPT_MAX_ENGINE_GROUPS ; grp + + ) {
writeq ( 0 , cpt - > reg_base + OTX_CPT_PF_GX_EN ( grp ) ) ;
udelay ( CSR_DELAY ) ;
}
reg = readq ( cpt - > reg_base + OTX_CPT_PF_EXEC_BUSY ) ;
while ( reg ) {
udelay ( CSR_DELAY ) ;
reg = readq ( cpt - > reg_base + OTX_CPT_PF_EXEC_BUSY ) ;
if ( timeout - - ) {
2020-04-11 14:06:33 +02:00
dev_warn ( & cpt - > pdev - > dev , " Cores still busy \n " ) ;
2020-03-13 17:17:06 +05:30
break ;
}
}
/* Disable the cores */
writeq ( 0 , cpt - > reg_base + OTX_CPT_PF_EXE_CTL ) ;
}
void otx_cpt_cleanup_eng_grps ( struct pci_dev * pdev ,
struct otx_cpt_eng_grps * eng_grps )
{
struct otx_cpt_eng_grp_info * grp ;
int i , j ;
mutex_lock ( & eng_grps - > lock ) ;
if ( eng_grps - > is_ucode_load_created ) {
device_remove_file ( & pdev - > dev ,
& eng_grps - > ucode_load_attr ) ;
eng_grps - > is_ucode_load_created = false ;
}
/* First delete all mirroring engine groups */
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + )
if ( eng_grps - > grp [ i ] . mirror . is_ena )
delete_engine_group ( & pdev - > dev , & eng_grps - > grp [ i ] ) ;
/* Delete remaining engine groups */
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + )
delete_engine_group ( & pdev - > dev , & eng_grps - > grp [ i ] ) ;
/* Release memory */
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + ) {
grp = & eng_grps - > grp [ i ] ;
for ( j = 0 ; j < OTX_CPT_MAX_ETYPES_PER_GRP ; j + + ) {
kfree ( grp - > engs [ j ] . bmap ) ;
grp - > engs [ j ] . bmap = NULL ;
}
}
mutex_unlock ( & eng_grps - > lock ) ;
}
int otx_cpt_init_eng_grps ( struct pci_dev * pdev ,
struct otx_cpt_eng_grps * eng_grps , int pf_type )
{
struct otx_cpt_eng_grp_info * grp ;
int i , j , ret = 0 ;
mutex_init ( & eng_grps - > lock ) ;
eng_grps - > obj = pci_get_drvdata ( pdev ) ;
eng_grps - > avail . se_cnt = eng_grps - > avail . max_se_cnt ;
eng_grps - > avail . ae_cnt = eng_grps - > avail . max_ae_cnt ;
eng_grps - > engs_num = eng_grps - > avail . max_se_cnt +
eng_grps - > avail . max_ae_cnt ;
if ( eng_grps - > engs_num > OTX_CPT_MAX_ENGINES ) {
dev_err ( & pdev - > dev ,
2020-04-11 14:06:33 +02:00
" Number of engines %d > than max supported %d \n " ,
2020-03-13 17:17:06 +05:30
eng_grps - > engs_num , OTX_CPT_MAX_ENGINES ) ;
ret = - EINVAL ;
goto err ;
}
for ( i = 0 ; i < OTX_CPT_MAX_ENGINE_GROUPS ; i + + ) {
grp = & eng_grps - > grp [ i ] ;
grp - > g = eng_grps ;
grp - > idx = i ;
snprintf ( grp - > sysfs_info_name , OTX_CPT_UCODE_NAME_LENGTH ,
" engine_group%d " , i ) ;
for ( j = 0 ; j < OTX_CPT_MAX_ETYPES_PER_GRP ; j + + ) {
grp - > engs [ j ] . bmap =
kcalloc ( BITS_TO_LONGS ( eng_grps - > engs_num ) ,
sizeof ( long ) , GFP_KERNEL ) ;
if ( ! grp - > engs [ j ] . bmap ) {
ret = - ENOMEM ;
goto err ;
}
}
}
switch ( pf_type ) {
case OTX_CPT_SE :
/* OcteonTX 83XX SE CPT PF has only SE engines attached */
eng_grps - > eng_types_supported = 1 < < OTX_CPT_SE_TYPES ;
break ;
case OTX_CPT_AE :
/* OcteonTX 83XX AE CPT PF has only AE engines attached */
eng_grps - > eng_types_supported = 1 < < OTX_CPT_AE_TYPES ;
break ;
default :
dev_err ( & pdev - > dev , " Unknown PF type %d \n " , pf_type ) ;
ret = - EINVAL ;
goto err ;
}
eng_grps - > ucode_load_attr . show = NULL ;
eng_grps - > ucode_load_attr . store = ucode_load_store ;
eng_grps - > ucode_load_attr . attr . name = " ucode_load " ;
eng_grps - > ucode_load_attr . attr . mode = 0220 ;
sysfs_attr_init ( & eng_grps - > ucode_load_attr . attr ) ;
ret = device_create_file ( & pdev - > dev ,
& eng_grps - > ucode_load_attr ) ;
if ( ret )
goto err ;
eng_grps - > is_ucode_load_created = true ;
print_dbg_info ( & pdev - > dev , eng_grps ) ;
return ret ;
err :
otx_cpt_cleanup_eng_grps ( pdev , eng_grps ) ;
return ret ;
}