]>
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 | ||
cc875c2e YM |
368 | static void qed_mcp_handle_link_change(struct qed_hwfn *p_hwfn, |
369 | struct qed_ptt *p_ptt, | |
370 | bool b_reset) | |
371 | { | |
372 | struct qed_mcp_link_state *p_link; | |
373 | u32 status = 0; | |
374 | ||
375 | p_link = &p_hwfn->mcp_info->link_output; | |
376 | memset(p_link, 0, sizeof(*p_link)); | |
377 | if (!b_reset) { | |
378 | status = qed_rd(p_hwfn, p_ptt, | |
379 | p_hwfn->mcp_info->port_addr + | |
380 | offsetof(struct public_port, link_status)); | |
381 | DP_VERBOSE(p_hwfn, (NETIF_MSG_LINK | QED_MSG_SP), | |
382 | "Received link update [0x%08x] from mfw [Addr 0x%x]\n", | |
383 | status, | |
384 | (u32)(p_hwfn->mcp_info->port_addr + | |
385 | offsetof(struct public_port, | |
386 | link_status))); | |
387 | } else { | |
388 | DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, | |
389 | "Resetting link indications\n"); | |
390 | return; | |
391 | } | |
392 | ||
393 | p_link->link_up = !!(status & LINK_STATUS_LINK_UP); | |
394 | ||
395 | p_link->full_duplex = true; | |
396 | switch ((status & LINK_STATUS_SPEED_AND_DUPLEX_MASK)) { | |
397 | case LINK_STATUS_SPEED_AND_DUPLEX_100G: | |
398 | p_link->speed = 100000; | |
399 | break; | |
400 | case LINK_STATUS_SPEED_AND_DUPLEX_50G: | |
401 | p_link->speed = 50000; | |
402 | break; | |
403 | case LINK_STATUS_SPEED_AND_DUPLEX_40G: | |
404 | p_link->speed = 40000; | |
405 | break; | |
406 | case LINK_STATUS_SPEED_AND_DUPLEX_25G: | |
407 | p_link->speed = 25000; | |
408 | break; | |
409 | case LINK_STATUS_SPEED_AND_DUPLEX_20G: | |
410 | p_link->speed = 20000; | |
411 | break; | |
412 | case LINK_STATUS_SPEED_AND_DUPLEX_10G: | |
413 | p_link->speed = 10000; | |
414 | break; | |
415 | case LINK_STATUS_SPEED_AND_DUPLEX_1000THD: | |
416 | p_link->full_duplex = false; | |
417 | /* Fall-through */ | |
418 | case LINK_STATUS_SPEED_AND_DUPLEX_1000TFD: | |
419 | p_link->speed = 1000; | |
420 | break; | |
421 | default: | |
422 | p_link->speed = 0; | |
423 | } | |
424 | ||
425 | /* Correct speed according to bandwidth allocation */ | |
426 | if (p_hwfn->mcp_info->func_info.bandwidth_max && p_link->speed) { | |
427 | p_link->speed = p_link->speed * | |
428 | p_hwfn->mcp_info->func_info.bandwidth_max / | |
429 | 100; | |
430 | qed_init_pf_rl(p_hwfn, p_ptt, p_hwfn->rel_pf_id, | |
431 | p_link->speed); | |
432 | DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, | |
433 | "Configured MAX bandwidth to be %08x Mb/sec\n", | |
434 | p_link->speed); | |
435 | } | |
436 | ||
437 | p_link->an = !!(status & LINK_STATUS_AUTO_NEGOTIATE_ENABLED); | |
438 | p_link->an_complete = !!(status & | |
439 | LINK_STATUS_AUTO_NEGOTIATE_COMPLETE); | |
440 | p_link->parallel_detection = !!(status & | |
441 | LINK_STATUS_PARALLEL_DETECTION_USED); | |
442 | p_link->pfc_enabled = !!(status & LINK_STATUS_PFC_ENABLED); | |
443 | ||
444 | p_link->partner_adv_speed |= | |
445 | (status & LINK_STATUS_LINK_PARTNER_1000TFD_CAPABLE) ? | |
446 | QED_LINK_PARTNER_SPEED_1G_FD : 0; | |
447 | p_link->partner_adv_speed |= | |
448 | (status & LINK_STATUS_LINK_PARTNER_1000THD_CAPABLE) ? | |
449 | QED_LINK_PARTNER_SPEED_1G_HD : 0; | |
450 | p_link->partner_adv_speed |= | |
451 | (status & LINK_STATUS_LINK_PARTNER_10G_CAPABLE) ? | |
452 | QED_LINK_PARTNER_SPEED_10G : 0; | |
453 | p_link->partner_adv_speed |= | |
454 | (status & LINK_STATUS_LINK_PARTNER_20G_CAPABLE) ? | |
455 | QED_LINK_PARTNER_SPEED_20G : 0; | |
456 | p_link->partner_adv_speed |= | |
457 | (status & LINK_STATUS_LINK_PARTNER_40G_CAPABLE) ? | |
458 | QED_LINK_PARTNER_SPEED_40G : 0; | |
459 | p_link->partner_adv_speed |= | |
460 | (status & LINK_STATUS_LINK_PARTNER_50G_CAPABLE) ? | |
461 | QED_LINK_PARTNER_SPEED_50G : 0; | |
462 | p_link->partner_adv_speed |= | |
463 | (status & LINK_STATUS_LINK_PARTNER_100G_CAPABLE) ? | |
464 | QED_LINK_PARTNER_SPEED_100G : 0; | |
465 | ||
466 | p_link->partner_tx_flow_ctrl_en = | |
467 | !!(status & LINK_STATUS_TX_FLOW_CONTROL_ENABLED); | |
468 | p_link->partner_rx_flow_ctrl_en = | |
469 | !!(status & LINK_STATUS_RX_FLOW_CONTROL_ENABLED); | |
470 | ||
471 | switch (status & LINK_STATUS_LINK_PARTNER_FLOW_CONTROL_MASK) { | |
472 | case LINK_STATUS_LINK_PARTNER_SYMMETRIC_PAUSE: | |
473 | p_link->partner_adv_pause = QED_LINK_PARTNER_SYMMETRIC_PAUSE; | |
474 | break; | |
475 | case LINK_STATUS_LINK_PARTNER_ASYMMETRIC_PAUSE: | |
476 | p_link->partner_adv_pause = QED_LINK_PARTNER_ASYMMETRIC_PAUSE; | |
477 | break; | |
478 | case LINK_STATUS_LINK_PARTNER_BOTH_PAUSE: | |
479 | p_link->partner_adv_pause = QED_LINK_PARTNER_BOTH_PAUSE; | |
480 | break; | |
481 | default: | |
482 | p_link->partner_adv_pause = 0; | |
483 | } | |
484 | ||
485 | p_link->sfp_tx_fault = !!(status & LINK_STATUS_SFP_TX_FAULT); | |
486 | ||
487 | qed_link_update(p_hwfn); | |
488 | } | |
489 | ||
490 | int qed_mcp_set_link(struct qed_hwfn *p_hwfn, | |
491 | struct qed_ptt *p_ptt, | |
492 | bool b_up) | |
493 | { | |
494 | struct qed_mcp_link_params *params = &p_hwfn->mcp_info->link_input; | |
495 | u32 param = 0, reply = 0, cmd; | |
496 | struct pmm_phy_cfg phy_cfg; | |
497 | int rc = 0; | |
498 | u32 i; | |
499 | ||
500 | if (!qed_mcp_is_init(p_hwfn)) { | |
501 | DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); | |
502 | return -EBUSY; | |
503 | } | |
504 | ||
505 | /* Set the shmem configuration according to params */ | |
506 | memset(&phy_cfg, 0, sizeof(phy_cfg)); | |
507 | cmd = b_up ? DRV_MSG_CODE_INIT_PHY : DRV_MSG_CODE_LINK_RESET; | |
508 | if (!params->speed.autoneg) | |
509 | phy_cfg.speed = params->speed.forced_speed; | |
510 | phy_cfg.pause |= (params->pause.autoneg) ? PMM_PAUSE_AUTONEG : 0; | |
511 | phy_cfg.pause |= (params->pause.forced_rx) ? PMM_PAUSE_RX : 0; | |
512 | phy_cfg.pause |= (params->pause.forced_tx) ? PMM_PAUSE_TX : 0; | |
513 | phy_cfg.adv_speed = params->speed.advertised_speeds; | |
514 | phy_cfg.loopback_mode = params->loopback_mode; | |
515 | ||
516 | /* Write the requested configuration to shmem */ | |
517 | for (i = 0; i < sizeof(phy_cfg); i += 4) | |
518 | qed_wr(p_hwfn, p_ptt, | |
519 | p_hwfn->mcp_info->drv_mb_addr + | |
520 | offsetof(struct public_drv_mb, union_data) + i, | |
521 | ((u32 *)&phy_cfg)[i >> 2]); | |
522 | ||
523 | if (b_up) { | |
524 | DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, | |
525 | "Configuring Link: Speed 0x%08x, Pause 0x%08x, adv_speed 0x%08x, loopback 0x%08x, features 0x%08x\n", | |
526 | phy_cfg.speed, | |
527 | phy_cfg.pause, | |
528 | phy_cfg.adv_speed, | |
529 | phy_cfg.loopback_mode, | |
530 | phy_cfg.feature_config_flags); | |
531 | } else { | |
532 | DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, | |
533 | "Resetting link\n"); | |
534 | } | |
535 | ||
536 | DP_VERBOSE(p_hwfn, QED_MSG_SP, "fw_seq 0x%08x, drv_pulse 0x%x\n", | |
537 | p_hwfn->mcp_info->drv_mb_seq, | |
538 | p_hwfn->mcp_info->drv_pulse_seq); | |
539 | ||
540 | /* Load Request */ | |
541 | rc = qed_mcp_cmd(p_hwfn, p_ptt, cmd, 0, &reply, ¶m); | |
542 | ||
543 | /* if mcp fails to respond we must abort */ | |
544 | if (rc) { | |
545 | DP_ERR(p_hwfn, "MCP response failure, aborting\n"); | |
546 | return rc; | |
547 | } | |
548 | ||
549 | /* Reset the link status if needed */ | |
550 | if (!b_up) | |
551 | qed_mcp_handle_link_change(p_hwfn, p_ptt, true); | |
552 | ||
553 | return 0; | |
554 | } | |
555 | ||
556 | int qed_mcp_handle_events(struct qed_hwfn *p_hwfn, | |
557 | struct qed_ptt *p_ptt) | |
558 | { | |
559 | struct qed_mcp_info *info = p_hwfn->mcp_info; | |
560 | int rc = 0; | |
561 | bool found = false; | |
562 | u16 i; | |
563 | ||
564 | DP_VERBOSE(p_hwfn, QED_MSG_SP, "Received message from MFW\n"); | |
565 | ||
566 | /* Read Messages from MFW */ | |
567 | qed_mcp_read_mb(p_hwfn, p_ptt); | |
568 | ||
569 | /* Compare current messages to old ones */ | |
570 | for (i = 0; i < info->mfw_mb_length; i++) { | |
571 | if (info->mfw_mb_cur[i] == info->mfw_mb_shadow[i]) | |
572 | continue; | |
573 | ||
574 | found = true; | |
575 | ||
576 | DP_VERBOSE(p_hwfn, NETIF_MSG_LINK, | |
577 | "Msg [%d] - old CMD 0x%02x, new CMD 0x%02x\n", | |
578 | i, info->mfw_mb_shadow[i], info->mfw_mb_cur[i]); | |
579 | ||
580 | switch (i) { | |
581 | case MFW_DRV_MSG_LINK_CHANGE: | |
582 | qed_mcp_handle_link_change(p_hwfn, p_ptt, false); | |
583 | break; | |
584 | default: | |
585 | DP_NOTICE(p_hwfn, "Unimplemented MFW message %d\n", i); | |
586 | rc = -EINVAL; | |
587 | } | |
588 | } | |
589 | ||
590 | /* ACK everything */ | |
591 | for (i = 0; i < MFW_DRV_MSG_MAX_DWORDS(info->mfw_mb_length); i++) { | |
592 | __be32 val = cpu_to_be32(((u32 *)info->mfw_mb_cur)[i]); | |
593 | ||
594 | /* MFW expect answer in BE, so we force write in that format */ | |
595 | qed_wr(p_hwfn, p_ptt, | |
596 | info->mfw_mb_addr + sizeof(u32) + | |
597 | MFW_DRV_MSG_MAX_DWORDS(info->mfw_mb_length) * | |
598 | sizeof(u32) + i * sizeof(u32), | |
599 | (__force u32)val); | |
600 | } | |
601 | ||
602 | if (!found) { | |
603 | DP_NOTICE(p_hwfn, | |
604 | "Received an MFW message indication but no new message!\n"); | |
605 | rc = -EINVAL; | |
606 | } | |
607 | ||
608 | /* Copy the new mfw messages into the shadow */ | |
609 | memcpy(info->mfw_mb_shadow, info->mfw_mb_cur, info->mfw_mb_length); | |
610 | ||
611 | return rc; | |
612 | } | |
613 | ||
fe56b9e6 YM |
614 | int qed_mcp_get_mfw_ver(struct qed_dev *cdev, |
615 | u32 *p_mfw_ver) | |
616 | { | |
617 | struct qed_hwfn *p_hwfn = &cdev->hwfns[0]; | |
618 | struct qed_ptt *p_ptt; | |
619 | u32 global_offsize; | |
620 | ||
621 | p_ptt = qed_ptt_acquire(p_hwfn); | |
622 | if (!p_ptt) | |
623 | return -EBUSY; | |
624 | ||
625 | global_offsize = qed_rd(p_hwfn, p_ptt, | |
626 | SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info-> | |
627 | public_base, | |
628 | PUBLIC_GLOBAL)); | |
629 | *p_mfw_ver = qed_rd(p_hwfn, p_ptt, | |
630 | SECTION_ADDR(global_offsize, 0) + | |
631 | offsetof(struct public_global, mfw_ver)); | |
632 | ||
633 | qed_ptt_release(p_hwfn, p_ptt); | |
634 | ||
635 | return 0; | |
636 | } | |
637 | ||
cc875c2e YM |
638 | int qed_mcp_get_media_type(struct qed_dev *cdev, |
639 | u32 *p_media_type) | |
640 | { | |
641 | struct qed_hwfn *p_hwfn = &cdev->hwfns[0]; | |
642 | struct qed_ptt *p_ptt; | |
643 | ||
644 | if (!qed_mcp_is_init(p_hwfn)) { | |
645 | DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); | |
646 | return -EBUSY; | |
647 | } | |
648 | ||
649 | *p_media_type = MEDIA_UNSPECIFIED; | |
650 | ||
651 | p_ptt = qed_ptt_acquire(p_hwfn); | |
652 | if (!p_ptt) | |
653 | return -EBUSY; | |
654 | ||
655 | *p_media_type = qed_rd(p_hwfn, p_ptt, p_hwfn->mcp_info->port_addr + | |
656 | offsetof(struct public_port, media_type)); | |
657 | ||
658 | qed_ptt_release(p_hwfn, p_ptt); | |
659 | ||
660 | return 0; | |
661 | } | |
662 | ||
fe56b9e6 YM |
663 | static u32 qed_mcp_get_shmem_func(struct qed_hwfn *p_hwfn, |
664 | struct qed_ptt *p_ptt, | |
665 | struct public_func *p_data, | |
666 | int pfid) | |
667 | { | |
668 | u32 addr = SECTION_OFFSIZE_ADDR(p_hwfn->mcp_info->public_base, | |
669 | PUBLIC_FUNC); | |
670 | u32 mfw_path_offsize = qed_rd(p_hwfn, p_ptt, addr); | |
671 | u32 func_addr = SECTION_ADDR(mfw_path_offsize, pfid); | |
672 | u32 i, size; | |
673 | ||
674 | memset(p_data, 0, sizeof(*p_data)); | |
675 | ||
676 | size = min_t(u32, sizeof(*p_data), | |
677 | QED_SECTION_SIZE(mfw_path_offsize)); | |
678 | for (i = 0; i < size / sizeof(u32); i++) | |
679 | ((u32 *)p_data)[i] = qed_rd(p_hwfn, p_ptt, | |
680 | func_addr + (i << 2)); | |
681 | ||
682 | return size; | |
683 | } | |
684 | ||
685 | static int | |
686 | qed_mcp_get_shmem_proto(struct qed_hwfn *p_hwfn, | |
687 | struct public_func *p_info, | |
688 | enum qed_pci_personality *p_proto) | |
689 | { | |
690 | int rc = 0; | |
691 | ||
692 | switch (p_info->config & FUNC_MF_CFG_PROTOCOL_MASK) { | |
693 | case FUNC_MF_CFG_PROTOCOL_ETHERNET: | |
694 | *p_proto = QED_PCI_ETH; | |
695 | break; | |
696 | default: | |
697 | rc = -EINVAL; | |
698 | } | |
699 | ||
700 | return rc; | |
701 | } | |
702 | ||
703 | int qed_mcp_fill_shmem_func_info(struct qed_hwfn *p_hwfn, | |
704 | struct qed_ptt *p_ptt) | |
705 | { | |
706 | struct qed_mcp_function_info *info; | |
707 | struct public_func shmem_info; | |
708 | ||
709 | qed_mcp_get_shmem_func(p_hwfn, p_ptt, &shmem_info, | |
710 | MCP_PF_ID(p_hwfn)); | |
711 | info = &p_hwfn->mcp_info->func_info; | |
712 | ||
713 | info->pause_on_host = (shmem_info.config & | |
714 | FUNC_MF_CFG_PAUSE_ON_HOST_RING) ? 1 : 0; | |
715 | ||
716 | if (qed_mcp_get_shmem_proto(p_hwfn, &shmem_info, | |
717 | &info->protocol)) { | |
718 | DP_ERR(p_hwfn, "Unknown personality %08x\n", | |
719 | (u32)(shmem_info.config & FUNC_MF_CFG_PROTOCOL_MASK)); | |
720 | return -EINVAL; | |
721 | } | |
722 | ||
723 | if (p_hwfn->cdev->mf_mode != SF) { | |
724 | info->bandwidth_min = (shmem_info.config & | |
725 | FUNC_MF_CFG_MIN_BW_MASK) >> | |
726 | FUNC_MF_CFG_MIN_BW_SHIFT; | |
727 | if (info->bandwidth_min < 1 || info->bandwidth_min > 100) { | |
728 | DP_INFO(p_hwfn, | |
729 | "bandwidth minimum out of bounds [%02x]. Set to 1\n", | |
730 | info->bandwidth_min); | |
731 | info->bandwidth_min = 1; | |
732 | } | |
733 | ||
734 | info->bandwidth_max = (shmem_info.config & | |
735 | FUNC_MF_CFG_MAX_BW_MASK) >> | |
736 | FUNC_MF_CFG_MAX_BW_SHIFT; | |
737 | if (info->bandwidth_max < 1 || info->bandwidth_max > 100) { | |
738 | DP_INFO(p_hwfn, | |
739 | "bandwidth maximum out of bounds [%02x]. Set to 100\n", | |
740 | info->bandwidth_max); | |
741 | info->bandwidth_max = 100; | |
742 | } | |
743 | } | |
744 | ||
745 | if (shmem_info.mac_upper || shmem_info.mac_lower) { | |
746 | info->mac[0] = (u8)(shmem_info.mac_upper >> 8); | |
747 | info->mac[1] = (u8)(shmem_info.mac_upper); | |
748 | info->mac[2] = (u8)(shmem_info.mac_lower >> 24); | |
749 | info->mac[3] = (u8)(shmem_info.mac_lower >> 16); | |
750 | info->mac[4] = (u8)(shmem_info.mac_lower >> 8); | |
751 | info->mac[5] = (u8)(shmem_info.mac_lower); | |
752 | } else { | |
753 | DP_NOTICE(p_hwfn, "MAC is 0 in shmem\n"); | |
754 | } | |
755 | ||
756 | info->wwn_port = (u64)shmem_info.fcoe_wwn_port_name_upper | | |
757 | (((u64)shmem_info.fcoe_wwn_port_name_lower) << 32); | |
758 | info->wwn_node = (u64)shmem_info.fcoe_wwn_node_name_upper | | |
759 | (((u64)shmem_info.fcoe_wwn_node_name_lower) << 32); | |
760 | ||
761 | info->ovlan = (u16)(shmem_info.ovlan_stag & FUNC_MF_CFG_OV_STAG_MASK); | |
762 | ||
763 | DP_VERBOSE(p_hwfn, (QED_MSG_SP | NETIF_MSG_IFUP), | |
764 | "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", | |
765 | info->pause_on_host, info->protocol, | |
766 | info->bandwidth_min, info->bandwidth_max, | |
767 | info->mac[0], info->mac[1], info->mac[2], | |
768 | info->mac[3], info->mac[4], info->mac[5], | |
769 | info->wwn_port, info->wwn_node, info->ovlan); | |
770 | ||
771 | return 0; | |
772 | } | |
773 | ||
cc875c2e YM |
774 | struct qed_mcp_link_params |
775 | *qed_mcp_get_link_params(struct qed_hwfn *p_hwfn) | |
776 | { | |
777 | if (!p_hwfn || !p_hwfn->mcp_info) | |
778 | return NULL; | |
779 | return &p_hwfn->mcp_info->link_input; | |
780 | } | |
781 | ||
782 | struct qed_mcp_link_state | |
783 | *qed_mcp_get_link_state(struct qed_hwfn *p_hwfn) | |
784 | { | |
785 | if (!p_hwfn || !p_hwfn->mcp_info) | |
786 | return NULL; | |
787 | return &p_hwfn->mcp_info->link_output; | |
788 | } | |
789 | ||
790 | struct qed_mcp_link_capabilities | |
791 | *qed_mcp_get_link_capabilities(struct qed_hwfn *p_hwfn) | |
792 | { | |
793 | if (!p_hwfn || !p_hwfn->mcp_info) | |
794 | return NULL; | |
795 | return &p_hwfn->mcp_info->link_capabilities; | |
796 | } | |
797 | ||
fe56b9e6 YM |
798 | int qed_mcp_drain(struct qed_hwfn *p_hwfn, |
799 | struct qed_ptt *p_ptt) | |
800 | { | |
801 | u32 resp = 0, param = 0; | |
802 | int rc; | |
803 | ||
804 | rc = qed_mcp_cmd(p_hwfn, p_ptt, | |
805 | DRV_MSG_CODE_NIG_DRAIN, 100, | |
806 | &resp, ¶m); | |
807 | ||
808 | /* Wait for the drain to complete before returning */ | |
809 | msleep(120); | |
810 | ||
811 | return rc; | |
812 | } | |
813 | ||
cee4d264 MC |
814 | int qed_mcp_get_flash_size(struct qed_hwfn *p_hwfn, |
815 | struct qed_ptt *p_ptt, | |
816 | u32 *p_flash_size) | |
817 | { | |
818 | u32 flash_size; | |
819 | ||
820 | flash_size = qed_rd(p_hwfn, p_ptt, MCP_REG_NVM_CFG4); | |
821 | flash_size = (flash_size & MCP_REG_NVM_CFG4_FLASH_SIZE) >> | |
822 | MCP_REG_NVM_CFG4_FLASH_SIZE_SHIFT; | |
823 | flash_size = (1 << (flash_size + MCP_BYTES_PER_MBIT_SHIFT)); | |
824 | ||
825 | *p_flash_size = flash_size; | |
826 | ||
827 | return 0; | |
828 | } | |
829 | ||
fe56b9e6 YM |
830 | int |
831 | qed_mcp_send_drv_version(struct qed_hwfn *p_hwfn, | |
832 | struct qed_ptt *p_ptt, | |
833 | struct qed_mcp_drv_version *p_ver) | |
834 | { | |
835 | int rc = 0; | |
836 | u32 param = 0, reply = 0, i; | |
837 | ||
838 | if (!qed_mcp_is_init(p_hwfn)) { | |
839 | DP_NOTICE(p_hwfn, "MFW is not initialized !\n"); | |
840 | return -EBUSY; | |
841 | } | |
842 | ||
843 | DRV_MB_WR(p_hwfn, p_ptt, union_data.drv_version.version, | |
844 | p_ver->version); | |
845 | /* Copy version string to shmem */ | |
846 | for (i = 0; i < (MCP_DRV_VER_STR_SIZE - 4) / 4; i++) { | |
847 | DRV_MB_WR(p_hwfn, p_ptt, | |
848 | union_data.drv_version.name[i * sizeof(u32)], | |
849 | *(u32 *)&p_ver->name[i * sizeof(u32)]); | |
850 | } | |
851 | ||
852 | rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_SET_VERSION, 0, &reply, | |
853 | ¶m); | |
854 | if (rc) { | |
855 | DP_ERR(p_hwfn, "MCP response failure, aborting\n"); | |
856 | return rc; | |
857 | } | |
858 | ||
859 | return 0; | |
860 | } |