[golan] Add Connect-IB, ConnectX-4 and ConnectX-4 Lx (Infiniband) support
[ipxe.git] / src / drivers / infiniband / mlx_nodnic / src / mlx_device.c
1 /*
2 * Copyright (C) 2015 Mellanox Technologies Ltd.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 FILE_LICENCE ( GPL2_OR_LATER );
21
22 #include "../include/mlx_device.h"
23 #include "../include/mlx_cmd.h"
24 #include "../../mlx_utils/include/public/mlx_bail.h"
25 #include "../../mlx_utils/include/public/mlx_pci.h"
26 #include "../../mlx_utils/include/public/mlx_memory.h"
27 #include "../../mlx_utils/include/public/mlx_logging.h"
28
29 #define CHECK_BIT(field, offset) (((field) & ((mlx_uint32)1 << (offset))) != 0)
30
31 static
32 mlx_status
33 check_nodnic_interface_supported(
34 IN nodnic_device_priv* device_priv,
35 OUT mlx_boolean *out
36 )
37 {
38 mlx_status status = MLX_SUCCESS;
39 mlx_uint32 output = 0;
40 status = nodnic_cmd_read(device_priv, NODNIC_NIC_INTERFACE_SUPPORTED_OFFSET,
41 &output);
42 MLX_FATAL_CHECK_STATUS(status, read_error, "failed to read nic_interface_supported");
43 *out = CHECK_BIT(output, NODNIC_NIC_INTERFACE_SUPPORTED_BIT);
44 read_error:
45 return status;
46 }
47
48 static
49 mlx_status
50 wait_for_device_initialization(
51 IN nodnic_device_priv* device_priv
52 )
53 {
54 mlx_status status = MLX_SUCCESS;
55 mlx_uint8 try = 0;
56 mlx_uint32 buffer = 0;
57
58 #define CHECK_DEVICE_INIT_TRIES 10
59 for( ; try < CHECK_DEVICE_INIT_TRIES ; try++){
60 status = nodnic_cmd_read(device_priv, NODNIC_INITIALIZING_OFFSET, &buffer);
61 MLX_CHECK_STATUS(device_priv, status, read_error, "failed to read initializing");
62 if( !CHECK_BIT(buffer, NODNIC_INITIALIZING_BIT)){
63 goto init_done;
64 }
65 mlx_utils_delay_in_ms(100);
66 }
67 status = MLX_FAILED;
68 read_error:
69 init_done:
70 return status;
71 }
72
73 static
74 mlx_status
75 disable_nodnic_inteface(
76 IN nodnic_device_priv *device_priv
77 )
78 {
79 mlx_status status = MLX_SUCCESS;
80 mlx_uint32 buffer = 0;
81
82 buffer = (1 << NODNIC_DISABLE_INTERFACE_BIT);
83 status = nodnic_cmd_write(device_priv, NODNIC_CMDQ_PHY_ADDR_LOW_OFFSET, buffer);
84 MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface");
85
86 status = wait_for_device_initialization(device_priv);
87 MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device");
88 init_err:
89 write_err:
90 return status;
91 }
92 static
93 mlx_status
94 nodnic_device_start_nodnic(
95 IN nodnic_device_priv *device_priv
96 )
97 {
98 mlx_status status = MLX_SUCCESS;
99 mlx_uint32 buffer = 0;
100 mlx_boolean nodnic_supported = 0;
101
102 status = wait_for_device_initialization(device_priv);
103 MLX_FATAL_CHECK_STATUS(status, wait_for_fw_err, "failed to initialize device");
104
105 status = check_nodnic_interface_supported(device_priv, &nodnic_supported);
106 MLX_FATAL_CHECK_STATUS(status, read_err,"failed to check nic_interface_supported");
107
108 if( nodnic_supported == 0 ){
109 status = MLX_UNSUPPORTED;
110 goto nodnic_unsupported;
111 }
112 buffer = (1 << NODNIC_NIC_INTERFACE_BIT);
113 status = nodnic_cmd_write(device_priv, NODNIC_NIC_INTERFACE_OFFSET, buffer);
114 MLX_FATAL_CHECK_STATUS(status, write_err, "failed to write cmdq_phy_addr + nic_interface");
115
116 status = wait_for_device_initialization(device_priv);
117 MLX_FATAL_CHECK_STATUS(status, init_err, "failed to initialize device");
118 init_err:
119 read_err:
120 write_err:
121 nodnic_unsupported:
122 wait_for_fw_err:
123 return status;
124 }
125
126 static
127 mlx_status
128 nodnic_device_get_nodnic_data(
129 IN nodnic_device_priv *device_priv
130 )
131 {
132 mlx_status status = MLX_SUCCESS;
133 mlx_uint32 buffer = 0;
134
135 status = nodnic_cmd_read(device_priv, NODNIC_LOCATION_OFFSET, &device_priv->device_offset);
136 MLX_FATAL_CHECK_STATUS(status, nodnic_offset_read_err, "failed to read nodnic offset");
137
138 status = nodnic_cmd_read(device_priv,
139 device_priv->device_offset + NODNIC_REVISION_OFFSET, &buffer);
140 MLX_FATAL_CHECK_STATUS(status, nodnic_revision_read_err, "failed to read nodnic revision");
141
142 device_priv->nodnic_revision = (buffer >> 24) & 0xFF;
143 if( device_priv->nodnic_revision != NODIC_SUPPORTED_REVISION ){
144 MLX_DEBUG_ERROR(device_priv, "nodnic revision not supported\n");
145 status = MLX_UNSUPPORTED;
146 goto unsupported_revision;
147 }
148
149 status = nodnic_cmd_read(device_priv,
150 device_priv->device_offset + NODNIC_HARDWARE_FORMAT_OFFSET, &buffer);
151 MLX_FATAL_CHECK_STATUS(status, nodnic_hardware_format_read_err, "failed to read nodnic revision");
152 device_priv->hardware_format = (buffer >> 16) & 0xFF;
153
154 return status;
155
156 unsupported_revision:
157 nodnic_hardware_format_read_err:
158 nodnic_offset_read_err:
159 nodnic_revision_read_err:
160 disable_nodnic_inteface(device_priv);
161 return status;
162 }
163
164 mlx_status
165 nodnic_device_clear_int (
166 IN nodnic_device_priv *device_priv
167 )
168 {
169 mlx_status status = MLX_SUCCESS;
170 mlx_uint32 disable = 1;
171 #ifndef DEVICE_CX3
172 status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable);
173 MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit");
174 #else
175 mlx_utils *utils = device_priv->utils;
176 mlx_uint64 clear_int = (mlx_uint64)(device_priv->crspace_clear_int);
177 mlx_uint32 swapped = 0;
178
179 if (device_priv->device_cap.crspace_doorbells == 0) {
180 status = nodnic_cmd_write(device_priv, NODNIC_NIC_DISABLE_INT_OFFSET, disable);
181 MLX_CHECK_STATUS(device_priv, status, clear_int_done, "failed writing to disable_bit");
182 } else {
183 /* Write the new index and update FW that new data was submitted */
184 disable = 0x80000000;
185 mlx_memory_cpu_to_be32(utils, disable, &swapped);
186 mlx_pci_mem_write (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped);
187 mlx_pci_mem_read (utils, MlxPciWidthUint32, 0, clear_int, 1, &swapped);
188 }
189 #endif
190 clear_int_done:
191 return status;
192 }
193
194 mlx_status
195 nodnic_device_init(
196 IN nodnic_device_priv *device_priv
197 )
198 {
199 mlx_status status = MLX_SUCCESS;
200
201 if( device_priv == NULL ){
202 status = MLX_INVALID_PARAMETER;
203 goto parm_err;
204 }
205 status = nodnic_device_start_nodnic(device_priv);
206 MLX_FATAL_CHECK_STATUS(status, start_nodnic_err, "nodnic_device_start_nodnic failed");
207
208 status = nodnic_device_get_nodnic_data(device_priv);
209 MLX_FATAL_CHECK_STATUS(status, data_err, "nodnic_device_get_nodnic_data failed");
210 return status;
211 data_err:
212 start_nodnic_err:
213 parm_err:
214 return status;
215 }
216
217 mlx_status
218 nodnic_device_teardown(
219 IN nodnic_device_priv *device_priv
220 )
221 {
222 mlx_status status = MLX_SUCCESS;
223 status = disable_nodnic_inteface(device_priv);
224 MLX_FATAL_CHECK_STATUS(status, disable_failed, "failed to disable nodnic interface");
225 disable_failed:
226 return status;
227 }
228
229 mlx_status
230 nodnic_device_get_cap(
231 IN nodnic_device_priv *device_priv
232 )
233 {
234 mlx_status status = MLX_SUCCESS;
235 nodnic_device_capabilites *device_cap = NULL;
236 mlx_uint32 buffer = 0;
237 mlx_uint64 guid_l = 0;
238 mlx_uint64 guid_h = 0;
239 if( device_priv == NULL ){
240 status = MLX_INVALID_PARAMETER;
241 goto parm_err;
242 }
243
244 device_cap = &device_priv->device_cap;
245
246 //get device capabilities
247 status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x0, &buffer);
248 MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic first dword");
249
250 #define NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET 15
251 #define NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET 14
252 #define NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET 13
253 #define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET 8
254 #define NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK 0x7
255 #define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET 4
256 #define NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK 0xF
257 #define NODNIC_DEVICE_NUM_PORTS_OFFSET 0
258 device_cap->support_mac_filters = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_MAC_FILTERS_OFFSET);
259
260 device_cap->support_promisc_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_FILTER_OFFSET);
261
262 device_cap->support_promisc_multicast_filter = CHECK_BIT(buffer, NODNIC_DEVICE_SUPPORT_PROMISC_MULT_FILTER_OFFSET);
263
264 device_cap->log_working_buffer_size =
265 (buffer >> NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_OFFSET) & NODNIC_DEVICE_LOG_WORKING_BUFFER_SIZE_MASK;
266
267 device_cap->log_pkey_table_size =
268 (buffer >> NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_OFFSET) & NODNIC_DEVICE_LOG_PKEY_TABLE_SIZE_MASK;
269
270 device_cap->num_ports = CHECK_BIT(buffer, NODNIC_DEVICE_NUM_PORTS_OFFSET) + 1;
271
272 #ifdef DEVICE_CX3
273 #define NODNIC_DEVICE_CRSPACE_DB_OFFSET 12
274 device_cap->crspace_doorbells = CHECK_BIT(buffer, NODNIC_DEVICE_CRSPACE_DB_OFFSET);
275 #endif
276
277 status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x4, &buffer);
278 MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic second dword");
279
280 #define NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET 24
281 #define NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK 0x3F
282 #define NODNIC_DEVICE_PD_MASK 0xFFFFFF
283 device_cap->log_max_ring_size =
284 (buffer >> NODNIC_DEVICE_LOG_MAX_RING_SIZE_OFFSET) & NODNIC_DEVICE_LOG_MAX_RING_SIZE_MASK;
285
286 //get device magic numbers
287 device_priv->pd = buffer & NODNIC_DEVICE_PD_MASK;
288
289 status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x8, &buffer);
290 MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic third dword");
291 device_priv->lkey = buffer;
292
293 #ifdef DEVICE_CX3
294 if ( device_cap->crspace_doorbells ) {
295 status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x18, &buffer);
296 MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic_crspace_clear_int address");
297 device_priv->crspace_clear_int = device_priv->utils->config + buffer;
298 }
299 #endif
300
301 status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x10, (mlx_uint32*)&guid_h);
302 MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_h");
303 status = nodnic_cmd_read(device_priv, device_priv->device_offset + 0x14, (mlx_uint32*)&guid_l);
304 MLX_FATAL_CHECK_STATUS(status, read_err, "failed to read nodnic guid_l");
305 device_priv->device_guid = guid_l | (guid_h << 32);
306 read_err:
307 parm_err:
308 return status;
309 }
310
311 mlx_status
312 nodnic_device_get_fw_version(
313 IN nodnic_device_priv *device_priv,
314 OUT mlx_uint16 *fw_ver_minor,
315 OUT mlx_uint16 *fw_ver_sub_minor,
316 OUT mlx_uint16 *fw_ver_major
317 ){
318 mlx_status status = MLX_SUCCESS;
319 mlx_uint32 buffer = 0;
320
321 if( device_priv == NULL ){
322 status = MLX_INVALID_PARAMETER;
323 goto parm_err;
324 }
325
326 status = nodnic_cmd_read(device_priv, 0x0, &buffer);
327 MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision major and minor");
328
329 *fw_ver_minor = (mlx_uint16)(buffer >> 16);
330 *fw_ver_major = (mlx_uint16)buffer;
331
332 status = nodnic_cmd_read(device_priv, 0x4, &buffer);
333 MLX_CHECK_STATUS(device_priv, status, read_err, "failed to read fw revision sub minor");
334
335 *fw_ver_sub_minor = (mlx_uint16)buffer;
336 read_err:
337 parm_err:
338 return status;
339 }