Commit | Line | Data |
---|---|---|
fe56b9e6 YM |
1 | /* QLogic qed NIC Driver |
2 | * Copyright (c) 2015 QLogic Corporation | |
3 | * | |
4 | * This software is available under the terms of the GNU General Public License | |
5 | * (GPL) Version 2, available from the file COPYING in the main directory of | |
6 | * this source tree. | |
7 | */ | |
8 | ||
9 | #include <linux/types.h> | |
10 | #include <asm/byteorder.h> | |
11 | #include <linux/delay.h> | |
12 | #include <linux/errno.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/mutex.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/string.h> | |
17 | #include "qed.h" | |
18 | #include "qed_hsi.h" | |
19 | #include "qed_hw.h" | |
20 | #include "qed_mcp.h" | |
21 | #include "qed_reg_addr.h" | |
22 | #define CHIP_MCP_RESP_ITER_US 10 | |
23 | ||
24 | #define QED_DRV_MB_MAX_RETRIES (500 * 1000) /* Account for 5 sec */ | |
25 | #define QED_MCP_RESET_RETRIES (50 * 1000) /* Account for 500 msec */ | |
26 | ||
27 | #define DRV_INNER_WR(_p_hwfn, _p_ptt, _ptr, _offset, _val) \ | |
28 | qed_wr(_p_hwfn, _p_ptt, (_p_hwfn->mcp_info->_ptr + _offset), \ | |
29 | _val) | |
30 | ||
31 | #define DRV_INNER_RD(_p_hwfn, _p_ptt, _ptr, _offset) \ | |
32 | qed_rd(_p_hwfn, _p_ptt, (_p_hwfn->mcp_info->_ptr + _offset)) | |
33 | ||
34 | #define DRV_MB_WR(_p_hwfn, _p_ptt, _field, _val) \ | |
35 | DRV_INNER_WR(p_hwfn, _p_ptt, drv_mb_addr, \ | |
36 | offsetof(struct public_drv_mb, _field), _val) | |
37 | ||
38 | #define DRV_MB_RD(_p_hwfn, _p_ptt, _field) \ | |
39 | DRV_INNER_RD(_p_hwfn, _p_ptt, drv_mb_addr, \ | |
40 | offsetof(struct public_drv_mb, _field)) | |
41 | ||
42 | #define PDA_COMP (((FW_MAJOR_VERSION) + (FW_MINOR_VERSION << 8)) << \ | |
43 | DRV_ID_PDA_COMP_VER_SHIFT) | |
44 | ||
45 | #define MCP_BYTES_PER_MBIT_SHIFT 17 | |
46 | ||
47 | bool qed_mcp_is_init(struct qed_hwfn *p_hwfn) | |
48 | { | |
49 | if (!p_hwfn->mcp_info || !p_hwfn->mcp_info->public_base) | |
50 | return false; | |
51 | return true; | |
52 | } | |
53 | ||
54 | void qed_mcp_cmd_port_init(struct qed_hwfn *p_hwfn, | |
55 | struct qed_ptt *p_ptt) | |
56 | { | |
57 | u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base, | |
58 | PUBLIC_PORT); | |
59 | u32 mfw_mb_offsize = qed_rd(p_hwfn, p_ptt, addr); | |
60 | ||
61 | p_hwfn->mcp_info->port_addr = SECTION_ADDR(mfw_mb_offsize, | |
62 | MFW_PORT(p_hwfn)); | |
63 | DP_VERBOSE(p_hwfn, QED_MSG_SP, | |
64 | "port_addr = 0x%x, port_id 0x%02x\n", | |
65 | p_hwfn->mcp_info->port_addr, MFW_PORT(p_hwfn)); | |
66 | } | |
67 | ||
68 | void qed_mcp_read_mb(struct qed_hwfn *p_hwfn, | |
69 | struct qed_ptt *p_ptt) | |
70 | { | |
71 | u32 length = MFW_DRV_MSG_MAX_DWORDS(p_hwfn->mcp_info->mfw_mb_length); | |
72 | u32 tmp, i; | |
73 | ||
74 | if (!p_hwfn->mcp_info->public_base) | |
75 | return; | |
76 | ||
77 | for (i = 0; i < length; i++) { | |
78 | tmp = qed_rd(p_hwfn, p_ptt, | |
79 | p_hwfn->mcp_info->mfw_mb_addr + | |
80 | (i << 2) + sizeof(u32)); | |
81 | ||
82 | /* The MB data is actually BE; Need to force it to cpu */ | |
83 | ((u32 *)p_hwfn->mcp_info->mfw_mb_cur)[i] = | |
84 | be32_to_cpu((__force __be32)tmp); | |
85 | } | |
86 | } | |
87 | ||
88 | int qed_mcp_free(struct qed_hwfn *p_hwfn) | |
89 | { | |
90 | if (p_hwfn->mcp_info) { | |
91 | kfree(p_hwfn->mcp_info->mfw_mb_cur); | |
92 | kfree(p_hwfn->mcp_info->mfw_mb_shadow); | |
93 | } | |
94 | kfree(p_hwfn->mcp_info); | |
95 | ||
96 | return 0; | |
97 | } | |
98 | ||
99 | static int qed_load_mcp_offsets(struct qed_hwfn *p_hwfn, | |
100 | struct qed_ptt *p_ptt) | |
101 | { | |
102 | struct qed_mcp_info *p_info = p_hwfn->mcp_info; | |
103 | u32 drv_mb_offsize, mfw_mb_offsize; | |
104 | u32 mcp_pf_id = MCP_PF_ID(p_hwfn); | |
105 | ||
106 | p_info->public_base = qed_rd(p_hwfn, p_ptt, MISC_REG_SHARED_MEM_ADDR); | |
107 | if (!p_info->public_base) | |
108 | return 0; | |
109 | ||
110 | p_info->public_base |= GRCBASE_MCP; | |
111 | ||
112 | /* Calculate the driver and MFW mailbox address */ | |
113 | drv_mb_offsize = qed_rd(p_hwfn, p_ptt, | |
114 | SECTION_OFFSIZE_ADDR(p_info->public_base, | |
115 | PUBLIC_DRV_MB)); | |
116 | p_info->drv_mb_addr = SECTION_ADDR(drv_mb_offsize, mcp_pf_id); | |
117 | DP_VERBOSE(p_hwfn, QED_MSG_SP, | |
118 | "drv_mb_offsiz = 0x%x, drv_mb_addr = 0x%x mcp_pf_id = 0x%x\n", | |
119 | drv_mb_offsize, p_info->drv_mb_addr, mcp_pf_id); | |
120 | ||
121 | /* Set the MFW MB address */ | |
122 | mfw_mb_offsize = qed_rd(p_hwfn, p_ptt, | |
123 | SECTION_OFFSIZE_ADDR(p_info->public_base, | |
124 | PUBLIC_MFW_MB)); | |
125 | p_info->mfw_mb_addr = SECTION_ADDR(mfw_mb_offsize, mcp_pf_id); | |
126 | p_info->mfw_mb_length = (u16)qed_rd(p_hwfn, p_ptt, p_info->mfw_mb_addr); | |
127 | ||
128 | /* Get the current driver mailbox sequence before sending | |
129 | * the first command | |
130 | */ | |
131 | p_info->drv_mb_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_mb_header) & | |
132 | DRV_MSG_SEQ_NUMBER_MASK; | |
133 | ||
134 | /* Get current FW pulse sequence */ | |
135 | p_info->drv_pulse_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_pulse_mb) & | |
136 | DRV_PULSE_SEQ_MASK; | |
137 | ||
138 | p_info->mcp_hist = (u16)qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0); | |
139 | ||
140 | return 0; | |
141 | } | |
142 | ||
143 | int qed_mcp_cmd_init(struct qed_hwfn *p_hwfn, | |
144 | struct qed_ptt *p_ptt) | |
145 | { | |
146 | struct qed_mcp_info *p_info; | |
147 | u32 size; | |
148 | ||
149 | /* Allocate mcp_info structure */ | |
150 | p_hwfn->mcp_info = kzalloc(sizeof(*p_hwfn->mcp_info), GFP_ATOMIC); | |
151 | if (!p_hwfn->mcp_info) | |
152 | goto err; | |
153 | p_info = p_hwfn->mcp_info; | |
154 | ||
155 | if (qed_load_mcp_offsets(p_hwfn, p_ptt) != 0) { | |
156 | DP_NOTICE(p_hwfn, "MCP is not initialized\n"); | |
157 | /* Do not free mcp_info here, since public_base indicate that | |
158 | * the MCP is not initialized | |
159 | */ | |
160 | return 0; | |
161 | } | |
162 | ||
163 | size = MFW_DRV_MSG_MAX_DWORDS(p_info->mfw_mb_length) * sizeof(u32); | |
164 | p_info->mfw_mb_cur = kzalloc(size, GFP_ATOMIC); | |
165 | p_info->mfw_mb_shadow = | |
166 | kzalloc(sizeof(u32) * MFW_DRV_MSG_MAX_DWORDS( | |
167 | p_info->mfw_mb_length), GFP_ATOMIC); | |
168 | if (!p_info->mfw_mb_shadow || !p_info->mfw_mb_addr) | |
169 | goto err; | |
170 | ||
171 | /* Initialize the MFW mutex */ | |
172 | mutex_init(&p_info->mutex); | |
173 | ||
174 | return 0; | |
175 | ||
176 | err: | |
177 | DP_NOTICE(p_hwfn, "Failed to allocate mcp memory\n"); | |
178 | qed_mcp_free(p_hwfn); | |
179 | return -ENOMEM; | |
180 | } | |
181 | ||
182 | int qed_mcp_reset(struct qed_hwfn *p_hwfn, | |
183 | struct qed_ptt *p_ptt) | |
184 | { | |
185 | u32 seq = ++p_hwfn->mcp_info->drv_mb_seq; | |
186 | u8 delay = CHIP_MCP_RESP_ITER_US; | |
187 | u32 org_mcp_reset_seq, cnt = 0; | |
188 | int rc = 0; | |
189 | ||
190 | /* Set drv command along with the updated sequence */ | |
191 | org_mcp_reset_seq = qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0); | |
192 | DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, | |
193 | (DRV_MSG_CODE_MCP_RESET | seq)); | |
194 | ||
195 | do { | |
196 | /* Wait for MFW response */ | |
197 | udelay(delay); | |
198 | /* Give the FW up to 500 second (50*1000*10usec) */ | |
199 | } while ((org_mcp_reset_seq == qed_rd(p_hwfn, p_ptt, | |
200 | MISCS_REG_GENERIC_POR_0)) && | |
201 | (cnt++ < QED_MCP_RESET_RETRIES)); | |
202 | ||
203 | if (org_mcp_reset_seq != | |
204 | qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0)) { | |
205 | DP_VERBOSE(p_hwfn, QED_MSG_SP, | |
206 | "MCP was reset after %d usec\n", cnt * delay); | |
207 | } else { | |
208 | DP_ERR(p_hwfn, "Failed to reset MCP\n"); | |
209 | rc = -EAGAIN; | |
210 | } | |
211 | ||
212 | return rc; | |
213 | } | |
214 | ||
215 | static int qed_do_mcp_cmd(struct qed_hwfn *p_hwfn, | |
216 | struct qed_ptt *p_ptt, | |
217 | u32 cmd, | |
218 | u32 param, | |
219 | u32 *o_mcp_resp, | |
220 | u32 *o_mcp_param) | |
221 | { | |
222 | u8 delay = CHIP_MCP_RESP_ITER_US; | |
223 | u32 seq, cnt = 1, actual_mb_seq; | |
224 | int rc = 0; | |
225 | ||
226 | /* Get actual driver mailbox sequence */ | |
227 | actual_mb_seq = DRV_MB_RD(p_hwfn, p_ptt, drv_mb_header) & | |
228 | DRV_MSG_SEQ_NUMBER_MASK; | |
229 | ||
230 | /* Use MCP history register to check if MCP reset occurred between | |
231 | * init time and now. | |
232 | */ | |
233 | if (p_hwfn->mcp_info->mcp_hist != | |
234 | qed_rd(p_hwfn, p_ptt, MISCS_REG_GENERIC_POR_0)) { | |
235 | DP_VERBOSE(p_hwfn, QED_MSG_SP, "Rereading MCP offsets\n"); | |
236 | qed_load_mcp_offsets(p_hwfn, p_ptt); | |
237 | qed_mcp_cmd_port_init(p_hwfn, p_ptt); | |
238 | } | |
239 | seq = ++p_hwfn->mcp_info->drv_mb_seq; | |
240 | ||
241 | /* Set drv param */ | |
242 | DRV_MB_WR(p_hwfn, p_ptt, drv_mb_param, param); | |
243 | ||
244 | /* Set drv command along with the updated sequence */ | |
245 | DRV_MB_WR(p_hwfn, p_ptt, drv_mb_header, (cmd | seq)); | |
246 | ||
247 | DP_VERBOSE(p_hwfn, QED_MSG_SP, | |
248 | "wrote command (%x) to MFW MB param 0x%08x\n", | |
249 | (cmd | seq), param); | |
250 | ||
251 | do { | |
252 | /* Wait for MFW response */ | |
253 | udelay(delay); | |
254 | *o_mcp_resp = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_header); | |
255 | ||
256 | /* Give the FW up to 5 second (500*10ms) */ | |
257 | } while ((seq != (*o_mcp_resp & FW_MSG_SEQ_NUMBER_MASK)) && | |
258 | (cnt++ < QED_DRV_MB_MAX_RETRIES)); | |
259 | ||
260 | DP_VERBOSE(p_hwfn, QED_MSG_SP, | |
261 | "[after %d ms] read (%x) seq is (%x) from FW MB\n", | |
262 | cnt * delay, *o_mcp_resp, seq); | |
263 | ||
264 | /* Is this a reply to our command? */ | |
265 | if (seq == (*o_mcp_resp & FW_MSG_SEQ_NUMBER_MASK)) { | |
266 | *o_mcp_resp &= FW_MSG_CODE_MASK; | |
267 | /* Get the MCP param */ | |
268 | *o_mcp_param = DRV_MB_RD(p_hwfn, p_ptt, fw_mb_param); | |
269 | } else { | |
270 | /* FW BUG! */ | |
271 | DP_ERR(p_hwfn, "MFW failed to respond!\n"); | |
272 | *o_mcp_resp = 0; | |
273 | rc = -EAGAIN; | |
274 | } | |
275 | return rc; | |
276 | } | |
277 | ||
278 | int qed_mcp_cmd(struct qed_hwfn *p_hwfn, | |
279 | struct qed_ptt *p_ptt, | |
280 | u32 cmd, | |
281 | u32 param, | |
282 | u32 *o_mcp_resp, | |
283 | u32 *o_mcp_param) | |
284 | { | |
285 | int rc = 0; | |
286 | ||
287 | /* MCP not initialized */ | |
288 | if (!qed_mcp_is_init(p_hwfn)) { | |
289 | DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); | |
290 | return -EBUSY; | |
291 | } | |
292 | ||
293 | /* Lock Mutex to ensure only single thread is | |
294 | * accessing the MCP at one time | |
295 | */ | |
296 | mutex_lock(&p_hwfn->mcp_info->mutex); | |
297 | rc = qed_do_mcp_cmd(p_hwfn, p_ptt, cmd, param, | |
298 | o_mcp_resp, o_mcp_param); | |
299 | /* Release Mutex */ | |
300 | mutex_unlock(&p_hwfn->mcp_info->mutex); | |
301 | ||
302 | return rc; | |
303 | } | |
304 | ||
305 | static void qed_mcp_set_drv_ver(struct qed_dev *cdev, | |
306 | struct qed_hwfn *p_hwfn, | |
307 | struct qed_ptt *p_ptt) | |
308 | { | |
309 | u32 i; | |
310 | ||
311 | /* Copy version string to MCP */ | |
312 | for (i = 0; i < MCP_DRV_VER_STR_SIZE_DWORD; i++) | |
313 | DRV_MB_WR(p_hwfn, p_ptt, union_data.ver_str[i], | |
314 | *(u32 *)&cdev->ver_str[i * sizeof(u32)]); | |
315 | } | |
316 | ||
317 | int qed_mcp_load_req(struct qed_hwfn *p_hwfn, | |
318 | struct qed_ptt *p_ptt, | |
319 | u32 *p_load_code) | |
320 | { | |
321 | struct qed_dev *cdev = p_hwfn->cdev; | |
322 | u32 param; | |
323 | int rc; | |
324 | ||
325 | if (!qed_mcp_is_init(p_hwfn)) { | |
326 | DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); | |
327 | return -EBUSY; | |
328 | } | |
329 | ||
330 | /* Save driver's version to shmem */ | |
331 | qed_mcp_set_drv_ver(cdev, p_hwfn, p_ptt); | |
332 | ||
333 | DP_VERBOSE(p_hwfn, QED_MSG_SP, "fw_seq 0x%08x, drv_pulse 0x%x\n", | |
334 | p_hwfn->mcp_info->drv_mb_seq, | |
335 | p_hwfn->mcp_info->drv_pulse_seq); | |
336 | ||
337 | /* Load Request */ | |
338 | rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_LOAD_REQ, | |
339 | (PDA_COMP | DRV_ID_MCP_HSI_VER_CURRENT | | |
340 | cdev->drv_type), | |
341 | p_load_code, ¶m); | |
342 | ||
343 | /* if mcp fails to respond we must abort */ | |
344 | if (rc) { | |
345 | DP_ERR(p_hwfn, "MCP response failure, aborting\n"); | |
346 | return rc; | |
347 | } | |
348 | ||
349 | /* If MFW refused (e.g. other port is in diagnostic mode) we | |
350 | * must abort. This can happen in the following cases: | |
351 | * - Other port is in diagnostic mode | |
352 | * - Previously loaded function on the engine is not compliant with | |
353 | * the requester. | |
354 | * - MFW cannot cope with the requester's DRV_MFW_HSI_VERSION. | |
355 | * - | |
356 | */ | |
357 | if (!(*p_load_code) || | |
358 | ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_HSI) || | |
359 | ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_PDA) || | |
360 | ((*p_load_code) == FW_MSG_CODE_DRV_LOAD_REFUSED_DIAG)) { | |
361 | DP_ERR(p_hwfn, "MCP refused load request, aborting\n"); | |
362 | return -EBUSY; | |
363 | } | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | int qed_mcp_get_mfw_ver(struct qed_dev *cdev, | |
369 | u32 *p_mfw_ver) | |
370 | { | |
371 | struct qed_hwfn *p_hwfn = &cdev->hwfns[0]; | |
372 | struct qed_ptt *p_ptt; | |
373 | u32 global_offsize; | |
374 | ||
375 | p_ptt = qed_ptt_acquire(p_hwfn); | |
376 | if (!p_ptt) | |
377 | return -EBUSY; | |
378 | ||
379 | global_offsize = qed_rd(p_hwfn, p_ptt, | |
380 | SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info-> | |
381 | public_base, | |
382 | PUBLIC_GLOBAL)); | |
383 | *p_mfw_ver = qed_rd(p_hwfn, p_ptt, | |
384 | SECTION_ADDR(global_offsize, 0) + | |
385 | offsetof(struct public_global, mfw_ver)); | |
386 | ||
387 | qed_ptt_release(p_hwfn, p_ptt); | |
388 | ||
389 | return 0; | |
390 | } | |
391 | ||
392 | static u32 qed_mcp_get_shmem_func(struct qed_hwfn *p_hwfn, | |
393 | struct qed_ptt *p_ptt, | |
394 | struct public_func *p_data, | |
395 | int pfid) | |
396 | { | |
397 | u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base, | |
398 | PUBLIC_FUNC); | |
399 | u32 mfw_path_offsize = qed_rd(p_hwfn, p_ptt, addr); | |
400 | u32 func_addr = SECTION_ADDR(mfw_path_offsize, pfid); | |
401 | u32 i, size; | |
402 | ||
403 | memset(p_data, 0, sizeof(*p_data)); | |
404 | ||
405 | size = min_t(u32, sizeof(*p_data), | |
406 | QED_SECTION_SIZE(mfw_path_offsize)); | |
407 | for (i = 0; i < size / sizeof(u32); i++) | |
408 | ((u32 *)p_data)[i] = qed_rd(p_hwfn, p_ptt, | |
409 | func_addr + (i << 2)); | |
410 | ||
411 | return size; | |
412 | } | |
413 | ||
414 | static int | |
415 | qed_mcp_get_shmem_proto(struct qed_hwfn *p_hwfn, | |
416 | struct public_func *p_info, | |
417 | enum qed_pci_personality *p_proto) | |
418 | { | |
419 | int rc = 0; | |
420 | ||
421 | switch (p_info->config & FUNC_MF_CFG_PROTOCOL_MASK) { | |
422 | case FUNC_MF_CFG_PROTOCOL_ETHERNET: | |
423 | *p_proto = QED_PCI_ETH; | |
424 | break; | |
425 | default: | |
426 | rc = -EINVAL; | |
427 | } | |
428 | ||
429 | return rc; | |
430 | } | |
431 | ||
432 | int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, | |
433 | struct qed_ptt *p_ptt) | |
434 | { | |
435 | struct qed_mcp_function_info *info; | |
436 | struct public_func shmem_info; | |
437 | ||
438 | qed_mcp_get_shmem_func(p_hwfn, p_ptt, &shmem_info, | |
439 | MCP_PF_ID(p_hwfn)); | |
440 | info = &p_hwfn->mcp_info->func_info; | |
441 | ||
442 | info->pause_on_host = (shmem_info.config & | |
443 | FUNC_MF_CFG_PAUSE_ON_HOST_RING) ? 1 : 0; | |
444 | ||
445 | if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info, | |
446 | &info->protocol)) { | |
447 | DP_ERR(p_hwfn, "Unknown personality %08x\n", | |
448 | (u32)(shmem_info.config & FUNC_MF_CFG_PROTOCOL_MASK)); | |
449 | return -EINVAL; | |
450 | } | |
451 | ||
452 | if (p_hwfn->cdev->mf_mode != SF) { | |
453 | info->bandwidth_min = (shmem_info.config & | |
454 | FUNC_MF_CFG_MIN_BW_MASK) >> | |
455 | FUNC_MF_CFG_MIN_BW_SHIFT; | |
456 | if (info->bandwidth_min < 1 || info->bandwidth_min > 100) { | |
457 | DP_INFO(p_hwfn, | |
458 | "bandwidth minimum out of bounds [%02x]. Set to 1\n", | |
459 | info->bandwidth_min); | |
460 | info->bandwidth_min = 1; | |
461 | } | |
462 | ||
463 | info->bandwidth_max = (shmem_info.config & | |
464 | FUNC_MF_CFG_MAX_BW_MASK) >> | |
465 | FUNC_MF_CFG_MAX_BW_SHIFT; | |
466 | if (info->bandwidth_max < 1 || info->bandwidth_max > 100) { | |
467 | DP_INFO(p_hwfn, | |
468 | "bandwidth maximum out of bounds [%02x]. Set to 100\n", | |
469 | info->bandwidth_max); | |
470 | info->bandwidth_max = 100; | |
471 | } | |
472 | } | |
473 | ||
474 | if (shmem_info.mac_upper || shmem_info.mac_lower) { | |
475 | info->mac[0] = (u8)(shmem_info.mac_upper >> 8); | |
476 | info->mac[1] = (u8)(shmem_info.mac_upper); | |
477 | info->mac[2] = (u8)(shmem_info.mac_lower >> 24); | |
478 | info->mac[3] = (u8)(shmem_info.mac_lower >> 16); | |
479 | info->mac[4] = (u8)(shmem_info.mac_lower >> 8); | |
480 | info->mac[5] = (u8)(shmem_info.mac_lower); | |
481 | } else { | |
482 | DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n"); | |
483 | } | |
484 | ||
485 | info->wwn_port = (u64)shmem_info.fcoe_wwn_port_name_upper | | |
486 | (((u64)shmem_info.fcoe_wwn_port_name_lower) << 32); | |
487 | info->wwn_node = (u64)shmem_info.fcoe_wwn_node_name_upper | | |
488 | (((u64)shmem_info.fcoe_wwn_node_name_lower) << 32); | |
489 | ||
490 | info->ovlan = (u16)(shmem_info.ovlan_stag & FUNC_MF_CFG_OV_STAG_MASK); | |
491 | ||
492 | DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP), | |
493 | "Read configuration from shmem: pause_on_host %02x protocol %02x BW [%02x - %02x] MAC %02x:%02x:%02x:%02x:%02x:%02x wwn port %llx node %llx ovlan %04x\n", | |
494 | info->pause_on_host, info->protocol, | |
495 | info->bandwidth_min, info->bandwidth_max, | |
496 | info->mac[0], info->mac[1], info->mac[2], | |
497 | info->mac[3], info->mac[4], info->mac[5], | |
498 | info->wwn_port, info->wwn_node, info->ovlan); | |
499 | ||
500 | return 0; | |
501 | } | |
502 | ||
503 | int qed_mcp_drain(struct qed_hwfn *p_hwfn, | |
504 | struct qed_ptt *p_ptt) | |
505 | { | |
506 | u32 resp = 0, param = 0; | |
507 | int rc; | |
508 | ||
509 | rc = qed_mcp_cmd(p_hwfn, p_ptt, | |
510 | DRV_MSG_CODE_NIG_DRAIN, 100, | |
511 | &resp, ¶m); | |
512 | ||
513 | /* Wait for the drain to complete before returning */ | |
514 | msleep(120); | |
515 | ||
516 | return rc; | |
517 | } | |
518 | ||
cee4d264 MC |
519 | int qed_mcp_get_flash_size(struct qed_hwfn *p_hwfn, |
520 | struct qed_ptt *p_ptt, | |
521 | u32 *p_flash_size) | |
522 | { | |
523 | u32 flash_size; | |
524 | ||
525 | flash_size = qed_rd(p_hwfn, p_ptt, MCP_REG_NVM_CFG4); | |
526 | flash_size = (flash_size & MCP_REG_NVM_CFG4_FLASH_SIZE) >> | |
527 | MCP_REG_NVM_CFG4_FLASH_SIZE_SHIFT; | |
528 | flash_size = (1 << (flash_size + MCP_BYTES_PER_MBIT_SHIFT)); | |
529 | ||
530 | *p_flash_size = flash_size; | |
531 | ||
532 | return 0; | |
533 | } | |
534 | ||
fe56b9e6 YM |
535 | int |
536 | qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, | |
537 | struct qed_ptt *p_ptt, | |
538 | struct qed_mcp_drv_version *p_ver) | |
539 | { | |
540 | int rc = 0; | |
541 | u32 param = 0, reply = 0, i; | |
542 | ||
543 | if (!qed_mcp_is_init(p_hwfn)) { | |
544 | DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); | |
545 | return -EBUSY; | |
546 | } | |
547 | ||
548 | DRV_MB_WR(p_hwfn, p_ptt, union_data.drv_version.version, | |
549 | p_ver->version); | |
550 | /* Copy version string to shmem */ | |
551 | for (i = 0; i < (MCP_DRV_VER_STR_SIZE - 4) / 4; i++) { | |
552 | DRV_MB_WR(p_hwfn, p_ptt, | |
553 | union_data.drv_version.name[i * sizeof(u32)], | |
554 | *(u32 *)&p_ver->name[i * sizeof(u32)]); | |
555 | } | |
556 | ||
557 | rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_VERSION, 0, &reply, | |
558 | ¶m); | |
559 | if (rc) { | |
560 | DP_ERR(p_hwfn, "MCP response failure, aborting\n"); | |
561 | return rc; | |
562 | } | |
563 | ||
564 | return 0; | |
565 | } |