From 90b3f1705785f0e30de6f41abc8764aae1391245 Mon Sep 17 00:00:00 2001 From: Biwen Li Date: Wed, 17 Apr 2019 18:58:28 +0800 Subject: [PATCH] dpaa2-ethernet: support layerscape MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is an integrated patch of dpaa2-ethernet for layerscape Signed-off-by: Biwen Li Signed-off-by: Bogdan Purcareata Signed-off-by: Camelia Groza Signed-off-by: David S. Miller Signed-off-by: Greg Kroah-Hartman Signed-off-by: Guanhua Gao Signed-off-by: Horia Geantă Signed-off-by: Ioana Ciornei Signed-off-by: Ioana Radulescu Signed-off-by: Nipun Gupta Signed-off-by: Valentin Catalin Neacsu Signed-off-by: Yangbo Lu --- drivers/staging/fsl-dpaa2/Kconfig | 7 + drivers/staging/fsl-dpaa2/ethernet/Makefile | 3 + .../fsl-dpaa2/ethernet/dpaa2-eth-ceetm.c | 1187 ++++++++ .../fsl-dpaa2/ethernet/dpaa2-eth-ceetm.h | 183 ++ .../fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c | 356 +++ .../fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h | 60 + .../fsl-dpaa2/ethernet/dpaa2-eth-trace.h | 29 +- .../staging/fsl-dpaa2/ethernet/dpaa2-eth.c | 2509 +++++++++++++---- .../staging/fsl-dpaa2/ethernet/dpaa2-eth.h | 394 ++- .../fsl-dpaa2/ethernet/dpaa2-ethtool.c | 716 ++++- drivers/staging/fsl-dpaa2/ethernet/dpkg.h | 380 ++- drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h | 255 +- drivers/staging/fsl-dpaa2/ethernet/dpni.c | 704 ++++- drivers/staging/fsl-dpaa2/ethernet/dpni.h | 401 ++- drivers/staging/fsl-dpaa2/ethernet/net.h | 30 +- 15 files changed, 6315 insertions(+), 899 deletions(-) create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.c create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.h create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c create mode 100644 drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h --- a/drivers/staging/fsl-dpaa2/Kconfig +++ b/drivers/staging/fsl-dpaa2/Kconfig @@ -17,6 +17,13 @@ config FSL_DPAA2_ETH Ethernet driver for Freescale DPAA2 SoCs, using the Freescale MC bus driver +config FSL_DPAA2_ETH_CEETM + depends on NET_SCHED + bool "DPAA2 Ethernet CEETM QoS" + default n + ---help--- + Enable QoS offloading support through the CEETM hardware block. + if FSL_DPAA2_ETH config FSL_DPAA2_ETH_USE_ERR_QUEUE bool "Enable Rx error queue" --- a/drivers/staging/fsl-dpaa2/ethernet/Makefile +++ b/drivers/staging/fsl-dpaa2/ethernet/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 # # Makefile for the Freescale DPAA2 Ethernet controller # @@ -5,6 +6,8 @@ obj-$(CONFIG_FSL_DPAA2_ETH) += fsl-dpaa2-eth.o fsl-dpaa2-eth-objs := dpaa2-eth.o dpaa2-ethtool.o dpni.o +fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_DEBUGFS} += dpaa2-eth-debugfs.o +fsl-dpaa2-eth-${CONFIG_FSL_DPAA2_ETH_CEETM} += dpaa2-eth-ceetm.o # Needed by the tracing framework CFLAGS_dpaa2-eth.o := -I$(src) --- /dev/null +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.c @@ -0,0 +1,1187 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright 2017-2019 NXP + * + */ + +#include +#include + +#include "dpaa2-eth-ceetm.h" +#include "dpaa2-eth.h" + +#define DPAA2_CEETM_DESCRIPTION "FSL DPAA2 CEETM qdisc" +/* Conversion formula from userspace passed Bps to expected Mbit */ +#define dpaa2_eth_bps_to_mbit(rate) (rate >> 17) + +static const struct nla_policy dpaa2_ceetm_policy[DPAA2_CEETM_TCA_MAX] = { + [DPAA2_CEETM_TCA_COPT] = { .len = sizeof(struct dpaa2_ceetm_tc_copt) }, + [DPAA2_CEETM_TCA_QOPS] = { .len = sizeof(struct dpaa2_ceetm_tc_qopt) }, +}; + +struct Qdisc_ops dpaa2_ceetm_qdisc_ops; + +static inline int dpaa2_eth_set_ch_shaping(struct dpaa2_eth_priv *priv, + struct dpni_tx_shaping_cfg *scfg, + struct dpni_tx_shaping_cfg *ecfg, + int coupled, int ch_id) +{ + int err = 0; + + netdev_dbg(priv->net_dev, "%s: ch_id %d rate %d mbps\n", __func__, + ch_id, scfg->rate_limit); + err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, scfg, + ecfg, coupled); + if (err) + netdev_err(priv->net_dev, "dpni_set_tx_shaping err\n"); + + return err; +} + +static inline int dpaa2_eth_reset_ch_shaping(struct dpaa2_eth_priv *priv, + int ch_id) +{ + struct dpni_tx_shaping_cfg cfg = { 0 }; + + return dpaa2_eth_set_ch_shaping(priv, &cfg, &cfg, 0, ch_id); +} + +static inline int +dpaa2_eth_update_shaping_cfg(struct net_device *dev, + struct dpaa2_ceetm_shaping_cfg cfg, + struct dpni_tx_shaping_cfg *scfg, + struct dpni_tx_shaping_cfg *ecfg) +{ + scfg->rate_limit = dpaa2_eth_bps_to_mbit(cfg.cir); + ecfg->rate_limit = dpaa2_eth_bps_to_mbit(cfg.eir); + + if (cfg.cbs > DPAA2_ETH_MAX_BURST_SIZE) { + netdev_err(dev, "Committed burst size must be under %d\n", + DPAA2_ETH_MAX_BURST_SIZE); + return -EINVAL; + } + + scfg->max_burst_size = cfg.cbs; + + if (cfg.ebs > DPAA2_ETH_MAX_BURST_SIZE) { + netdev_err(dev, "Excess burst size must be under %d\n", + DPAA2_ETH_MAX_BURST_SIZE); + return -EINVAL; + } + + ecfg->max_burst_size = cfg.ebs; + + if ((!cfg.cir || !cfg.eir) && cfg.coupled) { + netdev_err(dev, "Coupling can be set when both CIR and EIR are finite\n"); + return -EINVAL; + } + + return 0; +} + +enum update_tx_prio { + DPAA2_ETH_ADD_CQ, + DPAA2_ETH_DEL_CQ, +}; + +/* Normalize weights based on max passed value */ +static inline int dpaa2_eth_normalize_tx_prio(struct dpaa2_ceetm_qdisc *priv) +{ + struct dpni_tx_schedule_cfg *sched_cfg; + struct dpaa2_ceetm_class *cl; + u32 qpri; + u16 weight_max = 0, increment; + int i; + + /* Check the boundaries of the provided values */ + for (i = 0; i < priv->clhash.hashsize; i++) + hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) + weight_max = (weight_max == 0 ? cl->prio.weight : + (weight_max < cl->prio.weight ? + cl->prio.weight : weight_max)); + + /* If there are no elements, there's nothing to do */ + if (weight_max == 0) + return 0; + + increment = (DPAA2_CEETM_MAX_WEIGHT - DPAA2_CEETM_MIN_WEIGHT) / + weight_max; + + for (i = 0; i < priv->clhash.hashsize; i++) { + hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) { + if (cl->prio.mode == STRICT_PRIORITY) + continue; + + qpri = cl->prio.qpri; + sched_cfg = &priv->prio.tx_prio_cfg.tc_sched[qpri]; + + sched_cfg->delta_bandwidth = + DPAA2_CEETM_MIN_WEIGHT + + (cl->prio.weight * increment); + + pr_debug("%s: Normalized CQ qpri %d weight to %d\n", + __func__, qpri, sched_cfg->delta_bandwidth); + } + } + + return 0; +} + +static inline int dpaa2_eth_update_tx_prio(struct dpaa2_eth_priv *priv, + struct dpaa2_ceetm_class *cl, + enum update_tx_prio type) +{ + struct dpaa2_ceetm_qdisc *sch = qdisc_priv(cl->parent); + struct dpni_tx_schedule_cfg *sched_cfg; + struct dpni_taildrop td = {0}; + u8 ch_id = 0, tc_id = 0; + u32 qpri = 0; + int err = 0; + + qpri = cl->prio.qpri; + tc_id = DPNI_BUILD_CH_TC(ch_id, qpri); + + switch (type) { + case DPAA2_ETH_ADD_CQ: + /* Enable taildrop */ + td.enable = 1; + td.units = DPNI_CONGESTION_UNIT_FRAMES; + td.threshold = DPAA2_CEETM_TD_THRESHOLD; + err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, + DPNI_CP_GROUP, DPNI_QUEUE_TX, tc_id, + 0, &td); + if (err) { + netdev_err(priv->net_dev, "Error enabling Tx taildrop %d\n", + err); + return err; + } + break; + case DPAA2_ETH_DEL_CQ: + /* Disable taildrop */ + td.enable = 0; + err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, + DPNI_CP_GROUP, DPNI_QUEUE_TX, tc_id, + 0, &td); + if (err) { + netdev_err(priv->net_dev, "Error disabling Tx taildrop %d\n", + err); + return err; + } + break; + } + + /* We can zero out the structure in the tx_prio_conf array */ + if (type == DPAA2_ETH_DEL_CQ) { + sched_cfg = &sch->prio.tx_prio_cfg.tc_sched[qpri]; + memset(sched_cfg, 0, sizeof(*sched_cfg)); + } + + /* Normalize priorities */ + err = dpaa2_eth_normalize_tx_prio(sch); + + /* Debug print goes here */ + print_hex_dump_debug("tx_prio: ", DUMP_PREFIX_OFFSET, 16, 1, + &sch->prio.tx_prio_cfg, + sizeof(sch->prio.tx_prio_cfg), 0); + + /* Call dpni_set_tx_priorities for the entire prio qdisc */ + err = dpni_set_tx_priorities(priv->mc_io, 0, priv->mc_token, + &sch->prio.tx_prio_cfg); + if (err) + netdev_err(priv->net_dev, "dpni_set_tx_priorities err %d\n", + err); + + return err; +} + +static void dpaa2_eth_ceetm_enable(struct dpaa2_eth_priv *priv) +{ + priv->ceetm_en = true; +} + +static void dpaa2_eth_ceetm_disable(struct dpaa2_eth_priv *priv) +{ + priv->ceetm_en = false; +} + +/* Find class in qdisc hash table using given handle */ +static inline struct dpaa2_ceetm_class *dpaa2_ceetm_find(u32 handle, + struct Qdisc *sch) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct Qdisc_class_common *clc; + + pr_debug(KBUILD_BASENAME " : %s : find class %X in qdisc %X\n", + __func__, handle, sch->handle); + + clc = qdisc_class_find(&priv->clhash, handle); + return clc ? container_of(clc, struct dpaa2_ceetm_class, common) : NULL; +} + +/* Insert a class in the qdisc's class hash */ +static void dpaa2_ceetm_link_class(struct Qdisc *sch, + struct Qdisc_class_hash *clhash, + struct Qdisc_class_common *common) +{ + sch_tree_lock(sch); + qdisc_class_hash_insert(clhash, common); + sch_tree_unlock(sch); + qdisc_class_hash_grow(sch, clhash); +} + +/* Destroy a ceetm class */ +static void dpaa2_ceetm_cls_destroy(struct Qdisc *sch, + struct dpaa2_ceetm_class *cl) +{ + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_eth_priv *priv = netdev_priv(dev); + + if (!cl) + return; + + pr_debug(KBUILD_BASENAME " : %s : destroy class %X from under %X\n", + __func__, cl->common.classid, sch->handle); + + /* Recurse into child first */ + if (cl->child) { + qdisc_destroy(cl->child); + cl->child = NULL; + } + + switch (cl->type) { + case CEETM_ROOT: + if (dpaa2_eth_reset_ch_shaping(priv, cl->root.ch_id)) + netdev_err(dev, "Error resetting channel shaping\n"); + + break; + + case CEETM_PRIO: + if (dpaa2_eth_update_tx_prio(priv, cl, DPAA2_ETH_DEL_CQ)) + netdev_err(dev, "Error resetting tx_priorities\n"); + + if (cl->prio.cstats) + free_percpu(cl->prio.cstats); + + break; + } + + tcf_block_put(cl->block); + kfree(cl); +} + +/* Destroy a ceetm qdisc */ +static void dpaa2_ceetm_destroy(struct Qdisc *sch) +{ + unsigned int i; + struct hlist_node *next; + struct dpaa2_ceetm_class *cl; + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_eth_priv *priv_eth = netdev_priv(dev); + + pr_debug(KBUILD_BASENAME " : %s : destroy qdisc %X\n", + __func__, sch->handle); + + /* All filters need to be removed before destroying the classes */ + tcf_block_put(priv->block); + + for (i = 0; i < priv->clhash.hashsize; i++) { + hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) + tcf_block_put(cl->block); + } + + for (i = 0; i < priv->clhash.hashsize; i++) { + hlist_for_each_entry_safe(cl, next, &priv->clhash.hash[i], + common.hnode) + dpaa2_ceetm_cls_destroy(sch, cl); + } + + qdisc_class_hash_destroy(&priv->clhash); + + switch (priv->type) { + case CEETM_ROOT: + dpaa2_eth_ceetm_disable(priv_eth); + + if (priv->root.qstats) + free_percpu(priv->root.qstats); + + if (!priv->root.qdiscs) + break; + + /* Destroy the pfifo qdiscs in case they haven't been attached + * to the netdev queues yet. + */ + for (i = 0; i < dev->num_tx_queues; i++) + if (priv->root.qdiscs[i]) + qdisc_destroy(priv->root.qdiscs[i]); + + kfree(priv->root.qdiscs); + break; + + case CEETM_PRIO: + if (priv->prio.parent) + priv->prio.parent->child = NULL; + break; + } +} + +static int dpaa2_ceetm_dump(struct Qdisc *sch, struct sk_buff *skb) +{ + struct Qdisc *qdisc; + unsigned int ntx, i; + struct nlattr *nest; + struct dpaa2_ceetm_tc_qopt qopt; + struct dpaa2_ceetm_qdisc_stats *qstats; + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + sch_tree_lock(sch); + memset(&qopt, 0, sizeof(qopt)); + qopt.type = priv->type; + qopt.shaped = priv->shaped; + + switch (priv->type) { + case CEETM_ROOT: + /* Gather statistics from the underlying pfifo qdiscs */ + sch->q.qlen = 0; + memset(&sch->bstats, 0, sizeof(sch->bstats)); + memset(&sch->qstats, 0, sizeof(sch->qstats)); + + for (ntx = 0; ntx < dev->num_tx_queues; ntx++) { + qdisc = netdev_get_tx_queue(dev, ntx)->qdisc_sleeping; + sch->q.qlen += qdisc->q.qlen; + sch->bstats.bytes += qdisc->bstats.bytes; + sch->bstats.packets += qdisc->bstats.packets; + sch->qstats.qlen += qdisc->qstats.qlen; + sch->qstats.backlog += qdisc->qstats.backlog; + sch->qstats.drops += qdisc->qstats.drops; + sch->qstats.requeues += qdisc->qstats.requeues; + sch->qstats.overlimits += qdisc->qstats.overlimits; + } + + for_each_online_cpu(i) { + qstats = per_cpu_ptr(priv->root.qstats, i); + sch->qstats.drops += qstats->drops; + } + + break; + + case CEETM_PRIO: + qopt.prio_group_A = priv->prio.tx_prio_cfg.prio_group_A; + qopt.prio_group_B = priv->prio.tx_prio_cfg.prio_group_B; + qopt.separate_groups = priv->prio.tx_prio_cfg.separate_groups; + break; + + default: + pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__); + sch_tree_unlock(sch); + return -EINVAL; + } + + nest = nla_nest_start(skb, TCA_OPTIONS); + if (!nest) + goto nla_put_failure; + if (nla_put(skb, DPAA2_CEETM_TCA_QOPS, sizeof(qopt), &qopt)) + goto nla_put_failure; + nla_nest_end(skb, nest); + + sch_tree_unlock(sch); + return skb->len; + +nla_put_failure: + sch_tree_unlock(sch); + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int dpaa2_ceetm_change_prio(struct Qdisc *sch, + struct dpaa2_ceetm_qdisc *priv, + struct dpaa2_ceetm_tc_qopt *qopt) +{ + /* TODO: Once LX2 support is added */ + /* priv->shaped = parent_cl->shaped; */ + priv->prio.tx_prio_cfg.prio_group_A = qopt->prio_group_A; + priv->prio.tx_prio_cfg.prio_group_B = qopt->prio_group_B; + priv->prio.tx_prio_cfg.separate_groups = qopt->separate_groups; + + return 0; +} + +/* Edit a ceetm qdisc */ +static int dpaa2_ceetm_change(struct Qdisc *sch, struct nlattr *opt) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct nlattr *tb[DPAA2_CEETM_TCA_QOPS + 1]; + struct dpaa2_ceetm_tc_qopt *qopt; + int err; + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + err = nla_parse_nested(tb, DPAA2_CEETM_TCA_QOPS, opt, + dpaa2_ceetm_policy, NULL); + if (err < 0) { + pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__, + "nla_parse_nested"); + return err; + } + + if (!tb[DPAA2_CEETM_TCA_QOPS]) { + pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__, + "tb"); + return -EINVAL; + } + + if (TC_H_MIN(sch->handle)) { + pr_err("CEETM: a qdisc should not have a minor\n"); + return -EINVAL; + } + + qopt = nla_data(tb[DPAA2_CEETM_TCA_QOPS]); + + if (priv->type != qopt->type) { + pr_err("CEETM: qdisc %X is not of the provided type\n", + sch->handle); + return -EINVAL; + } + + switch (priv->type) { + case CEETM_PRIO: + err = dpaa2_ceetm_change_prio(sch, priv, qopt); + break; + default: + pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__); + err = -EINVAL; + } + + return err; +} + +/* Configure a root ceetm qdisc */ +static int dpaa2_ceetm_init_root(struct Qdisc *sch, + struct dpaa2_ceetm_qdisc *priv, + struct dpaa2_ceetm_tc_qopt *qopt) +{ + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_eth_priv *priv_eth = netdev_priv(dev); + struct netdev_queue *dev_queue; + unsigned int i, parent_id; + struct Qdisc *qdisc; + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + /* Validate inputs */ + if (sch->parent != TC_H_ROOT) { + pr_err("CEETM: a root ceetm qdisc must be root\n"); + return -EINVAL; + } + + /* Pre-allocate underlying pfifo qdiscs. + * + * We want to offload shaping and scheduling decisions to the hardware. + * The pfifo qdiscs will be attached to the netdev queues and will + * guide the traffic from the IP stack down to the driver with minimum + * interference. + * + * The CEETM qdiscs and classes will be crossed when the traffic + * reaches the driver. + */ + priv->root.qdiscs = kcalloc(dev->num_tx_queues, + sizeof(priv->root.qdiscs[0]), + GFP_KERNEL); + if (!priv->root.qdiscs) + return -ENOMEM; + + for (i = 0; i < dev->num_tx_queues; i++) { + dev_queue = netdev_get_tx_queue(dev, i); + parent_id = TC_H_MAKE(TC_H_MAJ(sch->handle), + TC_H_MIN(i + PFIFO_MIN_OFFSET)); + + qdisc = qdisc_create_dflt(dev_queue, &pfifo_qdisc_ops, + parent_id); + if (!qdisc) + return -ENOMEM; + + priv->root.qdiscs[i] = qdisc; + qdisc->flags |= TCQ_F_ONETXQUEUE; + } + + sch->flags |= TCQ_F_MQROOT; + + priv->root.qstats = alloc_percpu(struct dpaa2_ceetm_qdisc_stats); + if (!priv->root.qstats) { + pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n", + __func__); + return -ENOMEM; + } + + dpaa2_eth_ceetm_enable(priv_eth); + return 0; +} + +/* Configure a prio ceetm qdisc */ +static int dpaa2_ceetm_init_prio(struct Qdisc *sch, + struct dpaa2_ceetm_qdisc *priv, + struct dpaa2_ceetm_tc_qopt *qopt) +{ + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_ceetm_class *parent_cl; + struct Qdisc *parent_qdisc; + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + if (sch->parent == TC_H_ROOT) { + pr_err("CEETM: a prio ceetm qdisc can not be root\n"); + return -EINVAL; + } + + parent_qdisc = qdisc_lookup(dev, TC_H_MAJ(sch->parent)); + if (strcmp(parent_qdisc->ops->id, dpaa2_ceetm_qdisc_ops.id)) { + pr_err("CEETM: a ceetm qdisc can not be attached to other qdisc/class types\n"); + return -EINVAL; + } + + /* Obtain the parent root ceetm_class */ + parent_cl = dpaa2_ceetm_find(sch->parent, parent_qdisc); + + if (!parent_cl || parent_cl->type != CEETM_ROOT) { + pr_err("CEETM: a prio ceetm qdiscs can be added only under a root ceetm class\n"); + return -EINVAL; + } + + priv->prio.parent = parent_cl; + parent_cl->child = sch; + + return dpaa2_ceetm_change_prio(sch, priv, qopt); +} + +/* Configure a generic ceetm qdisc */ +static int dpaa2_ceetm_init(struct Qdisc *sch, struct nlattr *opt) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct nlattr *tb[DPAA2_CEETM_TCA_QOPS + 1]; + struct dpaa2_ceetm_tc_qopt *qopt; + int err; + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + if (!netif_is_multiqueue(dev)) + return -EOPNOTSUPP; + + err = tcf_block_get(&priv->block, &priv->filter_list); + if (err) { + pr_err("CEETM: unable to get tcf_block\n"); + return err; + } + + if (!opt) { + pr_err(KBUILD_BASENAME " : %s : tc error - opt = NULL\n", + __func__); + return -EINVAL; + } + + err = nla_parse_nested(tb, DPAA2_CEETM_TCA_QOPS, opt, + dpaa2_ceetm_policy, NULL); + if (err < 0) { + pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__, + "nla_parse_nested"); + return err; + } + + if (!tb[DPAA2_CEETM_TCA_QOPS]) { + pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__, + "tb"); + return -EINVAL; + } + + if (TC_H_MIN(sch->handle)) { + pr_err("CEETM: a qdisc should not have a minor\n"); + return -EINVAL; + } + + qopt = nla_data(tb[DPAA2_CEETM_TCA_QOPS]); + + /* Initialize the class hash list. Each qdisc has its own class hash */ + err = qdisc_class_hash_init(&priv->clhash); + if (err < 0) { + pr_err(KBUILD_BASENAME " : %s : qdisc_class_hash_init failed\n", + __func__); + return err; + } + + priv->type = qopt->type; + priv->shaped = qopt->shaped; + + switch (priv->type) { + case CEETM_ROOT: + err = dpaa2_ceetm_init_root(sch, priv, qopt); + break; + case CEETM_PRIO: + err = dpaa2_ceetm_init_prio(sch, priv, qopt); + break; + default: + pr_err(KBUILD_BASENAME " : %s : invalid qdisc\n", __func__); + /* Note: dpaa2_ceetm_destroy() will be called by our caller */ + err = -EINVAL; + } + + return err; +} + +/* Attach the underlying pfifo qdiscs */ +static void dpaa2_ceetm_attach(struct Qdisc *sch) +{ + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct Qdisc *qdisc, *old_qdisc; + unsigned int i; + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + for (i = 0; i < dev->num_tx_queues; i++) { + qdisc = priv->root.qdiscs[i]; + old_qdisc = dev_graft_qdisc(qdisc->dev_queue, qdisc); + if (old_qdisc) + qdisc_destroy(old_qdisc); + } + + /* Remove the references to the pfifo qdiscs since the kernel will + * destroy them when needed. No cleanup from our part is required from + * this point on. + */ + kfree(priv->root.qdiscs); + priv->root.qdiscs = NULL; +} + +static unsigned long dpaa2_ceetm_cls_find(struct Qdisc *sch, u32 classid) +{ + struct dpaa2_ceetm_class *cl; + + pr_debug(KBUILD_BASENAME " : %s : classid %X from qdisc %X\n", + __func__, classid, sch->handle); + cl = dpaa2_ceetm_find(classid, sch); + + return (unsigned long)cl; +} + +static int dpaa2_ceetm_cls_change_root(struct dpaa2_ceetm_class *cl, + struct dpaa2_ceetm_tc_copt *copt, + struct net_device *dev) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct dpni_tx_shaping_cfg scfg = { 0 }, ecfg = { 0 }; + int err = 0; + + pr_debug(KBUILD_BASENAME " : %s : class %X\n", __func__, + cl->common.classid); + + if (!cl->shaped) + return 0; + + if (dpaa2_eth_update_shaping_cfg(dev, copt->shaping_cfg, + &scfg, &ecfg)) + return -EINVAL; + + err = dpaa2_eth_set_ch_shaping(priv, &scfg, &ecfg, + copt->shaping_cfg.coupled, + cl->root.ch_id); + if (err) + return err; + + memcpy(&cl->root.shaping_cfg, &copt->shaping_cfg, + sizeof(struct dpaa2_ceetm_shaping_cfg)); + + return err; +} + +static int dpaa2_ceetm_cls_change_prio(struct dpaa2_ceetm_class *cl, + struct dpaa2_ceetm_tc_copt *copt, + struct net_device *dev) +{ + struct dpaa2_ceetm_qdisc *sch = qdisc_priv(cl->parent); + struct dpni_tx_schedule_cfg *sched_cfg; + struct dpaa2_eth_priv *priv = netdev_priv(dev); + int err; + + pr_debug(KBUILD_BASENAME " : %s : class %X mode %d weight %d\n", + __func__, cl->common.classid, copt->mode, copt->weight); + + if (!cl->prio.cstats) { + cl->prio.cstats = alloc_percpu(struct dpaa2_ceetm_class_stats); + if (!cl->prio.cstats) { + pr_err(KBUILD_BASENAME " : %s : alloc_percpu() failed\n", + __func__); + return -ENOMEM; + } + } + + cl->prio.mode = copt->mode; + cl->prio.weight = copt->weight; + + sched_cfg = &sch->prio.tx_prio_cfg.tc_sched[cl->prio.qpri]; + + switch (copt->mode) { + case STRICT_PRIORITY: + sched_cfg->mode = DPNI_TX_SCHED_STRICT_PRIORITY; + break; + case WEIGHTED_A: + sched_cfg->mode = DPNI_TX_SCHED_WEIGHTED_A; + break; + case WEIGHTED_B: + sched_cfg->mode = DPNI_TX_SCHED_WEIGHTED_B; + break; + } + + err = dpaa2_eth_update_tx_prio(priv, cl, DPAA2_ETH_ADD_CQ); + + return err; +} + +/* Add a new ceetm class */ +static int dpaa2_ceetm_cls_add(struct Qdisc *sch, u32 classid, + struct dpaa2_ceetm_tc_copt *copt, + unsigned long *arg) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_eth_priv *priv_eth = netdev_priv(dev); + struct dpaa2_ceetm_class *cl; + int err; + + if (copt->type == CEETM_ROOT && + priv->clhash.hashelems == dpaa2_eth_ch_count(priv_eth)) { + pr_err("CEETM: only %d channel%s per DPNI allowed, sorry\n", + dpaa2_eth_ch_count(priv_eth), + dpaa2_eth_ch_count(priv_eth) == 1 ? "" : "s"); + return -EINVAL; + } + + if (copt->type == CEETM_PRIO && + priv->clhash.hashelems == dpaa2_eth_tc_count(priv_eth)) { + pr_err("CEETM: only %d queue%s per channel allowed, sorry\n", + dpaa2_eth_tc_count(priv_eth), + dpaa2_eth_tc_count(priv_eth) == 1 ? "" : "s"); + return -EINVAL; + } + + cl = kzalloc(sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + err = tcf_block_get(&cl->block, &cl->filter_list); + if (err) { + pr_err("%s: Unable to set new root class\n", __func__); + goto out_free; + } + + cl->common.classid = classid; + cl->parent = sch; + cl->child = NULL; + + /* Add class handle in Qdisc */ + dpaa2_ceetm_link_class(sch, &priv->clhash, &cl->common); + + cl->shaped = copt->shaped; + cl->type = copt->type; + + /* Claim a CEETM channel / tc - DPAA2. will assume transition from + * classid to qdid/qpri, starting from qdid / qpri 0 + */ + switch (copt->type) { + case CEETM_ROOT: + cl->root.ch_id = classid - sch->handle - 1; + err = dpaa2_ceetm_cls_change_root(cl, copt, dev); + break; + case CEETM_PRIO: + cl->prio.qpri = classid - sch->handle - 1; + err = dpaa2_ceetm_cls_change_prio(cl, copt, dev); + break; + } + + if (err) { + pr_err("%s: Unable to set new %s class\n", __func__, + (copt->type == CEETM_ROOT ? "root" : "prio")); + goto out_free; + } + + switch (copt->type) { + case CEETM_ROOT: + pr_debug(KBUILD_BASENAME " : %s : configured root class %X associated with channel qdid %d\n", + __func__, classid, cl->root.ch_id); + break; + case CEETM_PRIO: + pr_debug(KBUILD_BASENAME " : %s : configured prio class %X associated with queue qpri %d\n", + __func__, classid, cl->prio.qpri); + break; + } + + *arg = (unsigned long)cl; + return 0; + +out_free: + kfree(cl); + return err; +} + +/* Add or configure a ceetm class */ +static int dpaa2_ceetm_cls_change(struct Qdisc *sch, u32 classid, u32 parentid, + struct nlattr **tca, unsigned long *arg) +{ + struct dpaa2_ceetm_qdisc *priv; + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)*arg; + struct nlattr *opt = tca[TCA_OPTIONS]; + struct nlattr *tb[DPAA2_CEETM_TCA_MAX]; + struct dpaa2_ceetm_tc_copt *copt; + struct net_device *dev = qdisc_dev(sch); + int err; + + pr_debug(KBUILD_BASENAME " : %s : classid %X under qdisc %X\n", + __func__, classid, sch->handle); + + if (strcmp(sch->ops->id, dpaa2_ceetm_qdisc_ops.id)) { + pr_err("CEETM: a ceetm class can not be attached to other qdisc/class types\n"); + return -EINVAL; + } + + priv = qdisc_priv(sch); + + if (!opt) { + pr_err(KBUILD_BASENAME " : %s : tc error NULL opt\n", __func__); + return -EINVAL; + } + + err = nla_parse_nested(tb, DPAA2_CEETM_TCA_COPT, opt, + dpaa2_ceetm_policy, NULL); + if (err < 0) { + pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__, + "nla_parse_nested"); + return -EINVAL; + } + + if (!tb[DPAA2_CEETM_TCA_COPT]) { + pr_err(KBUILD_BASENAME " : %s : tc error in %s\n", __func__, + "tb"); + return -EINVAL; + } + + copt = nla_data(tb[DPAA2_CEETM_TCA_COPT]); + + /* Configure an existing ceetm class */ + if (cl) { + if (copt->type != cl->type) { + pr_err("CEETM: class %X is not of the provided type\n", + cl->common.classid); + return -EINVAL; + } + + switch (copt->type) { + case CEETM_ROOT: + return dpaa2_ceetm_cls_change_root(cl, copt, dev); + case CEETM_PRIO: + return dpaa2_ceetm_cls_change_prio(cl, copt, dev); + + default: + pr_err(KBUILD_BASENAME " : %s : invalid class\n", + __func__); + return -EINVAL; + } + } + + return dpaa2_ceetm_cls_add(sch, classid, copt, arg); +} + +static void dpaa2_ceetm_cls_walk(struct Qdisc *sch, struct qdisc_walker *arg) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct dpaa2_ceetm_class *cl; + unsigned int i; + + pr_debug(KBUILD_BASENAME " : %s : qdisc %X\n", __func__, sch->handle); + + if (arg->stop) + return; + + for (i = 0; i < priv->clhash.hashsize; i++) { + hlist_for_each_entry(cl, &priv->clhash.hash[i], common.hnode) { + if (arg->count < arg->skip) { + arg->count++; + continue; + } + if (arg->fn(sch, (unsigned long)cl, arg) < 0) { + arg->stop = 1; + return; + } + arg->count++; + } + } +} + +static int dpaa2_ceetm_cls_dump(struct Qdisc *sch, unsigned long arg, + struct sk_buff *skb, struct tcmsg *tcm) +{ + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg; + struct nlattr *nest; + struct dpaa2_ceetm_tc_copt copt; + + pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", + __func__, cl->common.classid, sch->handle); + + sch_tree_lock(sch); + + tcm->tcm_parent = ((struct Qdisc *)cl->parent)->handle; + tcm->tcm_handle = cl->common.classid; + + memset(&copt, 0, sizeof(copt)); + + copt.shaped = cl->shaped; + copt.type = cl->type; + + switch (cl->type) { + case CEETM_ROOT: + if (cl->child) + tcm->tcm_info = cl->child->handle; + + memcpy(&copt.shaping_cfg, &cl->root.shaping_cfg, + sizeof(struct dpaa2_ceetm_shaping_cfg)); + + break; + + case CEETM_PRIO: + if (cl->child) + tcm->tcm_info = cl->child->handle; + + copt.mode = cl->prio.mode; + copt.weight = cl->prio.weight; + + break; + } + + nest = nla_nest_start(skb, TCA_OPTIONS); + if (!nest) + goto nla_put_failure; + if (nla_put(skb, DPAA2_CEETM_TCA_COPT, sizeof(copt), &copt)) + goto nla_put_failure; + nla_nest_end(skb, nest); + sch_tree_unlock(sch); + return skb->len; + +nla_put_failure: + sch_tree_unlock(sch); + nla_nest_cancel(skb, nest); + return -EMSGSIZE; +} + +static int dpaa2_ceetm_cls_delete(struct Qdisc *sch, unsigned long arg) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg; + + pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", + __func__, cl->common.classid, sch->handle); + + sch_tree_lock(sch); + qdisc_class_hash_remove(&priv->clhash, &cl->common); + sch_tree_unlock(sch); + return 0; +} + +/* Get the class' child qdisc, if any */ +static struct Qdisc *dpaa2_ceetm_cls_leaf(struct Qdisc *sch, unsigned long arg) +{ + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg; + + pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", + __func__, cl->common.classid, sch->handle); + + switch (cl->type) { + case CEETM_ROOT: + case CEETM_PRIO: + return cl->child; + } + + return NULL; +} + +static int dpaa2_ceetm_cls_graft(struct Qdisc *sch, unsigned long arg, + struct Qdisc *new, struct Qdisc **old) +{ + if (new && strcmp(new->ops->id, dpaa2_ceetm_qdisc_ops.id)) { + pr_err("CEETM: only ceetm qdiscs can be attached to ceetm classes\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int dpaa2_ceetm_cls_dump_stats(struct Qdisc *sch, unsigned long arg, + struct gnet_dump *d) +{ + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg; + struct gnet_stats_basic_packed tmp_bstats; + struct dpaa2_ceetm_tc_xstats xstats; + union dpni_statistics dpni_stats; + struct net_device *dev = qdisc_dev(sch); + struct dpaa2_eth_priv *priv_eth = netdev_priv(dev); + u8 ch_id = 0; + int err; + + memset(&xstats, 0, sizeof(xstats)); + memset(&tmp_bstats, 0, sizeof(tmp_bstats)); + + if (cl->type == CEETM_ROOT) + return 0; + + err = dpni_get_statistics(priv_eth->mc_io, 0, priv_eth->mc_token, 3, + DPNI_BUILD_CH_TC(ch_id, cl->prio.qpri), + &dpni_stats); + if (err) + netdev_warn(dev, "dpni_get_stats(%d) failed - %d\n", 3, err); + + xstats.ceetm_dequeue_bytes = dpni_stats.page_3.ceetm_dequeue_bytes; + xstats.ceetm_dequeue_frames = dpni_stats.page_3.ceetm_dequeue_frames; + xstats.ceetm_reject_bytes = dpni_stats.page_3.ceetm_reject_bytes; + xstats.ceetm_reject_frames = dpni_stats.page_3.ceetm_reject_frames; + + return gnet_stats_copy_app(d, &xstats, sizeof(xstats)); +} + +static struct tcf_block *dpaa2_ceetm_tcf_block(struct Qdisc *sch, + unsigned long arg) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg; + + pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__, + cl ? cl->common.classid : 0, sch->handle); + return cl ? cl->block : priv->block; +} + +static unsigned long dpaa2_ceetm_tcf_bind(struct Qdisc *sch, + unsigned long parent, + u32 classid) +{ + struct dpaa2_ceetm_class *cl = dpaa2_ceetm_find(classid, sch); + + pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__, + cl ? cl->common.classid : 0, sch->handle); + return (unsigned long)cl; +} + +static void dpaa2_ceetm_tcf_unbind(struct Qdisc *sch, unsigned long arg) +{ + struct dpaa2_ceetm_class *cl = (struct dpaa2_ceetm_class *)arg; + + pr_debug(KBUILD_BASENAME " : %s : class %X under qdisc %X\n", __func__, + cl ? cl->common.classid : 0, sch->handle); +} + +const struct Qdisc_class_ops dpaa2_ceetm_cls_ops = { + .graft = dpaa2_ceetm_cls_graft, + .leaf = dpaa2_ceetm_cls_leaf, + .find = dpaa2_ceetm_cls_find, + .change = dpaa2_ceetm_cls_change, + .delete = dpaa2_ceetm_cls_delete, + .walk = dpaa2_ceetm_cls_walk, + .tcf_block = dpaa2_ceetm_tcf_block, + .bind_tcf = dpaa2_ceetm_tcf_bind, + .unbind_tcf = dpaa2_ceetm_tcf_unbind, + .dump = dpaa2_ceetm_cls_dump, + .dump_stats = dpaa2_ceetm_cls_dump_stats, +}; + +struct Qdisc_ops dpaa2_ceetm_qdisc_ops __read_mostly = { + .id = "ceetm", + .priv_size = sizeof(struct dpaa2_ceetm_qdisc), + .cl_ops = &dpaa2_ceetm_cls_ops, + .init = dpaa2_ceetm_init, + .destroy = dpaa2_ceetm_destroy, + .change = dpaa2_ceetm_change, + .dump = dpaa2_ceetm_dump, + .attach = dpaa2_ceetm_attach, + .owner = THIS_MODULE, +}; + +/* Run the filters and classifiers attached to the qdisc on the provided skb */ +int dpaa2_ceetm_classify(struct sk_buff *skb, struct Qdisc *sch, + int *qdid, u8 *qpri) +{ + struct dpaa2_ceetm_qdisc *priv = qdisc_priv(sch); + struct dpaa2_ceetm_class *cl = NULL; + struct tcf_result res; + struct tcf_proto *tcf; + int result; + + tcf = rcu_dereference_bh(priv->filter_list); + while (tcf && (result = tcf_classify(skb, tcf, &res, false)) >= 0) { +#ifdef CONFIG_NET_CLS_ACT + switch (result) { + case TC_ACT_QUEUED: + case TC_ACT_STOLEN: + case TC_ACT_SHOT: + /* No valid class found due to action */ + return -1; + } +#endif + cl = (void *)res.class; + if (!cl) { + /* The filter leads to the qdisc */ + if (res.classid == sch->handle) + return 0; + + cl = dpaa2_ceetm_find(res.classid, sch); + /* The filter leads to an invalid class */ + if (!cl) + break; + } + + /* The class might have its own filters attached */ + tcf = rcu_dereference_bh(cl->filter_list); + } + + /* No valid class found */ + if (!cl) + return 0; + + switch (cl->type) { + case CEETM_ROOT: + *qdid = cl->root.ch_id; + + /* The root class does not have a child prio qdisc */ + if (!cl->child) + return 0; + + /* Run the prio qdisc classifiers */ + return dpaa2_ceetm_classify(skb, cl->child, qdid, qpri); + + case CEETM_PRIO: + *qpri = cl->prio.qpri; + break; + } + + return 0; +} + +int __init dpaa2_ceetm_register(void) +{ + int err = 0; + + pr_debug(KBUILD_MODNAME ": " DPAA2_CEETM_DESCRIPTION "\n"); + + err = register_qdisc(&dpaa2_ceetm_qdisc_ops); + if (unlikely(err)) + pr_err(KBUILD_MODNAME + ": %s:%hu:%s(): register_qdisc() = %d\n", + KBUILD_BASENAME ".c", __LINE__, __func__, err); + + return err; +} + +void __exit dpaa2_ceetm_unregister(void) +{ + pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", + KBUILD_BASENAME ".c", __func__); + + unregister_qdisc(&dpaa2_ceetm_qdisc_ops); +} --- /dev/null +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-ceetm.h @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright 2017 NXP + * + */ + +#ifndef __DPAA2_ETH_CEETM_H +#define __DPAA2_ETH_CEETM_H + +#include +#include +#include + +#include "dpaa2-eth.h" + +/* For functional purposes, there are num_tx_queues pfifo qdiscs through which + * frames reach the driver. Their handles start from 1:21. Handles 1:1 to 1:20 + * are reserved for the maximum 32 CEETM channels (majors and minors are in + * hex). + */ +#define PFIFO_MIN_OFFSET 0x21 + +#define DPAA2_CEETM_MIN_WEIGHT 100 +#define DPAA2_CEETM_MAX_WEIGHT 24800 + +#define DPAA2_CEETM_TD_THRESHOLD 1000 + +enum wbfs_group_type { + WBFS_GRP_A, + WBFS_GRP_B, + WBFS_GRP_LARGE +}; + +enum { + DPAA2_CEETM_TCA_UNSPEC, + DPAA2_CEETM_TCA_COPT, + DPAA2_CEETM_TCA_QOPS, + DPAA2_CEETM_TCA_MAX, +}; + +/* CEETM configuration types */ +enum dpaa2_ceetm_type { + CEETM_ROOT = 1, + CEETM_PRIO, +}; + +enum { + STRICT_PRIORITY = 0, + WEIGHTED_A, + WEIGHTED_B, +}; + +struct dpaa2_ceetm_shaping_cfg { + __u64 cir; /* committed information rate */ + __u64 eir; /* excess information rate */ + __u16 cbs; /* committed burst size */ + __u16 ebs; /* excess burst size */ + __u8 coupled; /* shaper coupling */ +}; + +extern const struct nla_policy ceetm_policy[DPAA2_CEETM_TCA_MAX]; + +struct dpaa2_ceetm_class; +struct dpaa2_ceetm_qdisc_stats; +struct dpaa2_ceetm_class_stats; + +/* corresponds to CEETM shaping at LNI level */ +struct dpaa2_root_q { + struct Qdisc **qdiscs; + struct dpaa2_ceetm_qdisc_stats __percpu *qstats; +}; + +/* corresponds to the number of priorities a channel serves */ +struct dpaa2_prio_q { + struct dpaa2_ceetm_class *parent; + struct dpni_tx_priorities_cfg tx_prio_cfg; +}; + +struct dpaa2_ceetm_qdisc { + struct Qdisc_class_hash clhash; + struct tcf_proto *filter_list; /* qdisc attached filters */ + struct tcf_block *block; + + enum dpaa2_ceetm_type type; /* ROOT/PRIO */ + bool shaped; + union { + struct dpaa2_root_q root; + struct dpaa2_prio_q prio; + }; +}; + +/* CEETM Qdisc configuration parameters */ +struct dpaa2_ceetm_tc_qopt { + enum dpaa2_ceetm_type type; + __u16 shaped; + __u8 prio_group_A; + __u8 prio_group_B; + __u8 separate_groups; +}; + +/* root class - corresponds to a channel */ +struct dpaa2_root_c { + struct dpaa2_ceetm_shaping_cfg shaping_cfg; + u32 ch_id; +}; + +/* prio class - corresponds to a strict priority queue (group) */ +struct dpaa2_prio_c { + struct dpaa2_ceetm_class_stats __percpu *cstats; + u32 qpri; + u8 mode; + u16 weight; +}; + +struct dpaa2_ceetm_class { + struct Qdisc_class_common common; + struct tcf_proto *filter_list; /* class attached filters */ + struct tcf_block *block; + struct Qdisc *parent; + struct Qdisc *child; + + enum dpaa2_ceetm_type type; /* ROOT/PRIO */ + bool shaped; + union { + struct dpaa2_root_c root; + struct dpaa2_prio_c prio; + }; +}; + +/* CEETM Class configuration parameters */ +struct dpaa2_ceetm_tc_copt { + enum dpaa2_ceetm_type type; + struct dpaa2_ceetm_shaping_cfg shaping_cfg; + __u16 shaped; + __u8 mode; + __u16 weight; +}; + +/* CEETM stats */ +struct dpaa2_ceetm_qdisc_stats { + __u32 drops; +}; + +struct dpaa2_ceetm_class_stats { + /* Software counters */ + struct gnet_stats_basic_packed bstats; + __u32 ern_drop_count; + __u32 congested_count; +}; + +struct dpaa2_ceetm_tc_xstats { + __u64 ceetm_dequeue_bytes; + __u64 ceetm_dequeue_frames; + __u64 ceetm_reject_bytes; + __u64 ceetm_reject_frames; +}; + +#ifdef CONFIG_FSL_DPAA2_ETH_CEETM +int __init dpaa2_ceetm_register(void); +void __exit dpaa2_ceetm_unregister(void); +int dpaa2_ceetm_classify(struct sk_buff *skb, struct Qdisc *sch, + int *qdid, u8 *qpri); +#else +static inline int dpaa2_ceetm_register(void) +{ + return 0; +} + +static inline void dpaa2_ceetm_unregister(void) {} + +static inline int dpaa2_ceetm_classify(struct sk_buff *skb, struct Qdisc *sch, + int *qdid, u8 *qpri) +{ + return 0; +} +#endif + +static inline bool dpaa2_eth_ceetm_is_enabled(struct dpaa2_eth_priv *priv) +{ + return priv->ceetm_en; +} + +#endif --- /dev/null +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.c @@ -0,0 +1,356 @@ + +/* Copyright 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "dpaa2-eth.h" +#include "dpaa2-eth-debugfs.h" + +#define DPAA2_ETH_DBG_ROOT "dpaa2-eth" + +static struct dentry *dpaa2_dbg_root; + +static int dpaa2_dbg_cpu_show(struct seq_file *file, void *offset) +{ + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; + struct rtnl_link_stats64 *stats; + struct dpaa2_eth_drv_stats *extras; + int i; + + seq_printf(file, "Per-CPU stats for %s\n", priv->net_dev->name); + seq_printf(file, "%s%16s%16s%16s%16s%16s%16s%16s%16s%16s\n", + "CPU", "Rx", "Rx Err", "Rx SG", "Tx", "Tx Err", "Tx conf", + "Tx SG", "Tx realloc", "Enq busy"); + + for_each_online_cpu(i) { + stats = per_cpu_ptr(priv->percpu_stats, i); + extras = per_cpu_ptr(priv->percpu_extras, i); + seq_printf(file, "%3d%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu%16llu\n", + i, + stats->rx_packets, + stats->rx_errors, + extras->rx_sg_frames, + stats->tx_packets, + stats->tx_errors, + extras->tx_conf_frames, + extras->tx_sg_frames, + extras->tx_reallocs, + extras->tx_portal_busy); + } + + return 0; +} + +static int dpaa2_dbg_cpu_open(struct inode *inode, struct file *file) +{ + int err; + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; + + err = single_open(file, dpaa2_dbg_cpu_show, priv); + if (err < 0) + netdev_err(priv->net_dev, "single_open() failed\n"); + + return err; +} + +static const struct file_operations dpaa2_dbg_cpu_ops = { + .open = dpaa2_dbg_cpu_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static char *fq_type_to_str(struct dpaa2_eth_fq *fq) +{ + switch (fq->type) { + case DPAA2_RX_FQ: + return "Rx"; + case DPAA2_TX_CONF_FQ: + return "Tx conf"; + case DPAA2_RX_ERR_FQ: + return "Rx err"; + default: + return "N/A"; + } +} + +static int dpaa2_dbg_fqs_show(struct seq_file *file, void *offset) +{ + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; + struct dpaa2_eth_fq *fq; + u32 fcnt, bcnt; + int i, err; + + seq_printf(file, "non-zero FQ stats for %s:\n", priv->net_dev->name); + seq_printf(file, "%s%16s%16s%16s%16s%16s\n", + "VFQID", "CPU", "Traffic Class", "Type", "Frames", + "Pending frames"); + + for (i = 0; i < priv->num_fqs; i++) { + fq = &priv->fq[i]; + err = dpaa2_io_query_fq_count(NULL, fq->fqid, &fcnt, &bcnt); + if (err) + fcnt = 0; + + /* A lot of queues, no use displaying zero traffic ones */ + if (!fq->stats.frames && !fcnt) + continue; + + seq_printf(file, "%5d%16d%16d%16s%16llu%16u\n", + fq->fqid, + fq->target_cpu, + fq->tc, + fq_type_to_str(fq), + fq->stats.frames, + fcnt); + } + + return 0; +} + +static int dpaa2_dbg_fqs_open(struct inode *inode, struct file *file) +{ + int err; + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; + + err = single_open(file, dpaa2_dbg_fqs_show, priv); + if (err < 0) + netdev_err(priv->net_dev, "single_open() failed\n"); + + return err; +} + +static const struct file_operations dpaa2_dbg_fq_ops = { + .open = dpaa2_dbg_fqs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int dpaa2_dbg_ch_show(struct seq_file *file, void *offset) +{ + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)file->private; + struct dpaa2_eth_channel *ch; + int i; + + seq_printf(file, "Channel stats for %s:\n", priv->net_dev->name); + seq_printf(file, "%s%16s%16s%16s%16s%16s%16s\n", + "CHID", "CPU", "Deq busy", "Frames", "CDANs", + "Avg frm/CDAN", "Buf count"); + + for (i = 0; i < priv->num_channels; i++) { + ch = priv->channel[i]; + seq_printf(file, "%4d%16d%16llu%16llu%16llu%16llu%16d\n", + ch->ch_id, + ch->nctx.desired_cpu, + ch->stats.dequeue_portal_busy, + ch->stats.frames, + ch->stats.cdan, + ch->stats.frames / ch->stats.cdan, + ch->buf_count); + } + + return 0; +} + +static int dpaa2_dbg_ch_open(struct inode *inode, struct file *file) +{ + int err; + struct dpaa2_eth_priv *priv = (struct dpaa2_eth_priv *)inode->i_private; + + err = single_open(file, dpaa2_dbg_ch_show, priv); + if (err < 0) + netdev_err(priv->net_dev, "single_open() failed\n"); + + return err; +} + +static const struct file_operations dpaa2_dbg_ch_ops = { + .open = dpaa2_dbg_ch_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t dpaa2_dbg_reset_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + struct dpaa2_eth_priv *priv = file->private_data; + struct rtnl_link_stats64 *percpu_stats; + struct dpaa2_eth_drv_stats *percpu_extras; + struct dpaa2_eth_fq *fq; + struct dpaa2_eth_channel *ch; + int i; + + for_each_online_cpu(i) { + percpu_stats = per_cpu_ptr(priv->percpu_stats, i); + memset(percpu_stats, 0, sizeof(*percpu_stats)); + + percpu_extras = per_cpu_ptr(priv->percpu_extras, i); + memset(percpu_extras, 0, sizeof(*percpu_extras)); + } + + for (i = 0; i < priv->num_fqs; i++) { + fq = &priv->fq[i]; + memset(&fq->stats, 0, sizeof(fq->stats)); + } + + for (i = 0; i < priv->num_channels; i++) { + ch = priv->channel[i]; + memset(&ch->stats, 0, sizeof(ch->stats)); + } + + return count; +} + +static const struct file_operations dpaa2_dbg_reset_ops = { + .open = simple_open, + .write = dpaa2_dbg_reset_write, +}; + +static ssize_t dpaa2_dbg_reset_mc_write(struct file *file, + const char __user *buf, + size_t count, loff_t *offset) +{ + struct dpaa2_eth_priv *priv = file->private_data; + int err; + + err = dpni_reset_statistics(priv->mc_io, 0, priv->mc_token); + if (err) + netdev_err(priv->net_dev, + "dpni_reset_statistics() failed %d\n", err); + + return count; +} + +static const struct file_operations dpaa2_dbg_reset_mc_ops = { + .open = simple_open, + .write = dpaa2_dbg_reset_mc_write, +}; + +void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) +{ + if (!dpaa2_dbg_root) + return; + + /* Create a directory for the interface */ + priv->dbg.dir = debugfs_create_dir(priv->net_dev->name, + dpaa2_dbg_root); + if (!priv->dbg.dir) { + netdev_err(priv->net_dev, "debugfs_create_dir() failed\n"); + return; + } + + /* per-cpu stats file */ + priv->dbg.cpu_stats = debugfs_create_file("cpu_stats", 0444, + priv->dbg.dir, priv, + &dpaa2_dbg_cpu_ops); + if (!priv->dbg.cpu_stats) { + netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); + goto err_cpu_stats; + } + + /* per-fq stats file */ + priv->dbg.fq_stats = debugfs_create_file("fq_stats", 0444, + priv->dbg.dir, priv, + &dpaa2_dbg_fq_ops); + if (!priv->dbg.fq_stats) { + netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); + goto err_fq_stats; + } + + /* per-fq stats file */ + priv->dbg.ch_stats = debugfs_create_file("ch_stats", 0444, + priv->dbg.dir, priv, + &dpaa2_dbg_ch_ops); + if (!priv->dbg.fq_stats) { + netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); + goto err_ch_stats; + } + + /* reset stats */ + priv->dbg.reset_stats = debugfs_create_file("reset_stats", 0200, + priv->dbg.dir, priv, + &dpaa2_dbg_reset_ops); + if (!priv->dbg.reset_stats) { + netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); + goto err_reset_stats; + } + + /* reset MC stats */ + priv->dbg.reset_mc_stats = debugfs_create_file("reset_mc_stats", + 0222, priv->dbg.dir, priv, + &dpaa2_dbg_reset_mc_ops); + if (!priv->dbg.reset_mc_stats) { + netdev_err(priv->net_dev, "debugfs_create_file() failed\n"); + goto err_reset_mc_stats; + } + + return; + +err_reset_mc_stats: + debugfs_remove(priv->dbg.reset_stats); +err_reset_stats: + debugfs_remove(priv->dbg.ch_stats); +err_ch_stats: + debugfs_remove(priv->dbg.fq_stats); +err_fq_stats: + debugfs_remove(priv->dbg.cpu_stats); +err_cpu_stats: + debugfs_remove(priv->dbg.dir); +} + +void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) +{ + debugfs_remove(priv->dbg.reset_mc_stats); + debugfs_remove(priv->dbg.reset_stats); + debugfs_remove(priv->dbg.fq_stats); + debugfs_remove(priv->dbg.ch_stats); + debugfs_remove(priv->dbg.cpu_stats); + debugfs_remove(priv->dbg.dir); +} + +void dpaa2_eth_dbg_init(void) +{ + dpaa2_dbg_root = debugfs_create_dir(DPAA2_ETH_DBG_ROOT, NULL); + if (!dpaa2_dbg_root) { + pr_err("DPAA2-ETH: debugfs create failed\n"); + return; + } + + pr_info("DPAA2-ETH: debugfs created\n"); +} + +void __exit dpaa2_eth_dbg_exit(void) +{ + debugfs_remove(dpaa2_dbg_root); +} --- /dev/null +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-debugfs.h @@ -0,0 +1,60 @@ +/* Copyright 2015 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DPAA2_ETH_DEBUGFS_H +#define DPAA2_ETH_DEBUGFS_H + +#include + +struct dpaa2_eth_priv; + +struct dpaa2_debugfs { + struct dentry *dir; + struct dentry *fq_stats; + struct dentry *ch_stats; + struct dentry *cpu_stats; + struct dentry *reset_stats; + struct dentry *reset_mc_stats; +}; + +#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS +void dpaa2_eth_dbg_init(void); +void dpaa2_eth_dbg_exit(void); +void dpaa2_dbg_add(struct dpaa2_eth_priv *priv); +void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv); +#else +static inline void dpaa2_eth_dbg_init(void) {} +static inline void dpaa2_eth_dbg_exit(void) {} +static inline void dpaa2_dbg_add(struct dpaa2_eth_priv *priv) {} +static inline void dpaa2_dbg_remove(struct dpaa2_eth_priv *priv) {} +#endif /* CONFIG_FSL_DPAA2_ETH_DEBUGFS */ + +#endif /* DPAA2_ETH_DEBUGFS_H */ --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth-trace.h @@ -1,32 +1,5 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2014-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #undef TRACE_SYSTEM --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.c @@ -1,33 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2014-2016 Freescale Semiconductor Inc. * Copyright 2016-2017 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include @@ -38,9 +11,14 @@ #include #include #include - +#include +#include +#include +#include +#include #include "../../fsl-mc/include/mc.h" #include "dpaa2-eth.h" +#include "dpaa2-eth-ceetm.h" /* CREATE_TRACE_POINTS only needs to be defined once. Other dpa files * using trace events only need to #include @@ -52,8 +30,6 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION("Freescale DPAA2 Ethernet Driver"); -const char dpaa2_eth_drv_version[] = "0.1"; - static void *dpaa2_iova_to_virt(struct iommu_domain *domain, dma_addr_t iova_addr) { @@ -104,26 +80,27 @@ static void free_rx_fd(struct dpaa2_eth_ /* We don't support any other format */ return; - /* For S/G frames, we first need to free all SG entries */ + /* For S/G frames, we first need to free all SG entries + * except the first one, which was taken care of already + */ sgt = vaddr + dpaa2_fd_get_offset(fd); - for (i = 0; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { + for (i = 1; i < DPAA2_ETH_MAX_SG_ENTRIES; i++) { addr = dpaa2_sg_get_addr(&sgt[i]); sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); - dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, - DMA_FROM_DEVICE); + dma_unmap_page(dev, addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); - skb_free_frag(sg_vaddr); + free_pages((unsigned long)sg_vaddr, 0); if (dpaa2_sg_is_final(&sgt[i])) break; } free_buf: - skb_free_frag(vaddr); + free_pages((unsigned long)vaddr, 0); } /* Build a linear skb based on a single-buffer frame descriptor */ -static struct sk_buff *build_linear_skb(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, +static struct sk_buff *build_linear_skb(struct dpaa2_eth_channel *ch, const struct dpaa2_fd *fd, void *fd_vaddr) { @@ -133,8 +110,7 @@ static struct sk_buff *build_linear_skb( ch->buf_count--; - skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_SIZE + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + skb = build_skb(fd_vaddr, DPAA2_ETH_RX_BUF_RAW_SIZE); if (unlikely(!skb)) return NULL; @@ -169,16 +145,20 @@ static struct sk_buff *build_frag_skb(st /* Get the address and length from the S/G entry */ sg_addr = dpaa2_sg_get_addr(sge); sg_vaddr = dpaa2_iova_to_virt(priv->iommu_domain, sg_addr); - dma_unmap_single(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE, - DMA_FROM_DEVICE); + dma_unmap_page(dev, sg_addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); sg_length = dpaa2_sg_get_len(sge); if (i == 0) { /* We build the skb around the first data buffer */ - skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_SIZE + - SKB_DATA_ALIGN(sizeof(struct skb_shared_info))); + skb = build_skb(sg_vaddr, DPAA2_ETH_RX_BUF_RAW_SIZE); if (unlikely(!skb)) { + /* Free the first SG entry now, since we already + * unmapped it and obtained the virtual address + */ + free_pages((unsigned long)sg_vaddr, 0); + /* We still need to subtract the buffers used * by this FD from our software counter */ @@ -213,17 +193,172 @@ static struct sk_buff *build_frag_skb(st break; } + WARN_ONCE(i == DPAA2_ETH_MAX_SG_ENTRIES, "Final bit not set in SGT"); + /* Count all data buffers + SG table buffer */ ch->buf_count -= i + 2; return skb; } +static int dpaa2_eth_xdp_tx(struct dpaa2_eth_priv *priv, + struct dpaa2_fd *fd, + void *buf_start, + u16 queue_id) +{ + struct dpaa2_eth_fq *fq; + struct rtnl_link_stats64 *percpu_stats; + struct dpaa2_eth_drv_stats *percpu_extras; + struct dpaa2_faead *faead; + u32 ctrl, frc; + int i, err; + + /* Mark the egress frame annotation area as valid */ + frc = dpaa2_fd_get_frc(fd); + dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); + dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL); + + ctrl = DPAA2_FAEAD_A4V | DPAA2_FAEAD_A2V | DPAA2_FAEAD_EBDDV; + faead = dpaa2_get_faead(buf_start, false); + faead->ctrl = cpu_to_le32(ctrl); + faead->conf_fqid = 0; + + percpu_stats = this_cpu_ptr(priv->percpu_stats); + percpu_extras = this_cpu_ptr(priv->percpu_extras); + + fq = &priv->fq[queue_id]; + for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) { + err = priv->enqueue(priv, fq, fd, 0); + if (err != -EBUSY) + break; + } + + percpu_extras->tx_portal_busy += i; + if (unlikely(err)) { + percpu_stats->tx_errors++; + } else { + percpu_stats->tx_packets++; + percpu_stats->tx_bytes += dpaa2_fd_get_len(fd); + } + + return err; +} + +/* Free buffers acquired from the buffer pool or which were meant to + * be released in the pool + */ +static void free_bufs(struct dpaa2_eth_priv *priv, u64 *buf_array, int count) +{ + struct device *dev = priv->net_dev->dev.parent; + void *vaddr; + int i; + + for (i = 0; i < count; i++) { + vaddr = dpaa2_iova_to_virt(priv->iommu_domain, buf_array[i]); + dma_unmap_page(dev, buf_array[i], DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); + free_pages((unsigned long)vaddr, 0); + } +} + +static void release_fd_buf(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + dma_addr_t addr) +{ + int err; + + ch->rel_buf_array[ch->rel_buf_cnt++] = addr; + if (likely(ch->rel_buf_cnt < DPAA2_ETH_BUFS_PER_CMD)) + return; + + while ((err = dpaa2_io_service_release(ch->dpio, priv->bpid, + ch->rel_buf_array, + ch->rel_buf_cnt)) == -EBUSY) + cpu_relax(); + + if (err) + free_bufs(priv, ch->rel_buf_array, ch->rel_buf_cnt); + + ch->rel_buf_cnt = 0; +} + +static u32 dpaa2_eth_run_xdp(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + struct dpaa2_fd *fd, + u16 queue_id, + void *vaddr) +{ + struct device *dev = priv->net_dev->dev.parent; + dma_addr_t addr = dpaa2_fd_get_addr(fd); + struct rtnl_link_stats64 *percpu_stats; + struct bpf_prog *xdp_prog; + struct xdp_buff xdp; + u32 xdp_act = XDP_PASS; + + xdp_prog = READ_ONCE(ch->xdp_prog); + if (!xdp_prog) + return xdp_act; + + percpu_stats = this_cpu_ptr(priv->percpu_stats); + + xdp.data = vaddr + dpaa2_fd_get_offset(fd); + xdp.data_end = xdp.data + dpaa2_fd_get_len(fd); + /* Allow the XDP program to use the specially reserved headroom */ + xdp.data_hard_start = xdp.data - XDP_PACKET_HEADROOM; + + rcu_read_lock(); + xdp_act = bpf_prog_run_xdp(xdp_prog, &xdp); + + /* xdp.data pointer may have changed */ + dpaa2_fd_set_offset(fd, xdp.data - vaddr); + dpaa2_fd_set_len(fd, xdp.data_end - xdp.data); + + switch (xdp_act) { + case XDP_PASS: + break; + default: + bpf_warn_invalid_xdp_action(xdp_act); + case XDP_ABORTED: + case XDP_DROP: + /* This is our buffer, so we can release it back to hardware */ + release_fd_buf(priv, ch, addr); + percpu_stats->rx_dropped++; + break; + case XDP_TX: + if (dpaa2_eth_xdp_tx(priv, fd, vaddr, queue_id)) { + dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); + free_rx_fd(priv, fd, vaddr); + ch->buf_count--; + } + break; + case XDP_REDIRECT: + dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); + ch->buf_count--; + ch->flush = true; + /* Mark the actual start of the data buffer */ + xdp.data_hard_start = vaddr; + if (xdp_do_redirect(priv->net_dev, &xdp, xdp_prog)) + free_rx_fd(priv, fd, vaddr); + break; + } + + if (xdp_act == XDP_TX || xdp_act == XDP_REDIRECT) { + percpu_stats->rx_packets++; + percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); + } + + rcu_read_unlock(); + + return xdp_act; +} + /* Main Rx frame processing routine */ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *ch, const struct dpaa2_fd *fd, - struct napi_struct *napi) + struct dpaa2_eth_fq *fq) { dma_addr_t addr = dpaa2_fd_get_addr(fd); u8 fd_format = dpaa2_fd_get_format(fd); @@ -235,14 +370,16 @@ static void dpaa2_eth_rx(struct dpaa2_et struct dpaa2_fas *fas; void *buf_data; u32 status = 0; + u32 xdp_act; /* Tracing point */ trace_dpaa2_rx_fd(priv->net_dev, fd); vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); - dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(dev, addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); - fas = dpaa2_get_fas(vaddr); + fas = dpaa2_get_fas(vaddr, false); prefetch(fas); buf_data = vaddr + dpaa2_fd_get_offset(fd); prefetch(buf_data); @@ -251,22 +388,43 @@ static void dpaa2_eth_rx(struct dpaa2_et percpu_extras = this_cpu_ptr(priv->percpu_extras); if (fd_format == dpaa2_fd_single) { - skb = build_linear_skb(priv, ch, fd, vaddr); + xdp_act = dpaa2_eth_run_xdp(priv, ch, (struct dpaa2_fd *)fd, + fq->flowid, vaddr); + if (xdp_act != XDP_PASS) + return; + + dma_unmap_page(dev, addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); + skb = build_linear_skb(ch, fd, vaddr); } else if (fd_format == dpaa2_fd_sg) { + dma_unmap_page(dev, addr, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); skb = build_frag_skb(priv, ch, buf_data); - skb_free_frag(vaddr); + free_pages((unsigned long)vaddr, 0); percpu_extras->rx_sg_frames++; percpu_extras->rx_sg_bytes += dpaa2_fd_get_len(fd); } else { /* We don't support any other format */ - goto err_frame_format; + goto drop_cnt; } if (unlikely(!skb)) - goto err_build_skb; + goto drop_fd; prefetch(skb->data); + /* Get the timestamp value */ + if (priv->ts_rx_en) { + struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb); + __le64 *ts = dpaa2_get_ts(vaddr, false); + u64 ns; + + memset(shhwtstamps, 0, sizeof(*shhwtstamps)); + + ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ts); + shhwtstamps->hwtstamp = ns_to_ktime(ns); + } + /* Check if we need to validate the L4 csum */ if (likely(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV)) { status = le32_to_cpu(fas->status); @@ -274,30 +432,80 @@ static void dpaa2_eth_rx(struct dpaa2_et } skb->protocol = eth_type_trans(skb, priv->net_dev); + skb_record_rx_queue(skb, fq->flowid); percpu_stats->rx_packets++; percpu_stats->rx_bytes += dpaa2_fd_get_len(fd); - napi_gro_receive(napi, skb); + napi_gro_receive(&ch->napi, skb); return; -err_build_skb: +drop_fd: free_rx_fd(priv, fd, vaddr); -err_frame_format: +drop_cnt: percpu_stats->rx_dropped++; } +#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE +/* Processing of Rx frames received on the error FQ + * We check and print the error bits and then free the frame + */ +static void dpaa2_eth_rx_err(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct napi_struct *napi __always_unused, + u16 queue_id __always_unused) +{ + struct device *dev = priv->net_dev->dev.parent; + dma_addr_t addr = dpaa2_fd_get_addr(fd); + void *vaddr; + struct rtnl_link_stats64 *percpu_stats; + struct dpaa2_fas *fas; + u32 status = 0; + u32 fd_errors; + bool has_fas_errors = false; + + vaddr = dpaa2_iova_to_virt(priv->iommu_domain, addr); + dma_unmap_single(dev, addr, DPAA2_ETH_RX_BUF_SIZE, DMA_BIDIRECTIONAL); + + /* check frame errors in the FD field */ + fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_RX_ERR_MASK; + if (likely(fd_errors)) { + has_fas_errors = (fd_errors & FD_CTRL_FAERR) && + !!(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV); + if (net_ratelimit()) + netdev_dbg(priv->net_dev, "RX frame FD err: %08x\n", + fd_errors); + } + + /* check frame errors in the FAS field */ + if (has_fas_errors) { + fas = dpaa2_get_fas(vaddr, false); + status = le32_to_cpu(fas->status); + if (net_ratelimit()) + netdev_dbg(priv->net_dev, "Rx frame FAS err: 0x%08x\n", + status & DPAA2_FAS_RX_ERR_MASK); + } + free_rx_fd(priv, fd, vaddr); + + percpu_stats = this_cpu_ptr(priv->percpu_stats); + percpu_stats->rx_errors++; + ch->buf_count--; +} +#endif + /* Consume all frames pull-dequeued into the store. This is the simplest way to * make sure we don't accidentally issue another volatile dequeue which would * overwrite (leak) frames already in the store. * * Observance of NAPI budget is not our concern, leaving that to the caller. */ -static int consume_frames(struct dpaa2_eth_channel *ch) +static int consume_frames(struct dpaa2_eth_channel *ch, + struct dpaa2_eth_fq **src) { struct dpaa2_eth_priv *priv = ch->priv; - struct dpaa2_eth_fq *fq; + struct dpaa2_eth_fq *fq = NULL; struct dpaa2_dq *dq; const struct dpaa2_fd *fd; int cleaned = 0; @@ -315,16 +523,51 @@ static int consume_frames(struct dpaa2_e } fd = dpaa2_dq_fd(dq); + prefetch(fd); + fq = (struct dpaa2_eth_fq *)(uintptr_t)dpaa2_dq_fqd_ctx(dq); - fq->stats.frames++; - fq->consume(priv, ch, fd, &ch->napi); + fq->consume(priv, ch, fd, fq); cleaned++; } while (!is_last); + if (!cleaned) + return 0; + + fq->stats.frames += cleaned; + ch->stats.frames += cleaned; + + /* A dequeue operation only pulls frames from a single queue + * into the store. Return the frame queue as an out param. + */ + if (src) + *src = fq; + return cleaned; } +/* Configure the egress frame annotation for timestamp update */ +static void enable_tx_tstamp(struct dpaa2_fd *fd, void *buf_start) +{ + struct dpaa2_faead *faead; + u32 ctrl, frc; + + /* Mark the egress frame annotation area as valid */ + frc = dpaa2_fd_get_frc(fd); + dpaa2_fd_set_frc(fd, frc | DPAA2_FD_FRC_FAEADV); + + /* Set hardware annotation size */ + ctrl = dpaa2_fd_get_ctrl(fd); + dpaa2_fd_set_ctrl(fd, ctrl | DPAA2_FD_CTRL_ASAL); + + /* enable UPD (update prepanded data) bit in FAEAD field of + * hardware frame annotation area + */ + ctrl = DPAA2_FAEAD_A2V | DPAA2_FAEAD_UPDV | DPAA2_FAEAD_UPD; + faead = dpaa2_get_faead(buf_start, true); + faead->ctrl = cpu_to_le32(ctrl); +} + /* Create a frame descriptor based on a fragmented skb */ static int build_sg_fd(struct dpaa2_eth_priv *priv, struct sk_buff *skb, @@ -341,7 +584,6 @@ static int build_sg_fd(struct dpaa2_eth_ int num_sg; int num_dma_bufs; struct dpaa2_eth_swa *swa; - struct dpaa2_fas *fas; /* Create and map scatterlist. * We don't advertise NETIF_F_FRAGLIST, so skb_to_sgvec() will not have @@ -365,21 +607,14 @@ static int build_sg_fd(struct dpaa2_eth_ /* Prepare the HW SGT structure */ sgt_buf_size = priv->tx_data_offset + - sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs); - sgt_buf = kzalloc(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN, GFP_ATOMIC); + sizeof(struct dpaa2_sg_entry) * num_dma_bufs; + sgt_buf = netdev_alloc_frag(sgt_buf_size + DPAA2_ETH_TX_BUF_ALIGN); if (unlikely(!sgt_buf)) { err = -ENOMEM; goto sgt_buf_alloc_failed; } sgt_buf = PTR_ALIGN(sgt_buf, DPAA2_ETH_TX_BUF_ALIGN); - - /* PTA from egress side is passed as is to the confirmation side so - * we need to clear some fields here in order to find consistent values - * on TX confirmation. We are clearing FAS (Frame Annotation Status) - * field from the hardware annotation area - */ - fas = dpaa2_get_fas(sgt_buf); - memset(fas, 0, DPAA2_FAS_SIZE); + memset(sgt_buf, 0, sgt_buf_size); sgt = (struct dpaa2_sg_entry *)(sgt_buf + priv->tx_data_offset); @@ -402,10 +637,11 @@ static int build_sg_fd(struct dpaa2_eth_ * all of them on Tx Conf. */ swa = (struct dpaa2_eth_swa *)sgt_buf; - swa->skb = skb; - swa->scl = scl; - swa->num_sg = num_sg; - swa->num_dma_bufs = num_dma_bufs; + swa->type = DPAA2_ETH_SWA_SG; + swa->sg.skb = skb; + swa->sg.scl = scl; + swa->sg.num_sg = num_sg; + swa->sg.sgt_size = sgt_buf_size; /* Separately map the SGT buffer */ addr = dma_map_single(dev, sgt_buf, sgt_buf_size, DMA_BIDIRECTIONAL); @@ -417,13 +653,15 @@ static int build_sg_fd(struct dpaa2_eth_ dpaa2_fd_set_format(fd, dpaa2_fd_sg); dpaa2_fd_set_addr(fd, addr); dpaa2_fd_set_len(fd, skb->len); - dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | - DPAA2_FD_CTRL_PTV1); + dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); + + if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) + enable_tx_tstamp(fd, sgt_buf); return 0; dma_map_single_failed: - kfree(sgt_buf); + skb_free_frag(sgt_buf); sgt_buf_alloc_failed: dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL); dma_map_sg_failed: @@ -437,29 +675,27 @@ static int build_single_fd(struct dpaa2_ struct dpaa2_fd *fd) { struct device *dev = priv->net_dev->dev.parent; - u8 *buffer_start; - struct dpaa2_fas *fas; - struct sk_buff **skbh; + u8 *buffer_start, *aligned_start; + struct dpaa2_eth_swa *swa; dma_addr_t addr; - buffer_start = PTR_ALIGN(skb->data - priv->tx_data_offset - - DPAA2_ETH_TX_BUF_ALIGN, - DPAA2_ETH_TX_BUF_ALIGN); - - /* PTA from egress side is passed as is to the confirmation side so - * we need to clear some fields here in order to find consistent values - * on TX confirmation. We are clearing FAS (Frame Annotation Status) - * field from the hardware annotation area + buffer_start = skb->data - dpaa2_eth_needed_headroom(priv, skb); + + /* If there's enough room to align the FD address, do it. + * It will help hardware optimize accesses. */ - fas = dpaa2_get_fas(buffer_start); - memset(fas, 0, DPAA2_FAS_SIZE); + aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN, + DPAA2_ETH_TX_BUF_ALIGN); + if (aligned_start >= skb->head) + buffer_start = aligned_start; /* Store a backpointer to the skb at the beginning of the buffer * (in the private data area) such that we can release it * on Tx confirm */ - skbh = (struct sk_buff **)buffer_start; - *skbh = skb; + swa = (struct dpaa2_eth_swa *)buffer_start; + swa->type = DPAA2_ETH_SWA_SINGLE; + swa->single.skb = skb; addr = dma_map_single(dev, buffer_start, skb_tail_pointer(skb) - buffer_start, @@ -471,8 +707,10 @@ static int build_single_fd(struct dpaa2_ dpaa2_fd_set_offset(fd, (u16)(skb->data - buffer_start)); dpaa2_fd_set_len(fd, skb->len); dpaa2_fd_set_format(fd, dpaa2_fd_single); - dpaa2_fd_set_ctrl(fd, DPAA2_FD_CTRL_ASAL | DPAA2_FD_CTRL_PTA | - DPAA2_FD_CTRL_PTV1); + dpaa2_fd_set_ctrl(fd, FD_CTRL_PTA); + + if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) + enable_tx_tstamp(fd, buffer_start); return 0; } @@ -483,72 +721,75 @@ static int build_single_fd(struct dpaa2_ * back-pointed to is also freed. * This can be called either from dpaa2_eth_tx_conf() or on the error path of * dpaa2_eth_tx(). - * Optionally, return the frame annotation status word (FAS), which needs - * to be checked if we're on the confirmation path. */ static void free_tx_fd(const struct dpaa2_eth_priv *priv, - const struct dpaa2_fd *fd, - u32 *status) + const struct dpaa2_fd *fd, bool in_napi) { struct device *dev = priv->net_dev->dev.parent; dma_addr_t fd_addr; - struct sk_buff **skbh, *skb; + struct sk_buff *skb = NULL; unsigned char *buffer_start; - int unmap_size; - struct scatterlist *scl; - int num_sg, num_dma_bufs; struct dpaa2_eth_swa *swa; u8 fd_format = dpaa2_fd_get_format(fd); - struct dpaa2_fas *fas; fd_addr = dpaa2_fd_get_addr(fd); - skbh = dpaa2_iova_to_virt(priv->iommu_domain, fd_addr); - fas = dpaa2_get_fas(skbh); + buffer_start = dpaa2_iova_to_virt(priv->iommu_domain, fd_addr); + swa = (struct dpaa2_eth_swa *)buffer_start; if (fd_format == dpaa2_fd_single) { - skb = *skbh; - buffer_start = (unsigned char *)skbh; - /* Accessing the skb buffer is safe before dma unmap, because - * we didn't map the actual skb shell. - */ - dma_unmap_single(dev, fd_addr, - skb_tail_pointer(skb) - buffer_start, - DMA_BIDIRECTIONAL); + if (swa->type == DPAA2_ETH_SWA_SINGLE) { + skb = swa->single.skb; + /* Accessing the skb buffer is safe before dma unmap, + * because we didn't map the actual skb shell. + */ + dma_unmap_single(dev, fd_addr, + skb_tail_pointer(skb) - buffer_start, + DMA_BIDIRECTIONAL); + } else { + WARN_ONCE(swa->type != DPAA2_ETH_SWA_XDP, + "Wrong SWA type"); + dma_unmap_single(dev, fd_addr, swa->xdp.dma_size, + DMA_BIDIRECTIONAL); + } } else if (fd_format == dpaa2_fd_sg) { - swa = (struct dpaa2_eth_swa *)skbh; - skb = swa->skb; - scl = swa->scl; - num_sg = swa->num_sg; - num_dma_bufs = swa->num_dma_bufs; + skb = swa->sg.skb; /* Unmap the scatterlist */ - dma_unmap_sg(dev, scl, num_sg, DMA_BIDIRECTIONAL); - kfree(scl); + dma_unmap_sg(dev, swa->sg.scl, swa->sg.num_sg, DMA_BIDIRECTIONAL); + kfree(swa->sg.scl); /* Unmap the SGT buffer */ - unmap_size = priv->tx_data_offset + - sizeof(struct dpaa2_sg_entry) * (1 + num_dma_bufs); - dma_unmap_single(dev, fd_addr, unmap_size, DMA_BIDIRECTIONAL); + dma_unmap_single(dev, fd_addr, swa->sg.sgt_size, + DMA_BIDIRECTIONAL); } else { - /* Unsupported format, mark it as errored and give up */ - if (status) - *status = ~0; + netdev_dbg(priv->net_dev, "Invalid FD format\n"); return; } - /* Read the status from the Frame Annotation after we unmap the first - * buffer but before we free it. The caller function is responsible - * for checking the status value. - */ - if (status) - *status = le32_to_cpu(fas->status); + if (swa->type == DPAA2_ETH_SWA_XDP) { + page_frag_free(buffer_start); + return; + } + + /* Get the timestamp value */ + if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) { + struct skb_shared_hwtstamps shhwtstamps; + __le64 *ts = dpaa2_get_ts(buffer_start, true); + u64 ns; + + memset(&shhwtstamps, 0, sizeof(shhwtstamps)); + + ns = DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS * le64_to_cpup(ts); + shhwtstamps.hwtstamp = ns_to_ktime(ns); + skb_tstamp_tx(skb, &shhwtstamps); + } - /* Free SGT buffer kmalloc'ed on tx */ + /* Free SGT buffer allocated on tx */ if (fd_format != dpaa2_fd_single) - kfree(skbh); + skb_free_frag(buffer_start); /* Move on with skb release */ - dev_kfree_skb(skb); + napi_consume_skb(skb, in_napi); } static netdev_tx_t dpaa2_eth_tx(struct sk_buff *skb, struct net_device *net_dev) @@ -558,20 +799,41 @@ static netdev_tx_t dpaa2_eth_tx(struct s struct rtnl_link_stats64 *percpu_stats; struct dpaa2_eth_drv_stats *percpu_extras; struct dpaa2_eth_fq *fq; + struct netdev_queue *nq; u16 queue_mapping; - int err, i; + unsigned int needed_headroom; + u32 fd_len; + u8 prio; + int err, i, ch_id = 0; + + queue_mapping = skb_get_queue_mapping(skb); + prio = netdev_txq_to_tc(net_dev, queue_mapping); + /* Hardware interprets priority level 0 as being the highest, + * so we need to do a reverse mapping to the netdev tc index + */ + if (net_dev->num_tc) + prio = net_dev->num_tc - prio - 1; + + queue_mapping %= dpaa2_eth_queue_count(priv); + fq = &priv->fq[queue_mapping]; percpu_stats = this_cpu_ptr(priv->percpu_stats); percpu_extras = this_cpu_ptr(priv->percpu_extras); - if (unlikely(skb_headroom(skb) < DPAA2_ETH_NEEDED_HEADROOM(priv))) { + needed_headroom = dpaa2_eth_needed_headroom(priv, skb); + if (skb_headroom(skb) < needed_headroom) { struct sk_buff *ns; - ns = skb_realloc_headroom(skb, DPAA2_ETH_NEEDED_HEADROOM(priv)); + ns = skb_realloc_headroom(skb, needed_headroom); if (unlikely(!ns)) { percpu_stats->tx_dropped++; goto err_alloc_headroom; } + percpu_extras->tx_reallocs++; + + if (skb->sk) + skb_set_owner_w(ns, skb->sk); + dev_kfree_skb(skb); skb = ns; } @@ -602,17 +864,24 @@ static netdev_tx_t dpaa2_eth_tx(struct s goto err_build_fd; } + if (dpaa2_eth_ceetm_is_enabled(priv)) { + err = dpaa2_ceetm_classify(skb, net_dev->qdisc, &ch_id, &prio); + if (err) + goto err_ceetm_classify; + } + /* Tracing point */ trace_dpaa2_tx_fd(net_dev, &fd); - /* TxConf FQ selection primarily based on cpu affinity; this is - * non-migratable context, so it's safe to call smp_processor_id(). + fd_len = dpaa2_fd_get_len(&fd); + nq = netdev_get_tx_queue(net_dev, queue_mapping); + netdev_tx_sent_queue(nq, fd_len); + + /* Everything that happens after this enqueues might race with + * the Tx confirmation callback for this frame */ - queue_mapping = smp_processor_id() % dpaa2_eth_queue_count(priv); - fq = &priv->fq[queue_mapping]; for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) { - err = dpaa2_io_service_enqueue_qd(NULL, priv->tx_qdid, 0, - fq->tx_qdbin, &fd); + err = priv->enqueue(priv, fq, &fd, 0); if (err != -EBUSY) break; } @@ -620,14 +889,17 @@ static netdev_tx_t dpaa2_eth_tx(struct s if (unlikely(err < 0)) { percpu_stats->tx_errors++; /* Clean up everything, including freeing the skb */ - free_tx_fd(priv, &fd, NULL); + free_tx_fd(priv, &fd, false); + netdev_tx_completed_queue(nq, 1, fd_len); } else { percpu_stats->tx_packets++; - percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd); + percpu_stats->tx_bytes += fd_len; } return NETDEV_TX_OK; +err_ceetm_classify: + free_tx_fd(priv, &fd, false); err_build_fd: err_alloc_headroom: dev_kfree_skb(skb); @@ -637,48 +909,39 @@ err_alloc_headroom: /* Tx confirmation frame processing routine */ static void dpaa2_eth_tx_conf(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch, + struct dpaa2_eth_channel *ch __always_unused, const struct dpaa2_fd *fd, - struct napi_struct *napi __always_unused) + struct dpaa2_eth_fq *fq) { struct rtnl_link_stats64 *percpu_stats; struct dpaa2_eth_drv_stats *percpu_extras; - u32 status = 0; + u32 fd_len = dpaa2_fd_get_len(fd); u32 fd_errors; - bool has_fas_errors = false; /* Tracing point */ trace_dpaa2_tx_conf_fd(priv->net_dev, fd); percpu_extras = this_cpu_ptr(priv->percpu_extras); percpu_extras->tx_conf_frames++; - percpu_extras->tx_conf_bytes += dpaa2_fd_get_len(fd); + percpu_extras->tx_conf_bytes += fd_len; + + fq->dq_frames++; + fq->dq_bytes += fd_len; /* Check frame errors in the FD field */ fd_errors = dpaa2_fd_get_ctrl(fd) & DPAA2_FD_TX_ERR_MASK; - if (unlikely(fd_errors)) { - /* We only check error bits in the FAS field if corresponding - * FAERR bit is set in FD and the FAS field is marked as valid - */ - has_fas_errors = (fd_errors & DPAA2_FD_CTRL_FAERR) && - !!(dpaa2_fd_get_frc(fd) & DPAA2_FD_FRC_FASV); - if (net_ratelimit()) - netdev_dbg(priv->net_dev, "TX frame FD error: 0x%08x\n", - fd_errors); - } - - free_tx_fd(priv, fd, has_fas_errors ? &status : NULL); + free_tx_fd(priv, fd, true); if (likely(!fd_errors)) return; + if (net_ratelimit()) + netdev_dbg(priv->net_dev, "TX frame FD error: 0x%08x\n", + fd_errors); + percpu_stats = this_cpu_ptr(priv->percpu_stats); /* Tx-conf logically pertains to the egress path. */ percpu_stats->tx_errors++; - - if (has_fas_errors && net_ratelimit()) - netdev_dbg(priv->net_dev, "TX frame FAS error: 0x%08x\n", - status & DPAA2_FAS_TX_ERR_MASK); } static int set_rx_csum(struct dpaa2_eth_priv *priv, bool enable) @@ -728,26 +991,29 @@ static int set_tx_csum(struct dpaa2_eth_ /* Perform a single release command to add buffers * to the specified buffer pool */ -static int add_bufs(struct dpaa2_eth_priv *priv, u16 bpid) +static int add_bufs(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, u16 bpid) { struct device *dev = priv->net_dev->dev.parent; u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; - void *buf; + struct page *page; dma_addr_t addr; - int i; + int i, err; for (i = 0; i < DPAA2_ETH_BUFS_PER_CMD; i++) { /* Allocate buffer visible to WRIOP + skb shared info + * alignment padding */ - buf = napi_alloc_frag(DPAA2_ETH_BUF_RAW_SIZE); - if (unlikely(!buf)) + /* allocate one page for each Rx buffer. WRIOP sees + * the entire page except for a tailroom reserved for + * skb shared info + */ + page = dev_alloc_pages(0); + if (!page) goto err_alloc; - buf = PTR_ALIGN(buf, DPAA2_ETH_RX_BUF_ALIGN); - - addr = dma_map_single(dev, buf, DPAA2_ETH_RX_BUF_SIZE, - DMA_FROM_DEVICE); + addr = dma_map_page(dev, page, 0, DPAA2_ETH_RX_BUF_SIZE, + DMA_BIDIRECTIONAL); if (unlikely(dma_mapping_error(dev, addr))) goto err_map; @@ -755,28 +1021,33 @@ static int add_bufs(struct dpaa2_eth_pri /* tracing point */ trace_dpaa2_eth_buf_seed(priv->net_dev, - buf, DPAA2_ETH_BUF_RAW_SIZE, + page, DPAA2_ETH_RX_BUF_RAW_SIZE, addr, DPAA2_ETH_RX_BUF_SIZE, bpid); } release_bufs: - /* In case the portal is busy, retry until successful. - * The buffer release function would only fail if the QBMan portal - * was busy, which implies portal contention (i.e. more CPUs than - * portals, i.e. GPPs w/o affine DPIOs). For all practical purposes, - * there is little we can realistically do, short of giving up - - * in which case we'd risk depleting the buffer pool and never again - * receiving the Rx interrupt which would kick-start the refill logic. - * So just keep retrying, at the risk of being moved to ksoftirqd. - */ - while (dpaa2_io_service_release(NULL, bpid, buf_array, i)) + /* In case the portal is busy, retry until successful */ + while ((err = dpaa2_io_service_release(ch->dpio, bpid, + buf_array, i)) == -EBUSY) cpu_relax(); + + /* If release command failed, clean up and bail out; + * not much else we can do about it + */ + if (err) { + free_bufs(priv, buf_array, i); + return 0; + } + return i; err_map: - skb_free_frag(buf); + __free_pages(page, 0); err_alloc: + /* If we managed to allocate at least some buffers, + * release them to hardware + */ if (i) goto release_bufs; @@ -796,9 +1067,10 @@ static int seed_pool(struct dpaa2_eth_pr */ preempt_disable(); for (j = 0; j < priv->num_channels; j++) { - for (i = 0; i < DPAA2_ETH_NUM_BUFS; + priv->channel[j]->buf_count = 0; + for (i = 0; i < priv->max_bufs_per_ch; i += DPAA2_ETH_BUFS_PER_CMD) { - new_count = add_bufs(priv, bpid); + new_count = add_bufs(priv, priv->channel[j], bpid); priv->channel[j]->buf_count += new_count; if (new_count < DPAA2_ETH_BUFS_PER_CMD) { @@ -818,10 +1090,8 @@ static int seed_pool(struct dpaa2_eth_pr */ static void drain_bufs(struct dpaa2_eth_priv *priv, int count) { - struct device *dev = priv->net_dev->dev.parent; u64 buf_array[DPAA2_ETH_BUFS_PER_CMD]; - void *vaddr; - int ret, i; + int ret; do { ret = dpaa2_io_service_acquire(NULL, priv->bpid, @@ -830,27 +1100,16 @@ static void drain_bufs(struct dpaa2_eth_ netdev_err(priv->net_dev, "dpaa2_io_service_acquire() failed\n"); return; } - for (i = 0; i < ret; i++) { - /* Same logic as on regular Rx path */ - vaddr = dpaa2_iova_to_virt(priv->iommu_domain, - buf_array[i]); - dma_unmap_single(dev, buf_array[i], - DPAA2_ETH_RX_BUF_SIZE, - DMA_FROM_DEVICE); - skb_free_frag(vaddr); - } + free_bufs(priv, buf_array, ret); } while (ret); } static void drain_pool(struct dpaa2_eth_priv *priv) { - int i; - + preempt_disable(); drain_bufs(priv, DPAA2_ETH_BUFS_PER_CMD); drain_bufs(priv, 1); - - for (i = 0; i < priv->num_channels; i++) - priv->channel[i]->buf_count = 0; + preempt_enable(); } /* Function is called from softirq context only, so we don't need to guard @@ -862,19 +1121,19 @@ static int refill_pool(struct dpaa2_eth_ { int new_count; - if (likely(ch->buf_count >= DPAA2_ETH_REFILL_THRESH)) + if (likely(ch->buf_count >= priv->refill_thresh)) return 0; do { - new_count = add_bufs(priv, bpid); + new_count = add_bufs(priv, ch, bpid); if (unlikely(!new_count)) { /* Out of memory; abort for now, we'll try later on */ break; } ch->buf_count += new_count; - } while (ch->buf_count < DPAA2_ETH_NUM_BUFS); + } while (ch->buf_count < priv->max_bufs_per_ch); - if (unlikely(ch->buf_count < DPAA2_ETH_NUM_BUFS)) + if (unlikely(ch->buf_count < priv->max_bufs_per_ch)) return -ENOMEM; return 0; @@ -887,7 +1146,8 @@ static int pull_channel(struct dpaa2_eth /* Retry while portal is busy */ do { - err = dpaa2_io_service_pull_channel(NULL, ch->ch_id, ch->store); + err = dpaa2_io_service_pull_channel(ch->dpio, ch->ch_id, + ch->store); dequeues++; cpu_relax(); } while (err == -EBUSY); @@ -908,14 +1168,17 @@ static int pull_channel(struct dpaa2_eth static int dpaa2_eth_poll(struct napi_struct *napi, int budget) { struct dpaa2_eth_channel *ch; - int cleaned = 0, store_cleaned; struct dpaa2_eth_priv *priv; + int rx_cleaned = 0, txconf_cleaned = 0; + struct dpaa2_eth_fq *fq, *txc_fq = NULL; + struct netdev_queue *nq; + int store_cleaned, work_done; int err; ch = container_of(napi, struct dpaa2_eth_channel, napi); priv = ch->priv; - while (cleaned < budget) { + do { err = pull_channel(ch); if (unlikely(err)) break; @@ -923,29 +1186,56 @@ static int dpaa2_eth_poll(struct napi_st /* Refill pool if appropriate */ refill_pool(priv, ch, priv->bpid); - store_cleaned = consume_frames(ch); - cleaned += store_cleaned; + store_cleaned = consume_frames(ch, &fq); + if (!store_cleaned) + break; + if (fq->type == DPAA2_RX_FQ) { + rx_cleaned += store_cleaned; + /* If these are XDP_REDIRECT frames, flush them now */ + /* TODO: Do we need this? */ + if (ch->flush) { + xdp_do_flush_map(); + ch->flush = false; + } + } else { + txconf_cleaned += store_cleaned; + /* We have a single Tx conf FQ on this channel */ + txc_fq = fq; + } - /* If we have enough budget left for a full store, - * try a new pull dequeue, otherwise we're done here + /* If we either consumed the whole NAPI budget with Rx frames + * or we reached the Tx confirmations threshold, we're done. */ - if (store_cleaned == 0 || - cleaned > budget - DPAA2_ETH_STORE_SIZE) - break; - } + if (rx_cleaned >= budget || + txconf_cleaned >= DPAA2_ETH_TXCONF_PER_NAPI) { + work_done = budget; + goto out; + } + } while (store_cleaned); - if (cleaned < budget) { - napi_complete_done(napi, cleaned); - /* Re-enable data available notifications */ - do { - err = dpaa2_io_service_rearm(NULL, &ch->nctx); - cpu_relax(); - } while (err == -EBUSY); - } + /* We didn't consume the entire budget, so finish napi and + * re-enable data availability notifications + */ + napi_complete_done(napi, rx_cleaned); + do { + err = dpaa2_io_service_rearm(ch->dpio, &ch->nctx); + cpu_relax(); + } while (err == -EBUSY); + WARN_ONCE(err, "CDAN notifications rearm failed on core %d", + ch->nctx.desired_cpu); - ch->stats.frames += cleaned; + work_done = max(rx_cleaned, 1); - return cleaned; +out: + if (txc_fq) { + nq = netdev_get_tx_queue(priv->net_dev, txc_fq->flowid); + netdev_tx_completed_queue(nq, txc_fq->dq_frames, + txc_fq->dq_bytes); + txc_fq->dq_frames = 0; + txc_fq->dq_bytes = 0; + } + + return work_done; } static void enable_ch_napi(struct dpaa2_eth_priv *priv) @@ -970,9 +1260,23 @@ static void disable_ch_napi(struct dpaa2 } } +static void update_tx_fqids(struct dpaa2_eth_priv *priv); + +static void update_pf(struct dpaa2_eth_priv *priv, + struct dpni_link_state *state) +{ + bool pause_frames; + + pause_frames = !!(state->options & DPNI_LINK_OPT_PAUSE); + if (priv->tx_pause_frames != pause_frames) { + priv->tx_pause_frames = pause_frames; + set_rx_taildrop(priv); + } +} + static int link_state_update(struct dpaa2_eth_priv *priv) { - struct dpni_link_state state; + struct dpni_link_state state = {0}; int err; err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); @@ -988,6 +1292,8 @@ static int link_state_update(struct dpaa priv->link_state = state; if (state.up) { + update_tx_fqids(priv); + update_pf(priv, &state); netif_carrier_on(priv->net_dev); netif_tx_start_all_queues(priv->net_dev); } else { @@ -1006,28 +1312,30 @@ static int dpaa2_eth_open(struct net_dev struct dpaa2_eth_priv *priv = netdev_priv(net_dev); int err; - err = seed_pool(priv, priv->bpid); - if (err) { - /* Not much to do; the buffer pool, though not filled up, - * may still contain some buffers which would enable us - * to limp on. - */ - netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", - priv->dpbp_dev->obj_desc.id, priv->bpid); - } - /* We'll only start the txqs when the link is actually ready; make sure * we don't race against the link up notification, which may come * immediately after dpni_enable(); */ netif_tx_stop_all_queues(net_dev); - enable_ch_napi(priv); + /* Also, explicitly set carrier off, otherwise netif_carrier_ok() will * return true and cause 'ip link show' to report the LOWER_UP flag, * even though the link notification wasn't even received. */ netif_carrier_off(net_dev); + err = seed_pool(priv, priv->bpid); + if (err) { + /* Not much to do; the buffer pool, though not filled up, + * may still contain some buffers which would enable us + * to limp on. + */ + netdev_err(net_dev, "Buffer seeding failed for DPBP %d (bpid=%d)\n", + priv->dpbp_dev->obj_desc.id, priv->bpid); + } + + priv->refill_thresh = DPAA2_ETH_REFILL_THRESH(priv); + err = dpni_enable(priv->mc_io, 0, priv->mc_token); if (err < 0) { netdev_err(net_dev, "dpni_enable() failed\n"); @@ -1047,51 +1355,20 @@ static int dpaa2_eth_open(struct net_dev link_state_err: enable_err: - disable_ch_napi(priv); + priv->refill_thresh = 0; drain_pool(priv); return err; } -/* The DPIO store must be empty when we call this, - * at the end of every NAPI cycle. - */ -static u32 drain_channel(struct dpaa2_eth_priv *priv, - struct dpaa2_eth_channel *ch) +static int dpaa2_eth_stop(struct net_device *net_dev) { - u32 drained = 0, total = 0; - - do { - pull_channel(ch); - drained = consume_frames(ch); - total += drained; - } while (drained); - - return total; -} + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int dpni_enabled = 0; + int retries = 10, i; + int err = 0; -static u32 drain_ingress_frames(struct dpaa2_eth_priv *priv) -{ - struct dpaa2_eth_channel *ch; - int i; - u32 drained = 0; - - for (i = 0; i < priv->num_channels; i++) { - ch = priv->channel[i]; - drained += drain_channel(priv, ch); - } - - return drained; -} - -static int dpaa2_eth_stop(struct net_device *net_dev) -{ - struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - int dpni_enabled; - int retries = 10; - u32 drained; - - netif_tx_stop_all_queues(net_dev); - netif_carrier_off(net_dev); + netif_tx_stop_all_queues(net_dev); + netif_carrier_off(net_dev); /* Loop while dpni_disable() attempts to drain the egress FQs * and confirm them back to us. @@ -1105,56 +1382,24 @@ static int dpaa2_eth_stop(struct net_dev } while (dpni_enabled && --retries); if (!retries) { netdev_warn(net_dev, "Retry count exceeded disabling DPNI\n"); - /* Must go on and disable NAPI nonetheless, so we don't crash at - * the next "ifconfig up" + /* Must go on and finish processing pending frames, so we don't + * crash at the next "ifconfig up" */ + err = -ETIMEDOUT; } - /* Wait for NAPI to complete on every core and disable it. - * In particular, this will also prevent NAPI from being rescheduled if - * a new CDAN is serviced, effectively discarding the CDAN. We therefore - * don't even need to disarm the channels, except perhaps for the case - * of a huge coalescing value. - */ - disable_ch_napi(priv); + priv->refill_thresh = 0; - /* Manually drain the Rx and TxConf queues */ - drained = drain_ingress_frames(priv); - if (drained) - netdev_dbg(net_dev, "Drained %d frames.\n", drained); + /* Wait for all running napi poll routines to finish, so that no + * new refill operations are started + */ + for (i = 0; i < priv->num_channels; i++) + napi_synchronize(&priv->channel[i]->napi); /* Empty the buffer pool */ drain_pool(priv); - return 0; -} - -static int dpaa2_eth_init(struct net_device *net_dev) -{ - u64 supported = 0; - u64 not_supported = 0; - struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - u32 options = priv->dpni_attrs.options; - - /* Capabilities listing */ - supported |= IFF_LIVE_ADDR_CHANGE; - - if (options & DPNI_OPT_NO_MAC_FILTER) - not_supported |= IFF_UNICAST_FLT; - else - supported |= IFF_UNICAST_FLT; - - net_dev->priv_flags |= supported; - net_dev->priv_flags &= ~not_supported; - - /* Features */ - net_dev->features = NETIF_F_RXCSUM | - NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | - NETIF_F_SG | NETIF_F_HIGHDMA | - NETIF_F_LLTX; - net_dev->hw_features = net_dev->features; - - return 0; + return err; } static int dpaa2_eth_set_addr(struct net_device *net_dev, void *addr) @@ -1200,25 +1445,6 @@ static void dpaa2_eth_get_stats(struct n } } -static int dpaa2_eth_change_mtu(struct net_device *net_dev, int mtu) -{ - struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - int err; - - /* Set the maximum Rx frame length to match the transmit side; - * account for L2 headers when computing the MFL - */ - err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, - (u16)DPAA2_ETH_L2_MAX_FRM(mtu)); - if (err) { - netdev_err(net_dev, "dpni_set_max_frame_length() failed\n"); - return err; - } - - net_dev->mtu = mtu; - return 0; -} - /* Copy mac unicast addresses from @net_dev to @priv. * Its sole purpose is to make dpaa2_eth_set_rx_mode() more readable. */ @@ -1380,16 +1606,430 @@ static int dpaa2_eth_set_features(struct return 0; } +static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + struct hwtstamp_config config; + + if (copy_from_user(&config, rq->ifr_data, sizeof(config))) + return -EFAULT; + + switch (config.tx_type) { + case HWTSTAMP_TX_OFF: + priv->ts_tx_en = false; + break; + case HWTSTAMP_TX_ON: + priv->ts_tx_en = true; + break; + default: + return -ERANGE; + } + + if (config.rx_filter == HWTSTAMP_FILTER_NONE) { + priv->ts_rx_en = false; + } else { + priv->ts_rx_en = true; + /* TS is set for all frame types, not only those requested */ + config.rx_filter = HWTSTAMP_FILTER_ALL; + } + + return copy_to_user(rq->ifr_data, &config, sizeof(config)) ? + -EFAULT : 0; +} + +static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + if (cmd == SIOCSHWTSTAMP) + return dpaa2_eth_ts_ioctl(dev, rq, cmd); + + return -EINVAL; +} + +static int set_buffer_layout(struct dpaa2_eth_priv *priv) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_buffer_layout buf_layout = {0}; + u16 rx_buf_align; + int err; + + /* We need to check for WRIOP version 1.0.0, but depending on the MC + * version, this number is not always provided correctly on rev1. + * We need to check for both alternatives in this situation. + */ + if (priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(0, 0, 0) || + priv->dpni_attrs.wriop_version == DPAA2_WRIOP_VERSION(1, 0, 0)) + rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN_REV1; + else + rx_buf_align = DPAA2_ETH_RX_BUF_ALIGN; + + /* tx buffer */ + buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; + buf_layout.pass_timestamp = true; + buf_layout.options = DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | + DPNI_BUF_LAYOUT_OPT_TIMESTAMP; + err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_TX, &buf_layout); + if (err) { + dev_err(dev, "dpni_set_buffer_layout(TX) failed\n"); + return err; + } + + /* tx-confirm buffer */ + buf_layout.options = DPNI_BUF_LAYOUT_OPT_TIMESTAMP; + err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_TX_CONFIRM, &buf_layout); + if (err) { + dev_err(dev, "dpni_set_buffer_layout(TX_CONF) failed\n"); + return err; + } + + /* Now that we've set our tx buffer layout, retrieve the minimum + * required tx data offset. + */ + err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token, + &priv->tx_data_offset); + if (err) { + dev_err(dev, "dpni_get_tx_data_offset() failed\n"); + return err; + } + + if ((priv->tx_data_offset % 64) != 0) + dev_warn(dev, "Tx data offset (%d) not a multiple of 64B\n", + priv->tx_data_offset); + + /* rx buffer */ + buf_layout.pass_frame_status = true; + buf_layout.pass_parser_result = true; + buf_layout.data_align = rx_buf_align; + buf_layout.data_head_room = dpaa2_eth_rx_headroom(priv); + buf_layout.private_data_size = 0; + /* If XDP program is attached, reserve extra space for + * potential header expansions + */ + if (priv->has_xdp_prog) + buf_layout.data_head_room += XDP_PACKET_HEADROOM; + buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | + DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | + DPNI_BUF_LAYOUT_OPT_DATA_ALIGN | + DPNI_BUF_LAYOUT_OPT_DATA_HEAD_ROOM | + DPNI_BUF_LAYOUT_OPT_TIMESTAMP; + err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_RX, &buf_layout); + if (err) { + dev_err(dev, "dpni_set_buffer_layout(RX) failed\n"); + return err; + } + + return 0; +} + +#define DPNI_ENQUEUE_FQID_VER_MAJOR 7 +#define DPNI_ENQUEUE_FQID_VER_MINOR 9 + +static inline int dpaa2_eth_enqueue_qd(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_fq *fq, + struct dpaa2_fd *fd, u8 prio) +{ + return dpaa2_io_service_enqueue_qd(fq->channel->dpio, + priv->tx_qdid, prio, + fq->tx_qdbin, fd); +} + +static inline int dpaa2_eth_enqueue_fq(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_fq *fq, + struct dpaa2_fd *fd, + u8 prio __always_unused) +{ + return dpaa2_io_service_enqueue_fq(fq->channel->dpio, + fq->tx_fqid, fd); +} + +static void set_enqueue_mode(struct dpaa2_eth_priv *priv) +{ + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_ENQUEUE_FQID_VER_MAJOR, + DPNI_ENQUEUE_FQID_VER_MINOR) < 0) + priv->enqueue = dpaa2_eth_enqueue_qd; + else + priv->enqueue = dpaa2_eth_enqueue_fq; +} + +static void update_tx_fqids(struct dpaa2_eth_priv *priv) +{ + struct dpaa2_eth_fq *fq; + struct dpni_queue queue; + struct dpni_queue_id qid = {0}; + int i, err; + + /* We only use Tx FQIDs for FQID-based enqueue, so check + * if DPNI version supports it before updating FQIDs + */ + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_ENQUEUE_FQID_VER_MAJOR, + DPNI_ENQUEUE_FQID_VER_MINOR) < 0) + return; + + for (i = 0; i < priv->num_fqs; i++) { + fq = &priv->fq[i]; + if (fq->type != DPAA2_TX_CONF_FQ) + continue; + err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_TX, 0, fq->flowid, + &queue, &qid); + if (err) + goto out_err; + + fq->tx_fqid = qid.fqid; + if (fq->tx_fqid == 0) + goto out_err; + } + + return; + +out_err: + netdev_info(priv->net_dev, + "Error reading Tx FQID, fallback to QDID-based enqueue"); + priv->enqueue = dpaa2_eth_enqueue_qd; +} + +static int dpaa2_eth_set_xdp(struct net_device *net_dev, struct bpf_prog *prog) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpaa2_eth_channel *ch; + struct bpf_prog *old_prog = NULL; + int i, err; + + /* No support for SG frames */ + if (DPAA2_ETH_L2_MAX_FRM(net_dev->mtu) > DPAA2_ETH_RX_BUF_SIZE) + return -EINVAL; + + if (netif_running(net_dev)) { + err = dpaa2_eth_stop(net_dev); + if (err) + return err; + } + + if (prog) { + prog = bpf_prog_add(prog, priv->num_channels - 1); + if (IS_ERR(prog)) + return PTR_ERR(prog); + } + + priv->has_xdp_prog = !!prog; + + for (i = 0; i < priv->num_channels; i++) { + ch = priv->channel[i]; + old_prog = xchg(&ch->xdp_prog, prog); + if (old_prog) + bpf_prog_put(old_prog); + } + + /* When turning XDP on/off we need to do some reconfiguring + * of the Rx buffer layout. Buffer pool was drained on dpaa2_eth_stop, + * so we are sure no old format buffers will be used from now on + */ + if (priv->has_xdp_prog != !!old_prog) + set_buffer_layout(priv); + + if (netif_running(net_dev)) { + err = dpaa2_eth_open(net_dev); + if (err) + return err; + } + + return 0; +} + +static int dpaa2_eth_xdp(struct net_device *dev, struct netdev_xdp *xdp) +{ + struct dpaa2_eth_priv *priv = netdev_priv(dev); + + switch (xdp->command) { + case XDP_SETUP_PROG: + return dpaa2_eth_set_xdp(dev, xdp->prog); + case XDP_QUERY_PROG: + xdp->prog_attached = priv->has_xdp_prog; + return 0; + default: + return -EINVAL; + } +} + +static int dpaa2_eth_xdp_xmit(struct net_device *net_dev, struct xdp_buff *xdp) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct device *dev = net_dev->dev.parent; + struct rtnl_link_stats64 *percpu_stats; + struct dpaa2_eth_drv_stats *percpu_extras; + unsigned int needed_headroom; + struct dpaa2_eth_swa *swa; + struct dpaa2_eth_fq *fq; + struct dpaa2_fd fd; + void *buffer_start, *aligned_start; + dma_addr_t addr; + int err, i; + + if (!netif_running(net_dev)) + return -ENETDOWN; + + /* We require a minimum headroom to be able to transmit the frame. + * Otherwise return an error and let the original net_device handle it + */ + /* TODO: Do we update i/f counters here or just on the Rx device? */ + needed_headroom = dpaa2_eth_needed_headroom(priv, NULL); + if (xdp->data < xdp->data_hard_start || + xdp->data - xdp->data_hard_start < needed_headroom) { + percpu_stats->tx_dropped++; + return -EINVAL; + } + + percpu_stats = this_cpu_ptr(priv->percpu_stats); + percpu_extras = this_cpu_ptr(priv->percpu_extras); + + /* Setup the FD fields */ + memset(&fd, 0, sizeof(fd)); + + /* Align FD address, if possible */ + buffer_start = xdp->data - needed_headroom; + aligned_start = PTR_ALIGN(buffer_start - DPAA2_ETH_TX_BUF_ALIGN, + DPAA2_ETH_TX_BUF_ALIGN); + if (aligned_start >= xdp->data_hard_start) + buffer_start = aligned_start; + + swa = (struct dpaa2_eth_swa *)buffer_start; + /* fill in necessary fields here */ + swa->type = DPAA2_ETH_SWA_XDP; + swa->xdp.dma_size = xdp->data_end - buffer_start; + + addr = dma_map_single(dev, buffer_start, + xdp->data_end - buffer_start, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(dev, addr))) { + percpu_stats->tx_dropped++; + return -ENOMEM; + } + + dpaa2_fd_set_addr(&fd, addr); + dpaa2_fd_set_offset(&fd, xdp->data - buffer_start); + dpaa2_fd_set_len(&fd, xdp->data_end - xdp->data); + dpaa2_fd_set_format(&fd, dpaa2_fd_single); + dpaa2_fd_set_ctrl(&fd, FD_CTRL_PTA); + + fq = &priv->fq[smp_processor_id()]; + for (i = 0; i < DPAA2_ETH_ENQUEUE_RETRIES; i++) { + err = dpaa2_io_service_enqueue_qd(NULL, priv->tx_qdid, 0, + fq->tx_qdbin, &fd); + if (err != -EBUSY) + break; + } + percpu_extras->tx_portal_busy += i; + if (unlikely(err < 0)) { + percpu_stats->tx_errors++; + /* let the Rx device handle the cleanup */ + return err; + } + + percpu_stats->tx_packets++; + percpu_stats->tx_bytes += dpaa2_fd_get_len(&fd); + + return 0; +} + +static void dpaa2_eth_xdp_flush(struct net_device *net_dev) +{ + /* We don't have hardware support for Tx batching, + * so we do the actual frame enqueue in ndo_xdp_xmit + */ +} +static int dpaa2_eth_update_xps(struct dpaa2_eth_priv *priv) +{ + struct net_device *net_dev = priv->net_dev; + unsigned int i, num_queues; + struct cpumask xps_mask; + struct dpaa2_eth_fq *fq; + int err = 0; + + num_queues = (net_dev->num_tc ? : 1) * dpaa2_eth_queue_count(priv); + for (i = 0; i < num_queues; i++) { + fq = &priv->fq[i % dpaa2_eth_queue_count(priv)]; + cpumask_clear(&xps_mask); + cpumask_set_cpu(fq->target_cpu, &xps_mask); + err = netif_set_xps_queue(net_dev, &xps_mask, i); + if (err) { + dev_info_once(net_dev->dev.parent, + "Error setting XPS queue\n"); + break; + } + } + + return err; +} + +static int dpaa2_eth_setup_tc(struct net_device *net_dev, + enum tc_setup_type type, + void *type_data) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = (struct tc_mqprio_qopt *)type_data; + int i, err = 0; + + if (type != TC_SETUP_MQPRIO) + return -EINVAL; + + if (mqprio->num_tc > dpaa2_eth_tc_count(priv)) { + netdev_err(net_dev, "Max %d traffic classes supported\n", + dpaa2_eth_tc_count(priv)); + return -EINVAL; + } + + if (mqprio->num_tc == net_dev->num_tc) + return 0; + + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + + if (!mqprio->num_tc) { + netdev_reset_tc(net_dev); + err = netif_set_real_num_tx_queues(net_dev, + dpaa2_eth_queue_count(priv)); + if (err) + return err; + + goto update_xps; + } + + err = netdev_set_num_tc(net_dev, mqprio->num_tc); + if (err) + return err; + + err = netif_set_real_num_tx_queues(net_dev, mqprio->num_tc * + dpaa2_eth_queue_count(priv)); + if (err) + return err; + + for (i = 0; i < mqprio->num_tc; i++) { + err = netdev_set_tc_queue(net_dev, i, + dpaa2_eth_queue_count(priv), + i * dpaa2_eth_queue_count(priv)); + if (err) + return err; + } + +update_xps: + err = dpaa2_eth_update_xps(priv); + return err; +} + static const struct net_device_ops dpaa2_eth_ops = { .ndo_open = dpaa2_eth_open, .ndo_start_xmit = dpaa2_eth_tx, .ndo_stop = dpaa2_eth_stop, - .ndo_init = dpaa2_eth_init, .ndo_set_mac_address = dpaa2_eth_set_addr, .ndo_get_stats64 = dpaa2_eth_get_stats, - .ndo_change_mtu = dpaa2_eth_change_mtu, .ndo_set_rx_mode = dpaa2_eth_set_rx_mode, .ndo_set_features = dpaa2_eth_set_features, + .ndo_do_ioctl = dpaa2_eth_ioctl, + .ndo_xdp = dpaa2_eth_xdp, + .ndo_xdp_xmit = dpaa2_eth_xdp_xmit, + .ndo_xdp_flush = dpaa2_eth_xdp_flush, + .ndo_setup_tc = dpaa2_eth_setup_tc, }; static void cdan_cb(struct dpaa2_io_notification_ctx *ctx) @@ -1422,34 +2062,32 @@ static struct fsl_mc_device *setup_dpcon err = dpcon_open(priv->mc_io, 0, dpcon->obj_desc.id, &dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_open() failed\n"); - goto err_open; + goto free; } err = dpcon_reset(priv->mc_io, 0, dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_reset() failed\n"); - goto err_reset; + goto close; } err = dpcon_get_attributes(priv->mc_io, 0, dpcon->mc_handle, &attrs); if (err) { dev_err(dev, "dpcon_get_attributes() failed\n"); - goto err_get_attr; + goto close; } err = dpcon_enable(priv->mc_io, 0, dpcon->mc_handle); if (err) { dev_err(dev, "dpcon_enable() failed\n"); - goto err_enable; + goto close; } return dpcon; -err_enable: -err_get_attr: -err_reset: +close: dpcon_close(priv->mc_io, 0, dpcon->mc_handle); -err_open: +free: fsl_mc_object_free(dpcon); return NULL; @@ -1502,7 +2140,14 @@ err_setup: static void free_channel(struct dpaa2_eth_priv *priv, struct dpaa2_eth_channel *channel) { + struct bpf_prog *prog; + free_dpcon(priv, channel->dpcon); + + prog = READ_ONCE(channel->xdp_prog); + if (prog) + bpf_prog_put(prog); + kfree(channel); } @@ -1546,7 +2191,8 @@ static int setup_dpio(struct dpaa2_eth_p nctx->desired_cpu = i; /* Register the new context */ - err = dpaa2_io_service_register(NULL, nctx); + channel->dpio = dpaa2_io_service_select(i); + err = dpaa2_io_service_register(channel->dpio, nctx, dev); if (err) { dev_dbg(dev, "No affine DPIO for cpu %d\n", i); /* If no affine DPIO for this core, there's probably @@ -1579,14 +2225,14 @@ static int setup_dpio(struct dpaa2_eth_p /* Stop if we already have enough channels to accommodate all * RX and TX conf queues */ - if (priv->num_channels == dpaa2_eth_queue_count(priv)) + if (priv->num_channels == priv->dpni_attrs.num_queues) break; } return 0; err_set_cdan: - dpaa2_io_service_deregister(NULL, nctx); + dpaa2_io_service_deregister(channel->dpio, nctx, dev); err_service_reg: free_channel(priv, channel); err_alloc_ch: @@ -1603,13 +2249,14 @@ err_alloc_ch: static void free_dpio(struct dpaa2_eth_priv *priv) { - int i; + struct device *dev = priv->net_dev->dev.parent; struct dpaa2_eth_channel *ch; + int i; /* deregister CDAN notifications and free channels */ for (i = 0; i < priv->num_channels; i++) { ch = priv->channel[i]; - dpaa2_io_service_deregister(NULL, &ch->nctx); + dpaa2_io_service_deregister(ch->dpio, &ch->nctx, dev); free_channel(priv, ch); } } @@ -1636,8 +2283,7 @@ static void set_fq_affinity(struct dpaa2 { struct device *dev = priv->net_dev->dev.parent; struct dpaa2_eth_fq *fq; - int rx_cpu, txc_cpu; - int i; + int rx_cpu, txc_cpu, i; /* For each FQ, pick one channel/CPU to deliver frames to. * This may well change at runtime, either through irqbalance or @@ -1649,6 +2295,7 @@ static void set_fq_affinity(struct dpaa2 fq = &priv->fq[i]; switch (fq->type) { case DPAA2_RX_FQ: + case DPAA2_RX_ERR_FQ: fq->target_cpu = rx_cpu; rx_cpu = cpumask_next(rx_cpu, &priv->dpio_cpumask); if (rx_cpu >= nr_cpu_ids) @@ -1665,11 +2312,13 @@ static void set_fq_affinity(struct dpaa2 } fq->channel = get_affine_channel(priv, fq->target_cpu); } + + dpaa2_eth_update_xps(priv); } static void setup_fqs(struct dpaa2_eth_priv *priv) { - int i; + int i, j; /* We have one TxConf FQ per Tx flow. * The number of Tx and Rx queues is the same. @@ -1681,11 +2330,19 @@ static void setup_fqs(struct dpaa2_eth_p priv->fq[priv->num_fqs++].flowid = (u16)i; } - for (i = 0; i < dpaa2_eth_queue_count(priv); i++) { - priv->fq[priv->num_fqs].type = DPAA2_RX_FQ; - priv->fq[priv->num_fqs].consume = dpaa2_eth_rx; - priv->fq[priv->num_fqs++].flowid = (u16)i; - } + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) + for (j = 0; j < dpaa2_eth_queue_count(priv); j++) { + priv->fq[priv->num_fqs].type = DPAA2_RX_FQ; + priv->fq[priv->num_fqs].consume = dpaa2_eth_rx; + priv->fq[priv->num_fqs].tc = (u8)i; + priv->fq[priv->num_fqs++].flowid = (u16)j; + } + +#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE + /* We have exactly one Rx error queue per DPNI */ + priv->fq[priv->num_fqs].type = DPAA2_RX_ERR_FQ; + priv->fq[priv->num_fqs++].consume = dpaa2_eth_rx_err; +#endif /* For each FQ, decide on which core to process incoming frames */ set_fq_affinity(priv); @@ -1735,6 +2392,9 @@ static int setup_dpbp(struct dpaa2_eth_p } priv->bpid = dpbp_attrs.bpid; + /* By default we start with flow control enabled */ + priv->max_bufs_per_ch = DPAA2_ETH_NUM_BUFS_FC / priv->num_channels; + return 0; err_get_attr: @@ -1762,7 +2422,7 @@ static int setup_dpni(struct fsl_mc_devi struct device *dev = &ls_dev->dev; struct dpaa2_eth_priv *priv; struct net_device *net_dev; - struct dpni_buffer_layout buf_layout = {0}; + struct dpni_link_cfg cfg = {0}; int err; net_dev = dev_get_drvdata(dev); @@ -1772,7 +2432,22 @@ static int setup_dpni(struct fsl_mc_devi err = dpni_open(priv->mc_io, 0, ls_dev->obj_desc.id, &priv->mc_token); if (err) { dev_err(dev, "dpni_open() failed\n"); - goto err_open; + return err; + } + + /* Check if we can work with this DPNI object */ + err = dpni_get_api_version(priv->mc_io, 0, &priv->dpni_ver_major, + &priv->dpni_ver_minor); + if (err) { + dev_err(dev, "dpni_get_api_version() failed\n"); + goto close; + } + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_VER_MAJOR, DPNI_VER_MINOR) < 0) { + dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n", + priv->dpni_ver_major, priv->dpni_ver_minor, + DPNI_VER_MAJOR, DPNI_VER_MINOR); + err = -ENOTSUPP; + goto close; } ls_dev->mc_io = priv->mc_io; @@ -1781,77 +2456,41 @@ static int setup_dpni(struct fsl_mc_devi err = dpni_reset(priv->mc_io, 0, priv->mc_token); if (err) { dev_err(dev, "dpni_reset() failed\n"); - goto err_reset; + goto close; } err = dpni_get_attributes(priv->mc_io, 0, priv->mc_token, &priv->dpni_attrs); if (err) { dev_err(dev, "dpni_get_attributes() failed (err=%d)\n", err); - goto err_get_attr; + goto close; } - /* Configure buffer layouts */ - /* rx buffer */ - buf_layout.pass_parser_result = true; - buf_layout.pass_frame_status = true; - buf_layout.private_data_size = DPAA2_ETH_SWA_SIZE; - buf_layout.data_align = DPAA2_ETH_RX_BUF_ALIGN; - buf_layout.options = DPNI_BUF_LAYOUT_OPT_PARSER_RESULT | - DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | - DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE | - DPNI_BUF_LAYOUT_OPT_DATA_ALIGN; - err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, - DPNI_QUEUE_RX, &buf_layout); - if (err) { - dev_err(dev, "dpni_set_buffer_layout(RX) failed\n"); - goto err_buf_layout; - } + err = set_buffer_layout(priv); + if (err) + goto close; - /* tx buffer */ - buf_layout.options = DPNI_BUF_LAYOUT_OPT_FRAME_STATUS | - DPNI_BUF_LAYOUT_OPT_PRIVATE_DATA_SIZE; - err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, - DPNI_QUEUE_TX, &buf_layout); - if (err) { - dev_err(dev, "dpni_set_buffer_layout(TX) failed\n"); - goto err_buf_layout; - } + set_enqueue_mode(priv); - /* tx-confirm buffer */ - buf_layout.options = DPNI_BUF_LAYOUT_OPT_FRAME_STATUS; - err = dpni_set_buffer_layout(priv->mc_io, 0, priv->mc_token, - DPNI_QUEUE_TX_CONFIRM, &buf_layout); - if (err) { - dev_err(dev, "dpni_set_buffer_layout(TX_CONF) failed\n"); - goto err_buf_layout; - } + priv->cls_rule = devm_kzalloc(dev, sizeof(struct dpaa2_eth_cls_rule) * + dpaa2_eth_fs_count(priv), GFP_KERNEL); + if (!priv->cls_rule) + goto close; - /* Now that we've set our tx buffer layout, retrieve the minimum - * required tx data offset. - */ - err = dpni_get_tx_data_offset(priv->mc_io, 0, priv->mc_token, - &priv->tx_data_offset); + /* Enable flow control */ + cfg.options = DPNI_LINK_OPT_AUTONEG | DPNI_LINK_OPT_PAUSE; + priv->tx_pause_frames = true; + err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); if (err) { - dev_err(dev, "dpni_get_tx_data_offset() failed\n"); - goto err_data_offset; + dev_err(dev, "dpni_set_link_cfg() failed\n"); + goto close; } - if ((priv->tx_data_offset % 64) != 0) - dev_warn(dev, "Tx data offset (%d) not a multiple of 64B\n", - priv->tx_data_offset); - - /* Accommodate software annotation space (SWA) */ - priv->tx_data_offset += DPAA2_ETH_SWA_SIZE; - return 0; -err_data_offset: -err_buf_layout: -err_get_attr: -err_reset: +close: dpni_close(priv->mc_io, 0, priv->mc_token); -err_open: + return err; } @@ -1865,6 +2504,7 @@ static void free_dpni(struct dpaa2_eth_p err); dpni_close(priv->mc_io, 0, priv->mc_token); + } static int setup_rx_flow(struct dpaa2_eth_priv *priv, @@ -1873,11 +2513,10 @@ static int setup_rx_flow(struct dpaa2_et struct device *dev = priv->net_dev->dev.parent; struct dpni_queue queue; struct dpni_queue_id qid; - struct dpni_taildrop td; int err; err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, - DPNI_QUEUE_RX, 0, fq->flowid, &queue, &qid); + DPNI_QUEUE_RX, fq->tc, fq->flowid, &queue, &qid); if (err) { dev_err(dev, "dpni_get_queue(RX) failed\n"); return err; @@ -1889,24 +2528,136 @@ static int setup_rx_flow(struct dpaa2_et queue.destination.type = DPNI_DEST_DPCON; queue.destination.priority = 1; queue.user_context = (u64)(uintptr_t)fq; + queue.flc.stash_control = 1; + queue.flc.value &= 0xFFFFFFFFFFFFFFC0; + /* 01 01 00 - data, annotation, flow context*/ + queue.flc.value |= 0x14; + err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, - DPNI_QUEUE_RX, 0, fq->flowid, - DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST, + DPNI_QUEUE_RX, fq->tc, fq->flowid, + DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST | + DPNI_QUEUE_OPT_FLC, &queue); if (err) { dev_err(dev, "dpni_set_queue(RX) failed\n"); return err; } - td.enable = 1; - td.threshold = DPAA2_ETH_TAILDROP_THRESH; - err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, DPNI_CP_QUEUE, - DPNI_QUEUE_RX, 0, fq->flowid, &td); - if (err) { - dev_err(dev, "dpni_set_threshold() failed\n"); - return err; + return 0; +} + +static int set_queue_taildrop(struct dpaa2_eth_priv *priv, + struct dpni_taildrop *td) +{ + struct device *dev = priv->net_dev->dev.parent; + int i, err; + + for (i = 0; i < priv->num_fqs; i++) { + if (priv->fq[i].type != DPAA2_RX_FQ) + continue; + + err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, + DPNI_CP_QUEUE, DPNI_QUEUE_RX, + priv->fq[i].tc, priv->fq[i].flowid, + td); + if (err) { + dev_err(dev, "dpni_set_taildrop() failed (%d)\n", err); + return err; + } + + dev_dbg(dev, "%s taildrop for Rx queue id %d tc %d\n", + (td->enable ? "Enabled" : "Disabled"), + priv->fq[i].flowid, priv->fq[i].tc); + } + + return 0; +} + +static int set_group_taildrop(struct dpaa2_eth_priv *priv, + struct dpni_taildrop *td) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_taildrop disable_td, *tc_td; + int i, err; + + memset(&disable_td, 0, sizeof(struct dpni_taildrop)); + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + if (td->enable && dpaa2_eth_is_pfc_enabled(priv, i)) + /* Do not set taildrop thresholds for PFC-enabled + * traffic classes. We will enable congestion + * notifications for them. + */ + tc_td = &disable_td; + else + tc_td = td; + + err = dpni_set_taildrop(priv->mc_io, 0, priv->mc_token, + DPNI_CP_GROUP, DPNI_QUEUE_RX, + i, 0, tc_td); + if (err) { + dev_err(dev, "dpni_set_taildrop() failed (%d)\n", err); + return err; + } + + dev_dbg(dev, "%s taildrop for Rx group tc %d\n", + (tc_td->enable ? "Enabled" : "Disabled"), + i); + } + + return 0; +} + +/* Enable/disable Rx FQ taildrop + * + * Rx FQ taildrop is mutually exclusive with flow control and it only gets + * disabled when FC is active. Depending on FC status, we need to compute + * the maximum number of buffers in the pool differently, so use the + * opportunity to update max number of buffers as well. + */ +int set_rx_taildrop(struct dpaa2_eth_priv *priv) +{ + enum dpaa2_eth_td_cfg cfg = dpaa2_eth_get_td_type(priv); + struct dpni_taildrop td_queue, td_group; + int err = 0; + + switch (cfg) { + case DPAA2_ETH_TD_NONE: + memset(&td_queue, 0, sizeof(struct dpni_taildrop)); + memset(&td_group, 0, sizeof(struct dpni_taildrop)); + priv->max_bufs_per_ch = DPAA2_ETH_NUM_BUFS_FC / + priv->num_channels; + break; + case DPAA2_ETH_TD_QUEUE: + memset(&td_group, 0, sizeof(struct dpni_taildrop)); + td_queue.enable = 1; + td_queue.units = DPNI_CONGESTION_UNIT_BYTES; + td_queue.threshold = DPAA2_ETH_TAILDROP_THRESH / + dpaa2_eth_tc_count(priv); + priv->max_bufs_per_ch = DPAA2_ETH_NUM_BUFS_PER_CH; + break; + case DPAA2_ETH_TD_GROUP: + memset(&td_queue, 0, sizeof(struct dpni_taildrop)); + td_group.enable = 1; + td_group.units = DPNI_CONGESTION_UNIT_FRAMES; + td_group.threshold = NAPI_POLL_WEIGHT * + dpaa2_eth_queue_count(priv); + priv->max_bufs_per_ch = NAPI_POLL_WEIGHT * + dpaa2_eth_tc_count(priv); + break; + default: + break; } + err = set_queue_taildrop(priv, &td_queue); + if (err) + return err; + + err = set_group_taildrop(priv, &td_group); + if (err) + return err; + + priv->refill_thresh = DPAA2_ETH_REFILL_THRESH(priv); + return 0; } @@ -1926,6 +2677,7 @@ static int setup_tx_flow(struct dpaa2_et } fq->tx_qdbin = qid.qdbin; + fq->tx_fqid = qid.fqid; err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, DPNI_QUEUE_TX_CONFIRM, 0, fq->flowid, @@ -1953,23 +2705,88 @@ static int setup_tx_flow(struct dpaa2_et return 0; } -/* Hash key is a 5-tuple: IPsrc, IPdst, IPnextproto, L4src, L4dst */ -static const struct dpaa2_eth_hash_fields hash_fields[] = { +#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE +static int setup_rx_err_flow(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_fq *fq) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_queue q = { { 0 } }; + struct dpni_queue_id qid; + u8 q_opt = DPNI_QUEUE_OPT_USER_CTX | DPNI_QUEUE_OPT_DEST; + int err; + + err = dpni_get_queue(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_RX_ERR, 0, 0, &q, &qid); + if (err) { + dev_err(dev, "dpni_get_queue() failed (%d)\n", err); + return err; + } + + fq->fqid = qid.fqid; + + q.destination.id = fq->channel->dpcon_id; + q.destination.type = DPNI_DEST_DPCON; + q.destination.priority = 1; + q.user_context = (u64)fq; + err = dpni_set_queue(priv->mc_io, 0, priv->mc_token, + DPNI_QUEUE_RX_ERR, 0, 0, q_opt, &q); + if (err) { + dev_err(dev, "dpni_set_queue() failed (%d)\n", err); + return err; + } + + return 0; +} +#endif + +/* Supported header fields for Rx hash distribution key */ +static const struct dpaa2_eth_dist_fields dist_fields[] = { { + /* L2 header */ + .rxnfc_field = RXH_L2DA, + .cls_prot = NET_PROT_ETH, + .cls_field = NH_FLD_ETH_DA, + .id = DPAA2_ETH_DIST_ETHDST, + .size = 6, + }, { + .cls_prot = NET_PROT_ETH, + .cls_field = NH_FLD_ETH_SA, + .id = DPAA2_ETH_DIST_ETHSRC, + .size = 6, + }, { + /* This is the last ethertype field parsed: + * depending on frame format, it can be the MAC ethertype + * or the VLAN etype. + */ + .cls_prot = NET_PROT_ETH, + .cls_field = NH_FLD_ETH_TYPE, + .id = DPAA2_ETH_DIST_ETHTYPE, + .size = 2, + }, { + /* VLAN header */ + .rxnfc_field = RXH_VLAN, + .cls_prot = NET_PROT_VLAN, + .cls_field = NH_FLD_VLAN_TCI, + .id = DPAA2_ETH_DIST_VLAN, + .size = 2, + }, { /* IP header */ .rxnfc_field = RXH_IP_SRC, .cls_prot = NET_PROT_IP, .cls_field = NH_FLD_IP_SRC, + .id = DPAA2_ETH_DIST_IPSRC, .size = 4, }, { .rxnfc_field = RXH_IP_DST, .cls_prot = NET_PROT_IP, .cls_field = NH_FLD_IP_DST, + .id = DPAA2_ETH_DIST_IPDST, .size = 4, }, { .rxnfc_field = RXH_L3_PROTO, .cls_prot = NET_PROT_IP, .cls_field = NH_FLD_IP_PROTO, + .id = DPAA2_ETH_DIST_IPPROTO, .size = 1, }, { /* Using UDP ports, this is functionally equivalent to raw @@ -1978,41 +2795,170 @@ static const struct dpaa2_eth_hash_field .rxnfc_field = RXH_L4_B_0_1, .cls_prot = NET_PROT_UDP, .cls_field = NH_FLD_UDP_PORT_SRC, + .id = DPAA2_ETH_DIST_L4SRC, .size = 2, }, { .rxnfc_field = RXH_L4_B_2_3, .cls_prot = NET_PROT_UDP, .cls_field = NH_FLD_UDP_PORT_DST, + .id = DPAA2_ETH_DIST_L4DST, .size = 2, }, }; -/* Set RX hash options +/* Configure the Rx hash key using the legacy API */ +static int config_legacy_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_rx_tc_dist_cfg dist_cfg; + int i, err = 0; + + memset(&dist_cfg, 0, sizeof(dist_cfg)); + + dist_cfg.key_cfg_iova = key; + dist_cfg.dist_size = dpaa2_eth_queue_count(priv); + dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; + + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, + i, &dist_cfg); + if (err) { + dev_err(dev, "dpni_set_rx_tc_dist failed\n"); + break; + } + } + + return err; +} + +/* Configure the Rx hash key using the new API */ +static int config_hash_key(struct dpaa2_eth_priv *priv, dma_addr_t key) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_rx_dist_cfg dist_cfg; + int i, err = 0; + + memset(&dist_cfg, 0, sizeof(dist_cfg)); + + dist_cfg.key_cfg_iova = key; + dist_cfg.dist_size = dpaa2_eth_queue_count(priv); + dist_cfg.enable = 1; + + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + dist_cfg.tc = i; + err = dpni_set_rx_hash_dist(priv->mc_io, 0, priv->mc_token, + &dist_cfg); + if (err) { + dev_err(dev, "dpni_set_rx_hash_dist failed\n"); + break; + } + } + + return err; +} + +/* Configure the Rx flow classification key */ +static int config_cls_key(struct dpaa2_eth_priv *priv, dma_addr_t key) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_rx_dist_cfg dist_cfg; + int i, err = 0; + + memset(&dist_cfg, 0, sizeof(dist_cfg)); + + dist_cfg.key_cfg_iova = key; + dist_cfg.dist_size = dpaa2_eth_queue_count(priv); + dist_cfg.enable = 1; + + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + dist_cfg.tc = i; + err = dpni_set_rx_fs_dist(priv->mc_io, 0, priv->mc_token, + &dist_cfg); + if (err) { + dev_err(dev, "dpni_set_rx_fs_dist failed\n"); + break; + } + } + + return err; +} + +/* Size of the Rx flow classification key */ +int dpaa2_eth_cls_key_size(u64 fields) +{ + int i, size = 0; + + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + if (!(fields & dist_fields[i].id)) + continue; + size += dist_fields[i].size; + } + + return size; +} + +/* Offset of header field in Rx classification key */ +int dpaa2_eth_cls_fld_off(int prot, int field) +{ + int i, off = 0; + + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + if (dist_fields[i].cls_prot == prot && + dist_fields[i].cls_field == field) + return off; + off += dist_fields[i].size; + } + + WARN_ONCE(1, "Unsupported header field used for Rx flow cls\n"); + return 0; +} + +/* Prune unused fields from the classification rule. + * Used when masking is not supported + */ +void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields) +{ + int off = 0, new_off = 0; + int i, size; + + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { + size = dist_fields[i].size; + if (dist_fields[i].id & fields) { + memcpy(key_mem + new_off, key_mem + off, size); + new_off += size; + } + off += size; + } +} + +/* Set Rx distribution (hash or flow classification) key * flags is a combination of RXH_ bits */ -static int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags) +static int dpaa2_eth_set_dist_key(struct net_device *net_dev, + enum dpaa2_eth_rx_dist type, u64 flags) { struct device *dev = net_dev->dev.parent; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); struct dpkg_profile_cfg cls_cfg; - struct dpni_rx_tc_dist_cfg dist_cfg; + u32 rx_hash_fields = 0; + dma_addr_t key_iova; u8 *dma_mem; int i; int err = 0; - if (!dpaa2_eth_hash_enabled(priv)) { - dev_dbg(dev, "Hashing support is not enabled\n"); - return 0; - } - memset(&cls_cfg, 0, sizeof(cls_cfg)); - for (i = 0; i < ARRAY_SIZE(hash_fields); i++) { + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) { struct dpkg_extract *key = &cls_cfg.extracts[cls_cfg.num_extracts]; - if (!(flags & hash_fields[i].rxnfc_field)) + /* For both Rx hashing and classification keys + * we set only the selected fields. + */ + if (!(flags & dist_fields[i].id)) continue; + if (type == DPAA2_ETH_RX_DIST_HASH) + rx_hash_fields |= dist_fields[i].rxnfc_field; if (cls_cfg.num_extracts >= DPKG_MAX_NUM_OF_EXTRACTS) { dev_err(dev, "error adding key extraction rule, too many rules?\n"); @@ -2020,49 +2966,107 @@ static int dpaa2_eth_set_hash(struct net } key->type = DPKG_EXTRACT_FROM_HDR; - key->extract.from_hdr.prot = hash_fields[i].cls_prot; + key->extract.from_hdr.prot = dist_fields[i].cls_prot; key->extract.from_hdr.type = DPKG_FULL_FIELD; - key->extract.from_hdr.field = hash_fields[i].cls_field; + key->extract.from_hdr.field = dist_fields[i].cls_field; cls_cfg.num_extracts++; + } + + dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL); + if (!dma_mem) + return -ENOMEM; + + err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); + if (err) { + dev_err(dev, "dpni_prepare_key_cfg error %d\n", err); + goto free_key; + } + + /* Prepare for setting the rx dist */ + key_iova = dma_map_single(dev, dma_mem, DPAA2_CLASSIFIER_DMA_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, key_iova)) { + dev_err(dev, "DMA mapping failed\n"); + err = -ENOMEM; + goto free_key; + } + + if (type == DPAA2_ETH_RX_DIST_HASH) { + if (dpaa2_eth_has_legacy_dist(priv)) + err = config_legacy_hash_key(priv, key_iova); + else + err = config_hash_key(priv, key_iova); + } else { + err = config_cls_key(priv, key_iova); + } + + dma_unmap_single(dev, key_iova, DPAA2_CLASSIFIER_DMA_SIZE, + DMA_TO_DEVICE); + if (!err && type == DPAA2_ETH_RX_DIST_HASH) + priv->rx_hash_fields = rx_hash_fields; + +free_key: + kfree(dma_mem); + return err; +} + +int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u64 key = 0; + int i; + + if (!dpaa2_eth_hash_enabled(priv)) + return -EOPNOTSUPP; + + for (i = 0; i < ARRAY_SIZE(dist_fields); i++) + if (dist_fields[i].rxnfc_field & flags) + key |= dist_fields[i].id; + + return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_HASH, key); +} + +int dpaa2_eth_set_cls(struct net_device *net_dev, u64 flags) +{ + return dpaa2_eth_set_dist_key(net_dev, DPAA2_ETH_RX_DIST_CLS, flags); +} + +static int dpaa2_eth_set_default_cls(struct dpaa2_eth_priv *priv) +{ + struct device *dev = priv->net_dev->dev.parent; + int err; - priv->rx_hash_fields |= hash_fields[i].rxnfc_field; + /* Check if we actually support Rx flow classification */ + if (dpaa2_eth_has_legacy_dist(priv)) { + dev_dbg(dev, "Rx cls not supported by current MC version\n"); + return -EOPNOTSUPP; } - dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL); - if (!dma_mem) - return -ENOMEM; - - err = dpni_prepare_key_cfg(&cls_cfg, dma_mem); - if (err) { - dev_err(dev, "dpni_prepare_key_cfg error %d\n", err); - goto err_prep_key; + if (!dpaa2_eth_fs_enabled(priv)) { + dev_dbg(dev, "Rx cls disabled in DPNI options\n"); + return -EOPNOTSUPP; } - memset(&dist_cfg, 0, sizeof(dist_cfg)); - - /* Prepare for setting the rx dist */ - dist_cfg.key_cfg_iova = dma_map_single(dev, dma_mem, - DPAA2_CLASSIFIER_DMA_SIZE, - DMA_TO_DEVICE); - if (dma_mapping_error(dev, dist_cfg.key_cfg_iova)) { - dev_err(dev, "DMA mapping failed\n"); - err = -ENOMEM; - goto err_dma_map; + if (!dpaa2_eth_hash_enabled(priv)) { + dev_dbg(dev, "Rx cls disabled for single queue DPNIs\n"); + return -EOPNOTSUPP; } - dist_cfg.dist_size = dpaa2_eth_queue_count(priv); - dist_cfg.dist_mode = DPNI_DIST_MODE_HASH; + /* If there is no support for masking in the classification table, + * we don't set a default key, as it will depend on the rules + * added by the user at runtime. + */ + if (!dpaa2_eth_fs_mask_enabled(priv)) + goto out; - err = dpni_set_rx_tc_dist(priv->mc_io, 0, priv->mc_token, 0, &dist_cfg); - dma_unmap_single(dev, dist_cfg.key_cfg_iova, - DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE); + err = dpaa2_eth_set_cls(priv->net_dev, DPAA2_ETH_DIST_ALL); if (err) - dev_err(dev, "dpni_set_rx_tc_dist() error %d\n", err); + return err; -err_dma_map: -err_prep_key: - kfree(dma_mem); - return err; +out: + priv->rx_cls_enabled = 1; + + return 0; } /* Bind the DPNI to its needed objects and resources: buffer pool, DPIOs, @@ -2080,6 +3084,7 @@ static int bind_dpni(struct dpaa2_eth_pr pools_params.num_dpbp = 1; pools_params.pools[0].dpbp_id = priv->dpbp_dev->obj_desc.id; pools_params.pools[0].backup_pool = 0; + pools_params.pools[0].priority_mask = 0xff; pools_params.pools[0].buffer_size = DPAA2_ETH_RX_BUF_SIZE; err = dpni_set_pools(priv->mc_io, 0, priv->mc_token, &pools_params); if (err) { @@ -2087,17 +3092,28 @@ static int bind_dpni(struct dpaa2_eth_pr return err; } - /* have the interface implicitly distribute traffic based on supported - * header fields + /* have the interface implicitly distribute traffic based on + * the default hash key */ - err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_SUPPORTED); - if (err) - netdev_err(net_dev, "Failed to configure hashing\n"); + err = dpaa2_eth_set_hash(net_dev, DPAA2_RXH_DEFAULT); + if (err && err != -EOPNOTSUPP) + dev_err(dev, "Failed to configure hashing\n"); + + /* Configure the flow classification key; it includes all + * supported header fields and cannot be modified at runtime + */ + err = dpaa2_eth_set_default_cls(priv); + if (err && err != -EOPNOTSUPP) + dev_err(dev, "Failed to configure Rx classification key\n"); /* Configure handling of error frames */ err_cfg.errors = DPAA2_FAS_RX_ERR_MASK; err_cfg.set_frame_annotation = 1; +#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE + err_cfg.error_action = DPNI_ERROR_ACTION_SEND_TO_ERROR_QUEUE; +#else err_cfg.error_action = DPNI_ERROR_ACTION_DISCARD; +#endif err = dpni_set_errors_behavior(priv->mc_io, 0, priv->mc_token, &err_cfg); if (err) { @@ -2114,6 +3130,11 @@ static int bind_dpni(struct dpaa2_eth_pr case DPAA2_TX_CONF_FQ: err = setup_tx_flow(priv, &priv->fq[i]); break; +#ifdef CONFIG_FSL_DPAA2_ETH_USE_ERR_QUEUE + case DPAA2_RX_ERR_FQ: + err = setup_rx_err_flow(priv, &priv->fq[i]); + break; +#endif default: dev_err(dev, "Invalid FQ type %d\n", priv->fq[i].type); return -EINVAL; @@ -2237,11 +3258,14 @@ static int netdev_init(struct net_device { struct device *dev = net_dev->dev.parent; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u32 options = priv->dpni_attrs.options; + u64 supported = 0, not_supported = 0; u8 bcast_addr[ETH_ALEN]; u8 num_queues; int err; net_dev->netdev_ops = &dpaa2_eth_ops; + net_dev->ethtool_ops = &dpaa2_ethtool_ops; err = set_mac_addr(priv); if (err) @@ -2255,14 +3279,14 @@ static int netdev_init(struct net_device return err; } - /* Reserve enough space to align buffer as per hardware requirement; - * NOTE: priv->tx_data_offset MUST be initialized at this point. - */ - net_dev->needed_headroom = DPAA2_ETH_NEEDED_HEADROOM(priv); - - /* Set MTU limits */ - net_dev->min_mtu = 68; + /* Set MTU upper limit; lower limit is 68B (default value) */ net_dev->max_mtu = DPAA2_ETH_MAX_MTU; + err = dpni_set_max_frame_length(priv->mc_io, 0, priv->mc_token, + DPAA2_ETH_MFL); + if (err) { + dev_err(dev, "dpni_set_max_frame_length() failed\n"); + return err; + } /* Set actual number of queues in the net device */ num_queues = dpaa2_eth_queue_count(priv); @@ -2277,12 +3301,23 @@ static int netdev_init(struct net_device return err; } - /* Our .ndo_init will be called herein */ - err = register_netdev(net_dev); - if (err < 0) { - dev_err(dev, "register_netdev() failed\n"); - return err; - } + /* Capabilities listing */ + supported |= IFF_LIVE_ADDR_CHANGE; + + if (options & DPNI_OPT_NO_MAC_FILTER) + not_supported |= IFF_UNICAST_FLT; + else + supported |= IFF_UNICAST_FLT; + + net_dev->priv_flags |= supported; + net_dev->priv_flags &= ~not_supported; + + /* Features */ + net_dev->features = NETIF_F_RXCSUM | + NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | + NETIF_F_SG | NETIF_F_HIGHDMA | + NETIF_F_LLTX; + net_dev->hw_features = net_dev->features; return 0; } @@ -2303,14 +3338,9 @@ static int poll_link_state(void *arg) return 0; } -static irqreturn_t dpni_irq0_handler(int irq_num, void *arg) -{ - return IRQ_WAKE_THREAD; -} - static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg) { - u32 status = 0, clear = 0; + u32 status = ~0; struct device *dev = (struct device *)arg; struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev); struct net_device *net_dev = dev_get_drvdata(dev); @@ -2320,18 +3350,12 @@ static irqreturn_t dpni_irq0_handler_thr DPNI_IRQ_INDEX, &status); if (unlikely(err)) { netdev_err(net_dev, "Can't get irq status (err %d)\n", err); - clear = 0xffffffff; - goto out; + return IRQ_HANDLED; } - if (status & DPNI_IRQ_EVENT_LINK_CHANGED) { - clear |= DPNI_IRQ_EVENT_LINK_CHANGED; + if (status & DPNI_IRQ_EVENT_LINK_CHANGED) link_state_update(netdev_priv(net_dev)); - } -out: - dpni_clear_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle, - DPNI_IRQ_INDEX, clear); return IRQ_HANDLED; } @@ -2348,8 +3372,7 @@ static int setup_irqs(struct fsl_mc_devi irq = ls_dev->irqs[0]; err = devm_request_threaded_irq(&ls_dev->dev, irq->msi_desc->irq, - dpni_irq0_handler, - dpni_irq0_handler_thread, + NULL, dpni_irq0_handler_thread, IRQF_NO_SUSPEND | IRQF_ONESHOT, dev_name(&ls_dev->dev), &ls_dev->dev); if (err < 0) { @@ -2405,6 +3428,393 @@ static void del_ch_napi(struct dpaa2_eth } } +/* SysFS support */ +static ssize_t dpaa2_eth_show_tx_shaping(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); + /* No MC API for getting the shaping config. We're stateful. */ + struct dpni_tx_shaping_cfg *scfg = &priv->shaping_cfg; + + return sprintf(buf, "%u %hu\n", scfg->rate_limit, scfg->max_burst_size); +} + +static ssize_t dpaa2_eth_write_tx_shaping(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int err, items; + struct dpaa2_eth_priv *priv = netdev_priv(to_net_dev(dev)); + struct dpni_tx_shaping_cfg scfg, ercfg = { 0 }; + + items = sscanf(buf, "%u %hu", &scfg.rate_limit, &scfg.max_burst_size); + if (items != 2) { + pr_err("Expected format: \"rate_limit(Mbps) max_burst_size(bytes)\"\n"); + return -EINVAL; + } + /* Size restriction as per MC API documentation */ + if (scfg.max_burst_size > DPAA2_ETH_MAX_BURST_SIZE) { + pr_err("max_burst_size must be <= %d\n", + DPAA2_ETH_MAX_BURST_SIZE); + return -EINVAL; + } + + err = dpni_set_tx_shaping(priv->mc_io, 0, priv->mc_token, &scfg, + &ercfg, 0); + if (err) { + dev_err(dev, "dpni_set_tx_shaping() failed\n"); + return -EPERM; + } + /* If successful, save the current configuration for future inquiries */ + priv->shaping_cfg = scfg; + + return count; +} + +static struct device_attribute dpaa2_eth_attrs[] = { + __ATTR(tx_shaping, + 0600, + dpaa2_eth_show_tx_shaping, + dpaa2_eth_write_tx_shaping), +}; + +static void dpaa2_eth_sysfs_init(struct device *dev) +{ + int i, err; + + for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) { + err = device_create_file(dev, &dpaa2_eth_attrs[i]); + if (err) { + dev_err(dev, "ERROR creating sysfs file\n"); + goto undo; + } + } + return; + +undo: + while (i > 0) + device_remove_file(dev, &dpaa2_eth_attrs[--i]); +} + +static void dpaa2_eth_sysfs_remove(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dpaa2_eth_attrs); i++) + device_remove_file(dev, &dpaa2_eth_attrs[i]); +} + +#ifdef CONFIG_FSL_DPAA2_ETH_DCB +static int dpaa2_eth_dcbnl_ieee_getpfc(struct net_device *net_dev, + struct ieee_pfc *pfc) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpni_congestion_notification_cfg notification_cfg; + struct dpni_link_state state; + int err, i; + + priv->pfc.pfc_cap = dpaa2_eth_tc_count(priv); + + err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); + if (err) { + netdev_err(net_dev, "ERROR %d getting link state", err); + return err; + } + + if (!(state.options & DPNI_LINK_OPT_PFC_PAUSE)) + return 0; + + priv->pfc.pfc_en = 0; + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + err = dpni_get_congestion_notification(priv->mc_io, 0, + priv->mc_token, + DPNI_QUEUE_RX, + i, ¬ification_cfg); + if (err) { + netdev_err(net_dev, "Error %d getting congestion notif", + err); + return err; + } + + if (notification_cfg.threshold_entry) + priv->pfc.pfc_en |= 1 << i; + } + + memcpy(pfc, &priv->pfc, sizeof(priv->pfc)); + + return 0; +} + +/* Configure ingress classification based on VLAN PCP */ +static int set_vlan_qos(struct dpaa2_eth_priv *priv) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpkg_profile_cfg kg_cfg = {0}; + struct dpni_qos_tbl_cfg qos_cfg = {0}; + struct dpni_rule_cfg key_params; + u8 *params_iova, *key, *mask = NULL; + /* We only need the trailing 16 bits, without the TPID */ + u8 key_size = VLAN_HLEN / 2; + int err = 0, i, j = 0; + + if (priv->vlan_clsf_set) + return 0; + + params_iova = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL); + if (!params_iova) + return -ENOMEM; + + kg_cfg.num_extracts = 1; + kg_cfg.extracts[0].type = DPKG_EXTRACT_FROM_HDR; + kg_cfg.extracts[0].extract.from_hdr.prot = NET_PROT_VLAN; + kg_cfg.extracts[0].extract.from_hdr.type = DPKG_FULL_FIELD; + kg_cfg.extracts[0].extract.from_hdr.field = NH_FLD_VLAN_TCI; + + err = dpni_prepare_key_cfg(&kg_cfg, params_iova); + if (err) { + dev_err(dev, "dpkg_prepare_key_cfg failed: %d\n", err); + goto out_free; + } + + /* Set QoS table */ + qos_cfg.default_tc = 0; + qos_cfg.discard_on_miss = 0; + qos_cfg.key_cfg_iova = dma_map_single(dev, params_iova, + DPAA2_CLASSIFIER_DMA_SIZE, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, qos_cfg.key_cfg_iova)) { + dev_err(dev, "%s: DMA mapping failed\n", __func__); + err = -ENOMEM; + goto out_free; + } + err = dpni_set_qos_table(priv->mc_io, 0, priv->mc_token, &qos_cfg); + dma_unmap_single(dev, qos_cfg.key_cfg_iova, + DPAA2_CLASSIFIER_DMA_SIZE, DMA_TO_DEVICE); + + if (err) { + dev_err(dev, "dpni_set_qos_table failed: %d\n", err); + goto out_free; + } + + key_params.key_size = key_size; + + if (dpaa2_eth_fs_mask_enabled(priv)) { + mask = kzalloc(key_size, GFP_KERNEL); + if (!mask) + goto out_free; + + *mask = cpu_to_be16(VLAN_PRIO_MASK); + + key_params.mask_iova = dma_map_single(dev, mask, key_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, key_params.mask_iova)) { + dev_err(dev, "DMA mapping failed %s\n", __func__); + err = -ENOMEM; + goto out_free_mask; + } + } else { + key_params.mask_iova = 0; + } + + key = kzalloc(key_size, GFP_KERNEL); + if (!key) + goto out_cleanup_mask; + + key_params.key_iova = dma_map_single(dev, key, key_size, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, key_params.key_iova)) { + dev_err(dev, "%s: DMA mapping failed\n", __func__); + err = -ENOMEM; + goto out_free_key; + } + + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + *key = cpu_to_be16(i << VLAN_PRIO_SHIFT); + + dma_sync_single_for_device(dev, key_params.key_iova, + key_size, DMA_TO_DEVICE); + + err = dpni_add_qos_entry(priv->mc_io, 0, priv->mc_token, + &key_params, i, j++); + if (err) { + dev_err(dev, "dpni_add_qos_entry failed: %d\n", err); + goto out_remove; + } + } + + priv->vlan_clsf_set = true; + dev_dbg(dev, "Vlan PCP QoS classification set\n"); + goto out_cleanup; + +out_remove: + for (j = 0; j < i; j++) { + *key = cpu_to_be16(j << VLAN_PRIO_SHIFT); + + dma_sync_single_for_device(dev, key_params.key_iova, key_size, + DMA_TO_DEVICE); + + err = dpni_remove_qos_entry(priv->mc_io, 0, priv->mc_token, + &key_params); + if (err) + dev_err(dev, "dpni_remove_qos_entry failed: %d\n", err); + } + +out_cleanup: + dma_unmap_single(dev, key_params.key_iova, key_size, DMA_TO_DEVICE); +out_free_key: + kfree(key); +out_cleanup_mask: + if (key_params.mask_iova) + dma_unmap_single(dev, key_params.mask_iova, key_size, + DMA_TO_DEVICE); +out_free_mask: + kfree(mask); +out_free: + kfree(params_iova); + return err; +} + +static int dpaa2_eth_dcbnl_ieee_setpfc(struct net_device *net_dev, + struct ieee_pfc *pfc) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpni_congestion_notification_cfg notification_cfg = {0}; + struct dpni_link_state state = {0}; + struct dpni_link_cfg cfg = {0}; + struct ieee_pfc old_pfc; + int err = 0, i; + + if (dpaa2_eth_tc_count(priv) == 1) { + netdev_dbg(net_dev, "DPNI has 1 TC, PFC configuration N/A\n"); + return 0; + } + + /* Zero out pfc_enabled prios greater than tc_count */ + pfc->pfc_en &= (1 << dpaa2_eth_tc_count(priv)) - 1; + + if (priv->pfc.pfc_en == pfc->pfc_en) + /* Same enabled mask, nothing to be done */ + return 0; + + err = set_vlan_qos(priv); + if (err) + return err; + + err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); + if (err) { + netdev_err(net_dev, "ERROR %d getting link state", err); + return err; + } + + cfg.rate = state.rate; + cfg.options = state.options; + if (pfc->pfc_en) + cfg.options |= DPNI_LINK_OPT_PFC_PAUSE; + else + cfg.options &= ~DPNI_LINK_OPT_PFC_PAUSE; + + err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); + if (err) { + netdev_err(net_dev, "ERROR %d setting link cfg", err); + return err; + } + + memcpy(&old_pfc, &priv->pfc, sizeof(priv->pfc)); + memcpy(&priv->pfc, pfc, sizeof(priv->pfc)); + + err = set_rx_taildrop(priv); + if (err) + goto out_restore_config; + + /* configure congestion notifications */ + notification_cfg.notification_mode = DPNI_CONG_OPT_FLOW_CONTROL; + notification_cfg.units = DPNI_CONGESTION_UNIT_FRAMES; + notification_cfg.message_iova = 0ULL; + notification_cfg.message_ctx = 0ULL; + + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + if (dpaa2_eth_is_pfc_enabled(priv, i)) { + notification_cfg.threshold_entry = NAPI_POLL_WEIGHT; + notification_cfg.threshold_exit = NAPI_POLL_WEIGHT / 2; + } else { + notification_cfg.threshold_entry = 0; + notification_cfg.threshold_exit = 0; + } + + err = dpni_set_congestion_notification(priv->mc_io, 0, + priv->mc_token, + DPNI_QUEUE_RX, + i, ¬ification_cfg); + if (err) { + netdev_err(net_dev, "Error %d setting congestion notif", + err); + goto out_restore_config; + } + + netdev_dbg(net_dev, "%s congestion notifications for tc %d\n", + (notification_cfg.threshold_entry ? + "Enabled" : "Disabled"), i); + } + + return 0; + +out_restore_config: + memcpy(&priv->pfc, &old_pfc, sizeof(priv->pfc)); + return err; +} + +static u8 dpaa2_eth_dcbnl_getdcbx(struct net_device *net_dev) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + + return priv->dcbx_mode; +} + +static u8 dpaa2_eth_dcbnl_setdcbx(struct net_device *net_dev, u8 mode) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + + priv->dcbx_mode = mode; + return 0; +} + +static u8 dpaa2_eth_dcbnl_getcap(struct net_device *net_dev, int capid, u8 *cap) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + + switch (capid) { + case DCB_CAP_ATTR_PFC: + *cap = true; + break; + case DCB_CAP_ATTR_PFC_TCS: + /* bitmap where each bit represents a number of traffic + * classes the device can be configured to use for Priority + * Flow Control + */ + *cap = 1 << (dpaa2_eth_tc_count(priv) - 1); + break; + case DCB_CAP_ATTR_DCBX: + *cap = priv->dcbx_mode; + break; + default: + *cap = false; + break; + } + + return 0; +} + +const struct dcbnl_rtnl_ops dpaa2_eth_dcbnl_ops = { + .ieee_getpfc = dpaa2_eth_dcbnl_ieee_getpfc, + .ieee_setpfc = dpaa2_eth_dcbnl_ieee_setpfc, + .getdcbx = dpaa2_eth_dcbnl_getdcbx, + .setdcbx = dpaa2_eth_dcbnl_setdcbx, + .getcap = dpaa2_eth_dcbnl_getcap, +}; +#endif + static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev) { struct device *dev; @@ -2415,7 +3825,7 @@ static int dpaa2_eth_probe(struct fsl_mc dev = &dpni_dev->dev; /* Net device */ - net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_TX_QUEUES); + net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA2_ETH_MAX_NETDEV_QUEUES); if (!net_dev) { dev_err(dev, "alloc_etherdev_mq() failed\n"); return -ENOMEM; @@ -2433,7 +3843,10 @@ static int dpaa2_eth_probe(struct fsl_mc err = fsl_mc_portal_allocate(dpni_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &priv->mc_io); if (err) { - dev_err(dev, "MC portal allocation failed\n"); + if (err == -ENXIO) + err = -EPROBE_DEFER; + else + dev_err(dev, "MC portal allocation failed\n"); goto err_portal_alloc; } @@ -2456,9 +3869,6 @@ static int dpaa2_eth_probe(struct fsl_mc if (err) goto err_bind; - /* Add a NAPI context for each channel */ - add_ch_napi(priv); - /* Percpu statistics */ priv->percpu_stats = alloc_percpu(*priv->percpu_stats); if (!priv->percpu_stats) { @@ -2491,7 +3901,14 @@ static int dpaa2_eth_probe(struct fsl_mc if (err) goto err_alloc_rings; - net_dev->ethtool_ops = &dpaa2_ethtool_ops; +#ifdef CONFIG_FSL_DPAA2_ETH_DCB + net_dev->dcbnl_ops = &dpaa2_eth_dcbnl_ops; + priv->dcbx_mode = DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE; +#endif + + /* Add a NAPI context for each channel */ + add_ch_napi(priv); + enable_ch_napi(priv); err = setup_irqs(dpni_dev); if (err) { @@ -2499,25 +3916,41 @@ static int dpaa2_eth_probe(struct fsl_mc priv->poll_thread = kthread_run(poll_link_state, priv, "%s_poll_link", net_dev->name); if (IS_ERR(priv->poll_thread)) { - netdev_err(net_dev, "Error starting polling thread\n"); + dev_err(dev, "Error starting polling thread\n"); goto err_poll_thread; } priv->do_link_poll = true; } + err = register_netdev(net_dev); + if (err < 0) { + dev_err(dev, "register_netdev() failed\n"); + goto err_netdev_reg; + } + + dpaa2_eth_sysfs_init(&net_dev->dev); +#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS + dpaa2_dbg_add(priv); +#endif + dev_info(dev, "Probed interface %s\n", net_dev->name); return 0; +err_netdev_reg: + if (priv->do_link_poll) + kthread_stop(priv->poll_thread); + else + fsl_mc_free_irqs(dpni_dev); err_poll_thread: free_rings(priv); err_alloc_rings: err_csum: - unregister_netdev(net_dev); err_netdev_init: free_percpu(priv->percpu_extras); err_alloc_percpu_extras: free_percpu(priv->percpu_stats); err_alloc_percpu_stats: + disable_ch_napi(priv); del_ch_napi(priv); err_bind: free_dpbp(priv); @@ -2544,8 +3977,15 @@ static int dpaa2_eth_remove(struct fsl_m net_dev = dev_get_drvdata(dev); priv = netdev_priv(net_dev); +#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS + dpaa2_dbg_remove(priv); +#endif + dpaa2_eth_sysfs_remove(&net_dev->dev); + unregister_netdev(net_dev); - dev_info(net_dev->dev.parent, "Removed interface %s\n", net_dev->name); + + disable_ch_napi(priv); + del_ch_napi(priv); if (priv->do_link_poll) kthread_stop(priv->poll_thread); @@ -2555,17 +3995,16 @@ static int dpaa2_eth_remove(struct fsl_m free_rings(priv); free_percpu(priv->percpu_stats); free_percpu(priv->percpu_extras); - - del_ch_napi(priv); free_dpbp(priv); free_dpio(priv); free_dpni(priv); fsl_mc_portal_free(priv->mc_io); - dev_set_drvdata(dev, NULL); free_netdev(net_dev); + dev_dbg(net_dev->dev.parent, "Removed interface %s\n", net_dev->name); + return 0; } @@ -2588,4 +4027,34 @@ static struct fsl_mc_driver dpaa2_eth_dr .match_id_table = dpaa2_eth_match_id_table }; -module_fsl_mc_driver(dpaa2_eth_driver); +static int __init dpaa2_eth_driver_init(void) +{ + int err; + + dpaa2_eth_dbg_init(); + err = fsl_mc_driver_register(&dpaa2_eth_driver); + if (err) + goto out_debugfs_err; + + err = dpaa2_ceetm_register(); + if (err) + goto out_ceetm_err; + + return 0; + +out_ceetm_err: + fsl_mc_driver_unregister(&dpaa2_eth_driver); +out_debugfs_err: + dpaa2_eth_dbg_exit(); + return err; +} + +static void __exit dpaa2_eth_driver_exit(void) +{ + dpaa2_ceetm_unregister(); + fsl_mc_driver_unregister(&dpaa2_eth_driver); + dpaa2_eth_dbg_exit(); +} + +module_init(dpaa2_eth_driver_init); +module_exit(dpaa2_eth_driver_exit); --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-eth.h @@ -1,40 +1,15 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2014-2016 Freescale Semiconductor Inc. * Copyright 2016 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef __DPAA2_ETH_H #define __DPAA2_ETH_H +#include #include #include +#include #include "../../fsl-mc/include/dpaa2-io.h" #include "../../fsl-mc/include/dpaa2-fd.h" @@ -44,6 +19,9 @@ #include "dpni-cmd.h" #include "dpaa2-eth-trace.h" +#include "dpaa2-eth-debugfs.h" + +#define DPAA2_WRIOP_VERSION(x, y, z) ((x) << 10 | (y) << 5 | (z) << 0) #define DPAA2_ETH_STORE_SIZE 16 @@ -60,43 +38,59 @@ /* Convert L3 MTU to L2 MFL */ #define DPAA2_ETH_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN) -/* Set the taildrop threshold (in bytes) to allow the enqueue of several jumbo - * frames in the Rx queues (length of the current frame is not - * taken into account when making the taildrop decision) - */ -#define DPAA2_ETH_TAILDROP_THRESH (64 * 1024) - -/* Buffer quota per queue. Must be large enough such that for minimum sized - * frames taildrop kicks in before the bpool gets depleted, so we compute - * how many 64B frames fit inside the taildrop threshold and add a margin - * to accommodate the buffer refill delay. - */ -#define DPAA2_ETH_MAX_FRAMES_PER_QUEUE (DPAA2_ETH_TAILDROP_THRESH / 64) -#define DPAA2_ETH_NUM_BUFS (DPAA2_ETH_MAX_FRAMES_PER_QUEUE + 256) -#define DPAA2_ETH_REFILL_THRESH DPAA2_ETH_MAX_FRAMES_PER_QUEUE +/* Maximum burst size value for Tx shaping */ +#define DPAA2_ETH_MAX_BURST_SIZE 0xF7FF /* Maximum number of buffers that can be acquired/released through a single * QBMan command */ #define DPAA2_ETH_BUFS_PER_CMD 7 -/* Hardware requires alignment for ingress/egress buffer addresses - * and ingress buffer lengths. +/* Set the taildrop threshold to 1MB to allow the enqueue of a sufficiently + * large number of jumbo frames in the Rx queues (length of the current frame + * is not taken into account when making the taildrop decision) + */ +#define DPAA2_ETH_TAILDROP_THRESH (1024 * 1024) + +/* Maximum number of Tx confirmation frames to be processed + * in a single NAPI call + */ +#define DPAA2_ETH_TXCONF_PER_NAPI 256 + +/* Buffer quota per channel. + * We want to keep in check number of ingress frames in flight: for small + * sized frames, buffer pool depletion will kick in first; for large sizes, + * Rx FQ taildrop threshold will ensure only a reasonable number of frames + * will be pending at any given time. */ -#define DPAA2_ETH_RX_BUF_SIZE 2048 +#define DPAA2_ETH_NUM_BUFS_PER_CH 1024 +#define DPAA2_ETH_REFILL_THRESH(priv) \ + ((priv)->max_bufs_per_ch - DPAA2_ETH_BUFS_PER_CMD) + +/* Global buffer quota in case flow control is enabled */ +#define DPAA2_ETH_NUM_BUFS_FC 256 + +/* Hardware requires alignment for ingress/egress buffer addresses */ #define DPAA2_ETH_TX_BUF_ALIGN 64 -#define DPAA2_ETH_RX_BUF_ALIGN 256 -#define DPAA2_ETH_NEEDED_HEADROOM(p_priv) \ - ((p_priv)->tx_data_offset + DPAA2_ETH_TX_BUF_ALIGN) - -/* Hardware only sees DPAA2_ETH_RX_BUF_SIZE, but we need to allocate ingress - * buffers large enough to allow building an skb around them and also account - * for alignment restrictions - */ -#define DPAA2_ETH_BUF_RAW_SIZE \ - (DPAA2_ETH_RX_BUF_SIZE + \ - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) + \ - DPAA2_ETH_RX_BUF_ALIGN) + +#define DPAA2_ETH_RX_BUF_RAW_SIZE PAGE_SIZE +#define DPAA2_ETH_RX_BUF_TAILROOM \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +#define DPAA2_ETH_RX_BUF_SIZE \ + (DPAA2_ETH_RX_BUF_RAW_SIZE - DPAA2_ETH_RX_BUF_TAILROOM) + +/* Hardware annotation area in RX/TX buffers */ +#define DPAA2_ETH_RX_HWA_SIZE 64 +#define DPAA2_ETH_TX_HWA_SIZE 128 + +/* PTP nominal frequency 1GHz */ +#define DPAA2_PTP_NOMINAL_FREQ_PERIOD_NS 1 + +/* Due to a limitation in WRIOP 1.0.0, the RX buffer data must be aligned + * to 256B. For newer revisions, the requirement is only for 64B alignment + */ +#define DPAA2_ETH_RX_BUF_ALIGN_REV1 256 +#define DPAA2_ETH_RX_BUF_ALIGN 64 /* We are accommodating a skb backpointer and some S/G info * in the frame's software annotation. The hardware @@ -104,12 +98,32 @@ */ #define DPAA2_ETH_SWA_SIZE 64 +/* We store different information in the software annotation area of a Tx frame + * based on what type of frame it is + */ +enum dpaa2_eth_swa_type { + DPAA2_ETH_SWA_SINGLE, + DPAA2_ETH_SWA_SG, + DPAA2_ETH_SWA_XDP, +}; + /* Must keep this struct smaller than DPAA2_ETH_SWA_SIZE */ struct dpaa2_eth_swa { - struct sk_buff *skb; - struct scatterlist *scl; - int num_sg; - int num_dma_bufs; + enum dpaa2_eth_swa_type type; + union { + struct { + struct sk_buff *skb; + } single; + struct { + struct sk_buff *skb; + struct scatterlist *scl; + int num_sg; + int sgt_size; + } sg; + struct { + int dma_size; + } xdp; + }; }; /* Annotation valid bits in FD FRC */ @@ -121,22 +135,14 @@ struct dpaa2_eth_swa { #define DPAA2_FD_FRC_FAICFDV 0x0400 /* Error bits in FD CTRL */ -#define DPAA2_FD_CTRL_UFD 0x00000004 -#define DPAA2_FD_CTRL_SBE 0x00000008 -#define DPAA2_FD_CTRL_FSE 0x00000020 -#define DPAA2_FD_CTRL_FAERR 0x00000040 - -#define DPAA2_FD_RX_ERR_MASK (DPAA2_FD_CTRL_SBE | \ - DPAA2_FD_CTRL_FAERR) -#define DPAA2_FD_TX_ERR_MASK (DPAA2_FD_CTRL_UFD | \ - DPAA2_FD_CTRL_SBE | \ - DPAA2_FD_CTRL_FSE | \ - DPAA2_FD_CTRL_FAERR) +#define DPAA2_FD_RX_ERR_MASK (FD_CTRL_SBE | FD_CTRL_FAERR) +#define DPAA2_FD_TX_ERR_MASK (FD_CTRL_UFD | \ + FD_CTRL_SBE | \ + FD_CTRL_FSE | \ + FD_CTRL_FAERR) /* Annotation bits in FD CTRL */ -#define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128 */ -#define DPAA2_FD_CTRL_PTA 0x00800000 -#define DPAA2_FD_CTRL_PTV1 0x00400000 +#define DPAA2_FD_CTRL_ASAL 0x00020000 /* ASAL = 128B */ /* Frame annotation status */ struct dpaa2_fas { @@ -144,7 +150,7 @@ struct dpaa2_fas { u8 ppid; __le16 ifpid; __le32 status; -} __packed; +}; /* Frame annotation status word is located in the first 8 bytes * of the buffer's hardware annoatation area @@ -152,11 +158,45 @@ struct dpaa2_fas { #define DPAA2_FAS_OFFSET 0 #define DPAA2_FAS_SIZE (sizeof(struct dpaa2_fas)) +/* Timestamp is located in the next 8 bytes of the buffer's + * hardware annotation area + */ +#define DPAA2_TS_OFFSET 0x8 + +/* Frame annotation egress action descriptor */ +#define DPAA2_FAEAD_OFFSET 0x58 + +struct dpaa2_faead { + __le32 conf_fqid; + __le32 ctrl; +}; + +#define DPAA2_FAEAD_A2V 0x20000000 +#define DPAA2_FAEAD_A4V 0x08000000 +#define DPAA2_FAEAD_UPDV 0x00001000 +#define DPAA2_FAEAD_EBDDV 0x00002000 +#define DPAA2_FAEAD_UPD 0x00000010 + /* Accessors for the hardware annotation fields that we use */ -#define dpaa2_get_hwa(buf_addr) \ - ((void *)(buf_addr) + DPAA2_ETH_SWA_SIZE) -#define dpaa2_get_fas(buf_addr) \ - (struct dpaa2_fas *)(dpaa2_get_hwa(buf_addr) + DPAA2_FAS_OFFSET) +static inline void *dpaa2_get_hwa(void *buf_addr, bool swa) +{ + return buf_addr + (swa ? DPAA2_ETH_SWA_SIZE : 0); +} + +static inline struct dpaa2_fas *dpaa2_get_fas(void *buf_addr, bool swa) +{ + return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAS_OFFSET; +} + +static inline __le64 *dpaa2_get_ts(void *buf_addr, bool swa) +{ + return dpaa2_get_hwa(buf_addr, swa) + DPAA2_TS_OFFSET; +} + +static inline struct dpaa2_faead *dpaa2_get_faead(void *buf_addr, bool swa) +{ + return dpaa2_get_hwa(buf_addr, swa) + DPAA2_FAEAD_OFFSET; +} /* Error and status bits in the frame annotation status word */ /* Debug frame, otherwise supposed to be discarded */ @@ -203,11 +243,6 @@ struct dpaa2_fas { DPAA2_FAS_BLE | \ DPAA2_FAS_L3CE | \ DPAA2_FAS_L4CE) -/* Tx errors */ -#define DPAA2_FAS_TX_ERR_MASK (DPAA2_FAS_KSE | \ - DPAA2_FAS_EOFHE | \ - DPAA2_FAS_MNLE | \ - DPAA2_FAS_TIDE) /* Time in milliseconds between link state updates */ #define DPAA2_ETH_LINK_STATE_REFRESH 1000 @@ -226,6 +261,7 @@ struct dpaa2_eth_drv_stats { __u64 tx_conf_bytes; __u64 tx_sg_frames; __u64 tx_sg_bytes; + __u64 tx_reallocs; __u64 rx_sg_frames; __u64 rx_sg_bytes; /* Enqueues retried due to portal busy */ @@ -250,17 +286,23 @@ struct dpaa2_eth_ch_stats { __u64 pull_err; }; +#define DPAA2_ETH_MAX_TCS 8 + /* Maximum number of queues associated with a DPNI */ -#define DPAA2_ETH_MAX_RX_QUEUES 16 -#define DPAA2_ETH_MAX_TX_QUEUES NR_CPUS +#define DPAA2_ETH_MAX_RX_QUEUES (DPNI_MAX_DIST_SIZE * DPAA2_ETH_MAX_TCS) +#define DPAA2_ETH_MAX_TX_QUEUES DPNI_MAX_SENDERS +#define DPAA2_ETH_MAX_RX_ERR_QUEUES 1 #define DPAA2_ETH_MAX_QUEUES (DPAA2_ETH_MAX_RX_QUEUES + \ - DPAA2_ETH_MAX_TX_QUEUES) + DPAA2_ETH_MAX_TX_QUEUES + \ + DPAA2_ETH_MAX_RX_ERR_QUEUES) +#define DPAA2_ETH_MAX_NETDEV_QUEUES (DPNI_MAX_DIST_SIZE * DPAA2_ETH_MAX_TCS) -#define DPAA2_ETH_MAX_DPCONS NR_CPUS +#define DPAA2_ETH_MAX_DPCONS 16 enum dpaa2_eth_fq_type { DPAA2_RX_FQ = 0, DPAA2_TX_CONF_FQ, + DPAA2_RX_ERR_FQ }; struct dpaa2_eth_priv; @@ -268,15 +310,19 @@ struct dpaa2_eth_priv; struct dpaa2_eth_fq { u32 fqid; u32 tx_qdbin; + u32 tx_fqid; u16 flowid; + u8 tc; int target_cpu; + u32 dq_frames; + u32 dq_bytes; struct dpaa2_eth_channel *channel; enum dpaa2_eth_fq_type type; - void (*consume)(struct dpaa2_eth_priv *, - struct dpaa2_eth_channel *, - const struct dpaa2_fd *, - struct napi_struct *); + void (*consume)(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_channel *ch, + const struct dpaa2_fd *fd, + struct dpaa2_eth_fq *fq); struct dpaa2_eth_fq_stats stats; }; @@ -285,19 +331,29 @@ struct dpaa2_eth_channel { struct fsl_mc_device *dpcon; int dpcon_id; int ch_id; - int dpio_id; struct napi_struct napi; + struct dpaa2_io *dpio; struct dpaa2_io_store *store; struct dpaa2_eth_priv *priv; int buf_count; struct dpaa2_eth_ch_stats stats; + struct bpf_prog *xdp_prog; + u64 rel_buf_array[DPAA2_ETH_BUFS_PER_CMD]; + u8 rel_buf_cnt; + bool flush; }; -struct dpaa2_eth_hash_fields { +struct dpaa2_eth_dist_fields { u64 rxnfc_field; enum net_prot cls_prot; int cls_field; int size; + u64 id; +}; + +struct dpaa2_eth_cls_rule { + struct ethtool_rx_flow_spec fs; + u8 in_use; }; /* Driver private data */ @@ -306,17 +362,29 @@ struct dpaa2_eth_priv { u8 num_fqs; struct dpaa2_eth_fq fq[DPAA2_ETH_MAX_QUEUES]; + int (*enqueue)(struct dpaa2_eth_priv *priv, + struct dpaa2_eth_fq *fq, + struct dpaa2_fd *fd, u8 prio); u8 num_channels; struct dpaa2_eth_channel *channel[DPAA2_ETH_MAX_DPCONS]; + int max_bufs_per_ch; + int refill_thresh; + + bool has_xdp_prog; struct dpni_attr dpni_attrs; + u16 dpni_ver_major; + u16 dpni_ver_minor; u16 tx_data_offset; struct fsl_mc_device *dpbp_dev; u16 bpid; struct iommu_domain *iommu_domain; + bool ts_tx_en; /* Tx timestamping enabled */ + bool ts_rx_en; /* Rx timestamping enabled */ + u16 tx_qdid; struct fsl_mc_io *mc_io; /* Cores which have an affine DPIO/DPCON. @@ -337,13 +405,30 @@ struct dpaa2_eth_priv { /* enabled ethtool hashing bits */ u64 rx_hash_fields; + u64 rx_cls_fields; + struct dpaa2_eth_cls_rule *cls_rule; + u8 rx_cls_enabled; +#ifdef CONFIG_FSL_DPAA2_ETH_DEBUGFS + struct dpaa2_debugfs dbg; +#endif + struct dpni_tx_shaping_cfg shaping_cfg; + + u8 dcbx_mode; + struct ieee_pfc pfc; + bool vlan_clsf_set; + bool tx_pause_frames; + + bool ceetm_en; }; -/* default Rx hash options, set during probing */ #define DPAA2_RXH_SUPPORTED (RXH_L2DA | RXH_VLAN | RXH_L3_PROTO \ | RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 \ | RXH_L4_B_2_3) +/* default Rx hash options, set during probing */ +#define DPAA2_RXH_DEFAULT (RXH_L3_PROTO | RXH_IP_SRC | RXH_IP_DST | \ + RXH_L4_B_0_1 | RXH_L4_B_2_3) + #define dpaa2_eth_hash_enabled(priv) \ ((priv)->dpni_attrs.num_queues > 1) @@ -352,10 +437,127 @@ struct dpaa2_eth_priv { extern const struct ethtool_ops dpaa2_ethtool_ops; extern const char dpaa2_eth_drv_version[]; +extern int dpaa2_phc_index; + +static inline int dpaa2_eth_cmp_dpni_ver(struct dpaa2_eth_priv *priv, + u16 ver_major, u16 ver_minor) +{ + if (priv->dpni_ver_major == ver_major) + return priv->dpni_ver_minor - ver_minor; + return priv->dpni_ver_major - ver_major; +} + +/* Minimum firmware version that supports a more flexible API + * for configuring the Rx flow hash key + */ +#define DPNI_RX_DIST_KEY_VER_MAJOR 7 +#define DPNI_RX_DIST_KEY_VER_MINOR 5 + +#define dpaa2_eth_has_legacy_dist(priv) \ + (dpaa2_eth_cmp_dpni_ver((priv), DPNI_RX_DIST_KEY_VER_MAJOR, \ + DPNI_RX_DIST_KEY_VER_MINOR) < 0) + +#define dpaa2_eth_fs_enabled(priv) \ + (!((priv)->dpni_attrs.options & DPNI_OPT_NO_FS)) + +#define dpaa2_eth_fs_mask_enabled(priv) \ + ((priv)->dpni_attrs.options & DPNI_OPT_HAS_KEY_MASKING) + +#define dpaa2_eth_fs_count(priv) \ + ((priv)->dpni_attrs.fs_entries) + +#define dpaa2_eth_queue_count(priv) \ + ((priv)->num_channels) + +#define dpaa2_eth_tc_count(priv) \ + ((priv)->dpni_attrs.num_tcs) + +enum dpaa2_eth_rx_dist { + DPAA2_ETH_RX_DIST_HASH, + DPAA2_ETH_RX_DIST_CLS +}; + +/* Unique IDs for the supported Rx classification header fields */ +#define DPAA2_ETH_DIST_ETHDST BIT(0) +#define DPAA2_ETH_DIST_ETHSRC BIT(1) +#define DPAA2_ETH_DIST_ETHTYPE BIT(2) +#define DPAA2_ETH_DIST_VLAN BIT(3) +#define DPAA2_ETH_DIST_IPSRC BIT(4) +#define DPAA2_ETH_DIST_IPDST BIT(5) +#define DPAA2_ETH_DIST_IPPROTO BIT(6) +#define DPAA2_ETH_DIST_L4SRC BIT(7) +#define DPAA2_ETH_DIST_L4DST BIT(8) +#define DPAA2_ETH_DIST_ALL (~0U) + +static inline +unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv, + struct sk_buff *skb) +{ + unsigned int headroom = DPAA2_ETH_SWA_SIZE; + + /* If we don't have an skb (e.g. XDP buffer), we only need space for + * the software annotation area + */ + if (!skb) + return headroom; -static int dpaa2_eth_queue_count(struct dpaa2_eth_priv *priv) + /* For non-linear skbs we have no headroom requirement, as we build a + * SG frame with a newly allocated SGT buffer + */ + if (skb_is_nonlinear(skb)) + return 0; + + /* If we have Tx timestamping, need 128B hardware annotation */ + if (priv->ts_tx_en && skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) + headroom += DPAA2_ETH_TX_HWA_SIZE; + + return headroom; +} + +/* Extra headroom space requested to hardware, in order to make sure there's + * no realloc'ing in forwarding scenarios + */ +static inline unsigned int dpaa2_eth_rx_headroom(struct dpaa2_eth_priv *priv) +{ + return priv->tx_data_offset - DPAA2_ETH_RX_HWA_SIZE; +} + +static inline bool dpaa2_eth_is_pfc_enabled(struct dpaa2_eth_priv *priv, + int traffic_class) +{ + return priv->pfc.pfc_en & (1 << traffic_class); +} + +enum dpaa2_eth_td_cfg { + DPAA2_ETH_TD_NONE, + DPAA2_ETH_TD_QUEUE, + DPAA2_ETH_TD_GROUP +}; + +static inline enum dpaa2_eth_td_cfg +dpaa2_eth_get_td_type(struct dpaa2_eth_priv *priv) +{ + bool pfc_enabled = !!(priv->pfc.pfc_en); + + if (pfc_enabled) + return DPAA2_ETH_TD_GROUP; + else if (priv->tx_pause_frames) + return DPAA2_ETH_TD_NONE; + else + return DPAA2_ETH_TD_QUEUE; +} + +static inline int dpaa2_eth_ch_count(struct dpaa2_eth_priv *priv) { - return priv->dpni_attrs.num_queues; + return 1; } +int dpaa2_eth_set_hash(struct net_device *net_dev, u64 flags); +int dpaa2_eth_set_cls(struct net_device *net_dev, u64 key); +int dpaa2_eth_cls_key_size(u64 key); +int dpaa2_eth_cls_fld_off(int prot, int field); +void dpaa2_eth_cls_trim_rule(void *key_mem, u64 fields); + +int set_rx_taildrop(struct dpaa2_eth_priv *priv); + #endif /* __DPAA2_H */ --- a/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c +++ b/drivers/staging/fsl-dpaa2/ethernet/dpaa2-ethtool.c @@ -1,35 +1,10 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2014-2016 Freescale Semiconductor Inc. - * Copyright 2016 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Freescale Semiconductor nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Copyright 2016-2017 NXP */ +#include + #include "dpni.h" /* DPNI_LINK_OPT_* */ #include "dpaa2-eth.h" @@ -52,6 +27,10 @@ static char dpaa2_ethtool_stats[][ETH_GS "[hw] rx nobuffer discards", "[hw] tx discarded frames", "[hw] tx confirmed frames", + "[hw] tx dequeued bytes", + "[hw] tx dequeued frames", + "[hw] tx rejected bytes", + "[hw] tx rejected frames", }; #define DPAA2_ETH_NUM_STATS ARRAY_SIZE(dpaa2_ethtool_stats) @@ -62,6 +41,7 @@ static char dpaa2_ethtool_extras[][ETH_G "[drv] tx conf bytes", "[drv] tx sg frames", "[drv] tx sg bytes", + "[drv] tx realloc frames", "[drv] rx sg frames", "[drv] rx sg bytes", "[drv] enqueue portal busy", @@ -69,6 +49,12 @@ static char dpaa2_ethtool_extras[][ETH_G "[drv] dequeue portal busy", "[drv] channel pull errors", "[drv] cdan", + /* FQ stats */ + "rx pending frames", + "rx pending bytes", + "tx conf pending frames", + "tx conf pending bytes", + "buffer count" }; #define DPAA2_ETH_NUM_EXTRA_STATS ARRAY_SIZE(dpaa2_ethtool_extras) @@ -76,14 +62,55 @@ static char dpaa2_ethtool_extras[][ETH_G static void dpaa2_eth_get_drvinfo(struct net_device *net_dev, struct ethtool_drvinfo *drvinfo) { + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); - strlcpy(drvinfo->version, dpaa2_eth_drv_version, - sizeof(drvinfo->version)); - strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%u.%u", priv->dpni_ver_major, priv->dpni_ver_minor); + strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), sizeof(drvinfo->bus_info)); } +#define DPNI_LINK_AUTONEG_VER_MAJOR 7 +#define DPNI_LINK_AUTONEG_VER_MINOR 8 + +struct dpaa2_eth_link_mode_map { + u64 dpni_lm; + u64 ethtool_lm; +}; + +static const struct dpaa2_eth_link_mode_map dpaa2_eth_lm_map[] = { + {DPNI_ADVERTISED_10BASET_FULL, ETHTOOL_LINK_MODE_10baseT_Full_BIT}, + {DPNI_ADVERTISED_100BASET_FULL, ETHTOOL_LINK_MODE_100baseT_Full_BIT}, + {DPNI_ADVERTISED_1000BASET_FULL, ETHTOOL_LINK_MODE_1000baseT_Full_BIT}, + {DPNI_ADVERTISED_10000BASET_FULL, ETHTOOL_LINK_MODE_10000baseT_Full_BIT}, + {DPNI_ADVERTISED_2500BASEX_FULL, ETHTOOL_LINK_MODE_2500baseX_Full_BIT}, + {DPNI_ADVERTISED_AUTONEG, ETHTOOL_LINK_MODE_Autoneg_BIT}, +}; + +static void link_mode_dpni2ethtool(u64 dpni_lm, unsigned long *ethtool_lm) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dpaa2_eth_lm_map); i++) { + if (dpni_lm & dpaa2_eth_lm_map[i].dpni_lm) + __set_bit(dpaa2_eth_lm_map[i].ethtool_lm, ethtool_lm); + } +} + +static void link_mode_ethtool2dpni(const unsigned long *ethtool_lm, + u64 *dpni_lm) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dpaa2_eth_lm_map); i++) { + if (test_bit(dpaa2_eth_lm_map[i].ethtool_lm, ethtool_lm)) + *dpni_lm |= dpaa2_eth_lm_map[i].dpni_lm; + } +} + static int dpaa2_eth_get_link_ksettings(struct net_device *net_dev, struct ethtool_link_ksettings *link_settings) @@ -92,17 +119,27 @@ dpaa2_eth_get_link_ksettings(struct net_ int err = 0; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); - err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); - if (err) { - netdev_err(net_dev, "ERROR %d getting link state\n", err); - goto out; + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_LINK_AUTONEG_VER_MAJOR, + DPNI_LINK_AUTONEG_VER_MINOR) < 0) { + err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, + &state); + if (err) { + netdev_err(net_dev, "dpni_get_link_state failed\n"); + goto out; + } + } else { + err = dpni_get_link_state_v2(priv->mc_io, 0, priv->mc_token, + &state); + if (err) { + netdev_err(net_dev, "dpni_get_link_state_v2 failed\n"); + goto out; + } + link_mode_dpni2ethtool(state.supported, + link_settings->link_modes.supported); + link_mode_dpni2ethtool(state.advertising, + link_settings->link_modes.advertising); } - /* At the moment, we have no way of interrogating the DPMAC - * from the DPNI side - and for that matter there may exist - * no DPMAC at all. So for now we just don't report anything - * beyond the DPNI attributes. - */ if (state.options & DPNI_LINK_OPT_AUTONEG) link_settings->base.autoneg = AUTONEG_ENABLE; if (!(state.options & DPNI_LINK_OPT_HALF_DUPLEX)) @@ -113,25 +150,37 @@ out: return err; } +#define DPNI_DYNAMIC_LINK_SET_VER_MAJOR 7 +#define DPNI_DYNAMIC_LINK_SET_VER_MINOR 1 static int dpaa2_eth_set_link_ksettings(struct net_device *net_dev, const struct ethtool_link_ksettings *link_settings) { - struct dpni_link_cfg cfg = {0}; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpni_link_state state = {0}; + struct dpni_link_cfg cfg = {0}; int err = 0; - netdev_dbg(net_dev, "Setting link parameters..."); + /* If using an older MC version, the DPNI must be down + * in order to be able to change link settings. Taking steps to let + * the user know that. + */ + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_DYNAMIC_LINK_SET_VER_MAJOR, + DPNI_DYNAMIC_LINK_SET_VER_MINOR) < 0) { + if (netif_running(net_dev)) { + netdev_info(net_dev, "Interface must be brought down first.\n"); + return -EACCES; + } + } - /* Due to a temporary MC limitation, the DPNI must be down - * in order to be able to change link settings. Taking steps to let - * the user know that. - */ - if (netif_running(net_dev)) { - netdev_info(net_dev, "Sorry, interface must be brought down first.\n"); - return -EACCES; + /* Need to interrogate link state to get flow control params */ + err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); + if (err) { + netdev_err(net_dev, "Error getting link state\n"); + goto out; } + cfg.options = state.options; cfg.rate = link_settings->base.speed; if (link_settings->base.autoneg == AUTONEG_ENABLE) cfg.options |= DPNI_LINK_OPT_AUTONEG; @@ -142,13 +191,92 @@ dpaa2_eth_set_link_ksettings(struct net_ else cfg.options &= ~DPNI_LINK_OPT_HALF_DUPLEX; + if (dpaa2_eth_cmp_dpni_ver(priv, DPNI_LINK_AUTONEG_VER_MAJOR, + DPNI_LINK_AUTONEG_VER_MINOR)) { + err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); + } else { + link_mode_ethtool2dpni(link_settings->link_modes.advertising, + &cfg.advertising); + dpni_set_link_cfg_v2(priv->mc_io, 0, priv->mc_token, &cfg); + } + if (err) + netdev_err(net_dev, "dpni_set_link_cfg failed"); + +out: + return err; +} + +static void dpaa2_eth_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpni_link_state state = {0}; + int err; + + err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); + if (err) + netdev_dbg(net_dev, "Error getting link state\n"); + + /* Report general port autonegotiation status */ + pause->autoneg = !!(state.options & DPNI_LINK_OPT_AUTONEG); + pause->rx_pause = !!(state.options & DPNI_LINK_OPT_PAUSE); + pause->tx_pause = pause->rx_pause ^ + !!(state.options & DPNI_LINK_OPT_ASYM_PAUSE); +} + +static int dpaa2_eth_set_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpni_link_state state = {0}; + struct dpni_link_cfg cfg = {0}; + u32 current_tx_pause; + int err = 0; + + err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); + if (err) { + netdev_dbg(net_dev, "Error getting link state\n"); + goto out; + } + + cfg.rate = state.rate; + cfg.options = state.options; + current_tx_pause = !!(cfg.options & DPNI_LINK_OPT_PAUSE) ^ + !!(cfg.options & DPNI_LINK_OPT_ASYM_PAUSE); + + /* We don't support changing pause frame autonegotiation separately + * from general port autoneg + */ + if (pause->autoneg != !!(state.options & DPNI_LINK_OPT_AUTONEG)) + netdev_warn(net_dev, + "Cannot change pause frame autoneg separately\n"); + + if (pause->rx_pause) + cfg.options |= DPNI_LINK_OPT_PAUSE; + else + cfg.options &= ~DPNI_LINK_OPT_PAUSE; + + if (pause->rx_pause ^ pause->tx_pause) + cfg.options |= DPNI_LINK_OPT_ASYM_PAUSE; + else + cfg.options &= ~DPNI_LINK_OPT_ASYM_PAUSE; + err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); + if (err) { + netdev_dbg(net_dev, "Error setting link\n"); + goto out; + } + + /* Enable/disable Rx FQ taildrop if Tx pause frames have changed */ + if (current_tx_pause == pause->tx_pause) + goto out; + + priv->tx_pause_frames = pause->tx_pause; + err = set_rx_taildrop(priv); if (err) - /* ethtool will be loud enough if we return an error; no point - * in putting our own error message on the console by default - */ - netdev_dbg(net_dev, "ERROR %d setting link cfg\n", err); + netdev_dbg(net_dev, "Error configuring taildrop\n"); +out: return err; } @@ -192,6 +320,10 @@ static void dpaa2_eth_get_ethtool_stats( int j, k, err; int num_cnt; union dpni_statistics dpni_stats; + u32 fcnt, bcnt; + u32 fcnt_rx_total = 0, fcnt_tx_total = 0; + u32 bcnt_rx_total = 0, bcnt_tx_total = 0; + u32 buf_cnt; u64 cdan = 0; u64 portal_busy = 0, pull_err = 0; struct dpaa2_eth_priv *priv = netdev_priv(net_dev); @@ -202,9 +334,9 @@ static void dpaa2_eth_get_ethtool_stats( sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS)); /* Print standard counters, from DPNI statistics */ - for (j = 0; j <= 2; j++) { + for (j = 0; j <= 3; j++) { err = dpni_get_statistics(priv->mc_io, 0, priv->mc_token, - j, &dpni_stats); + j, 0, &dpni_stats); if (err != 0) netdev_warn(net_dev, "dpni_get_stats(%d) failed\n", j); switch (j) { @@ -217,6 +349,9 @@ static void dpaa2_eth_get_ethtool_stats( case 2: num_cnt = sizeof(dpni_stats.page_2) / sizeof(u64); break; + case 3: + num_cnt = sizeof(dpni_stats.page_3) / sizeof(u64); + break; } for (k = 0; k < num_cnt; k++) *(data + i++) = dpni_stats.raw.counter[k]; @@ -240,12 +375,410 @@ static void dpaa2_eth_get_ethtool_stats( *(data + i++) = portal_busy; *(data + i++) = pull_err; *(data + i++) = cdan; + + for (j = 0; j < priv->num_fqs; j++) { + /* Print FQ instantaneous counts */ + err = dpaa2_io_query_fq_count(NULL, priv->fq[j].fqid, + &fcnt, &bcnt); + if (err) { + netdev_warn(net_dev, "FQ query error %d", err); + return; + } + + if (priv->fq[j].type == DPAA2_TX_CONF_FQ) { + fcnt_tx_total += fcnt; + bcnt_tx_total += bcnt; + } else { + fcnt_rx_total += fcnt; + bcnt_rx_total += bcnt; + } + } + + *(data + i++) = fcnt_rx_total; + *(data + i++) = bcnt_rx_total; + *(data + i++) = fcnt_tx_total; + *(data + i++) = bcnt_tx_total; + + err = dpaa2_io_query_bp_count(NULL, priv->bpid, &buf_cnt); + if (err) { + netdev_warn(net_dev, "Buffer count query error %d\n", err); + return; + } + *(data + i++) = buf_cnt; +} + +static int prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask, + void *key, void *mask, u64 *fields) +{ + int off; + + if (eth_mask->h_proto) { + off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE); + *(__be16 *)(key + off) = eth_value->h_proto; + *(__be16 *)(mask + off) = eth_mask->h_proto; + *fields |= DPAA2_ETH_DIST_ETHTYPE; + } + + if (!is_zero_ether_addr(eth_mask->h_source)) { + off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_SA); + ether_addr_copy(key + off, eth_value->h_source); + ether_addr_copy(mask + off, eth_mask->h_source); + *fields |= DPAA2_ETH_DIST_ETHSRC; + } + + if (!is_zero_ether_addr(eth_mask->h_dest)) { + off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA); + ether_addr_copy(key + off, eth_value->h_dest); + ether_addr_copy(mask + off, eth_mask->h_dest); + *fields |= DPAA2_ETH_DIST_ETHDST; + } + + return 0; +} + +static int prep_user_ip_rule(struct ethtool_usrip4_spec *uip_value, + struct ethtool_usrip4_spec *uip_mask, + void *key, void *mask, u64 *fields) +{ + int off; + u32 tmp_value, tmp_mask; + + if (uip_mask->tos || uip_mask->ip_ver) + return -EOPNOTSUPP; + + if (uip_mask->ip4src) { + off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC); + *(__be32 *)(key + off) = uip_value->ip4src; + *(__be32 *)(mask + off) = uip_mask->ip4src; + *fields |= DPAA2_ETH_DIST_IPSRC; + } + + if (uip_mask->ip4dst) { + off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST); + *(__be32 *)(key + off) = uip_value->ip4dst; + *(__be32 *)(mask + off) = uip_mask->ip4dst; + *fields |= DPAA2_ETH_DIST_IPDST; + } + + if (uip_mask->proto) { + off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO); + *(u8 *)(key + off) = uip_value->proto; + *(u8 *)(mask + off) = uip_mask->proto; + *fields |= DPAA2_ETH_DIST_IPPROTO; + } + + if (uip_mask->l4_4_bytes) { + tmp_value = be32_to_cpu(uip_value->l4_4_bytes); + tmp_mask = be32_to_cpu(uip_mask->l4_4_bytes); + + off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); + *(__be16 *)(key + off) = htons(tmp_value >> 16); + *(__be16 *)(mask + off) = htons(tmp_mask >> 16); + *fields |= DPAA2_ETH_DIST_L4SRC; + + off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST); + *(__be16 *)(key + off) = htons(tmp_value & 0xFFFF); + *(__be16 *)(mask + off) = htons(tmp_mask & 0xFFFF); + *fields |= DPAA2_ETH_DIST_L4DST; + } + + /* Only apply the rule for IPv4 frames */ + off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE); + *(__be16 *)(key + off) = htons(ETH_P_IP); + *(__be16 *)(mask + off) = htons(0xFFFF); + *fields |= DPAA2_ETH_DIST_ETHTYPE; + + return 0; +} + +static int prep_l4_rule(struct ethtool_tcpip4_spec *l4_value, + struct ethtool_tcpip4_spec *l4_mask, + void *key, void *mask, u8 l4_proto, u64 *fields) +{ + int off; + + if (l4_mask->tos) + return -EOPNOTSUPP; + if (l4_mask->ip4src) { + off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_SRC); + *(__be32 *)(key + off) = l4_value->ip4src; + *(__be32 *)(mask + off) = l4_mask->ip4src; + *fields |= DPAA2_ETH_DIST_IPSRC; + } + + if (l4_mask->ip4dst) { + off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_DST); + *(__be32 *)(key + off) = l4_value->ip4dst; + *(__be32 *)(mask + off) = l4_mask->ip4dst; + *fields |= DPAA2_ETH_DIST_IPDST; + } + + if (l4_mask->psrc) { + off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_SRC); + *(__be16 *)(key + off) = l4_value->psrc; + *(__be16 *)(mask + off) = l4_mask->psrc; + *fields |= DPAA2_ETH_DIST_L4SRC; + } + + if (l4_mask->pdst) { + off = dpaa2_eth_cls_fld_off(NET_PROT_UDP, NH_FLD_UDP_PORT_DST); + *(__be16 *)(key + off) = l4_value->pdst; + *(__be16 *)(mask + off) = l4_mask->pdst; + *fields |= DPAA2_ETH_DIST_L4DST; + } + + /* Only apply the rule for the user-specified L4 protocol + * and if ethertype matches IPv4 + */ + off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_TYPE); + *(__be16 *)(key + off) = htons(ETH_P_IP); + *(__be16 *)(mask + off) = htons(0xFFFF); + *fields |= DPAA2_ETH_DIST_ETHTYPE; + + off = dpaa2_eth_cls_fld_off(NET_PROT_IP, NH_FLD_IP_PROTO); + *(u8 *)(key + off) = l4_proto; + *(u8 *)(mask + off) = 0xFF; + *fields |= DPAA2_ETH_DIST_IPPROTO; + + return 0; +} + +static int prep_ext_rule(struct ethtool_flow_ext *ext_value, + struct ethtool_flow_ext *ext_mask, + void *key, void *mask, u64 *fields) +{ + int off; + + if (ext_mask->vlan_etype) + return -EOPNOTSUPP; + + if (ext_mask->vlan_tci) { + off = dpaa2_eth_cls_fld_off(NET_PROT_VLAN, NH_FLD_VLAN_TCI); + *(__be16 *)(key + off) = ext_value->vlan_tci; + *(__be16 *)(mask + off) = ext_mask->vlan_tci; + *fields |= DPAA2_ETH_DIST_VLAN; + } + + return 0; +} + +static int prep_mac_ext_rule(struct ethtool_flow_ext *ext_value, + struct ethtool_flow_ext *ext_mask, + void *key, void *mask, u64 *fields) +{ + int off; + + if (!is_zero_ether_addr(ext_mask->h_dest)) { + off = dpaa2_eth_cls_fld_off(NET_PROT_ETH, NH_FLD_ETH_DA); + ether_addr_copy(key + off, ext_value->h_dest); + ether_addr_copy(mask + off, ext_mask->h_dest); + *fields |= DPAA2_ETH_DIST_ETHDST; + } + + return 0; +} + +static int prep_cls_rule(struct ethtool_rx_flow_spec *fs, void *key, void *mask, + u64 *fields) +{ + int err; + + switch (fs->flow_type & 0xFF) { + case ETHER_FLOW: + err = prep_eth_rule(&fs->h_u.ether_spec, &fs->m_u.ether_spec, + key, mask, fields); + break; + case IP_USER_FLOW: + err = prep_user_ip_rule(&fs->h_u.usr_ip4_spec, + &fs->m_u.usr_ip4_spec, key, mask, fields); + break; + case TCP_V4_FLOW: + err = prep_l4_rule(&fs->h_u.tcp_ip4_spec, &fs->m_u.tcp_ip4_spec, + key, mask, IPPROTO_TCP, fields); + break; + case UDP_V4_FLOW: + err = prep_l4_rule(&fs->h_u.udp_ip4_spec, &fs->m_u.udp_ip4_spec, + key, mask, IPPROTO_UDP, fields); + break; + case SCTP_V4_FLOW: + err = prep_l4_rule(&fs->h_u.sctp_ip4_spec, &fs->m_u.sctp_ip4_spec, + key, mask, IPPROTO_SCTP, fields); + break; + default: + return -EOPNOTSUPP; + } + + if (err) + return err; + + if (fs->flow_type & FLOW_EXT) { + err = prep_ext_rule(&fs->h_ext, &fs->m_ext, key, mask, fields); + if (err) + return err; + } + + if (fs->flow_type & FLOW_MAC_EXT) { + err = prep_mac_ext_rule(&fs->h_ext, &fs->m_ext, key, mask, + fields); + if (err) + return err; + } + + return 0; +} + +static int do_cls_rule(struct net_device *net_dev, + struct ethtool_rx_flow_spec *fs, + bool add) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct device *dev = net_dev->dev.parent; + struct dpni_rule_cfg rule_cfg = { 0 }; + struct dpni_fs_action_cfg fs_act = { 0 }; + dma_addr_t key_iova; + u64 fields = 0; + void *key_buf; + int i, err = 0; + + if (fs->ring_cookie != RX_CLS_FLOW_DISC && + fs->ring_cookie >= dpaa2_eth_queue_count(priv)) + return -EINVAL; + + rule_cfg.key_size = dpaa2_eth_cls_key_size(DPAA2_ETH_DIST_ALL); + + /* allocate twice the key size, for the actual key and for mask */ + key_buf = kzalloc(rule_cfg.key_size * 2, GFP_KERNEL); + if (!key_buf) + return -ENOMEM; + + /* Fill the key and mask memory areas */ + err = prep_cls_rule(fs, key_buf, key_buf + rule_cfg.key_size, &fields); + if (err) + goto free_mem; + + if (!dpaa2_eth_fs_mask_enabled(priv)) { + /* Masking allows us to configure a maximal key during init and + * use it for all flow steering rules. Without it, we include + * in the key only the fields actually used, so we need to + * extract the others from the final key buffer. + * + * Program the FS key if needed, or return error if previously + * set key can't be used for the current rule. User needs to + * delete existing rules in this case to allow for the new one. + */ + if (!priv->rx_cls_fields) { + err = dpaa2_eth_set_cls(net_dev, fields); + if (err) + goto free_mem; + + priv->rx_cls_fields = fields; + } else if (priv->rx_cls_fields != fields) { + netdev_err(net_dev, "No support for multiple FS keys, need to delete existing rules\n"); + err = -EOPNOTSUPP; + goto free_mem; + } + + dpaa2_eth_cls_trim_rule(key_buf, fields); + rule_cfg.key_size = dpaa2_eth_cls_key_size(fields); + } + + key_iova = dma_map_single(dev, key_buf, rule_cfg.key_size * 2, + DMA_TO_DEVICE); + if (dma_mapping_error(dev, key_iova)) { + err = -ENOMEM; + goto free_mem; + } + + rule_cfg.key_iova = key_iova; + if (dpaa2_eth_fs_mask_enabled(priv)) + rule_cfg.mask_iova = key_iova + rule_cfg.key_size; + + if (add) { + if (fs->ring_cookie == RX_CLS_FLOW_DISC) + fs_act.options |= DPNI_FS_OPT_DISCARD; + else + fs_act.flow_id = fs->ring_cookie; + } + for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { + if (add) + err = dpni_add_fs_entry(priv->mc_io, 0, priv->mc_token, + i, fs->location, &rule_cfg, + &fs_act); + else + err = dpni_remove_fs_entry(priv->mc_io, 0, + priv->mc_token, i, + &rule_cfg); + if (err) + break; + } + + dma_unmap_single(dev, key_iova, rule_cfg.key_size * 2, DMA_TO_DEVICE); + +free_mem: + kfree(key_buf); + + return err; +} + +static int num_rules(struct dpaa2_eth_priv *priv) +{ + int i, rules = 0; + + for (i = 0; i < dpaa2_eth_fs_count(priv); i++) + if (priv->cls_rule[i].in_use) + rules++; + + return rules; +} + +static int update_cls_rule(struct net_device *net_dev, + struct ethtool_rx_flow_spec *new_fs, + int location) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + struct dpaa2_eth_cls_rule *rule; + int err = -EINVAL; + + if (!priv->rx_cls_enabled) + return -EOPNOTSUPP; + + if (location >= dpaa2_eth_fs_count(priv)) + return -EINVAL; + + rule = &priv->cls_rule[location]; + + /* If a rule is present at the specified location, delete it. */ + if (rule->in_use) { + err = do_cls_rule(net_dev, &rule->fs, false); + if (err) + return err; + + rule->in_use = 0; + + if (!dpaa2_eth_fs_mask_enabled(priv) && !num_rules(priv)) + priv->rx_cls_fields = 0; + } + + /* If no new entry to add, return here */ + if (!new_fs) + return err; + + err = do_cls_rule(net_dev, new_fs, true); + if (err) + return err; + + rule->in_use = 1; + rule->fs = *new_fs; + + return 0; } static int dpaa2_eth_get_rxnfc(struct net_device *net_dev, struct ethtool_rxnfc *rxnfc, u32 *rule_locs) { struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + int rule_cnt = dpaa2_eth_fs_count(priv); + int i, j = 0; switch (rxnfc->cmd) { case ETHTOOL_GRXFH: @@ -258,6 +791,29 @@ static int dpaa2_eth_get_rxnfc(struct ne case ETHTOOL_GRXRINGS: rxnfc->data = dpaa2_eth_queue_count(priv); break; + case ETHTOOL_GRXCLSRLCNT: + rxnfc->rule_cnt = 0; + rxnfc->rule_cnt = num_rules(priv); + rxnfc->data = rule_cnt; + break; + case ETHTOOL_GRXCLSRULE: + if (rxnfc->fs.location >= rule_cnt) + return -EINVAL; + if (!priv->cls_rule[rxnfc->fs.location].in_use) + return -EINVAL; + rxnfc->fs = priv->cls_rule[rxnfc->fs.location].fs; + break; + case ETHTOOL_GRXCLSRLALL: + for (i = 0; i < rule_cnt; i++) { + if (!priv->cls_rule[i].in_use) + continue; + if (j == rxnfc->rule_cnt) + return -EMSGSIZE; + rule_locs[j++] = i; + } + rxnfc->rule_cnt = j; + rxnfc->data = rule_cnt; + break; default: return -EOPNOTSUPP; } @@ -265,13 +821,61 @@ static int dpaa2_eth_get_rxnfc(struct ne return 0; } +int dpaa2_phc_index = -1; +EXPORT_SYMBOL(dpaa2_phc_index); + +static int dpaa2_eth_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + info->so_timestamping = SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + + info->phc_index = dpaa2_phc_index; + + info->tx_types = (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + + info->rx_filters = (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_ALL); + return 0; +} + +static int dpaa2_eth_set_rxnfc(struct net_device *net_dev, + struct ethtool_rxnfc *rxnfc) +{ + int err = 0; + + switch (rxnfc->cmd) { + case ETHTOOL_SRXFH: + if ((rxnfc->data & DPAA2_RXH_SUPPORTED) != rxnfc->data) + return -EOPNOTSUPP; + err = dpaa2_eth_set_hash(net_dev, rxnfc->data); + break; + case ETHTOOL_SRXCLSRLINS: + err = update_cls_rule(net_dev, &rxnfc->fs, rxnfc->fs.location); + break; + case ETHTOOL_SRXCLSRLDEL: + err = update_cls_rule(net_dev, NULL, rxnfc->fs.location); + break; + default: + err = -EOPNOTSUPP; + } + + return err; +} + const struct ethtool_ops dpaa2_ethtool_ops = { .get_drvinfo = dpaa2_eth_get_drvinfo, .get_link = ethtool_op_get_link, .get_link_ksettings = dpaa2_eth_get_link_ksettings, .set_link_ksettings = dpaa2_eth_set_link_ksettings, + .get_pauseparam = dpaa2_eth_get_pauseparam, + .set_pauseparam = dpaa2_eth_set_pauseparam, .get_sset_count = dpaa2_eth_get_sset_count, .get_ethtool_stats = dpaa2_eth_get_ethtool_stats, .get_strings = dpaa2_eth_get_strings, .get_rxnfc = dpaa2_eth_get_rxnfc, + .set_rxnfc = dpaa2_eth_set_rxnfc, + .get_ts_info = dpaa2_eth_get_ts_info, }; --- a/drivers/staging/fsl-dpaa2/ethernet/dpkg.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpkg.h @@ -1,39 +1,10 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2013-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the above-listed copyright holders nor the - * names of any contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FSL_DPKG_H_ #define __FSL_DPKG_H_ #include -#include "net.h" /* Data Path Key Generator API * Contains initialization APIs and runtime APIs for the Key Generator @@ -86,6 +57,355 @@ struct dpkg_mask { u8 offset; }; +/* Protocol fields */ + +/* Ethernet fields */ +#define NH_FLD_ETH_DA BIT(0) +#define NH_FLD_ETH_SA BIT(1) +#define NH_FLD_ETH_LENGTH BIT(2) +#define NH_FLD_ETH_TYPE BIT(3) +#define NH_FLD_ETH_FINAL_CKSUM BIT(4) +#define NH_FLD_ETH_PADDING BIT(5) +#define NH_FLD_ETH_ALL_FIELDS (BIT(6) - 1) + +/* VLAN fields */ +#define NH_FLD_VLAN_VPRI BIT(0) +#define NH_FLD_VLAN_CFI BIT(1) +#define NH_FLD_VLAN_VID BIT(2) +#define NH_FLD_VLAN_LENGTH BIT(3) +#define NH_FLD_VLAN_TYPE BIT(4) +#define NH_FLD_VLAN_ALL_FIELDS (BIT(5) - 1) + +#define NH_FLD_VLAN_TCI (NH_FLD_VLAN_VPRI | \ + NH_FLD_VLAN_CFI | \ + NH_FLD_VLAN_VID) + +/* IP (generic) fields */ +#define NH_FLD_IP_VER BIT(0) +#define NH_FLD_IP_DSCP BIT(2) +#define NH_FLD_IP_ECN BIT(3) +#define NH_FLD_IP_PROTO BIT(4) +#define NH_FLD_IP_SRC BIT(5) +#define NH_FLD_IP_DST BIT(6) +#define NH_FLD_IP_TOS_TC BIT(7) +#define NH_FLD_IP_ID BIT(8) +#define NH_FLD_IP_ALL_FIELDS (BIT(9) - 1) + +/* IPV4 fields */ +#define NH_FLD_IPV4_VER BIT(0) +#define NH_FLD_IPV4_HDR_LEN BIT(1) +#define NH_FLD_IPV4_TOS BIT(2) +#define NH_FLD_IPV4_TOTAL_LEN BIT(3) +#define NH_FLD_IPV4_ID BIT(4) +#define NH_FLD_IPV4_FLAG_D BIT(5) +#define NH_FLD_IPV4_FLAG_M BIT(6) +#define NH_FLD_IPV4_OFFSET BIT(7) +#define NH_FLD_IPV4_TTL BIT(8) +#define NH_FLD_IPV4_PROTO BIT(9) +#define NH_FLD_IPV4_CKSUM BIT(10) +#define NH_FLD_IPV4_SRC_IP BIT(11) +#define NH_FLD_IPV4_DST_IP BIT(12) +#define NH_FLD_IPV4_OPTS BIT(13) +#define NH_FLD_IPV4_OPTS_COUNT BIT(14) +#define NH_FLD_IPV4_ALL_FIELDS (BIT(15) - 1) + +/* IPV6 fields */ +#define NH_FLD_IPV6_VER BIT(0) +#define NH_FLD_IPV6_TC BIT(1) +#define NH_FLD_IPV6_SRC_IP BIT(2) +#define NH_FLD_IPV6_DST_IP BIT(3) +#define NH_FLD_IPV6_NEXT_HDR BIT(4) +#define NH_FLD_IPV6_FL BIT(5) +#define NH_FLD_IPV6_HOP_LIMIT BIT(6) +#define NH_FLD_IPV6_ID BIT(7) +#define NH_FLD_IPV6_ALL_FIELDS (BIT(8) - 1) + +/* ICMP fields */ +#define NH_FLD_ICMP_TYPE BIT(0) +#define NH_FLD_ICMP_CODE BIT(1) +#define NH_FLD_ICMP_CKSUM BIT(2) +#define NH_FLD_ICMP_ID BIT(3) +#define NH_FLD_ICMP_SQ_NUM BIT(4) +#define NH_FLD_ICMP_ALL_FIELDS (BIT(5) - 1) + +/* IGMP fields */ +#define NH_FLD_IGMP_VERSION BIT(0) +#define NH_FLD_IGMP_TYPE BIT(1) +#define NH_FLD_IGMP_CKSUM BIT(2) +#define NH_FLD_IGMP_DATA BIT(3) +#define NH_FLD_IGMP_ALL_FIELDS (BIT(4) - 1) + +/* TCP fields */ +#define NH_FLD_TCP_PORT_SRC BIT(0) +#define NH_FLD_TCP_PORT_DST BIT(1) +#define NH_FLD_TCP_SEQ BIT(2) +#define NH_FLD_TCP_ACK BIT(3) +#define NH_FLD_TCP_OFFSET BIT(4) +#define NH_FLD_TCP_FLAGS BIT(5) +#define NH_FLD_TCP_WINDOW BIT(6) +#define NH_FLD_TCP_CKSUM BIT(7) +#define NH_FLD_TCP_URGPTR BIT(8) +#define NH_FLD_TCP_OPTS BIT(9) +#define NH_FLD_TCP_OPTS_COUNT BIT(10) +#define NH_FLD_TCP_ALL_FIELDS (BIT(11) - 1) + +/* UDP fields */ +#define NH_FLD_UDP_PORT_SRC BIT(0) +#define NH_FLD_UDP_PORT_DST BIT(1) +#define NH_FLD_UDP_LEN BIT(2) +#define NH_FLD_UDP_CKSUM BIT(3) +#define NH_FLD_UDP_ALL_FIELDS (BIT(4) - 1) + +/* UDP-lite fields */ +#define NH_FLD_UDP_LITE_PORT_SRC BIT(0) +#define NH_FLD_UDP_LITE_PORT_DST BIT(1) +#define NH_FLD_UDP_LITE_ALL_FIELDS (BIT(2) - 1) + +/* UDP-encap-ESP fields */ +#define NH_FLD_UDP_ENC_ESP_PORT_SRC BIT(0) +#define NH_FLD_UDP_ENC_ESP_PORT_DST BIT(1) +#define NH_FLD_UDP_ENC_ESP_LEN BIT(2) +#define NH_FLD_UDP_ENC_ESP_CKSUM BIT(3) +#define NH_FLD_UDP_ENC_ESP_SPI BIT(4) +#define NH_FLD_UDP_ENC_ESP_SEQUENCE_NUM BIT(5) +#define NH_FLD_UDP_ENC_ESP_ALL_FIELDS (BIT(6) - 1) + +/* SCTP fields */ +#define NH_FLD_SCTP_PORT_SRC BIT(0) +#define NH_FLD_SCTP_PORT_DST BIT(1) +#define NH_FLD_SCTP_VER_TAG BIT(2) +#define NH_FLD_SCTP_CKSUM BIT(3) +#define NH_FLD_SCTP_ALL_FIELDS (BIT(4) - 1) + +/* DCCP fields */ +#define NH_FLD_DCCP_PORT_SRC BIT(0) +#define NH_FLD_DCCP_PORT_DST BIT(1) +#define NH_FLD_DCCP_ALL_FIELDS (BIT(2) - 1) + +/* IPHC fields */ +#define NH_FLD_IPHC_CID BIT(0) +#define NH_FLD_IPHC_CID_TYPE BIT(1) +#define NH_FLD_IPHC_HCINDEX BIT(2) +#define NH_FLD_IPHC_GEN BIT(3) +#define NH_FLD_IPHC_D_BIT BIT(4) +#define NH_FLD_IPHC_ALL_FIELDS (BIT(5) - 1) + +/* SCTP fields */ +#define NH_FLD_SCTP_CHUNK_DATA_TYPE BIT(0) +#define NH_FLD_SCTP_CHUNK_DATA_FLAGS BIT(1) +#define NH_FLD_SCTP_CHUNK_DATA_LENGTH BIT(2) +#define NH_FLD_SCTP_CHUNK_DATA_TSN BIT(3) +#define NH_FLD_SCTP_CHUNK_DATA_STREAM_ID BIT(4) +#define NH_FLD_SCTP_CHUNK_DATA_STREAM_SQN BIT(5) +#define NH_FLD_SCTP_CHUNK_DATA_PAYLOAD_PID BIT(6) +#define NH_FLD_SCTP_CHUNK_DATA_UNORDERED BIT(7) +#define NH_FLD_SCTP_CHUNK_DATA_BEGGINING BIT(8) +#define NH_FLD_SCTP_CHUNK_DATA_END BIT(9) +#define NH_FLD_SCTP_CHUNK_DATA_ALL_FIELDS (BIT(10) - 1) + +/* L2TPV2 fields */ +#define NH_FLD_L2TPV2_TYPE_BIT BIT(0) +#define NH_FLD_L2TPV2_LENGTH_BIT BIT(1) +#define NH_FLD_L2TPV2_SEQUENCE_BIT BIT(2) +#define NH_FLD_L2TPV2_OFFSET_BIT BIT(3) +#define NH_FLD_L2TPV2_PRIORITY_BIT BIT(4) +#define NH_FLD_L2TPV2_VERSION BIT(5) +#define NH_FLD_L2TPV2_LEN BIT(6) +#define NH_FLD_L2TPV2_TUNNEL_ID BIT(7) +#define NH_FLD_L2TPV2_SESSION_ID BIT(8) +#define NH_FLD_L2TPV2_NS BIT(9) +#define NH_FLD_L2TPV2_NR BIT(10) +#define NH_FLD_L2TPV2_OFFSET_SIZE BIT(11) +#define NH_FLD_L2TPV2_FIRST_BYTE BIT(12) +#define NH_FLD_L2TPV2_ALL_FIELDS (BIT(13) - 1) + +/* L2TPV3 fields */ +#define NH_FLD_L2TPV3_CTRL_TYPE_BIT BIT(0) +#define NH_FLD_L2TPV3_CTRL_LENGTH_BIT BIT(1) +#define NH_FLD_L2TPV3_CTRL_SEQUENCE_BIT BIT(2) +#define NH_FLD_L2TPV3_CTRL_VERSION BIT(3) +#define NH_FLD_L2TPV3_CTRL_LENGTH BIT(4) +#define NH_FLD_L2TPV3_CTRL_CONTROL BIT(5) +#define NH_FLD_L2TPV3_CTRL_SENT BIT(6) +#define NH_FLD_L2TPV3_CTRL_RECV BIT(7) +#define NH_FLD_L2TPV3_CTRL_FIRST_BYTE BIT(8) +#define NH_FLD_L2TPV3_CTRL_ALL_FIELDS (BIT(9) - 1) + +#define NH_FLD_L2TPV3_SESS_TYPE_BIT BIT(0) +#define NH_FLD_L2TPV3_SESS_VERSION BIT(1) +#define NH_FLD_L2TPV3_SESS_ID BIT(2) +#define NH_FLD_L2TPV3_SESS_COOKIE BIT(3) +#define NH_FLD_L2TPV3_SESS_ALL_FIELDS (BIT(4) - 1) + +/* PPP fields */ +#define NH_FLD_PPP_PID BIT(0) +#define NH_FLD_PPP_COMPRESSED BIT(1) +#define NH_FLD_PPP_ALL_FIELDS (BIT(2) - 1) + +/* PPPoE fields */ +#define NH_FLD_PPPOE_VER BIT(0) +#define NH_FLD_PPPOE_TYPE BIT(1) +#define NH_FLD_PPPOE_CODE BIT(2) +#define NH_FLD_PPPOE_SID BIT(3) +#define NH_FLD_PPPOE_LEN BIT(4) +#define NH_FLD_PPPOE_SESSION BIT(5) +#define NH_FLD_PPPOE_PID BIT(6) +#define NH_FLD_PPPOE_ALL_FIELDS (BIT(7) - 1) + +/* PPP-Mux fields */ +#define NH_FLD_PPPMUX_PID BIT(0) +#define NH_FLD_PPPMUX_CKSUM BIT(1) +#define NH_FLD_PPPMUX_COMPRESSED BIT(2) +#define NH_FLD_PPPMUX_ALL_FIELDS (BIT(3) - 1) + +/* PPP-Mux sub-frame fields */ +#define NH_FLD_PPPMUX_SUBFRM_PFF BIT(0) +#define NH_FLD_PPPMUX_SUBFRM_LXT BIT(1) +#define NH_FLD_PPPMUX_SUBFRM_LEN BIT(2) +#define NH_FLD_PPPMUX_SUBFRM_PID BIT(3) +#define NH_FLD_PPPMUX_SUBFRM_USE_PID BIT(4) +#define NH_FLD_PPPMUX_SUBFRM_ALL_FIELDS (BIT(5) - 1) + +/* LLC fields */ +#define NH_FLD_LLC_DSAP BIT(0) +#define NH_FLD_LLC_SSAP BIT(1) +#define NH_FLD_LLC_CTRL BIT(2) +#define NH_FLD_LLC_ALL_FIELDS (BIT(3) - 1) + +/* NLPID fields */ +#define NH_FLD_NLPID_NLPID BIT(0) +#define NH_FLD_NLPID_ALL_FIELDS (BIT(1) - 1) + +/* SNAP fields */ +#define NH_FLD_SNAP_OUI BIT(0) +#define NH_FLD_SNAP_PID BIT(1) +#define NH_FLD_SNAP_ALL_FIELDS (BIT(2) - 1) + +/* LLC SNAP fields */ +#define NH_FLD_LLC_SNAP_TYPE BIT(0) +#define NH_FLD_LLC_SNAP_ALL_FIELDS (BIT(1) - 1) + +/* ARP fields */ +#define NH_FLD_ARP_HTYPE BIT(0) +#define NH_FLD_ARP_PTYPE BIT(1) +#define NH_FLD_ARP_HLEN BIT(2) +#define NH_FLD_ARP_PLEN BIT(3) +#define NH_FLD_ARP_OPER BIT(4) +#define NH_FLD_ARP_SHA BIT(5) +#define NH_FLD_ARP_SPA BIT(6) +#define NH_FLD_ARP_THA BIT(7) +#define NH_FLD_ARP_TPA BIT(8) +#define NH_FLD_ARP_ALL_FIELDS (BIT(9) - 1) + +/* RFC2684 fields */ +#define NH_FLD_RFC2684_LLC BIT(0) +#define NH_FLD_RFC2684_NLPID BIT(1) +#define NH_FLD_RFC2684_OUI BIT(2) +#define NH_FLD_RFC2684_PID BIT(3) +#define NH_FLD_RFC2684_VPN_OUI BIT(4) +#define NH_FLD_RFC2684_VPN_IDX BIT(5) +#define NH_FLD_RFC2684_ALL_FIELDS (BIT(6) - 1) + +/* User defined fields */ +#define NH_FLD_USER_DEFINED_SRCPORT BIT(0) +#define NH_FLD_USER_DEFINED_PCDID BIT(1) +#define NH_FLD_USER_DEFINED_ALL_FIELDS (BIT(2) - 1) + +/* Payload fields */ +#define NH_FLD_PAYLOAD_BUFFER BIT(0) +#define NH_FLD_PAYLOAD_SIZE BIT(1) +#define NH_FLD_MAX_FRM_SIZE BIT(2) +#define NH_FLD_MIN_FRM_SIZE BIT(3) +#define NH_FLD_PAYLOAD_TYPE BIT(4) +#define NH_FLD_FRAME_SIZE BIT(5) +#define NH_FLD_PAYLOAD_ALL_FIELDS (BIT(6) - 1) + +/* GRE fields */ +#define NH_FLD_GRE_TYPE BIT(0) +#define NH_FLD_GRE_ALL_FIELDS (BIT(1) - 1) + +/* MINENCAP fields */ +#define NH_FLD_MINENCAP_SRC_IP BIT(0) +#define NH_FLD_MINENCAP_DST_IP BIT(1) +#define NH_FLD_MINENCAP_TYPE BIT(2) +#define NH_FLD_MINENCAP_ALL_FIELDS (BIT(3) - 1) + +/* IPSEC AH fields */ +#define NH_FLD_IPSEC_AH_SPI BIT(0) +#define NH_FLD_IPSEC_AH_NH BIT(1) +#define NH_FLD_IPSEC_AH_ALL_FIELDS (BIT(2) - 1) + +/* IPSEC ESP fields */ +#define NH_FLD_IPSEC_ESP_SPI BIT(0) +#define NH_FLD_IPSEC_ESP_SEQUENCE_NUM BIT(1) +#define NH_FLD_IPSEC_ESP_ALL_FIELDS (BIT(2) - 1) + +/* MPLS fields */ +#define NH_FLD_MPLS_LABEL_STACK BIT(0) +#define NH_FLD_MPLS_LABEL_STACK_ALL_FIELDS (BIT(1) - 1) + +/* MACSEC fields */ +#define NH_FLD_MACSEC_SECTAG BIT(0) +#define NH_FLD_MACSEC_ALL_FIELDS (BIT(1) - 1) + +/* GTP fields */ +#define NH_FLD_GTP_TEID BIT(0) + +/* Supported protocols */ +enum net_prot { + NET_PROT_NONE = 0, + NET_PROT_PAYLOAD, + NET_PROT_ETH, + NET_PROT_VLAN, + NET_PROT_IPV4, + NET_PROT_IPV6, + NET_PROT_IP, + NET_PROT_TCP, + NET_PROT_UDP, + NET_PROT_UDP_LITE, + NET_PROT_IPHC, + NET_PROT_SCTP, + NET_PROT_SCTP_CHUNK_DATA, + NET_PROT_PPPOE, + NET_PROT_PPP, + NET_PROT_PPPMUX, + NET_PROT_PPPMUX_SUBFRM, + NET_PROT_L2TPV2, + NET_PROT_L2TPV3_CTRL, + NET_PROT_L2TPV3_SESS, + NET_PROT_LLC, + NET_PROT_LLC_SNAP, + NET_PROT_NLPID, + NET_PROT_SNAP, + NET_PROT_MPLS, + NET_PROT_IPSEC_AH, + NET_PROT_IPSEC_ESP, + NET_PROT_UDP_ENC_ESP, /* RFC 3948 */ + NET_PROT_MACSEC, + NET_PROT_GRE, + NET_PROT_MINENCAP, + NET_PROT_DCCP, + NET_PROT_ICMP, + NET_PROT_IGMP, + NET_PROT_ARP, + NET_PROT_CAPWAP_DATA, + NET_PROT_CAPWAP_CTRL, + NET_PROT_RFC2684, + NET_PROT_ICMPV6, + NET_PROT_FCOE, + NET_PROT_FIP, + NET_PROT_ISCSI, + NET_PROT_GTP, + NET_PROT_USER_DEFINED_L2, + NET_PROT_USER_DEFINED_L3, + NET_PROT_USER_DEFINED_L4, + NET_PROT_USER_DEFINED_L5, + NET_PROT_USER_DEFINED_SHIM1, + NET_PROT_USER_DEFINED_SHIM2, + + NET_PROT_DUMMY_LAST +}; + /** * struct dpkg_extract - A structure for defining a single extraction * @type: Determines how the union below is interpreted: --- a/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni-cmd.h @@ -1,34 +1,6 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2013-2016 Freescale Semiconductor Inc. * Copyright 2016 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the above-listed copyright holders nor the - * names of any contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. */ #ifndef _FSL_DPNI_CMD_H #define _FSL_DPNI_CMD_H @@ -39,9 +11,11 @@ #define DPNI_VER_MAJOR 7 #define DPNI_VER_MINOR 0 #define DPNI_CMD_BASE_VERSION 1 +#define DPNI_CMD_2ND_VERSION 2 #define DPNI_CMD_ID_OFFSET 4 #define DPNI_CMD(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_BASE_VERSION) +#define DPNI_CMD_V2(id) (((id) << DPNI_CMD_ID_OFFSET) | DPNI_CMD_2ND_VERSION) #define DPNI_CMDID_OPEN DPNI_CMD(0x801) #define DPNI_CMDID_CLOSE DPNI_CMD(0x800) @@ -64,16 +38,18 @@ #define DPNI_CMDID_GET_IRQ_STATUS DPNI_CMD(0x016) #define DPNI_CMDID_CLEAR_IRQ_STATUS DPNI_CMD(0x017) -#define DPNI_CMDID_SET_POOLS DPNI_CMD(0x200) +#define DPNI_CMDID_SET_POOLS DPNI_CMD_V2(0x200) #define DPNI_CMDID_SET_ERRORS_BEHAVIOR DPNI_CMD(0x20B) #define DPNI_CMDID_GET_QDID DPNI_CMD(0x210) #define DPNI_CMDID_GET_TX_DATA_OFFSET DPNI_CMD(0x212) #define DPNI_CMDID_GET_LINK_STATE DPNI_CMD(0x215) +#define DPNI_CMDID_GET_LINK_STATE_V2 DPNI_CMD_V2(0x215) #define DPNI_CMDID_SET_MAX_FRAME_LENGTH DPNI_CMD(0x216) #define DPNI_CMDID_GET_MAX_FRAME_LENGTH DPNI_CMD(0x217) #define DPNI_CMDID_SET_LINK_CFG DPNI_CMD(0x21A) -#define DPNI_CMDID_SET_TX_SHAPING DPNI_CMD(0x21B) +#define DPNI_CMDID_SET_LINK_CFG_V2 DPNI_CMD_V2(0x21A) +#define DPNI_CMDID_SET_TX_SHAPING DPNI_CMD_V2(0x21B) #define DPNI_CMDID_SET_MCAST_PROMISC DPNI_CMD(0x220) #define DPNI_CMDID_GET_MCAST_PROMISC DPNI_CMD(0x221) @@ -87,11 +63,16 @@ #define DPNI_CMDID_SET_RX_TC_DIST DPNI_CMD(0x235) +#define DPNI_CMDID_SET_QOS_TBL DPNI_CMD(0x240) +#define DPNI_CMDID_ADD_QOS_ENT DPNI_CMD(0x241) +#define DPNI_CMDID_REMOVE_QOS_ENT DPNI_CMD(0x242) #define DPNI_CMDID_ADD_FS_ENT DPNI_CMD(0x244) #define DPNI_CMDID_REMOVE_FS_ENT DPNI_CMD(0x245) #define DPNI_CMDID_CLR_FS_ENT DPNI_CMD(0x246) -#define DPNI_CMDID_GET_STATISTICS DPNI_CMD(0x25D) +#define DPNI_CMDID_SET_TX_PRIORITIES DPNI_CMD_V2(0x250) +#define DPNI_CMDID_GET_STATISTICS DPNI_CMD_V2(0x25D) +#define DPNI_CMDID_RESET_STATISTICS DPNI_CMD(0x25E) #define DPNI_CMDID_GET_QUEUE DPNI_CMD(0x25F) #define DPNI_CMDID_SET_QUEUE DPNI_CMD(0x260) #define DPNI_CMDID_GET_TAILDROP DPNI_CMD(0x261) @@ -110,6 +91,9 @@ #define DPNI_CMDID_GET_OFFLOAD DPNI_CMD(0x26B) #define DPNI_CMDID_SET_OFFLOAD DPNI_CMD(0x26C) +#define DPNI_CMDID_SET_RX_FS_DIST DPNI_CMD(0x273) +#define DPNI_CMDID_SET_RX_HASH_DIST DPNI_CMD(0x274) + /* Macros for accessing command fields smaller than 1byte */ #define DPNI_MASK(field) \ GENMASK(DPNI_##field##_SHIFT + DPNI_##field##_SIZE - 1, \ @@ -126,13 +110,14 @@ struct dpni_cmd_open { #define DPNI_BACKUP_POOL(val, order) (((val) & 0x1) << (order)) struct dpni_cmd_set_pools { - /* cmd word 0 */ u8 num_dpbp; u8 backup_pool_mask; __le16 pad; - /* cmd word 0..4 */ - __le32 dpbp_id[DPNI_MAX_DPBP]; - /* cmd word 4..6 */ + struct { + __le16 dpbp_id; + u8 priority_mask; + u8 pad; + } pool[DPNI_MAX_DPBP]; __le16 buffer_size[DPNI_MAX_DPBP]; }; @@ -303,6 +288,7 @@ struct dpni_rsp_get_tx_data_offset { struct dpni_cmd_get_statistics { u8 page_number; + u8 param; }; struct dpni_rsp_get_statistics { @@ -319,8 +305,22 @@ struct dpni_cmd_set_link_cfg { __le64 options; }; +struct dpni_cmd_set_link_cfg_v2 { + /* cmd word 0 */ + __le64 pad0; + /* cmd word 1 */ + __le32 rate; + __le32 pad1; + /* cmd word 2 */ + __le64 options; + /* cmd word 3 */ + __le64 advertising; +}; + #define DPNI_LINK_STATE_SHIFT 0 #define DPNI_LINK_STATE_SIZE 1 +#define DPNI_STATE_VALID_SHIFT 1 +#define DPNI_STATE_VALID_SIZE 1 struct dpni_rsp_get_link_state { /* response word 0 */ @@ -335,6 +335,39 @@ struct dpni_rsp_get_link_state { __le64 options; }; +struct dpni_rsp_get_link_state_v2 { + /* response word 0 */ + __le32 pad0; + /* from LSB: up:1, valid:1 */ + u8 flags; + u8 pad1[3]; + /* response word 1 */ + __le32 rate; + __le32 pad2; + /* response word 2 */ + __le64 options; + /* cmd word 3 */ + __le64 supported; + /* cmd word 4 */ + __le64 advertising; +}; + +#define DPNI_COUPLED_SHIFT 0 +#define DPNI_COUPLED_SIZE 1 + +struct dpni_cmd_set_tx_shaping { + /* cmd word 0 */ + __le16 tx_cr_max_burst_size; + __le16 tx_er_max_burst_size; + __le32 pad; + /* cmd word 1 */ + __le32 tx_cr_rate_limit; + __le32 tx_er_rate_limit; + /* cmd word 2 */ + /* from LSB: coupled:1 */ + u8 coupled; +}; + struct dpni_cmd_set_max_frame_length { __le16 max_frame_length; }; @@ -394,6 +427,24 @@ struct dpni_cmd_clear_mac_filters { u8 flags; }; +#define DPNI_SEPARATE_GRP_SHIFT 0 +#define DPNI_SEPARATE_GRP_SIZE 1 +#define DPNI_MODE_1_SHIFT 0 +#define DPNI_MODE_1_SIZE 4 +#define DPNI_MODE_2_SHIFT 4 +#define DPNI_MODE_2_SIZE 4 + +struct dpni_cmd_set_tx_priorities { + __le16 flags; + u8 prio_group_A; + u8 prio_group_B; + __le32 pad0; + u8 modes[4]; + __le32 pad1; + __le64 pad2; + __le16 delta_bandwidth[8]; +}; + #define DPNI_DIST_MODE_SHIFT 0 #define DPNI_DIST_MODE_SIZE 4 #define DPNI_MISS_ACTION_SHIFT 4 @@ -503,6 +554,63 @@ struct dpni_cmd_set_queue { __le64 user_context; }; +#define DPNI_DISCARD_ON_MISS_SHIFT 0 +#define DPNI_DISCARD_ON_MISS_SIZE 1 + +struct dpni_cmd_set_qos_table { + __le32 pad; + u8 default_tc; + /* only the LSB */ + u8 discard_on_miss; + __le16 pad1[21]; + __le64 key_cfg_iova; +}; + +struct dpni_cmd_add_qos_entry { + __le16 pad; + u8 tc_id; + u8 key_size; + __le16 index; + __le16 pad2; + __le64 key_iova; + __le64 mask_iova; +}; + +struct dpni_cmd_remove_qos_entry { + u8 pad1[3]; + u8 key_size; + __le32 pad2; + __le64 key_iova; + __le64 mask_iova; +}; + +struct dpni_cmd_add_fs_entry { + /* cmd word 0 */ + __le16 options; + u8 tc_id; + u8 key_size; + __le16 index; + __le16 flow_id; + /* cmd word 1 */ + __le64 key_iova; + /* cmd word 2 */ + __le64 mask_iova; + /* cmd word 3 */ + __le64 flc; +}; + +struct dpni_cmd_remove_fs_entry { + /* cmd word 0 */ + __le16 pad0; + u8 tc_id; + u8 key_size; + __le32 pad1; + /* cmd word 1 */ + __le64 key_iova; + /* cmd word 2 */ + __le64 mask_iova; +}; + struct dpni_cmd_set_taildrop { /* cmd word 0 */ u8 congestion_point; @@ -538,4 +646,79 @@ struct dpni_rsp_get_taildrop { __le32 threshold; }; +struct dpni_rsp_get_api_version { + u16 major; + u16 minor; +}; + +#define DPNI_DEST_TYPE_SHIFT 0 +#define DPNI_DEST_TYPE_SIZE 4 +#define DPNI_CONG_UNITS_SHIFT 4 +#define DPNI_CONG_UNITS_SIZE 2 + +struct dpni_cmd_set_congestion_notification { + /* cmd word 0 */ + u8 qtype; + u8 tc; + u8 pad[6]; + /* cmd word 1 */ + __le32 dest_id; + __le16 notification_mode; + u8 dest_priority; + /* from LSB: dest_type: 4 units:2 */ + u8 type_units; + /* cmd word 2 */ + __le64 message_iova; + /* cmd word 3 */ + __le64 message_ctx; + /* cmd word 4 */ + __le32 threshold_entry; + __le32 threshold_exit; +}; + +struct dpni_cmd_get_congestion_notification { + /* cmd word 0 */ + u8 qtype; + u8 tc; +}; + +struct dpni_rsp_get_congestion_notification { + /* cmd word 0 */ + __le64 pad; + /* cmd word 1 */ + __le32 dest_id; + __le16 notification_mode; + u8 dest_priority; + /* from LSB: dest_type: 4 units:2 */ + u8 type_units; + /* cmd word 2 */ + __le64 message_iova; + /* cmd word 3 */ + __le64 message_ctx; + /* cmd word 4 */ + __le32 threshold_entry; + __le32 threshold_exit; +}; + +#define DPNI_RX_FS_DIST_ENABLE_SHIFT 0 +#define DPNI_RX_FS_DIST_ENABLE_SIZE 1 +struct dpni_cmd_set_rx_fs_dist { + __le16 dist_size; + u8 enable; + u8 tc; + __le16 miss_flow_id; + __le16 pad; + __le64 key_cfg_iova; +}; + +#define DPNI_RX_HASH_DIST_ENABLE_SHIFT 0 +#define DPNI_RX_HASH_DIST_ENABLE_SIZE 1 +struct dpni_cmd_set_rx_hash_dist { + __le16 dist_size; + u8 enable; + u8 tc; + __le32 pad; + __le64 key_cfg_iova; +}; + #endif /* _FSL_DPNI_CMD_H */ --- a/drivers/staging/fsl-dpaa2/ethernet/dpni.c +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.c @@ -1,34 +1,6 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* Copyright 2013-2016 Freescale Semiconductor Inc. * Copyright 2016 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the above-listed copyright holders nor the - * names of any contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. */ #include #include @@ -122,7 +94,7 @@ int dpni_open(struct fsl_mc_io *mc_io, int dpni_id, u16 *token) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_open *cmd_params; int err; @@ -160,7 +132,7 @@ int dpni_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; /* prepare command */ cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLOSE, @@ -188,7 +160,7 @@ int dpni_set_pools(struct fsl_mc_io *mc_ u16 token, const struct dpni_pools_cfg *cfg) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_pools *cmd_params; int i; @@ -199,7 +171,10 @@ int dpni_set_pools(struct fsl_mc_io *mc_ cmd_params = (struct dpni_cmd_set_pools *)cmd.params; cmd_params->num_dpbp = cfg->num_dpbp; for (i = 0; i < DPNI_MAX_DPBP; i++) { - cmd_params->dpbp_id[i] = cpu_to_le32(cfg->pools[i].dpbp_id); + cmd_params->pool[i].dpbp_id = + cpu_to_le16(cfg->pools[i].dpbp_id); + cmd_params->pool[i].priority_mask = + cfg->pools[i].priority_mask; cmd_params->buffer_size[i] = cpu_to_le16(cfg->pools[i].buffer_size); cmd_params->backup_pool_mask |= @@ -222,7 +197,7 @@ int dpni_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; /* prepare command */ cmd.header = mc_encode_cmd_header(DPNI_CMDID_ENABLE, @@ -245,7 +220,7 @@ int dpni_disable(struct fsl_mc_io *mc_io u32 cmd_flags, u16 token) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; /* prepare command */ cmd.header = mc_encode_cmd_header(DPNI_CMDID_DISABLE, @@ -270,7 +245,7 @@ int dpni_is_enabled(struct fsl_mc_io *mc u16 token, int *en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_is_enabled *rsp_params; int err; @@ -303,7 +278,7 @@ int dpni_reset(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; /* prepare command */ cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET, @@ -335,7 +310,7 @@ int dpni_set_irq_enable(struct fsl_mc_io u8 irq_index, u8 en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_irq_enable *cmd_params; /* prepare command */ @@ -366,7 +341,7 @@ int dpni_get_irq_enable(struct fsl_mc_io u8 irq_index, u8 *en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_irq_enable *cmd_params; struct dpni_rsp_get_irq_enable *rsp_params; @@ -413,7 +388,7 @@ int dpni_set_irq_mask(struct fsl_mc_io * u8 irq_index, u32 mask) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_irq_mask *cmd_params; /* prepare command */ @@ -447,7 +422,7 @@ int dpni_get_irq_mask(struct fsl_mc_io * u8 irq_index, u32 *mask) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_irq_mask *cmd_params; struct dpni_rsp_get_irq_mask *rsp_params; int err; @@ -489,7 +464,7 @@ int dpni_get_irq_status(struct fsl_mc_io u8 irq_index, u32 *status) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_irq_status *cmd_params; struct dpni_rsp_get_irq_status *rsp_params; int err; @@ -532,7 +507,7 @@ int dpni_clear_irq_status(struct fsl_mc_ u8 irq_index, u32 status) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_clear_irq_status *cmd_params; /* prepare command */ @@ -561,7 +536,7 @@ int dpni_get_attributes(struct fsl_mc_io u16 token, struct dpni_attr *attr) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_attr *rsp_params; int err; @@ -609,7 +584,7 @@ int dpni_set_errors_behavior(struct fsl_ u16 token, struct dpni_error_cfg *cfg) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_errors_behavior *cmd_params; /* prepare command */ @@ -641,7 +616,7 @@ int dpni_get_buffer_layout(struct fsl_mc enum dpni_queue_type qtype, struct dpni_buffer_layout *layout) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_buffer_layout *cmd_params; struct dpni_rsp_get_buffer_layout *rsp_params; int err; @@ -689,7 +664,7 @@ int dpni_set_buffer_layout(struct fsl_mc enum dpni_queue_type qtype, const struct dpni_buffer_layout *layout) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_buffer_layout *cmd_params; /* prepare command */ @@ -731,7 +706,7 @@ int dpni_set_offload(struct fsl_mc_io *m enum dpni_offload type, u32 config) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_offload *cmd_params; cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_OFFLOAD, @@ -750,7 +725,7 @@ int dpni_get_offload(struct fsl_mc_io *m enum dpni_offload type, u32 *config) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_offload *cmd_params; struct dpni_rsp_get_offload *rsp_params; int err; @@ -792,7 +767,7 @@ int dpni_get_qdid(struct fsl_mc_io *mc_i enum dpni_queue_type qtype, u16 *qdid) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_qdid *cmd_params; struct dpni_rsp_get_qdid *rsp_params; int err; @@ -830,7 +805,7 @@ int dpni_get_tx_data_offset(struct fsl_m u16 token, u16 *data_offset) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_tx_data_offset *rsp_params; int err; @@ -865,7 +840,7 @@ int dpni_set_link_cfg(struct fsl_mc_io * u16 token, const struct dpni_link_cfg *cfg) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_link_cfg *cmd_params; /* prepare command */ @@ -881,6 +856,36 @@ int dpni_set_link_cfg(struct fsl_mc_io * } /** + * dpni_set_link_cfg_v2() - set the link configuration. + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: Link configuration + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_set_link_cfg_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_link_cfg *cfg) +{ + struct fsl_mc_command cmd = { 0 }; + struct dpni_cmd_set_link_cfg_v2 *cmd_params; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_LINK_CFG_V2, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_link_cfg_v2 *)cmd.params; + cmd_params->rate = cpu_to_le32(cfg->rate); + cmd_params->options = cpu_to_le64(cfg->options); + cmd_params->advertising = cpu_to_le64(cfg->advertising); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** * dpni_get_link_state() - Return the link state (either up or down) * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' @@ -894,7 +899,7 @@ int dpni_get_link_state(struct fsl_mc_io u16 token, struct dpni_link_state *state) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_link_state *rsp_params; int err; @@ -918,6 +923,84 @@ int dpni_get_link_state(struct fsl_mc_io } /** + * dpni_get_link_state_v2() - Return the link state (either up or down) + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @state: Returned link state; + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_get_link_state_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpni_link_state *state) +{ + struct fsl_mc_command cmd = { 0 }; + struct dpni_rsp_get_link_state_v2 *rsp_params; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_LINK_STATE_V2, + cmd_flags, + token); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + rsp_params = (struct dpni_rsp_get_link_state_v2 *)cmd.params; + state->up = dpni_get_field(rsp_params->flags, LINK_STATE); + state->state_valid = dpni_get_field(rsp_params->flags, STATE_VALID); + state->rate = le32_to_cpu(rsp_params->rate); + state->options = le64_to_cpu(rsp_params->options); + state->supported = le64_to_cpu(rsp_params->supported); + state->advertising = le64_to_cpu(rsp_params->advertising); + + return 0; +} + +/** + * dpni_set_tx_shaping() - Set the transmit shaping + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @tx_cr_shaper: TX committed rate shaping configuration + * @tx_er_shaper: TX excess rate shaping configuration + * @coupled: Committed and excess rate shapers are coupled + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_tx_shaping_cfg *tx_cr_shaper, + const struct dpni_tx_shaping_cfg *tx_er_shaper, + int coupled) +{ + struct fsl_mc_command cmd = { 0 }; + struct dpni_cmd_set_tx_shaping *cmd_params; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_SHAPING, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_tx_shaping *)cmd.params; + cmd_params->tx_cr_max_burst_size = + cpu_to_le16(tx_cr_shaper->max_burst_size); + cmd_params->tx_er_max_burst_size = + cpu_to_le16(tx_er_shaper->max_burst_size); + cmd_params->tx_cr_rate_limit = cpu_to_le32(tx_cr_shaper->rate_limit); + cmd_params->tx_er_rate_limit = cpu_to_le32(tx_er_shaper->rate_limit); + dpni_set_field(cmd_params->coupled, COUPLED, coupled); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** * dpni_set_max_frame_length() - Set the maximum received frame length. * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' @@ -933,7 +1016,7 @@ int dpni_set_max_frame_length(struct fsl u16 token, u16 max_frame_length) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_max_frame_length *cmd_params; /* prepare command */ @@ -963,7 +1046,7 @@ int dpni_get_max_frame_length(struct fsl u16 token, u16 *max_frame_length) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_max_frame_length *rsp_params; int err; @@ -998,7 +1081,7 @@ int dpni_set_multicast_promisc(struct fs u16 token, int en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_multicast_promisc *cmd_params; /* prepare command */ @@ -1026,7 +1109,7 @@ int dpni_get_multicast_promisc(struct fs u16 token, int *en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_multicast_promisc *rsp_params; int err; @@ -1061,7 +1144,7 @@ int dpni_set_unicast_promisc(struct fsl_ u16 token, int en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_unicast_promisc *cmd_params; /* prepare command */ @@ -1089,7 +1172,7 @@ int dpni_get_unicast_promisc(struct fsl_ u16 token, int *en) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_unicast_promisc *rsp_params; int err; @@ -1124,7 +1207,7 @@ int dpni_set_primary_mac_addr(struct fsl u16 token, const u8 mac_addr[6]) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_primary_mac_addr *cmd_params; int i; @@ -1154,7 +1237,7 @@ int dpni_get_primary_mac_addr(struct fsl u16 token, u8 mac_addr[6]) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_primary_mac_addr *rsp_params; int i, err; @@ -1193,7 +1276,7 @@ int dpni_get_port_mac_addr(struct fsl_mc u16 token, u8 mac_addr[6]) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_rsp_get_port_mac_addr *rsp_params; int i, err; @@ -1229,7 +1312,7 @@ int dpni_add_mac_addr(struct fsl_mc_io * u16 token, const u8 mac_addr[6]) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_add_mac_addr *cmd_params; int i; @@ -1259,7 +1342,7 @@ int dpni_remove_mac_addr(struct fsl_mc_i u16 token, const u8 mac_addr[6]) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_remove_mac_addr *cmd_params; int i; @@ -1293,7 +1376,7 @@ int dpni_clear_mac_filters(struct fsl_mc int unicast, int multicast) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_clear_mac_filters *cmd_params; /* prepare command */ @@ -1309,6 +1392,55 @@ int dpni_clear_mac_filters(struct fsl_mc } /** + * dpni_set_tx_priorities() - Set transmission TC priority configuration + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: Transmission selection configuration + * + * warning: Allowed only when DPNI is disabled + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_set_tx_priorities(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_tx_priorities_cfg *cfg) +{ + struct dpni_cmd_set_tx_priorities *cmd_params; + struct fsl_mc_command cmd = { 0 }; + int i; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_TX_PRIORITIES, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_tx_priorities *)cmd.params; + dpni_set_field(cmd_params->flags, + SEPARATE_GRP, + cfg->separate_groups); + cmd_params->prio_group_A = cfg->prio_group_A; + cmd_params->prio_group_B = cfg->prio_group_B; + + for (i = 0; i + 1 < DPNI_MAX_TC; i += 2) { + dpni_set_field(cmd_params->modes[i / 2], + MODE_1, + cfg->tc_sched[i].mode); + dpni_set_field(cmd_params->modes[i / 2], + MODE_2, + cfg->tc_sched[i + 1].mode); + } + + for (i = 0; i < DPNI_MAX_TC; i++) { + cmd_params->delta_bandwidth[i] = + cpu_to_le16(cfg->tc_sched[i].delta_bandwidth); + } + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** * dpni_set_rx_tc_dist() - Set Rx traffic class distribution configuration * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' @@ -1327,7 +1459,7 @@ int dpni_set_rx_tc_dist(struct fsl_mc_io u8 tc_id, const struct dpni_rx_tc_dist_cfg *cfg) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_rx_tc_dist *cmd_params; /* prepare command */ @@ -1346,6 +1478,215 @@ int dpni_set_rx_tc_dist(struct fsl_mc_io return mc_send_command(mc_io, &cmd); } +/* + * dpni_set_qos_table() - Set QoS mapping table + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: QoS table configuration + * + * This function and all QoS-related functions require that + *'max_tcs > 1' was set at DPNI creation. + * + * warning: Before calling this function, call dpkg_prepare_key_cfg() to + * prepare the key_cfg_iova parameter + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_set_qos_table(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_qos_tbl_cfg *cfg) +{ + struct dpni_cmd_set_qos_table *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_QOS_TBL, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_qos_table *)cmd.params; + cmd_params->default_tc = cfg->default_tc; + cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova); + dpni_set_field(cmd_params->discard_on_miss, + ENABLE, + cfg->discard_on_miss); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_add_qos_entry() - Add QoS mapping entry (to select a traffic class) + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: QoS rule to add + * @tc_id: Traffic class selection (0-7) + * @index: Location in the QoS table where to insert the entry. + * Only relevant if MASKING is enabled for QoS classification on + * this DPNI, it is ignored for exact match. + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_add_qos_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rule_cfg *cfg, + u8 tc_id, + u16 index) +{ + struct dpni_cmd_add_qos_entry *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_QOS_ENT, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_add_qos_entry *)cmd.params; + cmd_params->tc_id = tc_id; + cmd_params->key_size = cfg->key_size; + cmd_params->index = cpu_to_le16(index); + cmd_params->key_iova = cpu_to_le64(cfg->key_iova); + cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_remove_qos_entry() - Remove QoS mapping entry + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: QoS rule to remove + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rule_cfg *cfg) +{ + struct dpni_cmd_remove_qos_entry *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_QOS_ENT, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_remove_qos_entry *)cmd.params; + cmd_params->key_size = cfg->key_size; + cmd_params->key_iova = cpu_to_le64(cfg->key_iova); + cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_set_congestion_notification() - Set traffic class congestion + * notification configuration + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @qtype: Type of queue - Rx, Tx and Tx confirm types are supported + * @tc_id: Traffic class selection (0-7) + * @cfg: Congestion notification configuration + * + * Return: '0' on Success; error code otherwise. + */ +int dpni_set_congestion_notification( + struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + enum dpni_queue_type qtype, + u8 tc_id, + const struct dpni_congestion_notification_cfg *cfg) +{ + struct dpni_cmd_set_congestion_notification *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header( + DPNI_CMDID_SET_CONGESTION_NOTIFICATION, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_congestion_notification *)cmd.params; + cmd_params->qtype = qtype; + cmd_params->tc = tc_id; + cmd_params->dest_id = cpu_to_le32(cfg->dest_cfg.dest_id); + cmd_params->notification_mode = cpu_to_le16(cfg->notification_mode); + cmd_params->dest_priority = cfg->dest_cfg.priority; + dpni_set_field(cmd_params->type_units, DEST_TYPE, + cfg->dest_cfg.dest_type); + dpni_set_field(cmd_params->type_units, CONG_UNITS, cfg->units); + cmd_params->message_iova = cpu_to_le64(cfg->message_iova); + cmd_params->message_ctx = cpu_to_le64(cfg->message_ctx); + cmd_params->threshold_entry = cpu_to_le32(cfg->threshold_entry); + cmd_params->threshold_exit = cpu_to_le32(cfg->threshold_exit); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_get_congestion_notification() - Get traffic class congestion + * notification configuration + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @qtype: Type of queue - Rx, Tx and Tx confirm types are supported + * @tc_id: bits 7-4 contain ceetm channel index (valid only for TX); + * bits 3-0 contain traffic class. + * Use macro DPNI_BUILD_CH_TC() to build correct value for + * tc_id parameter. + * @cfg: congestion notification configuration + * + * Return: '0' on Success; error code otherwise. + */ +int dpni_get_congestion_notification( + struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + enum dpni_queue_type qtype, + u8 tc_id, + struct dpni_congestion_notification_cfg *cfg) +{ + struct dpni_rsp_get_congestion_notification *rsp_params; + struct dpni_cmd_get_congestion_notification *cmd_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header( + DPNI_CMDID_GET_CONGESTION_NOTIFICATION, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_get_congestion_notification *)cmd.params; + cmd_params->qtype = qtype; + cmd_params->tc = tc_id; + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpni_rsp_get_congestion_notification *)cmd.params; + cfg->units = dpni_get_field(rsp_params->type_units, CONG_UNITS); + cfg->threshold_entry = le32_to_cpu(rsp_params->threshold_entry); + cfg->threshold_exit = le32_to_cpu(rsp_params->threshold_exit); + cfg->message_ctx = le64_to_cpu(rsp_params->message_ctx); + cfg->message_iova = le64_to_cpu(rsp_params->message_iova); + cfg->notification_mode = le16_to_cpu(rsp_params->notification_mode); + cfg->dest_cfg.dest_id = le32_to_cpu(rsp_params->dest_id); + cfg->dest_cfg.priority = rsp_params->dest_priority; + cfg->dest_cfg.dest_type = dpni_get_field(rsp_params->type_units, + DEST_TYPE); + + return 0; +} + /** * dpni_set_queue() - Set queue parameters * @mc_io: Pointer to MC portal's I/O object @@ -1371,7 +1712,7 @@ int dpni_set_queue(struct fsl_mc_io *mc_ u8 options, const struct dpni_queue *queue) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_queue *cmd_params; /* prepare command */ @@ -1419,7 +1760,7 @@ int dpni_get_queue(struct fsl_mc_io *mc_ struct dpni_queue *queue, struct dpni_queue_id *qid) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_queue *cmd_params; struct dpni_rsp_get_queue *rsp_params; int err; @@ -1463,6 +1804,8 @@ int dpni_get_queue(struct fsl_mc_io *mc_ * @token: Token of DPNI object * @page: Selects the statistics page to retrieve, see * DPNI_GET_STATISTICS output. Pages are numbered 0 to 2. + * @param: Custom parameter for some pages used to select a certain + * statistic source, for example the TC. * @stat: Structure containing the statistics * * Return: '0' on Success; Error code otherwise. @@ -1471,9 +1814,10 @@ int dpni_get_statistics(struct fsl_mc_io u32 cmd_flags, u16 token, u8 page, + u8 param, union dpni_statistics *stat) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_statistics *cmd_params; struct dpni_rsp_get_statistics *rsp_params; int i, err; @@ -1484,6 +1828,7 @@ int dpni_get_statistics(struct fsl_mc_io token); cmd_params = (struct dpni_cmd_get_statistics *)cmd.params; cmd_params->page_number = page; + cmd_params->param = param; /* send command to mc */ err = mc_send_command(mc_io, &cmd); @@ -1499,6 +1844,29 @@ int dpni_get_statistics(struct fsl_mc_io } /** + * dpni_reset_statistics() - Clears DPNI statistics + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_reset_statistics(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token) +{ + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_RESET_STATISTICS, + cmd_flags, + token); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** * dpni_set_taildrop() - Set taildrop per queue or TC * @mc_io: Pointer to MC portal's I/O object * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' @@ -1506,7 +1874,10 @@ int dpni_get_statistics(struct fsl_mc_io * @cg_point: Congestion point * @q_type: Queue type on which the taildrop is configured. * Only Rx queues are supported for now - * @tc: Traffic class to apply this taildrop to + * @tc: bits 7-4 contain ceetm channel index (valid only for TX); + * bits 3-0 contain traffic class. + * Use macro DPNI_BUILD_CH_TC() to build correct value for + * tc parameter. * @q_index: Index of the queue if the DPNI supports multiple queues for * traffic distribution. Ignored if CONGESTION_POINT is not 0. * @taildrop: Taildrop structure @@ -1522,7 +1893,7 @@ int dpni_set_taildrop(struct fsl_mc_io * u8 index, struct dpni_taildrop *taildrop) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_set_taildrop *cmd_params; /* prepare command */ @@ -1550,7 +1921,10 @@ int dpni_set_taildrop(struct fsl_mc_io * * @cg_point: Congestion point * @q_type: Queue type on which the taildrop is configured. * Only Rx queues are supported for now - * @tc: Traffic class to apply this taildrop to + * @tc: bits 7-4 contain ceetm channel index (valid only for TX); + * bits 3-0 contain traffic class. + * Use macro DPNI_BUILD_CH_TC() to build correct value for + * tc parameter. * @q_index: Index of the queue if the DPNI supports multiple queues for * traffic distribution. Ignored if CONGESTION_POINT is not 0. * @taildrop: Taildrop structure @@ -1566,7 +1940,7 @@ int dpni_get_taildrop(struct fsl_mc_io * u8 index, struct dpni_taildrop *taildrop) { - struct mc_command cmd = { 0 }; + struct fsl_mc_command cmd = { 0 }; struct dpni_cmd_get_taildrop *cmd_params; struct dpni_rsp_get_taildrop *rsp_params; int err; @@ -1594,3 +1968,187 @@ int dpni_get_taildrop(struct fsl_mc_io * return 0; } + +/** + * dpni_get_api_version() - Get Data Path Network Interface API version + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @major_ver: Major version of data path network interface API + * @minor_ver: Minor version of data path network interface API + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_get_api_version(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 *major_ver, + u16 *minor_ver) +{ + struct dpni_rsp_get_api_version *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_API_VERSION, + cmd_flags, 0); + + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpni_rsp_get_api_version *)cmd.params; + *major_ver = le16_to_cpu(rsp_params->major); + *minor_ver = le16_to_cpu(rsp_params->minor); + + return 0; +} + +/** + * dpni_set_rx_fs_dist() - Set Rx traffic class FS distribution + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: Distribution configuration + * If the FS is already enabled with a previous call the classification + * key will be changed but all the table rules are kept. If the + * existing rules do not match the key the results will not be + * predictable. It is the user responsibility to keep key integrity. + * If cfg.enable is set to 1 the command will create a flow steering table + * and will classify packets according to this table. The packets that + * miss all the table rules will be classified according to settings + * made in dpni_set_rx_hash_dist() + * If cfg.enable is set to 0 the command will clear flow steering table. + * The packets will be classified according to settings made in + * dpni_set_rx_hash_dist() + */ +int dpni_set_rx_fs_dist(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rx_dist_cfg *cfg) +{ + struct dpni_cmd_set_rx_fs_dist *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_FS_DIST, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_rx_fs_dist *)cmd.params; + cmd_params->dist_size = cpu_to_le16(cfg->dist_size); + dpni_set_field(cmd_params->enable, RX_FS_DIST_ENABLE, cfg->enable); + cmd_params->tc = cfg->tc; + cmd_params->miss_flow_id = cpu_to_le16(cfg->fs_miss_flow_id); + cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_set_rx_hash_dist() - Set Rx traffic class HASH distribution + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @cfg: Distribution configuration + * If cfg.enable is set to 1 the packets will be classified using a hash + * function based on the key received in cfg.key_cfg_iova parameter. + * If cfg.enable is set to 0 the packets will be sent to the queue configured + * in dpni_set_rx_dist_default_queue() call + */ +int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rx_dist_cfg *cfg) +{ + struct dpni_cmd_set_rx_hash_dist *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_RX_HASH_DIST, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_set_rx_hash_dist *)cmd.params; + cmd_params->dist_size = cpu_to_le16(cfg->dist_size); + dpni_set_field(cmd_params->enable, RX_FS_DIST_ENABLE, cfg->enable); + cmd_params->tc = cfg->tc; + cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_add_fs_entry() - Add Flow Steering entry for a specific traffic class + * (to select a flow ID) + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @tc_id: Traffic class selection (0-7) + * @index: Location in the QoS table where to insert the entry. + * Only relevant if MASKING is enabled for QoS + * classification on this DPNI, it is ignored for exact match. + * @cfg: Flow steering rule to add + * @action: Action to be taken as result of a classification hit + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_add_fs_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 tc_id, + u16 index, + const struct dpni_rule_cfg *cfg, + const struct dpni_fs_action_cfg *action) +{ + struct dpni_cmd_add_fs_entry *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_FS_ENT, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_add_fs_entry *)cmd.params; + cmd_params->tc_id = tc_id; + cmd_params->key_size = cfg->key_size; + cmd_params->index = cpu_to_le16(index); + cmd_params->key_iova = cpu_to_le64(cfg->key_iova); + cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova); + cmd_params->options = cpu_to_le16(action->options); + cmd_params->flow_id = cpu_to_le16(action->flow_id); + cmd_params->flc = cpu_to_le64(action->flc); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpni_remove_fs_entry() - Remove Flow Steering entry from a specific + * traffic class + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPNI object + * @tc_id: Traffic class selection (0-7) + * @cfg: Flow steering rule to remove + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_remove_fs_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 tc_id, + const struct dpni_rule_cfg *cfg) +{ + struct dpni_cmd_remove_fs_entry *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_FS_ENT, + cmd_flags, + token); + cmd_params = (struct dpni_cmd_remove_fs_entry *)cmd.params; + cmd_params->tc_id = tc_id; + cmd_params->key_size = cfg->key_size; + cmd_params->key_iova = cpu_to_le64(cfg->key_iova); + cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} --- a/drivers/staging/fsl-dpaa2/ethernet/dpni.h +++ b/drivers/staging/fsl-dpaa2/ethernet/dpni.h @@ -1,34 +1,6 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2013-2016 Freescale Semiconductor Inc. * Copyright 2016 NXP - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the above-listed copyright holders nor the - * names of any contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FSL_DPNI_H #define __FSL_DPNI_H @@ -52,6 +24,14 @@ struct fsl_mc_io; * Maximum number of buffer pools per DPNI */ #define DPNI_MAX_DPBP 8 +/** + * Maximum number of senders + */ +#define DPNI_MAX_SENDERS 16 +/** + * Maximum distribution size + */ +#define DPNI_MAX_DIST_SIZE 16 /** * All traffic classes considered; see dpni_set_queue() @@ -123,13 +103,15 @@ struct dpni_pools_cfg { /** * struct pools - Buffer pools parameters * @dpbp_id: DPBP object ID + * @priority_mask: priorities served by DPBP * @buffer_size: Buffer size * @backup_pool: Backup pool */ struct { - int dpbp_id; + u16 dpbp_id; + u8 priority_mask; u16 buffer_size; - int backup_pool; + u8 backup_pool; } pools[DPNI_MAX_DPBP]; }; @@ -476,6 +458,24 @@ union dpni_statistics { u64 egress_confirmed_frames; } page_2; /** + * struct page_3 - Page_3 statistics structure with values for the + * selected TC + * @ceetm_dequeue_bytes: Cumulative count of the number of bytes + * dequeued + * @ceetm_dequeue_frames: Cumulative count of the number of frames + * dequeued + * @ceetm_reject_bytes: Cumulative count of the number of bytes in all + * frames whose enqueue was rejected + * @ceetm_reject_frames: Cumulative count of all frame enqueues + * rejected + */ + struct { + u64 ceetm_dequeue_bytes; + u64 ceetm_dequeue_frames; + u64 ceetm_reject_bytes; + u64 ceetm_reject_frames; + } page_3; + /** * struct raw - raw statistics structure */ struct { @@ -487,8 +487,13 @@ int dpni_get_statistics(struct fsl_mc_io u32 cmd_flags, u16 token, u8 page, + u8 param, union dpni_statistics *stat); +int dpni_reset_statistics(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token); + /** * Enable auto-negotiation */ @@ -505,6 +510,23 @@ int dpni_get_statistics(struct fsl_mc_io * Enable a-symmetric pause frames */ #define DPNI_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL +/** + * Enable priority flow control pause frames + */ +#define DPNI_LINK_OPT_PFC_PAUSE 0x0000000000000010ULL +/** + * Advertised link speeds + */ +#define DPNI_ADVERTISED_10BASET_FULL 0x0000000000000001ULL +#define DPNI_ADVERTISED_100BASET_FULL 0x0000000000000002ULL +#define DPNI_ADVERTISED_1000BASET_FULL 0x0000000000000004ULL +#define DPNI_ADVERTISED_10000BASET_FULL 0x0000000000000010ULL +#define DPNI_ADVERTISED_2500BASEX_FULL 0x0000000000000020ULL + +/** + * Advertise auto-negotiation enabled + */ +#define DPNI_ADVERTISED_AUTONEG 0x0000000000000008ULL /** * struct - Structure representing DPNI link configuration @@ -514,6 +536,7 @@ int dpni_get_statistics(struct fsl_mc_io struct dpni_link_cfg { u32 rate; u64 options; + u64 advertising; }; int dpni_set_link_cfg(struct fsl_mc_io *mc_io, @@ -521,6 +544,11 @@ int dpni_set_link_cfg(struct fsl_mc_io u16 token, const struct dpni_link_cfg *cfg); +int dpni_set_link_cfg_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_link_cfg *cfg); + /** * struct dpni_link_state - Structure representing DPNI link state * @rate: Rate @@ -530,7 +558,10 @@ int dpni_set_link_cfg(struct fsl_mc_io struct dpni_link_state { u32 rate; u64 options; + u64 supported; + u64 advertising; int up; + int state_valid; }; int dpni_get_link_state(struct fsl_mc_io *mc_io, @@ -538,6 +569,28 @@ int dpni_get_link_state(struct fsl_mc_io u16 token, struct dpni_link_state *state); +int dpni_get_link_state_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpni_link_state *state); + +/** + * struct dpni_tx_shaping - Structure representing DPNI tx shaping configuration + * @rate_limit: rate in Mbps + * @max_burst_size: burst size in bytes (up to 64KB) + */ +struct dpni_tx_shaping_cfg { + u32 rate_limit; + u16 max_burst_size; +}; + +int dpni_set_tx_shaping(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_tx_shaping_cfg *tx_cr_shaper, + const struct dpni_tx_shaping_cfg *tx_er_shaper, + int coupled); + int dpni_set_max_frame_length(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, @@ -639,6 +692,70 @@ int dpni_prepare_key_cfg(const struct dp u8 *key_cfg_buf); /** + * struct dpni_qos_tbl_cfg - Structure representing QOS table configuration + * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with + * key extractions to be used as the QoS criteria by calling + * dpkg_prepare_key_cfg() + * @discard_on_miss: Set to '1' to discard frames in case of no match (miss); + * '0' to use the 'default_tc' in such cases + * @default_tc: Used in case of no-match and 'discard_on_miss'= 0 + */ +struct dpni_qos_tbl_cfg { + u64 key_cfg_iova; + int discard_on_miss; + u8 default_tc; +}; + +int dpni_set_qos_table(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_qos_tbl_cfg *cfg); + +/** + * enum dpni_tx_schedule_mode - DPNI Tx scheduling mode + * @DPNI_TX_SCHED_STRICT_PRIORITY: strict priority + * @DPNI_TX_SCHED_WEIGHTED_A: weighted based scheduling in group A + * @DPNI_TX_SCHED_WEIGHTED_B: weighted based scheduling in group B + */ +enum dpni_tx_schedule_mode { + DPNI_TX_SCHED_STRICT_PRIORITY = 0, + DPNI_TX_SCHED_WEIGHTED_A, + DPNI_TX_SCHED_WEIGHTED_B, +}; + +/** + * struct dpni_tx_schedule_cfg - Structure representing Tx scheduling conf + * @mode: Scheduling mode + * @delta_bandwidth: Bandwidth represented in weights from 100 to 10000; + * not applicable for 'strict-priority' mode; + */ +struct dpni_tx_schedule_cfg { + enum dpni_tx_schedule_mode mode; + u16 delta_bandwidth; +}; + +/** + * struct dpni_tx_priorities_cfg - Structure representing transmission + * priorities for DPNI TCs + * @tc_sched: An array of traffic-classes + * @prio_group_A: Priority of group A + * @prio_group_B: Priority of group B + * @separate_groups: Treat A and B groups as separate + * @ceetm_ch_idx: ceetm channel index to apply the changes + */ +struct dpni_tx_priorities_cfg { + struct dpni_tx_schedule_cfg tc_sched[DPNI_MAX_TC]; + u8 prio_group_A; + u8 prio_group_B; + u8 separate_groups; +}; + +int dpni_set_tx_priorities(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_tx_priorities_cfg *cfg); + +/** * struct dpni_rx_tc_dist_cfg - Rx traffic class distribution configuration * @dist_size: Set the distribution size; * supported values: 1,2,3,4,6,7,8,12,14,16,24,28,32,48,56,64,96, @@ -784,6 +901,108 @@ enum dpni_congestion_point { }; /** + * struct dpni_dest_cfg - Structure representing DPNI destination parameters + * @dest_type: Destination type + * @dest_id: Either DPIO ID or DPCON ID, depending on the destination type + * @priority: Priority selection within the DPIO or DPCON channel; valid + * values are 0-1 or 0-7, depending on the number of priorities + * in that channel; not relevant for 'DPNI_DEST_NONE' option + */ +struct dpni_dest_cfg { + enum dpni_dest dest_type; + int dest_id; + u8 priority; +}; + +/* DPNI congestion options */ + +/** + * CSCN message is written to message_iova once entering a + * congestion state (see 'threshold_entry') + */ +#define DPNI_CONG_OPT_WRITE_MEM_ON_ENTER 0x00000001 +/** + * CSCN message is written to message_iova once exiting a + * congestion state (see 'threshold_exit') + */ +#define DPNI_CONG_OPT_WRITE_MEM_ON_EXIT 0x00000002 +/** + * CSCN write will attempt to allocate into a cache (coherent write); + * valid only if 'DPNI_CONG_OPT_WRITE_MEM_' is selected + */ +#define DPNI_CONG_OPT_COHERENT_WRITE 0x00000004 +/** + * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to + * DPIO/DPCON's WQ channel once entering a congestion state + * (see 'threshold_entry') + */ +#define DPNI_CONG_OPT_NOTIFY_DEST_ON_ENTER 0x00000008 +/** + * if 'dest_cfg.dest_type != DPNI_DEST_NONE' CSCN message is sent to + * DPIO/DPCON's WQ channel once exiting a congestion state + * (see 'threshold_exit') + */ +#define DPNI_CONG_OPT_NOTIFY_DEST_ON_EXIT 0x00000010 +/** + * if 'dest_cfg.dest_type != DPNI_DEST_NONE' when the CSCN is written to the + * sw-portal's DQRR, the DQRI interrupt is asserted immediately (if enabled) + */ +#define DPNI_CONG_OPT_INTR_COALESCING_DISABLED 0x00000020 +/** + * This congestion will trigger flow control or priority flow control. + * This will have effect only if flow control is enabled with + * dpni_set_link_cfg(). + */ +#define DPNI_CONG_OPT_FLOW_CONTROL 0x00000040 + +/** + * struct dpni_congestion_notification_cfg - congestion notification + * configuration + * @units: Units type + * @threshold_entry: Above this threshold we enter a congestion state. + * set it to '0' to disable it + * @threshold_exit: Below this threshold we exit the congestion state. + * @message_ctx: The context that will be part of the CSCN message + * @message_iova: I/O virtual address (must be in DMA-able memory), + * must be 16B aligned; valid only if 'DPNI_CONG_OPT_WRITE_MEM_' + * is contained in 'options' + * @dest_cfg: CSCN can be send to either DPIO or DPCON WQ channel + * @notification_mode: Mask of available options; use 'DPNI_CONG_OPT_' values + */ + +struct dpni_congestion_notification_cfg { + enum dpni_congestion_unit units; + u32 threshold_entry; + u32 threshold_exit; + u64 message_ctx; + u64 message_iova; + struct dpni_dest_cfg dest_cfg; + u16 notification_mode; +}; + +/** Compose TC parameter for function dpni_set_congestion_notification() + * and dpni_get_congestion_notification(). + */ +#define DPNI_BUILD_CH_TC(ceetm_ch_idx, tc) \ + ((((ceetm_ch_idx) & 0x0F) << 4) | ((tc) & 0x0F)) + +int dpni_set_congestion_notification( + struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + enum dpni_queue_type qtype, + u8 tc_id, + const struct dpni_congestion_notification_cfg *cfg); + +int dpni_get_congestion_notification( + struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + enum dpni_queue_type qtype, + u8 tc_id, + struct dpni_congestion_notification_cfg *cfg); + +/** * struct dpni_taildrop - Structure representing the taildrop * @enable: Indicates whether the taildrop is active or not. * @units: Indicates the unit of THRESHOLD. Queue taildrop only supports @@ -829,4 +1048,124 @@ struct dpni_rule_cfg { u8 key_size; }; +int dpni_get_api_version(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 *major_ver, + u16 *minor_ver); + +int dpni_add_qos_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rule_cfg *cfg, + u8 tc_id, + u16 index); + +int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rule_cfg *cfg); + +int dpni_clear_qos_table(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token); + +/** + * Discard matching traffic. If set, this takes precedence over any other + * configuration and matching traffic is always discarded. + */ + #define DPNI_FS_OPT_DISCARD 0x1 + +/** + * Set FLC value. If set, flc member of struct dpni_fs_action_cfg is used to + * override the FLC value set per queue. + * For more details check the Frame Descriptor section in the hardware + * documentation. + */ +#define DPNI_FS_OPT_SET_FLC 0x2 + +/* + * Indicates whether the 6 lowest significant bits of FLC are used for stash + * control. If set, the 6 least significant bits in value are interpreted as + * follows: + * - bits 0-1: indicates the number of 64 byte units of context that are + * stashed. FLC value is interpreted as a memory address in this case, + * excluding the 6 LS bits. + * - bits 2-3: indicates the number of 64 byte units of frame annotation + * to be stashed. Annotation is placed at FD[ADDR]. + * - bits 4-5: indicates the number of 64 byte units of frame data to be + * stashed. Frame data is placed at FD[ADDR] + FD[OFFSET]. + * This flag is ignored if DPNI_FS_OPT_SET_FLC is not specified. + */ +#define DPNI_FS_OPT_SET_STASH_CONTROL 0x4 + +/** + * struct dpni_fs_action_cfg - Action configuration for table look-up + * @flc: FLC value for traffic matching this rule. Please check the + * Frame Descriptor section in the hardware documentation for + * more information. + * @flow_id: Identifies the Rx queue used for matching traffic. Supported + * values are in range 0 to num_queue-1. + * @options: Any combination of DPNI_FS_OPT_ values. + */ +struct dpni_fs_action_cfg { + u64 flc; + u16 flow_id; + u16 options; +}; + +int dpni_add_fs_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 tc_id, + u16 index, + const struct dpni_rule_cfg *cfg, + const struct dpni_fs_action_cfg *action); + +int dpni_remove_fs_entry(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 tc_id, + const struct dpni_rule_cfg *cfg); + +/** + * When used for queue_idx in function dpni_set_rx_dist_default_queue + * will signal to dpni to drop all unclassified frames + */ +#define DPNI_FS_MISS_DROP ((uint16_t)-1) + +/** + * struct dpni_rx_dist_cfg - distribution configuration + * @dist_size: distribution size; supported values: 1,2,3,4,6,7,8, + * 12,14,16,24,28,32,48,56,64,96,112,128,192,224,256,384,448, + * 512,768,896,1024 + * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with + * the extractions to be used for the distribution key by calling + * dpkg_prepare_key_cfg() relevant only when enable!=0 otherwise + * it can be '0' + * @enable: enable/disable the distribution. + * @tc: TC id for which distribution is set + * @fs_miss_flow_id: when packet misses all rules from flow steering table and + * hash is disabled it will be put into this queue id; use + * DPNI_FS_MISS_DROP to drop frames. The value of this field is + * used only when flow steering distribution is enabled and hash + * distribution is disabled + */ +struct dpni_rx_dist_cfg { + u16 dist_size; + u64 key_cfg_iova; + u8 enable; + u8 tc; + u16 fs_miss_flow_id; +}; + +int dpni_set_rx_fs_dist(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rx_dist_cfg *cfg); + +int dpni_set_rx_hash_dist(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const struct dpni_rx_dist_cfg *cfg); + #endif /* __FSL_DPNI_H */ --- a/drivers/staging/fsl-dpaa2/ethernet/net.h +++ b/drivers/staging/fsl-dpaa2/ethernet/net.h @@ -1,33 +1,5 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */ /* Copyright 2013-2015 Freescale Semiconductor Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the above-listed copyright holders nor the - * names of any contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * - * ALTERNATIVELY, this software may be distributed under the terms of the - * GNU General Public License ("GPL") as published by the Free Software - * Foundation, either version 2 of that License or (at your option) any - * later version. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. */ #ifndef __FSL_NET_H #define __FSL_NET_H