1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (C) 2017-2019 Netronome Systems, Inc. */ |
3 | |
4 | #include <linux/bitfield.h> |
5 | #include <linux/errno.h> |
6 | #include <linux/etherdevice.h> |
7 | #include <linux/if_link.h> |
8 | #include <linux/if_ether.h> |
9 | |
10 | #include "nfpcore/nfp_cpp.h" |
11 | #include "nfp_app.h" |
12 | #include "nfp_main.h" |
13 | #include "nfp_net_ctrl.h" |
14 | #include "nfp_net.h" |
15 | #include "nfp_net_sriov.h" |
16 | |
17 | static int |
18 | nfp_net_sriov_check(struct nfp_app *app, int vf, u16 cap, const char *msg, bool warn) |
19 | { |
20 | u16 cap_vf; |
21 | |
22 | if (!app || !app->pf->vfcfg_tbl2) |
23 | return -EOPNOTSUPP; |
24 | |
25 | cap_vf = readw(addr: app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_CAP); |
26 | if ((cap_vf & cap) != cap) { |
27 | if (warn) |
28 | nfp_warn(app->pf->cpp, "ndo_set_vf_%s not supported\n" , msg); |
29 | return -EOPNOTSUPP; |
30 | } |
31 | |
32 | if (vf < 0 || vf >= app->pf->num_vfs) { |
33 | if (warn) |
34 | nfp_warn(app->pf->cpp, "invalid VF id %d\n" , vf); |
35 | return -EINVAL; |
36 | } |
37 | |
38 | return 0; |
39 | } |
40 | |
41 | static int |
42 | nfp_net_sriov_update(struct nfp_app *app, int vf, u16 update, const char *msg) |
43 | { |
44 | struct nfp_net *nn; |
45 | int ret; |
46 | |
47 | /* Write update info to mailbox in VF config symbol */ |
48 | writeb(val: vf, addr: app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_VF_NUM); |
49 | writew(val: update, addr: app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_UPD); |
50 | |
51 | nn = list_first_entry(&app->pf->vnics, struct nfp_net, vnic_list); |
52 | /* Signal VF reconfiguration */ |
53 | ret = nfp_net_reconfig(nn, NFP_NET_CFG_UPDATE_VF); |
54 | if (ret) |
55 | return ret; |
56 | |
57 | ret = readw(addr: app->pf->vfcfg_tbl2 + NFP_NET_VF_CFG_MB_RET); |
58 | if (ret) |
59 | nfp_warn(app->pf->cpp, |
60 | "FW refused VF %s update with errno: %d\n" , msg, ret); |
61 | return -ret; |
62 | } |
63 | |
64 | int nfp_app_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) |
65 | { |
66 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
67 | unsigned int vf_offset; |
68 | int err; |
69 | |
70 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_MAC, msg: "mac" , warn: true); |
71 | if (err) |
72 | return err; |
73 | |
74 | if (is_multicast_ether_addr(addr: mac)) { |
75 | nfp_warn(app->pf->cpp, |
76 | "invalid Ethernet address %pM for VF id %d\n" , |
77 | mac, vf); |
78 | return -EINVAL; |
79 | } |
80 | |
81 | /* Write MAC to VF entry in VF config symbol */ |
82 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; |
83 | writel(val: get_unaligned_be32(p: mac), addr: app->pf->vfcfg_tbl2 + vf_offset); |
84 | writew(val: get_unaligned_be16(p: mac + 4), |
85 | addr: app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO); |
86 | |
87 | err = nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_MAC, msg: "MAC" ); |
88 | if (!err) |
89 | nfp_info(app->pf->cpp, |
90 | "MAC %pM set on VF %d, reload the VF driver to make this change effective.\n" , |
91 | mac, vf); |
92 | |
93 | return err; |
94 | } |
95 | |
96 | int nfp_app_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, |
97 | __be16 vlan_proto) |
98 | { |
99 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
100 | u16 update = NFP_NET_VF_CFG_MB_UPD_VLAN; |
101 | bool is_proto_sup = true; |
102 | unsigned int vf_offset; |
103 | u32 vlan_tag; |
104 | int err; |
105 | |
106 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_VLAN, msg: "vlan" , warn: true); |
107 | if (err) |
108 | return err; |
109 | |
110 | if (!eth_type_vlan(ethertype: vlan_proto)) |
111 | return -EOPNOTSUPP; |
112 | |
113 | if (vlan > 4095 || qos > 7) { |
114 | nfp_warn(app->pf->cpp, |
115 | "invalid vlan id or qos for VF id %d\n" , vf); |
116 | return -EINVAL; |
117 | } |
118 | |
119 | /* Check if fw supports or not */ |
120 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_VLAN_PROTO, msg: "vlan_proto" , warn: true); |
121 | if (err) |
122 | is_proto_sup = false; |
123 | |
124 | if (vlan_proto != htons(ETH_P_8021Q)) { |
125 | if (!is_proto_sup) |
126 | return -EOPNOTSUPP; |
127 | update |= NFP_NET_VF_CFG_MB_UPD_VLAN_PROTO; |
128 | } |
129 | |
130 | /* Write VLAN tag to VF entry in VF config symbol */ |
131 | vlan_tag = FIELD_PREP(NFP_NET_VF_CFG_VLAN_VID, vlan) | |
132 | FIELD_PREP(NFP_NET_VF_CFG_VLAN_QOS, qos); |
133 | |
134 | /* vlan_tag of 0 means that the configuration should be cleared and in |
135 | * such circumstances setting the TPID has no meaning when |
136 | * configuring firmware. |
137 | */ |
138 | if (vlan_tag && is_proto_sup) |
139 | vlan_tag |= FIELD_PREP(NFP_NET_VF_CFG_VLAN_PROT, ntohs(vlan_proto)); |
140 | |
141 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; |
142 | writel(val: vlan_tag, addr: app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN); |
143 | |
144 | return nfp_net_sriov_update(app, vf, update, msg: "vlan" ); |
145 | } |
146 | |
147 | int nfp_app_set_vf_rate(struct net_device *netdev, int vf, |
148 | int min_tx_rate, int max_tx_rate) |
149 | { |
150 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
151 | u32 vf_offset, ratevalue; |
152 | int err; |
153 | |
154 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_RATE, msg: "rate" , warn: true); |
155 | if (err) |
156 | return err; |
157 | |
158 | if (max_tx_rate >= NFP_NET_VF_RATE_MAX || |
159 | min_tx_rate >= NFP_NET_VF_RATE_MAX) { |
160 | nfp_warn(app->cpp, "tx-rate exceeds %d.\n" , |
161 | NFP_NET_VF_RATE_MAX); |
162 | return -EINVAL; |
163 | } |
164 | |
165 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; |
166 | ratevalue = FIELD_PREP(NFP_NET_VF_CFG_MAX_RATE, |
167 | max_tx_rate ? max_tx_rate : |
168 | NFP_NET_VF_RATE_MAX) | |
169 | FIELD_PREP(NFP_NET_VF_CFG_MIN_RATE, min_tx_rate); |
170 | |
171 | writel(val: ratevalue, |
172 | addr: app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_RATE); |
173 | |
174 | return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_RATE, |
175 | msg: "rate" ); |
176 | } |
177 | |
178 | int nfp_app_set_vf_spoofchk(struct net_device *netdev, int vf, bool enable) |
179 | { |
180 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
181 | unsigned int vf_offset; |
182 | u8 vf_ctrl; |
183 | int err; |
184 | |
185 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_SPOOF, |
186 | msg: "spoofchk" , warn: true); |
187 | if (err) |
188 | return err; |
189 | |
190 | /* Write spoof check control bit to VF entry in VF config symbol */ |
191 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + |
192 | NFP_NET_VF_CFG_CTRL; |
193 | vf_ctrl = readb(addr: app->pf->vfcfg_tbl2 + vf_offset); |
194 | vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_SPOOF; |
195 | vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_SPOOF, enable); |
196 | writeb(val: vf_ctrl, addr: app->pf->vfcfg_tbl2 + vf_offset); |
197 | |
198 | return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_SPOOF, |
199 | msg: "spoofchk" ); |
200 | } |
201 | |
202 | int nfp_app_set_vf_trust(struct net_device *netdev, int vf, bool enable) |
203 | { |
204 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
205 | unsigned int vf_offset; |
206 | u8 vf_ctrl; |
207 | int err; |
208 | |
209 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_TRUST, |
210 | msg: "trust" , warn: true); |
211 | if (err) |
212 | return err; |
213 | |
214 | /* Write trust control bit to VF entry in VF config symbol */ |
215 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + |
216 | NFP_NET_VF_CFG_CTRL; |
217 | vf_ctrl = readb(addr: app->pf->vfcfg_tbl2 + vf_offset); |
218 | vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_TRUST; |
219 | vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_TRUST, enable); |
220 | writeb(val: vf_ctrl, addr: app->pf->vfcfg_tbl2 + vf_offset); |
221 | |
222 | return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_TRUST, |
223 | msg: "trust" ); |
224 | } |
225 | |
226 | int nfp_app_set_vf_link_state(struct net_device *netdev, int vf, |
227 | int link_state) |
228 | { |
229 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
230 | unsigned int vf_offset; |
231 | u8 vf_ctrl; |
232 | int err; |
233 | |
234 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_LINK_STATE, |
235 | msg: "link_state" , warn: true); |
236 | if (err) |
237 | return err; |
238 | |
239 | switch (link_state) { |
240 | case IFLA_VF_LINK_STATE_AUTO: |
241 | case IFLA_VF_LINK_STATE_ENABLE: |
242 | case IFLA_VF_LINK_STATE_DISABLE: |
243 | break; |
244 | default: |
245 | return -EINVAL; |
246 | } |
247 | |
248 | /* Write link state to VF entry in VF config symbol */ |
249 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ + |
250 | NFP_NET_VF_CFG_CTRL; |
251 | vf_ctrl = readb(addr: app->pf->vfcfg_tbl2 + vf_offset); |
252 | vf_ctrl &= ~NFP_NET_VF_CFG_CTRL_LINK_STATE; |
253 | vf_ctrl |= FIELD_PREP(NFP_NET_VF_CFG_CTRL_LINK_STATE, link_state); |
254 | writeb(val: vf_ctrl, addr: app->pf->vfcfg_tbl2 + vf_offset); |
255 | |
256 | return nfp_net_sriov_update(app, vf, NFP_NET_VF_CFG_MB_UPD_LINK_STATE, |
257 | msg: "link state" ); |
258 | } |
259 | |
260 | int nfp_app_get_vf_config(struct net_device *netdev, int vf, |
261 | struct ifla_vf_info *ivi) |
262 | { |
263 | struct nfp_app *app = nfp_app_from_netdev(netdev); |
264 | u32 vf_offset, mac_hi, rate; |
265 | u32 vlan_tag; |
266 | u16 mac_lo; |
267 | u8 flags; |
268 | int err; |
269 | |
270 | err = nfp_net_sriov_check(app, vf, cap: 0, msg: "" , warn: true); |
271 | if (err) |
272 | return err; |
273 | |
274 | vf_offset = NFP_NET_VF_CFG_MB_SZ + vf * NFP_NET_VF_CFG_SZ; |
275 | |
276 | mac_hi = readl(addr: app->pf->vfcfg_tbl2 + vf_offset); |
277 | mac_lo = readw(addr: app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_MAC_LO); |
278 | |
279 | flags = readb(addr: app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_CTRL); |
280 | vlan_tag = readl(addr: app->pf->vfcfg_tbl2 + vf_offset + NFP_NET_VF_CFG_VLAN); |
281 | |
282 | memset(ivi, 0, sizeof(*ivi)); |
283 | ivi->vf = vf; |
284 | |
285 | put_unaligned_be32(val: mac_hi, p: &ivi->mac[0]); |
286 | put_unaligned_be16(val: mac_lo, p: &ivi->mac[4]); |
287 | |
288 | ivi->vlan = FIELD_GET(NFP_NET_VF_CFG_VLAN_VID, vlan_tag); |
289 | ivi->qos = FIELD_GET(NFP_NET_VF_CFG_VLAN_QOS, vlan_tag); |
290 | if (!nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_VLAN_PROTO, msg: "vlan_proto" , warn: false)) |
291 | ivi->vlan_proto = htons(FIELD_GET(NFP_NET_VF_CFG_VLAN_PROT, vlan_tag)); |
292 | ivi->spoofchk = FIELD_GET(NFP_NET_VF_CFG_CTRL_SPOOF, flags); |
293 | ivi->trusted = FIELD_GET(NFP_NET_VF_CFG_CTRL_TRUST, flags); |
294 | ivi->linkstate = FIELD_GET(NFP_NET_VF_CFG_CTRL_LINK_STATE, flags); |
295 | |
296 | err = nfp_net_sriov_check(app, vf, NFP_NET_VF_CFG_MB_CAP_RATE, msg: "rate" , warn: false); |
297 | if (!err) { |
298 | rate = readl(addr: app->pf->vfcfg_tbl2 + vf_offset + |
299 | NFP_NET_VF_CFG_RATE); |
300 | |
301 | ivi->max_tx_rate = FIELD_GET(NFP_NET_VF_CFG_MAX_RATE, rate); |
302 | ivi->min_tx_rate = FIELD_GET(NFP_NET_VF_CFG_MIN_RATE, rate); |
303 | |
304 | if (ivi->max_tx_rate == NFP_NET_VF_RATE_MAX) |
305 | ivi->max_tx_rate = 0; |
306 | if (ivi->min_tx_rate == NFP_NET_VF_RATE_MAX) |
307 | ivi->min_tx_rate = 0; |
308 | } |
309 | |
310 | return 0; |
311 | } |
312 | |