1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) 2019-2021, Intel Corporation. */ |
3 | |
4 | #include "ice_common.h" |
5 | |
6 | /** |
7 | * ice_pkg_get_supported_vlan_mode - determine if DDP supports Double VLAN mode |
8 | * @hw: pointer to the HW struct |
9 | * @dvm: output variable to determine if DDP supports DVM(true) or SVM(false) |
10 | */ |
11 | static int |
12 | ice_pkg_get_supported_vlan_mode(struct ice_hw *hw, bool *dvm) |
13 | { |
14 | u16 meta_init_size = sizeof(struct ice_meta_init_section); |
15 | struct ice_meta_init_section *sect; |
16 | struct ice_buf_build *bld; |
17 | int status; |
18 | |
19 | /* if anything fails, we assume there is no DVM support */ |
20 | *dvm = false; |
21 | |
22 | bld = ice_pkg_buf_alloc_single_section(hw, |
23 | ICE_SID_RXPARSER_METADATA_INIT, |
24 | size: meta_init_size, section: (void **)§); |
25 | if (!bld) |
26 | return -ENOMEM; |
27 | |
28 | /* only need to read a single section */ |
29 | sect->count = cpu_to_le16(1); |
30 | sect->offset = cpu_to_le16(ICE_META_VLAN_MODE_ENTRY); |
31 | |
32 | status = ice_aq_upload_section(hw, |
33 | pkg_buf: (struct ice_buf_hdr *)ice_pkg_buf(bld), |
34 | ICE_PKG_BUF_SIZE, NULL); |
35 | if (!status) { |
36 | DECLARE_BITMAP(entry, ICE_META_INIT_BITS); |
37 | u32 arr[ICE_META_INIT_DW_CNT]; |
38 | u16 i; |
39 | |
40 | /* convert to host bitmap format */ |
41 | for (i = 0; i < ICE_META_INIT_DW_CNT; i++) |
42 | arr[i] = le32_to_cpu(sect->entry.bm[i]); |
43 | |
44 | bitmap_from_arr32(bitmap: entry, buf: arr, nbits: (u16)ICE_META_INIT_BITS); |
45 | |
46 | /* check if DVM is supported */ |
47 | *dvm = test_bit(ICE_META_VLAN_MODE_BIT, entry); |
48 | } |
49 | |
50 | ice_pkg_buf_free(hw, bld); |
51 | |
52 | return status; |
53 | } |
54 | |
55 | /** |
56 | * ice_aq_get_vlan_mode - get the VLAN mode of the device |
57 | * @hw: pointer to the HW structure |
58 | * @get_params: structure FW fills in based on the current VLAN mode config |
59 | * |
60 | * Get VLAN Mode Parameters (0x020D) |
61 | */ |
62 | static int |
63 | ice_aq_get_vlan_mode(struct ice_hw *hw, |
64 | struct ice_aqc_get_vlan_mode *get_params) |
65 | { |
66 | struct ice_aq_desc desc; |
67 | |
68 | if (!get_params) |
69 | return -EINVAL; |
70 | |
71 | ice_fill_dflt_direct_cmd_desc(desc: &desc, |
72 | opcode: ice_aqc_opc_get_vlan_mode_parameters); |
73 | |
74 | return ice_aq_send_cmd(hw, desc: &desc, buf: get_params, buf_size: sizeof(*get_params), |
75 | NULL); |
76 | } |
77 | |
78 | /** |
79 | * ice_aq_is_dvm_ena - query FW to check if double VLAN mode is enabled |
80 | * @hw: pointer to the HW structure |
81 | * |
82 | * Returns true if the hardware/firmware is configured in double VLAN mode, |
83 | * else return false signaling that the hardware/firmware is configured in |
84 | * single VLAN mode. |
85 | * |
86 | * Also, return false if this call fails for any reason (i.e. firmware doesn't |
87 | * support this AQ call). |
88 | */ |
89 | static bool ice_aq_is_dvm_ena(struct ice_hw *hw) |
90 | { |
91 | struct ice_aqc_get_vlan_mode get_params = { 0 }; |
92 | int status; |
93 | |
94 | status = ice_aq_get_vlan_mode(hw, get_params: &get_params); |
95 | if (status) { |
96 | ice_debug(hw, ICE_DBG_AQ, "Failed to get VLAN mode, status %d\n" , |
97 | status); |
98 | return false; |
99 | } |
100 | |
101 | return (get_params.vlan_mode & ICE_AQ_VLAN_MODE_DVM_ENA); |
102 | } |
103 | |
104 | /** |
105 | * ice_is_dvm_ena - check if double VLAN mode is enabled |
106 | * @hw: pointer to the HW structure |
107 | * |
108 | * The device is configured in single or double VLAN mode on initialization and |
109 | * this cannot be dynamically changed during runtime. Based on this there is no |
110 | * need to make an AQ call every time the driver needs to know the VLAN mode. |
111 | * Instead, use the cached VLAN mode. |
112 | */ |
113 | bool ice_is_dvm_ena(struct ice_hw *hw) |
114 | { |
115 | return hw->dvm_ena; |
116 | } |
117 | |
118 | /** |
119 | * ice_cache_vlan_mode - cache VLAN mode after DDP is downloaded |
120 | * @hw: pointer to the HW structure |
121 | * |
122 | * This is only called after downloading the DDP and after the global |
123 | * configuration lock has been released because all ports on a device need to |
124 | * cache the VLAN mode. |
125 | */ |
126 | static void ice_cache_vlan_mode(struct ice_hw *hw) |
127 | { |
128 | hw->dvm_ena = ice_aq_is_dvm_ena(hw) ? true : false; |
129 | } |
130 | |
131 | /** |
132 | * ice_pkg_supports_dvm - find out if DDP supports DVM |
133 | * @hw: pointer to the HW structure |
134 | */ |
135 | static bool ice_pkg_supports_dvm(struct ice_hw *hw) |
136 | { |
137 | bool pkg_supports_dvm; |
138 | int status; |
139 | |
140 | status = ice_pkg_get_supported_vlan_mode(hw, dvm: &pkg_supports_dvm); |
141 | if (status) { |
142 | ice_debug(hw, ICE_DBG_PKG, "Failed to get supported VLAN mode, status %d\n" , |
143 | status); |
144 | return false; |
145 | } |
146 | |
147 | return pkg_supports_dvm; |
148 | } |
149 | |
150 | /** |
151 | * ice_fw_supports_dvm - find out if FW supports DVM |
152 | * @hw: pointer to the HW structure |
153 | */ |
154 | static bool ice_fw_supports_dvm(struct ice_hw *hw) |
155 | { |
156 | struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 }; |
157 | int status; |
158 | |
159 | /* If firmware returns success, then it supports DVM, else it only |
160 | * supports SVM |
161 | */ |
162 | status = ice_aq_get_vlan_mode(hw, get_params: &get_vlan_mode); |
163 | if (status) { |
164 | ice_debug(hw, ICE_DBG_NVM, "Failed to get VLAN mode, status %d\n" , |
165 | status); |
166 | return false; |
167 | } |
168 | |
169 | return true; |
170 | } |
171 | |
172 | /** |
173 | * ice_is_dvm_supported - check if Double VLAN Mode is supported |
174 | * @hw: pointer to the hardware structure |
175 | * |
176 | * Returns true if Double VLAN Mode (DVM) is supported and false if only Single |
177 | * VLAN Mode (SVM) is supported. In order for DVM to be supported the DDP and |
178 | * firmware must support it, otherwise only SVM is supported. This function |
179 | * should only be called while the global config lock is held and after the |
180 | * package has been successfully downloaded. |
181 | */ |
182 | static bool ice_is_dvm_supported(struct ice_hw *hw) |
183 | { |
184 | if (!ice_pkg_supports_dvm(hw)) { |
185 | ice_debug(hw, ICE_DBG_PKG, "DDP doesn't support DVM\n" ); |
186 | return false; |
187 | } |
188 | |
189 | if (!ice_fw_supports_dvm(hw)) { |
190 | ice_debug(hw, ICE_DBG_PKG, "FW doesn't support DVM\n" ); |
191 | return false; |
192 | } |
193 | |
194 | return true; |
195 | } |
196 | |
197 | #define ICE_EXTERNAL_VLAN_ID_FV_IDX 11 |
198 | #define ICE_SW_LKUP_VLAN_LOC_LKUP_IDX 1 |
199 | #define ICE_SW_LKUP_VLAN_PKT_FLAGS_LKUP_IDX 2 |
200 | #define ICE_SW_LKUP_PROMISC_VLAN_LOC_LKUP_IDX 2 |
201 | #define ICE_PKT_FLAGS_0_TO_15_FV_IDX 1 |
202 | static struct ice_update_recipe_lkup_idx_params ice_dvm_dflt_recipes[] = { |
203 | { |
204 | /* Update recipe ICE_SW_LKUP_VLAN to filter based on the |
205 | * outer/single VLAN in DVM |
206 | */ |
207 | .rid = ICE_SW_LKUP_VLAN, |
208 | .fv_idx = ICE_EXTERNAL_VLAN_ID_FV_IDX, |
209 | .ignore_valid = true, |
210 | .mask = 0, |
211 | .mask_valid = false, /* use pre-existing mask */ |
212 | .lkup_idx = ICE_SW_LKUP_VLAN_LOC_LKUP_IDX, |
213 | }, |
214 | { |
215 | /* Update recipe ICE_SW_LKUP_VLAN to filter based on the VLAN |
216 | * packet flags to support VLAN filtering on multiple VLAN |
217 | * ethertypes (i.e. 0x8100 and 0x88a8) in DVM |
218 | */ |
219 | .rid = ICE_SW_LKUP_VLAN, |
220 | .fv_idx = ICE_PKT_FLAGS_0_TO_15_FV_IDX, |
221 | .ignore_valid = false, |
222 | .mask = ICE_PKT_VLAN_MASK, |
223 | .mask_valid = true, |
224 | .lkup_idx = ICE_SW_LKUP_VLAN_PKT_FLAGS_LKUP_IDX, |
225 | }, |
226 | { |
227 | /* Update recipe ICE_SW_LKUP_PROMISC_VLAN to filter based on the |
228 | * outer/single VLAN in DVM |
229 | */ |
230 | .rid = ICE_SW_LKUP_PROMISC_VLAN, |
231 | .fv_idx = ICE_EXTERNAL_VLAN_ID_FV_IDX, |
232 | .ignore_valid = true, |
233 | .mask = 0, |
234 | .mask_valid = false, /* use pre-existing mask */ |
235 | .lkup_idx = ICE_SW_LKUP_PROMISC_VLAN_LOC_LKUP_IDX, |
236 | }, |
237 | }; |
238 | |
239 | /** |
240 | * ice_dvm_update_dflt_recipes - update default switch recipes in DVM |
241 | * @hw: hardware structure used to update the recipes |
242 | */ |
243 | static int ice_dvm_update_dflt_recipes(struct ice_hw *hw) |
244 | { |
245 | unsigned long i; |
246 | |
247 | for (i = 0; i < ARRAY_SIZE(ice_dvm_dflt_recipes); i++) { |
248 | struct ice_update_recipe_lkup_idx_params *params; |
249 | int status; |
250 | |
251 | params = &ice_dvm_dflt_recipes[i]; |
252 | |
253 | status = ice_update_recipe_lkup_idx(hw, params); |
254 | if (status) { |
255 | ice_debug(hw, ICE_DBG_INIT, "Failed to update RID %d lkup_idx %d fv_idx %d mask_valid %s mask 0x%04x\n" , |
256 | params->rid, params->lkup_idx, params->fv_idx, |
257 | params->mask_valid ? "true" : "false" , |
258 | params->mask); |
259 | return status; |
260 | } |
261 | } |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | /** |
267 | * ice_aq_set_vlan_mode - set the VLAN mode of the device |
268 | * @hw: pointer to the HW structure |
269 | * @set_params: requested VLAN mode configuration |
270 | * |
271 | * Set VLAN Mode Parameters (0x020C) |
272 | */ |
273 | static int |
274 | ice_aq_set_vlan_mode(struct ice_hw *hw, |
275 | struct ice_aqc_set_vlan_mode *set_params) |
276 | { |
277 | u8 rdma_packet, mng_vlan_prot_id; |
278 | struct ice_aq_desc desc; |
279 | |
280 | if (!set_params) |
281 | return -EINVAL; |
282 | |
283 | if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX) |
284 | return -EINVAL; |
285 | |
286 | rdma_packet = set_params->rdma_packet; |
287 | if (rdma_packet != ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING && |
288 | rdma_packet != ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING) |
289 | return -EINVAL; |
290 | |
291 | mng_vlan_prot_id = set_params->mng_vlan_prot_id; |
292 | if (mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER && |
293 | mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER) |
294 | return -EINVAL; |
295 | |
296 | ice_fill_dflt_direct_cmd_desc(desc: &desc, |
297 | opcode: ice_aqc_opc_set_vlan_mode_parameters); |
298 | desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD); |
299 | |
300 | return ice_aq_send_cmd(hw, desc: &desc, buf: set_params, buf_size: sizeof(*set_params), |
301 | NULL); |
302 | } |
303 | |
304 | /** |
305 | * ice_set_dvm - sets up software and hardware for double VLAN mode |
306 | * @hw: pointer to the hardware structure |
307 | */ |
308 | static int ice_set_dvm(struct ice_hw *hw) |
309 | { |
310 | struct ice_aqc_set_vlan_mode params = { 0 }; |
311 | int status; |
312 | |
313 | params.l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_OUTER_CTAG; |
314 | params.rdma_packet = ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING; |
315 | params.mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER; |
316 | |
317 | status = ice_aq_set_vlan_mode(hw, set_params: ¶ms); |
318 | if (status) { |
319 | ice_debug(hw, ICE_DBG_INIT, "Failed to set double VLAN mode parameters, status %d\n" , |
320 | status); |
321 | return status; |
322 | } |
323 | |
324 | status = ice_dvm_update_dflt_recipes(hw); |
325 | if (status) { |
326 | ice_debug(hw, ICE_DBG_INIT, "Failed to update default recipes for double VLAN mode, status %d\n" , |
327 | status); |
328 | return status; |
329 | } |
330 | |
331 | status = ice_aq_set_port_params(pi: hw->port_info, double_vlan: true, NULL); |
332 | if (status) { |
333 | ice_debug(hw, ICE_DBG_INIT, "Failed to set port in double VLAN mode, status %d\n" , |
334 | status); |
335 | return status; |
336 | } |
337 | |
338 | status = ice_set_dvm_boost_entries(hw); |
339 | if (status) { |
340 | ice_debug(hw, ICE_DBG_INIT, "Failed to set boost TCAM entries for double VLAN mode, status %d\n" , |
341 | status); |
342 | return status; |
343 | } |
344 | |
345 | return 0; |
346 | } |
347 | |
348 | /** |
349 | * ice_set_svm - set single VLAN mode |
350 | * @hw: pointer to the HW structure |
351 | */ |
352 | static int ice_set_svm(struct ice_hw *hw) |
353 | { |
354 | struct ice_aqc_set_vlan_mode *set_params; |
355 | int status; |
356 | |
357 | status = ice_aq_set_port_params(pi: hw->port_info, double_vlan: false, NULL); |
358 | if (status) { |
359 | ice_debug(hw, ICE_DBG_INIT, "Failed to set port parameters for single VLAN mode\n" ); |
360 | return status; |
361 | } |
362 | |
363 | set_params = devm_kzalloc(dev: ice_hw_to_dev(hw), size: sizeof(*set_params), |
364 | GFP_KERNEL); |
365 | if (!set_params) |
366 | return -ENOMEM; |
367 | |
368 | /* default configuration for SVM configurations */ |
369 | set_params->l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG; |
370 | set_params->rdma_packet = ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING; |
371 | set_params->mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER; |
372 | |
373 | status = ice_aq_set_vlan_mode(hw, set_params); |
374 | if (status) |
375 | ice_debug(hw, ICE_DBG_INIT, "Failed to configure port in single VLAN mode\n" ); |
376 | |
377 | devm_kfree(dev: ice_hw_to_dev(hw), p: set_params); |
378 | return status; |
379 | } |
380 | |
381 | /** |
382 | * ice_set_vlan_mode |
383 | * @hw: pointer to the HW structure |
384 | */ |
385 | int ice_set_vlan_mode(struct ice_hw *hw) |
386 | { |
387 | if (!ice_is_dvm_supported(hw)) |
388 | return 0; |
389 | |
390 | if (!ice_set_dvm(hw)) |
391 | return 0; |
392 | |
393 | return ice_set_svm(hw); |
394 | } |
395 | |
396 | /** |
397 | * ice_print_dvm_not_supported - print if DDP and/or FW doesn't support DVM |
398 | * @hw: pointer to the HW structure |
399 | * |
400 | * The purpose of this function is to print that QinQ is not supported due to |
401 | * incompatibilty from the DDP and/or FW. This will give a hint to the user to |
402 | * update one and/or both components if they expect QinQ functionality. |
403 | */ |
404 | static void ice_print_dvm_not_supported(struct ice_hw *hw) |
405 | { |
406 | bool pkg_supports_dvm = ice_pkg_supports_dvm(hw); |
407 | bool fw_supports_dvm = ice_fw_supports_dvm(hw); |
408 | |
409 | if (!fw_supports_dvm && !pkg_supports_dvm) |
410 | dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your DDP package and NVM to versions that support QinQ.\n" ); |
411 | else if (!pkg_supports_dvm) |
412 | dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your DDP package to a version that supports QinQ.\n" ); |
413 | else if (!fw_supports_dvm) |
414 | dev_info(ice_hw_to_dev(hw), "QinQ functionality cannot be enabled on this device. Update your NVM to a version that supports QinQ.\n" ); |
415 | } |
416 | |
417 | /** |
418 | * ice_post_pkg_dwnld_vlan_mode_cfg - configure VLAN mode after DDP download |
419 | * @hw: pointer to the HW structure |
420 | * |
421 | * This function is meant to configure any VLAN mode specific functionality |
422 | * after the global configuration lock has been released and the DDP has been |
423 | * downloaded. |
424 | * |
425 | * Since only one PF downloads the DDP and configures the VLAN mode there needs |
426 | * to be a way to configure the other PFs after the DDP has been downloaded and |
427 | * the global configuration lock has been released. All such code should go in |
428 | * this function. |
429 | */ |
430 | void ice_post_pkg_dwnld_vlan_mode_cfg(struct ice_hw *hw) |
431 | { |
432 | ice_cache_vlan_mode(hw); |
433 | |
434 | if (ice_is_dvm_ena(hw)) |
435 | ice_change_proto_id_to_dvm(); |
436 | else |
437 | ice_print_dvm_not_supported(hw); |
438 | } |
439 | |