summaryrefslogtreecommitdiff
path: root/drivers/crypto
diff options
context:
space:
mode:
authorFranck LENORMAND <franck.lenormand@nxp.com>2019-03-19 16:05:30 +0100
committerFranck LENORMAND <franck.lenormand@nxp.com>2019-03-21 15:16:45 +0100
commita89d4b65df12efac82b732a02d9fd7779cb98d1f (patch)
tree18dcce5f53e4850440886dcdbb8458eacf464c23 /drivers/crypto
parent3079488be5eecd8dceafd6c8a88c0474c4376a26 (diff)
MLK-19449: crypto: caam - Add Power Management
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 Limitation: When the CAAM is powered OFF, it is resetted so the JDKEK and TDKEK changes. This impacts crypto transforms using MDHA split-keys which are kept over suspend as they are encrypted with the JDKEK: - hmac(*) from caamhash.c - authenc(hmac(*),*) from caamalg.c - echainiv(authenc(hmac(*),*)) from caamalg.c The issue was already present in current code so this patch does not add a regression in this regard. Reviewed-by: Horia Geantă <horia.geanta@nxp.com> Signed-off-by: Franck LENORMAND <franck.lenormand@nxp.com> (cherry picked from commit b90e25f285a65ee8c8433aba7fe8b19b2cdf70b9)
Diffstat (limited to 'drivers/crypto')
-rw-r--r--drivers/crypto/caam/ctrl.c177
-rw-r--r--drivers/crypto/caam/inst_rng.c13
-rw-r--r--drivers/crypto/caam/intern.h35
-rw-r--r--drivers/crypto/caam/jr.c337
4 files changed, 446 insertions, 116 deletions
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index 1097ae664347..df5ec6479b9f 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -2,7 +2,7 @@
* Controller-level driver, kernel property detection, initialization
*
* Copyright 2008-2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018 NXP
+ * Copyright 2017-2019 NXP
*/
#include <linux/device.h>
@@ -43,6 +43,12 @@ static void enable_qi(struct caam_drv_private *ctrlpriv, int block_offset);
static int read_first_jr_index(struct caam_drv_private *ctrlpriv);
static int probe_w_seco(struct caam_drv_private *ctrlpriv);
static void init_debugfs(struct caam_drv_private *ctrlpriv);
+static void caam_ctrl_hw_configuration(struct caam_drv_private *ctrlpriv);
+static void enable_virt(struct caam_drv_private *ctrlpriv);
+
+#ifdef CONFIG_PM_SLEEP
+static int caam_off_during_pm(void);
+#endif
/*
* i.MX targets tend to have clock control subsystems that can
@@ -299,6 +305,27 @@ exit:
return ret;
}
+static void caam_ctrl_hw_configuration(struct caam_drv_private *ctrlpriv)
+{
+ /*
+ * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel,
+ * long pointers in master configuration register.
+ * In case of DPAA 2.x, Management Complex firmware performs
+ * the configuration.
+ */
+ if (!caam_dpaa2)
+ clrsetbits_32(&ctrlpriv->ctrl->mcr,
+ MCFGR_AWCACHE_MASK | MCFGR_LONG_PTR,
+ MCFGR_AWCACHE_CACH | MCFGR_AWCACHE_BUFF |
+ MCFGR_WDENABLE | MCFGR_LARGE_BURST |
+ (sizeof(dma_addr_t) == sizeof(u64) ?
+ MCFGR_LONG_PTR : 0));
+
+ handle_imx6_err005766(ctrlpriv);
+
+ enable_virt(ctrlpriv);
+}
+
/* Probe routine for CAAM top (controller) level */
static int caam_probe(struct platform_device *pdev)
{
@@ -349,16 +376,30 @@ static int caam_probe(struct platform_device *pdev)
}
ctrlpriv->ctrl = (struct caam_ctrl __force *)ctrl;
+ if (of_find_compatible_node(NULL, NULL, "linaro,optee-tz"))
+ ctrlpriv->has_optee = 1;
+
if (of_machine_is_compatible("fsl,imx8qm") ||
- of_machine_is_compatible("fsl,imx8qxp")) {
+ of_machine_is_compatible("fsl,imx8qxp"))
+ ctrlpriv->has_seco = 1;
+
+ /*
+ * The driver does not have access to Page 0 of the CAAM if there
+ * is a secure component managing the CAAM as optee or SECO.
+ */
+ ctrlpriv->has_access_p0 = !(ctrlpriv->has_optee || ctrlpriv->has_seco);
+
+#ifdef CONFIG_PM_SLEEP
+ ctrlpriv->caam_off_during_pm = caam_off_during_pm();
+#endif
+
+ if (ctrlpriv->has_seco) {
ret = probe_w_seco(ctrlpriv);
if (ret)
goto iounmap_ctrl;
return ret;
}
- ctrlpriv->has_seco = false;
-
if (caam_imx)
caam_little_end = true;
else
@@ -417,24 +458,12 @@ static int caam_probe(struct platform_device *pdev)
ctrlpriv->sm_size = PG_SIZE_64K;
}
- /*
- * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel,
- * long pointers in master configuration register.
- * In case of DPAA 2.x, Management Complex firmware performs
- * the configuration.
- */
caam_dpaa2 = !!(comp_params & CTPR_MS_DPAA2);
- if (!caam_dpaa2)
- clrsetbits_32(&ctrl->mcr, MCFGR_AWCACHE_MASK | MCFGR_LONG_PTR,
- MCFGR_AWCACHE_CACH | MCFGR_AWCACHE_BUFF |
- MCFGR_WDENABLE | MCFGR_LARGE_BURST |
- (sizeof(dma_addr_t) == sizeof(u64) ?
- MCFGR_LONG_PTR : 0));
-
- handle_imx6_err005766(ctrlpriv);
-
check_virt(ctrlpriv, comp_params);
+ if (ctrlpriv->has_access_p0)
+ caam_ctrl_hw_configuration(ctrlpriv);
+
/* Set DMA masks according to platform ranging */
if (of_machine_is_compatible("fsl,imx8mm") ||
of_machine_is_compatible("fsl,imx8qm") ||
@@ -504,6 +533,14 @@ exit:
return ret;
}
+static void enable_virt(struct caam_drv_private *ctrlpriv)
+{
+ if (ctrlpriv->virt_en == 1)
+ clrsetbits_32(&ctrlpriv->ctrl->jrstart, 0, JRSTART_JR0_START |
+ JRSTART_JR1_START | JRSTART_JR2_START |
+ JRSTART_JR3_START);
+}
+
static void check_virt(struct caam_drv_private *ctrlpriv, u32 comp_params)
{
/*
@@ -528,11 +565,6 @@ static void check_virt(struct caam_drv_private *ctrlpriv, u32 comp_params)
if (comp_params & CTPR_MS_VIRT_EN_POR)
ctrlpriv->virt_en = 1;
}
-
- if (ctrlpriv->virt_en == 1)
- clrsetbits_32(&ctrlpriv->ctrl->jrstart, 0, JRSTART_JR0_START |
- JRSTART_JR1_START | JRSTART_JR2_START |
- JRSTART_JR3_START);
}
static int enable_jobrings(struct caam_drv_private *ctrlpriv, int block_offset)
@@ -804,10 +836,107 @@ static const struct of_device_id caam_match[] = {
};
MODULE_DEVICE_TABLE(of, caam_match);
+#ifdef CONFIG_PM_SLEEP
+
+/*
+ * Indicate if the internal state of the CAAM is lost during PM
+ */
+static int caam_off_during_pm(void)
+{
+ if (IS_ENABLED(CONFIG_ARM64))
+ return 1;
+
+ if (of_machine_is_compatible("fsl,imx6sx") ||
+ of_machine_is_compatible("fsl,imx6ul") ||
+ of_machine_is_compatible("fsl,imx7ulp") ||
+ of_machine_is_compatible("fsl,imx7d") ||
+ of_machine_is_compatible("fsl,imx7s"))
+ return 1;
+
+ return 0;
+}
+
+static void caam_state_save(struct device *dev)
+{
+ int idx;
+ struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev);
+ struct caam_ctl_state *state = &ctrlpriv->state;
+ struct caam_ctrl *ctrl = ctrlpriv->ctrl;
+
+ /* Save SCFGR */
+ state->scfgr = rd_reg32(&ctrl->scfgr);
+
+ /* Save DECO mid */
+ state->deco_mid[0].liodn_ms = rd_reg32(&ctrl->deco_mid[0].liodn_ms);
+ state->deco_mid[0].liodn_ls = rd_reg32(&ctrl->deco_mid[0].liodn_ls);
+
+ /* Save the MID for JR assigned to linux */
+ for (idx = 0; idx < JOBR_MAX_COUNT; idx++)
+ if (ctrlpriv->jr[idx]) {
+ state->jr_mid[idx].liodn_ms =
+ rd_reg32(&ctrl->jr_mid[idx].liodn_ms);
+ state->jr_mid[idx].liodn_ls =
+ rd_reg32(&ctrl->jr_mid[idx].liodn_ls);
+ }
+}
+
+static void caam_state_restore(const struct device *dev)
+{
+ int idx;
+ const struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev);
+ const struct caam_ctl_state *state = &ctrlpriv->state;
+ struct caam_ctrl *ctrl = ctrlpriv->ctrl;
+
+ /* Restore SCFGR */
+ wr_reg32(&ctrl->scfgr, state->scfgr);
+
+ /* Restore DECO mid */
+ wr_reg32(&ctrl->deco_mid[0].liodn_ms, state->deco_mid[0].liodn_ms);
+ wr_reg32(&ctrl->deco_mid[0].liodn_ls, state->deco_mid[0].liodn_ls);
+
+ /* Restore the MID for JR assigned to linux */
+ for (idx = 0; idx < JOBR_MAX_COUNT; idx++)
+ if (ctrlpriv->jr[idx]) {
+ wr_reg32(&ctrl->jr_mid[idx].liodn_ms,
+ state->jr_mid[idx].liodn_ms);
+ wr_reg32(&ctrl->jr_mid[idx].liodn_ls,
+ state->jr_mid[idx].liodn_ls);
+ }
+}
+
+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->has_access_p0)
+ caam_state_save(dev);
+
+ return 0;
+}
+
+static int caam_ctrl_resume(struct device *dev)
+{
+ struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev);
+
+ if (ctrlpriv->caam_off_during_pm && ctrlpriv->has_access_p0) {
+ caam_state_restore(dev);
+ caam_ctrl_hw_configuration(ctrlpriv);
+ }
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(caam_ctrl_pm_ops, caam_ctrl_suspend, caam_ctrl_resume);
+
+#endif /* CONFIG_PM_SLEEP */
+
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,
.remove = caam_remove,
diff --git a/drivers/crypto/caam/inst_rng.c b/drivers/crypto/caam/inst_rng.c
index c5c83ab9c158..2cdbef347e23 100644
--- a/drivers/crypto/caam/inst_rng.c
+++ b/drivers/crypto/caam/inst_rng.c
@@ -2,7 +2,7 @@
/*
* CAAM RNG instantiation driver backend
*
- * Copyright 2017-2018 NXP
+ * Copyright 2017-2019 NXP
*/
#include <linux/device.h>
@@ -290,17 +290,6 @@ int inst_rng_imx(struct platform_device *pdev)
ctrlpriv = dev_get_drvdata(ctrldev);
ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl;
-#ifndef CONFIG_ARM64
- /*
- * Check if the Secure Firmware is running,
- * check only for i.MX6 and i.MX7
- */
- if (of_find_compatible_node(NULL, NULL, "linaro,optee-tz")) {
- pr_info("RNG Instantation done by Secure Firmware\n");
- return ret;
- }
-#endif
-
cha_vid_ls = rd_reg32(&ctrl->perfmon.cha_id_ls);
/*
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index 5763536ba9bb..debdb603ff0e 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -4,7 +4,7 @@
* Private/internal definitions between modules
*
* Copyright 2008-2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018 NXP
+ * Copyright 2017-2019 NXP
*
*/
@@ -41,6 +41,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 */
@@ -48,6 +60,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 */
/* Number of scatterlist crypt transforms active on the JobR */
@@ -64,7 +77,19 @@ struct caam_drv_private_jr {
int out_ring_read_index; /* Output index "tail" */
int tail; /* entinfo (s/w ring) tail index */
struct jr_outentry *outring; /* Base of output ring, DMA-safe */
+
+#ifdef CONFIG_PM_SLEEP
+ struct caam_jr_state state; /* State of the JR during PM */
+#endif
+};
+
+#ifdef CONFIG_PM_SLEEP
+struct caam_ctl_state {
+ u32 scfgr;
+ struct masterid deco_mid[1];
+ struct masterid jr_mid[4];
};
+#endif
/*
* Driver-private storage for a single CAAM block instance
@@ -125,6 +150,14 @@ struct caam_drv_private {
struct debugfs_blob_wrapper ctl_kek_wrap, ctl_tkek_wrap, ctl_tdsk_wrap;
struct dentry *ctl_kek, *ctl_tkek, *ctl_tdsk;
#endif
+
+ int has_optee;
+ int has_access_p0; /* If driver has acces to page 0 of the CAAM */
+
+#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
};
void caam_jr_algapi_init(struct device *dev);
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 616a42709010..88ec227b67a9 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -3,7 +3,7 @@
* JobR backend functionality
*
* Copyright 2008-2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018 NXP
+ * Copyright 2017-2019 NXP
*/
#include <linux/of_irq.h>
@@ -25,47 +25,122 @@ struct jr_driver_data {
static struct jr_driver_data driver_data;
-static int caam_reset_hw_jr(struct device *dev)
+static inline void mask_itr(struct device *dev)
{
struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
- unsigned int timeout = 100000;
- unsigned int reg_value;
- /*
- * mask interrupts since we are going to poll
- * for reset completion status
- */
clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK);
+}
- /* initiate flush (required prior to reset) */
- wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
+static inline void unmask_itr(struct device *dev)
+{
+ struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
+
+ clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0);
+}
+
+/*
+ * Put the CAAM in quiesce, ie stop
+ *
+ * Must be called with itr disabled
+ */
+static int caam_jr_stop_processing(struct device *dev, u32 jrcr_bits)
+{
+ unsigned int timeout = 100000;
+ unsigned int halt_status;
+ struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
+
+ if (rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) {
+ dev_err(dev, "Not ready to quiesce\n");
+ return -EINVAL;
+ }
+
+ /* initiate quiesce */
+ wr_reg32(&jrp->rregs->jrcommand, jrcr_bits);
+
+ /* Wait for the quiesce completion or timeout run out */
do {
cpu_relax();
- reg_value = rd_reg32(&jrp->rregs->jrintstatus);
- } while (((reg_value & JRINT_ERR_HALT_MASK) ==
- JRINT_ERR_HALT_INPROGRESS) && --timeout);
+ halt_status = rd_reg32(&jrp->rregs->jrintstatus) &
+ JRINT_ERR_HALT_MASK;
+ } while ((halt_status == JRINT_ERR_HALT_INPROGRESS) &&
+ --timeout);
- if ((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) !=
- JRINT_ERR_HALT_COMPLETE || timeout == 0) {
- dev_err(dev, "failed to flush job ring %d\n", jrp->ridx);
+ halt_status = rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK;
+
+ /* Check that the flush is complete */
+ if (halt_status != JRINT_ERR_HALT_COMPLETE) {
+ dev_err(dev, "failed to quiesce\n");
return -EIO;
}
+ return 0;
+}
+
+/*
+ * Flush the job ring, so the jobs running will be stopped, jobs queued will be
+ * invalidated and the CAAM will no longer fetch fron input ring.
+ *
+ * Must be called with itr disabled
+ */
+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 park_status = rd_reg32(&jrp->rregs->jrintstatus) &
+ JRINT_ERR_HALT_MASK;
+
+ /* Check that the flush/park is completed */
+ if (park_status != JRINT_ERR_HALT_COMPLETE)
+ return -1;
+
+ /* Resume processing of jobs */
+ wr_reg32(&jrp->rregs->jrintstatus, JRINT_ERR_HALT_COMPLETE);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM_SLEEP */
+
+static int caam_reset_hw_jr(struct device *dev)
+{
+ int err = 0;
+ struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
+ unsigned int timeout = 100000;
+ unsigned int reset_status;
+
+ /*
+ * mask interrupts since we are going to poll
+ * for reset completion status
+ */
+ mask_itr(dev);
+
+ err = caam_jr_flush(dev);
+ if (err)
+ return err;
+
/* initiate reset */
- timeout = 100000;
wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
do {
cpu_relax();
- reg_value = rd_reg32(&jrp->rregs->jrcommand);
- } while ((reg_value & JRCR_RESET) && --timeout);
+ reset_status = rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET;
+ } while (reset_status && --timeout);
- if (timeout == 0) {
+ reset_status = rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET;
+ if (reset_status != 0) {
dev_err(dev, "failed to reset job ring %d\n", jrp->ridx);
return -EIO;
}
/* unmask interrupts */
- clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0);
+ unmask_itr(dev);
return 0;
}
@@ -161,7 +236,7 @@ static irqreturn_t caam_jr_interrupt(int irq, void *st_dev)
}
/* mask valid interrupts */
- clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK);
+ mask_itr(dev);
/* Have valid interrupt at this point, just ACK and trigger */
wr_reg32(&jrp->rregs->jrintstatus, irqstate);
@@ -174,10 +249,11 @@ static irqreturn_t caam_jr_interrupt(int irq, void *st_dev)
}
/* Deferred service handler, run as interrupt-fired tasklet */
-static void caam_jr_dequeue(unsigned long devarg)
+static void caam_jr_dequeue(unsigned long data)
{
int hw_idx, sw_idx, i, head, tail;
- struct device *dev = (struct device *)devarg;
+ struct caam_jr_dequeue_params *params = (void *)data;
+ 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;
@@ -251,7 +327,8 @@ static void caam_jr_dequeue(unsigned long devarg)
}
/* reenable / unmask IRQs */
- clrsetbits_32(&jrp->rregs->rconfig_lo, JRCFG_IMSK, 0);
+ if (params->enable_itr)
+ unmask_itr(dev);
}
/**
@@ -396,6 +473,30 @@ 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->inp_ring_write_index = 0;
+ jrp->out_ring_read_index = 0;
+ jrp->head = 0;
+ jrp->tail = 0;
+}
+
/*
* Init JobR independent of platform property detection
*/
@@ -411,7 +512,10 @@ static int caam_jr_init(struct device *dev)
if (error)
goto out_kill_deq;
- 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 = request_irq(jrp->irq, caam_jr_interrupt, IRQF_SHARED,
@@ -441,26 +545,15 @@ static int caam_jr_init(struct device *dev)
jrp->entinfo[i].desc_addr_dma = !0;
/* Setup rings */
- jrp->inp_ring_write_index = 0;
- 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->ringsize = JOBR_DEPTH;
+ caam_jr_init_hw(dev, inpbusaddr, outbusaddr);
+
spin_lock_init(&jrp->inplock);
spin_lock_init(&jrp->outlock);
- /* 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));
-
return 0;
out_free_outring:
@@ -477,6 +570,29 @@ out_kill_deq:
return error;
}
+static int caam_jr_instantiate_rng(struct device *jrdev)
+{
+ int error = 0;
+ struct caam_drv_private_jr *jrpriv;
+ struct caam_drv_private *ctrlpriv = dev_get_drvdata(jrdev->parent);
+
+ if (ctrlpriv->has_seco || ctrlpriv->has_optee) {
+ dev_dbg(jrdev, "RNG instantiated by secure component\n");
+ goto exit;
+ }
+
+ jrpriv = dev_get_drvdata(jrdev);
+
+ /*
+ * If this is the first available JR
+ * then try to instantiate RNG
+ */
+ if (jrpriv->ridx == 0)
+ error = inst_rng_imx(to_platform_device(jrdev));
+
+exit:
+ return error;
+}
/*
* Probe routine for each detected JobR subsystem.
@@ -486,7 +602,7 @@ static int caam_jr_probe(struct platform_device *pdev)
struct device *jrdev;
struct device_node *nprop;
struct caam_job_ring __iomem *ctrl;
- struct caam_drv_private_jr *jrpriv, *jrppriv;
+ struct caam_drv_private_jr *jrpriv;
static int total_jobrs;
int error;
@@ -498,6 +614,7 @@ static int caam_jr_probe(struct platform_device *pdev)
}
dev_set_drvdata(jrdev, jrpriv);
+ jrpriv->dev = jrdev;
/* save ring identity relative to detection */
jrpriv->ridx = total_jobrs++;
@@ -548,11 +665,9 @@ static int caam_jr_probe(struct platform_device *pdev)
/* Now do the platform independent part */
error = caam_jr_init(jrdev); /* now turn on hardware */
- if (error) {
+ if (error)
goto dispose_irq_mapping;
- }
- jrpriv->dev = jrdev;
spin_lock(&driver_data.jr_alloc_lock);
list_add_tail(&jrpriv->list_node, &driver_data.jr_list);
spin_unlock(&driver_data.jr_alloc_lock);
@@ -561,32 +676,8 @@ static int caam_jr_probe(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 1);
device_set_wakeup_enable(&pdev->dev, false);
- /*
- * Instantiate RNG by JR rather than DECO
- */
- spin_lock(&driver_data.jr_alloc_lock);
- if (list_empty(&driver_data.jr_list)) {
- spin_unlock(&driver_data.jr_alloc_lock);
- dev_err(jrdev, "jr_list is empty\n");
- error = -ENODEV;
- goto dispose_irq_mapping;
- }
- jrppriv = list_first_entry(&driver_data.jr_list,
- struct caam_drv_private_jr, list_node);
- spin_unlock(&driver_data.jr_alloc_lock);
- /*
- * If this is the first available JR
- * then try to instantiate RNG
- */
- if (jrppriv->ridx == jrpriv->ridx) {
- if (!of_machine_is_compatible("fsl,imx8qm") &&
- !of_machine_is_compatible("fsl,imx8qxp"))
- /*
- * This call is done for legacy SOCs:
- * i.MX6 i.MX7 and i.MX8M (mScale).
- */
- error = inst_rng_imx(pdev);
- }
+
+ error = caam_jr_instantiate_rng(jrdev);
if (error)
goto remove_jr_from_list;
@@ -605,32 +696,120 @@ exit:
return error;
}
-#ifdef CONFIG_PM
+#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)
{
+ int err = 0;
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,
+ };
+
+ if (ctrlpriv->caam_off_during_pm) {
+ tasklet_disable(&jrpriv->irqtask);
+
+ /* mask itr to call flush */
+ mask_itr(dev);
+
+ /* Invalid job in process */
+ err = caam_jr_flush(dev);
+ if (err) {
+ dev_err(dev, "Failed to flush\n");
+ goto exit;
+ }
+
+ /* Dequeing jobs flushed */
+ caam_jr_dequeue((unsigned long)&suspend_params);
- if (device_may_wakeup(&pdev->dev))
+ /* Save state */
+ caam_jr_get_hw_state(dev);
+
+ } else if (device_may_wakeup(&pdev->dev)) {
enable_irq_wake(jrpriv->irq);
+ }
- return 0;
+exit:
+ return err;
}
static int caam_jr_resume(struct device *dev)
{
+ int err = 0;
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;
+
+ /*
+ * 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)
+ goto exit;
+
+ tasklet_enable(&jrpriv->irqtask);
+
+ unmask_itr(dev);
+
+ goto exit;
+ } else if (ctrlpriv->has_optee) {
+ /* JR has been used by OPTEE, reset it */
+ err = caam_reset_hw_jr(dev);
+ if (err) {
+ dev_err(dev, "Failed to reset JR\n");
+ goto exit;
+ }
+ } else {
+ /* No explanation, return error */
+ err = -EIO;
+ goto exit;
+ }
+ }
+
+ caam_jr_reset_index(jrpriv);
- if (device_may_wakeup(&pdev->dev))
+ caam_jr_init_hw(dev, jrpriv->state.inpbusaddr,
+ jrpriv->state.outbusaddr);
+
+ tasklet_enable(&jrpriv->irqtask);
+
+ err = caam_jr_instantiate_rng(dev);
+ if (err) {
+ dev_err(dev, "Failed to instantiate RNG\n");
+ goto exit;
+ }
+
+ } else if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(jrpriv->irq);
+ }
- return 0;
+exit:
+ return err;
}
-static SIMPLE_DEV_PM_OPS(caam_jr_pm_ops, caam_jr_suspend,
- caam_jr_resume);
-#endif
+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[] = {
{
@@ -647,7 +826,7 @@ static struct platform_driver caam_jr_driver = {
.driver = {
.name = "caam_jr",
.of_match_table = caam_jr_match,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.pm = &caam_jr_pm_ops,
#endif
},