diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c index 2c6f9b1225b85c96b2330a43c7de0af5b89ef511..5402867272be4a13b03b780258a86af7dda49380 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c @@ -1208,9 +1208,37 @@ static void disable_ch_napi(struct dpaa2_eth_priv *priv) } } +static void dpaa2_eth_set_rx_taildrop(struct dpaa2_eth_priv *priv, bool enable) +{ + struct dpni_taildrop td = {0}; + int i, err; + + if (priv->rx_td_enabled == enable) + return; + + td.enable = enable; + td.threshold = DPAA2_ETH_TAILDROP_THRESH; + + 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, 0, + priv->fq[i].flowid, &td); + if (err) { + netdev_err(priv->net_dev, + "dpni_set_taildrop() failed\n"); + break; + } + } + + priv->rx_td_enabled = enable; +} + static int link_state_update(struct dpaa2_eth_priv *priv) { struct dpni_link_state state = {0}; + bool tx_pause; int err; err = dpni_get_link_state(priv->mc_io, 0, priv->mc_token, &state); @@ -1220,6 +1248,14 @@ static int link_state_update(struct dpaa2_eth_priv *priv) return err; } + /* If Tx pause frame settings have changed, we need to update + * Rx FQ taildrop configuration as well. We configure taildrop + * only when pause frame generation is disabled. + */ + tx_pause = !!(state.options & DPNI_LINK_OPT_PAUSE) ^ + !!(state.options & DPNI_LINK_OPT_ASYM_PAUSE); + dpaa2_eth_set_rx_taildrop(priv, !tx_pause); + /* Chech link state; speed / duplex changes are not treated yet */ if (priv->link_state.up == state.up) goto out; @@ -2445,6 +2481,33 @@ static void set_enqueue_mode(struct dpaa2_eth_priv *priv) priv->enqueue = dpaa2_eth_enqueue_fq; } +static int set_pause(struct dpaa2_eth_priv *priv) +{ + struct device *dev = priv->net_dev->dev.parent; + struct dpni_link_cfg link_cfg = {0}; + int err; + + /* Get the default link options so we don't override other flags */ + err = dpni_get_link_cfg(priv->mc_io, 0, priv->mc_token, &link_cfg); + if (err) { + dev_err(dev, "dpni_get_link_cfg() failed\n"); + return err; + } + + /* By default, enable both Rx and Tx pause frames */ + link_cfg.options |= DPNI_LINK_OPT_PAUSE; + link_cfg.options &= ~DPNI_LINK_OPT_ASYM_PAUSE; + err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &link_cfg); + if (err) { + dev_err(dev, "dpni_set_link_cfg() failed\n"); + return err; + } + + priv->link_state.options = link_cfg.options; + + return 0; +} + /* Configure the DPNI object this interface is associated with */ static int setup_dpni(struct fsl_mc_device *ls_dev) { @@ -2500,6 +2563,13 @@ static int setup_dpni(struct fsl_mc_device *ls_dev) set_enqueue_mode(priv); + /* Enable pause frame support */ + if (dpaa2_eth_has_pause_support(priv)) { + err = set_pause(priv); + if (err) + goto close; + } + priv->cls_rules = devm_kzalloc(dev, sizeof(struct dpaa2_eth_cls_rule) * dpaa2_eth_fs_count(priv), GFP_KERNEL); if (!priv->cls_rules) @@ -2531,7 +2601,6 @@ static int setup_rx_flow(struct dpaa2_eth_priv *priv, 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, @@ -2556,15 +2625,6 @@ static int setup_rx_flow(struct dpaa2_eth_priv *priv, 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; - } - /* xdp_rxq setup */ err = xdp_rxq_info_reg(&fq->channel->xdp_rxq, priv->net_dev, fq->flowid); diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h index 9af18c24221f714d605604d604b5fcce9158efea..8a0e65b3267faeaead722ff37028b7cf02a9e1a9 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h @@ -392,6 +392,7 @@ struct dpaa2_eth_priv { struct dpaa2_eth_drv_stats __percpu *percpu_extras; u16 mc_token; + u8 rx_td_enabled; struct dpni_link_state link_state; bool do_link_poll; @@ -476,6 +477,12 @@ enum dpaa2_eth_rx_dist { #define DPAA2_ETH_DIST_L4DST BIT(8) #define DPAA2_ETH_DIST_ALL (~0ULL) +#define DPNI_PAUSE_VER_MAJOR 7 +#define DPNI_PAUSE_VER_MINOR 13 +#define dpaa2_eth_has_pause_support(priv) \ + (dpaa2_eth_cmp_dpni_ver((priv), DPNI_PAUSE_VER_MAJOR, \ + DPNI_PAUSE_VER_MINOR) >= 0) + static inline unsigned int dpaa2_eth_needed_headroom(struct dpaa2_eth_priv *priv, struct sk_buff *skb) diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c index fb170425e9ceb501e95377f07d4dbeff752da0bd..93076fe01b454d1246965c935387949669ea7188 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c @@ -88,6 +88,59 @@ dpaa2_eth_get_link_ksettings(struct net_device *net_dev, return 0; } +static void dpaa2_eth_get_pauseparam(struct net_device *net_dev, + struct ethtool_pauseparam *pause) +{ + struct dpaa2_eth_priv *priv = netdev_priv(net_dev); + u64 link_options = priv->link_state.options; + + pause->rx_pause = !!(link_options & DPNI_LINK_OPT_PAUSE); + pause->tx_pause = pause->rx_pause ^ + !!(link_options & DPNI_LINK_OPT_ASYM_PAUSE); + pause->autoneg = AUTONEG_DISABLE; +} + +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_cfg cfg = {0}; + int err; + + if (!dpaa2_eth_has_pause_support(priv)) { + netdev_info(net_dev, "No pause frame support for DPNI version < %d.%d\n", + DPNI_PAUSE_VER_MAJOR, DPNI_PAUSE_VER_MINOR); + return -EOPNOTSUPP; + } + + if (pause->autoneg) + return -EOPNOTSUPP; + + cfg.rate = priv->link_state.rate; + cfg.options = priv->link_state.options; + 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; + + if (cfg.options == priv->link_state.options) + return 0; + + err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &cfg); + if (err) { + netdev_err(net_dev, "dpni_set_link_state failed\n"); + return err; + } + + priv->link_state.options = cfg.options; + + return 0; +} + static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset, u8 *data) { @@ -664,6 +717,8 @@ 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, + .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, diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h index 7b44d7d9b19aab9cb6e5806904f3b09377d36ddc..d9b6918807afd8440155d75d8bba0a0e8f47677c 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h @@ -84,6 +84,7 @@ #define DPNI_CMDID_SET_RX_FS_DIST DPNI_CMD(0x273) #define DPNI_CMDID_SET_RX_HASH_DIST DPNI_CMD(0x274) +#define DPNI_CMDID_GET_LINK_CFG DPNI_CMD(0x278) /* Macros for accessing command fields smaller than 1byte */ #define DPNI_MASK(field) \ @@ -284,7 +285,7 @@ struct dpni_rsp_get_statistics { __le64 counter[DPNI_STATISTICS_CNT]; }; -struct dpni_cmd_set_link_cfg { +struct dpni_cmd_link_cfg { /* cmd word 0 */ __le64 pad0; /* cmd word 1 */ diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.c b/drivers/net/ethernet/freescale/dpaa2/dpni.c index 220dfc806a246190aa8ea00d245b5f6ebbd85749..05e30893dee6970773b78cc79fcfed55d6885fdc 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c @@ -838,13 +838,13 @@ int dpni_set_link_cfg(struct fsl_mc_io *mc_io, const struct dpni_link_cfg *cfg) { struct fsl_mc_command cmd = { 0 }; - struct dpni_cmd_set_link_cfg *cmd_params; + struct dpni_cmd_link_cfg *cmd_params; /* prepare command */ cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_LINK_CFG, cmd_flags, token); - cmd_params = (struct dpni_cmd_set_link_cfg *)cmd.params; + cmd_params = (struct dpni_cmd_link_cfg *)cmd.params; cmd_params->rate = cpu_to_le32(cfg->rate); cmd_params->options = cpu_to_le64(cfg->options); @@ -852,6 +852,42 @@ int dpni_set_link_cfg(struct fsl_mc_io *mc_io, return mc_send_command(mc_io, &cmd); } +/** + * dpni_get_link_cfg() - return 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 from dpni object + * + * Return: '0' on Success; Error code otherwise. + */ +int dpni_get_link_cfg(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpni_link_cfg *cfg) +{ + struct fsl_mc_command cmd = { 0 }; + struct dpni_cmd_link_cfg *rsp_params; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPNI_CMDID_GET_LINK_CFG, + 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_cmd_link_cfg *)cmd.params; + cfg->rate = le32_to_cpu(rsp_params->rate); + cfg->options = le64_to_cpu(rsp_params->options); + + return err; +} + /** * dpni_get_link_state() - Return the link state (either up or down) * @mc_io: Pointer to MC portal's I/O object diff --git a/drivers/net/ethernet/freescale/dpaa2/dpni.h b/drivers/net/ethernet/freescale/dpaa2/dpni.h index a521242e23537621cb00708809996ac0e964a1a8..3e8fc6c7a8da31a87a234b63ca1112d899e12656 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpni.h +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h @@ -485,6 +485,11 @@ int dpni_set_link_cfg(struct fsl_mc_io *mc_io, u16 token, const struct dpni_link_cfg *cfg); +int dpni_get_link_cfg(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpni_link_cfg *cfg); + /** * struct dpni_link_state - Structure representing DPNI link state * @rate: Rate