diff options
author | Zoltan Herpai <wigyori@uid0.hu> | 2013-10-14 19:01:23 +0000 |
---|---|---|
committer | Zoltan Herpai <wigyori@uid0.hu> | 2013-10-14 19:01:23 +0000 |
commit | 07043a853a34292a12044ab524d301ab5932315f (patch) | |
tree | 8feab1a6446b1621759f8eb2d7618af7d037ca8e /target/linux/imx23/patches/105-imx23-dcp.patch | |
parent | 156ea733d4e88fb47b8b3582c1775a854d57c366 (diff) | |
download | upstream-07043a853a34292a12044ab524d301ab5932315f.tar.gz upstream-07043a853a34292a12044ab524d301ab5932315f.tar.bz2 upstream-07043a853a34292a12044ab524d301ab5932315f.zip |
imx23: rename imx23 to mxs for upcoming imx23/28 support
Signed-off-by: Michael Heimpold <mhei@heimpold.de>
Signed-off-by: Zoltan HERPAI <wigyori@uid0.hu>
SVN-Revision: 38394
Diffstat (limited to 'target/linux/imx23/patches/105-imx23-dcp.patch')
-rw-r--r-- | target/linux/imx23/patches/105-imx23-dcp.patch | 957 |
1 files changed, 0 insertions, 957 deletions
diff --git a/target/linux/imx23/patches/105-imx23-dcp.patch b/target/linux/imx23/patches/105-imx23-dcp.patch deleted file mode 100644 index ae74209775..0000000000 --- a/target/linux/imx23/patches/105-imx23-dcp.patch +++ /dev/null @@ -1,957 +0,0 @@ ---- a/drivers/crypto/Kconfig -+++ b/drivers/crypto/Kconfig -@@ -287,6 +287,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 ---- a/drivers/crypto/Makefile -+++ b/drivers/crypto/Makefile -@@ -13,6 +13,7 @@ obj-$(CONFIG_CRYPTO_DEV_OMAP_SHAM) += om - 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/ ---- /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"); |