1 | // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
2 | /* Copyright 2020 NXP */ |
3 | |
4 | #include "dpaa2-eth.h" |
5 | |
6 | static int dpaa2_eth_dcbnl_ieee_getpfc(struct net_device *net_dev, |
7 | struct ieee_pfc *pfc) |
8 | { |
9 | struct dpaa2_eth_priv *priv = netdev_priv(dev: net_dev); |
10 | |
11 | if (!(priv->link_state.options & DPNI_LINK_OPT_PFC_PAUSE)) |
12 | return 0; |
13 | |
14 | memcpy(pfc, &priv->pfc, sizeof(priv->pfc)); |
15 | pfc->pfc_cap = dpaa2_eth_tc_count(priv); |
16 | |
17 | return 0; |
18 | } |
19 | |
20 | static inline bool dpaa2_eth_is_prio_enabled(u8 pfc_en, u8 tc) |
21 | { |
22 | return !!(pfc_en & (1 << tc)); |
23 | } |
24 | |
25 | static int dpaa2_eth_set_pfc_cn(struct dpaa2_eth_priv *priv, u8 pfc_en) |
26 | { |
27 | struct dpni_congestion_notification_cfg cfg = {0}; |
28 | int i, err; |
29 | |
30 | cfg.notification_mode = DPNI_CONG_OPT_FLOW_CONTROL; |
31 | cfg.units = DPNI_CONGESTION_UNIT_FRAMES; |
32 | cfg.message_iova = 0ULL; |
33 | cfg.message_ctx = 0ULL; |
34 | |
35 | for (i = 0; i < dpaa2_eth_tc_count(priv); i++) { |
36 | if (dpaa2_eth_is_prio_enabled(pfc_en, tc: i)) { |
37 | cfg.threshold_entry = DPAA2_ETH_CN_THRESH_ENTRY(priv); |
38 | cfg.threshold_exit = DPAA2_ETH_CN_THRESH_EXIT(priv); |
39 | } else { |
40 | /* For priorities not set in the pfc_en mask, we leave |
41 | * the congestion thresholds at zero, which effectively |
42 | * disables generation of PFC frames for them |
43 | */ |
44 | cfg.threshold_entry = 0; |
45 | cfg.threshold_exit = 0; |
46 | } |
47 | |
48 | err = dpni_set_congestion_notification(mc_io: priv->mc_io, cmd_flags: 0, |
49 | token: priv->mc_token, |
50 | qtype: DPNI_QUEUE_RX, tc_id: i, cfg: &cfg); |
51 | if (err) { |
52 | netdev_err(dev: priv->net_dev, |
53 | format: "dpni_set_congestion_notification failed\n" ); |
54 | return err; |
55 | } |
56 | } |
57 | |
58 | return 0; |
59 | } |
60 | |
61 | static int dpaa2_eth_dcbnl_ieee_setpfc(struct net_device *net_dev, |
62 | struct ieee_pfc *pfc) |
63 | { |
64 | struct dpaa2_eth_priv *priv = netdev_priv(dev: net_dev); |
65 | struct dpni_link_cfg link_cfg = {0}; |
66 | bool tx_pause; |
67 | int err; |
68 | |
69 | if (pfc->mbc || pfc->delay) |
70 | return -EOPNOTSUPP; |
71 | |
72 | /* If same PFC enabled mask, nothing to do */ |
73 | if (priv->pfc.pfc_en == pfc->pfc_en) |
74 | return 0; |
75 | |
76 | /* We allow PFC configuration even if it won't have any effect until |
77 | * general pause frames are enabled |
78 | */ |
79 | tx_pause = dpaa2_eth_tx_pause_enabled(link_options: priv->link_state.options); |
80 | if (!dpaa2_eth_rx_pause_enabled(link_options: priv->link_state.options) || !tx_pause) |
81 | netdev_warn(dev: net_dev, format: "Pause support must be enabled in order for PFC to work!\n" ); |
82 | |
83 | link_cfg.rate = priv->link_state.rate; |
84 | link_cfg.options = priv->link_state.options; |
85 | if (pfc->pfc_en) |
86 | link_cfg.options |= DPNI_LINK_OPT_PFC_PAUSE; |
87 | else |
88 | link_cfg.options &= ~DPNI_LINK_OPT_PFC_PAUSE; |
89 | err = dpni_set_link_cfg(mc_io: priv->mc_io, cmd_flags: 0, token: priv->mc_token, cfg: &link_cfg); |
90 | if (err) { |
91 | netdev_err(dev: net_dev, format: "dpni_set_link_cfg failed\n" ); |
92 | return err; |
93 | } |
94 | |
95 | /* Configure congestion notifications for the enabled priorities */ |
96 | err = dpaa2_eth_set_pfc_cn(priv, pfc_en: pfc->pfc_en); |
97 | if (err) |
98 | return err; |
99 | |
100 | memcpy(&priv->pfc, pfc, sizeof(priv->pfc)); |
101 | priv->pfc_enabled = !!pfc->pfc_en; |
102 | |
103 | dpaa2_eth_set_rx_taildrop(priv, tx_pause, pfc: priv->pfc_enabled); |
104 | |
105 | return 0; |
106 | } |
107 | |
108 | static u8 dpaa2_eth_dcbnl_getdcbx(struct net_device *net_dev) |
109 | { |
110 | struct dpaa2_eth_priv *priv = netdev_priv(dev: net_dev); |
111 | |
112 | return priv->dcbx_mode; |
113 | } |
114 | |
115 | static u8 dpaa2_eth_dcbnl_setdcbx(struct net_device *net_dev, u8 mode) |
116 | { |
117 | struct dpaa2_eth_priv *priv = netdev_priv(dev: net_dev); |
118 | |
119 | return (mode != (priv->dcbx_mode)) ? 1 : 0; |
120 | } |
121 | |
122 | static u8 dpaa2_eth_dcbnl_getcap(struct net_device *net_dev, int capid, u8 *cap) |
123 | { |
124 | struct dpaa2_eth_priv *priv = netdev_priv(dev: net_dev); |
125 | |
126 | switch (capid) { |
127 | case DCB_CAP_ATTR_PFC: |
128 | *cap = true; |
129 | break; |
130 | case DCB_CAP_ATTR_PFC_TCS: |
131 | *cap = 1 << (dpaa2_eth_tc_count(priv) - 1); |
132 | break; |
133 | case DCB_CAP_ATTR_DCBX: |
134 | *cap = priv->dcbx_mode; |
135 | break; |
136 | default: |
137 | *cap = false; |
138 | break; |
139 | } |
140 | |
141 | return 0; |
142 | } |
143 | |
144 | const struct dcbnl_rtnl_ops dpaa2_eth_dcbnl_ops = { |
145 | .ieee_getpfc = dpaa2_eth_dcbnl_ieee_getpfc, |
146 | .ieee_setpfc = dpaa2_eth_dcbnl_ieee_setpfc, |
147 | .getdcbx = dpaa2_eth_dcbnl_getdcbx, |
148 | .setdcbx = dpaa2_eth_dcbnl_setdcbx, |
149 | .getcap = dpaa2_eth_dcbnl_getcap, |
150 | }; |
151 | |