kernel_samsung_a34x-permissive/drivers/misc/mediatek/connectivity/connfem/connfem_epaelna.c
2024-04-28 17:35:24 +02:00

831 lines
21 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
//#include <linux/err.h>
#include <linux/string.h>
#include <linux/of_platform.h>
#include <linux/of_device.h>
#include "connfem.h"
/*******************************************************************************
* M A C R O S
******************************************************************************/
#define CFM_DT_PROP_VID "vid"
#define CFM_DT_PROP_PID "pid"
#define FEMID_VID 0
#define FEMID_PID 1
#define FEMID_ITEMS 2
/*******************************************************************************
* D A T A T Y P E S
******************************************************************************/
/*******************************************************************************
* F U N C T I O N D E C L A R A T I O N S
******************************************************************************/
static int cfm_epaelna_feminfo_part_populate(struct device_node *np,
struct connfem_part *result_out);
static int cfm_epaelna_pincfg_mapping_populate(
struct cfm_dt_epaelna_pctl_data_context *pctl_data,
struct connfem_epaelna_pin_info *result_out);
static int cfm_epaelna_pincfg_laa_pinmux_populate(
struct cfm_dt_epaelna_pctl_data_context *pctl_data,
struct connfem_epaelna_laa_pin_info *result_out);
static int cfm_epaelna_flags_subsys_populate(
struct device_node *np,
struct connfem_epaelna_flag_tbl_entry *tbl,
struct cfm_container **result_out);
static struct connfem_epaelna_flag_tbl_entry*
cfm_epaelna_flags_subsys_find(
char *name,
struct connfem_epaelna_flag_tbl_entry *tbl);
/*******************************************************************************
* P U B L I C D A T A
******************************************************************************/
char *cfm_subsys_name[CONNFEM_SUBSYS_NUM] = {
/* [CONNFEM_SUBSYS_NONE] */ NULL,
/* [CONNFEM_SUBSYS_WIFI] */ CFM_DT_NODE_WIFI,
/* [CONNFEM_SUBSYS_BT] */ CFM_DT_NODE_BT
};
/*******************************************************************************
* P R I V A T E D A T A
******************************************************************************/
/* Align with FemNo coding in firmware
* 0xAABCDDEE:
* [31:24] AA: reserved
* [23:20] B: G-Band VID
* [19:16] C: A-Band VID
* [15:8] DD: G-Band PID
* [ 7:0] EE: A-Band PID
*/
static int fem_id_shift[CONNFEM_PORT_NUM][FEMID_ITEMS] = {
/* [0]CONNFEM_PORT_WFG */
{
/* [0]FEMID_VID */ 20,
/* [1]FEMID_PID */ 8
},
/* [1]CONNFEM_PORT_WFA */
{
/* [0]FEMID_VID */ 16,
/* [1]FEMID_PID */ 0
}
};
static struct connfem_epaelna_subsys_cb *subsys_cb[CONNFEM_SUBSYS_NUM] = {
/* [CONNFEM_SUBSYS_NONE] */ NULL,
/* [CONNFEM_SUBSYS_WIFI] */ &cfm_wf_epaelna_cb,
/* [CONNFEM_SUBSYS_BT] */ &cfm_bt_epaelna_cb
};
/*******************************************************************************
* F U N C T I O N S
******************************************************************************/
void cfm_epaelna_config_free(struct cfm_epaelna_config *cfg, bool free_all)
{
if (!cfg)
return;
/* In case we need to keep some critical information for later use,
* the data related to FEM info and flags are especially important,
* these should only be freed if "free_all" is specified.
*
* In another word, if free_all == false, the only thing we could
* clean up is the PIN related info.
*/
if (!free_all) {
memset(&cfg->pin_cfg, 0, sizeof(cfg->pin_cfg));
return;
}
cfm_epaelna_flags_free(cfg->flags_cfg);
memset(cfg, 0, sizeof(*cfg));
}
void cfm_epaelna_flags_free(struct cfm_epaelna_flags_config *flags)
{
if (!flags)
return;
if (flags->name_entries) {
cfm_container_entries_free((void **)flags->name_entries);
flags->name_entries = NULL;
}
if (flags->names) {
cfm_container_free(flags->names);
flags->names = NULL;
}
}
int cfm_epaelna_feminfo_populate(struct cfm_dt_epaelna_context *dt,
struct connfem_epaelna_fem_info *result_out)
{
struct connfem_epaelna_fem_info result;
struct device_node *np;
int i;
memset(&result, 0, sizeof(result));
/* Retrieves FEM info from each of the parts nodes
* We shall keep as much info as possible, so FW has the chance to
* know if a FEM does exist, and could do some FEM protection logic.
*/
for (i = 0; i < CONNFEM_PORT_NUM; i++) {
np = dt->parts_np[i];
/* VID, PID */
cfm_epaelna_feminfo_part_populate(np, &result.part[i]);
/* Encode FEM ID for firmware (FEM Number) */
result.id = result.id |
(result.part[i].vid << fem_id_shift[i][FEMID_VID]) |
(result.part[i].pid << fem_id_shift[i][FEMID_PID]);
/* Part name */
strncpy(result.part_name[i], np->name,
sizeof(result.part_name[i]) - 1);
result.part_name[i][sizeof(result.part_name[i]) - 1] = 0;
}
/* Update output parameter */
memcpy(result_out, &result,
sizeof(result));
cfm_epaelna_feminfo_dump(result_out);
return 0;
}
/**
* cfm_epaelna_feminfo_part_populate
* Extract info from a single part node
*
* Parameters
* np : Pointer to the part node containing 'vid'/'pid'
* result_out: the result output
*
* Return value
* 0 : Success, result will contain valid value
* -EINVAL : Error
*
*/
static int cfm_epaelna_feminfo_part_populate(struct device_node *np,
struct connfem_part *result_out)
{
int err = 0;
struct connfem_part result;
unsigned int value;
memset(&result, 0, sizeof(result));
/* VID */
value = 0;
err = of_property_read_u32(np, CFM_DT_PROP_VID, &value);
if (err < 0 || value > 0xFF) {
pr_info("[WARN] %s.%s: %d missing or not a 8-bit value, err %d",
np->name, CFM_DT_PROP_VID, value, err);
value = 0;
}
result.vid = (unsigned char)value;
/* PID */
value = 0;
err = of_property_read_u32(np, CFM_DT_PROP_PID, &value);
if (err < 0 || value > 0xFF) {
pr_info("[WARN] %s.%s: %d missing or not a 8-bit value, err %d",
np->name, CFM_DT_PROP_PID, value, err);
value = 0;
}
result.pid = (unsigned char)value;
/* Update output parameter */
memcpy(result_out, &result, sizeof(result));
return 0;
}
/**
* cfm_epaelna_pincfg_populate
* Populate pin config from the pinctrl data node.
*
* NOTE: the result will be APPENDED in the output, instead of
* overwriting the whole structure as other API does.
*
* Parameters
* pctl_data: Pointer to the pinctrl data dt context
*
* Return value
* 0 : Success, result output will be valid
* -EINVAL : Error
*
*/
int cfm_epaelna_pincfg_populate(
struct cfm_dt_epaelna_pctl_data_context *pctl_data,
struct cfm_epaelna_pin_config *result_out)
{
int err = 0;
err = cfm_epaelna_pincfg_mapping_populate(pctl_data,
&result_out->pin_info);
if (err < 0)
return -EINVAL;
err = cfm_epaelna_pincfg_laa_pinmux_populate(pctl_data,
&result_out->laa_pin_info);
if (err < 0)
return -EINVAL;
return 0;
}
/**
* cfm_epaelna_pincfg_mapping_populate
* Populate ANTSEL & FEM pin mapping from the pinctrl data node
*
* NOTE: the result will be APPENDED in the output, instead of
* overwriting the whole structure as other API does.
*
* Parameters
* pctl_data: Pointer to the pinctrl data dt context
*
* Return value
* 0 : Success, result output will be valid
* -EINVAL : Error
*
*/
static int cfm_epaelna_pincfg_mapping_populate(
struct cfm_dt_epaelna_pctl_data_context *pctl_data,
struct connfem_epaelna_pin_info *result_out)
{
int err = 0;
int p, i, start;
unsigned int p_ofst;
unsigned int value = 0;
unsigned char *addr;
struct connfem_epaelna_pin_info result;
/* Picking up from where we left off */
memcpy(&result, result_out, sizeof(result));
/* Check if we still have enough storage for new pins */
if (pctl_data->pin_cnt + result.count > CONNFEM_EPAELNA_PIN_COUNT) {
pr_info("[WARN] Too many ANTSEL PINs! Not enough space for %d",
pctl_data->pin_cnt);
cfm_epaelna_pininfo_dump(&result);
return -ENOMEM;
}
p_ofst = result.count;
p = i = start = 0;
for (p = 0; p < pctl_data->pin_cnt; p++) {
start = p * CFM_DT_MAPPING_SIZE;
for (i = 0; i < CFM_DT_MAPPING_SIZE; i++) {
err = of_property_read_u32_index(pctl_data->np,
CFM_DT_PROP_MAPPING,
start + i,
&value);
if (err < 0)
goto mapping_populate_read_err;
if (i == CFM_DT_MAPPING_POLARITY_INDEX && value > 1) {
err = -EINVAL;
goto mapping_populate_polarity_err;
}
if (value > 0xFF) {
err = -EINVAL;
goto mapping_populate_outofbound_err;
}
addr = (unsigned char *)(&result.pin[p + p_ofst]);
*(addr + i) = (unsigned char)value;
}
}
result.count += pctl_data->pin_cnt;
memcpy(result_out, &result, sizeof(result));
return 0;
mapping_populate_read_err:
pr_info("[WARN] %s,pin:%d idx:%d,real idx:%d,Read err %d",
CFM_DT_PROP_MAPPING,
p, i, (start + i), err);
return err;
mapping_populate_polarity_err:
pr_info("[WARN] %s,pin:%d idx:%d,real idx:%d,Polarity %d is not 0 or 1",
CFM_DT_PROP_MAPPING,
p, i, (start + i), value);
return err;
mapping_populate_outofbound_err:
pr_info("[WARN] %s,pin:%d idx:%d,real idx:%d,Value %d exceeds 8-bit",
CFM_DT_PROP_MAPPING,
p, i, (start + i), value);
return err;
}
/**
* cfm_epaelna_pincfg_laa_pinmux_populate
* Populate LAA pinmux from the pinctrl data node
*
* NOTE: the result will be APPENDED in the output, instead of
* overwriting the whole structure as other API does.
*
* Parameters
* pctl_data: Pointer to the pinctrl data dt context
*
* Return value
* 0 : Success, result output will be valid
* -EINVAL : Error
*
*/
static int cfm_epaelna_pincfg_laa_pinmux_populate(
struct cfm_dt_epaelna_pctl_data_context *pctl_data,
struct connfem_epaelna_laa_pin_info *result_out)
{
int err = 0;
int p, i, start;
unsigned int p_ofst;
unsigned int value[CFM_DT_LAA_PINMUX_SIZE] = {0};
struct connfem_epaelna_laa_pin_info result;
/* Picking up from where we left off */
memcpy(&result, result_out, sizeof(result));
/* Check if we still have enough storage for new pins */
if (pctl_data->laa_cnt + result.count >
CONNFEM_EPAELNA_LAA_PIN_COUNT) {
pr_info("[WARN] Too many LAA PINs! Not enough space for %d",
pctl_data->laa_cnt);
cfm_epaelna_laainfo_dump(&result);
return -ENOMEM;
}
p_ofst = result.count;
p = i = start = 0;
for (p = 0; p < pctl_data->laa_cnt; p++) {
start = p * CFM_DT_LAA_PINMUX_SIZE;
for (i = 0; i < CFM_DT_LAA_PINMUX_SIZE; i++) {
err = of_property_read_u32_index(pctl_data->np,
CFM_DT_PROP_LAA_PINMUX,
start + i,
&value[i]);
if (err < 0)
goto laa_pinmux_populate_read_err;
if (CFM_DT_GET_PIN_NO(value[i]) > 0xFFFF) {
err = -EINVAL;
goto laa_pinmux_populate_pin_outofbound_err;
}
}
if (CFM_DT_GET_PIN_NO(value[CFM_DT_LAA_PINMUX_WF_INDEX]) !=
CFM_DT_GET_PIN_NO(value[CFM_DT_LAA_PINMUX_MD_INDEX])) {
err = -EINVAL;
goto laa_pinmux_populate_pin_mismatch_err;
}
result.pin[p + p_ofst].gpio =
CFM_DT_GET_PIN_NO(value[CFM_DT_LAA_PINMUX_WF_INDEX]);
result.pin[p + p_ofst].wf_mode =
CFM_DT_GET_PIN_FUNC(value[CFM_DT_LAA_PINMUX_WF_INDEX]);
result.pin[p + p_ofst].md_mode =
CFM_DT_GET_PIN_FUNC(value[CFM_DT_LAA_PINMUX_MD_INDEX]);
}
result.count += pctl_data->laa_cnt;
memcpy(result_out, &result, sizeof(result));
return 0;
laa_pinmux_populate_read_err:
pr_info("[WARN] %s,pin:%d idx:%d,real idx:%d,Read err %d",
CFM_DT_PROP_LAA_PINMUX,
p, i, (start + i), err);
return err;
laa_pinmux_populate_pin_outofbound_err:
pr_info("[WARN] %s,pin:%d idx:%d,real idx:%d,Value 0x%x,GPIO exceed %d",
CFM_DT_PROP_LAA_PINMUX,
p, i, (start + i),
value[i], 0xFFFF);
return err;
laa_pinmux_populate_pin_mismatch_err:
pr_info("[WARN] %s,pin:%d idx:%d,GPIO mismatch WF:%d/MD:%d",
CFM_DT_PROP_LAA_PINMUX,
p, start,
CFM_DT_GET_PIN_NO(value[CFM_DT_LAA_PINMUX_WF_INDEX]),
CFM_DT_GET_PIN_NO(value[CFM_DT_LAA_PINMUX_MD_INDEX]));
return err;
}
/**
* cfm_epaelna_flags_populate
* Populate subsys' flags from the parsed flags node
*
* Parameters
* dt_flags: Pointer to the flags dt context
* result : Pointer to an array of size CONNFEM_SUBSYS_NUM, and
* element type of struct cfm_epaelna_flags_config
*
* Return value
* 0 : Success, result output will be valid
* -ENOMEM : Out of memory
* -EINVAL : Error
*
*/
int cfm_epaelna_flags_populate(
struct cfm_dt_epaelna_flags_context *dt_flags,
struct cfm_epaelna_flags_config *result_out)
{
int err = 0;
int s;
struct connfem_epaelna_flag_tbl_entry *tbl = NULL;
struct cfm_epaelna_flags_config result[CONNFEM_SUBSYS_NUM];
memset(&result, 0, sizeof(result));
for (s = 0; s < CONNFEM_SUBSYS_NUM; s++) {
if (!dt_flags->np[s] || !subsys_cb[s])
continue;
if (!subsys_cb[s]->flags_get) {
pr_info("[WARN] Undefined %s.flags_get",
cfm_subsys_name[s]);
continue;
}
if (!subsys_cb[s]->flags_tbl_get) {
pr_info("[WARN] Undefined %s.flags_tbl_get",
cfm_subsys_name[s]);
continue;
}
tbl = subsys_cb[s]->flags_tbl_get();
if (!tbl) {
pr_info("[WARN] %s flags mapping table is NULL",
cfm_subsys_name[s]);
continue;
}
err = cfm_epaelna_flags_subsys_populate(dt_flags->np[s],
tbl,
&result[s].names);
if (err < 0)
break;
result[s].name_entries = (char **)cfm_container_entries(
result[s].names);
if (result[s].names && (result[s].names->cnt > 0) &&
!result[s].name_entries) {
err = -ENOMEM;
break;
}
cfm_epaelna_flags_names_dump(s, result[s].names);
result[s].obj = subsys_cb[s]->flags_get();
if (!result[s].obj) {
/* If subsys doesn't want to return its flags struct,
* doesn't harm us, so continue normally...
*/
pr_info("[WARN] %s flags structure is NULL, continue..",
cfm_subsys_name[s]);
}
}
if (err < 0) {
pr_info("Error while populating %s '%s'",
cfm_subsys_name[s], dt_flags->node_name);
for (s = 0; s < CONNFEM_SUBSYS_NUM; s++)
cfm_epaelna_flags_free(&result[s]);
} else {
memcpy(result_out, &result, sizeof(result));
}
return err;
}
/**
* cfm_epaelna_flags_subsys_populate
* Function traverses through all props in the give subsys' flags node,
* and updates subsys' flags structure through mapping table,
* and collect flag names into the ConnFem container.
*
* On success, the container will be allocated, caller needs to release it
* via cfm_container_free().
*
* Parameters
* np : Pointer to subsys' flags device tree node
* tbl : Pointer to subsys' flags mapping table
* result_out: Pointer to ConnFem container for storing flags names
*
* Return value
* 0 : Success, result output will be valid
* -ENOMEM : Out of memory
*
*/
static int cfm_epaelna_flags_subsys_populate(
struct device_node *np,
struct connfem_epaelna_flag_tbl_entry *tbl,
struct cfm_container **result_out)
{
unsigned int i, len;
struct property *prop = NULL;
struct connfem_epaelna_flag_tbl_entry *entry;
struct cfm_container *result = NULL;
/* Prepare flags names container storage */
i = 0;
for_each_property_of_node(np, prop) {
i++;
}
result = cfm_container_alloc(i, CONNFEM_FLAG_NAME_SIZE);
if (!result)
return -ENOMEM;
/* Retrieves flags name from device tree */
i = 0;
for_each_property_of_node(np, prop) {
/* Skip built-in property 'name' */
if (strcmp(prop->name, "name") == 0)
continue;
/* Skip if not supported by subsys */
entry = cfm_epaelna_flags_subsys_find(prop->name, tbl);
if (!entry)
continue;
/* Discard flag with name exceeding length limit */
len = strlen(prop->name) + 1;
if (len > CONNFEM_FLAG_NAME_SIZE) {
pr_info("[WARN] Drop '%s' prop, len %u > %u",
prop->name,
len - 1,
CONNFEM_FLAG_NAME_SIZE - 1);
continue;
}
/* Double check if container is big enough to keep this flag */
if (i + 1 <= result->cnt) {
memcpy(cfm_container_entry(result, i),
prop->name,
len);
/* Update subsys' flags table only if all else ok.
* No need to check for NULL, as already done at:
* cfm_epaelna_flags_subsys_find.
*/
*(entry->addr) = true;
i++;
} else {
pr_info("[ERR] Drop '%s' prop, too many flags %d > %d",
prop->name, i + 1, result->cnt);
}
}
/* Updates container size, the end result could be smaller,
* due to removal of long name and built-in property.
*/
result->cnt = i;
*result_out = result;
return 0;
}
/**
* connfem_epaelna_flag_tbl_entry
* Function traverses through all props in the give subsys' flags node,
* and updates subsys' flags structure through mapping table,
* and collect flag names into the ConnFem container.
*
* On success, the container will be allocated, caller needs to release it
* via cfm_container_free().
*
* Parameters
* name: Name of the flag to search
* tbl : Pointer to subsys' flags mapping table
*
* Return value
* Pointer to the subsys' flag table entry, or NULL if can't be found.
*
*/
static struct connfem_epaelna_flag_tbl_entry*
cfm_epaelna_flags_subsys_find(
char *name,
struct connfem_epaelna_flag_tbl_entry *tbl)
{
struct connfem_epaelna_flag_tbl_entry *iter = tbl;
while (iter && (iter->name != NULL && iter->addr != NULL)) {
if (strncmp(name, iter->name, CONNFEM_FLAG_NAME_SIZE) == 0)
return iter;
iter++;
}
pr_info("Unsupported flag '%s'", name);
return NULL;
}
void cfm_epaelna_config_dump(struct cfm_epaelna_config *cfg)
{
int i;
if (!cfg) {
pr_info("ePAeLNA Config, (null)");
return;
}
pr_info("ePAeLNA Config, available:%d", cfg->available);
cfm_epaelna_feminfo_dump(&cfg->fem_info);
cfm_epaelna_pininfo_dump(&cfg->pin_cfg.pin_info);
cfm_epaelna_laainfo_dump(&cfg->pin_cfg.laa_pin_info);
for (i = 0; i < CONNFEM_SUBSYS_NUM; i++)
cfm_epaelna_flags_dump(i, &cfg->flags_cfg[i]);
}
void cfm_epaelna_feminfo_dump(struct connfem_epaelna_fem_info *fem_info)
{
int i;
if (!fem_info) {
pr_info("FemInfo, (null)");
return;
}
pr_info("FemInfo, id:0x%08x", fem_info->id);
for (i = 0; i < CONNFEM_PORT_NUM; i++) {
pr_info("FemInfo, [%d]vid:0x%02x,pid:0x%02x,name:'%s'",
i,
fem_info->part[i].vid,
fem_info->part[i].pid,
fem_info->part_name[i]);
}
}
void cfm_epaelna_pininfo_dump(struct connfem_epaelna_pin_info *pin_info)
{
int i;
if (!pin_info) {
pr_info("PinInfo, (null)");
return;
}
pr_info("PinInfo, count:%d, max:%d",
pin_info->count, CONNFEM_EPAELNA_PIN_COUNT);
for (i = 0; i < pin_info->count; i++) {
pr_info("PinInfo, [%d]antsel:%d,fem:0x%02x,polarity:%d",
i,
pin_info->pin[i].antsel,
pin_info->pin[i].fem,
pin_info->pin[i].polarity);
}
}
void cfm_epaelna_laainfo_dump(struct connfem_epaelna_laa_pin_info *laa)
{
int i;
if (!laa) {
pr_info("LaaInfo, (null)");
return;
}
pr_info("LaaInfo, count:%d, max:%d, id:0x%x",
laa->count, CONNFEM_EPAELNA_LAA_PIN_COUNT, laa->chip_id);
for (i = 0; i < laa->count; i++) {
pr_info("LaaInfo, [%d]gpio:%d,wf:%d,md:%d",
i,
laa->pin[i].gpio,
laa->pin[i].wf_mode,
laa->pin[i].md_mode);
}
}
/* Dump a single subsys' flags config */
void cfm_epaelna_flags_dump(enum connfem_subsys subsys,
struct cfm_epaelna_flags_config *flags)
{
unsigned int cnt = 0;
if (subsys <= CONNFEM_SUBSYS_NONE || subsys >= CONNFEM_SUBSYS_NUM)
return;
if (!flags) {
pr_info("Flags, %s (null)", cfm_subsys_name[subsys]);
return;
}
cfm_epaelna_flags_obj_dump(subsys, flags->obj);
cfm_epaelna_flags_names_dump(subsys, flags->names);
if (flags->names)
cnt = flags->names->cnt;
cfm_epaelna_flags_name_entries_dump(subsys, cnt, flags->name_entries);
}
void cfm_epaelna_flags_obj_dump(enum connfem_subsys subsys,
void *flags_obj)
{
struct connfem_epaelna_flags_wifi *wf_flags = NULL;
struct connfem_epaelna_flags_bt *bt_flags = NULL;
if (!flags_obj) {
pr_info("FlagsObj, %s (null)", cfm_subsys_name[subsys]);
return;
}
switch (subsys) {
case CONNFEM_SUBSYS_WIFI:
wf_flags = (struct connfem_epaelna_flags_wifi *)flags_obj;
pr_info("WfFlags.open_loop: %d", wf_flags->open_loop);
pr_info("WfFlags.laa: %d", wf_flags->laa);
break;
case CONNFEM_SUBSYS_BT:
bt_flags = (struct connfem_epaelna_flags_bt *)flags_obj;
pr_info("BtFlags.bypass: %d", bt_flags->bypass);
pr_info("BtFlags.epa_elna: %d", bt_flags->epa_elna);
pr_info("BtFlags.elna: %d", bt_flags->elna);
pr_info("BtFlags.epa: %d", bt_flags->epa);
break;
default:
break;
}
}
void cfm_epaelna_flags_names_dump(enum connfem_subsys subsys,
struct cfm_container *names)
{
int i;
if (!names) {
pr_info("[%s]FlagNames, (null)", cfm_subsys_name[subsys]);
return;
}
pr_info("[%s]FlagNames, count:%d, entry_sz:%d",
cfm_subsys_name[subsys], names->cnt, names->entry_sz);
for (i = 0; i < names->cnt; i++) {
pr_info("[%s]FlagNames, [%d]'%s'",
cfm_subsys_name[subsys],
i,
(char *)cfm_container_entry(names, i));
}
}
void cfm_epaelna_flags_name_entries_dump(enum connfem_subsys subsys,
unsigned int cnt,
char **name_entries)
{
int i;
if (!name_entries) {
pr_info("[%s]FlagNameEntries, (null)", cfm_subsys_name[subsys]);
return;
}
pr_info("[%s]FlagNameEntries, count:%d", cfm_subsys_name[subsys], cnt);
for (i = 0; i < cnt; i++) {
pr_info("[%s]FlagNameEntries, [%d]'%s'",
cfm_subsys_name[subsys],
i,
(char *)name_entries[i]);
}
}