2020-08-23 14:19:25 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/* Marvell/Qlogic FastLinQ NIC driver
*
* Copyright ( C ) 2020 Marvell International Ltd .
*/
# include <linux/kernel.h>
2020-08-23 14:19:26 +03:00
# include <linux/qed/qed_if.h>
2020-08-23 14:19:32 +03:00
# include <linux/vmalloc.h>
2020-08-23 14:19:25 +03:00
# include "qed.h"
# include "qed_devlink.h"
enum qed_devlink_param_id {
QED_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX ,
QED_DEVLINK_PARAM_ID_IWARP_CMT ,
} ;
2020-08-23 14:19:30 +03:00
struct qed_fw_fatal_ctx {
enum qed_hw_err_type err_type ;
} ;
int qed_report_fatal_error ( struct devlink * devlink , enum qed_hw_err_type err_type )
{
struct qed_devlink * qdl = devlink_priv ( devlink ) ;
struct qed_fw_fatal_ctx fw_fatal_ctx = {
. err_type = err_type ,
} ;
if ( qdl - > fw_reporter )
devlink_health_report ( qdl - > fw_reporter ,
" Fatal error occurred " , & fw_fatal_ctx ) ;
return 0 ;
}
2020-08-23 14:19:32 +03:00
static int
qed_fw_fatal_reporter_dump ( struct devlink_health_reporter * reporter ,
struct devlink_fmsg * fmsg , void * priv_ctx ,
struct netlink_ext_ack * extack )
{
struct qed_devlink * qdl = devlink_health_reporter_priv ( reporter ) ;
struct qed_fw_fatal_ctx * fw_fatal_ctx = priv_ctx ;
struct qed_dev * cdev = qdl - > cdev ;
u32 dbg_data_buf_size ;
u8 * p_dbg_data_buf ;
int err ;
/* Having context means that was a dump request after fatal,
* so we enable extra debugging while gathering the dump ,
* just in case
*/
cdev - > print_dbg_data = fw_fatal_ctx ? true : false ;
dbg_data_buf_size = qed_dbg_all_data_size ( cdev ) ;
p_dbg_data_buf = vzalloc ( dbg_data_buf_size ) ;
if ( ! p_dbg_data_buf ) {
DP_NOTICE ( cdev ,
" Failed to allocate memory for a debug data buffer \n " ) ;
return - ENOMEM ;
}
err = qed_dbg_all_data ( cdev , p_dbg_data_buf ) ;
if ( err ) {
DP_NOTICE ( cdev , " Failed to obtain debug data \n " ) ;
vfree ( p_dbg_data_buf ) ;
return err ;
}
err = devlink_fmsg_binary_pair_put ( fmsg , " dump_data " ,
p_dbg_data_buf , dbg_data_buf_size ) ;
vfree ( p_dbg_data_buf ) ;
return err ;
}
2020-08-23 14:19:31 +03:00
static int
qed_fw_fatal_reporter_recover ( struct devlink_health_reporter * reporter ,
void * priv_ctx ,
struct netlink_ext_ack * extack )
{
struct qed_devlink * qdl = devlink_health_reporter_priv ( reporter ) ;
struct qed_dev * cdev = qdl - > cdev ;
qed_recovery_process ( cdev ) ;
return 0 ;
}
2020-08-23 14:19:29 +03:00
static const struct devlink_health_reporter_ops qed_fw_fatal_reporter_ops = {
. name = " fw_fatal " ,
2020-08-23 14:19:31 +03:00
. recover = qed_fw_fatal_reporter_recover ,
2020-08-23 14:19:32 +03:00
. dump = qed_fw_fatal_reporter_dump ,
2020-08-23 14:19:29 +03:00
} ;
2021-08-24 04:02:46 +00:00
# define QED_REPORTER_FW_GRACEFUL_PERIOD 0
2020-08-23 14:19:29 +03:00
void qed_fw_reporters_create ( struct devlink * devlink )
{
struct qed_devlink * dl = devlink_priv ( devlink ) ;
dl - > fw_reporter = devlink_health_reporter_create ( devlink , & qed_fw_fatal_reporter_ops ,
QED_REPORTER_FW_GRACEFUL_PERIOD , dl ) ;
if ( IS_ERR ( dl - > fw_reporter ) ) {
DP_NOTICE ( dl - > cdev , " Failed to create fw reporter, err = %ld \n " ,
PTR_ERR ( dl - > fw_reporter ) ) ;
dl - > fw_reporter = NULL ;
}
}
void qed_fw_reporters_destroy ( struct devlink * devlink )
{
struct qed_devlink * dl = devlink_priv ( devlink ) ;
struct devlink_health_reporter * rep ;
rep = dl - > fw_reporter ;
if ( ! IS_ERR_OR_NULL ( rep ) )
devlink_health_reporter_destroy ( rep ) ;
}
2020-08-23 14:19:25 +03:00
static int qed_dl_param_get ( struct devlink * dl , u32 id ,
struct devlink_param_gset_ctx * ctx )
{
2020-08-23 14:19:26 +03:00
struct qed_devlink * qed_dl = devlink_priv ( dl ) ;
2020-08-23 14:19:25 +03:00
struct qed_dev * cdev ;
cdev = qed_dl - > cdev ;
ctx - > val . vbool = cdev - > iwarp_cmt ;
return 0 ;
}
static int qed_dl_param_set ( struct devlink * dl , u32 id ,
struct devlink_param_gset_ctx * ctx )
{
2020-08-23 14:19:26 +03:00
struct qed_devlink * qed_dl = devlink_priv ( dl ) ;
2020-08-23 14:19:25 +03:00
struct qed_dev * cdev ;
cdev = qed_dl - > cdev ;
cdev - > iwarp_cmt = ctx - > val . vbool ;
return 0 ;
}
static const struct devlink_param qed_devlink_params [ ] = {
DEVLINK_PARAM_DRIVER ( QED_DEVLINK_PARAM_ID_IWARP_CMT ,
" iwarp_cmt " , DEVLINK_PARAM_TYPE_BOOL ,
BIT ( DEVLINK_PARAM_CMODE_RUNTIME ) ,
qed_dl_param_get , qed_dl_param_set , NULL ) ,
} ;
2020-08-23 14:19:28 +03:00
static int qed_devlink_info_get ( struct devlink * devlink ,
struct devlink_info_req * req ,
struct netlink_ext_ack * extack )
{
struct qed_devlink * qed_dl = devlink_priv ( devlink ) ;
struct qed_dev * cdev = qed_dl - > cdev ;
struct qed_dev_info * dev_info ;
char buf [ 100 ] ;
int err ;
dev_info = & cdev - > common_dev_info ;
memcpy ( buf , cdev - > hwfns [ 0 ] . hw_info . part_num , sizeof ( cdev - > hwfns [ 0 ] . hw_info . part_num ) ) ;
buf [ sizeof ( cdev - > hwfns [ 0 ] . hw_info . part_num ) ] = 0 ;
if ( buf [ 0 ] ) {
err = devlink_info_board_serial_number_put ( req , buf ) ;
if ( err )
return err ;
}
snprintf ( buf , sizeof ( buf ) , " %d.%d.%d.%d " ,
GET_MFW_FIELD ( dev_info - > mfw_rev , QED_MFW_VERSION_3 ) ,
GET_MFW_FIELD ( dev_info - > mfw_rev , QED_MFW_VERSION_2 ) ,
GET_MFW_FIELD ( dev_info - > mfw_rev , QED_MFW_VERSION_1 ) ,
GET_MFW_FIELD ( dev_info - > mfw_rev , QED_MFW_VERSION_0 ) ) ;
err = devlink_info_version_stored_put ( req ,
DEVLINK_INFO_VERSION_GENERIC_FW_MGMT , buf ) ;
if ( err )
return err ;
snprintf ( buf , sizeof ( buf ) , " %d.%d.%d.%d " ,
dev_info - > fw_major ,
dev_info - > fw_minor ,
dev_info - > fw_rev ,
dev_info - > fw_eng ) ;
return devlink_info_version_running_put ( req ,
DEVLINK_INFO_VERSION_GENERIC_FW_APP , buf ) ;
}
static const struct devlink_ops qed_dl_ops = {
. info_get = qed_devlink_info_get ,
} ;
2020-08-23 14:19:25 +03:00
2020-08-23 14:19:26 +03:00
struct devlink * qed_devlink_register ( struct qed_dev * cdev )
2020-08-23 14:19:25 +03:00
{
2020-08-23 14:19:26 +03:00
struct qed_devlink * qdevlink ;
2020-08-23 14:19:25 +03:00
struct devlink * dl ;
int rc ;
2021-08-08 21:57:43 +03:00
dl = devlink_alloc ( & qed_dl_ops , sizeof ( struct qed_devlink ) ,
& cdev - > pdev - > dev ) ;
2020-08-23 14:19:25 +03:00
if ( ! dl )
2020-08-23 14:19:26 +03:00
return ERR_PTR ( - ENOMEM ) ;
2020-08-23 14:19:25 +03:00
2020-08-23 14:19:26 +03:00
qdevlink = devlink_priv ( dl ) ;
qdevlink - > cdev = cdev ;
2020-08-23 14:19:25 +03:00
rc = devlink_params_register ( dl , qed_devlink_params ,
ARRAY_SIZE ( qed_devlink_params ) ) ;
if ( rc )
goto err_unregister ;
cdev - > iwarp_cmt = false ;
2020-08-23 14:19:29 +03:00
qed_fw_reporters_create ( dl ) ;
2021-09-25 14:22:55 +03:00
devlink_register ( dl ) ;
2020-08-23 14:19:26 +03:00
return dl ;
2020-08-23 14:19:25 +03:00
err_unregister :
devlink_free ( dl ) ;
2020-08-23 14:19:26 +03:00
return ERR_PTR ( rc ) ;
2020-08-23 14:19:25 +03:00
}
2020-08-23 14:19:26 +03:00
void qed_devlink_unregister ( struct devlink * devlink )
2020-08-23 14:19:25 +03:00
{
2020-08-23 14:19:26 +03:00
if ( ! devlink )
2020-08-23 14:19:25 +03:00
return ;
2021-09-25 14:22:55 +03:00
devlink_unregister ( devlink ) ;
2020-08-23 14:19:29 +03:00
qed_fw_reporters_destroy ( devlink ) ;
2020-08-23 14:19:26 +03:00
devlink_params_unregister ( devlink , qed_devlink_params ,
2020-08-23 14:19:25 +03:00
ARRAY_SIZE ( qed_devlink_params ) ) ;
2020-08-23 14:19:26 +03:00
devlink_free ( devlink ) ;
2020-08-23 14:19:25 +03:00
}