#include <linux/platform_device.h>
 #include <linux/stmp_device.h>
 #include <linux/clk.h>
+#include <soc/fsl/dcp.h>
 
 #include <crypto/aes.h>
 #include <crypto/sha1.h>
        struct crypto_skcipher          *fallback;
        unsigned int                    key_len;
        uint8_t                         key[AES_KEYSIZE_128];
+       bool                            key_referenced;
 };
 
 struct dcp_aes_req_ctx {
 #define MXS_DCP_CONTROL0_HASH_TERM             (1 << 13)
 #define MXS_DCP_CONTROL0_HASH_INIT             (1 << 12)
 #define MXS_DCP_CONTROL0_PAYLOAD_KEY           (1 << 11)
+#define MXS_DCP_CONTROL0_OTP_KEY               (1 << 10)
 #define MXS_DCP_CONTROL0_CIPHER_ENCRYPT                (1 << 8)
 #define MXS_DCP_CONTROL0_CIPHER_INIT           (1 << 9)
 #define MXS_DCP_CONTROL0_ENABLE_HASH           (1 << 6)
 #define MXS_DCP_CONTROL1_CIPHER_MODE_ECB       (0 << 4)
 #define MXS_DCP_CONTROL1_CIPHER_SELECT_AES128  (0 << 0)
 
+#define MXS_DCP_CONTROL1_KEY_SELECT_SHIFT      8
+
 static int mxs_dcp_start_dma(struct dcp_async_ctx *actx)
 {
        int dma_err;
        struct dcp *sdcp = global_sdcp;
        struct dcp_dma_desc *desc = &sdcp->coh->desc[actx->chan];
        struct dcp_aes_req_ctx *rctx = skcipher_request_ctx(req);
+       bool key_referenced = actx->key_referenced;
        int ret;
 
-       key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
-                                 2 * AES_KEYSIZE_128, DMA_TO_DEVICE);
-       ret = dma_mapping_error(sdcp->dev, key_phys);
-       if (ret)
-               return ret;
+       if (!key_referenced) {
+               key_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_key,
+                                         2 * AES_KEYSIZE_128, DMA_TO_DEVICE);
+               ret = dma_mapping_error(sdcp->dev, key_phys);
+               if (ret)
+                       return ret;
+       }
 
        src_phys = dma_map_single(sdcp->dev, sdcp->coh->aes_in_buf,
                                  DCP_BUF_SZ, DMA_TO_DEVICE);
                    MXS_DCP_CONTROL0_INTERRUPT |
                    MXS_DCP_CONTROL0_ENABLE_CIPHER;
 
-       /* Payload contains the key. */
-       desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
+       if (key_referenced)
+               /* Set OTP key bit to select the key via KEY_SELECT. */
+               desc->control0 |= MXS_DCP_CONTROL0_OTP_KEY;
+       else
+               /* Payload contains the key. */
+               desc->control0 |= MXS_DCP_CONTROL0_PAYLOAD_KEY;
 
        if (rctx->enc)
                desc->control0 |= MXS_DCP_CONTROL0_CIPHER_ENCRYPT;
        else
                desc->control1 |= MXS_DCP_CONTROL1_CIPHER_MODE_CBC;
 
+       if (key_referenced)
+               desc->control1 |= sdcp->coh->aes_key[0] << MXS_DCP_CONTROL1_KEY_SELECT_SHIFT;
+
        desc->next_cmd_addr = 0;
        desc->source = src_phys;
        desc->destination = dst_phys;
 err_dst:
        dma_unmap_single(sdcp->dev, src_phys, DCP_BUF_SZ, DMA_TO_DEVICE);
 err_src:
-       dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
-                        DMA_TO_DEVICE);
-
+       if (!key_referenced)
+               dma_unmap_single(sdcp->dev, key_phys, 2 * AES_KEYSIZE_128,
+                                DMA_TO_DEVICE);
        return ret;
 }
 
        struct dcp_aes_req_ctx *rctx = skcipher_request_ctx(req);
        int ret;
 
-       if (unlikely(actx->key_len != AES_KEYSIZE_128))
+       if (unlikely(actx->key_len != AES_KEYSIZE_128 && !actx->key_referenced))
                return mxs_dcp_block_fallback(req, enc);
 
        rctx->enc = enc;
         * there can still be an operation in progress.
         */
        actx->key_len = len;
+       actx->key_referenced = false;
        if (len == AES_KEYSIZE_128) {
                memcpy(actx->key, key, len);
                return 0;
        return crypto_skcipher_setkey(actx->fallback, key, len);
 }
 
+static int mxs_dcp_aes_setrefkey(struct crypto_skcipher *tfm, const u8 *key,
+                                unsigned int len)
+{
+       struct dcp_async_ctx *actx = crypto_skcipher_ctx(tfm);
+
+       if (len != DCP_PAES_KEYSIZE)
+               return -EINVAL;
+
+       switch (key[0]) {
+       case DCP_PAES_KEY_SLOT0:
+       case DCP_PAES_KEY_SLOT1:
+       case DCP_PAES_KEY_SLOT2:
+       case DCP_PAES_KEY_SLOT3:
+       case DCP_PAES_KEY_UNIQUE:
+       case DCP_PAES_KEY_OTP:
+               memcpy(actx->key, key, len);
+               actx->key_len = len;
+               actx->key_referenced = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int mxs_dcp_aes_fallback_init_tfm(struct crypto_skcipher *tfm)
 {
        const char *name = crypto_tfm_alg_name(crypto_skcipher_tfm(tfm));
        crypto_free_skcipher(actx->fallback);
 }
 
+static int mxs_dcp_paes_init_tfm(struct crypto_skcipher *tfm)
+{
+       crypto_skcipher_set_reqsize(tfm, sizeof(struct dcp_aes_req_ctx));
+
+       return 0;
+}
+
 /*
  * Hashing (SHA1/SHA256)
  */
                .ivsize                 = AES_BLOCK_SIZE,
                .init                   = mxs_dcp_aes_fallback_init_tfm,
                .exit                   = mxs_dcp_aes_fallback_exit_tfm,
+       }, {
+               .base.cra_name          = "ecb(paes)",
+               .base.cra_driver_name   = "ecb-paes-dcp",
+               .base.cra_priority      = 401,
+               .base.cra_alignmask     = 15,
+               .base.cra_flags         = CRYPTO_ALG_ASYNC | CRYPTO_ALG_INTERNAL,
+               .base.cra_blocksize     = AES_BLOCK_SIZE,
+               .base.cra_ctxsize       = sizeof(struct dcp_async_ctx),
+               .base.cra_module        = THIS_MODULE,
+
+               .min_keysize            = DCP_PAES_KEYSIZE,
+               .max_keysize            = DCP_PAES_KEYSIZE,
+               .setkey                 = mxs_dcp_aes_setrefkey,
+               .encrypt                = mxs_dcp_aes_ecb_encrypt,
+               .decrypt                = mxs_dcp_aes_ecb_decrypt,
+               .init                   = mxs_dcp_paes_init_tfm,
+       }, {
+               .base.cra_name          = "cbc(paes)",
+               .base.cra_driver_name   = "cbc-paes-dcp",
+               .base.cra_priority      = 401,
+               .base.cra_alignmask     = 15,
+               .base.cra_flags         = CRYPTO_ALG_ASYNC | CRYPTO_ALG_INTERNAL,
+               .base.cra_blocksize     = AES_BLOCK_SIZE,
+               .base.cra_ctxsize       = sizeof(struct dcp_async_ctx),
+               .base.cra_module        = THIS_MODULE,
+
+               .min_keysize            = DCP_PAES_KEYSIZE,
+               .max_keysize            = DCP_PAES_KEYSIZE,
+               .setkey                 = mxs_dcp_aes_setrefkey,
+               .encrypt                = mxs_dcp_aes_cbc_encrypt,
+               .decrypt                = mxs_dcp_aes_cbc_decrypt,
+               .ivsize                 = AES_BLOCK_SIZE,
+               .init                   = mxs_dcp_paes_init_tfm,
        },
 };