commit e456233df67552708dc684d24404cbfa6ca75451
Author: zhaoxc0502 <zhaoxc0502@thundersoft.com>
Date:   Sat Jul 16 10:34:11 2022 +0800

    0016_linux_drivers_crypto
    
    Change-Id: I7e862e8b6ebf135f53df0e7dd7d7e0dd299c448e

diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index 84ea7cba5..283afcd42 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -8,6 +8,17 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC
 config CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC
 	tristate
 
+config CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC
+	tristate
+
+config CRYPTO_DEV_FSL_CAAM_SECVIO
+	tristate "CAAM/SNVS Security Violation Handler"
+	depends on ARCH_MXC
+	help
+	  Enables installation of an interrupt handler with registrable
+          handler functions which can be specified to act on the consequences
+          of a security violation.
+
 config CRYPTO_DEV_FSL_CAAM
 	tristate "Freescale CAAM-Multicore platform driver backend"
 	depends on FSL_SOC || ARCH_MXC || ARCH_LAYERSCAPE
@@ -109,7 +120,7 @@ config CRYPTO_DEV_FSL_CAAM_CRYPTO_API
 
 config CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI
 	bool "Queue Interface as Crypto API backend"
-	depends on FSL_DPAA && NET
+	depends on FSL_SDK_DPA && NET
 	default y
 	select CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC
 	select CRYPTO_AUTHENC
@@ -151,6 +162,71 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API
 	  Selecting this will register the SEC4 hardware rng to
 	  the hw_random API for supplying the kernel entropy pool.
 
+config CRYPTO_DEV_FSL_CAAM_TK_API
+	bool "Register tagged key cryptography implementations with Crypto API"
+	default y
+	select CRYPTO_DEV_FSL_CAAM_CRYPTO_API
+	select CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC
+	help
+	  Selecting this will register algorithms supporting tagged key and
+	  generate black keys and encapsulate them into black blobs.
+
+	  Tagged keys are black keys that contain metadata indicating what
+	  they are and how to handle them.
+	  CAAM protects data in a data structure called a Blob, which provides
+	  both confidentiality and integrity protection.
+
+config CRYPTO_DEV_FSL_CAAM_RNG_TEST
+	bool "Test caam rng"
+	depends on CRYPTO_DEV_FSL_CAAM_RNG_API
+	help
+	  Selecting this will enable a self-test to run for the
+	  caam RNG. This test is several minutes long and executes
+	  just before the RNG is registered with the hw_random API.
+
+config CRYPTO_DEV_FSL_CAAM_SM
+	bool "CAAM Secure Memory / Keystore API"
+	default y
+	help
+	  Enables use of a prototype kernel-level Keystore API with CAAM
+	  Secure Memory for insertion/extraction of bus-protected secrets.
+
+config CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE
+	int "Size of each keystore slot in Secure Memory"
+	depends on CRYPTO_DEV_FSL_CAAM_SM
+	range 5 9
+	default 7
+	help
+	  Select size of allocation units to divide Secure Memory pages into
+	  (the size of a "slot" as referenced inside the API code).
+	  Established as powers of two.
+	  Examples:
+		5 => 32 bytes
+		6 => 64 bytes
+		7 => 128 bytes
+		8 => 256 bytes
+		9 => 512 bytes
+
+config CRYPTO_DEV_FSL_CAAM_SM_TEST
+	tristate "CAAM Secure Memory - Keystore Test/Example"
+	depends on CRYPTO_DEV_FSL_CAAM_SM
+	depends on m
+	help
+	  Example thread to exercise the Keystore API and to verify that
+	  stored and recovered secrets can be used for general purpose
+	  encryption/decryption.
+
+config CRYPTO_DEV_FSL_CAAM_JR_UIO
+	tristate "Freescale Job Ring UIO support"
+	depends on UIO
+	default m
+	help
+	  Selecting this will allow job ring UIO support for
+	  Userspace drivers
+
+	  To compile this as a module, choose M here: the module
+	  will be called fsl_jr_uio.
+
 endif # CRYPTO_DEV_FSL_CAAM_JR
 
 endif # CRYPTO_DEV_FSL_CAAM
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 3570286eb..1f7670419 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -13,14 +13,20 @@ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR) += caam_jr.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_DESC) += caamalg_desc.o
 obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API_DESC) += caamhash_desc.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_KEYBLOB_API_DESC) += caamkeyblob_desc.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_JR_UIO) += fsl_jr_uio.o
 
 caam-y := ctrl.o
 caam_jr-y := jr.o key_gen.o
+caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) += tag_object.o caamkeyblob.o caamkeygen.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API) += caamalg.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
 caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o
+caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM) += sm_store.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SM_TEST) += sm_test.o
+obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_SECVIO) += secvio.o
 
 caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += qi.o
 ifneq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI),)
diff --git a/drivers/crypto/caam/caamalg.c b/drivers/crypto/caam/caamalg.c
index 8697ae53b..46dcf0728 100644
--- a/drivers/crypto/caam/caamalg.c
+++ b/drivers/crypto/caam/caamalg.c
@@ -3,7 +3,7 @@
  * caam - Freescale FSL CAAM support for crypto API
  *
  * Copyright 2008-2011 Freescale Semiconductor, Inc.
- * Copyright 2016-2019 NXP
+ * Copyright 2016-2020 NXP
  *
  * Based on talitos crypto API driver.
  *
@@ -60,6 +60,10 @@
 #include <crypto/xts.h>
 #include <asm/unaligned.h>
 
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API
+#include "tag_object.h"
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API */
+
 /*
  * crypto alg
  */
@@ -86,6 +90,7 @@ struct caam_alg_entry {
 	bool rfc3686;
 	bool geniv;
 	bool nodkp;
+	bool support_tagged_key;
 };
 
 struct caam_aead_alg {
@@ -738,12 +743,19 @@ static int skcipher_setkey(struct crypto_skcipher *skcipher, const u8 *key,
 	u32 *desc;
 	const bool is_rfc3686 = alg->caam.rfc3686;
 
-	print_hex_dump_debug("key in @"__stringify(__LINE__)": ",
-			     DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+	/*
+	 * If the algorithm has support for tagged key,
+	 * this is already set in tk_skcipher_setkey().
+	 * Otherwise, set here the algorithm details.
+	 */
+	if (!alg->caam.support_tagged_key) {
+		ctx->cdata.keylen = keylen;
+		ctx->cdata.key_virt = key;
+		ctx->cdata.key_inline = true;
+	}
 
-	ctx->cdata.keylen = keylen;
-	ctx->cdata.key_virt = key;
-	ctx->cdata.key_inline = true;
+	print_hex_dump_debug("key in @" __stringify(__LINE__) ": ",
+			     DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
 
 	/* skcipher_encrypt shared descriptor */
 	desc = ctx->sh_desc_enc;
@@ -815,6 +827,63 @@ static int ctr_skcipher_setkey(struct crypto_skcipher *skcipher,
 	return skcipher_setkey(skcipher, key, keylen, ctx1_iv_off);
 }
 
+
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API
+static int tk_skcipher_setkey(struct crypto_skcipher *skcipher,
+			      const u8 *key, unsigned int keylen)
+{
+	struct caam_ctx *ctx = crypto_skcipher_ctx(skcipher);
+	struct device *jrdev = ctx->jrdev;
+	struct header_conf *header;
+	struct tagged_object *tag_obj;
+	int ret;
+
+	ctx->cdata.key_inline = true;
+
+	/* Check if one can retrieve the tag object header configuration */
+	if (keylen <= TAG_OVERHEAD_SIZE)
+		return -EINVAL;
+
+	/* Retrieve the tag object */
+	tag_obj = (struct tagged_object *)key;
+
+	/*
+	 * Check tag object header configuration
+	 * and retrieve the tag object header configuration
+	 */
+	if (is_valid_header_conf(&tag_obj->header)) {
+		header = &tag_obj->header;
+	} else {
+		dev_err(jrdev,
+			"unable to get tag object header configuration\n");
+		return -EINVAL;
+	}
+
+	/* Check if the tag object header is a black key */
+	if (!is_black_key(header)) {
+		dev_err(jrdev,
+			"tagged key provided is not a black key\n");
+		return -EINVAL;
+	}
+
+	/* Retrieve the black key configuration */
+	get_key_conf(header,
+		     &ctx->cdata.key_real_len,
+		     &ctx->cdata.keylen,
+		     &ctx->cdata.key_cmd_opt);
+
+	/* Retrieve the address of the data of the tagged object */
+	ctx->cdata.key_virt = &tag_obj->object;
+
+	/* Validate key length for AES algorithms */
+	ret = aes_check_keylen(ctx->cdata.key_real_len);
+	if (ret)
+		return ret;
+
+	return skcipher_setkey(skcipher, NULL, 0, 0);
+}
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API */
+
 static int des_skcipher_setkey(struct crypto_skcipher *skcipher,
 			       const u8 *key, unsigned int keylen)
 {
@@ -1874,6 +1943,25 @@ static struct caam_skcipher_alg driver_algs[] = {
 		},
 		.caam.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
 	},
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API
+	{
+		.skcipher = {
+			.base = {
+				.cra_name = "tk(cbc(aes))",
+				.cra_driver_name = "tk-cbc-aes-caam",
+				.cra_blocksize = AES_BLOCK_SIZE,
+			},
+			.setkey = tk_skcipher_setkey,
+			.encrypt = skcipher_encrypt,
+			.decrypt = skcipher_decrypt,
+			.min_keysize = TAG_MIN_SIZE,
+			.max_keysize = CAAM_MAX_KEY_SIZE,
+			.ivsize = AES_BLOCK_SIZE,
+		},
+		.caam.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+		.caam.support_tagged_key = true,
+	},
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API */
 	{
 		.skcipher = {
 			.base = {
@@ -1994,6 +2082,24 @@ static struct caam_skcipher_alg driver_algs[] = {
 		},
 		.caam.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_ECB,
 	},
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API
+	{
+		.skcipher = {
+			.base = {
+				.cra_name = "tk(ecb(aes))",
+				.cra_driver_name = "tk-ecb-aes-caam",
+				.cra_blocksize = AES_BLOCK_SIZE,
+			},
+			.setkey = tk_skcipher_setkey,
+			.encrypt = skcipher_encrypt,
+			.decrypt = skcipher_decrypt,
+			.min_keysize = TAG_MIN_SIZE,
+			.max_keysize = CAAM_MAX_KEY_SIZE,
+		},
+		.caam.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_ECB,
+		.caam.support_tagged_key = true,
+	},
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API */
 	{
 		.skcipher = {
 			.base = {
@@ -3520,13 +3626,14 @@ int caam_algapi_init(struct device *ctrldev)
 	 * First, detect presence and attributes of DES, AES, and MD blocks.
 	 */
 	if (priv->era < 10) {
+		struct caam_perfmon __iomem *perfmon = &priv->jr[0]->perfmon;
 		u32 cha_vid, cha_inst, aes_rn;
 
-		cha_vid = rd_reg32(&priv->ctrl->perfmon.cha_id_ls);
+		cha_vid = rd_reg32(&perfmon->cha_id_ls);
 		aes_vid = cha_vid & CHA_ID_LS_AES_MASK;
 		md_vid = (cha_vid & CHA_ID_LS_MD_MASK) >> CHA_ID_LS_MD_SHIFT;
 
-		cha_inst = rd_reg32(&priv->ctrl->perfmon.cha_num_ls);
+		cha_inst = rd_reg32(&perfmon->cha_num_ls);
 		des_inst = (cha_inst & CHA_ID_LS_DES_MASK) >>
 			   CHA_ID_LS_DES_SHIFT;
 		aes_inst = cha_inst & CHA_ID_LS_AES_MASK;
@@ -3534,23 +3641,23 @@ int caam_algapi_init(struct device *ctrldev)
 		ccha_inst = 0;
 		ptha_inst = 0;
 
-		aes_rn = rd_reg32(&priv->ctrl->perfmon.cha_rev_ls) &
-			 CHA_ID_LS_AES_MASK;
+		aes_rn = rd_reg32(&perfmon->cha_rev_ls) & CHA_ID_LS_AES_MASK;
 		gcm_support = !(aes_vid == CHA_VER_VID_AES_LP && aes_rn < 8);
 	} else {
+		struct version_regs __iomem *vreg = &priv->jr[0]->vreg;
 		u32 aesa, mdha;
 
-		aesa = rd_reg32(&priv->ctrl->vreg.aesa);
-		mdha = rd_reg32(&priv->ctrl->vreg.mdha);
+		aesa = rd_reg32(&vreg->aesa);
+		mdha = rd_reg32(&vreg->mdha);
 
 		aes_vid = (aesa & CHA_VER_VID_MASK) >> CHA_VER_VID_SHIFT;
 		md_vid = (mdha & CHA_VER_VID_MASK) >> CHA_VER_VID_SHIFT;
 
-		des_inst = rd_reg32(&priv->ctrl->vreg.desa) & CHA_VER_NUM_MASK;
+		des_inst = rd_reg32(&vreg->desa) & CHA_VER_NUM_MASK;
 		aes_inst = aesa & CHA_VER_NUM_MASK;
 		md_inst = mdha & CHA_VER_NUM_MASK;
-		ccha_inst = rd_reg32(&priv->ctrl->vreg.ccha) & CHA_VER_NUM_MASK;
-		ptha_inst = rd_reg32(&priv->ctrl->vreg.ptha) & CHA_VER_NUM_MASK;
+		ccha_inst = rd_reg32(&vreg->ccha) & CHA_VER_NUM_MASK;
+		ptha_inst = rd_reg32(&vreg->ptha) & CHA_VER_NUM_MASK;
 
 		gcm_support = aesa & CHA_VER_MISC_AES_GCM;
 	}
diff --git a/drivers/crypto/caam/caamalg_desc.c b/drivers/crypto/caam/caamalg_desc.c
index 7571e1ac9..fa1309e90 100644
--- a/drivers/crypto/caam/caamalg_desc.c
+++ b/drivers/crypto/caam/caamalg_desc.c
@@ -622,6 +622,420 @@ void cnstr_shdsc_aead_givencap(u32 * const desc, struct alginfo *cdata,
 }
 EXPORT_SYMBOL(cnstr_shdsc_aead_givencap);
 
+/**
+ * cnstr_shdsc_tls_encap - tls encapsulation shared descriptor
+ * @desc: pointer to buffer used for descriptor construction
+ * @cdata: pointer to block cipher transform definitions
+ *         Valid algorithm values - one of OP_ALG_ALGSEL_AES ANDed
+ *         with OP_ALG_AAI_CBC
+ * @adata: pointer to authentication transform definitions.
+ *         A split key is required for SEC Era < 6; the size of the split key
+ *         is specified in this case. Valid algorithm values OP_ALG_ALGSEL_SHA1
+ *         ANDed with OP_ALG_AAI_HMAC_PRECOMP.
+ * @assoclen: associated data length
+ * @ivsize: initialization vector size
+ * @authsize: authentication data size
+ * @blocksize: block cipher size
+ * @era: SEC Era
+ */
+void cnstr_shdsc_tls_encap(u32 * const desc, struct alginfo *cdata,
+			   struct alginfo *adata, unsigned int assoclen,
+			   unsigned int ivsize, unsigned int authsize,
+			   unsigned int blocksize, int era)
+{
+	u32 *key_jump_cmd, *zero_payload_jump_cmd;
+	u32 genpad, idx_ld_datasz, idx_ld_pad, stidx;
+
+	/*
+	 * Compute the index (in bytes) for the LOAD with destination of
+	 * Class 1 Data Size Register and for the LOAD that generates padding
+	 */
+	if (adata->key_inline) {
+		idx_ld_datasz = DESC_TLS10_ENC_LEN + adata->keylen_pad +
+				cdata->keylen - 4 * CAAM_CMD_SZ;
+		idx_ld_pad = DESC_TLS10_ENC_LEN + adata->keylen_pad +
+			     cdata->keylen - 2 * CAAM_CMD_SZ;
+	} else {
+		idx_ld_datasz = DESC_TLS10_ENC_LEN + 2 * CAAM_PTR_SZ -
+				4 * CAAM_CMD_SZ;
+		idx_ld_pad = DESC_TLS10_ENC_LEN + 2 * CAAM_PTR_SZ -
+			     2 * CAAM_CMD_SZ;
+	}
+
+	stidx = 1 << HDR_START_IDX_SHIFT;
+	init_sh_desc(desc, HDR_SHARE_SERIAL | stidx);
+
+	/* skip key loading if they are loaded due to sharing */
+	key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+				   JUMP_COND_SHRD);
+
+	if (era < 6) {
+		if (adata->key_inline)
+			append_key_as_imm(desc, adata->key_virt,
+					  adata->keylen_pad, adata->keylen,
+					  CLASS_2 | KEY_DEST_MDHA_SPLIT |
+					  KEY_ENC);
+		else
+			append_key(desc, adata->key_dma, adata->keylen,
+				   CLASS_2 | KEY_DEST_MDHA_SPLIT | KEY_ENC);
+	} else {
+		append_proto_dkp(desc, adata);
+	}
+
+	if (cdata->key_inline)
+		append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
+				  cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
+	else
+		append_key(desc, cdata->key_dma, cdata->keylen, CLASS_1 |
+			   KEY_DEST_CLASS_REG);
+
+	set_jump_tgt_here(desc, key_jump_cmd);
+
+	/* class 2 operation */
+	append_operation(desc, adata->algtype | OP_ALG_AS_INITFINAL |
+			 OP_ALG_ENCRYPT);
+	/* class 1 operation */
+	append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
+			 OP_ALG_ENCRYPT);
+
+	/* payloadlen = input data length - (assoclen + ivlen) */
+	append_math_sub_imm_u32(desc, REG0, SEQINLEN, IMM, assoclen + ivsize);
+
+	/* math1 = payloadlen + icvlen */
+	append_math_add_imm_u32(desc, REG1, REG0, IMM, authsize);
+
+	/* padlen = block_size - math1 % block_size */
+	append_math_and_imm_u32(desc, REG3, REG1, IMM, blocksize - 1);
+	append_math_sub_imm_u32(desc, REG2, IMM, REG3, blocksize);
+
+	/* cryptlen = payloadlen + icvlen + padlen */
+	append_math_add(desc, VARSEQOUTLEN, REG1, REG2, 4);
+
+	/*
+	 * update immediate data with the padding length value
+	 * for the LOAD in the class 1 data size register.
+	 */
+	append_move(desc, MOVE_SRC_DESCBUF | MOVE_DEST_MATH2 |
+			(idx_ld_datasz << MOVE_OFFSET_SHIFT) | 7);
+	append_move(desc, MOVE_WAITCOMP | MOVE_SRC_MATH2 | MOVE_DEST_DESCBUF |
+			(idx_ld_datasz << MOVE_OFFSET_SHIFT) | 8);
+
+	/* overwrite PL field for the padding iNFO FIFO entry  */
+	append_move(desc, MOVE_SRC_DESCBUF | MOVE_DEST_MATH2 |
+			(idx_ld_pad << MOVE_OFFSET_SHIFT) | 7);
+	append_move(desc, MOVE_WAITCOMP | MOVE_SRC_MATH2 | MOVE_DEST_DESCBUF |
+			(idx_ld_pad << MOVE_OFFSET_SHIFT) | 8);
+
+	/* store encrypted payload, icv and padding */
+	append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | LDST_VLF);
+
+	/* if payload length is zero, jump to zero-payload commands */
+	append_math_add(desc, VARSEQINLEN, ZERO, REG0, 4);
+	zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
+					    JUMP_COND_MATH_Z);
+
+	/* load iv in context1 */
+	append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_CLASS_CTX |
+		   LDST_CLASS_1_CCB | ivsize);
+
+	/* read assoc for authentication */
+	append_seq_fifo_load(desc, assoclen, FIFOLD_CLASS_CLASS2 |
+			     FIFOLD_TYPE_MSG);
+	/* insnoop payload */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH | FIFOLD_TYPE_MSG |
+			     FIFOLD_TYPE_LAST2 | FIFOLDST_VLF);
+
+	/* jump the zero-payload commands */
+	append_jump(desc, JUMP_TEST_ALL | 3);
+
+	/* zero-payload commands */
+	set_jump_tgt_here(desc, zero_payload_jump_cmd);
+
+	/* load iv in context1 */
+	append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_CLASS_CTX |
+		   LDST_CLASS_1_CCB | ivsize);
+
+	/* assoc data is the only data for authentication */
+	append_seq_fifo_load(desc, assoclen, FIFOLD_CLASS_CLASS2 |
+			     FIFOLD_TYPE_MSG | FIFOLD_TYPE_LAST2);
+
+	/* send icv to encryption */
+	append_move(desc, MOVE_SRC_CLASS2CTX | MOVE_DEST_CLASS1INFIFO |
+		    authsize);
+
+	/* update class 1 data size register with padding length */
+	append_load_imm_u32(desc, 0, LDST_CLASS_1_CCB |
+			    LDST_SRCDST_WORD_DATASZ_REG | LDST_IMM);
+
+	/* generate padding and send it to encryption */
+	genpad = NFIFOENTRY_DEST_CLASS1 | NFIFOENTRY_LC1 | NFIFOENTRY_FC1 |
+	      NFIFOENTRY_STYPE_PAD | NFIFOENTRY_DTYPE_MSG | NFIFOENTRY_PTYPE_N;
+	append_load_imm_u32(desc, genpad, LDST_CLASS_IND_CCB |
+			    LDST_SRCDST_WORD_INFO_FIFO | LDST_IMM);
+
+#ifdef DEBUG
+	print_hex_dump(KERN_ERR, "tls enc shdesc@" __stringify(__LINE__) ": ",
+		       DUMP_PREFIX_ADDRESS, 16, 4, desc,
+		       desc_bytes(desc), 1);
+#endif
+}
+EXPORT_SYMBOL(cnstr_shdsc_tls_encap);
+
+/**
+ * cnstr_shdsc_tls_decap - tls decapsulation shared descriptor
+ * @desc: pointer to buffer used for descriptor construction
+ * @cdata: pointer to block cipher transform definitions
+ *         Valid algorithm values - one of OP_ALG_ALGSEL_AES ANDed
+ *         with OP_ALG_AAI_CBC
+ * @adata: pointer to authentication transform definitions.
+ *         A split key is required for SEC Era < 6; the size of the split key
+ *         is specified in this case. Valid algorithm values OP_ALG_ALGSEL_SHA1
+ *         ANDed with OP_ALG_AAI_HMAC_PRECOMP.
+ * @assoclen: associated data length
+ * @ivsize: initialization vector size
+ * @authsize: authentication data size
+ * @blocksize: block cipher size
+ * @era: SEC Era
+ */
+void cnstr_shdsc_tls_decap(u32 * const desc, struct alginfo *cdata,
+			   struct alginfo *adata, unsigned int assoclen,
+			   unsigned int ivsize, unsigned int authsize,
+			   unsigned int blocksize, int era)
+{
+	u32 stidx, jumpback;
+	u32 *key_jump_cmd, *zero_payload_jump_cmd, *skip_zero_jump_cmd;
+	/*
+	 * Pointer Size bool determines the size of address pointers.
+	 * false - Pointers fit in one 32-bit word.
+	 * true - Pointers fit in two 32-bit words.
+	 */
+	bool ps = (CAAM_PTR_SZ != CAAM_CMD_SZ);
+
+	stidx = 1 << HDR_START_IDX_SHIFT;
+	init_sh_desc(desc, HDR_SHARE_SERIAL | stidx);
+
+	/* skip key loading if they are loaded due to sharing */
+	key_jump_cmd = append_jump(desc, JUMP_JSL | JUMP_TEST_ALL |
+				   JUMP_COND_SHRD);
+
+	if (era < 6)
+		append_key(desc, adata->key_dma, adata->keylen, CLASS_2 |
+			   KEY_DEST_MDHA_SPLIT | KEY_ENC);
+	else
+		append_proto_dkp(desc, adata);
+
+	append_key(desc, cdata->key_dma, cdata->keylen, CLASS_1 |
+		   KEY_DEST_CLASS_REG);
+
+	set_jump_tgt_here(desc, key_jump_cmd);
+
+	/* class 2 operation */
+	append_operation(desc, adata->algtype | OP_ALG_AS_INITFINAL |
+			 OP_ALG_DECRYPT | OP_ALG_ICV_ON);
+	/* class 1 operation */
+	append_operation(desc, cdata->algtype | OP_ALG_AS_INITFINAL |
+			 OP_ALG_DECRYPT);
+
+	/* VSIL = input data length - 2 * block_size */
+	append_math_sub_imm_u32(desc, VARSEQINLEN, SEQINLEN, IMM, 2 *
+				blocksize);
+
+	/*
+	 * payloadlen + icvlen + padlen = input data length - (assoclen +
+	 * ivsize)
+	 */
+	append_math_sub_imm_u32(desc, REG3, SEQINLEN, IMM, assoclen + ivsize);
+
+	/* skip data to the last but one cipher block */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_SKIP | LDST_VLF);
+
+	/* load iv for the last cipher block */
+	append_cmd(desc, CMD_SEQ_LOAD | LDST_SRCDST_WORD_CLASS_CTX |
+		   LDST_CLASS_1_CCB | ivsize);
+
+	/* read last cipher block */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_MSG |
+			     FIFOLD_TYPE_LAST1 | blocksize);
+
+	/* move decrypted block into math0 and math1 */
+	append_move(desc, MOVE_WAITCOMP | MOVE_SRC_OUTFIFO | MOVE_DEST_MATH0 |
+		    blocksize);
+
+	/* reset AES CHA */
+	append_load_imm_u32(desc, CCTRL_RESET_CHA_AESA, LDST_CLASS_IND_CCB |
+			    LDST_SRCDST_WORD_CHACTRL | LDST_IMM);
+
+	/* rewind input sequence */
+	append_seq_in_ptr_intlen(desc, 0, 65535, SQIN_RTO);
+
+	/* key1 is in decryption form */
+	append_operation(desc, cdata->algtype | OP_ALG_AAI_DK |
+			 OP_ALG_AS_INITFINAL | OP_ALG_DECRYPT);
+
+	/* load iv in context1 */
+	append_cmd(desc, CMD_SEQ_LOAD | LDST_CLASS_1_CCB |
+		   LDST_SRCDST_WORD_CLASS_CTX | ivsize);
+
+	/* read sequence number */
+	append_seq_fifo_load(desc, 8, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_MSG);
+	/* load Type, Version and Len fields in math0 */
+	append_cmd(desc, CMD_SEQ_LOAD | LDST_CLASS_DECO |
+		   LDST_SRCDST_WORD_DECO_MATH0 | (3 << LDST_OFFSET_SHIFT) | 5);
+
+	/* compute (padlen - 1) */
+	append_math_and_imm_u64(desc, REG1, REG1, IMM, 255);
+
+	/* math2 = icvlen + (padlen - 1) + 1 */
+	append_math_add_imm_u32(desc, REG2, REG1, IMM, authsize + 1);
+
+	append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM | 1);
+
+	/* VSOL = payloadlen + icvlen + padlen */
+	append_math_add(desc, VARSEQOUTLEN, ZERO, REG3, 4);
+
+	if (caam_little_end)
+		append_moveb(desc, MOVE_WAITCOMP |
+			     MOVE_SRC_MATH0 | MOVE_DEST_MATH0 | 8);
+
+	/* update Len field */
+	append_math_sub(desc, REG0, REG0, REG2, 8);
+
+	/* store decrypted payload, icv and padding */
+	append_seq_fifo_store(desc, 0, FIFOST_TYPE_MESSAGE_DATA | LDST_VLF);
+
+	/* VSIL = (payloadlen + icvlen + padlen) - (icvlen + padlen)*/
+	append_math_sub(desc, VARSEQINLEN, REG3, REG2, 4);
+
+	zero_payload_jump_cmd = append_jump(desc, JUMP_TEST_ALL |
+					    JUMP_COND_MATH_Z);
+
+	/* send Type, Version and Len(pre ICV) fields to authentication */
+	append_move(desc, MOVE_WAITCOMP |
+		    MOVE_SRC_MATH0 | MOVE_DEST_CLASS2INFIFO |
+		    (3 << MOVE_OFFSET_SHIFT) | 5);
+
+	/* outsnooping payload */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_BOTH |
+			     FIFOLD_TYPE_MSG1OUT2 | FIFOLD_TYPE_LAST2 |
+			     FIFOLDST_VLF);
+	skip_zero_jump_cmd = append_jump(desc, JUMP_TEST_ALL | 2);
+
+	set_jump_tgt_here(desc, zero_payload_jump_cmd);
+	/* send Type, Version and Len(pre ICV) fields to authentication */
+	append_move(desc, MOVE_WAITCOMP | MOVE_AUX_LS |
+		    MOVE_SRC_MATH0 | MOVE_DEST_CLASS2INFIFO |
+		    (3 << MOVE_OFFSET_SHIFT) | 5);
+
+	set_jump_tgt_here(desc, skip_zero_jump_cmd);
+	append_math_add(desc, VARSEQINLEN, ZERO, REG2, 4);
+
+	/* load icvlen and padlen */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS1 | FIFOLD_TYPE_MSG |
+			     FIFOLD_TYPE_LAST1 | FIFOLDST_VLF);
+
+	/* VSIL = (payloadlen + icvlen + padlen) - icvlen + padlen */
+	append_math_sub(desc, VARSEQINLEN, REG3, REG2, 4);
+
+	/*
+	 * Start a new input sequence using the SEQ OUT PTR command options,
+	 * pointer and length used when the current output sequence was defined.
+	 */
+	if (ps) {
+		/*
+		 * Move the lower 32 bits of Shared Descriptor address, the
+		 * SEQ OUT PTR command, Output Pointer (2 words) and
+		 * Output Length into math registers.
+		 */
+		if (caam_little_end)
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_DESCBUF |
+				    MOVE_DEST_MATH0 |
+				    (55 * 4 << MOVE_OFFSET_SHIFT) | 20);
+		else
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_DESCBUF |
+				    MOVE_DEST_MATH0 |
+				    (54 * 4 << MOVE_OFFSET_SHIFT) | 20);
+
+		/* Transform SEQ OUT PTR command in SEQ IN PTR command */
+		append_math_and_imm_u32(desc, REG0, REG0, IMM,
+					~(CMD_SEQ_IN_PTR ^ CMD_SEQ_OUT_PTR));
+		/* Append a JUMP command after the copied fields */
+		jumpback = CMD_JUMP | (char)-9;
+		append_load_imm_u32(desc, jumpback, LDST_CLASS_DECO | LDST_IMM |
+				    LDST_SRCDST_WORD_DECO_MATH2 |
+				    (4 << LDST_OFFSET_SHIFT));
+		append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM | 1);
+		/* Move the updated fields back to the Job Descriptor */
+		if (caam_little_end)
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_MATH0 |
+				    MOVE_DEST_DESCBUF |
+				    (55 * 4 << MOVE_OFFSET_SHIFT) | 24);
+		else
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_MATH0 |
+				    MOVE_DEST_DESCBUF |
+				    (54 * 4 << MOVE_OFFSET_SHIFT) | 24);
+
+		/*
+		 * Read the new SEQ IN PTR command, Input Pointer, Input Length
+		 * and then jump back to the next command from the
+		 * Shared Descriptor.
+		 */
+		append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM | 6);
+	} else {
+		/*
+		 * Move the SEQ OUT PTR command, Output Pointer (1 word) and
+		 * Output Length into math registers.
+		 */
+		if (caam_little_end)
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_DESCBUF |
+				    MOVE_DEST_MATH0 |
+				    (54 * 4 << MOVE_OFFSET_SHIFT) | 12);
+		else
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_DESCBUF |
+				    MOVE_DEST_MATH0 |
+				    (53 * 4 << MOVE_OFFSET_SHIFT) | 12);
+
+		/* Transform SEQ OUT PTR command in SEQ IN PTR command */
+		append_math_and_imm_u64(desc, REG0, REG0, IMM,
+					~(((u64)(CMD_SEQ_IN_PTR ^
+						 CMD_SEQ_OUT_PTR)) << 32));
+		/* Append a JUMP command after the copied fields */
+		jumpback = CMD_JUMP | (char)-7;
+		append_load_imm_u32(desc, jumpback, LDST_CLASS_DECO | LDST_IMM |
+				    LDST_SRCDST_WORD_DECO_MATH1 |
+				    (4 << LDST_OFFSET_SHIFT));
+		append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM | 1);
+		/* Move the updated fields back to the Job Descriptor */
+		if (caam_little_end)
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_MATH0 |
+				    MOVE_DEST_DESCBUF |
+				    (54 * 4 << MOVE_OFFSET_SHIFT) | 16);
+		else
+			append_move(desc, MOVE_WAITCOMP | MOVE_SRC_MATH0 |
+				    MOVE_DEST_DESCBUF |
+				    (53 * 4 << MOVE_OFFSET_SHIFT) | 16);
+
+		/*
+		 * Read the new SEQ IN PTR command, Input Pointer, Input Length
+		 * and then jump back to the next command from the
+		 * Shared Descriptor.
+		 */
+		 append_jump(desc, JUMP_TEST_ALL | JUMP_COND_CALM | 5);
+	}
+
+	/* skip payload */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_SKIP | FIFOLDST_VLF);
+	/* check icv */
+	append_seq_fifo_load(desc, 0, FIFOLD_CLASS_CLASS2 | FIFOLD_TYPE_ICV |
+			     FIFOLD_TYPE_LAST2 | authsize);
+
+#ifdef DEBUG
+	print_hex_dump(KERN_ERR, "tls dec shdesc@" __stringify(__LINE__) ": ",
+		       DUMP_PREFIX_ADDRESS, 16, 4, desc,
+		       desc_bytes(desc), 1);
+#endif
+}
+EXPORT_SYMBOL(cnstr_shdsc_tls_decap);
+
 /**
  * cnstr_shdsc_gcm_encap - gcm encapsulation shared descriptor
  * @desc: pointer to buffer used for descriptor construction
@@ -1390,8 +1804,18 @@ void cnstr_shdsc_skcipher_encap(u32 * const desc, struct alginfo *cdata,
 				   JUMP_COND_SHRD);
 
 	/* Load class1 key only */
-	append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
-			  cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
+	if (IS_ENABLED(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) &&
+	    cdata->key_cmd_opt)
+		/*
+		 * Black keys can be loaded using only a KEY command
+		 * with ENC=1 and the proper setting of the EKT bit.
+		 */
+		append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
+				  cdata->key_real_len, CLASS_1 |
+				  KEY_DEST_CLASS_REG | cdata->key_cmd_opt);
+	else
+		append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
+				  cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
 
 	/* Load nonce into CONTEXT1 reg */
 	if (is_rfc3686) {
@@ -1465,8 +1889,18 @@ void cnstr_shdsc_skcipher_decap(u32 * const desc, struct alginfo *cdata,
 				   JUMP_COND_SHRD);
 
 	/* Load class1 key only */
-	append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
-			  cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
+	if (IS_ENABLED(CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API) &&
+	    cdata->key_cmd_opt)
+		/*
+		 * Black keys can be loaded using only a KEY command
+		 * with ENC=1 and the proper setting of the EKT bit.
+		 */
+		append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
+				  cdata->key_real_len, CLASS_1 |
+				  KEY_DEST_CLASS_REG | cdata->key_cmd_opt);
+	else
+		append_key_as_imm(desc, cdata->key_virt, cdata->keylen,
+				  cdata->keylen, CLASS_1 | KEY_DEST_CLASS_REG);
 
 	/* Load nonce into CONTEXT1 reg */
 	if (is_rfc3686) {
diff --git a/drivers/crypto/caam/caamalg_desc.h b/drivers/crypto/caam/caamalg_desc.h
index f2893393b..99f0d1471 100644
--- a/drivers/crypto/caam/caamalg_desc.h
+++ b/drivers/crypto/caam/caamalg_desc.h
@@ -17,6 +17,9 @@
 #define DESC_QI_AEAD_DEC_LEN		(DESC_AEAD_DEC_LEN + 3 * CAAM_CMD_SZ)
 #define DESC_QI_AEAD_GIVENC_LEN		(DESC_AEAD_GIVENC_LEN + 3 * CAAM_CMD_SZ)
 
+#define DESC_TLS_BASE			(4 * CAAM_CMD_SZ)
+#define DESC_TLS10_ENC_LEN		(DESC_TLS_BASE + 29 * CAAM_CMD_SZ)
+
 /* Note: Nonce is counted in cdata.keylen */
 #define DESC_AEAD_CTR_RFC3686_LEN	(4 * CAAM_CMD_SZ)
 
@@ -72,6 +75,16 @@ void cnstr_shdsc_aead_givencap(u32 * const desc, struct alginfo *cdata,
 			       u32 *nonce, const u32 ctx1_iv_off,
 			       const bool is_qi, int era);
 
+void cnstr_shdsc_tls_encap(u32 *const desc, struct alginfo *cdata,
+			   struct alginfo *adata, unsigned int assoclen,
+			   unsigned int ivsize, unsigned int authsize,
+			   unsigned int blocksize, int era);
+
+void cnstr_shdsc_tls_decap(u32 *const desc, struct alginfo *cdata,
+			   struct alginfo *adata, unsigned int assoclen,
+			   unsigned int ivsize, unsigned int authsize,
+			   unsigned int blocksize, int era);
+
 void cnstr_shdsc_gcm_encap(u32 * const desc, struct alginfo *cdata,
 			   unsigned int ivsize, unsigned int icvsize,
 			   const bool is_qi);
diff --git a/drivers/crypto/caam/caamalg_qi.c b/drivers/crypto/caam/caamalg_qi.c
index a24ae966d..81a8ee300 100644
--- a/drivers/crypto/caam/caamalg_qi.c
+++ b/drivers/crypto/caam/caamalg_qi.c
@@ -297,6 +297,166 @@ static int des3_aead_setkey(struct crypto_aead *aead, const u8 *key,
 	return err;
 }
 
+static int tls_set_sh_desc(struct crypto_aead *tls)
+{
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+	unsigned int ivsize = crypto_aead_ivsize(tls);
+	unsigned int blocksize = crypto_aead_blocksize(tls);
+	unsigned int assoclen = 13; /* always 13 bytes for TLS */
+	unsigned int data_len[2];
+	u32 inl_mask;
+	struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctx->jrdev->parent);
+
+	if (!ctx->cdata.keylen || !ctx->authsize)
+		return 0;
+
+	/*
+	 * TLS 1.0 encrypt shared descriptor
+	 * Job Descriptor and Shared Descriptor
+	 * must fit into the 64-word Descriptor h/w Buffer
+	 */
+	data_len[0] = ctx->adata.keylen_pad;
+	data_len[1] = ctx->cdata.keylen;
+
+	if (desc_inline_query(DESC_TLS10_ENC_LEN, DESC_JOB_IO_LEN, data_len,
+			      &inl_mask, ARRAY_SIZE(data_len)) < 0)
+		return -EINVAL;
+
+	if (inl_mask & 1)
+		ctx->adata.key_virt = ctx->key;
+	else
+		ctx->adata.key_dma = ctx->key_dma;
+
+	if (inl_mask & 2)
+		ctx->cdata.key_virt = ctx->key + ctx->adata.keylen_pad;
+	else
+		ctx->cdata.key_dma = ctx->key_dma + ctx->adata.keylen_pad;
+
+	ctx->adata.key_inline = !!(inl_mask & 1);
+	ctx->cdata.key_inline = !!(inl_mask & 2);
+
+	cnstr_shdsc_tls_encap(ctx->sh_desc_enc, &ctx->cdata, &ctx->adata,
+			      assoclen, ivsize, ctx->authsize, blocksize,
+			      ctrlpriv->era);
+
+	/*
+	 * TLS 1.0 decrypt shared descriptor
+	 * Keys do not fit inline, regardless of algorithms used
+	 */
+	ctx->adata.key_inline = false;
+	ctx->adata.key_dma = ctx->key_dma;
+	ctx->cdata.key_dma = ctx->key_dma + ctx->adata.keylen_pad;
+
+	cnstr_shdsc_tls_decap(ctx->sh_desc_dec, &ctx->cdata, &ctx->adata,
+			      assoclen, ivsize, ctx->authsize, blocksize,
+			      ctrlpriv->era);
+
+	return 0;
+}
+
+static int tls_setauthsize(struct crypto_aead *tls, unsigned int authsize)
+{
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+
+	ctx->authsize = authsize;
+	tls_set_sh_desc(tls);
+
+	return 0;
+}
+
+static int tls_setkey(struct crypto_aead *tls, const u8 *key,
+		      unsigned int keylen)
+{
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+	struct device *jrdev = ctx->jrdev;
+	struct caam_drv_private *ctrlpriv = dev_get_drvdata(jrdev->parent);
+	struct crypto_authenc_keys keys;
+	int ret = 0;
+
+	if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
+		goto badkey;
+
+#ifdef DEBUG
+	dev_err(jrdev, "keylen %d enckeylen %d authkeylen %d\n",
+		keys.authkeylen + keys.enckeylen, keys.enckeylen,
+		keys.authkeylen);
+	print_hex_dump(KERN_ERR, "key in @" __stringify(__LINE__)": ",
+		       DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+	/*
+	 * If DKP is supported, use it in the shared descriptor to generate
+	 * the split key.
+	 */
+	if (ctrlpriv->era >= 6) {
+		ctx->adata.keylen = keys.authkeylen;
+		ctx->adata.keylen_pad = split_key_len(ctx->adata.algtype &
+						      OP_ALG_ALGSEL_MASK);
+
+		if (ctx->adata.keylen_pad + keys.enckeylen > CAAM_MAX_KEY_SIZE)
+			goto badkey;
+
+		memcpy(ctx->key, keys.authkey, keys.authkeylen);
+		memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey,
+		       keys.enckeylen);
+		dma_sync_single_for_device(jrdev, ctx->key_dma,
+					   ctx->adata.keylen_pad +
+					   keys.enckeylen, ctx->dir);
+		goto skip_split_key;
+	}
+
+	ret = gen_split_key(jrdev, ctx->key, &ctx->adata, keys.authkey,
+			    keys.authkeylen, CAAM_MAX_KEY_SIZE -
+			    keys.enckeylen);
+	if (ret)
+		goto badkey;
+
+	/* postpend encryption key to auth split key */
+	memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen);
+	dma_sync_single_for_device(jrdev, ctx->key_dma, ctx->adata.keylen_pad +
+				   keys.enckeylen, ctx->dir);
+
+#ifdef DEBUG
+	dev_err(jrdev, "split keylen %d split keylen padded %d\n",
+		ctx->adata.keylen, ctx->adata.keylen_pad);
+	print_hex_dump(KERN_ERR, "ctx.key@" __stringify(__LINE__)": ",
+		       DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
+		       ctx->adata.keylen_pad + keys.enckeylen, 1);
+#endif
+
+skip_split_key:
+	ctx->cdata.keylen = keys.enckeylen;
+
+	ret = tls_set_sh_desc(tls);
+	if (ret)
+		goto badkey;
+
+	/* Now update the driver contexts with the new shared descriptor */
+	if (ctx->drv_ctx[ENCRYPT]) {
+		ret = caam_drv_ctx_update(ctx->drv_ctx[ENCRYPT],
+					  ctx->sh_desc_enc);
+		if (ret) {
+			dev_err(jrdev, "driver enc context update failed\n");
+			goto badkey;
+		}
+	}
+
+	if (ctx->drv_ctx[DECRYPT]) {
+		ret = caam_drv_ctx_update(ctx->drv_ctx[DECRYPT],
+					  ctx->sh_desc_dec);
+		if (ret) {
+			dev_err(jrdev, "driver dec context update failed\n");
+			goto badkey;
+		}
+	}
+
+	memzero_explicit(&keys, sizeof(keys));
+	return ret;
+badkey:
+	memzero_explicit(&keys, sizeof(keys));
+	return -EINVAL;
+}
+
 static int gcm_set_sh_desc(struct crypto_aead *aead)
 {
 	struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -806,6 +966,29 @@ struct aead_edesc {
 	struct qm_sg_entry sgt[];
 };
 
+/*
+ * tls_edesc - s/w-extended tls descriptor
+ * @src_nents: number of segments in input scatterlist
+ * @dst_nents: number of segments in output scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
+ * @qm_sg_bytes: length of dma mapped h/w link table
+ * @tmp: array of scatterlists used by 'scatterwalk_ffwd'
+ * @qm_sg_dma: bus physical mapped address of h/w link table
+ * @drv_req: driver-specific request structure
+ * @sgt: the h/w link table, followed by IV
+ */
+struct tls_edesc {
+	int src_nents;
+	int dst_nents;
+	dma_addr_t iv_dma;
+	int qm_sg_bytes;
+	dma_addr_t qm_sg_dma;
+	struct scatterlist tmp[2];
+	struct scatterlist *dst;
+	struct caam_drv_req drv_req;
+	struct qm_sg_entry sgt[0];
+};
+
 /*
  * skcipher_edesc - s/w-extended skcipher descriptor
  * @src_nents: number of segments in input scatterlist
@@ -898,6 +1081,18 @@ static void aead_unmap(struct device *dev,
 	dma_unmap_single(dev, edesc->assoclen_dma, 4, DMA_TO_DEVICE);
 }
 
+static void tls_unmap(struct device *dev,
+		      struct tls_edesc *edesc,
+		      struct aead_request *req)
+{
+	struct crypto_aead *aead = crypto_aead_reqtfm(req);
+	int ivsize = crypto_aead_ivsize(aead);
+
+	caam_unmap(dev, req->src, edesc->dst, edesc->src_nents,
+		   edesc->dst_nents, edesc->iv_dma, ivsize, DMA_TO_DEVICE,
+		   edesc->qm_sg_dma, edesc->qm_sg_bytes);
+}
+
 static void skcipher_unmap(struct device *dev, struct skcipher_edesc *edesc,
 			   struct skcipher_request *req)
 {
@@ -1190,6 +1385,238 @@ static int aead_decrypt(struct aead_request *req)
 	return aead_crypt(req, false);
 }
 
+static void tls_done(struct caam_drv_req *drv_req, u32 status)
+{
+	struct device *qidev;
+	struct tls_edesc *edesc;
+	struct aead_request *aead_req = drv_req->app_ctx;
+	struct crypto_aead *aead = crypto_aead_reqtfm(aead_req);
+	struct caam_ctx *caam_ctx = crypto_aead_ctx(aead);
+	int ecode = 0;
+
+	qidev = caam_ctx->qidev;
+
+	if (unlikely(status))
+		ecode = caam_jr_strstatus(qidev, status);
+
+	edesc = container_of(drv_req, typeof(*edesc), drv_req);
+	tls_unmap(qidev, edesc, aead_req);
+
+	aead_request_complete(aead_req, ecode);
+	qi_cache_free(edesc);
+}
+
+/*
+ * allocate and map the tls extended descriptor
+ */
+static struct tls_edesc *tls_edesc_alloc(struct aead_request *req, bool encrypt)
+{
+	struct crypto_aead *aead = crypto_aead_reqtfm(req);
+	struct caam_ctx *ctx = crypto_aead_ctx(aead);
+	unsigned int blocksize = crypto_aead_blocksize(aead);
+	unsigned int padsize, authsize;
+	struct caam_aead_alg *alg = container_of(crypto_aead_alg(aead),
+						 typeof(*alg), aead);
+	struct device *qidev = ctx->qidev;
+	gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+		      GFP_KERNEL : GFP_ATOMIC;
+	int src_nents, mapped_src_nents, dst_nents = 0, mapped_dst_nents = 0;
+	struct tls_edesc *edesc;
+	dma_addr_t qm_sg_dma, iv_dma = 0;
+	int ivsize = crypto_aead_ivsize(aead);
+	u8 *iv;
+	int qm_sg_index, qm_sg_ents = 0, qm_sg_bytes;
+	int src_len, dst_len, data_len;
+	struct qm_sg_entry *sg_table, *fd_sgt;
+	struct caam_drv_ctx *drv_ctx;
+	struct scatterlist *dst;
+
+	if (encrypt) {
+		padsize = blocksize - ((req->cryptlen + ctx->authsize) %
+					blocksize);
+		authsize = ctx->authsize + padsize;
+	} else {
+		authsize = ctx->authsize;
+	}
+
+	drv_ctx = get_drv_ctx(ctx, encrypt ? ENCRYPT : DECRYPT);
+	if (unlikely(IS_ERR_OR_NULL(drv_ctx)))
+		return (struct tls_edesc *)drv_ctx;
+
+	/* allocate space for base edesc, link tables and IV */
+	edesc = qi_cache_alloc(GFP_DMA | flags);
+	if (unlikely(!edesc)) {
+		dev_err(qidev, "could not allocate extended descriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	data_len = req->assoclen + req->cryptlen;
+	dst_len = req->cryptlen + (encrypt ? authsize : 0);
+
+	if (likely(req->src == req->dst)) {
+		src_len = req->assoclen + dst_len;
+
+		src_nents = sg_nents_for_len(req->src, src_len);
+		if (unlikely(src_nents < 0)) {
+			dev_err(qidev, "Insufficient bytes (%d) in src S/G\n",
+				src_len);
+			qi_cache_free(edesc);
+			return ERR_PTR(src_nents);
+		}
+
+		mapped_src_nents = dma_map_sg(qidev, req->src, src_nents,
+					      DMA_BIDIRECTIONAL);
+		if (unlikely(!mapped_src_nents)) {
+			dev_err(qidev, "unable to map source\n");
+			qi_cache_free(edesc);
+			return ERR_PTR(-ENOMEM);
+		}
+		dst = req->dst;
+	} else {
+		src_len = data_len;
+
+		src_nents = sg_nents_for_len(req->src, src_len);
+		if (unlikely(src_nents < 0)) {
+			dev_err(qidev, "Insufficient bytes (%d) in src S/G\n",
+				src_len);
+			qi_cache_free(edesc);
+			return ERR_PTR(src_nents);
+		}
+
+		dst = scatterwalk_ffwd(edesc->tmp, req->dst, req->assoclen);
+
+		dst_nents = sg_nents_for_len(dst, dst_len);
+		if (unlikely(dst_nents < 0)) {
+			dev_err(qidev, "Insufficient bytes (%d) in dst S/G\n",
+				dst_len);
+			qi_cache_free(edesc);
+			return ERR_PTR(dst_nents);
+		}
+
+		if (src_nents) {
+			mapped_src_nents = dma_map_sg(qidev, req->src,
+						      src_nents, DMA_TO_DEVICE);
+			if (unlikely(!mapped_src_nents)) {
+				dev_err(qidev, "unable to map source\n");
+				qi_cache_free(edesc);
+				return ERR_PTR(-ENOMEM);
+			}
+		} else {
+			mapped_src_nents = 0;
+		}
+
+		mapped_dst_nents = dma_map_sg(qidev, dst, dst_nents,
+					      DMA_FROM_DEVICE);
+		if (unlikely(!mapped_dst_nents)) {
+			dev_err(qidev, "unable to map destination\n");
+			dma_unmap_sg(qidev, req->src, src_nents, DMA_TO_DEVICE);
+			qi_cache_free(edesc);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+
+	/*
+	 * Create S/G table: IV, src, dst.
+	 * Input is not contiguous.
+	 */
+	qm_sg_ents = 1 + mapped_src_nents +
+		     (mapped_dst_nents > 1 ? mapped_dst_nents : 0);
+	sg_table = &edesc->sgt[0];
+	qm_sg_bytes = qm_sg_ents * sizeof(*sg_table);
+
+	iv = (u8 *)(sg_table + qm_sg_ents);
+	/* Make sure IV is located in a DMAable area */
+	memcpy(iv, req->iv, ivsize);
+	iv_dma = dma_map_single(qidev, iv, ivsize, DMA_TO_DEVICE);
+	if (dma_mapping_error(qidev, iv_dma)) {
+		dev_err(qidev, "unable to map IV\n");
+		caam_unmap(qidev, req->src, dst, src_nents, dst_nents, 0, 0,
+			   DMA_NONE, 0, 0);
+		qi_cache_free(edesc);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	edesc->src_nents = src_nents;
+	edesc->dst_nents = dst_nents;
+	edesc->dst = dst;
+	edesc->iv_dma = iv_dma;
+	edesc->drv_req.app_ctx = req;
+	edesc->drv_req.cbk = tls_done;
+	edesc->drv_req.drv_ctx = drv_ctx;
+
+	dma_to_qm_sg_one(sg_table, iv_dma, ivsize, 0);
+	qm_sg_index = 1;
+
+	sg_to_qm_sg_last(req->src, src_len, sg_table + qm_sg_index, 0);
+	qm_sg_index += mapped_src_nents;
+
+	if (mapped_dst_nents > 1)
+		sg_to_qm_sg_last(dst, dst_len, sg_table + qm_sg_index, 0);
+
+	qm_sg_dma = dma_map_single(qidev, sg_table, qm_sg_bytes, DMA_TO_DEVICE);
+	if (dma_mapping_error(qidev, qm_sg_dma)) {
+		dev_err(qidev, "unable to map S/G table\n");
+		caam_unmap(qidev, req->src, dst, src_nents, dst_nents, iv_dma,
+			   ivsize, DMA_TO_DEVICE, 0, 0);
+		qi_cache_free(edesc);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	edesc->qm_sg_dma = qm_sg_dma;
+	edesc->qm_sg_bytes = qm_sg_bytes;
+
+	fd_sgt = &edesc->drv_req.fd_sgt[0];
+
+	dma_to_qm_sg_one_last_ext(&fd_sgt[1], qm_sg_dma, ivsize + data_len, 0);
+
+	if (req->dst == req->src)
+		dma_to_qm_sg_one_ext(&fd_sgt[0], qm_sg_dma +
+				    (sg_nents_for_len(req->src, req->assoclen) +
+				     1) * sizeof(*sg_table), dst_len, 0);
+	else if (mapped_dst_nents == 1)
+		dma_to_qm_sg_one(&fd_sgt[0], sg_dma_address(dst), dst_len, 0);
+	else
+		dma_to_qm_sg_one_ext(&fd_sgt[0], qm_sg_dma + sizeof(*sg_table) *
+				     qm_sg_index, dst_len, 0);
+
+	return edesc;
+}
+
+static int tls_crypt(struct aead_request *req, bool encrypt)
+{
+	struct tls_edesc *edesc;
+	struct crypto_aead *aead = crypto_aead_reqtfm(req);
+	struct caam_ctx *ctx = crypto_aead_ctx(aead);
+	int ret;
+
+	if (unlikely(caam_congested))
+		return -EAGAIN;
+
+	edesc = tls_edesc_alloc(req, encrypt);
+	if (IS_ERR_OR_NULL(edesc))
+		return PTR_ERR(edesc);
+
+	ret = caam_qi_enqueue(ctx->qidev, &edesc->drv_req);
+	if (!ret) {
+		ret = -EINPROGRESS;
+	} else {
+		tls_unmap(ctx->qidev, edesc, req);
+		qi_cache_free(edesc);
+	}
+
+	return ret;
+}
+
+static int tls_encrypt(struct aead_request *req)
+{
+	return tls_crypt(req, true);
+}
+
+static int tls_decrypt(struct aead_request *req)
+{
+	return tls_crypt(req, false);
+}
+
 static int ipsec_gcm_encrypt(struct aead_request *req)
 {
 	return crypto_ipsec_check_assoclen(req->assoclen) ? : aead_crypt(req,
@@ -2440,6 +2867,26 @@ static struct caam_aead_alg driver_aeads[] = {
 			.geniv = true,
 		}
 	},
+	{
+		.aead = {
+			.base = {
+				.cra_name = "tls10(hmac(sha1),cbc(aes))",
+				.cra_driver_name = "tls10-hmac-sha1-cbc-aes-caam-qi",
+				.cra_blocksize = AES_BLOCK_SIZE,
+			},
+			.setkey = tls_setkey,
+			.setauthsize = tls_setauthsize,
+			.encrypt = tls_encrypt,
+			.decrypt = tls_decrypt,
+			.ivsize = AES_BLOCK_SIZE,
+			.maxauthsize = SHA1_DIGEST_SIZE,
+		},
+		.caam = {
+			.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+			.class2_alg_type = OP_ALG_ALGSEL_SHA1 |
+					   OP_ALG_AAI_HMAC_PRECOMP,
+		}
+	}
 };
 
 static int caam_init_common(struct caam_ctx *ctx, struct caam_alg_entry *caam,
@@ -2447,6 +2894,16 @@ static int caam_init_common(struct caam_ctx *ctx, struct caam_alg_entry *caam,
 {
 	struct caam_drv_private *priv;
 	struct device *dev;
+	/* Digest sizes for MD5, SHA1, SHA-224, SHA-256, SHA-384, SHA-512 */
+	static const u8 digest_size[] = {
+		MD5_DIGEST_SIZE,
+		SHA1_DIGEST_SIZE,
+		SHA224_DIGEST_SIZE,
+		SHA256_DIGEST_SIZE,
+		SHA384_DIGEST_SIZE,
+		SHA512_DIGEST_SIZE
+	};
+	u8 op_id;
 
 	/*
 	 * distribute tfms across job rings to ensure in-order
@@ -2478,6 +2935,21 @@ static int caam_init_common(struct caam_ctx *ctx, struct caam_alg_entry *caam,
 	ctx->adata.algtype = OP_TYPE_CLASS2_ALG | caam->class2_alg_type;
 
 	ctx->qidev = dev;
+	if (ctx->adata.algtype) {
+		op_id = (ctx->adata.algtype & OP_ALG_ALGSEL_SUBMASK)
+				>> OP_ALG_ALGSEL_SHIFT;
+		if (op_id < ARRAY_SIZE(digest_size)) {
+			ctx->authsize = digest_size[op_id];
+		} else {
+			dev_err(ctx->jrdev,
+				"incorrect op_id %d; must be less than %zu\n",
+				op_id, ARRAY_SIZE(digest_size));
+			caam_jr_free(ctx->jrdev);
+			return -EINVAL;
+		}
+	} else {
+		ctx->authsize = 0;
+	}
 
 	spin_lock_init(&ctx->lock);
 	ctx->drv_ctx[ENCRYPT] = NULL;
diff --git a/drivers/crypto/caam/caamalg_qi2.c b/drivers/crypto/caam/caamalg_qi2.c
index a780e6278..4592b634a 100644
--- a/drivers/crypto/caam/caamalg_qi2.c
+++ b/drivers/crypto/caam/caamalg_qi2.c
@@ -582,6 +582,254 @@ static struct aead_edesc *aead_edesc_alloc(struct aead_request *req,
 	return edesc;
 }
 
+static struct tls_edesc *tls_edesc_alloc(struct aead_request *req,
+					 bool encrypt)
+{
+	struct crypto_aead *tls = crypto_aead_reqtfm(req);
+	unsigned int blocksize = crypto_aead_blocksize(tls);
+	unsigned int padsize, authsize;
+	struct caam_request *req_ctx = aead_request_ctx(req);
+	struct dpaa2_fl_entry *in_fle = &req_ctx->fd_flt[1];
+	struct dpaa2_fl_entry *out_fle = &req_ctx->fd_flt[0];
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+	struct caam_aead_alg *alg = container_of(crypto_aead_alg(tls),
+						 typeof(*alg), aead);
+	struct device *dev = ctx->dev;
+	gfp_t flags = (req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP) ?
+		      GFP_KERNEL : GFP_ATOMIC;
+	int src_nents, mapped_src_nents, dst_nents = 0, mapped_dst_nents = 0;
+	struct tls_edesc *edesc;
+	dma_addr_t qm_sg_dma, iv_dma = 0;
+	int ivsize = crypto_aead_ivsize(tls);
+	u8 *iv;
+	int qm_sg_index, qm_sg_ents = 0, qm_sg_bytes;
+	int src_len, dst_len, data_len;
+	struct dpaa2_sg_entry *sg_table;
+	struct scatterlist *dst;
+
+	if (encrypt) {
+		padsize = blocksize - ((req->cryptlen + ctx->authsize) %
+					blocksize);
+		authsize = ctx->authsize + padsize;
+	} else {
+		authsize = ctx->authsize;
+	}
+
+	/* allocate space for base edesc, link tables and IV */
+	edesc = qi_cache_zalloc(GFP_DMA | flags);
+	if (unlikely(!edesc)) {
+		dev_err(dev, "could not allocate extended descriptor\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	data_len = req->assoclen + req->cryptlen;
+	dst_len = req->cryptlen + (encrypt ? authsize : 0);
+
+	if (likely(req->src == req->dst)) {
+		src_len = req->assoclen + dst_len;
+
+		src_nents = sg_nents_for_len(req->src, src_len);
+		if (unlikely(src_nents < 0)) {
+			dev_err(dev, "Insufficient bytes (%d) in src S/G\n",
+				src_len);
+			qi_cache_free(edesc);
+			return ERR_PTR(src_nents);
+		}
+
+		mapped_src_nents = dma_map_sg(dev, req->src, src_nents,
+					      DMA_BIDIRECTIONAL);
+		if (unlikely(!mapped_src_nents)) {
+			dev_err(dev, "unable to map source\n");
+			qi_cache_free(edesc);
+			return ERR_PTR(-ENOMEM);
+		}
+		dst = req->dst;
+	} else {
+		src_len = data_len;
+
+		src_nents = sg_nents_for_len(req->src, src_len);
+		if (unlikely(src_nents < 0)) {
+			dev_err(dev, "Insufficient bytes (%d) in src S/G\n",
+				src_len);
+			qi_cache_free(edesc);
+			return ERR_PTR(src_nents);
+		}
+
+		dst = scatterwalk_ffwd(edesc->tmp, req->dst, req->assoclen);
+
+		dst_nents = sg_nents_for_len(dst, dst_len);
+		if (unlikely(dst_nents < 0)) {
+			dev_err(dev, "Insufficient bytes (%d) in dst S/G\n",
+				dst_len);
+			qi_cache_free(edesc);
+			return ERR_PTR(dst_nents);
+		}
+
+		if (src_nents) {
+			mapped_src_nents = dma_map_sg(dev, req->src,
+						      src_nents, DMA_TO_DEVICE);
+			if (unlikely(!mapped_src_nents)) {
+				dev_err(dev, "unable to map source\n");
+				qi_cache_free(edesc);
+				return ERR_PTR(-ENOMEM);
+			}
+		} else {
+			mapped_src_nents = 0;
+		}
+
+		mapped_dst_nents = dma_map_sg(dev, dst, dst_nents,
+					      DMA_FROM_DEVICE);
+		if (unlikely(!mapped_dst_nents)) {
+			dev_err(dev, "unable to map destination\n");
+			dma_unmap_sg(dev, req->src, src_nents, DMA_TO_DEVICE);
+			qi_cache_free(edesc);
+			return ERR_PTR(-ENOMEM);
+		}
+	}
+
+	/*
+	 * Create S/G table: IV, src, dst.
+	 * Input is not contiguous.
+	 */
+	qm_sg_ents = 1 + mapped_src_nents +
+		     (mapped_dst_nents > 1 ? mapped_dst_nents : 0);
+	sg_table = &edesc->sgt[0];
+	qm_sg_bytes = qm_sg_ents * sizeof(*sg_table);
+
+	iv = (u8 *)(sg_table + qm_sg_ents);
+	/* Make sure IV is located in a DMAable area */
+	memcpy(iv, req->iv, ivsize);
+	iv_dma = dma_map_single(dev, iv, ivsize, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, iv_dma)) {
+		dev_err(dev, "unable to map IV\n");
+		caam_unmap(dev, req->src, dst, src_nents, dst_nents, 0, 0,
+		DMA_NONE, 0, 0);
+		qi_cache_free(edesc);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	edesc->src_nents = src_nents;
+	edesc->dst_nents = dst_nents;
+	edesc->dst = dst;
+	edesc->iv_dma = iv_dma;
+
+	dma_to_qm_sg_one(sg_table, iv_dma, ivsize, 0);
+	qm_sg_index = 1;
+
+	sg_to_qm_sg_last(req->src, src_len, sg_table + qm_sg_index, 0);
+	qm_sg_index += mapped_src_nents;
+
+	if (mapped_dst_nents > 1)
+		sg_to_qm_sg_last(dst, dst_len, sg_table + qm_sg_index, 0);
+
+	qm_sg_dma = dma_map_single(dev, sg_table, qm_sg_bytes, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, qm_sg_dma)) {
+		dev_err(dev, "unable to map S/G table\n");
+		caam_unmap(dev, req->src, dst, src_nents, dst_nents, iv_dma,
+			   ivsize, DMA_TO_DEVICE, 0, 0);
+		qi_cache_free(edesc);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	edesc->qm_sg_dma = qm_sg_dma;
+	edesc->qm_sg_bytes = qm_sg_bytes;
+
+	memset(&req_ctx->fd_flt, 0, sizeof(req_ctx->fd_flt));
+	dpaa2_fl_set_final(in_fle, true);
+	dpaa2_fl_set_format(in_fle, dpaa2_fl_sg);
+	dpaa2_fl_set_addr(in_fle, qm_sg_dma);
+	dpaa2_fl_set_len(in_fle, ivsize + data_len);
+
+	if (req->dst == req->src) {
+		dpaa2_fl_set_format(out_fle, dpaa2_fl_sg);
+		dpaa2_fl_set_addr(out_fle, qm_sg_dma +
+				  (sg_nents_for_len(req->src, req->assoclen) +
+				   1) * sizeof(*sg_table));
+	} else if (mapped_dst_nents == 1) {
+		dpaa2_fl_set_format(out_fle, dpaa2_fl_single);
+		dpaa2_fl_set_addr(out_fle, sg_dma_address(dst));
+	} else {
+		dpaa2_fl_set_format(out_fle, dpaa2_fl_sg);
+		dpaa2_fl_set_addr(out_fle, qm_sg_dma + qm_sg_index *
+				  sizeof(*sg_table));
+	}
+
+	dpaa2_fl_set_len(out_fle, dst_len);
+
+	return edesc;
+}
+
+static int tls_set_sh_desc(struct crypto_aead *tls)
+{
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+	unsigned int ivsize = crypto_aead_ivsize(tls);
+	unsigned int blocksize = crypto_aead_blocksize(tls);
+	struct device *dev = ctx->dev;
+	struct dpaa2_caam_priv *priv = dev_get_drvdata(dev);
+	struct caam_flc *flc;
+	u32 *desc;
+	unsigned int assoclen = 13; /* always 13 bytes for TLS */
+	unsigned int data_len[2];
+	u32 inl_mask;
+
+	if (!ctx->cdata.keylen || !ctx->authsize)
+		return 0;
+
+	/*
+	 * TLS 1.0 encrypt shared descriptor
+	 * Job Descriptor and Shared Descriptor
+	 * must fit into the 64-word Descriptor h/w Buffer
+	 */
+	data_len[0] = ctx->adata.keylen_pad;
+	data_len[1] = ctx->cdata.keylen;
+
+	if (desc_inline_query(DESC_TLS10_ENC_LEN, DESC_JOB_IO_LEN, data_len,
+			      &inl_mask, ARRAY_SIZE(data_len)) < 0)
+		return -EINVAL;
+
+	if (inl_mask & 1)
+		ctx->adata.key_virt = ctx->key;
+	else
+		ctx->adata.key_dma = ctx->key_dma;
+
+	if (inl_mask & 2)
+		ctx->cdata.key_virt = ctx->key + ctx->adata.keylen_pad;
+	else
+		ctx->cdata.key_dma = ctx->key_dma + ctx->adata.keylen_pad;
+
+	ctx->adata.key_inline = !!(inl_mask & 1);
+	ctx->cdata.key_inline = !!(inl_mask & 2);
+
+	flc = &ctx->flc[ENCRYPT];
+	desc = flc->sh_desc;
+	cnstr_shdsc_tls_encap(desc, &ctx->cdata, &ctx->adata,
+			      assoclen, ivsize, ctx->authsize, blocksize,
+			      priv->sec_attr.era);
+	flc->flc[1] = cpu_to_caam32(desc_len(desc));
+	dma_sync_single_for_device(dev, ctx->flc_dma[ENCRYPT],
+				   sizeof(flc->flc) + desc_bytes(desc),
+				   ctx->dir);
+
+	/*
+	 * TLS 1.0 decrypt shared descriptor
+	 * Keys do not fit inline, regardless of algorithms used
+	 */
+	ctx->adata.key_inline = false;
+	ctx->adata.key_dma = ctx->key_dma;
+	ctx->cdata.key_dma = ctx->key_dma + ctx->adata.keylen_pad;
+
+	flc = &ctx->flc[DECRYPT];
+	desc = flc->sh_desc;
+	cnstr_shdsc_tls_decap(desc, &ctx->cdata, &ctx->adata, assoclen, ivsize,
+			      ctx->authsize, blocksize, priv->sec_attr.era);
+	flc->flc[1] = cpu_to_caam32(desc_len(desc));
+	dma_sync_single_for_device(dev, ctx->flc_dma[DECRYPT],
+				   sizeof(flc->flc) + desc_bytes(desc),
+				   ctx->dir);
+
+	return 0;
+}
+
 static int chachapoly_set_sh_desc(struct crypto_aead *aead)
 {
 	struct caam_ctx *ctx = crypto_aead_ctx(aead);
@@ -626,6 +874,60 @@ static int chachapoly_setauthsize(struct crypto_aead *aead,
 	return chachapoly_set_sh_desc(aead);
 }
 
+static int tls_setkey(struct crypto_aead *tls, const u8 *key,
+		      unsigned int keylen)
+{
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+	struct device *dev = ctx->dev;
+	struct crypto_authenc_keys keys;
+
+	if (crypto_authenc_extractkeys(&keys, key, keylen) != 0)
+		goto badkey;
+
+#ifdef DEBUG
+	dev_err(dev, "keylen %d enckeylen %d authkeylen %d\n",
+		keys.authkeylen + keys.enckeylen, keys.enckeylen,
+		keys.authkeylen);
+	print_hex_dump(KERN_ERR, "key in @" __stringify(__LINE__)": ",
+		       DUMP_PREFIX_ADDRESS, 16, 4, key, keylen, 1);
+#endif
+
+	ctx->adata.keylen = keys.authkeylen;
+	ctx->adata.keylen_pad = split_key_len(ctx->adata.algtype &
+					      OP_ALG_ALGSEL_MASK);
+
+	if (ctx->adata.keylen_pad + keys.enckeylen > CAAM_MAX_KEY_SIZE)
+		goto badkey;
+
+	memcpy(ctx->key, keys.authkey, keys.authkeylen);
+	memcpy(ctx->key + ctx->adata.keylen_pad, keys.enckey, keys.enckeylen);
+	dma_sync_single_for_device(dev, ctx->key_dma, ctx->adata.keylen_pad +
+				   keys.enckeylen, ctx->dir);
+#ifdef DEBUG
+	print_hex_dump(KERN_ERR, "ctx.key@" __stringify(__LINE__)": ",
+		       DUMP_PREFIX_ADDRESS, 16, 4, ctx->key,
+		       ctx->adata.keylen_pad + keys.enckeylen, 1);
+#endif
+
+	ctx->cdata.keylen = keys.enckeylen;
+
+	memzero_explicit(&keys, sizeof(keys));
+	return tls_set_sh_desc(tls);
+badkey:
+	memzero_explicit(&keys, sizeof(keys));
+	return -EINVAL;
+}
+
+static int tls_setauthsize(struct crypto_aead *tls, unsigned int authsize)
+{
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+
+	ctx->authsize = authsize;
+	tls_set_sh_desc(tls);
+
+	return 0;
+}
+
 static int chachapoly_setkey(struct crypto_aead *aead, const u8 *key,
 			     unsigned int keylen)
 {
@@ -1264,6 +1566,17 @@ static void aead_unmap(struct device *dev, struct aead_edesc *edesc,
 	dma_unmap_single(dev, edesc->assoclen_dma, 4, DMA_TO_DEVICE);
 }
 
+static void tls_unmap(struct device *dev, struct tls_edesc *edesc,
+		      struct aead_request *req)
+{
+	struct crypto_aead *tls = crypto_aead_reqtfm(req);
+	int ivsize = crypto_aead_ivsize(tls);
+
+	caam_unmap(dev, req->src, edesc->dst, edesc->src_nents,
+		   edesc->dst_nents, edesc->iv_dma, ivsize, DMA_TO_DEVICE,
+		   edesc->qm_sg_dma, edesc->qm_sg_bytes);
+}
+
 static void skcipher_unmap(struct device *dev, struct skcipher_edesc *edesc,
 			   struct skcipher_request *req)
 {
@@ -1275,7 +1588,7 @@ static void skcipher_unmap(struct device *dev, struct skcipher_edesc *edesc,
 		   edesc->qm_sg_bytes);
 }
 
-static void aead_encrypt_done(void *cbk_ctx, u32 status)
+static void aead_crypt_done(void *cbk_ctx, u32 status)
 {
 	struct crypto_async_request *areq = cbk_ctx;
 	struct aead_request *req = container_of(areq, struct aead_request,
@@ -1296,28 +1609,7 @@ static void aead_encrypt_done(void *cbk_ctx, u32 status)
 	aead_request_complete(req, ecode);
 }
 
-static void aead_decrypt_done(void *cbk_ctx, u32 status)
-{
-	struct crypto_async_request *areq = cbk_ctx;
-	struct aead_request *req = container_of(areq, struct aead_request,
-						base);
-	struct caam_request *req_ctx = to_caam_req(areq);
-	struct aead_edesc *edesc = req_ctx->edesc;
-	struct crypto_aead *aead = crypto_aead_reqtfm(req);
-	struct caam_ctx *ctx = crypto_aead_ctx(aead);
-	int ecode = 0;
-
-	dev_dbg(ctx->dev, "%s %d: err 0x%x\n", __func__, __LINE__, status);
-
-	if (unlikely(status))
-		ecode = caam_qi2_strstatus(ctx->dev, status);
-
-	aead_unmap(ctx->dev, edesc, req);
-	qi_cache_free(edesc);
-	aead_request_complete(req, ecode);
-}
-
-static int aead_encrypt(struct aead_request *req)
+static int aead_crypt(struct aead_request *req, enum optype op)
 {
 	struct aead_edesc *edesc;
 	struct crypto_aead *aead = crypto_aead_reqtfm(req);
@@ -1326,13 +1618,13 @@ static int aead_encrypt(struct aead_request *req)
 	int ret;
 
 	/* allocate extended descriptor */
-	edesc = aead_edesc_alloc(req, true);
+	edesc = aead_edesc_alloc(req, op == ENCRYPT);
 	if (IS_ERR(edesc))
 		return PTR_ERR(edesc);
 
-	caam_req->flc = &ctx->flc[ENCRYPT];
-	caam_req->flc_dma = ctx->flc_dma[ENCRYPT];
-	caam_req->cbk = aead_encrypt_done;
+	caam_req->flc = &ctx->flc[op];
+	caam_req->flc_dma = ctx->flc_dma[op];
+	caam_req->cbk = aead_crypt_done;
 	caam_req->ctx = &req->base;
 	caam_req->edesc = edesc;
 	ret = dpaa2_caam_enqueue(ctx->dev, caam_req);
@@ -1345,83 +1637,88 @@ static int aead_encrypt(struct aead_request *req)
 	return ret;
 }
 
+static int aead_encrypt(struct aead_request *req)
+{
+	return aead_crypt(req, ENCRYPT);
+}
+
 static int aead_decrypt(struct aead_request *req)
 {
-	struct aead_edesc *edesc;
-	struct crypto_aead *aead = crypto_aead_reqtfm(req);
-	struct caam_ctx *ctx = crypto_aead_ctx(aead);
+	return aead_crypt(req, DECRYPT);
+}
+
+static void tls_crypt_done(void *cbk_ctx, u32 status)
+{
+	struct crypto_async_request *areq = cbk_ctx;
+	struct aead_request *req = container_of(areq, struct aead_request,
+						base);
+	struct caam_request *req_ctx = to_caam_req(areq);
+	struct tls_edesc *edesc = req_ctx->edesc;
+	struct crypto_aead *tls = crypto_aead_reqtfm(req);
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
+	int ecode = 0;
+
+#ifdef DEBUG
+	dev_err(ctx->dev, "%s %d: err 0x%x\n", __func__, __LINE__, status);
+#endif
+
+	if (unlikely(status))
+		ecode = caam_qi2_strstatus(ctx->dev, status);
+
+	tls_unmap(ctx->dev, edesc, req);
+	qi_cache_free(edesc);
+	aead_request_complete(req, ecode);
+}
+
+static int tls_crypt(struct aead_request *req, enum optype op)
+{
+	struct tls_edesc *edesc;
+	struct crypto_aead *tls = crypto_aead_reqtfm(req);
+	struct caam_ctx *ctx = crypto_aead_ctx(tls);
 	struct caam_request *caam_req = aead_request_ctx(req);
 	int ret;
 
 	/* allocate extended descriptor */
-	edesc = aead_edesc_alloc(req, false);
+	edesc = tls_edesc_alloc(req, op == ENCRYPT);
 	if (IS_ERR(edesc))
 		return PTR_ERR(edesc);
 
-	caam_req->flc = &ctx->flc[DECRYPT];
-	caam_req->flc_dma = ctx->flc_dma[DECRYPT];
-	caam_req->cbk = aead_decrypt_done;
+	caam_req->flc = &ctx->flc[op];
+	caam_req->flc_dma = ctx->flc_dma[op];
+	caam_req->cbk = tls_crypt_done;
 	caam_req->ctx = &req->base;
 	caam_req->edesc = edesc;
 	ret = dpaa2_caam_enqueue(ctx->dev, caam_req);
 	if (ret != -EINPROGRESS &&
 	    !(ret == -EBUSY && req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
-		aead_unmap(ctx->dev, edesc, req);
+		tls_unmap(ctx->dev, edesc, req);
 		qi_cache_free(edesc);
 	}
 
 	return ret;
 }
 
-static int ipsec_gcm_encrypt(struct aead_request *req)
+static int tls_encrypt(struct aead_request *req)
 {
-	return crypto_ipsec_check_assoclen(req->assoclen) ? : aead_encrypt(req);
+	return tls_crypt(req, ENCRYPT);
 }
 
-static int ipsec_gcm_decrypt(struct aead_request *req)
+static int tls_decrypt(struct aead_request *req)
 {
-	return crypto_ipsec_check_assoclen(req->assoclen) ? : aead_decrypt(req);
+	return tls_crypt(req, DECRYPT);
 }
 
-static void skcipher_encrypt_done(void *cbk_ctx, u32 status)
+static int ipsec_gcm_encrypt(struct aead_request *req)
 {
-	struct crypto_async_request *areq = cbk_ctx;
-	struct skcipher_request *req = skcipher_request_cast(areq);
-	struct caam_request *req_ctx = to_caam_req(areq);
-	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
-	struct caam_ctx *ctx = crypto_skcipher_ctx(skcipher);
-	struct skcipher_edesc *edesc = req_ctx->edesc;
-	int ecode = 0;
-	int ivsize = crypto_skcipher_ivsize(skcipher);
-
-	dev_dbg(ctx->dev, "%s %d: err 0x%x\n", __func__, __LINE__, status);
-
-	if (unlikely(status))
-		ecode = caam_qi2_strstatus(ctx->dev, status);
-
-	print_hex_dump_debug("dstiv  @" __stringify(__LINE__)": ",
-			     DUMP_PREFIX_ADDRESS, 16, 4, req->iv,
-			     edesc->src_nents > 1 ? 100 : ivsize, 1);
-	caam_dump_sg("dst    @" __stringify(__LINE__)": ",
-		     DUMP_PREFIX_ADDRESS, 16, 4, req->dst,
-		     edesc->dst_nents > 1 ? 100 : req->cryptlen, 1);
-
-	skcipher_unmap(ctx->dev, edesc, req);
-
-	/*
-	 * The crypto API expects us to set the IV (req->iv) to the last
-	 * ciphertext block (CBC mode) or last counter (CTR mode).
-	 * This is used e.g. by the CTS mode.
-	 */
-	if (!ecode)
-		memcpy(req->iv, (u8 *)&edesc->sgt[0] + edesc->qm_sg_bytes,
-		       ivsize);
+	return crypto_ipsec_check_assoclen(req->assoclen) ? : aead_encrypt(req);
+}
 
-	qi_cache_free(edesc);
-	skcipher_request_complete(req, ecode);
+static int ipsec_gcm_decrypt(struct aead_request *req)
+{
+	return crypto_ipsec_check_assoclen(req->assoclen) ? : aead_decrypt(req);
 }
 
-static void skcipher_decrypt_done(void *cbk_ctx, u32 status)
+static void skcipher_crypt_done(void *cbk_ctx, u32 status)
 {
 	struct crypto_async_request *areq = cbk_ctx;
 	struct skcipher_request *req = skcipher_request_cast(areq);
@@ -1467,7 +1764,7 @@ static inline bool xts_skcipher_ivsize(struct skcipher_request *req)
 	return !!get_unaligned((u64 *)(req->iv + (ivsize / 2)));
 }
 
-static int skcipher_encrypt(struct skcipher_request *req)
+static int skcipher_crypt(struct skcipher_request *req, enum optype op)
 {
 	struct skcipher_edesc *edesc;
 	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
@@ -1494,7 +1791,9 @@ static int skcipher_encrypt(struct skcipher_request *req)
 		skcipher_request_set_crypt(&caam_req->fallback_req, req->src,
 					   req->dst, req->cryptlen, req->iv);
 
-		return crypto_skcipher_encrypt(&caam_req->fallback_req);
+		return (op == ENCRYPT) ?
+			crypto_skcipher_encrypt(&caam_req->fallback_req) :
+			crypto_skcipher_decrypt(&caam_req->fallback_req);
 	}
 
 	/* allocate extended descriptor */
@@ -1502,9 +1801,9 @@ static int skcipher_encrypt(struct skcipher_request *req)
 	if (IS_ERR(edesc))
 		return PTR_ERR(edesc);
 
-	caam_req->flc = &ctx->flc[ENCRYPT];
-	caam_req->flc_dma = ctx->flc_dma[ENCRYPT];
-	caam_req->cbk = skcipher_encrypt_done;
+	caam_req->flc = &ctx->flc[op];
+	caam_req->flc_dma = ctx->flc_dma[op];
+	caam_req->cbk = skcipher_crypt_done;
 	caam_req->ctx = &req->base;
 	caam_req->edesc = edesc;
 	ret = dpaa2_caam_enqueue(ctx->dev, caam_req);
@@ -1517,54 +1816,14 @@ static int skcipher_encrypt(struct skcipher_request *req)
 	return ret;
 }
 
-static int skcipher_decrypt(struct skcipher_request *req)
+static int skcipher_encrypt(struct skcipher_request *req)
 {
-	struct skcipher_edesc *edesc;
-	struct crypto_skcipher *skcipher = crypto_skcipher_reqtfm(req);
-	struct caam_ctx *ctx = crypto_skcipher_ctx(skcipher);
-	struct caam_request *caam_req = skcipher_request_ctx(req);
-	struct dpaa2_caam_priv *priv = dev_get_drvdata(ctx->dev);
-	int ret;
-
-	/*
-	 * XTS is expected to return an error even for input length = 0
-	 * Note that the case input length < block size will be caught during
-	 * HW offloading and return an error.
-	 */
-	if (!req->cryptlen && !ctx->fallback)
-		return 0;
-
-	if (ctx->fallback && ((priv->sec_attr.era <= 8 && xts_skcipher_ivsize(req)) ||
-			      ctx->xts_key_fallback)) {
-		skcipher_request_set_tfm(&caam_req->fallback_req, ctx->fallback);
-		skcipher_request_set_callback(&caam_req->fallback_req,
-					      req->base.flags,
-					      req->base.complete,
-					      req->base.data);
-		skcipher_request_set_crypt(&caam_req->fallback_req, req->src,
-					   req->dst, req->cryptlen, req->iv);
-
-		return crypto_skcipher_decrypt(&caam_req->fallback_req);
-	}
-
-	/* allocate extended descriptor */
-	edesc = skcipher_edesc_alloc(req);
-	if (IS_ERR(edesc))
-		return PTR_ERR(edesc);
-
-	caam_req->flc = &ctx->flc[DECRYPT];
-	caam_req->flc_dma = ctx->flc_dma[DECRYPT];
-	caam_req->cbk = skcipher_decrypt_done;
-	caam_req->ctx = &req->base;
-	caam_req->edesc = edesc;
-	ret = dpaa2_caam_enqueue(ctx->dev, caam_req);
-	if (ret != -EINPROGRESS &&
-	    !(ret == -EBUSY && req->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
-		skcipher_unmap(ctx->dev, edesc, req);
-		qi_cache_free(edesc);
-	}
+	return skcipher_crypt(req, ENCRYPT);
+}
 
-	return ret;
+static int skcipher_decrypt(struct skcipher_request *req)
+{
+	return skcipher_crypt(req, DECRYPT);
 }
 
 static int caam_cra_init(struct caam_ctx *ctx, struct caam_alg_entry *caam,
@@ -2997,6 +3256,26 @@ static struct caam_aead_alg driver_aeads[] = {
 			.geniv = true,
 		},
 	},
+	{
+		.aead = {
+			.base = {
+				.cra_name = "tls10(hmac(sha1),cbc(aes))",
+				.cra_driver_name = "tls10-hmac-sha1-cbc-aes-caam-qi2",
+				.cra_blocksize = AES_BLOCK_SIZE,
+			},
+			.setkey = tls_setkey,
+			.setauthsize = tls_setauthsize,
+			.encrypt = tls_encrypt,
+			.decrypt = tls_decrypt,
+			.ivsize = AES_BLOCK_SIZE,
+			.maxauthsize = SHA1_DIGEST_SIZE,
+		},
+		.caam = {
+			.class1_alg_type = OP_ALG_ALGSEL_AES | OP_ALG_AAI_CBC,
+			.class2_alg_type = OP_ALG_ALGSEL_SHA1 |
+					   OP_ALG_AAI_HMAC_PRECOMP,
+		},
+	},
 };
 
 static void caam_skcipher_alg_init(struct caam_skcipher_alg *t_alg)
diff --git a/drivers/crypto/caam/caamalg_qi2.h b/drivers/crypto/caam/caamalg_qi2.h
index d35253407..0b610c106 100644
--- a/drivers/crypto/caam/caamalg_qi2.h
+++ b/drivers/crypto/caam/caamalg_qi2.h
@@ -118,6 +118,28 @@ struct aead_edesc {
 	struct dpaa2_sg_entry sgt[];
 };
 
+/*
+ * tls_edesc - s/w-extended tls descriptor
+ * @src_nents: number of segments in input scatterlist
+ * @dst_nents: number of segments in output scatterlist
+ * @iv_dma: dma address of iv for checking continuity and link table
+ * @qm_sg_bytes: length of dma mapped h/w link table
+ * @qm_sg_dma: bus physical mapped address of h/w link table
+ * @tmp: array of scatterlists used by 'scatterwalk_ffwd'
+ * @dst: pointer to output scatterlist, usefull for unmapping
+ * @sgt: the h/w link table, followed by IV
+ */
+struct tls_edesc {
+	int src_nents;
+	int dst_nents;
+	dma_addr_t iv_dma;
+	int qm_sg_bytes;
+	dma_addr_t qm_sg_dma;
+	struct scatterlist tmp[2];
+	struct scatterlist *dst;
+	struct dpaa2_sg_entry sgt[0];
+};
+
 /*
  * skcipher_edesc - s/w-extended skcipher descriptor
  * @src_nents: number of segments in input scatterlist
diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c
index e8a6d8bc4..ae2ae28e3 100644
--- a/drivers/crypto/caam/caamhash.c
+++ b/drivers/crypto/caam/caamhash.c
@@ -1946,12 +1946,14 @@ int caam_algapi_hash_init(struct device *ctrldev)
 	 * presence and attributes of MD block.
 	 */
 	if (priv->era < 10) {
-		md_vid = (rd_reg32(&priv->ctrl->perfmon.cha_id_ls) &
+		struct caam_perfmon __iomem *perfmon = &priv->jr[0]->perfmon;
+
+		md_vid = (rd_reg32(&perfmon->cha_id_ls) &
 			  CHA_ID_LS_MD_MASK) >> CHA_ID_LS_MD_SHIFT;
-		md_inst = (rd_reg32(&priv->ctrl->perfmon.cha_num_ls) &
+		md_inst = (rd_reg32(&perfmon->cha_num_ls) &
 			   CHA_ID_LS_MD_MASK) >> CHA_ID_LS_MD_SHIFT;
 	} else {
-		u32 mdha = rd_reg32(&priv->ctrl->vreg.mdha);
+		u32 mdha = rd_reg32(&priv->jr[0]->vreg.mdha);
 
 		md_vid = (mdha & CHA_VER_VID_MASK) >> CHA_VER_VID_SHIFT;
 		md_inst = mdha & CHA_VER_NUM_MASK;
diff --git a/drivers/crypto/caam/caamkeyblob.c b/drivers/crypto/caam/caamkeyblob.c
new file mode 100644
index 000000000..03821d437
--- /dev/null
+++ b/drivers/crypto/caam/caamkeyblob.c
@@ -0,0 +1,670 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Black key generation and blob encapsulation/decapsulation for CAAM
+ *
+ * Copyright 2018-2020 NXP
+ */
+#include "caamkeyblob.h"
+#include "error.h"
+
+/* Black key generation and blob encap/decap job completion handler */
+static void caam_key_blob_done(struct device *dev, u32 *desc, u32 err,
+			       void *context)
+{
+	struct jr_job_result *res = context;
+	int ecode = 0;
+
+	dev_dbg(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+
+	if (err)
+		ecode = caam_jr_strstatus(dev, err);
+
+	/* Save the error for post-processing */
+	res->error = ecode;
+	/* Mark job as complete */
+	complete(&res->completion);
+}
+
+/**
+ * map_write_data   - Prepare data to be written to CAAM
+ *
+ * @dev             : struct device of the job ring to be used
+ * @data            : The data to be prepared
+ * @size            : The size of data to be prepared
+ * @dma_addr        : The retrieve DMA address of the input data
+ * @allocated_data  : Pointer to a DMA-able address where the input
+ *                    data is copied and synchronized
+ *
+ * Return           : '0' on success, error code otherwise
+ */
+static int map_write_data(struct device *dev, const u8 *data, size_t size,
+			  dma_addr_t *dma_addr, u8 **allocated_data)
+{
+	int ret = 0;
+
+	/* Allocate memory for data and copy it to DMA zone */
+	*allocated_data = kmemdup(data, size, GFP_KERNEL | GFP_DMA);
+	if (!*allocated_data) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	*dma_addr = dma_map_single(dev, *allocated_data, size, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, *dma_addr)) {
+		dev_err(dev, "Unable to map write data\n");
+		ret = -ENOMEM;
+		goto free_alloc;
+	}
+
+	goto exit;
+
+free_alloc:
+	kfree(*allocated_data);
+
+exit:
+	return ret;
+}
+
+/**
+ * map_read_data   - Prepare data to be read from CAAM
+ *
+ * @dev             : struct device of the job ring to be used
+ * @size            : The size of data to be prepared
+ * @dma_addr        : The retrieve DMA address of the data to be read
+ * @allocated_data  : Pointer to a DMA-able address where the data
+ *                    to be read will be copied and synchronized
+ *
+ * Return           : '0' on success, error code otherwise
+ */
+static int map_read_data(struct device *dev, size_t size, dma_addr_t *dma_addr,
+			 u8 **allocated_data)
+{
+	int ret = 0;
+
+	/* Allocate memory for data compatible with DMA */
+	*allocated_data = kmalloc(size, GFP_KERNEL | GFP_DMA);
+	if (!*allocated_data) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	*dma_addr = dma_map_single(dev, *allocated_data, size, DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, *dma_addr)) {
+		dev_err(dev, "Unable to map read data\n");
+		ret = -ENOMEM;
+		goto free_alloc;
+	}
+
+	goto exit;
+
+free_alloc:
+	kfree(*allocated_data);
+
+exit:
+	return ret;
+}
+
+/**
+ * read_map_data   - Read the data from CAAM
+ *
+ * @dev             : struct device of the job ring to be used
+ * @data            : The read data from CAAM will be copied here
+ * @dma_addr        : The DMA address of the data to be read
+ * @allocated_data  : Pointer to a DMA-able address where the data
+ *                    to be read is
+ * @size            : The size of data to be read
+ */
+static void read_map_data(struct device *dev, u8 *data, dma_addr_t dma_addr,
+			  u8 *allocated_data, size_t size)
+{
+	/* Synchronize the DMA and copy the data */
+	dma_sync_single_for_cpu(dev, dma_addr, size, DMA_FROM_DEVICE);
+	memcpy(data, allocated_data, size);
+}
+
+/**
+ * unmap_read_write_data - Unmap the data needed for or from CAAM
+ *
+ * @dev             : struct device of the job ring to be used
+ * @dma_addr        : The DMA address of the data used for DMA transfer
+ * @allocated_data  : The data used for DMA transfer
+ * @size            : The size of data
+ * @dir             : The DMA_API direction
+ */
+static void unmap_read_write_data(struct device *dev, dma_addr_t dma_addr,
+				  u8 *allocated_data, size_t size,
+				  enum dma_data_direction dir)
+{
+	/* Free the resources and clear the data*/
+	dma_unmap_single(dev, dma_addr, size, dir);
+	kfree_sensitive(allocated_data);
+}
+
+/**
+ * get_caam_dma_addr - Get the CAAM DMA address of a physical address.
+ *
+ * @phy_address     : The physical address
+ *
+ * Return           : The CAAM DMA address
+ */
+static dma_addr_t get_caam_dma_addr(const void *phy_address)
+{
+	uintptr_t ptr_conv;
+	dma_addr_t caam_dma_address = 0;
+
+	/* Check if conversion is possible */
+	if (sizeof(caam_dma_address) < sizeof(phy_address)) {
+		/*
+		 * Check that all bits sets in the phy_address
+		 * can be stored in caam_dma_address
+		 */
+
+		/* Generate a mask of the representable bits */
+		u64 mask = GENMASK_ULL(sizeof(caam_dma_address) * 8 - 1, 0);
+
+		/*
+		 * Check that the bits not representable of
+		 * the physical address are not set
+		 */
+		if ((uintptr_t)phy_address & ~mask)
+			goto exit;
+	}
+
+	/* Convert address to caam_dma_address */
+	ptr_conv = (uintptr_t)phy_address;
+	caam_dma_address = (dma_addr_t)ptr_conv;
+
+exit:
+	return caam_dma_address;
+}
+
+/**
+ * generate_black_key - Generate a black key from a plaintext or random,
+ *                      based on the given input: a size for a random black
+ *                      key, or a plaintext (input key).
+ *
+ * If the memory type is Secure Memory, the key to cover is read
+ * directly by CAAM from Secure Memory without intermediate copy.
+ * The value of the input key (plaintext) must be a physical address
+ * in Secure Memory.
+ *
+ * Notes:
+ * Limited to Class 1 keys, at the present time.
+ * The input and output data are copied to temporary arrays
+ * except for the input key if the memory type is Secure Memory.
+ * For now, we have support for Black keys, stored in General Memory.
+ *
+ * @dev             : struct device of the job ring to be used
+ * @info            : keyblob_info structure, will be updated with
+ *                    the black key data from CAAM.
+ *                    This contains, also, all the data necessary to generate
+ *                    a black key from plaintext/random like: key encryption
+ *                    key, memory type, input key, etc.
+ *
+ * Return           : '0' on success, error code otherwise
+ */
+int generate_black_key(struct device *dev, struct keyblob_info *info)
+{
+	int ret = 0;
+	bool not_random = false;
+	u8 trusted_key, key_enc;
+	u32 *desc = NULL;
+	size_t black_key_length_req = 0;
+	dma_addr_t black_key_dma;
+	u8 *tmp_black_key = NULL;
+
+	/* Validate device */
+	if (!dev)
+		return -EINVAL;
+
+	/*
+	 * If an input key (plaintext) is given,
+	 * generate a black key from it, not from random
+	 */
+	if (info->key)
+		not_random = true;
+
+	/* Get trusted key and key encryption type from type */
+	trusted_key = (info->type >> TAG_OBJ_TK_OFFSET) & 0x1;
+	key_enc = (info->type >> TAG_OBJ_EKT_OFFSET) & 0x1;
+
+	dev_dbg(dev, "%s input: [key: (%zu) black_key: %p(%zu), key_enc: %x]\n",
+		__func__, info->key_len, info->black_key, info->black_key_len,
+		key_enc);
+	if (not_random)
+		print_hex_dump_debug("input key @" __stringify(__LINE__) ": ",
+				     DUMP_PREFIX_ADDRESS, 16, 4, info->key,
+				     info->key_len, 1);
+
+	/* Validate key type - only JDKEK keys are supported */
+	if (!is_key_type(info->type) || is_trusted_type(info->type))
+		return -EINVAL;
+
+	/*
+	 * Validate key size, expected values are
+	 * between 16 and 64 bytes.
+	 * See TODO from cnstr_desc_black_key().
+	 */
+	if (info->key_len < MIN_KEY_SIZE || info->key_len > MAX_KEY_SIZE)
+		return -EINVAL;
+
+	/*
+	 * Based on key encryption type (ecb or ccm),
+	 * compute the black key size
+	 */
+	if (key_enc == KEY_COVER_ECB)
+		/*
+		 * ECB-Black Key will be padded with zeros to make it a
+		 * multiple of 16 bytes long before it is encrypted,
+		 * and the resulting Black Key will be this length.
+		 */
+		black_key_length_req = ECB_BLACK_KEY_SIZE(info->key_len);
+	else if (key_enc == KEY_COVER_CCM)
+		/*
+		 * CCM-Black Key will always be at least 12 bytes longer,
+		 * since the encapsulation uses a 6-byte nonce and adds
+		 * a 6-byte ICV. But first, the key is padded as necessary so
+		 * that CCM-Black Key is a multiple of 8 bytes long.
+		 */
+		black_key_length_req = CCM_BLACK_KEY_SIZE(info->key_len);
+
+	/* Check if there is enough space for black key */
+	if (info->black_key_len < black_key_length_req) {
+		info->black_key_len = black_key_length_req;
+		return -EINVAL;
+	}
+
+	/* Black key will have at least the same length as the input key */
+	info->black_key_len = info->key_len;
+
+	dev_dbg(dev, "%s processing: [key: (%zu) black_key: %p(%zu)",
+		__func__, info->key_len, info->black_key, info->black_key_len);
+	dev_dbg(dev, "req:%zu, key_enc: 0x%x]\n", black_key_length_req, key_enc);
+
+	/* Map black key, this will be read from CAAM */
+	if (map_read_data(dev, black_key_length_req,
+			  &black_key_dma, &tmp_black_key)) {
+		dev_err(dev, "Unable to map black key\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	/* Construct descriptor for black key */
+	if (not_random)
+		ret = cnstr_desc_black_key(&desc, info->key, info->key_len,
+					   black_key_dma, info->black_key_len,
+					   key_enc, trusted_key);
+	else
+		ret = cnstr_desc_random_black_key(&desc, info->key_len,
+						  black_key_dma,
+						  info->black_key_len,
+						  key_enc, trusted_key);
+
+	if (ret) {
+		dev_err(dev,
+			"Failed to construct the descriptor for black key\n");
+		goto unmap_black_key;
+	}
+
+	/* Execute descriptor and wait for its completion */
+	ret = caam_jr_run_and_wait_for_completion(dev, desc,
+						  caam_key_blob_done);
+	if (ret) {
+		dev_err(dev, "Failed to execute black key descriptor\n");
+		goto free_desc;
+	}
+
+	/* Read black key from CAAM */
+	read_map_data(dev, info->black_key, black_key_dma,
+		      tmp_black_key, black_key_length_req);
+
+	/* Update black key length with the correct size */
+	info->black_key_len = black_key_length_req;
+
+free_desc:
+	kfree(desc);
+
+unmap_black_key:
+	unmap_read_write_data(dev, black_key_dma, tmp_black_key,
+			      black_key_length_req, DMA_FROM_DEVICE);
+
+exit:
+	return ret;
+}
+EXPORT_SYMBOL(generate_black_key);
+
+/**
+ * caam_blob_encap - Encapsulate a black key into a blob
+ *
+ * If the memory type is Secure Memory, the key to encapsulate is read
+ * directly by CAAM from Secure Memory without intermediate copy.
+ * The value of the key (black key) must be a physical address
+ * in Secure Memory.
+ *
+ * Notes:
+ * For now, we have support for Black keys, stored in General Memory and
+ * encapsulated into black blobs.
+ *
+ * @dev             : struct device of the job ring to be used
+ * @info            : keyblob_info structure, will be updated with
+ *                    the blob data from CAAM.
+ *                    This contains, also, all the data necessary to
+ *                    encapsulate a black key into a blob: key encryption
+ *                    key, memory type, color, etc.
+ *
+ * Return           : '0' on success, error code otherwise
+ */
+int caam_blob_encap(struct device *dev, struct keyblob_info *info)
+{
+	int ret = 0;
+	u32 *desc = NULL;
+	size_t black_key_real_len = 0;
+	size_t blob_req_len = 0;
+	u8 mem_type, color, key_enc, trusted_key;
+	dma_addr_t black_key_dma, blob_dma;
+	unsigned char *blob = info->blob;
+	u8 *tmp_black_key = NULL, *tmp_blob = NULL;
+
+	/* Validate device */
+	if (!dev)
+		return -EINVAL;
+
+	/*
+	 * Get memory type, trusted key, key encryption
+	 * type and color from type
+	 */
+	mem_type = (info->type >> TAG_OBJ_MEM_OFFSET) & 0x1;
+	color = (info->type >> TAG_OBJ_COLOR_OFFSET) & 0x1;
+	key_enc = (info->type >> TAG_OBJ_EKT_OFFSET) & 0x1;
+	trusted_key = (info->type >> TAG_OBJ_TK_OFFSET) & 0x1;
+
+	/* Validate input data*/
+	if (!info->key_mod || !blob)
+		return -EINVAL;
+
+	/* Validate object type - only JDKEK keys are supported */
+	if (is_trusted_type(info->type))
+		return -EINVAL;
+
+	dev_dbg(dev, "%s input:[black_key: %p (%zu) color: %x, key_enc: %x",
+		__func__, info->black_key, info->black_key_len, color, key_enc);
+	dev_dbg(dev, ", key_mod: %p (%zu)", info->key_mod, info->key_mod_len);
+	dev_dbg(dev, "blob: %p (%zu)]\n", blob, info->blob_len);
+
+	/*
+	 * Based on memory type, the key modifier length
+	 * can be 8-byte or 16-byte.
+	 */
+	if (mem_type == DATA_SECMEM)
+		info->key_mod_len = KEYMOD_SIZE_SM;
+	else
+		info->key_mod_len = KEYMOD_SIZE_GM;
+
+	/* Adapt the size of the black key */
+	black_key_real_len = info->black_key_len;
+
+	blob_req_len = CCM_BLACK_KEY_SIZE(info->key_len);
+
+	/* Check if the blob can be stored */
+	if (info->blob_len < (blob_req_len + BLOB_OVERHEAD))
+		return -EINVAL;
+
+	/* Update the blob length */
+	info->blob_len = blob_req_len + BLOB_OVERHEAD;
+
+	dev_dbg(dev, "%s processing: [black_key: %p (%zu) cnstr: %zu",
+		__func__, info->black_key, info->black_key_len,
+		black_key_real_len);
+	dev_dbg(dev, " color: %x key_enc: %x, mem_type: %x,",
+		color, key_enc, mem_type);
+	dev_dbg(dev, ", key_mod: %p (%zu) ", info->key_mod, info->key_mod_len);
+	dev_dbg(dev, "blob: %p (%zu)]\n", blob, info->blob_len);
+
+	/* Map black key, this will be transferred to CAAM */
+	if (mem_type == DATA_GENMEM) {
+		if (map_write_data(dev, info->black_key, info->black_key_len,
+				   &black_key_dma, &tmp_black_key)) {
+			dev_err(dev, "Unable to map black key for blob\n");
+			ret = -ENOMEM;
+			goto exit;
+		}
+	} else {
+		black_key_dma = get_caam_dma_addr(info->black_key);
+		if (!black_key_dma)
+			return -ENOMEM;
+	}
+
+	/* Map blob, this will be read to CAAM */
+	if (mem_type == DATA_GENMEM) {
+		if (map_read_data(dev, info->blob_len, &blob_dma, &tmp_blob)) {
+			dev_err(dev, "Unable to map blob\n");
+			ret = -ENOMEM;
+			goto unmap_black_key;
+		}
+	} else {
+		blob_dma = get_caam_dma_addr(info->blob);
+		if (!blob_dma)
+			return -ENOMEM;
+	}
+
+	/* Construct descriptor for blob encapsulation */
+	ret = cnstr_desc_blob_encap(&desc, black_key_dma, info->key_len,
+				    color, key_enc, trusted_key, mem_type,
+				    info->key_mod, info->key_mod_len,
+				    blob_dma, info->blob_len);
+	if (ret) {
+		dev_err(dev,
+			"Failed to construct the descriptor for blob encap\n");
+		goto unmap_blob;
+	}
+
+	/* Execute descriptor and wait for its completion */
+	ret = caam_jr_run_and_wait_for_completion(dev, desc,
+						  caam_key_blob_done);
+	if (ret) {
+		dev_err(dev, "Failed to execute blob encap descriptor\n");
+		goto free_desc;
+	}
+
+	/* Read blob from CAAM */
+	if (mem_type == DATA_GENMEM)
+		read_map_data(dev, blob, blob_dma, tmp_blob, info->blob_len);
+
+	print_hex_dump_debug("blob @" __stringify(__LINE__) ": ",
+			     DUMP_PREFIX_ADDRESS, 16, 4, blob,
+			     info->blob_len, 1);
+free_desc:
+	kfree(desc);
+
+unmap_blob:
+	if (mem_type == DATA_GENMEM)
+		unmap_read_write_data(dev, blob_dma, tmp_blob,
+				      info->blob_len, DMA_FROM_DEVICE);
+
+unmap_black_key:
+	if (mem_type == DATA_GENMEM)
+		unmap_read_write_data(dev, black_key_dma, tmp_black_key,
+				      info->black_key_len, DMA_TO_DEVICE);
+
+exit:
+	return ret;
+}
+EXPORT_SYMBOL(caam_blob_encap);
+
+/**
+ * caam_blob_decap - Decapsulate a black key from a blob
+ *
+ * Notes:
+ * For now, we have support for Black blob, stored in General Memory and
+ * can be decapsulated into a black key.
+ *
+ * @dev             : struct device of the job ring to be used
+ * @info            : keyblob_info structure, will be updated with
+ *                    the black key decapsulated from the blob.
+ *                    This contains, also, all the data necessary to
+ *                    encapsulate a black key into a blob: key encryption
+ *                    key, memory type, color, etc.
+ *
+ * Return           : '0' on success, error code otherwise
+ */
+int caam_blob_decap(struct device *dev, struct keyblob_info *info)
+{
+	int ret = 0;
+	u32 *desc = NULL;
+	u8 mem_type, color, key_enc, trusted_key;
+	size_t black_key_real_len;
+	dma_addr_t black_key_dma, blob_dma;
+	unsigned char *blob = info->blob + TAG_OVERHEAD_SIZE;
+	u8 *tmp_black_key = NULL, *tmp_blob = NULL;
+
+	/* Validate device */
+	if (!dev)
+		return -EINVAL;
+
+	/*
+	 * Get memory type, trusted key, key encryption
+	 * type and color from type
+	 */
+	mem_type = (info->type >> TAG_OBJ_MEM_OFFSET) & 0x1;
+	color = (info->type >> TAG_OBJ_COLOR_OFFSET) & 0x1;
+	key_enc = (info->type >> TAG_OBJ_EKT_OFFSET) & 0x1;
+	trusted_key = (info->type >> TAG_OBJ_TK_OFFSET) & 0x1;
+
+	/* Validate input data*/
+	if (!info->key_mod || !blob)
+		return -EINVAL;
+
+	dev_dbg(dev, "%s input: [blob: %p (%zu), mem_type: %x, color: %x",
+		__func__, blob, info->blob_len, mem_type, color);
+	dev_dbg(dev, " keymod: %p (%zu)", info->key_mod, info->key_mod_len);
+	dev_dbg(dev, " secret: %p (%zu) key_enc: %x]\n",
+		info->black_key, info->black_key_len, key_enc);
+
+	/* Validate object type - only JDKEK keys are supported */
+	if (is_trusted_type(info->type))
+		return -EINVAL;
+
+	print_hex_dump_debug("blob @" __stringify(__LINE__) ": ",
+			     DUMP_PREFIX_ADDRESS, 16, 4, blob,
+			     info->blob_len, 1);
+
+	/*
+	 * Based on memory type, the key modifier length
+	 * can be 8-byte or 16-byte.
+	 */
+	if (mem_type == DATA_SECMEM)
+		info->key_mod_len = KEYMOD_SIZE_SM;
+	else
+		info->key_mod_len = KEYMOD_SIZE_GM;
+
+	/* Check if the blob is valid */
+	if (info->blob_len <= BLOB_OVERHEAD)
+		return -EINVAL;
+
+	/* Initialize black key length */
+	black_key_real_len = info->blob_len - BLOB_OVERHEAD;
+
+	/* Check if the black key has enough space to be stored */
+	if (info->black_key_len < black_key_real_len)
+		return -EINVAL;
+
+	/*
+	 * Based on key encryption type (ecb or ccm),
+	 * compute the black key size
+	 */
+	if (key_enc == KEY_COVER_ECB)
+		/*
+		 * ECB-Black Key will be padded with zeros to make it a
+		 * multiple of 16 bytes long before it is encrypted,
+		 * and the resulting Black Key will be this length.
+		 */
+		black_key_real_len = ECB_BLACK_KEY_SIZE(info->key_len);
+	else if (key_enc == KEY_COVER_CCM)
+		/*
+		 * CCM-Black Key will always be at least 12 bytes longer,
+		 * since the encapsulation uses a 6-byte nonce and adds
+		 * a 6-byte ICV. But first, the key is padded as necessary so
+		 * that CCM-Black Key is a multiple of 8 bytes long.
+		 */
+		black_key_real_len = CCM_BLACK_KEY_SIZE(info->key_len);
+
+	/* Check if there is enough space for black key */
+	if (info->black_key_len < black_key_real_len)
+		return -EINVAL;
+
+	/* Update black key length with the one computed based on key_enc */
+	info->black_key_len = black_key_real_len;
+
+	dev_dbg(dev, "%s processing: [blob: %p (%zu), mem_type: %x, color: %x,",
+		__func__, blob, info->blob_len, mem_type, color);
+	dev_dbg(dev, " key_mod: %p (%zu), black_key: %p (%zu) real_len: %zu]\n",
+		info->key_mod, info->key_mod_len, info->black_key,
+		info->black_key_len, black_key_real_len);
+
+	/* Map blob, this will be transferred to CAAM */
+	if (mem_type == DATA_GENMEM) {
+		if (map_write_data(dev, blob, info->blob_len,
+				   &blob_dma, &tmp_blob)) {
+			dev_err(dev, "Unable to map blob for decap\n");
+			ret = -ENOMEM;
+			goto exit;
+		}
+	} else {
+		blob_dma = get_caam_dma_addr(blob);
+		if (!blob_dma)
+			return -ENOMEM;
+	}
+
+	/* Map black key, this will be read from CAAM */
+	if (mem_type == DATA_GENMEM) {
+		if (map_read_data(dev, info->black_key_len,
+				  &black_key_dma, &tmp_black_key)) {
+			dev_err(dev, "Unable to map black key for blob decap\n");
+			ret = -ENOMEM;
+			goto unmap_blob;
+		}
+	} else {
+		black_key_dma = get_caam_dma_addr(info->black_key);
+		if (!black_key_dma)
+			return -ENOMEM;
+	}
+
+	ret = cnstr_desc_blob_decap(&desc, blob_dma, info->blob_len,
+				    info->key_mod, info->key_mod_len,
+				    black_key_dma, info->key_len,
+				    color, key_enc, trusted_key, mem_type);
+	if (ret) {
+		dev_err(dev,
+			"Failed to construct the descriptor for blob decap\n");
+		goto unmap_black_key;
+	}
+
+	ret = caam_jr_run_and_wait_for_completion(dev, desc,
+						  caam_key_blob_done);
+	if (ret) {
+		dev_err(dev, "Failed to execute blob decap descriptor\n");
+		goto free_desc;
+	}
+
+	/* Read black key from CAAM */
+	if (mem_type == DATA_GENMEM)
+		read_map_data(dev, info->black_key, black_key_dma,
+			      tmp_black_key, info->black_key_len);
+
+free_desc:
+	kfree(desc);
+
+unmap_black_key:
+	if (mem_type == DATA_GENMEM)
+		unmap_read_write_data(dev, black_key_dma, tmp_black_key,
+				      info->black_key_len, DMA_FROM_DEVICE);
+
+unmap_blob:
+	if (mem_type == DATA_GENMEM)
+		unmap_read_write_data(dev, blob_dma, tmp_blob,
+				      info->blob_len, DMA_TO_DEVICE);
+
+exit:
+	return ret;
+}
+EXPORT_SYMBOL(caam_blob_decap);
diff --git a/drivers/crypto/caam/caamkeyblob.h b/drivers/crypto/caam/caamkeyblob.h
new file mode 100644
index 000000000..495b74c29
--- /dev/null
+++ b/drivers/crypto/caam/caamkeyblob.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Black key generation and blob encapsulation/decapsualtion for CAAM
+ *
+ * Copyright 2018-2020 NXP
+ */
+
+#ifndef _CAAMKEYBLOB_H_
+#define _CAAMKEYBLOB_H_
+
+#include <linux/device.h>
+#include "caamkeyblob_desc.h"
+
+/*
+ * Minimum key size to be used is 16 bytes and maximum key size fixed
+ * is 64 bytes.
+ * Blob size to be kept is Maximum key size + tag object header added by CAAM.
+ */
+
+#define MIN_KEY_SIZE			16
+#define MAX_KEY_SIZE			64
+
+#define MAX_BLACK_KEY_SIZE		(MAX_KEY_SIZE + CCM_OVERHEAD +\
+					TAG_OVERHEAD_SIZE)
+
+/*
+ * For blobs a randomly-generated, 256-bit blob key is used to
+ * encrypt the data using the AES-CCM cryptographic algorithm.
+ * Therefore, blob size is max key size, CCM_OVERHEAD, blob header
+ * added by CAAM and the tagged object header size.
+ */
+#define MAX_BLOB_SIZE			(MAX_KEY_SIZE + CCM_OVERHEAD +\
+					BLOB_OVERHEAD + TAG_OVERHEAD_SIZE)
+
+/* Key modifier for CAAM blobs, used as a revision number */
+static const char caam_key_modifier[KEYMOD_SIZE_GM] = {
+		'C', 'A', 'A', 'M', '_', 'K', 'E', 'Y',
+		'_', 'T', 'Y', 'P', 'E', '_', 'V', '1',
+};
+
+/**
+ * struct keyblob_info - Structure that contains all the data necessary
+ *                       to generate a black key and encapsulate it into a blob
+ *
+ * @key                : The plaintext used as input key
+ *                       for black key generation
+ * @key_len            : Size of plaintext or size of key in case of
+ *                       black key generated from random
+ * @type               : The type of data contained (e.g. black key, blob, etc.)
+ * @black_key_len      : Length of the generated black key
+ * @black_key          : Black key data obtained from CAAM
+ * @blob_len           : Length of the blob that encapsulates the black key
+ * @blob               : Blob data obtained from CAAM
+ * @key_modifier_len   : 8-byte or 16-byte Key_Modifier based on general or
+ *                       secure memory blob type
+ * @key_modifier       : can be either a secret value, or used as a revision
+ *                       number, revision date or nonce
+ *                       In this case is used as a revision number.
+ */
+struct keyblob_info {
+	char *key;
+	size_t key_len;
+
+	u32 type;
+
+	size_t black_key_len;
+	unsigned char black_key[MAX_BLACK_KEY_SIZE];
+
+	size_t blob_len;
+	unsigned char blob[MAX_BLOB_SIZE];
+
+	size_t key_mod_len;
+	const void *key_mod;
+};
+
+int generate_black_key(struct device *dev, struct keyblob_info *info);
+
+int caam_blob_encap(struct device *dev, struct keyblob_info *info);
+
+int caam_blob_decap(struct device *dev, struct keyblob_info *info);
+
+#endif /* _CAAMKEYBLOB_H_ */
diff --git a/drivers/crypto/caam/caamkeyblob_desc.c b/drivers/crypto/caam/caamkeyblob_desc.c
new file mode 100644
index 000000000..628873167
--- /dev/null
+++ b/drivers/crypto/caam/caamkeyblob_desc.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Shared descriptors for CAAM black key
+ * and blob encapsulation/decapsulation
+ *
+ * Copyright 2018-2020 NXP
+ */
+#include "caamkeyblob_desc.h"
+
+/* Size of tmp buffer for descriptor const. */
+#define INITIAL_DESCSZ 64
+
+/*
+ * Construct a black key conversion job descriptor
+ *
+ * This function constructs a job descriptor capable of performing
+ * a key blackening operation on a plaintext secure memory resident object.
+ *
+ * @desc          : Pointer to a pointer to the descriptor generated by this
+ *                  function. Caller will be responsible to kfree() this
+ *                  descriptor after execution.
+ * @key           : Pointer to the plaintext, which will also hold
+ *                  the result. Since encryption occurs in place, caller must
+ *                  ensure that the space is large enough to accommodate the
+ *                  blackened key
+ * @key_len       : Size of the plaintext
+ * @black_key     : DMA address of the black key obtained from hardware
+ * @black_key_len : Size of the black key
+ * @key_enc       : Encrypted Key Type (AES-ECB or AES-CCM)
+ * @trusted_key   : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK)
+ *                  or Trusted Descriptor Key Encryption Key (TDKEK) to
+ *                  decrypt the key to be loaded into a Key Register).
+ *
+ * Return         : '0' on success, error code otherwise
+ */
+int cnstr_desc_black_key(u32 **desc, char *key, size_t key_len,
+			 dma_addr_t black_key, size_t black_key_len,
+			 u8 key_enc, u8 trusted_key)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize;
+	u32 bk_store;
+	u32 key_length_for_desc = key_len;
+
+	/* Trusted key not supported */
+	if (trusted_key != UNTRUSTED_KEY)
+		return -EOPNOTSUPP;
+
+	memset(tmpdesc, 0, sizeof(tmpdesc));
+
+	init_job_desc(tmpdesc, 0);
+
+	/*
+	 * KEY commands seems limited to 32 bytes, so we should use the load
+	 * command instead which can load up to 64 bytes.
+	 * The size must also be loaded.
+	 *
+	 * TODO: The KEY command indicate it should be able to load key bigger
+	 * than 32bytes but it doesn't work in practice
+	 *
+	 * TODO: The LOAD command indicate it should be able to load up to 96
+	 * byte keys it doesn't work in practice and is limited to 64 bytes
+	 */
+
+	/* Load key to class 1 key register */
+	append_load_as_imm(tmpdesc, (void *)key, key_length_for_desc,
+			   LDST_CLASS_1_CCB | LDST_SRCDST_BYTE_KEY);
+
+	/* Load the size of the key */
+	append_load_imm_u32(tmpdesc, key_length_for_desc, LDST_CLASS_1_CCB |
+			    LDST_IMM | LDST_SRCDST_WORD_KEYSZ_REG);
+
+	/* ...and write back out via FIFO store*/
+	bk_store = CLASS_1;
+	if (key_enc == KEY_COVER_ECB)
+		bk_store |= FIFOST_TYPE_KEY_KEK;
+	else
+		bk_store |= FIFOST_TYPE_KEY_CCM_JKEK;
+
+	/* Save the key as black key in memory */
+	append_fifo_store(tmpdesc, black_key, black_key_len, bk_store);
+
+	dsize = desc_bytes(&tmpdesc);
+
+	/* Now allocate execution buffer and coat it with executable */
+	tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA);
+	if (!tdesc)
+		return -ENOMEM;
+
+	*desc = tdesc;
+
+	print_hex_dump_debug("black key desc@" __stringify(__LINE__) ":",
+			     DUMP_PREFIX_ADDRESS, 16, 4, *desc,
+			     desc_bytes(*desc), 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(cnstr_desc_black_key);
+
+/*
+ * Construct a black key using RNG job descriptor
+ *
+ * This function constructs a job descriptor capable of performing
+ * a key blackening operation on RNG generated.
+ *
+ * @desc          : Pointer to a pointer to the descriptor generated by this
+ *                  function. Caller will be responsible to kfree() this
+ *                  descriptor after execution.
+ * @key_len       : Size of the random plaintext
+ * @black_key     : DMA address of the black key obtained from hardware
+ * @black_key_len : Size of the black key
+ * @key_enc       : Encrypted Key Type (AES-ECB or AES-CCM)
+ * @trusted_key   : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK)
+ *                  or Trusted Descriptor Key Encryption Key (TDKEK) to
+ *                  decrypt the key to be loaded into a Key Register).
+ *
+ * Return         : '0' on success, error code otherwise
+ */
+int cnstr_desc_random_black_key(u32 **desc, size_t key_len,
+				dma_addr_t black_key, size_t black_key_len,
+				u8 key_enc, u8 trusted_key)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize;
+	u32 bk_store;
+
+	memset(tmpdesc, 0, sizeof(tmpdesc));
+
+	init_job_desc(tmpdesc, 0);
+
+	/* Prepare RNG */
+	append_operation(tmpdesc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG);
+
+	/* Generate RNG and left it in output data fifo */
+	append_cmd(tmpdesc, CMD_FIFO_STORE | FIFOST_TYPE_RNGFIFO | key_len);
+
+	/* Copy RNG from outfifo to class 1 Key register */
+	append_move(tmpdesc, MOVE_SRC_OUTFIFO | MOVE_DEST_CLASS1KEY |
+			MOVE_WAITCOMP | (key_len & MOVE_LEN_MASK));
+
+	/* Write the size of the key moved */
+	append_load_imm_u32(tmpdesc, key_len, LDST_CLASS_1_CCB |
+			    LDST_SRCDST_WORD_KEYSZ_REG | LDST_IMM);
+
+	bk_store = CLASS_1;
+	if (key_enc == KEY_COVER_ECB)
+		bk_store |= FIFOST_TYPE_KEY_KEK;
+	else
+		bk_store |= FIFOST_TYPE_KEY_CCM_JKEK;
+
+	/* Fifo store to save the key as black key in memory */
+	append_fifo_store(tmpdesc, black_key, black_key_len, bk_store);
+
+	dsize = desc_bytes(&tmpdesc);
+
+	/* Now allocate execution buffer and coat it with executable */
+	tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA);
+	if (!tdesc)
+		return -ENOMEM;
+
+	*desc = tdesc;
+
+	print_hex_dump_debug("black key random desc@" __stringify(__LINE__) ":",
+			     DUMP_PREFIX_ADDRESS, 16, 4, *desc,
+			     desc_bytes(*desc), 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(cnstr_desc_random_black_key);
+
+/*
+ * Construct a blob encapsulation job descriptor
+ *
+ * This function dynamically constructs a blob encapsulation job descriptor
+ * from the following arguments:
+ *
+ * @desc          : Pointer to a pointer to the descriptor generated by this
+ *                  function. Caller will be responsible to kfree() this
+ *                  descriptor after execution.
+ * @black_key     : Physical pointer to a secret, normally a black or red key,
+ *                  possibly residing within an accessible secure memory page,
+ *                  of the secret to be encapsulated to an output blob.
+ * @black_key_len : Size of input secret, in bytes. This is limited to 65536
+ *                  less the size of blob overhead, since the length embeds
+ *                  into DECO pointer in/out instructions.
+ * @keycolor      : Determines if the source data is covered (black key) or
+ *                  plaintext (red key). RED_KEY or BLACK_KEY are defined in
+ *                  for this purpose.
+ * @key_enc       : If BLACK_KEY source is covered via AES-CCM, specify
+ *                  KEY_COVER_CCM, else uses AES-ECB (KEY_COVER_ECB).
+ * @trusted_key   : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK)
+ *                  or Trusted Descriptor Key Encryption Key (TDKEK) to
+ *                  decrypt the key to be loaded into a Key Register).
+ * @mem_type      : Determine if encapsulated blob should be a secure memory
+ *                  blob (DATA_SECMEM), with partition data embedded with key
+ *                  material, or a general memory blob (DATA_GENMEM).
+ * @key_mod       : Pointer to a key modifier, which must reside in a
+ *                  contiguous piece of memory. Modifier will be assumed to be
+ *                  8 bytes long for a blob of type DATA_SECMEM, or 16 bytes
+ *                  long for a blob of type DATA_GENMEM
+ * @key_mod_len   : Modifier length is 8 bytes long for a blob of type
+ *                  DATA_SECMEM, or 16 bytes long for a blob of type DATA_GENMEM
+ * @blob          : Physical pointer to the destination buffer to receive the
+ *                  encapsulated output. This buffer will need to be 48 bytes
+ *                  larger than the input because of the added encapsulation
+ *                  data. The generated descriptor will account for the
+ *                  increase in size, but the caller must also account for
+ *                  this increase in the buffer allocator.
+ * @blob_len      : Size of the destination buffer to receive the
+ *                  encapsulated output.
+ * Return         : '0' on success, error code otherwise
+ *
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable blob
+ * suitable for use across power cycles.
+ *
+ * This is an example of a black key encapsulation job into a general memory
+ * blob. Notice the 16-byte key modifier in the LOAD instruction. Also note
+ * the output 48 bytes longer than the input:
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400010           ld: ccb2-key len=16 offs=0
+ * [02] 08144891               ptr->@0x08144891
+ * [03] F800003A    seqoutptr: len=58
+ * [04] 01000000               out_ptr->@0x01000000
+ * [05] F000000A     seqinptr: len=10
+ * [06] 09745090               in_ptr->@0x09745090
+ * [07] 870D0004    operation: encap blob  reg=memory, black, format=normal
+ *
+ * This is an example of a red key encapsulation job for storing a red key
+ * into a secure memory blob. Note the 8 byte modifier on the 12 byte offset
+ * in the LOAD instruction; this accounts for blob permission storage:
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400C08           ld: ccb2-key len=8 offs=12
+ * [02] 087D0784               ptr->@0x087d0784
+ * [03] F8000050    seqoutptr: len=80
+ * [04] 09251BB2               out_ptr->@0x09251bb2
+ * [05] F0000020     seqinptr: len=32
+ * [06] 40000F31               in_ptr->@0x40000f31
+ * [07] 870D0008    operation: encap blob  reg=memory, red, sec_mem,
+ *                             format=normal
+ */
+int cnstr_desc_blob_encap(u32 **desc, dma_addr_t black_key,
+			  size_t key_len, u8 keycolor, u8 key_enc,
+			  u8 trusted_key, u8 mem_type, const void *key_mod,
+			  size_t key_mod_len, dma_addr_t blob, size_t blob_len)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize;
+	u32 bk_store;
+
+	/* Trusted key not supported */
+	if (trusted_key != UNTRUSTED_KEY)
+		return -EOPNOTSUPP;
+
+	memset(tmpdesc, 0, sizeof(tmpdesc));
+
+	init_job_desc(tmpdesc, 0);
+
+	/*
+	 * Key modifier works differently for secure/general memory blobs
+	 * This accounts for the permission/protection data encapsulated
+	 * within the blob if a secure memory blob is requested
+	 */
+	if (mem_type == DATA_SECMEM)
+		append_load_as_imm(tmpdesc, key_mod, key_mod_len,
+				   LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY |
+				   ((12 << LDST_OFFSET_SHIFT) &
+				    LDST_OFFSET_MASK));
+	else /* is general memory blob */
+		append_load_as_imm(tmpdesc, key_mod, key_mod_len,
+				   LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY);
+
+	/* Input data, should be somewhere in secure memory */
+	append_seq_in_ptr_intlen(tmpdesc, black_key, key_len, 0);
+
+	/*
+	 * Encapsulation output must include space for blob key encryption
+	 * key and MAC tag
+	 */
+	append_seq_out_ptr_intlen(tmpdesc, blob, CCM_BLACK_KEY_SIZE(key_len) +
+				  BLOB_OVERHEAD, 0);
+
+	bk_store = OP_PCLID_BLOB;
+	if (mem_type == DATA_SECMEM)
+		bk_store |= OP_PCL_BLOB_PTXT_SECMEM;
+
+	if (key_enc == KEY_COVER_CCM)
+		bk_store |= OP_PCL_BLOB_EKT;
+
+	/* An input black key cannot be stored in a red blob */
+	if (keycolor == BLACK_KEY)
+		bk_store |= OP_PCL_BLOB_BLACK;
+
+	/* Set blob encap, then color */
+	append_operation(tmpdesc, OP_TYPE_ENCAP_PROTOCOL | bk_store);
+
+	dsize = desc_bytes(&tmpdesc);
+
+	tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA);
+	if (!tdesc)
+		return -ENOMEM;
+
+	*desc = tdesc;
+
+	print_hex_dump_debug("blob encap desc@" __stringify(__LINE__) ":",
+			     DUMP_PREFIX_ADDRESS, 16, 4, *desc,
+			     desc_bytes(*desc), 1);
+	return 0;
+}
+EXPORT_SYMBOL(cnstr_desc_blob_encap);
+
+/*
+ * Construct a blob decapsulation job descriptor
+ *
+ * This function dynamically constructs a blob decapsulation job descriptor
+ * from the following arguments:
+ *
+ * @desc          : Pointer to a pointer to the descriptor generated by this
+ *                  function. Caller will be responsible to kfree() this
+ *                  descriptor after execution.
+ * @blob          : Physical pointer (into external memory) of the blob to
+ *                  be decapsulated. Blob must reside in a contiguous memory
+ *                  segment.
+ * @blob_len      : Size of the blob buffer to be decapsulated.
+ * @key_mod       : Pointer to a key modifier, which must reside in a
+ *                  contiguous piece of memory. Modifier will be assumed to be
+ *                  8 bytes long for a blob of type DATA_SECMEM, or 16 bytes
+ *                  long for a blob of type DATA_GENMEM
+ * @key_mod_len   : Modifier length is 8 bytes long for a blob of type
+ *                  DATA_SECMEM, or 16 bytes long for a blob of type DATA_GENMEM
+ * @black_key     : Physical pointer of the decapsulated output, possibly into
+ *                  a location within a secure memory page. Must be contiguous.
+ * @black_key_len : Size of encapsulated secret in bytes (not the size of the
+ *                  input blob).
+ * @keycolor      : Determines if the source data is covered (black key) or
+ *                  plaintext (red key). RED_KEY or BLACK_KEY are defined in
+ *                  for this purpose.
+ * @key_enc       : If BLACK_KEY source is covered via AES-CCM, specify
+ *                  KEY_COVER_CCM, else uses AES-ECB (KEY_COVER_ECB).
+ * @trusted_key   : Trusted Key (use Job Descriptor Key Encryption Key (JDKEK)
+ *                  or Trusted Descriptor Key Encryption Key (TDKEK) to
+ *                  decrypt the key to be loaded into a Key Register).
+ * @mem_type      : Determine if encapsulated blob should be a secure memory
+ *                  blob (DATA_SECMEM), with partition data embedded with key
+ *                  material, or a general memory blob (DATA_GENMEM).
+ * Return         : '0' on success, error code otherwise
+ *
+ * Upon completion, desc points to a buffer containing a CAAM job descriptor
+ * that decapsulates a key blob from external memory into a black (encrypted)
+ * key or red (plaintext) content.
+ *
+ * This is an example of a black key decapsulation job from a general memory
+ * blob. Notice the 16-byte key modifier in the LOAD instruction.
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400010           ld: ccb2-key len=16 offs=0
+ * [02] 08A63B7F               ptr->@0x08a63b7f
+ * [03] F8000010    seqoutptr: len=16
+ * [04] 01000000               out_ptr->@0x01000000
+ * [05] F000003A     seqinptr: len=58
+ * [06] 01000010               in_ptr->@0x01000010
+ * [07] 860D0004    operation: decap blob  reg=memory, black, format=normal
+ *
+ * This is an example of a red key decapsulation job for restoring a red key
+ * from a secure memory blob. Note the 8 byte modifier on the 12 byte offset
+ * in the LOAD instruction:
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400C08           ld: ccb2-key len=8 offs=12
+ * [02] 01000000               ptr->@0x01000000
+ * [03] F8000020    seqoutptr: len=32
+ * [04] 400000E6               out_ptr->@0x400000e6
+ * [05] F0000050     seqinptr: len=80
+ * [06] 08F0C0EA               in_ptr->@0x08f0c0ea
+ * [07] 860D0008    operation: decap blob  reg=memory, red, sec_mem,
+ *			       format=normal
+ */
+int cnstr_desc_blob_decap(u32 **desc, dma_addr_t blob, size_t blob_len,
+			  const void *key_mod, size_t key_mod_len,
+			  dma_addr_t black_key, size_t plaintext_len,
+			  u8 keycolor, u8 key_enc, u8 trusted_key, u8 mem_type)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize;
+	u32 bk_store;
+
+	/* Trusted key not supported */
+	if (trusted_key != UNTRUSTED_KEY)
+		return -EOPNOTSUPP;
+
+	memset(tmpdesc, 0, sizeof(tmpdesc));
+
+	init_job_desc(tmpdesc, 0);
+
+	/* Load key modifier */
+	if (mem_type == DATA_SECMEM)
+		append_load_as_imm(tmpdesc, key_mod, key_mod_len,
+				   LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY |
+				   ((12 << LDST_OFFSET_SHIFT) &
+				    LDST_OFFSET_MASK));
+	else /* is general memory blob */
+		append_load_as_imm(tmpdesc, key_mod, key_mod_len,
+				   LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY);
+
+	/* Compensate blob header + MAC tag over size of encapsulated secret */
+	append_seq_in_ptr_intlen(tmpdesc, blob, plaintext_len + BLOB_OVERHEAD,
+				 0);
+
+	append_seq_out_ptr_intlen(tmpdesc, black_key, plaintext_len, 0);
+
+	/* Decapsulate from secure memory partition to black blob */
+	bk_store = OP_PCLID_BLOB;
+	if (mem_type == DATA_SECMEM)
+		bk_store |= OP_PCL_BLOB_PTXT_SECMEM;
+
+	if (key_enc == KEY_COVER_CCM)
+		bk_store |= OP_PCL_BLOB_EKT;
+
+	/* An input black key cannot be stored in a red blob */
+	if (keycolor == BLACK_KEY)
+		bk_store |= OP_PCL_BLOB_BLACK;
+
+	/* Set blob encap, then color */
+	append_operation(tmpdesc, OP_TYPE_DECAP_PROTOCOL | bk_store);
+
+	dsize = desc_bytes(&tmpdesc);
+
+	tdesc = kmemdup(tmpdesc, dsize, GFP_KERNEL | GFP_DMA);
+	if (!tdesc)
+		return -ENOMEM;
+
+	*desc = tdesc;
+
+	print_hex_dump_debug("blob decap desc@" __stringify(__LINE__) ":",
+			     DUMP_PREFIX_ADDRESS, 16, 4, *desc,
+			     desc_bytes(*desc), 1);
+
+	return 0;
+}
+EXPORT_SYMBOL(cnstr_desc_blob_decap);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("NXP CAAM Black Key and Blob descriptors");
+MODULE_AUTHOR("NXP Semiconductors");
diff --git a/drivers/crypto/caam/caamkeyblob_desc.h b/drivers/crypto/caam/caamkeyblob_desc.h
new file mode 100644
index 000000000..0affbb184
--- /dev/null
+++ b/drivers/crypto/caam/caamkeyblob_desc.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Shared descriptors for CAAM black key and blob
+ *
+ * Copyright 2018-2020 NXP
+ */
+
+#ifndef _CAAMKEYBLOB_DESC_H_
+#define _CAAMKEYBLOB_DESC_H_
+
+#include <linux/types.h>
+
+#include "jr.h"
+#include "regs.h"
+#include "desc.h"
+
+#include "compat.h"
+#include "tag_object.h"
+#include "desc_constr.h"
+
+/* Defines for secure memory and general memory blobs */
+#define DATA_GENMEM 0
+#define DATA_SECMEM 1
+
+/* Encrypted key */
+#define BLACK_KEY 1
+
+/* Define key encryption/covering options */
+#define KEY_COVER_ECB 0	/* cover key in AES-ECB */
+#define KEY_COVER_CCM 1 /* cover key with AES-CCM */
+
+/* Define the trust in the key, to select either JDKEK or TDKEK */
+#define UNTRUSTED_KEY 0
+#define TRUSTED_KEY 1
+
+/* Define space required for BKEK + MAC tag storage in any blob */
+#define BLOB_OVERHEAD (32 + 16)
+
+#define PAD_16_BYTE(_key_size) (roundup(_key_size, 16))
+#define PAD_8_BYTE(_key_size) (roundup(_key_size, 8))
+
+/*
+ * ECB-Black Key will be padded with zeros to make it a
+ * multiple of 16 bytes long before it is encrypted,
+ * and the resulting Black Key will be this length.
+ */
+#define ECB_BLACK_KEY_SIZE(_key_size) (PAD_16_BYTE(_key_size))
+
+/*
+ * CCM-Black Key will always be at least 12 bytes longer,
+ * since the encapsulation uses a 6-byte nonce and adds
+ * a 6-byte ICV. But first, the key is padded as necessary so
+ * that CCM-Black Key is a multiple of 8 bytes long.
+ */
+#define NONCE_SIZE 6
+#define ICV_SIZE 6
+#define CCM_OVERHEAD (NONCE_SIZE + ICV_SIZE)
+#define CCM_BLACK_KEY_SIZE(_key_size) (PAD_8_BYTE(_key_size) \
+							+ CCM_OVERHEAD)
+
+static inline int secret_size_in_ccm_black_key(int key_size)
+{
+	return ((key_size >= CCM_OVERHEAD) ? key_size - CCM_OVERHEAD : 0);
+}
+
+#define SECRET_SIZE_IN_CCM_BLACK_KEY(_key_size) \
+	secret_size_in_ccm_black_key(_key_size)
+
+/* A red key is not encrypted so its size is the same */
+#define RED_KEY_SIZE(_key_size) (_key_size)
+
+/*
+ * Based on memory type, the key modifier length
+ * can be either 8-byte or 16-byte.
+ */
+#define KEYMOD_SIZE_SM 8
+#define KEYMOD_SIZE_GM 16
+
+/* Create job descriptor to cover key */
+int cnstr_desc_black_key(u32 **desc, char *key, size_t key_len,
+			 dma_addr_t black_key, size_t black_key_len,
+			 u8 key_enc, u8 trusted_key);
+
+/* Create job descriptor to generate a random key and cover it */
+int cnstr_desc_random_black_key(u32 **desc, size_t key_len,
+				dma_addr_t black_key, size_t black_key_len,
+				u8 key_enc, u8 trusted_key);
+
+/* Encapsulate data in a blob */
+int cnstr_desc_blob_encap(u32 **desc, dma_addr_t black_key,
+			  size_t black_key_len, u8 color, u8 key_enc,
+			  u8 trusted_key, u8 mem_type, const void *key_mod,
+			  size_t key_mod_len, dma_addr_t blob, size_t blob_len);
+
+/* Decapsulate data from a blob */
+int cnstr_desc_blob_decap(u32 **desc, dma_addr_t blob, size_t blob_len,
+			  const void *key_mod, size_t key_mod_len,
+			  dma_addr_t black_key, size_t black_key_len,
+			  u8 keycolor, u8 key_enc, u8 trusted_key, u8 mem_type);
+
+#endif /* _CAAMKEYBLOB_DESC_H_ */
diff --git a/drivers/crypto/caam/caamkeygen.c b/drivers/crypto/caam/caamkeygen.c
new file mode 100644
index 000000000..9d4d909a6
--- /dev/null
+++ b/drivers/crypto/caam/caamkeygen.c
@@ -0,0 +1,630 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/ioctl.h>
+
+#include "caamkeyblob.h"
+#include "intern.h"
+#include <linux/caam_keygen.h>
+
+#define DEVICE_NAME "caam-keygen"
+
+static long caam_keygen_ioctl(struct file *file, unsigned int cmd,
+			      unsigned long arg);
+
+/**
+ * tag_black_obj      - Tag a black object (key/blob) with a tag object header.
+ *
+ * @info              : keyblob_info structure, which contains
+ *                      the black key/blob, obtained from CAAM,
+ *                      that needs to be tagged
+ * @black_max_len     : The maximum size of the black object (blob/key)
+ * @blob              : Used to determine if it's a blob or key object
+ *
+ * Return             : '0' on success, error code otherwise
+ */
+static int tag_black_obj(struct keyblob_info *info, size_t black_max_len,
+			 bool blob)
+{
+	struct header_conf tag;
+	u32 type;
+	int ret;
+	u32 size_tagged = black_max_len;
+
+	if (!info)
+		return -EINVAL;
+
+	type = info->type;
+
+	/* Prepare and set the tag */
+	if (blob) {
+		init_tag_object_header(&tag, 0, type, info->key_len,
+				       info->blob_len);
+		ret = set_tag_object_header_conf(&tag, info->blob,
+						 info->blob_len,
+						 &size_tagged);
+	} else {
+		init_tag_object_header(&tag, 0, type, info->key_len,
+				       info->black_key_len);
+		ret = set_tag_object_header_conf(&tag, info->black_key,
+						 info->black_key_len,
+						 &size_tagged);
+	}
+	if (ret)
+		return ret;
+
+	/* Update the size of the black key tagged */
+	if (blob)
+		info->blob_len = size_tagged;
+	else
+		info->black_key_len = size_tagged;
+
+	return ret;
+}
+
+/**
+ * send_err_msg      - Send the error message from kernel to user-space
+ *
+ * @msg              : The message to be sent
+ * @output           : The output buffer where we want to copy the error msg
+ * @size             : The size of output buffer
+ */
+static void send_err_msg(char *msg, void __user *output, size_t size)
+{
+	size_t min_s;
+	char null_ch = 0;
+
+	/* Not enough space to copy any message */
+	if (size <= 1)
+		return;
+
+	min_s = min(size - 1, strlen(msg));
+	/*
+	 * Avoid compile and checkpatch warnings, since we don't
+	 * care about return value from copy_to_user
+	 */
+	(void)(copy_to_user(output, msg, min_s) + 1);
+	/* Copy null terminator */
+	(void)(copy_to_user((output + min_s), &null_ch, 1) + 1);
+}
+
+/**
+ * validate_key_size - Validate the key size from user.
+ *                     This can be the exact size given by user when
+ *                     generating a black key from random (with -s),
+ *                     or the size of the plaintext (with -t).
+ *
+ * @key_len          : The size of key we want to validate
+ * @output           : The output buffer where we want to copy the error msg
+ * @size             : The size of output buffer
+ *
+ *Return             : '0' on success, error code otherwise
+ */
+static int validate_key_size(size_t key_len, void __user *output, size_t size)
+{
+	char *msg = NULL;
+
+	if (key_len < MIN_KEY_SIZE || key_len > MAX_KEY_SIZE) {
+		msg = "Invalid key size, expected values are between 16 and 64 bytes.\n";
+		send_err_msg(msg, output, size);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * validate_input    - Validate the input from user and set the
+ *                     keyblob_info structure.
+ *                     This contains the input key in case of black key
+ *                     generated from plaintext or size for random
+ *                     black key.
+ *
+ * @key_crt          : Structure with data from user
+ * @arg              : User-space argument from ioctl call
+ * @info             : keyblob_info structure, will be updated with all the
+ *                     data from user-space
+ * @create_key_op    : Used to determine if it's a create or import operation
+ *
+ * Return            : '0' on success, error code otherwise
+ */
+static int validate_input(struct caam_keygen_cmd *key_crt, unsigned long arg,
+			  struct keyblob_info *info, bool create_key_op)
+{
+	char *tmp, *msg;
+	size_t tmp_size;
+	bool random = false;
+	int ret = 0;
+	u32 tmp_len = 0;
+	char null_ch = 0;
+
+	/*
+	 * So far, we only support Black keys, encrypted with JDKEK,
+	 * kept in general memory, non-secure state.
+	 * Therefore, default value for type is 1.
+	 */
+	u32 type = 1;
+
+	if (copy_from_user(key_crt, (void __user *)arg,
+			   sizeof(struct caam_keygen_cmd)))
+		return -EFAULT;
+
+	/* Get blob_len from user. */
+	info->blob_len = key_crt->blob_len;
+	/* Get black_key_len from user. */
+	info->black_key_len = key_crt->black_key_len;
+
+	/*
+	 * Based on operation type validate a different set of input data.
+	 *
+	 * For key creation, validate the Encrypted Key Type,
+	 * the Key Mode and Key Value
+	 */
+	if (create_key_op) {
+		/*
+		 * Validate arguments received from user.
+		 * These must be at least 1 since
+		 * they have null terminator.
+		 */
+		if (key_crt->key_enc_len < 1 || key_crt->key_mode_len < 1 ||
+		    key_crt->key_value_len < 1) {
+			msg = "Invalid arguments.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			return -EFAULT;
+		}
+		/*
+		 * Allocate memory for temporary buffer used to
+		 * get the user arguments from user-space
+		 */
+		tmp_size = max_t(size_t, key_crt->key_enc_len,
+				 max_t(size_t, key_crt->key_mode_len,
+				       key_crt->key_value_len)) + 1;
+		tmp = kmalloc(tmp_size, GFP_KERNEL);
+		if (!tmp) {
+			msg = "Unable to allocate memory for temporary buffer.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			return -ENOMEM;
+		}
+		/* Add null terminator */
+		tmp[tmp_size - 1] = null_ch;
+		/*
+		 * Validate and set, in type, the Encrypted Key Type
+		 * given from user-space.
+		 * This must be ecb or ccm.
+		 */
+		if (copy_from_user(tmp, u64_to_user_ptr(key_crt->key_enc),
+				   key_crt->key_enc_len)) {
+			msg = "Unable to copy from user the Encrypted Key Type.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			ret = -EFAULT;
+			goto free_resource;
+		}
+		if (!strcmp(tmp, "ccm")) {
+			type |= BIT(TAG_OBJ_EKT_OFFSET);
+		} else if (strcmp(tmp, "ecb")) {
+			msg = "Invalid argument for Encrypted Key Type, expected ecb or ccm.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			ret = -EINVAL;
+			goto free_resource;
+		}
+		/*
+		 * Validate the Key Mode given from user-space.
+		 * This must be -t (text), for a black key generated
+		 * from a plaintext, or -s (size) for a black key
+		 * generated from random.
+		 */
+		if (copy_from_user(tmp, u64_to_user_ptr(key_crt->key_mode),
+				   key_crt->key_mode_len)) {
+			msg = "Unable to copy from user the Key Mode: random (-s) or plaintext (-t).\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			ret = -EFAULT;
+			goto free_resource;
+		}
+		if (!strcmp(tmp, "-s")) {
+			random = true; /* black key generated from random */
+		} else if (strcmp(tmp, "-t")) {
+			msg = "Invalid argument for Key Mode, expected -s or -t.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			ret = -EINVAL;
+			goto free_resource;
+		}
+		/*
+		 * Validate and set, into keyblob_info structure,
+		 * the plaintext or key size, based on Key Mode.
+		 */
+		if (copy_from_user(tmp, u64_to_user_ptr(key_crt->key_value),
+				   key_crt->key_value_len)) {
+			msg = "Unable to copy from user the Key Value: size or plaintext.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			ret = -EFAULT;
+			goto free_resource;
+		}
+		/* Black key generated from random, get its size */
+		if (random) {
+			info->key = NULL;
+			ret = kstrtou32(tmp, 10, &tmp_len);
+			if (ret != 0) {
+				msg = "Invalid key size.\n";
+				send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+					     key_crt->blob_len);
+				goto free_resource;
+			}
+			ret = validate_key_size(tmp_len,
+						u64_to_user_ptr(key_crt->blob),
+						key_crt->blob_len);
+			if (ret)
+				goto free_resource;
+
+			info->key_len = tmp_len;
+		} else {
+			/*
+			 * Black key generated from plaintext,
+			 * get the plaintext (input key) and its size
+			 */
+			ret = validate_key_size(strlen(tmp),
+						u64_to_user_ptr(key_crt->blob),
+						key_crt->blob_len);
+			if (ret)
+				goto free_resource;
+
+			info->key = tmp;
+			info->key_len = strlen(tmp);
+		}
+		info->type = type;
+	} else {
+		/* For key import, get the blob from user-space */
+		if (copy_from_user(info->blob, u64_to_user_ptr(key_crt->blob),
+				   info->blob_len)) {
+			msg = "Unable to copy from user the blob.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->black_key),
+				     key_crt->black_key_len);
+			return -EFAULT;
+		}
+	}
+
+	goto exit;
+
+free_resource:
+	kfree(tmp);
+
+exit:
+	return ret;
+}
+
+/**
+ * keygen_create_keyblob - Generate key and blob
+ *
+ * @info             : keyblob_info structure, will be updated with
+ *                     the black key and blob data from CAAM
+ *
+ * Return            : '0' on success, error code otherwise
+ */
+static int keygen_create_keyblob(struct keyblob_info *info)
+{
+	int ret = 0;
+	struct device *jrdev;
+
+	/* Allocate caam job ring for operation to be performed from CAAM */
+	jrdev = caam_jr_alloc();
+	if (IS_ERR(jrdev)) {
+		pr_err("Job Ring Device allocation failed\n");
+		return PTR_ERR(jrdev);
+	}
+
+	/* Create a black key */
+	ret = generate_black_key(jrdev, info);
+	if (ret) {
+		dev_err(jrdev, "Black key generation failed: (%d)\n", ret);
+		goto free_jr;
+	}
+
+	/* Clear the input key, if exists */
+	if (info->key)
+		memset(info->key, 0, info->key_len);
+
+	/* Set key modifier, used as revision number, for blob */
+	info->key_mod = caam_key_modifier;
+	info->key_mod_len = ARRAY_SIZE(caam_key_modifier);
+
+	/*
+	 * Encapsulate the key, into a black blob, in general memory
+	 * (the only memory type supported, right now)
+	 */
+	ret = caam_blob_encap(jrdev, info);
+	if (ret) {
+		dev_err(jrdev, "Blob encapsulation of black key failed: %d\n",
+			ret);
+		goto free_jr;
+	}
+
+	/* Tag the black key so it can be passed to CAAM Crypto API */
+	ret = tag_black_obj(info, sizeof(info->black_key), false);
+	if (ret) {
+		dev_err(jrdev, "Black key tagging failed: %d\n", ret);
+		goto free_jr;
+	}
+
+	/* Tag the black blob so it can be passed to CAAM Crypto API */
+	ret = tag_black_obj(info, sizeof(info->blob), true);
+	if (ret) {
+		dev_err(jrdev, "Black blob tagging failed: %d\n", ret);
+		goto free_jr;
+	}
+
+free_jr:
+	caam_jr_free(jrdev);
+
+	return ret;
+}
+
+/**
+ * keygen_import_key - Import a black key from a blob
+ *
+ * @info             : keyblob_info structure, will be updated with
+ *                     the black key obtained after blob decapsulation by CAAM
+ *
+ * Return            : '0' on success, error code otherwise
+ */
+static int keygen_import_key(struct keyblob_info *info)
+{
+	int ret = 0;
+	struct device *jrdev;
+	struct header_conf *header;
+	struct tagged_object *tag_obj;
+
+	/* Allocate CAAM Job Ring for operation to be performed from CAAM */
+	jrdev = caam_jr_alloc();
+	if (IS_ERR(jrdev)) {
+		pr_err("Job Ring Device allocation failed\n");
+		return PTR_ERR(jrdev);
+	}
+
+	/* Set key modifier, used as revision number, for blob */
+	info->key_mod = caam_key_modifier;
+	info->key_mod_len = ARRAY_SIZE(caam_key_modifier);
+
+	print_hex_dump_debug("input blob @ " __stringify(__LINE__) " : ",
+			     DUMP_PREFIX_ADDRESS, 16, 4, info->blob,
+			     info->blob_len, 1);
+
+	/* Check if one can retrieve the tag object header configuration */
+	if (info->blob_len <= TAG_OVERHEAD_SIZE) {
+		dev_err(jrdev, "Invalid blob length\n");
+		ret = -EINVAL;
+		goto free_jr;
+	}
+
+	/* Retrieve the tag object */
+	tag_obj = (struct tagged_object *)info->blob;
+
+	/*
+	 * Check tag object header configuration
+	 * and retrieve the tag object header configuration
+	 */
+	if (is_valid_header_conf(&tag_obj->header)) {
+		header = &tag_obj->header;
+	} else {
+		dev_err(jrdev,
+			"Unable to get tag object header configuration for blob\n");
+		ret = -EINVAL;
+		goto free_jr;
+	}
+
+	info->key_len = header->red_key_len;
+
+	/* Validate the red key size extracted from blob */
+	if (info->key_len < MIN_KEY_SIZE || info->key_len > MAX_KEY_SIZE) {
+		dev_err(jrdev,
+			"Invalid red key length extracted from blob, expected values are between 16 and 64 bytes\n");
+		ret = -EINVAL;
+		goto free_jr;
+	}
+
+	info->type = header->type;
+
+	/* Update blob length by removing the header size */
+	info->blob_len -= TAG_OVERHEAD_SIZE;
+
+	/*
+	 * Check the received, from user, blob length
+	 * with the one from tag header
+	 */
+	if (info->blob_len != header->obj_len) {
+		dev_err(jrdev, "Mismatch between received blob length and the one from tag header\n");
+		ret = -EINVAL;
+		goto free_jr;
+	}
+
+	/*
+	 * Decapsulate the blob into a black key,
+	 * in general memory (the only memory type supported, right now)
+	 */
+	ret = caam_blob_decap(jrdev, info);
+	if (ret) {
+		dev_err(jrdev, "Blob decapsulation failed: %d\n", ret);
+		goto free_jr;
+	}
+
+	/* Tag the black key so it can be passed to CAAM Crypto API */
+	ret = tag_black_obj(info, sizeof(info->black_key), false);
+	if (ret)
+		dev_err(jrdev, "Black key tagging failed: %d\n", ret);
+
+free_jr:
+	caam_jr_free(jrdev);
+
+	return ret;
+}
+
+/**
+ * send_output       - Send the output data (tagged key and blob)
+ *                     from kernel to user-space.
+ *
+ * @key_crt          : Structure used to transfer data
+ *                     from user-space to kernel
+ * @info             : keyblob_info structure, which contains all
+ *                     the data obtained from CAAM that needs to
+ *                     be transferred to user-space
+ * @create_key_op    : Used to determine if it's a create or import operation
+ * @err              : Error code received from previous operations
+ *
+ * Return            : '0' on success, error code otherwise
+ */
+static int send_output(struct caam_keygen_cmd *key_crt, unsigned long arg,
+		       struct keyblob_info *info, bool create_key_op, int err)
+{
+	int ret = 0;
+	char *msg;
+
+	/* Free resource used on validate_input */
+	kfree(info->key);
+
+	if (err)
+		return err;
+
+	/* Check if there's enough space to copy black key to user */
+	if (key_crt->black_key_len < info->black_key_len) {
+		msg = "Not enough space for black key.\n";
+		send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+			     key_crt->blob_len);
+		/* Send, to user, the necessary size for key */
+		key_crt->black_key_len = info->black_key_len;
+
+		ret = -EINVAL;
+		goto exit;
+	}
+	key_crt->black_key_len = info->black_key_len;
+
+	/* For key import, copy to user only the black key */
+	if (copy_to_user(u64_to_user_ptr(key_crt->black_key),
+			 info->black_key, info->black_key_len))
+		return -EFAULT;
+
+	/* For key creation, copy to user, also, the blob */
+	if (create_key_op) {
+		/* Check if there's enough space to copy blob user */
+		if (key_crt->blob_len < info->blob_len) {
+			msg = "Not enough space for blob key.\n";
+			send_err_msg(msg, u64_to_user_ptr(key_crt->blob),
+				     key_crt->blob_len);
+			/* Send, to user, the necessary size for blob */
+			key_crt->blob_len = info->blob_len;
+
+			ret = -EINVAL;
+			goto exit;
+		}
+
+		key_crt->blob_len = info->blob_len;
+
+		if (copy_to_user(u64_to_user_ptr(key_crt->blob), info->blob,
+				 info->blob_len))
+			return -EFAULT;
+	}
+
+exit:
+	if (copy_to_user((void __user *)arg, key_crt,
+			 sizeof(struct caam_keygen_cmd)))
+		return -EFAULT;
+
+	return ret;
+}
+
+static long caam_keygen_ioctl(struct file *file, unsigned int cmd,
+			      unsigned long arg)
+{
+	int ret = 0;
+	struct keyblob_info info = {.key = NULL};
+	struct caam_keygen_cmd key_crt;
+	/* Used to determine if it's a create or import operation */
+	bool create_key_op = false;
+
+	switch (cmd) {
+	case CAAM_KEYGEN_IOCTL_CREATE:
+	{
+		create_key_op = true;
+
+		/* Validate user-space input */
+		ret = validate_input(&key_crt, arg, &info, create_key_op);
+		if (ret)
+			break;
+
+		/* Create tagged key and blob */
+		ret = keygen_create_keyblob(&info);
+
+		/* Send data from kernel to user-space */
+		ret = send_output(&key_crt, arg, &info, create_key_op, ret);
+
+		break;
+	}
+	case CAAM_KEYGEN_IOCTL_IMPORT:
+	{
+		/* Validate user-space input */
+		ret = validate_input(&key_crt, arg, &info, create_key_op);
+		if (ret)
+			break;
+
+		/* Import tagged key from blob */
+		ret = keygen_import_key(&info);
+
+		/* Send data from kernel to user-space */
+		ret = send_output(&key_crt, arg, &info, create_key_op, ret);
+
+		break;
+	}
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+static const struct file_operations fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = caam_keygen_ioctl,
+	.compat_ioctl = compat_ptr_ioctl,
+};
+
+static struct miscdevice caam_keygen_dev = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = DEVICE_NAME,
+	.fops = &fops
+};
+
+int caam_keygen_init(void)
+{
+	int ret;
+
+	ret = misc_register(&caam_keygen_dev);
+	if (ret) {
+		pr_err("Failed to register device %s\n",
+		       caam_keygen_dev.name);
+		return ret;
+	}
+
+	pr_info("Device %s registered\n", caam_keygen_dev.name);
+
+	return 0;
+}
+
+void caam_keygen_exit(void)
+{
+	misc_deregister(&caam_keygen_dev);
+
+	pr_info("caam_keygen unregistered\n");
+}
diff --git a/drivers/crypto/caam/caamrng.c b/drivers/crypto/caam/caamrng.c
index 77d048dfe..a550e7230 100644
--- a/drivers/crypto/caam/caamrng.c
+++ b/drivers/crypto/caam/caamrng.c
@@ -170,6 +170,52 @@ static void caam_cleanup(struct hwrng *rng)
 	kfifo_free(&ctx->fifo);
 }
 
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
+static inline void test_len(struct hwrng *rng, size_t len, bool wait)
+{
+	u8 *buf;
+	int real_len;
+	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
+	struct device *dev = ctx->ctrldev;
+
+	buf = kzalloc(sizeof(u8) * len, GFP_KERNEL);
+	real_len = rng->read(rng, buf, len, wait);
+	dev_info(dev, "wanted %zu bytes, got %d\n", len, real_len);
+	if (real_len < 0)
+		dev_err(dev, "READ FAILED\n");
+	else if (real_len == 0 && wait)
+		dev_err(dev, "WAITING FAILED\n");
+	if (real_len > 0)
+		print_hex_dump_debug("random bytes@: ", DUMP_PREFIX_ADDRESS, 16,
+				     4, buf, real_len, 1);
+	kfree(buf);
+}
+
+static inline void test_mode_once(struct hwrng *rng, bool wait)
+{
+	test_len(rng, 32, wait);
+	test_len(rng, 64, wait);
+	test_len(rng, 128, wait);
+}
+
+static inline void test_mode(struct hwrng *rng, bool wait)
+{
+#define TEST_PASS 1
+	int i;
+
+	for (i = 0; i < TEST_PASS; i++)
+		test_mode_once(rng, wait);
+}
+
+static void self_test(struct hwrng *rng)
+{
+	pr_info("testing without waiting\n");
+	test_mode(rng, false);
+	pr_info("testing with waiting\n");
+	test_mode(rng, true);
+}
+#endif
+
 static int caam_init(struct hwrng *rng)
 {
 	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
@@ -224,10 +270,10 @@ int caam_rng_init(struct device *ctrldev)
 
 	/* Check for an instantiated RNG before registration */
 	if (priv->era < 10)
-		rng_inst = (rd_reg32(&priv->ctrl->perfmon.cha_num_ls) &
+		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
 			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
 	else
-		rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK;
+		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
 
 	if (!rng_inst)
 		return 0;
@@ -256,6 +302,10 @@ int caam_rng_init(struct device *ctrldev)
 		return ret;
 	}
 
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
+	self_test(&ctx->rng);
+#endif
+
 	devres_close_group(ctrldev, caam_rng_init);
 	return 0;
 }
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index ca0361b2d..1cf3de63f 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/device.h>
+#include <linux/dma-map-ops.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
 #include <linux/sys_soc.h>
@@ -19,6 +20,7 @@
 #include "jr.h"
 #include "desc_constr.h"
 #include "ctrl.h"
+#include "sm.h"
 
 bool caam_dpaa2;
 EXPORT_SYMBOL(caam_dpaa2);
@@ -79,6 +81,14 @@ static void build_deinstantiation_desc(u32 *desc, int handle)
 	append_jump(desc, JUMP_CLASS_CLASS1 | JUMP_TYPE_HALT);
 }
 
+static const struct of_device_id imx8m_machine_match[] = {
+	{ .compatible = "fsl,imx8mm", },
+	{ .compatible = "fsl,imx8mn", },
+	{ .compatible = "fsl,imx8mp", },
+	{ .compatible = "fsl,imx8mq", },
+	{ }
+};
+
 /*
  * run_descriptor_deco0 - runs a descriptor on DECO0, under direct control of
  *			  the software (no JR/QI used).
@@ -105,10 +115,7 @@ static inline int run_descriptor_deco0(struct device *ctrldev, u32 *desc,
 	     * Apparently on i.MX8M{Q,M,N,P} it doesn't matter if virt_en == 1
 	     * and the following steps should be performed regardless
 	     */
-	    of_machine_is_compatible("fsl,imx8mq") ||
-	    of_machine_is_compatible("fsl,imx8mm") ||
-	    of_machine_is_compatible("fsl,imx8mn") ||
-	    of_machine_is_compatible("fsl,imx8mp")) {
+	    of_match_node(imx8m_machine_match, of_root)) {
 		clrsetbits_32(&ctrl->deco_rsr, 0, DECORSR_JR0);
 
 		while (!(rd_reg32(&ctrl->deco_rsr) & DECORSR_VALID) &&
@@ -342,16 +349,15 @@ static int instantiate_rng(struct device *ctrldev, int state_handle_mask,
 /*
  * kick_trng - sets the various parameters for enabling the initialization
  *	       of the RNG4 block in CAAM
- * @pdev - pointer to the platform device
+ * @dev - pointer to the controller device
  * @ent_delay - Defines the length (in system clocks) of each entropy sample.
  */
-static void kick_trng(struct platform_device *pdev, int ent_delay)
+static void kick_trng(struct device *dev, int ent_delay)
 {
-	struct device *ctrldev = &pdev->dev;
-	struct caam_drv_private *ctrlpriv = dev_get_drvdata(ctrldev);
+	struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev);
 	struct caam_ctrl __iomem *ctrl;
 	struct rng4tst __iomem *r4tst;
-	u32 val;
+	u32 val, rtsdctl;
 
 	ctrl = (struct caam_ctrl __iomem *)ctrlpriv->ctrl;
 	r4tst = &ctrl->r4tst[0];
@@ -367,26 +373,38 @@ static void kick_trng(struct platform_device *pdev, int ent_delay)
 	 * Performance-wise, it does not make sense to
 	 * set the delay to a value that is lower
 	 * than the last one that worked (i.e. the state handles
-	 * were instantiated properly. Thus, instead of wasting
-	 * time trying to set the values controlling the sample
-	 * frequency, the function simply returns.
+	 * were instantiated properly).
 	 */
-	val = (rd_reg32(&r4tst->rtsdctl) & RTSDCTL_ENT_DLY_MASK)
-	      >> RTSDCTL_ENT_DLY_SHIFT;
-	if (ent_delay <= val)
-		goto start_rng;
-
-	val = rd_reg32(&r4tst->rtsdctl);
-	val = (val & ~RTSDCTL_ENT_DLY_MASK) |
-	      (ent_delay << RTSDCTL_ENT_DLY_SHIFT);
-	wr_reg32(&r4tst->rtsdctl, val);
-	/* min. freq. count, equal to 1/4 of the entropy sample length */
-	wr_reg32(&r4tst->rtfrqmin, ent_delay >> 2);
-	/* disable maximum frequency count */
-	wr_reg32(&r4tst->rtfrqmax, RTFRQMAX_DISABLE);
-	/* read the control register */
-	val = rd_reg32(&r4tst->rtmctl);
-start_rng:
+	rtsdctl = rd_reg32(&r4tst->rtsdctl);
+	val = (rtsdctl & RTSDCTL_ENT_DLY_MASK) >> RTSDCTL_ENT_DLY_SHIFT;
+	if (ent_delay > val) {
+		val = ent_delay;
+		/* min. freq. count, equal to 1/4 of the entropy sample length */
+		wr_reg32(&r4tst->rtfrqmin, val >> 2);
+		/* max. freq. count, equal to 16 times the entropy sample length */
+		wr_reg32(&r4tst->rtfrqmax, val << 4);
+	}
+
+	wr_reg32(&r4tst->rtsdctl, (val << RTSDCTL_ENT_DLY_SHIFT) |
+		 RTSDCTL_SAMP_SIZE_VAL);
+
+	/*
+	 * To avoid reprogramming the self-test parameters over and over again,
+	 * use RTSDCTL[SAMP_SIZE] as an indicator.
+	 */
+	if ((rtsdctl & RTSDCTL_SAMP_SIZE_MASK) != RTSDCTL_SAMP_SIZE_VAL) {
+		wr_reg32(&r4tst->rtscmisc, (2 << 16) | 32);
+		wr_reg32(&r4tst->rtpkrrng, 570);
+		wr_reg32(&r4tst->rtpkrmax, 1600);
+		wr_reg32(&r4tst->rtscml, (122 << 16) | 317);
+		wr_reg32(&r4tst->rtscrl[0], (80 << 16) | 107);
+		wr_reg32(&r4tst->rtscrl[1], (57 << 16) | 62);
+		wr_reg32(&r4tst->rtscrl[2], (39 << 16) | 39);
+		wr_reg32(&r4tst->rtscrl[3], (27 << 16) | 26);
+		wr_reg32(&r4tst->rtscrl[4], (19 << 16) | 18);
+		wr_reg32(&r4tst->rtscrl[5], (18 << 16) | 17);
+	}
+
 	/*
 	 * select raw sampling in both entropy shifter
 	 * and statistical checker; ; put RNG4 into run mode
@@ -395,7 +413,7 @@ static void kick_trng(struct platform_device *pdev, int ent_delay)
 		      RTMCTL_SAMP_MODE_RAW_ES_SC);
 }
 
-static int caam_get_era_from_hw(struct caam_ctrl __iomem *ctrl)
+static int caam_get_era_from_hw(struct caam_perfmon __iomem *perfmon)
 {
 	static const struct {
 		u16 ip_id;
@@ -421,12 +439,12 @@ static int caam_get_era_from_hw(struct caam_ctrl __iomem *ctrl)
 	u16 ip_id;
 	int i;
 
-	ccbvid = rd_reg32(&ctrl->perfmon.ccb_id);
+	ccbvid = rd_reg32(&perfmon->ccb_id);
 	era = (ccbvid & CCBVID_ERA_MASK) >> CCBVID_ERA_SHIFT;
 	if (era)	/* This is '0' prior to CAAM ERA-6 */
 		return era;
 
-	id_ms = rd_reg32(&ctrl->perfmon.caam_id_ms);
+	id_ms = rd_reg32(&perfmon->caam_id_ms);
 	ip_id = (id_ms & SECVID_MS_IPID_MASK) >> SECVID_MS_IPID_SHIFT;
 	maj_rev = (id_ms & SECVID_MS_MAJ_REV_MASK) >> SECVID_MS_MAJ_REV_SHIFT;
 
@@ -446,7 +464,7 @@ static int caam_get_era_from_hw(struct caam_ctrl __iomem *ctrl)
  *
  * @ctrl:	controller region
  */
-static int caam_get_era(struct caam_ctrl __iomem *ctrl)
+static int caam_get_era(struct caam_perfmon __iomem *perfmon)
 {
 	struct device_node *caam_node;
 	int ret;
@@ -459,7 +477,7 @@ static int caam_get_era(struct caam_ctrl __iomem *ctrl)
 	if (!ret)
 		return prop;
 	else
-		return caam_get_era_from_hw(ctrl);
+		return caam_get_era_from_hw(perfmon);
 }
 
 /*
@@ -589,6 +607,214 @@ static void caam_remove_debugfs(void *root)
 	debugfs_remove_recursive(root);
 }
 
+static void caam_dma_dev_unregister(void *data)
+{
+	platform_device_unregister(data);
+}
+
+static int caam_ctrl_rng_init(struct device *dev)
+{
+	struct caam_drv_private *ctrlpriv = dev_get_drvdata(dev);
+	struct caam_ctrl __iomem *ctrl = ctrlpriv->ctrl;
+	int ret, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN;
+	u8 rng_vid;
+
+	if (ctrlpriv->era < 10) {
+		struct caam_perfmon __iomem *perfmon;
+
+		perfmon = ctrlpriv->total_jobrs ?
+			  (struct caam_perfmon *)&ctrlpriv->jr[0]->perfmon :
+			  (struct caam_perfmon *)&ctrl->perfmon;
+
+		rng_vid = (rd_reg32(&perfmon->cha_id_ls) &
+			   CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
+	} else {
+		struct version_regs __iomem *vreg;
+
+		vreg = ctrlpriv->total_jobrs ?
+			(struct version_regs *)&ctrlpriv->jr[0]->vreg :
+			(struct version_regs *)&ctrl->vreg;
+
+		rng_vid = (rd_reg32(&vreg->rng) & CHA_VER_VID_MASK) >>
+			  CHA_VER_VID_SHIFT;
+	}
+
+	/*
+	 * If SEC has RNG version >= 4 and RNG state handle has not been
+	 * already instantiated, do RNG instantiation
+	 * In case of SoCs with Management Complex, RNG is managed by MC f/w.
+	 */
+	if (!(ctrlpriv->mc_en && ctrlpriv->pr_support) && rng_vid >= 4) {
+		ctrlpriv->rng4_sh_init =
+			rd_reg32(&ctrl->r4tst[0].rdsta);
+		/*
+		 * If the secure keys (TDKEK, JDKEK, TDSK), were already
+		 * generated, signal this to the function that is instantiating
+		 * the state handles. An error would occur if RNG4 attempts
+		 * to regenerate these keys before the next POR.
+		 */
+		gen_sk = ctrlpriv->rng4_sh_init & RDSTA_SKVN ? 0 : 1;
+		ctrlpriv->rng4_sh_init &= RDSTA_MASK;
+		do {
+			int inst_handles =
+				rd_reg32(&ctrl->r4tst[0].rdsta) & RDSTA_MASK;
+			/*
+			 * If either SH were instantiated by somebody else
+			 * (e.g. u-boot) then it is assumed that the entropy
+			 * parameters are properly set and thus the function
+			 * setting these (kick_trng(...)) is skipped.
+			 * Also, if a handle was instantiated, do not change
+			 * the TRNG parameters.
+			 */
+			if (!(ctrlpriv->rng4_sh_init || inst_handles)) {
+				dev_info(dev,
+					 "Entropy delay = %u\n",
+					 ent_delay);
+				kick_trng(dev, ent_delay);
+				ent_delay += 400;
+			}
+			/*
+			 * if instantiate_rng(...) fails, the loop will rerun
+			 * and the kick_trng(...) function will modify the
+			 * upper and lower limits of the entropy sampling
+			 * interval, leading to a successful initialization of
+			 * the RNG.
+			 */
+			ret = instantiate_rng(dev, inst_handles,
+					      gen_sk);
+			if (ret == -EAGAIN)
+				/*
+				 * if here, the loop will rerun,
+				 * so don't hog the CPU
+				 */
+				cpu_relax();
+		} while ((ret == -EAGAIN) && (ent_delay < RTSDCTL_ENT_DLY_MAX));
+		if (ret) {
+			dev_err(dev, "failed to instantiate RNG");
+			return ret;
+		}
+		/*
+		 * Set handles initialized by this module as the complement of
+		 * the already initialized ones
+		 */
+		ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_MASK;
+
+		/* Enable RDB bit so that RNG works faster */
+		clrsetbits_32(&ctrl->scfgr, 0, SCFGR_RDBENABLE);
+	}
+
+	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 < ctrlpriv->total_jobrs; 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->scu_en &&
+	    !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->scu_en &&
+	    !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;
+}
+
+SIMPLE_DEV_PM_OPS(caam_ctrl_pm_ops, caam_ctrl_suspend, caam_ctrl_resume);
+
+#endif /* CONFIG_PM_SLEEP */
+
 #ifdef CONFIG_FSL_MC_BUS
 static bool check_version(struct fsl_mc_version *mc_version, u32 major,
 			  u32 minor, u32 revision)
@@ -612,19 +838,25 @@ static bool check_version(struct fsl_mc_version *mc_version, u32 major,
 /* Probe routine for CAAM top (controller) level */
 static int caam_probe(struct platform_device *pdev)
 {
-	int ret, ring, gen_sk, ent_delay = RTSDCTL_ENT_DLY_MIN;
+	int ret, ring;
 	u64 caam_id;
 	const struct soc_device_attribute *imx_soc_match;
+	static struct platform_device_info caam_dma_pdev_info = {
+		.name = "caam-dma",
+		.id = PLATFORM_DEVID_NONE
+	};
+	static struct platform_device *caam_dma_dev;
 	struct device *dev;
 	struct device_node *nprop, *np;
+	struct resource res_regs;
 	struct caam_ctrl __iomem *ctrl;
 	struct caam_drv_private *ctrlpriv;
+	struct caam_perfmon __iomem *perfmon;
 	struct dentry *dfs_root;
 	u32 scfgr, comp_params;
-	u8 rng_vid;
 	int pg_size;
 	int BLOCK_OFFSET = 0;
-	bool pr_support = false;
+	bool reg_access = true;
 
 	ctrlpriv = devm_kzalloc(&pdev->dev, sizeof(*ctrlpriv), GFP_KERNEL);
 	if (!ctrlpriv)
@@ -635,9 +867,44 @@ static int caam_probe(struct platform_device *pdev)
 	nprop = pdev->dev.of_node;
 
 	imx_soc_match = soc_device_match(caam_imx_soc_table);
+	if (!imx_soc_match && of_match_node(imx8m_machine_match, of_root))
+		return -EPROBE_DEFER;
+
 	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) {
+		np = of_find_compatible_node(NULL, NULL, "fsl,imx-scu");
+
+		if (!np)
+			np = of_find_compatible_node(NULL, NULL, "fsl,imx-sentinel");
+
+		ctrlpriv->scu_en = !!np;
+		of_node_put(np);
+
+		reg_access = !ctrlpriv->scu_en;
+
+		/*
+		 * CAAM clocks cannot be controlled from kernel.
+		 * They are automatically turned on by SCU f/w.
+		 */
+		if (ctrlpriv->scu_en)
+			goto iomap_ctrl;
+
+		/*
+		 * Until Layerscape and i.MX OP-TEE get in sync,
+		 * only i.MX OP-TEE use cases disallow access to
+		 * caam page 0 (controller) registers.
+		 */
+		np = of_find_compatible_node(NULL, NULL, "linaro,optee-tz");
+		ctrlpriv->optee_en = !!np;
+		of_node_put(np);
+
+		reg_access = reg_access && !ctrlpriv->optee_en;
+
 		if (!imx_soc_match->data) {
 			dev_err(dev, "No clock data provided for i.MX SoC");
 			return -EINVAL;
@@ -648,7 +915,7 @@ static int caam_probe(struct platform_device *pdev)
 			return ret;
 	}
 
-
+iomap_ctrl:
 	/* Get configuration properties from device tree */
 	/* First, get register page */
 	ctrl = devm_of_iomap(dev, nprop, 0, NULL);
@@ -658,10 +925,38 @@ static int caam_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	caam_little_end = !(bool)(rd_reg32(&ctrl->perfmon.status) &
+	ring = 0;
+	for_each_available_child_of_node(nprop, np)
+		if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") ||
+		    of_device_is_compatible(np, "fsl,sec4.0-job-ring")) {
+			u32 reg;
+
+			if (of_property_read_u32_index(np, "reg", 0, &reg)) {
+				dev_err(dev, "%s read reg property error\n",
+					np->full_name);
+				continue;
+			}
+
+			ctrlpriv->jr[ring] = (struct caam_job_ring __iomem __force *)
+					     ((__force uint8_t *)ctrl + reg);
+
+			ctrlpriv->total_jobrs++;
+			ring++;
+		}
+
+	/*
+	 * Wherever possible, instead of accessing registers from the global page,
+	 * use the alias registers in the first (cf. DT nodes order)
+	 * job ring's page.
+	 */
+	perfmon = ring ? (struct caam_perfmon *)&ctrlpriv->jr[0]->perfmon :
+			 (struct caam_perfmon *)&ctrl->perfmon;
+
+	caam_little_end = !(bool)(rd_reg32(&perfmon->status) &
 				  (CSTA_PLEND | CSTA_ALT_PLEND));
-	comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ms);
-	if (comp_params & CTPR_MS_PS && rd_reg32(&ctrl->mcr) & MCFGR_LONG_PTR)
+	comp_params = rd_reg32(&perfmon->comp_parms_ms);
+	if (reg_access && comp_params & CTPR_MS_PS &&
+	    rd_reg32(&ctrl->mcr) & MCFGR_LONG_PTR)
 		caam_ptr_sz = sizeof(u64);
 	else
 		caam_ptr_sz = sizeof(u32);
@@ -708,8 +1003,6 @@ static int caam_probe(struct platform_device *pdev)
 			 BLOCK_OFFSET * DECO_BLOCK_NUMBER
 			 );
 
-	/* Get the IRQ of the controller (for security violations only) */
-	ctrlpriv->secvio_irq = irq_of_parse_and_map(nprop, 0);
 	np = of_find_compatible_node(NULL, NULL, "fsl,qoriq-mc");
 	ctrlpriv->mc_en = !!np;
 	of_node_put(np);
@@ -720,12 +1013,44 @@ static int caam_probe(struct platform_device *pdev)
 
 		mc_version = fsl_mc_get_version();
 		if (mc_version)
-			pr_support = check_version(mc_version, 10, 20, 0);
+			ctrlpriv->pr_support = check_version(mc_version, 10, 20,
+							     0);
 		else
 			return -EPROBE_DEFER;
 	}
 #endif
 
+	/* Only i.MX SoCs have sm */
+	if (!imx_soc_match)
+		goto mc_fw;
+
+	/* Get CAAM-SM node and of_iomap() and save */
+	np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm");
+	if (!np)
+		return -ENODEV;
+
+	/* Get CAAM SM registers base address from device tree */
+	ret = of_address_to_resource(np, 0, &res_regs);
+	if (ret) {
+		dev_err(dev, "failed to retrieve registers base from device tree\n");
+		of_node_put(np);
+		return -ENODEV;
+	}
+
+	ctrlpriv->sm_phy = res_regs.start;
+	ctrlpriv->sm_base = devm_ioremap_resource(dev, &res_regs);
+	if (IS_ERR(ctrlpriv->sm_base)) {
+		of_node_put(np);
+		return PTR_ERR(ctrlpriv->sm_base);
+	}
+
+	ctrlpriv->sm_present = 1;
+	of_node_put(np);
+
+	if (!reg_access)
+		goto set_dma_mask;
+
+mc_fw:
 	/*
 	 * Enable DECO watchdogs and, if this is a PHYS_ADDR_T_64BIT kernel,
 	 * long pointers in master configuration register.
@@ -765,13 +1090,14 @@ static int caam_probe(struct platform_device *pdev)
 			      JRSTART_JR1_START | JRSTART_JR2_START |
 			      JRSTART_JR3_START);
 
+set_dma_mask:
 	ret = dma_set_mask_and_coherent(dev, caam_get_dma_mask(dev));
 	if (ret) {
 		dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", ret);
 		return ret;
 	}
 
-	ctrlpriv->era = caam_get_era(ctrl);
+	ctrlpriv->era = caam_get_era(perfmon);
 	ctrlpriv->domain = iommu_get_domain_for_dev(dev);
 
 	dfs_root = debugfs_create_dir(dev_name(dev), NULL);
@@ -782,7 +1108,7 @@ static int caam_probe(struct platform_device *pdev)
 			return ret;
 	}
 
-	caam_debugfs_init(ctrlpriv, dfs_root);
+	caam_debugfs_init(ctrlpriv, perfmon, dfs_root);
 
 	/* Check to see if (DPAA 1.x) QI present. If so, enable */
 	if (ctrlpriv->qi_present && !caam_dpaa2) {
@@ -801,101 +1127,34 @@ static int caam_probe(struct platform_device *pdev)
 #endif
 	}
 
-	ring = 0;
-	for_each_available_child_of_node(nprop, np)
-		if (of_device_is_compatible(np, "fsl,sec-v4.0-job-ring") ||
-		    of_device_is_compatible(np, "fsl,sec4.0-job-ring")) {
-			ctrlpriv->jr[ring] = (struct caam_job_ring __iomem __force *)
-					     ((__force uint8_t *)ctrl +
-					     (ring + JR_BLOCK_NUMBER) *
-					      BLOCK_OFFSET
-					     );
-			ctrlpriv->total_jobrs++;
-			ring++;
-		}
-
 	/* If no QI and no rings specified, quit and go home */
 	if ((!ctrlpriv->qi_present) && (!ctrlpriv->total_jobrs)) {
 		dev_err(dev, "no queues configured, terminating\n");
 		return -ENOMEM;
 	}
 
-	if (ctrlpriv->era < 10)
-		rng_vid = (rd_reg32(&ctrl->perfmon.cha_id_ls) &
-			   CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
-	else
-		rng_vid = (rd_reg32(&ctrl->vreg.rng) & CHA_VER_VID_MASK) >>
-			   CHA_VER_VID_SHIFT;
-
-	/*
-	 * If SEC has RNG version >= 4 and RNG state handle has not been
-	 * already instantiated, do RNG instantiation
-	 * In case of SoCs with Management Complex, RNG is managed by MC f/w.
-	 */
-	if (!(ctrlpriv->mc_en && pr_support) && rng_vid >= 4) {
-		ctrlpriv->rng4_sh_init =
-			rd_reg32(&ctrl->r4tst[0].rdsta);
-		/*
-		 * If the secure keys (TDKEK, JDKEK, TDSK), were already
-		 * generated, signal this to the function that is instantiating
-		 * the state handles. An error would occur if RNG4 attempts
-		 * to regenerate these keys before the next POR.
-		 */
-		gen_sk = ctrlpriv->rng4_sh_init & RDSTA_SKVN ? 0 : 1;
-		ctrlpriv->rng4_sh_init &= RDSTA_MASK;
-		do {
-			int inst_handles =
-				rd_reg32(&ctrl->r4tst[0].rdsta) &
-								RDSTA_MASK;
-			/*
-			 * If either SH were instantiated by somebody else
-			 * (e.g. u-boot) then it is assumed that the entropy
-			 * parameters are properly set and thus the function
-			 * setting these (kick_trng(...)) is skipped.
-			 * Also, if a handle was instantiated, do not change
-			 * the TRNG parameters.
-			 */
-			if (!(ctrlpriv->rng4_sh_init || inst_handles)) {
-				dev_info(dev,
-					 "Entropy delay = %u\n",
-					 ent_delay);
-				kick_trng(pdev, ent_delay);
-				ent_delay += 400;
-			}
-			/*
-			 * if instantiate_rng(...) fails, the loop will rerun
-			 * and the kick_trng(...) function will modify the
-			 * upper and lower limits of the entropy sampling
-			 * interval, leading to a successful initialization of
-			 * the RNG.
-			 */
-			ret = instantiate_rng(dev, inst_handles,
-					      gen_sk);
-			if (ret == -EAGAIN)
-				/*
-				 * if here, the loop will rerun,
-				 * so don't hog the CPU
-				 */
-				cpu_relax();
-		} while ((ret == -EAGAIN) && (ent_delay < RTSDCTL_ENT_DLY_MAX));
-		if (ret) {
-			dev_err(dev, "failed to instantiate RNG");
+	caam_dma_pdev_info.parent = dev;
+	caam_dma_pdev_info.dma_mask = dma_get_mask(dev);
+	caam_dma_dev = platform_device_register_full(&caam_dma_pdev_info);
+	if (IS_ERR(caam_dma_dev)) {
+		dev_err(dev, "Unable to create and register caam-dma dev\n");
+		return PTR_ERR(caam_dma_dev);
+	} else {
+		set_dma_ops(&caam_dma_dev->dev, get_dma_ops(dev));
+		ret = devm_add_action_or_reset(dev, caam_dma_dev_unregister,
+					       caam_dma_dev);
+		if (ret)
 			return ret;
-		}
-		/*
-		 * Set handles initialized by this module as the complement of
-		 * the already initialized ones
-		 */
-		ctrlpriv->rng4_sh_init = ~ctrlpriv->rng4_sh_init & RDSTA_MASK;
-
-		/* Enable RDB bit so that RNG works faster */
-		clrsetbits_32(&ctrl->scfgr, 0, SCFGR_RDBENABLE);
 	}
 
-	/* NOTE: RTIC detection ought to go here, around Si time */
+	if (reg_access) {
+		ret = caam_ctrl_rng_init(dev);
+		if (ret)
+			return ret;
+	}
 
-	caam_id = (u64)rd_reg32(&ctrl->perfmon.caam_id_ms) << 32 |
-		  (u64)rd_reg32(&ctrl->perfmon.caam_id_ls);
+	caam_id = (u64)rd_reg32(&perfmon->caam_id_ms) << 32 |
+		  (u64)rd_reg32(&perfmon->caam_id_ls);
 
 	/* Report "alive" for developer to see */
 	dev_info(dev, "device ID = 0x%016llx (Era %d)\n", caam_id,
@@ -914,6 +1173,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/debugfs.c b/drivers/crypto/caam/debugfs.c
index 8ebf18398..a34d71ac1 100644
--- a/drivers/crypto/caam/debugfs.c
+++ b/drivers/crypto/caam/debugfs.c
@@ -42,16 +42,14 @@ void caam_debugfs_qi_init(struct caam_drv_private *ctrlpriv)
 }
 #endif
 
-void caam_debugfs_init(struct caam_drv_private *ctrlpriv, struct dentry *root)
+void caam_debugfs_init(struct caam_drv_private *ctrlpriv,
+		       struct caam_perfmon *perfmon, struct dentry *root)
 {
-	struct caam_perfmon *perfmon;
-
 	/*
 	 * FIXME: needs better naming distinction, as some amalgamation of
 	 * "caam" and nprop->full_name. The OF name isn't distinctive,
 	 * but does separate instances
 	 */
-	perfmon = (struct caam_perfmon __force *)&ctrlpriv->ctrl->perfmon;
 
 	ctrlpriv->ctl = debugfs_create_dir("ctl", root);
 
@@ -78,6 +76,9 @@ void caam_debugfs_init(struct caam_drv_private *ctrlpriv, struct dentry *root)
 	debugfs_create_file("fault_status", 0444, ctrlpriv->ctl,
 			    &perfmon->status, &caam_fops_u32_ro);
 
+	if (ctrlpriv->scu_en || ctrlpriv->optee_en)
+		return;
+
 	/* Internal covering keys (useful in non-secure mode only) */
 	ctrlpriv->ctl_kek_wrap.data = (__force void *)&ctrlpriv->ctrl->kek[0];
 	ctrlpriv->ctl_kek_wrap.size = KEK_KEY_SIZE * sizeof(u32);
diff --git a/drivers/crypto/caam/debugfs.h b/drivers/crypto/caam/debugfs.h
index 661d768ac..6c566a2b7 100644
--- a/drivers/crypto/caam/debugfs.h
+++ b/drivers/crypto/caam/debugfs.h
@@ -6,11 +6,14 @@
 
 struct dentry;
 struct caam_drv_private;
+struct caam_perfmon;
 
 #ifdef CONFIG_DEBUG_FS
-void caam_debugfs_init(struct caam_drv_private *ctrlpriv, struct dentry *root);
+void caam_debugfs_init(struct caam_drv_private *ctrlpriv,
+		       struct caam_perfmon *perfmon, struct dentry *root);
 #else
 static inline void caam_debugfs_init(struct caam_drv_private *ctrlpriv,
+				     struct caam_perfmon *perfmon,
 				     struct dentry *root)
 {}
 #endif
diff --git a/drivers/crypto/caam/desc.h b/drivers/crypto/caam/desc.h
index e13470901..c2c58f818 100644
--- a/drivers/crypto/caam/desc.h
+++ b/drivers/crypto/caam/desc.h
@@ -43,6 +43,7 @@
 #define CMD_SEQ_LOAD		(0x03 << CMD_SHIFT)
 #define CMD_FIFO_LOAD		(0x04 << CMD_SHIFT)
 #define CMD_SEQ_FIFO_LOAD	(0x05 << CMD_SHIFT)
+#define CMD_MOVEB		(0x07 << CMD_SHIFT)
 #define CMD_STORE		(0x0a << CMD_SHIFT)
 #define CMD_SEQ_STORE		(0x0b << CMD_SHIFT)
 #define CMD_FIFO_STORE		(0x0c << CMD_SHIFT)
@@ -152,7 +153,7 @@
  * with the TDKEK if TK is set
  */
 #define KEY_ENC			0x00400000
-
+#define KEY_ENC_OFFSET		22
 /*
  * No Write Back - Do not allow key to be FIFO STOREd
  */
@@ -162,11 +163,13 @@
  * Enhanced Encryption of Key
  */
 #define KEY_EKT			0x00100000
+#define KEY_EKT_OFFSET		20
 
 /*
  * Encrypted with Trusted Key
  */
 #define KEY_TK			0x00008000
+#define KEY_TK_OFFSET		15
 
 /*
  * KDEST - Key Destination: 0 - class key register,
@@ -363,6 +366,7 @@
 #define FIFOLD_TYPE_PK_N	(0x08 << FIFOLD_TYPE_SHIFT)
 #define FIFOLD_TYPE_PK_A	(0x0c << FIFOLD_TYPE_SHIFT)
 #define FIFOLD_TYPE_PK_B	(0x0d << FIFOLD_TYPE_SHIFT)
+#define FIFOLD_TYPE_IFIFO	(0x0f << FIFOLD_TYPE_SHIFT)
 
 /* Other types. Need to OR in last/flush bits as desired */
 #define FIFOLD_TYPE_MSG_MASK	(0x38 << FIFOLD_TYPE_SHIFT)
@@ -403,6 +407,10 @@
 #define FIFOST_TYPE_PKHA_N	 (0x08 << FIFOST_TYPE_SHIFT)
 #define FIFOST_TYPE_PKHA_A	 (0x0c << FIFOST_TYPE_SHIFT)
 #define FIFOST_TYPE_PKHA_B	 (0x0d << FIFOST_TYPE_SHIFT)
+#define FIFOST_TYPE_AF_SBOX_CCM_JKEK	(0x10 << FIFOST_TYPE_SHIFT)
+#define FIFOST_TYPE_AF_SBOX_CCM_TKEK	(0x11 << FIFOST_TYPE_SHIFT)
+#define FIFOST_TYPE_KEY_CCM_JKEK	(0x14 << FIFOST_TYPE_SHIFT)
+#define FIFOST_TYPE_KEY_CCM_TKEK	(0x15 << FIFOST_TYPE_SHIFT)
 #define FIFOST_TYPE_AF_SBOX_JKEK (0x20 << FIFOST_TYPE_SHIFT)
 #define FIFOST_TYPE_AF_SBOX_TKEK (0x21 << FIFOST_TYPE_SHIFT)
 #define FIFOST_TYPE_PKHA_E_JKEK	 (0x22 << FIFOST_TYPE_SHIFT)
@@ -1136,6 +1144,23 @@
 #define OP_PCL_PKPROT_ECC			 0x0002
 #define OP_PCL_PKPROT_F2M			 0x0001
 
+/* Blob protocol protinfo bits */
+#define OP_PCL_BLOB_TK			0x0200
+#define OP_PCL_BLOB_EKT			0x0100
+
+#define OP_PCL_BLOB_K2KR_MEM		0x0000
+#define OP_PCL_BLOB_K2KR_C1KR		0x0010
+#define OP_PCL_BLOB_K2KR_C2KR		0x0030
+#define OP_PCL_BLOB_K2KR_AFHAS		0x0050
+#define OP_PCL_BLOB_K2KR_C2KR_SPLIT	0x0070
+
+#define OP_PCL_BLOB_PTXT_SECMEM		0x0008
+#define OP_PCL_BLOB_BLACK		0x0004
+
+#define OP_PCL_BLOB_FMT_NORMAL		0x0000
+#define OP_PCL_BLOB_FMT_MSTR		0x0002
+#define OP_PCL_BLOB_FMT_TEST		0x0003
+
 /* For non-protocol/alg-only op commands */
 #define OP_ALG_TYPE_SHIFT	24
 #define OP_ALG_TYPE_MASK	(0x7 << OP_ALG_TYPE_SHIFT)
@@ -1502,6 +1527,7 @@
 #define MATH_SRC1_INFIFO	(0x0a << MATH_SRC1_SHIFT)
 #define MATH_SRC1_OUTFIFO	(0x0b << MATH_SRC1_SHIFT)
 #define MATH_SRC1_ONE		(0x0c << MATH_SRC1_SHIFT)
+#define MATH_SRC1_ZERO		(0x0f << MATH_SRC1_SHIFT)
 
 /* Destination selectors */
 #define MATH_DEST_SHIFT		8
@@ -1684,4 +1710,31 @@
 /* Frame Descriptor Command for Replacement Job Descriptor */
 #define FD_CMD_REPLACE_JOB_DESC				0x20000000
 
+/* CHA Control Register bits */
+#define CCTRL_RESET_CHA_ALL          0x1
+#define CCTRL_RESET_CHA_AESA         0x2
+#define CCTRL_RESET_CHA_DESA         0x4
+#define CCTRL_RESET_CHA_AFHA         0x8
+#define CCTRL_RESET_CHA_KFHA         0x10
+#define CCTRL_RESET_CHA_SF8A         0x20
+#define CCTRL_RESET_CHA_PKHA         0x40
+#define CCTRL_RESET_CHA_MDHA         0x80
+#define CCTRL_RESET_CHA_CRCA         0x100
+#define CCTRL_RESET_CHA_RNG          0x200
+#define CCTRL_RESET_CHA_SF9A         0x400
+#define CCTRL_RESET_CHA_ZUCE         0x800
+#define CCTRL_RESET_CHA_ZUCA         0x1000
+#define CCTRL_UNLOAD_PK_A0           0x10000
+#define CCTRL_UNLOAD_PK_A1           0x20000
+#define CCTRL_UNLOAD_PK_A2           0x40000
+#define CCTRL_UNLOAD_PK_A3           0x80000
+#define CCTRL_UNLOAD_PK_B0           0x100000
+#define CCTRL_UNLOAD_PK_B1           0x200000
+#define CCTRL_UNLOAD_PK_B2           0x400000
+#define CCTRL_UNLOAD_PK_B3           0x800000
+#define CCTRL_UNLOAD_PK_N            0x1000000
+#define CCTRL_UNLOAD_PK_A            0x4000000
+#define CCTRL_UNLOAD_PK_B            0x8000000
+#define CCTRL_UNLOAD_SBOX            0x10000000
+
 #endif /* DESC_H */
diff --git a/drivers/crypto/caam/desc_constr.h b/drivers/crypto/caam/desc_constr.h
index 62ce6421b..f98dec8f2 100644
--- a/drivers/crypto/caam/desc_constr.h
+++ b/drivers/crypto/caam/desc_constr.h
@@ -240,6 +240,7 @@ static inline u32 *append_##cmd(u32 * const desc, u32 options) \
 APPEND_CMD_RET(jump, JUMP)
 APPEND_CMD_RET(move, MOVE)
 APPEND_CMD_RET(move_len, MOVE_LEN)
+APPEND_CMD_RET(moveb, MOVEB)
 
 static inline void set_jump_tgt_here(u32 * const desc, u32 *jump_cmd)
 {
@@ -500,6 +501,8 @@ do { \
  * @key_virt: virtual address where algorithm key resides
  * @key_inline: true - key can be inlined in the descriptor; false - key is
  *              referenced by the descriptor
+ * @key_real_len: size of the key to be loaded by the CAAM
+ * @key_cmd_opt: optional parameters for KEY command
  */
 struct alginfo {
 	u32 algtype;
@@ -508,6 +511,8 @@ struct alginfo {
 	dma_addr_t key_dma;
 	const void *key_virt;
 	bool key_inline;
+	u32 key_real_len;
+	u32 key_cmd_opt;
 };
 
 /**
diff --git a/drivers/crypto/caam/dpseci.c b/drivers/crypto/caam/dpseci.c
index 039df6c57..23dbb87f7 100644
--- a/drivers/crypto/caam/dpseci.c
+++ b/drivers/crypto/caam/dpseci.c
@@ -5,6 +5,7 @@
  */
 
 #include <linux/fsl/mc.h>
+#include <soc/fsl/dpaa2-io.h>
 #include "dpseci.h"
 #include "dpseci_cmd.h"
 
@@ -16,8 +17,8 @@
  * @token:	Returned token; use in subsequent API calls
  *
  * This function can be used to open a control session for an already created
- * object; an object may have been declared statically in the DPL
- * or created dynamically.
+ * object; an object may have been declared in the DPL or by calling the
+ * dpseci_create() function.
  * This function returns a unique authentication token, associated with the
  * specific object ID and the specific MC portal; this token must be used in all
  * subsequent commands for this specific object.
@@ -66,6 +67,85 @@ int dpseci_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token)
 	return mc_send_command(mc_io, &cmd);
 }
 
+/**
+ * dpseci_create() - Create the DPSECI object
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @dprc_token:	Parent container token; '0' for default container
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @cfg:	Configuration structure
+ * @obj_id:	returned object id
+ *
+ * Create the DPSECI object, allocate required resources and perform required
+ * initialization.
+ *
+ * The object can be created either by declaring it in the DPL file, or by
+ * calling this function.
+ *
+ * The function accepts an authentication token of a parent container that this
+ * object should be assigned to. The token can be '0' so the object will be
+ * assigned to the default container.
+ * The newly created object can be opened with the returned object id and using
+ * the container's associated tokens and MC portals.
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_create(struct fsl_mc_io *mc_io, u16 dprc_token, u32 cmd_flags,
+		  const struct dpseci_cfg *cfg, u32 *obj_id)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_create *cmd_params;
+	int i, err;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_CREATE,
+					  cmd_flags,
+					  dprc_token);
+	cmd_params = (struct dpseci_cmd_create *)cmd.params;
+	for (i = 0; i < 8; i++)
+		cmd_params->priorities[i] = cfg->priorities[i];
+	for (i = 0; i < 8; i++)
+		cmd_params->priorities2[i] = cfg->priorities[8 + i];
+	cmd_params->num_tx_queues = cfg->num_tx_queues;
+	cmd_params->num_rx_queues = cfg->num_rx_queues;
+	cmd_params->options = cpu_to_le32(cfg->options);
+	err = mc_send_command(mc_io, &cmd);
+	if (err)
+		return err;
+
+	*obj_id = mc_cmd_read_object_id(&cmd);
+
+	return 0;
+}
+
+/**
+ * dpseci_destroy() - Destroy the DPSECI object and release all its resources
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @dprc_token: Parent container token; '0' for default container
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @object_id:	The object id; it must be a valid id within the container that
+ *		created this object
+ *
+ * The function accepts the authentication token of the parent container that
+ * created the object (not the one that currently owns the object). The object
+ * is searched within parent using the provided 'object_id'.
+ * All tokens to the object must be closed before calling destroy.
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_destroy(struct fsl_mc_io *mc_io, u16 dprc_token, u32 cmd_flags,
+		   u32 object_id)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_destroy *cmd_params;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_DESTROY,
+					  cmd_flags,
+					  dprc_token);
+	cmd_params = (struct dpseci_cmd_destroy *)cmd.params;
+	cmd_params->object_id = cpu_to_le32(object_id);
+
+	return mc_send_command(mc_io, &cmd);
+}
+
 /**
  * dpseci_enable() - Enable the DPSECI, allow sending and receiving frames
  * @mc_io:	Pointer to MC portal's I/O object
@@ -150,6 +230,198 @@ int dpseci_is_enabled(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
 	return 0;
 }
 
+/**
+ * dpseci_get_irq_enable() - Get overall interrupt state
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @irq_index:	The interrupt index to configure
+ * @en:		Returned Interrupt state - enable = 1, disable = 0
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_get_irq_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			  u8 irq_index, u8 *en)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_irq_enable *cmd_params;
+	struct dpseci_rsp_get_irq_enable *rsp_params;
+	int err;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_GET_IRQ_ENABLE,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_irq_enable *)cmd.params;
+	cmd_params->irq_index = irq_index;
+	err = mc_send_command(mc_io, &cmd);
+	if (err)
+		return err;
+
+	rsp_params = (struct dpseci_rsp_get_irq_enable *)cmd.params;
+	*en = rsp_params->enable_state;
+
+	return 0;
+}
+
+/**
+ * dpseci_set_irq_enable() - Set overall interrupt state.
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @irq_index:	The interrupt index to configure
+ * @en:		Interrupt state - enable = 1, disable = 0
+ *
+ * Allows GPP software to control when interrupts are generated.
+ * Each interrupt can have up to 32 causes. The enable/disable control's the
+ * overall interrupt state. If the interrupt is disabled no causes will cause
+ * an interrupt.
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_set_irq_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			  u8 irq_index, u8 en)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_irq_enable *cmd_params;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_SET_IRQ_ENABLE,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_irq_enable *)cmd.params;
+	cmd_params->irq_index = irq_index;
+	cmd_params->enable_state = en;
+
+	return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpseci_get_irq_mask() - Get interrupt mask.
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @irq_index:	The interrupt index to configure
+ * @mask:	Returned event mask to trigger interrupt
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently.
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_get_irq_mask(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			u8 irq_index, u32 *mask)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_irq_mask *cmd_params;
+	int err;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_GET_IRQ_MASK,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_irq_mask *)cmd.params;
+	cmd_params->irq_index = irq_index;
+	err = mc_send_command(mc_io, &cmd);
+	if (err)
+		return err;
+
+	*mask = le32_to_cpu(cmd_params->mask);
+
+	return 0;
+}
+
+/**
+ * dpseci_set_irq_mask() - Set interrupt mask.
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @irq_index:	The interrupt index to configure
+ * @mask:	event mask to trigger interrupt;
+ *		each bit:
+ *			0 = ignore event
+ *			1 = consider event for asserting IRQ
+ *
+ * Every interrupt can have up to 32 causes and the interrupt model supports
+ * masking/unmasking each cause independently
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_set_irq_mask(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			u8 irq_index, u32 mask)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_irq_mask *cmd_params;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_SET_IRQ_MASK,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_irq_mask *)cmd.params;
+	cmd_params->mask = cpu_to_le32(mask);
+	cmd_params->irq_index = irq_index;
+
+	return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpseci_get_irq_status() - Get the current status of any pending interrupts
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @irq_index:	The interrupt index to configure
+ * @status:	Returned interrupts status - one bit per cause:
+ *			0 = no interrupt pending
+ *			1 = interrupt pending
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_get_irq_status(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			  u8 irq_index, u32 *status)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_irq_status *cmd_params;
+	int err;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_GET_IRQ_STATUS,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_irq_status *)cmd.params;
+	cmd_params->status = cpu_to_le32(*status);
+	cmd_params->irq_index = irq_index;
+	err = mc_send_command(mc_io, &cmd);
+	if (err)
+		return err;
+
+	*status = le32_to_cpu(cmd_params->status);
+
+	return 0;
+}
+
+/**
+ * dpseci_clear_irq_status() - Clear a pending interrupt's status
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @irq_index:	The interrupt index to configure
+ * @status:	bits to clear (W1C) - one bit per cause:
+ *			0 = don't change
+ *			1 = clear status bit
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_clear_irq_status(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			    u8 irq_index, u32 status)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_irq_status *cmd_params;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_CLEAR_IRQ_STATUS,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_irq_status *)cmd.params;
+	cmd_params->status = cpu_to_le32(status);
+	cmd_params->irq_index = irq_index;
+
+	return mc_send_command(mc_io, &cmd);
+}
+
 /**
  * dpseci_get_attributes() - Retrieve DPSECI attributes
  * @mc_io:	Pointer to MC portal's I/O object
@@ -339,6 +611,42 @@ int dpseci_get_sec_attr(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
 	return 0;
 }
 
+/**
+ * dpseci_get_sec_counters() - Retrieve SEC accelerator counters
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @counters:	Returned SEC counters
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_get_sec_counters(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			    struct dpseci_sec_counters *counters)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_rsp_get_sec_counters *rsp_params;
+	int err;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_GET_SEC_COUNTERS,
+					  cmd_flags,
+					  token);
+	err = mc_send_command(mc_io, &cmd);
+	if (err)
+		return err;
+
+	rsp_params = (struct dpseci_rsp_get_sec_counters *)cmd.params;
+	counters->dequeued_requests =
+		le64_to_cpu(rsp_params->dequeued_requests);
+	counters->ob_enc_requests = le64_to_cpu(rsp_params->ob_enc_requests);
+	counters->ib_dec_requests = le64_to_cpu(rsp_params->ib_dec_requests);
+	counters->ob_enc_bytes = le64_to_cpu(rsp_params->ob_enc_bytes);
+	counters->ob_prot_bytes = le64_to_cpu(rsp_params->ob_prot_bytes);
+	counters->ib_dec_bytes = le64_to_cpu(rsp_params->ib_dec_bytes);
+	counters->ib_valid_bytes = le64_to_cpu(rsp_params->ib_valid_bytes);
+
+	return 0;
+}
+
 /**
  * dpseci_get_api_version() - Get Data Path SEC Interface API version
  * @mc_io:	Pointer to MC portal's I/O object
@@ -368,6 +676,90 @@ int dpseci_get_api_version(struct fsl_mc_io *mc_io, u32 cmd_flags,
 	return 0;
 }
 
+/**
+ * dpseci_set_opr() - Set Order Restoration configuration
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @index:	The queue index
+ * @options:	Configuration mode options; can be OPR_OPT_CREATE or
+ *		OPR_OPT_RETIRE
+ * @cfg:	Configuration options for the OPR
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_set_opr(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u8 index,
+		   u8 options, struct opr_cfg *cfg)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_opr *cmd_params;
+
+	cmd.header = mc_encode_cmd_header(
+			DPSECI_CMDID_SET_OPR,
+			cmd_flags,
+			token);
+	cmd_params = (struct dpseci_cmd_opr *)cmd.params;
+	cmd_params->index = index;
+	cmd_params->options = options;
+	cmd_params->oloe = cfg->oloe;
+	cmd_params->oeane = cfg->oeane;
+	cmd_params->olws = cfg->olws;
+	cmd_params->oa = cfg->oa;
+	cmd_params->oprrws = cfg->oprrws;
+
+	return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpseci_get_opr() - Retrieve Order Restoration config and query
+ * @mc_io:	Pointer to MC portal's I/O object
+ * @cmd_flags:	Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:	Token of DPSECI object
+ * @index:	The queue index
+ * @cfg:	Returned OPR configuration
+ * @qry:	Returned OPR query
+ *
+ * Return:	'0' on success, error code otherwise
+ */
+int dpseci_get_opr(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u8 index,
+		   struct opr_cfg *cfg, struct opr_qry *qry)
+{
+	struct fsl_mc_command cmd = { 0 };
+	struct dpseci_cmd_opr *cmd_params;
+	struct dpseci_rsp_get_opr *rsp_params;
+	int err;
+
+	cmd.header = mc_encode_cmd_header(DPSECI_CMDID_GET_OPR,
+					  cmd_flags,
+					  token);
+	cmd_params = (struct dpseci_cmd_opr *)cmd.params;
+	cmd_params->index = index;
+	err = mc_send_command(mc_io, &cmd);
+	if (err)
+		return err;
+
+	rsp_params = (struct dpseci_rsp_get_opr *)cmd.params;
+	qry->rip = dpseci_get_field(rsp_params->flags, OPR_RIP);
+	qry->enable = dpseci_get_field(rsp_params->flags, OPR_ENABLE);
+	cfg->oloe = rsp_params->oloe;
+	cfg->oeane = rsp_params->oeane;
+	cfg->olws = rsp_params->olws;
+	cfg->oa = rsp_params->oa;
+	cfg->oprrws = rsp_params->oprrws;
+	qry->nesn = le16_to_cpu(rsp_params->nesn);
+	qry->ndsn = le16_to_cpu(rsp_params->ndsn);
+	qry->ea_tseq = le16_to_cpu(rsp_params->ea_tseq);
+	qry->tseq_nlis = dpseci_get_field(rsp_params->tseq_nlis, OPR_TSEQ_NLIS);
+	qry->ea_hseq = le16_to_cpu(rsp_params->ea_hseq);
+	qry->hseq_nlis = dpseci_get_field(rsp_params->hseq_nlis, OPR_HSEQ_NLIS);
+	qry->ea_hptr = le16_to_cpu(rsp_params->ea_hptr);
+	qry->ea_tptr = le16_to_cpu(rsp_params->ea_tptr);
+	qry->opr_vid = le16_to_cpu(rsp_params->opr_vid);
+	qry->opr_id = le16_to_cpu(rsp_params->opr_id);
+
+	return 0;
+}
+
 /**
  * dpseci_set_congestion_notification() - Set congestion group
  *	notification configuration
diff --git a/drivers/crypto/caam/dpseci.h b/drivers/crypto/caam/dpseci.h
index 6dcd9be81..d453baac7 100644
--- a/drivers/crypto/caam/dpseci.h
+++ b/drivers/crypto/caam/dpseci.h
@@ -12,6 +12,8 @@
  */
 
 struct fsl_mc_io;
+struct opr_cfg;
+struct opr_qry;
 
 /**
  * General DPSECI macros
@@ -37,10 +39,22 @@ int dpseci_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);
  */
 #define DPSECI_OPT_HAS_CG		0x000020
 
+/**
+ * Enable the Order Restoration support
+ */
+#define DPSECI_OPT_HAS_OPR		0x000040
+
+/**
+ * Order Point Records are shared for the entire DPSECI
+ */
+#define DPSECI_OPT_OPR_SHARED		0x000080
+
 /**
  * struct dpseci_cfg - Structure representing DPSECI configuration
- * @options: Any combination of the following flags:
+ * @options: Any combination of the following options:
  *		DPSECI_OPT_HAS_CG
+ *		DPSECI_OPT_HAS_OPR
+ *		DPSECI_OPT_OPR_SHARED
  * @num_tx_queues: num of queues towards the SEC
  * @num_rx_queues: num of queues back from the SEC
  * @priorities: Priorities for the SEC hardware processing;
@@ -55,6 +69,12 @@ struct dpseci_cfg {
 	u8 priorities[DPSECI_MAX_QUEUE_NUM];
 };
 
+int dpseci_create(struct fsl_mc_io *mc_io, u16 dprc_token, u32 cmd_flags,
+		  const struct dpseci_cfg *cfg, u32 *obj_id);
+
+int dpseci_destroy(struct fsl_mc_io *mc_io, u16 dprc_token, u32 cmd_flags,
+		   u32 object_id);
+
 int dpseci_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);
 
 int dpseci_disable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);
@@ -64,13 +84,33 @@ int dpseci_reset(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);
 int dpseci_is_enabled(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
 		      int *en);
 
+int dpseci_get_irq_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			  u8 irq_index, u8 *en);
+
+int dpseci_set_irq_enable(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			  u8 irq_index, u8 en);
+
+int dpseci_get_irq_mask(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			u8 irq_index, u32 *mask);
+
+int dpseci_set_irq_mask(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			u8 irq_index, u32 mask);
+
+int dpseci_get_irq_status(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			  u8 irq_index, u32 *status);
+
+int dpseci_clear_irq_status(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			    u8 irq_index, u32 status);
+
 /**
  * struct dpseci_attr - Structure representing DPSECI attributes
  * @id: DPSECI object ID
  * @num_tx_queues: number of queues towards the SEC
  * @num_rx_queues: number of queues back from the SEC
- * @options: any combination of the following flags:
+ * @options: any combination of the following options:
  *		DPSECI_OPT_HAS_CG
+ *		DPSECI_OPT_HAS_OPR
+ *		DPSECI_OPT_OPR_SHARED
  */
 struct dpseci_attr {
 	int id;
@@ -250,9 +290,39 @@ struct dpseci_sec_attr {
 int dpseci_get_sec_attr(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
 			struct dpseci_sec_attr *attr);
 
+/**
+ * struct dpseci_sec_counters - Structure representing global SEC counters and
+ *				not per dpseci counters
+ * @dequeued_requests:	Number of Requests Dequeued
+ * @ob_enc_requests:	Number of Outbound Encrypt Requests
+ * @ib_dec_requests:	Number of Inbound Decrypt Requests
+ * @ob_enc_bytes:	Number of Outbound Bytes Encrypted
+ * @ob_prot_bytes:	Number of Outbound Bytes Protected
+ * @ib_dec_bytes:	Number of Inbound Bytes Decrypted
+ * @ib_valid_bytes:	Number of Inbound Bytes Validated
+ */
+struct dpseci_sec_counters {
+	u64 dequeued_requests;
+	u64 ob_enc_requests;
+	u64 ib_dec_requests;
+	u64 ob_enc_bytes;
+	u64 ob_prot_bytes;
+	u64 ib_dec_bytes;
+	u64 ib_valid_bytes;
+};
+
+int dpseci_get_sec_counters(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+			    struct dpseci_sec_counters *counters);
+
 int dpseci_get_api_version(struct fsl_mc_io *mc_io, u32 cmd_flags,
 			   u16 *major_ver, u16 *minor_ver);
 
+int dpseci_set_opr(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u8 index,
+		   u8 options, struct opr_cfg *cfg);
+
+int dpseci_get_opr(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token, u8 index,
+		   struct opr_cfg *cfg, struct opr_qry *qry);
+
 /**
  * enum dpseci_congestion_unit - DPSECI congestion units
  * @DPSECI_CONGESTION_UNIT_BYTES: bytes units
diff --git a/drivers/crypto/caam/dpseci_cmd.h b/drivers/crypto/caam/dpseci_cmd.h
index 71a007c85..9fec1c92e 100644
--- a/drivers/crypto/caam/dpseci_cmd.h
+++ b/drivers/crypto/caam/dpseci_cmd.h
@@ -17,6 +17,7 @@
 /* Command versioning */
 #define DPSECI_CMD_BASE_VERSION		1
 #define DPSECI_CMD_BASE_VERSION_V2	2
+#define DPSECI_CMD_BASE_VERSION_V3	3
 #define DPSECI_CMD_ID_OFFSET		4
 
 #define DPSECI_CMD_V1(id)	(((id) << DPSECI_CMD_ID_OFFSET) | \
@@ -25,9 +26,14 @@
 #define DPSECI_CMD_V2(id)	(((id) << DPSECI_CMD_ID_OFFSET) | \
 				 DPSECI_CMD_BASE_VERSION_V2)
 
+#define DPSECI_CMD_V3(id)	(((id) << DPSECI_CMD_ID_OFFSET) | \
+				 DPSECI_CMD_BASE_VERSION_V3)
+
 /* Command IDs */
 #define DPSECI_CMDID_CLOSE				DPSECI_CMD_V1(0x800)
 #define DPSECI_CMDID_OPEN				DPSECI_CMD_V1(0x809)
+#define DPSECI_CMDID_CREATE				DPSECI_CMD_V3(0x909)
+#define DPSECI_CMDID_DESTROY				DPSECI_CMD_V1(0x989)
 #define DPSECI_CMDID_GET_API_VERSION			DPSECI_CMD_V1(0xa09)
 
 #define DPSECI_CMDID_ENABLE				DPSECI_CMD_V1(0x002)
@@ -36,10 +42,20 @@
 #define DPSECI_CMDID_RESET				DPSECI_CMD_V1(0x005)
 #define DPSECI_CMDID_IS_ENABLED				DPSECI_CMD_V1(0x006)
 
+#define DPSECI_CMDID_SET_IRQ_ENABLE			DPSECI_CMD_V1(0x012)
+#define DPSECI_CMDID_GET_IRQ_ENABLE			DPSECI_CMD_V1(0x013)
+#define DPSECI_CMDID_SET_IRQ_MASK			DPSECI_CMD_V1(0x014)
+#define DPSECI_CMDID_GET_IRQ_MASK			DPSECI_CMD_V1(0x015)
+#define DPSECI_CMDID_GET_IRQ_STATUS			DPSECI_CMD_V1(0x016)
+#define DPSECI_CMDID_CLEAR_IRQ_STATUS			DPSECI_CMD_V1(0x017)
+
 #define DPSECI_CMDID_SET_RX_QUEUE			DPSECI_CMD_V1(0x194)
 #define DPSECI_CMDID_GET_RX_QUEUE			DPSECI_CMD_V1(0x196)
 #define DPSECI_CMDID_GET_TX_QUEUE			DPSECI_CMD_V1(0x197)
 #define DPSECI_CMDID_GET_SEC_ATTR			DPSECI_CMD_V2(0x198)
+#define DPSECI_CMDID_GET_SEC_COUNTERS			DPSECI_CMD_V1(0x199)
+#define DPSECI_CMDID_SET_OPR				DPSECI_CMD_V1(0x19A)
+#define DPSECI_CMDID_GET_OPR				DPSECI_CMD_V1(0x19B)
 #define DPSECI_CMDID_SET_CONGESTION_NOTIFICATION	DPSECI_CMD_V1(0x170)
 #define DPSECI_CMDID_GET_CONGESTION_NOTIFICATION	DPSECI_CMD_V1(0x171)
 
@@ -58,6 +74,20 @@ struct dpseci_cmd_open {
 	__le32 dpseci_id;
 };
 
+struct dpseci_cmd_create {
+	u8 priorities[8];
+	u8 num_tx_queues;
+	u8 num_rx_queues;
+	u8 pad0[6];
+	__le32 options;
+	__le32 pad1;
+	u8 priorities2[8];
+};
+
+struct dpseci_cmd_destroy {
+	__le32 object_id;
+};
+
 #define DPSECI_ENABLE_SHIFT	0
 #define DPSECI_ENABLE_SIZE	1
 
@@ -65,6 +95,26 @@ struct dpseci_rsp_is_enabled {
 	u8 is_enabled;
 };
 
+struct dpseci_cmd_irq_enable {
+	u8 enable_state;
+	u8 pad[3];
+	u8 irq_index;
+};
+
+struct dpseci_rsp_get_irq_enable {
+	u8 enable_state;
+};
+
+struct dpseci_cmd_irq_mask {
+	__le32 mask;
+	u8 irq_index;
+};
+
+struct dpseci_cmd_irq_status {
+	__le32 status;
+	u8 irq_index;
+};
+
 struct dpseci_rsp_get_attributes {
 	__le32 id;
 	__le32 pad0;
@@ -126,11 +176,70 @@ struct dpseci_rsp_get_sec_attr {
 	u8 ptha_acc_num;
 };
 
+struct dpseci_rsp_get_sec_counters {
+	__le64 dequeued_requests;
+	__le64 ob_enc_requests;
+	__le64 ib_dec_requests;
+	__le64 ob_enc_bytes;
+	__le64 ob_prot_bytes;
+	__le64 ib_dec_bytes;
+	__le64 ib_valid_bytes;
+};
+
 struct dpseci_rsp_get_api_version {
 	__le16 major;
 	__le16 minor;
 };
 
+struct dpseci_cmd_opr {
+	__le16 pad;
+	u8 index;
+	u8 options;
+	u8 pad1[7];
+	u8 oloe;
+	u8 oeane;
+	u8 olws;
+	u8 oa;
+	u8 oprrws;
+};
+
+#define DPSECI_OPR_RIP_SHIFT		0
+#define DPSECI_OPR_RIP_SIZE		1
+#define DPSECI_OPR_ENABLE_SHIFT		1
+#define DPSECI_OPR_ENABLE_SIZE		1
+#define DPSECI_OPR_TSEQ_NLIS_SHIFT	0
+#define DPSECI_OPR_TSEQ_NLIS_SIZE	1
+#define DPSECI_OPR_HSEQ_NLIS_SHIFT	0
+#define DPSECI_OPR_HSEQ_NLIS_SIZE	1
+
+struct dpseci_rsp_get_opr {
+	__le64 pad;
+	u8 flags;
+	u8 pad0[2];
+	u8 oloe;
+	u8 oeane;
+	u8 olws;
+	u8 oa;
+	u8 oprrws;
+	__le16 nesn;
+	__le16 pad1;
+	__le16 ndsn;
+	__le16 pad2;
+	__le16 ea_tseq;
+	u8 tseq_nlis;
+	u8 pad3;
+	__le16 ea_hseq;
+	u8 hseq_nlis;
+	u8 pad4;
+	__le16 ea_hptr;
+	__le16 pad5;
+	__le16 ea_tptr;
+	__le16 pad6;
+	__le16 opr_vid;
+	__le16 pad7;
+	__le16 opr_id;
+};
+
 #define DPSECI_CGN_DEST_TYPE_SHIFT	0
 #define DPSECI_CGN_DEST_TYPE_SIZE	4
 #define DPSECI_CGN_UNITS_SHIFT		4
diff --git a/drivers/crypto/caam/fsl_jr_uio.c b/drivers/crypto/caam/fsl_jr_uio.c
new file mode 100644
index 000000000..2d3a0554a
--- /dev/null
+++ b/drivers/crypto/caam/fsl_jr_uio.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2013 Freescale Semiconductor, Inc.
+ * Copyright 2018 NXP
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/uio_driver.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include "regs.h"
+#include "fsl_jr_uio.h"
+
+static const char jr_uio_version[] = "fsl JR UIO driver v1.0";
+
+#define NAME_LENGTH 30
+#define JR_INDEX_OFFSET 12
+
+static const char uio_device_name[] = "fsl-jr";
+static LIST_HEAD(jr_list);
+
+struct jr_uio_info {
+	atomic_t ref; /* exclusive, only one open() at a time */
+	struct uio_info uio;
+	char name[NAME_LENGTH];
+};
+
+struct jr_dev {
+	u32 revision;
+	u32 index;
+	u32 irq;
+	struct caam_job_ring __iomem *global_regs;
+	struct device *dev;
+	struct resource *res;
+	struct jr_uio_info info;
+	struct list_head node;
+	struct list_head jr_list;
+};
+
+static int jr_uio_open(struct uio_info *info, struct inode *inode)
+{
+	struct jr_uio_info *uio_info = container_of(info,
+					struct jr_uio_info, uio);
+
+	if (!atomic_dec_and_test(&uio_info->ref)) {
+		pr_err("%s: failing non-exclusive open()\n", uio_info->name);
+		atomic_inc(&uio_info->ref);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int jr_uio_release(struct uio_info *info, struct inode *inode)
+{
+	struct jr_uio_info *uio_info = container_of(info,
+					struct jr_uio_info, uio);
+	atomic_inc(&uio_info->ref);
+
+	return 0;
+}
+
+static irqreturn_t jr_uio_irq_handler(int irq, struct uio_info *dev_info)
+{
+	struct jr_dev *jrdev = dev_info->priv;
+	u32 irqstate;
+
+	irqstate = rd_reg32(&jrdev->global_regs->jrintstatus);
+
+	if (!irqstate)
+		return IRQ_NONE;
+
+	if (irqstate & JRINT_JR_ERROR)
+		dev_info(jrdev->dev, "uio job ring error - irqstate: %08x\n",
+			 irqstate);
+
+	/*mask valid interrupts */
+	clrsetbits_32(&jrdev->global_regs->rconfig_lo, 0, JRCFG_IMSK);
+
+	/* Have valid interrupt at this point, just ACK and trigger */
+	wr_reg32(&jrdev->global_regs->jrintstatus, irqstate);
+
+	return IRQ_HANDLED;
+}
+
+static int jr_uio_irqcontrol(struct uio_info *dev_info, int irqon)
+{
+	struct jr_dev *jrdev = dev_info->priv;
+
+	switch (irqon) {
+	case SEC_UIO_SIMULATE_IRQ_CMD:
+		uio_event_notify(dev_info);
+		break;
+	case SEC_UIO_ENABLE_IRQ_CMD:
+		/* Enable Job Ring interrupt */
+		clrsetbits_32(&jrdev->global_regs->rconfig_lo, JRCFG_IMSK, 0);
+		break;
+	case SEC_UIO_DISABLE_IRQ_CMD:
+		/* Disable Job Ring interrupt */
+		clrsetbits_32(&jrdev->global_regs->rconfig_lo, 0, JRCFG_IMSK);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int __init jr_uio_init(struct jr_dev *uio_dev)
+{
+	int ret;
+	struct jr_uio_info *info;
+
+	info = &uio_dev->info;
+	atomic_set(&info->ref, 1);
+	info->uio.version = jr_uio_version;
+	info->uio.name = uio_dev->info.name;
+	info->uio.mem[0].name = "JR config space";
+	info->uio.mem[0].addr = uio_dev->res->start;
+	info->uio.mem[0].size = resource_size(uio_dev->res);
+	info->uio.mem[0].internal_addr = uio_dev->global_regs;
+	info->uio.mem[0].memtype = UIO_MEM_PHYS;
+	info->uio.irq = uio_dev->irq;
+	info->uio.irq_flags = IRQF_SHARED;
+	info->uio.handler = jr_uio_irq_handler;
+	info->uio.irqcontrol = jr_uio_irqcontrol;
+	info->uio.open = jr_uio_open;
+	info->uio.release = jr_uio_release;
+	info->uio.priv = uio_dev;
+
+	ret = uio_register_device(uio_dev->dev, &info->uio);
+	if (ret) {
+		dev_err(uio_dev->dev, "jr_uio: UIO registration failed\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id jr_ids[] = {
+	{ .compatible = "fsl,sec-v4.0-job-ring", },
+	{ .compatible = "fsl,sec-v4.4-job-ring", },
+	{ .compatible = "fsl,sec-v5.0-job-ring", },
+	{ .compatible = "fsl,sec-v6.0-job-ring", },
+	{},
+};
+
+static int fsl_jr_probe(struct platform_device *dev)
+{
+	struct jr_dev *jr_dev;
+	struct device_node *jr_node;
+	int ret, count = 0;
+	struct list_head *p;
+
+	jr_node = dev->dev.of_node;
+	if (!jr_node) {
+		dev_err(&dev->dev, "Device OF-Node is NULL\n");
+		return -EFAULT;
+	}
+
+	jr_dev = devm_kzalloc(&dev->dev, sizeof(*jr_dev), GFP_KERNEL);
+	if (!jr_dev)
+		return -ENOMEM;
+
+	/* Creat name and index */
+	list_for_each(p, &jr_list) {
+		count++;
+	}
+	jr_dev->index = count;
+
+	snprintf(jr_dev->info.name, sizeof(jr_dev->info.name) - 1,
+		 "%s%d", uio_device_name, jr_dev->index);
+
+	jr_dev->dev = &dev->dev;
+	platform_set_drvdata(dev, jr_dev);
+
+	jr_dev->res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (unlikely(!jr_dev->res)) {
+		dev_err(jr_dev->dev, "platform_get_resource() failed\n");
+		ret = -ENOMEM;
+		goto abort;
+	}
+
+	jr_dev->global_regs =
+		devm_ioremap(&dev->dev, jr_dev->res->start,
+			     resource_size(jr_dev->res));
+	if (unlikely(jr_dev->global_regs == 0)) {
+		dev_err(jr_dev->dev, "devm_ioremap failed\n");
+		ret = -EIO;
+		goto abort;
+	}
+	jr_dev->irq = irq_of_parse_and_map(jr_node, 0);
+	dev_dbg(jr_dev->dev, "errirq: %d\n", jr_dev->irq);
+
+	/* Register UIO */
+	ret = jr_uio_init(jr_dev);
+	if (ret) {
+		dev_err(&dev->dev, "UIO init Failed\n");
+		goto abort;
+	}
+
+	list_add_tail(&jr_dev->node, &jr_list);
+
+	dev_info(jr_dev->dev, "UIO device full name %s initialized\n",
+		 jr_dev->info.name);
+
+	return 0;
+
+abort:
+	return ret;
+}
+
+static int fsl_jr_remove(struct platform_device *dev)
+{
+	struct jr_dev *jr_dev = platform_get_drvdata(dev);
+
+	if (!jr_dev)
+		return 0;
+
+	list_del(&jr_dev->node);
+	uio_unregister_device(&jr_dev->info.uio);
+
+	return 0;
+}
+
+MODULE_DEVICE_TABLE(of, jr_ids);
+
+static struct platform_driver fsl_jr_driver = {
+	.driver = {
+		.name = "fsl-jr-uio",
+		.of_match_table = jr_ids,
+	},
+	.probe = fsl_jr_probe,
+	.remove = fsl_jr_remove,
+};
+
+module_platform_driver(fsl_jr_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("FSL SEC UIO Driver");
diff --git a/drivers/crypto/caam/fsl_jr_uio.h b/drivers/crypto/caam/fsl_jr_uio.h
new file mode 100644
index 000000000..2956645e1
--- /dev/null
+++ b/drivers/crypto/caam/fsl_jr_uio.h
@@ -0,0 +1,25 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CAAM Job RING UIO support header file
+ *
+ * Copyright 2013 Freescale Semiconductor, Inc
+ * Copyright 2018 NXP
+ */
+
+#ifndef FSL_JR_UIO_H
+#define FSL_JR_UIO_H
+
+/** UIO command used by user-space driver to request
+ *  disabling IRQs on a certain job ring
+ */
+#define SEC_UIO_DISABLE_IRQ_CMD         0
+/** UIO command used by user-space driver to request
+ *  enabling IRQs on a certain job ring
+ */
+#define SEC_UIO_ENABLE_IRQ_CMD          1
+/** UIO command used by user-space driver to request SEC kernel driver
+ *  to simulate that an IRQ is generated on a certain job ring
+ */
+#define SEC_UIO_SIMULATE_IRQ_CMD        2
+
+#endif
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index 9112279a4..ad83f3a83 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -39,6 +39,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 */
@@ -46,6 +58,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;
 
@@ -63,18 +76,35 @@ 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
  */
 struct caam_drv_private {
+	struct device *smdev;
+
 	/* Physical-presence section */
 	struct caam_ctrl __iomem *ctrl; /* controller region */
 	struct caam_deco __iomem *deco; /* DECO/CCB views */
 	struct caam_assurance __iomem *assure;
 	struct caam_queue_if __iomem *qi; /* QI control region */
 	struct caam_job_ring __iomem *jr[4];	/* JobR's register space */
+	dma_addr_t __iomem *sm_base;	/* Secure memory storage base */
+	phys_addr_t sm_phy;		/* Secure memory storage physical */
 
 	struct iommu_domain *domain;
 
@@ -84,8 +114,11 @@ struct caam_drv_private {
 	 */
 	u8 total_jobrs;		/* Total Job Rings in device */
 	u8 qi_present;		/* Nonzero if QI present in device */
+	u8 sm_present;		/* Nonzero if Secure Memory is supported */
 	u8 mc_en;		/* Nonzero if MC f/w is active */
-	int secvio_irq;		/* Security violation interrupt number */
+	u8 scu_en;		/* Nonzero if SCU f/w is active */
+	u8 optee_en;		/* Nonzero if OP-TEE f/w is active */
+	bool pr_support;	/* RNG prediction resistance available */
 	int virt_en;		/* Virtualization enabled in CAAM */
 	int era;		/* CAAM Era (internal HW revision) */
 
@@ -105,6 +138,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
@@ -195,6 +233,42 @@ static inline void caam_qi_algapi_exit(void)
 
 #endif /* CONFIG_CAAM_QI */
 
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_SM
+
+int caam_sm_startup(struct device *dev);
+void caam_sm_shutdown(struct device *dev);
+
+#else
+
+static inline int caam_sm_startup(struct device *dev)
+{
+	return 0;
+}
+
+static inline void caam_sm_shutdown(struct device *dev)
+{
+}
+
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_SM */
+
+#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API
+
+int caam_keygen_init(void);
+void caam_keygen_exit(void);
+
+#else
+
+static inline int caam_keygen_init(void)
+{
+	return 0;
+}
+
+static inline void caam_keygen_exit(void)
+{
+}
+
+#endif /* CONFIG_CRYPTO_DEV_FSL_CAAM_TK_API */
+
 static inline u64 caam_get_dma_mask(struct device *dev)
 {
 	struct device_node *nprop = dev->of_node;
diff --git a/drivers/crypto/caam/jr.c b/drivers/crypto/caam/jr.c
index 6f669966b..1572d31c9 100644
--- a/drivers/crypto/caam/jr.c
+++ b/drivers/crypto/caam/jr.c
@@ -4,7 +4,7 @@
  * JobR backend functionality
  *
  * Copyright 2008-2012 Freescale Semiconductor, Inc.
- * Copyright 2019 NXP
+ * Copyright 2019-2020 NXP
  */
 
 #include <linux/of_irq.h>
@@ -27,8 +27,40 @@ static struct jr_driver_data driver_data;
 static DEFINE_MUTEX(algs_lock);
 static unsigned int active_devs;
 
-static void register_algs(struct caam_drv_private_jr *jrpriv,
-			  struct device *dev)
+static void init_misc_func(struct caam_drv_private_jr *jrpriv,
+			   struct device *dev)
+{
+	mutex_lock(&algs_lock);
+
+	if (active_devs != 1)
+		goto algs_unlock;
+
+	jrpriv->hwrng = !caam_rng_init(dev);
+	caam_sm_startup(dev);
+	caam_keygen_init();
+
+algs_unlock:
+	mutex_unlock(&algs_lock);
+}
+
+static void exit_misc_func(struct caam_drv_private_jr *jrpriv,
+			   struct device *dev)
+{
+	mutex_lock(&algs_lock);
+
+	if (active_devs != 1)
+		goto algs_unlock;
+
+	caam_keygen_exit();
+	caam_sm_shutdown(dev);
+	if (jrpriv->hwrng)
+		caam_rng_exit(dev);
+
+algs_unlock:
+	mutex_unlock(&algs_lock);
+}
+
+static void register_algs(struct device *dev)
 {
 	mutex_lock(&algs_lock);
 
@@ -38,14 +70,13 @@ static void register_algs(struct caam_drv_private_jr *jrpriv,
 	caam_algapi_init(dev);
 	caam_algapi_hash_init(dev);
 	caam_pkc_init(dev);
-	jrpriv->hwrng = !caam_rng_init(dev);
 	caam_qi_algapi_init(dev);
 
 algs_unlock:
 	mutex_unlock(&algs_lock);
 }
 
-static void unregister_algs(void)
+static void unregister_algs(struct device *dev)
 {
 	mutex_lock(&algs_lock);
 
@@ -62,6 +93,14 @@ static void unregister_algs(void)
 	mutex_unlock(&algs_lock);
 }
 
+static int jr_driver_probed;
+
+int caam_jr_driver_probed(void)
+{
+	return jr_driver_probed;
+}
+EXPORT_SYMBOL(caam_jr_driver_probed);
+
 static void caam_jr_crypto_engine_exit(void *data)
 {
 	struct device *jrdev = data;
@@ -71,19 +110,27 @@ static void caam_jr_crypto_engine_exit(void *data)
 	crypto_engine_exit(jrpriv->engine);
 }
 
-static int caam_reset_hw_jr(struct device *dev)
+/*
+ * 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)
 {
 	struct caam_drv_private_jr *jrp = dev_get_drvdata(dev);
 	unsigned int timeout = 100000;
 
-	/*
-	 * mask interrupts since we are going to poll
-	 * for reset completion status
-	 */
-	clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK);
+	/* Check the current status */
+	if (rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_INPROGRESS)
+		goto wait_quiesce_completion;
 
-	/* initiate flush (required prior to reset) */
-	wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
+	/* Reset the field */
+	clrsetbits_32(&jrp->rregs->jrintstatus, JRINT_ERR_HALT_MASK, 0);
+
+	/* initiate flush / park (required prior to reset) */
+	wr_reg32(&jrp->rregs->jrcommand, jrcr_bits);
+
+wait_quiesce_completion:
 	while (((rd_reg32(&jrp->rregs->jrintstatus) & JRINT_ERR_HALT_MASK) ==
 		JRINT_ERR_HALT_INPROGRESS) && --timeout)
 		cpu_relax();
@@ -94,8 +141,56 @@ static int caam_reset_hw_jr(struct device *dev)
 		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 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);
+	unsigned int timeout = 100000;
+	int err;
+
+	/*
+	 * mask interrupts since we are going to poll
+	 * for reset completion status
+	 */
+	clrsetbits_32(&jrp->rregs->rconfig_lo, 0, JRCFG_IMSK);
+
+	err = caam_jr_flush(dev);
+	if (err)
+		return err;
+
 	/* initiate reset */
-	timeout = 100000;
 	wr_reg32(&jrp->rregs->jrcommand, JRCR_RESET);
 	while ((rd_reg32(&jrp->rregs->jrcommand) & JRCR_RESET) && --timeout)
 		cpu_relax();
@@ -135,8 +230,7 @@ static int caam_jr_remove(struct platform_device *pdev)
 	jrdev = &pdev->dev;
 	jrpriv = dev_get_drvdata(jrdev);
 
-	if (jrpriv->hwrng)
-		caam_rng_exit(jrdev->parent);
+	exit_misc_func(jrpriv, jrdev->parent);
 
 	/*
 	 * Return EBUSY if job ring already allocated.
@@ -147,7 +241,7 @@ static int caam_jr_remove(struct platform_device *pdev)
 	}
 
 	/* Unregister JR-based RNG & crypto algorithms */
-	unregister_algs();
+	unregister_algs(jrdev->parent);
 
 	/* Remove the node from Physical JobR list maintained by driver */
 	spin_lock(&driver_data.jr_alloc_lock);
@@ -159,6 +253,8 @@ static int caam_jr_remove(struct platform_device *pdev)
 	if (ret)
 		dev_err(jrdev, "Failed to shut down job ring\n");
 
+	jr_driver_probed--;
+
 	return ret;
 }
 
@@ -174,7 +270,7 @@ static irqreturn_t caam_jr_interrupt(int irq, void *st_dev)
 	 * tasklet if jobs done.
 	 */
 	irqstate = rd_reg32(&jrp->rregs->jrintstatus);
-	if (!irqstate)
+	if (!(irqstate & JRINT_JR_INT))
 		return IRQ_NONE;
 
 	/*
@@ -204,7 +300,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;
@@ -278,8 +375,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);
 }
 
 /**
@@ -322,6 +420,36 @@ struct device *caam_jr_alloc(void)
 }
 EXPORT_SYMBOL(caam_jr_alloc);
 
+/**
+ * caam_jridx_alloc() - Alloc a specific job ring based on its index.
+ *
+ * returns :  pointer to the newly allocated physical
+ *	      JobR dev can be written to if successful.
+ **/
+struct device *caam_jridx_alloc(int idx)
+{
+	struct caam_drv_private_jr *jrpriv;
+	struct device *dev = ERR_PTR(-ENODEV);
+
+	spin_lock(&driver_data.jr_alloc_lock);
+
+	if (list_empty(&driver_data.jr_list))
+		goto end;
+
+	list_for_each_entry(jrpriv, &driver_data.jr_list, list_node) {
+		if (jrpriv->ridx == idx) {
+			atomic_inc(&jrpriv->tfm_count);
+			dev = jrpriv->dev;
+			break;
+		}
+	}
+
+end:
+	spin_unlock(&driver_data.jr_alloc_lock);
+	return dev;
+}
+EXPORT_SYMBOL(caam_jridx_alloc);
+
 /**
  * caam_jr_free() - Free the Job Ring
  * @rdev:      points to the dev that identifies the Job ring to
@@ -404,8 +532,16 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
 	 * Guarantee that the descriptor's DMA address has been written to
 	 * the next slot in the ring before the write index is updated, since
 	 * other cores may update this index independently.
+	 *
+	 * Under heavy DDR load, smp_wmb() or dma_wmb() fail to make the input
+	 * ring be updated before the CAAM starts reading it. So, CAAM will
+	 * process, again, an old descriptor address and will put it in the
+	 * output ring. This will make caam_jr_dequeue() to fail, since this
+	 * old descriptor is not in the software ring.
+	 * To fix this, use wmb() which works on the full system instead of
+	 * inner/outer shareable domains.
 	 */
-	smp_wmb();
+	wmb();
 
 	jrp->head = (head + 1) & (JOBR_DEPTH - 1);
 
@@ -429,6 +565,80 @@ int caam_jr_enqueue(struct device *dev, u32 *desc,
 }
 EXPORT_SYMBOL(caam_jr_enqueue);
 
+/**
+ * caam_jr_run_and_wait_for_completion() - Enqueue a job and wait for its
+ * completion. Returns 0 if OK, -ENOSPC if the queue is full,
+ * -EIO if it cannot map the caller's descriptor.
+ * @dev:  struct device of the job ring to be used
+ * @desc: points to a job descriptor that execute our request. All
+ *        descriptors (and all referenced data) must be in a DMAable
+ *        region, and all data references must be physical addresses
+ *        accessible to CAAM (i.e. within a PAMU window granted
+ *        to it).
+ * @cbk:  pointer to a callback function to be invoked upon completion
+ *        of this request. This has the form:
+ *        callback(struct device *dev, u32 *desc, u32 stat, void *arg)
+ *        where:
+ *        @dev:    contains the job ring device that processed this
+ *                 response.
+ *        @desc:   descriptor that initiated the request, same as
+ *                 "desc" being argued to caam_jr_enqueue().
+ *        @status: untranslated status received from CAAM. See the
+ *                 reference manual for a detailed description of
+ *                 error meaning, or see the JRSTA definitions in the
+ *                 register header file
+ *        @areq:   optional pointer to an argument passed with the
+ *                 original request
+ **/
+int caam_jr_run_and_wait_for_completion(struct device *dev, u32 *desc,
+					void (*cbk)(struct device *dev,
+						    u32 *desc, u32 status,
+						    void *areq))
+{
+	int ret = 0;
+	struct jr_job_result jobres = {0};
+
+	/* Initialize the completion structure */
+	init_completion(&jobres.completion);
+
+	/* Enqueue job for execution */
+	ret = caam_jr_enqueue(dev, desc, cbk, &jobres);
+	if (ret != -EINPROGRESS)
+		return ret;
+
+	/* Wait for job completion */
+	wait_for_completion(&jobres.completion);
+
+	/* Get return code processed in cbk */
+	ret = jobres.error;
+
+	return ret;
+}
+EXPORT_SYMBOL(caam_jr_run_and_wait_for_completion);
+
+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
  */
@@ -465,25 +675,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,
@@ -592,11 +793,138 @@ static int caam_jr_probe(struct platform_device *pdev)
 
 	atomic_set(&jrpriv->tfm_count, 0);
 
-	register_algs(jrpriv, jrdev->parent);
+	device_init_wakeup(&pdev->dev, 1);
+	device_set_wakeup_enable(&pdev->dev, false);
+
+	register_algs(jrdev->parent);
+	init_misc_func(jrpriv, jrdev->parent);
+	jr_driver_probed++;
 
 	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;
+}
+
+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",
@@ -612,6 +940,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/jr.h b/drivers/crypto/caam/jr.h
index eab611530..768df0a6a 100644
--- a/drivers/crypto/caam/jr.h
+++ b/drivers/crypto/caam/jr.h
@@ -3,17 +3,38 @@
  * CAAM public-level include definitions for the JobR backend
  *
  * Copyright 2008-2011 Freescale Semiconductor, Inc.
+ * Copyright 2020 NXP
  */
 
 #ifndef JR_H
 #define JR_H
 
+#include <linux/completion.h>
+
+ /**
+  * struct jr_job_result - Job Ring result structure, used for requests
+  *                        that need to run and wait for their completion
+  *
+  * @error               : The result returned after request was executed
+  * @completion          : Structure used to maintain state for a "completion"
+  */
+struct jr_job_result {
+	int error;
+	struct completion completion;
+};
+
 /* Prototypes for backend-level services exposed to APIs */
+int caam_jr_driver_probed(void);
 struct device *caam_jr_alloc(void);
+struct device *caam_jridx_alloc(int idx);
 void caam_jr_free(struct device *rdev);
 int caam_jr_enqueue(struct device *dev, u32 *desc,
 		    void (*cbk)(struct device *dev, u32 *desc, u32 status,
 				void *areq),
 		    void *areq);
+int caam_jr_run_and_wait_for_completion(struct device *dev, u32 *desc,
+					void (*cbk)(struct device *dev,
+						    u32 *desc, u32 status,
+						    void *areq));
 
 #endif /* JR_H */
diff --git a/drivers/crypto/caam/qi.c b/drivers/crypto/caam/qi.c
index ec53528d8..f0762c2e3 100644
--- a/drivers/crypto/caam/qi.c
+++ b/drivers/crypto/caam/qi.c
@@ -9,7 +9,7 @@
 
 #include <linux/cpumask.h>
 #include <linux/kthread.h>
-#include <soc/fsl/qman.h>
+#include <linux/fsl_qman.h>
 
 #include "debugfs.h"
 #include "regs.h"
@@ -99,23 +99,21 @@ static void *caam_iova_to_virt(struct iommu_domain *domain,
 int caam_qi_enqueue(struct device *qidev, struct caam_drv_req *req)
 {
 	struct qm_fd fd;
-	dma_addr_t addr;
 	int ret;
 	int num_retries = 0;
 
-	qm_fd_clear_fd(&fd);
-	qm_fd_set_compound(&fd, qm_sg_entry_get_len(&req->fd_sgt[1]));
-
-	addr = dma_map_single(qidev, req->fd_sgt, sizeof(req->fd_sgt),
+	fd.cmd = 0;
+	fd.format = qm_fd_compound;
+	fd.cong_weight = caam32_to_cpu(req->fd_sgt[1].length);
+	fd.addr = dma_map_single(qidev, req->fd_sgt, sizeof(req->fd_sgt),
 			      DMA_BIDIRECTIONAL);
-	if (dma_mapping_error(qidev, addr)) {
+	if (dma_mapping_error(qidev, fd.addr)) {
 		dev_err(qidev, "DMA mapping error for QI enqueue request\n");
 		return -EIO;
 	}
-	qm_fd_addr_set64(&fd, addr);
 
 	do {
-		ret = qman_enqueue(req->drv_ctx->req_fq, &fd);
+		ret = qman_enqueue(req->drv_ctx->req_fq, &fd, 0);
 		if (likely(!ret)) {
 			refcount_inc(&req->drv_ctx->refcnt);
 			return 0;
@@ -133,7 +131,7 @@ int caam_qi_enqueue(struct device *qidev, struct caam_drv_req *req)
 EXPORT_SYMBOL(caam_qi_enqueue);
 
 static void caam_fq_ern_cb(struct qman_portal *qm, struct qman_fq *fq,
-			   const union qm_mr_entry *msg)
+			   const struct qm_mr_entry *msg)
 {
 	const struct qm_fd *fd;
 	struct caam_drv_req *drv_req;
@@ -151,7 +149,7 @@ static void caam_fq_ern_cb(struct qman_portal *qm, struct qman_fq *fq,
 
 	refcount_dec(&drv_req->drv_ctx->refcnt);
 
-	if (qm_fd_get_format(fd) != qm_fd_compound) {
+	if (fd->format != qm_fd_compound) {
 		dev_err(qidev, "Non-compound FD from CAAM\n");
 		return;
 	}
@@ -182,20 +180,22 @@ static struct qman_fq *create_caam_req_fq(struct device *qidev,
 	req_fq->cb.fqs = NULL;
 
 	ret = qman_create_fq(0, QMAN_FQ_FLAG_DYNAMIC_FQID |
-				QMAN_FQ_FLAG_TO_DCPORTAL, req_fq);
+				QMAN_FQ_FLAG_TO_DCPORTAL | QMAN_FQ_FLAG_LOCKED,
+			     req_fq);
 	if (ret) {
 		dev_err(qidev, "Failed to create session req FQ\n");
 		goto create_req_fq_fail;
 	}
 
-	memset(&opts, 0, sizeof(opts));
-	opts.we_mask = cpu_to_be16(QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_DESTWQ |
-				   QM_INITFQ_WE_CONTEXTB |
-				   QM_INITFQ_WE_CONTEXTA | QM_INITFQ_WE_CGID);
-	opts.fqd.fq_ctrl = cpu_to_be16(QM_FQCTRL_CPCSTASH | QM_FQCTRL_CGE);
-	qm_fqd_set_destwq(&opts.fqd, qm_channel_caam, 2);
-	opts.fqd.context_b = cpu_to_be32(qman_fq_fqid(rsp_fq));
-	qm_fqd_context_a_set64(&opts.fqd, hwdesc);
+	opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_DESTWQ |
+		       QM_INITFQ_WE_CONTEXTB | QM_INITFQ_WE_CONTEXTA |
+		       QM_INITFQ_WE_CGID;
+	opts.fqd.fq_ctrl = QM_FQCTRL_CPCSTASH | QM_FQCTRL_CGE;
+	opts.fqd.dest.channel = qm_channel_caam;
+	opts.fqd.dest.wq = 2;
+	opts.fqd.context_b = qman_fq_fqid(rsp_fq);
+	opts.fqd.context_a.hi = upper_32_bits(hwdesc);
+	opts.fqd.context_a.lo = lower_32_bits(hwdesc);
 	opts.fqd.cgid = qipriv.cgr.cgrid;
 
 	ret = qman_init_fq(req_fq, fq_sched_flag, &opts);
@@ -209,7 +209,7 @@ static struct qman_fq *create_caam_req_fq(struct device *qidev,
 	return req_fq;
 
 init_req_fq_fail:
-	qman_destroy_fq(req_fq);
+	qman_destroy_fq(req_fq, 0);
 create_req_fq_fail:
 	kfree(req_fq);
 	return ERR_PTR(ret);
@@ -277,7 +277,7 @@ static int kill_fq(struct device *qidev, struct qman_fq *fq)
 	if (ret)
 		dev_err(qidev, "OOS of FQID: %u failed\n", fq->fqid);
 
-	qman_destroy_fq(fq);
+	qman_destroy_fq(fq, 0);
 	kfree(fq);
 
 	return ret;
@@ -295,7 +295,7 @@ static int empty_caam_fq(struct qman_fq *fq, struct caam_drv_ctx *drv_ctx)
 		if (ret)
 			return ret;
 
-		if (!qm_mcr_np_get(&np, frm_cnt))
+		if (!np.frm_cnt)
 			break;
 
 		msleep(20);
@@ -571,14 +571,13 @@ static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p,
 	const struct qm_fd *fd;
 	struct device *qidev = &(raw_cpu_ptr(&pcpu_qipriv)->net_dev.dev);
 	struct caam_drv_private *priv = dev_get_drvdata(qidev);
-	u32 status;
 
 	if (caam_qi_napi_schedule(p, caam_napi))
 		return qman_cb_dqrr_stop;
 
 	fd = &dqrr->fd;
 
-	drv_req = caam_iova_to_virt(priv->domain, qm_fd_addr_get64(fd));
+	drv_req = caam_iova_to_virt(priv->domain, fd->addr);
 	if (unlikely(!drv_req)) {
 		dev_err(qidev,
 			"Can't find original request for caam response\n");
@@ -587,19 +586,18 @@ static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p,
 
 	refcount_dec(&drv_req->drv_ctx->refcnt);
 
-	status = be32_to_cpu(fd->status);
-	if (unlikely(status)) {
-		u32 ssrc = status & JRSTA_SSRC_MASK;
-		u8 err_id = status & JRSTA_CCBERR_ERRID_MASK;
+	if (unlikely(fd->status)) {
+		u32 ssrc = fd->status & JRSTA_SSRC_MASK;
+		u8 err_id = fd->status & JRSTA_CCBERR_ERRID_MASK;
 
 		if (ssrc != JRSTA_SSRC_CCB_ERROR ||
 		    err_id != JRSTA_CCBERR_ERRID_ICVCHK)
 			dev_err_ratelimited(qidev,
 					    "Error: %#x in CAAM response FD\n",
-					    status);
+					    fd->status);
 	}
 
-	if (unlikely(qm_fd_get_format(fd) != qm_fd_compound)) {
+	if (unlikely(fd->format != qm_fd_compound)) {
 		dev_err(qidev, "Non-compound FD from CAAM\n");
 		return qman_cb_dqrr_consume;
 	}
@@ -607,7 +605,7 @@ static enum qman_cb_dqrr_result caam_rsp_fq_dqrr_cb(struct qman_portal *p,
 	dma_unmap_single(drv_req->drv_ctx->qidev, qm_fd_addr(fd),
 			 sizeof(drv_req->fd_sgt), DMA_BIDIRECTIONAL);
 
-	drv_req->cbk(drv_req, status);
+	drv_req->cbk(drv_req, fd->status);
 	return qman_cb_dqrr_consume;
 }
 
@@ -631,17 +629,18 @@ static int alloc_rsp_fq_cpu(struct device *qidev, unsigned int cpu)
 		return -ENODEV;
 	}
 
-	memset(&opts, 0, sizeof(opts));
-	opts.we_mask = cpu_to_be16(QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_DESTWQ |
-				   QM_INITFQ_WE_CONTEXTB |
-				   QM_INITFQ_WE_CONTEXTA | QM_INITFQ_WE_CGID);
-	opts.fqd.fq_ctrl = cpu_to_be16(QM_FQCTRL_CTXASTASHING |
-				       QM_FQCTRL_CPCSTASH | QM_FQCTRL_CGE);
-	qm_fqd_set_destwq(&opts.fqd, qman_affine_channel(cpu), 3);
+	opts.we_mask = QM_INITFQ_WE_FQCTRL | QM_INITFQ_WE_DESTWQ |
+		QM_INITFQ_WE_CONTEXTB | QM_INITFQ_WE_CONTEXTA |
+		QM_INITFQ_WE_CGID;
+	opts.fqd.fq_ctrl = QM_FQCTRL_CTXASTASHING | QM_FQCTRL_CPCSTASH |
+			   QM_FQCTRL_CGE;
+	opts.fqd.dest.channel = qman_affine_channel(cpu);
+	opts.fqd.dest.wq = 3;
 	opts.fqd.cgid = qipriv.cgr.cgrid;
 	opts.fqd.context_a.stashing.exclusive =	QM_STASHING_EXCL_CTX |
 						QM_STASHING_EXCL_DATA;
-	qm_fqd_set_stashing(&opts.fqd, 0, 1, 1);
+	opts.fqd.context_a.stashing.data_cl = 1;
+	opts.fqd.context_a.stashing.context_cl = 1;
 
 	ret = qman_init_fq(fq, QMAN_INITFQ_FLAG_SCHED, &opts);
 	if (ret) {
@@ -671,8 +670,7 @@ static int init_cgr(struct device *qidev)
 
 	qipriv.cgr.cb = cgr_cb;
 	memset(&opts, 0, sizeof(opts));
-	opts.we_mask = cpu_to_be16(QM_CGR_WE_CSCN_EN | QM_CGR_WE_CS_THRES |
-				   QM_CGR_WE_MODE);
+	opts.we_mask = QM_CGR_WE_CSCN_EN | QM_CGR_WE_CS_THRES | QM_CGR_WE_MODE;
 	opts.cgr.cscn_en = QM_CGR_EN;
 	opts.cgr.mode = QMAN_CGR_MODE_FRAME;
 	qm_cgr_cs_thres_set64(&opts.cgr.cs_thres, val, 1);
diff --git a/drivers/crypto/caam/qi.h b/drivers/crypto/caam/qi.h
index 5894f16f8..aa68e37c8 100644
--- a/drivers/crypto/caam/qi.h
+++ b/drivers/crypto/caam/qi.h
@@ -9,7 +9,7 @@
 #ifndef __QI_H__
 #define __QI_H__
 
-#include <soc/fsl/qman.h>
+#include <linux/fsl_qman.h>
 #include "compat.h"
 #include "desc.h"
 #include "desc_constr.h"
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index 3738625c0..70ac8ad90 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -385,6 +385,12 @@ struct version_regs {
 #define CHA_VER_VID_MD_LP512	0x1ull
 #define CHA_VER_VID_MD_HP	0x2ull
 
+/*
+ * caam_perfmon - Performance Monitor/Secure Memory Status/
+ *                CAAM Global Status/Component Version IDs
+ *
+ * Spans f00-fff wherever instantiated
+ */
 struct sec_vid {
 	u16 ip_id;
 	u8 maj_rev;
@@ -415,17 +421,22 @@ struct caam_perfmon {
 #define CTPR_MS_PG_SZ_SHIFT	4
 	u32 comp_parms_ms;	/* CTPR - Compile Parameters Register	*/
 	u32 comp_parms_ls;	/* CTPR - Compile Parameters Register	*/
-	u64 rsvd1[2];
+	/* Secure Memory State Visibility */
+	u32 rsvd1;
+	u32 smstatus;	/* Secure memory status */
+	u32 rsvd2;
+	u32 smpartown;	/* Secure memory partition owner */
 
 	/* CAAM Global Status					fc0-fdf */
 	u64 faultaddr;	/* FAR  - Fault Address		*/
 	u32 faultliodn;	/* FALR - Fault Address LIODN	*/
 	u32 faultdetail;	/* FADR - Fault Addr Detail	*/
-	u32 rsvd2;
 #define CSTA_PLEND		BIT(10)
 #define CSTA_ALT_PLEND		BIT(18)
+	u32 rsvd3;
 	u32 status;		/* CSTA - CAAM Status */
-	u64 rsvd3;
+	u32 smpart;		/* Secure Memory Partition Parameters */
+	u32 smvid;		/* Secure Memory Version ID */
 
 	/* Component Instantiation Parameters			fe0-fff */
 	u32 rtic_id;		/* RVID - RTIC Version ID	*/
@@ -444,6 +455,62 @@ struct caam_perfmon {
 	u32 caam_id_ls;		/* CAAMVID - CAAM Version ID LS	*/
 };
 
+#define SMSTATUS_PART_SHIFT	28
+#define SMSTATUS_PART_MASK	(0xf << SMSTATUS_PART_SHIFT)
+#define SMSTATUS_PAGE_SHIFT	16
+#define SMSTATUS_PAGE_MASK	(0x7ff << SMSTATUS_PAGE_SHIFT)
+#define SMSTATUS_MID_SHIFT	8
+#define SMSTATUS_MID_MASK	(0x3f << SMSTATUS_MID_SHIFT)
+#define SMSTATUS_ACCERR_SHIFT	4
+#define SMSTATUS_ACCERR_MASK	(0xf << SMSTATUS_ACCERR_SHIFT)
+#define SMSTATUS_ACCERR_NONE	0
+#define SMSTATUS_ACCERR_ALLOC	1	/* Page not allocated */
+#define SMSTATUS_ACCESS_ID	2	/* Not granted by ID */
+#define SMSTATUS_ACCESS_WRITE	3	/* Writes not allowed */
+#define SMSTATUS_ACCESS_READ	4	/* Reads not allowed */
+#define SMSTATUS_ACCESS_NONKEY	6	/* Non-key reads not allowed */
+#define SMSTATUS_ACCESS_BLOB	9	/* Blob access not allowed */
+#define SMSTATUS_ACCESS_DESCB	10	/* Descriptor Blob access spans pages */
+#define SMSTATUS_ACCESS_NON_SM	11	/* Outside Secure Memory range */
+#define SMSTATUS_ACCESS_XPAGE	12	/* Access crosses pages */
+#define SMSTATUS_ACCESS_INITPG	13	/* Page still initializing */
+#define SMSTATUS_STATE_SHIFT	0
+#define SMSTATUS_STATE_MASK	(0xf << SMSTATUS_STATE_SHIFT)
+#define SMSTATUS_STATE_RESET	0
+#define SMSTATUS_STATE_INIT	1
+#define SMSTATUS_STATE_NORMAL	2
+#define SMSTATUS_STATE_FAIL	3
+
+/* up to 15 rings, 2 bits shifted by ring number */
+#define SMPARTOWN_RING_SHIFT	2
+#define SMPARTOWN_RING_MASK	3
+#define SMPARTOWN_AVAILABLE	0
+#define SMPARTOWN_NOEXIST	1
+#define SMPARTOWN_UNAVAILABLE	2
+#define SMPARTOWN_OURS		3
+
+/* Maximum number of pages possible */
+#define SMPART_MAX_NUMPG_SHIFT	16
+#define SMPART_MAX_NUMPG_MASK	(0x3f << SMPART_MAX_NUMPG_SHIFT)
+
+/* Maximum partition number */
+#define SMPART_MAX_PNUM_SHIFT	12
+#define SMPART_MAX_PNUM_MASK	(0xf << SMPART_MAX_PNUM_SHIFT)
+
+/* Highest possible page number */
+#define SMPART_MAX_PG_SHIFT	0
+#define SMPART_MAX_PG_MASK	(0x3f << SMPART_MAX_PG_SHIFT)
+
+/* Max size of a page */
+#define SMVID_PG_SIZE_SHIFT	16
+#define SMVID_PG_SIZE_MASK	(0x7 << SMVID_PG_SIZE_SHIFT)
+
+/* Major/Minor Version ID */
+#define SMVID_MAJ_VERS_SHIFT	8
+#define SMVID_MAJ_VERS		(0xf << SMVID_MAJ_VERS_SHIFT)
+#define SMVID_MIN_VERS_SHIFT	0
+#define SMVID_MIN_VERS		(0xf << SMVID_MIN_VERS_SHIFT)
+
 /* LIODN programming for DMA configuration */
 #define MSTRID_LOCK_LIODN	0x80000000
 #define MSTRID_LOCK_MAKETRUSTED	0x00010000	/* only for JR masterid */
@@ -454,12 +521,6 @@ struct masterid {
 	u32 liodn_ls;	/* LIODN for non-sequence and seq access */
 };
 
-/* Partition ID for DMA configuration */
-struct partid {
-	u32 rsvd1;
-	u32 pidr;	/* partition ID, DECO */
-};
-
 /* RNGB test mode (replicated twice in some configurations) */
 /* Padded out to 0x100 */
 struct rngtst {
@@ -518,6 +579,8 @@ struct rng4tst {
 #define RTSDCTL_ENT_DLY_MASK (0xffff << RTSDCTL_ENT_DLY_SHIFT)
 #define RTSDCTL_ENT_DLY_MIN 3200
 #define RTSDCTL_ENT_DLY_MAX 12800
+#define RTSDCTL_SAMP_SIZE_MASK 0xffff
+#define RTSDCTL_SAMP_SIZE_VAL 512
 	u32 rtsdctl;		/* seed control register */
 	union {
 		u32 rtsblim;	/* PRGM=1: sparse bit limit register */
@@ -529,7 +592,15 @@ struct rng4tst {
 		u32 rtfrqmax;	/* PRGM=1: freq. count max. limit register */
 		u32 rtfrqcnt;	/* PRGM=0: freq. count register */
 	};
-	u32 rsvd1[40];
+	union {
+		u32 rtscmc;	/* statistical check run monobit count */
+		u32 rtscml;	/* statistical check run monobit limit */
+	};
+	union {
+		u32 rtscrc[6];	/* statistical check run length count */
+		u32 rtscrl[6];	/* statistical check run length limit */
+	};
+	u32 rsvd1[33];
 #define RDSTA_SKVT 0x80000000
 #define RDSTA_SKVN 0x40000000
 #define RDSTA_PR0 BIT(4)
@@ -575,8 +646,7 @@ struct caam_ctrl {
 	u32 deco_rsr;			/* DECORSR - Deco Request Source */
 	u32 rsvd11;
 	u32 deco_rq;			/* DECORR - DECO Request */
-	struct partid 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 */
@@ -650,6 +720,35 @@ struct caam_ctrl {
 #define JRSTART_JR2_START       0x00000004 /* Start Job ring 2 */
 #define JRSTART_JR3_START       0x00000008 /* Start Job ring 3 */
 
+/* Secure Memory Configuration - if you have it */
+/* Secure Memory Register Offset from JR Base Reg*/
+#define SM_V1_OFFSET 0x0f4
+#define SM_V2_OFFSET 0xa00
+
+/* Minimum SM Version ID requiring v2 SM register mapping */
+#define SMVID_V2 0x20105
+
+struct caam_secure_mem_v1 {
+	u32 sm_cmd;	/* SMCJRx - Secure memory command */
+	u32 rsvd1;
+	u32 sm_status;	/* SMCSJRx - Secure memory status */
+	u32 rsvd2;
+
+	u32 sm_perm;	/* SMAPJRx - Secure memory access perms */
+	u32 sm_group2;	/* SMAP2JRx - Secure memory access group 2 */
+	u32 sm_group1;	/* SMAP1JRx - Secure memory access group 1 */
+};
+
+struct caam_secure_mem_v2 {
+	u32 sm_perm;	/* SMAPJRx - Secure memory access perms */
+	u32 sm_group2;	/* SMAP2JRx - Secure memory access group 2 */
+	u32 sm_group1;	/* SMAP1JRx - Secure memory access group 1 */
+	u32 rsvd1[118];
+	u32 sm_cmd;	/* SMCJRx - Secure memory command */
+	u32 rsvd2;
+	u32 sm_status;	/* SMCSJRx - Secure memory status */
+};
+
 /*
  * caam_job_ring - direct job ring setup
  * 1-4 possible per instantiation, base + 1000/2000/3000/4000
@@ -820,6 +919,62 @@ struct caam_job_ring {
 
 #define JRCR_RESET                  0x01
 
+/* secure memory command */
+#define SMC_PAGE_SHIFT	16
+#define SMC_PAGE_MASK	(0xffff << SMC_PAGE_SHIFT)
+#define SMC_PART_SHIFT	8
+#define SMC_PART_MASK	(0x0f << SMC_PART_SHIFT)
+#define SMC_CMD_SHIFT	0
+#define SMC_CMD_MASK	(0x0f << SMC_CMD_SHIFT)
+
+#define SMC_CMD_ALLOC_PAGE	0x01	/* allocate page to this partition */
+#define SMC_CMD_DEALLOC_PAGE	0x02	/* deallocate page from partition */
+#define SMC_CMD_DEALLOC_PART	0x03	/* deallocate partition */
+#define SMC_CMD_PAGE_INQUIRY	0x05	/* find partition associate with page */
+
+/* secure memory (command) status */
+#define SMCS_PAGE_SHIFT		16
+#define SMCS_PAGE_MASK		(0x0fff << SMCS_PAGE_SHIFT)
+#define SMCS_CMDERR_SHIFT	14
+#define SMCS_CMDERR_MASK	(3 << SMCS_CMDERR_SHIFT)
+#define SMCS_ALCERR_SHIFT	12
+#define SMCS_ALCERR_MASK	(3 << SMCS_ALCERR_SHIFT)
+#define SMCS_PGOWN_SHIFT	6
+#define SMCS_PGWON_MASK		(3 << SMCS_PGOWN_SHIFT)
+#define SMCS_PART_SHIFT		0
+#define SMCS_PART_MASK		(0xf << SMCS_PART_SHIFT)
+
+#define SMCS_CMDERR_NONE	0
+#define SMCS_CMDERR_INCOMP	1	/* Command not yet complete */
+#define SMCS_CMDERR_SECFAIL	2	/* Security failure occurred */
+#define SMCS_CMDERR_OVERFLOW	3	/* Command overflow */
+
+#define SMCS_ALCERR_NONE	0
+#define SMCS_ALCERR_PSPERR	1	/* Partion marked PSP (dealloc only) */
+#define SMCS_ALCERR_PAGEAVAIL	2	/* Page not available */
+#define SMCS_ALCERR_PARTOWN	3	/* Partition ownership error */
+
+#define SMCS_PGOWN_AVAIL	0	/* Page is available */
+#define SMCS_PGOWN_NOEXIST	1	/* Page initializing or nonexistent */
+#define SMCS_PGOWN_NOOWN	2	/* Page owned by another processor */
+#define SMCS_PGOWN_OWNED	3	/* Page belongs to this processor */
+
+/* secure memory access permissions */
+#define SMCS_PERM_KEYMOD_SHIFT	16
+#define SMCA_PERM_KEYMOD_MASK	(0xff << SMCS_PERM_KEYMOD_SHIFT)
+#define SMCA_PERM_CSP_ZERO	0x8000	/* Zero when deallocated or released */
+#define SMCA_PERM_PSP_LOCK	0x4000	/* Part./pages can't be deallocated */
+#define SMCA_PERM_PERM_LOCK	0x2000	/* Lock permissions */
+#define SMCA_PERM_GRP_LOCK	0x1000	/* Lock access groups */
+#define SMCA_PERM_RINGID_SHIFT	10
+#define SMCA_PERM_RINGID_MASK	(3 << SMCA_PERM_RINGID_SHIFT)
+#define SMCA_PERM_G2_BLOB	0x0080	/* Group 2 blob import/export */
+#define SMCA_PERM_G2_WRITE	0x0020	/* Group 2 write */
+#define SMCA_PERM_G2_READ	0x0010	/* Group 2 read */
+#define SMCA_PERM_G1_BLOB	0x0008	/* Group 1... */
+#define SMCA_PERM_G1_WRITE	0x0002
+#define SMCA_PERM_G1_READ	0x0001
+
 /*
  * caam_assurance - Assurance Controller View
  * base + 0x6000 padded out to 0x1000
diff --git a/drivers/crypto/caam/secvio.c b/drivers/crypto/caam/secvio.c
new file mode 100644
index 000000000..d6ebe0af4
--- /dev/null
+++ b/drivers/crypto/caam/secvio.c
@@ -0,0 +1,341 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * SNVS Security Violation Handler
+ *
+ * Copyright 2012-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017-2019 NXP
+ */
+
+#include "compat.h"
+#include "secvio.h"
+#include "regs.h"
+#include "intern.h"
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+
+/* The driver is matched with node caam_snvs to get regmap
+ * It will then retrieve interruption and tamper alarm configuration from
+ * node caam-secvio searching for the compat string "fsl,imx6q-caam-secvio"
+ */
+#define DRIVER_NAME "caam-snvs"
+
+/*
+ * These names are associated with each violation handler.
+ * The source names were taken from MX6, and are based on recommendations
+ * for most common SoCs.
+ */
+static const u8 *violation_src_name[] = {
+	"CAAM Internal Security Violation",
+	"JTAG Alarm",
+	"Watchdog",
+	"(reserved)",
+	"External Boot",
+	"External Tamper Detect",
+};
+
+/* These names help describe security monitor state for the console */
+static const u8 *snvs_ssm_state_name[] = {
+	"init",
+	"hard fail",
+	"(undef:2)",
+	"soft fail",
+	"(undef:4)",
+	"(undef:5)",
+	"(undef:6)",
+	"(undef:7)",
+	"transition",
+	"check",
+	"(undef:10)",
+	"non-secure",
+	"(undef:12)",
+	"trusted",
+	"(undef:14)",
+	"secure",
+};
+
+/* Top-level security violation interrupt */
+static irqreturn_t snvs_secvio_interrupt(int irq, void *snvsdev)
+{
+	struct device *dev = snvsdev;
+	struct snvs_secvio_drv_private *svpriv = dev_get_drvdata(dev);
+
+	clk_enable(svpriv->clk);
+	/* Check the HP secvio status register */
+	svpriv->irqcause = rd_reg32(&svpriv->svregs->hp.secvio_status) &
+				    HP_SECVIOST_SECVIOMASK;
+
+	if (!svpriv->irqcause) {
+		clk_disable(svpriv->clk);
+		return IRQ_NONE;
+	}
+
+	/* Now ACK cause */
+	clrsetbits_32(&svpriv->svregs->hp.secvio_status, 0, svpriv->irqcause);
+
+	/* And run deferred service */
+	preempt_disable();
+	tasklet_schedule(&svpriv->irqtask[smp_processor_id()]);
+	preempt_enable();
+
+	clk_disable(svpriv->clk);
+
+	return IRQ_HANDLED;
+}
+
+/* Deferred service handler. Tasklet arg is simply the SNVS dev */
+static void snvs_secvio_dispatch(unsigned long indev)
+{
+	struct device *dev = (struct device *)indev;
+	struct snvs_secvio_drv_private *svpriv = dev_get_drvdata(dev);
+	unsigned long flags;
+	int i;
+
+
+	/* Look through stored causes, call each handler if exists */
+	for (i = 0; i < MAX_SECVIO_SOURCES; i++)
+		if (svpriv->irqcause & (1 << i)) {
+			spin_lock_irqsave(&svpriv->svlock, flags);
+			svpriv->intsrc[i].handler(dev, i,
+						  svpriv->intsrc[i].ext);
+			spin_unlock_irqrestore(&svpriv->svlock, flags);
+		};
+
+	/* Re-enable now-serviced interrupts */
+	clrsetbits_32(&svpriv->svregs->hp.secvio_intcfg, 0, svpriv->irqcause);
+}
+
+/*
+ * Default cause handler, used in lieu of an application-defined handler.
+ * All it does at this time is print a console message. It could force a halt.
+ */
+static void snvs_secvio_default(struct device *dev, u32 cause, void *ext)
+{
+	struct snvs_secvio_drv_private *svpriv = dev_get_drvdata(dev);
+
+	dev_err(dev, "Unhandled Security Violation Interrupt %d = %s\n",
+		cause, svpriv->intsrc[cause].intname);
+}
+
+/*
+ * Install an application-defined handler for a specified cause
+ * Arguments:
+ * - dev        points to SNVS-owning device
+ * - cause      interrupt source cause
+ * - handler    application-defined handler, gets called with dev
+ *              source cause, and locally-defined handler argument
+ * - cause_description   points to a string to override the default cause
+ *                       name, this can be used as an alternate for error
+ *                       messages and such. If left NULL, the default
+ *                       description string is used.
+ * - ext        pointer to any extra data needed by the handler.
+ */
+int snvs_secvio_install_handler(struct device *dev, enum secvio_cause cause,
+				void (*handler)(struct device *dev, u32 cause,
+						void *ext),
+				u8 *cause_description, void *ext)
+{
+	unsigned long flags;
+	struct snvs_secvio_drv_private *svpriv;
+
+	svpriv = dev_get_drvdata(dev);
+
+	if ((handler == NULL) || (cause > SECVIO_CAUSE_SOURCE_5))
+		return -EINVAL;
+
+	spin_lock_irqsave(&svpriv->svlock, flags);
+	svpriv->intsrc[cause].handler = handler;
+	if (cause_description != NULL)
+		svpriv->intsrc[cause].intname = cause_description;
+	if (ext != NULL)
+		svpriv->intsrc[cause].ext = ext;
+	spin_unlock_irqrestore(&svpriv->svlock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(snvs_secvio_install_handler);
+
+/*
+ * Remove an application-defined handler for a specified cause (and, by
+ * implication, restore the "default".
+ * Arguments:
+ * - dev	points to SNVS-owning device
+ * - cause	interrupt source cause
+ */
+int snvs_secvio_remove_handler(struct device *dev, enum secvio_cause cause)
+{
+	unsigned long flags;
+	struct snvs_secvio_drv_private *svpriv;
+
+	svpriv = dev_get_drvdata(dev);
+
+	if (cause > SECVIO_CAUSE_SOURCE_5)
+		return -EINVAL;
+
+	spin_lock_irqsave(&svpriv->svlock, flags);
+	svpriv->intsrc[cause].intname = violation_src_name[cause];
+	svpriv->intsrc[cause].handler = snvs_secvio_default;
+	svpriv->intsrc[cause].ext = NULL;
+	spin_unlock_irqrestore(&svpriv->svlock, flags);
+	return 0;
+}
+EXPORT_SYMBOL(snvs_secvio_remove_handler);
+
+static int snvs_secvio_remove(struct platform_device *pdev)
+{
+	struct device *svdev;
+	struct snvs_secvio_drv_private *svpriv;
+	int i;
+
+	svdev = &pdev->dev;
+	svpriv = dev_get_drvdata(svdev);
+
+	clk_enable(svpriv->clk);
+	/* Set all sources to nonfatal */
+	wr_reg32(&svpriv->svregs->hp.secvio_intcfg, 0);
+
+	/* Remove tasklets and release interrupt */
+	for_each_possible_cpu(i)
+		tasklet_kill(&svpriv->irqtask[i]);
+
+	clk_disable_unprepare(svpriv->clk);
+	free_irq(svpriv->irq, svdev);
+	iounmap(svpriv->svregs);
+	kfree(svpriv);
+
+	return 0;
+}
+
+static int snvs_secvio_probe(struct platform_device *pdev)
+{
+	struct device *svdev;
+	struct snvs_secvio_drv_private *svpriv;
+	struct device_node *np, *npirq;
+	struct snvs_full __iomem *snvsregs;
+	int i, error;
+	u32 hpstate;
+	const void *jtd, *wtd, *itd, *etd;
+	u32 td_en;
+
+	svpriv = kzalloc(sizeof(struct snvs_secvio_drv_private), GFP_KERNEL);
+	if (!svpriv)
+		return -ENOMEM;
+
+	svdev = &pdev->dev;
+	dev_set_drvdata(svdev, svpriv);
+	svpriv->pdev = pdev;
+	spin_lock_init(&svpriv->svlock);
+	np = pdev->dev.of_node;
+
+	npirq = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-secvio");
+	if (!npirq) {
+		dev_err(svdev, "can't find secvio node\n");
+		kfree(svpriv);
+		return -EINVAL;
+	}
+	svpriv->irq = irq_of_parse_and_map(npirq, 0);
+	if (svpriv->irq <= 0) {
+		dev_err(svdev, "can't identify secvio interrupt\n");
+		kfree(svpriv);
+		return -EINVAL;
+	}
+
+	jtd = of_get_property(npirq, "jtag-tamper", NULL);
+	wtd = of_get_property(npirq, "watchdog-tamper", NULL);
+	itd = of_get_property(npirq, "internal-boot-tamper", NULL);
+	etd = of_get_property(npirq, "external-pin-tamper", NULL);
+	if (!jtd | !wtd | !itd | !etd ) {
+		dev_err(svdev, "can't identify all tamper alarm configuration\n");
+		kfree(svpriv);
+		return -EINVAL;
+	}
+
+	/*
+	 * Configure all sources  according to device tree property.
+	 * If the property is enabled then the source is ser as
+	 * fatal violations except LP section,
+	 * source #5 (typically used as an external tamper detect), and
+	 * source #3 (typically unused). Whenever the transition to
+	 * secure mode has occurred, these will now be "fatal" violations
+	 */
+	td_en = HP_SECVIO_INTEN_SRC0;
+	if (!strcmp(jtd, "enabled"))
+		td_en |= HP_SECVIO_INTEN_SRC1;
+	if (!strcmp(wtd, "enabled"))
+		td_en |= HP_SECVIO_INTEN_SRC2;
+	if (!strcmp(itd, "enabled"))
+		td_en |= HP_SECVIO_INTEN_SRC4;
+	if (!strcmp(etd, "enabled"))
+		td_en |= HP_SECVIO_INTEN_SRC5;
+
+	snvsregs = of_iomap(np, 0);
+	if (!snvsregs) {
+		dev_err(svdev, "register mapping failed\n");
+		return -ENOMEM;
+	}
+	svpriv->svregs = (struct snvs_full __force *)snvsregs;
+
+	svpriv->clk = devm_clk_get_optional(&pdev->dev, "ipg");
+	if (IS_ERR(svpriv->clk))
+		return PTR_ERR(svpriv->clk);
+
+	clk_prepare_enable(svpriv->clk);
+
+	/* Write the Secvio Enable Config the SVCR */
+	wr_reg32(&svpriv->svregs->hp.secvio_ctl, td_en);
+	wr_reg32(&svpriv->svregs->hp.secvio_intcfg, td_en);
+
+	 /* Device data set up. Now init interrupt source descriptions */
+	for (i = 0; i < MAX_SECVIO_SOURCES; i++) {
+		svpriv->intsrc[i].intname = violation_src_name[i];
+		svpriv->intsrc[i].handler = snvs_secvio_default;
+	}
+	/* Connect main handler */
+	for_each_possible_cpu(i)
+		tasklet_init(&svpriv->irqtask[i], snvs_secvio_dispatch,
+			     (unsigned long)svdev);
+
+	error = request_irq(svpriv->irq, snvs_secvio_interrupt,
+			    IRQF_SHARED, DRIVER_NAME, svdev);
+	if (error) {
+		dev_err(svdev, "can't connect secvio interrupt\n");
+		irq_dispose_mapping(svpriv->irq);
+		svpriv->irq = 0;
+		iounmap(svpriv->svregs);
+		kfree(svpriv);
+		return -EINVAL;
+	}
+
+	hpstate = (rd_reg32(&svpriv->svregs->hp.status) &
+			    HP_STATUS_SSM_ST_MASK) >> HP_STATUS_SSM_ST_SHIFT;
+	dev_info(svdev, "violation handlers armed - %s state\n",
+		 snvs_ssm_state_name[hpstate]);
+
+	clk_disable(svpriv->clk);
+
+	return 0;
+}
+
+static struct of_device_id snvs_secvio_match[] = {
+	{
+		.compatible = "fsl,imx6q-caam-snvs",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, snvs_secvio_match);
+
+static struct platform_driver snvs_secvio_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = snvs_secvio_match,
+	},
+	.probe       = snvs_secvio_probe,
+	.remove      = snvs_secvio_remove,
+};
+
+module_platform_driver(snvs_secvio_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL SNVS Security Violation Handler");
+MODULE_AUTHOR("Freescale Semiconductor - MCU");
diff --git a/drivers/crypto/caam/secvio.h b/drivers/crypto/caam/secvio.h
new file mode 100644
index 000000000..a7a245a3d
--- /dev/null
+++ b/drivers/crypto/caam/secvio.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * CAAM Security Violation Handler
+ *
+ * Copyright 2012-2015 Freescale Semiconductor, Inc.
+ * Copyright 2016-2019 NXP
+ */
+
+#ifndef SECVIO_H
+#define SECVIO_H
+
+#include "snvsregs.h"
+
+
+/*
+ * Defines the published interfaces to install/remove application-specified
+ * handlers for catching violations
+ */
+
+#define MAX_SECVIO_SOURCES 6
+
+/* these are the untranslated causes */
+enum secvio_cause {
+	SECVIO_CAUSE_SOURCE_0,
+	SECVIO_CAUSE_SOURCE_1,
+	SECVIO_CAUSE_SOURCE_2,
+	SECVIO_CAUSE_SOURCE_3,
+	SECVIO_CAUSE_SOURCE_4,
+	SECVIO_CAUSE_SOURCE_5
+};
+
+/* These are common "recommended" cause definitions for most devices */
+#define SECVIO_CAUSE_CAAM_VIOLATION	SECVIO_CAUSE_SOURCE_0
+#define SECVIO_CAUSE_JTAG_ALARM		SECVIO_CAUSE_SOURCE_1
+#define SECVIO_CAUSE_WATCHDOG		SECVIO_CAUSE_SOURCE_2
+#define SECVIO_CAUSE_EXTERNAL_BOOT	SECVIO_CAUSE_SOURCE_4
+#define SECVIO_CAUSE_TAMPER_DETECT	SECVIO_CAUSE_SOURCE_5
+
+int snvs_secvio_install_handler(struct device *dev, enum secvio_cause cause,
+				void (*handler)(struct device *dev, u32 cause,
+						void *ext),
+				u8 *cause_description, void *ext);
+int snvs_secvio_remove_handler(struct device *dev, enum  secvio_cause cause);
+
+/*
+ * Private data definitions for the secvio "driver"
+ */
+
+struct secvio_int_src {
+	const u8 *intname;	/* Points to a descriptive name for source */
+	void *ext;		/* Extended data to pass to the handler */
+	void (*handler)(struct device *dev, u32 cause, void *ext);
+};
+
+struct snvs_secvio_drv_private {
+	struct platform_device *pdev;
+	spinlock_t svlock ____cacheline_aligned;
+	struct tasklet_struct irqtask[NR_CPUS];
+	struct snvs_full __iomem *svregs;	/* both HP and LP domains */
+	struct clk *clk;
+	int irq;
+	u32 irqcause; /* stashed cause of violation interrupt */
+
+	/* Registered handlers for each violation */
+	struct secvio_int_src intsrc[MAX_SECVIO_SOURCES];
+
+};
+
+#endif /* SECVIO_H */
diff --git a/drivers/crypto/caam/sg_sw_qm.h b/drivers/crypto/caam/sg_sw_qm.h
index d56cc7efb..9465b5773 100644
--- a/drivers/crypto/caam/sg_sw_qm.h
+++ b/drivers/crypto/caam/sg_sw_qm.h
@@ -7,46 +7,61 @@
 #ifndef __SG_SW_QM_H
 #define __SG_SW_QM_H
 
-#include <soc/fsl/qman.h>
+#include <linux/fsl_qman.h>
 #include "regs.h"
 
+static inline void cpu_to_hw_sg(struct qm_sg_entry *qm_sg_ptr)
+{
+	dma_addr_t addr = qm_sg_ptr->opaque;
+
+	qm_sg_ptr->opaque = cpu_to_caam64(addr);
+	qm_sg_ptr->sgt_efl = cpu_to_caam32(qm_sg_ptr->sgt_efl);
+}
+
 static inline void __dma_to_qm_sg(struct qm_sg_entry *qm_sg_ptr, dma_addr_t dma,
-				  u16 offset)
+				  u32 len, u16 offset)
 {
-	qm_sg_entry_set64(qm_sg_ptr, dma);
+	qm_sg_ptr->addr = dma;
+	qm_sg_ptr->length = len;
 	qm_sg_ptr->__reserved2 = 0;
 	qm_sg_ptr->bpid = 0;
-	qm_sg_ptr->offset = cpu_to_be16(offset & QM_SG_OFF_MASK);
+	qm_sg_ptr->__reserved3 = 0;
+	qm_sg_ptr->offset = offset & QM_SG_OFFSET_MASK;
+
+	cpu_to_hw_sg(qm_sg_ptr);
 }
 
 static inline void dma_to_qm_sg_one(struct qm_sg_entry *qm_sg_ptr,
 				    dma_addr_t dma, u32 len, u16 offset)
 {
-	__dma_to_qm_sg(qm_sg_ptr, dma, offset);
-	qm_sg_entry_set_len(qm_sg_ptr, len);
+	qm_sg_ptr->extension = 0;
+	qm_sg_ptr->final = 0;
+	__dma_to_qm_sg(qm_sg_ptr, dma, len, offset);
 }
 
 static inline void dma_to_qm_sg_one_last(struct qm_sg_entry *qm_sg_ptr,
 					 dma_addr_t dma, u32 len, u16 offset)
 {
-	__dma_to_qm_sg(qm_sg_ptr, dma, offset);
-	qm_sg_entry_set_f(qm_sg_ptr, len);
+	qm_sg_ptr->extension = 0;
+	qm_sg_ptr->final = 1;
+	__dma_to_qm_sg(qm_sg_ptr, dma, len, offset);
 }
 
 static inline void dma_to_qm_sg_one_ext(struct qm_sg_entry *qm_sg_ptr,
 					dma_addr_t dma, u32 len, u16 offset)
 {
-	__dma_to_qm_sg(qm_sg_ptr, dma, offset);
-	qm_sg_ptr->cfg = cpu_to_be32(QM_SG_EXT | (len & QM_SG_LEN_MASK));
+	qm_sg_ptr->extension = 1;
+	qm_sg_ptr->final = 0;
+	__dma_to_qm_sg(qm_sg_ptr, dma, len, offset);
 }
 
 static inline void dma_to_qm_sg_one_last_ext(struct qm_sg_entry *qm_sg_ptr,
 					     dma_addr_t dma, u32 len,
 					     u16 offset)
 {
-	__dma_to_qm_sg(qm_sg_ptr, dma, offset);
-	qm_sg_ptr->cfg = cpu_to_be32(QM_SG_EXT | QM_SG_FIN |
-				     (len & QM_SG_LEN_MASK));
+	qm_sg_ptr->extension = 1;
+	qm_sg_ptr->final = 1;
+	__dma_to_qm_sg(qm_sg_ptr, dma, len, offset);
 }
 
 /*
@@ -79,7 +94,10 @@ static inline void sg_to_qm_sg_last(struct scatterlist *sg, int len,
 				    struct qm_sg_entry *qm_sg_ptr, u16 offset)
 {
 	qm_sg_ptr = sg_to_qm_sg(sg, len, qm_sg_ptr, offset);
-	qm_sg_entry_set_f(qm_sg_ptr, qm_sg_entry_get_len(qm_sg_ptr));
+
+	qm_sg_ptr->sgt_efl = caam32_to_cpu(qm_sg_ptr->sgt_efl);
+	qm_sg_ptr->final = 1;
+	qm_sg_ptr->sgt_efl = cpu_to_caam32(qm_sg_ptr->sgt_efl);
 }
 
 #endif /* __SG_SW_QM_H */
diff --git a/drivers/crypto/caam/sm.h b/drivers/crypto/caam/sm.h
new file mode 100644
index 000000000..614c9b4d3
--- /dev/null
+++ b/drivers/crypto/caam/sm.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * CAAM Secure Memory/Keywrap API Definitions
+ *
+ * Copyright 2008-2015 Freescale Semiconductor, Inc.
+ * Copyright 2016-2019 NXP
+ */
+
+#ifndef SM_H
+#define SM_H
+
+
+/* Storage access permissions */
+#define SM_PERM_READ 0x01
+#define SM_PERM_WRITE 0x02
+#define SM_PERM_BLOB 0x03
+
+/* Define treatment of secure memory vs. general memory blobs */
+#define SM_SECMEM 0
+#define SM_GENMEM 1
+
+/* Define treatment of red/black keys */
+#define RED_KEY 0
+#define BLACK_KEY 1
+
+/* Define key encryption/covering options */
+#define KEY_COVER_ECB 0	/* cover key in AES-ECB */
+#define KEY_COVER_CCM 1 /* cover key with AES-CCM */
+
+/*
+ * Round a key size up to an AES blocksize boundary so to allow for
+ * padding out to a full block
+ */
+#define AES_BLOCK_PAD(x) ((x % 16) ? ((x >> 4) + 1) << 4 : x)
+
+/* Define space required for BKEK + MAC tag storage in any blob */
+#define BLOB_OVERHEAD (32 + 16)
+
+/* Keystore maintenance functions */
+void sm_init_keystore(struct device *dev);
+u32 sm_detect_keystore_units(struct device *dev);
+int sm_establish_keystore(struct device *dev, u32 unit);
+void sm_release_keystore(struct device *dev, u32 unit);
+int caam_sm_example_init(struct platform_device *pdev);
+
+/* Keystore accessor functions */
+extern int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size,
+				  u32 *slot);
+extern int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot);
+extern int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot,
+				 const u8 *key_data, u32 key_length);
+extern int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot,
+				 u32 key_length, u8 *key_data);
+extern int sm_keystore_cover_key(struct device *dev, u32 unit, u32 slot,
+				 u16 key_length, u8 keyauth);
+extern int sm_keystore_slot_export(struct device *dev, u32 unit, u32 slot,
+				   u8 keycolor, u8 keyauth, u8 *outbuf,
+				   u16 keylen, u8 *keymod);
+extern int sm_keystore_slot_import(struct device *dev, u32 unit, u32 slot,
+				   u8 keycolor, u8 keyauth, u8 *inbuf,
+				   u16 keylen, u8 *keymod);
+
+/* Prior functions from legacy API, deprecated */
+extern int sm_keystore_slot_encapsulate(struct device *dev, u32 unit,
+					u32 inslot, u32 outslot, u16 secretlen,
+					u8 *keymod, u16 keymodlen);
+extern int sm_keystore_slot_decapsulate(struct device *dev, u32 unit,
+					u32 inslot, u32 outslot, u16 secretlen,
+					u8 *keymod, u16 keymodlen);
+
+/* Data structure to hold per-slot information */
+struct keystore_data_slot_info {
+	u8	allocated;	/* Track slot assignments */
+	u32	key_length;	/* Size of the key */
+};
+
+/* Data structure to hold keystore information */
+struct keystore_data {
+	void	*base_address;	/* Virtual base of secure memory pages */
+	void	*phys_address;	/* Physical base of secure memory pages */
+	u32	slot_count;	/* Number of slots in the keystore */
+	struct keystore_data_slot_info *slot; /* Per-slot information */
+};
+
+/* store the detected attributes of a secure memory page */
+struct sm_page_descriptor {
+	u16 phys_pagenum;	/* may be discontiguous */
+	u16 own_part;		/* Owning partition */
+	void *pg_base;		/* Calculated virtual address */
+	void *pg_phys;		/* Calculated physical address */
+	struct keystore_data *ksdata;
+};
+
+struct caam_drv_private_sm {
+	struct device *parentdev;	/* this ends up as the controller */
+	struct device *smringdev;	/* ring that owns this instance */
+	struct platform_device *sm_pdev;  /* Secure Memory platform device */
+	spinlock_t kslock ____cacheline_aligned;
+
+	/* SM Register offset from JR base address */
+	u32 sm_reg_offset;
+
+	/* Default parameters for geometry */
+	u32 max_pages;		/* maximum pages this instance can support */
+	u32 top_partition;	/* highest partition number in this instance */
+	u32 top_page;		/* highest page number in this instance */
+	u32 page_size;		/* page size */
+	u32 slot_size;		/* selected size of each storage block */
+
+	/* Partition/Page Allocation Map */
+	u32 localpages;		/* Number of pages we can access */
+	struct sm_page_descriptor *pagedesc;	/* Allocated per-page */
+
+	/* Installed handlers for keystore access */
+	int (*data_init)(struct device *dev, u32 unit);
+	void (*data_cleanup)(struct device *dev, u32 unit);
+	int (*slot_alloc)(struct device *dev, u32 unit, u32 size, u32 *slot);
+	int (*slot_dealloc)(struct device *dev, u32 unit, u32 slot);
+	void *(*slot_get_address)(struct device *dev, u32 unit, u32 handle);
+	void *(*slot_get_physical)(struct device *dev, u32 unit, u32 handle);
+	u32 (*slot_get_base)(struct device *dev, u32 unit, u32 handle);
+	u32 (*slot_get_offset)(struct device *dev, u32 unit, u32 handle);
+	u32 (*slot_get_slot_size)(struct device *dev, u32 unit, u32 handle);
+};
+
+#endif /* SM_H */
diff --git a/drivers/crypto/caam/sm_store.c b/drivers/crypto/caam/sm_store.c
new file mode 100644
index 000000000..88e0706e9
--- /dev/null
+++ b/drivers/crypto/caam/sm_store.c
@@ -0,0 +1,1270 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * CAAM Secure Memory Storage Interface
+ *
+ * Copyright 2008-2015 Freescale Semiconductor, Inc.
+ * Copyright 2016-2019 NXP
+ *
+ * Loosely based on the SHW Keystore API for SCC/SCC2
+ * Experimental implementation and NOT intended for upstream use. Expect
+ * this interface to be amended significantly in the future once it becomes
+ * integrated into live applications.
+ *
+ * Known issues:
+ *
+ * - Executes one instance of an secure memory "driver". This is tied to the
+ *   fact that job rings can't run as standalone instances in the present
+ *   configuration.
+ *
+ * - It does not expose a userspace interface. The value of a userspace
+ *   interface for access to secrets is a point for further architectural
+ *   discussion.
+ *
+ * - Partition/permission management is not part of this interface. It
+ *   depends on some level of "knowledge" agreed upon between bootloader,
+ *   provisioning applications, and OS-hosted software (which uses this
+ *   driver).
+ *
+ * - No means of identifying the location or purpose of secrets managed by
+ *   this interface exists; "slot location" and format of a given secret
+ *   needs to be agreed upon between bootloader, provisioner, and OS-hosted
+ *   application.
+ */
+
+#include "compat.h"
+#include "regs.h"
+#include "jr.h"
+#include "desc.h"
+#include "intern.h"
+#include "error.h"
+#include "sm.h"
+#include <linux/of_address.h>
+
+#define SECMEM_KEYMOD_LEN 8
+#define GENMEM_KEYMOD_LEN 16
+
+#ifdef SM_DEBUG_CONT
+void sm_show_page(struct device *dev, struct sm_page_descriptor *pgdesc)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	u32 i, *smdata;
+
+	dev_info(dev, "physical page %d content at 0x%08x\n",
+		 pgdesc->phys_pagenum, pgdesc->pg_base);
+	smdata = pgdesc->pg_base;
+	for (i = 0; i < (smpriv->page_size / sizeof(u32)); i += 4)
+		dev_info(dev, "[0x%08x] 0x%08x 0x%08x 0x%08x 0x%08x\n",
+			 (u32)&smdata[i], smdata[i], smdata[i+1], smdata[i+2],
+			 smdata[i+3]);
+}
+#endif
+
+#define INITIAL_DESCSZ 16	/* size of tmp buffer for descriptor const. */
+
+static __always_inline u32 sm_send_cmd(struct caam_drv_private_sm *smpriv,
+					     struct caam_drv_private_jr *jrpriv,
+					     u32 cmd, u32 *status)
+{
+	void __iomem *write_address;
+	void __iomem *read_address;
+
+	if (smpriv->sm_reg_offset == SM_V1_OFFSET) {
+		struct caam_secure_mem_v1 *sm_regs_v1;
+
+		sm_regs_v1 = (struct caam_secure_mem_v1 *)
+			((void *)jrpriv->rregs + SM_V1_OFFSET);
+		write_address = &sm_regs_v1->sm_cmd;
+		read_address = &sm_regs_v1->sm_status;
+
+	} else if (smpriv->sm_reg_offset == SM_V2_OFFSET) {
+		struct caam_secure_mem_v2 *sm_regs_v2;
+
+		sm_regs_v2 = (struct caam_secure_mem_v2 *)
+			((void *)jrpriv->rregs + SM_V2_OFFSET);
+		write_address = &sm_regs_v2->sm_cmd;
+		read_address = &sm_regs_v2->sm_status;
+
+	} else {
+		return -EINVAL;
+	}
+
+	wr_reg32(write_address, cmd);
+
+	udelay(10);
+
+	/* Read until the command has terminated and the status is correct */
+	do {
+		*status = rd_reg32(read_address);
+	} while (((*status & SMCS_CMDERR_MASK) >>  SMCS_CMDERR_SHIFT)
+				   == SMCS_CMDERR_INCOMP);
+
+	return 0;
+}
+
+/*
+ * Construct a black key conversion job descriptor
+ *
+ * This function constructs a job descriptor capable of performing
+ * a key blackening operation on a plaintext secure memory resident object.
+ *
+ * - desc	pointer to a pointer to the descriptor generated by this
+ *		function. Caller will be responsible to kfree() this
+ *		descriptor after execution.
+ * - key	physical pointer to the plaintext, which will also hold
+ *		the result. Since encryption occurs in place, caller must
+ *              ensure that the space is large enough to accommodate the
+ *              blackened key
+ * - keysz	size of the plaintext
+ * - auth	if a CCM-covered key is required, use KEY_COVER_CCM, else
+ *		use KEY_COVER_ECB.
+ *
+ * KEY to key1 from @key_addr LENGTH 16 BYTES;
+ * FIFO STORE from key1[ecb] TO @key_addr LENGTH 16 BYTES;
+ *
+ * Note that this variant uses the JDKEK only; it does not accommodate the
+ * trusted key encryption key at this time.
+ *
+ */
+static int blacken_key_jobdesc(u32 **desc, void *key, u16 keysz, bool auth)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize, idx;
+
+	memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32));
+	idx = 1;
+
+	/* Load key to class 1 key register */
+	tmpdesc[idx++] = CMD_KEY | CLASS_1 | (keysz & KEY_LENGTH_MASK);
+	tmpdesc[idx++] = (uintptr_t)key;
+
+	/* ...and write back out via FIFO store*/
+	tmpdesc[idx] = CMD_FIFO_STORE | CLASS_1 | (keysz & KEY_LENGTH_MASK);
+
+	/* plus account for ECB/CCM option in FIFO_STORE */
+	if (auth == KEY_COVER_ECB)
+		tmpdesc[idx] |= FIFOST_TYPE_KEY_KEK;
+	else
+		tmpdesc[idx] |= FIFOST_TYPE_KEY_CCM_JKEK;
+
+	idx++;
+	tmpdesc[idx++] = (uintptr_t)key;
+
+	/* finish off the job header */
+	tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK);
+	dsize = idx * sizeof(u32);
+
+	/* now allocate execution buffer and coat it with executable */
+	tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA);
+	if (tdesc == NULL)
+		return 0;
+
+	memcpy(tdesc, tmpdesc, dsize);
+	*desc = tdesc;
+
+	return dsize;
+}
+
+/*
+ * Construct a blob encapsulation job descriptor
+ *
+ * This function dynamically constructs a blob encapsulation job descriptor
+ * from the following arguments:
+ *
+ * - desc	pointer to a pointer to the descriptor generated by this
+ *		function. Caller will be responsible to kfree() this
+ *		descriptor after execution.
+ * - keymod	Physical pointer to a key modifier, which must reside in a
+ *		contiguous piece of memory. Modifier will be assumed to be
+ *		8 bytes long for a blob of type SM_SECMEM, or 16 bytes long
+ *		for a blob of type SM_GENMEM (see blobtype argument).
+ * - secretbuf	Physical pointer to a secret, normally a black or red key,
+ *		possibly residing within an accessible secure memory page,
+ *		of the secret to be encapsulated to an output blob.
+ * - outbuf	Physical pointer to the destination buffer to receive the
+ *		encapsulated output. This buffer will need to be 48 bytes
+ *		larger than the input because of the added encapsulation data.
+ *		The generated descriptor will account for the increase in size,
+ *		but the caller must also account for this increase in the
+ *		buffer allocator.
+ * - secretsz	Size of input secret, in bytes. This is limited to 65536
+ *		less the size of blob overhead, since the length embeds into
+ *		DECO pointer in/out instructions.
+ * - keycolor   Determines if the source data is covered (black key) or
+ *		plaintext (red key). RED_KEY or BLACK_KEY are defined in
+ *		for this purpose.
+ * - blobtype	Determine if encapsulated blob should be a secure memory
+ *		blob (SM_SECMEM), with partition data embedded with key
+ *		material, or a general memory blob (SM_GENMEM).
+ * - auth	If BLACK_KEY source is covered via AES-CCM, specify
+ *		KEY_COVER_CCM, else uses AES-ECB (KEY_COVER_ECB).
+ *
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable blob
+ * suitable for use across power cycles.
+ *
+ * This is an example of a black key encapsulation job into a general memory
+ * blob. Notice the 16-byte key modifier in the LOAD instruction. Also note
+ * the output 48 bytes longer than the input:
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400010           ld: ccb2-key len=16 offs=0
+ * [02] 08144891               ptr->@0x08144891
+ * [03] F800003A    seqoutptr: len=58
+ * [04] 01000000               out_ptr->@0x01000000
+ * [05] F000000A     seqinptr: len=10
+ * [06] 09745090               in_ptr->@0x09745090
+ * [07] 870D0004    operation: encap blob  reg=memory, black, format=normal
+ *
+ * This is an example of a red key encapsulation job for storing a red key
+ * into a secure memory blob. Note the 8 byte modifier on the 12 byte offset
+ * in the LOAD instruction; this accounts for blob permission storage:
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400C08           ld: ccb2-key len=8 offs=12
+ * [02] 087D0784               ptr->@0x087d0784
+ * [03] F8000050    seqoutptr: len=80
+ * [04] 09251BB2               out_ptr->@0x09251bb2
+ * [05] F0000020     seqinptr: len=32
+ * [06] 40000F31               in_ptr->@0x40000f31
+ * [07] 870D0008    operation: encap blob  reg=memory, red, sec_mem,
+ *                             format=normal
+ *
+ * Note: this function only generates 32-bit pointers at present, and should
+ * be refactored using a scheme that allows both 32 and 64 bit addressing
+ */
+
+static int blob_encap_jobdesc(u32 **desc, dma_addr_t keymod,
+			      void *secretbuf, dma_addr_t outbuf,
+			      u16 secretsz, u8 keycolor, u8 blobtype, u8 auth)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize, idx;
+
+	memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32));
+	idx = 1;
+
+	/*
+	 * Key modifier works differently for secure/general memory blobs
+	 * This accounts for the permission/protection data encapsulated
+	 * within the blob if a secure memory blob is requested
+	 */
+	if (blobtype == SM_SECMEM)
+		tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB |
+				 LDST_SRCDST_BYTE_KEY |
+				 ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK)
+				 | (8 & LDST_LEN_MASK);
+	else /* is general memory blob */
+		tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB |
+				 LDST_SRCDST_BYTE_KEY | (16 & LDST_LEN_MASK);
+
+	tmpdesc[idx++] = (u32)keymod;
+
+	/*
+	 * Encapsulation output must include space for blob key encryption
+	 * key and MAC tag
+	 */
+	tmpdesc[idx++] = CMD_SEQ_OUT_PTR | (secretsz + BLOB_OVERHEAD);
+	tmpdesc[idx++] = (u32)outbuf;
+
+	/* Input data, should be somewhere in secure memory */
+	tmpdesc[idx++] = CMD_SEQ_IN_PTR | secretsz;
+	tmpdesc[idx++] = (uintptr_t)secretbuf;
+
+	/* Set blob encap, then color */
+	tmpdesc[idx] = CMD_OPERATION | OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB;
+
+	if (blobtype == SM_SECMEM)
+		tmpdesc[idx] |= OP_PCL_BLOB_PTXT_SECMEM;
+
+	if (auth == KEY_COVER_CCM)
+		tmpdesc[idx] |= OP_PCL_BLOB_EKT;
+
+	if (keycolor == BLACK_KEY)
+		tmpdesc[idx] |= OP_PCL_BLOB_BLACK;
+
+	idx++;
+	tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK);
+	dsize = idx * sizeof(u32);
+
+	tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA);
+	if (tdesc == NULL)
+		return 0;
+
+	memcpy(tdesc, tmpdesc, dsize);
+	*desc = tdesc;
+	return dsize;
+}
+
+/*
+ * Construct a blob decapsulation job descriptor
+ *
+ * This function dynamically constructs a blob decapsulation job descriptor
+ * from the following arguments:
+ *
+ * - desc	pointer to a pointer to the descriptor generated by this
+ *		function. Caller will be responsible to kfree() this
+ *		descriptor after execution.
+ * - keymod	Physical pointer to a key modifier, which must reside in a
+ *		contiguous piece of memory. Modifier will be assumed to be
+ *		8 bytes long for a blob of type SM_SECMEM, or 16 bytes long
+ *		for a blob of type SM_GENMEM (see blobtype argument).
+ * - blobbuf	Physical pointer (into external memory) of the blob to
+ *		be decapsulated. Blob must reside in a contiguous memory
+ *		segment.
+ * - outbuf	Physical pointer of the decapsulated output, possibly into
+ *		a location within a secure memory page. Must be contiguous.
+ * - secretsz	Size of encapsulated secret in bytes (not the size of the
+ *		input blob).
+ * - keycolor   Determines if decapsulated content is encrypted (BLACK_KEY)
+ *		or left as plaintext (RED_KEY).
+ * - blobtype	Determine if encapsulated blob should be a secure memory
+ *		blob (SM_SECMEM), with partition data embedded with key
+ *		material, or a general memory blob (SM_GENMEM).
+ * - auth	If decapsulation path is specified by BLACK_KEY, then if
+ *		AES-CCM is requested for key covering use KEY_COVER_CCM, else
+ *		use AES-ECB (KEY_COVER_ECB).
+ *
+ * Upon completion, desc points to a buffer containing a CAAM job descriptor
+ * that decapsulates a key blob from external memory into a black (encrypted)
+ * key or red (plaintext) content.
+ *
+ * This is an example of a black key decapsulation job from a general memory
+ * blob. Notice the 16-byte key modifier in the LOAD instruction.
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400010           ld: ccb2-key len=16 offs=0
+ * [02] 08A63B7F               ptr->@0x08a63b7f
+ * [03] F8000010    seqoutptr: len=16
+ * [04] 01000000               out_ptr->@0x01000000
+ * [05] F000003A     seqinptr: len=58
+ * [06] 01000010               in_ptr->@0x01000010
+ * [07] 860D0004    operation: decap blob  reg=memory, black, format=normal
+ *
+ * This is an example of a red key decapsulation job for restoring a red key
+ * from a secure memory blob. Note the 8 byte modifier on the 12 byte offset
+ * in the LOAD instruction:
+ *
+ * [00] B0800008       jobhdr: stidx=0 len=8
+ * [01] 14400C08           ld: ccb2-key len=8 offs=12
+ * [02] 01000000               ptr->@0x01000000
+ * [03] F8000020    seqoutptr: len=32
+ * [04] 400000E6               out_ptr->@0x400000e6
+ * [05] F0000050     seqinptr: len=80
+ * [06] 08F0C0EA               in_ptr->@0x08f0c0ea
+ * [07] 860D0008    operation: decap blob  reg=memory, red, sec_mem,
+ *			       format=normal
+ *
+ * Note: this function only generates 32-bit pointers at present, and should
+ * be refactored using a scheme that allows both 32 and 64 bit addressing
+ */
+
+static int blob_decap_jobdesc(u32 **desc, dma_addr_t keymod, dma_addr_t blobbuf,
+			      u8 *outbuf, u16 secretsz, u8 keycolor,
+			      u8 blobtype, u8 auth)
+{
+	u32 *tdesc, tmpdesc[INITIAL_DESCSZ];
+	u16 dsize, idx;
+
+	memset(tmpdesc, 0, INITIAL_DESCSZ * sizeof(u32));
+	idx = 1;
+
+	/* Load key modifier */
+	if (blobtype == SM_SECMEM)
+		tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB |
+				 LDST_SRCDST_BYTE_KEY |
+				 ((12 << LDST_OFFSET_SHIFT) & LDST_OFFSET_MASK)
+				 | (8 & LDST_LEN_MASK);
+	else /* is general memory blob */
+		tmpdesc[idx++] = CMD_LOAD | LDST_CLASS_2_CCB |
+				 LDST_SRCDST_BYTE_KEY | (16 & LDST_LEN_MASK);
+
+	tmpdesc[idx++] = (u32)keymod;
+
+	/* Compensate BKEK + MAC tag over size of encapsulated secret */
+	tmpdesc[idx++] = CMD_SEQ_IN_PTR | (secretsz + BLOB_OVERHEAD);
+	tmpdesc[idx++] = (u32)blobbuf;
+	tmpdesc[idx++] = CMD_SEQ_OUT_PTR | secretsz;
+	tmpdesc[idx++] = (uintptr_t)outbuf;
+
+	/* Decapsulate from secure memory partition to black blob */
+	tmpdesc[idx] = CMD_OPERATION | OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB;
+
+	if (blobtype == SM_SECMEM)
+		tmpdesc[idx] |= OP_PCL_BLOB_PTXT_SECMEM;
+
+	if (auth == KEY_COVER_CCM)
+		tmpdesc[idx] |= OP_PCL_BLOB_EKT;
+
+	if (keycolor == BLACK_KEY)
+		tmpdesc[idx] |= OP_PCL_BLOB_BLACK;
+
+	idx++;
+	tmpdesc[0] = CMD_DESC_HDR | HDR_ONE | (idx & HDR_DESCLEN_MASK);
+	dsize = idx * sizeof(u32);
+
+	tdesc = kmalloc(dsize, GFP_KERNEL | GFP_DMA);
+	if (tdesc == NULL)
+		return 0;
+
+	memcpy(tdesc, tmpdesc, dsize);
+	*desc = tdesc;
+	return dsize;
+}
+
+/*
+ * Pseudo-synchronous ring access functions for carrying out key
+ * encapsulation and decapsulation
+ */
+
+struct sm_key_job_result {
+	int error;
+	struct completion completion;
+};
+
+void sm_key_job_done(struct device *dev, u32 *desc, u32 err, void *context)
+{
+	struct sm_key_job_result *res = context;
+
+	if (err)
+		caam_jr_strstatus(dev, err);
+
+	res->error = err;	/* save off the error for postprocessing */
+
+	complete(&res->completion);	/* mark us complete */
+}
+
+static int sm_key_job(struct device *ksdev, u32 *jobdesc)
+{
+	struct sm_key_job_result testres = {0};
+	struct caam_drv_private_sm *kspriv;
+	int rtn = 0;
+
+	kspriv = dev_get_drvdata(ksdev);
+
+	init_completion(&testres.completion);
+
+	rtn = caam_jr_enqueue(kspriv->smringdev, jobdesc, sm_key_job_done,
+			      &testres);
+	if (rtn != -EINPROGRESS)
+		goto exit;
+
+	wait_for_completion(&testres.completion);
+	rtn = testres.error;
+
+exit:
+	return rtn;
+}
+
+/*
+ * Following section establishes the default methods for keystore access
+ * They are NOT intended for use external to this module
+ *
+ * In the present version, these are the only means for the higher-level
+ * interface to deal with the mechanics of accessing the phyiscal keystore
+ */
+
+
+int slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+	u32 i;
+#ifdef SM_DEBUG
+	dev_info(dev, "slot_alloc(): requesting slot for %d bytes\n", size);
+#endif
+
+	if (size > smpriv->slot_size)
+		return -EKEYREJECTED;
+
+	for (i = 0; i < ksdata->slot_count; i++) {
+		if (ksdata->slot[i].allocated == 0) {
+			ksdata->slot[i].allocated = 1;
+			(*slot) = i;
+#ifdef SM_DEBUG
+			dev_info(dev, "slot_alloc(): new slot %d allocated\n",
+				 *slot);
+#endif
+			return 0;
+		}
+	}
+
+	return -ENOSPC;
+}
+EXPORT_SYMBOL(slot_alloc);
+
+int slot_dealloc(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+	u8 __iomem *slotdata;
+
+#ifdef SM_DEBUG
+	dev_info(dev, "slot_dealloc(): releasing slot %d\n", slot);
+#endif
+	if (slot >= ksdata->slot_count)
+		return -EINVAL;
+	slotdata = ksdata->base_address + slot * smpriv->slot_size;
+
+	if (ksdata->slot[slot].allocated == 1) {
+		/* Forcibly overwrite the data from the keystore */
+		memset_io(ksdata->base_address + slot * smpriv->slot_size, 0,
+		       smpriv->slot_size);
+
+		ksdata->slot[slot].allocated = 0;
+#ifdef SM_DEBUG
+		dev_info(dev, "slot_dealloc(): slot %d released\n", slot);
+#endif
+		return 0;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(slot_dealloc);
+
+void *slot_get_address(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+	if (slot >= ksdata->slot_count)
+		return NULL;
+
+#ifdef SM_DEBUG
+	dev_info(dev, "slot_get_address(): slot %d is 0x%08x\n", slot,
+		 (u32)ksdata->base_address + slot * smpriv->slot_size);
+#endif
+
+	return ksdata->base_address + slot * smpriv->slot_size;
+}
+
+void *slot_get_physical(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+	if (slot >= ksdata->slot_count)
+		return NULL;
+
+#ifdef SM_DEBUG
+	dev_info(dev, "%s: slot %d is 0x%08x\n", __func__, slot,
+		 (u32)ksdata->phys_address + slot * smpriv->slot_size);
+#endif
+
+	return ksdata->phys_address + slot * smpriv->slot_size;
+}
+
+u32 slot_get_base(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+	/*
+	 * There could potentially be more than one secure partition object
+	 * associated with this keystore.  For now, there is just one.
+	 */
+
+	(void)slot;
+
+#ifdef SM_DEBUG
+	dev_info(dev, "slot_get_base(): slot %d = 0x%08x\n",
+		slot, (u32)ksdata->base_address);
+#endif
+
+	return (uintptr_t)(ksdata->base_address);
+}
+
+u32 slot_get_offset(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *ksdata = smpriv->pagedesc[unit].ksdata;
+
+	if (slot >= ksdata->slot_count)
+		return -EINVAL;
+
+#ifdef SM_DEBUG
+	dev_info(dev, "slot_get_offset(): slot %d = %d\n", slot,
+		slot * smpriv->slot_size);
+#endif
+
+	return slot * smpriv->slot_size;
+}
+
+u32 slot_get_slot_size(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+
+#ifdef SM_DEBUG
+	dev_info(dev, "slot_get_slot_size(): slot %d = %d\n", slot,
+		 smpriv->slot_size);
+#endif
+	/* All slots are the same size in the default implementation */
+	return smpriv->slot_size;
+}
+
+
+
+int kso_init_data(struct device *dev, u32 unit)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *keystore_data = NULL;
+	u32 slot_count;
+	u32 keystore_data_size;
+
+	/*
+	 * Calculate the required size of the keystore data structure, based
+	 * on the number of keys that can fit in the partition.
+	 */
+	slot_count = smpriv->page_size / smpriv->slot_size;
+#ifdef SM_DEBUG
+	dev_info(dev, "kso_init_data: %d slots initializing\n", slot_count);
+#endif
+
+	keystore_data_size = sizeof(struct keystore_data) +
+				slot_count *
+				sizeof(struct keystore_data_slot_info);
+
+	keystore_data = kzalloc(keystore_data_size, GFP_KERNEL);
+
+	if (!keystore_data)
+		return -ENOMEM;
+
+#ifdef SM_DEBUG
+	dev_info(dev, "kso_init_data: keystore data size = %d\n",
+		 keystore_data_size);
+#endif
+
+	/*
+	 * Place the slot information structure directly after the keystore data
+	 * structure.
+	 */
+	keystore_data->slot = (struct keystore_data_slot_info *)
+			      (keystore_data + 1);
+	keystore_data->slot_count = slot_count;
+
+	smpriv->pagedesc[unit].ksdata = keystore_data;
+	smpriv->pagedesc[unit].ksdata->base_address =
+		smpriv->pagedesc[unit].pg_base;
+	smpriv->pagedesc[unit].ksdata->phys_address =
+		smpriv->pagedesc[unit].pg_phys;
+
+	return 0;
+}
+
+void kso_cleanup_data(struct device *dev, u32 unit)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	struct keystore_data *keystore_data = NULL;
+
+	if (smpriv->pagedesc[unit].ksdata != NULL)
+		keystore_data = smpriv->pagedesc[unit].ksdata;
+
+	/* Release the allocated keystore management data */
+	kfree(smpriv->pagedesc[unit].ksdata);
+
+	return;
+}
+
+
+
+/*
+ * Keystore management section
+ */
+
+void sm_init_keystore(struct device *dev)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+	smpriv->data_init = kso_init_data;
+	smpriv->data_cleanup = kso_cleanup_data;
+	smpriv->slot_alloc = slot_alloc;
+	smpriv->slot_dealloc = slot_dealloc;
+	smpriv->slot_get_address = slot_get_address;
+	smpriv->slot_get_physical = slot_get_physical;
+	smpriv->slot_get_base = slot_get_base;
+	smpriv->slot_get_offset = slot_get_offset;
+	smpriv->slot_get_slot_size = slot_get_slot_size;
+#ifdef SM_DEBUG
+	dev_info(dev, "sm_init_keystore(): handlers installed\n");
+#endif
+}
+EXPORT_SYMBOL(sm_init_keystore);
+
+/* Return available pages/units */
+u32 sm_detect_keystore_units(struct device *dev)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+	return smpriv->localpages;
+}
+EXPORT_SYMBOL(sm_detect_keystore_units);
+
+/*
+ * Do any keystore specific initializations
+ */
+int sm_establish_keystore(struct device *dev, u32 unit)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+#ifdef SM_DEBUG
+	dev_info(dev, "sm_establish_keystore(): unit %d initializing\n", unit);
+#endif
+
+	if (smpriv->data_init == NULL)
+		return -EINVAL;
+
+	/* Call the data_init function for any user setup */
+	return smpriv->data_init(dev, unit);
+}
+EXPORT_SYMBOL(sm_establish_keystore);
+
+void sm_release_keystore(struct device *dev, u32 unit)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+
+#ifdef SM_DEBUG
+	dev_info(dev, "sm_establish_keystore(): unit %d releasing\n", unit);
+#endif
+	if ((smpriv != NULL) && (smpriv->data_cleanup != NULL))
+		smpriv->data_cleanup(dev, unit);
+
+	return;
+}
+EXPORT_SYMBOL(sm_release_keystore);
+
+/*
+ * Subsequent interfacce (sm_keystore_*) forms the accessor interfacce to
+ * the keystore
+ */
+int sm_keystore_slot_alloc(struct device *dev, u32 unit, u32 size, u32 *slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = -EINVAL;
+
+	spin_lock(&smpriv->kslock);
+
+	if ((smpriv->slot_alloc == NULL) ||
+	    (smpriv->pagedesc[unit].ksdata == NULL))
+		goto out;
+
+	retval =  smpriv->slot_alloc(dev, unit, size, slot);
+
+out:
+	spin_unlock(&smpriv->kslock);
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_alloc);
+
+int sm_keystore_slot_dealloc(struct device *dev, u32 unit, u32 slot)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = -EINVAL;
+
+	spin_lock(&smpriv->kslock);
+
+	if ((smpriv->slot_alloc == NULL) ||
+	    (smpriv->pagedesc[unit].ksdata == NULL))
+		goto out;
+
+	retval = smpriv->slot_dealloc(dev, unit, slot);
+out:
+	spin_unlock(&smpriv->kslock);
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_dealloc);
+
+int sm_keystore_slot_load(struct device *dev, u32 unit, u32 slot,
+			  const u8 *key_data, u32 key_length)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = -EINVAL;
+	u32 slot_size;
+	u8 __iomem *slot_location;
+
+	spin_lock(&smpriv->kslock);
+
+	slot_size = smpriv->slot_get_slot_size(dev, unit, slot);
+
+	if (key_length > slot_size) {
+		retval = -EFBIG;
+		goto out;
+	}
+
+	slot_location = smpriv->slot_get_address(dev, unit, slot);
+
+	memcpy_toio(slot_location, key_data, key_length);
+
+	retval = 0;
+
+out:
+	spin_unlock(&smpriv->kslock);
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_load);
+
+int sm_keystore_slot_read(struct device *dev, u32 unit, u32 slot,
+			  u32 key_length, u8 *key_data)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = -EINVAL;
+	u8 __iomem *slot_addr;
+	u32 slot_size;
+
+	spin_lock(&smpriv->kslock);
+
+	slot_addr = smpriv->slot_get_address(dev, unit, slot);
+	slot_size = smpriv->slot_get_slot_size(dev, unit, slot);
+
+	if (key_length > slot_size) {
+		retval = -EKEYREJECTED;
+		goto out;
+	}
+
+	memcpy_fromio(key_data, slot_addr, key_length);
+	retval = 0;
+
+out:
+	spin_unlock(&smpriv->kslock);
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_read);
+
+/*
+ * Blacken a clear key in a slot. Operates "in place".
+ * Limited to class 1 keys at the present time
+ */
+int sm_keystore_cover_key(struct device *dev, u32 unit, u32 slot,
+			  u16 key_length, u8 keyauth)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = 0;
+	u8 __iomem *slotaddr;
+	void *slotphys;
+	u32 dsize, jstat;
+	u32 __iomem *coverdesc = NULL;
+
+	/* Get the address of the object in the slot */
+	slotaddr = (u8 *)smpriv->slot_get_address(dev, unit, slot);
+	slotphys = (u8 *)smpriv->slot_get_physical(dev, unit, slot);
+
+	dsize = blacken_key_jobdesc(&coverdesc, slotphys, key_length, keyauth);
+	if (!dsize)
+		return -ENOMEM;
+	jstat = sm_key_job(dev, coverdesc);
+	if (jstat)
+		retval = -EIO;
+
+	kfree(coverdesc);
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_cover_key);
+
+/* Export a black/red key to a blob in external memory */
+int sm_keystore_slot_export(struct device *dev, u32 unit, u32 slot, u8 keycolor,
+			    u8 keyauth, u8 *outbuf, u16 keylen, u8 *keymod)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = 0;
+	u8 __iomem *slotaddr, *lkeymod;
+	u8 __iomem *slotphys;
+	dma_addr_t keymod_dma, outbuf_dma;
+	u32 dsize, jstat;
+	u32 __iomem *encapdesc = NULL;
+	struct device *dev_for_dma_op;
+
+	/* Use the ring as device for DMA operations */
+	dev_for_dma_op = smpriv->smringdev;
+
+	/* Get the base address(es) of the specified slot */
+	slotaddr = (u8 *)smpriv->slot_get_address(dev, unit, slot);
+	slotphys = smpriv->slot_get_physical(dev, unit, slot);
+
+	/* Allocate memory for key modifier compatible with DMA */
+	lkeymod = kmalloc(SECMEM_KEYMOD_LEN, GFP_KERNEL | GFP_DMA);
+	if (!lkeymod) {
+		retval = (-ENOMEM);
+		goto exit;
+	}
+
+	/* Get DMA address for the key modifier */
+	keymod_dma = dma_map_single(dev_for_dma_op, lkeymod,
+					SECMEM_KEYMOD_LEN, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev_for_dma_op, keymod_dma)) {
+		dev_err(dev, "unable to map keymod: %p\n", lkeymod);
+		retval = (-ENOMEM);
+		goto free_keymod;
+	}
+
+	/* Copy the keymod and synchronize the DMA */
+	memcpy(lkeymod, keymod, SECMEM_KEYMOD_LEN);
+	dma_sync_single_for_device(dev_for_dma_op, keymod_dma,
+					SECMEM_KEYMOD_LEN, DMA_TO_DEVICE);
+
+	/* Get DMA address for the destination */
+	outbuf_dma = dma_map_single(dev_for_dma_op, outbuf,
+				keylen + BLOB_OVERHEAD, DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev_for_dma_op, outbuf_dma)) {
+		dev_err(dev, "unable to map outbuf: %p\n", outbuf);
+		retval = (-ENOMEM);
+		goto unmap_keymod;
+	}
+
+	/* Build the encapsulation job descriptor */
+	dsize = blob_encap_jobdesc(&encapdesc, keymod_dma, slotphys, outbuf_dma,
+				   keylen, keycolor, SM_SECMEM, keyauth);
+	if (!dsize) {
+		dev_err(dev, "can't alloc an encapsulation descriptor\n");
+		retval = -ENOMEM;
+		goto unmap_outbuf;
+	}
+
+	/* Run the job */
+	jstat = sm_key_job(dev, encapdesc);
+	if (jstat) {
+		retval = (-EIO);
+		goto free_desc;
+	}
+
+	/* Synchronize the data received */
+	dma_sync_single_for_cpu(dev_for_dma_op, outbuf_dma,
+			keylen + BLOB_OVERHEAD, DMA_FROM_DEVICE);
+
+free_desc:
+	kfree(encapdesc);
+
+unmap_outbuf:
+	dma_unmap_single(dev_for_dma_op, outbuf_dma, keylen + BLOB_OVERHEAD,
+			DMA_FROM_DEVICE);
+
+unmap_keymod:
+	dma_unmap_single(dev_for_dma_op, keymod_dma, SECMEM_KEYMOD_LEN,
+			DMA_TO_DEVICE);
+
+free_keymod:
+	kfree(lkeymod);
+
+exit:
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_export);
+
+/* Import a black/red key from a blob residing in external memory */
+int sm_keystore_slot_import(struct device *dev, u32 unit, u32 slot, u8 keycolor,
+			    u8 keyauth, u8 *inbuf, u16 keylen, u8 *keymod)
+{
+	struct caam_drv_private_sm *smpriv = dev_get_drvdata(dev);
+	int retval = 0;
+	u8 __iomem *slotaddr, *lkeymod;
+	u8 __iomem *slotphys;
+	dma_addr_t keymod_dma, inbuf_dma;
+	u32 dsize, jstat;
+	u32 __iomem *decapdesc = NULL;
+	struct device *dev_for_dma_op;
+
+	/* Use the ring as device for DMA operations */
+	dev_for_dma_op = smpriv->smringdev;
+
+	/* Get the base address(es) of the specified slot */
+	slotaddr = (u8 *)smpriv->slot_get_address(dev, unit, slot);
+	slotphys = smpriv->slot_get_physical(dev, unit, slot);
+
+	/* Allocate memory for key modifier compatible with DMA */
+	lkeymod = kmalloc(SECMEM_KEYMOD_LEN, GFP_KERNEL | GFP_DMA);
+	if (!lkeymod) {
+		retval = (-ENOMEM);
+		goto exit;
+	}
+
+	/* Get DMA address for the key modifier */
+	keymod_dma = dma_map_single(dev_for_dma_op, lkeymod,
+					SECMEM_KEYMOD_LEN, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev_for_dma_op, keymod_dma)) {
+		dev_err(dev, "unable to map keymod: %p\n", lkeymod);
+		retval = (-ENOMEM);
+		goto free_keymod;
+	}
+
+	/* Copy the keymod and synchronize the DMA */
+	memcpy(lkeymod, keymod, SECMEM_KEYMOD_LEN);
+	dma_sync_single_for_device(dev_for_dma_op, keymod_dma,
+					SECMEM_KEYMOD_LEN, DMA_TO_DEVICE);
+
+	/* Get DMA address for the input */
+	inbuf_dma = dma_map_single(dev_for_dma_op, inbuf,
+					keylen + BLOB_OVERHEAD, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev_for_dma_op, inbuf_dma)) {
+		dev_err(dev, "unable to map inbuf: %p\n", inbuf);
+		retval = (-ENOMEM);
+		goto unmap_keymod;
+	}
+
+	/* synchronize the DMA */
+	dma_sync_single_for_device(dev_for_dma_op, inbuf_dma,
+					keylen + BLOB_OVERHEAD, DMA_TO_DEVICE);
+
+	/* Build the encapsulation job descriptor */
+	dsize = blob_decap_jobdesc(&decapdesc, keymod_dma, inbuf_dma, slotphys,
+				   keylen, keycolor, SM_SECMEM, keyauth);
+	if (!dsize) {
+		dev_err(dev, "can't alloc a decapsulation descriptor\n");
+		retval = -ENOMEM;
+		goto unmap_inbuf;
+	}
+
+	/* Run the job */
+	jstat = sm_key_job(dev, decapdesc);
+
+	/*
+	 * May want to expand upon error meanings a bit. Any CAAM status
+	 * is reported as EIO, but we might want to look for something more
+	 * meaningful for something like an ICV error on restore, otherwise
+	 * the caller is left guessing.
+	 */
+	if (jstat) {
+		retval = (-EIO);
+		goto free_desc;
+	}
+
+free_desc:
+	kfree(decapdesc);
+
+unmap_inbuf:
+	dma_unmap_single(dev_for_dma_op, inbuf_dma, keylen + BLOB_OVERHEAD,
+			DMA_TO_DEVICE);
+
+unmap_keymod:
+	dma_unmap_single(dev_for_dma_op, keymod_dma, SECMEM_KEYMOD_LEN,
+			DMA_TO_DEVICE);
+
+free_keymod:
+	kfree(lkeymod);
+
+exit:
+	return retval;
+}
+EXPORT_SYMBOL(sm_keystore_slot_import);
+
+/*
+ * Initialization/shutdown subsystem
+ * Assumes statically-invoked startup/shutdown from the controller driver
+ * for the present time, to be reworked when a device tree becomes
+ * available. This code will not modularize in present form.
+ *
+ * Also, simply uses ring 0 for execution at the present
+ */
+
+int caam_sm_startup(struct device *ctrldev)
+{
+	struct device *smdev;
+	struct caam_drv_private *ctrlpriv;
+	struct caam_drv_private_sm *smpriv;
+	struct caam_drv_private_jr *jrpriv;	/* need this for reg page */
+	struct platform_device *sm_pdev;
+	struct sm_page_descriptor *lpagedesc;
+	u32 page, pgstat, lpagect, detectedpage, smvid, smpart;
+	int ret = 0;
+
+	struct device_node *np;
+	ctrlpriv = dev_get_drvdata(ctrldev);
+
+	if (!ctrlpriv->sm_present)
+		return 0;
+
+	/*
+	 * Set up the private block for secure memory
+	 * Only one instance is possible
+	 */
+	smpriv = kzalloc(sizeof(struct caam_drv_private_sm), GFP_KERNEL);
+	if (smpriv == NULL) {
+		dev_err(ctrldev, "can't alloc private mem for secure memory\n");
+		ret = -ENOMEM;
+		goto exit;
+	}
+	smpriv->parentdev = ctrldev; /* copy of parent dev is handy */
+	spin_lock_init(&smpriv->kslock);
+
+	/* Create the dev */
+	np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-caam-sm");
+	if (np)
+		of_node_clear_flag(np, OF_POPULATED);
+	sm_pdev = of_platform_device_create(np, "caam_sm", ctrldev);
+
+	if (sm_pdev == NULL) {
+		ret = -EINVAL;
+		goto free_smpriv;
+	}
+
+	/* Save a pointer to the platform device for Secure Memory */
+	smpriv->sm_pdev = sm_pdev;
+	smdev = &sm_pdev->dev;
+	dev_set_drvdata(smdev, smpriv);
+	ctrlpriv->smdev = smdev;
+
+	/* Set the Secure Memory Register Map Version */
+	smvid = rd_reg32(&ctrlpriv->jr[0]->perfmon.smvid);
+	smpart = rd_reg32(&ctrlpriv->jr[0]->perfmon.smpart);
+
+	if (smvid < SMVID_V2)
+		smpriv->sm_reg_offset = SM_V1_OFFSET;
+	else
+		smpriv->sm_reg_offset = SM_V2_OFFSET;
+
+	/*
+	 * Collect configuration limit data for reference
+	 * This batch comes from the partition data/vid registers in perfmon
+	 */
+	smpriv->max_pages = ((smpart & SMPART_MAX_NUMPG_MASK) >>
+			    SMPART_MAX_NUMPG_SHIFT) + 1;
+	smpriv->top_partition = ((smpart & SMPART_MAX_PNUM_MASK) >>
+				SMPART_MAX_PNUM_SHIFT) + 1;
+	smpriv->top_page =  ((smpart & SMPART_MAX_PG_MASK) >>
+				SMPART_MAX_PG_SHIFT) + 1;
+	smpriv->page_size = 1024 << ((smvid & SMVID_PG_SIZE_MASK) >>
+				SMVID_PG_SIZE_SHIFT);
+	smpriv->slot_size = 1 << CONFIG_CRYPTO_DEV_FSL_CAAM_SM_SLOTSIZE;
+
+#ifdef SM_DEBUG
+	dev_info(smdev, "max pages = %d, top partition = %d\n",
+			smpriv->max_pages, smpriv->top_partition);
+	dev_info(smdev, "top page = %d, page size = %d (total = %d)\n",
+			smpriv->top_page, smpriv->page_size,
+			smpriv->top_page * smpriv->page_size);
+	dev_info(smdev, "selected slot size = %d\n", smpriv->slot_size);
+#endif
+
+	/*
+	 * Now probe for partitions/pages to which we have access. Note that
+	 * these have likely been set up by a bootloader or platform
+	 * provisioning application, so we have to assume that we "inherit"
+	 * a configuration and work within the constraints of what it might be.
+	 *
+	 * Assume use of the zeroth ring in the present iteration (until
+	 * we can divorce the controller and ring drivers, and then assign
+	 * an SM instance to any ring instance).
+	 */
+	smpriv->smringdev = caam_jr_alloc();
+	if (!smpriv->smringdev) {
+		dev_err(smdev, "Device for job ring not created\n");
+		ret = -ENODEV;
+		goto unregister_smpdev;
+	}
+
+	jrpriv = dev_get_drvdata(smpriv->smringdev);
+	lpagect = 0;
+	pgstat = 0;
+	lpagedesc = kzalloc(sizeof(struct sm_page_descriptor)
+			    * smpriv->max_pages, GFP_KERNEL);
+	if (lpagedesc == NULL) {
+		ret = -ENOMEM;
+		goto free_smringdev;
+	}
+
+	for (page = 0; page < smpriv->max_pages; page++) {
+		u32 page_ownership;
+
+		if (sm_send_cmd(smpriv, jrpriv,
+				((page << SMC_PAGE_SHIFT) & SMC_PAGE_MASK) |
+				(SMC_CMD_PAGE_INQUIRY & SMC_CMD_MASK),
+				&pgstat)) {
+			ret = -EINVAL;
+			goto free_lpagedesc;
+		}
+
+		page_ownership = (pgstat & SMCS_PGWON_MASK) >> SMCS_PGOWN_SHIFT;
+		if ((page_ownership == SMCS_PGOWN_OWNED)
+			|| (page_ownership == SMCS_PGOWN_NOOWN)) {
+			/* page allocated */
+			lpagedesc[page].phys_pagenum =
+				(pgstat & SMCS_PAGE_MASK) >> SMCS_PAGE_SHIFT;
+			lpagedesc[page].own_part =
+				(pgstat & SMCS_PART_SHIFT) >> SMCS_PART_MASK;
+			lpagedesc[page].pg_base = (u8 *)ctrlpriv->sm_base +
+				(smpriv->page_size * page);
+			if (ctrlpriv->scu_en) {
+/* FIXME: get different addresses viewed by CPU and CAAM from
+ * platform property
+ */
+				lpagedesc[page].pg_phys = (u8 *)0x20800000 +
+					(smpriv->page_size * page);
+			} else {
+				lpagedesc[page].pg_phys =
+					(u8 *) ctrlpriv->sm_phy +
+					(smpriv->page_size * page);
+			}
+			lpagect++;
+#ifdef SM_DEBUG
+			dev_info(smdev,
+				"physical page %d, owning partition = %d\n",
+				lpagedesc[page].phys_pagenum,
+				lpagedesc[page].own_part);
+#endif
+		}
+	}
+
+	smpriv->pagedesc = kzalloc(sizeof(struct sm_page_descriptor) * lpagect,
+				   GFP_KERNEL);
+	if (smpriv->pagedesc == NULL) {
+		ret = -ENOMEM;
+		goto free_lpagedesc;
+	}
+	smpriv->localpages = lpagect;
+
+	detectedpage = 0;
+	for (page = 0; page < smpriv->max_pages; page++) {
+		if (lpagedesc[page].pg_base != NULL) {	/* e.g. live entry */
+			memcpy(&smpriv->pagedesc[detectedpage],
+			       &lpagedesc[page],
+			       sizeof(struct sm_page_descriptor));
+#ifdef SM_DEBUG_CONT
+			sm_show_page(smdev, &smpriv->pagedesc[detectedpage]);
+#endif
+			detectedpage++;
+		}
+	}
+
+	kfree(lpagedesc);
+
+	sm_init_keystore(smdev);
+
+	goto exit;
+
+free_lpagedesc:
+	kfree(lpagedesc);
+free_smringdev:
+	caam_jr_free(smpriv->smringdev);
+unregister_smpdev:
+	of_device_unregister(smpriv->sm_pdev);
+free_smpriv:
+	kfree(smpriv);
+
+exit:
+	return ret;
+}
+
+void caam_sm_shutdown(struct device *ctrldev)
+{
+	struct device *smdev;
+	struct caam_drv_private *priv;
+	struct caam_drv_private_sm *smpriv;
+
+	priv = dev_get_drvdata(ctrldev);
+	if (!priv->sm_present)
+		return;
+
+	smdev = priv->smdev;
+
+	/* Return if resource not initialized by startup */
+	if (smdev == NULL)
+		return;
+
+	smpriv = dev_get_drvdata(smdev);
+
+	caam_jr_free(smpriv->smringdev);
+
+	/* Remove Secure Memory Platform Device */
+	of_device_unregister(smpriv->sm_pdev);
+
+	kfree(smpriv->pagedesc);
+	kfree(smpriv);
+}
+EXPORT_SYMBOL(caam_sm_shutdown);
diff --git a/drivers/crypto/caam/sm_test.c b/drivers/crypto/caam/sm_test.c
new file mode 100644
index 000000000..2a84a91a0
--- /dev/null
+++ b/drivers/crypto/caam/sm_test.c
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Secure Memory / Keystore Exemplification Module
+ *
+ * Copyright 2012-2015 Freescale Semiconductor, Inc.
+ * Copyright 2016-2019 NXP
+ *
+ * This module has been overloaded as an example to show:
+ * - Secure memory subsystem initialization/shutdown
+ * - Allocation/deallocation of "slots" in a secure memory page
+ * - Loading and unloading of key material into slots
+ * - Covering of secure memory objects into "black keys" (ECB only at present)
+ * - Verification of key covering (by differentiation only)
+ * - Exportation of keys into secure memory blobs (with display of result)
+ * - Importation of keys from secure memory blobs (with display of result)
+ * - Verification of re-imported keys where possible.
+ *
+ * The module does not show the use of key objects as working key register
+ * source material at this time.
+ *
+ * This module can use a substantial amount of refactoring, which may occur
+ * after the API gets some mileage. Furthermore, expect this module to
+ * eventually disappear once the API is integrated into "real" software.
+ */
+
+#include "compat.h"
+#include "regs.h"
+#include "intern.h"
+#include "desc.h"
+#include "error.h"
+#include "jr.h"
+#include "sm.h"
+
+/* Fixed known pattern for a key modifier */
+static u8 skeymod[] = {
+	0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+	0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+
+/* Fixed known pattern for a key */
+static u8 clrkey[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x0f, 0x06, 0x07,
+	0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+	0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+	0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+	0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+	0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+	0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+	0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+	0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+	0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+	0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+	0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+	0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+static void key_display(struct device *dev, const char *label, u16 size,
+			u8 *key)
+{
+	unsigned i;
+
+	dev_dbg(dev, "%s", label);
+	for (i = 0; i < size; i += 8)
+		dev_dbg(dev,
+			"[%04d] %02x %02x %02x %02x %02x %02x %02x %02x\n",
+			i, key[i], key[i + 1], key[i + 2], key[i + 3],
+			key[i + 4], key[i + 5], key[i + 6], key[i + 7]);
+}
+
+int caam_sm_example_init(struct platform_device *pdev)
+{
+	struct device *ctrldev, *ksdev;
+	struct caam_drv_private *ctrlpriv;
+	struct caam_drv_private_sm *kspriv;
+	u32 unit, units;
+	int rtnval;
+	u8 clrkey8[8], clrkey16[16], clrkey24[24], clrkey32[32];
+	u8 blkkey8[AES_BLOCK_PAD(8)], blkkey16[AES_BLOCK_PAD(16)];
+	u8 blkkey24[AES_BLOCK_PAD(24)], blkkey32[AES_BLOCK_PAD(32)];
+	u8 rstkey8[AES_BLOCK_PAD(8)], rstkey16[AES_BLOCK_PAD(16)];
+	u8 rstkey24[AES_BLOCK_PAD(24)], rstkey32[AES_BLOCK_PAD(32)];
+	u8 __iomem *blob8, *blob16, *blob24, *blob32;
+	u32 keyslot8, keyslot16, keyslot24, keyslot32 = 0;
+
+	blob8 = blob16 = blob24 = blob32 = NULL;
+
+	/*
+	 * 3.5.x and later revs for MX6 should be able to ditch this
+	 * and detect via dts property
+	 */
+	ctrldev = &pdev->dev;
+	ctrlpriv = dev_get_drvdata(ctrldev);
+
+	/*
+	 * If ctrlpriv is NULL, it's probably because the caam driver wasn't
+	 * properly initialized (e.g. RNG4 init failed). Thus, bail out here.
+	 */
+	if (!ctrlpriv)
+		return -ENODEV;
+
+	ksdev = ctrlpriv->smdev;
+	kspriv = dev_get_drvdata(ksdev);
+	if (kspriv == NULL)
+		return -ENODEV;
+
+	/* What keystores are available ? */
+	units = sm_detect_keystore_units(ksdev);
+	if (!units)
+		dev_err(ksdev, "blkkey_ex: no keystore units available\n");
+
+	/*
+	 * MX6 bootloader stores some stuff in unit 0, so let's
+	 * use 1 or above
+	 */
+	if (units < 2) {
+		dev_err(ksdev, "blkkey_ex: insufficient keystore units\n");
+		return -ENODEV;
+	}
+	unit = 1;
+
+	dev_info(ksdev, "blkkey_ex: %d keystore units available\n", units);
+
+	/* Initialize/Establish Keystore */
+	sm_establish_keystore(ksdev, unit);	/* Initalize store in #1 */
+
+	/*
+	 * Now let's set up buffers for blobs in DMA-able memory. All are
+	 * larger than need to be so that blob size can be seen.
+	 */
+	blob8 = kzalloc(128, GFP_KERNEL | GFP_DMA);
+	blob16 = kzalloc(128, GFP_KERNEL | GFP_DMA);
+	blob24 = kzalloc(128, GFP_KERNEL | GFP_DMA);
+	blob32 = kzalloc(128, GFP_KERNEL | GFP_DMA);
+
+	if ((blob8 == NULL) || (blob16 == NULL) || (blob24 == NULL) ||
+	    (blob32 == NULL)) {
+		rtnval = -ENOMEM;
+		dev_err(ksdev, "blkkey_ex: can't get blob buffers\n");
+		goto freemem;
+	}
+
+	/* Initialize clear keys with a known and recognizable pattern */
+	memcpy(clrkey8, clrkey, 8);
+	memcpy(clrkey16, clrkey, 16);
+	memcpy(clrkey24, clrkey, 24);
+	memcpy(clrkey32, clrkey, 32);
+
+	memset(blkkey8, 0, AES_BLOCK_PAD(8));
+	memset(blkkey16, 0, AES_BLOCK_PAD(16));
+	memset(blkkey24, 0, AES_BLOCK_PAD(24));
+	memset(blkkey32, 0, AES_BLOCK_PAD(32));
+
+	memset(rstkey8, 0, AES_BLOCK_PAD(8));
+	memset(rstkey16, 0, AES_BLOCK_PAD(16));
+	memset(rstkey24, 0, AES_BLOCK_PAD(24));
+	memset(rstkey32, 0, AES_BLOCK_PAD(32));
+
+	/*
+	 * Allocate keyslots. Since we're going to blacken keys in-place,
+	 * we want slots big enough to pad out to the next larger AES blocksize
+	 * so pad them out.
+	 */
+	rtnval = sm_keystore_slot_alloc(ksdev, unit, AES_BLOCK_PAD(8),
+					&keyslot8);
+	if (rtnval)
+		goto freemem;
+
+	rtnval = sm_keystore_slot_alloc(ksdev, unit, AES_BLOCK_PAD(16),
+					&keyslot16);
+	if (rtnval)
+		goto dealloc_slot8;
+
+	rtnval = sm_keystore_slot_alloc(ksdev, unit, AES_BLOCK_PAD(24),
+					&keyslot24);
+	if (rtnval)
+		goto dealloc_slot16;
+
+	rtnval = sm_keystore_slot_alloc(ksdev, unit, AES_BLOCK_PAD(32),
+					&keyslot32);
+	if (rtnval)
+		goto dealloc_slot24;
+
+
+	/* Now load clear key data into the newly allocated slots */
+	rtnval = sm_keystore_slot_load(ksdev, unit, keyslot8, clrkey8, 8);
+	if (rtnval)
+		goto dealloc;
+
+	rtnval = sm_keystore_slot_load(ksdev, unit, keyslot16, clrkey16, 16);
+	if (rtnval)
+		goto dealloc;
+
+	rtnval = sm_keystore_slot_load(ksdev, unit, keyslot24, clrkey24, 24);
+	if (rtnval)
+		goto dealloc;
+
+	rtnval = sm_keystore_slot_load(ksdev, unit, keyslot32, clrkey32, 32);
+	if (rtnval)
+		goto dealloc;
+
+	/*
+	 * All cleartext keys are loaded into slots (in an unprotected
+	 * partition at this time)
+	 *
+	 * Cover keys in-place
+	 */
+	rtnval = sm_keystore_cover_key(ksdev, unit, keyslot8, 8, KEY_COVER_ECB);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't cover 64-bit key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_cover_key(ksdev, unit, keyslot16, 16,
+				       KEY_COVER_ECB);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't cover 128-bit key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_cover_key(ksdev, unit, keyslot24, 24,
+				       KEY_COVER_ECB);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't cover 192-bit key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_cover_key(ksdev, unit, keyslot32, 32,
+				       KEY_COVER_ECB);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't cover 256-bit key\n");
+		goto dealloc;
+	}
+
+	/*
+	 * Keys should be covered and appear sufficiently "random"
+	 * as a result of the covering (blackening) process. Assuming
+	 * non-secure mode, read them back out for examination; they should
+	 * appear as random data, completely differing from the clear
+	 * inputs. So, this will read them back from secure memory and
+	 * compare them. If they match the clear key, then the covering
+	 * operation didn't occur.
+	 */
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot8, AES_BLOCK_PAD(8),
+				       blkkey8);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't read 64-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot16,
+				       AES_BLOCK_PAD(16), blkkey16);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't read 128-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot24,
+				       AES_BLOCK_PAD(24), blkkey24);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't read 192-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot32,
+				       AES_BLOCK_PAD(32), blkkey32);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't read 256-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = -EINVAL;
+	if (!memcmp(blkkey8, clrkey8, 8)) {
+		dev_err(ksdev, "blkkey_ex: 64-bit key cover failed\n");
+		goto dealloc;
+	}
+
+	if (!memcmp(blkkey16, clrkey16, 16)) {
+		dev_err(ksdev, "blkkey_ex: 128-bit key cover failed\n");
+		goto dealloc;
+	}
+
+	if (!memcmp(blkkey24, clrkey24, 24)) {
+		dev_err(ksdev, "blkkey_ex: 192-bit key cover failed\n");
+		goto dealloc;
+	}
+
+	if (!memcmp(blkkey32, clrkey32, 32)) {
+		dev_err(ksdev, "blkkey_ex: 256-bit key cover failed\n");
+		goto dealloc;
+	}
+
+
+	key_display(ksdev, "64-bit clear key:", 8, clrkey8);
+	key_display(ksdev, "64-bit black key:", AES_BLOCK_PAD(8), blkkey8);
+
+	key_display(ksdev, "128-bit clear key:", 16, clrkey16);
+	key_display(ksdev, "128-bit black key:", AES_BLOCK_PAD(16), blkkey16);
+
+	key_display(ksdev, "192-bit clear key:", 24, clrkey24);
+	key_display(ksdev, "192-bit black key:", AES_BLOCK_PAD(24), blkkey24);
+
+	key_display(ksdev, "256-bit clear key:", 32, clrkey32);
+	key_display(ksdev, "256-bit black key:", AES_BLOCK_PAD(32), blkkey32);
+
+	/*
+	 * Now encapsulate all keys as SM blobs out to external memory
+	 * Blobs will appear as random-looking blocks of data different
+	 * from the original source key, and 48 bytes longer than the
+	 * original key, to account for the extra data encapsulated within.
+	 */
+	key_display(ksdev, "64-bit unwritten blob:", 96, blob8);
+	key_display(ksdev, "128-bit unwritten blob:", 96, blob16);
+	key_display(ksdev, "196-bit unwritten blob:", 96, blob24);
+	key_display(ksdev, "256-bit unwritten blob:", 96, blob32);
+
+	rtnval = sm_keystore_slot_export(ksdev, unit, keyslot8, BLACK_KEY,
+					 KEY_COVER_ECB, blob8, 8, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't encapsulate 64-bit key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_export(ksdev, unit, keyslot16, BLACK_KEY,
+					 KEY_COVER_ECB, blob16, 16, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't encapsulate 128-bit key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_export(ksdev, unit, keyslot24, BLACK_KEY,
+					 KEY_COVER_ECB, blob24, 24, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't encapsulate 192-bit key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_export(ksdev, unit, keyslot32, BLACK_KEY,
+					 KEY_COVER_ECB, blob32, 32, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't encapsulate 256-bit key\n");
+		goto dealloc;
+	}
+
+	key_display(ksdev, "64-bit black key in blob:", 96, blob8);
+	key_display(ksdev, "128-bit black key in blob:", 96, blob16);
+	key_display(ksdev, "192-bit black key in blob:", 96, blob24);
+	key_display(ksdev, "256-bit black key in blob:", 96, blob32);
+
+	/*
+	 * Now re-import black keys from secure-memory blobs stored
+	 * in general memory from the previous operation. Since we are
+	 * working with black keys, and since power has not cycled, the
+	 * restored black keys should match the original blackened keys
+	 * (this would not be true if the blobs were save in some non-volatile
+	 * store, and power was cycled between the save and restore)
+	 */
+	rtnval = sm_keystore_slot_import(ksdev, unit, keyslot8, BLACK_KEY,
+					 KEY_COVER_ECB, blob8, 8, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't decapsulate 64-bit blob\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_import(ksdev, unit, keyslot16, BLACK_KEY,
+					 KEY_COVER_ECB, blob16, 16, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't decapsulate 128-bit blob\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_import(ksdev, unit, keyslot24, BLACK_KEY,
+					 KEY_COVER_ECB, blob24, 24, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't decapsulate 196-bit blob\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_import(ksdev, unit, keyslot32, BLACK_KEY,
+					 KEY_COVER_ECB, blob32, 32, skeymod);
+	if (rtnval) {
+		dev_err(ksdev, "blkkey_ex: can't decapsulate 256-bit blob\n");
+		goto dealloc;
+	}
+
+
+	/*
+	 * Blobs are now restored as black keys. Read those black keys back
+	 * for a comparison with the original black key, they should match
+	 */
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot8, AES_BLOCK_PAD(8),
+				       rstkey8);
+	if (rtnval) {
+		dev_err(ksdev,
+			"blkkey_ex: can't read restored 64-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot16,
+				       AES_BLOCK_PAD(16), rstkey16);
+	if (rtnval) {
+		dev_err(ksdev,
+			"blkkey_ex: can't read restored 128-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot24,
+				       AES_BLOCK_PAD(24), rstkey24);
+	if (rtnval) {
+		dev_err(ksdev,
+			"blkkey_ex: can't read restored 196-bit black key\n");
+		goto dealloc;
+	}
+
+	rtnval = sm_keystore_slot_read(ksdev, unit, keyslot32,
+				       AES_BLOCK_PAD(32), rstkey32);
+	if (rtnval) {
+		dev_err(ksdev,
+			"blkkey_ex: can't read restored 256-bit black key\n");
+		goto dealloc;
+	}
+
+	key_display(ksdev, "restored 64-bit black key:", AES_BLOCK_PAD(8),
+		    rstkey8);
+	key_display(ksdev, "restored 128-bit black key:", AES_BLOCK_PAD(16),
+		    rstkey16);
+	key_display(ksdev, "restored 192-bit black key:", AES_BLOCK_PAD(24),
+		    rstkey24);
+	key_display(ksdev, "restored 256-bit black key:", AES_BLOCK_PAD(32),
+		    rstkey32);
+
+	/*
+	 * Compare the restored black keys with the original blackened keys
+	 * As long as we're operating within the same power cycle, a black key
+	 * restored from a blob should match the original black key IF the
+	 * key happens to be of a size that matches a multiple of the AES
+	 * blocksize. Any key that is padded to fill the block size will not
+	 * match, excepting a key that exceeds a block; only the first full
+	 * blocks will match (assuming ECB).
+	 *
+	 * Therefore, compare the 16 and 32 bit keys, they should match.
+	 * The 24 bit key can only match within the first 16 byte block.
+	 */
+
+	if (memcmp(rstkey16, blkkey16, AES_BLOCK_PAD(16))) {
+		dev_err(ksdev, "blkkey_ex: 128-bit restored key mismatch\n");
+		rtnval = -EINVAL;
+	}
+
+	/* Only first AES block will match, remainder subject to padding */
+	if (memcmp(rstkey24, blkkey24, 16)) {
+		dev_err(ksdev, "blkkey_ex: 192-bit restored key mismatch\n");
+		rtnval = -EINVAL;
+	}
+
+	if (memcmp(rstkey32, blkkey32, AES_BLOCK_PAD(32))) {
+		dev_err(ksdev, "blkkey_ex: 256-bit restored key mismatch\n");
+		rtnval = -EINVAL;
+	}
+
+
+	/* Remove keys from keystore */
+dealloc:
+	sm_keystore_slot_dealloc(ksdev, unit, keyslot32);
+dealloc_slot24:
+	sm_keystore_slot_dealloc(ksdev, unit, keyslot24);
+dealloc_slot16:
+	sm_keystore_slot_dealloc(ksdev, unit, keyslot16);
+dealloc_slot8:
+	sm_keystore_slot_dealloc(ksdev, unit, keyslot8);
+
+	/* Free resources */
+freemem:
+	kfree(blob8);
+	kfree(blob16);
+	kfree(blob24);
+	kfree(blob32);
+
+	/* Disconnect from keystore and leave */
+	sm_release_keystore(ksdev, unit);
+
+	return rtnval;
+}
+EXPORT_SYMBOL(caam_sm_example_init);
+
+void caam_sm_example_shutdown(void)
+{
+	/* unused in present version */
+	struct device_node *dev_node;
+	struct platform_device *pdev;
+
+	/*
+	 * Do of_find_compatible_node() then of_find_device_by_node()
+	 * once a functional device tree is available
+	 */
+	dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+	if (!dev_node) {
+		dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+		if (!dev_node)
+			return;
+	}
+
+	pdev = of_find_device_by_node(dev_node);
+	if (!pdev)
+		return;
+
+	of_node_get(dev_node);
+
+}
+
+static int __init caam_sm_test_init(void)
+{
+	struct device_node *dev_node;
+	struct platform_device *pdev;
+	struct caam_drv_private *priv;
+	int ret;
+
+	/*
+	 * Do of_find_compatible_node() then of_find_device_by_node()
+	 * once a functional device tree is available
+	 */
+	dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec-v4.0");
+	if (!dev_node) {
+		dev_node = of_find_compatible_node(NULL, NULL, "fsl,sec4.0");
+		if (!dev_node)
+			return -ENODEV;
+	}
+
+	pdev = of_find_device_by_node(dev_node);
+	if (!pdev)
+		return -ENODEV;
+
+	of_node_put(dev_node);
+
+	priv = dev_get_drvdata(&pdev->dev);
+	if (!priv) {
+		dev_info(&pdev->dev, "SM driver not ready, aborting tests\n");
+		return -ENODEV;
+	}
+	if (!priv->sm_present) {
+		dev_info(&pdev->dev, "No SM support, skipping tests\n");
+		return -ENODEV;
+	}
+	if (!priv->smdev) {
+		dev_info(&pdev->dev, "SM not initialized (no job rings?) skipping tests\n");
+		return -ENODEV;
+	}
+
+	ret = caam_sm_example_init(pdev);
+	if (ret)
+		dev_err(&pdev->dev, "SM test failed: %d\n", ret);
+	else
+		dev_info(&pdev->dev, "SM test passed\n");
+
+	return ret;
+}
+
+
+/* Module-based initialization needs to wait for dev tree */
+#ifdef CONFIG_OF
+module_init(caam_sm_test_init);
+module_exit(caam_sm_example_shutdown);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("FSL CAAM Black Key Usage Example");
+MODULE_AUTHOR("Freescale Semiconductor - NMSG/MAD");
+#endif
diff --git a/drivers/crypto/caam/snvsregs.h b/drivers/crypto/caam/snvsregs.h
new file mode 100644
index 000000000..4a7e76933
--- /dev/null
+++ b/drivers/crypto/caam/snvsregs.h
@@ -0,0 +1,239 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * SNVS hardware register-level view
+ *
+ * Copyright 2012-2015 Freescale Semiconductor, Inc.
+ * Copyright 2016-2019 NXP
+ */
+
+#ifndef SNVSREGS_H
+#define SNVSREGS_H
+
+#include <linux/types.h>
+#include <linux/io.h>
+
+/*
+ * SNVS High Power Domain
+ * Includes security violations, HA counter, RTC, alarm
+ */
+struct snvs_hp {
+	u32 lock;		/* HPLR - HP Lock */
+	u32 cmd;		/* HPCOMR - HP Command */
+	u32 ctl;		/* HPCR - HP Control */
+	u32 secvio_intcfg;	/* HPSICR - Security Violation Int Config */
+	u32 secvio_ctl;		/* HPSVCR - Security Violation Control */
+	u32 status;		/* HPSR - HP Status */
+	u32 secvio_status;	/* HPSVSR - Security Violation Status */
+	u32 ha_counteriv;	/* High Assurance Counter IV */
+	u32 ha_counter;		/* High Assurance Counter */
+	u32 rtc_msb;		/* Real Time Clock/Counter MSB */
+	u32 rtc_lsb;		/* Real Time Counter LSB */
+	u32 time_alarm_msb;	/* Time Alarm MSB */
+	u32 time_alarm_lsb;	/* Time Alarm LSB */
+};
+
+#define HP_LOCK_HAC_LCK		0x00040000
+#define HP_LOCK_HPSICR_LCK	0x00020000
+#define HP_LOCK_HPSVCR_LCK	0x00010000
+#define HP_LOCK_MKEYSEL_LCK	0x00000200
+#define HP_LOCK_TAMPCFG_LCK	0x00000100
+#define HP_LOCK_TAMPFLT_LCK	0x00000080
+#define HP_LOCK_SECVIO_LCK	0x00000040
+#define HP_LOCK_GENP_LCK	0x00000020
+#define HP_LOCK_MONOCTR_LCK	0x00000010
+#define HP_LOCK_CALIB_LCK	0x00000008
+#define HP_LOCK_SRTC_LCK	0x00000004
+#define HP_LOCK_ZMK_RD_LCK	0x00000002
+#define HP_LOCK_ZMK_WT_LCK	0x00000001
+
+#define HP_CMD_NONPRIV_AXS	0x80000000
+#define HP_CMD_HAC_STOP		0x00080000
+#define HP_CMD_HAC_CLEAR	0x00040000
+#define HP_CMD_HAC_LOAD		0x00020000
+#define HP_CMD_HAC_CFG_EN	0x00010000
+#define HP_CMD_SNVS_MSTR_KEY	0x00002000
+#define HP_CMD_PROG_ZMK		0x00001000
+#define HP_CMD_SW_LPSV		0x00000400
+#define HP_CMD_SW_FSV		0x00000200
+#define HP_CMD_SW_SV		0x00000100
+#define HP_CMD_LP_SWR_DIS	0x00000020
+#define HP_CMD_LP_SWR		0x00000010
+#define HP_CMD_SSM_SFNS_DIS	0x00000004
+#define HP_CMD_SSM_ST_DIS	0x00000002
+#define HP_CMD_SMM_ST		0x00000001
+
+#define HP_CTL_TIME_SYNC	0x00010000
+#define HP_CTL_CAL_VAL_SHIFT	10
+#define HP_CTL_CAL_VAL_MASK	(0x1f << HP_CTL_CALIB_SHIFT)
+#define HP_CTL_CALIB_EN		0x00000100
+#define HP_CTL_PI_FREQ_SHIFT	4
+#define HP_CTL_PI_FREQ_MASK	(0xf << HP_CTL_PI_FREQ_SHIFT)
+#define HP_CTL_PI_EN		0x00000008
+#define HP_CTL_TIMEALARM_EN	0x00000002
+#define HP_CTL_RTC_EN		0x00000001
+
+#define HP_SECVIO_INTEN_EN	0x10000000
+#define HP_SECVIO_INTEN_SRC5	0x00000020
+#define HP_SECVIO_INTEN_SRC4	0x00000010
+#define HP_SECVIO_INTEN_SRC3	0x00000008
+#define HP_SECVIO_INTEN_SRC2	0x00000004
+#define HP_SECVIO_INTEN_SRC1	0x00000002
+#define HP_SECVIO_INTEN_SRC0	0x00000001
+#define HP_SECVIO_INTEN_ALL	0x8000003f
+
+#define HP_SECVIO_ICTL_CFG_SHIFT	30
+#define HP_SECVIO_ICTL_CFG_MASK		(0x3 << HP_SECVIO_ICTL_CFG_SHIFT)
+#define HP_SECVIO_ICTL_CFG5_SHIFT	5
+#define HP_SECVIO_ICTL_CFG5_MASK	(0x3 << HP_SECVIO_ICTL_CFG5_SHIFT)
+#define HP_SECVIO_ICTL_CFG_DISABLE	0
+#define HP_SECVIO_ICTL_CFG_NONFATAL	1
+#define HP_SECVIO_ICTL_CFG_FATAL	2
+#define HP_SECVIO_ICTL_CFG4_FATAL	0x00000010
+#define HP_SECVIO_ICTL_CFG3_FATAL	0x00000008
+#define HP_SECVIO_ICTL_CFG2_FATAL	0x00000004
+#define HP_SECVIO_ICTL_CFG1_FATAL	0x00000002
+#define HP_SECVIO_ICTL_CFG0_FATAL	0x00000001
+
+#define HP_STATUS_ZMK_ZERO		0x80000000
+#define HP_STATUS_OTPMK_ZERO		0x08000000
+#define HP_STATUS_OTPMK_SYN_SHIFT	16
+#define HP_STATUS_OTPMK_SYN_MASK	(0x1ff << HP_STATUS_OTPMK_SYN_SHIFT)
+#define HP_STATUS_SSM_ST_SHIFT		8
+#define HP_STATUS_SSM_ST_MASK		(0xf << HP_STATUS_SSM_ST_SHIFT)
+#define HP_STATUS_SSM_ST_INIT		0
+#define HP_STATUS_SSM_ST_HARDFAIL	1
+#define HP_STATUS_SSM_ST_SOFTFAIL	3
+#define HP_STATUS_SSM_ST_INITINT	8
+#define HP_STATUS_SSM_ST_CHECK		9
+#define HP_STATUS_SSM_ST_NONSECURE	11
+#define HP_STATUS_SSM_ST_TRUSTED	13
+#define HP_STATUS_SSM_ST_SECURE		15
+
+#define HP_SECVIOST_ZMK_ECC_FAIL	0x08000000	/* write to clear */
+#define HP_SECVIOST_ZMK_SYN_SHIFT	16
+#define HP_SECVIOST_ZMK_SYN_MASK	(0x1ff << HP_SECVIOST_ZMK_SYN_SHIFT)
+#define HP_SECVIOST_SECVIO5		0x00000020
+#define HP_SECVIOST_SECVIO4		0x00000010
+#define HP_SECVIOST_SECVIO3		0x00000008
+#define HP_SECVIOST_SECVIO2		0x00000004
+#define HP_SECVIOST_SECVIO1		0x00000002
+#define HP_SECVIOST_SECVIO0		0x00000001
+#define HP_SECVIOST_SECVIOMASK		0x0000003f
+
+/*
+ * SNVS Low Power Domain
+ * Includes glitch detector, SRTC, alarm, monotonic counter, ZMK
+ */
+struct snvs_lp {
+	u32 lock;
+	u32 ctl;
+	u32 mstr_key_ctl;	/* Master Key Control */
+	u32 secvio_ctl;		/* Security Violation Control */
+	u32 tamper_filt_cfg;	/* Tamper Glitch Filters Configuration */
+	u32 tamper_det_cfg;	/* Tamper Detectors Configuration */
+	u32 status;
+	u32 srtc_msb;		/* Secure Real Time Clock/Counter MSB */
+	u32 srtc_lsb;		/* Secure Real Time Clock/Counter LSB */
+	u32 time_alarm;		/* Time Alarm */
+	u32 smc_msb;		/* Secure Monotonic Counter MSB */
+	u32 smc_lsb;		/* Secure Monotonic Counter LSB */
+	u32 pwr_glitch_det;	/* Power Glitch Detector */
+	u32 gen_purpose;
+	u32 zmk[8];		/* Zeroizable Master Key */
+};
+
+#define LP_LOCK_MKEYSEL_LCK	0x00000200
+#define LP_LOCK_TAMPDET_LCK	0x00000100
+#define LP_LOCK_TAMPFLT_LCK	0x00000080
+#define LP_LOCK_SECVIO_LCK	0x00000040
+#define LP_LOCK_GENP_LCK	0x00000020
+#define LP_LOCK_MONOCTR_LCK	0x00000010
+#define LP_LOCK_CALIB_LCK	0x00000008
+#define LP_LOCK_SRTC_LCK	0x00000004
+#define LP_LOCK_ZMK_RD_LCK	0x00000002
+#define LP_LOCK_ZMK_WT_LCK	0x00000001
+
+#define LP_CTL_CAL_VAL_SHIFT	10
+#define LP_CTL_CAL_VAL_MASK	(0x1f << LP_CTL_CAL_VAL_SHIFT)
+#define LP_CTL_CALIB_EN		0x00000100
+#define LP_CTL_SRTC_INVAL_EN	0x00000010
+#define LP_CTL_WAKE_INT_EN	0x00000008
+#define LP_CTL_MONOCTR_EN	0x00000004
+#define LP_CTL_TIMEALARM_EN	0x00000002
+#define LP_CTL_SRTC_EN		0x00000001
+
+#define LP_MKEYCTL_ZMKECC_SHIFT	8
+#define LP_MKEYCTL_ZMKECC_MASK	(0xff << LP_MKEYCTL_ZMKECC_SHIFT)
+#define LP_MKEYCTL_ZMKECC_EN	0x00000010
+#define LP_MKEYCTL_ZMKECC_VAL	0x00000008
+#define LP_MKEYCTL_ZMKECC_PROG	0x00000004
+#define LP_MKEYCTL_MKSEL_SHIFT	0
+#define LP_MKEYCTL_MKSEL_MASK	(3 << LP_MKEYCTL_MKSEL_SHIFT)
+#define LP_MKEYCTL_MK_OTP	0
+#define LP_MKEYCTL_MK_ZMK	2
+#define LP_MKEYCTL_MK_COMB	3
+
+#define LP_SECVIO_CTL_SRC5	0x20
+#define LP_SECVIO_CTL_SRC4	0x10
+#define LP_SECVIO_CTL_SRC3	0x08
+#define LP_SECVIO_CTL_SRC2	0x04
+#define LP_SECVIO_CTL_SRC1	0x02
+#define LP_SECVIO_CTL_SRC0	0x01
+
+#define LP_TAMPFILT_EXT2_EN	0x80000000
+#define LP_TAMPFILT_EXT2_SHIFT	24
+#define LP_TAMPFILT_EXT2_MASK	(0x1f << LP_TAMPFILT_EXT2_SHIFT)
+#define LP_TAMPFILT_EXT1_EN	0x00800000
+#define LP_TAMPFILT_EXT1_SHIFT	16
+#define LP_TAMPFILT_EXT1_MASK	(0x1f << LP_TAMPFILT_EXT1_SHIFT)
+#define LP_TAMPFILT_WM_EN	0x00000080
+#define LP_TAMPFILT_WM_SHIFT	0
+#define LP_TAMPFILT_WM_MASK	(0x1f << LP_TAMPFILT_WM_SHIFT)
+
+#define LP_TAMPDET_OSC_BPS	0x10000000
+#define LP_TAMPDET_VRC_SHIFT	24
+#define LP_TAMPDET_VRC_MASK	(3 << LP_TAMPFILT_VRC_SHIFT)
+#define LP_TAMPDET_HTDC_SHIFT	20
+#define LP_TAMPDET_HTDC_MASK	(3 << LP_TAMPFILT_HTDC_SHIFT)
+#define LP_TAMPDET_LTDC_SHIFT	16
+#define LP_TAMPDET_LTDC_MASK	(3 << LP_TAMPFILT_LTDC_SHIFT)
+#define LP_TAMPDET_POR_OBS	0x00008000
+#define LP_TAMPDET_PFD_OBS	0x00004000
+#define LP_TAMPDET_ET2_EN	0x00000400
+#define LP_TAMPDET_ET1_EN	0x00000200
+#define LP_TAMPDET_WMT2_EN	0x00000100
+#define LP_TAMPDET_WMT1_EN	0x00000080
+#define LP_TAMPDET_VT_EN	0x00000040
+#define LP_TAMPDET_TT_EN	0x00000020
+#define LP_TAMPDET_CT_EN	0x00000010
+#define LP_TAMPDET_MCR_EN	0x00000004
+#define LP_TAMPDET_SRTCR_EN	0x00000002
+
+#define LP_STATUS_SECURE
+#define LP_STATUS_NONSECURE
+#define LP_STATUS_SCANEXIT	0x00100000	/* all write 1 clear here on */
+#define LP_STATUS_EXT_SECVIO	0x00010000
+#define LP_STATUS_ET2		0x00000400
+#define LP_STATUS_ET1		0x00000200
+#define LP_STATUS_WMT2		0x00000100
+#define LP_STATUS_WMT1		0x00000080
+#define LP_STATUS_VTD		0x00000040
+#define LP_STATUS_TTD		0x00000020
+#define LP_STATUS_CTD		0x00000010
+#define LP_STATUS_PGD		0x00000008
+#define LP_STATUS_MCR		0x00000004
+#define LP_STATUS_SRTCR		0x00000002
+#define LP_STATUS_LPTA		0x00000001
+
+/* Full SNVS register page, including version/options */
+struct snvs_full {
+	struct snvs_hp hp;
+	struct snvs_lp lp;
+	u32 rsvd[731];		/* deadspace 0x08c-0xbf7 */
+
+	/* Version / Revision / Option ID space - end of register page */
+	u32 vid;		/* 0xbf8 HP Version ID (VID 1) */
+	u32 opt_rev;		/* 0xbfc HP Options / Revision (VID 2) */
+};
+
+#endif /* SNVSREGS_H */
diff --git a/drivers/crypto/caam/tag_object.c b/drivers/crypto/caam/tag_object.c
new file mode 100644
index 000000000..53f70129e
--- /dev/null
+++ b/drivers/crypto/caam/tag_object.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/*
+ * Copyright 2018-2020 NXP
+ */
+
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include "tag_object.h"
+#include "desc.h"
+
+/**
+ * is_key_type -	Check if the object is a key
+ *
+ * @type:		The object type
+ *
+ * Return:		True if the object is a key (of black or red color),
+ *			false otherwise
+ */
+bool is_key_type(u32 type)
+{
+	/* Check type bitfield from object type */
+	return ((type >> TAG_OBJ_TYPE_OFFSET) & TAG_OBJ_TYPE_MASK) == 0;
+}
+EXPORT_SYMBOL(is_key_type);
+
+/**
+ * is_trusted_type -	Check if the object is a trusted key
+ *			Trusted Descriptor Key Encryption Key (TDKEK)
+ *
+ * @type:		The object type
+ *
+ * Return:		True if the object is a trusted key,
+ *			false otherwise
+ */
+bool is_trusted_type(u32 type)
+{
+	/* Check type bitfield from object type */
+	return ((type >> TAG_OBJ_TK_OFFSET) & TAG_OBJ_TK_MASK) == 1;
+}
+EXPORT_SYMBOL(is_trusted_type);
+
+/**
+ * is_black_key -	Check if the tag object header is a black key
+ * @header:		The tag object header configuration
+ *
+ * Return:		True if is a black key, false otherwise
+ */
+bool is_black_key(const struct header_conf *header)
+{
+	u32 type = header->type;
+	/* Check type and color bitfields from tag object type */
+	return (type & (BIT(TAG_OBJ_COLOR_OFFSET) |
+			BIT(TAG_OBJ_TYPE_OFFSET))) == BIT(TAG_OBJ_COLOR_OFFSET);
+}
+EXPORT_SYMBOL(is_black_key);
+
+/**
+ * is_valid_header_conf - Check if the header configuration is valid
+ * @header:		The header configuration
+ *
+ * Return:		True if the header of the tag object configuration,
+ *			has the TAG_OBJECT_MAGIC number and a valid type,
+ *			false otherwise
+ */
+bool is_valid_header_conf(const struct header_conf *header)
+{
+	return (header->_magic_number == TAG_OBJECT_MAGIC);
+}
+EXPORT_SYMBOL(is_valid_header_conf);
+
+/**
+ * get_key_conf -	Retrieve the key configuration,
+ *			meaning the length of the black key and
+ *			the KEY command parameters needed for CAAM
+ * @header:		The tag object header configuration
+ * @red_key_len:	Red key length
+ * @obj_len:		Black/Red key/blob length
+ * @load_param:		Load parameters for KEY command:
+ *			- indicator for encrypted keys: plaintext or black
+ *			- indicator for encryption mode: AES-ECB or AES-CCM
+ *			- indicator for encryption keys: JDKEK or TDKEK
+ */
+void get_key_conf(const struct header_conf *header,
+		  u32 *red_key_len, u32 *obj_len, u32 *load_param)
+{
+	*red_key_len = header->red_key_len;
+	*obj_len = header->obj_len;
+	/* Based on the color of the key, set key encryption bit (ENC) */
+	*load_param = ((header->type >> TAG_OBJ_COLOR_OFFSET) &
+		       TAG_OBJ_COLOR_MASK) << KEY_ENC_OFFSET;
+	/*
+	 * For red keys, the TK and EKT bits are ignored.
+	 * So we set them anyway, to be valid when the key is black.
+	 */
+	*load_param |= ((header->type >> TAG_OBJ_TK_OFFSET) &
+			 TAG_OBJ_TK_MASK) << KEY_TK_OFFSET;
+	*load_param |= ((header->type >> TAG_OBJ_EKT_OFFSET) &
+			 TAG_OBJ_EKT_MASK) << KEY_EKT_OFFSET;
+}
+EXPORT_SYMBOL(get_key_conf);
+
+/**
+ * init_tag_object_header - Initialize the tag object header by setting up
+ *			the TAG_OBJECT_MAGIC number, tag object version,
+ *			a valid type and the object's length
+ * @header:		The header configuration to initialize
+ * @version:		The tag object version
+ * @type:		The tag object type
+ * @red_key_len:	The red key length
+ * @obj_len:		The object (actual data) length
+ */
+void init_tag_object_header(struct header_conf *header, u32 version,
+			    u32 type, size_t red_key_len, size_t obj_len)
+{
+	header->_magic_number = TAG_OBJECT_MAGIC;
+	header->version = version;
+	header->type = type;
+	header->red_key_len = red_key_len;
+	header->obj_len = obj_len;
+}
+EXPORT_SYMBOL(init_tag_object_header);
+
+/**
+ * set_tag_object_header_conf - Set tag object header configuration
+ * @header:			The tag object header configuration to set
+ * @buffer:			The buffer needed to be tagged
+ * @buf_size:			The buffer size
+ * @tag_obj_size:		The tagged object size
+ *
+ * Return:			'0' on success, error code otherwise
+ */
+int set_tag_object_header_conf(const struct header_conf *header,
+			       void *buffer, size_t buf_size, u32 *tag_obj_size)
+{
+	/* Retrieve the tag object */
+	struct tagged_object *tag_obj = (struct tagged_object *)buffer;
+	/*
+	 * Requested size for the tagged object is the buffer size
+	 * and the header configuration size (TAG_OVERHEAD_SIZE)
+	 */
+	size_t req_size = buf_size + TAG_OVERHEAD_SIZE;
+
+	/*
+	 * Check if the configuration can be set,
+	 * based on the size of the tagged object
+	 */
+	if (*tag_obj_size < req_size)
+		return -EINVAL;
+
+	/*
+	 * Buffers might overlap, use memmove to
+	 * copy the buffer into the tagged object
+	 */
+	memmove(&tag_obj->object, buffer, buf_size);
+	/* Copy the tag object header configuration into the tagged object */
+	memcpy(&tag_obj->header, header, TAG_OVERHEAD_SIZE);
+	/* Set tagged object size */
+	*tag_obj_size = req_size;
+
+	return 0;
+}
+EXPORT_SYMBOL(set_tag_object_header_conf);
diff --git a/drivers/crypto/caam/tag_object.h b/drivers/crypto/caam/tag_object.h
new file mode 100644
index 000000000..6c840c30c
--- /dev/null
+++ b/drivers/crypto/caam/tag_object.h
@@ -0,0 +1,111 @@
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
+/*
+ * Copyright 2018-2020 NXP
+ */
+
+#ifndef _TAG_OBJECT_H_
+#define _TAG_OBJECT_H_
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+/**
+ * Magic number to identify the tag object structure
+ * 0x54 = 'T'
+ * 0x61 = 'a'
+ * 0x67 = 'g'
+ * 0x4f = 'O'
+ */
+#define TAG_OBJECT_MAGIC	0x5461674f
+#define TAG_OVERHEAD_SIZE	sizeof(struct header_conf)
+#define MIN_KEY_SIZE		16
+#define TAG_MIN_SIZE		(MIN_KEY_SIZE + TAG_OVERHEAD_SIZE)
+/*
+ * Tag object type is a bitfield:
+ *
+ * EKT:	Encrypted Key Type (AES-ECB or AES-CCM)
+ * TK:	Trusted Key (use Job Descriptor Key Encryption Key (JDKEK)
+ *	or Trusted Descriptor Key Encryption Key (TDKEK) to
+ *	decrypt the key to be loaded into a Key Register).
+ *
+ *| Denomination | Security state | Memory  | EKT | TK    | Type | Color |
+ *| ------------ | -------------- | ------- | --- | ----- | ---- | ----- |
+ *| bit(s)       | 5-6            | 4       | 3   | 2     | 1    | 0     |
+ *| option 0     | non-secure     | general | ECB | JDKEK | key  | red   |
+ *| option 1     | secure         | secure  | CCM | TDKEK | blob | black |
+ *| option 2     | trusted        |         |     |       |      |       |
+ *
+ * CAAM supports two different Black Key encapsulation schemes,
+ * one intended for quick decryption (uses AES-ECB encryption),
+ * and another intended for high assurance (uses AES-CCM encryption).
+ *
+ * CAAM implements both Trusted and normal (non-Trusted) Black Keys,
+ * which are encrypted with different key-encryption keys.
+ * Both Trusted and normal Descriptors are allowed to encrypt or decrypt
+ * normal Black Keys, but only Trusted Descriptors are allowed to
+ * encrypt or decrypt Trusted Black Keys.
+ */
+#define TAG_OBJ_COLOR_OFFSET		0
+#define TAG_OBJ_COLOR_MASK		0x1
+#define TAG_OBJ_TYPE_OFFSET		1
+#define TAG_OBJ_TYPE_MASK		0x1
+#define TAG_OBJ_TK_OFFSET		2
+#define TAG_OBJ_TK_MASK			0x1
+#define TAG_OBJ_EKT_OFFSET		3
+#define TAG_OBJ_EKT_MASK		0x1
+#define TAG_OBJ_MEM_OFFSET		4
+#define TAG_OBJ_MEM_MASK		0x1
+#define TAG_OBJ_SEC_STATE_OFFSET	5
+
+/**
+ * struct header_conf - Header configuration structure, which represents
+ *			the metadata (or simply a header) applied to the
+ *			actual data (e.g. black key)
+ * @_magic_number     : A magic number to identify the structure
+ * @version           : The version of the data contained (e.g. tag object)
+ * @type              : The type of data contained (e.g. black key, blob, etc.)
+ * @red_key_len       : Length of the red key to be loaded by CAAM (for key
+ *                      generation or blob encapsulation)
+ * @obj_len           : The total length of the (black/red) object (key/blob),
+ *                      after encryption/encapsulation
+ */
+struct header_conf {
+	u32 _magic_number;
+	u32 version;
+	u32 type;
+	u32 red_key_len;
+	u32 obj_len;
+};
+
+/**
+ * struct tagged_object - Tag object structure, which represents the metadata
+ *                        (or simply a header) and the actual data
+ *                        (e.g. black key) obtained from hardware
+ * @tag                 : The configuration of the data (e.g. header)
+ * @object              : The actual data (e.g. black key)
+ */
+struct tagged_object {
+	struct header_conf header;
+	char object;
+};
+
+bool is_key_type(u32 type);
+
+bool is_trusted_type(u32 type);
+
+bool is_black_key(const struct header_conf * const header);
+
+bool is_black_key(const struct header_conf * const header);
+
+bool is_valid_header_conf(const struct header_conf *header);
+
+void get_key_conf(const struct header_conf *header,
+		  u32 *red_key_len, u32 *obj_len, u32 *load_param);
+
+void init_tag_object_header(struct header_conf *header, u32 version,
+			    u32 type, size_t red_key_len, size_t obj_len);
+
+int set_tag_object_header_conf(const struct header_conf *header,
+			       void *buffer, size_t obj_size, u32 *to_size);
+
+#endif /* _TAG_OBJECT_H_ */
diff --git a/drivers/crypto/mxs-dcp.c b/drivers/crypto/mxs-dcp.c
index 5edc91cdb..37266abbd 100644
--- a/drivers/crypto/mxs-dcp.c
+++ b/drivers/crypto/mxs-dcp.c
@@ -16,6 +16,10 @@
 #include <linux/stmp_device.h>
 #include <linux/clk.h>
 
+#ifdef CONFIG_PM_SLEEP
+#include <linux/freezer.h>
+#endif
+
 #include <crypto/aes.h>
 #include <crypto/sha.h>
 #include <crypto/internal/hash.h>
@@ -123,7 +127,10 @@ struct dcp_export_state {
  * design of Linux Crypto API.
  */
 static struct dcp *global_sdcp;
-
+#ifdef CONFIG_PM_SLEEP
+static uint32_t ctrl_bak;
+static int dcp_vmi_irq_bak, dcp_irq_bak;
+#endif
 /* DCP register layout. */
 #define MXS_DCP_CTRL				0x00
 #define MXS_DCP_CTRL_GATHER_RESIDUAL_WRITES	(1 << 23)
@@ -316,6 +323,9 @@ static int mxs_dcp_aes_block_crypt(struct crypto_async_request *arq)
 	int init = 0;
 	bool limit_hit = false;
 
+	if (!req->cryptlen)
+		return 0;
+
 	actx->fill = 0;
 
 	/* Copy the key from the temporary location. */
@@ -396,9 +406,15 @@ static int dcp_chan_thread_aes(void *data)
 
 	int ret;
 
+#ifdef CONFIG_PM_SLEEP
+	set_freezable();
+#endif
 	while (!kthread_should_stop()) {
 		set_current_state(TASK_INTERRUPTIBLE);
 
+#ifdef CONFIG_PM_SLEEP
+		try_to_freeze();
+#endif
 		spin_lock(&sdcp->lock[chan]);
 		backlog = crypto_get_backlog(&sdcp->queue[chan]);
 		arq = crypto_dequeue_request(&sdcp->queue[chan]);
@@ -436,6 +452,10 @@ static int mxs_dcp_block_fallback(struct skcipher_request *req, int enc)
 	skcipher_request_set_crypt(&rctx->fallback_req, req->src, req->dst,
 				   req->cryptlen, req->iv);
 
+#ifdef CONFIG_PM_SLEEP
+set_freezable();
+try_to_freeze();
+#endif
 	if (enc)
 		ret = crypto_skcipher_encrypt(&rctx->fallback_req);
 	else
@@ -692,9 +712,15 @@ static int dcp_chan_thread_sha(void *data)
 	struct crypto_async_request *arq;
 	int ret;
 
+#ifdef CONFIG_PM_SLEEP
+	set_freezable();
+#endif
 	while (!kthread_should_stop()) {
 		set_current_state(TASK_INTERRUPTIBLE);
 
+#ifdef CONFIG_PM_SLEEP
+		try_to_freeze();
+#endif
 		spin_lock(&sdcp->lock[chan]);
 		backlog = crypto_get_backlog(&sdcp->queue[chan]);
 		arq = crypto_dequeue_request(&sdcp->queue[chan]);
@@ -967,6 +993,49 @@ static irqreturn_t mxs_dcp_irq(int irq, void *context)
 	return IRQ_HANDLED;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int mxs_dcp_resume(struct device *dev)
+{
+	struct dcp *sdcp = global_sdcp;
+	int ret;
+
+	/* Restart the DCP block */
+	ret = stmp_reset_block(sdcp->base);
+	if (ret) {
+		dev_err(dev, "Failed reset\n");
+		clk_disable_unprepare(sdcp->dcp_clk);
+		return ret;
+	}
+
+	/* Restore control register */
+	writel(ctrl_bak, sdcp->base + MXS_DCP_CTRL);
+	/* Enable all DCP DMA channels */
+	writel(MXS_DCP_CHANNELCTRL_ENABLE_CHANNEL_MASK,
+	       sdcp->base + MXS_DCP_CHANNELCTRL);
+
+	/* Re-enable DCP interrupts */
+	enable_irq(dcp_irq_bak);
+	enable_irq(dcp_vmi_irq_bak);
+
+	return 0;
+}
+
+static int mxs_dcp_suspend(struct device *dev)
+{
+	struct dcp *sdcp = global_sdcp;
+
+	/* Backup control register */
+	ctrl_bak = readl(sdcp->base + MXS_DCP_CTRL);
+	/* Temporarily disable DCP interrupts */
+	disable_irq(dcp_irq_bak);
+	disable_irq(dcp_vmi_irq_bak);
+
+	return 0;
+}
+
+SIMPLE_DEV_PM_OPS(mxs_dcp_pm_ops, mxs_dcp_suspend, mxs_dcp_resume);
+#endif
+
 static int mxs_dcp_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -986,7 +1055,10 @@ static int mxs_dcp_probe(struct platform_device *pdev)
 	dcp_irq = platform_get_irq(pdev, 1);
 	if (dcp_irq < 0)
 		return dcp_irq;
-
+#ifdef CONFIG_PM_SLEEP
+	dcp_vmi_irq_bak = dcp_vmi_irq;
+	dcp_irq_bak = dcp_irq;
+#endif
 	sdcp = devm_kzalloc(dev, sizeof(*sdcp), GFP_KERNEL);
 	if (!sdcp)
 		return -ENOMEM;
@@ -1178,6 +1250,9 @@ static struct platform_driver mxs_dcp_driver = {
 	.driver	= {
 		.name		= "mxs-dcp",
 		.of_match_table	= mxs_dcp_dt_ids,
+#ifdef CONFIG_PM_SLEEP
+		.pm = &mxs_dcp_pm_ops
+#endif
 	},
 };
 
