| // SPDX-License-Identifier: GPL-2.0 | |
| #include <linux/cpumask.h> | |
| #include <linux/dma-mapping.h> | |
| #include <linux/dmapool.h> | |
| #include <linux/delay.h> | |
| #include <linux/gfp.h> | |
| #include <linux/kernel.h> | |
| #include <linux/module.h> | |
| #include <linux/pci_regs.h> | |
| #include <linux/vmalloc.h> | |
| #include <linux/pci.h> | |
| #include "nitrox_dev.h" | |
| #include "nitrox_common.h" | |
| #include "nitrox_req.h" | |
| #include "nitrox_csr.h" | |
| #define CRYPTO_CTX_SIZE 256 | |
| /* packet inuput ring alignments */ | |
| #define PKTIN_Q_ALIGN_BYTES 16 | |
| /* AQM Queue input alignments */ | |
| #define AQM_Q_ALIGN_BYTES 32 | |
| static int nitrox_cmdq_init(struct nitrox_cmdq *cmdq, int align_bytes) | |
| { | |
| struct nitrox_device *ndev = cmdq->ndev; | |
| cmdq->qsize = (ndev->qlen * cmdq->instr_size) + align_bytes; | |
| cmdq->unalign_base = dma_alloc_coherent(DEV(ndev), cmdq->qsize, | |
| &cmdq->unalign_dma, | |
| GFP_KERNEL); | |
| if (!cmdq->unalign_base) | |
| return -ENOMEM; | |
| cmdq->dma = PTR_ALIGN(cmdq->unalign_dma, align_bytes); | |
| cmdq->base = cmdq->unalign_base + (cmdq->dma - cmdq->unalign_dma); | |
| cmdq->write_idx = 0; | |
| spin_lock_init(&cmdq->cmd_qlock); | |
| spin_lock_init(&cmdq->resp_qlock); | |
| spin_lock_init(&cmdq->backlog_qlock); | |
| INIT_LIST_HEAD(&cmdq->response_head); | |
| INIT_LIST_HEAD(&cmdq->backlog_head); | |
| INIT_WORK(&cmdq->backlog_qflush, backlog_qflush_work); | |
| atomic_set(&cmdq->pending_count, 0); | |
| atomic_set(&cmdq->backlog_count, 0); | |
| return 0; | |
| } | |
| static void nitrox_cmdq_reset(struct nitrox_cmdq *cmdq) | |
| { | |
| cmdq->write_idx = 0; | |
| atomic_set(&cmdq->pending_count, 0); | |
| atomic_set(&cmdq->backlog_count, 0); | |
| } | |
| static void nitrox_cmdq_cleanup(struct nitrox_cmdq *cmdq) | |
| { | |
| struct nitrox_device *ndev; | |
| if (!cmdq) | |
| return; | |
| if (!cmdq->unalign_base) | |
| return; | |
| ndev = cmdq->ndev; | |
| cancel_work_sync(&cmdq->backlog_qflush); | |
| dma_free_coherent(DEV(ndev), cmdq->qsize, | |
| cmdq->unalign_base, cmdq->unalign_dma); | |
| nitrox_cmdq_reset(cmdq); | |
| cmdq->dbell_csr_addr = NULL; | |
| cmdq->compl_cnt_csr_addr = NULL; | |
| cmdq->unalign_base = NULL; | |
| cmdq->base = NULL; | |
| cmdq->unalign_dma = 0; | |
| cmdq->dma = 0; | |
| cmdq->qsize = 0; | |
| cmdq->instr_size = 0; | |
| } | |
| static void nitrox_free_aqm_queues(struct nitrox_device *ndev) | |
| { | |
| int i; | |
| for (i = 0; i < ndev->nr_queues; i++) { | |
| nitrox_cmdq_cleanup(ndev->aqmq[i]); | |
| kfree_sensitive(ndev->aqmq[i]); | |
| ndev->aqmq[i] = NULL; | |
| } | |
| } | |
| static int nitrox_alloc_aqm_queues(struct nitrox_device *ndev) | |
| { | |
| int i, err; | |
| for (i = 0; i < ndev->nr_queues; i++) { | |
| struct nitrox_cmdq *cmdq; | |
| u64 offset; | |
| cmdq = kzalloc_node(sizeof(*cmdq), GFP_KERNEL, ndev->node); | |
| if (!cmdq) { | |
| err = -ENOMEM; | |
| goto aqmq_fail; | |
| } | |
| cmdq->ndev = ndev; | |
| cmdq->qno = i; | |
| cmdq->instr_size = sizeof(struct aqmq_command_s); | |
| /* AQM Queue Doorbell Counter Register Address */ | |
| offset = AQMQ_DRBLX(i); | |
| cmdq->dbell_csr_addr = NITROX_CSR_ADDR(ndev, offset); | |
| /* AQM Queue Commands Completed Count Register Address */ | |
| offset = AQMQ_CMD_CNTX(i); | |
| cmdq->compl_cnt_csr_addr = NITROX_CSR_ADDR(ndev, offset); | |
| err = nitrox_cmdq_init(cmdq, AQM_Q_ALIGN_BYTES); | |
| if (err) { | |
| kfree_sensitive(cmdq); | |
| goto aqmq_fail; | |
| } | |
| ndev->aqmq[i] = cmdq; | |
| } | |
| return 0; | |
| aqmq_fail: | |
| nitrox_free_aqm_queues(ndev); | |
| return err; | |
| } | |
| static void nitrox_free_pktin_queues(struct nitrox_device *ndev) | |
| { | |
| int i; | |
| for (i = 0; i < ndev->nr_queues; i++) { | |
| struct nitrox_cmdq *cmdq = &ndev->pkt_inq[i]; | |
| nitrox_cmdq_cleanup(cmdq); | |
| } | |
| kfree(ndev->pkt_inq); | |
| ndev->pkt_inq = NULL; | |
| } | |
| static int nitrox_alloc_pktin_queues(struct nitrox_device *ndev) | |
| { | |
| int i, err; | |
| ndev->pkt_inq = kcalloc_node(ndev->nr_queues, | |
| sizeof(struct nitrox_cmdq), | |
| GFP_KERNEL, ndev->node); | |
| if (!ndev->pkt_inq) | |
| return -ENOMEM; | |
| for (i = 0; i < ndev->nr_queues; i++) { | |
| struct nitrox_cmdq *cmdq; | |
| u64 offset; | |
| cmdq = &ndev->pkt_inq[i]; | |
| cmdq->ndev = ndev; | |
| cmdq->qno = i; | |
| cmdq->instr_size = sizeof(struct nps_pkt_instr); | |
| /* packet input ring doorbell address */ | |
| offset = NPS_PKT_IN_INSTR_BAOFF_DBELLX(i); | |
| cmdq->dbell_csr_addr = NITROX_CSR_ADDR(ndev, offset); | |
| /* packet solicit port completion count address */ | |
| offset = NPS_PKT_SLC_CNTSX(i); | |
| cmdq->compl_cnt_csr_addr = NITROX_CSR_ADDR(ndev, offset); | |
| err = nitrox_cmdq_init(cmdq, PKTIN_Q_ALIGN_BYTES); | |
| if (err) | |
| goto pktq_fail; | |
| } | |
| return 0; | |
| pktq_fail: | |
| nitrox_free_pktin_queues(ndev); | |
| return err; | |
| } | |
| static int create_crypto_dma_pool(struct nitrox_device *ndev) | |
| { | |
| size_t size; | |
| /* Crypto context pool, 16 byte aligned */ | |
| size = CRYPTO_CTX_SIZE + sizeof(struct ctx_hdr); | |
| ndev->ctx_pool = dma_pool_create("nitrox-context", | |
| DEV(ndev), size, 16, 0); | |
| if (!ndev->ctx_pool) | |
| return -ENOMEM; | |
| return 0; | |
| } | |
| static void destroy_crypto_dma_pool(struct nitrox_device *ndev) | |
| { | |
| if (!ndev->ctx_pool) | |
| return; | |
| dma_pool_destroy(ndev->ctx_pool); | |
| ndev->ctx_pool = NULL; | |
| } | |
| /* | |
| * crypto_alloc_context - Allocate crypto context from pool | |
| * @ndev: NITROX Device | |
| */ | |
| void *crypto_alloc_context(struct nitrox_device *ndev) | |
| { | |
| struct ctx_hdr *ctx; | |
| struct crypto_ctx_hdr *chdr; | |
| void *vaddr; | |
| dma_addr_t dma; | |
| chdr = kmalloc(sizeof(*chdr), GFP_KERNEL); | |
| if (!chdr) | |
| return NULL; | |
| vaddr = dma_pool_zalloc(ndev->ctx_pool, GFP_KERNEL, &dma); | |
| if (!vaddr) { | |
| kfree(chdr); | |
| return NULL; | |
| } | |
| /* fill meta data */ | |
| ctx = vaddr; | |
| ctx->pool = ndev->ctx_pool; | |
| ctx->dma = dma; | |
| ctx->ctx_dma = dma + sizeof(struct ctx_hdr); | |
| chdr->pool = ndev->ctx_pool; | |
| chdr->dma = dma; | |
| chdr->vaddr = vaddr; | |
| return chdr; | |
| } | |
| /** | |
| * crypto_free_context - Free crypto context to pool | |
| * @ctx: context to free | |
| */ | |
| void crypto_free_context(void *ctx) | |
| { | |
| struct crypto_ctx_hdr *ctxp; | |
| if (!ctx) | |
| return; | |
| ctxp = ctx; | |
| dma_pool_free(ctxp->pool, ctxp->vaddr, ctxp->dma); | |
| kfree(ctxp); | |
| } | |
| /** | |
| * nitrox_common_sw_init - allocate software resources. | |
| * @ndev: NITROX device | |
| * | |
| * Allocates crypto context pools and command queues etc. | |
| * | |
| * Return: 0 on success, or a negative error code on error. | |
| */ | |
| int nitrox_common_sw_init(struct nitrox_device *ndev) | |
| { | |
| int err = 0; | |
| /* per device crypto context pool */ | |
| err = create_crypto_dma_pool(ndev); | |
| if (err) | |
| return err; | |
| err = nitrox_alloc_pktin_queues(ndev); | |
| if (err) | |
| destroy_crypto_dma_pool(ndev); | |
| err = nitrox_alloc_aqm_queues(ndev); | |
| if (err) { | |
| nitrox_free_pktin_queues(ndev); | |
| destroy_crypto_dma_pool(ndev); | |
| } | |
| return err; | |
| } | |
| /** | |
| * nitrox_common_sw_cleanup - free software resources. | |
| * @ndev: NITROX device | |
| */ | |
| void nitrox_common_sw_cleanup(struct nitrox_device *ndev) | |
| { | |
| nitrox_free_aqm_queues(ndev); | |
| nitrox_free_pktin_queues(ndev); | |
| destroy_crypto_dma_pool(ndev); | |
| } |