diff options
author | Amit Kumar Salecha <amit.salecha@qlogic.com> | 2010-01-12 19:37:25 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-01-16 04:17:15 -0500 |
commit | af19b49152bdb68fda894183e88096d6d1aa5c3d (patch) | |
tree | 811e6ecedf58c3356ac721a18d9a5270e9b2c40c /drivers/net/qlcnic/qlcnic_ctx.c | |
parent | 6eb3a8553345ba2b4efd5390709e158289b9ece4 (diff) |
qlcnic: Qlogic ethernet driver for CNA devices
o 1G/10G Ethernet Driver for Qlgic QLE8240 and QLE8242 CNA devices.
Signed-off-by: Amit Kumar Salecha <amit.salecha@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/qlcnic/qlcnic_ctx.c')
-rw-r--r-- | drivers/net/qlcnic/qlcnic_ctx.c | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/drivers/net/qlcnic/qlcnic_ctx.c b/drivers/net/qlcnic/qlcnic_ctx.c new file mode 100644 index 000000000000..71c16a183458 --- /dev/null +++ b/drivers/net/qlcnic/qlcnic_ctx.c | |||
@@ -0,0 +1,536 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 - QLogic Corporation. | ||
3 | * All rights reserved. | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU General Public License | ||
7 | * as published by the Free Software Foundation; either version 2 | ||
8 | * of the License, or (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, but | ||
11 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, | ||
18 | * MA 02111-1307, USA. | ||
19 | * | ||
20 | * The full GNU General Public License is included in this distribution | ||
21 | * in the file called "COPYING". | ||
22 | * | ||
23 | */ | ||
24 | |||
25 | #include "qlcnic.h" | ||
26 | |||
27 | #define QLCHAL_VERSION 1 | ||
28 | |||
29 | static u32 | ||
30 | qlcnic_poll_rsp(struct qlcnic_adapter *adapter) | ||
31 | { | ||
32 | u32 rsp; | ||
33 | int timeout = 0; | ||
34 | |||
35 | do { | ||
36 | /* give atleast 1ms for firmware to respond */ | ||
37 | msleep(1); | ||
38 | |||
39 | if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT) | ||
40 | return QLCNIC_CDRP_RSP_TIMEOUT; | ||
41 | |||
42 | rsp = QLCRD32(adapter, QLCNIC_CDRP_CRB_OFFSET); | ||
43 | } while (!QLCNIC_CDRP_IS_RSP(rsp)); | ||
44 | |||
45 | return rsp; | ||
46 | } | ||
47 | |||
48 | static u32 | ||
49 | qlcnic_issue_cmd(struct qlcnic_adapter *adapter, | ||
50 | u32 pci_fn, u32 version, u32 arg1, u32 arg2, u32 arg3, u32 cmd) | ||
51 | { | ||
52 | u32 rsp; | ||
53 | u32 signature; | ||
54 | u32 rcode = QLCNIC_RCODE_SUCCESS; | ||
55 | struct pci_dev *pdev = adapter->pdev; | ||
56 | |||
57 | signature = QLCNIC_CDRP_SIGNATURE_MAKE(pci_fn, version); | ||
58 | |||
59 | /* Acquire semaphore before accessing CRB */ | ||
60 | if (qlcnic_api_lock(adapter)) | ||
61 | return QLCNIC_RCODE_TIMEOUT; | ||
62 | |||
63 | QLCWR32(adapter, QLCNIC_SIGN_CRB_OFFSET, signature); | ||
64 | QLCWR32(adapter, QLCNIC_ARG1_CRB_OFFSET, arg1); | ||
65 | QLCWR32(adapter, QLCNIC_ARG2_CRB_OFFSET, arg2); | ||
66 | QLCWR32(adapter, QLCNIC_ARG3_CRB_OFFSET, arg3); | ||
67 | QLCWR32(adapter, QLCNIC_CDRP_CRB_OFFSET, QLCNIC_CDRP_FORM_CMD(cmd)); | ||
68 | |||
69 | rsp = qlcnic_poll_rsp(adapter); | ||
70 | |||
71 | if (rsp == QLCNIC_CDRP_RSP_TIMEOUT) { | ||
72 | dev_err(&pdev->dev, "card response timeout.\n"); | ||
73 | rcode = QLCNIC_RCODE_TIMEOUT; | ||
74 | } else if (rsp == QLCNIC_CDRP_RSP_FAIL) { | ||
75 | rcode = QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); | ||
76 | dev_err(&pdev->dev, "failed card response code:0x%x\n", | ||
77 | rcode); | ||
78 | } | ||
79 | |||
80 | /* Release semaphore */ | ||
81 | qlcnic_api_unlock(adapter); | ||
82 | |||
83 | return rcode; | ||
84 | } | ||
85 | |||
86 | int | ||
87 | qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu) | ||
88 | { | ||
89 | struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx; | ||
90 | |||
91 | if (recv_ctx->state == QLCNIC_HOST_CTX_STATE_ACTIVE) { | ||
92 | if (qlcnic_issue_cmd(adapter, | ||
93 | adapter->ahw.pci_func, | ||
94 | QLCHAL_VERSION, | ||
95 | recv_ctx->context_id, | ||
96 | mtu, | ||
97 | 0, | ||
98 | QLCNIC_CDRP_CMD_SET_MTU)) { | ||
99 | |||
100 | dev_err(&adapter->pdev->dev, "Failed to set mtu\n"); | ||
101 | return -EIO; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int | ||
109 | qlcnic_fw_cmd_create_rx_ctx(struct qlcnic_adapter *adapter) | ||
110 | { | ||
111 | void *addr; | ||
112 | struct qlcnic_hostrq_rx_ctx *prq; | ||
113 | struct qlcnic_cardrsp_rx_ctx *prsp; | ||
114 | struct qlcnic_hostrq_rds_ring *prq_rds; | ||
115 | struct qlcnic_hostrq_sds_ring *prq_sds; | ||
116 | struct qlcnic_cardrsp_rds_ring *prsp_rds; | ||
117 | struct qlcnic_cardrsp_sds_ring *prsp_sds; | ||
118 | struct qlcnic_host_rds_ring *rds_ring; | ||
119 | struct qlcnic_host_sds_ring *sds_ring; | ||
120 | |||
121 | dma_addr_t hostrq_phys_addr, cardrsp_phys_addr; | ||
122 | u64 phys_addr; | ||
123 | |||
124 | int i, nrds_rings, nsds_rings; | ||
125 | size_t rq_size, rsp_size; | ||
126 | u32 cap, reg, val; | ||
127 | int err; | ||
128 | |||
129 | struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx; | ||
130 | |||
131 | nrds_rings = adapter->max_rds_rings; | ||
132 | nsds_rings = adapter->max_sds_rings; | ||
133 | |||
134 | rq_size = | ||
135 | SIZEOF_HOSTRQ_RX(struct qlcnic_hostrq_rx_ctx, nrds_rings, | ||
136 | nsds_rings); | ||
137 | rsp_size = | ||
138 | SIZEOF_CARDRSP_RX(struct qlcnic_cardrsp_rx_ctx, nrds_rings, | ||
139 | nsds_rings); | ||
140 | |||
141 | addr = pci_alloc_consistent(adapter->pdev, | ||
142 | rq_size, &hostrq_phys_addr); | ||
143 | if (addr == NULL) | ||
144 | return -ENOMEM; | ||
145 | prq = (struct qlcnic_hostrq_rx_ctx *)addr; | ||
146 | |||
147 | addr = pci_alloc_consistent(adapter->pdev, | ||
148 | rsp_size, &cardrsp_phys_addr); | ||
149 | if (addr == NULL) { | ||
150 | err = -ENOMEM; | ||
151 | goto out_free_rq; | ||
152 | } | ||
153 | prsp = (struct qlcnic_cardrsp_rx_ctx *)addr; | ||
154 | |||
155 | prq->host_rsp_dma_addr = cpu_to_le64(cardrsp_phys_addr); | ||
156 | |||
157 | cap = (QLCNIC_CAP0_LEGACY_CONTEXT | QLCNIC_CAP0_LEGACY_MN); | ||
158 | cap |= (QLCNIC_CAP0_JUMBO_CONTIGUOUS | QLCNIC_CAP0_LRO_CONTIGUOUS); | ||
159 | |||
160 | prq->capabilities[0] = cpu_to_le32(cap); | ||
161 | prq->host_int_crb_mode = | ||
162 | cpu_to_le32(QLCNIC_HOST_INT_CRB_MODE_SHARED); | ||
163 | prq->host_rds_crb_mode = | ||
164 | cpu_to_le32(QLCNIC_HOST_RDS_CRB_MODE_UNIQUE); | ||
165 | |||
166 | prq->num_rds_rings = cpu_to_le16(nrds_rings); | ||
167 | prq->num_sds_rings = cpu_to_le16(nsds_rings); | ||
168 | prq->rds_ring_offset = cpu_to_le32(0); | ||
169 | |||
170 | val = le32_to_cpu(prq->rds_ring_offset) + | ||
171 | (sizeof(struct qlcnic_hostrq_rds_ring) * nrds_rings); | ||
172 | prq->sds_ring_offset = cpu_to_le32(val); | ||
173 | |||
174 | prq_rds = (struct qlcnic_hostrq_rds_ring *)(prq->data + | ||
175 | le32_to_cpu(prq->rds_ring_offset)); | ||
176 | |||
177 | for (i = 0; i < nrds_rings; i++) { | ||
178 | |||
179 | rds_ring = &recv_ctx->rds_rings[i]; | ||
180 | |||
181 | prq_rds[i].host_phys_addr = cpu_to_le64(rds_ring->phys_addr); | ||
182 | prq_rds[i].ring_size = cpu_to_le32(rds_ring->num_desc); | ||
183 | prq_rds[i].ring_kind = cpu_to_le32(i); | ||
184 | prq_rds[i].buff_size = cpu_to_le64(rds_ring->dma_size); | ||
185 | } | ||
186 | |||
187 | prq_sds = (struct qlcnic_hostrq_sds_ring *)(prq->data + | ||
188 | le32_to_cpu(prq->sds_ring_offset)); | ||
189 | |||
190 | for (i = 0; i < nsds_rings; i++) { | ||
191 | |||
192 | sds_ring = &recv_ctx->sds_rings[i]; | ||
193 | |||
194 | prq_sds[i].host_phys_addr = cpu_to_le64(sds_ring->phys_addr); | ||
195 | prq_sds[i].ring_size = cpu_to_le32(sds_ring->num_desc); | ||
196 | prq_sds[i].msi_index = cpu_to_le16(i); | ||
197 | } | ||
198 | |||
199 | phys_addr = hostrq_phys_addr; | ||
200 | err = qlcnic_issue_cmd(adapter, | ||
201 | adapter->ahw.pci_func, | ||
202 | QLCHAL_VERSION, | ||
203 | (u32)(phys_addr >> 32), | ||
204 | (u32)(phys_addr & 0xffffffff), | ||
205 | rq_size, | ||
206 | QLCNIC_CDRP_CMD_CREATE_RX_CTX); | ||
207 | if (err) { | ||
208 | dev_err(&adapter->pdev->dev, | ||
209 | "Failed to create rx ctx in firmware%d\n", err); | ||
210 | goto out_free_rsp; | ||
211 | } | ||
212 | |||
213 | |||
214 | prsp_rds = ((struct qlcnic_cardrsp_rds_ring *) | ||
215 | &prsp->data[le32_to_cpu(prsp->rds_ring_offset)]); | ||
216 | |||
217 | for (i = 0; i < le16_to_cpu(prsp->num_rds_rings); i++) { | ||
218 | rds_ring = &recv_ctx->rds_rings[i]; | ||
219 | |||
220 | reg = le32_to_cpu(prsp_rds[i].host_producer_crb); | ||
221 | rds_ring->crb_rcv_producer = qlcnic_get_ioaddr(adapter, | ||
222 | QLCNIC_REG(reg - 0x200)); | ||
223 | } | ||
224 | |||
225 | prsp_sds = ((struct qlcnic_cardrsp_sds_ring *) | ||
226 | &prsp->data[le32_to_cpu(prsp->sds_ring_offset)]); | ||
227 | |||
228 | for (i = 0; i < le16_to_cpu(prsp->num_sds_rings); i++) { | ||
229 | sds_ring = &recv_ctx->sds_rings[i]; | ||
230 | |||
231 | reg = le32_to_cpu(prsp_sds[i].host_consumer_crb); | ||
232 | sds_ring->crb_sts_consumer = qlcnic_get_ioaddr(adapter, | ||
233 | QLCNIC_REG(reg - 0x200)); | ||
234 | |||
235 | reg = le32_to_cpu(prsp_sds[i].interrupt_crb); | ||
236 | sds_ring->crb_intr_mask = qlcnic_get_ioaddr(adapter, | ||
237 | QLCNIC_REG(reg - 0x200)); | ||
238 | } | ||
239 | |||
240 | recv_ctx->state = le32_to_cpu(prsp->host_ctx_state); | ||
241 | recv_ctx->context_id = le16_to_cpu(prsp->context_id); | ||
242 | recv_ctx->virt_port = prsp->virt_port; | ||
243 | |||
244 | out_free_rsp: | ||
245 | pci_free_consistent(adapter->pdev, rsp_size, prsp, cardrsp_phys_addr); | ||
246 | out_free_rq: | ||
247 | pci_free_consistent(adapter->pdev, rq_size, prq, hostrq_phys_addr); | ||
248 | return err; | ||
249 | } | ||
250 | |||
251 | static void | ||
252 | qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter) | ||
253 | { | ||
254 | struct qlcnic_recv_context *recv_ctx = &adapter->recv_ctx; | ||
255 | |||
256 | if (qlcnic_issue_cmd(adapter, | ||
257 | adapter->ahw.pci_func, | ||
258 | QLCHAL_VERSION, | ||
259 | recv_ctx->context_id, | ||
260 | QLCNIC_DESTROY_CTX_RESET, | ||
261 | 0, | ||
262 | QLCNIC_CDRP_CMD_DESTROY_RX_CTX)) { | ||
263 | |||
264 | dev_err(&adapter->pdev->dev, | ||
265 | "Failed to destroy rx ctx in firmware\n"); | ||
266 | } | ||
267 | } | ||
268 | |||
269 | static int | ||
270 | qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter) | ||
271 | { | ||
272 | struct qlcnic_hostrq_tx_ctx *prq; | ||
273 | struct qlcnic_hostrq_cds_ring *prq_cds; | ||
274 | struct qlcnic_cardrsp_tx_ctx *prsp; | ||
275 | void *rq_addr, *rsp_addr; | ||
276 | size_t rq_size, rsp_size; | ||
277 | u32 temp; | ||
278 | int err; | ||
279 | u64 phys_addr; | ||
280 | dma_addr_t rq_phys_addr, rsp_phys_addr; | ||
281 | struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; | ||
282 | |||
283 | rq_size = SIZEOF_HOSTRQ_TX(struct qlcnic_hostrq_tx_ctx); | ||
284 | rq_addr = pci_alloc_consistent(adapter->pdev, | ||
285 | rq_size, &rq_phys_addr); | ||
286 | if (!rq_addr) | ||
287 | return -ENOMEM; | ||
288 | |||
289 | rsp_size = SIZEOF_CARDRSP_TX(struct qlcnic_cardrsp_tx_ctx); | ||
290 | rsp_addr = pci_alloc_consistent(adapter->pdev, | ||
291 | rsp_size, &rsp_phys_addr); | ||
292 | if (!rsp_addr) { | ||
293 | err = -ENOMEM; | ||
294 | goto out_free_rq; | ||
295 | } | ||
296 | |||
297 | memset(rq_addr, 0, rq_size); | ||
298 | prq = (struct qlcnic_hostrq_tx_ctx *)rq_addr; | ||
299 | |||
300 | memset(rsp_addr, 0, rsp_size); | ||
301 | prsp = (struct qlcnic_cardrsp_tx_ctx *)rsp_addr; | ||
302 | |||
303 | prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr); | ||
304 | |||
305 | temp = (QLCNIC_CAP0_LEGACY_CONTEXT | QLCNIC_CAP0_LEGACY_MN | | ||
306 | QLCNIC_CAP0_LSO); | ||
307 | prq->capabilities[0] = cpu_to_le32(temp); | ||
308 | |||
309 | prq->host_int_crb_mode = | ||
310 | cpu_to_le32(QLCNIC_HOST_INT_CRB_MODE_SHARED); | ||
311 | |||
312 | prq->interrupt_ctl = 0; | ||
313 | prq->msi_index = 0; | ||
314 | prq->cmd_cons_dma_addr = cpu_to_le64(tx_ring->hw_cons_phys_addr); | ||
315 | |||
316 | prq_cds = &prq->cds_ring; | ||
317 | |||
318 | prq_cds->host_phys_addr = cpu_to_le64(tx_ring->phys_addr); | ||
319 | prq_cds->ring_size = cpu_to_le32(tx_ring->num_desc); | ||
320 | |||
321 | phys_addr = rq_phys_addr; | ||
322 | err = qlcnic_issue_cmd(adapter, | ||
323 | adapter->ahw.pci_func, | ||
324 | QLCHAL_VERSION, | ||
325 | (u32)(phys_addr >> 32), | ||
326 | ((u32)phys_addr & 0xffffffff), | ||
327 | rq_size, | ||
328 | QLCNIC_CDRP_CMD_CREATE_TX_CTX); | ||
329 | |||
330 | if (err == QLCNIC_RCODE_SUCCESS) { | ||
331 | temp = le32_to_cpu(prsp->cds_ring.host_producer_crb); | ||
332 | tx_ring->crb_cmd_producer = qlcnic_get_ioaddr(adapter, | ||
333 | QLCNIC_REG(temp - 0x200)); | ||
334 | |||
335 | adapter->tx_context_id = | ||
336 | le16_to_cpu(prsp->context_id); | ||
337 | } else { | ||
338 | dev_err(&adapter->pdev->dev, | ||
339 | "Failed to create tx ctx in firmware%d\n", err); | ||
340 | err = -EIO; | ||
341 | } | ||
342 | |||
343 | pci_free_consistent(adapter->pdev, rsp_size, rsp_addr, rsp_phys_addr); | ||
344 | |||
345 | out_free_rq: | ||
346 | pci_free_consistent(adapter->pdev, rq_size, rq_addr, rq_phys_addr); | ||
347 | |||
348 | return err; | ||
349 | } | ||
350 | |||
351 | static void | ||
352 | qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter) | ||
353 | { | ||
354 | if (qlcnic_issue_cmd(adapter, | ||
355 | adapter->ahw.pci_func, | ||
356 | QLCHAL_VERSION, | ||
357 | adapter->tx_context_id, | ||
358 | QLCNIC_DESTROY_CTX_RESET, | ||
359 | 0, | ||
360 | QLCNIC_CDRP_CMD_DESTROY_TX_CTX)) { | ||
361 | |||
362 | dev_err(&adapter->pdev->dev, | ||
363 | "Failed to destroy tx ctx in firmware\n"); | ||
364 | } | ||
365 | } | ||
366 | |||
367 | int | ||
368 | qlcnic_fw_cmd_query_phy(struct qlcnic_adapter *adapter, u32 reg, u32 *val) | ||
369 | { | ||
370 | |||
371 | if (qlcnic_issue_cmd(adapter, | ||
372 | adapter->ahw.pci_func, | ||
373 | QLCHAL_VERSION, | ||
374 | reg, | ||
375 | 0, | ||
376 | 0, | ||
377 | QLCNIC_CDRP_CMD_READ_PHY)) { | ||
378 | |||
379 | return -EIO; | ||
380 | } | ||
381 | |||
382 | return QLCRD32(adapter, QLCNIC_ARG1_CRB_OFFSET); | ||
383 | } | ||
384 | |||
385 | int | ||
386 | qlcnic_fw_cmd_set_phy(struct qlcnic_adapter *adapter, u32 reg, u32 val) | ||
387 | { | ||
388 | return qlcnic_issue_cmd(adapter, | ||
389 | adapter->ahw.pci_func, | ||
390 | QLCHAL_VERSION, | ||
391 | reg, | ||
392 | val, | ||
393 | 0, | ||
394 | QLCNIC_CDRP_CMD_WRITE_PHY); | ||
395 | } | ||
396 | |||
397 | int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter) | ||
398 | { | ||
399 | void *addr; | ||
400 | int err; | ||
401 | int ring; | ||
402 | struct qlcnic_recv_context *recv_ctx; | ||
403 | struct qlcnic_host_rds_ring *rds_ring; | ||
404 | struct qlcnic_host_sds_ring *sds_ring; | ||
405 | struct qlcnic_host_tx_ring *tx_ring; | ||
406 | |||
407 | struct pci_dev *pdev = adapter->pdev; | ||
408 | |||
409 | recv_ctx = &adapter->recv_ctx; | ||
410 | tx_ring = adapter->tx_ring; | ||
411 | |||
412 | tx_ring->hw_consumer = (__le32 *)pci_alloc_consistent(pdev, sizeof(u32), | ||
413 | &tx_ring->hw_cons_phys_addr); | ||
414 | if (tx_ring->hw_consumer == NULL) { | ||
415 | dev_err(&pdev->dev, "failed to allocate tx consumer\n"); | ||
416 | return -ENOMEM; | ||
417 | } | ||
418 | *(tx_ring->hw_consumer) = 0; | ||
419 | |||
420 | /* cmd desc ring */ | ||
421 | addr = pci_alloc_consistent(pdev, TX_DESC_RINGSIZE(tx_ring), | ||
422 | &tx_ring->phys_addr); | ||
423 | |||
424 | if (addr == NULL) { | ||
425 | dev_err(&pdev->dev, "failed to allocate tx desc ring\n"); | ||
426 | return -ENOMEM; | ||
427 | } | ||
428 | |||
429 | tx_ring->desc_head = (struct cmd_desc_type0 *)addr; | ||
430 | |||
431 | for (ring = 0; ring < adapter->max_rds_rings; ring++) { | ||
432 | rds_ring = &recv_ctx->rds_rings[ring]; | ||
433 | addr = pci_alloc_consistent(adapter->pdev, | ||
434 | RCV_DESC_RINGSIZE(rds_ring), | ||
435 | &rds_ring->phys_addr); | ||
436 | if (addr == NULL) { | ||
437 | dev_err(&pdev->dev, | ||
438 | "failed to allocate rds ring [%d]\n", ring); | ||
439 | err = -ENOMEM; | ||
440 | goto err_out_free; | ||
441 | } | ||
442 | rds_ring->desc_head = (struct rcv_desc *)addr; | ||
443 | |||
444 | } | ||
445 | |||
446 | for (ring = 0; ring < adapter->max_sds_rings; ring++) { | ||
447 | sds_ring = &recv_ctx->sds_rings[ring]; | ||
448 | |||
449 | addr = pci_alloc_consistent(adapter->pdev, | ||
450 | STATUS_DESC_RINGSIZE(sds_ring), | ||
451 | &sds_ring->phys_addr); | ||
452 | if (addr == NULL) { | ||
453 | dev_err(&pdev->dev, | ||
454 | "failed to allocate sds ring [%d]\n", ring); | ||
455 | err = -ENOMEM; | ||
456 | goto err_out_free; | ||
457 | } | ||
458 | sds_ring->desc_head = (struct status_desc *)addr; | ||
459 | } | ||
460 | |||
461 | |||
462 | err = qlcnic_fw_cmd_create_rx_ctx(adapter); | ||
463 | if (err) | ||
464 | goto err_out_free; | ||
465 | err = qlcnic_fw_cmd_create_tx_ctx(adapter); | ||
466 | if (err) | ||
467 | goto err_out_free; | ||
468 | |||
469 | set_bit(__QLCNIC_FW_ATTACHED, &adapter->state); | ||
470 | return 0; | ||
471 | |||
472 | err_out_free: | ||
473 | qlcnic_free_hw_resources(adapter); | ||
474 | return err; | ||
475 | } | ||
476 | |||
477 | void qlcnic_free_hw_resources(struct qlcnic_adapter *adapter) | ||
478 | { | ||
479 | struct qlcnic_recv_context *recv_ctx; | ||
480 | struct qlcnic_host_rds_ring *rds_ring; | ||
481 | struct qlcnic_host_sds_ring *sds_ring; | ||
482 | struct qlcnic_host_tx_ring *tx_ring; | ||
483 | int ring; | ||
484 | |||
485 | |||
486 | if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) { | ||
487 | qlcnic_fw_cmd_destroy_rx_ctx(adapter); | ||
488 | qlcnic_fw_cmd_destroy_tx_ctx(adapter); | ||
489 | |||
490 | /* Allow dma queues to drain after context reset */ | ||
491 | msleep(20); | ||
492 | } | ||
493 | |||
494 | recv_ctx = &adapter->recv_ctx; | ||
495 | |||
496 | tx_ring = adapter->tx_ring; | ||
497 | if (tx_ring->hw_consumer != NULL) { | ||
498 | pci_free_consistent(adapter->pdev, | ||
499 | sizeof(u32), | ||
500 | tx_ring->hw_consumer, | ||
501 | tx_ring->hw_cons_phys_addr); | ||
502 | tx_ring->hw_consumer = NULL; | ||
503 | } | ||
504 | |||
505 | if (tx_ring->desc_head != NULL) { | ||
506 | pci_free_consistent(adapter->pdev, | ||
507 | TX_DESC_RINGSIZE(tx_ring), | ||
508 | tx_ring->desc_head, tx_ring->phys_addr); | ||
509 | tx_ring->desc_head = NULL; | ||
510 | } | ||
511 | |||
512 | for (ring = 0; ring < adapter->max_rds_rings; ring++) { | ||
513 | rds_ring = &recv_ctx->rds_rings[ring]; | ||
514 | |||
515 | if (rds_ring->desc_head != NULL) { | ||
516 | pci_free_consistent(adapter->pdev, | ||
517 | RCV_DESC_RINGSIZE(rds_ring), | ||
518 | rds_ring->desc_head, | ||
519 | rds_ring->phys_addr); | ||
520 | rds_ring->desc_head = NULL; | ||
521 | } | ||
522 | } | ||
523 | |||
524 | for (ring = 0; ring < adapter->max_sds_rings; ring++) { | ||
525 | sds_ring = &recv_ctx->sds_rings[ring]; | ||
526 | |||
527 | if (sds_ring->desc_head != NULL) { | ||
528 | pci_free_consistent(adapter->pdev, | ||
529 | STATUS_DESC_RINGSIZE(sds_ring), | ||
530 | sds_ring->desc_head, | ||
531 | sds_ring->phys_addr); | ||
532 | sds_ring->desc_head = NULL; | ||
533 | } | ||
534 | } | ||
535 | } | ||
536 | |||