| .. | .. |
|---|
| 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 | } |
|---|