1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2013-2014 Chelsio Communications. All rights reserved. |
4 | * |
5 | * Written by Anish Bhatt (anish@chelsio.com) |
6 | * Casey Leedom (leedom@chelsio.com) |
7 | */ |
8 | |
9 | #include "cxgb4.h" |
10 | |
11 | /* DCBx version control |
12 | */ |
13 | const char * const dcb_ver_array[] = { |
14 | "Unknown" , |
15 | "DCBx-CIN" , |
16 | "DCBx-CEE 1.01" , |
17 | "DCBx-IEEE" , |
18 | "" , "" , "" , |
19 | "Auto Negotiated" |
20 | }; |
21 | |
22 | static inline bool cxgb4_dcb_state_synced(enum cxgb4_dcb_state state) |
23 | { |
24 | if (state == CXGB4_DCB_STATE_FW_ALLSYNCED || |
25 | state == CXGB4_DCB_STATE_HOST) |
26 | return true; |
27 | else |
28 | return false; |
29 | } |
30 | |
31 | /* Initialize a port's Data Center Bridging state. |
32 | */ |
33 | void cxgb4_dcb_state_init(struct net_device *dev) |
34 | { |
35 | struct port_info *pi = netdev2pinfo(dev); |
36 | struct port_dcb_info *dcb = &pi->dcb; |
37 | int version_temp = dcb->dcb_version; |
38 | |
39 | memset(dcb, 0, sizeof(struct port_dcb_info)); |
40 | dcb->state = CXGB4_DCB_STATE_START; |
41 | if (version_temp) |
42 | dcb->dcb_version = version_temp; |
43 | |
44 | netdev_dbg(dev, "%s: Initializing DCB state for port[%d]\n" , |
45 | __func__, pi->port_id); |
46 | } |
47 | |
48 | void cxgb4_dcb_version_init(struct net_device *dev) |
49 | { |
50 | struct port_info *pi = netdev2pinfo(dev); |
51 | struct port_dcb_info *dcb = &pi->dcb; |
52 | |
53 | /* Any writes here are only done on kernels that exlicitly need |
54 | * a specific version, say < 2.6.38 which only support CEE |
55 | */ |
56 | dcb->dcb_version = FW_PORT_DCB_VER_AUTO; |
57 | } |
58 | |
59 | static void cxgb4_dcb_cleanup_apps(struct net_device *dev) |
60 | { |
61 | struct port_info *pi = netdev2pinfo(dev); |
62 | struct adapter *adap = pi->adapter; |
63 | struct port_dcb_info *dcb = &pi->dcb; |
64 | struct dcb_app app; |
65 | int i, err; |
66 | |
67 | /* zero priority implies remove */ |
68 | app.priority = 0; |
69 | |
70 | for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { |
71 | /* Check if app list is exhausted */ |
72 | if (!dcb->app_priority[i].protocolid) |
73 | break; |
74 | |
75 | app.protocol = dcb->app_priority[i].protocolid; |
76 | |
77 | if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) { |
78 | app.priority = dcb->app_priority[i].user_prio_map; |
79 | app.selector = dcb->app_priority[i].sel_field + 1; |
80 | err = dcb_ieee_delapp(dev, &app); |
81 | } else { |
82 | app.selector = !!(dcb->app_priority[i].sel_field); |
83 | err = dcb_setapp(dev, &app); |
84 | } |
85 | |
86 | if (err) { |
87 | dev_err(adap->pdev_dev, |
88 | "Failed DCB Clear %s Application Priority: sel=%d, prot=%d, err=%d\n" , |
89 | dcb_ver_array[dcb->dcb_version], app.selector, |
90 | app.protocol, -err); |
91 | break; |
92 | } |
93 | } |
94 | } |
95 | |
96 | /* Reset a port's Data Center Bridging state. Typically used after a |
97 | * Link Down event. |
98 | */ |
99 | void cxgb4_dcb_reset(struct net_device *dev) |
100 | { |
101 | cxgb4_dcb_cleanup_apps(dev); |
102 | cxgb4_dcb_state_init(dev); |
103 | } |
104 | |
105 | /* update the dcb port support, if version is IEEE then set it to |
106 | * FW_PORT_DCB_VER_IEEE and if DCB_CAP_DCBX_VER_CEE is already set then |
107 | * clear that. and if it is set to CEE then set dcb supported to |
108 | * DCB_CAP_DCBX_VER_CEE & if DCB_CAP_DCBX_VER_IEEE is set, clear it |
109 | */ |
110 | static inline void cxgb4_dcb_update_support(struct port_dcb_info *dcb) |
111 | { |
112 | if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) { |
113 | if (dcb->supported & DCB_CAP_DCBX_VER_CEE) |
114 | dcb->supported &= ~DCB_CAP_DCBX_VER_CEE; |
115 | dcb->supported |= DCB_CAP_DCBX_VER_IEEE; |
116 | } else if (dcb->dcb_version == FW_PORT_DCB_VER_CEE1D01) { |
117 | if (dcb->supported & DCB_CAP_DCBX_VER_IEEE) |
118 | dcb->supported &= ~DCB_CAP_DCBX_VER_IEEE; |
119 | dcb->supported |= DCB_CAP_DCBX_VER_CEE; |
120 | } |
121 | } |
122 | |
123 | /* Finite State machine for Data Center Bridging. |
124 | */ |
125 | void cxgb4_dcb_state_fsm(struct net_device *dev, |
126 | enum cxgb4_dcb_state_input transition_to) |
127 | { |
128 | struct port_info *pi = netdev2pinfo(dev); |
129 | struct port_dcb_info *dcb = &pi->dcb; |
130 | struct adapter *adap = pi->adapter; |
131 | enum cxgb4_dcb_state current_state = dcb->state; |
132 | |
133 | netdev_dbg(dev, "%s: State change from %d to %d for %s\n" , |
134 | __func__, dcb->state, transition_to, dev->name); |
135 | |
136 | switch (current_state) { |
137 | case CXGB4_DCB_STATE_START: { |
138 | switch (transition_to) { |
139 | case CXGB4_DCB_INPUT_FW_DISABLED: { |
140 | /* we're going to use Host DCB */ |
141 | dcb->state = CXGB4_DCB_STATE_HOST; |
142 | dcb->supported = CXGB4_DCBX_HOST_SUPPORT; |
143 | break; |
144 | } |
145 | |
146 | case CXGB4_DCB_INPUT_FW_ENABLED: { |
147 | /* we're going to use Firmware DCB */ |
148 | dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE; |
149 | dcb->supported = DCB_CAP_DCBX_LLD_MANAGED; |
150 | if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) |
151 | dcb->supported |= DCB_CAP_DCBX_VER_IEEE; |
152 | else |
153 | dcb->supported |= DCB_CAP_DCBX_VER_CEE; |
154 | break; |
155 | } |
156 | |
157 | case CXGB4_DCB_INPUT_FW_INCOMPLETE: { |
158 | /* expected transition */ |
159 | break; |
160 | } |
161 | |
162 | case CXGB4_DCB_INPUT_FW_ALLSYNCED: { |
163 | dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED; |
164 | break; |
165 | } |
166 | |
167 | default: |
168 | goto bad_state_input; |
169 | } |
170 | break; |
171 | } |
172 | |
173 | case CXGB4_DCB_STATE_FW_INCOMPLETE: { |
174 | if (transition_to != CXGB4_DCB_INPUT_FW_DISABLED) { |
175 | /* during this CXGB4_DCB_STATE_FW_INCOMPLETE state, |
176 | * check if the dcb version is changed (there can be |
177 | * mismatch in default config & the negotiated switch |
178 | * configuration at FW, so update the dcb support |
179 | * accordingly. |
180 | */ |
181 | cxgb4_dcb_update_support(dcb); |
182 | } |
183 | switch (transition_to) { |
184 | case CXGB4_DCB_INPUT_FW_ENABLED: { |
185 | /* we're alreaady in firmware DCB mode */ |
186 | break; |
187 | } |
188 | |
189 | case CXGB4_DCB_INPUT_FW_INCOMPLETE: { |
190 | /* we're already incomplete */ |
191 | break; |
192 | } |
193 | |
194 | case CXGB4_DCB_INPUT_FW_ALLSYNCED: { |
195 | dcb->state = CXGB4_DCB_STATE_FW_ALLSYNCED; |
196 | dcb->enabled = 1; |
197 | linkwatch_fire_event(dev); |
198 | break; |
199 | } |
200 | |
201 | default: |
202 | goto bad_state_input; |
203 | } |
204 | break; |
205 | } |
206 | |
207 | case CXGB4_DCB_STATE_FW_ALLSYNCED: { |
208 | switch (transition_to) { |
209 | case CXGB4_DCB_INPUT_FW_ENABLED: { |
210 | /* we're alreaady in firmware DCB mode */ |
211 | break; |
212 | } |
213 | |
214 | case CXGB4_DCB_INPUT_FW_INCOMPLETE: { |
215 | /* We were successfully running with firmware DCB but |
216 | * now it's telling us that it's in an "incomplete |
217 | * state. We need to reset back to a ground state |
218 | * of incomplete. |
219 | */ |
220 | cxgb4_dcb_reset(dev); |
221 | dcb->state = CXGB4_DCB_STATE_FW_INCOMPLETE; |
222 | dcb->supported = CXGB4_DCBX_FW_SUPPORT; |
223 | linkwatch_fire_event(dev); |
224 | break; |
225 | } |
226 | |
227 | case CXGB4_DCB_INPUT_FW_ALLSYNCED: { |
228 | /* we're already all sync'ed |
229 | * this is only applicable for IEEE or |
230 | * when another VI already completed negotiaton |
231 | */ |
232 | dcb->enabled = 1; |
233 | linkwatch_fire_event(dev); |
234 | break; |
235 | } |
236 | |
237 | default: |
238 | goto bad_state_input; |
239 | } |
240 | break; |
241 | } |
242 | |
243 | case CXGB4_DCB_STATE_HOST: { |
244 | switch (transition_to) { |
245 | case CXGB4_DCB_INPUT_FW_DISABLED: { |
246 | /* we're alreaady in Host DCB mode */ |
247 | break; |
248 | } |
249 | |
250 | default: |
251 | goto bad_state_input; |
252 | } |
253 | break; |
254 | } |
255 | |
256 | default: |
257 | goto bad_state_transition; |
258 | } |
259 | return; |
260 | |
261 | bad_state_input: |
262 | dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: illegal input symbol %d\n" , |
263 | transition_to); |
264 | return; |
265 | |
266 | bad_state_transition: |
267 | dev_err(adap->pdev_dev, "cxgb4_dcb_state_fsm: bad state transition, state = %d, input = %d\n" , |
268 | current_state, transition_to); |
269 | } |
270 | |
271 | /* Handle a DCB/DCBX update message from the firmware. |
272 | */ |
273 | void cxgb4_dcb_handle_fw_update(struct adapter *adap, |
274 | const struct fw_port_cmd *pcmd) |
275 | { |
276 | const union fw_port_dcb *fwdcb = &pcmd->u.dcb; |
277 | int port = FW_PORT_CMD_PORTID_G(be32_to_cpu(pcmd->op_to_portid)); |
278 | struct net_device *dev = adap->port[adap->chan_map[port]]; |
279 | struct port_info *pi = netdev_priv(dev); |
280 | struct port_dcb_info *dcb = &pi->dcb; |
281 | int dcb_type = pcmd->u.dcb.pgid.type; |
282 | int dcb_running_version; |
283 | |
284 | /* Handle Firmware DCB Control messages separately since they drive |
285 | * our state machine. |
286 | */ |
287 | if (dcb_type == FW_PORT_DCB_TYPE_CONTROL) { |
288 | enum cxgb4_dcb_state_input input = |
289 | ((pcmd->u.dcb.control.all_syncd_pkd & |
290 | FW_PORT_CMD_ALL_SYNCD_F) |
291 | ? CXGB4_DCB_INPUT_FW_ALLSYNCED |
292 | : CXGB4_DCB_INPUT_FW_INCOMPLETE); |
293 | |
294 | if (dcb->dcb_version != FW_PORT_DCB_VER_UNKNOWN) { |
295 | dcb_running_version = FW_PORT_CMD_DCB_VERSION_G( |
296 | be16_to_cpu( |
297 | pcmd->u.dcb.control.dcb_version_to_app_state)); |
298 | if (dcb_running_version == FW_PORT_DCB_VER_CEE1D01 || |
299 | dcb_running_version == FW_PORT_DCB_VER_IEEE) { |
300 | dcb->dcb_version = dcb_running_version; |
301 | dev_warn(adap->pdev_dev, "Interface %s is running %s\n" , |
302 | dev->name, |
303 | dcb_ver_array[dcb->dcb_version]); |
304 | } else { |
305 | dev_warn(adap->pdev_dev, |
306 | "Something screwed up, requested firmware for %s, but firmware returned %s instead\n" , |
307 | dcb_ver_array[dcb->dcb_version], |
308 | dcb_ver_array[dcb_running_version]); |
309 | dcb->dcb_version = FW_PORT_DCB_VER_UNKNOWN; |
310 | } |
311 | } |
312 | |
313 | cxgb4_dcb_state_fsm(dev, transition_to: input); |
314 | return; |
315 | } |
316 | |
317 | /* It's weird, and almost certainly an error, to get Firmware DCB |
318 | * messages when we either haven't been told whether we're going to be |
319 | * doing Host or Firmware DCB; and even worse when we've been told |
320 | * that we're doing Host DCB! |
321 | */ |
322 | if (dcb->state == CXGB4_DCB_STATE_START || |
323 | dcb->state == CXGB4_DCB_STATE_HOST) { |
324 | dev_err(adap->pdev_dev, "Receiving Firmware DCB messages in State %d\n" , |
325 | dcb->state); |
326 | return; |
327 | } |
328 | |
329 | /* Now handle the general Firmware DCB update messages ... |
330 | */ |
331 | switch (dcb_type) { |
332 | case FW_PORT_DCB_TYPE_PGID: |
333 | dcb->pgid = be32_to_cpu(fwdcb->pgid.pgid); |
334 | dcb->msgs |= CXGB4_DCB_FW_PGID; |
335 | break; |
336 | |
337 | case FW_PORT_DCB_TYPE_PGRATE: |
338 | dcb->pg_num_tcs_supported = fwdcb->pgrate.num_tcs_supported; |
339 | memcpy(dcb->pgrate, &fwdcb->pgrate.pgrate, |
340 | sizeof(dcb->pgrate)); |
341 | memcpy(dcb->tsa, &fwdcb->pgrate.tsa, |
342 | sizeof(dcb->tsa)); |
343 | dcb->msgs |= CXGB4_DCB_FW_PGRATE; |
344 | if (dcb->msgs & CXGB4_DCB_FW_PGID) |
345 | IEEE_FAUX_SYNC(dev, dcb); |
346 | break; |
347 | |
348 | case FW_PORT_DCB_TYPE_PRIORATE: |
349 | memcpy(dcb->priorate, &fwdcb->priorate.strict_priorate, |
350 | sizeof(dcb->priorate)); |
351 | dcb->msgs |= CXGB4_DCB_FW_PRIORATE; |
352 | break; |
353 | |
354 | case FW_PORT_DCB_TYPE_PFC: |
355 | dcb->pfcen = fwdcb->pfc.pfcen; |
356 | dcb->pfc_num_tcs_supported = fwdcb->pfc.max_pfc_tcs; |
357 | dcb->msgs |= CXGB4_DCB_FW_PFC; |
358 | IEEE_FAUX_SYNC(dev, dcb); |
359 | break; |
360 | |
361 | case FW_PORT_DCB_TYPE_APP_ID: { |
362 | const struct fw_port_app_priority *fwap = &fwdcb->app_priority; |
363 | int idx = fwap->idx; |
364 | struct app_priority *ap = &dcb->app_priority[idx]; |
365 | |
366 | struct dcb_app app = { |
367 | .protocol = be16_to_cpu(fwap->protocolid), |
368 | }; |
369 | int err; |
370 | |
371 | /* Convert from firmware format to relevant format |
372 | * when using app selector |
373 | */ |
374 | if (dcb->dcb_version == FW_PORT_DCB_VER_IEEE) { |
375 | app.selector = (fwap->sel_field + 1); |
376 | app.priority = ffs(fwap->user_prio_map) - 1; |
377 | err = dcb_ieee_setapp(dev, &app); |
378 | IEEE_FAUX_SYNC(dev, dcb); |
379 | } else { |
380 | /* Default is CEE */ |
381 | app.selector = !!(fwap->sel_field); |
382 | app.priority = fwap->user_prio_map; |
383 | err = dcb_setapp(dev, &app); |
384 | } |
385 | |
386 | if (err) |
387 | dev_err(adap->pdev_dev, |
388 | "Failed DCB Set Application Priority: sel=%d, prot=%d, prio=%d, err=%d\n" , |
389 | app.selector, app.protocol, app.priority, -err); |
390 | |
391 | ap->user_prio_map = fwap->user_prio_map; |
392 | ap->sel_field = fwap->sel_field; |
393 | ap->protocolid = be16_to_cpu(fwap->protocolid); |
394 | dcb->msgs |= CXGB4_DCB_FW_APP_ID; |
395 | break; |
396 | } |
397 | |
398 | default: |
399 | dev_err(adap->pdev_dev, "Unknown DCB update type received %x\n" , |
400 | dcb_type); |
401 | break; |
402 | } |
403 | } |
404 | |
405 | /* Data Center Bridging netlink operations. |
406 | */ |
407 | |
408 | |
409 | /* Get current DCB enabled/disabled state. |
410 | */ |
411 | static u8 cxgb4_getstate(struct net_device *dev) |
412 | { |
413 | struct port_info *pi = netdev2pinfo(dev); |
414 | |
415 | return pi->dcb.enabled; |
416 | } |
417 | |
418 | /* Set DCB enabled/disabled. |
419 | */ |
420 | static u8 cxgb4_setstate(struct net_device *dev, u8 enabled) |
421 | { |
422 | struct port_info *pi = netdev2pinfo(dev); |
423 | |
424 | /* If DCBx is host-managed, dcb is enabled by outside lldp agents */ |
425 | if (pi->dcb.state == CXGB4_DCB_STATE_HOST) { |
426 | pi->dcb.enabled = enabled; |
427 | return 0; |
428 | } |
429 | |
430 | /* Firmware doesn't provide any mechanism to control the DCB state. |
431 | */ |
432 | if (enabled != (pi->dcb.state == CXGB4_DCB_STATE_FW_ALLSYNCED)) |
433 | return 1; |
434 | |
435 | return 0; |
436 | } |
437 | |
438 | static void cxgb4_getpgtccfg(struct net_device *dev, int tc, |
439 | u8 *prio_type, u8 *pgid, u8 *bw_per, |
440 | u8 *up_tc_map, int local) |
441 | { |
442 | struct fw_port_cmd pcmd; |
443 | struct port_info *pi = netdev2pinfo(dev); |
444 | struct adapter *adap = pi->adapter; |
445 | int err; |
446 | |
447 | *prio_type = *pgid = *bw_per = *up_tc_map = 0; |
448 | |
449 | if (local) |
450 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
451 | else |
452 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
453 | |
454 | pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; |
455 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
456 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
457 | dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n" , -err); |
458 | return; |
459 | } |
460 | *pgid = (be32_to_cpu(pcmd.u.dcb.pgid.pgid) >> (tc * 4)) & 0xf; |
461 | |
462 | if (local) |
463 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
464 | else |
465 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
466 | pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; |
467 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
468 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
469 | dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n" , |
470 | -err); |
471 | return; |
472 | } |
473 | |
474 | *bw_per = pcmd.u.dcb.pgrate.pgrate[*pgid]; |
475 | *up_tc_map = (1 << tc); |
476 | |
477 | /* prio_type is link strict */ |
478 | if (*pgid != 0xF) |
479 | *prio_type = 0x2; |
480 | } |
481 | |
482 | static void cxgb4_getpgtccfg_tx(struct net_device *dev, int tc, |
483 | u8 *prio_type, u8 *pgid, u8 *bw_per, |
484 | u8 *up_tc_map) |
485 | { |
486 | /* tc 0 is written at MSB position */ |
487 | return cxgb4_getpgtccfg(dev, tc: (7 - tc), prio_type, pgid, bw_per, |
488 | up_tc_map, local: 1); |
489 | } |
490 | |
491 | |
492 | static void cxgb4_getpgtccfg_rx(struct net_device *dev, int tc, |
493 | u8 *prio_type, u8 *pgid, u8 *bw_per, |
494 | u8 *up_tc_map) |
495 | { |
496 | /* tc 0 is written at MSB position */ |
497 | return cxgb4_getpgtccfg(dev, tc: (7 - tc), prio_type, pgid, bw_per, |
498 | up_tc_map, local: 0); |
499 | } |
500 | |
501 | static void cxgb4_setpgtccfg_tx(struct net_device *dev, int tc, |
502 | u8 prio_type, u8 pgid, u8 bw_per, |
503 | u8 up_tc_map) |
504 | { |
505 | struct fw_port_cmd pcmd; |
506 | struct port_info *pi = netdev2pinfo(dev); |
507 | struct adapter *adap = pi->adapter; |
508 | int fw_tc = 7 - tc; |
509 | u32 _pgid; |
510 | int err; |
511 | |
512 | if (pgid == DCB_ATTR_VALUE_UNDEFINED) |
513 | return; |
514 | if (bw_per == DCB_ATTR_VALUE_UNDEFINED) |
515 | return; |
516 | |
517 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
518 | pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; |
519 | |
520 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
521 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
522 | dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n" , -err); |
523 | return; |
524 | } |
525 | |
526 | _pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); |
527 | _pgid &= ~(0xF << (fw_tc * 4)); |
528 | _pgid |= pgid << (fw_tc * 4); |
529 | pcmd.u.dcb.pgid.pgid = cpu_to_be32(_pgid); |
530 | |
531 | INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); |
532 | |
533 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
534 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
535 | dev_err(adap->pdev_dev, "DCB write PGID failed with %d\n" , |
536 | -err); |
537 | return; |
538 | } |
539 | |
540 | memset(&pcmd, 0, sizeof(struct fw_port_cmd)); |
541 | |
542 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
543 | pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; |
544 | |
545 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
546 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
547 | dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n" , |
548 | -err); |
549 | return; |
550 | } |
551 | |
552 | pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per; |
553 | |
554 | INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); |
555 | if (pi->dcb.state == CXGB4_DCB_STATE_HOST) |
556 | pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY_F); |
557 | |
558 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
559 | if (err != FW_PORT_DCB_CFG_SUCCESS) |
560 | dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n" , |
561 | -err); |
562 | } |
563 | |
564 | static void cxgb4_getpgbwgcfg(struct net_device *dev, int pgid, u8 *bw_per, |
565 | int local) |
566 | { |
567 | struct fw_port_cmd pcmd; |
568 | struct port_info *pi = netdev2pinfo(dev); |
569 | struct adapter *adap = pi->adapter; |
570 | int err; |
571 | |
572 | if (local) |
573 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
574 | else |
575 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
576 | |
577 | pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; |
578 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
579 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
580 | dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n" , |
581 | -err); |
582 | return; |
583 | } |
584 | |
585 | *bw_per = pcmd.u.dcb.pgrate.pgrate[pgid]; |
586 | } |
587 | |
588 | static void cxgb4_getpgbwgcfg_tx(struct net_device *dev, int pgid, u8 *bw_per) |
589 | { |
590 | return cxgb4_getpgbwgcfg(dev, pgid, bw_per, local: 1); |
591 | } |
592 | |
593 | static void cxgb4_getpgbwgcfg_rx(struct net_device *dev, int pgid, u8 *bw_per) |
594 | { |
595 | return cxgb4_getpgbwgcfg(dev, pgid, bw_per, local: 0); |
596 | } |
597 | |
598 | static void cxgb4_setpgbwgcfg_tx(struct net_device *dev, int pgid, |
599 | u8 bw_per) |
600 | { |
601 | struct fw_port_cmd pcmd; |
602 | struct port_info *pi = netdev2pinfo(dev); |
603 | struct adapter *adap = pi->adapter; |
604 | int err; |
605 | |
606 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
607 | pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; |
608 | |
609 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
610 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
611 | dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n" , |
612 | -err); |
613 | return; |
614 | } |
615 | |
616 | pcmd.u.dcb.pgrate.pgrate[pgid] = bw_per; |
617 | |
618 | INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); |
619 | if (pi->dcb.state == CXGB4_DCB_STATE_HOST) |
620 | pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY_F); |
621 | |
622 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
623 | |
624 | if (err != FW_PORT_DCB_CFG_SUCCESS) |
625 | dev_err(adap->pdev_dev, "DCB write PGRATE failed with %d\n" , |
626 | -err); |
627 | } |
628 | |
629 | /* Return whether the specified Traffic Class Priority has Priority Pause |
630 | * Frames enabled. |
631 | */ |
632 | static void cxgb4_getpfccfg(struct net_device *dev, int priority, u8 *pfccfg) |
633 | { |
634 | struct port_info *pi = netdev2pinfo(dev); |
635 | struct port_dcb_info *dcb = &pi->dcb; |
636 | |
637 | if (!cxgb4_dcb_state_synced(state: dcb->state) || |
638 | priority >= CXGB4_MAX_PRIORITY) |
639 | *pfccfg = 0; |
640 | else |
641 | *pfccfg = (pi->dcb.pfcen >> (7 - priority)) & 1; |
642 | } |
643 | |
644 | /* Enable/disable Priority Pause Frames for the specified Traffic Class |
645 | * Priority. |
646 | */ |
647 | static void cxgb4_setpfccfg(struct net_device *dev, int priority, u8 pfccfg) |
648 | { |
649 | struct fw_port_cmd pcmd; |
650 | struct port_info *pi = netdev2pinfo(dev); |
651 | struct adapter *adap = pi->adapter; |
652 | int err; |
653 | |
654 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state) || |
655 | priority >= CXGB4_MAX_PRIORITY) |
656 | return; |
657 | |
658 | INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); |
659 | if (pi->dcb.state == CXGB4_DCB_STATE_HOST) |
660 | pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY_F); |
661 | |
662 | pcmd.u.dcb.pfc.type = FW_PORT_DCB_TYPE_PFC; |
663 | pcmd.u.dcb.pfc.pfcen = pi->dcb.pfcen; |
664 | |
665 | if (pfccfg) |
666 | pcmd.u.dcb.pfc.pfcen |= (1 << (7 - priority)); |
667 | else |
668 | pcmd.u.dcb.pfc.pfcen &= (~(1 << (7 - priority))); |
669 | |
670 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
671 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
672 | dev_err(adap->pdev_dev, "DCB PFC write failed with %d\n" , -err); |
673 | return; |
674 | } |
675 | |
676 | pi->dcb.pfcen = pcmd.u.dcb.pfc.pfcen; |
677 | } |
678 | |
679 | static u8 cxgb4_setall(struct net_device *dev) |
680 | { |
681 | return 0; |
682 | } |
683 | |
684 | /* Return DCB capabilities. |
685 | */ |
686 | static u8 cxgb4_getcap(struct net_device *dev, int cap_id, u8 *caps) |
687 | { |
688 | struct port_info *pi = netdev2pinfo(dev); |
689 | |
690 | switch (cap_id) { |
691 | case DCB_CAP_ATTR_PG: |
692 | case DCB_CAP_ATTR_PFC: |
693 | *caps = true; |
694 | break; |
695 | |
696 | case DCB_CAP_ATTR_PG_TCS: |
697 | /* 8 priorities for PG represented by bitmap */ |
698 | *caps = 0x80; |
699 | break; |
700 | |
701 | case DCB_CAP_ATTR_PFC_TCS: |
702 | /* 8 priorities for PFC represented by bitmap */ |
703 | *caps = 0x80; |
704 | break; |
705 | |
706 | case DCB_CAP_ATTR_GSP: |
707 | *caps = true; |
708 | break; |
709 | |
710 | case DCB_CAP_ATTR_UP2TC: |
711 | case DCB_CAP_ATTR_BCN: |
712 | *caps = false; |
713 | break; |
714 | |
715 | case DCB_CAP_ATTR_DCBX: |
716 | *caps = pi->dcb.supported; |
717 | break; |
718 | |
719 | default: |
720 | *caps = false; |
721 | } |
722 | |
723 | return 0; |
724 | } |
725 | |
726 | /* Return the number of Traffic Classes for the indicated Traffic Class ID. |
727 | */ |
728 | static int cxgb4_getnumtcs(struct net_device *dev, int tcs_id, u8 *num) |
729 | { |
730 | struct port_info *pi = netdev2pinfo(dev); |
731 | |
732 | switch (tcs_id) { |
733 | case DCB_NUMTCS_ATTR_PG: |
734 | if (pi->dcb.msgs & CXGB4_DCB_FW_PGRATE) |
735 | *num = pi->dcb.pg_num_tcs_supported; |
736 | else |
737 | *num = 0x8; |
738 | break; |
739 | |
740 | case DCB_NUMTCS_ATTR_PFC: |
741 | *num = 0x8; |
742 | break; |
743 | |
744 | default: |
745 | return -EINVAL; |
746 | } |
747 | |
748 | return 0; |
749 | } |
750 | |
751 | /* Set the number of Traffic Classes supported for the indicated Traffic Class |
752 | * ID. |
753 | */ |
754 | static int cxgb4_setnumtcs(struct net_device *dev, int tcs_id, u8 num) |
755 | { |
756 | /* Setting the number of Traffic Classes isn't supported. |
757 | */ |
758 | return -ENOSYS; |
759 | } |
760 | |
761 | /* Return whether Priority Flow Control is enabled. */ |
762 | static u8 cxgb4_getpfcstate(struct net_device *dev) |
763 | { |
764 | struct port_info *pi = netdev2pinfo(dev); |
765 | |
766 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state)) |
767 | return false; |
768 | |
769 | return pi->dcb.pfcen != 0; |
770 | } |
771 | |
772 | /* Enable/disable Priority Flow Control. */ |
773 | static void cxgb4_setpfcstate(struct net_device *dev, u8 state) |
774 | { |
775 | /* We can't enable/disable Priority Flow Control but we also can't |
776 | * return an error ... |
777 | */ |
778 | } |
779 | |
780 | /* Return the Application User Priority Map associated with the specified |
781 | * Application ID. |
782 | */ |
783 | static int __cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id, |
784 | int peer) |
785 | { |
786 | struct port_info *pi = netdev2pinfo(dev); |
787 | struct adapter *adap = pi->adapter; |
788 | int i; |
789 | |
790 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state)) |
791 | return 0; |
792 | |
793 | for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { |
794 | struct fw_port_cmd pcmd; |
795 | int err; |
796 | |
797 | if (peer) |
798 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
799 | else |
800 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
801 | |
802 | pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; |
803 | pcmd.u.dcb.app_priority.idx = i; |
804 | |
805 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
806 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
807 | dev_err(adap->pdev_dev, "DCB APP read failed with %d\n" , |
808 | -err); |
809 | return err; |
810 | } |
811 | if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) |
812 | if (pcmd.u.dcb.app_priority.sel_field == app_idtype) |
813 | return pcmd.u.dcb.app_priority.user_prio_map; |
814 | |
815 | /* exhausted app list */ |
816 | if (!pcmd.u.dcb.app_priority.protocolid) |
817 | break; |
818 | } |
819 | |
820 | return -EEXIST; |
821 | } |
822 | |
823 | /* Return the Application User Priority Map associated with the specified |
824 | * Application ID. |
825 | */ |
826 | static int cxgb4_getapp(struct net_device *dev, u8 app_idtype, u16 app_id) |
827 | { |
828 | /* Convert app_idtype to firmware format before querying */ |
829 | return __cxgb4_getapp(dev, app_idtype: app_idtype == DCB_APP_IDTYPE_ETHTYPE ? |
830 | app_idtype : 3, app_id, peer: 0); |
831 | } |
832 | |
833 | /* Write a new Application User Priority Map for the specified Application ID |
834 | */ |
835 | static int __cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id, |
836 | u8 app_prio) |
837 | { |
838 | struct fw_port_cmd pcmd; |
839 | struct port_info *pi = netdev2pinfo(dev); |
840 | struct adapter *adap = pi->adapter; |
841 | int i, err; |
842 | |
843 | |
844 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state)) |
845 | return -EINVAL; |
846 | |
847 | /* DCB info gets thrown away on link up */ |
848 | if (!netif_carrier_ok(dev)) |
849 | return -ENOLINK; |
850 | |
851 | for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { |
852 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
853 | pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; |
854 | pcmd.u.dcb.app_priority.idx = i; |
855 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
856 | |
857 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
858 | dev_err(adap->pdev_dev, "DCB app table read failed with %d\n" , |
859 | -err); |
860 | return err; |
861 | } |
862 | if (be16_to_cpu(pcmd.u.dcb.app_priority.protocolid) == app_id) { |
863 | /* overwrite existing app table */ |
864 | pcmd.u.dcb.app_priority.protocolid = 0; |
865 | break; |
866 | } |
867 | /* find first empty slot */ |
868 | if (!pcmd.u.dcb.app_priority.protocolid) |
869 | break; |
870 | } |
871 | |
872 | if (i == CXGB4_MAX_DCBX_APP_SUPPORTED) { |
873 | /* no empty slots available */ |
874 | dev_err(adap->pdev_dev, "DCB app table full\n" ); |
875 | return -EBUSY; |
876 | } |
877 | |
878 | /* write out new app table entry */ |
879 | INIT_PORT_DCB_WRITE_CMD(pcmd, pi->port_id); |
880 | if (pi->dcb.state == CXGB4_DCB_STATE_HOST) |
881 | pcmd.op_to_portid |= cpu_to_be32(FW_PORT_CMD_APPLY_F); |
882 | |
883 | pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; |
884 | pcmd.u.dcb.app_priority.protocolid = cpu_to_be16(app_id); |
885 | pcmd.u.dcb.app_priority.sel_field = app_idtype; |
886 | pcmd.u.dcb.app_priority.user_prio_map = app_prio; |
887 | pcmd.u.dcb.app_priority.idx = i; |
888 | |
889 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
890 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
891 | dev_err(adap->pdev_dev, "DCB app table write failed with %d\n" , |
892 | -err); |
893 | return err; |
894 | } |
895 | |
896 | return 0; |
897 | } |
898 | |
899 | /* Priority for CEE inside dcb_app is bitmask, with 0 being an invalid value */ |
900 | static int cxgb4_setapp(struct net_device *dev, u8 app_idtype, u16 app_id, |
901 | u8 app_prio) |
902 | { |
903 | int ret; |
904 | struct dcb_app app = { |
905 | .selector = app_idtype, |
906 | .protocol = app_id, |
907 | .priority = app_prio, |
908 | }; |
909 | |
910 | if (app_idtype != DCB_APP_IDTYPE_ETHTYPE && |
911 | app_idtype != DCB_APP_IDTYPE_PORTNUM) |
912 | return -EINVAL; |
913 | |
914 | /* Convert app_idtype to a format that firmware understands */ |
915 | ret = __cxgb4_setapp(dev, app_idtype: app_idtype == DCB_APP_IDTYPE_ETHTYPE ? |
916 | app_idtype : 3, app_id, app_prio); |
917 | if (ret) |
918 | return ret; |
919 | |
920 | return dcb_setapp(dev, &app); |
921 | } |
922 | |
923 | /* Return whether IEEE Data Center Bridging has been negotiated. |
924 | */ |
925 | static inline int |
926 | cxgb4_ieee_negotiation_complete(struct net_device *dev, |
927 | enum cxgb4_dcb_fw_msgs dcb_subtype) |
928 | { |
929 | struct port_info *pi = netdev2pinfo(dev); |
930 | struct port_dcb_info *dcb = &pi->dcb; |
931 | |
932 | if (dcb->state == CXGB4_DCB_STATE_FW_ALLSYNCED) |
933 | if (dcb_subtype && !(dcb->msgs & dcb_subtype)) |
934 | return 0; |
935 | |
936 | return (cxgb4_dcb_state_synced(state: dcb->state) && |
937 | (dcb->supported & DCB_CAP_DCBX_VER_IEEE)); |
938 | } |
939 | |
940 | static int cxgb4_ieee_read_ets(struct net_device *dev, struct ieee_ets *ets, |
941 | int local) |
942 | { |
943 | struct port_info *pi = netdev2pinfo(dev); |
944 | struct port_dcb_info *dcb = &pi->dcb; |
945 | struct adapter *adap = pi->adapter; |
946 | uint32_t tc_info; |
947 | struct fw_port_cmd pcmd; |
948 | int i, bwg, err; |
949 | |
950 | if (!(dcb->msgs & (CXGB4_DCB_FW_PGID | CXGB4_DCB_FW_PGRATE))) |
951 | return 0; |
952 | |
953 | ets->ets_cap = dcb->pg_num_tcs_supported; |
954 | |
955 | if (local) { |
956 | ets->willing = 1; |
957 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
958 | } else { |
959 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
960 | } |
961 | |
962 | pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; |
963 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
964 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
965 | dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n" , -err); |
966 | return err; |
967 | } |
968 | |
969 | tc_info = be32_to_cpu(pcmd.u.dcb.pgid.pgid); |
970 | |
971 | if (local) |
972 | INIT_PORT_DCB_READ_LOCAL_CMD(pcmd, pi->port_id); |
973 | else |
974 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
975 | |
976 | pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; |
977 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
978 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
979 | dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n" , |
980 | -err); |
981 | return err; |
982 | } |
983 | |
984 | for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) { |
985 | bwg = (tc_info >> ((7 - i) * 4)) & 0xF; |
986 | ets->prio_tc[i] = bwg; |
987 | ets->tc_tx_bw[i] = pcmd.u.dcb.pgrate.pgrate[i]; |
988 | ets->tc_rx_bw[i] = ets->tc_tx_bw[i]; |
989 | ets->tc_tsa[i] = pcmd.u.dcb.pgrate.tsa[i]; |
990 | } |
991 | |
992 | return 0; |
993 | } |
994 | |
995 | static int cxgb4_ieee_get_ets(struct net_device *dev, struct ieee_ets *ets) |
996 | { |
997 | return cxgb4_ieee_read_ets(dev, ets, local: 1); |
998 | } |
999 | |
1000 | /* We reuse this for peer PFC as well, as we can't have it enabled one way */ |
1001 | static int cxgb4_ieee_get_pfc(struct net_device *dev, struct ieee_pfc *pfc) |
1002 | { |
1003 | struct port_info *pi = netdev2pinfo(dev); |
1004 | struct port_dcb_info *dcb = &pi->dcb; |
1005 | |
1006 | memset(pfc, 0, sizeof(struct ieee_pfc)); |
1007 | |
1008 | if (!(dcb->msgs & CXGB4_DCB_FW_PFC)) |
1009 | return 0; |
1010 | |
1011 | pfc->pfc_cap = dcb->pfc_num_tcs_supported; |
1012 | pfc->pfc_en = bitswap_1(val: dcb->pfcen); |
1013 | |
1014 | return 0; |
1015 | } |
1016 | |
1017 | static int cxgb4_ieee_peer_ets(struct net_device *dev, struct ieee_ets *ets) |
1018 | { |
1019 | return cxgb4_ieee_read_ets(dev, ets, local: 0); |
1020 | } |
1021 | |
1022 | /* Fill in the Application User Priority Map associated with the |
1023 | * specified Application. |
1024 | * Priority for IEEE dcb_app is an integer, with 0 being a valid value |
1025 | */ |
1026 | static int cxgb4_ieee_getapp(struct net_device *dev, struct dcb_app *app) |
1027 | { |
1028 | int prio; |
1029 | |
1030 | if (!cxgb4_ieee_negotiation_complete(dev, dcb_subtype: CXGB4_DCB_FW_APP_ID)) |
1031 | return -EINVAL; |
1032 | if (!(app->selector && app->protocol)) |
1033 | return -EINVAL; |
1034 | |
1035 | /* Try querying firmware first, use firmware format */ |
1036 | prio = __cxgb4_getapp(dev, app_idtype: app->selector - 1, app_id: app->protocol, peer: 0); |
1037 | |
1038 | if (prio < 0) |
1039 | prio = dcb_ieee_getapp_mask(dev, app); |
1040 | |
1041 | app->priority = ffs(prio) - 1; |
1042 | return 0; |
1043 | } |
1044 | |
1045 | /* Write a new Application User Priority Map for the specified Application ID. |
1046 | * Priority for IEEE dcb_app is an integer, with 0 being a valid value |
1047 | */ |
1048 | static int cxgb4_ieee_setapp(struct net_device *dev, struct dcb_app *app) |
1049 | { |
1050 | int ret; |
1051 | |
1052 | if (!cxgb4_ieee_negotiation_complete(dev, dcb_subtype: CXGB4_DCB_FW_APP_ID)) |
1053 | return -EINVAL; |
1054 | if (!(app->selector && app->protocol)) |
1055 | return -EINVAL; |
1056 | |
1057 | if (!(app->selector > IEEE_8021QAZ_APP_SEL_ETHERTYPE && |
1058 | app->selector < IEEE_8021QAZ_APP_SEL_ANY)) |
1059 | return -EINVAL; |
1060 | |
1061 | /* change selector to a format that firmware understands */ |
1062 | ret = __cxgb4_setapp(dev, app_idtype: app->selector - 1, app_id: app->protocol, |
1063 | app_prio: (1 << app->priority)); |
1064 | if (ret) |
1065 | return ret; |
1066 | |
1067 | return dcb_ieee_setapp(dev, app); |
1068 | } |
1069 | |
1070 | /* Return our DCBX parameters. |
1071 | */ |
1072 | static u8 cxgb4_getdcbx(struct net_device *dev) |
1073 | { |
1074 | struct port_info *pi = netdev2pinfo(dev); |
1075 | |
1076 | /* This is already set by cxgb4_set_dcb_caps, so just return it */ |
1077 | return pi->dcb.supported; |
1078 | } |
1079 | |
1080 | /* Set our DCBX parameters. |
1081 | */ |
1082 | static u8 cxgb4_setdcbx(struct net_device *dev, u8 dcb_request) |
1083 | { |
1084 | struct port_info *pi = netdev2pinfo(dev); |
1085 | |
1086 | /* Filter out requests which exceed our capabilities. |
1087 | */ |
1088 | if ((dcb_request & (CXGB4_DCBX_FW_SUPPORT | CXGB4_DCBX_HOST_SUPPORT)) |
1089 | != dcb_request) |
1090 | return 1; |
1091 | |
1092 | /* Can't enable DCB if we haven't successfully negotiated it. |
1093 | */ |
1094 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state)) |
1095 | return 1; |
1096 | |
1097 | /* There's currently no mechanism to allow for the firmware DCBX |
1098 | * negotiation to be changed from the Host Driver. If the caller |
1099 | * requests exactly the same parameters that we already have then |
1100 | * we'll allow them to be successfully "set" ... |
1101 | */ |
1102 | if (dcb_request != pi->dcb.supported) |
1103 | return 1; |
1104 | |
1105 | pi->dcb.supported = dcb_request; |
1106 | return 0; |
1107 | } |
1108 | |
1109 | static int cxgb4_getpeer_app(struct net_device *dev, |
1110 | struct dcb_peer_app_info *info, u16 *app_count) |
1111 | { |
1112 | struct fw_port_cmd pcmd; |
1113 | struct port_info *pi = netdev2pinfo(dev); |
1114 | struct adapter *adap = pi->adapter; |
1115 | int i, err = 0; |
1116 | |
1117 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state)) |
1118 | return 1; |
1119 | |
1120 | info->willing = 0; |
1121 | info->error = 0; |
1122 | |
1123 | *app_count = 0; |
1124 | for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { |
1125 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
1126 | pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; |
1127 | pcmd.u.dcb.app_priority.idx = *app_count; |
1128 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
1129 | |
1130 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
1131 | dev_err(adap->pdev_dev, "DCB app table read failed with %d\n" , |
1132 | -err); |
1133 | return err; |
1134 | } |
1135 | |
1136 | /* find first empty slot */ |
1137 | if (!pcmd.u.dcb.app_priority.protocolid) |
1138 | break; |
1139 | } |
1140 | *app_count = i; |
1141 | return err; |
1142 | } |
1143 | |
1144 | static int cxgb4_getpeerapp_tbl(struct net_device *dev, struct dcb_app *table) |
1145 | { |
1146 | struct fw_port_cmd pcmd; |
1147 | struct port_info *pi = netdev2pinfo(dev); |
1148 | struct adapter *adap = pi->adapter; |
1149 | int i, err = 0; |
1150 | |
1151 | if (!cxgb4_dcb_state_synced(state: pi->dcb.state)) |
1152 | return 1; |
1153 | |
1154 | for (i = 0; i < CXGB4_MAX_DCBX_APP_SUPPORTED; i++) { |
1155 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
1156 | pcmd.u.dcb.app_priority.type = FW_PORT_DCB_TYPE_APP_ID; |
1157 | pcmd.u.dcb.app_priority.idx = i; |
1158 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
1159 | |
1160 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
1161 | dev_err(adap->pdev_dev, "DCB app table read failed with %d\n" , |
1162 | -err); |
1163 | return err; |
1164 | } |
1165 | |
1166 | /* find first empty slot */ |
1167 | if (!pcmd.u.dcb.app_priority.protocolid) |
1168 | break; |
1169 | |
1170 | table[i].selector = (pcmd.u.dcb.app_priority.sel_field + 1); |
1171 | table[i].protocol = |
1172 | be16_to_cpu(pcmd.u.dcb.app_priority.protocolid); |
1173 | table[i].priority = |
1174 | ffs(pcmd.u.dcb.app_priority.user_prio_map) - 1; |
1175 | } |
1176 | return err; |
1177 | } |
1178 | |
1179 | /* Return Priority Group information. |
1180 | */ |
1181 | static int cxgb4_cee_peer_getpg(struct net_device *dev, struct cee_pg *pg) |
1182 | { |
1183 | struct fw_port_cmd pcmd; |
1184 | struct port_info *pi = netdev2pinfo(dev); |
1185 | struct adapter *adap = pi->adapter; |
1186 | u32 pgid; |
1187 | int i, err; |
1188 | |
1189 | /* We're always "willing" -- the Switch Fabric always dictates the |
1190 | * DCBX parameters to us. |
1191 | */ |
1192 | pg->willing = true; |
1193 | |
1194 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
1195 | pcmd.u.dcb.pgid.type = FW_PORT_DCB_TYPE_PGID; |
1196 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
1197 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
1198 | dev_err(adap->pdev_dev, "DCB read PGID failed with %d\n" , -err); |
1199 | return err; |
1200 | } |
1201 | pgid = be32_to_cpu(pcmd.u.dcb.pgid.pgid); |
1202 | |
1203 | for (i = 0; i < CXGB4_MAX_PRIORITY; i++) |
1204 | pg->prio_pg[7 - i] = (pgid >> (i * 4)) & 0xF; |
1205 | |
1206 | INIT_PORT_DCB_READ_PEER_CMD(pcmd, pi->port_id); |
1207 | pcmd.u.dcb.pgrate.type = FW_PORT_DCB_TYPE_PGRATE; |
1208 | err = t4_wr_mbox(adap, mbox: adap->mbox, cmd: &pcmd, size: sizeof(pcmd), rpl: &pcmd); |
1209 | if (err != FW_PORT_DCB_CFG_SUCCESS) { |
1210 | dev_err(adap->pdev_dev, "DCB read PGRATE failed with %d\n" , |
1211 | -err); |
1212 | return err; |
1213 | } |
1214 | |
1215 | for (i = 0; i < CXGB4_MAX_PRIORITY; i++) |
1216 | pg->pg_bw[i] = pcmd.u.dcb.pgrate.pgrate[i]; |
1217 | |
1218 | pg->tcs_supported = pcmd.u.dcb.pgrate.num_tcs_supported; |
1219 | |
1220 | return 0; |
1221 | } |
1222 | |
1223 | /* Return Priority Flow Control information. |
1224 | */ |
1225 | static int cxgb4_cee_peer_getpfc(struct net_device *dev, struct cee_pfc *pfc) |
1226 | { |
1227 | struct port_info *pi = netdev2pinfo(dev); |
1228 | |
1229 | cxgb4_getnumtcs(dev, tcs_id: DCB_NUMTCS_ATTR_PFC, num: &(pfc->tcs_supported)); |
1230 | |
1231 | /* Firmware sends this to us in a formwat that is a bit flipped version |
1232 | * of spec, correct it before we send it to host. This is taken care of |
1233 | * by bit shifting in other uses of pfcen |
1234 | */ |
1235 | pfc->pfc_en = bitswap_1(val: pi->dcb.pfcen); |
1236 | |
1237 | pfc->tcs_supported = pi->dcb.pfc_num_tcs_supported; |
1238 | |
1239 | return 0; |
1240 | } |
1241 | |
1242 | const struct dcbnl_rtnl_ops cxgb4_dcb_ops = { |
1243 | .ieee_getets = cxgb4_ieee_get_ets, |
1244 | .ieee_getpfc = cxgb4_ieee_get_pfc, |
1245 | .ieee_getapp = cxgb4_ieee_getapp, |
1246 | .ieee_setapp = cxgb4_ieee_setapp, |
1247 | .ieee_peer_getets = cxgb4_ieee_peer_ets, |
1248 | .ieee_peer_getpfc = cxgb4_ieee_get_pfc, |
1249 | |
1250 | /* CEE std */ |
1251 | .getstate = cxgb4_getstate, |
1252 | .setstate = cxgb4_setstate, |
1253 | .getpgtccfgtx = cxgb4_getpgtccfg_tx, |
1254 | .getpgbwgcfgtx = cxgb4_getpgbwgcfg_tx, |
1255 | .getpgtccfgrx = cxgb4_getpgtccfg_rx, |
1256 | .getpgbwgcfgrx = cxgb4_getpgbwgcfg_rx, |
1257 | .setpgtccfgtx = cxgb4_setpgtccfg_tx, |
1258 | .setpgbwgcfgtx = cxgb4_setpgbwgcfg_tx, |
1259 | .setpfccfg = cxgb4_setpfccfg, |
1260 | .getpfccfg = cxgb4_getpfccfg, |
1261 | .setall = cxgb4_setall, |
1262 | .getcap = cxgb4_getcap, |
1263 | .getnumtcs = cxgb4_getnumtcs, |
1264 | .setnumtcs = cxgb4_setnumtcs, |
1265 | .getpfcstate = cxgb4_getpfcstate, |
1266 | .setpfcstate = cxgb4_setpfcstate, |
1267 | .getapp = cxgb4_getapp, |
1268 | .setapp = cxgb4_setapp, |
1269 | |
1270 | /* DCBX configuration */ |
1271 | .getdcbx = cxgb4_getdcbx, |
1272 | .setdcbx = cxgb4_setdcbx, |
1273 | |
1274 | /* peer apps */ |
1275 | .peer_getappinfo = cxgb4_getpeer_app, |
1276 | .peer_getapptable = cxgb4_getpeerapp_tbl, |
1277 | |
1278 | /* CEE peer */ |
1279 | .cee_peer_getpg = cxgb4_cee_peer_getpg, |
1280 | .cee_peer_getpfc = cxgb4_cee_peer_getpfc, |
1281 | }; |
1282 | |