1/******************************************************************************
2
3  Copyright (c) 2013-2018, Intel Corporation
4  All rights reserved.
5
6  Redistribution and use in source and binary forms, with or without
7  modification, are permitted provided that the following conditions are met:
8
9   1. Redistributions of source code must retain the above copyright notice,
10      this list of conditions and the following disclaimer.
11
12   2. Redistributions in binary form must reproduce the above copyright
13      notice, this list of conditions and the following disclaimer in the
14      documentation and/or other materials provided with the distribution.
15
16   3. Neither the name of the Intel Corporation nor the names of its
17      contributors may be used to endorse or promote products derived from
18      this software without specific prior written permission.
19
20  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  POSSIBILITY OF SUCH DAMAGE.
31
32******************************************************************************/
33/*$FreeBSD$*/
34
35#include "i40e_adminq.h"
36#include "i40e_prototype.h"
37#include "i40e_dcb.h"
38
39/**
40 * i40e_get_dcbx_status
41 * @hw: pointer to the hw struct
42 * @status: Embedded DCBX Engine Status
43 *
44 * Get the DCBX status from the Firmware
45 **/
46enum i40e_status_code i40e_get_dcbx_status(struct i40e_hw *hw, u16 *status)
47{
48	u32 reg;
49
50	if (!status)
51		return I40E_ERR_PARAM;
52
53	reg = rd32(hw, I40E_PRTDCB_GENS);
54	*status = (u16)((reg & I40E_PRTDCB_GENS_DCBX_STATUS_MASK) >>
55			I40E_PRTDCB_GENS_DCBX_STATUS_SHIFT);
56
57	return I40E_SUCCESS;
58}
59
60/**
61 * i40e_parse_ieee_etscfg_tlv
62 * @tlv: IEEE 802.1Qaz ETS CFG TLV
63 * @dcbcfg: Local store to update ETS CFG data
64 *
65 * Parses IEEE 802.1Qaz ETS CFG TLV
66 **/
67static void i40e_parse_ieee_etscfg_tlv(struct i40e_lldp_org_tlv *tlv,
68				       struct i40e_dcbx_config *dcbcfg)
69{
70	struct i40e_dcb_ets_config *etscfg;
71	u8 *buf = tlv->tlvinfo;
72	u16 offset = 0;
73	u8 priority;
74	int i;
75
76	/* First Octet post subtype
77	 * --------------------------
78	 * |will-|CBS  | Re-  | Max |
79	 * |ing  |     |served| TCs |
80	 * --------------------------
81	 * |1bit | 1bit|3 bits|3bits|
82	 */
83	etscfg = &dcbcfg->etscfg;
84	etscfg->willing = (u8)((buf[offset] & I40E_IEEE_ETS_WILLING_MASK) >>
85			       I40E_IEEE_ETS_WILLING_SHIFT);
86	etscfg->cbs = (u8)((buf[offset] & I40E_IEEE_ETS_CBS_MASK) >>
87			   I40E_IEEE_ETS_CBS_SHIFT);
88	etscfg->maxtcs = (u8)((buf[offset] & I40E_IEEE_ETS_MAXTC_MASK) >>
89			      I40E_IEEE_ETS_MAXTC_SHIFT);
90
91	/* Move offset to Priority Assignment Table */
92	offset++;
93
94	/* Priority Assignment Table (4 octets)
95	 * Octets:|    1    |    2    |    3    |    4    |
96	 *        -----------------------------------------
97	 *        |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7|
98	 *        -----------------------------------------
99	 *   Bits:|7  4|3  0|7  4|3  0|7  4|3  0|7  4|3  0|
100	 *        -----------------------------------------
101	 */
102	for (i = 0; i < 4; i++) {
103		priority = (u8)((buf[offset] & I40E_IEEE_ETS_PRIO_1_MASK) >>
104				I40E_IEEE_ETS_PRIO_1_SHIFT);
105		etscfg->prioritytable[i * 2] =  priority;
106		priority = (u8)((buf[offset] & I40E_IEEE_ETS_PRIO_0_MASK) >>
107				I40E_IEEE_ETS_PRIO_0_SHIFT);
108		etscfg->prioritytable[i * 2 + 1] = priority;
109		offset++;
110	}
111
112	/* TC Bandwidth Table (8 octets)
113	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
114	 *        ---------------------------------
115	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
116	 *        ---------------------------------
117	 */
118	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
119		etscfg->tcbwtable[i] = buf[offset++];
120
121	/* TSA Assignment Table (8 octets)
122	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
123	 *        ---------------------------------
124	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
125	 *        ---------------------------------
126	 */
127	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
128		etscfg->tsatable[i] = buf[offset++];
129}
130
131/**
132 * i40e_parse_ieee_etsrec_tlv
133 * @tlv: IEEE 802.1Qaz ETS REC TLV
134 * @dcbcfg: Local store to update ETS REC data
135 *
136 * Parses IEEE 802.1Qaz ETS REC TLV
137 **/
138static void i40e_parse_ieee_etsrec_tlv(struct i40e_lldp_org_tlv *tlv,
139				       struct i40e_dcbx_config *dcbcfg)
140{
141	u8 *buf = tlv->tlvinfo;
142	u16 offset = 0;
143	u8 priority;
144	int i;
145
146	/* Move offset to priority table */
147	offset++;
148
149	/* Priority Assignment Table (4 octets)
150	 * Octets:|    1    |    2    |    3    |    4    |
151	 *        -----------------------------------------
152	 *        |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7|
153	 *        -----------------------------------------
154	 *   Bits:|7  4|3  0|7  4|3  0|7  4|3  0|7  4|3  0|
155	 *        -----------------------------------------
156	 */
157	for (i = 0; i < 4; i++) {
158		priority = (u8)((buf[offset] & I40E_IEEE_ETS_PRIO_1_MASK) >>
159				I40E_IEEE_ETS_PRIO_1_SHIFT);
160		dcbcfg->etsrec.prioritytable[i*2] =  priority;
161		priority = (u8)((buf[offset] & I40E_IEEE_ETS_PRIO_0_MASK) >>
162				I40E_IEEE_ETS_PRIO_0_SHIFT);
163		dcbcfg->etsrec.prioritytable[i*2 + 1] = priority;
164		offset++;
165	}
166
167	/* TC Bandwidth Table (8 octets)
168	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
169	 *        ---------------------------------
170	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
171	 *        ---------------------------------
172	 */
173	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
174		dcbcfg->etsrec.tcbwtable[i] = buf[offset++];
175
176	/* TSA Assignment Table (8 octets)
177	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
178	 *        ---------------------------------
179	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
180	 *        ---------------------------------
181	 */
182	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
183		dcbcfg->etsrec.tsatable[i] = buf[offset++];
184}
185
186/**
187 * i40e_parse_ieee_pfccfg_tlv
188 * @tlv: IEEE 802.1Qaz PFC CFG TLV
189 * @dcbcfg: Local store to update PFC CFG data
190 *
191 * Parses IEEE 802.1Qaz PFC CFG TLV
192 **/
193static void i40e_parse_ieee_pfccfg_tlv(struct i40e_lldp_org_tlv *tlv,
194				       struct i40e_dcbx_config *dcbcfg)
195{
196	u8 *buf = tlv->tlvinfo;
197
198	/* ----------------------------------------
199	 * |will-|MBC  | Re-  | PFC |  PFC Enable  |
200	 * |ing  |     |served| cap |              |
201	 * -----------------------------------------
202	 * |1bit | 1bit|2 bits|4bits| 1 octet      |
203	 */
204	dcbcfg->pfc.willing = (u8)((buf[0] & I40E_IEEE_PFC_WILLING_MASK) >>
205				   I40E_IEEE_PFC_WILLING_SHIFT);
206	dcbcfg->pfc.mbc = (u8)((buf[0] & I40E_IEEE_PFC_MBC_MASK) >>
207			       I40E_IEEE_PFC_MBC_SHIFT);
208	dcbcfg->pfc.pfccap = (u8)((buf[0] & I40E_IEEE_PFC_CAP_MASK) >>
209				  I40E_IEEE_PFC_CAP_SHIFT);
210	dcbcfg->pfc.pfcenable = buf[1];
211}
212
213/**
214 * i40e_parse_ieee_app_tlv
215 * @tlv: IEEE 802.1Qaz APP TLV
216 * @dcbcfg: Local store to update APP PRIO data
217 *
218 * Parses IEEE 802.1Qaz APP PRIO TLV
219 **/
220static void i40e_parse_ieee_app_tlv(struct i40e_lldp_org_tlv *tlv,
221				    struct i40e_dcbx_config *dcbcfg)
222{
223	u16 typelength;
224	u16 offset = 0;
225	u16 length;
226	int i = 0;
227	u8 *buf;
228
229	typelength = I40E_NTOHS(tlv->typelength);
230	length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
231		       I40E_LLDP_TLV_LEN_SHIFT);
232	buf = tlv->tlvinfo;
233
234	/* The App priority table starts 5 octets after TLV header */
235	length -= (sizeof(tlv->ouisubtype) + 1);
236
237	/* Move offset to App Priority Table */
238	offset++;
239
240	/* Application Priority Table (3 octets)
241	 * Octets:|         1          |    2    |    3    |
242	 *        -----------------------------------------
243	 *        |Priority|Rsrvd| Sel |    Protocol ID    |
244	 *        -----------------------------------------
245	 *   Bits:|23    21|20 19|18 16|15                0|
246	 *        -----------------------------------------
247	 */
248	while (offset < length) {
249		dcbcfg->app[i].priority = (u8)((buf[offset] &
250						I40E_IEEE_APP_PRIO_MASK) >>
251					       I40E_IEEE_APP_PRIO_SHIFT);
252		dcbcfg->app[i].selector = (u8)((buf[offset] &
253						I40E_IEEE_APP_SEL_MASK) >>
254					       I40E_IEEE_APP_SEL_SHIFT);
255		dcbcfg->app[i].protocolid = (buf[offset + 1] << 0x8) |
256					     buf[offset + 2];
257		/* Move to next app */
258		offset += 3;
259		i++;
260		if (i >= I40E_DCBX_MAX_APPS)
261			break;
262	}
263
264	dcbcfg->numapps = i;
265}
266
267/**
268 * i40e_parse_ieee_etsrec_tlv
269 * @tlv: IEEE 802.1Qaz TLV
270 * @dcbcfg: Local store to update ETS REC data
271 *
272 * Get the TLV subtype and send it to parsing function
273 * based on the subtype value
274 **/
275static void i40e_parse_ieee_tlv(struct i40e_lldp_org_tlv *tlv,
276				struct i40e_dcbx_config *dcbcfg)
277{
278	u32 ouisubtype;
279	u8 subtype;
280
281	ouisubtype = I40E_NTOHL(tlv->ouisubtype);
282	subtype = (u8)((ouisubtype & I40E_LLDP_TLV_SUBTYPE_MASK) >>
283		       I40E_LLDP_TLV_SUBTYPE_SHIFT);
284	switch (subtype) {
285	case I40E_IEEE_SUBTYPE_ETS_CFG:
286		i40e_parse_ieee_etscfg_tlv(tlv, dcbcfg);
287		break;
288	case I40E_IEEE_SUBTYPE_ETS_REC:
289		i40e_parse_ieee_etsrec_tlv(tlv, dcbcfg);
290		break;
291	case I40E_IEEE_SUBTYPE_PFC_CFG:
292		i40e_parse_ieee_pfccfg_tlv(tlv, dcbcfg);
293		break;
294	case I40E_IEEE_SUBTYPE_APP_PRI:
295		i40e_parse_ieee_app_tlv(tlv, dcbcfg);
296		break;
297	default:
298		break;
299	}
300}
301
302/**
303 * i40e_parse_cee_pgcfg_tlv
304 * @tlv: CEE DCBX PG CFG TLV
305 * @dcbcfg: Local store to update ETS CFG data
306 *
307 * Parses CEE DCBX PG CFG TLV
308 **/
309static void i40e_parse_cee_pgcfg_tlv(struct i40e_cee_feat_tlv *tlv,
310				     struct i40e_dcbx_config *dcbcfg)
311{
312	struct i40e_dcb_ets_config *etscfg;
313	u8 *buf = tlv->tlvinfo;
314	u16 offset = 0;
315	u8 priority;
316	int i;
317
318	etscfg = &dcbcfg->etscfg;
319
320	if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK)
321		etscfg->willing = 1;
322
323	etscfg->cbs = 0;
324	/* Priority Group Table (4 octets)
325	 * Octets:|    1    |    2    |    3    |    4    |
326	 *        -----------------------------------------
327	 *        |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7|
328	 *        -----------------------------------------
329	 *   Bits:|7  4|3  0|7  4|3  0|7  4|3  0|7  4|3  0|
330	 *        -----------------------------------------
331	 */
332	for (i = 0; i < 4; i++) {
333		priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_1_MASK) >>
334				 I40E_CEE_PGID_PRIO_1_SHIFT);
335		etscfg->prioritytable[i * 2] =  priority;
336		priority = (u8)((buf[offset] & I40E_CEE_PGID_PRIO_0_MASK) >>
337				 I40E_CEE_PGID_PRIO_0_SHIFT);
338		etscfg->prioritytable[i * 2 + 1] = priority;
339		offset++;
340	}
341
342	/* PG Percentage Table (8 octets)
343	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
344	 *        ---------------------------------
345	 *        |pg0|pg1|pg2|pg3|pg4|pg5|pg6|pg7|
346	 *        ---------------------------------
347	 */
348	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
349		etscfg->tcbwtable[i] = buf[offset++];
350
351	/* Number of TCs supported (1 octet) */
352	etscfg->maxtcs = buf[offset];
353}
354
355/**
356 * i40e_parse_cee_pfccfg_tlv
357 * @tlv: CEE DCBX PFC CFG TLV
358 * @dcbcfg: Local store to update PFC CFG data
359 *
360 * Parses CEE DCBX PFC CFG TLV
361 **/
362static void i40e_parse_cee_pfccfg_tlv(struct i40e_cee_feat_tlv *tlv,
363				      struct i40e_dcbx_config *dcbcfg)
364{
365	u8 *buf = tlv->tlvinfo;
366
367	if (tlv->en_will_err & I40E_CEE_FEAT_TLV_WILLING_MASK)
368		dcbcfg->pfc.willing = 1;
369
370	/* ------------------------
371	 * | PFC Enable | PFC TCs |
372	 * ------------------------
373	 * | 1 octet    | 1 octet |
374	 */
375	dcbcfg->pfc.pfcenable = buf[0];
376	dcbcfg->pfc.pfccap = buf[1];
377}
378
379/**
380 * i40e_parse_cee_app_tlv
381 * @tlv: CEE DCBX APP TLV
382 * @dcbcfg: Local store to update APP PRIO data
383 *
384 * Parses CEE DCBX APP PRIO TLV
385 **/
386static void i40e_parse_cee_app_tlv(struct i40e_cee_feat_tlv *tlv,
387				   struct i40e_dcbx_config *dcbcfg)
388{
389	u16 length, typelength, offset = 0;
390	struct i40e_cee_app_prio *app;
391	u8 i;
392
393	typelength = I40E_NTOHS(tlv->hdr.typelen);
394	length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
395		       I40E_LLDP_TLV_LEN_SHIFT);
396
397	dcbcfg->numapps = length / sizeof(*app);
398	if (!dcbcfg->numapps)
399		return;
400	if (dcbcfg->numapps > I40E_DCBX_MAX_APPS)
401		dcbcfg->numapps = I40E_DCBX_MAX_APPS;
402
403	for (i = 0; i < dcbcfg->numapps; i++) {
404		u8 up, selector;
405
406		app = (struct i40e_cee_app_prio *)(tlv->tlvinfo + offset);
407		for (up = 0; up < I40E_MAX_USER_PRIORITY; up++) {
408			if (app->prio_map & BIT(up))
409				break;
410		}
411		dcbcfg->app[i].priority = up;
412
413		/* Get Selector from lower 2 bits, and convert to IEEE */
414		selector = (app->upper_oui_sel & I40E_CEE_APP_SELECTOR_MASK);
415		switch (selector) {
416		case I40E_CEE_APP_SEL_ETHTYPE:
417			dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE;
418			break;
419		case I40E_CEE_APP_SEL_TCPIP:
420			dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP;
421			break;
422		default:
423			/* Keep selector as it is for unknown types */
424			dcbcfg->app[i].selector = selector;
425		}
426
427		dcbcfg->app[i].protocolid = I40E_NTOHS(app->protocol);
428		/* Move to next app */
429		offset += sizeof(*app);
430	}
431}
432
433/**
434 * i40e_parse_cee_tlv
435 * @tlv: CEE DCBX TLV
436 * @dcbcfg: Local store to update DCBX config data
437 *
438 * Get the TLV subtype and send it to parsing function
439 * based on the subtype value
440 **/
441static void i40e_parse_cee_tlv(struct i40e_lldp_org_tlv *tlv,
442			       struct i40e_dcbx_config *dcbcfg)
443{
444	u16 len, tlvlen, sublen, typelength;
445	struct i40e_cee_feat_tlv *sub_tlv;
446	u8 subtype, feat_tlv_count = 0;
447	u32 ouisubtype;
448
449	ouisubtype = I40E_NTOHL(tlv->ouisubtype);
450	subtype = (u8)((ouisubtype & I40E_LLDP_TLV_SUBTYPE_MASK) >>
451		       I40E_LLDP_TLV_SUBTYPE_SHIFT);
452	/* Return if not CEE DCBX */
453	if (subtype != I40E_CEE_DCBX_TYPE)
454		return;
455
456	typelength = I40E_NTOHS(tlv->typelength);
457	tlvlen = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
458			I40E_LLDP_TLV_LEN_SHIFT);
459	len = sizeof(tlv->typelength) + sizeof(ouisubtype) +
460	      sizeof(struct i40e_cee_ctrl_tlv);
461	/* Return if no CEE DCBX Feature TLVs */
462	if (tlvlen <= len)
463		return;
464
465	sub_tlv = (struct i40e_cee_feat_tlv *)((char *)tlv + len);
466	while (feat_tlv_count < I40E_CEE_MAX_FEAT_TYPE) {
467		typelength = I40E_NTOHS(sub_tlv->hdr.typelen);
468		sublen = (u16)((typelength &
469				I40E_LLDP_TLV_LEN_MASK) >>
470				I40E_LLDP_TLV_LEN_SHIFT);
471		subtype = (u8)((typelength & I40E_LLDP_TLV_TYPE_MASK) >>
472				I40E_LLDP_TLV_TYPE_SHIFT);
473		switch (subtype) {
474		case I40E_CEE_SUBTYPE_PG_CFG:
475			i40e_parse_cee_pgcfg_tlv(sub_tlv, dcbcfg);
476			break;
477		case I40E_CEE_SUBTYPE_PFC_CFG:
478			i40e_parse_cee_pfccfg_tlv(sub_tlv, dcbcfg);
479			break;
480		case I40E_CEE_SUBTYPE_APP_PRI:
481			i40e_parse_cee_app_tlv(sub_tlv, dcbcfg);
482			break;
483		default:
484			return; /* Invalid Sub-type return */
485		}
486		feat_tlv_count++;
487		/* Move to next sub TLV */
488		sub_tlv = (struct i40e_cee_feat_tlv *)((char *)sub_tlv +
489						sizeof(sub_tlv->hdr.typelen) +
490						sublen);
491	}
492}
493
494/**
495 * i40e_parse_org_tlv
496 * @tlv: Organization specific TLV
497 * @dcbcfg: Local store to update ETS REC data
498 *
499 * Currently only IEEE 802.1Qaz TLV is supported, all others
500 * will be returned
501 **/
502static void i40e_parse_org_tlv(struct i40e_lldp_org_tlv *tlv,
503			       struct i40e_dcbx_config *dcbcfg)
504{
505	u32 ouisubtype;
506	u32 oui;
507
508	ouisubtype = I40E_NTOHL(tlv->ouisubtype);
509	oui = (u32)((ouisubtype & I40E_LLDP_TLV_OUI_MASK) >>
510		    I40E_LLDP_TLV_OUI_SHIFT);
511	switch (oui) {
512	case I40E_IEEE_8021QAZ_OUI:
513		i40e_parse_ieee_tlv(tlv, dcbcfg);
514		break;
515	case I40E_CEE_DCBX_OUI:
516		i40e_parse_cee_tlv(tlv, dcbcfg);
517		break;
518	default:
519		break;
520	}
521}
522
523/**
524 * i40e_lldp_to_dcb_config
525 * @lldpmib: LLDPDU to be parsed
526 * @dcbcfg: store for LLDPDU data
527 *
528 * Parse DCB configuration from the LLDPDU
529 **/
530enum i40e_status_code i40e_lldp_to_dcb_config(u8 *lldpmib,
531				    struct i40e_dcbx_config *dcbcfg)
532{
533	enum i40e_status_code ret = I40E_SUCCESS;
534	struct i40e_lldp_org_tlv *tlv;
535	u16 type;
536	u16 length;
537	u16 typelength;
538	u16 offset = 0;
539
540	if (!lldpmib || !dcbcfg)
541		return I40E_ERR_PARAM;
542
543	/* set to the start of LLDPDU */
544	lldpmib += I40E_LLDP_MIB_HLEN;
545	tlv = (struct i40e_lldp_org_tlv *)lldpmib;
546	while (1) {
547		typelength = I40E_NTOHS(tlv->typelength);
548		type = (u16)((typelength & I40E_LLDP_TLV_TYPE_MASK) >>
549			     I40E_LLDP_TLV_TYPE_SHIFT);
550		length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
551			       I40E_LLDP_TLV_LEN_SHIFT);
552		offset += sizeof(typelength) + length;
553
554		/* END TLV or beyond LLDPDU size */
555		if ((type == I40E_TLV_TYPE_END) || (offset > I40E_LLDPDU_SIZE))
556			break;
557
558		switch (type) {
559		case I40E_TLV_TYPE_ORG:
560			i40e_parse_org_tlv(tlv, dcbcfg);
561			break;
562		default:
563			break;
564		}
565
566		/* Move to next TLV */
567		tlv = (struct i40e_lldp_org_tlv *)((char *)tlv +
568						    sizeof(tlv->typelength) +
569						    length);
570	}
571
572	return ret;
573}
574
575/**
576 * i40e_aq_get_dcb_config
577 * @hw: pointer to the hw struct
578 * @mib_type: mib type for the query
579 * @bridgetype: bridge type for the query (remote)
580 * @dcbcfg: store for LLDPDU data
581 *
582 * Query DCB configuration from the Firmware
583 **/
584enum i40e_status_code i40e_aq_get_dcb_config(struct i40e_hw *hw, u8 mib_type,
585				   u8 bridgetype,
586				   struct i40e_dcbx_config *dcbcfg)
587{
588	enum i40e_status_code ret = I40E_SUCCESS;
589	struct i40e_virt_mem mem;
590	u8 *lldpmib;
591
592	/* Allocate the LLDPDU */
593	ret = i40e_allocate_virt_mem(hw, &mem, I40E_LLDPDU_SIZE);
594	if (ret)
595		return ret;
596
597	lldpmib = (u8 *)mem.va;
598	ret = i40e_aq_get_lldp_mib(hw, bridgetype, mib_type,
599				   (void *)lldpmib, I40E_LLDPDU_SIZE,
600				   NULL, NULL, NULL);
601	if (ret)
602		goto free_mem;
603
604	/* Parse LLDP MIB to get dcb configuration */
605	ret = i40e_lldp_to_dcb_config(lldpmib, dcbcfg);
606
607free_mem:
608	i40e_free_virt_mem(hw, &mem);
609	return ret;
610}
611
612/**
613 * i40e_cee_to_dcb_v1_config
614 * @cee_cfg: pointer to CEE v1 response configuration struct
615 * @dcbcfg: DCB configuration struct
616 *
617 * Convert CEE v1 configuration from firmware to DCB configuration
618 **/
619static void i40e_cee_to_dcb_v1_config(
620			struct i40e_aqc_get_cee_dcb_cfg_v1_resp *cee_cfg,
621			struct i40e_dcbx_config *dcbcfg)
622{
623	u16 status, tlv_status = LE16_TO_CPU(cee_cfg->tlv_status);
624	u16 app_prio = LE16_TO_CPU(cee_cfg->oper_app_prio);
625	u8 i, tc, err;
626
627	/* CEE PG data to ETS config */
628	dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc;
629
630	/* Note that the FW creates the oper_prio_tc nibbles reversed
631	 * from those in the CEE Priority Group sub-TLV.
632	 */
633	for (i = 0; i < 4; i++) {
634		tc = (u8)((cee_cfg->oper_prio_tc[i] &
635			 I40E_CEE_PGID_PRIO_0_MASK) >>
636			 I40E_CEE_PGID_PRIO_0_SHIFT);
637		dcbcfg->etscfg.prioritytable[i*2] =  tc;
638		tc = (u8)((cee_cfg->oper_prio_tc[i] &
639			 I40E_CEE_PGID_PRIO_1_MASK) >>
640			 I40E_CEE_PGID_PRIO_1_SHIFT);
641		dcbcfg->etscfg.prioritytable[i*2 + 1] = tc;
642	}
643
644	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
645		dcbcfg->etscfg.tcbwtable[i] = cee_cfg->oper_tc_bw[i];
646
647	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
648		if (dcbcfg->etscfg.prioritytable[i] == I40E_CEE_PGID_STRICT) {
649			/* Map it to next empty TC */
650			dcbcfg->etscfg.prioritytable[i] =
651						cee_cfg->oper_num_tc - 1;
652			dcbcfg->etscfg.tsatable[i] = I40E_IEEE_TSA_STRICT;
653		} else {
654			dcbcfg->etscfg.tsatable[i] = I40E_IEEE_TSA_ETS;
655		}
656	}
657
658	/* CEE PFC data to ETS config */
659	dcbcfg->pfc.pfcenable = cee_cfg->oper_pfc_en;
660	dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS;
661
662	status = (tlv_status & I40E_AQC_CEE_APP_STATUS_MASK) >>
663		  I40E_AQC_CEE_APP_STATUS_SHIFT;
664	err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
665	/* Add APPs if Error is False */
666	if (!err) {
667		/* CEE operating configuration supports FCoE/iSCSI/FIP only */
668		dcbcfg->numapps = I40E_CEE_OPER_MAX_APPS;
669
670		/* FCoE APP */
671		dcbcfg->app[0].priority =
672			(app_prio & I40E_AQC_CEE_APP_FCOE_MASK) >>
673			 I40E_AQC_CEE_APP_FCOE_SHIFT;
674		dcbcfg->app[0].selector = I40E_APP_SEL_ETHTYPE;
675		dcbcfg->app[0].protocolid = I40E_APP_PROTOID_FCOE;
676
677		/* iSCSI APP */
678		dcbcfg->app[1].priority =
679			(app_prio & I40E_AQC_CEE_APP_ISCSI_MASK) >>
680			 I40E_AQC_CEE_APP_ISCSI_SHIFT;
681		dcbcfg->app[1].selector = I40E_APP_SEL_TCPIP;
682		dcbcfg->app[1].protocolid = I40E_APP_PROTOID_ISCSI;
683
684		/* FIP APP */
685		dcbcfg->app[2].priority =
686			(app_prio & I40E_AQC_CEE_APP_FIP_MASK) >>
687			 I40E_AQC_CEE_APP_FIP_SHIFT;
688		dcbcfg->app[2].selector = I40E_APP_SEL_ETHTYPE;
689		dcbcfg->app[2].protocolid = I40E_APP_PROTOID_FIP;
690	}
691}
692
693/**
694 * i40e_cee_to_dcb_config
695 * @cee_cfg: pointer to CEE configuration struct
696 * @dcbcfg: DCB configuration struct
697 *
698 * Convert CEE configuration from firmware to DCB configuration
699 **/
700static void i40e_cee_to_dcb_config(
701				struct i40e_aqc_get_cee_dcb_cfg_resp *cee_cfg,
702				struct i40e_dcbx_config *dcbcfg)
703{
704	u32 status, tlv_status = LE32_TO_CPU(cee_cfg->tlv_status);
705	u16 app_prio = LE16_TO_CPU(cee_cfg->oper_app_prio);
706	u8 i, tc, err, sync, oper;
707
708	/* CEE PG data to ETS config */
709	dcbcfg->etscfg.maxtcs = cee_cfg->oper_num_tc;
710
711	/* Note that the FW creates the oper_prio_tc nibbles reversed
712	 * from those in the CEE Priority Group sub-TLV.
713	 */
714	for (i = 0; i < 4; i++) {
715		tc = (u8)((cee_cfg->oper_prio_tc[i] &
716			 I40E_CEE_PGID_PRIO_0_MASK) >>
717			 I40E_CEE_PGID_PRIO_0_SHIFT);
718		dcbcfg->etscfg.prioritytable[i*2] =  tc;
719		tc = (u8)((cee_cfg->oper_prio_tc[i] &
720			 I40E_CEE_PGID_PRIO_1_MASK) >>
721			 I40E_CEE_PGID_PRIO_1_SHIFT);
722		dcbcfg->etscfg.prioritytable[i*2 + 1] = tc;
723	}
724
725	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
726		dcbcfg->etscfg.tcbwtable[i] = cee_cfg->oper_tc_bw[i];
727
728	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) {
729		if (dcbcfg->etscfg.prioritytable[i] == I40E_CEE_PGID_STRICT) {
730			/* Map it to next empty TC */
731			dcbcfg->etscfg.prioritytable[i] =
732						cee_cfg->oper_num_tc - 1;
733			dcbcfg->etscfg.tsatable[i] = I40E_IEEE_TSA_STRICT;
734		} else {
735			dcbcfg->etscfg.tsatable[i] = I40E_IEEE_TSA_ETS;
736		}
737	}
738
739	/* CEE PFC data to ETS config */
740	dcbcfg->pfc.pfcenable = cee_cfg->oper_pfc_en;
741	dcbcfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS;
742
743	i = 0;
744	status = (tlv_status & I40E_AQC_CEE_FCOE_STATUS_MASK) >>
745		  I40E_AQC_CEE_FCOE_STATUS_SHIFT;
746	err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
747	sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
748	oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
749	/* Add FCoE APP if Error is False and Oper/Sync is True */
750	if (!err && sync && oper) {
751		/* FCoE APP */
752		dcbcfg->app[i].priority =
753			(app_prio & I40E_AQC_CEE_APP_FCOE_MASK) >>
754			 I40E_AQC_CEE_APP_FCOE_SHIFT;
755		dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE;
756		dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FCOE;
757		i++;
758	}
759
760	status = (tlv_status & I40E_AQC_CEE_ISCSI_STATUS_MASK) >>
761		  I40E_AQC_CEE_ISCSI_STATUS_SHIFT;
762	err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
763	sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
764	oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
765	/* Add iSCSI APP if Error is False and Oper/Sync is True */
766	if (!err && sync && oper) {
767		/* iSCSI APP */
768		dcbcfg->app[i].priority =
769			(app_prio & I40E_AQC_CEE_APP_ISCSI_MASK) >>
770			 I40E_AQC_CEE_APP_ISCSI_SHIFT;
771		dcbcfg->app[i].selector = I40E_APP_SEL_TCPIP;
772		dcbcfg->app[i].protocolid = I40E_APP_PROTOID_ISCSI;
773		i++;
774	}
775
776	status = (tlv_status & I40E_AQC_CEE_FIP_STATUS_MASK) >>
777		  I40E_AQC_CEE_FIP_STATUS_SHIFT;
778	err = (status & I40E_TLV_STATUS_ERR) ? 1 : 0;
779	sync = (status & I40E_TLV_STATUS_SYNC) ? 1 : 0;
780	oper = (status & I40E_TLV_STATUS_OPER) ? 1 : 0;
781	/* Add FIP APP if Error is False and Oper/Sync is True */
782	if (!err && sync && oper) {
783		/* FIP APP */
784		dcbcfg->app[i].priority =
785			(app_prio & I40E_AQC_CEE_APP_FIP_MASK) >>
786			 I40E_AQC_CEE_APP_FIP_SHIFT;
787		dcbcfg->app[i].selector = I40E_APP_SEL_ETHTYPE;
788		dcbcfg->app[i].protocolid = I40E_APP_PROTOID_FIP;
789		i++;
790	}
791	dcbcfg->numapps = i;
792}
793
794/**
795 * i40e_get_ieee_dcb_config
796 * @hw: pointer to the hw struct
797 *
798 * Get IEEE mode DCB configuration from the Firmware
799 **/
800static enum i40e_status_code i40e_get_ieee_dcb_config(struct i40e_hw *hw)
801{
802	enum i40e_status_code ret = I40E_SUCCESS;
803
804	/* IEEE mode */
805	hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_IEEE;
806	/* Get Local DCB Config */
807	ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0,
808				     &hw->local_dcbx_config);
809	if (ret)
810		goto out;
811
812	/* Get Remote DCB Config */
813	ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE,
814				     I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE,
815				     &hw->remote_dcbx_config);
816	/* Don't treat ENOENT as an error for Remote MIBs */
817	if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT)
818		ret = I40E_SUCCESS;
819
820out:
821	return ret;
822}
823
824/**
825 * i40e_get_dcb_config
826 * @hw: pointer to the hw struct
827 *
828 * Get DCB configuration from the Firmware
829 **/
830enum i40e_status_code i40e_get_dcb_config(struct i40e_hw *hw)
831{
832	enum i40e_status_code ret = I40E_SUCCESS;
833	struct i40e_aqc_get_cee_dcb_cfg_resp cee_cfg;
834	struct i40e_aqc_get_cee_dcb_cfg_v1_resp cee_v1_cfg;
835
836	/* If Firmware version < v4.33 on X710/XL710, IEEE only */
837	if ((hw->mac.type == I40E_MAC_XL710) &&
838	    (((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver < 33)) ||
839	      (hw->aq.fw_maj_ver < 4)))
840		return i40e_get_ieee_dcb_config(hw);
841
842	/* If Firmware version == v4.33 on X710/XL710, use old CEE struct */
843	if ((hw->mac.type == I40E_MAC_XL710) &&
844	    ((hw->aq.fw_maj_ver == 4) && (hw->aq.fw_min_ver == 33))) {
845		ret = i40e_aq_get_cee_dcb_config(hw, &cee_v1_cfg,
846						 sizeof(cee_v1_cfg), NULL);
847		if (ret == I40E_SUCCESS) {
848			/* CEE mode */
849			hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_CEE;
850			hw->local_dcbx_config.tlv_status =
851					LE16_TO_CPU(cee_v1_cfg.tlv_status);
852			i40e_cee_to_dcb_v1_config(&cee_v1_cfg,
853						  &hw->local_dcbx_config);
854		}
855	} else {
856		ret = i40e_aq_get_cee_dcb_config(hw, &cee_cfg,
857						 sizeof(cee_cfg), NULL);
858		if (ret == I40E_SUCCESS) {
859			/* CEE mode */
860			hw->local_dcbx_config.dcbx_mode = I40E_DCBX_MODE_CEE;
861			hw->local_dcbx_config.tlv_status =
862					LE32_TO_CPU(cee_cfg.tlv_status);
863			i40e_cee_to_dcb_config(&cee_cfg,
864					       &hw->local_dcbx_config);
865		}
866	}
867
868	/* CEE mode not enabled try querying IEEE data */
869	if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT)
870		return i40e_get_ieee_dcb_config(hw);
871
872	if (ret != I40E_SUCCESS)
873		goto out;
874
875	/* Get CEE DCB Desired Config */
876	ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_LOCAL, 0,
877				     &hw->desired_dcbx_config);
878	if (ret)
879		goto out;
880
881	/* Get Remote DCB Config */
882	ret = i40e_aq_get_dcb_config(hw, I40E_AQ_LLDP_MIB_REMOTE,
883			     I40E_AQ_LLDP_BRIDGE_TYPE_NEAREST_BRIDGE,
884			     &hw->remote_dcbx_config);
885	/* Don't treat ENOENT as an error for Remote MIBs */
886	if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT)
887		ret = I40E_SUCCESS;
888
889out:
890	return ret;
891}
892
893/**
894 * i40e_init_dcb
895 * @hw: pointer to the hw struct
896 * @enable_mib_change: enable mib change event
897 *
898 * Update DCB configuration from the Firmware
899 **/
900enum i40e_status_code i40e_init_dcb(struct i40e_hw *hw, bool enable_mib_change)
901{
902	enum i40e_status_code ret = I40E_SUCCESS;
903	struct i40e_lldp_variables lldp_cfg;
904	u8 adminstatus = 0;
905
906	if (!hw->func_caps.dcb)
907		return I40E_NOT_SUPPORTED;
908
909	/* Read LLDP NVM area */
910	if (hw->flags & I40E_HW_FLAG_FW_LLDP_PERSISTENT) {
911		u8 offset = 0;
912
913		if (hw->mac.type == I40E_MAC_XL710)
914			offset = I40E_LLDP_CURRENT_STATUS_XL710_OFFSET;
915		else if (hw->mac.type == I40E_MAC_X722)
916			offset = I40E_LLDP_CURRENT_STATUS_X722_OFFSET;
917		else
918			return I40E_NOT_SUPPORTED;
919
920		ret = i40e_read_nvm_module_data(hw,
921						I40E_SR_EMP_SR_SETTINGS_PTR,
922						offset,
923						I40E_LLDP_CURRENT_STATUS_OFFSET,
924						I40E_LLDP_CURRENT_STATUS_SIZE,
925						&lldp_cfg.adminstatus);
926	} else {
927		ret = i40e_read_lldp_cfg(hw, &lldp_cfg);
928	}
929	if (ret)
930		return I40E_ERR_NOT_READY;
931
932	/* Get the LLDP AdminStatus for the current port */
933	adminstatus = lldp_cfg.adminstatus >> (hw->port * 4);
934	adminstatus &= 0xF;
935
936	/* LLDP agent disabled */
937	if (!adminstatus) {
938		hw->dcbx_status = I40E_DCBX_STATUS_DISABLED;
939		return I40E_ERR_NOT_READY;
940	}
941
942	/* Get DCBX status */
943	ret = i40e_get_dcbx_status(hw, &hw->dcbx_status);
944	if (ret)
945		return ret;
946
947	/* Check the DCBX Status */
948	if (hw->dcbx_status == I40E_DCBX_STATUS_DONE ||
949	    hw->dcbx_status == I40E_DCBX_STATUS_IN_PROGRESS) {
950		/* Get current DCBX configuration */
951		ret = i40e_get_dcb_config(hw);
952		if (ret)
953			return ret;
954	} else if (hw->dcbx_status == I40E_DCBX_STATUS_DISABLED) {
955		return I40E_ERR_NOT_READY;
956	}
957
958	/* Configure the LLDP MIB change event */
959	if (enable_mib_change)
960		ret = i40e_aq_cfg_lldp_mib_change_event(hw, TRUE, NULL);
961
962	return ret;
963}
964
965/**
966 * i40e_get_fw_lldp_status
967 * @hw: pointer to the hw struct
968 * @lldp_status: pointer to the status enum
969 *
970 * Get status of FW Link Layer Discovery Protocol (LLDP) Agent.
971 * Status of agent is reported via @lldp_status parameter.
972 **/
973enum i40e_status_code
974i40e_get_fw_lldp_status(struct i40e_hw *hw,
975			enum i40e_get_fw_lldp_status_resp *lldp_status)
976{
977	enum i40e_status_code ret;
978	struct i40e_virt_mem mem;
979	u8 *lldpmib;
980
981	if (!lldp_status)
982		return I40E_ERR_PARAM;
983
984	/* Allocate buffer for the LLDPDU */
985	ret = i40e_allocate_virt_mem(hw, &mem, I40E_LLDPDU_SIZE);
986	if (ret)
987		return ret;
988
989	lldpmib = (u8 *)mem.va;
990	ret = i40e_aq_get_lldp_mib(hw, 0, 0, (void *)lldpmib,
991				   I40E_LLDPDU_SIZE, NULL, NULL, NULL);
992
993	if (ret == I40E_SUCCESS) {
994		*lldp_status = I40E_GET_FW_LLDP_STATUS_ENABLED;
995	} else if (hw->aq.asq_last_status == I40E_AQ_RC_ENOENT) {
996		/* MIB is not available yet but the agent is running */
997		*lldp_status = I40E_GET_FW_LLDP_STATUS_ENABLED;
998		ret = I40E_SUCCESS;
999	} else if (hw->aq.asq_last_status == I40E_AQ_RC_EPERM) {
1000		*lldp_status = I40E_GET_FW_LLDP_STATUS_DISABLED;
1001		ret = I40E_SUCCESS;
1002	}
1003
1004	i40e_free_virt_mem(hw, &mem);
1005	return ret;
1006}
1007
1008
1009/**
1010 * i40e_add_ieee_ets_tlv - Prepare ETS TLV in IEEE format
1011 * @tlv: Fill the ETS config data in IEEE format
1012 * @dcbcfg: Local store which holds the DCB Config
1013 *
1014 * Prepare IEEE 802.1Qaz ETS CFG TLV
1015 **/
1016static void i40e_add_ieee_ets_tlv(struct i40e_lldp_org_tlv *tlv,
1017				  struct i40e_dcbx_config *dcbcfg)
1018{
1019	u8 priority0, priority1, maxtcwilling = 0;
1020	struct i40e_dcb_ets_config *etscfg;
1021	u16 offset = 0, typelength, i;
1022	u8 *buf = tlv->tlvinfo;
1023	u32 ouisubtype;
1024
1025	typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) |
1026			I40E_IEEE_ETS_TLV_LENGTH);
1027	tlv->typelength = I40E_HTONS(typelength);
1028
1029	ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) |
1030			I40E_IEEE_SUBTYPE_ETS_CFG);
1031	tlv->ouisubtype = I40E_HTONL(ouisubtype);
1032
1033	/* First Octet post subtype
1034	 * --------------------------
1035	 * |will-|CBS  | Re-  | Max |
1036	 * |ing  |     |served| TCs |
1037	 * --------------------------
1038	 * |1bit | 1bit|3 bits|3bits|
1039	 */
1040	etscfg = &dcbcfg->etscfg;
1041	if (etscfg->willing)
1042		maxtcwilling = BIT(I40E_IEEE_ETS_WILLING_SHIFT);
1043	maxtcwilling |= etscfg->maxtcs & I40E_IEEE_ETS_MAXTC_MASK;
1044	buf[offset] = maxtcwilling;
1045
1046	/* Move offset to Priority Assignment Table */
1047	offset++;
1048
1049	/* Priority Assignment Table (4 octets)
1050	 * Octets:|    1    |    2    |    3    |    4    |
1051	 *        -----------------------------------------
1052	 *        |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7|
1053	 *        -----------------------------------------
1054	 *   Bits:|7  4|3  0|7  4|3  0|7  4|3  0|7  4|3  0|
1055	 *        -----------------------------------------
1056	 */
1057	for (i = 0; i < 4; i++) {
1058		priority0 = etscfg->prioritytable[i * 2] & 0xF;
1059		priority1 = etscfg->prioritytable[i * 2 + 1] & 0xF;
1060		buf[offset] = (priority0 << I40E_IEEE_ETS_PRIO_1_SHIFT) |
1061				priority1;
1062		offset++;
1063	}
1064
1065	/* TC Bandwidth Table (8 octets)
1066	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1067	 *        ---------------------------------
1068	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
1069	 *        ---------------------------------
1070	 */
1071	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
1072		buf[offset++] = etscfg->tcbwtable[i];
1073
1074	/* TSA Assignment Table (8 octets)
1075	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1076	 *        ---------------------------------
1077	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
1078	 *        ---------------------------------
1079	 */
1080	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
1081		buf[offset++] = etscfg->tsatable[i];
1082}
1083
1084/**
1085 * i40e_add_ieee_etsrec_tlv - Prepare ETS Recommended TLV in IEEE format
1086 * @tlv: Fill ETS Recommended TLV in IEEE format
1087 * @dcbcfg: Local store which holds the DCB Config
1088 *
1089 * Prepare IEEE 802.1Qaz ETS REC TLV
1090 **/
1091static void i40e_add_ieee_etsrec_tlv(struct i40e_lldp_org_tlv *tlv,
1092				     struct i40e_dcbx_config *dcbcfg)
1093{
1094	struct i40e_dcb_ets_config *etsrec;
1095	u16 offset = 0, typelength, i;
1096	u8 priority0, priority1;
1097	u8 *buf = tlv->tlvinfo;
1098	u32 ouisubtype;
1099
1100	typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) |
1101			I40E_IEEE_ETS_TLV_LENGTH);
1102	tlv->typelength = I40E_HTONS(typelength);
1103
1104	ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) |
1105			I40E_IEEE_SUBTYPE_ETS_REC);
1106	tlv->ouisubtype = I40E_HTONL(ouisubtype);
1107
1108	etsrec = &dcbcfg->etsrec;
1109	/* First Octet is reserved */
1110	/* Move offset to Priority Assignment Table */
1111	offset++;
1112
1113	/* Priority Assignment Table (4 octets)
1114	 * Octets:|    1    |    2    |    3    |    4    |
1115	 *        -----------------------------------------
1116	 *        |pri0|pri1|pri2|pri3|pri4|pri5|pri6|pri7|
1117	 *        -----------------------------------------
1118	 *   Bits:|7  4|3  0|7  4|3  0|7  4|3  0|7  4|3  0|
1119	 *        -----------------------------------------
1120	 */
1121	for (i = 0; i < 4; i++) {
1122		priority0 = etsrec->prioritytable[i * 2] & 0xF;
1123		priority1 = etsrec->prioritytable[i * 2 + 1] & 0xF;
1124		buf[offset] = (priority0 << I40E_IEEE_ETS_PRIO_1_SHIFT) |
1125				priority1;
1126		offset++;
1127	}
1128
1129	/* TC Bandwidth Table (8 octets)
1130	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1131	 *        ---------------------------------
1132	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
1133	 *        ---------------------------------
1134	 */
1135	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
1136		buf[offset++] = etsrec->tcbwtable[i];
1137
1138	/* TSA Assignment Table (8 octets)
1139	 * Octets:| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1140	 *        ---------------------------------
1141	 *        |tc0|tc1|tc2|tc3|tc4|tc5|tc6|tc7|
1142	 *        ---------------------------------
1143	 */
1144	for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++)
1145		buf[offset++] = etsrec->tsatable[i];
1146}
1147
1148 /**
1149 * i40e_add_ieee_pfc_tlv - Prepare PFC TLV in IEEE format
1150 * @tlv: Fill PFC TLV in IEEE format
1151 * @dcbcfg: Local store to get PFC CFG data
1152 *
1153 * Prepare IEEE 802.1Qaz PFC CFG TLV
1154 **/
1155static void i40e_add_ieee_pfc_tlv(struct i40e_lldp_org_tlv *tlv,
1156				  struct i40e_dcbx_config *dcbcfg)
1157{
1158	u8 *buf = tlv->tlvinfo;
1159	u32 ouisubtype;
1160	u16 typelength;
1161
1162	typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) |
1163			I40E_IEEE_PFC_TLV_LENGTH);
1164	tlv->typelength = I40E_HTONS(typelength);
1165
1166	ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) |
1167			I40E_IEEE_SUBTYPE_PFC_CFG);
1168	tlv->ouisubtype = I40E_HTONL(ouisubtype);
1169
1170	/* ----------------------------------------
1171	 * |will-|MBC  | Re-  | PFC |  PFC Enable  |
1172	 * |ing  |     |served| cap |              |
1173	 * -----------------------------------------
1174	 * |1bit | 1bit|2 bits|4bits| 1 octet      |
1175	 */
1176	if (dcbcfg->pfc.willing)
1177		buf[0] = BIT(I40E_IEEE_PFC_WILLING_SHIFT);
1178
1179	if (dcbcfg->pfc.mbc)
1180		buf[0] |= BIT(I40E_IEEE_PFC_MBC_SHIFT);
1181
1182	buf[0] |= dcbcfg->pfc.pfccap & 0xF;
1183	buf[1] = dcbcfg->pfc.pfcenable;
1184}
1185
1186/**
1187 * i40e_add_ieee_app_pri_tlv -  Prepare APP TLV in IEEE format
1188 * @tlv: Fill APP TLV in IEEE format
1189 * @dcbcfg: Local store to get APP CFG data
1190 *
1191 * Prepare IEEE 802.1Qaz APP CFG TLV
1192 **/
1193static void i40e_add_ieee_app_pri_tlv(struct i40e_lldp_org_tlv *tlv,
1194				      struct i40e_dcbx_config *dcbcfg)
1195{
1196	u16 typelength, length, offset = 0;
1197	u8 priority, selector, i = 0;
1198	u8 *buf = tlv->tlvinfo;
1199	u32 ouisubtype;
1200
1201	/* No APP TLVs then just return */
1202	if (dcbcfg->numapps == 0)
1203		return;
1204	ouisubtype = (u32)((I40E_IEEE_8021QAZ_OUI << I40E_LLDP_TLV_OUI_SHIFT) |
1205			I40E_IEEE_SUBTYPE_APP_PRI);
1206	tlv->ouisubtype = I40E_HTONL(ouisubtype);
1207
1208	/* Move offset to App Priority Table */
1209	offset++;
1210	/* Application Priority Table (3 octets)
1211	 * Octets:|         1          |    2    |    3    |
1212	 *        -----------------------------------------
1213	 *        |Priority|Rsrvd| Sel |    Protocol ID    |
1214	 *        -----------------------------------------
1215	 *   Bits:|23    21|20 19|18 16|15                0|
1216	 *        -----------------------------------------
1217	 */
1218	while (i < dcbcfg->numapps) {
1219		priority = dcbcfg->app[i].priority & 0x7;
1220		selector = dcbcfg->app[i].selector & 0x7;
1221		buf[offset] = (priority << I40E_IEEE_APP_PRIO_SHIFT) | selector;
1222		buf[offset + 1] = (dcbcfg->app[i].protocolid >> 0x8) & 0xFF;
1223		buf[offset + 2] =  dcbcfg->app[i].protocolid & 0xFF;
1224		/* Move to next app */
1225		offset += 3;
1226		i++;
1227		if (i >= I40E_DCBX_MAX_APPS)
1228			break;
1229	}
1230	/* length includes size of ouisubtype + 1 reserved + 3*numapps */
1231	length = sizeof(tlv->ouisubtype) + 1 + (i*3);
1232	typelength = (u16)((I40E_TLV_TYPE_ORG << I40E_LLDP_TLV_TYPE_SHIFT) |
1233		(length & 0x1FF));
1234	tlv->typelength = I40E_HTONS(typelength);
1235}
1236
1237 /**
1238 * i40e_add_dcb_tlv - Add all IEEE TLVs
1239 * @tlv: pointer to org tlv
1240 *
1241 * add tlv information
1242 **/
1243static void i40e_add_dcb_tlv(struct i40e_lldp_org_tlv *tlv,
1244			     struct i40e_dcbx_config *dcbcfg,
1245			     u16 tlvid)
1246{
1247	switch (tlvid) {
1248	case I40E_IEEE_TLV_ID_ETS_CFG:
1249		i40e_add_ieee_ets_tlv(tlv, dcbcfg);
1250		break;
1251	case I40E_IEEE_TLV_ID_ETS_REC:
1252		i40e_add_ieee_etsrec_tlv(tlv, dcbcfg);
1253		break;
1254	case I40E_IEEE_TLV_ID_PFC_CFG:
1255		i40e_add_ieee_pfc_tlv(tlv, dcbcfg);
1256		break;
1257	case I40E_IEEE_TLV_ID_APP_PRI:
1258		i40e_add_ieee_app_pri_tlv(tlv, dcbcfg);
1259		break;
1260	default:
1261		break;
1262	}
1263}
1264
1265 /**
1266 * i40e_set_dcb_config - Set the local LLDP MIB to FW
1267 * @hw: pointer to the hw struct
1268 *
1269 * Set DCB configuration to the Firmware
1270 **/
1271enum i40e_status_code i40e_set_dcb_config(struct i40e_hw *hw)
1272{
1273	enum i40e_status_code ret = I40E_SUCCESS;
1274	struct i40e_dcbx_config *dcbcfg;
1275	struct i40e_virt_mem mem;
1276	u8 mib_type, *lldpmib;
1277	u16 miblen;
1278
1279	/* update the hw local config */
1280	dcbcfg = &hw->local_dcbx_config;
1281	/* Allocate the LLDPDU */
1282	ret = i40e_allocate_virt_mem(hw, &mem, I40E_LLDPDU_SIZE);
1283	if (ret)
1284		return ret;
1285
1286	mib_type = SET_LOCAL_MIB_AC_TYPE_LOCAL_MIB;
1287	if (dcbcfg->app_mode == I40E_DCBX_APPS_NON_WILLING) {
1288		mib_type |= SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS <<
1289			    SET_LOCAL_MIB_AC_TYPE_NON_WILLING_APPS_SHIFT;
1290	}
1291	lldpmib = (u8 *)mem.va;
1292	ret = i40e_dcb_config_to_lldp(lldpmib, &miblen, dcbcfg);
1293	ret = i40e_aq_set_lldp_mib(hw, mib_type, (void *)lldpmib, miblen, NULL);
1294
1295	i40e_free_virt_mem(hw, &mem);
1296	return ret;
1297}
1298
1299/**
1300 * i40e_dcb_config_to_lldp - Convert Dcbconfig to MIB format
1301 * @lldpmib: pointer to mib to be output
1302 * @miblen: pointer to u16 for length of lldpmib
1303 * @dcbcfg: store for LLDPDU data
1304 *
1305 * send DCB configuration to FW
1306 **/
1307enum i40e_status_code i40e_dcb_config_to_lldp(u8 *lldpmib, u16 *miblen,
1308					      struct i40e_dcbx_config *dcbcfg)
1309{
1310	u16 length, offset = 0, tlvid = I40E_TLV_ID_START;
1311	enum i40e_status_code ret = I40E_SUCCESS;
1312	struct i40e_lldp_org_tlv *tlv;
1313	u16 typelength;
1314
1315	tlv = (struct i40e_lldp_org_tlv *)lldpmib;
1316	while (1) {
1317		i40e_add_dcb_tlv(tlv, dcbcfg, tlvid++);
1318		typelength = I40E_NTOHS(tlv->typelength);
1319		length = (u16)((typelength & I40E_LLDP_TLV_LEN_MASK) >>
1320				I40E_LLDP_TLV_LEN_SHIFT);
1321		if (length)
1322			offset += length + 2;
1323		/* END TLV or beyond LLDPDU size */
1324		if ((tlvid >= I40E_TLV_ID_END_OF_LLDPPDU) ||
1325		    (offset > I40E_LLDPDU_SIZE))
1326			break;
1327		/* Move to next TLV */
1328		if (length)
1329			tlv = (struct i40e_lldp_org_tlv *)((char *)tlv +
1330			      sizeof(tlv->typelength) + length);
1331	}
1332	*miblen = offset;
1333	return ret;
1334}
1335
1336
1337/**
1338 * _i40e_read_lldp_cfg - generic read of LLDP Configuration data from NVM
1339 * @hw: pointer to the HW structure
1340 * @lldp_cfg: pointer to hold lldp configuration variables
1341 * @module: address of the module pointer
1342 * @word_offset: offset of LLDP configuration
1343 *
1344 * Reads the LLDP configuration data from NVM using passed addresses
1345 **/
1346static enum i40e_status_code _i40e_read_lldp_cfg(struct i40e_hw *hw,
1347					  struct i40e_lldp_variables *lldp_cfg,
1348					  u8 module, u32 word_offset)
1349{
1350	u32 address, offset = (2 * word_offset);
1351	enum i40e_status_code ret;
1352	__le16 raw_mem;
1353	u16 mem;
1354
1355	ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
1356	if (ret != I40E_SUCCESS)
1357		return ret;
1358
1359	ret = i40e_aq_read_nvm(hw, 0x0, module * 2, sizeof(raw_mem), &raw_mem,
1360			       TRUE, NULL);
1361	i40e_release_nvm(hw);
1362	if (ret != I40E_SUCCESS)
1363		return ret;
1364
1365	mem = LE16_TO_CPU(raw_mem);
1366	/* Check if this pointer needs to be read in word size or 4K sector
1367	 * units.
1368	 */
1369	if (mem & I40E_PTR_TYPE)
1370		address = (0x7FFF & mem) * 4096;
1371	else
1372		address = (0x7FFF & mem) * 2;
1373
1374	ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
1375	if (ret != I40E_SUCCESS)
1376		goto err_lldp_cfg;
1377
1378	ret = i40e_aq_read_nvm(hw, module, offset, sizeof(raw_mem), &raw_mem,
1379			       TRUE, NULL);
1380	i40e_release_nvm(hw);
1381	if (ret != I40E_SUCCESS)
1382		return ret;
1383
1384	mem = LE16_TO_CPU(raw_mem);
1385	offset = mem + word_offset;
1386	offset *= 2;
1387
1388	ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
1389	if (ret != I40E_SUCCESS)
1390		goto err_lldp_cfg;
1391
1392	ret = i40e_aq_read_nvm(hw, 0, address + offset,
1393			       sizeof(struct i40e_lldp_variables), lldp_cfg,
1394			       TRUE, NULL);
1395	i40e_release_nvm(hw);
1396
1397err_lldp_cfg:
1398	return ret;
1399}
1400
1401/**
1402 * i40e_read_lldp_cfg - read LLDP Configuration data from NVM
1403 * @hw: pointer to the HW structure
1404 * @lldp_cfg: pointer to hold lldp configuration variables
1405 *
1406 * Reads the LLDP configuration data from NVM
1407 **/
1408enum i40e_status_code i40e_read_lldp_cfg(struct i40e_hw *hw,
1409					 struct i40e_lldp_variables *lldp_cfg)
1410{
1411	enum i40e_status_code ret = I40E_SUCCESS;
1412	u32 mem;
1413
1414	if (!lldp_cfg)
1415		return I40E_ERR_PARAM;
1416
1417	ret = i40e_acquire_nvm(hw, I40E_RESOURCE_READ);
1418	if (ret != I40E_SUCCESS)
1419		return ret;
1420
1421	ret = i40e_aq_read_nvm(hw, I40E_SR_NVM_CONTROL_WORD, 0, sizeof(mem),
1422			       &mem, TRUE, NULL);
1423	i40e_release_nvm(hw);
1424	if (ret != I40E_SUCCESS)
1425		return ret;
1426
1427	/* Read a bit that holds information whether we are running flat or
1428	 * structured NVM image. Flat image has LLDP configuration in shadow
1429	 * ram, so there is a need to pass different addresses for both cases.
1430	 */
1431	if (mem & I40E_SR_NVM_MAP_STRUCTURE_TYPE) {
1432		/* Flat NVM case */
1433		ret = _i40e_read_lldp_cfg(hw, lldp_cfg, I40E_SR_EMP_MODULE_PTR,
1434					  I40E_SR_LLDP_CFG_PTR);
1435	} else {
1436		/* Good old structured NVM image */
1437		ret = _i40e_read_lldp_cfg(hw, lldp_cfg, I40E_EMP_MODULE_PTR,
1438					  I40E_NVM_LLDP_CFG_PTR);
1439	}
1440
1441	return ret;
1442}
1443