[PATCH net-next 04/20] net: ethernet: qualcomm: Add PPE buffer manager configuration

From: Luo Jie
Date: Wed Jan 10 2024 - 06:45:37 EST


The BM config controls the flow control or pause frame generated on the
physical port. The number of hardware buffers configured for the port
influence the function of the flow control for that port.

In addition, the PPE register access functions are added.

Signed-off-by: Luo Jie <quic_luoj@xxxxxxxxxxx>
---
drivers/net/ethernet/qualcomm/ppe/ppe.c | 164 +++++++++++++++++++
drivers/net/ethernet/qualcomm/ppe/ppe.h | 6 +
drivers/net/ethernet/qualcomm/ppe/ppe_regs.h | 49 ++++++
3 files changed, 219 insertions(+)
create mode 100644 drivers/net/ethernet/qualcomm/ppe/ppe_regs.h

diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.c b/drivers/net/ethernet/qualcomm/ppe/ppe.c
index 23f9de105062..94fa13dd17da 100644
--- a/drivers/net/ethernet/qualcomm/ppe/ppe.c
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/soc/qcom/ppe.h>
#include "ppe.h"
+#include "ppe_regs.h"

static const char * const ppe_clock_name[PPE_CLK_MAX] = {
"cmn_ahb",
@@ -111,6 +112,49 @@ static const char * const ppe_reset_name[PPE_RST_MAX] = {
"nss_port6_mac",
};

+int ppe_write(struct ppe_device *ppe_dev, u32 reg, unsigned int val)
+{
+ return regmap_write(ppe_dev->regmap, reg, val);
+}
+
+int ppe_read(struct ppe_device *ppe_dev, u32 reg, unsigned int *val)
+{
+ return regmap_read(ppe_dev->regmap, reg, val);
+}
+
+int ppe_mask(struct ppe_device *ppe_dev, u32 reg, u32 mask, unsigned int set)
+{
+ return regmap_update_bits(ppe_dev->regmap, reg, mask, set);
+}
+
+int ppe_write_tbl(struct ppe_device *ppe_dev, u32 reg,
+ const unsigned int *val, int cnt)
+{
+ int i, ret;
+
+ for (i = 0; i < cnt / 4; i++) {
+ ret = ppe_write(ppe_dev, reg + i * 4, val[i]);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+int ppe_read_tbl(struct ppe_device *ppe_dev, u32 reg,
+ unsigned int *val, int cnt)
+{
+ int i, ret;
+
+ for (i = 0; i < cnt / 4; i++) {
+ ret = ppe_read(ppe_dev, reg + i * 4, &val[i]);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
int ppe_type_get(struct ppe_device *ppe_dev)
{
struct ppe_data *ppe_dev_priv = ppe_dev->ppe_priv;
@@ -323,6 +367,120 @@ static struct ppe_data *ppe_data_init(struct platform_device *pdev)
return ppe_dev_priv;
}

+static int of_parse_ppe_bm(struct ppe_device *ppe_dev,
+ struct device_node *ppe_node)
+{
+ union ppe_bm_port_fc_cfg_u fc_cfg;
+ struct device_node *bm_node;
+ int ret, cnt;
+ u32 *cfg, reg_val;
+
+ bm_node = of_get_child_by_name(ppe_node, "buffer-management-config");
+ if (!bm_node)
+ return dev_err_probe(ppe_dev->dev, -ENODEV,
+ "Fail to get buffer-management-config\n");
+
+ cnt = of_property_count_u32_elems(bm_node, "qcom,group-config");
+ if (cnt < 0)
+ return dev_err_probe(ppe_dev->dev, cnt,
+ "Fail to qcom,group-config\n");
+
+ cfg = kmalloc_array(cnt, sizeof(*cfg), GFP_KERNEL | __GFP_ZERO);
+ if (!cfg)
+ return -ENOMEM;
+
+ ret = of_property_read_u32_array(bm_node, "qcom,group-config", cfg, cnt);
+ if (ret) {
+ dev_err(ppe_dev->dev, "Fail to get qcom,group-config %d\n", ret);
+ goto parse_bm_err;
+ }
+
+ /* Parse BM group configuration,
+ * the dts propert: qcom,group-config = <group group_buf>;
+ *
+ * There are 3 kinds of buffer types, guaranteed buffer(port based),
+ * shared buffer(group based) and react buffer(cache in-flight packets).
+ *
+ * Maximum 4 groups supported by PPE.
+ */
+ ret = 0;
+ while ((cnt - ret) / 2) {
+ if (cfg[ret] < PPE_BM_SHARED_GROUP_CFG_NUM) {
+ reg_val = FIELD_PREP(PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT, cfg[ret + 1]);
+
+ ppe_write(ppe_dev, PPE_BM_SHARED_GROUP_CFG +
+ PPE_BM_SHARED_GROUP_CFG_INC * cfg[ret], reg_val);
+ }
+ ret += 2;
+ }
+
+ cnt = of_property_count_u32_elems(bm_node, "qcom,port-config");
+ if (cnt < 0) {
+ dev_err(ppe_dev->dev, "Fail to get qcom,port-config %d\n", cnt);
+ goto parse_bm_err;
+ }
+
+ cfg = krealloc_array(cfg, cnt, sizeof(*cfg), GFP_KERNEL | __GFP_ZERO);
+ if (!cfg) {
+ ret = -ENOMEM;
+ goto parse_bm_err;
+ }
+
+ ret = of_property_read_u32_array(bm_node, "qcom,port-config", cfg, cnt);
+ if (ret) {
+ dev_err(ppe_dev->dev, "Fail to get qcom,port-config %d\n", ret);
+ goto parse_bm_err;
+ }
+
+ /* Parse BM port configuration,
+ * the dts property: qcom,port-config = <group port prealloc react ceil
+ * weight res_off res_ceil dynamic>;
+ *
+ * The port based buffer is assigned to the group ID, which is the
+ * buffer dedicated to BM port, and the threshold to generate the
+ * pause frame, the threshold can be configured as the static value
+ * or dynamically adjusted according to the remain buffer.
+ */
+ ret = 0;
+ while ((cnt - ret) / 9) {
+ if (cfg[ret + 1] < PPE_BM_PORT_FC_MODE_NUM) {
+ memset(&fc_cfg, 0, sizeof(fc_cfg));
+
+ fc_cfg.bf.pre_alloc = cfg[ret + 2];
+ fc_cfg.bf.react_limit = cfg[ret + 3];
+ fc_cfg.bf.shared_ceiling_0 = cfg[ret + 4] & 0x7;
+ fc_cfg.bf.shared_ceiling_1 = cfg[ret + 4] >> 3;
+ fc_cfg.bf.shared_weight = cfg[ret + 5];
+ fc_cfg.bf.resum_offset = cfg[ret + 6];
+ fc_cfg.bf.resum_floor_th = cfg[ret + 7];
+ fc_cfg.bf.shared_dynamic = cfg[ret + 8];
+ ppe_write_tbl(ppe_dev, PPE_BM_PORT_FC_CFG +
+ PPE_BM_PORT_FC_CFG_INC * cfg[ret + 1],
+ fc_cfg.val, sizeof(fc_cfg.val));
+
+ reg_val = FIELD_PREP(PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID, cfg[ret]);
+ ppe_write(ppe_dev, PPE_BM_PORT_GROUP_ID +
+ PPE_BM_PORT_GROUP_ID_INC * cfg[ret + 1], reg_val);
+
+ reg_val = FIELD_PREP(PPE_BM_PORT_FC_MODE_EN, 1);
+ ppe_write(ppe_dev, PPE_BM_PORT_FC_MODE +
+ PPE_BM_PORT_FC_MODE_INC * cfg[ret + 1], reg_val);
+ }
+ ret += 9;
+ }
+ ret = 0;
+
+parse_bm_err:
+ kfree(cfg);
+ return ret;
+}
+
+static int of_parse_ppe_config(struct ppe_device *ppe_dev,
+ struct device_node *ppe_node)
+{
+ return of_parse_ppe_bm(ppe_dev, ppe_node);
+}
+
static int qcom_ppe_probe(struct platform_device *pdev)
{
struct ppe_device *ppe_dev;
@@ -359,6 +517,12 @@ static int qcom_ppe_probe(struct platform_device *pdev)
ret,
"ppe clock config failed\n");

+ ret = of_parse_ppe_config(ppe_dev, pdev->dev.of_node);
+ if (ret)
+ return dev_err_probe(&pdev->dev,
+ ret,
+ "of parse ppe failed\n");
+
ppe_dev->is_ppe_probed = true;
return 0;
}
diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe.h b/drivers/net/ethernet/qualcomm/ppe/ppe.h
index f54406a6feb7..6caef42ab235 100644
--- a/drivers/net/ethernet/qualcomm/ppe/ppe.h
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe.h
@@ -140,4 +140,10 @@ struct ppe_data {
};

int ppe_type_get(struct ppe_device *ppe_dev);
+
+int ppe_write(struct ppe_device *ppe_dev, u32 reg, unsigned int val);
+int ppe_read(struct ppe_device *ppe_dev, u32 reg, unsigned int *val);
+int ppe_mask(struct ppe_device *ppe_dev, u32 reg, u32 mask, unsigned int set);
+int ppe_write_tbl(struct ppe_device *ppe_dev, u32 reg, const unsigned int *val, int cnt);
+int ppe_read_tbl(struct ppe_device *ppe_dev, u32 reg, unsigned int *val, int cnt);
#endif
diff --git a/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
new file mode 100644
index 000000000000..e11d8f2a26b7
--- /dev/null
+++ b/drivers/net/ethernet/qualcomm/ppe/ppe_regs.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+/* PPE hardware register and table declarations. */
+#ifndef __PPE_REGS_H__
+#define __PPE_REGS_H__
+
+#define PPE_BM_PORT_FC_MODE 0x600100
+#define PPE_BM_PORT_FC_MODE_NUM 15
+#define PPE_BM_PORT_FC_MODE_INC 4
+#define PPE_BM_PORT_FC_MODE_EN BIT(0)
+
+#define PPE_BM_PORT_GROUP_ID 0x600180
+#define PPE_BM_PORT_GROUP_ID_NUM 15
+#define PPE_BM_PORT_GROUP_ID_INC 4
+#define PPE_BM_PORT_GROUP_ID_SHARED_GROUP_ID GENMASK(1, 0)
+
+#define PPE_BM_SHARED_GROUP_CFG 0x600290
+#define PPE_BM_SHARED_GROUP_CFG_NUM 4
+#define PPE_BM_SHARED_GROUP_CFG_INC 4
+#define PPE_BM_SHARED_GROUP_CFG_SHARED_LIMIT GENMASK(10, 0)
+
+#define PPE_BM_PORT_FC_CFG 0x601000
+#define PPE_BM_PORT_FC_CFG_NUM 15
+#define PPE_BM_PORT_FC_CFG_INC 0x10
+
+/* BM port configurations, BM port(0-7) for CPU port, BM port(8-13) for physical
+ * port 1-6.
+ */
+struct ppe_bm_port_fc_cfg {
+ u32 react_limit:9,
+ resum_floor_th:9,
+ resum_offset:11,
+ shared_ceiling_0:3;
+ u32 shared_ceiling_1:8,
+ shared_weight:3,
+ shared_dynamic:1,
+ pre_alloc:11,
+ res0:9;
+};
+
+union ppe_bm_port_fc_cfg_u {
+ u32 val[2];
+ struct ppe_bm_port_fc_cfg bf;
+};
+
+#endif
--
2.42.0