Reviewed-by: Gaurav Jain <gaurav.jain@xxxxxxx> > -----Original Message----- > From: Meenakshi Aggarwal <meenakshi.aggarwal@xxxxxxx> > Sent: Wednesday, July 12, 2023 11:37 AM > To: Horia Geanta <horia.geanta@xxxxxxx>; Varun Sethi <V.Sethi@xxxxxxx>; > Pankaj Gupta <pankaj.gupta@xxxxxxx>; Gaurav Jain <gaurav.jain@xxxxxxx>; > herbert@xxxxxxxxxxxxxxxxxxx; davem@xxxxxxxxxxxxx; linux- > crypto@xxxxxxxxxxxxxxx; linux-kernel@xxxxxxxxxxxxxxx > Cc: Victoria Milhoan <vicki.milhoan@xxxxxxxxxxxxx>; Dan Douglass > <dan.douglass@xxxxxxx>; Vipul Kumar <vipul_kumar@xxxxxxxxxx>; Franck > Lenormand <franck.lenormand@xxxxxxx>; Meenakshi Aggarwal > <meenakshi.aggarwal@xxxxxxx> > Subject: [PATCH 2/2] crypto: caam - add power management support > > From: Horia Geanta <horia.geanta@xxxxxxx> > > Add support for suspend and resume operation for PM in CAAM driver. > > When the CAAM goes in suspend, the hardware is considered to do nothing. > > On some platforms, the power of the CAAM is not turned off so it keeps its > configuration. > On other platforms, it doesn't so it is necessary to save the state of the CAAM: > - JRs MID > - Address of input and output rings > > Signed-off-by: Horia Geanta <horia.geanta@xxxxxxx> > Signed-off-by: Victoria Milhoan <vicki.milhoan@xxxxxxxxxxxxx> > Signed-off-by: Dan Douglass <dan.douglass@xxxxxxx> > Signed-off-by: Vipul Kumar <vipul_kumar@xxxxxxxxxx> > Signed-off-by: Franck LENORMAND <franck.lenormand@xxxxxxx> > Signed-off-by: Meenakshi Aggarwal <meenakshi.aggarwal@xxxxxxx> > --- > drivers/crypto/caam/ctrl.c | 114 ++++++++++++++++++++ > drivers/crypto/caam/intern.h | 33 +++++- > drivers/crypto/caam/jr.c | 199 +++++++++++++++++++++++++++++++---- > drivers/crypto/caam/regs.h | 3 +- > 4 files changed, 328 insertions(+), 21 deletions(-) > > diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index > ff9ddbbca377..64b6cd09d8a8 100644 > --- a/drivers/crypto/caam/ctrl.c > +++ b/drivers/crypto/caam/ctrl.c > @@ -740,6 +740,113 @@ static int caam_ctrl_rng_init(struct device *dev) > return 0; > } > > +#ifdef CONFIG_PM_SLEEP > + > +/* Indicate if the internal state of the CAAM is lost during PM */ > +static int caam_off_during_pm(void) { > + bool not_off_during_pm = of_machine_is_compatible("fsl,imx6q") || > + of_machine_is_compatible("fsl,imx6qp") || > + of_machine_is_compatible("fsl,imx6dl"); > + > + return not_off_during_pm ? 0 : 1; > +} > + > +static void caam_state_save(struct device *dev) { > + struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); > + struct caam_ctl_state *state = &ctrlpriv->state; > + struct caam_ctrl __iomem *ctrl = ctrlpriv->ctrl; > + u32 deco_inst, jr_inst; > + int i; > + > + state->mcr = rd_reg32(&ctrl->mcr); > + state->scfgr = rd_reg32(&ctrl->scfgr); > + > + deco_inst = (rd_reg32(&ctrl->perfmon.cha_num_ms) & > + CHA_ID_MS_DECO_MASK) >> CHA_ID_MS_DECO_SHIFT; > + for (i = 0; i < deco_inst; i++) { > + state->deco_mid[i].liodn_ms = > + rd_reg32(&ctrl->deco_mid[i].liodn_ms); > + state->deco_mid[i].liodn_ls = > + rd_reg32(&ctrl->deco_mid[i].liodn_ls); > + } > + > + jr_inst = (rd_reg32(&ctrl->perfmon.cha_num_ms) & > + CHA_ID_MS_JR_MASK) >> CHA_ID_MS_JR_SHIFT; > + for (i = 0; i < jr_inst; i++) { > + state->jr_mid[i].liodn_ms = > + rd_reg32(&ctrl->jr_mid[i].liodn_ms); > + state->jr_mid[i].liodn_ls = > + rd_reg32(&ctrl->jr_mid[i].liodn_ls); > + } > +} > + > +static void caam_state_restore(const struct device *dev) { > + const struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); > + const struct caam_ctl_state *state = &ctrlpriv->state; > + struct caam_ctrl __iomem *ctrl = ctrlpriv->ctrl; > + u32 deco_inst, jr_inst; > + int i; > + > + wr_reg32(&ctrl->mcr, state->mcr); > + wr_reg32(&ctrl->scfgr, state->scfgr); > + > + deco_inst = (rd_reg32(&ctrl->perfmon.cha_num_ms) & > + CHA_ID_MS_DECO_MASK) >> CHA_ID_MS_DECO_SHIFT; > + for (i = 0; i < deco_inst; i++) { > + wr_reg32(&ctrl->deco_mid[i].liodn_ms, > + state->deco_mid[i].liodn_ms); > + wr_reg32(&ctrl->deco_mid[i].liodn_ls, > + state->deco_mid[i].liodn_ls); > + } > + > + jr_inst = (rd_reg32(&ctrl->perfmon.cha_num_ms) & > + CHA_ID_MS_JR_MASK) >> CHA_ID_MS_JR_SHIFT; > + for (i = 0; i < jr_inst; i++) { > + wr_reg32(&ctrl->jr_mid[i].liodn_ms, > + state->jr_mid[i].liodn_ms); > + wr_reg32(&ctrl->jr_mid[i].liodn_ls, > + state->jr_mid[i].liodn_ls); > + } > + > + if (ctrlpriv->virt_en == 1) > + clrsetbits_32(&ctrl->jrstart, 0, JRSTART_JR0_START | > + JRSTART_JR1_START | JRSTART_JR2_START | > + JRSTART_JR3_START); > +} > + > +static int caam_ctrl_suspend(struct device *dev) { > + const struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); > + > + if (ctrlpriv->caam_off_during_pm && !ctrlpriv->optee_en) > + caam_state_save(dev); > + > + return 0; > +} > + > +static int caam_ctrl_resume(struct device *dev) { > + struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev); > + int ret = 0; > + > + if (ctrlpriv->caam_off_during_pm && !ctrlpriv->optee_en) { > + caam_state_restore(dev); > + > + /* HW and rng will be reset so deinstantiation can be removed > */ > + devm_remove_action(dev, devm_deinstantiate_rng, dev); > + ret = caam_ctrl_rng_init(dev); > + } > + > + return ret; > +} > + > +static SIMPLE_DEV_PM_OPS(caam_ctrl_pm_ops, caam_ctrl_suspend, > +caam_ctrl_resume); > + > +#endif /* CONFIG_PM_SLEEP */ > + > /* Probe routine for CAAM top (controller) level */ static int caam_probe(struct > platform_device *pdev) { @@ -771,6 +878,10 @@ static int caam_probe(struct > platform_device *pdev) > > caam_imx = (bool)imx_soc_match; > > +#ifdef CONFIG_PM_SLEEP > + ctrlpriv->caam_off_during_pm = caam_imx && caam_off_during_pm(); > +#endif > + > if (imx_soc_match) { > /* > * Until Layerscape and i.MX OP-TEE get in sync, @@ -1033,6 > +1144,9 @@ static struct platform_driver caam_driver = { > .driver = { > .name = "caam", > .of_match_table = caam_match, > +#ifdef CONFIG_PM_SLEEP > + .pm = &caam_ctrl_pm_ops, > +#endif > }, > .probe = caam_probe, > }; > diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h index > b4f7bf77f487..fcf0a080b5d6 100644 > --- a/drivers/crypto/caam/intern.h > +++ b/drivers/crypto/caam/intern.h > @@ -4,7 +4,7 @@ > * Private/internal definitions between modules > * > * Copyright 2008-2011 Freescale Semiconductor, Inc. > - * Copyright 2019 NXP > + * Copyright 2019, 2023 NXP > */ > > #ifndef INTERN_H > @@ -47,6 +47,18 @@ struct caam_jrentry_info { > u32 desc_size; /* Stored size for postprocessing, header derived */ > }; > > +#ifdef CONFIG_PM_SLEEP > +struct caam_jr_state { > + dma_addr_t inpbusaddr; > + dma_addr_t outbusaddr; > +}; > +#endif > + > +struct caam_jr_dequeue_params { > + struct device *dev; > + int enable_itr; > +}; > + > /* Private sub-storage for a single JobR */ struct caam_drv_private_jr { > struct list_head list_node; /* Job Ring device list */ > @@ -54,6 +66,7 @@ struct caam_drv_private_jr { > int ridx; > struct caam_job_ring __iomem *rregs; /* JobR's register space */ > struct tasklet_struct irqtask; > + struct caam_jr_dequeue_params tasklet_params; > int irq; /* One per queue */ > bool hwrng; > > @@ -71,7 +84,20 @@ struct caam_drv_private_jr { > int tail; /* entinfo (s/w ring) tail index */ > void *outring; /* Base of output ring, DMA-safe */ > struct crypto_engine *engine; > + > +#ifdef CONFIG_PM_SLEEP > + struct caam_jr_state state; /* State of the JR during PM */ > +#endif > +}; > + > +#ifdef CONFIG_PM_SLEEP > +struct caam_ctl_state { > + struct masterid deco_mid[16]; > + struct masterid jr_mid[4]; > + u32 mcr; > + u32 scfgr; > }; > +#endif > > /* > * Driver-private storage for a single CAAM block instance @@ -116,6 +142,11 > @@ struct caam_drv_private { > struct dentry *ctl; /* controller dir */ > struct debugfs_blob_wrapper ctl_kek_wrap, ctl_tkek_wrap, > ctl_tdsk_wrap; #endif > + > +#ifdef CONFIG_PM_SLEEP > + int caam_off_during_pm; /* If the CAAM is reset after > suspend */ > + struct caam_ctl_state state; /* State of the CTL during PM */ > +#endif > }; > > #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API > diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c index > 96dea5304d22..1801638eb1c8 100644 > --- a/drivers/crypto/caam/jr.c > +++ b/drivers/crypto/caam/jr.c > @@ -117,6 +117,25 @@ static int caam_jr_flush(struct device *dev) > return caam_jr_stop_processing(dev, JRCR_RESET); } > > +#ifdef CONFIG_PM_SLEEP > +/* The resume can be used after a park or a flush if CAAM has not been > +reset */ static int caam_jr_restart_processing(struct device *dev) { > + struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); > + u32 halt_status = rd_reg32(&jrp->rregs->jrintstatus) & > + JRINT_ERR_HALT_MASK; > + > + /* Check that the flush/park is completed */ > + if (halt_status != JRINT_ERR_HALT_COMPLETE) > + return -1; > + > + /* Resume processing of jobs */ > + clrsetbits_32(&jrp->rregs->jrintstatus, 0, JRINT_ERR_HALT_COMPLETE); > + > + return 0; > +} > +#endif /* CONFIG_PM_SLEEP */ > + > static int caam_reset_hw_jr(struct device *dev) { > struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); @@ -245,7 > +264,8 @@ static irqreturn_t caam_jr_interrupt(int irq, void *st_dev) static void > caam_jr_dequeue(unsigned long devarg) { > int hw_idx, sw_idx, i, head, tail; > - struct device *dev = (struct device *)devarg; > + struct caam_jr_dequeue_params *params = (void *)devarg; > + struct device *dev = params->dev; > struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); > void (*usercall)(struct device *dev, u32 *desc, u32 status, void *arg); > u32 *userdesc, userstatus; > @@ -319,8 +339,9 @@ static void caam_jr_dequeue(unsigned long devarg) > outring_used--; > } > > - /* reenable / unmask IRQs */ > - clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0); > + if (params->enable_itr) > + /* reenable / unmask IRQs */ > + clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0); > } > > /** > @@ -470,6 +491,29 @@ int caam_jr_enqueue(struct device *dev, u32 *desc, } > EXPORT_SYMBOL(caam_jr_enqueue); > > +static void caam_jr_init_hw(struct device *dev, dma_addr_t inpbusaddr, > + dma_addr_t outbusaddr) > +{ > + struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); > + > + wr_reg64(&jrp->rregs->inpring_base, inpbusaddr); > + wr_reg64(&jrp->rregs->outring_base, outbusaddr); > + wr_reg32(&jrp->rregs->inpring_size, JOBR_DEPTH); > + wr_reg32(&jrp->rregs->outring_size, JOBR_DEPTH); > + > + /* Select interrupt coalescing parameters */ > + clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JOBR_INTC | > + (JOBR_INTC_COUNT_THLD << JRCFG_ICDCT_SHIFT) | > + (JOBR_INTC_TIME_THLD << JRCFG_ICTT_SHIFT)); } > + > +static void caam_jr_reset_index(struct caam_drv_private_jr *jrp) { > + jrp->out_ring_read_index = 0; > + jrp->head = 0; > + jrp->tail = 0; > +} > + > /* > * Init JobR independent of platform property detection > */ > @@ -506,25 +550,16 @@ static int caam_jr_init(struct device *dev) > jrp->entinfo[i].desc_addr_dma = !0; > > /* Setup rings */ > - jrp->out_ring_read_index = 0; > - jrp->head = 0; > - jrp->tail = 0; > - > - wr_reg64(&jrp->rregs->inpring_base, inpbusaddr); > - wr_reg64(&jrp->rregs->outring_base, outbusaddr); > - wr_reg32(&jrp->rregs->inpring_size, JOBR_DEPTH); > - wr_reg32(&jrp->rregs->outring_size, JOBR_DEPTH); > - > + caam_jr_reset_index(jrp); > jrp->inpring_avail = JOBR_DEPTH; > + caam_jr_init_hw(dev, inpbusaddr, outbusaddr); > > spin_lock_init(&jrp->inplock); > > - /* Select interrupt coalescing parameters */ > - clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JOBR_INTC | > - (JOBR_INTC_COUNT_THLD << JRCFG_ICDCT_SHIFT) | > - (JOBR_INTC_TIME_THLD << JRCFG_ICTT_SHIFT)); > - > - tasklet_init(&jrp->irqtask, caam_jr_dequeue, (unsigned long)dev); > + jrp->tasklet_params.dev = dev; > + jrp->tasklet_params.enable_itr = 1; > + tasklet_init(&jrp->irqtask, caam_jr_dequeue, > + (unsigned long)&jrp->tasklet_params); > > /* Connect job ring interrupt handler. */ > error = devm_request_irq(dev, jrp->irq, caam_jr_interrupt, > IRQF_SHARED, @@ -635,11 +670,136 @@ static int caam_jr_probe(struct > platform_device *pdev) > > atomic_set(&jrpriv->tfm_count, 0); > > + device_init_wakeup(&pdev->dev, 1); > + device_set_wakeup_enable(&pdev->dev, false); > + > register_algs(jrpriv, jrdev->parent); > > return 0; > } > > +#ifdef CONFIG_PM_SLEEP > +static void caam_jr_get_hw_state(struct device *dev) { > + struct caam_drv_private_jr *jrp = dev_get_drvdata(dev); > + > + jrp->state.inpbusaddr = rd_reg64(&jrp->rregs->inpring_base); > + jrp->state.outbusaddr = rd_reg64(&jrp->rregs->outring_base); > +} > + > +static int caam_jr_suspend(struct device *dev) { > + struct platform_device *pdev = to_platform_device(dev); > + struct caam_drv_private_jr *jrpriv = platform_get_drvdata(pdev); > + struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev->parent); > + struct caam_jr_dequeue_params suspend_params = { > + .dev = dev, > + .enable_itr = 0, > + }; > + > + /* Remove the node from Physical JobR list maintained by driver */ > + spin_lock(&driver_data.jr_alloc_lock); > + list_del(&jrpriv->list_node); > + spin_unlock(&driver_data.jr_alloc_lock); > + > + if (jrpriv->hwrng) > + caam_rng_exit(dev->parent); > + > + if (ctrlpriv->caam_off_during_pm) { > + int err; > + > + tasklet_disable(&jrpriv->irqtask); > + > + /* mask itr to call flush */ > + clrsetbits_32(&jrpriv->rregs->rconfig_lo, 0, JRCFG_IMSK); > + > + /* Invalid job in process */ > + err = caam_jr_flush(dev); > + if (err) { > + dev_err(dev, "Failed to flush\n"); > + return err; > + } > + > + /* Dequeing jobs flushed */ > + caam_jr_dequeue((unsigned long)&suspend_params); > + > + /* Save state */ > + caam_jr_get_hw_state(dev); > + } else if (device_may_wakeup(&pdev->dev)) { > + enable_irq_wake(jrpriv->irq); > + } > + > + return 0; > +} > + > +static int caam_jr_resume(struct device *dev) { > + struct platform_device *pdev = to_platform_device(dev); > + struct caam_drv_private_jr *jrpriv = platform_get_drvdata(pdev); > + struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev->parent); > + > + if (ctrlpriv->caam_off_during_pm) { > + u64 inp_addr; > + int err; > + > + /* > + * Check if the CAAM has been resetted checking the address of > + * the input ring > + */ > + inp_addr = rd_reg64(&jrpriv->rregs->inpring_base); > + if (inp_addr != 0) { > + /* JR still has some configuration */ > + if (inp_addr == jrpriv->state.inpbusaddr) { > + /* JR has not been resetted */ > + err = caam_jr_restart_processing(dev); > + if (err) { > + dev_err(dev, > + "Restart processing failed\n"); > + return err; > + } > + > + tasklet_enable(&jrpriv->irqtask); > + > + clrsetbits_32(&jrpriv->rregs->rconfig_lo, > + JRCFG_IMSK, 0); > + > + goto add_jr; > + } else if (ctrlpriv->optee_en) { > + /* JR has been used by OPTEE, reset it */ > + err = caam_reset_hw_jr(dev); > + if (err) { > + dev_err(dev, "Failed to reset JR\n"); > + return err; > + } > + } else { > + /* No explanation, return error */ > + return -EIO; > + } > + } > + > + caam_jr_reset_index(jrpriv); > + caam_jr_init_hw(dev, jrpriv->state.inpbusaddr, > + jrpriv->state.outbusaddr); > + > + tasklet_enable(&jrpriv->irqtask); > + } else if (device_may_wakeup(&pdev->dev)) { > + disable_irq_wake(jrpriv->irq); > + } > + > +add_jr: > + spin_lock(&driver_data.jr_alloc_lock); > + list_add_tail(&jrpriv->list_node, &driver_data.jr_list); > + spin_unlock(&driver_data.jr_alloc_lock); > + > + if (jrpriv->hwrng) > + jrpriv->hwrng = !caam_rng_init(dev->parent); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(caam_jr_pm_ops, caam_jr_suspend, > +caam_jr_resume); #endif /* CONFIG_PM_SLEEP */ > + > static const struct of_device_id caam_jr_match[] = { > { > .compatible = "fsl,sec-v4.0-job-ring", @@ -655,6 +815,9 @@ > static struct platform_driver caam_jr_driver = { > .driver = { > .name = "caam_jr", > .of_match_table = caam_jr_match, > +#ifdef CONFIG_PM_SLEEP > + .pm = &caam_jr_pm_ops, > +#endif > }, > .probe = caam_jr_probe, > .remove = caam_jr_remove, > diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index > 0f87bd365582..873df9de9890 100644 > --- a/drivers/crypto/caam/regs.h > +++ b/drivers/crypto/caam/regs.h > @@ -584,8 +584,7 @@ struct caam_ctrl { > u32 deco_rsr; /* DECORSR - Deco Request Source */ > u32 rsvd11; > u32 deco_rq; /* DECORR - DECO Request */ > - struct masterid deco_mid[5]; /* DECOxLIODNR - 1 per DECO */ > - u32 rsvd5[22]; > + struct masterid deco_mid[16]; /* DECOxLIODNR - 1 per DECO */ > > /* DECO Availability/Reset Section 120-3ff */ > u32 deco_avail; /* DAR - DECO availability */ > -- > 2.25.1