1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2021 Broadcom. All Rights Reserved. The term |
4 | * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. |
5 | */ |
6 | |
7 | #include "efct_driver.h" |
8 | |
9 | #include "efct_hw.h" |
10 | #include "efct_unsol.h" |
11 | #include "efct_scsi.h" |
12 | |
13 | LIST_HEAD(efct_devices); |
14 | |
15 | static int logmask; |
16 | module_param(logmask, int, 0444); |
17 | MODULE_PARM_DESC(logmask, "logging bitmask (default 0)" ); |
18 | |
19 | static struct libefc_function_template efct_libefc_templ = { |
20 | .issue_mbox_rqst = efct_issue_mbox_rqst, |
21 | .send_els = efct_els_hw_srrs_send, |
22 | .send_bls = efct_efc_bls_send, |
23 | |
24 | .new_nport = efct_scsi_tgt_new_nport, |
25 | .del_nport = efct_scsi_tgt_del_nport, |
26 | .scsi_new_node = efct_scsi_new_initiator, |
27 | .scsi_del_node = efct_scsi_del_initiator, |
28 | .hw_seq_free = efct_efc_hw_sequence_free, |
29 | }; |
30 | |
31 | static int |
32 | efct_device_init(void) |
33 | { |
34 | int rc; |
35 | |
36 | /* driver-wide init for target-server */ |
37 | rc = efct_scsi_tgt_driver_init(); |
38 | if (rc) { |
39 | pr_err("efct_scsi_tgt_init failed rc=%d\n" , rc); |
40 | return rc; |
41 | } |
42 | |
43 | rc = efct_scsi_reg_fc_transport(); |
44 | if (rc) { |
45 | efct_scsi_tgt_driver_exit(); |
46 | pr_err("failed to register to FC host\n" ); |
47 | return rc; |
48 | } |
49 | |
50 | return 0; |
51 | } |
52 | |
53 | static void |
54 | efct_device_shutdown(void) |
55 | { |
56 | efct_scsi_release_fc_transport(); |
57 | |
58 | efct_scsi_tgt_driver_exit(); |
59 | } |
60 | |
61 | static void * |
62 | efct_device_alloc(u32 nid) |
63 | { |
64 | struct efct *efct = NULL; |
65 | |
66 | efct = kzalloc_node(size: sizeof(*efct), GFP_KERNEL, node: nid); |
67 | if (!efct) |
68 | return efct; |
69 | |
70 | INIT_LIST_HEAD(list: &efct->list_entry); |
71 | list_add_tail(new: &efct->list_entry, head: &efct_devices); |
72 | |
73 | return efct; |
74 | } |
75 | |
76 | static void |
77 | efct_teardown_msix(struct efct *efct) |
78 | { |
79 | u32 i; |
80 | |
81 | for (i = 0; i < efct->n_msix_vec; i++) { |
82 | free_irq(pci_irq_vector(dev: efct->pci, nr: i), |
83 | &efct->intr_context[i]); |
84 | } |
85 | |
86 | pci_free_irq_vectors(dev: efct->pci); |
87 | } |
88 | |
89 | static int |
90 | efct_efclib_config(struct efct *efct, struct libefc_function_template *tt) |
91 | { |
92 | struct efc *efc; |
93 | struct sli4 *sli; |
94 | int rc = 0; |
95 | |
96 | efc = kzalloc(size: sizeof(*efc), GFP_KERNEL); |
97 | if (!efc) |
98 | return -ENOMEM; |
99 | |
100 | efct->efcport = efc; |
101 | |
102 | memcpy(&efc->tt, tt, sizeof(*tt)); |
103 | efc->base = efct; |
104 | efc->pci = efct->pci; |
105 | |
106 | efc->def_wwnn = efct_get_wwnn(hw: &efct->hw); |
107 | efc->def_wwpn = efct_get_wwpn(hw: &efct->hw); |
108 | efc->enable_tgt = 1; |
109 | efc->log_level = EFC_LOG_LIB; |
110 | |
111 | sli = &efct->hw.sli; |
112 | efc->max_xfer_size = sli->sge_supported_length * |
113 | sli_get_max_sgl(sli4: &efct->hw.sli); |
114 | efc->sli = sli; |
115 | efc->fcfi = efct->hw.fcf_indicator; |
116 | |
117 | rc = efcport_init(efc); |
118 | if (rc) |
119 | efc_log_err(efc, "efcport_init failed\n" ); |
120 | |
121 | return rc; |
122 | } |
123 | |
124 | static int efct_request_firmware_update(struct efct *efct); |
125 | |
126 | static const char* |
127 | efct_pci_model(u16 device) |
128 | { |
129 | switch (device) { |
130 | case EFCT_DEVICE_LANCER_G6: return "LPE31004" ; |
131 | case EFCT_DEVICE_LANCER_G7: return "LPE36000" ; |
132 | default: return "unknown" ; |
133 | } |
134 | } |
135 | |
136 | static int |
137 | efct_device_attach(struct efct *efct) |
138 | { |
139 | u32 rc = 0, i = 0; |
140 | |
141 | if (efct->attached) { |
142 | efc_log_err(efct, "Device is already attached\n" ); |
143 | return -EIO; |
144 | } |
145 | |
146 | snprintf(buf: efct->name, size: sizeof(efct->name), fmt: "[%s%d] " , "fc" , |
147 | efct->instance_index); |
148 | |
149 | efct->logmask = logmask; |
150 | efct->filter_def = EFCT_DEFAULT_FILTER; |
151 | efct->max_isr_time_msec = EFCT_OS_MAX_ISR_TIME_MSEC; |
152 | |
153 | efct->model = efct_pci_model(device: efct->pci->device); |
154 | |
155 | efct->efct_req_fw_upgrade = true; |
156 | |
157 | /* Allocate transport object and bring online */ |
158 | efct->xport = efct_xport_alloc(efct); |
159 | if (!efct->xport) { |
160 | efc_log_err(efct, "failed to allocate transport object\n" ); |
161 | rc = -ENOMEM; |
162 | goto out; |
163 | } |
164 | |
165 | rc = efct_xport_attach(xport: efct->xport); |
166 | if (rc) { |
167 | efc_log_err(efct, "failed to attach transport object\n" ); |
168 | goto xport_out; |
169 | } |
170 | |
171 | rc = efct_xport_initialize(xport: efct->xport); |
172 | if (rc) { |
173 | efc_log_err(efct, "failed to initialize transport object\n" ); |
174 | goto xport_out; |
175 | } |
176 | |
177 | rc = efct_efclib_config(efct, tt: &efct_libefc_templ); |
178 | if (rc) { |
179 | efc_log_err(efct, "failed to init efclib\n" ); |
180 | goto efclib_out; |
181 | } |
182 | |
183 | for (i = 0; i < efct->n_msix_vec; i++) { |
184 | efc_log_debug(efct, "irq %d enabled\n" , i); |
185 | enable_irq(irq: pci_irq_vector(dev: efct->pci, nr: i)); |
186 | } |
187 | |
188 | efct->attached = true; |
189 | |
190 | if (efct->efct_req_fw_upgrade) |
191 | efct_request_firmware_update(efct); |
192 | |
193 | return rc; |
194 | |
195 | efclib_out: |
196 | efct_xport_detach(xport: efct->xport); |
197 | xport_out: |
198 | efct_xport_free(xport: efct->xport); |
199 | efct->xport = NULL; |
200 | out: |
201 | return rc; |
202 | } |
203 | |
204 | static int |
205 | efct_device_detach(struct efct *efct) |
206 | { |
207 | int i; |
208 | |
209 | if (!efct || !efct->attached) { |
210 | pr_err("Device is not attached\n" ); |
211 | return -EIO; |
212 | } |
213 | |
214 | if (efct_xport_control(xport: efct->xport, cmd: EFCT_XPORT_SHUTDOWN)) |
215 | efc_log_err(efct, "Transport Shutdown timed out\n" ); |
216 | |
217 | for (i = 0; i < efct->n_msix_vec; i++) |
218 | disable_irq(irq: pci_irq_vector(dev: efct->pci, nr: i)); |
219 | |
220 | efct_xport_detach(xport: efct->xport); |
221 | |
222 | efct_xport_free(xport: efct->xport); |
223 | efct->xport = NULL; |
224 | |
225 | efcport_destroy(efc: efct->efcport); |
226 | kfree(objp: efct->efcport); |
227 | |
228 | efct->attached = false; |
229 | |
230 | return 0; |
231 | } |
232 | |
233 | static void |
234 | efct_fw_write_cb(int status, u32 actual_write_length, |
235 | u32 change_status, void *arg) |
236 | { |
237 | struct efct_fw_write_result *result = arg; |
238 | |
239 | result->status = status; |
240 | result->actual_xfer = actual_write_length; |
241 | result->change_status = change_status; |
242 | |
243 | complete(&result->done); |
244 | } |
245 | |
246 | static int |
247 | efct_firmware_write(struct efct *efct, const u8 *buf, size_t buf_len, |
248 | u8 *change_status) |
249 | { |
250 | int rc = 0; |
251 | u32 bytes_left; |
252 | u32 xfer_size; |
253 | u32 offset; |
254 | struct efc_dma dma; |
255 | int last = 0; |
256 | struct efct_fw_write_result result; |
257 | |
258 | init_completion(x: &result.done); |
259 | |
260 | bytes_left = buf_len; |
261 | offset = 0; |
262 | |
263 | dma.size = FW_WRITE_BUFSIZE; |
264 | dma.virt = dma_alloc_coherent(dev: &efct->pci->dev, |
265 | size: dma.size, dma_handle: &dma.phys, GFP_KERNEL); |
266 | if (!dma.virt) |
267 | return -ENOMEM; |
268 | |
269 | while (bytes_left > 0) { |
270 | if (bytes_left > FW_WRITE_BUFSIZE) |
271 | xfer_size = FW_WRITE_BUFSIZE; |
272 | else |
273 | xfer_size = bytes_left; |
274 | |
275 | memcpy(dma.virt, buf + offset, xfer_size); |
276 | |
277 | if (bytes_left == xfer_size) |
278 | last = 1; |
279 | |
280 | efct_hw_firmware_write(hw: &efct->hw, dma: &dma, size: xfer_size, offset, |
281 | last, cb: efct_fw_write_cb, arg: &result); |
282 | |
283 | if (wait_for_completion_interruptible(x: &result.done) != 0) { |
284 | rc = -ENXIO; |
285 | break; |
286 | } |
287 | |
288 | if (result.actual_xfer == 0 || result.status != 0) { |
289 | rc = -EFAULT; |
290 | break; |
291 | } |
292 | |
293 | if (last) |
294 | *change_status = result.change_status; |
295 | |
296 | bytes_left -= result.actual_xfer; |
297 | offset += result.actual_xfer; |
298 | } |
299 | |
300 | dma_free_coherent(dev: &efct->pci->dev, size: dma.size, cpu_addr: dma.virt, dma_handle: dma.phys); |
301 | return rc; |
302 | } |
303 | |
304 | static int |
305 | efct_fw_reset(struct efct *efct) |
306 | { |
307 | /* |
308 | * Firmware reset to activate the new firmware. |
309 | * Function 0 will update and load the new firmware |
310 | * during attach. |
311 | */ |
312 | if (timer_pending(timer: &efct->xport->stats_timer)) |
313 | del_timer(timer: &efct->xport->stats_timer); |
314 | |
315 | if (efct_hw_reset(hw: &efct->hw, reset: EFCT_HW_RESET_FIRMWARE)) { |
316 | efc_log_info(efct, "failed to reset firmware\n" ); |
317 | return -EIO; |
318 | } |
319 | |
320 | efc_log_info(efct, "successfully reset firmware.Now resetting port\n" ); |
321 | |
322 | efct_device_detach(efct); |
323 | return efct_device_attach(efct); |
324 | } |
325 | |
326 | static int |
327 | efct_request_firmware_update(struct efct *efct) |
328 | { |
329 | int rc = 0; |
330 | u8 file_name[256], fw_change_status = 0; |
331 | const struct firmware *fw; |
332 | struct efct_hw_grp_hdr *fw_image; |
333 | |
334 | snprintf(buf: file_name, size: 256, fmt: "%s.grp" , efct->model); |
335 | |
336 | rc = request_firmware(fw: &fw, name: file_name, device: &efct->pci->dev); |
337 | if (rc) { |
338 | efc_log_debug(efct, "Firmware file(%s) not found.\n" , file_name); |
339 | return rc; |
340 | } |
341 | |
342 | fw_image = (struct efct_hw_grp_hdr *)fw->data; |
343 | |
344 | if (!strncmp(efct->hw.sli.fw_name[0], fw_image->revision, |
345 | strnlen(p: fw_image->revision, maxlen: 16))) { |
346 | efc_log_debug(efct, |
347 | "Skip update. Firmware is already up to date.\n" ); |
348 | goto exit; |
349 | } |
350 | |
351 | efc_log_info(efct, "Firmware update is initiated. %s -> %s\n" , |
352 | efct->hw.sli.fw_name[0], fw_image->revision); |
353 | |
354 | rc = efct_firmware_write(efct, buf: fw->data, buf_len: fw->size, change_status: &fw_change_status); |
355 | if (rc) { |
356 | efc_log_err(efct, "Firmware update failed. rc = %d\n" , rc); |
357 | goto exit; |
358 | } |
359 | |
360 | efc_log_info(efct, "Firmware updated successfully\n" ); |
361 | switch (fw_change_status) { |
362 | case 0x00: |
363 | efc_log_info(efct, "New firmware is active.\n" ); |
364 | break; |
365 | case 0x01: |
366 | efc_log_info(efct, |
367 | "System reboot needed to activate the new firmware\n" ); |
368 | break; |
369 | case 0x02: |
370 | case 0x03: |
371 | efc_log_info(efct, |
372 | "firmware reset to activate the new firmware\n" ); |
373 | efct_fw_reset(efct); |
374 | break; |
375 | default: |
376 | efc_log_info(efct, "Unexpected value change_status:%d\n" , |
377 | fw_change_status); |
378 | break; |
379 | } |
380 | |
381 | exit: |
382 | release_firmware(fw); |
383 | |
384 | return rc; |
385 | } |
386 | |
387 | static void |
388 | efct_device_free(struct efct *efct) |
389 | { |
390 | if (efct) { |
391 | list_del(entry: &efct->list_entry); |
392 | kfree(objp: efct); |
393 | } |
394 | } |
395 | |
396 | static int |
397 | efct_device_interrupts_required(struct efct *efct) |
398 | { |
399 | int rc; |
400 | |
401 | rc = efct_hw_setup(hw: &efct->hw, os: efct, pdev: efct->pci); |
402 | if (rc < 0) |
403 | return rc; |
404 | |
405 | return efct->hw.config.n_eq; |
406 | } |
407 | |
408 | static irqreturn_t |
409 | efct_intr_thread(int irq, void *handle) |
410 | { |
411 | struct efct_intr_context *intr_ctx = handle; |
412 | struct efct *efct = intr_ctx->efct; |
413 | |
414 | efct_hw_process(hw: &efct->hw, vector: intr_ctx->index, max_isr_time_msec: efct->max_isr_time_msec); |
415 | return IRQ_HANDLED; |
416 | } |
417 | |
418 | static irqreturn_t |
419 | efct_intr_msix(int irq, void *handle) |
420 | { |
421 | return IRQ_WAKE_THREAD; |
422 | } |
423 | |
424 | static int |
425 | efct_setup_msix(struct efct *efct, u32 num_intrs) |
426 | { |
427 | int rc = 0, i; |
428 | |
429 | if (!pci_find_capability(dev: efct->pci, PCI_CAP_ID_MSIX)) { |
430 | dev_err(&efct->pci->dev, |
431 | "%s : MSI-X not available\n" , __func__); |
432 | return -EIO; |
433 | } |
434 | |
435 | efct->n_msix_vec = num_intrs; |
436 | |
437 | rc = pci_alloc_irq_vectors(dev: efct->pci, min_vecs: num_intrs, max_vecs: num_intrs, |
438 | PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); |
439 | |
440 | if (rc < 0) { |
441 | dev_err(&efct->pci->dev, "Failed to alloc irq : %d\n" , rc); |
442 | return rc; |
443 | } |
444 | |
445 | for (i = 0; i < num_intrs; i++) { |
446 | struct efct_intr_context *intr_ctx = NULL; |
447 | |
448 | intr_ctx = &efct->intr_context[i]; |
449 | intr_ctx->efct = efct; |
450 | intr_ctx->index = i; |
451 | |
452 | rc = request_threaded_irq(irq: pci_irq_vector(dev: efct->pci, nr: i), |
453 | handler: efct_intr_msix, thread_fn: efct_intr_thread, flags: 0, |
454 | EFCT_DRIVER_NAME, dev: intr_ctx); |
455 | if (rc) { |
456 | dev_err(&efct->pci->dev, |
457 | "Failed to register %d vector: %d\n" , i, rc); |
458 | goto out; |
459 | } |
460 | } |
461 | |
462 | return rc; |
463 | |
464 | out: |
465 | while (--i >= 0) |
466 | free_irq(pci_irq_vector(dev: efct->pci, nr: i), |
467 | &efct->intr_context[i]); |
468 | |
469 | pci_free_irq_vectors(dev: efct->pci); |
470 | return rc; |
471 | } |
472 | |
473 | static struct pci_device_id efct_pci_table[] = { |
474 | {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G6), 0}, |
475 | {PCI_DEVICE(EFCT_VENDOR_ID, EFCT_DEVICE_LANCER_G7), 0}, |
476 | {} /* terminate list */ |
477 | }; |
478 | |
479 | static int |
480 | efct_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
481 | { |
482 | struct efct *efct = NULL; |
483 | int rc; |
484 | u32 i, r; |
485 | int num_interrupts = 0; |
486 | int nid; |
487 | |
488 | dev_info(&pdev->dev, "%s\n" , EFCT_DRIVER_NAME); |
489 | |
490 | rc = pci_enable_device_mem(dev: pdev); |
491 | if (rc) |
492 | return rc; |
493 | |
494 | pci_set_master(dev: pdev); |
495 | |
496 | rc = pci_set_mwi(dev: pdev); |
497 | if (rc) { |
498 | dev_info(&pdev->dev, "pci_set_mwi returned %d\n" , rc); |
499 | goto mwi_out; |
500 | } |
501 | |
502 | rc = pci_request_regions(pdev, EFCT_DRIVER_NAME); |
503 | if (rc) { |
504 | dev_err(&pdev->dev, "pci_request_regions failed %d\n" , rc); |
505 | goto req_regions_out; |
506 | } |
507 | |
508 | /* Fetch the Numa node id for this device */ |
509 | nid = dev_to_node(dev: &pdev->dev); |
510 | if (nid < 0) { |
511 | dev_err(&pdev->dev, "Warning Numa node ID is %d\n" , nid); |
512 | nid = 0; |
513 | } |
514 | |
515 | /* Allocate efct */ |
516 | efct = efct_device_alloc(nid); |
517 | if (!efct) { |
518 | dev_err(&pdev->dev, "Failed to allocate efct\n" ); |
519 | rc = -ENOMEM; |
520 | goto alloc_out; |
521 | } |
522 | |
523 | efct->pci = pdev; |
524 | efct->numa_node = nid; |
525 | |
526 | /* Map all memory BARs */ |
527 | for (i = 0, r = 0; i < EFCT_PCI_MAX_REGS; i++) { |
528 | if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) { |
529 | efct->reg[r] = ioremap(pci_resource_start(pdev, i), |
530 | pci_resource_len(pdev, i)); |
531 | r++; |
532 | } |
533 | |
534 | /* |
535 | * If the 64-bit attribute is set, both this BAR and the |
536 | * next form the complete address. Skip processing the |
537 | * next BAR. |
538 | */ |
539 | if (pci_resource_flags(pdev, i) & IORESOURCE_MEM_64) |
540 | i++; |
541 | } |
542 | |
543 | pci_set_drvdata(pdev, data: efct); |
544 | |
545 | rc = dma_set_mask_and_coherent(dev: &pdev->dev, DMA_BIT_MASK(64)); |
546 | if (rc) { |
547 | dev_err(&pdev->dev, "setting DMA_BIT_MASK failed\n" ); |
548 | goto dma_mask_out; |
549 | } |
550 | |
551 | num_interrupts = efct_device_interrupts_required(efct); |
552 | if (num_interrupts < 0) { |
553 | efc_log_err(efct, "efct_device_interrupts_required failed\n" ); |
554 | rc = -1; |
555 | goto dma_mask_out; |
556 | } |
557 | |
558 | /* |
559 | * Initialize MSIX interrupts, note, |
560 | * efct_setup_msix() enables the interrupt |
561 | */ |
562 | rc = efct_setup_msix(efct, num_intrs: num_interrupts); |
563 | if (rc) { |
564 | dev_err(&pdev->dev, "Can't setup msix\n" ); |
565 | goto dma_mask_out; |
566 | } |
567 | /* Disable interrupt for now */ |
568 | for (i = 0; i < efct->n_msix_vec; i++) { |
569 | efc_log_debug(efct, "irq %d disabled\n" , i); |
570 | disable_irq(irq: pci_irq_vector(dev: efct->pci, nr: i)); |
571 | } |
572 | |
573 | rc = efct_device_attach(efct); |
574 | if (rc) |
575 | goto attach_out; |
576 | |
577 | return 0; |
578 | |
579 | attach_out: |
580 | efct_teardown_msix(efct); |
581 | dma_mask_out: |
582 | pci_set_drvdata(pdev, NULL); |
583 | |
584 | for (i = 0; i < EFCT_PCI_MAX_REGS; i++) { |
585 | if (efct->reg[i]) |
586 | iounmap(addr: efct->reg[i]); |
587 | } |
588 | efct_device_free(efct); |
589 | alloc_out: |
590 | pci_release_regions(pdev); |
591 | req_regions_out: |
592 | pci_clear_mwi(dev: pdev); |
593 | mwi_out: |
594 | pci_disable_device(dev: pdev); |
595 | return rc; |
596 | } |
597 | |
598 | static void |
599 | efct_pci_remove(struct pci_dev *pdev) |
600 | { |
601 | struct efct *efct = pci_get_drvdata(pdev); |
602 | u32 i; |
603 | |
604 | if (!efct) |
605 | return; |
606 | |
607 | efct_device_detach(efct); |
608 | |
609 | efct_teardown_msix(efct); |
610 | |
611 | for (i = 0; i < EFCT_PCI_MAX_REGS; i++) { |
612 | if (efct->reg[i]) |
613 | iounmap(addr: efct->reg[i]); |
614 | } |
615 | |
616 | pci_set_drvdata(pdev, NULL); |
617 | |
618 | efct_device_free(efct); |
619 | |
620 | pci_release_regions(pdev); |
621 | |
622 | pci_disable_device(dev: pdev); |
623 | } |
624 | |
625 | static void |
626 | efct_device_prep_for_reset(struct efct *efct, struct pci_dev *pdev) |
627 | { |
628 | if (efct) { |
629 | efc_log_debug(efct, |
630 | "PCI channel disable preparing for reset\n" ); |
631 | efct_device_detach(efct); |
632 | /* Disable interrupt and pci device */ |
633 | efct_teardown_msix(efct); |
634 | } |
635 | pci_disable_device(dev: pdev); |
636 | } |
637 | |
638 | static void |
639 | efct_device_prep_for_recover(struct efct *efct) |
640 | { |
641 | if (efct) { |
642 | efc_log_debug(efct, "PCI channel preparing for recovery\n" ); |
643 | efct_hw_io_abort_all(hw: &efct->hw); |
644 | } |
645 | } |
646 | |
647 | /** |
648 | * efct_pci_io_error_detected - method for handling PCI I/O error |
649 | * @pdev: pointer to PCI device. |
650 | * @state: the current PCI connection state. |
651 | * |
652 | * This routine is registered to the PCI subsystem for error handling. This |
653 | * function is called by the PCI subsystem after a PCI bus error affecting |
654 | * this device has been detected. When this routine is invoked, it dispatches |
655 | * device error detected handling routine, which will perform the proper |
656 | * error detected operation. |
657 | * |
658 | * Return codes |
659 | * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery |
660 | * PCI_ERS_RESULT_DISCONNECT - device could not be recovered |
661 | */ |
662 | static pci_ers_result_t |
663 | efct_pci_io_error_detected(struct pci_dev *pdev, pci_channel_state_t state) |
664 | { |
665 | struct efct *efct = pci_get_drvdata(pdev); |
666 | pci_ers_result_t rc; |
667 | |
668 | switch (state) { |
669 | case pci_channel_io_normal: |
670 | efct_device_prep_for_recover(efct); |
671 | rc = PCI_ERS_RESULT_CAN_RECOVER; |
672 | break; |
673 | case pci_channel_io_frozen: |
674 | efct_device_prep_for_reset(efct, pdev); |
675 | rc = PCI_ERS_RESULT_NEED_RESET; |
676 | break; |
677 | case pci_channel_io_perm_failure: |
678 | efct_device_detach(efct); |
679 | rc = PCI_ERS_RESULT_DISCONNECT; |
680 | break; |
681 | default: |
682 | efc_log_debug(efct, "Unknown PCI error state:0x%x\n" , state); |
683 | efct_device_prep_for_reset(efct, pdev); |
684 | rc = PCI_ERS_RESULT_NEED_RESET; |
685 | break; |
686 | } |
687 | |
688 | return rc; |
689 | } |
690 | |
691 | static pci_ers_result_t |
692 | efct_pci_io_slot_reset(struct pci_dev *pdev) |
693 | { |
694 | int rc; |
695 | struct efct *efct = pci_get_drvdata(pdev); |
696 | |
697 | rc = pci_enable_device_mem(dev: pdev); |
698 | if (rc) { |
699 | efc_log_err(efct, "failed to enable PCI device after reset\n" ); |
700 | return PCI_ERS_RESULT_DISCONNECT; |
701 | } |
702 | |
703 | /* |
704 | * As the new kernel behavior of pci_restore_state() API call clears |
705 | * device saved_state flag, need to save the restored state again. |
706 | */ |
707 | |
708 | pci_save_state(dev: pdev); |
709 | |
710 | pci_set_master(dev: pdev); |
711 | |
712 | rc = efct_setup_msix(efct, num_intrs: efct->n_msix_vec); |
713 | if (rc) |
714 | efc_log_err(efct, "rc %d returned, IRQ allocation failed\n" , |
715 | rc); |
716 | |
717 | /* Perform device reset */ |
718 | efct_device_detach(efct); |
719 | /* Bring device to online*/ |
720 | efct_device_attach(efct); |
721 | |
722 | return PCI_ERS_RESULT_RECOVERED; |
723 | } |
724 | |
725 | static void |
726 | efct_pci_io_resume(struct pci_dev *pdev) |
727 | { |
728 | struct efct *efct = pci_get_drvdata(pdev); |
729 | |
730 | /* Perform device reset */ |
731 | efct_device_detach(efct); |
732 | /* Bring device to online*/ |
733 | efct_device_attach(efct); |
734 | } |
735 | |
736 | MODULE_DEVICE_TABLE(pci, efct_pci_table); |
737 | |
738 | static struct pci_error_handlers efct_pci_err_handler = { |
739 | .error_detected = efct_pci_io_error_detected, |
740 | .slot_reset = efct_pci_io_slot_reset, |
741 | .resume = efct_pci_io_resume, |
742 | }; |
743 | |
744 | static struct pci_driver efct_pci_driver = { |
745 | .name = EFCT_DRIVER_NAME, |
746 | .id_table = efct_pci_table, |
747 | .probe = efct_pci_probe, |
748 | .remove = efct_pci_remove, |
749 | .err_handler = &efct_pci_err_handler, |
750 | }; |
751 | |
752 | static |
753 | int __init efct_init(void) |
754 | { |
755 | int rc; |
756 | |
757 | rc = efct_device_init(); |
758 | if (rc) { |
759 | pr_err("efct_device_init failed rc=%d\n" , rc); |
760 | return rc; |
761 | } |
762 | |
763 | rc = pci_register_driver(&efct_pci_driver); |
764 | if (rc) { |
765 | pr_err("pci_register_driver failed rc=%d\n" , rc); |
766 | efct_device_shutdown(); |
767 | } |
768 | |
769 | return rc; |
770 | } |
771 | |
772 | static void __exit efct_exit(void) |
773 | { |
774 | pci_unregister_driver(dev: &efct_pci_driver); |
775 | efct_device_shutdown(); |
776 | } |
777 | |
778 | module_init(efct_init); |
779 | module_exit(efct_exit); |
780 | MODULE_VERSION(EFCT_DRIVER_VERSION); |
781 | MODULE_LICENSE("GPL" ); |
782 | MODULE_AUTHOR("Broadcom" ); |
783 | |