162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) 262306a36Sopenharmony_ci/* Copyright 2020 NXP */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include "dpaa2-eth.h" 562306a36Sopenharmony_ci 662306a36Sopenharmony_cistatic int dpaa2_eth_dcbnl_ieee_getpfc(struct net_device *net_dev, 762306a36Sopenharmony_ci struct ieee_pfc *pfc) 862306a36Sopenharmony_ci{ 962306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci if (!(priv->link_state.options & DPNI_LINK_OPT_PFC_PAUSE)) 1262306a36Sopenharmony_ci return 0; 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci memcpy(pfc, &priv->pfc, sizeof(priv->pfc)); 1562306a36Sopenharmony_ci pfc->pfc_cap = dpaa2_eth_tc_count(priv); 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci return 0; 1862306a36Sopenharmony_ci} 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic inline bool dpaa2_eth_is_prio_enabled(u8 pfc_en, u8 tc) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci return !!(pfc_en & (1 << tc)); 2362306a36Sopenharmony_ci} 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int dpaa2_eth_set_pfc_cn(struct dpaa2_eth_priv *priv, u8 pfc_en) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci struct dpni_congestion_notification_cfg cfg = {0}; 2862306a36Sopenharmony_ci int i, err; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci cfg.notification_mode = DPNI_CONG_OPT_FLOW_CONTROL; 3162306a36Sopenharmony_ci cfg.units = DPNI_CONGESTION_UNIT_FRAMES; 3262306a36Sopenharmony_ci cfg.message_iova = 0ULL; 3362306a36Sopenharmony_ci cfg.message_ctx = 0ULL; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { 3662306a36Sopenharmony_ci if (dpaa2_eth_is_prio_enabled(pfc_en, i)) { 3762306a36Sopenharmony_ci cfg.threshold_entry = DPAA2_ETH_CN_THRESH_ENTRY(priv); 3862306a36Sopenharmony_ci cfg.threshold_exit = DPAA2_ETH_CN_THRESH_EXIT(priv); 3962306a36Sopenharmony_ci } else { 4062306a36Sopenharmony_ci /* For priorities not set in the pfc_en mask, we leave 4162306a36Sopenharmony_ci * the congestion thresholds at zero, which effectively 4262306a36Sopenharmony_ci * disables generation of PFC frames for them 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci cfg.threshold_entry = 0; 4562306a36Sopenharmony_ci cfg.threshold_exit = 0; 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci err = dpni_set_congestion_notification(priv->mc_io, 0, 4962306a36Sopenharmony_ci priv->mc_token, 5062306a36Sopenharmony_ci DPNI_QUEUE_RX, i, &cfg); 5162306a36Sopenharmony_ci if (err) { 5262306a36Sopenharmony_ci netdev_err(priv->net_dev, 5362306a36Sopenharmony_ci "dpni_set_congestion_notification failed\n"); 5462306a36Sopenharmony_ci return err; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci return 0; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic int dpaa2_eth_dcbnl_ieee_setpfc(struct net_device *net_dev, 6262306a36Sopenharmony_ci struct ieee_pfc *pfc) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 6562306a36Sopenharmony_ci struct dpni_link_cfg link_cfg = {0}; 6662306a36Sopenharmony_ci bool tx_pause; 6762306a36Sopenharmony_ci int err; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (pfc->mbc || pfc->delay) 7062306a36Sopenharmony_ci return -EOPNOTSUPP; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* If same PFC enabled mask, nothing to do */ 7362306a36Sopenharmony_ci if (priv->pfc.pfc_en == pfc->pfc_en) 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* We allow PFC configuration even if it won't have any effect until 7762306a36Sopenharmony_ci * general pause frames are enabled 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_ci tx_pause = dpaa2_eth_tx_pause_enabled(priv->link_state.options); 8062306a36Sopenharmony_ci if (!dpaa2_eth_rx_pause_enabled(priv->link_state.options) || !tx_pause) 8162306a36Sopenharmony_ci netdev_warn(net_dev, "Pause support must be enabled in order for PFC to work!\n"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci link_cfg.rate = priv->link_state.rate; 8462306a36Sopenharmony_ci link_cfg.options = priv->link_state.options; 8562306a36Sopenharmony_ci if (pfc->pfc_en) 8662306a36Sopenharmony_ci link_cfg.options |= DPNI_LINK_OPT_PFC_PAUSE; 8762306a36Sopenharmony_ci else 8862306a36Sopenharmony_ci link_cfg.options &= ~DPNI_LINK_OPT_PFC_PAUSE; 8962306a36Sopenharmony_ci err = dpni_set_link_cfg(priv->mc_io, 0, priv->mc_token, &link_cfg); 9062306a36Sopenharmony_ci if (err) { 9162306a36Sopenharmony_ci netdev_err(net_dev, "dpni_set_link_cfg failed\n"); 9262306a36Sopenharmony_ci return err; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Configure congestion notifications for the enabled priorities */ 9662306a36Sopenharmony_ci err = dpaa2_eth_set_pfc_cn(priv, pfc->pfc_en); 9762306a36Sopenharmony_ci if (err) 9862306a36Sopenharmony_ci return err; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci memcpy(&priv->pfc, pfc, sizeof(priv->pfc)); 10162306a36Sopenharmony_ci priv->pfc_enabled = !!pfc->pfc_en; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci dpaa2_eth_set_rx_taildrop(priv, tx_pause, priv->pfc_enabled); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return 0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic u8 dpaa2_eth_dcbnl_getdcbx(struct net_device *net_dev) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return priv->dcbx_mode; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic u8 dpaa2_eth_dcbnl_setdcbx(struct net_device *net_dev, u8 mode) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return (mode != (priv->dcbx_mode)) ? 1 : 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic u8 dpaa2_eth_dcbnl_getcap(struct net_device *net_dev, int capid, u8 *cap) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct dpaa2_eth_priv *priv = netdev_priv(net_dev); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci switch (capid) { 12762306a36Sopenharmony_ci case DCB_CAP_ATTR_PFC: 12862306a36Sopenharmony_ci *cap = true; 12962306a36Sopenharmony_ci break; 13062306a36Sopenharmony_ci case DCB_CAP_ATTR_PFC_TCS: 13162306a36Sopenharmony_ci *cap = 1 << (dpaa2_eth_tc_count(priv) - 1); 13262306a36Sopenharmony_ci break; 13362306a36Sopenharmony_ci case DCB_CAP_ATTR_DCBX: 13462306a36Sopenharmony_ci *cap = priv->dcbx_mode; 13562306a36Sopenharmony_ci break; 13662306a36Sopenharmony_ci default: 13762306a36Sopenharmony_ci *cap = false; 13862306a36Sopenharmony_ci break; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ciconst struct dcbnl_rtnl_ops dpaa2_eth_dcbnl_ops = { 14562306a36Sopenharmony_ci .ieee_getpfc = dpaa2_eth_dcbnl_ieee_getpfc, 14662306a36Sopenharmony_ci .ieee_setpfc = dpaa2_eth_dcbnl_ieee_setpfc, 14762306a36Sopenharmony_ci .getdcbx = dpaa2_eth_dcbnl_getdcbx, 14862306a36Sopenharmony_ci .setdcbx = dpaa2_eth_dcbnl_setdcbx, 14962306a36Sopenharmony_ci .getcap = dpaa2_eth_dcbnl_getcap, 15062306a36Sopenharmony_ci}; 151