1/* SPDX-License-Identifier: BSD-3-Clause */
2/*  Copyright (c) 2024, Intel Corporation
3 *  All rights reserved.
4 *
5 *  Redistribution and use in source and binary forms, with or without
6 *  modification, are permitted provided that the following conditions are met:
7 *
8 *   1. Redistributions of source code must retain the above copyright notice,
9 *      this list of conditions and the following disclaimer.
10 *
11 *   2. Redistributions in binary form must reproduce the above copyright
12 *      notice, this list of conditions and the following disclaimer in the
13 *      documentation and/or other materials provided with the distribution.
14 *
15 *   3. Neither the name of the Intel Corporation nor the names of its
16 *      contributors may be used to endorse or promote products derived from
17 *      this software without specific prior written permission.
18 *
19 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 *  POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "ice_common.h"
33
34#include "ice_ddp_common.h"
35/**
36 * ice_pkg_get_supported_vlan_mode - chk if DDP supports Double VLAN mode (DVM)
37 * @hw: pointer to the HW struct
38 * @dvm: output variable to determine if DDP supports DVM(true) or SVM(false)
39 */
40static enum ice_status
41ice_pkg_get_supported_vlan_mode(struct ice_hw *hw, bool *dvm)
42{
43	u16 meta_init_size = sizeof(struct ice_meta_init_section);
44	struct ice_meta_init_section *sect;
45	struct ice_buf_build *bld;
46	enum ice_status status;
47
48	/* if anything fails, we assume there is no DVM support */
49	*dvm = false;
50
51	bld = ice_pkg_buf_alloc_single_section(hw,
52					       ICE_SID_RXPARSER_METADATA_INIT,
53					       meta_init_size, (void **)&sect);
54	if (!bld)
55		return ICE_ERR_NO_MEMORY;
56
57	/* only need to read a single section */
58	sect->count = CPU_TO_LE16(1);
59	sect->offset = CPU_TO_LE16(ICE_META_VLAN_MODE_ENTRY);
60
61	status = ice_aq_upload_section(hw,
62				       (struct ice_buf_hdr *)ice_pkg_buf(bld),
63				       ICE_PKG_BUF_SIZE, NULL);
64	if (!status) {
65		ice_declare_bitmap(entry, ICE_META_INIT_BITS);
66		u32 arr[ICE_META_INIT_DW_CNT];
67		u16 i;
68
69		/* convert to host bitmap format */
70		for (i = 0; i < ICE_META_INIT_DW_CNT; i++)
71			arr[i] = LE32_TO_CPU(sect->entry[0].bm[i]);
72
73		ice_bitmap_from_array32(entry, arr, (u16)ICE_META_INIT_BITS);
74
75		/* check if DVM is supported */
76		*dvm = ice_is_bit_set(entry, ICE_META_VLAN_MODE_BIT);
77	}
78
79	ice_pkg_buf_free(hw, bld);
80
81	return status;
82}
83
84/**
85 * ice_aq_get_vlan_mode - get the VLAN mode of the device
86 * @hw: pointer to the HW structure
87 * @get_params: structure FW fills in based on the current VLAN mode config
88 *
89 * Get VLAN Mode Parameters (0x020D)
90 */
91static enum ice_status
92ice_aq_get_vlan_mode(struct ice_hw *hw,
93		     struct ice_aqc_get_vlan_mode *get_params)
94{
95	struct ice_aq_desc desc;
96
97	if (!get_params)
98		return ICE_ERR_PARAM;
99
100	ice_fill_dflt_direct_cmd_desc(&desc,
101				      ice_aqc_opc_get_vlan_mode_parameters);
102
103	return ice_aq_send_cmd(hw, &desc, get_params, sizeof(*get_params),
104			       NULL);
105}
106
107/**
108 * ice_aq_is_dvm_ena - query FW to check if double VLAN mode is enabled
109 * @hw: pointer to the HW structure
110 *
111 * Returns true if the hardware/firmware is configured in double VLAN mode,
112 * else return false signaling that the hardware/firmware is configured in
113 * single VLAN mode.
114 *
115 * Also, return false if this call fails for any reason (i.e. firmware doesn't
116 * support this AQ call).
117 */
118static bool ice_aq_is_dvm_ena(struct ice_hw *hw)
119{
120	struct ice_aqc_get_vlan_mode get_params = { 0 };
121	enum ice_status status;
122
123	status = ice_aq_get_vlan_mode(hw, &get_params);
124	if (status) {
125		ice_debug(hw, ICE_DBG_AQ, "Failed to get VLAN mode, status %d\n",
126			  status);
127		return false;
128	}
129
130	return (get_params.vlan_mode & ICE_AQ_VLAN_MODE_DVM_ENA);
131}
132
133/**
134 * ice_is_dvm_ena - check if double VLAN mode is enabled
135 * @hw: pointer to the HW structure
136 *
137 * The device is configured in single or double VLAN mode on initialization and
138 * this cannot be dynamically changed during runtime. Based on this there is no
139 * need to make an AQ call every time the driver needs to know the VLAN mode.
140 * Instead, use the cached VLAN mode.
141 */
142bool ice_is_dvm_ena(struct ice_hw *hw)
143{
144	return hw->dvm_ena;
145}
146
147/**
148 * ice_cache_vlan_mode - cache VLAN mode after DDP is downloaded
149 * @hw: pointer to the HW structure
150 *
151 * This is only called after downloading the DDP and after the global
152 * configuration lock has been released because all ports on a device need to
153 * cache the VLAN mode.
154 */
155static void ice_cache_vlan_mode(struct ice_hw *hw)
156{
157	hw->dvm_ena = ice_aq_is_dvm_ena(hw) ? true : false;
158}
159
160/**
161 * ice_pkg_supports_dvm - find out if DDP supports DVM
162 * @hw: pointer to the HW structure
163 */
164static bool ice_pkg_supports_dvm(struct ice_hw *hw)
165{
166	enum ice_status status;
167	bool pkg_supports_dvm;
168
169	status = ice_pkg_get_supported_vlan_mode(hw, &pkg_supports_dvm);
170	if (status) {
171		ice_debug(hw, ICE_DBG_PKG, "Failed to get supported VLAN mode, status %d\n",
172			  status);
173		return false;
174	}
175
176	return pkg_supports_dvm;
177}
178
179/**
180 * ice_fw_supports_dvm - find out if FW supports DVM
181 * @hw: pointer to the HW structure
182 */
183static bool ice_fw_supports_dvm(struct ice_hw *hw)
184{
185	struct ice_aqc_get_vlan_mode get_vlan_mode = { 0 };
186	enum ice_status status;
187
188	/* If firmware returns success, then it supports DVM, else it only
189	 * supports SVM
190	 */
191	status = ice_aq_get_vlan_mode(hw, &get_vlan_mode);
192	if (status) {
193		ice_debug(hw, ICE_DBG_NVM, "Failed to get VLAN mode, status %d\n",
194			  status);
195		return false;
196	}
197
198	return true;
199}
200
201/**
202 * ice_is_dvm_supported - check if Double VLAN Mode is supported
203 * @hw: pointer to the hardware structure
204 *
205 * Returns true if Double VLAN Mode (DVM) is supported and false if only Single
206 * VLAN Mode (SVM) is supported. In order for DVM to be supported the DDP and
207 * firmware must support it, otherwise only SVM is supported. This function
208 * should only be called while the global config lock is held and after the
209 * package has been successfully downloaded.
210 */
211static bool ice_is_dvm_supported(struct ice_hw *hw)
212{
213	if (!ice_pkg_supports_dvm(hw)) {
214		ice_debug(hw, ICE_DBG_PKG, "DDP doesn't support DVM\n");
215		return false;
216	}
217
218	if (!ice_fw_supports_dvm(hw)) {
219		ice_debug(hw, ICE_DBG_PKG, "FW doesn't support DVM\n");
220		return false;
221	}
222
223	return true;
224}
225
226/**
227 * ice_aq_set_vlan_mode - set the VLAN mode of the device
228 * @hw: pointer to the HW structure
229 * @set_params: requested VLAN mode configuration
230 *
231 * Set VLAN Mode Parameters (0x020C)
232 */
233static enum ice_status
234ice_aq_set_vlan_mode(struct ice_hw *hw,
235		     struct ice_aqc_set_vlan_mode *set_params)
236{
237	u8 rdma_packet, mng_vlan_prot_id;
238	struct ice_aq_desc desc;
239
240	if (!set_params)
241		return ICE_ERR_PARAM;
242
243	if (set_params->l2tag_prio_tagging > ICE_AQ_VLAN_PRIO_TAG_MAX)
244		return ICE_ERR_PARAM;
245
246	rdma_packet = set_params->rdma_packet;
247	if (rdma_packet != ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING &&
248	    rdma_packet != ICE_AQ_DVM_VLAN_RDMA_PKT_FLAG_SETTING)
249		return ICE_ERR_PARAM;
250
251	mng_vlan_prot_id = set_params->mng_vlan_prot_id;
252	if (mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_OUTER &&
253	    mng_vlan_prot_id != ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER)
254		return ICE_ERR_PARAM;
255
256	ice_fill_dflt_direct_cmd_desc(&desc,
257				      ice_aqc_opc_set_vlan_mode_parameters);
258	desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
259
260	return ice_aq_send_cmd(hw, &desc, set_params, sizeof(*set_params),
261			       NULL);
262}
263
264/**
265 * ice_set_svm - set single VLAN mode
266 * @hw: pointer to the HW structure
267 */
268static enum ice_status ice_set_svm(struct ice_hw *hw)
269{
270	struct ice_aqc_set_vlan_mode *set_params;
271	enum ice_status status;
272
273	status = ice_aq_set_port_params(hw->port_info, 0, false, false, false, NULL);
274	if (status) {
275		ice_debug(hw, ICE_DBG_INIT, "Failed to set port parameters for single VLAN mode\n");
276		return status;
277	}
278
279	set_params = (struct ice_aqc_set_vlan_mode *)
280		ice_malloc(hw, sizeof(*set_params));
281	if (!set_params)
282		return ICE_ERR_NO_MEMORY;
283
284	/* default configuration for SVM configurations */
285	set_params->l2tag_prio_tagging = ICE_AQ_VLAN_PRIO_TAG_INNER_CTAG;
286	set_params->rdma_packet = ICE_AQ_SVM_VLAN_RDMA_PKT_FLAG_SETTING;
287	set_params->mng_vlan_prot_id = ICE_AQ_VLAN_MNG_PROTOCOL_ID_INNER;
288
289	status = ice_aq_set_vlan_mode(hw, set_params);
290	if (status)
291		ice_debug(hw, ICE_DBG_INIT, "Failed to configure port in single VLAN mode\n");
292
293	ice_free(hw, set_params);
294	return status;
295}
296
297/**
298 * ice_set_vlan_mode
299 * @hw: pointer to the HW structure
300 */
301enum ice_status ice_set_vlan_mode(struct ice_hw *hw)
302{
303	if (!ice_is_dvm_supported(hw))
304		return ICE_SUCCESS;
305
306	return ice_set_svm(hw);
307}
308
309/**
310 * ice_post_pkg_dwnld_vlan_mode_cfg - configure VLAN mode after DDP download
311 * @hw: pointer to the HW structure
312 *
313 * This function is meant to configure any VLAN mode specific functionality
314 * after the global configuration lock has been released and the DDP has been
315 * downloaded.
316 *
317 * Since only one PF downloads the DDP and configures the VLAN mode there needs
318 * to be a way to configure the other PFs after the DDP has been downloaded and
319 * the global configuration lock has been released. All such code should go in
320 * this function.
321 */
322void ice_post_pkg_dwnld_vlan_mode_cfg(struct ice_hw *hw)
323{
324	ice_cache_vlan_mode(hw);
325}
326