#include "ice_osdep.h"
#include "ice_common.h"
#include "ice_fwlog.h"
static void cache_cfg(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
hw->fwlog_cfg = *cfg;
}
static bool
valid_module_entries(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
u16 num_entries)
{
u16 i;
if (!entries) {
ice_debug(hw, ICE_DBG_FW_LOG, "Null ice_fwlog_module_entry array\n");
return false;
}
if (!num_entries) {
ice_debug(hw, ICE_DBG_FW_LOG, "num_entries must be non-zero\n");
return false;
}
for (i = 0; i < num_entries; i++) {
struct ice_fwlog_module_entry *entry = &entries[i];
if (entry->module_id >= ICE_AQC_FW_LOG_ID_MAX) {
ice_debug(hw, ICE_DBG_FW_LOG, "Invalid module_id %u, max valid module_id is %u\n",
entry->module_id, ICE_AQC_FW_LOG_ID_MAX - 1);
return false;
}
if (entry->log_level >= ICE_FWLOG_LEVEL_INVALID) {
ice_debug(hw, ICE_DBG_FW_LOG, "Invalid log_level %u, max valid log_level is %u\n",
entry->log_level,
ICE_AQC_FW_LOG_ID_MAX - 1);
return false;
}
}
return true;
}
static bool valid_cfg(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
if (!cfg) {
ice_debug(hw, ICE_DBG_FW_LOG, "Null ice_fwlog_cfg\n");
return false;
}
if (cfg->log_resolution < ICE_AQC_FW_LOG_MIN_RESOLUTION ||
cfg->log_resolution > ICE_AQC_FW_LOG_MAX_RESOLUTION) {
ice_debug(hw, ICE_DBG_FW_LOG, "Unsupported log_resolution %u, must be between %u and %u\n",
cfg->log_resolution, ICE_AQC_FW_LOG_MIN_RESOLUTION,
ICE_AQC_FW_LOG_MAX_RESOLUTION);
return false;
}
if (!valid_module_entries(hw, cfg->module_entries,
ICE_AQC_FW_LOG_ID_MAX))
return false;
return true;
}
int
ice_fwlog_init(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
if (!valid_cfg(hw, cfg))
return ICE_ERR_PARAM;
cache_cfg(hw, cfg);
return 0;
}
static int
ice_aq_fwlog_set(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
u16 num_entries, u16 options, u16 log_resolution)
{
struct ice_aqc_fw_log_cfg_resp *fw_modules;
struct ice_aqc_fw_log *cmd;
struct ice_aq_desc desc;
int status;
u16 i;
fw_modules = (struct ice_aqc_fw_log_cfg_resp *)
ice_calloc(hw, num_entries, sizeof(*fw_modules));
if (!fw_modules)
return ICE_ERR_NO_MEMORY;
for (i = 0; i < num_entries; i++) {
fw_modules[i].module_identifier =
CPU_TO_LE16(entries[i].module_id);
fw_modules[i].log_level = entries[i].log_level;
}
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_config);
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
cmd = &desc.params.fw_log;
cmd->cmd_flags = ICE_AQC_FW_LOG_CONF_SET_VALID;
cmd->ops.cfg.log_resolution = CPU_TO_LE16(log_resolution);
cmd->ops.cfg.mdl_cnt = CPU_TO_LE16(num_entries);
if (options & ICE_FWLOG_OPTION_ARQ_ENA)
cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_AQ_EN;
if (options & ICE_FWLOG_OPTION_UART_ENA)
cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_UART_EN;
status = ice_aq_send_cmd(hw, &desc, fw_modules,
sizeof(*fw_modules) * num_entries,
NULL);
ice_free(hw, fw_modules);
return status;
}
bool ice_fwlog_supported(struct ice_hw *hw)
{
return hw->fwlog_support_ena;
}
int
ice_fwlog_set(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
int status;
if (!ice_fwlog_supported(hw))
return ICE_ERR_NOT_SUPPORTED;
if (!valid_cfg(hw, cfg))
return ICE_ERR_PARAM;
status = ice_aq_fwlog_set(hw, cfg->module_entries,
ICE_AQC_FW_LOG_ID_MAX, cfg->options,
cfg->log_resolution);
if (!status)
cache_cfg(hw, cfg);
return status;
}
static void
update_cached_entries(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
u16 num_entries)
{
u16 i;
for (i = 0; i < num_entries; i++) {
struct ice_fwlog_module_entry *updated = &entries[i];
u16 j;
for (j = 0; j < ICE_AQC_FW_LOG_ID_MAX; j++) {
struct ice_fwlog_module_entry *cached =
&hw->fwlog_cfg.module_entries[j];
if (cached->module_id == updated->module_id) {
cached->log_level = updated->log_level;
break;
}
}
}
}
int
ice_fwlog_update_modules(struct ice_hw *hw,
struct ice_fwlog_module_entry *entries,
u16 num_entries)
{
struct ice_fwlog_cfg *cfg;
int status;
if (!ice_fwlog_supported(hw))
return ICE_ERR_NOT_SUPPORTED;
if (!valid_module_entries(hw, entries, num_entries))
return ICE_ERR_PARAM;
cfg = (struct ice_fwlog_cfg *)ice_calloc(hw, 1, sizeof(*cfg));
if (!cfg)
return ICE_ERR_NO_MEMORY;
status = ice_fwlog_get(hw, cfg);
if (status)
goto status_out;
status = ice_aq_fwlog_set(hw, entries, num_entries, cfg->options,
cfg->log_resolution);
if (!status)
update_cached_entries(hw, entries, num_entries);
status_out:
ice_free(hw, cfg);
return status;
}
static int ice_aq_fwlog_register(struct ice_hw *hw, bool reg)
{
struct ice_aq_desc desc;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_register);
if (reg)
desc.params.fw_log.cmd_flags = ICE_AQC_FW_LOG_AQ_REGISTER;
return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
}
int ice_fwlog_register(struct ice_hw *hw)
{
int status;
if (!ice_fwlog_supported(hw))
return ICE_ERR_NOT_SUPPORTED;
status = ice_aq_fwlog_register(hw, true);
if (status)
ice_debug(hw, ICE_DBG_FW_LOG, "Failed to register for firmware logging events over ARQ\n");
else
hw->fwlog_cfg.options |= ICE_FWLOG_OPTION_IS_REGISTERED;
return status;
}
int ice_fwlog_unregister(struct ice_hw *hw)
{
int status;
if (!ice_fwlog_supported(hw))
return ICE_ERR_NOT_SUPPORTED;
status = ice_aq_fwlog_register(hw, false);
if (status)
ice_debug(hw, ICE_DBG_FW_LOG, "Failed to unregister from firmware logging events over ARQ\n");
else
hw->fwlog_cfg.options &= ~ICE_FWLOG_OPTION_IS_REGISTERED;
return status;
}
static int
ice_aq_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
struct ice_aqc_fw_log_cfg_resp *fw_modules;
struct ice_aqc_fw_log *cmd;
struct ice_aq_desc desc;
u16 i, module_id_cnt;
int status;
void *buf;
ice_memset(cfg, 0, sizeof(*cfg), ICE_NONDMA_MEM);
buf = ice_calloc(hw, 1, ICE_AQ_MAX_BUF_LEN);
if (!buf)
return ICE_ERR_NO_MEMORY;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_query);
cmd = &desc.params.fw_log;
cmd->cmd_flags = ICE_AQC_FW_LOG_AQ_QUERY;
status = ice_aq_send_cmd(hw, &desc, buf, ICE_AQ_MAX_BUF_LEN, NULL);
if (status) {
ice_debug(hw, ICE_DBG_FW_LOG, "Failed to get FW log configuration\n");
goto status_out;
}
module_id_cnt = LE16_TO_CPU(cmd->ops.cfg.mdl_cnt);
if (module_id_cnt < ICE_AQC_FW_LOG_ID_MAX) {
ice_debug(hw, ICE_DBG_FW_LOG, "FW returned less than the expected number of FW log module IDs\n");
} else {
if (module_id_cnt > ICE_AQC_FW_LOG_ID_MAX)
ice_debug(hw, ICE_DBG_FW_LOG, "FW returned more than expected number of FW log module IDs, setting module_id_cnt to software expected max %u\n",
ICE_AQC_FW_LOG_ID_MAX);
module_id_cnt = ICE_AQC_FW_LOG_ID_MAX;
}
cfg->log_resolution = LE16_TO_CPU(cmd->ops.cfg.log_resolution);
if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_AQ_EN)
cfg->options |= ICE_FWLOG_OPTION_ARQ_ENA;
if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_UART_EN)
cfg->options |= ICE_FWLOG_OPTION_UART_ENA;
if (cmd->cmd_flags & ICE_AQC_FW_LOG_QUERY_REGISTERED)
cfg->options |= ICE_FWLOG_OPTION_IS_REGISTERED;
fw_modules = (struct ice_aqc_fw_log_cfg_resp *)buf;
for (i = 0; i < module_id_cnt; i++) {
struct ice_aqc_fw_log_cfg_resp *fw_module = &fw_modules[i];
cfg->module_entries[i].module_id =
LE16_TO_CPU(fw_module->module_identifier);
cfg->module_entries[i].log_level = fw_module->log_level;
}
status_out:
ice_free(hw, buf);
return status;
}
void ice_fwlog_set_support_ena(struct ice_hw *hw)
{
struct ice_fwlog_cfg *cfg;
int status;
hw->fwlog_support_ena = false;
cfg = (struct ice_fwlog_cfg *)ice_calloc(hw, 1, sizeof(*cfg));
if (!cfg)
return;
status = ice_aq_fwlog_get(hw, cfg);
if (status)
ice_debug(hw, ICE_DBG_FW_LOG, "ice_fwlog_get failed, FW logging is not supported on this version of FW, status %d\n",
status);
else
hw->fwlog_support_ena = true;
ice_free(hw, cfg);
}
int
ice_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
int status;
if (!ice_fwlog_supported(hw))
return ICE_ERR_NOT_SUPPORTED;
if (!cfg)
return ICE_ERR_PARAM;
status = ice_aq_fwlog_get(hw, cfg);
if (status)
return status;
cache_cfg(hw, cfg);
return 0;
}
void
ice_fwlog_event_dump(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf)
{
if (!ice_fwlog_supported(hw))
return;
ice_info_fwlog(hw, 32, 1, (u8 *)buf, LE16_TO_CPU(desc->datalen));
}