]>
Commit | Line | Data |
---|---|---|
1ac5a404 SX |
1 | /* |
2 | * Broadcom NetXtreme-E RoCE driver. | |
3 | * | |
4 | * Copyright (c) 2016 - 2017, Broadcom. All rights reserved. The term | |
5 | * Broadcom refers to Broadcom Limited and/or its subsidiaries. | |
6 | * | |
7 | * This software is available to you under a choice of one of two | |
8 | * licenses. You may choose to be licensed under the terms of the GNU | |
9 | * General Public License (GPL) Version 2, available from the file | |
10 | * COPYING in the main directory of this source tree, or the | |
11 | * BSD license below: | |
12 | * | |
13 | * Redistribution and use in source and binary forms, with or without | |
14 | * modification, are permitted provided that the following conditions | |
15 | * are met: | |
16 | * | |
17 | * 1. Redistributions of source code must retain the above copyright | |
18 | * notice, this list of conditions and the following disclaimer. | |
19 | * 2. Redistributions in binary form must reproduce the above copyright | |
20 | * notice, this list of conditions and the following disclaimer in | |
21 | * the documentation and/or other materials provided with the | |
22 | * distribution. | |
23 | * | |
24 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' | |
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
26 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
27 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS | |
28 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | |
31 | * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
32 | * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
33 | * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN | |
34 | * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
35 | * | |
36 | * Description: Slow Path Operators | |
37 | */ | |
38 | ||
39 | #include <linux/interrupt.h> | |
40 | #include <linux/spinlock.h> | |
41 | #include <linux/sched.h> | |
42 | #include <linux/pci.h> | |
43 | ||
44 | #include "roce_hsi.h" | |
45 | ||
46 | #include "qplib_res.h" | |
47 | #include "qplib_rcfw.h" | |
48 | #include "qplib_sp.h" | |
49 | ||
50 | const struct bnxt_qplib_gid bnxt_qplib_gid_zero = {{ 0, 0, 0, 0, 0, 0, 0, 0, | |
51 | 0, 0, 0, 0, 0, 0, 0, 0 } }; | |
52 | ||
53 | /* Device */ | |
54 | int bnxt_qplib_get_dev_attr(struct bnxt_qplib_rcfw *rcfw, | |
55 | struct bnxt_qplib_dev_attr *attr) | |
56 | { | |
57 | struct cmdq_query_func req; | |
58 | struct creq_query_func_resp *resp; | |
59 | struct creq_query_func_resp_sb *sb; | |
60 | u16 cmd_flags = 0; | |
61 | u32 temp; | |
62 | u8 *tqm_alloc; | |
63 | int i; | |
64 | ||
65 | RCFW_CMD_PREP(req, QUERY_FUNC, cmd_flags); | |
66 | ||
67 | req.resp_size = sizeof(*sb) / BNXT_QPLIB_CMDQE_UNITS; | |
68 | resp = (struct creq_query_func_resp *) | |
69 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, (void **)&sb, | |
70 | 0); | |
71 | if (!resp) { | |
72 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: QUERY_FUNC send failed"); | |
73 | return -EINVAL; | |
74 | } | |
75 | if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { | |
76 | /* Cmd timed out */ | |
77 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: QUERY_FUNC timed out"); | |
78 | return -ETIMEDOUT; | |
79 | } | |
80 | if (resp->status || | |
81 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
82 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: QUERY_FUNC failed "); | |
83 | dev_err(&rcfw->pdev->dev, | |
84 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
85 | resp->status, le16_to_cpu(req.cookie), | |
86 | le16_to_cpu(resp->cookie)); | |
87 | return -EINVAL; | |
88 | } | |
89 | /* Extract the context from the side buffer */ | |
90 | attr->max_qp = le32_to_cpu(sb->max_qp); | |
91 | attr->max_qp_rd_atom = | |
92 | sb->max_qp_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? | |
93 | BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_rd_atom; | |
94 | attr->max_qp_init_rd_atom = | |
95 | sb->max_qp_init_rd_atom > BNXT_QPLIB_MAX_OUT_RD_ATOM ? | |
96 | BNXT_QPLIB_MAX_OUT_RD_ATOM : sb->max_qp_init_rd_atom; | |
97 | attr->max_qp_wqes = le16_to_cpu(sb->max_qp_wr); | |
98 | attr->max_qp_sges = sb->max_sge; | |
99 | attr->max_cq = le32_to_cpu(sb->max_cq); | |
100 | attr->max_cq_wqes = le32_to_cpu(sb->max_cqe); | |
101 | attr->max_cq_sges = attr->max_qp_sges; | |
102 | attr->max_mr = le32_to_cpu(sb->max_mr); | |
103 | attr->max_mw = le32_to_cpu(sb->max_mw); | |
104 | ||
105 | attr->max_mr_size = le64_to_cpu(sb->max_mr_size); | |
106 | attr->max_pd = 64 * 1024; | |
107 | attr->max_raw_ethy_qp = le32_to_cpu(sb->max_raw_eth_qp); | |
108 | attr->max_ah = le32_to_cpu(sb->max_ah); | |
109 | ||
110 | attr->max_fmr = le32_to_cpu(sb->max_fmr); | |
111 | attr->max_map_per_fmr = sb->max_map_per_fmr; | |
112 | ||
113 | attr->max_srq = le16_to_cpu(sb->max_srq); | |
114 | attr->max_srq_wqes = le32_to_cpu(sb->max_srq_wr) - 1; | |
115 | attr->max_srq_sges = sb->max_srq_sge; | |
116 | /* Bono only reports 1 PKEY for now, but it can support > 1 */ | |
117 | attr->max_pkey = le32_to_cpu(sb->max_pkeys); | |
118 | ||
119 | attr->max_inline_data = le32_to_cpu(sb->max_inline_data); | |
120 | attr->l2_db_size = (sb->l2_db_space_size + 1) * PAGE_SIZE; | |
121 | attr->max_sgid = le32_to_cpu(sb->max_gid); | |
122 | ||
123 | strlcpy(attr->fw_ver, "20.6.28.0", sizeof(attr->fw_ver)); | |
124 | ||
125 | for (i = 0; i < MAX_TQM_ALLOC_REQ / 4; i++) { | |
126 | temp = le32_to_cpu(sb->tqm_alloc_reqs[i]); | |
127 | tqm_alloc = (u8 *)&temp; | |
128 | attr->tqm_alloc_reqs[i * 4] = *tqm_alloc; | |
129 | attr->tqm_alloc_reqs[i * 4 + 1] = *(++tqm_alloc); | |
130 | attr->tqm_alloc_reqs[i * 4 + 2] = *(++tqm_alloc); | |
131 | attr->tqm_alloc_reqs[i * 4 + 3] = *(++tqm_alloc); | |
132 | } | |
133 | return 0; | |
134 | } | |
135 | ||
136 | /* SGID */ | |
137 | int bnxt_qplib_get_sgid(struct bnxt_qplib_res *res, | |
138 | struct bnxt_qplib_sgid_tbl *sgid_tbl, int index, | |
139 | struct bnxt_qplib_gid *gid) | |
140 | { | |
141 | if (index > sgid_tbl->max) { | |
142 | dev_err(&res->pdev->dev, | |
143 | "QPLIB: Index %d exceeded SGID table max (%d)", | |
144 | index, sgid_tbl->max); | |
145 | return -EINVAL; | |
146 | } | |
147 | memcpy(gid, &sgid_tbl->tbl[index], sizeof(*gid)); | |
148 | return 0; | |
149 | } | |
150 | ||
151 | int bnxt_qplib_del_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, | |
152 | struct bnxt_qplib_gid *gid, bool update) | |
153 | { | |
154 | struct bnxt_qplib_res *res = to_bnxt_qplib(sgid_tbl, | |
155 | struct bnxt_qplib_res, | |
156 | sgid_tbl); | |
157 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
158 | int index; | |
159 | ||
160 | if (!sgid_tbl) { | |
161 | dev_err(&res->pdev->dev, "QPLIB: SGID table not allocated"); | |
162 | return -EINVAL; | |
163 | } | |
164 | /* Do we need a sgid_lock here? */ | |
165 | if (!sgid_tbl->active) { | |
166 | dev_err(&res->pdev->dev, | |
167 | "QPLIB: SGID table has no active entries"); | |
168 | return -ENOMEM; | |
169 | } | |
170 | for (index = 0; index < sgid_tbl->max; index++) { | |
171 | if (!memcmp(&sgid_tbl->tbl[index], gid, sizeof(*gid))) | |
172 | break; | |
173 | } | |
174 | if (index == sgid_tbl->max) { | |
175 | dev_warn(&res->pdev->dev, "GID not found in the SGID table"); | |
176 | return 0; | |
177 | } | |
178 | /* Remove GID from the SGID table */ | |
179 | if (update) { | |
180 | struct cmdq_delete_gid req; | |
181 | struct creq_delete_gid_resp *resp; | |
182 | u16 cmd_flags = 0; | |
183 | ||
184 | RCFW_CMD_PREP(req, DELETE_GID, cmd_flags); | |
185 | if (sgid_tbl->hw_id[index] == 0xFFFF) { | |
186 | dev_err(&res->pdev->dev, | |
187 | "QPLIB: GID entry contains an invalid HW id"); | |
188 | return -EINVAL; | |
189 | } | |
190 | req.gid_index = cpu_to_le16(sgid_tbl->hw_id[index]); | |
191 | resp = (struct creq_delete_gid_resp *) | |
192 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, NULL, | |
193 | 0); | |
194 | if (!resp) { | |
195 | dev_err(&res->pdev->dev, | |
196 | "QPLIB: SP: DELETE_GID send failed"); | |
197 | return -EINVAL; | |
198 | } | |
199 | if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, | |
200 | le16_to_cpu(req.cookie))) { | |
201 | /* Cmd timed out */ | |
202 | dev_err(&res->pdev->dev, | |
203 | "QPLIB: SP: DELETE_GID timed out"); | |
204 | return -ETIMEDOUT; | |
205 | } | |
206 | if (resp->status || | |
207 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
208 | dev_err(&res->pdev->dev, | |
209 | "QPLIB: SP: DELETE_GID failed "); | |
210 | dev_err(&res->pdev->dev, | |
211 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
212 | resp->status, le16_to_cpu(req.cookie), | |
213 | le16_to_cpu(resp->cookie)); | |
214 | return -EINVAL; | |
215 | } | |
216 | } | |
217 | memcpy(&sgid_tbl->tbl[index], &bnxt_qplib_gid_zero, | |
218 | sizeof(bnxt_qplib_gid_zero)); | |
219 | sgid_tbl->active--; | |
220 | dev_dbg(&res->pdev->dev, | |
221 | "QPLIB: SGID deleted hw_id[0x%x] = 0x%x active = 0x%x", | |
222 | index, sgid_tbl->hw_id[index], sgid_tbl->active); | |
223 | sgid_tbl->hw_id[index] = (u16)-1; | |
224 | ||
225 | /* unlock */ | |
226 | return 0; | |
227 | } | |
228 | ||
229 | int bnxt_qplib_add_sgid(struct bnxt_qplib_sgid_tbl *sgid_tbl, | |
230 | struct bnxt_qplib_gid *gid, u8 *smac, u16 vlan_id, | |
231 | bool update, u32 *index) | |
232 | { | |
233 | struct bnxt_qplib_res *res = to_bnxt_qplib(sgid_tbl, | |
234 | struct bnxt_qplib_res, | |
235 | sgid_tbl); | |
236 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
237 | int i, free_idx, rc = 0; | |
238 | ||
239 | if (!sgid_tbl) { | |
240 | dev_err(&res->pdev->dev, "QPLIB: SGID table not allocated"); | |
241 | return -EINVAL; | |
242 | } | |
243 | /* Do we need a sgid_lock here? */ | |
244 | if (sgid_tbl->active == sgid_tbl->max) { | |
245 | dev_err(&res->pdev->dev, "QPLIB: SGID table is full"); | |
246 | return -ENOMEM; | |
247 | } | |
248 | free_idx = sgid_tbl->max; | |
249 | for (i = 0; i < sgid_tbl->max; i++) { | |
250 | if (!memcmp(&sgid_tbl->tbl[i], gid, sizeof(*gid))) { | |
251 | dev_dbg(&res->pdev->dev, | |
252 | "QPLIB: SGID entry already exist in entry %d!", | |
253 | i); | |
254 | *index = i; | |
255 | return -EALREADY; | |
256 | } else if (!memcmp(&sgid_tbl->tbl[i], &bnxt_qplib_gid_zero, | |
257 | sizeof(bnxt_qplib_gid_zero)) && | |
258 | free_idx == sgid_tbl->max) { | |
259 | free_idx = i; | |
260 | } | |
261 | } | |
262 | if (free_idx == sgid_tbl->max) { | |
263 | dev_err(&res->pdev->dev, | |
264 | "QPLIB: SGID table is FULL but count is not MAX??"); | |
265 | return -ENOMEM; | |
266 | } | |
267 | if (update) { | |
268 | struct cmdq_add_gid req; | |
269 | struct creq_add_gid_resp *resp; | |
270 | u16 cmd_flags = 0; | |
271 | u32 temp32[4]; | |
272 | u16 temp16[3]; | |
273 | ||
274 | RCFW_CMD_PREP(req, ADD_GID, cmd_flags); | |
275 | ||
276 | memcpy(temp32, gid->data, sizeof(struct bnxt_qplib_gid)); | |
277 | req.gid[0] = cpu_to_be32(temp32[3]); | |
278 | req.gid[1] = cpu_to_be32(temp32[2]); | |
279 | req.gid[2] = cpu_to_be32(temp32[1]); | |
280 | req.gid[3] = cpu_to_be32(temp32[0]); | |
281 | if (vlan_id != 0xFFFF) | |
282 | req.vlan = cpu_to_le16((vlan_id & | |
283 | CMDQ_ADD_GID_VLAN_VLAN_ID_MASK) | | |
284 | CMDQ_ADD_GID_VLAN_TPID_TPID_8100 | | |
285 | CMDQ_ADD_GID_VLAN_VLAN_EN); | |
286 | ||
287 | /* MAC in network format */ | |
288 | memcpy(temp16, smac, 6); | |
289 | req.src_mac[0] = cpu_to_be16(temp16[0]); | |
290 | req.src_mac[1] = cpu_to_be16(temp16[1]); | |
291 | req.src_mac[2] = cpu_to_be16(temp16[2]); | |
292 | ||
293 | resp = (struct creq_add_gid_resp *) | |
294 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
295 | NULL, 0); | |
296 | if (!resp) { | |
297 | dev_err(&res->pdev->dev, | |
298 | "QPLIB: SP: ADD_GID send failed"); | |
299 | return -EINVAL; | |
300 | } | |
301 | if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, | |
302 | le16_to_cpu(req.cookie))) { | |
303 | /* Cmd timed out */ | |
304 | dev_err(&res->pdev->dev, | |
305 | "QPIB: SP: ADD_GID timed out"); | |
306 | return -ETIMEDOUT; | |
307 | } | |
308 | if (resp->status || | |
309 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
310 | dev_err(&res->pdev->dev, "QPLIB: SP: ADD_GID failed "); | |
311 | dev_err(&res->pdev->dev, | |
312 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
313 | resp->status, le16_to_cpu(req.cookie), | |
314 | le16_to_cpu(resp->cookie)); | |
315 | return -EINVAL; | |
316 | } | |
317 | sgid_tbl->hw_id[free_idx] = le32_to_cpu(resp->xid); | |
318 | } | |
319 | /* Add GID to the sgid_tbl */ | |
320 | memcpy(&sgid_tbl->tbl[free_idx], gid, sizeof(*gid)); | |
321 | sgid_tbl->active++; | |
322 | dev_dbg(&res->pdev->dev, | |
323 | "QPLIB: SGID added hw_id[0x%x] = 0x%x active = 0x%x", | |
324 | free_idx, sgid_tbl->hw_id[free_idx], sgid_tbl->active); | |
325 | ||
326 | *index = free_idx; | |
327 | /* unlock */ | |
328 | return rc; | |
329 | } | |
330 | ||
331 | /* pkeys */ | |
332 | int bnxt_qplib_get_pkey(struct bnxt_qplib_res *res, | |
333 | struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 index, | |
334 | u16 *pkey) | |
335 | { | |
336 | if (index == 0xFFFF) { | |
337 | *pkey = 0xFFFF; | |
338 | return 0; | |
339 | } | |
340 | if (index > pkey_tbl->max) { | |
341 | dev_err(&res->pdev->dev, | |
342 | "QPLIB: Index %d exceeded PKEY table max (%d)", | |
343 | index, pkey_tbl->max); | |
344 | return -EINVAL; | |
345 | } | |
346 | memcpy(pkey, &pkey_tbl->tbl[index], sizeof(*pkey)); | |
347 | return 0; | |
348 | } | |
349 | ||
350 | int bnxt_qplib_del_pkey(struct bnxt_qplib_res *res, | |
351 | struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 *pkey, | |
352 | bool update) | |
353 | { | |
354 | int i, rc = 0; | |
355 | ||
356 | if (!pkey_tbl) { | |
357 | dev_err(&res->pdev->dev, "QPLIB: PKEY table not allocated"); | |
358 | return -EINVAL; | |
359 | } | |
360 | ||
361 | /* Do we need a pkey_lock here? */ | |
362 | if (!pkey_tbl->active) { | |
363 | dev_err(&res->pdev->dev, | |
364 | "QPLIB: PKEY table has no active entries"); | |
365 | return -ENOMEM; | |
366 | } | |
367 | for (i = 0; i < pkey_tbl->max; i++) { | |
368 | if (!memcmp(&pkey_tbl->tbl[i], pkey, sizeof(*pkey))) | |
369 | break; | |
370 | } | |
371 | if (i == pkey_tbl->max) { | |
372 | dev_err(&res->pdev->dev, | |
373 | "QPLIB: PKEY 0x%04x not found in the pkey table", | |
374 | *pkey); | |
375 | return -ENOMEM; | |
376 | } | |
377 | memset(&pkey_tbl->tbl[i], 0, sizeof(*pkey)); | |
378 | pkey_tbl->active--; | |
379 | ||
380 | /* unlock */ | |
381 | return rc; | |
382 | } | |
383 | ||
384 | int bnxt_qplib_add_pkey(struct bnxt_qplib_res *res, | |
385 | struct bnxt_qplib_pkey_tbl *pkey_tbl, u16 *pkey, | |
386 | bool update) | |
387 | { | |
388 | int i, free_idx, rc = 0; | |
389 | ||
390 | if (!pkey_tbl) { | |
391 | dev_err(&res->pdev->dev, "QPLIB: PKEY table not allocated"); | |
392 | return -EINVAL; | |
393 | } | |
394 | ||
395 | /* Do we need a pkey_lock here? */ | |
396 | if (pkey_tbl->active == pkey_tbl->max) { | |
397 | dev_err(&res->pdev->dev, "QPLIB: PKEY table is full"); | |
398 | return -ENOMEM; | |
399 | } | |
400 | free_idx = pkey_tbl->max; | |
401 | for (i = 0; i < pkey_tbl->max; i++) { | |
402 | if (!memcmp(&pkey_tbl->tbl[i], pkey, sizeof(*pkey))) | |
403 | return -EALREADY; | |
404 | else if (!pkey_tbl->tbl[i] && free_idx == pkey_tbl->max) | |
405 | free_idx = i; | |
406 | } | |
407 | if (free_idx == pkey_tbl->max) { | |
408 | dev_err(&res->pdev->dev, | |
409 | "QPLIB: PKEY table is FULL but count is not MAX??"); | |
410 | return -ENOMEM; | |
411 | } | |
412 | /* Add PKEY to the pkey_tbl */ | |
413 | memcpy(&pkey_tbl->tbl[free_idx], pkey, sizeof(*pkey)); | |
414 | pkey_tbl->active++; | |
415 | ||
416 | /* unlock */ | |
417 | return rc; | |
418 | } | |
419 | ||
420 | /* AH */ | |
421 | int bnxt_qplib_create_ah(struct bnxt_qplib_res *res, struct bnxt_qplib_ah *ah) | |
422 | { | |
423 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
424 | struct cmdq_create_ah req; | |
425 | struct creq_create_ah_resp *resp; | |
426 | u16 cmd_flags = 0; | |
427 | u32 temp32[4]; | |
428 | u16 temp16[3]; | |
429 | ||
430 | RCFW_CMD_PREP(req, CREATE_AH, cmd_flags); | |
431 | ||
432 | memcpy(temp32, ah->dgid.data, sizeof(struct bnxt_qplib_gid)); | |
433 | req.dgid[0] = cpu_to_le32(temp32[0]); | |
434 | req.dgid[1] = cpu_to_le32(temp32[1]); | |
435 | req.dgid[2] = cpu_to_le32(temp32[2]); | |
436 | req.dgid[3] = cpu_to_le32(temp32[3]); | |
437 | ||
438 | req.type = ah->nw_type; | |
439 | req.hop_limit = ah->hop_limit; | |
440 | req.sgid_index = cpu_to_le16(res->sgid_tbl.hw_id[ah->sgid_index]); | |
441 | req.dest_vlan_id_flow_label = cpu_to_le32((ah->flow_label & | |
442 | CMDQ_CREATE_AH_FLOW_LABEL_MASK) | | |
443 | CMDQ_CREATE_AH_DEST_VLAN_ID_MASK); | |
444 | req.pd_id = cpu_to_le32(ah->pd->id); | |
445 | req.traffic_class = ah->traffic_class; | |
446 | ||
447 | /* MAC in network format */ | |
448 | memcpy(temp16, ah->dmac, 6); | |
449 | req.dest_mac[0] = cpu_to_le16(temp16[0]); | |
450 | req.dest_mac[1] = cpu_to_le16(temp16[1]); | |
451 | req.dest_mac[2] = cpu_to_le16(temp16[2]); | |
452 | ||
453 | resp = (struct creq_create_ah_resp *) | |
454 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
455 | NULL, 1); | |
456 | if (!resp) { | |
457 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: CREATE_AH send failed"); | |
458 | return -EINVAL; | |
459 | } | |
460 | if (!bnxt_qplib_rcfw_block_for_resp(rcfw, le16_to_cpu(req.cookie))) { | |
461 | /* Cmd timed out */ | |
462 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: CREATE_AH timed out"); | |
463 | return -ETIMEDOUT; | |
464 | } | |
465 | if (resp->status || | |
466 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
467 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: CREATE_AH failed "); | |
468 | dev_err(&rcfw->pdev->dev, | |
469 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
470 | resp->status, le16_to_cpu(req.cookie), | |
471 | le16_to_cpu(resp->cookie)); | |
472 | return -EINVAL; | |
473 | } | |
474 | ah->id = le32_to_cpu(resp->xid); | |
475 | return 0; | |
476 | } | |
477 | ||
478 | int bnxt_qplib_destroy_ah(struct bnxt_qplib_res *res, struct bnxt_qplib_ah *ah) | |
479 | { | |
480 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
481 | struct cmdq_destroy_ah req; | |
482 | struct creq_destroy_ah_resp *resp; | |
483 | u16 cmd_flags = 0; | |
484 | ||
485 | /* Clean up the AH table in the device */ | |
486 | RCFW_CMD_PREP(req, DESTROY_AH, cmd_flags); | |
487 | ||
488 | req.ah_cid = cpu_to_le32(ah->id); | |
489 | ||
490 | resp = (struct creq_destroy_ah_resp *) | |
491 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
492 | NULL, 1); | |
493 | if (!resp) { | |
494 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: DESTROY_AH send failed"); | |
495 | return -EINVAL; | |
496 | } | |
497 | if (!bnxt_qplib_rcfw_block_for_resp(rcfw, le16_to_cpu(req.cookie))) { | |
498 | /* Cmd timed out */ | |
499 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: DESTROY_AH timed out"); | |
500 | return -ETIMEDOUT; | |
501 | } | |
502 | if (resp->status || | |
503 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
504 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: DESTROY_AH failed "); | |
505 | dev_err(&rcfw->pdev->dev, | |
506 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
507 | resp->status, le16_to_cpu(req.cookie), | |
508 | le16_to_cpu(resp->cookie)); | |
509 | return -EINVAL; | |
510 | } | |
511 | return 0; | |
512 | } | |
513 | ||
514 | /* MRW */ | |
515 | int bnxt_qplib_free_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw) | |
516 | { | |
517 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
518 | struct cmdq_deallocate_key req; | |
519 | struct creq_deallocate_key_resp *resp; | |
520 | u16 cmd_flags = 0; | |
521 | ||
522 | if (mrw->lkey == 0xFFFFFFFF) { | |
523 | dev_info(&res->pdev->dev, | |
524 | "QPLIB: SP: Free a reserved lkey MRW"); | |
525 | return 0; | |
526 | } | |
527 | ||
528 | RCFW_CMD_PREP(req, DEALLOCATE_KEY, cmd_flags); | |
529 | ||
530 | req.mrw_flags = mrw->type; | |
531 | ||
532 | if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1) || | |
533 | (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A) || | |
534 | (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)) | |
535 | req.key = cpu_to_le32(mrw->rkey); | |
536 | else | |
537 | req.key = cpu_to_le32(mrw->lkey); | |
538 | ||
539 | resp = (struct creq_deallocate_key_resp *) | |
540 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
541 | NULL, 0); | |
542 | if (!resp) { | |
543 | dev_err(&res->pdev->dev, "QPLIB: SP: FREE_MR send failed"); | |
544 | return -EINVAL; | |
545 | } | |
546 | if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { | |
547 | /* Cmd timed out */ | |
548 | dev_err(&res->pdev->dev, "QPLIB: SP: FREE_MR timed out"); | |
549 | return -ETIMEDOUT; | |
550 | } | |
551 | if (resp->status || | |
552 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
553 | dev_err(&res->pdev->dev, "QPLIB: SP: FREE_MR failed "); | |
554 | dev_err(&res->pdev->dev, | |
555 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
556 | resp->status, le16_to_cpu(req.cookie), | |
557 | le16_to_cpu(resp->cookie)); | |
558 | return -EINVAL; | |
559 | } | |
560 | /* Free the qplib's MRW memory */ | |
561 | if (mrw->hwq.max_elements) | |
562 | bnxt_qplib_free_hwq(res->pdev, &mrw->hwq); | |
563 | ||
564 | return 0; | |
565 | } | |
566 | ||
567 | int bnxt_qplib_alloc_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw) | |
568 | { | |
569 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
570 | struct cmdq_allocate_mrw req; | |
571 | struct creq_allocate_mrw_resp *resp; | |
572 | u16 cmd_flags = 0; | |
573 | unsigned long tmp; | |
574 | ||
575 | RCFW_CMD_PREP(req, ALLOCATE_MRW, cmd_flags); | |
576 | ||
577 | req.pd_id = cpu_to_le32(mrw->pd->id); | |
578 | req.mrw_flags = mrw->type; | |
579 | if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_PMR && | |
580 | mrw->flags & BNXT_QPLIB_FR_PMR) || | |
581 | mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A || | |
582 | mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B) | |
583 | req.access = CMDQ_ALLOCATE_MRW_ACCESS_CONSUMER_OWNED_KEY; | |
584 | tmp = (unsigned long)mrw; | |
585 | req.mrw_handle = cpu_to_le64(tmp); | |
586 | ||
587 | resp = (struct creq_allocate_mrw_resp *) | |
588 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
589 | NULL, 0); | |
590 | if (!resp) { | |
591 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: ALLOC_MRW send failed"); | |
592 | return -EINVAL; | |
593 | } | |
594 | if (!bnxt_qplib_rcfw_wait_for_resp(rcfw, le16_to_cpu(req.cookie))) { | |
595 | /* Cmd timed out */ | |
596 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: ALLOC_MRW timed out"); | |
597 | return -ETIMEDOUT; | |
598 | } | |
599 | if (resp->status || | |
600 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
601 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: ALLOC_MRW failed "); | |
602 | dev_err(&rcfw->pdev->dev, | |
603 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
604 | resp->status, le16_to_cpu(req.cookie), | |
605 | le16_to_cpu(resp->cookie)); | |
606 | return -EINVAL; | |
607 | } | |
608 | if ((mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE1) || | |
609 | (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2A) || | |
610 | (mrw->type == CMDQ_ALLOCATE_MRW_MRW_FLAGS_MW_TYPE2B)) | |
611 | mrw->rkey = le32_to_cpu(resp->xid); | |
612 | else | |
613 | mrw->lkey = le32_to_cpu(resp->xid); | |
614 | return 0; | |
615 | } | |
616 | ||
617 | int bnxt_qplib_dereg_mrw(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mrw, | |
618 | bool block) | |
619 | { | |
620 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
621 | struct cmdq_deregister_mr req; | |
622 | struct creq_deregister_mr_resp *resp; | |
623 | u16 cmd_flags = 0; | |
624 | int rc; | |
625 | ||
626 | RCFW_CMD_PREP(req, DEREGISTER_MR, cmd_flags); | |
627 | ||
628 | req.lkey = cpu_to_le32(mrw->lkey); | |
629 | resp = (struct creq_deregister_mr_resp *) | |
630 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
631 | NULL, block); | |
632 | if (!resp) { | |
633 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: DEREG_MR send failed"); | |
634 | return -EINVAL; | |
635 | } | |
636 | if (block) | |
637 | rc = bnxt_qplib_rcfw_block_for_resp(rcfw, | |
638 | le16_to_cpu(req.cookie)); | |
639 | else | |
640 | rc = bnxt_qplib_rcfw_wait_for_resp(rcfw, | |
641 | le16_to_cpu(req.cookie)); | |
642 | if (!rc) { | |
643 | /* Cmd timed out */ | |
644 | dev_err(&res->pdev->dev, "QPLIB: SP: DEREG_MR timed out"); | |
645 | return -ETIMEDOUT; | |
646 | } | |
647 | if (resp->status || | |
648 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
649 | dev_err(&rcfw->pdev->dev, "QPLIB: SP: DEREG_MR failed "); | |
650 | dev_err(&rcfw->pdev->dev, | |
651 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
652 | resp->status, le16_to_cpu(req.cookie), | |
653 | le16_to_cpu(resp->cookie)); | |
654 | return -EINVAL; | |
655 | } | |
656 | ||
657 | /* Free the qplib's MR memory */ | |
658 | if (mrw->hwq.max_elements) { | |
659 | mrw->va = 0; | |
660 | mrw->total_size = 0; | |
661 | bnxt_qplib_free_hwq(res->pdev, &mrw->hwq); | |
662 | } | |
663 | ||
664 | return 0; | |
665 | } | |
666 | ||
667 | int bnxt_qplib_reg_mr(struct bnxt_qplib_res *res, struct bnxt_qplib_mrw *mr, | |
668 | u64 *pbl_tbl, int num_pbls, bool block) | |
669 | { | |
670 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
671 | struct cmdq_register_mr req; | |
672 | struct creq_register_mr_resp *resp; | |
673 | u16 cmd_flags = 0, level; | |
674 | int pg_ptrs, pages, i, rc; | |
675 | dma_addr_t **pbl_ptr; | |
676 | u32 pg_size; | |
677 | ||
678 | if (num_pbls) { | |
679 | pg_ptrs = roundup_pow_of_two(num_pbls); | |
680 | pages = pg_ptrs >> MAX_PBL_LVL_1_PGS_SHIFT; | |
681 | if (!pages) | |
682 | pages++; | |
683 | ||
684 | if (pages > MAX_PBL_LVL_1_PGS) { | |
685 | dev_err(&res->pdev->dev, "QPLIB: SP: Reg MR pages "); | |
686 | dev_err(&res->pdev->dev, | |
687 | "requested (0x%x) exceeded max (0x%x)", | |
688 | pages, MAX_PBL_LVL_1_PGS); | |
689 | return -ENOMEM; | |
690 | } | |
691 | /* Free the hwq if it already exist, must be a rereg */ | |
692 | if (mr->hwq.max_elements) | |
693 | bnxt_qplib_free_hwq(res->pdev, &mr->hwq); | |
694 | ||
695 | mr->hwq.max_elements = pages; | |
696 | rc = bnxt_qplib_alloc_init_hwq(res->pdev, &mr->hwq, NULL, 0, | |
697 | &mr->hwq.max_elements, | |
698 | PAGE_SIZE, 0, PAGE_SIZE, | |
699 | HWQ_TYPE_CTX); | |
700 | if (rc) { | |
701 | dev_err(&res->pdev->dev, | |
702 | "SP: Reg MR memory allocation failed"); | |
703 | return -ENOMEM; | |
704 | } | |
705 | /* Write to the hwq */ | |
706 | pbl_ptr = (dma_addr_t **)mr->hwq.pbl_ptr; | |
707 | for (i = 0; i < num_pbls; i++) | |
708 | pbl_ptr[PTR_PG(i)][PTR_IDX(i)] = | |
709 | (pbl_tbl[i] & PAGE_MASK) | PTU_PTE_VALID; | |
710 | } | |
711 | ||
712 | RCFW_CMD_PREP(req, REGISTER_MR, cmd_flags); | |
713 | ||
714 | /* Configure the request */ | |
715 | if (mr->hwq.level == PBL_LVL_MAX) { | |
716 | level = 0; | |
717 | req.pbl = 0; | |
718 | pg_size = PAGE_SIZE; | |
719 | } else { | |
720 | level = mr->hwq.level + 1; | |
721 | req.pbl = cpu_to_le64(mr->hwq.pbl[PBL_LVL_0].pg_map_arr[0]); | |
722 | pg_size = mr->hwq.pbl[PBL_LVL_0].pg_size; | |
723 | } | |
724 | req.log2_pg_size_lvl = (level << CMDQ_REGISTER_MR_LVL_SFT) | | |
725 | ((ilog2(pg_size) << | |
726 | CMDQ_REGISTER_MR_LOG2_PG_SIZE_SFT) & | |
727 | CMDQ_REGISTER_MR_LOG2_PG_SIZE_MASK); | |
728 | req.access = (mr->flags & 0xFFFF); | |
729 | req.va = cpu_to_le64(mr->va); | |
730 | req.key = cpu_to_le32(mr->lkey); | |
731 | req.mr_size = cpu_to_le64(mr->total_size); | |
732 | ||
733 | resp = (struct creq_register_mr_resp *) | |
734 | bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, | |
735 | NULL, block); | |
736 | if (!resp) { | |
737 | dev_err(&res->pdev->dev, "SP: REG_MR send failed"); | |
738 | rc = -EINVAL; | |
739 | goto fail; | |
740 | } | |
741 | if (block) | |
742 | rc = bnxt_qplib_rcfw_block_for_resp(rcfw, | |
743 | le16_to_cpu(req.cookie)); | |
744 | else | |
745 | rc = bnxt_qplib_rcfw_wait_for_resp(rcfw, | |
746 | le16_to_cpu(req.cookie)); | |
747 | if (!rc) { | |
748 | /* Cmd timed out */ | |
749 | dev_err(&res->pdev->dev, "SP: REG_MR timed out"); | |
750 | rc = -ETIMEDOUT; | |
751 | goto fail; | |
752 | } | |
753 | if (resp->status || | |
754 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
755 | dev_err(&res->pdev->dev, "QPLIB: SP: REG_MR failed "); | |
756 | dev_err(&res->pdev->dev, | |
757 | "QPLIB: SP: with status 0x%x cmdq 0x%x resp 0x%x", | |
758 | resp->status, le16_to_cpu(req.cookie), | |
759 | le16_to_cpu(resp->cookie)); | |
760 | rc = -EINVAL; | |
761 | goto fail; | |
762 | } | |
763 | return 0; | |
764 | ||
765 | fail: | |
766 | if (mr->hwq.max_elements) | |
767 | bnxt_qplib_free_hwq(res->pdev, &mr->hwq); | |
768 | return rc; | |
769 | } | |
770 | ||
771 | int bnxt_qplib_alloc_fast_reg_page_list(struct bnxt_qplib_res *res, | |
772 | struct bnxt_qplib_frpl *frpl, | |
773 | int max_pg_ptrs) | |
774 | { | |
775 | int pg_ptrs, pages, rc; | |
776 | ||
777 | /* Re-calculate the max to fit the HWQ allocation model */ | |
778 | pg_ptrs = roundup_pow_of_two(max_pg_ptrs); | |
779 | pages = pg_ptrs >> MAX_PBL_LVL_1_PGS_SHIFT; | |
780 | if (!pages) | |
781 | pages++; | |
782 | ||
783 | if (pages > MAX_PBL_LVL_1_PGS) | |
784 | return -ENOMEM; | |
785 | ||
786 | frpl->hwq.max_elements = pages; | |
787 | rc = bnxt_qplib_alloc_init_hwq(res->pdev, &frpl->hwq, NULL, 0, | |
788 | &frpl->hwq.max_elements, PAGE_SIZE, 0, | |
789 | PAGE_SIZE, HWQ_TYPE_CTX); | |
790 | if (!rc) | |
791 | frpl->max_pg_ptrs = pg_ptrs; | |
792 | ||
793 | return rc; | |
794 | } | |
795 | ||
796 | int bnxt_qplib_free_fast_reg_page_list(struct bnxt_qplib_res *res, | |
797 | struct bnxt_qplib_frpl *frpl) | |
798 | { | |
799 | bnxt_qplib_free_hwq(res->pdev, &frpl->hwq); | |
800 | return 0; | |
801 | } | |
802 | ||
803 | int bnxt_qplib_map_tc2cos(struct bnxt_qplib_res *res, u16 *cids) | |
804 | { | |
805 | struct bnxt_qplib_rcfw *rcfw = res->rcfw; | |
806 | struct cmdq_map_tc_to_cos req; | |
807 | struct creq_map_tc_to_cos_resp *resp; | |
808 | u16 cmd_flags = 0; | |
809 | int tleft; | |
810 | ||
811 | RCFW_CMD_PREP(req, MAP_TC_TO_COS, cmd_flags); | |
812 | req.cos0 = cpu_to_le16(cids[0]); | |
813 | req.cos1 = cpu_to_le16(cids[1]); | |
814 | ||
815 | resp = bnxt_qplib_rcfw_send_message(rcfw, (void *)&req, NULL, 0); | |
816 | if (!resp) { | |
817 | dev_err(&res->pdev->dev, "QPLIB: SP: MAP_TC2COS send failed"); | |
818 | return -EINVAL; | |
819 | } | |
820 | ||
821 | tleft = bnxt_qplib_rcfw_block_for_resp(rcfw, le16_to_cpu(req.cookie)); | |
822 | if (!tleft) { | |
823 | dev_err(&res->pdev->dev, "QPLIB: SP: MAP_TC2COS timed out"); | |
824 | return -ETIMEDOUT; | |
825 | } | |
826 | ||
827 | if (resp->status || | |
828 | le16_to_cpu(resp->cookie) != le16_to_cpu(req.cookie)) { | |
829 | dev_err(&res->pdev->dev, "QPLIB: SP: MAP_TC2COS failed "); | |
830 | dev_err(&res->pdev->dev, | |
831 | "QPLIB: with status 0x%x cmdq 0x%x resp 0x%x", | |
832 | resp->status, le16_to_cpu(req.cookie), | |
833 | le16_to_cpu(resp->cookie)); | |
834 | return -EINVAL; | |
835 | } | |
836 | ||
837 | return 0; | |
838 | } |