diff options
author | Zoltan Herpai <wigyori@uid0.hu> | 2013-08-03 10:35:18 +0000 |
---|---|---|
committer | Zoltan Herpai <wigyori@uid0.hu> | 2013-08-03 10:35:18 +0000 |
commit | 370ad945761fb4d651400f7374ca654c9fdb7c83 (patch) | |
tree | 085c35b579a91ef96be103f0e6c4c2b24fc5013d /target/linux/imx23/patches | |
parent | d2ed54596d5033b415edfa2da7217c73ee180aa3 (diff) | |
download | master-31e0f0ae-370ad945761fb4d651400f7374ca654c9fdb7c83.tar.gz master-31e0f0ae-370ad945761fb4d651400f7374ca654c9fdb7c83.tar.bz2 master-31e0f0ae-370ad945761fb4d651400f7374ca654c9fdb7c83.zip |
imx23: add hw crypto (dcp) support
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 37662
Diffstat (limited to 'target/linux/imx23/patches')
-rw-r--r-- | target/linux/imx23/patches/105-imx23-dcp.patch | 966 | ||||
-rw-r--r-- | target/linux/imx23/patches/106-add-dcp-dts.patch | 27 |
2 files changed, 993 insertions, 0 deletions
diff --git a/target/linux/imx23/patches/105-imx23-dcp.patch b/target/linux/imx23/patches/105-imx23-dcp.patch new file mode 100644 index 0000000000..cbc25a06b3 --- /dev/null +++ b/target/linux/imx23/patches/105-imx23-dcp.patch @@ -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 index 0000000000..6d50df6005 --- /dev/null +++ b/target/linux/imx23/patches/106-add-dcp-dts.patch @@ -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 { |