RE: [PATCH 2/2] crypto: caam - add power management support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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





[Index of Archives]     [Kernel]     [Gnu Classpath]     [Gnu Crypto]     [DM Crypt]     [Netfilter]     [Bugtraq]
  Powered by Linux