imx23: add hw crypto (dcp) support
authorZoltan Herpai <wigyori@uid0.hu>
Sat, 3 Aug 2013 10:35:18 +0000 (10:35 +0000)
committerZoltan Herpai <wigyori@uid0.hu>
Sat, 3 Aug 2013 10:35:18 +0000 (10:35 +0000)
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 37662

target/linux/imx23/modules.mk
target/linux/imx23/patches/105-imx23-dcp.patch [new file with mode: 0644]
target/linux/imx23/patches/106-add-dcp-dts.patch [new file with mode: 0644]

index 119ddd7bd74d3b136e8f68fb7934ff9518c44885..f999c31e85ad7e53ecbfeb93e1b8e9b7061a18a3 100644 (file)
@@ -115,3 +115,18 @@ define KernelPackage/iio-mxs-lradc/description
 endef
 
 $(eval $(call KernelPackage,iio-mxs-lradc))
+
+define KernelPackage/crypto-hw-dcp
+    TITLE:=i.MX23/28 DCP hardware crypto module
+    DEPENDS:=@TARGET_imx23
+    KCONFIG:=CONFIG_CRYPTO_DEV_DCP
+    FILES:=$(LINUX_DIR)/drivers/crypto/dcp.ko
+    AUTOLOAD:=$(call AutoLoad,90,dcp)
+    $(call AddDepends/crypto,+kmod-crypto-authenc +kmod-crypto-des)
+endef
+
+define KernelPackage/crypto-hw-dcp/description
+    Kernel support for the i.MX23/28 DCP crypto engine
+endef
+
+$(eval $(call KernelPackage,crypto-hw-dcp))
diff --git a/target/linux/imx23/patches/105-imx23-dcp.patch b/target/linux/imx23/patches/105-imx23-dcp.patch
new file mode 100644 (file)
index 0000000..cbc25a0
--- /dev/null
@@ -0,0 +1,966 @@
+diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
+index dffb855..3e11215 100644
+--- a/drivers/crypto/Kconfig
++++ b/drivers/crypto/Kconfig
+@@ -286,6 +286,16 @@ config CRYPTO_DEV_SAHARA
+          This option enables support for the SAHARA HW crypto accelerator
+          found in some Freescale i.MX chips.
++config CRYPTO_DEV_DCP
++       tristate "Support for the DCP engine"
++       depends on ARCH_MXS && OF
++       select CRYPTO_BLKCIPHER
++       select CRYPTO_AES
++       select CRYPTO_CBC
++       help
++         This options enables support for the hardware crypto-acceleration
++         capabilities of the DCP co-processor
++
+ config CRYPTO_DEV_S5P
+        tristate "Support for Samsung S5PV210 crypto accelerator"
+        depends on ARCH_S5PV210
+diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile
+index 38ce13d..b4946dd 100644
+--- a/drivers/crypto/Makefile
++++ b/drivers/crypto/Makefile
+@@ -13,6 +13,7 @@ obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += omap-sham.o
+ obj-$(CONFIG_CRYPTO_DEV_OMAP_AES) += omap-aes.o
+ obj-$(CONFIG_CRYPTO_DEV_PICOXCELL) += picoxcell_crypto.o
+ obj-$(CONFIG_CRYPTO_DEV_SAHARA) += sahara.o
++obj-$(CONFIG_CRYPTO_DEV_DCP) += dcp.o
+ obj-$(CONFIG_CRYPTO_DEV_S5P) += s5p-sss.o
+ obj-$(CONFIG_CRYPTO_DEV_TEGRA_AES) += tegra-aes.o
+ obj-$(CONFIG_CRYPTO_DEV_UX500) += ux500/
+diff --git a/drivers/crypto/dcp.c b/drivers/crypto/dcp.c
+new file mode 100644
+index 0000000..eea194c
+--- /dev/null
++++ b/drivers/crypto/dcp.c
+@@ -0,0 +1,925 @@
++/*
++ * Cryptographic API.
++ *
++ * Support for DCP cryptographic accelerator.
++ *
++ * Copyright (c) 2013
++ * Author: Tobias Rauter <tobias.rau...@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation.
++ *
++ * Based on tegra-aes.c, dcp.c (from freescale SDK) and sahara.c
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/io.h>
++#include <linux/mutex.h>
++#include <linux/interrupt.h>
++#include <linux/completion.h>
++#include <linux/workqueue.h>
++#include <linux/delay.h>
++#include <linux/crypto.h>
++#include <linux/miscdevice.h>
++
++#include <crypto/scatterwalk.h>
++#include <crypto/aes.h>
++
++
++/* IOCTL for DCP OTP Key AES - taken from Freescale's SDK*/
++#define DBS_IOCTL_BASE   'd'
++#define DBS_ENC        _IOW(DBS_IOCTL_BASE, 0x00, uint8_t[16])
++#define DBS_DEC _IOW(DBS_IOCTL_BASE, 0x01, uint8_t[16])
++
++/* DCP channel used for AES */
++#define USED_CHANNEL 1
++/* Ring Buffers' maximum size */
++#define DCP_MAX_PKG 20
++
++/* Control Register */
++#define DCP_REG_CTRL 0x000
++#define DCP_CTRL_SFRST (1<<31)
++#define DCP_CTRL_CLKGATE (1<<30)
++#define DCP_CTRL_CRYPTO_PRESENT (1<<29)
++#define DCP_CTRL_SHA_PRESENT (1<<28)
++#define DCP_CTRL_GATHER_RES_WRITE (1<<23)
++#define DCP_CTRL_ENABLE_CONTEXT_CACHE (1<<22)
++#define DCP_CTRL_ENABLE_CONTEXT_SWITCH (1<<21)
++#define DCP_CTRL_CH_IRQ_E_0 0x01
++#define DCP_CTRL_CH_IRQ_E_1 0x02
++#define DCP_CTRL_CH_IRQ_E_2 0x04
++#define DCP_CTRL_CH_IRQ_E_3 0x08
++
++/* Status register */
++#define DCP_REG_STAT 0x010
++#define DCP_STAT_OTP_KEY_READY (1<<28)
++#define DCP_STAT_CUR_CHANNEL(stat) ((stat>>24)&0x0F)
++#define DCP_STAT_READY_CHANNEL(stat) ((stat>>16)&0x0F)
++#define DCP_STAT_IRQ(stat) (stat&0x0F)
++#define DCP_STAT_CHAN_0 (0x01)
++#define DCP_STAT_CHAN_1 (0x02)
++#define DCP_STAT_CHAN_2 (0x04)
++#define DCP_STAT_CHAN_3 (0x08)
++
++/* Channel Control Register */
++#define DCP_REG_CHAN_CTRL 0x020
++#define DCP_CHAN_CTRL_CH0_IRQ_MERGED (1<<16)
++#define DCP_CHAN_CTRL_HIGH_PRIO_0 (0x0100)
++#define DCP_CHAN_CTRL_HIGH_PRIO_1 (0x0200)
++#define DCP_CHAN_CTRL_HIGH_PRIO_2 (0x0400)
++#define DCP_CHAN_CTRL_HIGH_PRIO_3 (0x0800)
++#define DCP_CHAN_CTRL_ENABLE_0 (0x01)
++#define DCP_CHAN_CTRL_ENABLE_1 (0x02)
++#define DCP_CHAN_CTRL_ENABLE_2 (0x04)
++#define DCP_CHAN_CTRL_ENABLE_3 (0x08)
++
++/*
++ * Channel Registers:
++ * The DCP has 4 channels. Each of this channels
++ * has 4 registers (command pointer, semaphore, status and options).
++ * The address of register REG of channel CHAN is obtained by
++ * dcp_chan_reg(REG, CHAN)
++ */
++#define DCP_REG_CHAN_PTR       0x00000100
++#define DCP_REG_CHAN_SEMA      0x00000110
++#define DCP_REG_CHAN_STAT      0x00000120
++#define DCP_REG_CHAN_OPT       0x00000130
++
++#define DCP_CHAN_STAT_NEXT_CHAIN_IS_0  0x010000
++#define DCP_CHAN_STAT_NO_CHAIN         0x020000
++#define DCP_CHAN_STAT_CONTEXT_ERROR    0x030000
++#define DCP_CHAN_STAT_PAYLOAD_ERROR    0x040000
++#define DCP_CHAN_STAT_INVALID_MODE     0x050000
++#define DCP_CHAN_STAT_PAGEFAULT                0x40
++#define DCP_CHAN_STAT_DST              0x20
++#define DCP_CHAN_STAT_SRC              0x10
++#define DCP_CHAN_STAT_PACKET           0x08
++#define DCP_CHAN_STAT_SETUP            0x04
++#define DCP_CHAN_STAT_MISMATCH         0x02
++
++/* hw packet control*/
++
++#define DCP_PKT_PAYLOAD_KEY    (1<<11)
++#define DCP_PKT_OTP_KEY                (1<<10)
++#define DCP_PKT_CIPHER_INIT    (1<<9)
++#define DCP_PKG_CIPHER_ENCRYPT (1<<8)
++#define DCP_PKT_CIPHER_ENABLE  (1<<5)
++#define DCP_PKT_DECR_SEM       (1<<1)
++#define DCP_PKT_CHAIN          (1<<2)
++#define DCP_PKT_IRQ            1
++
++#define DCP_PKT_MODE_CBC       (1<<4)
++#define DCP_PKT_KEYSELECT_OTP  (0xFF<<8)
++
++/* cipher flags */
++#define DCP_ENC                0x0001
++#define DCP_DEC                0x0002
++#define DCP_ECB                0x0004
++#define DCP_CBC                0x0008
++#define DCP_CBC_INIT   0x0010
++#define DCP_NEW_KEY    0x0040
++#define DCP_OTP_KEY    0x0080
++#define DCP_AES                0x1000
++
++/* DCP Flags */
++#define DCP_FLAG_BUSY  0x01
++#define DCP_FLAG_PRODUCING     0x02
++
++/* clock defines */
++#define CLOCK_ON       1
++#define CLOCK_OFF      0
++
++struct dcp_dev_req_ctx {
++       int mode;
++};
++
++struct dcp_op {
++       unsigned int            flags;
++       u8                      key[AES_KEYSIZE_128];
++       int                     keylen;
++
++       struct ablkcipher_request       *req;
++       struct crypto_ablkcipher        *fallback;
++
++       uint32_t stat;
++       uint32_t pkt1;
++       uint32_t pkt2;
++       struct ablkcipher_walk walk;
++};
++
++struct dcp_dev {
++       struct device *dev;
++       void __iomem *dcp_regs_base;
++
++       int dcp_vmi_irq;
++       int dcp_irq;
++
++       spinlock_t queue_lock;
++       struct crypto_queue queue;
++
++       uint32_t pkt_produced;
++       uint32_t pkt_consumed;
++
++       struct dcp_hw_packet *hw_pkg[DCP_MAX_PKG];
++       dma_addr_t hw_phys_pkg;
++
++       /* [KEY][IV] Both with 16 Bytes */
++       u8 *payload_base;
++       dma_addr_t payload_base_dma;
++
++
++       struct tasklet_struct   done_task;
++       struct tasklet_struct   queue_task;
++       struct timer_list       watchdog;
++
++       unsigned long           flags;
++
++       struct dcp_op *ctx;
++
++       struct miscdevice dcp_bootstream_misc;
++};
++
++struct dcp_hw_packet {
++       uint32_t next;
++       uint32_t pkt1;
++       uint32_t pkt2;
++       uint32_t src;
++       uint32_t dst;
++       uint32_t size;
++       uint32_t payload;
++       uint32_t stat;
++};
++
++struct dcp_dev *global_dev;
++
++static inline u32 dcp_chan_reg(u32 reg, int chan)
++{
++       return reg + (chan) * 0x40;
++}
++
++static inline void dcp_write(struct dcp_dev *dev, u32 data, u32 reg)
++{
++       writel(data, dev->dcp_regs_base + reg);
++}
++
++static inline void dcp_set(struct dcp_dev *dev, u32 data, u32 reg)
++{
++       writel(data, dev->dcp_regs_base + (reg | 0x04));
++}
++
++static inline void dcp_clear(struct dcp_dev *dev, u32 data, u32 reg)
++{
++       writel(data, dev->dcp_regs_base + (reg | 0x08));
++}
++
++static inline void dcp_toggle(struct dcp_dev *dev, u32 data, u32 reg)
++{
++       writel(data, dev->dcp_regs_base + (reg | 0x0C));
++}
++
++static inline unsigned int dcp_read(struct dcp_dev *dev, u32 reg)
++{
++       return readl(dev->dcp_regs_base + reg);
++}
++
++void dcp_dma_unmap(struct dcp_dev *dev, struct dcp_hw_packet *pkt)
++{
++       dma_unmap_page(dev->dev, pkt->src, pkt->size, DMA_TO_DEVICE);
++       dma_unmap_page(dev->dev, pkt->dst, pkt->size, DMA_FROM_DEVICE);
++       dev_dbg(dev->dev, "unmap packet %x", (unsigned int) pkt);
++}
++
++int dcp_dma_map(struct dcp_dev *dev,
++       struct ablkcipher_walk *walk, struct dcp_hw_packet *pkt)
++{
++       dev_dbg(dev->dev, "map packet %x", (unsigned int) pkt);
++       /* align to length = 16 */
++       pkt->size = walk->nbytes - (walk->nbytes % 16);
++
++       pkt->src = dma_map_page(dev->dev, walk->src.page, walk->src.offset,
++               pkt->size, DMA_TO_DEVICE);
++
++       if (pkt->src == 0) {
++               dev_err(dev->dev, "Unable to map src");
++               return -ENOMEM;
++       }
++
++       pkt->dst = dma_map_page(dev->dev, walk->dst.page, walk->dst.offset,
++               pkt->size, DMA_FROM_DEVICE);
++
++       if (pkt->dst == 0) {
++               dev_err(dev->dev, "Unable to map dst");
++               dma_unmap_page(dev->dev, pkt->src, pkt->size, DMA_TO_DEVICE);
++               return -ENOMEM;
++       }
++
++       return 0;
++}
++
++static void dcp_op_one(struct dcp_dev *dev, struct dcp_hw_packet *pkt,
++                       uint8_t last)
++{
++       struct dcp_op *ctx = dev->ctx;
++       pkt->pkt1 = ctx->pkt1;
++       pkt->pkt2 = ctx->pkt2;
++
++       pkt->payload = (u32) dev->payload_base_dma;
++       pkt->stat = 0;
++
++       if (ctx->flags & DCP_CBC_INIT) {
++               pkt->pkt1 |= DCP_PKT_CIPHER_INIT;
++               ctx->flags &= ~DCP_CBC_INIT;
++       }
++
++       mod_timer(&dev->watchdog, jiffies + msecs_to_jiffies(500));
++       pkt->pkt1 |= DCP_PKT_IRQ;
++       if (!last)
++               pkt->pkt1 |= DCP_PKT_CHAIN;
++
++       dev->pkt_produced++;
++
++       dcp_write(dev, 1,
++               dcp_chan_reg(DCP_REG_CHAN_SEMA, USED_CHANNEL));
++}
++
++static void dcp_op_proceed(struct dcp_dev *dev)
++{
++       struct dcp_op *ctx = dev->ctx;
++       struct dcp_hw_packet *pkt;
++
++       while (ctx->walk.nbytes) {
++               int err = 0;
++
++               pkt = dev->hw_pkg[dev->pkt_produced % DCP_MAX_PKG];
++               err = dcp_dma_map(dev, &ctx->walk, pkt);
++               if (err) {
++                       dev->ctx->stat |= err;
++                       /* start timer to wait for already set up calls */
++                       mod_timer(&dev->watchdog,
++                               jiffies + msecs_to_jiffies(500));
++                       break;
++               }
++
++
++               err = ctx->walk.nbytes - pkt->size;
++               ablkcipher_walk_done(dev->ctx->req, &dev->ctx->walk, err);
++
++               dcp_op_one(dev, pkt, ctx->walk.nbytes == 0);
++               /* we have to wait if no space is left in buffer */
++               if (dev->pkt_produced - dev->pkt_consumed == DCP_MAX_PKG)
++                       break;
++       }
++       clear_bit(DCP_FLAG_PRODUCING, &dev->flags);
++}
++
++static void dcp_op_start(struct dcp_dev *dev, uint8_t use_walk)
++{
++       struct dcp_op *ctx = dev->ctx;
++
++       if (ctx->flags & DCP_NEW_KEY) {
++               memcpy(dev->payload_base, ctx->key, ctx->keylen);
++               ctx->flags &= ~DCP_NEW_KEY;
++       }
++
++       ctx->pkt1 = 0;
++       ctx->pkt1 |= DCP_PKT_CIPHER_ENABLE;
++       ctx->pkt1 |= DCP_PKT_DECR_SEM;
++
++       if (ctx->flags & DCP_OTP_KEY)
++               ctx->pkt1 |= DCP_PKT_OTP_KEY;
++       else
++               ctx->pkt1 |= DCP_PKT_PAYLOAD_KEY;
++
++       if (ctx->flags & DCP_ENC)
++               ctx->pkt1 |= DCP_PKG_CIPHER_ENCRYPT;
++
++       ctx->pkt2 = 0;
++       if (ctx->flags & DCP_CBC)
++               ctx->pkt2 |= DCP_PKT_MODE_CBC;
++
++       dev->pkt_produced = 0;
++       dev->pkt_consumed = 0;
++
++       ctx->stat = 0;
++       dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
++       dcp_write(dev, (u32) dev->hw_phys_pkg,
++               dcp_chan_reg(DCP_REG_CHAN_PTR, USED_CHANNEL));
++
++       set_bit(DCP_FLAG_PRODUCING, &dev->flags);
++
++       if (use_walk) {
++               ablkcipher_walk_init(&ctx->walk, ctx->req->dst,
++                               ctx->req->src, ctx->req->nbytes);
++               ablkcipher_walk_phys(ctx->req, &ctx->walk);
++               dcp_op_proceed(dev);
++       } else {
++               dcp_op_one(dev, dev->hw_pkg[0], 1);
++               clear_bit(DCP_FLAG_PRODUCING, &dev->flags);
++       }
++}
++
++static void dcp_done_task(unsigned long data)
++{
++       struct dcp_dev *dev = (struct dcp_dev *)data;
++       struct dcp_hw_packet *last_packet;
++       int fin;
++       fin = 0;
++
++       for (last_packet = dev->hw_pkg[(dev->pkt_consumed) % DCP_MAX_PKG];
++               last_packet->stat == 1;
++               last_packet =
++                       dev->hw_pkg[++(dev->pkt_consumed) % DCP_MAX_PKG]) {
++
++               dcp_dma_unmap(dev, last_packet);
++               last_packet->stat = 0;
++               fin++;
++       }
++       /* the last call of this function already consumed this IRQ's packet */
++       if (fin == 0)
++               return;
++
++       dev_dbg(dev->dev,
++               "Packet(s) done with status %x; finished: %d, produced:%d, complete consumed: %d",
++               dev->ctx->stat, fin, dev->pkt_produced, dev->pkt_consumed);
++
++       last_packet = dev->hw_pkg[(dev->pkt_consumed - 1) % DCP_MAX_PKG];
++       if (!dev->ctx->stat && last_packet->pkt1 & DCP_PKT_CHAIN) {
++               if (!test_and_set_bit(DCP_FLAG_PRODUCING, &dev->flags))
++                       dcp_op_proceed(dev);
++               return;
++       }
++
++       while (unlikely(dev->pkt_consumed < dev->pkt_produced)) {
++               dcp_dma_unmap(dev,
++                       dev->hw_pkg[dev->pkt_consumed++ % DCP_MAX_PKG]);
++       }
++
++       if (dev->ctx->flags & DCP_OTP_KEY) {
++               /* we used the miscdevice, no walk to finish */
++               clear_bit(DCP_FLAG_BUSY, &dev->flags);
++               return;
++       }
++
++       ablkcipher_walk_complete(&dev->ctx->walk);
++       dev->ctx->req->base.complete(&dev->ctx->req->base,
++                       dev->ctx->stat);
++       dev->ctx->req = 0;
++       /* in case there are other requests in the queue */
++       tasklet_schedule(&dev->queue_task);
++}
++
++void dcp_watchdog(unsigned long data)
++{
++       struct dcp_dev *dev = (struct dcp_dev *)data;
++       dev->ctx->stat |= dcp_read(dev,
++                       dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
++
++       dev_err(dev->dev, "Timeout, Channel status: %x", dev->ctx->stat);
++
++       if (!dev->ctx->stat)
++               dev->ctx->stat = -ETIMEDOUT;
++
++       dcp_done_task(data);
++}
++
++
++static irqreturn_t dcp_common_irq(int irq, void *context)
++{
++       u32 msk;
++       struct dcp_dev *dev = (struct dcp_dev *) context;
++
++       del_timer(&dev->watchdog);
++
++       msk = DCP_STAT_IRQ(dcp_read(dev, DCP_REG_STAT));
++       dcp_clear(dev, msk, DCP_REG_STAT);
++       if (msk == 0)
++               return IRQ_NONE;
++
++       dev->ctx->stat |= dcp_read(dev,
++                       dcp_chan_reg(DCP_REG_CHAN_STAT, USED_CHANNEL));
++
++       if (msk & DCP_STAT_CHAN_1)
++               tasklet_schedule(&dev->done_task);
++
++       return IRQ_HANDLED;
++}
++
++static irqreturn_t dcp_vmi_irq(int irq, void *context)
++{
++       return dcp_common_irq(irq, context);
++}
++
++static irqreturn_t dcp_irq(int irq, void *context)
++{
++       return dcp_common_irq(irq, context);
++}
++
++static void dcp_crypt(struct dcp_dev *dev, struct dcp_op *ctx)
++{
++       dev->ctx = ctx;
++
++       if ((ctx->flags & DCP_CBC) && ctx->req->info) {
++               ctx->flags |= DCP_CBC_INIT;
++               memcpy(dev->payload_base + AES_KEYSIZE_128,
++                       ctx->req->info, AES_KEYSIZE_128);
++       }
++
++       dcp_op_start(dev, 1);
++}
++
++static void dcp_queue_task(unsigned long data)
++{
++       struct dcp_dev *dev = (struct dcp_dev *) data;
++       struct crypto_async_request *async_req, *backlog;
++       struct crypto_ablkcipher *tfm;
++       struct dcp_op *ctx;
++       struct dcp_dev_req_ctx *rctx;
++       struct ablkcipher_request *req;
++       unsigned long flags;
++
++       spin_lock_irqsave(&dev->queue_lock, flags);
++
++       backlog = crypto_get_backlog(&dev->queue);
++       async_req = crypto_dequeue_request(&dev->queue);
++
++       spin_unlock_irqrestore(&dev->queue_lock, flags);
++
++       if (!async_req)
++               goto ret_nothing_done;
++
++       if (backlog)
++               backlog->complete(backlog, -EINPROGRESS);
++
++       req = ablkcipher_request_cast(async_req);
++       tfm = crypto_ablkcipher_reqtfm(req);
++       rctx = ablkcipher_request_ctx(req);
++       ctx = crypto_ablkcipher_ctx(tfm);
++
++       if (!req->src || !req->dst)
++               goto ret_nothing_done;
++
++       ctx->flags |= rctx->mode;
++       ctx->req = req;
++
++       dcp_crypt(dev, ctx);
++
++       return;
++
++ret_nothing_done:
++       clear_bit(DCP_FLAG_BUSY, &dev->flags);
++}
++
++
++static int dcp_cra_init(struct crypto_tfm *tfm)
++{
++       const char *name = tfm->__crt_alg->cra_name;
++       struct dcp_op *ctx = crypto_tfm_ctx(tfm);
++
++       tfm->crt_ablkcipher.reqsize = sizeof(struct dcp_dev_req_ctx);
++
++       ctx->fallback = crypto_alloc_ablkcipher(name, 0,
++                               CRYPTO_ALG_ASYNC | CRYPTO_ALG_NEED_FALLBACK);
++
++       if (IS_ERR(ctx->fallback)) {
++               dev_err(global_dev->dev, "Error allocating fallback algo %s\n",
++                       name);
++               return PTR_ERR(ctx->fallback);
++       }
++
++       return 0;
++}
++
++static void dcp_cra_exit(struct crypto_tfm *tfm)
++{
++       struct dcp_op *ctx = crypto_tfm_ctx(tfm);
++
++       if (ctx->fallback)
++               crypto_free_ablkcipher(ctx->fallback);
++
++       ctx->fallback = NULL;
++}
++
++/* async interface */
++static int dcp_aes_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
++               unsigned int len)
++{
++       struct dcp_op *ctx = crypto_ablkcipher_ctx(tfm);
++       unsigned int ret = 0;
++       ctx->keylen = len;
++       ctx->flags = 0;
++       if (len == AES_KEYSIZE_128) {
++               if (memcmp(ctx->key, key, AES_KEYSIZE_128)) {
++                       memcpy(ctx->key, key, len);
++                       ctx->flags |= DCP_NEW_KEY;
++               }
++               return 0;
++       }
++
++       ctx->fallback->base.crt_flags &= ~CRYPTO_TFM_REQ_MASK;
++       ctx->fallback->base.crt_flags |=
++               (tfm->base.crt_flags & CRYPTO_TFM_REQ_MASK);
++
++       ret = crypto_ablkcipher_setkey(ctx->fallback, key, len);
++       if (ret) {
++               struct crypto_tfm *tfm_aux = crypto_ablkcipher_tfm(tfm);
++
++               tfm_aux->crt_flags &= ~CRYPTO_TFM_RES_MASK;
++               tfm_aux->crt_flags |=
++                       (ctx->fallback->base.crt_flags & CRYPTO_TFM_RES_MASK);
++       }
++       return ret;
++}
++
++static int dcp_aes_cbc_crypt(struct ablkcipher_request *req, int mode)
++{
++       struct dcp_dev_req_ctx *rctx = ablkcipher_request_ctx(req);
++       struct dcp_dev *dev = global_dev;
++       unsigned long flags;
++       int err = 0;
++
++       if (!IS_ALIGNED(req->nbytes, AES_BLOCK_SIZE))
++               return -EINVAL;
++
++       rctx->mode = mode;
++
++       spin_lock_irqsave(&dev->queue_lock, flags);
++       err = ablkcipher_enqueue_request(&dev->queue, req);
++       spin_unlock_irqrestore(&dev->queue_lock, flags);
++
++       flags = test_and_set_bit(DCP_FLAG_BUSY, &dev->flags);
++
++       if (!(flags & DCP_FLAG_BUSY))
++               tasklet_schedule(&dev->queue_task);
++
++       return err;
++}
++
++static int dcp_aes_cbc_encrypt(struct ablkcipher_request *req)
++{
++       struct crypto_tfm *tfm =
++               crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
++       struct dcp_op *ctx = crypto_ablkcipher_ctx(
++               crypto_ablkcipher_reqtfm(req));
++
++       if (unlikely(ctx->keylen != AES_KEYSIZE_128)) {
++               int err = 0;
++               ablkcipher_request_set_tfm(req, ctx->fallback);
++               err = crypto_ablkcipher_encrypt(req);
++               ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
++               return err;
++       }
++
++       return dcp_aes_cbc_crypt(req, DCP_AES | DCP_ENC | DCP_CBC);
++}
++
++static int dcp_aes_cbc_decrypt(struct ablkcipher_request *req)
++{
++       struct crypto_tfm *tfm =
++               crypto_ablkcipher_tfm(crypto_ablkcipher_reqtfm(req));
++       struct dcp_op *ctx = crypto_ablkcipher_ctx(
++               crypto_ablkcipher_reqtfm(req));
++
++       if (unlikely(ctx->keylen != AES_KEYSIZE_128)) {
++               int err = 0;
++               ablkcipher_request_set_tfm(req, ctx->fallback);
++               err = crypto_ablkcipher_decrypt(req);
++               ablkcipher_request_set_tfm(req, __crypto_ablkcipher_cast(tfm));
++               return err;
++       }
++       return dcp_aes_cbc_crypt(req, DCP_AES | DCP_DEC | DCP_CBC);
++}
++
++static struct crypto_alg algs[] = {
++       {
++               .cra_name = "cbc(aes)",
++               .cra_driver_name = "dcp-cbc-aes",
++               .cra_alignmask = 3,
++               .cra_flags = CRYPTO_ALG_TYPE_ABLKCIPHER | CRYPTO_ALG_ASYNC |
++                         CRYPTO_ALG_NEED_FALLBACK,
++               .cra_blocksize = AES_KEYSIZE_128,
++               .cra_type = &crypto_ablkcipher_type,
++               .cra_priority = 300,
++               .cra_u.ablkcipher = {
++                       .min_keysize =  AES_KEYSIZE_128,
++                       .max_keysize = AES_KEYSIZE_128,
++                       .setkey = dcp_aes_setkey,
++                       .encrypt = dcp_aes_cbc_encrypt,
++                       .decrypt = dcp_aes_cbc_decrypt,
++                       .ivsize = AES_KEYSIZE_128,
++               }
++
++       },
++};
++
++/* DCP bootstream verification interface: uses OTP key for crypto */
++static int dcp_bootstream_open(struct inode *inode, struct file *file)
++{
++       file->private_data = container_of((file->private_data),
++                       struct dcp_dev, dcp_bootstream_misc);
++       return 0;
++}
++
++static long dcp_bootstream_ioctl(struct file *file,
++                                        unsigned int cmd, unsigned long arg)
++{
++       struct dcp_dev *dev = (struct dcp_dev *) file->private_data;
++       void __user *argp = (void __user *)arg;
++       int ret;
++
++       if (dev == NULL)
++               return -EBADF;
++
++       if (cmd != DBS_ENC && cmd != DBS_DEC)
++               return -EINVAL;
++
++       if (copy_from_user(dev->payload_base, argp, 16))
++               return -EFAULT;
++
++       if (test_and_set_bit(DCP_FLAG_BUSY, &dev->flags))
++               return -EAGAIN;
++
++       dev->ctx = kzalloc(sizeof(struct dcp_op), GFP_KERNEL);
++       if (!dev->ctx) {
++               dev_err(dev->dev,
++                       "cannot allocate context for OTP crypto");
++               clear_bit(DCP_FLAG_BUSY, &dev->flags);
++               return -ENOMEM;
++       }
++
++       dev->ctx->flags = DCP_AES | DCP_ECB | DCP_OTP_KEY | DCP_CBC_INIT;
++       dev->ctx->flags |= (cmd == DBS_ENC) ? DCP_ENC : DCP_DEC;
++       dev->hw_pkg[0]->src = dev->payload_base_dma;
++       dev->hw_pkg[0]->dst = dev->payload_base_dma;
++       dev->hw_pkg[0]->size = 16;
++
++       dcp_op_start(dev, 0);
++
++       while (test_bit(DCP_FLAG_BUSY, &dev->flags))
++               cpu_relax();
++
++       ret = dev->ctx->stat;
++       if (!ret && copy_to_user(argp, dev->payload_base, 16))
++               ret =  -EFAULT;
++
++       kfree(dev->ctx);
++
++       return ret;
++}
++
++static const struct file_operations dcp_bootstream_fops = {
++       .owner =                THIS_MODULE,
++       .unlocked_ioctl =       dcp_bootstream_ioctl,
++       .open =                 dcp_bootstream_open,
++};
++
++static int dcp_probe(struct platform_device *pdev)
++{
++       struct dcp_dev *dev = NULL;
++       struct resource *r;
++       int i, ret, j;
++
++       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++       if (dev == NULL) {
++               dev_err(&pdev->dev, "Failed to allocate structure\n");
++               ret = -ENOMEM;
++               goto err;
++       }
++       global_dev = dev;
++       dev->dev = &pdev->dev;
++
++       platform_set_drvdata(pdev, dev);
++
++       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++       if (!r) {
++               dev_err(&pdev->dev, "failed to get IORESOURCE_MEM\n");
++               ret = -ENXIO;
++               goto err_dev;
++       }
++       dev->dcp_regs_base = ioremap(r->start, resource_size(r));
++
++
++       dcp_set(dev, DCP_CTRL_SFRST, DCP_REG_CTRL);
++       udelay(10);
++       dcp_clear(dev, DCP_CTRL_SFRST | DCP_CTRL_CLKGATE, DCP_REG_CTRL);
++
++       dcp_write(dev, DCP_CTRL_GATHER_RES_WRITE |
++               DCP_CTRL_ENABLE_CONTEXT_CACHE | DCP_CTRL_CH_IRQ_E_1,
++               DCP_REG_CTRL);
++
++       dcp_write(dev, DCP_CHAN_CTRL_ENABLE_1, DCP_REG_CHAN_CTRL);
++
++       for (i = 0; i < 4; i++)
++               dcp_clear(dev, -1, dcp_chan_reg(DCP_REG_CHAN_STAT, i));
++
++       dcp_clear(dev, -1, DCP_REG_STAT);
++
++
++       r = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
++       if (!r) {
++               dev_err(&pdev->dev, "can't get IRQ resource (0)\n");
++               ret = -EIO;
++               goto err_unmap_mem;
++       }
++       dev->dcp_vmi_irq = r->start;
++       ret = request_irq(dev->dcp_vmi_irq, dcp_vmi_irq, 0, "dcp", dev);
++       if (ret != 0) {
++               dev_err(&pdev->dev, "can't request_irq (0)\n");
++               ret = -EIO;
++               goto err_unmap_mem;
++       }
++
++       r = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
++       if (!r) {
++               dev_err(&pdev->dev, "can't get IRQ resource (1)\n");
++               ret = -EIO;
++               goto err_free_irq0;
++       }
++       dev->dcp_irq = r->start;
++       ret = request_irq(dev->dcp_irq, dcp_irq, 0, "dcp", dev);
++       if (ret != 0) {
++               dev_err(&pdev->dev, "can't request_irq (1)\n");
++               ret = -EIO;
++               goto err_free_irq0;
++       }
++
++       dev->hw_pkg[0] = dma_alloc_coherent(&pdev->dev,
++                       DCP_MAX_PKG * sizeof(struct dcp_hw_packet),
++                       &dev->hw_phys_pkg,
++                       GFP_KERNEL);
++       if (!dev->hw_pkg[0]) {
++               dev_err(&pdev->dev, "Could not allocate hw descriptors\n");
++               ret = -ENOMEM;
++               goto err_free_irq1;
++       }
++
++       for (i = 1; i < DCP_MAX_PKG; i++) {
++               dev->hw_pkg[i - 1]->next = dev->hw_phys_pkg
++                               + i * sizeof(struct dcp_hw_packet);
++               dev->hw_pkg[i] = dev->hw_pkg[i - 1] + 1;
++       }
++       dev->hw_pkg[i - 1]->next = dev->hw_phys_pkg;
++
++
++       dev->payload_base = dma_alloc_coherent(&pdev->dev, 2 * AES_KEYSIZE_128,
++                       &dev->payload_base_dma, GFP_KERNEL);
++       if (!dev->payload_base) {
++               dev_err(&pdev->dev, "Could not allocate memory for key\n");
++               ret = -ENOMEM;
++               goto err_free_hw_packet;
++       }
++       tasklet_init(&dev->queue_task, dcp_queue_task,
++               (unsigned long) dev);
++       tasklet_init(&dev->done_task, dcp_done_task,
++               (unsigned long) dev);
++       spin_lock_init(&dev->queue_lock);
++
++       crypto_init_queue(&dev->queue, 10);
++
++       init_timer(&dev->watchdog);
++       dev->watchdog.function = &dcp_watchdog;
++       dev->watchdog.data = (unsigned long)dev;
++
++       dev->dcp_bootstream_misc.minor = MISC_DYNAMIC_MINOR,
++       dev->dcp_bootstream_misc.name = "dcpboot",
++       dev->dcp_bootstream_misc.fops = &dcp_bootstream_fops,
++       ret = misc_register(&dev->dcp_bootstream_misc);
++       if (ret != 0) {
++               dev_err(dev->dev, "Unable to register misc device\n");
++               goto err_free_key_iv;
++       }
++
++       for (i = 0; i < ARRAY_SIZE(algs); i++) {
++               algs[i].cra_priority = 300;
++               algs[i].cra_ctxsize = sizeof(struct dcp_op);
++               algs[i].cra_module = THIS_MODULE;
++               algs[i].cra_init = dcp_cra_init;
++               algs[i].cra_exit = dcp_cra_exit;
++               if (crypto_register_alg(&algs[i])) {
++                       dev_err(&pdev->dev, "register algorithm failed\n");
++                       ret = -ENOMEM;
++                       goto err_unregister;
++               }
++       }
++       dev_notice(&pdev->dev, "DCP crypto enabled.!\n");
++
++       return 0;
++
++err_unregister:
++       for (j = 0; j < i; j++)
++               crypto_unregister_alg(&algs[j]);
++err_free_key_iv:
++       dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
++                       dev->payload_base_dma);
++err_free_hw_packet:
++       dma_free_coherent(&pdev->dev, DCP_MAX_PKG *
++               sizeof(struct dcp_hw_packet), dev->hw_pkg[0],
++               dev->hw_phys_pkg);
++err_free_irq1:
++       free_irq(dev->dcp_irq, dev);
++err_free_irq0:
++       free_irq(dev->dcp_vmi_irq, dev);
++err_unmap_mem:
++       iounmap((void *) dev->dcp_regs_base);
++err_dev:
++       kfree(dev);
++err:
++       return ret;
++}
++
++static int dcp_remove(struct platform_device *pdev)
++{
++       struct dcp_dev *dev;
++       int j;
++       dev = platform_get_drvdata(pdev);
++       platform_set_drvdata(pdev, NULL);
++
++       dma_free_coherent(&pdev->dev,
++                       DCP_MAX_PKG * sizeof(struct dcp_hw_packet),
++                       dev->hw_pkg[0], dev->hw_phys_pkg);
++
++       dma_free_coherent(&pdev->dev, 2 * AES_KEYSIZE_128, dev->payload_base,
++                       dev->payload_base_dma);
++
++       free_irq(dev->dcp_irq, dev);
++       free_irq(dev->dcp_vmi_irq, dev);
++
++       tasklet_kill(&dev->done_task);
++       tasklet_kill(&dev->queue_task);
++
++       iounmap((void *) dev->dcp_regs_base);
++
++       for (j = 0; j < ARRAY_SIZE(algs); j++)
++               crypto_unregister_alg(&algs[j]);
++
++       misc_deregister(&dev->dcp_bootstream_misc);
++
++       kfree(dev);
++       return 0;
++}
++
++static struct of_device_id fs_dcp_of_match[] = {
++       {       .compatible = "fsl-dcp"},
++       {},
++};
++
++static struct platform_driver fs_dcp_driver = {
++       .probe = dcp_probe,
++       .remove = dcp_remove,
++       .driver = {
++               .name = "fsl-dcp",
++               .owner = THIS_MODULE,
++               .of_match_table = fs_dcp_of_match
++       }
++};
++
++module_platform_driver(fs_dcp_driver);
++
++
++MODULE_AUTHOR("Tobias Rauter <tobias.rau...@gmail.com>");
++MODULE_DESCRIPTION("Freescale DCP Crypto Driver");
++MODULE_LICENSE("GPL");
+
+
diff --git a/target/linux/imx23/patches/106-add-dcp-dts.patch b/target/linux/imx23/patches/106-add-dcp-dts.patch
new file mode 100644 (file)
index 0000000..6d50df6
--- /dev/null
@@ -0,0 +1,27 @@
+diff -ruN linux-3.10/arch/arm/boot/dts/imx23.dtsi linux-3.10.dcp/arch/arm/boot/dts/imx23.dtsi
+--- linux-3.10/arch/arm/boot/dts/imx23.dtsi    2013-07-01 00:13:29.000000000 +0200
++++ linux-3.10.dcp/arch/arm/boot/dts/imx23.dtsi        2013-08-03 11:15:59.000000000 +0200
+@@ -333,6 +333,7 @@
+                       dcp@80028000 {
+                               reg = <0x80028000 0x2000>;
++                              interrupts = <52 53 54>;
+                               status = "disabled";
+                       };
+diff -ruN linux-3.10/arch/arm/boot/dts/imx23-olinuxino.dts linux-3.10.dcp/arch/arm/boot/dts/imx23-olinuxino.dts
+--- linux-3.10/arch/arm/boot/dts/imx23-olinuxino.dts   2013-07-01 00:13:29.000000000 +0200
++++ linux-3.10.dcp/arch/arm/boot/dts/imx23-olinuxino.dts       2013-08-02 12:30:07.000000000 +0200
+@@ -66,6 +66,12 @@
+                               pinctrl-0 = <&spi2_pins_a>;
+                               status = "okay";
+                       };
++                      
++                      dcp@80028000 {
++                              status = "okay";
++                              compatible = "fsl-dcp";
++                      };
++                                                                                                              
+               };
+               apbx@80040000 {