18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * CAAM/SEC 4.x transport/backend driver 48c2ecf20Sopenharmony_ci * JobR backend functionality 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2008-2012 Freescale Semiconductor, Inc. 78c2ecf20Sopenharmony_ci * Copyright 2019 NXP 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "compat.h" 148c2ecf20Sopenharmony_ci#include "ctrl.h" 158c2ecf20Sopenharmony_ci#include "regs.h" 168c2ecf20Sopenharmony_ci#include "jr.h" 178c2ecf20Sopenharmony_ci#include "desc.h" 188c2ecf20Sopenharmony_ci#include "intern.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistruct jr_driver_data { 218c2ecf20Sopenharmony_ci /* List of Physical JobR's with the Driver */ 228c2ecf20Sopenharmony_ci struct list_head jr_list; 238c2ecf20Sopenharmony_ci spinlock_t jr_alloc_lock; /* jr_list lock */ 248c2ecf20Sopenharmony_ci} ____cacheline_aligned; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct jr_driver_data driver_data; 278c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(algs_lock); 288c2ecf20Sopenharmony_cistatic unsigned int active_devs; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistatic void register_algs(struct caam_drv_private_jr *jrpriv, 318c2ecf20Sopenharmony_ci struct device *dev) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci mutex_lock(&algs_lock); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci if (++active_devs != 1) 368c2ecf20Sopenharmony_ci goto algs_unlock; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci caam_algapi_init(dev); 398c2ecf20Sopenharmony_ci caam_algapi_hash_init(dev); 408c2ecf20Sopenharmony_ci caam_pkc_init(dev); 418c2ecf20Sopenharmony_ci jrpriv->hwrng = !caam_rng_init(dev); 428c2ecf20Sopenharmony_ci caam_qi_algapi_init(dev); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cialgs_unlock: 458c2ecf20Sopenharmony_ci mutex_unlock(&algs_lock); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic void unregister_algs(void) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci mutex_lock(&algs_lock); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (--active_devs != 0) 538c2ecf20Sopenharmony_ci goto algs_unlock; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci caam_qi_algapi_exit(); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci caam_pkc_exit(); 588c2ecf20Sopenharmony_ci caam_algapi_hash_exit(); 598c2ecf20Sopenharmony_ci caam_algapi_exit(); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cialgs_unlock: 628c2ecf20Sopenharmony_ci mutex_unlock(&algs_lock); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic void caam_jr_crypto_engine_exit(void *data) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct device *jrdev = data; 688c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrpriv = dev_get_drvdata(jrdev); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* Free the resources of crypto-engine */ 718c2ecf20Sopenharmony_ci crypto_engine_exit(jrpriv->engine); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int caam_reset_hw_jr(struct device *dev) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 778c2ecf20Sopenharmony_ci unsigned int timeout = 100000; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * mask interrupts since we are going to poll 818c2ecf20Sopenharmony_ci * for reset completion status 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* initiate flush (required prior to reset) */ 868c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET); 878c2ecf20Sopenharmony_ci while (((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) == 888c2ecf20Sopenharmony_ci JRINT_ERR_HALT_INPROGRESS) && --timeout) 898c2ecf20Sopenharmony_ci cpu_relax(); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if ((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) != 928c2ecf20Sopenharmony_ci JRINT_ERR_HALT_COMPLETE || timeout == 0) { 938c2ecf20Sopenharmony_ci dev_err(dev, "failed to flush job ring %d\n", jrp->ridx); 948c2ecf20Sopenharmony_ci return -EIO; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* initiate reset */ 988c2ecf20Sopenharmony_ci timeout = 100000; 998c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET); 1008c2ecf20Sopenharmony_ci while ((rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET) && --timeout) 1018c2ecf20Sopenharmony_ci cpu_relax(); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (timeout == 0) { 1048c2ecf20Sopenharmony_ci dev_err(dev, "failed to reset job ring %d\n", jrp->ridx); 1058c2ecf20Sopenharmony_ci return -EIO; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* unmask interrupts */ 1098c2ecf20Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* 1158c2ecf20Sopenharmony_ci * Shutdown JobR independent of platform property code 1168c2ecf20Sopenharmony_ci */ 1178c2ecf20Sopenharmony_cistatic int caam_jr_shutdown(struct device *dev) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 1208c2ecf20Sopenharmony_ci int ret; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci ret = caam_reset_hw_jr(dev); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci tasklet_kill(&jrp->irqtask); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int caam_jr_remove(struct platform_device *pdev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci int ret; 1328c2ecf20Sopenharmony_ci struct device *jrdev; 1338c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrpriv; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci jrdev = &pdev->dev; 1368c2ecf20Sopenharmony_ci jrpriv = dev_get_drvdata(jrdev); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (jrpriv->hwrng) 1398c2ecf20Sopenharmony_ci caam_rng_exit(jrdev->parent); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* 1428c2ecf20Sopenharmony_ci * Return EBUSY if job ring already allocated. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci if (atomic_read(&jrpriv->tfm_count)) { 1458c2ecf20Sopenharmony_ci dev_err(jrdev, "Device is busy\n"); 1468c2ecf20Sopenharmony_ci return -EBUSY; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Unregister JR-based RNG & crypto algorithms */ 1508c2ecf20Sopenharmony_ci unregister_algs(); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Remove the node from Physical JobR list maintained by driver */ 1538c2ecf20Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 1548c2ecf20Sopenharmony_ci list_del(&jrpriv->list_node); 1558c2ecf20Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* Release ring */ 1588c2ecf20Sopenharmony_ci ret = caam_jr_shutdown(jrdev); 1598c2ecf20Sopenharmony_ci if (ret) 1608c2ecf20Sopenharmony_ci dev_err(jrdev, "Failed to shut down job ring\n"); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return ret; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* Main per-ring interrupt handler */ 1668c2ecf20Sopenharmony_cistatic irqreturn_t caam_jr_interrupt(int irq, void *st_dev) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct device *dev = st_dev; 1698c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 1708c2ecf20Sopenharmony_ci u32 irqstate; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* 1738c2ecf20Sopenharmony_ci * Check the output ring for ready responses, kick 1748c2ecf20Sopenharmony_ci * tasklet if jobs done. 1758c2ecf20Sopenharmony_ci */ 1768c2ecf20Sopenharmony_ci irqstate = rd_reg32(&jrp->rregs->jrintstatus); 1778c2ecf20Sopenharmony_ci if (!irqstate) 1788c2ecf20Sopenharmony_ci return IRQ_NONE; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* 1818c2ecf20Sopenharmony_ci * If JobR error, we got more development work to do 1828c2ecf20Sopenharmony_ci * Flag a bug now, but we really need to shut down and 1838c2ecf20Sopenharmony_ci * restart the queue (and fix code). 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci if (irqstate & JRINT_JR_ERROR) { 1868c2ecf20Sopenharmony_ci dev_err(dev, "job ring error: irqstate: %08x\n", irqstate); 1878c2ecf20Sopenharmony_ci BUG(); 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* mask valid interrupts */ 1918c2ecf20Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* Have valid interrupt at this point, just ACK and trigger */ 1948c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->jrintstatus, irqstate); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci preempt_disable(); 1978c2ecf20Sopenharmony_ci tasklet_schedule(&jrp->irqtask); 1988c2ecf20Sopenharmony_ci preempt_enable(); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci/* Deferred service handler, run as interrupt-fired tasklet */ 2048c2ecf20Sopenharmony_cistatic void caam_jr_dequeue(unsigned long devarg) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci int hw_idx, sw_idx, i, head, tail; 2078c2ecf20Sopenharmony_ci struct device *dev = (struct device *)devarg; 2088c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 2098c2ecf20Sopenharmony_ci void (*usercall)(struct device *dev, u32 *desc, u32 status, void *arg); 2108c2ecf20Sopenharmony_ci u32 *userdesc, userstatus; 2118c2ecf20Sopenharmony_ci void *userarg; 2128c2ecf20Sopenharmony_ci u32 outring_used = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci while (outring_used || 2158c2ecf20Sopenharmony_ci (outring_used = rd_reg32(&jrp->rregs->outring_used))) { 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci head = READ_ONCE(jrp->head); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci sw_idx = tail = jrp->tail; 2208c2ecf20Sopenharmony_ci hw_idx = jrp->out_ring_read_index; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci for (i = 0; CIRC_CNT(head, tail + i, JOBR_DEPTH) >= 1; i++) { 2238c2ecf20Sopenharmony_ci sw_idx = (tail + i) & (JOBR_DEPTH - 1); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (jr_outentry_desc(jrp->outring, hw_idx) == 2268c2ecf20Sopenharmony_ci caam_dma_to_cpu(jrp->entinfo[sw_idx].desc_addr_dma)) 2278c2ecf20Sopenharmony_ci break; /* found */ 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci /* we should never fail to find a matching descriptor */ 2308c2ecf20Sopenharmony_ci BUG_ON(CIRC_CNT(head, tail + i, JOBR_DEPTH) <= 0); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Unmap just-run descriptor so we can post-process */ 2338c2ecf20Sopenharmony_ci dma_unmap_single(dev, 2348c2ecf20Sopenharmony_ci caam_dma_to_cpu(jr_outentry_desc(jrp->outring, 2358c2ecf20Sopenharmony_ci hw_idx)), 2368c2ecf20Sopenharmony_ci jrp->entinfo[sw_idx].desc_size, 2378c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* mark completed, avoid matching on a recycled desc addr */ 2408c2ecf20Sopenharmony_ci jrp->entinfo[sw_idx].desc_addr_dma = 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Stash callback params */ 2438c2ecf20Sopenharmony_ci usercall = jrp->entinfo[sw_idx].callbk; 2448c2ecf20Sopenharmony_ci userarg = jrp->entinfo[sw_idx].cbkarg; 2458c2ecf20Sopenharmony_ci userdesc = jrp->entinfo[sw_idx].desc_addr_virt; 2468c2ecf20Sopenharmony_ci userstatus = caam32_to_cpu(jr_outentry_jrstatus(jrp->outring, 2478c2ecf20Sopenharmony_ci hw_idx)); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* 2508c2ecf20Sopenharmony_ci * Make sure all information from the job has been obtained 2518c2ecf20Sopenharmony_ci * before telling CAAM that the job has been removed from the 2528c2ecf20Sopenharmony_ci * output ring. 2538c2ecf20Sopenharmony_ci */ 2548c2ecf20Sopenharmony_ci mb(); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* set done */ 2578c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->outring_rmvd, 1); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci jrp->out_ring_read_index = (jrp->out_ring_read_index + 1) & 2608c2ecf20Sopenharmony_ci (JOBR_DEPTH - 1); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * if this job completed out-of-order, do not increment 2648c2ecf20Sopenharmony_ci * the tail. Otherwise, increment tail by 1 plus the 2658c2ecf20Sopenharmony_ci * number of subsequent jobs already completed out-of-order 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci if (sw_idx == tail) { 2688c2ecf20Sopenharmony_ci do { 2698c2ecf20Sopenharmony_ci tail = (tail + 1) & (JOBR_DEPTH - 1); 2708c2ecf20Sopenharmony_ci } while (CIRC_CNT(head, tail, JOBR_DEPTH) >= 1 && 2718c2ecf20Sopenharmony_ci jrp->entinfo[tail].desc_addr_dma == 0); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci jrp->tail = tail; 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Finally, execute user's callback */ 2778c2ecf20Sopenharmony_ci usercall(dev, userdesc, userstatus, userarg); 2788c2ecf20Sopenharmony_ci outring_used--; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* reenable / unmask IRQs */ 2828c2ecf20Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0); 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci/** 2868c2ecf20Sopenharmony_ci * caam_jr_alloc() - Alloc a job ring for someone to use as needed. 2878c2ecf20Sopenharmony_ci * 2888c2ecf20Sopenharmony_ci * returns : pointer to the newly allocated physical 2898c2ecf20Sopenharmony_ci * JobR dev can be written to if successful. 2908c2ecf20Sopenharmony_ci **/ 2918c2ecf20Sopenharmony_cistruct device *caam_jr_alloc(void) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrpriv, *min_jrpriv = NULL; 2948c2ecf20Sopenharmony_ci struct device *dev = ERR_PTR(-ENODEV); 2958c2ecf20Sopenharmony_ci int min_tfm_cnt = INT_MAX; 2968c2ecf20Sopenharmony_ci int tfm_cnt; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci if (list_empty(&driver_data.jr_list)) { 3018c2ecf20Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 3028c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci list_for_each_entry(jrpriv, &driver_data.jr_list, list_node) { 3068c2ecf20Sopenharmony_ci tfm_cnt = atomic_read(&jrpriv->tfm_count); 3078c2ecf20Sopenharmony_ci if (tfm_cnt < min_tfm_cnt) { 3088c2ecf20Sopenharmony_ci min_tfm_cnt = tfm_cnt; 3098c2ecf20Sopenharmony_ci min_jrpriv = jrpriv; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci if (!min_tfm_cnt) 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (min_jrpriv) { 3168c2ecf20Sopenharmony_ci atomic_inc(&min_jrpriv->tfm_count); 3178c2ecf20Sopenharmony_ci dev = min_jrpriv->dev; 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return dev; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caam_jr_alloc); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/** 3268c2ecf20Sopenharmony_ci * caam_jr_free() - Free the Job Ring 3278c2ecf20Sopenharmony_ci * @rdev: points to the dev that identifies the Job ring to 3288c2ecf20Sopenharmony_ci * be released. 3298c2ecf20Sopenharmony_ci **/ 3308c2ecf20Sopenharmony_civoid caam_jr_free(struct device *rdev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrpriv = dev_get_drvdata(rdev); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci atomic_dec(&jrpriv->tfm_count); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caam_jr_free); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci/** 3398c2ecf20Sopenharmony_ci * caam_jr_enqueue() - Enqueue a job descriptor head. Returns -EINPROGRESS 3408c2ecf20Sopenharmony_ci * if OK, -ENOSPC if the queue is full, -EIO if it cannot map the caller's 3418c2ecf20Sopenharmony_ci * descriptor. 3428c2ecf20Sopenharmony_ci * @dev: struct device of the job ring to be used 3438c2ecf20Sopenharmony_ci * @desc: points to a job descriptor that execute our request. All 3448c2ecf20Sopenharmony_ci * descriptors (and all referenced data) must be in a DMAable 3458c2ecf20Sopenharmony_ci * region, and all data references must be physical addresses 3468c2ecf20Sopenharmony_ci * accessible to CAAM (i.e. within a PAMU window granted 3478c2ecf20Sopenharmony_ci * to it). 3488c2ecf20Sopenharmony_ci * @cbk: pointer to a callback function to be invoked upon completion 3498c2ecf20Sopenharmony_ci * of this request. This has the form: 3508c2ecf20Sopenharmony_ci * callback(struct device *dev, u32 *desc, u32 stat, void *arg) 3518c2ecf20Sopenharmony_ci * where: 3528c2ecf20Sopenharmony_ci * dev: contains the job ring device that processed this 3538c2ecf20Sopenharmony_ci * response. 3548c2ecf20Sopenharmony_ci * desc: descriptor that initiated the request, same as 3558c2ecf20Sopenharmony_ci * "desc" being argued to caam_jr_enqueue(). 3568c2ecf20Sopenharmony_ci * status: untranslated status received from CAAM. See the 3578c2ecf20Sopenharmony_ci * reference manual for a detailed description of 3588c2ecf20Sopenharmony_ci * error meaning, or see the JRSTA definitions in the 3598c2ecf20Sopenharmony_ci * register header file 3608c2ecf20Sopenharmony_ci * areq: optional pointer to an argument passed with the 3618c2ecf20Sopenharmony_ci * original request 3628c2ecf20Sopenharmony_ci * @areq: optional pointer to a user argument for use at callback 3638c2ecf20Sopenharmony_ci * time. 3648c2ecf20Sopenharmony_ci **/ 3658c2ecf20Sopenharmony_ciint caam_jr_enqueue(struct device *dev, u32 *desc, 3668c2ecf20Sopenharmony_ci void (*cbk)(struct device *dev, u32 *desc, 3678c2ecf20Sopenharmony_ci u32 status, void *areq), 3688c2ecf20Sopenharmony_ci void *areq) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 3718c2ecf20Sopenharmony_ci struct caam_jrentry_info *head_entry; 3728c2ecf20Sopenharmony_ci int head, tail, desc_size; 3738c2ecf20Sopenharmony_ci dma_addr_t desc_dma; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci desc_size = (caam32_to_cpu(*desc) & HDR_JD_LENGTH_MASK) * sizeof(u32); 3768c2ecf20Sopenharmony_ci desc_dma = dma_map_single(dev, desc, desc_size, DMA_TO_DEVICE); 3778c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, desc_dma)) { 3788c2ecf20Sopenharmony_ci dev_err(dev, "caam_jr_enqueue(): can't map jobdesc\n"); 3798c2ecf20Sopenharmony_ci return -EIO; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci spin_lock_bh(&jrp->inplock); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci head = jrp->head; 3858c2ecf20Sopenharmony_ci tail = READ_ONCE(jrp->tail); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!jrp->inpring_avail || 3888c2ecf20Sopenharmony_ci CIRC_SPACE(head, tail, JOBR_DEPTH) <= 0) { 3898c2ecf20Sopenharmony_ci spin_unlock_bh(&jrp->inplock); 3908c2ecf20Sopenharmony_ci dma_unmap_single(dev, desc_dma, desc_size, DMA_TO_DEVICE); 3918c2ecf20Sopenharmony_ci return -ENOSPC; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci head_entry = &jrp->entinfo[head]; 3958c2ecf20Sopenharmony_ci head_entry->desc_addr_virt = desc; 3968c2ecf20Sopenharmony_ci head_entry->desc_size = desc_size; 3978c2ecf20Sopenharmony_ci head_entry->callbk = (void *)cbk; 3988c2ecf20Sopenharmony_ci head_entry->cbkarg = areq; 3998c2ecf20Sopenharmony_ci head_entry->desc_addr_dma = desc_dma; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci jr_inpentry_set(jrp->inpring, head, cpu_to_caam_dma(desc_dma)); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* 4048c2ecf20Sopenharmony_ci * Guarantee that the descriptor's DMA address has been written to 4058c2ecf20Sopenharmony_ci * the next slot in the ring before the write index is updated, since 4068c2ecf20Sopenharmony_ci * other cores may update this index independently. 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci smp_wmb(); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci jrp->head = (head + 1) & (JOBR_DEPTH - 1); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* 4138c2ecf20Sopenharmony_ci * Ensure that all job information has been written before 4148c2ecf20Sopenharmony_ci * notifying CAAM that a new job was added to the input ring 4158c2ecf20Sopenharmony_ci * using a memory barrier. The wr_reg32() uses api iowrite32() 4168c2ecf20Sopenharmony_ci * to do the register write. iowrite32() issues a memory barrier 4178c2ecf20Sopenharmony_ci * before the write operation. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->inpring_jobadd, 1); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci jrp->inpring_avail--; 4238c2ecf20Sopenharmony_ci if (!jrp->inpring_avail) 4248c2ecf20Sopenharmony_ci jrp->inpring_avail = rd_reg32(&jrp->rregs->inpring_avail); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci spin_unlock_bh(&jrp->inplock); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return -EINPROGRESS; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ciEXPORT_SYMBOL(caam_jr_enqueue); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci/* 4338c2ecf20Sopenharmony_ci * Init JobR independent of platform property detection 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_cistatic int caam_jr_init(struct device *dev) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrp; 4388c2ecf20Sopenharmony_ci dma_addr_t inpbusaddr, outbusaddr; 4398c2ecf20Sopenharmony_ci int i, error; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci jrp = dev_get_drvdata(dev); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci error = caam_reset_hw_jr(dev); 4448c2ecf20Sopenharmony_ci if (error) 4458c2ecf20Sopenharmony_ci return error; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci jrp->inpring = dmam_alloc_coherent(dev, SIZEOF_JR_INPENTRY * 4488c2ecf20Sopenharmony_ci JOBR_DEPTH, &inpbusaddr, 4498c2ecf20Sopenharmony_ci GFP_KERNEL); 4508c2ecf20Sopenharmony_ci if (!jrp->inpring) 4518c2ecf20Sopenharmony_ci return -ENOMEM; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci jrp->outring = dmam_alloc_coherent(dev, SIZEOF_JR_OUTENTRY * 4548c2ecf20Sopenharmony_ci JOBR_DEPTH, &outbusaddr, 4558c2ecf20Sopenharmony_ci GFP_KERNEL); 4568c2ecf20Sopenharmony_ci if (!jrp->outring) 4578c2ecf20Sopenharmony_ci return -ENOMEM; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci jrp->entinfo = devm_kcalloc(dev, JOBR_DEPTH, sizeof(*jrp->entinfo), 4608c2ecf20Sopenharmony_ci GFP_KERNEL); 4618c2ecf20Sopenharmony_ci if (!jrp->entinfo) 4628c2ecf20Sopenharmony_ci return -ENOMEM; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = 0; i < JOBR_DEPTH; i++) 4658c2ecf20Sopenharmony_ci jrp->entinfo[i].desc_addr_dma = !0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* Setup rings */ 4688c2ecf20Sopenharmony_ci jrp->out_ring_read_index = 0; 4698c2ecf20Sopenharmony_ci jrp->head = 0; 4708c2ecf20Sopenharmony_ci jrp->tail = 0; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci wr_reg64(&jrp->rregs->inpring_base, inpbusaddr); 4738c2ecf20Sopenharmony_ci wr_reg64(&jrp->rregs->outring_base, outbusaddr); 4748c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->inpring_size, JOBR_DEPTH); 4758c2ecf20Sopenharmony_ci wr_reg32(&jrp->rregs->outring_size, JOBR_DEPTH); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci jrp->inpring_avail = JOBR_DEPTH; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci spin_lock_init(&jrp->inplock); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* Select interrupt coalescing parameters */ 4828c2ecf20Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JOBR_INTC | 4838c2ecf20Sopenharmony_ci (JOBR_INTC_COUNT_THLD << JRCFG_ICDCT_SHIFT) | 4848c2ecf20Sopenharmony_ci (JOBR_INTC_TIME_THLD << JRCFG_ICTT_SHIFT)); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci tasklet_init(&jrp->irqtask, caam_jr_dequeue, (unsigned long)dev); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* Connect job ring interrupt handler. */ 4898c2ecf20Sopenharmony_ci error = devm_request_irq(dev, jrp->irq, caam_jr_interrupt, IRQF_SHARED, 4908c2ecf20Sopenharmony_ci dev_name(dev), dev); 4918c2ecf20Sopenharmony_ci if (error) { 4928c2ecf20Sopenharmony_ci dev_err(dev, "can't connect JobR %d interrupt (%d)\n", 4938c2ecf20Sopenharmony_ci jrp->ridx, jrp->irq); 4948c2ecf20Sopenharmony_ci tasklet_kill(&jrp->irqtask); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return error; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic void caam_jr_irq_dispose_mapping(void *data) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci irq_dispose_mapping((unsigned long)data); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci/* 5068c2ecf20Sopenharmony_ci * Probe routine for each detected JobR subsystem. 5078c2ecf20Sopenharmony_ci */ 5088c2ecf20Sopenharmony_cistatic int caam_jr_probe(struct platform_device *pdev) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct device *jrdev; 5118c2ecf20Sopenharmony_ci struct device_node *nprop; 5128c2ecf20Sopenharmony_ci struct caam_job_ring __iomem *ctrl; 5138c2ecf20Sopenharmony_ci struct caam_drv_private_jr *jrpriv; 5148c2ecf20Sopenharmony_ci static int total_jobrs; 5158c2ecf20Sopenharmony_ci struct resource *r; 5168c2ecf20Sopenharmony_ci int error; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci jrdev = &pdev->dev; 5198c2ecf20Sopenharmony_ci jrpriv = devm_kzalloc(jrdev, sizeof(*jrpriv), GFP_KERNEL); 5208c2ecf20Sopenharmony_ci if (!jrpriv) 5218c2ecf20Sopenharmony_ci return -ENOMEM; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci dev_set_drvdata(jrdev, jrpriv); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci /* save ring identity relative to detection */ 5268c2ecf20Sopenharmony_ci jrpriv->ridx = total_jobrs++; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci nprop = pdev->dev.of_node; 5298c2ecf20Sopenharmony_ci /* Get configuration properties from device tree */ 5308c2ecf20Sopenharmony_ci /* First, get register page */ 5318c2ecf20Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 5328c2ecf20Sopenharmony_ci if (!r) { 5338c2ecf20Sopenharmony_ci dev_err(jrdev, "platform_get_resource() failed\n"); 5348c2ecf20Sopenharmony_ci return -ENOMEM; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ctrl = devm_ioremap(jrdev, r->start, resource_size(r)); 5388c2ecf20Sopenharmony_ci if (!ctrl) { 5398c2ecf20Sopenharmony_ci dev_err(jrdev, "devm_ioremap() failed\n"); 5408c2ecf20Sopenharmony_ci return -ENOMEM; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci jrpriv->rregs = (struct caam_job_ring __iomem __force *)ctrl; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci error = dma_set_mask_and_coherent(jrdev, caam_get_dma_mask(jrdev)); 5468c2ecf20Sopenharmony_ci if (error) { 5478c2ecf20Sopenharmony_ci dev_err(jrdev, "dma_set_mask_and_coherent failed (%d)\n", 5488c2ecf20Sopenharmony_ci error); 5498c2ecf20Sopenharmony_ci return error; 5508c2ecf20Sopenharmony_ci } 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* Initialize crypto engine */ 5538c2ecf20Sopenharmony_ci jrpriv->engine = crypto_engine_alloc_init(jrdev, false); 5548c2ecf20Sopenharmony_ci if (!jrpriv->engine) { 5558c2ecf20Sopenharmony_ci dev_err(jrdev, "Could not init crypto-engine\n"); 5568c2ecf20Sopenharmony_ci return -ENOMEM; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci error = devm_add_action_or_reset(jrdev, caam_jr_crypto_engine_exit, 5608c2ecf20Sopenharmony_ci jrdev); 5618c2ecf20Sopenharmony_ci if (error) 5628c2ecf20Sopenharmony_ci return error; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci /* Start crypto engine */ 5658c2ecf20Sopenharmony_ci error = crypto_engine_start(jrpriv->engine); 5668c2ecf20Sopenharmony_ci if (error) { 5678c2ecf20Sopenharmony_ci dev_err(jrdev, "Could not start crypto-engine\n"); 5688c2ecf20Sopenharmony_ci return error; 5698c2ecf20Sopenharmony_ci } 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Identify the interrupt */ 5728c2ecf20Sopenharmony_ci jrpriv->irq = irq_of_parse_and_map(nprop, 0); 5738c2ecf20Sopenharmony_ci if (!jrpriv->irq) { 5748c2ecf20Sopenharmony_ci dev_err(jrdev, "irq_of_parse_and_map failed\n"); 5758c2ecf20Sopenharmony_ci return -EINVAL; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci error = devm_add_action_or_reset(jrdev, caam_jr_irq_dispose_mapping, 5798c2ecf20Sopenharmony_ci (void *)(unsigned long)jrpriv->irq); 5808c2ecf20Sopenharmony_ci if (error) 5818c2ecf20Sopenharmony_ci return error; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci /* Now do the platform independent part */ 5848c2ecf20Sopenharmony_ci error = caam_jr_init(jrdev); /* now turn on hardware */ 5858c2ecf20Sopenharmony_ci if (error) 5868c2ecf20Sopenharmony_ci return error; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci jrpriv->dev = jrdev; 5898c2ecf20Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 5908c2ecf20Sopenharmony_ci list_add_tail(&jrpriv->list_node, &driver_data.jr_list); 5918c2ecf20Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci atomic_set(&jrpriv->tfm_count, 0); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci register_algs(jrpriv, jrdev->parent); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci return 0; 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistatic const struct of_device_id caam_jr_match[] = { 6018c2ecf20Sopenharmony_ci { 6028c2ecf20Sopenharmony_ci .compatible = "fsl,sec-v4.0-job-ring", 6038c2ecf20Sopenharmony_ci }, 6048c2ecf20Sopenharmony_ci { 6058c2ecf20Sopenharmony_ci .compatible = "fsl,sec4.0-job-ring", 6068c2ecf20Sopenharmony_ci }, 6078c2ecf20Sopenharmony_ci {}, 6088c2ecf20Sopenharmony_ci}; 6098c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, caam_jr_match); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic struct platform_driver caam_jr_driver = { 6128c2ecf20Sopenharmony_ci .driver = { 6138c2ecf20Sopenharmony_ci .name = "caam_jr", 6148c2ecf20Sopenharmony_ci .of_match_table = caam_jr_match, 6158c2ecf20Sopenharmony_ci }, 6168c2ecf20Sopenharmony_ci .probe = caam_jr_probe, 6178c2ecf20Sopenharmony_ci .remove = caam_jr_remove, 6188c2ecf20Sopenharmony_ci}; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int __init jr_driver_init(void) 6218c2ecf20Sopenharmony_ci{ 6228c2ecf20Sopenharmony_ci spin_lock_init(&driver_data.jr_alloc_lock); 6238c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&driver_data.jr_list); 6248c2ecf20Sopenharmony_ci return platform_driver_register(&caam_jr_driver); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_cistatic void __exit jr_driver_exit(void) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci platform_driver_unregister(&caam_jr_driver); 6308c2ecf20Sopenharmony_ci} 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cimodule_init(jr_driver_init); 6338c2ecf20Sopenharmony_cimodule_exit(jr_driver_exit); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6368c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FSL CAAM JR request backend"); 6378c2ecf20Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor - NMG/STC"); 638