162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CAAM/SEC 4.x transport/backend driver 462306a36Sopenharmony_ci * JobR backend functionality 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright 2008-2012 Freescale Semiconductor, Inc. 762306a36Sopenharmony_ci * Copyright 2019, 2023 NXP 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/of_irq.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "compat.h" 1562306a36Sopenharmony_ci#include "ctrl.h" 1662306a36Sopenharmony_ci#include "regs.h" 1762306a36Sopenharmony_ci#include "jr.h" 1862306a36Sopenharmony_ci#include "desc.h" 1962306a36Sopenharmony_ci#include "intern.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct jr_driver_data { 2262306a36Sopenharmony_ci /* List of Physical JobR's with the Driver */ 2362306a36Sopenharmony_ci struct list_head jr_list; 2462306a36Sopenharmony_ci spinlock_t jr_alloc_lock; /* jr_list lock */ 2562306a36Sopenharmony_ci} ____cacheline_aligned; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct jr_driver_data driver_data; 2862306a36Sopenharmony_cistatic DEFINE_MUTEX(algs_lock); 2962306a36Sopenharmony_cistatic unsigned int active_devs; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic void register_algs(struct caam_drv_private_jr *jrpriv, 3262306a36Sopenharmony_ci struct device *dev) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci mutex_lock(&algs_lock); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci if (++active_devs != 1) 3762306a36Sopenharmony_ci goto algs_unlock; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci caam_algapi_init(dev); 4062306a36Sopenharmony_ci caam_algapi_hash_init(dev); 4162306a36Sopenharmony_ci caam_pkc_init(dev); 4262306a36Sopenharmony_ci jrpriv->hwrng = !caam_rng_init(dev); 4362306a36Sopenharmony_ci caam_prng_register(dev); 4462306a36Sopenharmony_ci caam_qi_algapi_init(dev); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cialgs_unlock: 4762306a36Sopenharmony_ci mutex_unlock(&algs_lock); 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic void unregister_algs(void) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci mutex_lock(&algs_lock); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (--active_devs != 0) 5562306a36Sopenharmony_ci goto algs_unlock; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci caam_qi_algapi_exit(); 5862306a36Sopenharmony_ci caam_prng_unregister(NULL); 5962306a36Sopenharmony_ci caam_pkc_exit(); 6062306a36Sopenharmony_ci caam_algapi_hash_exit(); 6162306a36Sopenharmony_ci caam_algapi_exit(); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cialgs_unlock: 6462306a36Sopenharmony_ci mutex_unlock(&algs_lock); 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void caam_jr_crypto_engine_exit(void *data) 6862306a36Sopenharmony_ci{ 6962306a36Sopenharmony_ci struct device *jrdev = data; 7062306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv = dev_get_drvdata(jrdev); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* Free the resources of crypto-engine */ 7362306a36Sopenharmony_ci crypto_engine_exit(jrpriv->engine); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * Put the CAAM in quiesce, ie stop 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * Must be called with itr disabled 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_cistatic int caam_jr_stop_processing(struct device *dev, u32 jrcr_bits) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 8462306a36Sopenharmony_ci unsigned int timeout = 100000; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci /* Check the current status */ 8762306a36Sopenharmony_ci if (rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_INPROGRESS) 8862306a36Sopenharmony_ci goto wait_quiesce_completion; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci /* Reset the field */ 9162306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->jrintstatus, JRINT_ERR_HALT_MASK, 0); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* initiate flush / park (required prior to reset) */ 9462306a36Sopenharmony_ci wr_reg32(&jrp->rregs->jrcommand, jrcr_bits); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ciwait_quiesce_completion: 9762306a36Sopenharmony_ci while (((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) == 9862306a36Sopenharmony_ci JRINT_ERR_HALT_INPROGRESS) && --timeout) 9962306a36Sopenharmony_ci cpu_relax(); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if ((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) != 10262306a36Sopenharmony_ci JRINT_ERR_HALT_COMPLETE || timeout == 0) { 10362306a36Sopenharmony_ci dev_err(dev, "failed to flush job ring %d\n", jrp->ridx); 10462306a36Sopenharmony_ci return -EIO; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return 0; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci/* 11162306a36Sopenharmony_ci * Flush the job ring, so the jobs running will be stopped, jobs queued will be 11262306a36Sopenharmony_ci * invalidated and the CAAM will no longer fetch fron input ring. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * Must be called with itr disabled 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_cistatic int caam_jr_flush(struct device *dev) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci return caam_jr_stop_processing(dev, JRCR_RESET); 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* The resume can be used after a park or a flush if CAAM has not been reset */ 12262306a36Sopenharmony_cistatic int caam_jr_restart_processing(struct device *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 12562306a36Sopenharmony_ci u32 halt_status = rd_reg32(&jrp->rregs->jrintstatus) & 12662306a36Sopenharmony_ci JRINT_ERR_HALT_MASK; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Check that the flush/park is completed */ 12962306a36Sopenharmony_ci if (halt_status != JRINT_ERR_HALT_COMPLETE) 13062306a36Sopenharmony_ci return -1; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci /* Resume processing of jobs */ 13362306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->jrintstatus, 0, JRINT_ERR_HALT_COMPLETE); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int caam_reset_hw_jr(struct device *dev) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 14162306a36Sopenharmony_ci unsigned int timeout = 100000; 14262306a36Sopenharmony_ci int err; 14362306a36Sopenharmony_ci /* 14462306a36Sopenharmony_ci * mask interrupts since we are going to poll 14562306a36Sopenharmony_ci * for reset completion status 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK); 14862306a36Sopenharmony_ci err = caam_jr_flush(dev); 14962306a36Sopenharmony_ci if (err) 15062306a36Sopenharmony_ci return err; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* initiate reset */ 15362306a36Sopenharmony_ci wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET); 15462306a36Sopenharmony_ci while ((rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET) && --timeout) 15562306a36Sopenharmony_ci cpu_relax(); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (timeout == 0) { 15862306a36Sopenharmony_ci dev_err(dev, "failed to reset job ring %d\n", jrp->ridx); 15962306a36Sopenharmony_ci return -EIO; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* unmask interrupts */ 16362306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return 0; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci/* 16962306a36Sopenharmony_ci * Shutdown JobR independent of platform property code 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_cistatic int caam_jr_shutdown(struct device *dev) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = caam_reset_hw_jr(dev); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci tasklet_kill(&jrp->irqtask); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return ret; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic int caam_jr_remove(struct platform_device *pdev) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int ret; 18662306a36Sopenharmony_ci struct device *jrdev; 18762306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci jrdev = &pdev->dev; 19062306a36Sopenharmony_ci jrpriv = dev_get_drvdata(jrdev); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (jrpriv->hwrng) 19362306a36Sopenharmony_ci caam_rng_exit(jrdev->parent); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* 19662306a36Sopenharmony_ci * Return EBUSY if job ring already allocated. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci if (atomic_read(&jrpriv->tfm_count)) { 19962306a36Sopenharmony_ci dev_err(jrdev, "Device is busy\n"); 20062306a36Sopenharmony_ci return -EBUSY; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Unregister JR-based RNG & crypto algorithms */ 20462306a36Sopenharmony_ci unregister_algs(); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* Remove the node from Physical JobR list maintained by driver */ 20762306a36Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 20862306a36Sopenharmony_ci list_del(&jrpriv->list_node); 20962306a36Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Release ring */ 21262306a36Sopenharmony_ci ret = caam_jr_shutdown(jrdev); 21362306a36Sopenharmony_ci if (ret) 21462306a36Sopenharmony_ci dev_err(jrdev, "Failed to shut down job ring\n"); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic void caam_jr_platform_shutdown(struct platform_device *pdev) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci caam_jr_remove(pdev); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/* Main per-ring interrupt handler */ 22562306a36Sopenharmony_cistatic irqreturn_t caam_jr_interrupt(int irq, void *st_dev) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct device *dev = st_dev; 22862306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 22962306a36Sopenharmony_ci u32 irqstate; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* 23262306a36Sopenharmony_ci * Check the output ring for ready responses, kick 23362306a36Sopenharmony_ci * tasklet if jobs done. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci irqstate = rd_reg32(&jrp->rregs->jrintstatus); 23662306a36Sopenharmony_ci if (!(irqstate & JRINT_JR_INT)) 23762306a36Sopenharmony_ci return IRQ_NONE; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * If JobR error, we got more development work to do 24162306a36Sopenharmony_ci * Flag a bug now, but we really need to shut down and 24262306a36Sopenharmony_ci * restart the queue (and fix code). 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci if (irqstate & JRINT_JR_ERROR) { 24562306a36Sopenharmony_ci dev_err(dev, "job ring error: irqstate: %08x\n", irqstate); 24662306a36Sopenharmony_ci BUG(); 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci /* mask valid interrupts */ 25062306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Have valid interrupt at this point, just ACK and trigger */ 25362306a36Sopenharmony_ci wr_reg32(&jrp->rregs->jrintstatus, irqstate); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci preempt_disable(); 25662306a36Sopenharmony_ci tasklet_schedule(&jrp->irqtask); 25762306a36Sopenharmony_ci preempt_enable(); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci return IRQ_HANDLED; 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* Deferred service handler, run as interrupt-fired tasklet */ 26362306a36Sopenharmony_cistatic void caam_jr_dequeue(unsigned long devarg) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci int hw_idx, sw_idx, i, head, tail; 26662306a36Sopenharmony_ci struct caam_jr_dequeue_params *params = (void *)devarg; 26762306a36Sopenharmony_ci struct device *dev = params->dev; 26862306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 26962306a36Sopenharmony_ci void (*usercall)(struct device *dev, u32 *desc, u32 status, void *arg); 27062306a36Sopenharmony_ci u32 *userdesc, userstatus; 27162306a36Sopenharmony_ci void *userarg; 27262306a36Sopenharmony_ci u32 outring_used = 0; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci while (outring_used || 27562306a36Sopenharmony_ci (outring_used = rd_reg32(&jrp->rregs->outring_used))) { 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci head = READ_ONCE(jrp->head); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci sw_idx = tail = jrp->tail; 28062306a36Sopenharmony_ci hw_idx = jrp->out_ring_read_index; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci for (i = 0; CIRC_CNT(head, tail + i, JOBR_DEPTH) >= 1; i++) { 28362306a36Sopenharmony_ci sw_idx = (tail + i) & (JOBR_DEPTH - 1); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (jr_outentry_desc(jrp->outring, hw_idx) == 28662306a36Sopenharmony_ci caam_dma_to_cpu(jrp->entinfo[sw_idx].desc_addr_dma)) 28762306a36Sopenharmony_ci break; /* found */ 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci /* we should never fail to find a matching descriptor */ 29062306a36Sopenharmony_ci BUG_ON(CIRC_CNT(head, tail + i, JOBR_DEPTH) <= 0); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Unmap just-run descriptor so we can post-process */ 29362306a36Sopenharmony_ci dma_unmap_single(dev, 29462306a36Sopenharmony_ci caam_dma_to_cpu(jr_outentry_desc(jrp->outring, 29562306a36Sopenharmony_ci hw_idx)), 29662306a36Sopenharmony_ci jrp->entinfo[sw_idx].desc_size, 29762306a36Sopenharmony_ci DMA_TO_DEVICE); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* mark completed, avoid matching on a recycled desc addr */ 30062306a36Sopenharmony_ci jrp->entinfo[sw_idx].desc_addr_dma = 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* Stash callback params */ 30362306a36Sopenharmony_ci usercall = jrp->entinfo[sw_idx].callbk; 30462306a36Sopenharmony_ci userarg = jrp->entinfo[sw_idx].cbkarg; 30562306a36Sopenharmony_ci userdesc = jrp->entinfo[sw_idx].desc_addr_virt; 30662306a36Sopenharmony_ci userstatus = caam32_to_cpu(jr_outentry_jrstatus(jrp->outring, 30762306a36Sopenharmony_ci hw_idx)); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* 31062306a36Sopenharmony_ci * Make sure all information from the job has been obtained 31162306a36Sopenharmony_ci * before telling CAAM that the job has been removed from the 31262306a36Sopenharmony_ci * output ring. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci mb(); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* set done */ 31762306a36Sopenharmony_ci wr_reg32(&jrp->rregs->outring_rmvd, 1); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci jrp->out_ring_read_index = (jrp->out_ring_read_index + 1) & 32062306a36Sopenharmony_ci (JOBR_DEPTH - 1); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* 32362306a36Sopenharmony_ci * if this job completed out-of-order, do not increment 32462306a36Sopenharmony_ci * the tail. Otherwise, increment tail by 1 plus the 32562306a36Sopenharmony_ci * number of subsequent jobs already completed out-of-order 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci if (sw_idx == tail) { 32862306a36Sopenharmony_ci do { 32962306a36Sopenharmony_ci tail = (tail + 1) & (JOBR_DEPTH - 1); 33062306a36Sopenharmony_ci } while (CIRC_CNT(head, tail, JOBR_DEPTH) >= 1 && 33162306a36Sopenharmony_ci jrp->entinfo[tail].desc_addr_dma == 0); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci jrp->tail = tail; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* Finally, execute user's callback */ 33762306a36Sopenharmony_ci usercall(dev, userdesc, userstatus, userarg); 33862306a36Sopenharmony_ci outring_used--; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (params->enable_itr) 34262306a36Sopenharmony_ci /* reenable / unmask IRQs */ 34362306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0); 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci/** 34762306a36Sopenharmony_ci * caam_jr_alloc() - Alloc a job ring for someone to use as needed. 34862306a36Sopenharmony_ci * 34962306a36Sopenharmony_ci * returns : pointer to the newly allocated physical 35062306a36Sopenharmony_ci * JobR dev can be written to if successful. 35162306a36Sopenharmony_ci **/ 35262306a36Sopenharmony_cistruct device *caam_jr_alloc(void) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv, *min_jrpriv = NULL; 35562306a36Sopenharmony_ci struct device *dev = ERR_PTR(-ENODEV); 35662306a36Sopenharmony_ci int min_tfm_cnt = INT_MAX; 35762306a36Sopenharmony_ci int tfm_cnt; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (list_empty(&driver_data.jr_list)) { 36262306a36Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 36362306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci list_for_each_entry(jrpriv, &driver_data.jr_list, list_node) { 36762306a36Sopenharmony_ci tfm_cnt = atomic_read(&jrpriv->tfm_count); 36862306a36Sopenharmony_ci if (tfm_cnt < min_tfm_cnt) { 36962306a36Sopenharmony_ci min_tfm_cnt = tfm_cnt; 37062306a36Sopenharmony_ci min_jrpriv = jrpriv; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci if (!min_tfm_cnt) 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (min_jrpriv) { 37762306a36Sopenharmony_ci atomic_inc(&min_jrpriv->tfm_count); 37862306a36Sopenharmony_ci dev = min_jrpriv->dev; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return dev; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ciEXPORT_SYMBOL(caam_jr_alloc); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * caam_jr_free() - Free the Job Ring 38862306a36Sopenharmony_ci * @rdev: points to the dev that identifies the Job ring to 38962306a36Sopenharmony_ci * be released. 39062306a36Sopenharmony_ci **/ 39162306a36Sopenharmony_civoid caam_jr_free(struct device *rdev) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv = dev_get_drvdata(rdev); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci atomic_dec(&jrpriv->tfm_count); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ciEXPORT_SYMBOL(caam_jr_free); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci/** 40062306a36Sopenharmony_ci * caam_jr_enqueue() - Enqueue a job descriptor head. Returns -EINPROGRESS 40162306a36Sopenharmony_ci * if OK, -ENOSPC if the queue is full, -EIO if it cannot map the caller's 40262306a36Sopenharmony_ci * descriptor. 40362306a36Sopenharmony_ci * @dev: struct device of the job ring to be used 40462306a36Sopenharmony_ci * @desc: points to a job descriptor that execute our request. All 40562306a36Sopenharmony_ci * descriptors (and all referenced data) must be in a DMAable 40662306a36Sopenharmony_ci * region, and all data references must be physical addresses 40762306a36Sopenharmony_ci * accessible to CAAM (i.e. within a PAMU window granted 40862306a36Sopenharmony_ci * to it). 40962306a36Sopenharmony_ci * @cbk: pointer to a callback function to be invoked upon completion 41062306a36Sopenharmony_ci * of this request. This has the form: 41162306a36Sopenharmony_ci * callback(struct device *dev, u32 *desc, u32 stat, void *arg) 41262306a36Sopenharmony_ci * where: 41362306a36Sopenharmony_ci * dev: contains the job ring device that processed this 41462306a36Sopenharmony_ci * response. 41562306a36Sopenharmony_ci * desc: descriptor that initiated the request, same as 41662306a36Sopenharmony_ci * "desc" being argued to caam_jr_enqueue(). 41762306a36Sopenharmony_ci * status: untranslated status received from CAAM. See the 41862306a36Sopenharmony_ci * reference manual for a detailed description of 41962306a36Sopenharmony_ci * error meaning, or see the JRSTA definitions in the 42062306a36Sopenharmony_ci * register header file 42162306a36Sopenharmony_ci * areq: optional pointer to an argument passed with the 42262306a36Sopenharmony_ci * original request 42362306a36Sopenharmony_ci * @areq: optional pointer to a user argument for use at callback 42462306a36Sopenharmony_ci * time. 42562306a36Sopenharmony_ci **/ 42662306a36Sopenharmony_ciint caam_jr_enqueue(struct device *dev, u32 *desc, 42762306a36Sopenharmony_ci void (*cbk)(struct device *dev, u32 *desc, 42862306a36Sopenharmony_ci u32 status, void *areq), 42962306a36Sopenharmony_ci void *areq) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 43262306a36Sopenharmony_ci struct caam_jrentry_info *head_entry; 43362306a36Sopenharmony_ci int head, tail, desc_size; 43462306a36Sopenharmony_ci dma_addr_t desc_dma; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci desc_size = (caam32_to_cpu(*desc) & HDR_JD_LENGTH_MASK) * sizeof(u32); 43762306a36Sopenharmony_ci desc_dma = dma_map_single(dev, desc, desc_size, DMA_TO_DEVICE); 43862306a36Sopenharmony_ci if (dma_mapping_error(dev, desc_dma)) { 43962306a36Sopenharmony_ci dev_err(dev, "caam_jr_enqueue(): can't map jobdesc\n"); 44062306a36Sopenharmony_ci return -EIO; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci spin_lock_bh(&jrp->inplock); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci head = jrp->head; 44662306a36Sopenharmony_ci tail = READ_ONCE(jrp->tail); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci if (!jrp->inpring_avail || 44962306a36Sopenharmony_ci CIRC_SPACE(head, tail, JOBR_DEPTH) <= 0) { 45062306a36Sopenharmony_ci spin_unlock_bh(&jrp->inplock); 45162306a36Sopenharmony_ci dma_unmap_single(dev, desc_dma, desc_size, DMA_TO_DEVICE); 45262306a36Sopenharmony_ci return -ENOSPC; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci head_entry = &jrp->entinfo[head]; 45662306a36Sopenharmony_ci head_entry->desc_addr_virt = desc; 45762306a36Sopenharmony_ci head_entry->desc_size = desc_size; 45862306a36Sopenharmony_ci head_entry->callbk = (void *)cbk; 45962306a36Sopenharmony_ci head_entry->cbkarg = areq; 46062306a36Sopenharmony_ci head_entry->desc_addr_dma = desc_dma; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci jr_inpentry_set(jrp->inpring, head, cpu_to_caam_dma(desc_dma)); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* 46562306a36Sopenharmony_ci * Guarantee that the descriptor's DMA address has been written to 46662306a36Sopenharmony_ci * the next slot in the ring before the write index is updated, since 46762306a36Sopenharmony_ci * other cores may update this index independently. 46862306a36Sopenharmony_ci * 46962306a36Sopenharmony_ci * Under heavy DDR load, smp_wmb() or dma_wmb() fail to make the input 47062306a36Sopenharmony_ci * ring be updated before the CAAM starts reading it. So, CAAM will 47162306a36Sopenharmony_ci * process, again, an old descriptor address and will put it in the 47262306a36Sopenharmony_ci * output ring. This will make caam_jr_dequeue() to fail, since this 47362306a36Sopenharmony_ci * old descriptor is not in the software ring. 47462306a36Sopenharmony_ci * To fix this, use wmb() which works on the full system instead of 47562306a36Sopenharmony_ci * inner/outer shareable domains. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci wmb(); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci jrp->head = (head + 1) & (JOBR_DEPTH - 1); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci /* 48262306a36Sopenharmony_ci * Ensure that all job information has been written before 48362306a36Sopenharmony_ci * notifying CAAM that a new job was added to the input ring 48462306a36Sopenharmony_ci * using a memory barrier. The wr_reg32() uses api iowrite32() 48562306a36Sopenharmony_ci * to do the register write. iowrite32() issues a memory barrier 48662306a36Sopenharmony_ci * before the write operation. 48762306a36Sopenharmony_ci */ 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci wr_reg32(&jrp->rregs->inpring_jobadd, 1); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci jrp->inpring_avail--; 49262306a36Sopenharmony_ci if (!jrp->inpring_avail) 49362306a36Sopenharmony_ci jrp->inpring_avail = rd_reg32(&jrp->rregs->inpring_avail); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci spin_unlock_bh(&jrp->inplock); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return -EINPROGRESS; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ciEXPORT_SYMBOL(caam_jr_enqueue); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cistatic void caam_jr_init_hw(struct device *dev, dma_addr_t inpbusaddr, 50262306a36Sopenharmony_ci dma_addr_t outbusaddr) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci wr_reg64(&jrp->rregs->inpring_base, inpbusaddr); 50762306a36Sopenharmony_ci wr_reg64(&jrp->rregs->outring_base, outbusaddr); 50862306a36Sopenharmony_ci wr_reg32(&jrp->rregs->inpring_size, JOBR_DEPTH); 50962306a36Sopenharmony_ci wr_reg32(&jrp->rregs->outring_size, JOBR_DEPTH); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci /* Select interrupt coalescing parameters */ 51262306a36Sopenharmony_ci clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JOBR_INTC | 51362306a36Sopenharmony_ci (JOBR_INTC_COUNT_THLD << JRCFG_ICDCT_SHIFT) | 51462306a36Sopenharmony_ci (JOBR_INTC_TIME_THLD << JRCFG_ICTT_SHIFT)); 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void caam_jr_reset_index(struct caam_drv_private_jr *jrp) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci jrp->out_ring_read_index = 0; 52062306a36Sopenharmony_ci jrp->head = 0; 52162306a36Sopenharmony_ci jrp->tail = 0; 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci/* 52562306a36Sopenharmony_ci * Init JobR independent of platform property detection 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_cistatic int caam_jr_init(struct device *dev) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct caam_drv_private_jr *jrp; 53062306a36Sopenharmony_ci dma_addr_t inpbusaddr, outbusaddr; 53162306a36Sopenharmony_ci int i, error; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci jrp = dev_get_drvdata(dev); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci error = caam_reset_hw_jr(dev); 53662306a36Sopenharmony_ci if (error) 53762306a36Sopenharmony_ci return error; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci jrp->inpring = dmam_alloc_coherent(dev, SIZEOF_JR_INPENTRY * 54062306a36Sopenharmony_ci JOBR_DEPTH, &inpbusaddr, 54162306a36Sopenharmony_ci GFP_KERNEL); 54262306a36Sopenharmony_ci if (!jrp->inpring) 54362306a36Sopenharmony_ci return -ENOMEM; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci jrp->outring = dmam_alloc_coherent(dev, SIZEOF_JR_OUTENTRY * 54662306a36Sopenharmony_ci JOBR_DEPTH, &outbusaddr, 54762306a36Sopenharmony_ci GFP_KERNEL); 54862306a36Sopenharmony_ci if (!jrp->outring) 54962306a36Sopenharmony_ci return -ENOMEM; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci jrp->entinfo = devm_kcalloc(dev, JOBR_DEPTH, sizeof(*jrp->entinfo), 55262306a36Sopenharmony_ci GFP_KERNEL); 55362306a36Sopenharmony_ci if (!jrp->entinfo) 55462306a36Sopenharmony_ci return -ENOMEM; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci for (i = 0; i < JOBR_DEPTH; i++) 55762306a36Sopenharmony_ci jrp->entinfo[i].desc_addr_dma = !0; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* Setup rings */ 56062306a36Sopenharmony_ci caam_jr_reset_index(jrp); 56162306a36Sopenharmony_ci jrp->inpring_avail = JOBR_DEPTH; 56262306a36Sopenharmony_ci caam_jr_init_hw(dev, inpbusaddr, outbusaddr); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci spin_lock_init(&jrp->inplock); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci jrp->tasklet_params.dev = dev; 56762306a36Sopenharmony_ci jrp->tasklet_params.enable_itr = 1; 56862306a36Sopenharmony_ci tasklet_init(&jrp->irqtask, caam_jr_dequeue, 56962306a36Sopenharmony_ci (unsigned long)&jrp->tasklet_params); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci /* Connect job ring interrupt handler. */ 57262306a36Sopenharmony_ci error = devm_request_irq(dev, jrp->irq, caam_jr_interrupt, IRQF_SHARED, 57362306a36Sopenharmony_ci dev_name(dev), dev); 57462306a36Sopenharmony_ci if (error) { 57562306a36Sopenharmony_ci dev_err(dev, "can't connect JobR %d interrupt (%d)\n", 57662306a36Sopenharmony_ci jrp->ridx, jrp->irq); 57762306a36Sopenharmony_ci tasklet_kill(&jrp->irqtask); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return error; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_cistatic void caam_jr_irq_dispose_mapping(void *data) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci irq_dispose_mapping((unsigned long)data); 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci/* 58962306a36Sopenharmony_ci * Probe routine for each detected JobR subsystem. 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic int caam_jr_probe(struct platform_device *pdev) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct device *jrdev; 59462306a36Sopenharmony_ci struct device_node *nprop; 59562306a36Sopenharmony_ci struct caam_job_ring __iomem *ctrl; 59662306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv; 59762306a36Sopenharmony_ci static int total_jobrs; 59862306a36Sopenharmony_ci struct resource *r; 59962306a36Sopenharmony_ci int error; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci jrdev = &pdev->dev; 60262306a36Sopenharmony_ci jrpriv = devm_kzalloc(jrdev, sizeof(*jrpriv), GFP_KERNEL); 60362306a36Sopenharmony_ci if (!jrpriv) 60462306a36Sopenharmony_ci return -ENOMEM; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci dev_set_drvdata(jrdev, jrpriv); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* save ring identity relative to detection */ 60962306a36Sopenharmony_ci jrpriv->ridx = total_jobrs++; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci nprop = pdev->dev.of_node; 61262306a36Sopenharmony_ci /* Get configuration properties from device tree */ 61362306a36Sopenharmony_ci /* First, get register page */ 61462306a36Sopenharmony_ci r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 61562306a36Sopenharmony_ci if (!r) { 61662306a36Sopenharmony_ci dev_err(jrdev, "platform_get_resource() failed\n"); 61762306a36Sopenharmony_ci return -ENOMEM; 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci ctrl = devm_ioremap(jrdev, r->start, resource_size(r)); 62162306a36Sopenharmony_ci if (!ctrl) { 62262306a36Sopenharmony_ci dev_err(jrdev, "devm_ioremap() failed\n"); 62362306a36Sopenharmony_ci return -ENOMEM; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci jrpriv->rregs = (struct caam_job_ring __iomem __force *)ctrl; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci error = dma_set_mask_and_coherent(jrdev, caam_get_dma_mask(jrdev)); 62962306a36Sopenharmony_ci if (error) { 63062306a36Sopenharmony_ci dev_err(jrdev, "dma_set_mask_and_coherent failed (%d)\n", 63162306a36Sopenharmony_ci error); 63262306a36Sopenharmony_ci return error; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* Initialize crypto engine */ 63662306a36Sopenharmony_ci jrpriv->engine = crypto_engine_alloc_init_and_set(jrdev, true, NULL, 63762306a36Sopenharmony_ci false, 63862306a36Sopenharmony_ci CRYPTO_ENGINE_MAX_QLEN); 63962306a36Sopenharmony_ci if (!jrpriv->engine) { 64062306a36Sopenharmony_ci dev_err(jrdev, "Could not init crypto-engine\n"); 64162306a36Sopenharmony_ci return -ENOMEM; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci error = devm_add_action_or_reset(jrdev, caam_jr_crypto_engine_exit, 64562306a36Sopenharmony_ci jrdev); 64662306a36Sopenharmony_ci if (error) 64762306a36Sopenharmony_ci return error; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* Start crypto engine */ 65062306a36Sopenharmony_ci error = crypto_engine_start(jrpriv->engine); 65162306a36Sopenharmony_ci if (error) { 65262306a36Sopenharmony_ci dev_err(jrdev, "Could not start crypto-engine\n"); 65362306a36Sopenharmony_ci return error; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci /* Identify the interrupt */ 65762306a36Sopenharmony_ci jrpriv->irq = irq_of_parse_and_map(nprop, 0); 65862306a36Sopenharmony_ci if (!jrpriv->irq) { 65962306a36Sopenharmony_ci dev_err(jrdev, "irq_of_parse_and_map failed\n"); 66062306a36Sopenharmony_ci return -EINVAL; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci error = devm_add_action_or_reset(jrdev, caam_jr_irq_dispose_mapping, 66462306a36Sopenharmony_ci (void *)(unsigned long)jrpriv->irq); 66562306a36Sopenharmony_ci if (error) 66662306a36Sopenharmony_ci return error; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Now do the platform independent part */ 66962306a36Sopenharmony_ci error = caam_jr_init(jrdev); /* now turn on hardware */ 67062306a36Sopenharmony_ci if (error) 67162306a36Sopenharmony_ci return error; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci jrpriv->dev = jrdev; 67462306a36Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 67562306a36Sopenharmony_ci list_add_tail(&jrpriv->list_node, &driver_data.jr_list); 67662306a36Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci atomic_set(&jrpriv->tfm_count, 0); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci device_init_wakeup(&pdev->dev, 1); 68162306a36Sopenharmony_ci device_set_wakeup_enable(&pdev->dev, false); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci register_algs(jrpriv, jrdev->parent); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void caam_jr_get_hw_state(struct device *dev) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci jrp->state.inpbusaddr = rd_reg64(&jrp->rregs->inpring_base); 69362306a36Sopenharmony_ci jrp->state.outbusaddr = rd_reg64(&jrp->rregs->outring_base); 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int caam_jr_suspend(struct device *dev) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 69962306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv = platform_get_drvdata(pdev); 70062306a36Sopenharmony_ci struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev->parent); 70162306a36Sopenharmony_ci struct caam_jr_dequeue_params suspend_params = { 70262306a36Sopenharmony_ci .dev = dev, 70362306a36Sopenharmony_ci .enable_itr = 0, 70462306a36Sopenharmony_ci }; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci /* Remove the node from Physical JobR list maintained by driver */ 70762306a36Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 70862306a36Sopenharmony_ci list_del(&jrpriv->list_node); 70962306a36Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci if (jrpriv->hwrng) 71262306a36Sopenharmony_ci caam_rng_exit(dev->parent); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (ctrlpriv->caam_off_during_pm) { 71562306a36Sopenharmony_ci int err; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci tasklet_disable(&jrpriv->irqtask); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci /* mask itr to call flush */ 72062306a36Sopenharmony_ci clrsetbits_32(&jrpriv->rregs->rconfig_lo, 0, JRCFG_IMSK); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Invalid job in process */ 72362306a36Sopenharmony_ci err = caam_jr_flush(dev); 72462306a36Sopenharmony_ci if (err) { 72562306a36Sopenharmony_ci dev_err(dev, "Failed to flush\n"); 72662306a36Sopenharmony_ci return err; 72762306a36Sopenharmony_ci } 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Dequeing jobs flushed */ 73062306a36Sopenharmony_ci caam_jr_dequeue((unsigned long)&suspend_params); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Save state */ 73362306a36Sopenharmony_ci caam_jr_get_hw_state(dev); 73462306a36Sopenharmony_ci } else if (device_may_wakeup(&pdev->dev)) { 73562306a36Sopenharmony_ci enable_irq_wake(jrpriv->irq); 73662306a36Sopenharmony_ci } 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return 0; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic int caam_jr_resume(struct device *dev) 74262306a36Sopenharmony_ci{ 74362306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 74462306a36Sopenharmony_ci struct caam_drv_private_jr *jrpriv = platform_get_drvdata(pdev); 74562306a36Sopenharmony_ci struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev->parent); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (ctrlpriv->caam_off_during_pm) { 74862306a36Sopenharmony_ci u64 inp_addr; 74962306a36Sopenharmony_ci int err; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* 75262306a36Sopenharmony_ci * Check if the CAAM has been resetted checking the address of 75362306a36Sopenharmony_ci * the input ring 75462306a36Sopenharmony_ci */ 75562306a36Sopenharmony_ci inp_addr = rd_reg64(&jrpriv->rregs->inpring_base); 75662306a36Sopenharmony_ci if (inp_addr != 0) { 75762306a36Sopenharmony_ci /* JR still has some configuration */ 75862306a36Sopenharmony_ci if (inp_addr == jrpriv->state.inpbusaddr) { 75962306a36Sopenharmony_ci /* JR has not been resetted */ 76062306a36Sopenharmony_ci err = caam_jr_restart_processing(dev); 76162306a36Sopenharmony_ci if (err) { 76262306a36Sopenharmony_ci dev_err(dev, 76362306a36Sopenharmony_ci "Restart processing failed\n"); 76462306a36Sopenharmony_ci return err; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci tasklet_enable(&jrpriv->irqtask); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci clrsetbits_32(&jrpriv->rregs->rconfig_lo, 77062306a36Sopenharmony_ci JRCFG_IMSK, 0); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci goto add_jr; 77362306a36Sopenharmony_ci } else if (ctrlpriv->optee_en) { 77462306a36Sopenharmony_ci /* JR has been used by OPTEE, reset it */ 77562306a36Sopenharmony_ci err = caam_reset_hw_jr(dev); 77662306a36Sopenharmony_ci if (err) { 77762306a36Sopenharmony_ci dev_err(dev, "Failed to reset JR\n"); 77862306a36Sopenharmony_ci return err; 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci } else { 78162306a36Sopenharmony_ci /* No explanation, return error */ 78262306a36Sopenharmony_ci return -EIO; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci caam_jr_reset_index(jrpriv); 78762306a36Sopenharmony_ci caam_jr_init_hw(dev, jrpriv->state.inpbusaddr, 78862306a36Sopenharmony_ci jrpriv->state.outbusaddr); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci tasklet_enable(&jrpriv->irqtask); 79162306a36Sopenharmony_ci } else if (device_may_wakeup(&pdev->dev)) { 79262306a36Sopenharmony_ci disable_irq_wake(jrpriv->irq); 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ciadd_jr: 79662306a36Sopenharmony_ci spin_lock(&driver_data.jr_alloc_lock); 79762306a36Sopenharmony_ci list_add_tail(&jrpriv->list_node, &driver_data.jr_list); 79862306a36Sopenharmony_ci spin_unlock(&driver_data.jr_alloc_lock); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (jrpriv->hwrng) 80162306a36Sopenharmony_ci jrpriv->hwrng = !caam_rng_init(dev->parent); 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci return 0; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(caam_jr_pm_ops, caam_jr_suspend, caam_jr_resume); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_cistatic const struct of_device_id caam_jr_match[] = { 80962306a36Sopenharmony_ci { 81062306a36Sopenharmony_ci .compatible = "fsl,sec-v4.0-job-ring", 81162306a36Sopenharmony_ci }, 81262306a36Sopenharmony_ci { 81362306a36Sopenharmony_ci .compatible = "fsl,sec4.0-job-ring", 81462306a36Sopenharmony_ci }, 81562306a36Sopenharmony_ci {}, 81662306a36Sopenharmony_ci}; 81762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, caam_jr_match); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic struct platform_driver caam_jr_driver = { 82062306a36Sopenharmony_ci .driver = { 82162306a36Sopenharmony_ci .name = "caam_jr", 82262306a36Sopenharmony_ci .of_match_table = caam_jr_match, 82362306a36Sopenharmony_ci .pm = pm_ptr(&caam_jr_pm_ops), 82462306a36Sopenharmony_ci }, 82562306a36Sopenharmony_ci .probe = caam_jr_probe, 82662306a36Sopenharmony_ci .remove = caam_jr_remove, 82762306a36Sopenharmony_ci .shutdown = caam_jr_platform_shutdown, 82862306a36Sopenharmony_ci}; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic int __init jr_driver_init(void) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci spin_lock_init(&driver_data.jr_alloc_lock); 83362306a36Sopenharmony_ci INIT_LIST_HEAD(&driver_data.jr_list); 83462306a36Sopenharmony_ci return platform_driver_register(&caam_jr_driver); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic void __exit jr_driver_exit(void) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci platform_driver_unregister(&caam_jr_driver); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cimodule_init(jr_driver_init); 84362306a36Sopenharmony_cimodule_exit(jr_driver_exit); 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 84662306a36Sopenharmony_ciMODULE_DESCRIPTION("FSL CAAM JR request backend"); 84762306a36Sopenharmony_ciMODULE_AUTHOR("Freescale Semiconductor - NMG/STC"); 848