crypto: dcp - Added support for Freescale's DCP co-processor
This patch enables the DCP crypto functionality on imx28. Currently, only aes-128-cbc is supported. Moreover, the dcpboot misc-device, which is used by Freescale's SDK tools and uses a non-software-readable OTP-key, is added. Changes of v2: - ring buffer for hardware-descriptors - use of ablkcipher walk - OTP key encryption/decryption via misc-device (compatible to Freescale-SDK) - overall cleanup The DCP is also capable of sha1/sha256 but I won't be able to add that anytime soon. Tested with built-in runtime-self-test, tcrypt and openssl via cryptodev 1.6 on imx28-evk and a custom built imx28-board. Signed-off-by: Tobias Rauter <tobias.rauter@gmail.com> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
		
							parent
							
								
									91dc363a86
								
							
						
					
					
						commit
						519d8b1a9d
					
				| @ -699,7 +699,7 @@ | ||||
| 			dcp@80028000 { | ||||
| 				reg = <0x80028000 0x2000>; | ||||
| 				interrupts = <52 53 54>; | ||||
| 				status = "disabled"; | ||||
| 				compatible = "fsl-dcp"; | ||||
| 			}; | ||||
| 
 | ||||
| 			pxp@8002a000 { | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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/ | ||||
|  | ||||
							
								
								
									
										925
									
								
								drivers/crypto/dcp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										925
									
								
								drivers/crypto/dcp.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,925 @@ | ||||
| /*
 | ||||
|  * Cryptographic API. | ||||
|  * | ||||
|  * Support for DCP cryptographic accelerator. | ||||
|  * | ||||
|  * Copyright (c) 2013 | ||||
|  * Author: Tobias Rauter <tobias.rauter@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.rauter@gmail.com>"); | ||||
| MODULE_DESCRIPTION("Freescale DCP Crypto Driver"); | ||||
| MODULE_LICENSE("GPL"); | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user