.. | .. |
---|
4 | 4 | * Queue Interface backend functionality |
---|
5 | 5 | * |
---|
6 | 6 | * Copyright 2013-2016 Freescale Semiconductor, Inc. |
---|
7 | | - * Copyright 2016-2017 NXP |
---|
| 7 | + * Copyright 2016-2017, 2019-2020 NXP |
---|
8 | 8 | */ |
---|
9 | 9 | |
---|
10 | 10 | #include <linux/cpumask.h> |
---|
11 | 11 | #include <linux/kthread.h> |
---|
12 | 12 | #include <soc/fsl/qman.h> |
---|
13 | 13 | |
---|
| 14 | +#include "debugfs.h" |
---|
14 | 15 | #include "regs.h" |
---|
15 | 16 | #include "qi.h" |
---|
16 | 17 | #include "desc.h" |
---|
.. | .. |
---|
18 | 19 | #include "desc_constr.h" |
---|
19 | 20 | |
---|
20 | 21 | #define PREHDR_RSLS_SHIFT 31 |
---|
| 22 | +#define PREHDR_ABS BIT(25) |
---|
21 | 23 | |
---|
22 | 24 | /* |
---|
23 | 25 | * Use a reasonable backlog of frames (per CPU) as congestion threshold, |
---|
.. | .. |
---|
58 | 60 | /* |
---|
59 | 61 | * caam_qi_priv - CAAM QI backend private params |
---|
60 | 62 | * @cgr: QMan congestion group |
---|
61 | | - * @qi_pdev: platform device for QI backend |
---|
62 | 63 | */ |
---|
63 | 64 | struct caam_qi_priv { |
---|
64 | 65 | struct qman_cgr cgr; |
---|
65 | | - struct platform_device *qi_pdev; |
---|
66 | 66 | }; |
---|
67 | 67 | |
---|
68 | 68 | static struct caam_qi_priv qipriv ____cacheline_aligned; |
---|
.. | .. |
---|
73 | 73 | */ |
---|
74 | 74 | bool caam_congested __read_mostly; |
---|
75 | 75 | EXPORT_SYMBOL(caam_congested); |
---|
76 | | - |
---|
77 | | -#ifdef CONFIG_DEBUG_FS |
---|
78 | | -/* |
---|
79 | | - * This is a counter for the number of times the congestion group (where all |
---|
80 | | - * the request and response queueus are) reached congestion. Incremented |
---|
81 | | - * each time the congestion callback is called with congested == true. |
---|
82 | | - */ |
---|
83 | | -static u64 times_congested; |
---|
84 | | -#endif |
---|
85 | 76 | |
---|
86 | 77 | /* |
---|
87 | 78 | * This is a a cache of buffers, from which the users of CAAM QI driver |
---|
.. | .. |
---|
94 | 85 | * NOTE: The memcache is SMP-safe. No need to handle spinlocks in-here |
---|
95 | 86 | */ |
---|
96 | 87 | static struct kmem_cache *qi_cache; |
---|
| 88 | + |
---|
| 89 | +static void *caam_iova_to_virt(struct iommu_domain *domain, |
---|
| 90 | + dma_addr_t iova_addr) |
---|
| 91 | +{ |
---|
| 92 | + phys_addr_t phys_addr; |
---|
| 93 | + |
---|
| 94 | + phys_addr = domain ? iommu_iova_to_phys(domain, iova_addr) : iova_addr; |
---|
| 95 | + |
---|
| 96 | + return phys_to_virt(phys_addr); |
---|
| 97 | +} |
---|
97 | 98 | |
---|
98 | 99 | int caam_qi_enqueue(struct device *qidev, struct caam_drv_req *req) |
---|
99 | 100 | { |
---|
.. | .. |
---|
115 | 116 | |
---|
116 | 117 | do { |
---|
117 | 118 | ret = qman_enqueue(req->drv_ctx->req_fq, &fd); |
---|
118 | | - if (likely(!ret)) |
---|
| 119 | + if (likely(!ret)) { |
---|
| 120 | + refcount_inc(&req->drv_ctx->refcnt); |
---|
119 | 121 | return 0; |
---|
| 122 | + } |
---|
120 | 123 | |
---|
121 | 124 | if (ret != -EBUSY) |
---|
122 | 125 | break; |
---|
.. | .. |
---|
135 | 138 | const struct qm_fd *fd; |
---|
136 | 139 | struct caam_drv_req *drv_req; |
---|
137 | 140 | struct device *qidev = &(raw_cpu_ptr(&pcpu_qipriv)->net_dev.dev); |
---|
| 141 | + struct caam_drv_private *priv = dev_get_drvdata(qidev); |
---|
138 | 142 | |
---|
139 | 143 | fd = &msg->ern.fd; |
---|
140 | 144 | |
---|
141 | | - if (qm_fd_get_format(fd) != qm_fd_compound) { |
---|
142 | | - dev_err(qidev, "Non-compound FD from CAAM\n"); |
---|
143 | | - return; |
---|
144 | | - } |
---|
145 | | - |
---|
146 | | - drv_req = (struct caam_drv_req *)phys_to_virt(qm_fd_addr_get64(fd)); |
---|
| 145 | + drv_req = caam_iova_to_virt(priv->domain, qm_fd_addr_get64(fd)); |
---|
147 | 146 | if (!drv_req) { |
---|
148 | 147 | dev_err(qidev, |
---|
149 | 148 | "Can't find original request for CAAM response\n"); |
---|
150 | 149 | return; |
---|
151 | 150 | } |
---|
152 | 151 | |
---|
| 152 | + refcount_dec(&drv_req->drv_ctx->refcnt); |
---|
| 153 | + |
---|
| 154 | + if (qm_fd_get_format(fd) != qm_fd_compound) { |
---|
| 155 | + dev_err(qidev, "Non-compound FD from CAAM\n"); |
---|
| 156 | + return; |
---|
| 157 | + } |
---|
| 158 | + |
---|
153 | 159 | dma_unmap_single(drv_req->drv_ctx->qidev, qm_fd_addr(fd), |
---|
154 | 160 | sizeof(drv_req->fd_sgt), DMA_BIDIRECTIONAL); |
---|
155 | 161 | |
---|
156 | | - drv_req->cbk(drv_req, -EIO); |
---|
| 162 | + if (fd->status) |
---|
| 163 | + drv_req->cbk(drv_req, be32_to_cpu(fd->status)); |
---|
| 164 | + else |
---|
| 165 | + drv_req->cbk(drv_req, JRSTA_SSRC_QI); |
---|
157 | 166 | } |
---|
158 | 167 | |
---|
159 | 168 | static struct qman_fq *create_caam_req_fq(struct device *qidev, |
---|
.. | .. |
---|
274 | 283 | return ret; |
---|
275 | 284 | } |
---|
276 | 285 | |
---|
277 | | -static int empty_caam_fq(struct qman_fq *fq) |
---|
| 286 | +static int empty_caam_fq(struct qman_fq *fq, struct caam_drv_ctx *drv_ctx) |
---|
278 | 287 | { |
---|
279 | 288 | int ret; |
---|
| 289 | + int retries = 10; |
---|
280 | 290 | struct qm_mcr_queryfq_np np; |
---|
281 | 291 | |
---|
282 | 292 | /* Wait till the older CAAM FQ get empty */ |
---|
.. | .. |
---|
291 | 301 | msleep(20); |
---|
292 | 302 | } while (1); |
---|
293 | 303 | |
---|
294 | | - /* |
---|
295 | | - * Give extra time for pending jobs from this FQ in holding tanks |
---|
296 | | - * to get processed |
---|
297 | | - */ |
---|
298 | | - msleep(20); |
---|
| 304 | + /* Wait until pending jobs from this FQ are processed by CAAM */ |
---|
| 305 | + do { |
---|
| 306 | + if (refcount_read(&drv_ctx->refcnt) == 1) |
---|
| 307 | + break; |
---|
| 308 | + |
---|
| 309 | + msleep(20); |
---|
| 310 | + } while (--retries); |
---|
| 311 | + |
---|
| 312 | + if (!retries) |
---|
| 313 | + dev_warn_once(drv_ctx->qidev, "%d frames from FQID %u still pending in CAAM\n", |
---|
| 314 | + refcount_read(&drv_ctx->refcnt), fq->fqid); |
---|
| 315 | + |
---|
299 | 316 | return 0; |
---|
300 | 317 | } |
---|
301 | 318 | |
---|
.. | .. |
---|
318 | 335 | /* Create a new req FQ in parked state */ |
---|
319 | 336 | new_fq = create_caam_req_fq(drv_ctx->qidev, drv_ctx->rsp_fq, |
---|
320 | 337 | drv_ctx->context_a, 0); |
---|
321 | | - if (unlikely(IS_ERR_OR_NULL(new_fq))) { |
---|
| 338 | + if (IS_ERR(new_fq)) { |
---|
322 | 339 | dev_err(qidev, "FQ allocation for shdesc update failed\n"); |
---|
323 | 340 | return PTR_ERR(new_fq); |
---|
324 | 341 | } |
---|
.. | .. |
---|
327 | 344 | drv_ctx->req_fq = new_fq; |
---|
328 | 345 | |
---|
329 | 346 | /* Empty and remove the older FQ */ |
---|
330 | | - ret = empty_caam_fq(old_fq); |
---|
| 347 | + ret = empty_caam_fq(old_fq, drv_ctx); |
---|
331 | 348 | if (ret) { |
---|
332 | 349 | dev_err(qidev, "Old CAAM FQ empty failed: %d\n", ret); |
---|
333 | 350 | |
---|
.. | .. |
---|
346 | 363 | */ |
---|
347 | 364 | drv_ctx->prehdr[0] = cpu_to_caam32((1 << PREHDR_RSLS_SHIFT) | |
---|
348 | 365 | num_words); |
---|
| 366 | + drv_ctx->prehdr[1] = cpu_to_caam32(PREHDR_ABS); |
---|
349 | 367 | memcpy(drv_ctx->sh_desc, sh_desc, desc_bytes(sh_desc)); |
---|
350 | 368 | dma_sync_single_for_device(qidev, drv_ctx->context_a, |
---|
351 | 369 | sizeof(drv_ctx->sh_desc) + |
---|
.. | .. |
---|
401 | 419 | */ |
---|
402 | 420 | drv_ctx->prehdr[0] = cpu_to_caam32((1 << PREHDR_RSLS_SHIFT) | |
---|
403 | 421 | num_words); |
---|
| 422 | + drv_ctx->prehdr[1] = cpu_to_caam32(PREHDR_ABS); |
---|
404 | 423 | memcpy(drv_ctx->sh_desc, sh_desc, desc_bytes(sh_desc)); |
---|
405 | 424 | size = sizeof(drv_ctx->prehdr) + sizeof(drv_ctx->sh_desc); |
---|
406 | 425 | hwdesc = dma_map_single(qidev, drv_ctx->prehdr, size, |
---|
.. | .. |
---|
431 | 450 | /* Attach request FQ */ |
---|
432 | 451 | drv_ctx->req_fq = create_caam_req_fq(qidev, drv_ctx->rsp_fq, hwdesc, |
---|
433 | 452 | QMAN_INITFQ_FLAG_SCHED); |
---|
434 | | - if (unlikely(IS_ERR_OR_NULL(drv_ctx->req_fq))) { |
---|
| 453 | + if (IS_ERR(drv_ctx->req_fq)) { |
---|
435 | 454 | dev_err(qidev, "create_caam_req_fq failed\n"); |
---|
436 | 455 | dma_unmap_single(qidev, hwdesc, size, DMA_BIDIRECTIONAL); |
---|
437 | 456 | kfree(drv_ctx); |
---|
438 | 457 | return ERR_PTR(-ENOMEM); |
---|
439 | 458 | } |
---|
| 459 | + |
---|
| 460 | + /* init reference counter used to track references to request FQ */ |
---|
| 461 | + refcount_set(&drv_ctx->refcnt, 1); |
---|
440 | 462 | |
---|
441 | 463 | drv_ctx->qidev = qidev; |
---|
442 | 464 | return drv_ctx; |
---|
.. | .. |
---|
485 | 507 | } |
---|
486 | 508 | EXPORT_SYMBOL(caam_drv_ctx_rel); |
---|
487 | 509 | |
---|
488 | | -void caam_qi_shutdown(struct device *qidev) |
---|
| 510 | +static void caam_qi_shutdown(void *data) |
---|
489 | 511 | { |
---|
490 | 512 | int i; |
---|
491 | | - struct caam_qi_priv *priv = dev_get_drvdata(qidev); |
---|
| 513 | + struct device *qidev = data; |
---|
| 514 | + struct caam_qi_priv *priv = &qipriv; |
---|
492 | 515 | const cpumask_t *cpus = qman_affine_cpus(); |
---|
493 | 516 | |
---|
494 | 517 | for_each_cpu(i, cpus) { |
---|
.. | .. |
---|
506 | 529 | qman_release_cgrid(priv->cgr.cgrid); |
---|
507 | 530 | |
---|
508 | 531 | kmem_cache_destroy(qi_cache); |
---|
509 | | - |
---|
510 | | - platform_device_unregister(priv->qi_pdev); |
---|
511 | 532 | } |
---|
512 | 533 | |
---|
513 | 534 | static void cgr_cb(struct qman_portal *qm, struct qman_cgr *cgr, int congested) |
---|
.. | .. |
---|
515 | 536 | caam_congested = congested; |
---|
516 | 537 | |
---|
517 | 538 | if (congested) { |
---|
518 | | -#ifdef CONFIG_DEBUG_FS |
---|
519 | | - times_congested++; |
---|
520 | | -#endif |
---|
| 539 | + caam_debugfs_qi_congested(); |
---|
| 540 | + |
---|
521 | 541 | pr_debug_ratelimited("CAAM entered congestion\n"); |
---|
522 | 542 | |
---|
523 | 543 | } else { |
---|
.. | .. |
---|
550 | 570 | struct caam_drv_req *drv_req; |
---|
551 | 571 | const struct qm_fd *fd; |
---|
552 | 572 | struct device *qidev = &(raw_cpu_ptr(&pcpu_qipriv)->net_dev.dev); |
---|
| 573 | + struct caam_drv_private *priv = dev_get_drvdata(qidev); |
---|
553 | 574 | u32 status; |
---|
554 | 575 | |
---|
555 | 576 | if (caam_qi_napi_schedule(p, caam_napi)) |
---|
556 | 577 | return qman_cb_dqrr_stop; |
---|
557 | 578 | |
---|
558 | 579 | fd = &dqrr->fd; |
---|
| 580 | + |
---|
| 581 | + drv_req = caam_iova_to_virt(priv->domain, qm_fd_addr_get64(fd)); |
---|
| 582 | + if (unlikely(!drv_req)) { |
---|
| 583 | + dev_err(qidev, |
---|
| 584 | + "Can't find original request for caam response\n"); |
---|
| 585 | + return qman_cb_dqrr_consume; |
---|
| 586 | + } |
---|
| 587 | + |
---|
| 588 | + refcount_dec(&drv_req->drv_ctx->refcnt); |
---|
| 589 | + |
---|
559 | 590 | status = be32_to_cpu(fd->status); |
---|
560 | 591 | if (unlikely(status)) { |
---|
561 | 592 | u32 ssrc = status & JRSTA_SSRC_MASK; |
---|
.. | .. |
---|
563 | 594 | |
---|
564 | 595 | if (ssrc != JRSTA_SSRC_CCB_ERROR || |
---|
565 | 596 | err_id != JRSTA_CCBERR_ERRID_ICVCHK) |
---|
566 | | - dev_err(qidev, "Error: %#x in CAAM response FD\n", |
---|
567 | | - status); |
---|
| 597 | + dev_err_ratelimited(qidev, |
---|
| 598 | + "Error: %#x in CAAM response FD\n", |
---|
| 599 | + status); |
---|
568 | 600 | } |
---|
569 | 601 | |
---|
570 | 602 | if (unlikely(qm_fd_get_format(fd) != qm_fd_compound)) { |
---|
571 | 603 | dev_err(qidev, "Non-compound FD from CAAM\n"); |
---|
572 | | - return qman_cb_dqrr_consume; |
---|
573 | | - } |
---|
574 | | - |
---|
575 | | - drv_req = (struct caam_drv_req *)phys_to_virt(qm_fd_addr_get64(fd)); |
---|
576 | | - if (unlikely(!drv_req)) { |
---|
577 | | - dev_err(qidev, |
---|
578 | | - "Can't find original request for caam response\n"); |
---|
579 | 604 | return qman_cb_dqrr_consume; |
---|
580 | 605 | } |
---|
581 | 606 | |
---|
.. | .. |
---|
692 | 717 | int caam_qi_init(struct platform_device *caam_pdev) |
---|
693 | 718 | { |
---|
694 | 719 | int err, i; |
---|
695 | | - struct platform_device *qi_pdev; |
---|
696 | 720 | struct device *ctrldev = &caam_pdev->dev, *qidev; |
---|
697 | 721 | struct caam_drv_private *ctrlpriv; |
---|
698 | 722 | const cpumask_t *cpus = qman_affine_cpus(); |
---|
699 | | - static struct platform_device_info qi_pdev_info = { |
---|
700 | | - .name = "caam_qi", |
---|
701 | | - .id = PLATFORM_DEVID_NONE |
---|
702 | | - }; |
---|
703 | | - |
---|
704 | | - qi_pdev_info.parent = ctrldev; |
---|
705 | | - qi_pdev_info.dma_mask = dma_get_mask(ctrldev); |
---|
706 | | - qi_pdev = platform_device_register_full(&qi_pdev_info); |
---|
707 | | - if (IS_ERR(qi_pdev)) |
---|
708 | | - return PTR_ERR(qi_pdev); |
---|
709 | | - set_dma_ops(&qi_pdev->dev, get_dma_ops(ctrldev)); |
---|
710 | 723 | |
---|
711 | 724 | ctrlpriv = dev_get_drvdata(ctrldev); |
---|
712 | | - qidev = &qi_pdev->dev; |
---|
713 | | - |
---|
714 | | - qipriv.qi_pdev = qi_pdev; |
---|
715 | | - dev_set_drvdata(qidev, &qipriv); |
---|
| 725 | + qidev = ctrldev; |
---|
716 | 726 | |
---|
717 | 727 | /* Initialize the congestion detection */ |
---|
718 | 728 | err = init_cgr(qidev); |
---|
719 | 729 | if (err) { |
---|
720 | 730 | dev_err(qidev, "CGR initialization failed: %d\n", err); |
---|
721 | | - platform_device_unregister(qi_pdev); |
---|
722 | 731 | return err; |
---|
723 | 732 | } |
---|
724 | 733 | |
---|
.. | .. |
---|
727 | 736 | if (err) { |
---|
728 | 737 | dev_err(qidev, "Can't allocate CAAM response FQs: %d\n", err); |
---|
729 | 738 | free_rsp_fqs(); |
---|
730 | | - platform_device_unregister(qi_pdev); |
---|
731 | 739 | return err; |
---|
732 | 740 | } |
---|
733 | 741 | |
---|
.. | .. |
---|
750 | 758 | napi_enable(irqtask); |
---|
751 | 759 | } |
---|
752 | 760 | |
---|
753 | | - /* Hook up QI device to parent controlling caam device */ |
---|
754 | | - ctrlpriv->qidev = qidev; |
---|
755 | | - |
---|
756 | 761 | qi_cache = kmem_cache_create("caamqicache", CAAM_QI_MEMCACHE_SIZE, 0, |
---|
757 | 762 | SLAB_CACHE_DMA, NULL); |
---|
758 | 763 | if (!qi_cache) { |
---|
759 | 764 | dev_err(qidev, "Can't allocate CAAM cache\n"); |
---|
760 | 765 | free_rsp_fqs(); |
---|
761 | | - platform_device_unregister(qi_pdev); |
---|
762 | 766 | return -ENOMEM; |
---|
763 | 767 | } |
---|
764 | 768 | |
---|
765 | | -#ifdef CONFIG_DEBUG_FS |
---|
766 | | - debugfs_create_file("qi_congested", 0444, ctrlpriv->ctl, |
---|
767 | | - ×_congested, &caam_fops_u64_ro); |
---|
768 | | -#endif |
---|
| 769 | + caam_debugfs_qi_init(ctrlpriv); |
---|
| 770 | + |
---|
| 771 | + err = devm_add_action_or_reset(qidev, caam_qi_shutdown, ctrlpriv); |
---|
| 772 | + if (err) |
---|
| 773 | + return err; |
---|
| 774 | + |
---|
769 | 775 | dev_info(qidev, "Linux CAAM Queue I/F driver initialised\n"); |
---|
770 | 776 | return 0; |
---|
771 | 777 | } |
---|