/*
* Copyright (C) 2020-2021 MaxLinear, Inc.
* Copyright (C) 2017-2020 Intel Corporation
* 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.
* 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 APARTICULARPURPOSE.See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public
* License along with this program; if not,see
* .
* SPDX-License-Identifier: GPL-2.0-only
*
* Description: Packet Processor QoS Driver
*/
#define pr_fmt(fmt) "[PP_QOS_DBG]:%s:%d: " fmt, __func__, __LINE__
#include
#include
#include
#include
#include
#include
#include "pp_debugfs_common.h"
#include "pp_qos_common.h"
#include "pp_qos_utils.h"
#include "pp_qos_fw.h"
#include "pp_regs.h"
#include "infra.h"
#include "pp_desc.h"
#define PP_QOS_DEBUGFS_DIR "ppv4_qos"
#define AQM_LATENCY_TARGET (10) /* 10 miliseconds */
#define AQM_PEAK_RATE (13107200) /* 100 Mbps */
#define AQM_MSR (13107200) /* 100 Mbps */
#define AQM_BUFFER_SIZE (655360) /* 50ms base on MSR */
static u16 g_node;
static u8 g_raw_config;
void remove_node(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
char node_type[16];
u32 count, id = 0;
qdev = pp_qos_dev_open((unsigned long)data);
if (sscanf(cmd_buf, "%10s %u", node_type, &id) != 2) {
QOS_LOG_ERR("sscanf err\n");
return;
}
if (!strncmp(node_type, "port", strlen("port"))) {
pp_qos_port_remove(qdev, id);
} else if (!strncmp(node_type, "sched", strlen("sched"))) {
pp_qos_sched_remove(qdev, id);
} else if (!strncmp(node_type, "queue", strlen("queue"))) {
pp_qos_queue_remove(qdev, id);
} else if (!strncmp(node_type, "cont_queue", strlen("cont_queue"))) {
if (sscanf(cmd_buf, "%16s %u %u", node_type, &id,
&count) != 3) {
QOS_LOG_ERR("sscanf err\n");
return;
}
pp_qos_contiguous_queue_remove(qdev, id, count);
} else {
QOS_LOG_ERR("Type %s not supported\n", node_type);
}
}
void remove_node_help(struct seq_file *f)
{
seq_puts(f, " \n");
seq_puts(f, " \n");
}
PP_DEFINE_DEBUGFS(remove, remove_node_help, remove_node);
void allocate_node(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 phy = 0, id, count;
char node_type[16];
u32 *ids, *rlms;
s32 rc;
qdev = pp_qos_dev_open((unsigned long)data);
if (sscanf(cmd_buf, "%16s", node_type) != 1) {
QOS_LOG_ERR("sscanf err\n");
return;
}
if (!strncmp(node_type, "port", 6)) {
if (sscanf(cmd_buf, "%16s %u", node_type, &phy) != 2) {
QOS_LOG_ERR("sscanf err\n");
return;
}
pp_qos_port_allocate(qdev, phy, &id);
} else if (!strncmp(node_type, "sched", strlen("sched"))) {
pp_qos_sched_allocate(qdev, &id);
} else if (!strncmp(node_type, "queue_id_phy",
strlen("queue_id_phy"))) {
pp_qos_queue_allocate_id_phy(qdev, &id, &phy);
QOS_LOG_INFO("Allocated id %u rlm %u\n", id, phy);
return;
} else if (!strncmp(node_type, "queue", strlen("queue"))) {
pp_qos_queue_allocate(qdev, &id);
} else if (!strncmp(node_type, "cont_queue", strlen("cont_queue"))) {
if (sscanf(cmd_buf, "%16s %u", node_type, &count) != 2) {
QOS_LOG_ERR("sscanf err\n");
return;
}
ids = kzalloc(qdev->init_params.reserved_queues * sizeof(u32),
GFP_KERNEL);
rlms = kzalloc(qdev->init_params.reserved_queues * sizeof(u32),
GFP_KERNEL);
rc = pp_qos_contiguous_queue_allocate(qdev, ids, rlms, count);
if (!rc) {
QOS_LOG_INFO("Alloc %u contq start @ id %u rlm %u\n",
count, *ids, *rlms);
}
kfree(ids);
kfree(rlms);
return;
} else {
QOS_LOG_ERR("Type %s not supported\n", node_type);
return;
}
QOS_LOG_INFO("Allocated id %u\n", id);
}
void allocate_node_help(struct seq_file *f)
{
seq_printf(f, " \n",
ALLOC_PORT_ID);
seq_puts(f, " \n");
seq_puts(f, "\n");
}
PP_DEFINE_DEBUGFS(allocate, allocate_node_help, allocate_node);
void queue_flush(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 id = 0;
qdev = pp_qos_dev_open((unsigned long)data);
if (kstrtou32(cmd_buf, 10, &id)) {
QOS_LOG_ERR("kstrtou32 err\n");
return;
}
qos_queue_flush(qdev, id);
}
void queue_flush_help(struct seq_file *f)
{
seq_puts(f, "\n");
}
PP_DEFINE_DEBUGFS(flush, queue_flush_help, queue_flush);
void add_shared_bwl_group(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 max_burst;
u32 id = 0;
qdev = pp_qos_dev_open((unsigned long)data);
if (kstrtou32(cmd_buf, 10, &max_burst)) {
QOS_LOG_ERR("kstrtou32 err\n");
return;
}
pp_qos_shared_limit_group_add(qdev, max_burst, &id);
QOS_LOG_INFO("id %u, max_burst %u\n", id, max_burst);
}
void add_shared_bwl_group_help(struct seq_file *f)
{
seq_puts(f, "\n");
}
PP_DEFINE_DEBUGFS(shared_grp_add, add_shared_bwl_group_help,
add_shared_bwl_group);
void remove_shared_bwl_group(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 id = 0;
qdev = pp_qos_dev_open((unsigned long)data);
if (kstrtou32(cmd_buf, 10, &id)) {
QOS_LOG_ERR("kstrtou32 err\n");
return;
}
pp_qos_shared_limit_group_remove(qdev, id);
}
void remove_shared_bwl_group_help(struct seq_file *f)
{
seq_puts(f, "\n");
}
PP_DEFINE_DEBUGFS(shared_grp_del, remove_shared_bwl_group_help,
remove_shared_bwl_group);
struct dbg_prop {
char field[32];
char desc[128];
u8 data_type; /* 0 - normal, 1 - pointer */
u32 *dest;
unsigned long **pdest;
};
struct dbg_props_cbs {
s32 (*first_prop_cb)(struct pp_qos_dev *qdev,
char *field,
u32 val,
void *user_data,
u8 raw_config);
s32 (*done_props_cb)(struct pp_qos_dev *qdev,
u32 val, void *user_data);
};
void qos_dbg_props(char *cmd_buf,
struct pp_qos_dev *qdev,
struct dbg_props_cbs *cbs,
struct dbg_prop props[],
u16 num_props,
void *user_data)
{
u8 *field;
char *tok, *ptr, *pval;
unsigned long res;
u16 i, num_changed = 0;
u32 id = PP_QOS_INVALID_ID;
u32 len, first_prop = 1;
s32 ret;
field = kzalloc(PP_DBGFS_WR_STR_MAX, GFP_KERNEL);
if (!field)
return;
ptr = (char *)cmd_buf;
while ((tok = strsep(&ptr, " \t\n\r")) != NULL) {
if (tok[0] == '\0')
continue;
ret = strscpy(field, tok, PP_DBGFS_WR_STR_MAX);
if (ret < 0) {
QOS_LOG_ERR("Strscpy failed (%d)\n", ret);
goto out;
}
pval = strchr(field, '=');
if (!pval) {
QOS_LOG_ERR("Wrong format for prop %s\n", tok);
goto out;
}
*pval = '\0';
pval++;
ret = kstrtoul(pval, 0, &res);
if (ret) {
QOS_LOG_ERR("kstrtoul failure (%d)\n", ret);
goto out;
}
if (first_prop) {
first_prop = 0;
id = res;
if (cbs && cbs->first_prop_cb &&
cbs->first_prop_cb(qdev, field, res,
user_data, g_raw_config)) {
QOS_LOG_ERR("first_prop_cb failed\n");
goto out;
}
}
for (i = 0; i < num_props; i++) {
len = max(strlen(props[i].field), strlen(field));
if (!strncmp(field, props[i].field, len)) {
if (props[i].data_type == 0)
*(props[i].dest) = res;
else
*(props[i].pdest) = (void *)res;
num_changed++;
break;
}
}
if (i == num_props)
QOS_LOG_ERR("Not supported field %s", field);
}
if (id != PP_QOS_INVALID_ID) {
/* If only logical id was set, print current configuration */
if (num_changed == 1) {
QOS_LOG_INFO("Current configuration:\n");
for (i = 0; i < num_props; i++) {
if (props[i].data_type == 0) {
QOS_LOG_INFO("%-30s%u\n",
props[i].field,
*props[i].dest);
} else {
QOS_LOG_INFO("%-30s%#lx\n",
props[i].field,
(ulong)(*props[i].pdest));
}
}
goto out;
}
if (cbs && cbs->done_props_cb) {
if (cbs->done_props_cb(qdev, id, user_data)) {
QOS_LOG_ERR("done_props_cb failed\n");
goto out;
}
}
}
out:
kfree(field);
}
void qos_dbg_props_help(struct seq_file *f, const char *name,
const char *format, struct dbg_prop props[],
u16 num_props)
{
u32 idx;
seq_printf(f, "<---- %s---->\n", name);
seq_printf(f, "[FORMAT] %s\n", format);
seq_puts(f, "[FORMAT] If only id is set, operation is get conf\n");
seq_puts(f, "Supported fields\n");
for (idx = 0; idx < num_props; idx++)
seq_printf(f, "%-30s%s\n", props[idx].field, props[idx].desc);
}
static void dbg_add_prop(struct dbg_prop *props, u16 *pos, u16 size,
const char *name, const char *desc, u32 *dest)
{
s32 ret;
if (*pos >= size) {
QOS_LOG_ERR("pos %d >= size %d", *pos, size);
return;
}
ret = strscpy(props[*pos].field, name, sizeof(props[*pos].field));
if (ret < 0) {
QOS_LOG_ERR("Strscpy failed (%d)\n", ret);
return;
}
ret = strscpy(props[*pos].desc, desc, sizeof(props[*pos].desc));
if (ret < 0) {
QOS_LOG_ERR("Strscpy failed (%d)\n", ret);
return;
}
props[*pos].data_type = 0;
props[*pos].dest = dest;
(*pos)++;
}
static void dbg_add_prop_ptr(struct dbg_prop *props, u16 *pos, u16 size,
const char *name, const char *desc,
unsigned long **dest)
{
s32 ret;
if (*pos >= size) {
QOS_LOG_ERR("pos %d >= size %d", *pos, size);
return;
}
ret = strscpy(props[*pos].field, name, sizeof(props[*pos].field));
if (ret < 0) {
QOS_LOG_ERR("Strscpy failed (%d)\n", ret);
return;
}
ret = strscpy(props[*pos].desc, desc, sizeof(props[*pos].desc));
if (ret < 0) {
QOS_LOG_ERR("Strscpy failed (%d)\n", ret);
return;
}
props[*pos].data_type = 1;
props[*pos].pdest = dest;
(*pos)++;
}
static u16 create_port_props(struct dbg_prop *props, u16 size,
u32 *id, struct pp_qos_port_conf *pconf)
{
u16 num = 0;
dbg_add_prop(props, &num, size, "port",
"Logical id. Must exist as the first property!", id);
dbg_add_prop(props, &num, size, "bw", "Limit in kbps (80kbps steps)",
&pconf->common_prop.bandwidth_limit);
dbg_add_prop(props, &num, size, "shared",
"Shared bw group: 1-511 (0 for remove group)",
&pconf->common_prop.shared_bw_group);
dbg_add_prop(props, &num, size, "max_burst",
"Defines the max quantas that can be accumulated (1 << shift)",
&pconf->common_prop.max_burst);
dbg_add_prop(props, &num, size, "arb",
"Arbitration: 0 - WSP, 1 - WRR",
&pconf->port_parent_prop.arbitration);
dbg_add_prop(props, &num, size, "be",
"Best effort enable: best effort scheduling is enabled",
&pconf->port_parent_prop.best_effort_enable);
dbg_add_prop_ptr(props, &num, size, "r_addr",
"Ring address", (ulong **)&pconf->ring_address);
dbg_add_prop(props, &num, size, "r_size",
"Ring size", &pconf->ring_size);
dbg_add_prop(props, &num, size, "pkt_cred",
"Packet credit: 0 - byte credit, 1 - packet credit",
&pconf->packet_credit_enable);
dbg_add_prop(props, &num, size, "cred", "Port credit", &pconf->credit);
dbg_add_prop(props, &num, size, "dis",
"Disable port tx", &pconf->disable);
dbg_add_prop(props, &num, size, "green_threshold",
"Egress bytes green threshold", &pconf->green_threshold);
dbg_add_prop(props, &num, size, "yellow_threshold",
"Egress bytes yellow threshold",
&pconf->yellow_threshold);
dbg_add_prop(props, &num, size, "enhanced_wsp",
"Enhanced WSP",
&pconf->enhanced_wsp);
return num;
}
static s32 port_first_prop_cb(struct pp_qos_dev *qdev,
char *field,
u32 val,
void *user_data,
u8 raw_config)
{
/* Make sure first property is the port id */
if (strncmp(field, "port", strlen("port"))) {
QOS_LOG_ERR("First prop (%s) must be port\n", field);
return -EINVAL;
}
if (raw_config) {
pp_qos_port_conf_set_default(user_data);
} else if (pp_qos_port_conf_get(qdev, val, user_data) != 0) {
QOS_LOG_ERR("pp_qos_port_conf_get failed (id %u)", val);
return -EINVAL;
}
return 0;
}
static s32 port_done_props_cb(struct pp_qos_dev *qdev,
u32 val, void *user_data)
{
if (pp_qos_port_set(qdev, val, user_data) != 0) {
QOS_LOG_ERR("pp_qos_port_set failed (id %u)", val);
return -EINVAL;
}
return 0;
}
void port(char *cmd_buf, void *data)
{
struct pp_qos_port_conf conf;
struct dbg_props_cbs cbs = { port_first_prop_cb, port_done_props_cb };
u32 id = PP_QOS_INVALID_ID;
u16 num_props;
struct dbg_prop *props;
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)data);
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL | __GFP_ZERO);
if (!props)
return;
num_props = create_port_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props(cmd_buf, qdev, &cbs, props, num_props, &conf);
kfree(props);
}
void port_help(struct seq_file *f)
{
struct pp_qos_port_conf conf;
const char *name = "set port";
const char *format =
"echo port=[logical_id] [field]=[value]... > port";
struct dbg_prop *props;
u16 num_props;
u32 id = PP_QOS_INVALID_ID;
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_port_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props_help(f, name, format, props, num_props);
kfree(props);
}
PP_DEFINE_DEBUGFS(port_set, port_help, port);
static u16 create_sched_props(struct dbg_prop *props, u16 size,
u32 *id, struct pp_qos_sched_conf *pconf)
{
u16 num = 0;
dbg_add_prop(props, &num, size, "sched",
"Logical id. Must exist as the first property!", id);
dbg_add_prop(props, &num, size, "bw", "Limit in kbps (80kbps steps)",
&pconf->common_prop.bandwidth_limit);
dbg_add_prop(props, &num, size, "shared",
"Shared bw group: 1-511 (0 for remove group)",
&pconf->common_prop.shared_bw_group);
dbg_add_prop(props, &num, size, "max_burst",
"Defines the max quantas that can be accumulated (1 << shift)",
&pconf->common_prop.max_burst);
dbg_add_prop(props, &num, size, "arb",
"Arbitration: 0 - WSP, 1 - WRR",
&pconf->sched_parent_prop.arbitration);
dbg_add_prop(props, &num, size, "be",
"Best effort enable: best effort scheduling is enabled",
&pconf->sched_parent_prop.best_effort_enable);
dbg_add_prop(props, &num, size, "parent", "logical parent id",
&pconf->sched_child_prop.parent);
dbg_add_prop(props, &num, size, "priority",
"priority (0-7) in WSP",
&pconf->sched_child_prop.priority);
dbg_add_prop(props, &num, size, "wrr_weight", "percentage from parent",
&pconf->sched_child_prop.wrr_weight);
return num;
}
static s32 sched_first_prop_cb(struct pp_qos_dev *qdev,
char *field,
u32 val,
void *user_data,
u8 raw_config)
{
/* Make sure first property is the sched id */
if (strncmp(field, "sched", strlen("sched"))) {
QOS_LOG_ERR("First prop (%s) must be sched\n", field);
return -EINVAL;
}
if (raw_config) {
pp_qos_sched_conf_set_default(user_data);
} else if (pp_qos_sched_conf_get(qdev, val, user_data) != 0) {
QOS_LOG_ERR("pp_qos_sched_conf_get failed (id %u)", val);
return -EINVAL;
}
return 0;
}
static s32 sched_done_props_cb(struct pp_qos_dev *qdev,
u32 val, void *user_data)
{
if (pp_qos_sched_set(qdev, val, user_data) != 0) {
QOS_LOG_ERR("pp_qos_sched_set failed (id %u)", val);
return -EINVAL;
}
return 0;
}
void sched(char *cmd_buf, void *data)
{
struct pp_qos_sched_conf conf;
struct dbg_props_cbs cbs = { sched_first_prop_cb, sched_done_props_cb };
u32 id = PP_QOS_INVALID_ID;
u16 num_props;
struct dbg_prop *props;
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)data);
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_sched_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props(cmd_buf, qdev, &cbs, props, num_props, &conf);
kfree(props);
}
void sched_help(struct seq_file *f)
{
struct pp_qos_sched_conf conf;
const char *name = "set sched";
const char *format =
"echo sched=[logical_id] [field]=[value]... > sched";
struct dbg_prop *props;
u16 num_props;
u32 id = PP_QOS_INVALID_ID;
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_sched_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props_help(f, name, format, props, num_props);
kfree(props);
}
PP_DEFINE_DEBUGFS(sched_set, sched_help, sched);
static u16 create_queue_props(struct dbg_prop *props, u16 size,
u32 *id, struct pp_qos_queue_conf *pconf)
{
u16 num = 0;
dbg_add_prop(props, &num, size, "queue",
"Logical id. Must exist as the first property!", id);
dbg_add_prop(props, &num, size, "bw", "Limit in kbps (80kbps steps)",
&pconf->common_prop.bandwidth_limit);
dbg_add_prop(props, &num, size, "shared",
"Shared bw group: 1-511 (0 for remove group)",
&pconf->common_prop.shared_bw_group);
dbg_add_prop(props, &num, size, "max_burst",
"Defines the max quantas that can be accumulated (1 << shift)",
&pconf->common_prop.max_burst);
dbg_add_prop(props, &num, size, "parent", "logical parent id",
&pconf->queue_child_prop.parent);
dbg_add_prop(props, &num, size, "priority",
"priority (0-7) in WSP",
&pconf->queue_child_prop.priority);
dbg_add_prop(props, &num, size, "wrr_weight", "percentage from parent",
&pconf->queue_child_prop.wrr_weight);
dbg_add_prop(props, &num, size, "blocked", "drop enqueued packets",
&pconf->blocked);
dbg_add_prop(props, &num, size, "wred_enable", "enable wred drops",
&pconf->wred_enable);
dbg_add_prop(props, &num, size, "wred_fixed_drop_prob",
"fixed prob instead of slope",
&pconf->wred_fixed_drop_prob_enable);
dbg_add_prop(props, &num, size, "min_avg_green",
"Wred start of the slope area",
&pconf->wred_min_avg_green);
dbg_add_prop(props, &num, size, "max_avg_green",
"Wred end of the slope area",
&pconf->wred_max_avg_green);
dbg_add_prop(props, &num, size, "slope_green", "Wred 0-100 scale",
&pconf->wred_slope_green);
dbg_add_prop(props, &num, size, "fixed_drop_prob_green",
"Wred fixed drop rate",
&pconf->wred_fixed_drop_prob_green);
dbg_add_prop(props, &num, size, "min_avg_yellow",
"Wred start of the slope area",
&pconf->wred_min_avg_yellow);
dbg_add_prop(props, &num, size, "max_avg_yellow",
"Wred end of the slope area",
&pconf->wred_max_avg_yellow);
dbg_add_prop(props, &num, size, "slope_yellow", "Wred 0-100 scale",
&pconf->wred_slope_yellow);
dbg_add_prop(props, &num, size, "fixed_drop_prob_yellow",
"Wred fixed drop rate",
&pconf->wred_fixed_drop_prob_yellow);
dbg_add_prop(props, &num, size, "min_guaranteed",
"Wred min guaranteed for this queue",
&pconf->wred_min_guaranteed);
dbg_add_prop(props, &num, size, "max_allowed",
"Wred max allowed for this queue",
&pconf->wred_max_allowed);
dbg_add_prop(props, &num, size, "codel_en",
"Enable codel for this queue",
&pconf->codel_en);
dbg_add_prop(props, &num, size, "eir",
"Excess Information Rate",
&pconf->eir);
dbg_add_prop(props, &num, size, "ebs",
"Excess Burst Size",
&pconf->ebs);
return num;
}
static s32 queue_first_prop_cb(struct pp_qos_dev *qdev,
char *field,
u32 val,
void *user_data,
u8 raw_config)
{
/* Make sure first property is the queue id */
if (strncmp(field, "queue", strlen("queue"))) {
QOS_LOG_ERR("First prop (%s) must be queue\n", field);
return -EINVAL;
}
if (raw_config) {
pp_qos_queue_conf_set_default(user_data);
} else if (pp_qos_queue_conf_get(qdev, val, user_data) != 0) {
QOS_LOG_ERR("pp_qos_queue_conf_get failed (id %u)", val);
return -EINVAL;
}
return 0;
}
static s32 queue_done_props_cb(struct pp_qos_dev *qdev,
u32 val, void *user_data)
{
if (pp_qos_queue_set(qdev, val, user_data) != 0) {
QOS_LOG_ERR("pp_qos_queue_set failed (id %u)", val);
return -EINVAL;
}
return 0;
}
void queue(char *cmd_buf, void *data)
{
struct pp_qos_queue_conf conf;
struct dbg_props_cbs cbs = { queue_first_prop_cb, queue_done_props_cb };
u32 id = PP_QOS_INVALID_ID;
u16 num_props;
struct dbg_prop *props;
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)data);
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_queue_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props(cmd_buf, qdev, &cbs, props, num_props, &conf);
kfree(props);
}
void queue_help(struct seq_file *f)
{
struct pp_qos_queue_conf conf;
const char *name = "set queue";
const char *format =
"echo queue=[logical_id] [field]=[value]... > queue";
struct dbg_prop *props;
u16 num_props;
u32 id = PP_QOS_INVALID_ID;
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_queue_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props_help(f, name, format, props, num_props);
kfree(props);
}
PP_DEFINE_DEBUGFS(queue_set, queue_help, queue);
static s32 syncq_first_prop_cb(struct pp_qos_dev *qdev, char *field, u32 val,
void *user_data, u8 raw_config)
{
/* Make sure first property is the queue id */
if (strncmp(field, "queue", strlen("queue"))) {
QOS_LOG_ERR("First prop (%s) must be queue\n", field);
return -EINVAL;
}
if (raw_config) {
pp_qos_queue_conf_set_default(user_data);
} else {
QOS_LOG_ERR("Only raw config supported\n");
return -EINVAL;
}
return 0;
}
static s32 syncq_done_props_cb(struct pp_qos_dev *qdev, u32 val,
void *user_data)
{
u32 rlm;
if (qos_sync_queue_add(qdev, val, &rlm, user_data) != 0) {
QOS_LOG_ERR("qos_sync_queue_add failed (id %u)", val);
return -EINVAL;
}
return 0;
}
void syncq(char *cmd_buf, void *data)
{
struct pp_qos_queue_conf conf;
struct dbg_props_cbs cbs = { syncq_first_prop_cb, syncq_done_props_cb };
u32 id = PP_QOS_INVALID_ID;
u16 num_props;
struct dbg_prop *props;
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)data);
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_queue_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props(cmd_buf, qdev, &cbs, props, num_props, &conf);
kfree(props);
}
void syncq_help(struct seq_file *f)
{
struct pp_qos_queue_conf conf;
const char *name = "add sync queue";
const char *format =
"echo queue=[logical_id] [field]=[value]... > syncq";
struct dbg_prop *props;
u16 num_props;
u32 id = PP_QOS_INVALID_ID;
props = kmalloc_array(DBG_MAX_PROPS, sizeof(struct dbg_prop),
GFP_KERNEL);
if (!props)
return;
num_props = create_queue_props(props, DBG_MAX_PROPS, &id, &conf);
qos_dbg_props_help(f, name, format, props, num_props);
kfree(props);
}
PP_DEFINE_DEBUGFS(syncq_add, syncq_help, syncq);
static int fw_logger_get(void *data, u64 *val)
{
struct pp_qos_dev *qdev = pp_qos_dev_open((unsigned long)data);
if (!capable(CAP_SYS_PACCT))
return -EPERM;
print_fw_log(qdev);
return 0;
}
static int fw_logger_set(void *data, u64 val)
{
struct pp_qos_dev *qdev = pp_qos_dev_open((unsigned long)data);
s32 ret;
if (!capable(CAP_SYS_PACCT))
return -EPERM;
QOS_LOG_INFO("fw_logger_set setting new fw logger level %u\n",
(u32)val);
switch (val) {
case 0:
create_init_logger_cmd(qdev, UC_LOGGER_LEVEL_INFO,
UC_LOGGER_MODE_NONE);
break;
case UC_LOGGER_LEVEL_FATAL:
case UC_LOGGER_LEVEL_ERROR:
case UC_LOGGER_LEVEL_WARNING:
case UC_LOGGER_LEVEL_INFO:
case UC_LOGGER_LEVEL_DEBUG:
case UC_LOGGER_LEVEL_DUMP_REGS:
create_init_logger_cmd(qdev, (int)val,
UC_LOGGER_MODE_WRITE_HOST_MEM);
break;
default:
QOS_LOG_INFO("Not supported fw logger level");
QOS_LOG_INFO("Optional levels:\n");
QOS_LOG_INFO("None: 0\n");
QOS_LOG_INFO("Fatal: %d\n", UC_LOGGER_LEVEL_FATAL);
QOS_LOG_INFO("Error: %d\n", UC_LOGGER_LEVEL_ERROR);
QOS_LOG_INFO("Warning: %d\n", UC_LOGGER_LEVEL_WARNING);
QOS_LOG_INFO("Info: %d\n", UC_LOGGER_LEVEL_INFO);
QOS_LOG_INFO("Debug: %d\n", UC_LOGGER_LEVEL_DEBUG);
QOS_LOG_INFO("Register Dump: %d\n", UC_LOGGER_LEVEL_DUMP_REGS);
break;
}
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret)
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(dbg_fw_logger_fops, fw_logger_get,
fw_logger_set, "%llu\n");
/**
* @brief dump node information
*/
void pp_qos_dbg_node_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
const char *typename;
u32 phy;
u32 id;
u32 i;
static const char *const types[] = { "Port", "Sched", "Queue",
"Unknown" };
static const char *const yesno[] = { "No", "Yes" };
s32 rc;
struct pp_qos_node_info info = {
0,
};
qdev = pp_qos_dev_open((unsigned long)s->private);
QOS_LOG_INFO("node_show called\n");
if (qdev) {
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready !!!!\n");
return;
}
id = g_node;
phy = get_phy_from_id(qdev->mapping, id);
if (unlikely(!QOS_PHY_VALID(phy))) {
seq_printf(s, "Invalid id %u\n", id);
return;
}
rc = qos_get_node_info(qdev, id, &info);
if (rc) {
seq_printf(s, "Could not get info for node %u!!!!\n",
id);
return;
}
if (info.type >= PPV4_QOS_NODE_TYPE_PORT &&
info.type <= PPV4_QOS_NODE_TYPE_QUEUE)
typename = types[info.type];
else
typename = types[3];
seq_printf(s, "%u(%u) - %s: internal node(%s)\n",
id, phy,
typename,
yesno[!!info.is_internal]);
if (info.preds[0].phy != PPV4_QOS_INVALID) {
seq_printf(s, "%u(%u)", id, phy);
for (i = 0; i < 6; ++i) {
if (info.preds[i].phy == PPV4_QOS_INVALID)
break;
seq_printf(s, " ==> %u(%u)",
info.preds[i].id,
info.preds[i].phy);
}
seq_puts(s, "\n");
}
if (info.children[0].phy != PPV4_QOS_INVALID) {
for (i = 0; i < 8; ++i) {
if (info.children[i].phy == PPV4_QOS_INVALID)
break;
seq_printf(s, "%u(%u) ",
info.children[i].id,
info.children[i].phy);
}
seq_puts(s, "\n");
}
if (info.type == PPV4_QOS_NODE_TYPE_QUEUE) {
seq_printf(s, "Physical queue: %u\n",
info.queue_physical_id);
seq_printf(s, "Port: %u\n", info.port);
}
seq_printf(s, "Bandwidth: %u Kbps\n", info.bw_limit);
}
}
PP_DEFINE_DEBUGFS(node_show, pp_qos_dbg_node_show, NULL);
/**
* @brief dump node statistics
*/
void pp_qos_dbg_stat_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
struct pp_qos_queue_stat qstat;
struct pp_qos_port_stat pstat;
struct qos_node *node;
u32 phy;
u32 id;
qdev = pp_qos_dev_open((unsigned long)s->private);
QOS_LOG_INFO("node_show called\n");
if (unlikely(!qdev)) {
seq_puts(s, "qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
id = g_node;
phy = get_phy_from_id(qdev->mapping, id);
if (unlikely(!QOS_PHY_VALID(phy))) {
seq_printf(s, "Invalid id %u\n", id);
return;
}
node = get_node_from_phy(qdev->nodes, phy);
if (unlikely(!node_used(node))) {
seq_printf(s, "Node %u is unused\n", id);
return;
}
seq_printf(s, "%u(%u) - ", id, phy);
if (node_queue(node)) {
seq_puts(s, "Queue\n");
memset(&qstat, 0, sizeof(qstat));
if (pp_qos_queue_stat_get(qdev, id, &qstat) == 0) {
seq_printf(s, "queue_packets_occupancy:%u\n",
qstat.queue_packets_occupancy);
seq_printf(s, "queue_bytes_occupancy:%u\n",
qstat.queue_bytes_occupancy);
seq_printf(s, "total_packets_accepted:%u\n",
qstat.total_packets_accepted);
seq_printf(s, "total_packets_dropped:%u\n",
qstat.total_packets_dropped);
seq_printf(s, "total_packets_red_dropped:%u\n",
qstat.total_packets_red_dropped);
seq_printf(s, "total_bytes_accepted:%llu\n",
qstat.total_bytes_accepted);
seq_printf(s, "total_bytes_dropped:%llu\n",
qstat.total_bytes_dropped);
} else {
seq_puts(s, "Could not obtained statistics\n");
}
} else if (node_port(node)) {
seq_puts(s, "Port\n");
memset(&pstat, 0, sizeof(pstat));
if (pp_qos_port_stat_get(qdev, id, &pstat) == 0) {
seq_printf(s, "total_green_bytes in port's queues:%u\n",
pstat.total_green_bytes);
seq_printf(s, "total_yellow_bytes in port's queues:%u\n",
pstat.total_yellow_bytes);
seq_printf(s, "back pressure status:%u\n",
pstat.debug_back_pressure_status);
seq_printf(s, "Actual packet credit:%u\n",
pstat.debug_actual_packet_credit);
seq_printf(s, "Actual byte credit:%u\n",
pstat.debug_actual_byte_credit);
} else {
seq_puts(s, "Could not obtained statistics\n");
}
} else {
seq_puts(s, "Node is not a queue or port, no statistics\n");
}
}
PP_DEFINE_DEBUGFS(stats, pp_qos_dbg_stat_show, NULL);
/**
* @brief dump queue statistics
*/
void pp_qos_dbg_queues_stats_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)s->private);
if (unlikely(!qdev)) {
seq_puts(s, "qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
pp_stats_show_seq(s, sizeof(struct queue_stats), NUM_OF_NODES,
qos_queues_stats_get, qos_queues_stats_show, qdev);
}
/**
* @brief reset queues statistics
*/
void pp_qos_dbg_queues_stats_reset(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 is_reset;
if ((sscanf(cmd_buf, "%u ", &is_reset) == 1) && (is_reset == 0)) {
qdev = pp_qos_dev_open(PP_QOS_INSTANCE_ID);
if (unlikely(ptr_is_null(qdev)))
return;
pp_qos_stats_reset(qdev);
return;
}
QOS_LOG_INFO("\nqos_queues_stats help:\n");
QOS_LOG_INFO("cat pp/qos_queues_stats : Display statistics\n");
QOS_LOG_INFO("echo 0 > pp/qos_queues_stats : Reset statistics\n");
}
PP_DEFINE_DEBUGFS(queues_stats, pp_qos_dbg_queues_stats_show,
pp_qos_dbg_queues_stats_reset);
/**
* @brief dump queues pps statistics
*/
void pp_qos_dbg_queues_pps_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)s->private);
if (unlikely(!qdev)) {
seq_puts(s, "qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
pp_pps_show_seq(s, sizeof(struct queue_stats), NUM_OF_NODES,
qos_queues_stats_get, qos_queues_stats_diff,
qos_queues_stats_show, qdev);
}
PP_DEFINE_DEBUGFS(queues_pps, pp_qos_dbg_queues_pps_show, NULL);
void pp_qos_dbg_format_tree_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)s->private);
QOS_LOG_INFO("format_tree_show called\n");
if (unlikely(!qdev)) {
QOS_LOG_ERR("qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
QOS_LOG_ERR("Device is not ready\n");
return;
}
qos_dbg_format_tree_show_locked(qdev, true, s);
}
PP_DEFINE_DEBUGFS(format_tree, pp_qos_dbg_format_tree_show, NULL);
/**
* @brief dump complete qos tree
*/
void pp_qos_dbg_tree_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)s->private);
QOS_LOG_INFO("tree_show called\n");
if (unlikely(!qdev)) {
seq_puts(s, "qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
qos_dbg_tree_show_locked(qdev, true, s);
}
PP_DEFINE_DEBUGFS(tree, pp_qos_dbg_tree_show, NULL);
/**
* @brief dump complete qos tree
*/
void pp_qos_dbg_full_tree_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)s->private);
QOS_LOG_INFO("tree_show called\n");
if (unlikely(!qdev)) {
seq_puts(s, "qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
qos_dbg_tree_show_locked(qdev, false, s);
}
PP_DEFINE_DEBUGFS(full_tree, pp_qos_dbg_full_tree_show, NULL);
void pp_qos_dbg_tree_remove_help(struct seq_file *s)
{
seq_puts(s, "echo 0 > destroy_tree\n");
}
void pp_qos_dbg_tree_remove(char *cmd_buf, void *data)
{
u32 node_id, total_occupancy;
struct qos_node *node;
struct pp_qos_dev *qdev;
struct pp_qos_queue_stat stats;
u32 idx;
s32 node_phy;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qos_device_ready(qdev))) {
QOS_LOG_ERR("Device is not ready\n");
return;
}
/* Iterate through all queue nodes */
for (node_phy = NUM_OF_NODES - 1; node_phy >= 0; --node_phy) {
node = get_node_from_phy(qdev->nodes, node_phy);
if (node_queue(node) && !node_syncq(node)) {
node_id = get_id_from_phy(qdev->mapping, node_phy);
pp_qos_queue_block(qdev, node_id);
}
}
memset(&stats, 0, sizeof(stats));
/* Read stats */
for (idx = 0; idx < 100; ++idx) {
total_occupancy = 0;
for (node_phy = 0; node_phy < NUM_OF_NODES; ++node_phy) {
node = get_node_from_phy(qdev->nodes, node_phy);
if (!node_queue(node))
continue;
node_id = get_id_from_phy(qdev->mapping, node_phy);
pp_qos_queue_stat_get(qdev, node_id, &stats);
total_occupancy += stats.queue_packets_occupancy;
if (stats.queue_packets_occupancy) {
QOS_LOG_INFO("Queue %u has %u packet occ\n",
node_id,
stats.queue_packets_occupancy);
}
}
if (total_occupancy != 0)
qos_dbg_qm_stat_show(qdev, NULL);
else
break;
mdelay(10);
}
if (total_occupancy != 0) {
QOS_LOG_ERR("Cannot remove tree while occupancy=%u\n",
total_occupancy);
qos_dbg_qm_stat_show(qdev, NULL);
return;
}
delete_all_syncq(qdev);
/* Iterate through all queue nodes */
for (node_phy = NUM_OF_NODES - 1; node_phy >= 0; --node_phy) {
node = get_node_from_phy(qdev->nodes, node_phy);
if (node_queue(node)) {
node_id = get_id_from_phy(qdev->mapping, node_phy);
pp_qos_queue_remove(qdev, node_id);
}
}
/* Iterate through all port nodes */
for (node_phy = 0; node_phy < NUM_OF_NODES; ++node_phy) {
node = get_node_from_phy(qdev->nodes, node_phy);
if (node_port(node)) {
node_id = get_id_from_phy(qdev->mapping, node_phy);
pp_qos_port_remove(qdev, node_id);
}
}
qos_pools_clean(qdev);
qos_pools_init(qdev, qdev->init_params.max_ports,
qdev->init_params.max_queues,
qdev->init_params.reserved_queues,
qdev->init_params.num_syncqs);
}
PP_DEFINE_DEBUGFS(destroy_tree, pp_qos_dbg_tree_remove_help,
pp_qos_dbg_tree_remove);
/**
* @brief dump complete qos tree
*/
void pp_qos_dbg_qm_stat_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)s->private);
if (unlikely(!qdev)) {
seq_puts(s, "qdev Null\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
qos_dbg_qm_stat_show(qdev, s);
}
/**
* @brief reset queues statistics
*/
void pp_qos_dbg_qm_stat_reset(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 is_reset;
if ((sscanf(cmd_buf, "%u ", &is_reset) == 1) && (is_reset == 0)) {
qdev = pp_qos_dev_open(PP_QOS_INSTANCE_ID);
if (unlikely(ptr_is_null(qdev)))
return;
pp_qos_stats_reset(qdev);
return;
}
QOS_LOG_INFO("\npp/qos0/qstat help:\n");
QOS_LOG_INFO("cat pp/qos0/qstat : Display statistics\n");
QOS_LOG_INFO("echo 0 > pp/qos0/qstat : Reset statistics\n");
}
PP_DEFINE_DEBUGFS(qm_stats, pp_qos_dbg_qm_stat_show, pp_qos_dbg_qm_stat_reset);
/**
* @brief dump general qos info
*/
void pp_qos_dbg_gen_show(struct seq_file *s)
{
struct qos_drv_stats stats;
struct pp_qos_dev *qdev;
u32 major, minor, build;
qdev = pp_qos_dev_open((unsigned long)s->private);
if (unlikely(ptr_is_null(qdev)))
return;
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
if (unlikely(qos_drv_stats_get(qdev, &stats))) {
seq_puts(s, "failed to get driver stats\n");
return;
}
seq_printf(s, "Driver version: %s\n", PPV4_QOS_DRV_VER);
if (pp_qos_get_fw_version(qdev, &major, &minor, &build) == 0) {
seq_printf(s, "FW version:\tmajor(%u) minor(%u) build(%u)\n",
major, minor, build);
} else {
seq_puts(s, "Could not obtain FW version\n");
}
seq_printf(s, "Used nodes:\t%u\n", stats.active_nodes);
seq_printf(s, "Ports:\t\t%u\n", stats.active_ports);
seq_printf(s, "Scheds:\t\t%u\n", stats.active_sched);
seq_printf(s, "Queues:\t\t%u\n", stats.active_queues);
seq_printf(s, "Reserved Queues:\t%u\n", stats.active_reserved_queues);
seq_printf(s, "Internals:\t%u\n", stats.active_internals);
seq_printf(s, "Command queue watermark:\n\tcmd_q: %u\n\tpend_q: %u\n",
(u32)cmd_queue_watermark_get(qdev->drvcmds.cmdq),
(u32)cmd_queue_watermark_get(qdev->drvcmds.pendq));
seq_printf(s, "WRED resources:\t%u\n",
qdev->init_params.wred_total_avail_resources);
}
PP_DEFINE_DEBUGFS(gen_show, pp_qos_dbg_gen_show, NULL);
#ifndef PP_QOS_DISABLE_CMDQ
/**
* @brief send custom command to FW
*/
void dbg_cmd_write(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
unsigned long dst;
u32 cmd_idx;
u32 cmd_type;
u32 cmd_flags;
u32 cmd_len;
u32 cmd_params[5];
qdev = pp_qos_dev_open((unsigned long)data);
dst = qdev->init_params.fwcom.cmdbuf;
if (sscanf(cmd_buf, "%u %u %u",
&cmd_type, &cmd_flags, &cmd_len) != 3) {
QOS_LOG_ERR("sscanf err - minimum 3 params\n");
return;
}
PP_REG_WR32_INC(dst, cmd_type);
QOS_LOG_INFO("Wrote 0x%x\n", cmd_type);
PP_REG_WR32_INC(dst, cmd_flags);
QOS_LOG_INFO("Wrote 0x%x\n", cmd_flags);
PP_REG_WR32_INC(dst, cmd_len);
QOS_LOG_INFO("Wrote 0x%x\n", cmd_len);
switch (cmd_len - 3) {
case 0:
break;
case 1:
if (sscanf(cmd_buf, "%u %u %u %u",
&cmd_type,
&cmd_flags,
&cmd_len,
&cmd_params[0]) != 4) {
QOS_LOG_ERR("sscanf err\n");
return;
}
break;
case 2:
if (sscanf(cmd_buf, "%u %u %u %u %u",
&cmd_type,
&cmd_flags,
&cmd_len,
&cmd_params[0],
&cmd_params[1]) != 5) {
QOS_LOG_ERR("sscanf err\n");
return;
}
break;
case 3:
if (sscanf(cmd_buf, "%u %u %u %u %u %u",
&cmd_type,
&cmd_flags,
&cmd_len,
&cmd_params[0],
&cmd_params[1],
&cmd_params[2]) != 6) {
QOS_LOG_ERR("sscanf err\n");
return;
}
break;
case 4:
if (sscanf(cmd_buf, "%u %u %u %u %u %u %u",
&cmd_type,
&cmd_flags,
&cmd_len,
&cmd_params[0],
&cmd_params[1],
&cmd_params[2],
&cmd_params[3]) != 7) {
QOS_LOG_ERR("sscanf err\n");
return;
}
break;
case 5:
if (sscanf(cmd_buf, "%u %u %u %u %u %u %u %u",
&cmd_type,
&cmd_flags,
&cmd_len,
&cmd_params[0],
&cmd_params[1],
&cmd_params[2],
&cmd_params[3],
&cmd_params[4]) != 8) {
QOS_LOG_ERR("sscanf err\n");
return;
}
break;
default:
QOS_LOG_INFO("len %d is not supported (range is 3 - 8)\n",
cmd_len);
return;
}
if ((cmd_len - 3) > 5) {
QOS_LOG_INFO("len %d is not supported (range is 3 - 8)\n",
cmd_len);
return;
}
for (cmd_idx = 0; cmd_idx < cmd_len - 3; cmd_idx++) {
PP_REG_WR32_INC(dst, cmd_params[cmd_idx]);
QOS_LOG_INFO("Wrote 0x%x\n", cmd_params[cmd_idx]);
}
signal_uc(qdev);
}
PP_DEFINE_DEBUGFS(cmd, NULL, dbg_cmd_write);
#endif
/**
* @brief advanced debug features
*/
void ctrl_set(char *cmd_buf, void *data)
{
s32 ret;
u32 choise;
struct pp_qos_dev *qdev;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (kstrtou32(cmd_buf, 10, &choise)) {
QOS_LOG_ERR("kstrtou32 err\n");
goto ctrl_set_done;
}
switch (choise) {
case 1:
QOS_LOG_INFO("loading FW\n");
ret = load_firmware(qdev, FIRMWARE_FILE);
if (ret) {
QOS_LOG_ERR("load_firmware failed (%d)\n", ret);
goto ctrl_set_done;
}
break;
case 2:
#ifndef PP_QOS_DISABLE_CMDQ
QOS_LOG_INFO("Sending init logger cmd (Info)\n");
create_init_logger_cmd(qdev,
UC_LOGGER_LEVEL_INFO,
UC_LOGGER_MODE_WRITE_HOST_MEM);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret)
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
#endif
break;
case 3:
#ifndef PP_QOS_DISABLE_CMDQ
QOS_LOG_INFO("Sending init logger cmd (Debug)\n");
create_init_logger_cmd(qdev,
UC_LOGGER_LEVEL_DEBUG,
UC_LOGGER_MODE_WRITE_HOST_MEM);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret)
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
#endif
break;
case 4:
#ifndef PP_QOS_DISABLE_CMDQ
QOS_LOG_INFO("Sending init logger cmd (Off)\n");
create_init_logger_cmd(qdev,
UC_LOGGER_LEVEL_INFO,
UC_LOGGER_MODE_NONE);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret)
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
#endif
break;
case 5:
QOS_LOG_INFO("printing logger\n");
print_fw_log(qdev);
break;
case 6:
QOS_LOG_INFO("Sending init qos cmd\n");
create_init_qos_cmd(qdev);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret)
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
break;
case 7:
check_sync_with_fw(qdev, true);
break;
default:
QOS_LOG_INFO("unknown option\n");
break;
}
ctrl_set_done:
return;
}
void ctrl_set_help(struct seq_file *f)
{
seq_puts(f, "1: Load Firmware\n");
seq_puts(f, "2: Set FW Logger (Info)\n");
seq_puts(f, "3: Set FW Logger (Debug)\n");
seq_puts(f, "4: Set FW Logger (Off)\n");
seq_puts(f, "5: Print FW Logger\n");
seq_puts(f, "6: Init QoS\n");
seq_puts(f, "7: Check Sync With FW\n");
}
PP_DEFINE_DEBUGFS(ctrl, ctrl_set_help, ctrl_set);
/**
* @brief read hw table entry (will be dumped to logger)
*/
void read_table_entry(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 physical_id;
u32 table_id;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (sscanf(cmd_buf, "%u %u",
&physical_id, &table_id) != 2) {
QOS_LOG_ERR("sscanf err\n");
goto read_table_entry_done;
}
#ifndef PP_QOS_DISABLE_CMDQ
QOS_LOG_INFO("Sending read table entry (%u) from phy (%u)\n",
table_id, physical_id);
create_get_table_entry_cmd(qdev, physical_id,
qdev->hwconf.fw_stats_ddr_phys,
table_id);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret)
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
#endif
read_table_entry_done:
return;
}
void read_table_entry_help(struct seq_file *f)
{
seq_puts(f, " \n");
}
PP_DEFINE_DEBUGFS(table_entry, read_table_entry_help, read_table_entry);
/**
* @brief push descriptor to qos queue
*/
void queue_push(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
unsigned long addr;
u32 logical_id, size, color, policy, pool, port, data_off;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (sscanf(cmd_buf, "%u %u %u %u %u %u %u %lx",
&logical_id,
&size,
&pool,
&policy,
&color,
&port,
&data_off,
&addr) != 8) {
QOS_LOG_ERR("sscanf err\n");
goto queue_push_done;
}
qos_descriptor_push(qdev, logical_id, size, pool, policy, color, port,
data_off, addr);
queue_push_done:
return;
}
void queue_push_help(struct seq_file *f)
{
seq_puts(f, " "
" \n");
}
PP_DEFINE_DEBUGFS(queue_push, queue_push_help, queue_push);
/**
* @brief pop descriptor from qos queue
*/
void queue_pop(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
#ifdef CONFIG_PPV4_LGM
struct pp_qos_desc desc;
struct pp_qos_hw_desc hw_desc;
#endif
u32 descriptor[4];
u32 logical_id;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (sscanf(cmd_buf, "%u",
&logical_id) != 1) {
QOS_LOG_ERR("sscanf err\n");
return;
}
qos_descriptor_pop(qdev, logical_id, descriptor);
QOS_LOG_INFO("descriptor[0]: %#x\n", descriptor[0]);
QOS_LOG_INFO("descriptor[1]: %#x\n", descriptor[1]);
QOS_LOG_INFO("descriptor[2]: %#x\n", descriptor[2]);
QOS_LOG_INFO("descriptor[3]: %#x\n", descriptor[3]);
#ifdef CONFIG_PPV4_LGM
hw_desc.word[0] = descriptor[0];
hw_desc.word[1] = descriptor[1];
hw_desc.word[2] = descriptor[2];
hw_desc.word[3] = descriptor[3];
pp_qos_desc_decode(&desc, &hw_desc);
pp_qos_desc_dump(&desc);
#endif
}
void queue_pop_help(struct seq_file *f)
{
seq_puts(f, "\n");
}
PP_DEFINE_DEBUGFS(queue_pop, queue_pop_help, queue_pop);
/**
* @brief mcmda copy
*/
void mcdma_copy(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 src, dst, size;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (sscanf(cmd_buf, "%x %x %u",
&src, &dst, &size) != 3) {
QOS_LOG_ERR("sscanf err\n");
goto mcdma_copy_done;
}
create_mcdma_copy_cmd(qdev, src, dst, size);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (unlikely(ret))
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
mcdma_copy_done:
return;
}
void mcdma_copy_help(struct seq_file *f)
{
seq_puts(f, " "
"(addresses should be ddr contiguous)\n");
}
PP_DEFINE_DEBUGFS(mcdma_copy, mcdma_copy_help, mcdma_copy);
/**
* @brief wsp helper, set enhanced WSP parameters
*/
void ewsp_helper(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 enable, timeout, threshold;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (sscanf(cmd_buf, "%u %u %u",
&enable, &timeout, &threshold) != 3) {
QOS_LOG_ERR("sscanf err\n");
goto ewsp_helper_done;
}
if (timeout > 255) {
QOS_LOG_ERR("Timeout value is up to 255 uS\n");
goto ewsp_helper_done;
}
qdev->ewsp_cfg.enable = enable;
qdev->ewsp_cfg.timeout_microseconds = timeout;
qdev->ewsp_cfg.byte_threshold = threshold;
create_set_wsp_helper_cmd(qdev, enable, timeout, threshold);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (unlikely(ret))
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
ewsp_helper_done:
return;
}
void ewsp_helper_help(struct seq_file *f)
{
seq_puts(f, " \n");
}
PP_DEFINE_DEBUGFS(ewsp_helper, ewsp_helper_help, ewsp_helper);
/**
* @brief wsp helper reset, reset Enhanced WSP statistics
*/
void ewsp_helper_reset(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
struct wsp_stats_t stats;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
create_get_wsp_helper_stats_cmd(qdev, qdev->hwconf.fw_stats_ddr_phys,
true, &stats);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (unlikely(ret))
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
return;
}
/**
* @brief wsp helper stats, dump Enhanced WSP statistics
*/
void ewsp_helper_stats(struct seq_file *f)
{
struct pp_qos_dev *qdev;
struct wsp_stats_t stats;
s32 ret;
qdev = pp_qos_dev_open(PP_QOS_INSTANCE_ID);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
create_get_wsp_helper_stats_cmd(qdev, qdev->hwconf.fw_stats_ddr_phys,
false, &stats);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (unlikely(ret))
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
seq_puts(f, "Wsp helper cfg:\n");
seq_puts(f, "===========================\n");
seq_printf(f, "enable: %u\n", qdev->ewsp_cfg.enable);
seq_printf(f, "timeout_microseconds: %u\n",
qdev->ewsp_cfg.timeout_microseconds);
seq_printf(f, "byte_threshold: %u\n", qdev->ewsp_cfg.byte_threshold);
seq_printf(f, "num_queues: %u\n", qdev->ewsp_cfg.num_queues);
seq_puts(f, "\n--------------------------\n");
seq_puts(f, "Wsp helper stats:\n");
seq_puts(f, "===========================\n");
seq_printf(f, "num_timeouts: %u\n", stats.num_timeouts);
seq_printf(f, "num_toggles: %u\n", stats.num_toggles_wm);
seq_printf(f, "num_iterations: %u\n", stats.num_iterations_wm);
seq_printf(f, "num_queues: %u\n", stats.num_queues);
}
PP_DEFINE_DEBUGFS(ewsp_helper_stats, ewsp_helper_stats, ewsp_helper_reset);
/**
* @brief Set Qos modules registers write logs on/off
*/
static s32 mod_reg_log_en_set(void *data, u64 val)
{
struct pp_qos_dev *qdev;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (ptr_is_null(qdev))
return -EINVAL;
create_mod_log_bmap_set_cmd(qdev, (u32)val);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (unlikely(ret))
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
return ret;
}
/**
* @brief Get Qos modules registers write logs status
*/
static s32 mod_reg_log_en_get(void *data, u64 *val)
{
u32 bmap;
struct pp_qos_dev *qdev;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (ptr_is_null(qdev))
return -EINVAL;
create_mod_log_bmap_get_cmd(qdev, &bmap);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (unlikely(ret))
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
*val = (u64)bmap;
return ret;
}
PP_DEFINE_DBGFS_ATT_FMT(mod_reg_log_en, mod_reg_log_en_get, mod_reg_log_en_set,
"%llx\n");
/**
* @brief add sf
*/
void sf_add(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
struct pp_qos_aqm_sf_config sf_cfg;
u32 sf_id;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (sscanf(cmd_buf, "%u %u",
&sf_id, &sf_cfg.num_queues) != 2) {
QOS_LOG_ERR("sscanf err - 2 params\n");
goto sf_add_done;
}
if (unlikely(sf_id >= PP_QOS_AQM_MAX_SERVICE_FLOWS)) {
QOS_LOG_ERR("SF %u is invalid (max %u)\n",
sf_id, PP_QOS_AQM_MAX_SERVICE_FLOWS);
goto sf_add_done;
}
if (!sf_cfg.num_queues || sf_cfg.num_queues > 3) {
QOS_LOG_ERR("Num queues 1 - 3\n");
goto sf_add_done;
}
switch (sf_cfg.num_queues) {
case 1:
if (sscanf(cmd_buf, "%u %u %u",
&sf_id,
&sf_cfg.num_queues,
&sf_cfg.queue_id[0]) != 3) {
QOS_LOG_ERR("sscanf err - 3 params\n");
goto sf_add_done;
}
break;
case 2:
if (sscanf(cmd_buf, "%u %u %u %u",
&sf_id,
&sf_cfg.num_queues,
&sf_cfg.queue_id[0],
&sf_cfg.queue_id[1]) != 4) {
QOS_LOG_ERR("sscanf err - 4 params\n");
goto sf_add_done;
}
break;
case 3:
if (sscanf(cmd_buf, "%u %u %u %u %u",
&sf_id,
&sf_cfg.num_queues,
&sf_cfg.queue_id[0],
&sf_cfg.queue_id[1],
&sf_cfg.queue_id[2]) != 5) {
QOS_LOG_ERR("sscanf err - 5 params\n");
goto sf_add_done;
}
break;
default:
QOS_LOG_ERR("%u Queues is not supported for deubg\n",
sf_cfg.num_queues);
goto sf_add_done;
break;
}
QOS_LOG_INFO("setting sf %u with %u queues\n",
sf_id, sf_cfg.num_queues);
sf_cfg.latency_target_ms = AQM_LATENCY_TARGET;
sf_cfg.peak_rate = AQM_PEAK_RATE;
sf_cfg.msr = AQM_MSR;
sf_cfg.buffer_size = AQM_BUFFER_SIZE;
ret = pp_qos_aqm_sf_set(qdev, sf_id, &sf_cfg);
if (unlikely(ret)) {
QOS_LOG_ERR("pp_qos_aqm_sf_set %d failed\n", sf_id);
goto sf_add_done;
}
sf_add_done:
return;
}
void sf_add_help(struct seq_file *f)
{
seq_puts(f, " "
" \n");
}
PP_DEFINE_DEBUGFS(sf_add, sf_add_help, sf_add);
/**
* @brief del sf
*/
void sf_del(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
u32 sf_id;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (kstrtou32(cmd_buf, 10, &sf_id)) {
QOS_LOG_ERR("kstrtou32 err\n");
goto sf_del_done;
}
if (unlikely(sf_id >= PP_QOS_AQM_MAX_SERVICE_FLOWS)) {
QOS_LOG_ERR("SF %u is invalid (max %u)\n",
sf_id, PP_QOS_AQM_MAX_SERVICE_FLOWS);
goto sf_del_done;
}
QOS_LOG_INFO("Removing sf %u\n", sf_id);
ret = pp_qos_aqm_sf_remove(qdev, sf_id);
if (unlikely(ret)) {
QOS_LOG_ERR("pp_qos_aqm_sf_remove %d failed\n", sf_id);
goto sf_del_done;
}
sf_del_done:
return;
}
void sf_del_help(struct seq_file *f)
{
seq_puts(f, "\n");
}
PP_DEFINE_DEBUGFS(sf_del, sf_del_help, sf_del);
/**
* @brief configure CoDel
*/
void codel_cfg(char *cmd_buf, void *data)
{
struct pp_qos_dev *qdev;
struct pp_qos_codel_cfg cfg;
s32 ret;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
QOS_LOG_ERR("Device is not ready\n");
return;
}
if (sscanf(cmd_buf, "%u %u %u",
&cfg.consecutive_drop_limit,
&cfg.target_delay_msec,
&cfg.interval_time_msec) != 3) {
QOS_LOG_ERR("sscanf err - 3 params\n");
goto done;
}
ret = pp_qos_codel_cfg_set(qdev, &cfg);
if (unlikely(ret)) {
QOS_LOG_ERR("pp_qos_codel_cfg_set failed\n");
goto done;
}
done:
return;
}
void codel_cfg_help(struct seq_file *f)
{
seq_puts(f, " "
" \n");
}
PP_DEFINE_DEBUGFS(codel_cfg, codel_cfg_help, codel_cfg);
/**
* @brief configure CoDel
*/
void codel_stats(char *cmd_buf, void *data)
{
const struct qos_node *node;
struct pp_qos_dev *qdev;
struct pp_qos_codel_stat cstats;
u32 node_id, node_phy;
qdev = pp_qos_dev_open((unsigned long)data);
if (unlikely(!qdev)) {
QOS_LOG_ERR("Error: qdev NULL\n");
return;
}
if (unlikely(!qos_device_ready(qdev))) {
QOS_LOG_ERR("Device is not ready\n");
return;
}
memset(&cstats, 0, sizeof(cstats));
if (sscanf(cmd_buf, "%u %d", &node_id, &cstats.reset) != 2) {
QOS_LOG_ERR("sscanf err - 2 params\n");
goto done;
}
node_phy = get_phy_from_id(qdev->mapping, node_id);
if (unlikely(!QOS_PHY_VALID(node_phy))) {
QOS_LOG_ERR("Invalid id %u\n", node_id);
return;
}
node = get_node_from_phy(qdev->nodes, node_phy);
if (!node_queue(node)) {
QOS_LOG_ERR("not a queue node, node %u\n", node_id);
return;
}
if (unlikely(pp_qos_codel_queue_stat_get(qdev, node_id, &cstats))) {
QOS_LOG_ERR("codel_stat_get(%u) failed\n", node_id);
return;
}
QOS_LOG_INFO(" Dropped packets : %u\n",
cstats.packets_dropped);
QOS_LOG_INFO(" Dropped bytes : %u\n",
cstats.bytes_dropped);
QOS_LOG_INFO(" Max sojourn time [mSec] : %u\n",
cstats.max_sojourn_time);
QOS_LOG_INFO(" Min sojourn time [mSec] : %u\n",
cstats.min_sojourn_time);
QOS_LOG_INFO(" Total sojourn time [mSec] : %u\n",
cstats.total_packets);
QOS_LOG_INFO(" Max packet size : %u\n",
cstats.max_packet_size);
QOS_LOG_INFO(" Total packets : %u\n",
cstats.total_packets);
done:
return;
}
void codel_stats_help(struct seq_file *f)
{
seq_puts(f, " <1=reset stats>\n");
}
PP_DEFINE_DEBUGFS(codel_stats, codel_stats_help, codel_stats);
static int __gnode_set(void *data, u64 val)
{
g_node = (u16)val;
return 0;
}
static int __gnode_get(void *data, u64 *val)
{
*val = (u64)g_node;
return 0;
}
PP_DEFINE_DBGFS_ATT(gnode, __gnode_get, __gnode_set);
static int __g_raw_config_set(void *data, u64 val)
{
g_raw_config = (u8)val;
return 0;
}
static int __g_raw_config_get(void *data, u64 *val)
{
*val = (u64)g_raw_config;
return 0;
}
PP_DEFINE_DBGFS_ATT(raw_config, __g_raw_config_get, __g_raw_config_set);
void pp_qos_dbg_quanta_get(struct seq_file *s)
{
struct pp_qos_dev *qdev;
u32 quanta;
qdev = pp_qos_dev_open((unsigned long)s->private);
if (unlikely(ptr_is_null(qdev)))
return;
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
if (pp_qos_get_quanta(qdev, &quanta) == 0)
seq_printf(s, "quanta: %u\n", quanta);
else
seq_puts(s, "pp_qos_get_quanta failed\n");
}
PP_DEFINE_DEBUGFS(quanta, pp_qos_dbg_quanta_get, NULL);
void pp_qos_dbg_system_stats_show(struct seq_file *s)
{
struct pp_qos_dev *qdev;
struct system_stats_s sys_stats;
s32 ret = 0;
qdev = pp_qos_dev_open((unsigned long)s->private);
if (unlikely(ptr_is_null(qdev)))
return;
if (unlikely(!qos_device_ready(qdev))) {
seq_puts(s, "Device is not ready\n");
return;
}
create_get_sys_info_cmd(qdev, qdev->hwconf.fw_stats_ddr_phys,
&sys_stats, false);
update_cmd_id(&qdev->drvcmds);
ret = transmit_cmds(qdev);
if (ret) {
QOS_LOG_ERR("transmit_cmds failed (%d)\n", ret);
return;
}
seq_puts(s, "System stats:\n");
seq_puts(s, "=============\n");
seq_printf(s, "tscd_infinite_loop_error_occurred: %u\n",
sys_stats.tscd_infinite_loop_error_occurred);
seq_printf(s, "tscd_bwl_update_error_occurred: %u\n",
sys_stats.tscd_bwl_update_error_occurred);
seq_printf(s, "tscd_quanta: %u\n", sys_stats.tscd_quanta);
#if IS_ENABLED(CONFIG_PPV4_LGM)
if (pp_silicon_step_get() == PP_SSTEP_B) {
seq_printf(s, "wred_fake_pops: %u\n", sys_stats.wred_fake_pops);
seq_printf(s, "wred_pop_underflow_count: %u\n",
sys_stats.wred_pop_underflow_count);
seq_printf(s, "wred_pop_underflow_sum: %u\n",
sys_stats.wred_pop_underflow_sum);
seq_printf(s, "wred_last_push_address_high: %#x\n",
sys_stats.wred_last_push_address_high);
seq_printf(s, "wred_last_push_address_low: %#x\n",
sys_stats.wred_last_push_address_low);
seq_printf(s, "wred_last_push_drop: %u\n",
sys_stats.wred_last_push_drop);
seq_printf(s, "wred_last_push_info_q_id: %u\n",
sys_stats.wred_last_push_info_q_id);
seq_printf(s, "wred_last_push_info_color: %u\n",
sys_stats.wred_last_push_info_color);
seq_printf(s, "wred_last_push_info_pkt_size: %u\n",
sys_stats.wred_last_push_info_pkt_size);
seq_printf(s, "wred_last_pop_info_q_id: %u\n",
sys_stats.wred_last_pop_info_q_id);
seq_printf(s, "wred_last_pop_info_fake: %u\n",
sys_stats.wred_last_pop_info_fake);
seq_printf(s, "wred_last_pop_info_color: %u\n",
sys_stats.wred_last_pop_info_color);
seq_printf(s, "wred_last_pop_info_pkt_size: %u\n",
sys_stats.wred_last_pop_info_pkt_size);
}
#endif
seq_printf(s, "txmgr_bp_status_ports_0_31: %#x\n",
sys_stats.txmgr_bp_status_ports_0_31);
seq_printf(s, "txmgr_bp_status_ports_32_63: %#x\n",
sys_stats.txmgr_bp_status_ports_32_63);
seq_printf(s, "txmgr_bp_status_ports_64_95: %#x\n",
sys_stats.txmgr_bp_status_ports_64_95);
seq_printf(s, "txmgr_bp_status_ports_96_127: %#x\n",
sys_stats.txmgr_bp_status_ports_96_127);
#if IS_ENABLED(CONFIG_PPV4_LGM)
seq_printf(s, "txmgr_bp_status_ports_128_159: %#x\n",
sys_stats.txmgr_bp_status_ports_128_159);
seq_printf(s, "txmgr_bp_status_ports_160_191: %#x\n",
sys_stats.txmgr_bp_status_ports_160_191);
seq_printf(s, "txmgr_bp_status_ports_192_223: %#x\n",
sys_stats.txmgr_bp_status_ports_192_223);
seq_printf(s, "txmgr_bp_status_ports_224_255: %#x\n",
sys_stats.txmgr_bp_status_ports_224_255);
#endif
}
PP_DEFINE_DEBUGFS(system_stats, pp_qos_dbg_system_stats_show, NULL);
static struct debugfs_file qos_debugfs_files[] = {
{ "node", &PP_DEBUGFS_FOPS(gnode) },
{ "raw_config", &PP_DEBUGFS_FOPS(raw_config) },
{ "nodeinfo", &PP_DEBUGFS_FOPS(node_show) },
{ "read_table_entry", &PP_DEBUGFS_FOPS(table_entry) },
{ "queues_stats", &PP_DEBUGFS_FOPS(queues_stats) },
{ "qstat", &PP_DEBUGFS_FOPS(qm_stats) },
{ "queues_pps", &PP_DEBUGFS_FOPS(queues_pps) },
{ "stat", &PP_DEBUGFS_FOPS(stats) },
{ "tree", &PP_DEBUGFS_FOPS(tree) },
{ "format_tree", &PP_DEBUGFS_FOPS(format_tree) },
{ "full_tree", &PP_DEBUGFS_FOPS(full_tree) },
{ "destroy_tree", &PP_DEBUGFS_FOPS(destroy_tree) },
{ "fw_logger", &dbg_fw_logger_fops },
{ "ctrl", &PP_DEBUGFS_FOPS(ctrl) },
{ "geninfo", &PP_DEBUGFS_FOPS(gen_show) },
{ "sf_add", &PP_DEBUGFS_FOPS(sf_add) },
{ "sf_del", &PP_DEBUGFS_FOPS(sf_del) },
{ "codel_cfg", &PP_DEBUGFS_FOPS(codel_cfg) },
{ "codel_stats", &PP_DEBUGFS_FOPS(codel_stats) },
{ "mcdma_copy", &PP_DEBUGFS_FOPS(mcdma_copy) },
{ "queue", &PP_DEBUGFS_FOPS(queue_set) },
{ "syncq", &PP_DEBUGFS_FOPS(syncq_add) },
{ "sched", &PP_DEBUGFS_FOPS(sched_set) },
{ "shared_grp_add", &PP_DEBUGFS_FOPS(shared_grp_add) },
{ "shared_grp_rem", &PP_DEBUGFS_FOPS(shared_grp_del) },
{ "port", &PP_DEBUGFS_FOPS(port_set) },
{ "push_desc", &PP_DEBUGFS_FOPS(queue_push) },
{ "pop_desc", &PP_DEBUGFS_FOPS(queue_pop) },
{ "allocate", &PP_DEBUGFS_FOPS(allocate) },
{ "remove", &PP_DEBUGFS_FOPS(remove) },
{ "flush", &PP_DEBUGFS_FOPS(flush) },
{ "quanta", &PP_DEBUGFS_FOPS(quanta) },
{ "mod_reg_log_en", &PP_DEBUGFS_FOPS(mod_reg_log_en) },
{ "ewsp_helper", &PP_DEBUGFS_FOPS(ewsp_helper) },
{ "ewsp_helper_stats", &PP_DEBUGFS_FOPS(ewsp_helper_stats) },
{ "system_stats", &PP_DEBUGFS_FOPS(system_stats) },
#ifndef PP_QOS_DISABLE_CMDQ
{ "cmd", &PP_DEBUGFS_FOPS(cmd) },
#endif
};
static struct debugfs_file qos_root_debugfs_files[] = {
{ "qos_queues_stats", &PP_DEBUGFS_FOPS(queues_stats) },
{ "qos_queues_pps", &PP_DEBUGFS_FOPS(queues_pps) },
{ "qos_tree", &PP_DEBUGFS_FOPS(tree) },
};
#define MAX_DIR_NAME 11
s32 qos_dbg_module_init(struct pp_qos_dev *qdev)
{
char dirname[MAX_DIR_NAME];
s32 ret = 0;
QOS_LOG_DEBUG("\n");
snprintf(dirname, MAX_DIR_NAME, "qos%d", qdev->id);
ret = pp_debugfs_create(qdev->init_params.dbgfs, dirname, &qdev->dbgfs,
qos_debugfs_files,
ARRAY_SIZE(qos_debugfs_files),
(void *)(unsigned long)qdev->id);
if (unlikely(ret))
return ret;
/* create subset in the root directory */
ret = pp_debugfs_create(qdev->init_params.dbgfs, NULL, NULL,
qos_root_debugfs_files,
ARRAY_SIZE(qos_root_debugfs_files),
(void *)(unsigned long)qdev->id);
return ret;
}
void qos_dbg_module_clean(struct pp_qos_dev *qdev)
{
QOS_LOG_DEBUG("qdev->dbgfs %p\n", qdev->dbgfs);
debugfs_remove_recursive(qdev->dbgfs);
}